Add support for void in openapi3 emitter (#336)

This commit is contained in:
Timothee Guerin 2022-03-21 10:13:46 -07:00 коммит произвёл GitHub
Родитель e79ae0be46
Коммит 326c7c7b9d
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
7 изменённых файлов: 69 добавлений и 14 удалений

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

@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@cadl-lang/compiler",
"comment": "Add helper methods to detect `void` and `never` types",
"type": "minor"
}
],
"packageName": "@cadl-lang/compiler"
}

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

@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@cadl-lang/openapi3",
"comment": "Add support for `void` type",
"type": "minor"
}
],
"packageName": "@cadl-lang/openapi3"
}

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

@ -5,10 +5,12 @@ import {
Expression,
isIntrinsic,
JsSourceFileNode,
NeverType,
ProjectionModelExpressionNode,
ProjectionModelPropertyNode,
ProjectionModelSpreadPropertyNode,
SymbolFlags,
VoidType,
} from "./index.js";
import { createDiagnostic, reportDiagnostic } from "./messages.js";
import { hasParseError, visitChildren } from "./parser.js";
@ -33,7 +35,6 @@ import {
InterfaceStatementNode,
InterfaceType,
IntersectionExpressionNode,
IntrinsicType,
LiteralNode,
LiteralType,
MemberExpressionNode,
@ -134,9 +135,9 @@ export interface Checker {
node?: StringLiteralNode | NumericLiteralNode | BooleanLiteralNode
): StringLiteralType | NumericLiteralType | BooleanLiteralType;
errorType: IntrinsicType;
voidType: IntrinsicType;
neverType: IntrinsicType;
errorType: ErrorType;
voidType: VoidType;
neverType: NeverType;
}
interface TypePrototype {

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

@ -93,13 +93,21 @@ export interface Projector {
export interface IntrinsicType extends BaseType {
kind: "Intrinsic";
name: string;
name: "ErrorType" | "void" | "never";
}
export interface ErrorType extends IntrinsicType {
name: "ErrorType";
}
export interface VoidType extends IntrinsicType {
name: "void";
}
export interface NeverType extends IntrinsicType {
name: "never";
}
// represents a type that is being returned from the
// currently executing lambda or projection
export interface ReturnRecord {

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

@ -14,8 +14,10 @@ import {
ModelType,
ModelTypeProperty,
NamespaceType,
NeverType,
OperationType,
Type,
VoidType,
} from "../core/types.js";
export const namespace = "Cadl";
@ -125,6 +127,14 @@ export function isErrorType(type: Type): boolean {
return type.kind === "Intrinsic" && type.name === "ErrorType";
}
export function isVoidType(type: Type): type is VoidType {
return type.kind === "Intrinsic" && type.name === "void";
}
export function isNeverType(type: Type): type is NeverType {
return type.kind === "Intrinsic" && type.name === "never";
}
const numericTypesKey = Symbol();
export function $numeric({ program }: DecoratorContext, target: Type) {
if (!isIntrinsic(program, target)) {

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

@ -31,6 +31,7 @@ import {
isNumericType,
isSecret,
isStringType,
isVoidType,
mapChildModels,
ModelType,
ModelTypeProperty,
@ -379,7 +380,6 @@ function createOAPIEmitter(program: Program, options: OpenAPIEmitterOptions) {
// Get explicitly defined body
let bodyModel = getResponseBody(responseModel);
// If there is no explicit body, it should be conjured from the return type
// if it is a primitive type or it contains more than just response metadata
if (!bodyModel) {
@ -404,16 +404,11 @@ function createOAPIEmitter(program: Program, options: OpenAPIEmitterOptions) {
// If there is no explicit status code, set the default
if (statusCodes.length === 0) {
if (bodyModel) {
const defaultStatusCode = isErrorModel(program, responseModel) ? "default" : "200";
statusCodes.push(defaultStatusCode);
} else {
statusCodes.push("204");
}
statusCodes.push(getDefaultStatusCode(responseModel, bodyModel));
}
// If there is a body but no explicit content types, use application/json
if (bodyModel && contentTypes.length === 0) {
if (bodyModel && !isVoidType(bodyModel) && contentTypes.length === 0) {
contentTypes.push("application/json");
}
@ -443,7 +438,6 @@ function createOAPIEmitter(program: Program, options: OpenAPIEmitterOptions) {
if (Object.keys(headers).length > 0) {
response.headers = headers;
}
for (const contentType of contentTypes) {
response.content ??= {};
const isBinary = isBinaryPayload(bodyModel!, contentType);
@ -454,6 +448,18 @@ function createOAPIEmitter(program: Program, options: OpenAPIEmitterOptions) {
}
}
/**
* Return the default status code for the given response/body
* @param model representing the body
*/
function getDefaultStatusCode(responseModel: Type, bodyModel: Type | undefined) {
if (bodyModel === undefined || isVoidType(bodyModel)) {
return "204";
} else {
return isErrorModel(program, responseModel) ? "default" : "200";
}
}
// Get explicity defined status codes from response Model
// Return is an array of strings, possibly empty, which indicates no explicitly defined status codes.
// We do not check for duplicates here -- that will be done by the caller.

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

@ -586,6 +586,16 @@ describe("openapi3: return types", () => {
ok(responses["204"] === undefined);
});
it("defaults to 204 no content with void response type", async () => {
const res = await openApiFor(`@get op read(): void;`);
ok(res.paths["/"].get.responses["204"]);
});
it("defaults to 204 no content with void @body", async () => {
const res = await openApiFor(`@get op read(): {@body body: void};`);
ok(res.paths["/"].get.responses["204"]);
});
describe("binary responses", () => {
it("bytes responses should default to application/json with byte format", async () => {
const res = await openApiFor(