mongoose-to-swagger
Version:
Conversion library for transforming Mongoose schema objects into Swagger schema definitions.
201 lines • 6.96 kB
JavaScript
;
const bson_1 = require("bson");
const isString = value => typeof value === 'string';
const mapMongooseTypeToSwaggerType = (type) => {
if (!type) {
return null;
}
if (type === Number || (isString(type) && type.toLowerCase() === 'number')) {
return 'number';
}
if (type === String || (isString(type) && type.toLowerCase() === 'string')) {
return 'string';
}
if (type.schemaName === 'Mixed') {
return 'object';
}
if (type === 'ObjectId' || type === 'ObjectID') {
return 'string';
}
if (type === bson_1.ObjectId) {
return 'string';
}
if (type === Boolean || (isString(type) && type.toLowerCase() === 'boolean')) {
return 'boolean';
}
if (type === Map) {
return 'map';
}
if (type instanceof Function) {
if (type.name === 'ObjectId' || type.name === 'ObjectID') {
return 'string';
}
if (type.name === 'Date') {
return 'string';
}
if (type.name === 'Decimal128') {
return 'number';
}
return type.name.toLowerCase();
}
if (type.type != null) {
return mapMongooseTypeToSwaggerType(type.type);
}
if (type.instance) {
switch (type.instance) {
case 'Array':
case 'DocumentArray':
return 'array';
case 'ObjectId':
case 'ObjectID':
case 'SchemaDate':
return 'string';
case 'Mixed':
return 'object';
case 'String':
case 'SchemaString':
case 'SchemaBuffer':
case 'SchemaObjectId':
return 'string';
case 'SchemaArray':
return 'array';
case 'Boolean':
case 'SchemaBoolean':
return 'boolean';
case 'Number':
case 'Decimal128':
case 'SchemaNumber':
return 'number';
default:
}
}
if (Array.isArray(type)) {
return 'array';
}
if (type.$schemaType) {
return mapMongooseTypeToSwaggerType(type.$schemaType.tree);
}
if (type.getters && Array.isArray(type.getters) && type.path != null) {
return null;
}
return 'object';
};
const defaultSupportedMetaProps = [
'enum',
'required',
'description',
];
const mapSchemaTypeToFieldSchema = ({ key = null, value, props, omitFields, }) => {
const swaggerType = mapMongooseTypeToSwaggerType(value);
const meta = {};
for (const metaProp of props) {
if (value && value[metaProp] != null) {
meta[metaProp] = value[metaProp];
}
}
if (value === Date || value.type === Date) {
meta.format = 'date-time';
}
else if (swaggerType === 'array') {
const arraySchema = Array.isArray(value) ? value[0] : value.type[0];
const items = mapSchemaTypeToFieldSchema({ value: arraySchema || {}, props, omitFields });
meta.items = items;
}
else if (swaggerType === 'object') {
let fields = [];
if (value && value.constructor && value.constructor.name === 'Schema') {
fields = getFieldsFromMongooseSchema(value, { props, omitFields });
}
else {
const subSchema = value.type ? value.type : value;
if (subSchema.obj && Object.keys(subSchema.obj).length > 0) {
fields = getFieldsFromMongooseSchema({ tree: subSchema.tree ? subSchema.tree : subSchema }, { props, omitFields });
}
else if (subSchema.schemaName !== 'Mixed') {
fields = getFieldsFromMongooseSchema({ tree: subSchema.tree ? subSchema.tree : subSchema }, { props, omitFields });
}
}
const properties = {};
for (const field of fields.filter(f => f.type != null)) {
properties[field.field] = field;
delete field.field;
}
meta.properties = properties;
}
else if (swaggerType === 'map') {
const subSchema = mapSchemaTypeToFieldSchema({ value: value.of || {}, props, omitFields });
meta.type = 'object';
meta.additionalProperties = subSchema;
}
const result = Object.assign({ type: swaggerType }, meta);
if (key) {
result.field = key;
}
return result;
};
const getFieldsFromMongooseSchema = (schema, options) => {
const { props, omitFields, omitMongooseInternals = true } = options;
const omitted = new Set([...(omitMongooseInternals ? ['__v', 'id'] : []), ...omitFields || []]);
const tree = schema.tree;
const keys = Object.keys(schema.tree);
const fields = [];
for (const key of keys
.filter(x => !omitted.has(x))) {
const value = tree[key];
const field = mapSchemaTypeToFieldSchema({ key, value, props, omitFields });
const required = [];
if (field.type === 'object') {
const { field: propName } = field;
const fieldProperties = field.properties || field.additionalProperties;
for (const f of Object.values(fieldProperties)) {
if (f.required && propName != null) {
required.push(propName);
delete f.required;
}
}
}
if (field.type === 'array' && field.items.type === 'object') {
field.items.required = [];
for (const key in field.items.properties) {
const val = field.items.properties[key];
if (val.required) {
field.items.required.push(key);
delete val.required;
}
}
}
fields.push(field);
}
return fields;
};
function documentModel(Model, options = {}) {
let { props = [], omitFields = [], omitMongooseInternals = true, } = options;
props = [...defaultSupportedMetaProps, ...props];
const removeVirtual = (swaggerFieldSchema) => {
return swaggerFieldSchema.type != null;
};
const schema = Model.schema;
const fields = getFieldsFromMongooseSchema(schema, { props, omitFields, omitMongooseInternals });
const obj = {
title: Model.modelName,
required: [],
properties: {},
};
for (const field of fields.filter(removeVirtual)) {
const { field: fieldName } = field;
delete field.field;
obj.properties[fieldName] = field;
if (field.required && fieldName != null) {
obj.required.push(fieldName);
delete field.required;
}
}
if (!obj.required || !obj.required.length) {
delete obj.required;
}
return obj;
}
documentModel.adjustType = mapMongooseTypeToSwaggerType;
documentModel.getFieldsFromMongooseSchema = getFieldsFromMongooseSchema;
module.exports = documentModel;
//# sourceMappingURL=index.js.map