typescript-swagger
Version: 
Generate Swagger files from a decorator library like typescript-rest or a @decorators/express.
375 lines (373 loc) • 17.4 kB
JavaScript
"use strict";
var __extends = (this && this.__extends) || (function () {
    var extendStatics = function (d, b) {
        extendStatics = Object.setPrototypeOf ||
            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
            function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
        return extendStatics(d, b);
    };
    return function (d, b) {
        if (typeof b !== "function" && b !== null)
            throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.ParameterGenerator = void 0;
var ts = require("typescript");
var type_1 = require("../decorator/type");
var utils_1 = require("../decorator/utils");
var resolver_1 = require("./resolver");
var utils_2 = require("./resolver/utils");
var supportedParameterKeys = [
    'SERVER_CONTEXT',
    'SERVER_PARAMS',
    'SERVER_QUERY',
    'SERVER_FORM',
    'SERVER_BODY',
    'SERVER_HEADERS',
    'SERVER_COOKIES',
    'SERVER_PATH_PARAMS',
    'SERVER_FILES_PARAM'
];
var ParameterGenerator = /** @class */ (function () {
    function ParameterGenerator(parameter, method, path, current) {
        this.parameter = parameter;
        this.method = method;
        this.path = path;
        this.current = current;
    }
    ParameterGenerator.prototype.generate = function () {
        var decorators = utils_1.getDecorators(this.parameter);
        var decoratorNames = decorators.map(function (decorator) { return decorator.text; });
        for (var i = 0; i < supportedParameterKeys.length; i++) {
            var handler = type_1.Decorator.getRepresentationHandler(supportedParameterKeys[i], this.current.decoratorMap);
            var names = handler.getNames();
            var index = -1;
            for (var j = 0; j < names.length; j++) {
                index = decoratorNames.indexOf(names[j]);
                if (index !== -1) {
                    break;
                }
            }
            if (index === -1) {
                continue;
            }
            this.decorator = decorators[index];
            this.decoratorHandler = handler;
            switch (supportedParameterKeys[i]) {
                case 'SERVER_CONTEXT':
                    return this.getContextParameter();
                case 'SERVER_PARAMS':
                    return this.getRequestParameter();
                case 'SERVER_FORM':
                    return this.getFormParameter();
                case 'SERVER_QUERY':
                    return this.getQueryParameter();
                case 'SERVER_BODY':
                    return this.getBodyParameter();
                case 'SERVER_HEADERS':
                    return this.getHeaderParameter();
                case 'SERVER_COOKIES':
                    return this.getCookieParameter();
                case 'SERVER_PATH_PARAMS':
                    return this.getPathParameter();
                case 'SERVER_FILE_PARAM':
                    return this.getFileParameter();
                case 'SERVER_FILES_PARAM':
                    return this.getFileParameter(true);
            }
        }
        return this.getBodyParameter();
    };
    ParameterGenerator.prototype.getCurrentLocation = function () {
        var methodId = this.parameter.parent.name;
        var controllerId = this.parameter.parent.parent.name;
        return controllerId.text + "." + methodId.text;
    };
    ParameterGenerator.prototype.getRequestParameter = function () {
        var parameterName = this.parameter.name.text;
        var name = parameterName;
        var type = this.getValidatedType(this.parameter);
        if (!this.supportsBodyParameters(this.method)) {
            throw new Error("Param can't support '" + this.getCurrentLocation() + "' method.");
        }
        if (typeof this.decoratorHandler !== 'undefined' && typeof this.decorator !== 'undefined') {
            var argument = this.decoratorHandler.getDecoratorPropertyValueAsItem(this.decorator, this.decoratorHandler.getPropertyByType(this.decorator.text));
            if (typeof argument === 'string') {
                name = argument;
            }
        }
        return {
            description: this.getParameterDescription(this.parameter),
            in: 'param',
            name: name || parameterName,
            parameterName: parameterName,
            required: !this.parameter.questionToken,
            type: type
        };
    };
    ParameterGenerator.prototype.getContextParameter = function () {
        var parameterName = this.parameter.name.text;
        return {
            description: this.getParameterDescription(this.parameter),
            in: 'context',
            name: parameterName,
            parameterName: parameterName,
            required: !this.parameter.questionToken,
            type: null
        };
    };
    /*
    private getFileParameter(parameter: ts.ParameterDeclaration): Parameter {
        const parameterName = (parameter.name as ts.Identifier).text;
        if (!this.supportsBodyParameters(this.method)) {
            throw new Error(`FileParam can't support '${this.getCurrentLocation()}' method.`);
        }
        return {
            description: this.getParameterDescription(parameter),
            in: 'formData',
            name: getDecoratorTextValue(this.parameter, ident => ident.text === 'FileParam') || parameterName,
            parameterName: parameterName,
            required: !parameter.questionToken,
            type: { typeName: 'file' }
        };
    }
     */
    ParameterGenerator.prototype.getFileParameter = function (isArray) {
        var parameterName = this.parameter.name.text;
        var name = parameterName;
        if (!this.supportsBodyParameters(this.method)) {
            throw new Error("File(s)Param can't support '" + this.getCurrentLocation() + "' method.");
        }
        if (typeof this.decoratorHandler !== 'undefined' && typeof this.decorator !== 'undefined') {
            var argument = this.decoratorHandler.getDecoratorPropertyValueAsItem(this.decorator, this.decoratorHandler.getPropertyByType(this.decorator.text));
            if (typeof argument === 'string') {
                name = argument;
            }
        }
        var elementType = { typeName: 'file' };
        var type;
        if (isArray) {
            type = { typeName: 'array', elementType: elementType };
        }
        else {
            type = elementType;
        }
        return {
            description: this.getParameterDescription(this.parameter),
            in: 'formData',
            name: name || parameterName,
            parameterName: parameterName,
            required: !this.parameter.questionToken && !this.parameter.initializer,
            type: type
        };
    };
    ParameterGenerator.prototype.getFormParameter = function () {
        var parameterName = this.parameter.name.text;
        var name = parameterName;
        var type = this.getValidatedType(this.parameter);
        if (!this.supportsBodyParameters(this.method)) {
            throw new Error("Form can't support '" + this.getCurrentLocation() + "' method.");
        }
        if (typeof this.decoratorHandler !== 'undefined' && typeof this.decorator !== 'undefined') {
            var argument = this.decoratorHandler.getDecoratorPropertyValueAsItem(this.decorator, this.decoratorHandler.getPropertyByType(this.decorator.text));
            if (typeof argument === 'string') {
                name = argument;
            }
        }
        return {
            description: this.getParameterDescription(this.parameter),
            in: 'formData',
            name: name || parameterName,
            parameterName: parameterName,
            required: !this.parameter.questionToken && !this.parameter.initializer,
            type: type
        };
    };
    ParameterGenerator.prototype.getCookieParameter = function () {
        var parameterName = this.parameter.name.text;
        var name = parameterName;
        var type = this.getValidatedType(this.parameter);
        if (!this.supportPathDataType(type)) {
            throw new Error("Cookie can't support '" + this.getCurrentLocation() + "' method.");
        }
        if (typeof this.decoratorHandler !== 'undefined' && typeof this.decorator !== 'undefined') {
            var argument = this.decoratorHandler.getDecoratorPropertyValueAsItem(this.decorator, this.decoratorHandler.getPropertyByType(this.decorator.text));
            if (typeof argument === 'string') {
                name = argument;
            }
        }
        return {
            description: this.getParameterDescription(this.parameter),
            in: 'cookie',
            name: name || parameterName,
            parameterName: parameterName,
            required: !this.parameter.questionToken && !this.parameter.initializer,
            type: type
        };
    };
    ParameterGenerator.prototype.getBodyParameter = function () {
        var parameterName = this.parameter.name.text;
        var name = parameterName;
        var type = this.getValidatedType(this.parameter);
        if (!this.supportsBodyParameters(this.method)) {
            throw new Error("Body can't support " + this.method + " method");
        }
        if (typeof this.decoratorHandler !== 'undefined' && typeof this.decorator !== 'undefined') {
            var argument = this.decoratorHandler.getDecoratorPropertyValueAsItem(this.decorator, this.decoratorHandler.getPropertyByType(this.decorator.text));
            if (typeof argument === 'string') {
                name = argument;
            }
        }
        return {
            description: this.getParameterDescription(this.parameter),
            in: 'body',
            name: name || parameterName,
            parameterName: parameterName,
            required: !this.parameter.questionToken && !this.parameter.initializer,
            type: type
        };
    };
    ParameterGenerator.prototype.getHeaderParameter = function () {
        var parameterName = this.parameter.name.text;
        var name = parameterName;
        var type = this.getValidatedType(this.parameter);
        if (!this.supportPathDataType(type)) {
            throw new InvalidParameterException("Parameter '" + parameterName + "' can't be passed as a header parameter in '" + this.getCurrentLocation() + "'.");
        }
        if (typeof this.decoratorHandler !== 'undefined' && typeof this.decorator !== 'undefined') {
            var argument = this.decoratorHandler.getDecoratorPropertyValueAsItem(this.decorator, this.decoratorHandler.getPropertyByType(this.decorator.text));
            if (typeof argument === 'string') {
                name = argument;
            }
        }
        return {
            description: this.getParameterDescription(this.parameter),
            in: 'header',
            name: name || parameterName,
            parameterName: parameterName,
            required: !this.parameter.questionToken && !this.parameter.initializer,
            type: type
        };
    };
    ParameterGenerator.prototype.getQueryParameter = function () {
        var parameterName = this.parameter.name.text;
        var type = this.getValidatedType(this.parameter);
        if (!this.supportQueryDataType(type)) {
            /*
            const arrayType = getCommonPrimitiveAndArrayUnionType(parameter.type);
            if (arrayType && this.supportQueryDataType(arrayType)) {
                type = arrayType;
            } else {
                throw new InvalidParameterException(`Parameter '${parameterName}' can't be passed as a query parameter in '${this.getCurrentLocation()}'.`);
            }
             */
            // throw new InvalidParameterException(`Parameter '${parameterName}' can't be passed as a query parameter in '${this.getCurrentLocation()}'.`);
        }
        var name = parameterName;
        var options = {};
        if (typeof this.decoratorHandler !== 'undefined' && typeof this.decorator !== 'undefined') {
            var properties = this.decoratorHandler.getPropertiesByTypes(this.decorator.text, ['SIMPLE', 'OPTIONS']);
            // tslint:disable-next-line:forin
            for (var key in properties) {
                if (!properties.hasOwnProperty(key)) {
                    continue;
                }
                switch (key) {
                    case 'SIMPLE':
                        var propertyName = this.decoratorHandler.getDecoratorPropertyValueAsItem(this.decorator, properties[key]);
                        if (typeof propertyName !== 'undefined') {
                            name = propertyName;
                        }
                        break;
                    case 'OPTIONS':
                        var propertyOptions = this.decoratorHandler.getDecoratorPropertyValueAsItem(this.decorator, properties[key]);
                        if (typeof propertyOptions !== 'undefined') {
                            options = propertyOptions;
                        }
                        break;
                }
            }
        }
        return {
            allowEmptyValue: options.allowEmptyValue,
            collectionFormat: options.collectionFormat,
            default: utils_2.getInitializerValue(this.parameter.initializer, this.current.typeChecker, type),
            description: this.getParameterDescription(this.parameter),
            in: 'query',
            maxItems: options.maxItems,
            minItems: options.minItems,
            name: name,
            parameterName: parameterName,
            required: !this.parameter.questionToken && !this.parameter.initializer,
            type: type
        };
    };
    ParameterGenerator.prototype.getPathParameter = function () {
        var parameterName = this.parameter.name.text;
        var pathName = parameterName;
        var type = this.getValidatedType(this.parameter);
        if (typeof this.decoratorHandler !== 'undefined' && typeof this.decorator !== 'undefined') {
            var argument = this.decoratorHandler.getDecoratorPropertyValueAsItem(this.decorator, this.decoratorHandler.getPropertyByType(this.decorator.text));
            if (typeof argument === 'string') {
                pathName = argument;
            }
        }
        if (!this.supportPathDataType(type)) {
            throw new InvalidParameterException("Parameter '" + parameterName + ":" + type + "' can't be passed as a path parameter in '" + this.getCurrentLocation() + "'.");
        }
        if ((!this.path.includes("{" + pathName + "}")) && (!this.path.includes(":" + pathName))) {
            throw new Error("Parameter '" + parameterName + "' can't match in path: '" + this.path + "'");
        }
        return {
            description: this.getParameterDescription(this.parameter),
            in: 'path',
            name: pathName,
            parameterName: parameterName,
            required: true,
            type: type
        };
    };
    ParameterGenerator.prototype.getParameterDescription = function (node) {
        var symbol = this.current.typeChecker.getSymbolAtLocation(node.name);
        if (symbol) {
            var comments = symbol.getDocumentationComment(this.current.typeChecker);
            if (comments.length) {
                return ts.displayPartsToString(comments);
            }
        }
        return '';
    };
    ParameterGenerator.prototype.supportsBodyParameters = function (method) {
        return ['delete', 'post', 'put', 'patch', 'get'].some(function (m) { return m === method; });
    };
    ParameterGenerator.prototype.supportPathDataType = function (parameterType) {
        return ['string', 'integer', 'long', 'float', 'double', 'date', 'datetime', 'buffer', 'boolean', 'enum'].find(function (t) { return t === parameterType.typeName; });
    };
    ParameterGenerator.prototype.supportQueryDataType = function (parameterType) {
        // Copied from supportPathDataType and added 'array'. Not sure if all options apply to queries, but kept to avoid breaking change.
        return ['string', 'integer', 'long', 'float', 'double', 'date',
            'datetime', 'buffer', 'boolean', 'enum', 'array', 'object'].find(function (t) { return t === parameterType.typeName; });
    };
    ParameterGenerator.prototype.getValidatedType = function (parameter) {
        var typeNode = parameter.type;
        if (!typeNode) {
            var type = this.current.typeChecker.getTypeAtLocation(parameter);
            typeNode = this.current.typeChecker.typeToTypeNode(type, undefined, ts.NodeBuilderFlags.NoTruncation);
        }
        return new resolver_1.TypeNodeResolver(typeNode, this.current, parameter).resolve();
    };
    return ParameterGenerator;
}());
exports.ParameterGenerator = ParameterGenerator;
var InvalidParameterException = /** @class */ (function (_super) {
    __extends(InvalidParameterException, _super);
    function InvalidParameterException() {
        return _super !== null && _super.apply(this, arguments) || this;
    }
    return InvalidParameterException;
}(Error));
//# sourceMappingURL=parameterGenerator.js.map