From 4c391df7442e9c5139c007d07fdcf39a5619d0e5 Mon Sep 17 00:00:00 2001 From: Timothee Guerin Date: Tue, 12 Mar 2024 15:50:23 -0700 Subject: [PATCH 1/3] Fix: OpenAPI3 emitter produce format: binary when multipart part is union with bytes --- packages/openapi3/src/schema-emitter.ts | 13 ++++++++-- packages/openapi3/test/multipart.test.ts | 31 ++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/packages/openapi3/src/schema-emitter.ts b/packages/openapi3/src/schema-emitter.ts index 5b579c6dee..32cc4f4d09 100644 --- a/packages/openapi3/src/schema-emitter.ts +++ b/packages/openapi3/src/schema-emitter.ts @@ -339,7 +339,8 @@ export class OpenAPI3SchemaEmitter extends TypeEmitter< } const refSchema = this.emitter.emitTypeReference(prop.type, { - referenceContext: isMultipart ? { contentType: "application/json" } : {}, + referenceContext: + prop.type.kind !== "Union" && isMultipart ? { contentType: "application/json" } : {}, }); if (refSchema.kind !== "code") { @@ -471,6 +472,7 @@ export class OpenAPI3SchemaEmitter extends TypeEmitter< const schemaMembers: { schema: any; type: Type | null }[] = []; let nullable = false; const discriminator = getDiscriminator(program, union); + const isMultipart = this.#getContentType().startsWith("multipart/"); for (const variant of variants) { if (isNullType(variant.type)) { @@ -478,6 +480,11 @@ export class OpenAPI3SchemaEmitter extends TypeEmitter< continue; } + if (isMultipart && this.#isBytesKeptRaw(variant.type)) { + schemaMembers.push({ schema: { type: "string", format: "binary" }, type: variant.type }); + continue; + } + if (isLiteralType(variant.type)) { if (!literalVariantEnumByType[variant.type.kind]) { const enumSchema = this.emitter.emitTypeReference(variant.type); @@ -491,7 +498,9 @@ export class OpenAPI3SchemaEmitter extends TypeEmitter< literalVariantEnumByType[variant.type.kind].enum.push(variant.type.value); } } else { - const enumSchema = this.emitter.emitTypeReference(variant.type); + const enumSchema = this.emitter.emitTypeReference(variant.type, { + referenceContext: isMultipart ? { contentType: "application/json" } : {}, + }); compilerAssert(enumSchema.kind === "code", "Unexpected enum schema. Should be kind: code"); schemaMembers.push({ schema: enumSchema.value, type: variant.type }); } diff --git a/packages/openapi3/test/multipart.test.ts b/packages/openapi3/test/multipart.test.ts index ef50856d44..43707d3441 100644 --- a/packages/openapi3/test/multipart.test.ts +++ b/packages/openapi3/test/multipart.test.ts @@ -39,6 +39,37 @@ describe("typespec-autorest: multipart", () => { }); }); + it("part of type union `bytes | {content: bytes}` produce `type: string, format: binary`", async () => { + const res = await openApiFor( + ` + op upload(@header contentType: "multipart/form-data", profileImage: bytes | {content: bytes}): void; + ` + ); + const op = res.paths["/"].post; + deepStrictEqual(op.requestBody.content["multipart/form-data"], { + schema: { + type: "object", + properties: { + profileImage: { + anyOf: [ + { + format: "binary", + type: "string", + }, + { + type: "object", + properties: { + content: { type: "string" }, + }, + }, + ], + }, + }, + required: ["profileImage"], + }, + }); + }); + it("part of type `bytes[]` produce `type: array, items: {type: string, format: binary}`", async () => { const res = await openApiFor( ` From efdb51a0a4a17c1d2c1377750277c17040a6d762 Mon Sep 17 00:00:00 2001 From: Timothee Guerin Date: Tue, 12 Mar 2024 15:51:08 -0700 Subject: [PATCH 2/3] Fix test --- packages/openapi3/test/multipart.test.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/openapi3/test/multipart.test.ts b/packages/openapi3/test/multipart.test.ts index 43707d3441..7317d1e7b8 100644 --- a/packages/openapi3/test/multipart.test.ts +++ b/packages/openapi3/test/multipart.test.ts @@ -53,14 +53,15 @@ describe("typespec-autorest: multipart", () => { profileImage: { anyOf: [ { - format: "binary", type: "string", + format: "binary", }, { type: "object", properties: { - content: { type: "string" }, + content: { type: "string", format: "byte" }, }, + required: ["content"], }, ], }, From 5fa4f582c96c4fee12f3e5b44ebfcfbc5a24e8f8 Mon Sep 17 00:00:00 2001 From: Timothee Guerin Date: Tue, 12 Mar 2024 15:53:26 -0700 Subject: [PATCH 3/3] Create fix-openapi3-multipart-union-bytes-2024-2-12-22-52-55.md --- ...x-openapi3-multipart-union-bytes-2024-2-12-22-52-55.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .chronus/changes/fix-openapi3-multipart-union-bytes-2024-2-12-22-52-55.md diff --git a/.chronus/changes/fix-openapi3-multipart-union-bytes-2024-2-12-22-52-55.md b/.chronus/changes/fix-openapi3-multipart-union-bytes-2024-2-12-22-52-55.md new file mode 100644 index 0000000000..f5328e7012 --- /dev/null +++ b/.chronus/changes/fix-openapi3-multipart-union-bytes-2024-2-12-22-52-55.md @@ -0,0 +1,8 @@ +--- +# Change versionKind to one of: internal, fix, dependencies, feature, deprecation, breaking +changeKind: fix +packages: + - "@typespec/openapi3" +--- + +Fix: OpenAPI3 not marking part of bytes or something else as `format: binary`