loopback-component-explorer/lib/model-helper.js

123 lines
4.1 KiB
JavaScript

'use strict';
/**
* Module dependencies.
*/
var schemaBuilder = require('./schema-builder');
var typeConverter = require('./type-converter');
var TypeRegistry = require('./type-registry');
/**
* Export the modelHelper singleton.
*/
var modelHelper = module.exports = {
/**
* Given a class (from remotes.classes()), generate a model definition.
* This is used to generate the schema at the top of many endpoints.
* @param {Class} modelClass Model class.
* @param {TypeRegistry} typeRegistry Registry of types and models.
* @return {Object} Associated model definition.
*/
registerModelDefinition: function(modelCtor, typeRegistry) {
var lbdef = modelCtor.definition;
if (!lbdef) {
// The model does not have any definition, it was most likely
// created as a placeholder for an unknown property type
return;
}
var name = lbdef.name;
if (typeRegistry.isDefined(name)) {
// The model is already included
return;
}
var swaggerDef = {
description: typeConverter.convertText(
lbdef.description || (lbdef.settings && lbdef.settings.description)),
properties: {},
required: []
};
var properties = lbdef.rawProperties || lbdef.properties;
// Iterate through each property in the model definition.
// Types may be defined as constructors (e.g. String, Date, etc.),
// or as strings; swaggerSchema.builFromLoopBackType() will take
// care of the conversion.
Object.keys(properties).forEach(function(key) {
var prop = properties[key];
// Hide hidden properties.
if (modelHelper.isHiddenProperty(lbdef, key))
return;
// Eke a type out of the constructors we were passed.
var schema = schemaBuilder.buildFromLoopBackType(prop, typeRegistry);
var desc = typeConverter.convertText(prop.description || prop.doc);
if (desc) schema.description = desc;
// Required props sit in a per-model array.
if (prop.required || (prop.id && !prop.generated)) {
swaggerDef.required.push(key);
}
// Assign the schema to the properties object.
swaggerDef.properties[key] = schema;
});
if (lbdef.settings) {
var strict = lbdef.settings.strict;
var additionalProperties = lbdef.settings.additionalProperties;
var notAllowAdditionalProperties = strict || (additionalProperties !== true);
if (notAllowAdditionalProperties){
swaggerDef.additionalProperties = false;
}
}
if (!swaggerDef.required.length) {
// "required" must have at least one item when present
delete swaggerDef.required;
}
typeRegistry.register(name, swaggerDef);
// Add models from settings
if (lbdef.settings && lbdef.settings.models) {
for (var m in lbdef.settings.models) {
var model = modelCtor[m];
if (typeof model !== 'function' || !model.modelName) continue;
modelHelper.registerModelDefinition(model, typeRegistry);
// TODO it shouldn't be necessary to reference the model here,
// let accepts/returns/property reference it instead
typeRegistry.reference(model.modelName);
}
}
// Generate model definitions for related models
for (var r in modelCtor.relations) {
var rel = modelCtor.relations[r];
if (rel.modelTo) {
modelHelper.registerModelDefinition(rel.modelTo, typeRegistry);
// TODO it shouldn't be necessary to reference the model here,
// let accepts/returns/property reference it instead
typeRegistry.reference(rel.modelTo.modelName);
}
if (rel.modelThrough) {
modelHelper.registerModelDefinition(rel.modelThrough, typeRegistry);
// TODO it shouldn't be necessary to reference the model here,
// let accepts/returns/property reference it instead
typeRegistry.reference(rel.modelThrough.modelName);
}
}
},
isHiddenProperty: function(definition, propName) {
return definition.settings &&
Array.isArray(definition.settings.hidden) &&
definition.settings.hidden.indexOf(propName) !== -1;
},
};