Fix array docs not showing up in openapi emitter (#2004)

This commit is contained in:
Timothee Guerin 2023-06-02 12:08:30 -07:00 коммит произвёл GitHub
Родитель afd531aa2b
Коммит d53a6f2b2a
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
10 изменённых файлов: 64 добавлений и 19 удалений

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

@ -7,7 +7,7 @@
},
{
"packageName": "@typespec/compiler",
"comment": "**DEPRECATION** `object` is deprecated. Alternative is to use `{}` for an empty model, `Record<unknown>` for a record with unknown propertie types, `unknown[]` for an array.",
"comment": "**DEPRECATION** `object` is deprecated. Alternative is to use `{}` for an empty model, `Record<unknown>` for a record with unknown property types, `unknown[]` for an array.",
"type": "none"
}
],

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

@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@typespec/compiler",
"comment": "Mark `Array` and `Record` doc comment as for dev only",
"type": "none"
}
],
"packageName": "@typespec/compiler"
}

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

@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@typespec/openapi3",
"comment": "Fix: Documentation on `model is x[]` was not included in schema description",
"type": "none"
}
],
"packageName": "@typespec/openapi3"
}

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

@ -5307,7 +5307,8 @@ function extractMainDoc(type: Type): string | undefined {
for (const doc of type.node.docs) {
mainDoc += getDocContent(doc.content);
}
return mainDoc;
const trimmed = mainDoc.trim();
return trimmed === "" ? undefined : trimmed;
}
function extractParamDoc(node: OperationStatementNode, paramName: string): string | undefined {

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

@ -124,18 +124,18 @@ scalar boolean;
* Represent a model
*/
// Deprecated June 2023 sprint
@deprecated("object is deprecated. Please use {} for an empty model, `Record<unknown>` for a record with unknown propertie types, `unknown[]` for an array.")
@deprecated("object is deprecated. Please use {} for an empty model, `Record<unknown>` for a record with unknown property types, `unknown[]` for an array.")
model object {}
/**
* Array model type, equivalent to `T[]`
* @dev Array model type, equivalent to `T[]`
* @template T The type of the array elements
*/
@indexer(integer, T)
model Array<T> {}
/**
* Model with string properties where all the properties have type `T`
* @dev Model with string properties where all the properties have type `T`
* @template T The type of the properties
*/
@indexer(string, T)

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

@ -887,7 +887,7 @@ function createOAPIEmitter(program: Program, options: ResolvedOpenAPI3EmitterOpt
if (type.kind === "String" || type.kind === "Number" || type.kind === "Boolean") {
// For literal types, we just want to emit them directly as well.
return mapTypeSpecTypeToOpenAPI(type, visibility);
return getSchemaForLiterals(type);
}
if (type.kind === "Intrinsic" && type.name === "unknown") {
@ -1251,7 +1251,7 @@ function createOAPIEmitter(program: Program, options: ResolvedOpenAPI3EmitterOpt
}
function getSchemaForType(type: Type, visibility: Visibility): OpenAPI3Schema | undefined {
const builtinType = mapTypeSpecTypeToOpenAPI(type, visibility);
const builtinType = getSchemaForLiterals(type);
if (builtinType !== undefined) return builtinType;
switch (type.kind) {
@ -1364,7 +1364,7 @@ function createOAPIEmitter(program: Program, options: ResolvedOpenAPI3EmitterOpt
if (isLiteralType(variant.type)) {
if (!literalVariantEnumByType[variant.type.kind]) {
const enumSchema = mapTypeSpecTypeToOpenAPI(variant.type, visibility);
const enumSchema = getSchemaForLiterals(variant.type);
literalVariantEnumByType[variant.type.kind] = enumSchema;
schemaMembers.push({ schema: enumSchema, type: null });
} else {
@ -1502,6 +1502,13 @@ function createOAPIEmitter(program: Program, options: ResolvedOpenAPI3EmitterOpt
}
function getSchemaForModel(model: Model, visibility: Visibility) {
const arrayOrRecord = mapTypeSpecIntrinsicModelToOpenAPI(model, visibility);
if (arrayOrRecord) {
const arrayDoc = getDoc(program, model);
arrayOrRecord.description = arrayDoc;
return arrayOrRecord;
}
let modelSchema: OpenAPI3Schema & Required<Pick<OpenAPI3Schema, "properties">> = {
type: "object",
properties: {},
@ -1772,7 +1779,11 @@ function createOAPIEmitter(program: Program, options: ResolvedOpenAPI3EmitterOpt
// Map an TypeSpec type to an OA schema. Returns undefined when the resulting
// OA schema is just a regular object schema.
function mapTypeSpecTypeToOpenAPI(typespecType: Type, visibility: Visibility): any {
function getSchemaForLiterals(
typespecType: NumericLiteral | StringLiteral | BooleanLiteral
): OpenAPI3Schema;
function getSchemaForLiterals(typespecType: Type): OpenAPI3Schema | undefined;
function getSchemaForLiterals(typespecType: Type): OpenAPI3Schema | undefined {
switch (typespecType.kind) {
case "Number":
return { type: "number", enum: [typespecType.value] };
@ -1780,8 +1791,8 @@ function createOAPIEmitter(program: Program, options: ResolvedOpenAPI3EmitterOpt
return { type: "string", enum: [typespecType.value] };
case "Boolean":
return { type: "boolean", enum: [typespecType.value] };
case "Model":
return mapTypeSpecIntrinsicModelToOpenAPI(typespecType, visibility);
default:
return undefined;
}
}
@ -1836,7 +1847,7 @@ function createOAPIEmitter(program: Program, options: ResolvedOpenAPI3EmitterOpt
function mapTypeSpecIntrinsicModelToOpenAPI(
typespecType: Model,
visibility: Visibility
): any | undefined {
): OpenAPI3Schema | undefined {
if (typespecType.indexer) {
if (isNeverType(typespecType.indexer.key)) {
} else {
@ -1854,6 +1865,7 @@ function createOAPIEmitter(program: Program, options: ResolvedOpenAPI3EmitterOpt
}
}
}
return undefined;
}
function getSchemaForScalar(scalar: Scalar): OpenAPI3Schema {

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

@ -431,7 +431,7 @@ export type OpenAPI3Schema = Extensions & {
*
* @see https://tools.ietf.org/html/draft-wright-json-schema-validation-01#section-6.23
*/
enum?: string[];
enum?: (string | number | boolean)[];
/** the JSON type for the schema */
type?: JsonType;

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

@ -37,6 +37,18 @@ describe("openapi3: Array", () => {
});
});
it("named array applies doc", async () => {
const res = await oapiForModel(
"Pet",
`
@doc("This is a doc for PetNames")
model PetNames is string[] {}
model Pet { names: PetNames };
`
);
deepStrictEqual(res.schemas.PetNames.description, "This is a doc for PetNames");
});
it("can specify minItems using @minItems decorator", async () => {
const res = await oapiForModel(
"Pet",

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

@ -355,8 +355,8 @@ components:
type: array
items:
$ref: '#/components/schemas/Book'
x-typespec-name: Book[]
description: The list of books.
x-typespec-name: Book[]
next_page_token:
type: string
description: >-
@ -396,8 +396,8 @@ components:
type: array
items:
$ref: '#/components/schemas/Shelf'
x-typespec-name: Shelf[]
description: The list of shelves.
x-typespec-name: Shelf[]
next_page_token:
type: string
description: >-

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

@ -603,8 +603,8 @@ components:
type: array
items:
$ref: '#/components/schemas/Checkup'
x-typespec-name: Checkup[]
description: The items on this page
x-typespec-name: Checkup[]
nextLink:
type: string
format: uri
@ -670,8 +670,8 @@ components:
type: array
items:
$ref: '#/components/schemas/Owner'
x-typespec-name: Owner[]
description: The items on this page
x-typespec-name: Owner[]
nextLink:
type: string
format: uri
@ -731,8 +731,8 @@ components:
type: array
items:
$ref: '#/components/schemas/Pet'
x-typespec-name: Pet[]
description: The items on this page
x-typespec-name: Pet[]
nextLink:
type: string
format: uri
@ -810,8 +810,8 @@ components:
type: array
items:
$ref: '#/components/schemas/Toy'
x-typespec-name: Toy[]
description: The items on this page
x-typespec-name: Toy[]
nextLink:
type: string
format: uri