From eb265d21da37d000c5642bb2d33c8392c3ae1d37 Mon Sep 17 00:00:00 2001 From: Wanpeng Li Date: Fri, 27 Dec 2024 13:29:44 +0800 Subject: [PATCH 1/4] fix --- packages/openapi3/src/openapi.ts | 1 + packages/openapi3/test/emit-openapi.test.ts | 141 ++++++++++++++++++++ packages/openapi3/test/test-host.ts | 10 +- 3 files changed, 148 insertions(+), 4 deletions(-) create mode 100644 packages/openapi3/test/emit-openapi.test.ts diff --git a/packages/openapi3/src/openapi.ts b/packages/openapi3/src/openapi.ts index f70ef06230..b2f19c86dd 100644 --- a/packages/openapi3/src/openapi.ts +++ b/packages/openapi3/src/openapi.ts @@ -1844,6 +1844,7 @@ function serializeDocument(root: OpenAPI3Document, fileType: FileType): string { singleQuote: true, aliasDuplicateObjects: false, lineWidth: 0, + schema: "yaml-1.1", }); } } diff --git a/packages/openapi3/test/emit-openapi.test.ts b/packages/openapi3/test/emit-openapi.test.ts new file mode 100644 index 0000000000..b3e679ffd7 --- /dev/null +++ b/packages/openapi3/test/emit-openapi.test.ts @@ -0,0 +1,141 @@ +import { describe, expect, it } from "vitest"; +import { emitOpenApiWithDiagnostics } from "./test-host.js"; + +interface TestCase { + content: string; + expected: string; + description: string; +} + +function createExpectedContentForStringLiteral(expected: string) { + return `openapi: 3.0.0 +info: + title: (title) + version: 0.0.0 +tags: [] +paths: {} +components: + schemas: + Test: + type: object + required: + - test + properties: + test: + type: string + enum: + - ${expected} +`; +} + +function createExpectedContentForEnum(expected: string) { + return `openapi: 3.0.0 +info: + title: (title) + version: 0.0.0 +tags: [] +paths: {} +components: + schemas: + Test: + type: string + enum: + - ${expected} +`; +} + +function createTestCaseForStringLiteral(test: string, expected: string) { + return { + description: `string literal should output ${expected} in yaml file`, + content: `model Test {test: "${test}"}`, + expected: createExpectedContentForStringLiteral(`${expected}`), + }; +} + +function createTestCaseForEnum(test: string, expected: string) { + return { + description: `enum should output ${expected} in yaml file`, + content: `enum Test {test: "${test}"}`, + expected: createExpectedContentForEnum(`${expected}`), + }; +} + +const testCasesForStringLiteral: TestCase[] = [ + createTestCaseForStringLiteral(`y`, `'y'`), + createTestCaseForStringLiteral(`Y`, `'Y'`), + createTestCaseForStringLiteral(`yes`, `'yes'`), + createTestCaseForStringLiteral(`Yes`, `'Yes'`), + createTestCaseForStringLiteral(`YES`, `'YES'`), + createTestCaseForStringLiteral(`yES`, `yES`), + + createTestCaseForStringLiteral(`n`, `'n'`), + createTestCaseForStringLiteral(`N`, `'N'`), + createTestCaseForStringLiteral(`no`, `'no'`), + createTestCaseForStringLiteral(`No`, `'No'`), + createTestCaseForStringLiteral(`NO`, `'NO'`), + createTestCaseForStringLiteral(`nO`, `nO`), + + createTestCaseForStringLiteral(`true`, `'true'`), + createTestCaseForStringLiteral(`True`, `'True'`), + createTestCaseForStringLiteral(`TRUE`, `'TRUE'`), + createTestCaseForStringLiteral(`tRUE`, `tRUE`), + + createTestCaseForStringLiteral(`false`, `'false'`), + createTestCaseForStringLiteral(`False`, `'False'`), + createTestCaseForStringLiteral(`FALSE`, `'FALSE'`), + createTestCaseForStringLiteral(`fALSE`, `fALSE`), + + createTestCaseForStringLiteral(`on`, `'on'`), + createTestCaseForStringLiteral(`On`, `'On'`), + createTestCaseForStringLiteral(`ON`, `'ON'`), + createTestCaseForStringLiteral(`oN`, `oN`), + + createTestCaseForStringLiteral(`off`, `'off'`), + createTestCaseForStringLiteral(`Off`, `'Off'`), + createTestCaseForStringLiteral(`OFF`, `'OFF'`), + createTestCaseForStringLiteral(`oFF`, `oFF`), +]; + +const testCasesForEnum: TestCase[] = [ + createTestCaseForEnum(`y`, `'y'`), + createTestCaseForEnum(`Y`, `'Y'`), + createTestCaseForEnum(`yes`, `'yes'`), + createTestCaseForEnum(`Yes`, `'Yes'`), + createTestCaseForEnum(`YES`, `'YES'`), + createTestCaseForEnum(`yES`, `yES`), + + createTestCaseForEnum(`n`, `'n'`), + createTestCaseForEnum(`N`, `'N'`), + createTestCaseForEnum(`no`, `'no'`), + createTestCaseForEnum(`No`, `'No'`), + createTestCaseForEnum(`NO`, `'NO'`), + createTestCaseForEnum(`nO`, `nO`), + + createTestCaseForEnum(`true`, `'true'`), + createTestCaseForEnum(`True`, `'True'`), + createTestCaseForEnum(`TRUE`, `'TRUE'`), + createTestCaseForEnum(`tRUE`, `tRUE`), + + createTestCaseForEnum(`false`, `'false'`), + createTestCaseForEnum(`False`, `'False'`), + createTestCaseForEnum(`FALSE`, `'FALSE'`), + createTestCaseForEnum(`fALSE`, `fALSE`), + + createTestCaseForEnum(`on`, `'on'`), + createTestCaseForEnum(`On`, `'On'`), + createTestCaseForEnum(`ON`, `'ON'`), + createTestCaseForEnum(`oN`, `oN`), + + createTestCaseForEnum(`off`, `'off'`), + createTestCaseForEnum(`Off`, `'Off'`), + createTestCaseForEnum(`OFF`, `'OFF'`), + createTestCaseForEnum(`oFF`, `oFF`), +]; + + +describe("Scalar formats of serialized document in YAML", () => { + it.each([...testCasesForEnum, ...testCasesForStringLiteral])("$description", async (c: TestCase) => { + const [_, __, content] = await emitOpenApiWithDiagnostics(c.content); + expect(content).toBe(c.expected); + }); +}); diff --git a/packages/openapi3/test/test-host.ts b/packages/openapi3/test/test-host.ts index 2be1f42e0f..2aa902decb 100644 --- a/packages/openapi3/test/test-host.ts +++ b/packages/openapi3/test/test-host.ts @@ -11,6 +11,7 @@ import { RestTestLibrary } from "@typespec/rest/testing"; import { VersioningTestLibrary } from "@typespec/versioning/testing"; import { XmlTestLibrary } from "@typespec/xml/testing"; import { ok } from "assert"; +import { parse } from "yaml"; import { OpenAPI3EmitterOptions } from "../src/lib.js"; import { OpenAPI3TestLibrary } from "../src/testing/index.js"; import { OpenAPI3Document } from "../src/types.js"; @@ -56,9 +57,10 @@ export async function createOpenAPITestRunner({ export async function emitOpenApiWithDiagnostics( code: string, options: OpenAPI3EmitterOptions = {}, -): Promise<[OpenAPI3Document, readonly Diagnostic[]]> { +): Promise<[OpenAPI3Document, readonly Diagnostic[], string]> { const runner = await createOpenAPITestRunner(); - const outputFile = resolveVirtualPath("openapi.json"); + const fileType = options["file-type"] || "yaml"; + const outputFile = resolveVirtualPath("openapi" + fileType === "json" ? ".json" : ".yaml"); const diagnostics = await runner.diagnose(code, { noEmit: false, emit: ["@typespec/openapi3"], @@ -68,8 +70,8 @@ export async function emitOpenApiWithDiagnostics( }); const content = runner.fs.get(outputFile); ok(content, "Expected to have found openapi output"); - const doc = JSON.parse(content); - return [doc, diagnostics]; + const doc = fileType === "json" ? JSON.parse(content) : parse(content); + return [doc, diagnostics, content]; } export async function diagnoseOpenApiFor(code: string, options: OpenAPI3EmitterOptions = {}) { From eadcb4d203abd8573bdf024facb073d653117f25 Mon Sep 17 00:00:00 2001 From: Wanpeng Li Date: Fri, 27 Dec 2024 14:24:49 +0800 Subject: [PATCH 2/4] . --- packages/openapi3/test/emit-openapi.test.ts | 192 +++++++------------- 1 file changed, 65 insertions(+), 127 deletions(-) diff --git a/packages/openapi3/test/emit-openapi.test.ts b/packages/openapi3/test/emit-openapi.test.ts index b3e679ffd7..cbc58695c7 100644 --- a/packages/openapi3/test/emit-openapi.test.ts +++ b/packages/openapi3/test/emit-openapi.test.ts @@ -1,35 +1,41 @@ import { describe, expect, it } from "vitest"; import { emitOpenApiWithDiagnostics } from "./test-host.js"; -interface TestCase { - content: string; - expected: string; - description: string; -} - -function createExpectedContentForStringLiteral(expected: string) { - return `openapi: 3.0.0 -info: - title: (title) - version: 0.0.0 -tags: [] -paths: {} -components: - schemas: - Test: - type: object - required: - - test - properties: - test: - type: string - enum: - - ${expected} -`; -} - -function createExpectedContentForEnum(expected: string) { - return `openapi: 3.0.0 +describe("Scalar formats of serialized document in YAML", () => { + it("should add single quote for y|Y|yes|Yes|YES|n|N|no|No|NO|true|True|TRUE|false|False|FALSE|on|On|ON|off|Off|OFF", async (c: TestCase) => { + const [_, __, content] = await emitOpenApiWithDiagnostics(` + enum TestEnum { + y: "y", + Y: "Y", + yes: "yes", + Yes: "Yes", + YES: "YES", + yEs: "yEs", + n: "n", + N: "N", + no: "no", + No: "No", + NO: "NO", + nO: "nO", + "true": "true", + True: "True", + TRUE: "TRUE", + tRUE: "tRUE", + "false": "false", + False: "False", + FALSE: "FALSE", + fALSE: "fALSE", + on: "on", + On: "On", + ON: "ON", + oN: "oN", + off: "off", + Off: "Off", + OFF: "OFF", + oFF: "oFF" + } + `); + expect(content).toBe(`openapi: 3.0.0 info: title: (title) version: 0.0.0 @@ -37,105 +43,37 @@ tags: [] paths: {} components: schemas: - Test: + TestEnum: type: string enum: - - ${expected} -`; -} - -function createTestCaseForStringLiteral(test: string, expected: string) { - return { - description: `string literal should output ${expected} in yaml file`, - content: `model Test {test: "${test}"}`, - expected: createExpectedContentForStringLiteral(`${expected}`), - }; -} - -function createTestCaseForEnum(test: string, expected: string) { - return { - description: `enum should output ${expected} in yaml file`, - content: `enum Test {test: "${test}"}`, - expected: createExpectedContentForEnum(`${expected}`), - }; -} - -const testCasesForStringLiteral: TestCase[] = [ - createTestCaseForStringLiteral(`y`, `'y'`), - createTestCaseForStringLiteral(`Y`, `'Y'`), - createTestCaseForStringLiteral(`yes`, `'yes'`), - createTestCaseForStringLiteral(`Yes`, `'Yes'`), - createTestCaseForStringLiteral(`YES`, `'YES'`), - createTestCaseForStringLiteral(`yES`, `yES`), - - createTestCaseForStringLiteral(`n`, `'n'`), - createTestCaseForStringLiteral(`N`, `'N'`), - createTestCaseForStringLiteral(`no`, `'no'`), - createTestCaseForStringLiteral(`No`, `'No'`), - createTestCaseForStringLiteral(`NO`, `'NO'`), - createTestCaseForStringLiteral(`nO`, `nO`), - - createTestCaseForStringLiteral(`true`, `'true'`), - createTestCaseForStringLiteral(`True`, `'True'`), - createTestCaseForStringLiteral(`TRUE`, `'TRUE'`), - createTestCaseForStringLiteral(`tRUE`, `tRUE`), - - createTestCaseForStringLiteral(`false`, `'false'`), - createTestCaseForStringLiteral(`False`, `'False'`), - createTestCaseForStringLiteral(`FALSE`, `'FALSE'`), - createTestCaseForStringLiteral(`fALSE`, `fALSE`), - - createTestCaseForStringLiteral(`on`, `'on'`), - createTestCaseForStringLiteral(`On`, `'On'`), - createTestCaseForStringLiteral(`ON`, `'ON'`), - createTestCaseForStringLiteral(`oN`, `oN`), - - createTestCaseForStringLiteral(`off`, `'off'`), - createTestCaseForStringLiteral(`Off`, `'Off'`), - createTestCaseForStringLiteral(`OFF`, `'OFF'`), - createTestCaseForStringLiteral(`oFF`, `oFF`), -]; - -const testCasesForEnum: TestCase[] = [ - createTestCaseForEnum(`y`, `'y'`), - createTestCaseForEnum(`Y`, `'Y'`), - createTestCaseForEnum(`yes`, `'yes'`), - createTestCaseForEnum(`Yes`, `'Yes'`), - createTestCaseForEnum(`YES`, `'YES'`), - createTestCaseForEnum(`yES`, `yES`), - - createTestCaseForEnum(`n`, `'n'`), - createTestCaseForEnum(`N`, `'N'`), - createTestCaseForEnum(`no`, `'no'`), - createTestCaseForEnum(`No`, `'No'`), - createTestCaseForEnum(`NO`, `'NO'`), - createTestCaseForEnum(`nO`, `nO`), - - createTestCaseForEnum(`true`, `'true'`), - createTestCaseForEnum(`True`, `'True'`), - createTestCaseForEnum(`TRUE`, `'TRUE'`), - createTestCaseForEnum(`tRUE`, `tRUE`), - - createTestCaseForEnum(`false`, `'false'`), - createTestCaseForEnum(`False`, `'False'`), - createTestCaseForEnum(`FALSE`, `'FALSE'`), - createTestCaseForEnum(`fALSE`, `fALSE`), - - createTestCaseForEnum(`on`, `'on'`), - createTestCaseForEnum(`On`, `'On'`), - createTestCaseForEnum(`ON`, `'ON'`), - createTestCaseForEnum(`oN`, `oN`), - - createTestCaseForEnum(`off`, `'off'`), - createTestCaseForEnum(`Off`, `'Off'`), - createTestCaseForEnum(`OFF`, `'OFF'`), - createTestCaseForEnum(`oFF`, `oFF`), -]; - - -describe("Scalar formats of serialized document in YAML", () => { - it.each([...testCasesForEnum, ...testCasesForStringLiteral])("$description", async (c: TestCase) => { - const [_, __, content] = await emitOpenApiWithDiagnostics(c.content); - expect(content).toBe(c.expected); + - 'y' + - 'Y' + - 'yes' + - 'Yes' + - 'YES' + - yEs + - 'n' + - 'N' + - 'no' + - 'No' + - 'NO' + - nO + - 'true' + - 'True' + - 'TRUE' + - tRUE + - 'false' + - 'False' + - 'FALSE' + - fALSE + - 'on' + - 'On' + - 'ON' + - oN + - 'off' + - 'Off' + - 'OFF' + - oFF +`); }); }); From 55c41a342d4542b4210831a10c668260391c0172 Mon Sep 17 00:00:00 2001 From: Wanpeng Li Date: Fri, 27 Dec 2024 14:32:11 +0800 Subject: [PATCH 3/4] . --- .../changes/wanl-fix-noway-yaml-2024-11-27-14-31-43.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .chronus/changes/wanl-fix-noway-yaml-2024-11-27-14-31-43.md diff --git a/.chronus/changes/wanl-fix-noway-yaml-2024-11-27-14-31-43.md b/.chronus/changes/wanl-fix-noway-yaml-2024-11-27-14-31-43.md new file mode 100644 index 0000000000..ca86458fda --- /dev/null +++ b/.chronus/changes/wanl-fix-noway-yaml-2024-11-27-14-31-43.md @@ -0,0 +1,7 @@ +--- +changeKind: fix +packages: + - "@typespec/openapi3" +--- + +Fix Problem of YAML 1.1 Treats Some Strings as Other Types of Values From 21513f2811ae888451a004e3a4d04e93e487198e Mon Sep 17 00:00:00 2001 From: Wanpeng Li Date: Fri, 27 Dec 2024 14:34:35 +0800 Subject: [PATCH 4/4] . --- .chronus/changes/wanl-fix-noway-yaml-2024-11-27-14-31-43.md | 2 +- packages/openapi3/test/emit-openapi.test.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.chronus/changes/wanl-fix-noway-yaml-2024-11-27-14-31-43.md b/.chronus/changes/wanl-fix-noway-yaml-2024-11-27-14-31-43.md index ca86458fda..b71ee9dcbe 100644 --- a/.chronus/changes/wanl-fix-noway-yaml-2024-11-27-14-31-43.md +++ b/.chronus/changes/wanl-fix-noway-yaml-2024-11-27-14-31-43.md @@ -4,4 +4,4 @@ packages: - "@typespec/openapi3" --- -Fix Problem of YAML 1.1 Treats Some Strings as Other Types of Values +Fix: OpenAPI YAML converts strings to boolean diff --git a/packages/openapi3/test/emit-openapi.test.ts b/packages/openapi3/test/emit-openapi.test.ts index cbc58695c7..6567704d7f 100644 --- a/packages/openapi3/test/emit-openapi.test.ts +++ b/packages/openapi3/test/emit-openapi.test.ts @@ -2,7 +2,7 @@ import { describe, expect, it } from "vitest"; import { emitOpenApiWithDiagnostics } from "./test-host.js"; describe("Scalar formats of serialized document in YAML", () => { - it("should add single quote for y|Y|yes|Yes|YES|n|N|no|No|NO|true|True|TRUE|false|False|FALSE|on|On|ON|off|Off|OFF", async (c: TestCase) => { + it("should add single quote for y|Y|yes|Yes|YES|n|N|no|No|NO|true|True|TRUE|false|False|FALSE|on|On|ON|off|Off|OFF", async () => { const [_, __, content] = await emitOpenApiWithDiagnostics(` enum TestEnum { y: "y",