Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OpenAPI3: emit all properties for unreferenced schemas #2620

Merged
merged 6 commits into from
Nov 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@typespec/openapi3",
"comment": "Emitter will now emit all properties on unreferenced schemas.",
"type": "none"
}
],
"packageName": "@typespec/openapi3"
}
2 changes: 1 addition & 1 deletion packages/openapi3/src/openapi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
3 changes: 2 additions & 1 deletion packages/openapi3/src/visibility-usage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
);
}
Expand Down
88 changes: 88 additions & 0 deletions packages/openapi3/test/metadata.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,94 @@ import { deepStrictEqual } from "assert";
import { openApiFor } from "./test-host.js";

describe("openapi3: metadata", () => {
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;
@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("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",
},
},
},
SharedReadOrCreateOrUpdateOrDeleteOrQuery: {
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/SharedReadOrCreateOrUpdateOrDeleteOrQuery" },
},
required: ["r", "c", "shared"],
},
});
});

it("will expose create visibility properties on PATCH model using @requestVisibility", async () => {
const res = await openApiFor(`
model M {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ components:
id:
type: string
readOnly: true
secret:
type: string
tjprescott marked this conversation as resolved.
Show resolved Hide resolved
name:
type: string
test:
Expand All @@ -112,7 +114,7 @@ components:
relatives:
type: array
items:
$ref: '#/components/schemas/PersonRelative'
$ref: '#/components/schemas/PersonRelativeReadOrCreateOrUpdateOrDeleteOrQueryItem'
tjprescott marked this conversation as resolved.
Show resolved Hide resolved
Person:
type: object
required:
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand Down