tsoa
This commit is contained in:
33
node_modules/@tsoa/cli/dist/swagger/specGenerator.d.ts
generated
vendored
Normal file
33
node_modules/@tsoa/cli/dist/swagger/specGenerator.d.ts
generated
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
import { ExtendedSpecConfig } from '../cli';
|
||||
import { Tsoa, Swagger } from '@tsoa/runtime';
|
||||
export declare abstract class SpecGenerator {
|
||||
protected readonly metadata: Tsoa.Metadata;
|
||||
protected readonly config: ExtendedSpecConfig;
|
||||
constructor(metadata: Tsoa.Metadata, config: ExtendedSpecConfig);
|
||||
protected buildAdditionalProperties(type: Tsoa.Type): Swagger.BaseSchema<unknown>;
|
||||
protected buildOperationIdTemplate(inlineTemplate: string): HandlebarsTemplateDelegate<any>;
|
||||
protected getOperationId(controllerName: string, method: Tsoa.Method): string;
|
||||
throwIfNotDataFormat(strToTest: string): Swagger.DataFormat;
|
||||
throwIfNotDataType(strToTest: string): Swagger.DataType;
|
||||
protected getSwaggerType(type: Tsoa.Type, title?: string): Swagger.BaseSchema;
|
||||
protected abstract getSwaggerTypeForUnionType(type: Tsoa.UnionType, title?: string): Swagger.BaseSchema;
|
||||
protected abstract getSwaggerTypeForIntersectionType(type: Tsoa.IntersectionType, title?: string): Swagger.BaseSchema;
|
||||
protected abstract buildProperties(properties: Tsoa.Property[]): {
|
||||
[propertyName: string]: Swagger.Schema2;
|
||||
} | {
|
||||
[propertyName: string]: Swagger.Schema3;
|
||||
} | {
|
||||
[propertyName: string]: Swagger.Schema31;
|
||||
};
|
||||
getSwaggerTypeForObjectLiteral(objectLiteral: Tsoa.NestedObjectLiteralType, title?: string): Swagger.BaseSchema;
|
||||
protected getSwaggerTypeForReferenceType(_referenceType: Tsoa.ReferenceType): Swagger.BaseSchema;
|
||||
protected getSwaggerTypeForVoid(_dataType: 'void' | 'undefined'): Swagger.BaseSchema;
|
||||
protected determineImplicitAdditionalPropertiesValue: () => boolean;
|
||||
protected getSwaggerTypeForPrimitiveType(dataType: Tsoa.PrimitiveTypeLiteral): Swagger.BaseSchema;
|
||||
protected getSwaggerTypeForArrayType(arrayType: Tsoa.ArrayType, title?: string): Swagger.BaseSchema;
|
||||
protected determineTypesUsedInEnum(anEnum: Array<string | number | boolean | null>): Set<"string" | "number" | "boolean">;
|
||||
protected abstract getSwaggerTypeForEnumType(enumType: Tsoa.EnumType, title?: string): Swagger.Schema2 | Swagger.Schema3;
|
||||
protected hasUndefined(property: Tsoa.Property): boolean;
|
||||
protected queriesPropertyToQueryParameter(property: Tsoa.Property): Tsoa.Parameter;
|
||||
protected isRequiredWithoutDefault(prop: Tsoa.Property | Tsoa.Parameter): boolean | undefined;
|
||||
}
|
||||
251
node_modules/@tsoa/cli/dist/swagger/specGenerator.js
generated
vendored
Normal file
251
node_modules/@tsoa/cli/dist/swagger/specGenerator.js
generated
vendored
Normal file
@@ -0,0 +1,251 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || (function () {
|
||||
var ownKeys = function(o) {
|
||||
ownKeys = Object.getOwnPropertyNames || function (o) {
|
||||
var ar = [];
|
||||
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
||||
return ar;
|
||||
};
|
||||
return ownKeys(o);
|
||||
};
|
||||
return function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.SpecGenerator = void 0;
|
||||
const runtime_1 = require("@tsoa/runtime");
|
||||
const handlebars = __importStar(require("handlebars"));
|
||||
class SpecGenerator {
|
||||
constructor(metadata, config) {
|
||||
this.metadata = metadata;
|
||||
this.config = config;
|
||||
this.determineImplicitAdditionalPropertiesValue = () => {
|
||||
if (this.config.noImplicitAdditionalProperties === 'silently-remove-extras') {
|
||||
return false;
|
||||
}
|
||||
else if (this.config.noImplicitAdditionalProperties === 'throw-on-extras') {
|
||||
return false;
|
||||
}
|
||||
else if (this.config.noImplicitAdditionalProperties === 'ignore') {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return (0, runtime_1.assertNever)(this.config.noImplicitAdditionalProperties);
|
||||
}
|
||||
};
|
||||
}
|
||||
buildAdditionalProperties(type) {
|
||||
return this.getSwaggerType(type);
|
||||
}
|
||||
buildOperationIdTemplate(inlineTemplate) {
|
||||
handlebars.registerHelper('titleCase', (value) => (value ? value.charAt(0).toUpperCase() + value.slice(1) : value));
|
||||
handlebars.registerHelper('replace', (subject, searchValue, withValue = '') => (subject ? subject.replace(searchValue, withValue) : subject));
|
||||
return handlebars.compile(inlineTemplate, { noEscape: true });
|
||||
}
|
||||
getOperationId(controllerName, method) {
|
||||
return this.buildOperationIdTemplate(this.config.operationIdTemplate ?? '{{titleCase method.name}}')({
|
||||
method,
|
||||
controllerName,
|
||||
});
|
||||
}
|
||||
throwIfNotDataFormat(strToTest) {
|
||||
const guiltyUntilInnocent = strToTest;
|
||||
if (guiltyUntilInnocent === 'int32' ||
|
||||
guiltyUntilInnocent === 'int64' ||
|
||||
guiltyUntilInnocent === 'float' ||
|
||||
guiltyUntilInnocent === 'double' ||
|
||||
guiltyUntilInnocent === 'byte' ||
|
||||
guiltyUntilInnocent === 'binary' ||
|
||||
guiltyUntilInnocent === 'date' ||
|
||||
guiltyUntilInnocent === 'date-time' ||
|
||||
guiltyUntilInnocent === 'password') {
|
||||
return guiltyUntilInnocent;
|
||||
}
|
||||
else {
|
||||
return (0, runtime_1.assertNever)(guiltyUntilInnocent);
|
||||
}
|
||||
}
|
||||
throwIfNotDataType(strToTest) {
|
||||
const guiltyUntilInnocent = strToTest;
|
||||
if (guiltyUntilInnocent === 'array' ||
|
||||
guiltyUntilInnocent === 'boolean' ||
|
||||
guiltyUntilInnocent === 'integer' ||
|
||||
guiltyUntilInnocent === 'file' ||
|
||||
guiltyUntilInnocent === 'number' ||
|
||||
guiltyUntilInnocent === 'object' ||
|
||||
guiltyUntilInnocent === 'string' ||
|
||||
guiltyUntilInnocent === 'undefined') {
|
||||
return guiltyUntilInnocent;
|
||||
}
|
||||
else {
|
||||
return (0, runtime_1.assertNever)(guiltyUntilInnocent);
|
||||
}
|
||||
}
|
||||
getSwaggerType(type, title) {
|
||||
if (type.dataType === 'void' || type.dataType === 'undefined') {
|
||||
return this.getSwaggerTypeForVoid(type.dataType);
|
||||
}
|
||||
else if (type.dataType === 'refEnum' || type.dataType === 'refObject' || type.dataType === 'refAlias') {
|
||||
return this.getSwaggerTypeForReferenceType(type);
|
||||
}
|
||||
else if (type.dataType === 'any' ||
|
||||
type.dataType === 'binary' ||
|
||||
type.dataType === 'boolean' ||
|
||||
type.dataType === 'buffer' ||
|
||||
type.dataType === 'byte' ||
|
||||
type.dataType === 'date' ||
|
||||
type.dataType === 'datetime' ||
|
||||
type.dataType === 'double' ||
|
||||
type.dataType === 'float' ||
|
||||
type.dataType === 'file' ||
|
||||
type.dataType === 'integer' ||
|
||||
type.dataType === 'long' ||
|
||||
type.dataType === 'object' ||
|
||||
type.dataType === 'string') {
|
||||
return this.getSwaggerTypeForPrimitiveType(type.dataType);
|
||||
}
|
||||
else if (type.dataType === 'array') {
|
||||
return this.getSwaggerTypeForArrayType(type, title);
|
||||
}
|
||||
else if (type.dataType === 'enum') {
|
||||
return this.getSwaggerTypeForEnumType(type, title);
|
||||
}
|
||||
else if (type.dataType === 'union') {
|
||||
return this.getSwaggerTypeForUnionType(type, title);
|
||||
}
|
||||
else if (type.dataType === 'intersection') {
|
||||
return this.getSwaggerTypeForIntersectionType(type, title);
|
||||
}
|
||||
else if (type.dataType === 'nestedObjectLiteral') {
|
||||
return this.getSwaggerTypeForObjectLiteral(type, title);
|
||||
}
|
||||
else if (type.dataType === 'tuple') {
|
||||
throw new Error('Tuple types are only supported in OpenAPI 3.1+');
|
||||
}
|
||||
else {
|
||||
return (0, runtime_1.assertNever)(type);
|
||||
}
|
||||
}
|
||||
getSwaggerTypeForObjectLiteral(objectLiteral, title) {
|
||||
const properties = this.buildProperties(objectLiteral.properties);
|
||||
const additionalProperties = objectLiteral.additionalProperties && this.getSwaggerType(objectLiteral.additionalProperties);
|
||||
const required = objectLiteral.properties.filter(prop => this.isRequiredWithoutDefault(prop) && !this.hasUndefined(prop)).map(prop => prop.name);
|
||||
// An empty list required: [] is not valid.
|
||||
// If all properties are optional, do not specify the required keyword.
|
||||
return {
|
||||
...(title && { title }),
|
||||
properties,
|
||||
...(additionalProperties && { additionalProperties }),
|
||||
...(required && required.length && { required }),
|
||||
type: 'object',
|
||||
};
|
||||
}
|
||||
getSwaggerTypeForReferenceType(_referenceType) {
|
||||
return {
|
||||
// Don't set additionalProperties value here since it will be set within the $ref's model when that $ref gets created
|
||||
};
|
||||
}
|
||||
getSwaggerTypeForVoid(_dataType) {
|
||||
// Described here: https://swagger.io/docs/specification/describing-responses/#empty
|
||||
const voidSchema = {
|
||||
// isn't allowed to have additionalProperties at all (meaning not a boolean or object)
|
||||
};
|
||||
return voidSchema;
|
||||
}
|
||||
getSwaggerTypeForPrimitiveType(dataType) {
|
||||
if (dataType === 'object') {
|
||||
if (process.env.NODE_ENV !== 'tsoa_test') {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(`The type Object is discouraged. Please consider using an interface such as:
|
||||
export interface IStringToStringDictionary {
|
||||
[key: string]: string;
|
||||
}
|
||||
// or
|
||||
export interface IRecordOfAny {
|
||||
[key: string]: any;
|
||||
}
|
||||
`);
|
||||
}
|
||||
}
|
||||
const map = {
|
||||
any: {
|
||||
// While the any type is discouraged, it does explicitly allows anything, so it should always allow additionalProperties
|
||||
additionalProperties: true,
|
||||
},
|
||||
binary: { type: 'string', format: 'binary' },
|
||||
boolean: { type: 'boolean' },
|
||||
buffer: { type: 'string', format: 'byte' },
|
||||
byte: { type: 'string', format: 'byte' },
|
||||
date: { type: 'string', format: 'date' },
|
||||
datetime: { type: 'string', format: 'date-time' },
|
||||
double: { type: 'number', format: 'double' },
|
||||
file: { type: 'file' },
|
||||
float: { type: 'number', format: 'float' },
|
||||
integer: { type: 'integer', format: 'int32' },
|
||||
long: { type: 'integer', format: 'int64' },
|
||||
object: {
|
||||
additionalProperties: this.determineImplicitAdditionalPropertiesValue(),
|
||||
type: 'object',
|
||||
},
|
||||
string: { type: 'string' },
|
||||
};
|
||||
return map[dataType];
|
||||
}
|
||||
getSwaggerTypeForArrayType(arrayType, title) {
|
||||
return {
|
||||
items: this.getSwaggerType(arrayType.elementType, title),
|
||||
type: 'array',
|
||||
};
|
||||
}
|
||||
determineTypesUsedInEnum(anEnum) {
|
||||
const typesUsedInEnum = anEnum.reduce((theSet, curr) => {
|
||||
const typeUsed = curr === null ? 'number' : typeof curr;
|
||||
theSet.add(typeUsed);
|
||||
return theSet;
|
||||
}, new Set());
|
||||
return typesUsedInEnum;
|
||||
}
|
||||
hasUndefined(property) {
|
||||
return property.type.dataType === 'undefined' || (property.type.dataType === 'union' && property.type.types.some(type => type.dataType === 'undefined'));
|
||||
}
|
||||
queriesPropertyToQueryParameter(property) {
|
||||
return {
|
||||
parameterName: property.name,
|
||||
example: [property.example],
|
||||
description: property.description,
|
||||
in: 'query',
|
||||
name: property.name,
|
||||
required: this.isRequiredWithoutDefault(property),
|
||||
type: property.type,
|
||||
default: property.default,
|
||||
validators: property.validators,
|
||||
deprecated: property.deprecated,
|
||||
};
|
||||
}
|
||||
isRequiredWithoutDefault(prop) {
|
||||
return prop.required && prop.default == null;
|
||||
}
|
||||
}
|
||||
exports.SpecGenerator = SpecGenerator;
|
||||
//# sourceMappingURL=specGenerator.js.map
|
||||
1
node_modules/@tsoa/cli/dist/swagger/specGenerator.js.map
generated
vendored
Normal file
1
node_modules/@tsoa/cli/dist/swagger/specGenerator.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
27
node_modules/@tsoa/cli/dist/swagger/specGenerator2.d.ts
generated
vendored
Normal file
27
node_modules/@tsoa/cli/dist/swagger/specGenerator2.d.ts
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
import { Tsoa, Swagger } from '@tsoa/runtime';
|
||||
import { SpecGenerator } from './specGenerator';
|
||||
import { ExtendedSpecConfig } from '../cli';
|
||||
export declare class SpecGenerator2 extends SpecGenerator {
|
||||
protected readonly metadata: Tsoa.Metadata;
|
||||
protected readonly config: ExtendedSpecConfig;
|
||||
constructor(metadata: Tsoa.Metadata, config: ExtendedSpecConfig);
|
||||
GetSpec(): Swagger.Spec2;
|
||||
private buildDefinitions;
|
||||
private buildPaths;
|
||||
private buildMethod;
|
||||
protected buildOperation(controllerName: string, method: Tsoa.Method, defaultProduces?: string[]): Swagger.Operation;
|
||||
private buildBodyPropParameter;
|
||||
private buildQueriesParameter;
|
||||
private buildParameter;
|
||||
protected buildProperties(source: Tsoa.Property[]): {
|
||||
[propertyName: string]: Swagger.Schema2;
|
||||
};
|
||||
protected getSwaggerTypeForUnionType(type: Tsoa.UnionType): Swagger.BaseSchema<unknown>;
|
||||
protected getSwaggerTypeForIntersectionType(type: Tsoa.IntersectionType): {
|
||||
type: string;
|
||||
properties: {};
|
||||
};
|
||||
protected getSwaggerTypeForReferenceType(referenceType: Tsoa.ReferenceType): Swagger.BaseSchema;
|
||||
private decideEnumType;
|
||||
protected getSwaggerTypeForEnumType(enumType: Tsoa.EnumType): Swagger.Schema2;
|
||||
}
|
||||
464
node_modules/@tsoa/cli/dist/swagger/specGenerator2.js
generated
vendored
Normal file
464
node_modules/@tsoa/cli/dist/swagger/specGenerator2.js
generated
vendored
Normal file
@@ -0,0 +1,464 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.SpecGenerator2 = void 0;
|
||||
const merge_anything_1 = require("merge-anything");
|
||||
const ts_deepmerge_1 = require("ts-deepmerge");
|
||||
const runtime_1 = require("@tsoa/runtime");
|
||||
const specGenerator_1 = require("./specGenerator");
|
||||
const isVoidType_1 = require("../utils/isVoidType");
|
||||
const pathUtils_1 = require("../utils/pathUtils");
|
||||
const swaggerUtils_1 = require("../utils/swaggerUtils");
|
||||
const validatorUtils_1 = require("../utils/validatorUtils");
|
||||
class SpecGenerator2 extends specGenerator_1.SpecGenerator {
|
||||
constructor(metadata, config) {
|
||||
super(metadata, config);
|
||||
this.metadata = metadata;
|
||||
this.config = config;
|
||||
}
|
||||
GetSpec() {
|
||||
let spec = {
|
||||
basePath: (0, pathUtils_1.normalisePath)(this.config.basePath, '/', undefined, false),
|
||||
consumes: [swaggerUtils_1.DEFAULT_REQUEST_MEDIA_TYPE],
|
||||
definitions: this.buildDefinitions(),
|
||||
info: {
|
||||
title: '',
|
||||
},
|
||||
paths: this.buildPaths(),
|
||||
produces: [swaggerUtils_1.DEFAULT_RESPONSE_MEDIA_TYPE],
|
||||
swagger: '2.0',
|
||||
};
|
||||
const securityDefinitions = this.config.securityDefinitions ? this.config.securityDefinitions : {};
|
||||
const supportedSchemes = ['basic', 'apiKey', 'oauth2'];
|
||||
for (const { type } of Object.values(securityDefinitions)) {
|
||||
if (!supportedSchemes.includes(type)) {
|
||||
throw new Error(`Swagger 2.0 does not support "${type}" security scheme (allowed values: ${supportedSchemes.join(',')})`);
|
||||
}
|
||||
}
|
||||
spec.securityDefinitions = securityDefinitions;
|
||||
if (this.config.name) {
|
||||
spec.info.title = this.config.name;
|
||||
}
|
||||
if (this.config.version) {
|
||||
spec.info.version = this.config.version;
|
||||
}
|
||||
if (this.config.host) {
|
||||
spec.host = this.config.host;
|
||||
}
|
||||
if (this.config.description) {
|
||||
spec.info.description = this.config.description;
|
||||
}
|
||||
if (this.config.termsOfService) {
|
||||
spec.info.termsOfService = this.config.termsOfService;
|
||||
}
|
||||
if (this.config.tags) {
|
||||
spec.tags = this.config.tags;
|
||||
}
|
||||
if (this.config.license) {
|
||||
spec.info.license = { name: this.config.license };
|
||||
}
|
||||
if (this.config.contact) {
|
||||
spec.info.contact = this.config.contact;
|
||||
}
|
||||
if (this.config.spec) {
|
||||
this.config.specMerging = this.config.specMerging || 'immediate';
|
||||
const mergeFuncs = {
|
||||
immediate: Object.assign,
|
||||
recursive: merge_anything_1.merge,
|
||||
deepmerge: (spec, merge) => (0, ts_deepmerge_1.merge)(spec, merge),
|
||||
};
|
||||
spec = mergeFuncs[this.config.specMerging](spec, this.config.spec);
|
||||
}
|
||||
if (this.config.schemes) {
|
||||
spec.schemes = this.config.schemes;
|
||||
}
|
||||
return spec;
|
||||
}
|
||||
buildDefinitions() {
|
||||
const definitions = {};
|
||||
Object.keys(this.metadata.referenceTypeMap).map(typeName => {
|
||||
const referenceType = this.metadata.referenceTypeMap[typeName];
|
||||
if (referenceType.dataType === 'refObject') {
|
||||
const required = referenceType.properties.filter(p => this.isRequiredWithoutDefault(p) && !this.hasUndefined(p)).map(p => p.name);
|
||||
definitions[referenceType.refName] = {
|
||||
description: referenceType.description,
|
||||
properties: this.buildProperties(referenceType.properties),
|
||||
required: required && required.length > 0 ? Array.from(new Set(required)) : undefined,
|
||||
type: 'object',
|
||||
};
|
||||
if (referenceType.additionalProperties) {
|
||||
definitions[referenceType.refName].additionalProperties = this.buildAdditionalProperties(referenceType.additionalProperties);
|
||||
}
|
||||
else {
|
||||
// Since additionalProperties was not explicitly set in the TypeScript interface for this model
|
||||
// ...we need to make a decision
|
||||
definitions[referenceType.refName].additionalProperties = this.determineImplicitAdditionalPropertiesValue();
|
||||
}
|
||||
if (referenceType.example) {
|
||||
definitions[referenceType.refName].example = referenceType.example;
|
||||
}
|
||||
}
|
||||
else if (referenceType.dataType === 'refEnum') {
|
||||
definitions[referenceType.refName] = {
|
||||
description: referenceType.description,
|
||||
enum: referenceType.enums,
|
||||
type: this.decideEnumType(referenceType.enums, referenceType.refName),
|
||||
};
|
||||
if (this.config.xEnumVarnames && referenceType.enumVarnames !== undefined && referenceType.enums.length === referenceType.enumVarnames.length) {
|
||||
definitions[referenceType.refName]['x-enum-varnames'] = referenceType.enumVarnames;
|
||||
}
|
||||
if (referenceType.example) {
|
||||
definitions[referenceType.refName].example = referenceType.example;
|
||||
}
|
||||
}
|
||||
else if (referenceType.dataType === 'refAlias') {
|
||||
const swaggerType = this.getSwaggerType(referenceType.type);
|
||||
const format = referenceType.format;
|
||||
const validators = Object.keys(referenceType.validators)
|
||||
.filter(validatorUtils_1.shouldIncludeValidatorInSchema)
|
||||
.reduce((acc, key) => {
|
||||
return {
|
||||
...acc,
|
||||
[key]: referenceType.validators[key].value,
|
||||
};
|
||||
}, {});
|
||||
definitions[referenceType.refName] = {
|
||||
...swaggerType,
|
||||
default: referenceType.default || swaggerType.default,
|
||||
example: referenceType.example,
|
||||
format: format || swaggerType.format,
|
||||
description: referenceType.description,
|
||||
...validators,
|
||||
};
|
||||
}
|
||||
else {
|
||||
(0, runtime_1.assertNever)(referenceType);
|
||||
}
|
||||
if (referenceType.deprecated) {
|
||||
definitions[referenceType.refName]['x-deprecated'] = true;
|
||||
}
|
||||
});
|
||||
return definitions;
|
||||
}
|
||||
buildPaths() {
|
||||
const paths = {};
|
||||
this.metadata.controllers.forEach(controller => {
|
||||
const normalisedControllerPath = (0, pathUtils_1.normalisePath)(controller.path, '/');
|
||||
// construct documentation using all methods except @Hidden
|
||||
controller.methods
|
||||
.filter(method => !method.isHidden)
|
||||
.forEach(method => {
|
||||
const normalisedMethodPath = (0, pathUtils_1.normalisePath)(method.path, '/');
|
||||
let path = (0, pathUtils_1.normalisePath)(`${normalisedControllerPath}${normalisedMethodPath}`, '/', '', false);
|
||||
path = (0, pathUtils_1.convertColonPathParams)(path);
|
||||
paths[path] = paths[path] || {};
|
||||
this.buildMethod(controller.name, method, paths[path], controller.produces);
|
||||
});
|
||||
});
|
||||
return paths;
|
||||
}
|
||||
buildMethod(controllerName, method, pathObject, defaultProduces) {
|
||||
const pathMethod = (pathObject[method.method] = this.buildOperation(controllerName, method, defaultProduces));
|
||||
pathMethod.description = method.description;
|
||||
pathMethod.summary = method.summary;
|
||||
pathMethod.tags = method.tags;
|
||||
// Use operationId tag otherwise fallback to generated. Warning: This doesn't check uniqueness.
|
||||
pathMethod.operationId = method.operationId || pathMethod.operationId;
|
||||
if (method.deprecated) {
|
||||
pathMethod.deprecated = method.deprecated;
|
||||
}
|
||||
if (method.security) {
|
||||
pathMethod.security = method.security;
|
||||
}
|
||||
const queriesParams = method.parameters.filter(p => p.in === 'queries');
|
||||
pathMethod.parameters = method.parameters
|
||||
.filter(p => {
|
||||
return ['request', 'body-prop', 'res', 'queries', 'request-prop'].indexOf(p.in) === -1;
|
||||
})
|
||||
.map(p => this.buildParameter(p));
|
||||
if (queriesParams.length > 1) {
|
||||
throw new Error('Only one queries parameter allowed per controller method.');
|
||||
}
|
||||
if (queriesParams.length === 1) {
|
||||
pathMethod.parameters.push(...this.buildQueriesParameter(queriesParams[0]));
|
||||
}
|
||||
const bodyPropParameter = this.buildBodyPropParameter(controllerName, method);
|
||||
if (bodyPropParameter) {
|
||||
pathMethod.parameters.push(bodyPropParameter);
|
||||
}
|
||||
if (pathMethod.parameters.filter((p) => p.in === 'body').length > 1) {
|
||||
throw new Error('Only one body parameter allowed per controller method.');
|
||||
}
|
||||
method.extensions.forEach(ext => (pathMethod[ext.key] = ext.value));
|
||||
}
|
||||
buildOperation(controllerName, method, defaultProduces) {
|
||||
const swaggerResponses = {};
|
||||
let produces = [];
|
||||
method.responses.forEach((res) => {
|
||||
swaggerResponses[res.name] = {
|
||||
description: res.description,
|
||||
};
|
||||
if (res.schema && !(0, isVoidType_1.isVoidType)(res.schema)) {
|
||||
if (res.produces) {
|
||||
produces.push(...res.produces);
|
||||
}
|
||||
swaggerResponses[res.name].schema = this.getSwaggerType(res.schema);
|
||||
}
|
||||
if (res.examples && res.examples[0]) {
|
||||
if ((res.exampleLabels?.filter(e => e).length || 0) > 0) {
|
||||
console.warn('Example labels are not supported in OpenAPI 2');
|
||||
}
|
||||
swaggerResponses[res.name].examples = { [swaggerUtils_1.DEFAULT_RESPONSE_MEDIA_TYPE]: res.examples[0] };
|
||||
}
|
||||
if (res.headers) {
|
||||
const headers = {};
|
||||
if (res.headers.dataType === 'refObject' || res.headers.dataType === 'nestedObjectLiteral') {
|
||||
res.headers.properties.forEach((each) => {
|
||||
headers[each.name] = {
|
||||
...this.getSwaggerType(each.type),
|
||||
description: each.description,
|
||||
};
|
||||
});
|
||||
}
|
||||
else {
|
||||
(0, runtime_1.assertNever)(res.headers);
|
||||
}
|
||||
swaggerResponses[res.name].headers = headers;
|
||||
}
|
||||
});
|
||||
produces = Array.from(new Set(produces.filter(p => p !== undefined)));
|
||||
if (produces.length === 0) {
|
||||
produces = defaultProduces || [swaggerUtils_1.DEFAULT_RESPONSE_MEDIA_TYPE];
|
||||
}
|
||||
const operation = {
|
||||
operationId: this.getOperationId(controllerName, method),
|
||||
produces: produces,
|
||||
responses: swaggerResponses,
|
||||
};
|
||||
const hasBody = method.parameters.some(p => p.in === 'body');
|
||||
const hasFormData = method.parameters.some(p => p.in === 'formData');
|
||||
if (hasBody || hasFormData) {
|
||||
operation.consumes = [];
|
||||
if (hasBody) {
|
||||
operation.consumes.push(method.consumes || swaggerUtils_1.DEFAULT_REQUEST_MEDIA_TYPE);
|
||||
}
|
||||
if (hasFormData) {
|
||||
operation.consumes.push('multipart/form-data');
|
||||
}
|
||||
}
|
||||
return operation;
|
||||
}
|
||||
buildBodyPropParameter(controllerName, method) {
|
||||
const properties = {};
|
||||
const required = [];
|
||||
method.parameters
|
||||
.filter(p => p.in === 'body-prop')
|
||||
.forEach(p => {
|
||||
properties[p.name] = this.getSwaggerType(p.type);
|
||||
properties[p.name].default = p.default;
|
||||
properties[p.name].description = p.description;
|
||||
properties[p.name].example = p.example === undefined ? undefined : p.example[0];
|
||||
if (this.isRequiredWithoutDefault(p)) {
|
||||
required.push(p.name);
|
||||
}
|
||||
});
|
||||
if (!Object.keys(properties).length) {
|
||||
return;
|
||||
}
|
||||
const parameter = {
|
||||
in: 'body',
|
||||
name: 'body',
|
||||
schema: {
|
||||
properties,
|
||||
title: `${this.getOperationId(controllerName, method)}Body`,
|
||||
type: 'object',
|
||||
},
|
||||
};
|
||||
if (required.length) {
|
||||
parameter.schema.required = required;
|
||||
}
|
||||
return parameter;
|
||||
}
|
||||
buildQueriesParameter(source) {
|
||||
if (source.type.dataType === 'refObject' || source.type.dataType === 'nestedObjectLiteral') {
|
||||
const properties = source.type.properties;
|
||||
return properties.map(property => this.buildParameter(this.queriesPropertyToQueryParameter(property)));
|
||||
}
|
||||
throw new Error(`Queries '${source.name}' parameter must be an object.`);
|
||||
}
|
||||
buildParameter(source) {
|
||||
let type = source.type;
|
||||
if (source.in !== 'body' && source.type.dataType === 'refEnum') {
|
||||
// swagger does not support referencing enums
|
||||
// (except for body parameters), so we have to inline it
|
||||
type = {
|
||||
dataType: 'enum',
|
||||
enums: source.type.enums,
|
||||
};
|
||||
}
|
||||
const parameterType = this.getSwaggerType(type);
|
||||
let parameter = {
|
||||
default: source.default,
|
||||
description: source.description,
|
||||
in: source.in,
|
||||
name: source.name,
|
||||
required: this.isRequiredWithoutDefault(source),
|
||||
...(source.deprecated ? { 'x-deprecated': true } : {}),
|
||||
...(parameterType.$ref ? { schema: parameterType } : {}),
|
||||
...(parameterType.format ? { format: this.throwIfNotDataFormat(parameterType.format) } : {}),
|
||||
};
|
||||
if (runtime_1.Swagger.isQueryParameter(parameter) && parameterType.type === 'array') {
|
||||
parameter.collectionFormat = 'multi';
|
||||
}
|
||||
if (parameter.schema) {
|
||||
return parameter;
|
||||
}
|
||||
const validatorObjs = {};
|
||||
Object.keys(source.validators)
|
||||
.filter(validatorUtils_1.shouldIncludeValidatorInSchema)
|
||||
.forEach(key => {
|
||||
validatorObjs[key] = source.validators[key].value;
|
||||
});
|
||||
if (source.in === 'body' && source.type.dataType === 'array') {
|
||||
parameter.schema = {
|
||||
items: parameterType.items,
|
||||
type: 'array',
|
||||
};
|
||||
}
|
||||
else {
|
||||
if (source.type.dataType === 'any') {
|
||||
if (source.in === 'body') {
|
||||
parameter.schema = { type: 'object' };
|
||||
}
|
||||
else {
|
||||
parameter.type = 'string';
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (parameterType.type) {
|
||||
parameter.type = this.throwIfNotDataType(parameterType.type);
|
||||
}
|
||||
parameter.items = parameterType.items;
|
||||
parameter.enum = parameterType.enum;
|
||||
}
|
||||
}
|
||||
if (parameter.schema) {
|
||||
parameter.schema = Object.assign({}, parameter.schema, validatorObjs);
|
||||
}
|
||||
else {
|
||||
parameter = Object.assign({}, parameter, validatorObjs);
|
||||
}
|
||||
return parameter;
|
||||
}
|
||||
buildProperties(source) {
|
||||
const properties = {};
|
||||
source.forEach(property => {
|
||||
let swaggerType = this.getSwaggerType(property.type);
|
||||
const format = property.format;
|
||||
swaggerType.description = property.description;
|
||||
swaggerType.example = property.example;
|
||||
swaggerType.format = format || swaggerType.format;
|
||||
if (!swaggerType.$ref) {
|
||||
swaggerType.default = property.default;
|
||||
Object.keys(property.validators)
|
||||
.filter(validatorUtils_1.shouldIncludeValidatorInSchema)
|
||||
.forEach(key => {
|
||||
swaggerType = { ...swaggerType, [key]: property.validators[key].value };
|
||||
});
|
||||
}
|
||||
if (property.deprecated) {
|
||||
swaggerType['x-deprecated'] = true;
|
||||
}
|
||||
if (property.extensions) {
|
||||
property.extensions.forEach(property => {
|
||||
swaggerType[property.key] = property.value;
|
||||
});
|
||||
}
|
||||
properties[property.name] = swaggerType;
|
||||
});
|
||||
return properties;
|
||||
}
|
||||
getSwaggerTypeForUnionType(type) {
|
||||
const typesWithoutUndefined = type.types.filter(x => x.dataType !== 'undefined');
|
||||
// Backwards compatible representation of a literal enumeration
|
||||
if (typesWithoutUndefined.every(subType => subType.dataType === 'enum')) {
|
||||
const mergedEnum = { dataType: 'enum', enums: [] };
|
||||
typesWithoutUndefined.forEach(t => {
|
||||
/* eslint-disable @typescript-eslint/no-unnecessary-type-assertion */
|
||||
mergedEnum.enums = [...mergedEnum.enums, ...t.enums];
|
||||
});
|
||||
return this.getSwaggerTypeForEnumType(mergedEnum);
|
||||
}
|
||||
else if (typesWithoutUndefined.length === 2 && typesWithoutUndefined.find(typeInUnion => typeInUnion.dataType === 'enum' && typeInUnion.enums.includes(null))) {
|
||||
// Backwards compatible representation of dataType or null, $ref does not allow any sibling attributes, so we have to bail out
|
||||
const nullEnumIndex = typesWithoutUndefined.findIndex(type => type.dataType === 'enum' && type.enums.includes(null));
|
||||
const typeIndex = nullEnumIndex === 1 ? 0 : 1;
|
||||
const swaggerType = this.getSwaggerType(typesWithoutUndefined[typeIndex]);
|
||||
const isRef = !!swaggerType.$ref;
|
||||
if (isRef) {
|
||||
return { type: 'object' };
|
||||
}
|
||||
else {
|
||||
swaggerType['x-nullable'] = true;
|
||||
return swaggerType;
|
||||
}
|
||||
}
|
||||
else if (process.env.NODE_ENV !== 'tsoa_test') {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn('Swagger 2.0 does not support union types beyond string literals.\n' + 'If you would like to take advantage of this, please change tsoa.json\'s "specVersion" to 3.');
|
||||
}
|
||||
return { type: 'object' };
|
||||
}
|
||||
getSwaggerTypeForIntersectionType(type) {
|
||||
const properties = type.types.reduce((acc, type) => {
|
||||
if (type.dataType === 'refObject') {
|
||||
let refType = type;
|
||||
refType = this.metadata.referenceTypeMap[refType.refName];
|
||||
const props = refType &&
|
||||
refType.properties &&
|
||||
refType.properties.reduce((acc, prop) => {
|
||||
return {
|
||||
...acc,
|
||||
[prop.name]: this.getSwaggerType(prop.type),
|
||||
};
|
||||
}, {});
|
||||
return { ...acc, ...props };
|
||||
}
|
||||
else {
|
||||
process.env.NODE_ENV !== 'tsoa_test' &&
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn('Swagger 2.0 does not fully support this kind of intersection types. If you would like to take advantage of this, please change tsoa.json\'s "specVersion" to 3.');
|
||||
return { ...acc };
|
||||
}
|
||||
}, {});
|
||||
return { type: 'object', properties };
|
||||
}
|
||||
getSwaggerTypeForReferenceType(referenceType) {
|
||||
return { $ref: `#/definitions/${encodeURIComponent(referenceType.refName)}` };
|
||||
}
|
||||
decideEnumType(anEnum, nameOfEnum) {
|
||||
const typesUsedInEnum = this.determineTypesUsedInEnum(anEnum);
|
||||
const badEnumErrorMessage = () => {
|
||||
const valuesDelimited = Array.from(typesUsedInEnum).join(',');
|
||||
return `Enums can only have string or number values, but enum ${nameOfEnum} had ${valuesDelimited}`;
|
||||
};
|
||||
let enumTypeForSwagger;
|
||||
if (typesUsedInEnum.has('string') && typesUsedInEnum.size === 1) {
|
||||
enumTypeForSwagger = 'string';
|
||||
}
|
||||
else if (typesUsedInEnum.has('number') && typesUsedInEnum.size === 1) {
|
||||
enumTypeForSwagger = 'number';
|
||||
}
|
||||
else {
|
||||
throw new Error(badEnumErrorMessage());
|
||||
}
|
||||
return enumTypeForSwagger;
|
||||
}
|
||||
getSwaggerTypeForEnumType(enumType) {
|
||||
const types = this.determineTypesUsedInEnum(enumType.enums);
|
||||
const type = types.size === 1 ? types.values().next().value : 'string';
|
||||
const nullable = enumType.enums.includes(null) ? true : false;
|
||||
return { type, enum: enumType.enums.map(member => (0, swaggerUtils_1.getValue)(type, member)), ['x-nullable']: nullable };
|
||||
}
|
||||
}
|
||||
exports.SpecGenerator2 = SpecGenerator2;
|
||||
//# sourceMappingURL=specGenerator2.js.map
|
||||
1
node_modules/@tsoa/cli/dist/swagger/specGenerator2.js.map
generated
vendored
Normal file
1
node_modules/@tsoa/cli/dist/swagger/specGenerator2.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
158
node_modules/@tsoa/cli/dist/swagger/specGenerator3.d.ts
generated
vendored
Normal file
158
node_modules/@tsoa/cli/dist/swagger/specGenerator3.d.ts
generated
vendored
Normal file
@@ -0,0 +1,158 @@
|
||||
import { Swagger, Tsoa } from '@tsoa/runtime';
|
||||
import { ExtendedSpecConfig } from '../cli';
|
||||
import { SpecGenerator } from './specGenerator';
|
||||
/**
|
||||
* TODO:
|
||||
* Handle formData parameters
|
||||
* Handle requestBodies of type other than json
|
||||
* Handle requestBodies as reusable objects
|
||||
* Handle headers, examples, responses, etc.
|
||||
* Cleaner interface between SpecGenerator2 and SpecGenerator3
|
||||
* Also accept OpenAPI 3.0.0 metadata, like components/securitySchemes instead of securityDefinitions
|
||||
*/
|
||||
export declare class SpecGenerator3 extends SpecGenerator {
|
||||
protected readonly metadata: Tsoa.Metadata;
|
||||
protected readonly config: ExtendedSpecConfig;
|
||||
constructor(metadata: Tsoa.Metadata, config: ExtendedSpecConfig);
|
||||
GetSpec(): Swagger.Spec3;
|
||||
protected buildInfo(): Swagger.Info;
|
||||
protected buildComponents(): {
|
||||
examples: {};
|
||||
headers: {};
|
||||
parameters: {};
|
||||
requestBodies: {};
|
||||
responses: {};
|
||||
schemas: {
|
||||
[name: string]: Swagger.Schema3;
|
||||
};
|
||||
securitySchemes: {};
|
||||
};
|
||||
protected translateSecurityDefinitions(definitions: {
|
||||
[name: string]: Swagger.SecuritySchemes;
|
||||
}): {
|
||||
[name: string]: Swagger.SecuritySchemes;
|
||||
};
|
||||
protected hasOAuthFlow(definition: any): definition is {
|
||||
flow: string;
|
||||
};
|
||||
protected hasOAuthFlows(definition: any): definition is {
|
||||
flows: Swagger.OAuthFlow;
|
||||
};
|
||||
protected buildServers(): Swagger.Server[];
|
||||
protected buildSchema(): {
|
||||
[name: string]: Swagger.Schema3;
|
||||
};
|
||||
protected buildPaths(): {
|
||||
[pathName: string]: Swagger.Path3;
|
||||
};
|
||||
protected buildMethod(controllerName: string, method: Tsoa.Method, pathObject: any, defaultProduces?: string[]): void;
|
||||
protected buildOperation(controllerName: string, method: Tsoa.Method, defaultProduces?: string[]): Swagger.Operation3;
|
||||
protected buildRequestBodyWithFormData(controllerName: string, method: Tsoa.Method, parameters: Tsoa.Parameter[]): Swagger.RequestBody;
|
||||
protected buildRequestBody(controllerName: string, method: Tsoa.Method, parameter: Tsoa.Parameter): Swagger.RequestBody;
|
||||
protected buildMediaType(controllerName: string, method: Tsoa.Method, parameter: Tsoa.Parameter): Swagger.MediaType;
|
||||
protected buildQueriesParameter(source: Tsoa.Parameter): Swagger.Parameter3[];
|
||||
protected buildParameter(source: Tsoa.Parameter): Swagger.Parameter3;
|
||||
protected buildExamples(source: Pick<Tsoa.Parameter, 'example' | 'exampleLabels'>): {
|
||||
example?: unknown;
|
||||
examples?: {
|
||||
[name: string]: Swagger.Example3;
|
||||
};
|
||||
};
|
||||
protected buildProperties(source: Tsoa.Property[]): {
|
||||
[propertyName: string]: Swagger.Schema3;
|
||||
};
|
||||
protected getSwaggerTypeForReferenceType(referenceType: Tsoa.ReferenceType): Swagger.BaseSchema;
|
||||
protected getSwaggerTypeForPrimitiveType(dataType: Tsoa.PrimitiveTypeLiteral): Swagger.BaseSchema;
|
||||
protected isNull(type: Tsoa.Type): boolean;
|
||||
protected groupEnums(types: Swagger.BaseSchema[]): Swagger.BaseSchema<unknown>[];
|
||||
protected removeDuplicateSwaggerTypes(types: Swagger.BaseSchema[]): Swagger.BaseSchema[];
|
||||
protected getSwaggerTypeForUnionType(type: Tsoa.UnionType, title?: string): {
|
||||
allOf: Swagger.BaseSchema<unknown>[];
|
||||
nullable: true;
|
||||
} | {
|
||||
nullable: true;
|
||||
type?: string;
|
||||
format?: Swagger.DataFormat;
|
||||
$ref?: string;
|
||||
title?: string;
|
||||
description?: string;
|
||||
default?: string | boolean | number | unknown;
|
||||
multipleOf?: number;
|
||||
maximum?: number;
|
||||
exclusiveMaximum?: number;
|
||||
minimum?: number;
|
||||
exclusiveMinimum?: number;
|
||||
maxLength?: number;
|
||||
minLength?: number;
|
||||
pattern?: string;
|
||||
maxItems?: number;
|
||||
minItems?: number;
|
||||
uniqueItems?: boolean;
|
||||
maxProperties?: number;
|
||||
minProperties?: number;
|
||||
enum?: Array<boolean | string | number | null>;
|
||||
'x-enum-varnames'?: string[];
|
||||
additionalProperties?: boolean | Swagger.BaseSchema;
|
||||
properties?: {
|
||||
[propertyName: string]: unknown;
|
||||
} | undefined;
|
||||
discriminator?: string;
|
||||
readOnly?: boolean;
|
||||
xml?: Swagger.XML;
|
||||
externalDocs?: Swagger.ExternalDocs;
|
||||
example?: unknown;
|
||||
required?: string[];
|
||||
items?: Swagger.BaseSchema;
|
||||
allOf?: undefined;
|
||||
} | {
|
||||
anyOf: Swagger.BaseSchema<unknown>[];
|
||||
nullable: true;
|
||||
title?: string | undefined;
|
||||
allOf?: undefined;
|
||||
} | {
|
||||
type?: string;
|
||||
format?: Swagger.DataFormat;
|
||||
$ref?: string;
|
||||
title?: string;
|
||||
description?: string;
|
||||
default?: string | boolean | number | unknown;
|
||||
multipleOf?: number;
|
||||
maximum?: number;
|
||||
exclusiveMaximum?: number;
|
||||
minimum?: number;
|
||||
exclusiveMinimum?: number;
|
||||
maxLength?: number;
|
||||
minLength?: number;
|
||||
pattern?: string;
|
||||
maxItems?: number;
|
||||
minItems?: number;
|
||||
uniqueItems?: boolean;
|
||||
maxProperties?: number;
|
||||
minProperties?: number;
|
||||
enum?: Array<boolean | string | number | null>;
|
||||
'x-enum-varnames'?: string[];
|
||||
additionalProperties?: boolean | Swagger.BaseSchema;
|
||||
properties?: {
|
||||
[propertyName: string]: unknown;
|
||||
} | undefined;
|
||||
discriminator?: string;
|
||||
readOnly?: boolean;
|
||||
xml?: Swagger.XML;
|
||||
externalDocs?: Swagger.ExternalDocs;
|
||||
example?: unknown;
|
||||
required?: string[];
|
||||
items?: Swagger.BaseSchema;
|
||||
allOf?: undefined;
|
||||
nullable?: undefined;
|
||||
} | {
|
||||
anyOf: Swagger.BaseSchema<unknown>[];
|
||||
title?: string | undefined;
|
||||
allOf?: undefined;
|
||||
nullable?: undefined;
|
||||
};
|
||||
protected getSwaggerTypeForIntersectionType(type: Tsoa.IntersectionType, title?: string): {
|
||||
title?: string | undefined;
|
||||
allOf: Swagger.BaseSchema<unknown>[];
|
||||
};
|
||||
protected getSwaggerTypeForEnumType(enumType: Tsoa.EnumType, title?: string): Swagger.Schema3;
|
||||
}
|
||||
633
node_modules/@tsoa/cli/dist/swagger/specGenerator3.js
generated
vendored
Normal file
633
node_modules/@tsoa/cli/dist/swagger/specGenerator3.js
generated
vendored
Normal file
@@ -0,0 +1,633 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.SpecGenerator3 = void 0;
|
||||
const runtime_1 = require("@tsoa/runtime");
|
||||
const merge_anything_1 = require("merge-anything");
|
||||
const ts_deepmerge_1 = require("ts-deepmerge");
|
||||
const isVoidType_1 = require("../utils/isVoidType");
|
||||
const validatorUtils_1 = require("../utils/validatorUtils");
|
||||
const pathUtils_1 = require("./../utils/pathUtils");
|
||||
const swaggerUtils_1 = require("./../utils/swaggerUtils");
|
||||
const specGenerator_1 = require("./specGenerator");
|
||||
/**
|
||||
* TODO:
|
||||
* Handle formData parameters
|
||||
* Handle requestBodies of type other than json
|
||||
* Handle requestBodies as reusable objects
|
||||
* Handle headers, examples, responses, etc.
|
||||
* Cleaner interface between SpecGenerator2 and SpecGenerator3
|
||||
* Also accept OpenAPI 3.0.0 metadata, like components/securitySchemes instead of securityDefinitions
|
||||
*/
|
||||
class SpecGenerator3 extends specGenerator_1.SpecGenerator {
|
||||
constructor(metadata, config) {
|
||||
super(metadata, config);
|
||||
this.metadata = metadata;
|
||||
this.config = config;
|
||||
}
|
||||
GetSpec() {
|
||||
let spec = {
|
||||
openapi: '3.0.0',
|
||||
components: this.buildComponents(),
|
||||
info: this.buildInfo(),
|
||||
paths: this.buildPaths(),
|
||||
servers: this.buildServers(),
|
||||
tags: this.config.tags,
|
||||
};
|
||||
if (this.config.spec) {
|
||||
this.config.specMerging = this.config.specMerging || 'immediate';
|
||||
const mergeFuncs = {
|
||||
immediate: Object.assign,
|
||||
recursive: merge_anything_1.merge,
|
||||
deepmerge: (spec, merge) => (0, ts_deepmerge_1.merge)(spec, merge),
|
||||
};
|
||||
spec = mergeFuncs[this.config.specMerging](spec, this.config.spec);
|
||||
}
|
||||
return spec;
|
||||
}
|
||||
buildInfo() {
|
||||
const info = {
|
||||
title: this.config.name || '',
|
||||
};
|
||||
if (this.config.version) {
|
||||
info.version = this.config.version;
|
||||
}
|
||||
if (this.config.description) {
|
||||
info.description = this.config.description;
|
||||
}
|
||||
if (this.config.termsOfService) {
|
||||
info.termsOfService = this.config.termsOfService;
|
||||
}
|
||||
if (this.config.license) {
|
||||
info.license = { name: this.config.license };
|
||||
}
|
||||
if (this.config.contact) {
|
||||
info.contact = this.config.contact;
|
||||
}
|
||||
return info;
|
||||
}
|
||||
buildComponents() {
|
||||
const components = {
|
||||
examples: {},
|
||||
headers: {},
|
||||
parameters: {},
|
||||
requestBodies: {},
|
||||
responses: {},
|
||||
schemas: this.buildSchema(),
|
||||
securitySchemes: {},
|
||||
};
|
||||
if (this.config.securityDefinitions) {
|
||||
components.securitySchemes = this.translateSecurityDefinitions(this.config.securityDefinitions);
|
||||
}
|
||||
return components;
|
||||
}
|
||||
translateSecurityDefinitions(definitions) {
|
||||
const defs = {};
|
||||
Object.keys(definitions).forEach(key => {
|
||||
if (definitions[key].type === 'basic') {
|
||||
defs[key] = {
|
||||
scheme: 'basic',
|
||||
type: 'http',
|
||||
};
|
||||
}
|
||||
else if (definitions[key].type === 'oauth2') {
|
||||
/* eslint-disable @typescript-eslint/no-unnecessary-type-assertion */
|
||||
const definition = definitions[key];
|
||||
const oauth = (defs[key] || {
|
||||
type: 'oauth2',
|
||||
description: definitions[key].description,
|
||||
flows: (this.hasOAuthFlows(definition) && definition.flows) || {},
|
||||
});
|
||||
if (this.hasOAuthFlow(definition) && definition.flow === 'password') {
|
||||
oauth.flows.password = { tokenUrl: definition.tokenUrl, scopes: definition.scopes || {} };
|
||||
}
|
||||
else if (this.hasOAuthFlow(definition) && definition.flow === 'accessCode') {
|
||||
oauth.flows.authorizationCode = { tokenUrl: definition.tokenUrl, authorizationUrl: definition.authorizationUrl, scopes: definition.scopes || {} };
|
||||
}
|
||||
else if (this.hasOAuthFlow(definition) && definition.flow === 'application') {
|
||||
oauth.flows.clientCredentials = { tokenUrl: definition.tokenUrl, scopes: definition.scopes || {} };
|
||||
}
|
||||
else if (this.hasOAuthFlow(definition) && definition.flow === 'implicit') {
|
||||
oauth.flows.implicit = { authorizationUrl: definition.authorizationUrl, scopes: definition.scopes || {} };
|
||||
}
|
||||
defs[key] = oauth;
|
||||
}
|
||||
else {
|
||||
defs[key] = definitions[key];
|
||||
}
|
||||
});
|
||||
return defs;
|
||||
}
|
||||
hasOAuthFlow(definition) {
|
||||
return !!definition.flow;
|
||||
}
|
||||
hasOAuthFlows(definition) {
|
||||
return !!definition.flows;
|
||||
}
|
||||
buildServers() {
|
||||
const prefix = this.config.disableBasePathPrefixSlash ? undefined : '/';
|
||||
const basePath = (0, pathUtils_1.normalisePath)(this.config.basePath, prefix, undefined, false);
|
||||
const scheme = this.config.schemes ? this.config.schemes[0] : 'https';
|
||||
const hosts = this.config.servers ? this.config.servers : this.config.host ? [this.config.host] : undefined;
|
||||
const convertHost = (host) => ({ url: `${scheme}://${host}${basePath}` });
|
||||
return (hosts?.map(convertHost) || [{ url: basePath }]);
|
||||
}
|
||||
buildSchema() {
|
||||
const schema = {};
|
||||
Object.keys(this.metadata.referenceTypeMap).map(typeName => {
|
||||
const referenceType = this.metadata.referenceTypeMap[typeName];
|
||||
if (referenceType.dataType === 'refObject') {
|
||||
const required = referenceType.properties.filter(p => this.isRequiredWithoutDefault(p) && !this.hasUndefined(p)).map(p => p.name);
|
||||
schema[referenceType.refName] = {
|
||||
description: referenceType.description,
|
||||
properties: this.buildProperties(referenceType.properties),
|
||||
required: required && required.length > 0 ? Array.from(new Set(required)) : undefined,
|
||||
type: 'object',
|
||||
};
|
||||
if (referenceType.additionalProperties) {
|
||||
schema[referenceType.refName].additionalProperties = this.buildAdditionalProperties(referenceType.additionalProperties);
|
||||
}
|
||||
else {
|
||||
// Since additionalProperties was not explicitly set in the TypeScript interface for this model
|
||||
// ...we need to make a decision
|
||||
schema[referenceType.refName].additionalProperties = this.determineImplicitAdditionalPropertiesValue();
|
||||
}
|
||||
if (referenceType.example) {
|
||||
schema[referenceType.refName].example = referenceType.example;
|
||||
}
|
||||
}
|
||||
else if (referenceType.dataType === 'refEnum') {
|
||||
const enumTypes = this.determineTypesUsedInEnum(referenceType.enums);
|
||||
if (enumTypes.size === 1) {
|
||||
schema[referenceType.refName] = {
|
||||
description: referenceType.description,
|
||||
enum: referenceType.enums,
|
||||
type: enumTypes.has('string') ? 'string' : 'number',
|
||||
};
|
||||
if (this.config.xEnumVarnames && referenceType.enumVarnames !== undefined && referenceType.enums.length === referenceType.enumVarnames.length) {
|
||||
schema[referenceType.refName]['x-enum-varnames'] = referenceType.enumVarnames;
|
||||
}
|
||||
}
|
||||
else {
|
||||
schema[referenceType.refName] = {
|
||||
description: referenceType.description,
|
||||
anyOf: [
|
||||
{
|
||||
type: 'number',
|
||||
enum: referenceType.enums.filter(e => typeof e === 'number'),
|
||||
},
|
||||
{
|
||||
type: 'string',
|
||||
enum: referenceType.enums.filter(e => typeof e === 'string'),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
if (referenceType.example) {
|
||||
schema[referenceType.refName].example = referenceType.example;
|
||||
}
|
||||
}
|
||||
else if (referenceType.dataType === 'refAlias') {
|
||||
const swaggerType = this.getSwaggerType(referenceType.type);
|
||||
const format = referenceType.format;
|
||||
const validators = Object.keys(referenceType.validators)
|
||||
.filter(validatorUtils_1.shouldIncludeValidatorInSchema)
|
||||
.reduce((acc, key) => {
|
||||
return {
|
||||
...acc,
|
||||
[key]: referenceType.validators[key].value,
|
||||
};
|
||||
}, {});
|
||||
schema[referenceType.refName] = {
|
||||
...swaggerType,
|
||||
default: referenceType.default || swaggerType.default,
|
||||
example: referenceType.example,
|
||||
format: format || swaggerType.format,
|
||||
description: referenceType.description,
|
||||
...validators,
|
||||
};
|
||||
}
|
||||
else {
|
||||
(0, runtime_1.assertNever)(referenceType);
|
||||
}
|
||||
if (referenceType.deprecated) {
|
||||
schema[referenceType.refName].deprecated = true;
|
||||
}
|
||||
});
|
||||
return schema;
|
||||
}
|
||||
buildPaths() {
|
||||
const paths = {};
|
||||
this.metadata.controllers.forEach(controller => {
|
||||
const normalisedControllerPath = (0, pathUtils_1.normalisePath)(controller.path, '/');
|
||||
// construct documentation using all methods except @Hidden
|
||||
controller.methods
|
||||
.filter(method => !method.isHidden)
|
||||
.forEach(method => {
|
||||
const normalisedMethodPath = (0, pathUtils_1.normalisePath)(method.path, '/');
|
||||
let path = (0, pathUtils_1.normalisePath)(`${normalisedControllerPath}${normalisedMethodPath}`, '/', '', false);
|
||||
path = (0, pathUtils_1.convertColonPathParams)(path);
|
||||
paths[path] = paths[path] || {};
|
||||
this.buildMethod(controller.name, method, paths[path], controller.produces);
|
||||
});
|
||||
});
|
||||
return paths;
|
||||
}
|
||||
buildMethod(controllerName, method, pathObject, defaultProduces) {
|
||||
const pathMethod = (pathObject[method.method] = this.buildOperation(controllerName, method, defaultProduces));
|
||||
pathMethod.description = method.description;
|
||||
pathMethod.summary = method.summary;
|
||||
pathMethod.tags = method.tags;
|
||||
// Use operationId tag otherwise fallback to generated. Warning: This doesn't check uniqueness.
|
||||
pathMethod.operationId = method.operationId || pathMethod.operationId;
|
||||
if (method.deprecated) {
|
||||
pathMethod.deprecated = method.deprecated;
|
||||
}
|
||||
if (method.security) {
|
||||
pathMethod.security = method.security;
|
||||
}
|
||||
const bodyParams = method.parameters.filter(p => p.in === 'body');
|
||||
const bodyPropParams = method.parameters.filter(p => p.in === 'body-prop');
|
||||
const formParams = method.parameters.filter(p => p.in === 'formData');
|
||||
const queriesParams = method.parameters.filter(p => p.in === 'queries');
|
||||
pathMethod.parameters = method.parameters
|
||||
.filter(p => {
|
||||
return ['body', 'formData', 'request', 'body-prop', 'res', 'queries', 'request-prop'].indexOf(p.in) === -1;
|
||||
})
|
||||
.map(p => this.buildParameter(p));
|
||||
if (queriesParams.length > 1) {
|
||||
throw new Error('Only one queries parameter allowed per controller method.');
|
||||
}
|
||||
if (queriesParams.length === 1) {
|
||||
pathMethod.parameters.push(...this.buildQueriesParameter(queriesParams[0]));
|
||||
}
|
||||
if (bodyParams.length > 1) {
|
||||
throw new Error('Only one body parameter allowed per controller method.');
|
||||
}
|
||||
if (bodyParams.length > 0 && formParams.length > 0) {
|
||||
throw new Error('Either body parameter or form parameters allowed per controller method - not both.');
|
||||
}
|
||||
if (bodyPropParams.length > 0) {
|
||||
if (!bodyParams.length) {
|
||||
bodyParams.push({
|
||||
in: 'body',
|
||||
name: 'body',
|
||||
parameterName: 'body',
|
||||
required: true,
|
||||
type: {
|
||||
dataType: 'nestedObjectLiteral',
|
||||
properties: [],
|
||||
},
|
||||
validators: {},
|
||||
deprecated: false,
|
||||
});
|
||||
}
|
||||
const type = bodyParams[0].type;
|
||||
bodyPropParams.forEach((bodyParam) => {
|
||||
type.properties.push(bodyParam);
|
||||
});
|
||||
}
|
||||
if (bodyParams.length > 0) {
|
||||
pathMethod.requestBody = this.buildRequestBody(controllerName, method, bodyParams[0]);
|
||||
}
|
||||
else if (formParams.length > 0) {
|
||||
pathMethod.requestBody = this.buildRequestBodyWithFormData(controllerName, method, formParams);
|
||||
}
|
||||
method.extensions.forEach(ext => (pathMethod[ext.key] = ext.value));
|
||||
}
|
||||
buildOperation(controllerName, method, defaultProduces) {
|
||||
const swaggerResponses = {};
|
||||
method.responses.forEach((res) => {
|
||||
swaggerResponses[res.name] = {
|
||||
description: res.description,
|
||||
};
|
||||
if (res.schema && !(0, isVoidType_1.isVoidType)(res.schema)) {
|
||||
swaggerResponses[res.name].content = {};
|
||||
const produces = res.produces || defaultProduces || [swaggerUtils_1.DEFAULT_RESPONSE_MEDIA_TYPE];
|
||||
for (const p of produces) {
|
||||
const { content } = swaggerResponses[res.name];
|
||||
swaggerResponses[res.name].content = {
|
||||
...content,
|
||||
[p]: {
|
||||
schema: this.getSwaggerType(res.schema, this.config.useTitleTagsForInlineObjects ? this.getOperationId(controllerName, method) + 'Response' : undefined),
|
||||
},
|
||||
};
|
||||
}
|
||||
if (res.examples) {
|
||||
let exampleCounter = 1;
|
||||
const examples = res.examples.reduce((acc, ex, currentIndex) => {
|
||||
const exampleLabel = res.exampleLabels?.[currentIndex];
|
||||
return { ...acc, [exampleLabel === undefined ? `Example ${exampleCounter++}` : exampleLabel]: { value: ex } };
|
||||
}, {});
|
||||
for (const p of produces) {
|
||||
/* eslint-disable @typescript-eslint/dot-notation */
|
||||
(swaggerResponses[res.name].content || {})[p]['examples'] = examples;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (res.headers) {
|
||||
const headers = {};
|
||||
if (res.headers.dataType === 'refObject') {
|
||||
headers[res.headers.refName] = {
|
||||
schema: this.getSwaggerTypeForReferenceType(res.headers),
|
||||
description: res.headers.description,
|
||||
};
|
||||
}
|
||||
else if (res.headers.dataType === 'nestedObjectLiteral') {
|
||||
res.headers.properties.forEach((each) => {
|
||||
headers[each.name] = {
|
||||
schema: this.getSwaggerType(each.type),
|
||||
description: each.description,
|
||||
required: this.isRequiredWithoutDefault(each),
|
||||
};
|
||||
});
|
||||
}
|
||||
else {
|
||||
(0, runtime_1.assertNever)(res.headers);
|
||||
}
|
||||
swaggerResponses[res.name].headers = headers;
|
||||
}
|
||||
});
|
||||
const operation = {
|
||||
operationId: this.getOperationId(controllerName, method),
|
||||
responses: swaggerResponses,
|
||||
};
|
||||
return operation;
|
||||
}
|
||||
buildRequestBodyWithFormData(controllerName, method, parameters) {
|
||||
const required = [];
|
||||
const properties = {};
|
||||
for (const parameter of parameters) {
|
||||
const mediaType = this.buildMediaType(controllerName, method, parameter);
|
||||
properties[parameter.name] = mediaType.schema;
|
||||
if (this.isRequiredWithoutDefault(parameter)) {
|
||||
required.push(parameter.name);
|
||||
}
|
||||
if (parameter.deprecated) {
|
||||
properties[parameter.name].deprecated = parameter.deprecated;
|
||||
}
|
||||
}
|
||||
const requestBody = {
|
||||
required: required.length > 0,
|
||||
content: {
|
||||
'multipart/form-data': {
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties,
|
||||
// An empty list required: [] is not valid.
|
||||
// If all properties are optional, do not specify the required keyword.
|
||||
...(required && required.length && { required }),
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
return requestBody;
|
||||
}
|
||||
buildRequestBody(controllerName, method, parameter) {
|
||||
const mediaType = this.buildMediaType(controllerName, method, parameter);
|
||||
const consumes = method.consumes || swaggerUtils_1.DEFAULT_REQUEST_MEDIA_TYPE;
|
||||
const requestBody = {
|
||||
description: parameter.description,
|
||||
required: this.isRequiredWithoutDefault(parameter),
|
||||
content: {
|
||||
[consumes]: mediaType,
|
||||
},
|
||||
};
|
||||
return requestBody;
|
||||
}
|
||||
buildMediaType(controllerName, method, parameter) {
|
||||
const validators = Object.keys(parameter.validators)
|
||||
.filter(validatorUtils_1.shouldIncludeValidatorInSchema)
|
||||
.reduce((acc, key) => {
|
||||
return {
|
||||
...acc,
|
||||
[key]: parameter.validators[key].value,
|
||||
};
|
||||
}, {});
|
||||
const mediaType = {
|
||||
schema: {
|
||||
...this.getSwaggerType(parameter.type, this.config.useTitleTagsForInlineObjects ? this.getOperationId(controllerName, method) + 'RequestBody' : undefined),
|
||||
...validators,
|
||||
...(parameter.description && { description: parameter.description }),
|
||||
},
|
||||
};
|
||||
const parameterExamples = parameter.example;
|
||||
const parameterExampleLabels = parameter.exampleLabels;
|
||||
if (parameterExamples === undefined) {
|
||||
mediaType.example = parameterExamples;
|
||||
}
|
||||
else if (parameterExamples.length === 1) {
|
||||
mediaType.example = parameterExamples[0];
|
||||
}
|
||||
else {
|
||||
let exampleCounter = 1;
|
||||
mediaType.examples = parameterExamples.reduce((acc, ex, currentIndex) => {
|
||||
const exampleLabel = parameterExampleLabels?.[currentIndex];
|
||||
return { ...acc, [exampleLabel === undefined ? `Example ${exampleCounter++}` : exampleLabel]: { value: ex } };
|
||||
}, {});
|
||||
}
|
||||
return mediaType;
|
||||
}
|
||||
buildQueriesParameter(source) {
|
||||
if (source.type.dataType === 'refObject' || source.type.dataType === 'nestedObjectLiteral') {
|
||||
const properties = source.type.properties;
|
||||
return properties.map(property => this.buildParameter(this.queriesPropertyToQueryParameter(property)));
|
||||
}
|
||||
throw new Error(`Queries '${source.name}' parameter must be an object.`);
|
||||
}
|
||||
buildParameter(source) {
|
||||
const parameter = {
|
||||
description: source.description,
|
||||
in: source.in,
|
||||
name: source.name,
|
||||
required: this.isRequiredWithoutDefault(source),
|
||||
schema: {
|
||||
default: source.default,
|
||||
format: undefined,
|
||||
},
|
||||
};
|
||||
if (source.deprecated) {
|
||||
parameter.deprecated = true;
|
||||
}
|
||||
const parameterType = this.getSwaggerType(source.type);
|
||||
if (parameterType.format) {
|
||||
parameter.schema.format = this.throwIfNotDataFormat(parameterType.format);
|
||||
}
|
||||
if (parameterType.$ref) {
|
||||
parameter.schema = parameterType;
|
||||
return Object.assign(parameter, this.buildExamples(source));
|
||||
}
|
||||
const validatorObjs = {};
|
||||
Object.keys(source.validators)
|
||||
.filter(validatorUtils_1.shouldIncludeValidatorInSchema)
|
||||
.forEach(key => {
|
||||
validatorObjs[key] = source.validators[key].value;
|
||||
});
|
||||
if (source.type.dataType === 'any') {
|
||||
parameter.schema.type = 'string';
|
||||
}
|
||||
else {
|
||||
if (parameterType.type) {
|
||||
parameter.schema.type = this.throwIfNotDataType(parameterType.type);
|
||||
}
|
||||
parameter.schema.items = parameterType.items;
|
||||
parameter.schema.enum = parameterType.enum;
|
||||
}
|
||||
parameter.schema = Object.assign({}, parameter.schema, validatorObjs);
|
||||
return Object.assign(parameter, this.buildExamples(source));
|
||||
}
|
||||
buildExamples(source) {
|
||||
const { example: parameterExamples, exampleLabels } = source;
|
||||
if (parameterExamples === undefined) {
|
||||
return { example: undefined };
|
||||
}
|
||||
if (parameterExamples.length === 1) {
|
||||
return { example: parameterExamples[0] };
|
||||
}
|
||||
let exampleCounter = 1;
|
||||
const examples = parameterExamples.reduce((acc, ex, idx) => {
|
||||
const label = exampleLabels?.[idx];
|
||||
const name = label ?? `Example ${exampleCounter++}`;
|
||||
acc[name] = { value: ex };
|
||||
return acc;
|
||||
}, {});
|
||||
return { examples };
|
||||
}
|
||||
buildProperties(source) {
|
||||
const properties = {};
|
||||
source.forEach(property => {
|
||||
let swaggerType = this.getSwaggerType(property.type);
|
||||
const format = property.format;
|
||||
swaggerType.description = property.description;
|
||||
swaggerType.example = property.example;
|
||||
swaggerType.format = format || swaggerType.format;
|
||||
if (!swaggerType.$ref) {
|
||||
swaggerType.default = property.default;
|
||||
Object.keys(property.validators)
|
||||
.filter(validatorUtils_1.shouldIncludeValidatorInSchema)
|
||||
.forEach(key => {
|
||||
swaggerType = { ...swaggerType, [key]: property.validators[key].value };
|
||||
});
|
||||
}
|
||||
if (property.deprecated) {
|
||||
swaggerType.deprecated = true;
|
||||
}
|
||||
if (property.extensions) {
|
||||
property.extensions.forEach(property => {
|
||||
swaggerType[property.key] = property.value;
|
||||
});
|
||||
}
|
||||
properties[property.name] = swaggerType;
|
||||
});
|
||||
return properties;
|
||||
}
|
||||
getSwaggerTypeForReferenceType(referenceType) {
|
||||
return { $ref: `#/components/schemas/${encodeURIComponent(referenceType.refName)}` };
|
||||
}
|
||||
getSwaggerTypeForPrimitiveType(dataType) {
|
||||
if (dataType === 'any') {
|
||||
// Setting additionalProperties causes issues with code generators for OpenAPI 3
|
||||
// Therefore, we avoid setting it explicitly (since it's the implicit default already)
|
||||
return {};
|
||||
}
|
||||
else if (dataType === 'file') {
|
||||
return { type: 'string', format: 'binary' };
|
||||
}
|
||||
return super.getSwaggerTypeForPrimitiveType(dataType);
|
||||
}
|
||||
isNull(type) {
|
||||
return type.dataType === 'enum' && type.enums.length === 1 && type.enums[0] === null;
|
||||
}
|
||||
// Join disparate enums with the same type into one.
|
||||
//
|
||||
// grouping enums is helpful because it makes the spec more readable and it
|
||||
// bypasses a failure in openapi-generator caused by using anyOf with
|
||||
// duplicate types.
|
||||
groupEnums(types) {
|
||||
const returnTypes = [];
|
||||
const enumValuesByType = {};
|
||||
for (const type of types) {
|
||||
if (type.enum && type.type) {
|
||||
for (const enumValue of type.enum) {
|
||||
if (!enumValuesByType[type.type]) {
|
||||
enumValuesByType[type.type] = {};
|
||||
}
|
||||
enumValuesByType[type.type][String(enumValue)] = enumValue;
|
||||
}
|
||||
}
|
||||
// preserve non-enum types
|
||||
else {
|
||||
returnTypes.push(type);
|
||||
}
|
||||
}
|
||||
Object.keys(enumValuesByType).forEach(dataType => returnTypes.push({
|
||||
type: dataType,
|
||||
enum: Object.values(enumValuesByType[dataType]),
|
||||
}));
|
||||
return returnTypes;
|
||||
}
|
||||
removeDuplicateSwaggerTypes(types) {
|
||||
if (types.length === 1) {
|
||||
return types;
|
||||
}
|
||||
else {
|
||||
const typesSet = new Set();
|
||||
for (const type of types) {
|
||||
typesSet.add(JSON.stringify(type));
|
||||
}
|
||||
return Array.from(typesSet).map(typeString => JSON.parse(typeString));
|
||||
}
|
||||
}
|
||||
getSwaggerTypeForUnionType(type, title) {
|
||||
// Filter out nulls and undefineds
|
||||
const actualSwaggerTypes = this.removeDuplicateSwaggerTypes(this.groupEnums(type.types
|
||||
.filter(x => !this.isNull(x))
|
||||
.filter(x => x.dataType !== 'undefined')
|
||||
.map(x => this.getSwaggerType(x))));
|
||||
const nullable = type.types.some(x => this.isNull(x));
|
||||
if (nullable) {
|
||||
if (actualSwaggerTypes.length === 1) {
|
||||
const [swaggerType] = actualSwaggerTypes;
|
||||
// for ref union with null, use an allOf with a single
|
||||
// element since you can't attach nullable directly to a ref.
|
||||
// https://swagger.io/docs/specification/using-ref/#syntax
|
||||
if (swaggerType.$ref) {
|
||||
return { allOf: [swaggerType], nullable };
|
||||
}
|
||||
// Note that null must be explicitly included in the list of enum values. Using nullable: true alone is not enough here.
|
||||
// https://swagger.io/docs/specification/data-models/enums/
|
||||
if (swaggerType.enum) {
|
||||
swaggerType.enum.push(null);
|
||||
}
|
||||
return { ...(title && { title }), ...swaggerType, nullable };
|
||||
}
|
||||
else {
|
||||
return { ...(title && { title }), anyOf: actualSwaggerTypes, nullable };
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (actualSwaggerTypes.length === 1) {
|
||||
return { ...(title && { title }), ...actualSwaggerTypes[0] };
|
||||
}
|
||||
else {
|
||||
return { ...(title && { title }), anyOf: actualSwaggerTypes };
|
||||
}
|
||||
}
|
||||
}
|
||||
getSwaggerTypeForIntersectionType(type, title) {
|
||||
return { allOf: type.types.map(x => this.getSwaggerType(x)), ...(title && { title }) };
|
||||
}
|
||||
getSwaggerTypeForEnumType(enumType, title) {
|
||||
const types = this.determineTypesUsedInEnum(enumType.enums);
|
||||
if (types.size === 1) {
|
||||
const type = types.values().next().value;
|
||||
const nullable = enumType.enums.includes(null) ? true : false;
|
||||
return { ...(title && { title }), type, enum: enumType.enums.map(member => (0, swaggerUtils_1.getValue)(type, member)), nullable };
|
||||
}
|
||||
else {
|
||||
const valuesDelimited = Array.from(types).join(',');
|
||||
throw new Error(`Enums can only have string or number values, but enum had ${valuesDelimited}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.SpecGenerator3 = SpecGenerator3;
|
||||
//# sourceMappingURL=specGenerator3.js.map
|
||||
1
node_modules/@tsoa/cli/dist/swagger/specGenerator3.js.map
generated
vendored
Normal file
1
node_modules/@tsoa/cli/dist/swagger/specGenerator3.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
24
node_modules/@tsoa/cli/dist/swagger/specGenerator31.d.ts
generated
vendored
Normal file
24
node_modules/@tsoa/cli/dist/swagger/specGenerator31.d.ts
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
import { Swagger, Tsoa } from '@tsoa/runtime';
|
||||
import { ExtendedSpecConfig } from '../cli';
|
||||
import { SpecGenerator3 } from './specGenerator3';
|
||||
/**
|
||||
* OpenAPI 3.1 Spec Generator
|
||||
*
|
||||
* Extends SpecGenerator3 with OpenAPI 3.1 specific features:
|
||||
* - Tuple support via prefixItems
|
||||
* - OpenAPI version 3.1.0
|
||||
*
|
||||
* Uses inheritance to reuse all building logic from SpecGenerator3,
|
||||
* only overriding the version and adding tuple type support.
|
||||
*/
|
||||
export declare class SpecGenerator31 extends SpecGenerator3 {
|
||||
protected readonly metadata: Tsoa.Metadata;
|
||||
protected readonly config: ExtendedSpecConfig;
|
||||
constructor(metadata: Tsoa.Metadata, config: ExtendedSpecConfig);
|
||||
GetSpec(): Swagger.Spec31;
|
||||
/**
|
||||
* Override to add tuple type support (OpenAPI 3.1 feature via prefixItems)
|
||||
*/
|
||||
protected getSwaggerType(type: Tsoa.Type, title?: string): Swagger.BaseSchema;
|
||||
private isTupleType;
|
||||
}
|
||||
73
node_modules/@tsoa/cli/dist/swagger/specGenerator31.js
generated
vendored
Normal file
73
node_modules/@tsoa/cli/dist/swagger/specGenerator31.js
generated
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.SpecGenerator31 = void 0;
|
||||
const merge_anything_1 = require("merge-anything");
|
||||
const ts_deepmerge_1 = require("ts-deepmerge");
|
||||
const specGenerator3_1 = require("./specGenerator3");
|
||||
/**
|
||||
* OpenAPI 3.1 Spec Generator
|
||||
*
|
||||
* Extends SpecGenerator3 with OpenAPI 3.1 specific features:
|
||||
* - Tuple support via prefixItems
|
||||
* - OpenAPI version 3.1.0
|
||||
*
|
||||
* Uses inheritance to reuse all building logic from SpecGenerator3,
|
||||
* only overriding the version and adding tuple type support.
|
||||
*/
|
||||
class SpecGenerator31 extends specGenerator3_1.SpecGenerator3 {
|
||||
constructor(metadata, config) {
|
||||
super(metadata, config);
|
||||
this.metadata = metadata;
|
||||
this.config = config;
|
||||
}
|
||||
// Override with OpenAPI 3.1 specific return type
|
||||
// The base class returns Swagger.Spec30, but this generator produces Swagger.Spec31
|
||||
GetSpec() {
|
||||
let spec = {
|
||||
openapi: '3.1.0',
|
||||
components: this.buildComponents(),
|
||||
info: this.buildInfo(),
|
||||
paths: this.buildPaths(),
|
||||
servers: this.buildServers(),
|
||||
tags: this.config.tags,
|
||||
};
|
||||
if (this.config.spec) {
|
||||
this.config.specMerging = this.config.specMerging || 'immediate';
|
||||
const mergeFuncs = {
|
||||
immediate: Object.assign,
|
||||
recursive: merge_anything_1.merge,
|
||||
deepmerge: (spec, merge) => (0, ts_deepmerge_1.merge)(spec, merge),
|
||||
};
|
||||
spec = mergeFuncs[this.config.specMerging](spec, this.config.spec);
|
||||
}
|
||||
return spec;
|
||||
}
|
||||
/**
|
||||
* Override to add tuple type support (OpenAPI 3.1 feature via prefixItems)
|
||||
*/
|
||||
getSwaggerType(type, title) {
|
||||
if (this.isTupleType(type)) {
|
||||
const prefixItems = type.types.map((t) => this.getSwaggerType(t));
|
||||
const schema = {
|
||||
type: 'array',
|
||||
prefixItems,
|
||||
minItems: prefixItems.length,
|
||||
...(type.restType
|
||||
? {
|
||||
items: this.getSwaggerType(type.restType),
|
||||
}
|
||||
: {
|
||||
maxItems: prefixItems.length,
|
||||
items: false,
|
||||
}),
|
||||
};
|
||||
return schema;
|
||||
}
|
||||
return super.getSwaggerType(type, title);
|
||||
}
|
||||
isTupleType(type) {
|
||||
return type.dataType === 'tuple';
|
||||
}
|
||||
}
|
||||
exports.SpecGenerator31 = SpecGenerator31;
|
||||
//# sourceMappingURL=specGenerator31.js.map
|
||||
1
node_modules/@tsoa/cli/dist/swagger/specGenerator31.js.map
generated
vendored
Normal file
1
node_modules/@tsoa/cli/dist/swagger/specGenerator31.js.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"specGenerator31.js","sourceRoot":"","sources":["../../src/swagger/specGenerator31.ts"],"names":[],"mappings":";;;AACA,mDAAwD;AACxD,+CAAkD;AAIlD,qDAAkD;AAElD;;;;;;;;;GASG;AACH,MAAa,eAAgB,SAAQ,+BAAc;IACjD,YACqB,QAAuB,EACvB,MAA0B;QAE7C,KAAK,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QAHL,aAAQ,GAAR,QAAQ,CAAe;QACvB,WAAM,GAAN,MAAM,CAAoB;IAG/C,CAAC;IAED,iDAAiD;IACjD,oFAAoF;IACpE,OAAO;QACrB,IAAI,IAAI,GAAmB;YACzB,OAAO,EAAE,OAAO;YAChB,UAAU,EAAE,IAAI,CAAC,eAAe,EAA0B;YAC1D,IAAI,EAAE,IAAI,CAAC,SAAS,EAAE;YACtB,KAAK,EAAE,IAAI,CAAC,UAAU,EAAwC;YAC9D,OAAO,EAAE,IAAI,CAAC,YAAY,EAAE;YAC5B,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI;SACvB,CAAC;QAEF,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;YACrB,IAAI,CAAC,MAAM,CAAC,WAAW,GAAG,IAAI,CAAC,MAAM,CAAC,WAAW,IAAI,WAAW,CAAC;YACjE,MAAM,UAAU,GAAgG;gBAC9G,SAAS,EAAE,MAAM,CAAC,MAAM;gBACxB,SAAS,EAAE,sBAAa;gBACxB,SAAS,EAAE,CAAC,IAAuB,EAAE,KAAwB,EAAqB,EAAE,CAAC,IAAA,oBAAS,EAAC,IAAI,EAAE,KAAK,CAAC;aAC5G,CAAC;YAEF,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,IAAoC,EAAE,IAAI,CAAC,MAAM,CAAC,IAAyB,CAA8B,CAAC;QACvJ,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;OAEG;IACO,cAAc,CAAC,IAAe,EAAE,KAAc;QACtD,IAAI,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC;YAC3B,MAAM,WAAW,GAAuB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAY,EAAE,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,CAAqB,CAAC,CAAC;YAErH,MAAM,MAAM,GAAqB;gBAC/B,IAAI,EAAE,OAAO;gBACb,WAAW;gBACX,QAAQ,EAAE,WAAW,CAAC,MAAM;gBAC5B,GAAG,CAAC,IAAI,CAAC,QAAQ;oBACf,CAAC,CAAC;wBACE,KAAK,EAAE,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,QAAQ,CAAqB;qBAC9D;oBACH,CAAC,CAAC;wBACE,QAAQ,EAAE,WAAW,CAAC,MAAM;wBAC5B,KAAK,EAAE,KAAK;qBACb,CAAC;aACP,CAAC;YAEF,OAAO,MAAuC,CAAC;QACjD,CAAC;QAED,OAAO,KAAK,CAAC,cAAc,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAC3C,CAAC;IAEO,WAAW,CAAC,IAAe;QACjC,OAAO,IAAI,CAAC,QAAQ,KAAK,OAAO,CAAC;IACnC,CAAC;CACF;AAhED,0CAgEC"}
|
||||
Reference in New Issue
Block a user