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:
Travis Prescott 2023-11-30 10:16:50 -08:00 коммит произвёл GitHub
Родитель 9e167ad451
Коммит 52d0a8b5a7
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
5 изменённых файлов: 139 добавлений и 3 удалений

Просмотреть файл

@ -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: