Add support for void in openapi3 emitter (#336)
This commit is contained in:
Родитель
e79ae0be46
Коммит
326c7c7b9d
|
@ -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(
|
||||
|
|
Загрузка…
Ссылка в новой задаче