UNPKG

@techntools/sequelize-to-openapi

Version:
188 lines 8.77 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const openapi_1 = __importDefault(require("./strategies/openapi")); const type_mapper_1 = __importDefault(require("./type-mapper")); const attribute_validator_1 = __importDefault(require("./attribute-validator")); const type_checks_1 = require("./utils/type-checks"); const util_1 = require("./utils/util"); const _strategy = new WeakMap(); const _modelOptions = new WeakMap(); const _model = new WeakMap(); class SchemaManager { generate(model, strategy, options) { if (model === undefined) throw new Error('Missing method argument `model`'); if (strategy === undefined) throw new Error('Mising method argument `strategy`'); this.verifyModelOptions(Object.assign({}, { include: [], exclude: [], associations: false, includeAssociations: [], excludeAssociations: [], }, options)); this.verifyModel(model); this.verifyStrategy(strategy); const attributes = this.getAttributes(); const result = this.getModelContainer(); const requiredAttributes = []; for (const attributeName of Object.keys(attributes)) { result.properties[attributeName] = this.getAttributeContainer(attributeName, attributes[attributeName]); if (this.isRequiredProperty(attributes[attributeName])) { requiredAttributes.push(attributeName); } } if (requiredAttributes.length > 0) result.required = requiredAttributes; if (_modelOptions.get(this).associations === false) { return result; } for (const association of Object.keys(model.associations)) { Object.assign(result.properties, this.getModelPropertyForAssociation(association, model.associations[association])); } return result; } verifyStrategy(strategy) { if (!(strategy instanceof openapi_1.default)) throw new TypeError("Strategy must implement the 'OpenApiStrategy'"); _strategy.set(this, strategy); } verifyModelOptions(options) { (0, type_checks_1.checkTypeOptional)('include', options.include, 'array'); (0, type_checks_1.checkTypeOptional)('exclude', options.exclude, 'array'); if (options.include.length > 0 && options.exclude.length > 0) { throw new Error("Model options 'include' and 'exclude' are mutually exclusive"); } (0, type_checks_1.checkTypeRequired)('associations', options.associations, 'boolean'); (0, type_checks_1.checkTypeRequired)('includeAssociations', options.includeAssociations, 'array'); (0, type_checks_1.checkTypeRequired)('excludeAssociations', options.excludeAssociations, 'array'); if (options.includeAssociations.length > 0 && options.excludeAssociations.length > 0) { throw new Error("Model options 'includeAssociations' and 'excludeAssociations' are mutually exclusive"); } _modelOptions.set(this, options); } verifyModel(model) { if ('getAttributes' in model) { _model.set(this, model); return; } throw new TypeError('Provided model does not match expected format. Are you sure this is a Sequelize v6+ model ?'); } getAttributes() { const model = _model.get(this); const attributes = model.getAttributes.bind(model)(); if (_modelOptions.get(this).include.length > 0) return (0, util_1.pick)(attributes, _modelOptions.get(this).include); if (_modelOptions.get(this).exclude.length > 0) return (0, util_1.omit)(attributes, _modelOptions.get(this).exclude); return attributes; } getModelContainer() { const result = { type: 'object', properties: {}, additionalProperties: _strategy.get(this).additionalProperties }; return result; } getAttributeContainer(attributeName, attributeProperties) { const typeMapper = new type_mapper_1.default; const attributeValidator = new attribute_validator_1.default; const result = {}; Object.assign(result, typeMapper.map(attributeName, attributeProperties, _strategy.get(this))); Object.assign(result, attributeValidator.map(attributeProperties, _strategy.get(this))); Object.assign(result, this.getAttributePropertyTypeOverride(attributeName, attributeProperties)); Object.assign(result, this.getAttributePropertyDescription(attributeName, attributeProperties)); Object.assign(result, this.getPropertyReadOrWriteOnly(attributeName, attributeProperties)); Object.assign(result, this.getAttributeExamples(attributeProperties)); return result; } isRequiredProperty(attributeProperties) { if (attributeProperties.allowNull === false) return true; if (attributeProperties.defaultValue !== undefined) return true; return false; } getAttributeExamples(attributeProperties) { const examples = this.getCustomPropertyValue('examples', attributeProperties); if (examples === null) return null; if (!Array.isArray(examples)) { throw new TypeError("The 'examples' property MUST be an array"); } return _strategy.get(this).getPropertyExamples(examples); } getPropertyReadOrWriteOnly(attributeName, attributeProperties) { const readOnly = this.getCustomPropertyValue('readOnly', attributeProperties); const writeOnly = this.getCustomPropertyValue('writeOnly', attributeProperties); if (!(readOnly || writeOnly)) return null; if (readOnly && writeOnly) { throw new TypeError(`Custom properties 'readOnly' and 'writeOnly' for sequelize attribute '${attributeName}' are mutually exclusive`); } if (readOnly) { (0, type_checks_1.checkTypeRequired)('readOnly', readOnly, 'boolean'); return { readOnly: true }; } if (writeOnly) { (0, type_checks_1.checkTypeRequired)('writeOnly', writeOnly, 'boolean'); return { writeOnly: true }; } } getAttributePropertyDescription(attributeName, attributeProperties) { const description = this.getCustomPropertyValue('description', attributeProperties); if (!description) return null; (0, type_checks_1.checkTypeRequired)(attributeName, description, 'string'); return { description }; } getCustomPropertyValue(propertyName, attributeProperties) { const jsonSchema = attributeProperties['jsonSchema']; if (!jsonSchema) return null; if (!jsonSchema[propertyName]) return null; return jsonSchema[propertyName]; } getAttributePropertyTypeOverride(attributeName, attributeProperties) { const schema = this.getCustomPropertyValue('schema', attributeProperties); if (!schema) return null; if (typeof schema === 'object' && typeof schema.type === 'string') return schema; throw new TypeError(`Custom property 'schema' for sequelize attribute '${attributeName}' should be an object with a 'type' key`); } getModelPropertyForAssociation(associationName, association) { const options = _modelOptions.get(this); if (options.excludeAssociations.length > 0 && options.excludeAssociations.includes(associationName)) { return null; } if (options.includeAssociations.length > 0 && !options.includeAssociations.includes(associationName)) { return null; } switch (association.associationType) { case 'HasOne': return _strategy.get(this).getPropertyForHasOneAssociation(associationName, association); case 'BelongsTo': return _strategy.get(this).getPropertyForBelongsToAssociation(associationName, association); case 'HasMany': return _strategy.get(this).getPropertyForHasManyAssociation(associationName, association); case 'BelongsToMany': return _strategy.get(this).getPropertyForBelongsToManyAssociation(associationName, association); default: return null; } } } exports.default = SchemaManager; //# sourceMappingURL=schema-manager.js.map