Skip to content

Commit 50bf7b2

Browse files
olupymc9claude
authored
fix(openapi): use ZModel AST array flag for TypeDef[] @JSON fields (#2314)
Co-authored-by: Yiming <yiming@whimslab.io> Co-authored-by: Claude <noreply@anthropic.com>
1 parent fa7d2b3 commit 50bf7b2

File tree

2 files changed

+62
-1
lines changed

2 files changed

+62
-1
lines changed

packages/plugins/openapi/src/rpc-generator.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -785,9 +785,11 @@ export class RPCOpenAPIGenerator extends OpenAPIGeneratorBase {
785785
const field = dataModel.fields.find((f) => f.name === def.name);
786786
if (field?.type.reference?.ref && isTypeDef(field.type.reference.ref)) {
787787
// This Json field references a TypeDef
788+
// Use field.type.array from ZModel AST instead of def.isList from DMMF,
789+
// since Prisma treats TypeDef fields as plain Json and doesn't know about arrays
788790
return this.wrapArray(
789791
this.wrapNullable(this.ref(field.type.reference.ref.name, true), !def.isRequired),
790-
def.isList
792+
field.type.array
791793
);
792794
}
793795
}

packages/plugins/openapi/tests/openapi-rpc.test.ts

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,65 @@ model Product {
518518
}
519519
});
520520

521+
it('array of TypeDef with enum directly on model field', async () => {
522+
for (const specVersion of ['3.0.0', '3.1.0']) {
523+
const { model, dmmf, modelFile } = await loadZModelAndDmmf(`
524+
plugin openapi {
525+
provider = '${normalizePath(path.resolve(__dirname, '../dist'))}'
526+
specVersion = '${specVersion}'
527+
}
528+
529+
enum Language {
530+
FR
531+
EN
532+
ES
533+
DE
534+
IT
535+
}
536+
537+
type TranslatedField {
538+
language Language
539+
content String
540+
}
541+
542+
model Article {
543+
id String @id @default(cuid())
544+
title TranslatedField[] @json
545+
description TranslatedField[] @json
546+
547+
@@allow('all', true)
548+
}
549+
`);
550+
551+
const { name: output } = tmp.fileSync({ postfix: '.yaml' });
552+
553+
const options = buildOptions(model, modelFile, output);
554+
await generate(model, options, dmmf);
555+
556+
await OpenAPIParser.validate(output);
557+
558+
const parsed = YAML.parse(fs.readFileSync(output, 'utf-8'));
559+
expect(parsed.openapi).toBe(specVersion);
560+
561+
// Verify TranslatedField TypeDef is generated
562+
expect(parsed.components.schemas.TranslatedField).toBeDefined();
563+
564+
// Verify Language enum is generated
565+
expect(parsed.components.schemas.Language).toBeDefined();
566+
567+
// Verify enum reference inside TranslatedField
568+
expect(parsed.components.schemas.TranslatedField.properties.language.$ref).toBe('#/components/schemas/Language');
569+
570+
// Verify array of TypeDef directly on model field
571+
expect(parsed.components.schemas.Article.properties.title.type).toBe('array');
572+
expect(parsed.components.schemas.Article.properties.title.items.$ref).toBe('#/components/schemas/TranslatedField');
573+
574+
// Verify second array field as well
575+
expect(parsed.components.schemas.Article.properties.description.type).toBe('array');
576+
expect(parsed.components.schemas.Article.properties.description.items.$ref).toBe('#/components/schemas/TranslatedField');
577+
}
578+
});
579+
521580
it('full-text search', async () => {
522581
const { model, dmmf, modelFile } = await loadZModelAndDmmf(`
523582
generator js {

0 commit comments

Comments
 (0)