From 06e9ea73373b50ff7203636dc3b14134fd10ad1d Mon Sep 17 00:00:00 2001 From: Travis Prescott <trpresco@microsoft.com> Date: Wed, 1 Nov 2023 11:17:38 -0500 Subject: [PATCH 1/6] =?UTF-8?q?=EF=BB=BFFix=20#2571.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...erencedVisibilityFix_2023-11-01-16-22.json | 10 ++++++++ packages/openapi3/test/metadata.test.ts | 24 +++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 common/changes/@typespec/openapi3/unreferencedVisibilityFix_2023-11-01-16-22.json diff --git a/common/changes/@typespec/openapi3/unreferencedVisibilityFix_2023-11-01-16-22.json b/common/changes/@typespec/openapi3/unreferencedVisibilityFix_2023-11-01-16-22.json new file mode 100644 index 0000000000..209fbbe494 --- /dev/null +++ b/common/changes/@typespec/openapi3/unreferencedVisibilityFix_2023-11-01-16-22.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@typespec/openapi3", + "comment": "Emitter will now emit all properties on unreferenced schemas.", + "type": "none" + } + ], + "packageName": "@typespec/openapi3" +} \ No newline at end of file diff --git a/packages/openapi3/test/metadata.test.ts b/packages/openapi3/test/metadata.test.ts index 12f762f69d..87bd863d37 100644 --- a/packages/openapi3/test/metadata.test.ts +++ b/packages/openapi3/test/metadata.test.ts @@ -2,6 +2,30 @@ import { deepStrictEqual } from "assert"; import { openApiFor } from "./test-host.js"; describe("openapi3: metadata", () => { + it("will expose all properties on unreferenced models", async () => { + const res = await openApiFor(` + model M { + @visibility("read") r: string; + @visibility("create", "update") uc?: string; + @visibility("read", "create") rc?: string; + @visibility("read", "update", "create") ruc?: string; + } + `); + + deepStrictEqual(res.components.schemas, { + M: { + type: "object", + properties: { + r: { type: "string", readOnly: true }, + uc: { type: "string" }, + rc: { type: "string" }, + ruc: { type: "string" }, + }, + required: ["r"], + }, + }); + }); + it("will expose create visibility properties on PATCH model using @requestVisibility", async () => { const res = await openApiFor(` model M { From a114e7edfcbc5f719c494b48e399c798e8838aab Mon Sep 17 00:00:00 2001 From: Travis Prescott <trpresco@microsoft.com> Date: Wed, 1 Nov 2023 12:12:15 -0500 Subject: [PATCH 2/6] Regen sample --- .../@typespec/openapi3/openapi.yaml | 39 ++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/packages/samples/test/output/visibility/@typespec/openapi3/openapi.yaml b/packages/samples/test/output/visibility/@typespec/openapi3/openapi.yaml index 464f8a85ca..affea0069b 100644 --- a/packages/samples/test/output/visibility/@typespec/openapi3/openapi.yaml +++ b/packages/samples/test/output/visibility/@typespec/openapi3/openapi.yaml @@ -103,6 +103,8 @@ components: id: type: string readOnly: true + secret: + type: string name: type: string test: @@ -112,7 +114,7 @@ components: relatives: type: array items: - $ref: '#/components/schemas/PersonRelative' + $ref: '#/components/schemas/PersonRelativeReadOrCreateOrUpdateOrDeleteOrQueryItem' Person: type: object required: @@ -174,6 +176,31 @@ components: type: array items: $ref: '#/components/schemas/PersonRelativeCreateOrUpdateItem' + PersonReadOrCreateOrUpdateOrDeleteOrQueryItem: + type: object + required: + - id + - secret + - name + - test + - other + - relatives + properties: + id: + type: string + readOnly: true + secret: + type: string + name: + type: string + test: + type: string + other: + type: string + relatives: + type: array + items: + $ref: '#/components/schemas/PersonRelativeReadOrCreateOrUpdateOrDeleteOrQueryItem' PersonRelative: type: object required: @@ -204,6 +231,16 @@ components: $ref: '#/components/schemas/PersonCreateOrUpdateItem' relationship: type: string + PersonRelativeReadOrCreateOrUpdateOrDeleteOrQueryItem: + type: object + required: + - person + - relationship + properties: + person: + $ref: '#/components/schemas/PersonReadOrCreateOrUpdateOrDeleteOrQueryItem' + relationship: + type: string PersonRelativeUpdateItem: type: object required: From 4173f8b6089a52c7a354c3c0e375872d75b73e28 Mon Sep 17 00:00:00 2001 From: Travis Prescott <trpresco@microsoft.com> Date: Wed, 1 Nov 2023 12:47:33 -0500 Subject: [PATCH 3/6] =?UTF-8?q?=EF=BB=BFUpdate=20naming=20logic.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/openapi3/src/openapi.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/openapi3/src/openapi.ts b/packages/openapi3/src/openapi.ts index d0f68966a9..c0fcc7091c 100644 --- a/packages/openapi3/src/openapi.ts +++ b/packages/openapi3/src/openapi.ts @@ -1295,7 +1295,7 @@ function createOAPIEmitter( !paramModels.has(type) && !shouldInline(program, type) ) { - callSchemaEmitter(type, Visibility.Read); + callSchemaEmitter(type, Visibility.All); } }; const skipSubNamespaces = isGlobalNamespace(program, serviceNamespace); From e38aa3cc2748404ae77f0c6a5c25be7b15ced020 Mon Sep 17 00:00:00 2001 From: Travis Prescott <trpresco@microsoft.com> Date: Thu, 16 Nov 2023 11:04:29 -0800 Subject: [PATCH 4/6] =?UTF-8?q?=EF=BB=BFRevert=20changes.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/samples/specs/visibility/visibility.tsp | 4 ++++ .../visibility/@typespec/openapi3/openapi.yaml | 13 +++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/packages/samples/specs/visibility/visibility.tsp b/packages/samples/specs/visibility/visibility.tsp index dce916cbd6..0a26d8e5cd 100644 --- a/packages/samples/specs/visibility/visibility.tsp +++ b/packages/samples/specs/visibility/visibility.tsp @@ -72,6 +72,10 @@ namespace Hello { @post op create(@body person: WritablePerson): ReadablePerson; @put op update(@body person: WriteableOptionalPerson): ReadablePerson; + @route("/hello/optional") + @get + op getOptional(): OptionalPerson; + @TypeSpec.Rest.listsResource(Person) op list(): ListResult<Person>; diff --git a/packages/samples/test/output/visibility/@typespec/openapi3/openapi.yaml b/packages/samples/test/output/visibility/@typespec/openapi3/openapi.yaml index affea0069b..7d8b247c96 100644 --- a/packages/samples/test/output/visibility/@typespec/openapi3/openapi.yaml +++ b/packages/samples/test/output/visibility/@typespec/openapi3/openapi.yaml @@ -70,6 +70,17 @@ paths: application/json: schema: $ref: '#/components/schemas/PersonUpdate' + /hello/hello/optional: + get: + operationId: Hello_getOptional + parameters: [] + responses: + '200': + description: The request has succeeded. + content: + application/json: + schema: + $ref: '#/components/schemas/OptionalPerson' /hello/{id}: get: operationId: Hello_read @@ -103,8 +114,6 @@ components: id: type: string readOnly: true - secret: - type: string name: type: string test: From 69ebe03968255eed2422680e0a5c2875abaa3d3f Mon Sep 17 00:00:00 2001 From: Travis Prescott <trpresco@microsoft.com> Date: Tue, 28 Nov 2023 14:09:59 -0800 Subject: [PATCH 5/6] =?UTF-8?q?=EF=BB=BFAdd=20tests.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/openapi3/test/metadata.test.ts | 66 ++++++++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/packages/openapi3/test/metadata.test.ts b/packages/openapi3/test/metadata.test.ts index 87bd863d37..c9e2a2ed25 100644 --- a/packages/openapi3/test/metadata.test.ts +++ b/packages/openapi3/test/metadata.test.ts @@ -2,7 +2,7 @@ import { deepStrictEqual } from "assert"; import { openApiFor } from "./test-host.js"; describe("openapi3: metadata", () => { - it("will expose all properties on unreferenced models", async () => { + it("will expose all properties on unreferenced models but filter properties on referenced models", async () => { const res = await openApiFor(` model M { @visibility("read") r: string; @@ -26,6 +26,70 @@ describe("openapi3: metadata", () => { }); }); + it("prioritizes read visibility when referenced and unreferenced models share schemas", async () => { + const res = await openApiFor(` + model Shared { + @visibility("create", "update") password: string; + prop: string; + } + + model Unreferenced { + @visibility("read") r: string; + @visibility("create") c: string; + shared: Shared; + } + + model Referenced { + @visibility("read") r: string; + @visibility("create") c: string; + shared: Shared; + } + + @get op get(): Referenced; + `); + + deepStrictEqual(res.components.schemas, { + Referenced: { + type: "object", + properties: { + r: { type: "string", readOnly: true }, + shared: { $ref: "#/components/schemas/Shared" }, + }, + required: ["r", "shared"], + }, + Shared: { + type: "object", + required: ["prop"], + properties: { + prop: { + type: "string", + }, + }, + }, + SharedReadOrCreateOrUpdateOrDelete: { + type: "object", + required: ["password", "prop"], + properties: { + password: { + type: "string", + }, + prop: { + type: "string", + }, + }, + }, + Unreferenced: { + type: "object", + properties: { + c: { type: "string" }, + r: { type: "string", readOnly: true }, + shared: { $ref: "#/components/schemas/SharedReadOrCreateOrUpdateOrDelete" }, + }, + required: ["r", "c", "shared"], + }, + }); + }); + it("will expose create visibility properties on PATCH model using @requestVisibility", async () => { const res = await openApiFor(` model M { From f4440b568b7644ac12d4d8bd508f1687c63840e7 Mon Sep 17 00:00:00 2001 From: Travis Prescott <trpresco@microsoft.com> Date: Wed, 29 Nov 2023 13:54:49 -0800 Subject: [PATCH 6/6] CI fixes. --- packages/openapi3/src/visibility-usage.ts | 3 ++- packages/openapi3/test/metadata.test.ts | 4 ++-- packages/samples/specs/visibility/visibility.tsp | 4 ---- .../visibility/@typespec/openapi3/openapi.yaml | 13 ++----------- 4 files changed, 6 insertions(+), 18 deletions(-) diff --git a/packages/openapi3/src/visibility-usage.ts b/packages/openapi3/src/visibility-usage.ts index a7f069033f..6aa62b4e7d 100644 --- a/packages/openapi3/src/visibility-usage.ts +++ b/packages/openapi3/src/visibility-usage.ts @@ -34,9 +34,10 @@ export function resolveVisibilityUsage( const reachableTypes = new Set<Type>(usages.keys()); if (!omitUnreachableTypes) { + // Evaluate all unreferenced types and the types they reference with Visibility.All const trackType = (type: Type) => { if (!usages.has(type)) { - navigateReferencedTypes(type, Visibility.Read, (type, vis) => + navigateReferencedTypes(type, Visibility.All, (type, vis) => trackUsageExact(usages, type, vis) ); } diff --git a/packages/openapi3/test/metadata.test.ts b/packages/openapi3/test/metadata.test.ts index c9e2a2ed25..cadf76625a 100644 --- a/packages/openapi3/test/metadata.test.ts +++ b/packages/openapi3/test/metadata.test.ts @@ -66,7 +66,7 @@ describe("openapi3: metadata", () => { }, }, }, - SharedReadOrCreateOrUpdateOrDelete: { + SharedReadOrCreateOrUpdateOrDeleteOrQuery: { type: "object", required: ["password", "prop"], properties: { @@ -83,7 +83,7 @@ describe("openapi3: metadata", () => { properties: { c: { type: "string" }, r: { type: "string", readOnly: true }, - shared: { $ref: "#/components/schemas/SharedReadOrCreateOrUpdateOrDelete" }, + shared: { $ref: "#/components/schemas/SharedReadOrCreateOrUpdateOrDeleteOrQuery" }, }, required: ["r", "c", "shared"], }, diff --git a/packages/samples/specs/visibility/visibility.tsp b/packages/samples/specs/visibility/visibility.tsp index 0a26d8e5cd..dce916cbd6 100644 --- a/packages/samples/specs/visibility/visibility.tsp +++ b/packages/samples/specs/visibility/visibility.tsp @@ -72,10 +72,6 @@ namespace Hello { @post op create(@body person: WritablePerson): ReadablePerson; @put op update(@body person: WriteableOptionalPerson): ReadablePerson; - @route("/hello/optional") - @get - op getOptional(): OptionalPerson; - @TypeSpec.Rest.listsResource(Person) op list(): ListResult<Person>; diff --git a/packages/samples/test/output/visibility/@typespec/openapi3/openapi.yaml b/packages/samples/test/output/visibility/@typespec/openapi3/openapi.yaml index 7d8b247c96..affea0069b 100644 --- a/packages/samples/test/output/visibility/@typespec/openapi3/openapi.yaml +++ b/packages/samples/test/output/visibility/@typespec/openapi3/openapi.yaml @@ -70,17 +70,6 @@ paths: application/json: schema: $ref: '#/components/schemas/PersonUpdate' - /hello/hello/optional: - get: - operationId: Hello_getOptional - parameters: [] - responses: - '200': - description: The request has succeeded. - content: - application/json: - schema: - $ref: '#/components/schemas/OptionalPerson' /hello/{id}: get: operationId: Hello_read @@ -114,6 +103,8 @@ components: id: type: string readOnly: true + secret: + type: string name: type: string test: