From 5d6a2c5bac6d68ffdb5d6b785308379e45f05608 Mon Sep 17 00:00:00 2001 From: dualfroz Date: Sat, 17 Oct 2026 03:33:16 +0200 Subject: [PATCH] fix(core): don't mark optional blocks fields as required in openapi blocksToInputSchema ignored the attribute's required option, so a blocks field that is not required ended up in the required list of the POST request body in the generated OpenAPI spec. It now goes through maybeRequired like the other input converters. --- .../validation/__tests__/content-type.test.ts | 23 +++++++++++++++++++ .../core-api/routes/validation/attributes.ts | 9 ++++++-- .../src/core-api/routes/validation/mappers.ts | 2 +- .../openapi-generate.test.cli.ts.snap | 3 --- 4 files changed, 31 insertions(+), 6 deletions(-) diff --git a/packages/core/core/src/core-api/routes/validation/__tests__/content-type.test.ts b/packages/core/core/src/core-api/routes/validation/__tests__/content-type.test.ts index a833d59..2ec11ae 100644 --- a/packages/core/core/src/core-api/routes/validation/__tests__/content-type.test.ts +++ b/packages/core/core/src/core-api/routes/validation/__tests__/content-type.test.ts @@ -38,3 +38,26 @@ describe('CoreContentTypeRouteValidator query parameters', () => { expect(filters?.safeParse({ unknown: { $eq: 'hello' } }).success).toBe(false); }); }); + +describe('CoreContentTypeRouteValidator body', () => { + const strapi = { + getModel: jest.fn(() => ({ + attributes: { + title: { type: 'string', required: true }, + content: { type: 'blocks' }, + }, + })), + } as unknown as Core.Strapi; + + const validator = new CoreContentTypeRouteValidator(strapi, 'api::article.article'); + + test('non-required blocks attributes are optional', () => { + const { properties } = z.toJSONSchema(validator.body, { + target: 'draft-2020-12', + io: 'output', + }); + + expect(properties?.data).toMatchObject({ required: ['title'] }); + expect(validator.body.safeParse({ data: { title: 'Article' } }).success).toBe(true); + }); +}); diff --git a/packages/core/core/src/core-api/routes/validation/attributes.ts b/packages/core/core/src/core-api/routes/validation/attributes.ts index ae08723..9c3f6dc 100644 --- a/packages/core/core/src/core-api/routes/validation/attributes.ts +++ b/packages/core/core/src/core-api/routes/validation/attributes.ts @@ -418,11 +418,16 @@ export const bigIntegerToInputSchema = (attribute: Schema.Attribute.BigInteger) /** * Converts a blocks attribute to a Zod schema for input validation. + * @param attribute - The Blocks attribute object from the Strapi schema. * @returns A Zod schema for input validation of the blocks field. */ -export const blocksToInputSchema = () => { +export const blocksToInputSchema = (attribute: Schema.Attribute.Blocks) => { + const { required } = attribute; + // TODO: better support blocks data structure - return z.array(z.any()).describe('A blocks field'); + const schema = augmentSchema(z.array(z.any()), [maybeRequired(required)]); + + return schema.describe('A blocks field'); }; /** diff --git a/packages/core/core/src/core-api/routes/validation/mappers.ts b/packages/core/core/src/core-api/routes/validation/mappers.ts index 33b74fa..a2c12bc 100644 --- a/packages/core/core/src/core-api/routes/validation/mappers.ts +++ b/packages/core/core/src/core-api/routes/validation/mappers.ts @@ -213,7 +213,7 @@ export const mapAttributeToInputSchema = ( case 'biginteger': return attributes.bigIntegerToInputSchema(attribute); case 'blocks': - return attributes.blocksToInputSchema(); + return attributes.blocksToInputSchema(attribute); case 'boolean': return attributes.booleanToInputSchema(attribute); case 'component': diff --git a/tests/cli/tests/strapi/strapi/__snapshots__/openapi-generate.test.cli.ts.snap b/tests/cli/tests/strapi/strapi/__snapshots__/openapi-generate.test.cli.ts.snap index aa8a2f2..c85cac4 100644 --- a/tests/cli/tests/strapi/strapi/__snapshots__/openapi-generate.test.cli.ts.snap +++ b/tests/cli/tests/strapi/strapi/__snapshots__/openapi-generate.test.cli.ts.snap @@ -2303,7 +2303,6 @@ exports[`openapi:generate should generate a spec describing i18n and users-permi }, }, "required": [ - "content", "publishedAt", ], "type": "object", @@ -8339,7 +8338,6 @@ exports[`openapi:generate should generate a spec describing i18n and users-permi }, }, "required": [ - "blocks", "required", "publishedAt", ], @@ -16792,7 +16790,6 @@ exports[`openapi:generate should generate a spec describing i18n and users-permi "name", "slug", "isAvailable", - "description", "publishedAt", ], "type": "object",