UNPKG

@nestjs/swagger

Version:

Nest - modern, fast, powerful node.js web framework (@swagger)

153 lines (152 loc) 7.08 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const shared_utils_1 = require("@nestjs/common/utils/shared.utils"); const lodash_1 = require("lodash"); const constants_1 = require("../constants"); const helpers_1 = require("../decorators/helpers"); const api_extra_models_explorer_1 = require("../explorers/api-extra-models.explorer"); const utils_1 = require("../utils"); const is_body_parameter_util_1 = require("../utils/is-body-parameter.util"); const is_built_in_type_util_1 = require("../utils/is-built-in-type.util"); class SchemaObjectFactory { constructor(modelPropertiesAccessor, swaggerTypesMapper) { this.modelPropertiesAccessor = modelPropertiesAccessor; this.swaggerTypesMapper = swaggerTypesMapper; } createFromModel(parameters, schemas) { return parameters.map(param => { if (!is_body_parameter_util_1.isBodyParameter(param)) { return param; } if (this.isPrimitiveType(param.type)) { return param; } if (this.isArrayCtor(param.type)) { return this.mapArrayCtorParam(param); } const modelName = this.exploreModelSchema(param.type, schemas); const name = param.name || modelName; const schema = Object.assign(Object.assign({}, (param.schema || {})), { $ref: utils_1.getSchemaPath(modelName) }); const isArray = param.isArray; param = lodash_1.omit(param, 'isArray'); if (isArray) { return Object.assign(Object.assign({}, param), { name, schema: { type: 'array', items: schema } }); } return Object.assign(Object.assign({}, param), { name, schema }); }); } exploreModelSchema(type, schemas, schemaRefsStack = []) { if (this.isLazyTypeFunc(type)) { type = type(); } const { prototype } = type; if (!prototype) { return ''; } const extraModels = api_extra_models_explorer_1.exploreGlobalApiExtraModelsMetadata(type); extraModels.forEach(item => this.exploreModelSchema(item, schemas, schemaRefsStack)); const modelProperties = this.modelPropertiesAccessor.getModelProperties(prototype); const propertiesWithType = modelProperties.map(key => { const property = this.mergePropertyWithMetadata(key, prototype, schemas, schemaRefsStack); const schemaCombinators = ['oneOf', 'anyOf', 'allOf']; if (schemaCombinators.some(key => key in property)) { delete property.type; } return property; }); const typeDefinition = { type: 'object', properties: lodash_1.mapValues(lodash_1.keyBy(propertiesWithType, 'name'), property => lodash_1.omit(property, ['name', 'isArray', 'required'])) }; const typeDefinitionRequiredFields = propertiesWithType .filter(property => property.required != false) .map(property => property.name); if (typeDefinitionRequiredFields.length > 0) { typeDefinition['required'] = typeDefinitionRequiredFields; } schemas.push({ [type.name]: typeDefinition }); return type.name; } mergePropertyWithMetadata(key, prototype, schemas, schemaRefsStack = []) { const metadata = Reflect.getMetadata(constants_1.DECORATORS.API_MODEL_PROPERTIES, prototype, key) || {}; if (this.isLazyTypeFunc(metadata.type)) { metadata.type = metadata.type(); [metadata.type, metadata.isArray] = helpers_1.getTypeIsArrayTuple(metadata.type, metadata.isArray); } if (lodash_1.isString(metadata.type)) { return Object.assign(Object.assign({}, metadata), { name: metadata.name || key }); } if (!is_built_in_type_util_1.isBuiltInType(metadata.type)) { return this.createNotBuiltInTypeReference(key, metadata, schemas, schemaRefsStack); } const typeName = this.getTypeName(metadata.type); const itemType = this.swaggerTypesMapper.mapTypeToOpenAPIType(typeName); if (metadata.isArray) { return this.transformToArraySchemaProperty(metadata, key, { type: itemType }); } else if (itemType === 'array') { const defaultOnArray = 'string'; return this.transformToArraySchemaProperty(metadata, key, { type: defaultOnArray }); } return Object.assign(Object.assign({}, metadata), { name: metadata.name || key, type: itemType }); } createNotBuiltInTypeReference(key, metadata, schemas, schemaRefsStack) { if (shared_utils_1.isUndefined(metadata.type)) { throw new Error(`A circular dependency has been detected (property key: "${key}"). Please, make sure that each side of a bidirectional relationships are using lazy resolvers ("type: () => ClassType").`); } let schemaObjectName = metadata.type.name; if (!lodash_1.includes(schemaRefsStack, schemaObjectName)) { schemaRefsStack.push(schemaObjectName); schemaObjectName = this.exploreModelSchema(metadata.type, schemas, schemaRefsStack); } const $ref = utils_1.getSchemaPath(schemaObjectName); if (metadata.isArray) { return this.transformToArraySchemaProperty(metadata, key, { $ref }); } const keysToRemove = ['type', 'isArray']; const validMetadataObject = lodash_1.omit(metadata, keysToRemove); return Object.assign(Object.assign({}, validMetadataObject), { name: metadata.name || key, $ref }); } transformToArraySchemaProperty(metadata, key, type) { const keysToRemove = ['type', 'enum']; const schemaHost = Object.assign(Object.assign({}, lodash_1.omit(metadata, keysToRemove)), { name: metadata.name || key, type: 'array', items: lodash_1.isString(type) ? { type } : Object.assign({}, type) }); schemaHost.items = lodash_1.omitBy(schemaHost.items, shared_utils_1.isUndefined); return schemaHost; } mapArrayCtorParam(param) { return Object.assign(Object.assign({}, lodash_1.omit(param, 'type')), { schema: { type: 'array', items: { type: 'string' } } }); } isArrayCtor(type) { return type === Array; } isPrimitiveType(type) { return (lodash_1.isFunction(type) && [String, Boolean, Number].some(item => item === type)); } isLazyTypeFunc(type) { return lodash_1.isFunction(type) && type.name == 'type'; } getTypeName(type) { return type && lodash_1.isFunction(type) ? type.name : type; } } exports.SchemaObjectFactory = SchemaObjectFactory;