Skip to content

Commit 6a9e367

Browse files
authored
fix: generate:types inlines all blocks, add forceInlineBlocks property to use in plugin mcp (#15892)
Fixes #15820 which is a regression from #15675. This PR adds a new property to use it for plugin mcp internal purposes instead of modifying the behaviour globally.
1 parent de3e5ae commit 6a9e367

3 files changed

Lines changed: 92 additions & 31 deletions

File tree

packages/payload/src/utilities/configToJSONSchema.spec.ts

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -411,7 +411,11 @@ describe('configToJSONSchema', () => {
411411
someBlockField: {
412412
type: ['array', 'null'],
413413
items: {
414-
oneOf: [expectedBlockSchema],
414+
oneOf: [
415+
{
416+
$ref: '#/definitions/SharedBlock',
417+
},
418+
],
415419
},
416420
},
417421
},
@@ -453,4 +457,57 @@ describe('configToJSONSchema', () => {
453457
// @ts-expect-error
454458
expect(schema.definitions.test.properties.title.required).toStrictEqual(false)
455459
})
460+
461+
it('should propagate forceInlineBlocks to nested fields (array, group, tab)', async () => {
462+
const namedBlock: Block = {
463+
slug: 'myBlock',
464+
interfaceName: 'MyBlock',
465+
fields: [{ name: 'text', type: 'text' }],
466+
}
467+
468+
// @ts-expect-error
469+
const config: Config = {
470+
collections: [
471+
{
472+
slug: 'test',
473+
fields: [
474+
{
475+
name: 'arr',
476+
type: 'array',
477+
fields: [{ name: 'blocks', type: 'blocks', blocks: [namedBlock] }],
478+
},
479+
{
480+
name: 'grp',
481+
type: 'group',
482+
fields: [{ name: 'blocks', type: 'blocks', blocks: [namedBlock] }],
483+
},
484+
],
485+
timestamps: false,
486+
},
487+
],
488+
}
489+
490+
const sanitizedConfig = await sanitizeConfig(config)
491+
492+
// Without forceInlineBlocks: blocks field uses $ref
493+
const schemaDefault = configToJSONSchema(sanitizedConfig, 'text')
494+
const arrItemsDefault = schemaDefault.definitions!.test.properties!.arr.items as JSONSchema4
495+
const arrBlocksDefault = (arrItemsDefault.properties!.blocks.items as JSONSchema4).oneOf![0]
496+
expect(arrBlocksDefault).toStrictEqual({ $ref: '#/definitions/MyBlock' })
497+
498+
// With forceInlineBlocks: blocks field is inlined, no $ref
499+
const schemaInline = configToJSONSchema(sanitizedConfig, 'text', undefined, {
500+
forceInlineBlocks: true,
501+
})
502+
const arrItemsInline = schemaInline.definitions!.test.properties!.arr.items as JSONSchema4
503+
const arrBlocksInline = (arrItemsInline.properties!.blocks.items as JSONSchema4).oneOf![0]
504+
expect(arrBlocksInline).not.toHaveProperty('$ref')
505+
expect(arrBlocksInline.properties?.blockType).toStrictEqual({ const: 'myBlock' })
506+
507+
const grpBlocksInline = (
508+
schemaInline.definitions!.test.properties!.grp.properties!.blocks.items as JSONSchema4
509+
).oneOf![0]
510+
expect(grpBlocksInline).not.toHaveProperty('$ref')
511+
expect(grpBlocksInline.properties?.blockType).toStrictEqual({ const: 'myBlock' })
512+
})
456513
})

packages/payload/src/utilities/configToJSONSchema.ts

Lines changed: 31 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -157,11 +157,13 @@ function generateWidgetSchemas({
157157
config,
158158
i18n,
159159
interfaceNameDefinitions,
160+
opts = {},
160161
}: {
161162
collectionIDFieldTypes: { [key: string]: 'number' | 'string' }
162163
config: SanitizedConfig
163164
i18n?: I18n
164165
interfaceNameDefinitions: Map<string, JSONSchema4>
166+
opts?: ConfigToJSONSchemaOptions
165167
}): {
166168
definitions: Record<string, JSONSchema4>
167169
schema: JSONSchema4
@@ -185,6 +187,7 @@ function generateWidgetSchemas({
185187
interfaceNameDefinitions,
186188
config,
187189
i18n,
190+
opts,
188191
)
189192

190193
dataSchema = {
@@ -342,6 +345,11 @@ function entityOrFieldToJsDocs({
342345
}
343346
return description
344347
}
348+
349+
type ConfigToJSONSchemaOptions = {
350+
forceInlineBlocks?: boolean
351+
}
352+
345353
export function fieldsToJSONSchema(
346354
/**
347355
* Used for relationship fields, to determine whether to use a string or number type for the ID.
@@ -356,6 +364,7 @@ export function fieldsToJSONSchema(
356364
interfaceNameDefinitions: Map<string, JSONSchema4>,
357365
config?: SanitizedConfig,
358366
i18n?: I18n,
367+
opts: ConfigToJSONSchemaOptions = {},
359368
): {
360369
properties: {
361370
[k: string]: JSONSchema4
@@ -391,6 +400,7 @@ export function fieldsToJSONSchema(
391400
interfaceNameDefinitions,
392401
config,
393402
i18n,
403+
opts,
394404
),
395405
},
396406
}
@@ -420,47 +430,28 @@ export function fieldsToJSONSchema(
420430
oneOf: (field.blockReferences ?? field.blocks).map((block) => {
421431
if (typeof block === 'string') {
422432
const resolvedBlock = config?.blocks?.find((b) => b.slug === block)
433+
423434
if (!resolvedBlock) {
424435
return {}
425436
}
426437

427-
const resolvedBlockFieldSchemas = fieldsToJSONSchema(
428-
collectionIDFieldTypes,
429-
resolvedBlock.flattenedFields,
430-
interfaceNameDefinitions,
431-
config,
432-
i18n,
433-
)
434-
435-
const resolvedBlockSchema: JSONSchema4 = {
436-
type: 'object',
437-
additionalProperties: false,
438-
properties: {
439-
...resolvedBlockFieldSchemas.properties,
440-
blockType: {
441-
const: resolvedBlock.slug,
442-
},
443-
},
444-
required: ['blockType', ...resolvedBlockFieldSchemas.required],
445-
}
446-
447-
if (resolvedBlock.interfaceName) {
448-
interfaceNameDefinitions.set(
449-
resolvedBlock.interfaceName,
450-
resolvedBlockSchema,
451-
)
438+
if (!opts.forceInlineBlocks) {
439+
return {
440+
$ref: `#/definitions/${resolvedBlock.interfaceName ?? resolvedBlock.slug}`,
441+
}
452442
}
453443

454-
return resolvedBlockSchema
444+
block = resolvedBlock
455445
}
456-
457446
const blockFieldSchemas = fieldsToJSONSchema(
458447
collectionIDFieldTypes,
459448
block.flattenedFields,
460449
interfaceNameDefinitions,
461450
config,
462451
i18n,
452+
opts,
463453
)
454+
464455
const blockSchema: JSONSchema4 = {
465456
type: 'object',
466457
additionalProperties: false,
@@ -473,9 +464,12 @@ export function fieldsToJSONSchema(
473464
required: ['blockType', ...blockFieldSchemas.required],
474465
}
475466

476-
if (block.interfaceName) {
467+
if (!opts.forceInlineBlocks && block.interfaceName) {
477468
interfaceNameDefinitions.set(block.interfaceName, blockSchema)
478-
return blockSchema
469+
470+
return {
471+
$ref: `#/definitions/${block.interfaceName}`,
472+
}
479473
}
480474

481475
return blockSchema
@@ -515,6 +509,7 @@ export function fieldsToJSONSchema(
515509
interfaceNameDefinitions,
516510
config,
517511
i18n,
512+
opts,
518513
),
519514
}
520515

@@ -830,6 +825,7 @@ export function fieldsToJSONSchema(
830825
interfaceNameDefinitions,
831826
config,
832827
i18n,
828+
opts,
833829
),
834830
}
835831

@@ -889,6 +885,7 @@ export function entityToJSONSchema(
889885
defaultIDType: 'number' | 'text',
890886
collectionIDFieldTypes?: { [key: string]: 'number' | 'string' },
891887
i18n?: I18n,
888+
opts: ConfigToJSONSchemaOptions = {},
892889
): JSONSchema4 {
893890
if (!collectionIDFieldTypes) {
894891
collectionIDFieldTypes = getCollectionIDFieldTypes({ config, defaultIDType })
@@ -949,6 +946,7 @@ export function entityToJSONSchema(
949946
interfaceNameDefinitions,
950947
config,
951948
i18n,
949+
opts,
952950
)
953951

954952
// Add collection property to auth collections
@@ -1262,6 +1260,7 @@ export function configToJSONSchema(
12621260
config: SanitizedConfig,
12631261
defaultIDType?: 'number' | 'text',
12641262
i18n?: I18n,
1263+
opts: ConfigToJSONSchemaOptions = {},
12651264
): JSONSchema4 {
12661265
// a mutable Map to store custom top-level `interfaceName` types. Fields with an `interfaceName` property will be moved to the top-level definitions here
12671266
const interfaceNameDefinitions: Map<string, JSONSchema4> = new Map()
@@ -1295,6 +1294,7 @@ export function configToJSONSchema(
12951294
defaultIDType!,
12961295
collectionIDFieldTypes,
12971296
i18n,
1297+
opts,
12981298
)
12991299
const select = fieldsToSelectJSONSchema({
13001300
config,
@@ -1325,6 +1325,7 @@ export function configToJSONSchema(
13251325
config,
13261326
i18n,
13271327
interfaceNameDefinitions,
1328+
opts,
13281329
})
13291330

13301331
const authOperationDefinitions = [...config.collections]
@@ -1361,6 +1362,7 @@ export function configToJSONSchema(
13611362
interfaceNameDefinitions,
13621363
config,
13631364
i18n,
1365+
opts,
13641366
)
13651367

13661368
const blockSchema: JSONSchema4 = {

packages/plugin-mcp/src/mcp/getMcpHandler.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,9 @@ export const getMCPHandler = (
4949
req: PayloadRequest,
5050
) => {
5151
const { payload } = req
52-
const configSchema = configToJSONSchema(payload.config, payload.db.defaultIDType, req.i18n)
52+
const configSchema = configToJSONSchema(payload.config, payload.db.defaultIDType, req.i18n, {
53+
forceInlineBlocks: true,
54+
})
5355

5456
// Handler wrapper that injects req before the _extra argument
5557
const wrapHandler = (handler: (...args: any[]) => any) => {

0 commit comments

Comments
 (0)