diff --git a/lib/class-helper.js b/lib/class-helper.js index ff788d9..de3a0a7 100644 --- a/lib/class-helper.js +++ b/lib/class-helper.js @@ -28,7 +28,7 @@ var classHelper = module.exports = { basePath: opts.basePath, resourcePath: urlJoin('/', opts.resourcePath), apis: [], - models: modelHelper.generateModelDefinition(aClass) + models: modelHelper.generateModelDefinition(aClass.ctor, {}) }; }, /** diff --git a/lib/model-helper.js b/lib/model-helper.js index d165844..b5c5724 100644 --- a/lib/model-helper.js +++ b/lib/model-helper.js @@ -13,13 +13,18 @@ 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} class Remote class. - * @return {Object} Associated model definition. + * @param {Class} modelClass Model class. + * @param {Object} definitions Model definitions + * @return {Object} Associated model definition. */ - generateModelDefinition: function generateModelDefinition(aClass) { - var def = aClass.ctor.definition; + generateModelDefinition: function generateModelDefinition(modelClass, definitions) { + var def = modelClass.definition; var name = def.name; - + var out = definitions || {}; + if (out[name]) { + // The model is already included + return out; + } var required = []; // Don't modify original properties. var properties = _cloneDeep(def.properties); @@ -47,12 +52,20 @@ var modelHelper = module.exports = { properties[key] = prop; }); - var out = {}; out[name] = { id: name, properties: properties, required: required }; + + // Generate model definitions for related models + for (var r in modelClass.relations) { + var rel = modelClass.relations[r]; + generateModelDefinition(rel.modelTo, out); + if (rel.modelThrough) { + generateModelDefinition(rel.modelThrough, out); + } + } return out; }, diff --git a/test/model-helper.test.js b/test/model-helper.test.js index d639390..1a88b29 100644 --- a/test/model-helper.test.js +++ b/test/model-helper.test.js @@ -6,7 +6,7 @@ var expect = require('chai').expect; describe('model-helper', function() { describe('properly converts LDL definitions to swagger types', function() { it('converts constructor types', function() { - var def = getDefinition({ + var def = buildSwaggerModels({ str: String, // 'string' num: Number, // {type: 'number', format: 'double'} date: Date, // {type: 'string', format: 'date'} @@ -21,7 +21,7 @@ describe('model-helper', function() { expect(props.buf).to.eql({ type: 'string', format: 'byte' }); }); it('converts string types', function() { - var def = getDefinition({ + var def = buildSwaggerModels({ str: 'string', // 'string' num: 'number', // {type: 'number', format: 'double'} date: 'date', // {type: 'string', format: 'date'} @@ -41,7 +41,7 @@ describe('model-helper', function() { // ["string"], // [{type: String, ...}] it('converts [Constructor] type', function() { - var def = getDefinition({ + var def = buildSwaggerModels({ array: [String] }); var props = def.properties; @@ -51,7 +51,7 @@ describe('model-helper', function() { }); it('converts ["string"] type', function() { - var def = getDefinition({ + var def = buildSwaggerModels({ array: ['string'] }); var props = def.properties; @@ -61,7 +61,7 @@ describe('model-helper', function() { }); it('converts [{type: "string", length: 64}] type', function() { - var def = getDefinition({ + var def = buildSwaggerModels({ array: [{type: 'string', length: 64}] }); var props = def.properties; @@ -72,7 +72,7 @@ describe('model-helper', function() { }); it('converts [{type: "date"}] type (with `format`)', function() { - var def = getDefinition({ + var def = buildSwaggerModels({ array: [{type: 'date'}] }); var props = def.properties; @@ -82,7 +82,7 @@ describe('model-helper', function() { }); it('converts [] type', function() { - var def = getDefinition({ + var def = buildSwaggerModels({ array: [] }); var prop = def.properties.array; @@ -90,7 +90,7 @@ describe('model-helper', function() { }); it('converts [undefined] type', function() { - var def = getDefinition({ + var def = buildSwaggerModels({ // This value is somehow provided by loopback-boot called from // loopback-workspace. array: [undefined] @@ -100,7 +100,7 @@ describe('model-helper', function() { }); it('converts "array" type', function() { - var def = getDefinition({ + var def = buildSwaggerModels({ array: 'array' }); var prop = def.properties.array; @@ -108,10 +108,19 @@ describe('model-helper', function() { }); }); }); + describe('related models', function() { + it('should include related models', function() { + var defs = buildSwaggerModelsWithRelations({ + str: String // 'string' + }); + expect(defs).has.property('testModel'); + expect(defs).has.property('relatedModel'); + }); + }); }); // Simulates the format of a remoting class. -function getDefinition(model) { +function buildSwaggerModels(model) { Object.keys(model).forEach(function(name) { model[name] = {type: model[name]}; }); @@ -123,5 +132,36 @@ function getDefinition(model) { } } }; - return modelHelper.generateModelDefinition(aClass).testModel; + return modelHelper.generateModelDefinition(aClass.ctor, {}).testModel; } + +function buildSwaggerModelsWithRelations(model) { + Object.keys(model).forEach(function(name) { + model[name] = {type: model[name]}; + }); + // Mock up the related model + var relatedModel = { + definition: { + name: 'relatedModel', + properties: { + fk: String + } + } + }; + var aClass = { + ctor: { + definition: { + name: 'testModel', + properties: model + }, + // Mock up relations + relations: { + other: { + modelTo: relatedModel + } + } + } + }; + return modelHelper.generateModelDefinition(aClass.ctor, {}); +} +