@trapi/metadata
Version:
Generate REST-API metadata scheme from TypeScript Decorators.
188 lines • 7.84 kB
JavaScript
"use strict";
/*
* Copyright (c) 2021-2023.
* Author Peter Placzek (tada5hi)
* For the full copyright and license information,
* view the LICENSE file that was distributed with this source code.
*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.MethodGenerator = void 0;
const node_path_1 = __importDefault(require("node:path"));
const typescript_1 = require("typescript");
const decorator_1 = require("../../decorator");
const resolver_1 = require("../../resolver");
const utils_1 = require("../../utils");
const abstract_1 = require("../abstract");
const parameter_1 = require("../parameter");
class MethodGenerator extends abstract_1.AbstractGenerator {
// --------------------------------------------------------------------
constructor(node, current) {
super(node, current);
this.determineVerb();
}
// --------------------------------------------------------------------
isValid() {
return typeof this.method !== 'undefined';
}
getMethodName() {
const identifier = this.node.name;
return identifier.text;
}
generate(controllerPath) {
let nodeType = this.node.type;
if (!nodeType) {
const { typeChecker } = this.current;
const signature = typeChecker.getSignatureFromDeclaration(this.node);
const implicitType = typeChecker.getReturnTypeOfSignature(signature);
nodeType = typeChecker.typeToTypeNode(implicitType, undefined, typescript_1.NodeBuilderFlags.NoTruncation);
}
const type = new resolver_1.TypeNodeResolver(nodeType, this.current).resolve();
const responses = this.mergeResponses(this.buildResponses(), this.buildResponse(type));
const methodPath = this.buildPath();
return {
consumes: this.getConsumes(),
deprecated: this.isDeprecated(this.node),
description: (0, utils_1.getJSDocDescription)(this.node),
extensions: (0, resolver_1.getNodeExtensions)(this.node, this.current.decoratorResolver),
hidden: this.isHidden(this.node),
method: this.method,
name: this.node.name.text,
path: methodPath,
produces: this.getProduces(),
responses,
security: this.getSecurity(),
summary: (0, utils_1.getJSDocTagComment)(this.node, utils_1.JSDocTagName.SUMMARY),
tags: this.getTags(),
type,
parameters: this.buildParameters(controllerPath, methodPath),
};
}
getCurrentLocation() {
const methodId = this.node.name;
const controllerId = this.node.parent.name;
return `${controllerId.text}.${methodId.text}`;
}
buildParameters(controllerPath, methodPath) {
const controllerId = this.node.parent.name;
const methodId = this.node.name;
const fullPath = node_path_1.default.posix.join('/', controllerPath, methodPath);
const output = [];
let bodyParameterCount = 0;
let formParameterCount = 0;
for (let i = 0; i < this.node.parameters.length; i++) {
try {
const generator = new parameter_1.ParameterGenerator(this.node.parameters[i], this.method, fullPath, this.current);
const parameters = generator.generate();
for (let j = 0; j < parameters.length; j++) {
if (parameters[j].in === parameter_1.ParameterSource.BODY) {
bodyParameterCount++;
}
if (parameters[j].in === parameter_1.ParameterSource.FORM_DATA) {
formParameterCount++;
}
if (parameters[j].in !== parameter_1.ParameterSource.CONTEXT) {
output.push(parameters[j]);
}
}
}
catch (e) {
const parameterId = this.node.parameters[i].name;
throw new Error(`Parameter generation: '${controllerId.text}.${methodId.text}' argument: ${parameterId.text} ${e}`);
}
}
if (bodyParameterCount > 1) {
throw new Error(`Only one body parameter allowed in '${this.getCurrentLocation()}' method.`);
}
if (bodyParameterCount > 0 && formParameterCount > 0) {
throw new Error(`Choose either form-, file- or body-parameter in '${this.getCurrentLocation()}' method.`);
}
return output;
}
determineVerb() {
const methods = [
decorator_1.DecoratorID.ALL,
decorator_1.DecoratorID.DELETE,
decorator_1.DecoratorID.GET,
decorator_1.DecoratorID.HEAD,
decorator_1.DecoratorID.OPTIONS,
decorator_1.DecoratorID.PATCH,
decorator_1.DecoratorID.POST,
decorator_1.DecoratorID.PUT,
];
const decorators = (0, utils_1.getNodeDecorators)(this.node);
let method;
for (let i = 0; i < methods.length; i++) {
const representationManager = this.current.decoratorResolver.match(methods[i], decorators);
if (representationManager) {
method = methods[i];
break;
}
}
if (typeof method === 'undefined') {
return;
}
this.method = method.toLowerCase();
}
buildResponse(type) {
type = this.guessResponseType(type);
return {
description: (0, resolver_1.isVoidType)(type) ? 'No content' : 'Ok',
examples: this.getResponseExamples(),
schema: type,
status: (0, resolver_1.isVoidType)(type) ? '204' : '200',
name: (0, resolver_1.isVoidType)(type) ? '204' : '200',
};
}
guessResponseType(type) {
if (!(0, resolver_1.isVoidType)(type)) {
return type;
}
const representation = this.current.decoratorResolver.match(decorator_1.DecoratorID.EXAMPLE, this.node);
if (typeof representation === 'undefined') {
return type;
}
const value = representation.get('type');
if (typeof value !== 'undefined' &&
(0, utils_1.hasOwnProperty)(value, 'kind') &&
(0, typescript_1.isTypeNode)(value)) {
type = new resolver_1.TypeNodeResolver(value, this.current).resolve();
}
return type;
}
getResponseExamples() {
const representation = this.current.decoratorResolver.match(decorator_1.DecoratorID.EXAMPLE, this.node);
if (typeof representation === 'undefined') {
return [];
}
const output = [];
for (let i = 0; i < representation.decorators.length; i++) {
const value = representation.get('payload');
const label = representation.get('label');
if (typeof value !== 'undefined') {
output.push({ value, label });
}
}
return output;
}
mergeResponses(responses, exampleResponse) {
if (!responses || !responses.length) {
return [exampleResponse];
}
const index = responses.findIndex((resp) => resp.status === exampleResponse.status);
if (index >= 0) {
if (exampleResponse.examples &&
(!responses[index].examples || !responses[index].examples.length)) {
responses[index].examples = exampleResponse.examples;
}
}
else {
responses.push(exampleResponse);
}
return responses;
}
}
exports.MethodGenerator = MethodGenerator;
//# sourceMappingURL=module.js.map