Skip to content

Commit

Permalink
Fix input validation for missing child blocks (#2942)
Browse files Browse the repository at this point in the history
  • Loading branch information
johnnyomair authored Dec 18, 2024
1 parent 4df9d45 commit 58a99bb
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 7 deletions.
5 changes: 5 additions & 0 deletions .changeset/new-cherries-provide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@comet/blocks-api": patch
---

Fix input validation for missing child blocks
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { BlockData, BlockDataInterface, BlockInput, createBlock, ExtractBlockData, ExtractBlockInput, inputToData } from "../block";
import { createRichTextBlock } from "../createRichTextBlock";
import { ExternalLinkBlock } from "../ExternalLinkBlock";
import { ChildBlock } from "./child-block";
import { ChildBlockInput } from "./child-block-input";

const RichTextBlock = createRichTextBlock({ link: ExternalLinkBlock });

class HeadlineBlockData extends BlockData {
@ChildBlock(RichTextBlock)
headline: ExtractBlockData<typeof RichTextBlock>;
}

class HeadlineBlockInput extends BlockInput {
@ChildBlockInput(RichTextBlock)
headline: ExtractBlockInput<typeof RichTextBlock>;

transformToBlockData(): BlockDataInterface {
return inputToData(HeadlineBlockData, this);
}
}

const HeadlineBlock = createBlock(HeadlineBlockData, HeadlineBlockInput, "Headline");

describe("ChildBlockInput", () => {
it("should fail if no value is provided", () => {
// `@Transform()` allows any value, so we use any here.
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const plain: any = {};

expect(() => {
HeadlineBlock.blockInputFactory(plain);
}).toThrowError(`Missing child block input for 'headline' (RichText)`);
});
});
24 changes: 17 additions & 7 deletions packages/api/blocks-api/src/blocks/decorators/child-block-input.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Transform } from "class-transformer";
import { Expose, Transform } from "class-transformer";
import { ValidateNested } from "class-validator";

import { Block, isBlockInputInterface } from "../block";
Expand All @@ -22,13 +22,23 @@ export function ChildBlockInput(block: Block, options?: ChildBlockInputOptions):
ValidateNested()(target, key); // by default valdidate all child blocks
}
BlockField({ type: "block", block, nullable })(target, key);
// We need add `@Expose()` to make sure that `@Transform()` is called even when no value is provided.
// See https://github.com/typestack/class-transformer/issues/1599#issuecomment-2094934401.
Expose()(target, key);
Transform(
({ value }) =>
isBlockInputInterface(value)
? value
: nullable && (value === undefined || value === null)
? undefined
: block.blockInputFactory(value),
({ value }) => {
if (isBlockInputInterface(value)) {
return value;
} else if (nullable && value == null) {
return undefined;
} else {
if (value == null) {
throw new Error(`Missing child block input for '${key as string}' (${block.name})`);
}

return block.blockInputFactory(value);
}
},
{
toClassOnly: true,
},
Expand Down

0 comments on commit 58a99bb

Please sign in to comment.