Skip to content

Commit

Permalink
Fix RichTextBlock draft content validation (#1980)
Browse files Browse the repository at this point in the history
Extend validation to validate inline links in draft content.
  • Loading branch information
johnnyomair authored Apr 25, 2024
1 parent c1ca9c3 commit be8664c
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 3 deletions.
7 changes: 7 additions & 0 deletions .changeset/poor-cooks-protect.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@comet/blocks-api": patch
---

Fix `RichTextBlock` draft content validation

Extend validation to validate inline links in draft content.
53 changes: 50 additions & 3 deletions packages/api/blocks-api/src/blocks/createRichTextBlock.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { instanceToPlain, plainToInstance } from "class-transformer";
import { Allow, IsObject } from "class-validator";
import { registerDecorator, validate, ValidationArguments, ValidationOptions } from "class-validator";
import type { DraftBlockType, DraftEntityMutability, DraftInlineStyleType, RawDraftContentState, RawDraftEntityRange } from "draft-js";

import { createAppliedMigrationsBlockDataFactoryDecorator } from "../migrations/createAppliedMigrationsBlockDataFactoryDecorator";
Expand Down Expand Up @@ -106,8 +106,7 @@ export function createRichTextBlock<LinkBlock extends Block>(
}

class RichTextBlockInput implements RichTextBlockInputInterface<ExtractBlockInput<LinkBlock>> {
@Allow()
@IsObject()
@IsDraftContent(LinkBlock)
@BlockField({ type: "json" })
draftContent: DraftJsInput<ExtractBlockInput<LinkBlock>>;

Expand Down Expand Up @@ -192,3 +191,51 @@ export function createRichTextBlock<LinkBlock extends Block>(

return RichTextBlock;
}

function IsDraftContent(link: Block, validationOptions?: ValidationOptions) {
// eslint-disable-next-line @typescript-eslint/ban-types
return function (object: Object, propertyName: string) {
registerDecorator({
name: "isDraftContent",
target: object.constructor,
propertyName,
constraints: [link],
options: validationOptions,
validator: {
async validate(value: unknown, args: ValidationArguments) {
const LinkBlock = args.constraints[0] as Block;

if (isDraftJsInput(value)) {
for (const entity of Object.values(value.entityMap)) {
const validationErrors = await validate(LinkBlock.blockInputFactory(entity.data), {
forbidNonWhitelisted: true,
whitelist: true,
});

if (validationErrors.length > 0) {
return false;
}
}

return true;
}

return false;
},
},
});
};
}

function isDraftJsInput(value: unknown): value is DraftJsInput<BlockInputInterface> {
return (
typeof value === "object" &&
value !== null &&
"blocks" in value &&
"entityMap" in value &&
Array.isArray(value.blocks) &&
typeof value.entityMap === "object" &&
value.entityMap !== null &&
Object.values(value.entityMap).every((entity) => typeof entity === "object" && entity !== null && entity.type === "LINK")
);
}

0 comments on commit be8664c

Please sign in to comment.