From 90f0998419b4cf45456a58c2469226568dd2e66b Mon Sep 17 00:00:00 2001 From: Alitzel Mendez Bustillo Date: Thu, 12 Sep 2024 11:22:03 -0700 Subject: [PATCH 1/9] Default scalar value --- packages/openapi3/src/schema-emitter.ts | 2 ++ packages/openapi3/test/primitive-types.test.ts | 15 ++++++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/packages/openapi3/src/schema-emitter.ts b/packages/openapi3/src/schema-emitter.ts index 36a2993220..73ccb97b80 100644 --- a/packages/openapi3/src/schema-emitter.ts +++ b/packages/openapi3/src/schema-emitter.ts @@ -958,6 +958,8 @@ export function getDefaultValue(program: Program, defaultType: Value): any { return null; case "EnumValue": return defaultType.value.value ?? defaultType.value.name; + case "ScalarValue": + return defaultType.value.args.map((x) => getDefaultValue(program, x)); default: reportDiagnostic(program, { code: "invalid-default", diff --git a/packages/openapi3/test/primitive-types.test.ts b/packages/openapi3/test/primitive-types.test.ts index 9ee9f8e4af..2e05757ae5 100644 --- a/packages/openapi3/test/primitive-types.test.ts +++ b/packages/openapi3/test/primitive-types.test.ts @@ -1,5 +1,5 @@ import { deepStrictEqual, ok, strictEqual } from "assert"; -import { describe, it } from "vitest"; +import { describe, expect, it } from "vitest"; import { OpenAPI3Schema } from "../src/types.js"; import { oapiForModel, openApiFor } from "./test-host.js"; @@ -115,6 +115,19 @@ describe("openapi3: primitives", () => { }); }); + it("scalar used as a default value", async () => { + const res = await oapiForModel( + "Pet", + ` + scalar shortName { init name(value: string);} + + model Pet { name: shortName = shortName.name("Shorty"); } + ` + ); + + expect(res.schemas.Pet.properties.name.default).toEqual(["Shorty"]); + }); + it("merge the data from parent", async () => { const res = await oapiForModel( "Pet", From 606bb92c477c807f23231ab672ba54858699c192 Mon Sep 17 00:00:00 2001 From: Alitzel Mendez Bustillo Date: Thu, 12 Sep 2024 15:33:46 -0700 Subject: [PATCH 2/9] Add default type management for scalar value and object value --- packages/openapi3/src/schema-emitter.ts | 6 ++++-- packages/openapi3/test/primitive-types.test.ts | 15 +++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/packages/openapi3/src/schema-emitter.ts b/packages/openapi3/src/schema-emitter.ts index 73ccb97b80..4956e026e9 100644 --- a/packages/openapi3/src/schema-emitter.ts +++ b/packages/openapi3/src/schema-emitter.ts @@ -959,11 +959,13 @@ export function getDefaultValue(program: Program, defaultType: Value): any { case "EnumValue": return defaultType.value.value ?? defaultType.value.name; case "ScalarValue": - return defaultType.value.args.map((x) => getDefaultValue(program, x)); + return serializeValueAsJson(program, defaultType, defaultType.type); + case "ObjectValue": + return serializeValueAsJson(program, defaultType, defaultType.type); default: reportDiagnostic(program, { code: "invalid-default", - format: { type: defaultType.valueKind }, + format: { type: defaultType }, target: defaultType, }); } diff --git a/packages/openapi3/test/primitive-types.test.ts b/packages/openapi3/test/primitive-types.test.ts index 2e05757ae5..3a3f989141 100644 --- a/packages/openapi3/test/primitive-types.test.ts +++ b/packages/openapi3/test/primitive-types.test.ts @@ -128,6 +128,21 @@ describe("openapi3: primitives", () => { expect(res.schemas.Pet.properties.name.default).toEqual(["Shorty"]); }); + it("object value used as a default value", async () => { + const res = await oapiForModel( + "Test", + ` +model Test { + Pet: { + name: string; + } = #{ name: "Dog"}; +} + ` + ); + + expect(res.schemas.Test.properties.Pet.default.name).toEqual("Dog"); + }); + it("merge the data from parent", async () => { const res = await oapiForModel( "Pet", From 19dd4782c0b35a0780152804a41be8fa0236068d Mon Sep 17 00:00:00 2001 From: Alitzel Mendez Bustillo Date: Thu, 12 Sep 2024 15:34:54 -0700 Subject: [PATCH 3/9] Summary change --- .../almend-ScalarDefaultValue-2024-8-12-15-34-32.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .chronus/changes/almend-ScalarDefaultValue-2024-8-12-15-34-32.md diff --git a/.chronus/changes/almend-ScalarDefaultValue-2024-8-12-15-34-32.md b/.chronus/changes/almend-ScalarDefaultValue-2024-8-12-15-34-32.md new file mode 100644 index 0000000000..234ab1f96b --- /dev/null +++ b/.chronus/changes/almend-ScalarDefaultValue-2024-8-12-15-34-32.md @@ -0,0 +1,7 @@ +--- +changeKind: feature +packages: + - "@typespec/openapi3" +--- + +Added support to use Scalar and Object as default types \ No newline at end of file From 96e09573fc4d0efcc705276fbfa640da53649c25 Mon Sep 17 00:00:00 2001 From: Alitzel Mendez Bustillo Date: Thu, 12 Sep 2024 16:31:51 -0700 Subject: [PATCH 4/9] Fix test and small details --- packages/openapi3/test/primitive-types.test.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/packages/openapi3/test/primitive-types.test.ts b/packages/openapi3/test/primitive-types.test.ts index 3a3f989141..ffbb61c97f 100644 --- a/packages/openapi3/test/primitive-types.test.ts +++ b/packages/openapi3/test/primitive-types.test.ts @@ -125,18 +125,14 @@ describe("openapi3: primitives", () => { ` ); - expect(res.schemas.Pet.properties.name.default).toEqual(["Shorty"]); + expect(res.schemas.Pet.properties.name.default).toEqual("Shorty"); }); it("object value used as a default value", async () => { const res = await oapiForModel( "Test", ` -model Test { - Pet: { - name: string; - } = #{ name: "Dog"}; -} + model Test { Pet: {name: string;} = #{ name: "Dog"}; } ` ); From 37e100c95e0f37db503c3ef6de8d150fa9198091 Mon Sep 17 00:00:00 2001 From: Alitzel Mendez Bustillo Date: Fri, 13 Sep 2024 11:24:11 -0700 Subject: [PATCH 5/9] Add test for known scalar --- packages/openapi3/test/primitive-types.test.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/packages/openapi3/test/primitive-types.test.ts b/packages/openapi3/test/primitive-types.test.ts index ffbb61c97f..11eef1b51d 100644 --- a/packages/openapi3/test/primitive-types.test.ts +++ b/packages/openapi3/test/primitive-types.test.ts @@ -128,6 +128,19 @@ describe("openapi3: primitives", () => { expect(res.schemas.Pet.properties.name.default).toEqual("Shorty"); }); + it("known scalar used as a default value", async () => { + const res = await oapiForModel( + "Test", + ` + model Test { minDate: utcDateTime = utcDateTime.fromISO("2024-01-2T3:04:05Z"); } + ` + ); + + expect(res.schemas.Test.properties.minDate.default).toEqual("2024-01-2T3:04:05Z"); + expect(res.schemas.Test.properties.minDate.format).toEqual("date-time"); + expect(res.schemas.Test.properties.minDate.type).toEqual("string"); + }); + it("object value used as a default value", async () => { const res = await oapiForModel( "Test", From efd04dfb2d076dfe3633724a261ef6c16118aeeb Mon Sep 17 00:00:00 2001 From: Alitzel Mendez Bustillo Date: Fri, 13 Sep 2024 12:24:41 -0700 Subject: [PATCH 6/9] Feedback: Fix test --- packages/openapi3/test/primitive-types.test.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/openapi3/test/primitive-types.test.ts b/packages/openapi3/test/primitive-types.test.ts index 11eef1b51d..7c1ad566b6 100644 --- a/packages/openapi3/test/primitive-types.test.ts +++ b/packages/openapi3/test/primitive-types.test.ts @@ -137,8 +137,6 @@ describe("openapi3: primitives", () => { ); expect(res.schemas.Test.properties.minDate.default).toEqual("2024-01-2T3:04:05Z"); - expect(res.schemas.Test.properties.minDate.format).toEqual("date-time"); - expect(res.schemas.Test.properties.minDate.type).toEqual("string"); }); it("object value used as a default value", async () => { From fe24cb7d2d9a0966176e806bda535022ffb96759 Mon Sep 17 00:00:00 2001 From: Alitzel Mendez Bustillo Date: Fri, 13 Sep 2024 22:06:24 -0700 Subject: [PATCH 7/9] Fedback: Add test and add enconding for scalar value --- packages/openapi3/src/lib.ts | 6 --- packages/openapi3/src/schema-emitter.ts | 22 +++++++---- packages/openapi3/test/models.test.ts | 35 ++++++++++++++++++ .../openapi3/test/primitive-types.test.ts | 37 +------------------ 4 files changed, 50 insertions(+), 50 deletions(-) diff --git a/packages/openapi3/src/lib.ts b/packages/openapi3/src/lib.ts index 5a0995f68f..ffcb14991c 100644 --- a/packages/openapi3/src/lib.ts +++ b/packages/openapi3/src/lib.ts @@ -239,12 +239,6 @@ export const libDef = { default: "Enums are not supported unless all options are literals of the same type.", }, }, - "invalid-default": { - severity: "error", - messages: { - default: paramMessage`Invalid type '${"type"}' for a default value`, - }, - }, "inline-cycle": { severity: "error", messages: { diff --git a/packages/openapi3/src/schema-emitter.ts b/packages/openapi3/src/schema-emitter.ts index 9705eca31c..08777f74b4 100644 --- a/packages/openapi3/src/schema-emitter.ts +++ b/packages/openapi3/src/schema-emitter.ts @@ -377,7 +377,7 @@ export class OpenAPI3SchemaEmitter extends TypeEmitter< // Apply decorators on the property to the type's schema const additionalProps: Partial = this.#applyConstraints(prop, {}); if (prop.defaultValue) { - additionalProps.default = getDefaultValue(program, prop.defaultValue); + additionalProps.default = getDefaultValue(program, prop.defaultValue, prop); } if (isReadonlyProperty(program, prop)) { @@ -922,7 +922,11 @@ const B = { }, } as const; -export function getDefaultValue(program: Program, defaultType: Value): any { +export function getDefaultValue( + program: Program, + defaultType: Value, + modelProperty?: ModelProperty +): any { switch (defaultType.valueKind) { case "StringValue": return defaultType.value; @@ -937,15 +941,17 @@ export function getDefaultValue(program: Program, defaultType: Value): any { case "EnumValue": return defaultType.value.value ?? defaultType.value.name; case "ScalarValue": + if (modelProperty) { + return serializeValueAsJson( + program, + defaultType, + defaultType.type, + getEncode(program, modelProperty) + ); + } return serializeValueAsJson(program, defaultType, defaultType.type); case "ObjectValue": return serializeValueAsJson(program, defaultType, defaultType.type); - default: - reportDiagnostic(program, { - code: "invalid-default", - format: { type: defaultType }, - target: defaultType, - }); } } diff --git a/packages/openapi3/test/models.test.ts b/packages/openapi3/test/models.test.ts index eb6e02fd53..2942c42554 100644 --- a/packages/openapi3/test/models.test.ts +++ b/packages/openapi3/test/models.test.ts @@ -259,6 +259,41 @@ describe("openapi3: models", () => { }); }); + it("scalar used as a default value", async () => { + const res = await oapiForModel( + "Pet", + ` + scalar shortName { init name(value: string);} + + model Pet { name: shortName = shortName.name("Shorty"); } + ` + ); + + expect(res.schemas.Pet.properties.name.default).toEqual("Shorty"); + }); + + it("encode know scalar as a default value", async () => { + const res = await oapiForModel( + "Test", + ` + model Test { @encode("rfc7231") minDate: utcDateTime = utcDateTime.fromISO("2024-01-01T11:32:00Z"); } + ` + ); + + expect(res.schemas.Test.properties.minDate.default).toEqual("Mon, 01 Jan 2024 11:32:00 GMT"); + }); + + it("object value used as a default value", async () => { + const res = await oapiForModel( + "Test", + ` + model Test { Pet: {name: string;} = #{ name: "Dog"}; } + ` + ); + + expect(res.schemas.Test.properties.Pet.default.name).toEqual("Dog"); + }); + describe("numeric defaults", () => { it.each([ ["0.01", 0.01], diff --git a/packages/openapi3/test/primitive-types.test.ts b/packages/openapi3/test/primitive-types.test.ts index 7c1ad566b6..9ee9f8e4af 100644 --- a/packages/openapi3/test/primitive-types.test.ts +++ b/packages/openapi3/test/primitive-types.test.ts @@ -1,5 +1,5 @@ import { deepStrictEqual, ok, strictEqual } from "assert"; -import { describe, expect, it } from "vitest"; +import { describe, it } from "vitest"; import { OpenAPI3Schema } from "../src/types.js"; import { oapiForModel, openApiFor } from "./test-host.js"; @@ -115,41 +115,6 @@ describe("openapi3: primitives", () => { }); }); - it("scalar used as a default value", async () => { - const res = await oapiForModel( - "Pet", - ` - scalar shortName { init name(value: string);} - - model Pet { name: shortName = shortName.name("Shorty"); } - ` - ); - - expect(res.schemas.Pet.properties.name.default).toEqual("Shorty"); - }); - - it("known scalar used as a default value", async () => { - const res = await oapiForModel( - "Test", - ` - model Test { minDate: utcDateTime = utcDateTime.fromISO("2024-01-2T3:04:05Z"); } - ` - ); - - expect(res.schemas.Test.properties.minDate.default).toEqual("2024-01-2T3:04:05Z"); - }); - - it("object value used as a default value", async () => { - const res = await oapiForModel( - "Test", - ` - model Test { Pet: {name: string;} = #{ name: "Dog"}; } - ` - ); - - expect(res.schemas.Test.properties.Pet.default.name).toEqual("Dog"); - }); - it("merge the data from parent", async () => { const res = await oapiForModel( "Pet", From 9053872d718ce1b27e0c2a447d91983eb1529f7c Mon Sep 17 00:00:00 2001 From: Alitzel Mendez Bustillo Date: Mon, 16 Sep 2024 14:55:35 -0700 Subject: [PATCH 8/9] Simplify use of mmodel property --- packages/openapi3/src/schema-emitter.ts | 9 ++------- packages/openapi3/test/models.test.ts | 6 +++--- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/packages/openapi3/src/schema-emitter.ts b/packages/openapi3/src/schema-emitter.ts index 3f30d39f7f..e2a2be8631 100644 --- a/packages/openapi3/src/schema-emitter.ts +++ b/packages/openapi3/src/schema-emitter.ts @@ -925,7 +925,7 @@ const B = { export function getDefaultValue( program: Program, defaultType: Value, - modelProperty?: ModelProperty + modelProperty?: ModelProperty, ): any { switch (defaultType.valueKind) { case "StringValue": @@ -942,12 +942,7 @@ export function getDefaultValue( return defaultType.value.value ?? defaultType.value.name; case "ScalarValue": if (modelProperty) { - return serializeValueAsJson( - program, - defaultType, - defaultType.type, - getEncode(program, modelProperty) - ); + return serializeValueAsJson(program, defaultType, modelProperty); } return serializeValueAsJson(program, defaultType, defaultType.type); case "ObjectValue": diff --git a/packages/openapi3/test/models.test.ts b/packages/openapi3/test/models.test.ts index 0b4d52f939..70c198105f 100644 --- a/packages/openapi3/test/models.test.ts +++ b/packages/openapi3/test/models.test.ts @@ -266,7 +266,7 @@ describe("openapi3: models", () => { scalar shortName { init name(value: string);} model Pet { name: shortName = shortName.name("Shorty"); } - ` + `, ); expect(res.schemas.Pet.properties.name.default).toEqual("Shorty"); @@ -277,7 +277,7 @@ describe("openapi3: models", () => { "Test", ` model Test { @encode("rfc7231") minDate: utcDateTime = utcDateTime.fromISO("2024-01-01T11:32:00Z"); } - ` + `, ); expect(res.schemas.Test.properties.minDate.default).toEqual("Mon, 01 Jan 2024 11:32:00 GMT"); @@ -288,7 +288,7 @@ describe("openapi3: models", () => { "Test", ` model Test { Pet: {name: string;} = #{ name: "Dog"}; } - ` + `, ); expect(res.schemas.Test.properties.Pet.default.name).toEqual("Dog"); From c1794b97980269430a5d3b05e5835c5f29a5e585 Mon Sep 17 00:00:00 2001 From: Alitzel Mendez Bustillo Date: Tue, 17 Sep 2024 10:02:37 -0700 Subject: [PATCH 9/9] Simplify code --- packages/openapi3/src/openapi.ts | 4 ++-- packages/openapi3/src/schema-emitter.ts | 24 ++---------------------- 2 files changed, 4 insertions(+), 24 deletions(-) diff --git a/packages/openapi3/src/openapi.ts b/packages/openapi3/src/openapi.ts index 24a3f74296..9f714a0e3a 100644 --- a/packages/openapi3/src/openapi.ts +++ b/packages/openapi3/src/openapi.ts @@ -395,7 +395,7 @@ function createOAPIEmitter( } const variable: OpenAPI3ServerVariable = { - default: prop.defaultValue ? getDefaultValue(program, prop.defaultValue) : "", + default: prop.defaultValue ? getDefaultValue(program, prop.defaultValue, prop) : "", description: getDoc(program, prop), }; @@ -1371,7 +1371,7 @@ function createOAPIEmitter( options, ); if (param.defaultValue) { - schema.default = getDefaultValue(program, param.defaultValue); + schema.default = getDefaultValue(program, param.defaultValue, param); } // Description is already provided in the parameter itself. delete schema.description; diff --git a/packages/openapi3/src/schema-emitter.ts b/packages/openapi3/src/schema-emitter.ts index e2a2be8631..1bbac8029e 100644 --- a/packages/openapi3/src/schema-emitter.ts +++ b/packages/openapi3/src/schema-emitter.ts @@ -925,29 +925,9 @@ const B = { export function getDefaultValue( program: Program, defaultType: Value, - modelProperty?: ModelProperty, + modelProperty: ModelProperty, ): any { - switch (defaultType.valueKind) { - case "StringValue": - return defaultType.value; - case "NumericValue": - return defaultType.value.asNumber() ?? undefined; - case "BooleanValue": - return defaultType.value; - case "ArrayValue": - return defaultType.values.map((x) => getDefaultValue(program, x)); - case "NullValue": - return null; - case "EnumValue": - return defaultType.value.value ?? defaultType.value.name; - case "ScalarValue": - if (modelProperty) { - return serializeValueAsJson(program, defaultType, modelProperty); - } - return serializeValueAsJson(program, defaultType, defaultType.type); - case "ObjectValue": - return serializeValueAsJson(program, defaultType, defaultType.type); - } + return serializeValueAsJson(program, defaultType, modelProperty); } export function isBytesKeptRaw(program: Program, type: Type) {