OpenAPI3: emit all properties for unreferenced schemas (#2620)
Fix #2571. **BREAKING CHANGE**: Since the previous behavior was to emit unreferenced schemas with Read visibility, this change can produce a breaking change in Swagger if unreferenced schemas were previously emitted that had write visibility properties stripped.
This commit is contained in:
Родитель
9e167ad451
Коммит
52d0a8b5a7
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"changes": [
|
||||
{
|
||||
"packageName": "@typespec/openapi3",
|
||||
"comment": "Emitter will now emit all properties on unreferenced schemas.",
|
||||
"type": "none"
|
||||
}
|
||||
],
|
||||
"packageName": "@typespec/openapi3"
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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:
|
||||
|
|
Загрузка…
Ссылка в новой задаче