LDL to Swagger fixes & extensions.
- Supports all three LDL array type specifications. - Added model-helper tests. - translate-keys no longer modifies the incoming object. - Non-primitive types are now correctly converted into a primitive with a `format` attribute. - `id` properties are not assumed to be required if they are generated.
This commit is contained in:
parent
70dddef296
commit
77f01670de
|
@ -25,26 +25,26 @@ var modelHelper = module.exports = {
|
|||
var properties = _cloneDeep(def.properties);
|
||||
|
||||
// Iterate through each property in the model definition.
|
||||
// Types are defined as constructors (e.g. String, Date, etc.)
|
||||
// so we convert them to strings.
|
||||
Object.keys(def.properties).forEach(function(key) {
|
||||
// Types may be defined as constructors (e.g. String, Date, etc.),
|
||||
// or as strings; getPropType() will take care of the conversion.
|
||||
// See more on types:
|
||||
// https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#431-primitives
|
||||
Object.keys(properties).forEach(function(key) {
|
||||
var prop = properties[key];
|
||||
|
||||
// Eke a type out of the constructors we were passed.
|
||||
prop.type = getPropType(prop.type);
|
||||
if (prop.type === 'array') {
|
||||
prop.items = {
|
||||
type: getPropType(prop.type[0])
|
||||
};
|
||||
}
|
||||
prop = modelHelper.LDLPropToSwaggerDataType(prop);
|
||||
|
||||
// Required props sit in a per-model array.
|
||||
if (prop.required || prop.id) {
|
||||
if (prop.required || (prop.id && !prop.generated)) {
|
||||
required.push(key);
|
||||
}
|
||||
|
||||
// Change mismatched keys.
|
||||
prop = translateKeys(prop);
|
||||
|
||||
// Assign this back to the properties object.
|
||||
properties[key] = prop;
|
||||
});
|
||||
|
||||
var out = {};
|
||||
|
@ -54,21 +54,48 @@ var modelHelper = module.exports = {
|
|||
required: required
|
||||
};
|
||||
return out;
|
||||
},
|
||||
|
||||
/**
|
||||
* Given a propType (which may be a function, string, or array),
|
||||
* get a string type.
|
||||
* @param {*} propType Prop type description.
|
||||
* @return {String} Prop type string.
|
||||
*/
|
||||
getPropType: function getPropType(propType) {
|
||||
if (typeof propType === 'function') {
|
||||
propType = propType.name.toLowerCase();
|
||||
} else if(Array.isArray(propType)) {
|
||||
propType = 'array';
|
||||
}
|
||||
return propType;
|
||||
},
|
||||
|
||||
// Converts a prop defined with the LDL spec to one conforming to the
|
||||
// Swagger spec.
|
||||
// https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#431-primitives
|
||||
LDLPropToSwaggerDataType: function LDLPropToSwaggerDataType(prop) {
|
||||
var out = _cloneDeep(prop);
|
||||
out.type = modelHelper.getPropType(out.type);
|
||||
|
||||
if (out.type === 'array') {
|
||||
var arrayProp = prop.type[0];
|
||||
if (!arrayProp.type) arrayProp = {type: arrayProp};
|
||||
out.items = modelHelper.LDLPropToSwaggerDataType(arrayProp);
|
||||
}
|
||||
|
||||
if (out.type === 'date') {
|
||||
out.type = 'string';
|
||||
out.format = 'date';
|
||||
} else if (out.type === 'buffer') {
|
||||
out.type = 'string';
|
||||
out.format = 'byte';
|
||||
} else if (out.type === 'number') {
|
||||
out.format = 'double'; // Since all JS numbers are doubles
|
||||
}
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Given a propType (which may be a function, string, or array),
|
||||
* get a string type.
|
||||
* @param {*} propType Prop type description.
|
||||
* @return {String} Prop type string.
|
||||
*/
|
||||
function getPropType(propType) {
|
||||
if (typeof propType === "function") {
|
||||
propType = propType.name.toLowerCase();
|
||||
} else if(Array.isArray(propType)) {
|
||||
propType = 'array';
|
||||
}
|
||||
return propType;
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var _cloneDeep = require('lodash.clonedeep');
|
||||
|
||||
// Keys that are different between LDL and Swagger
|
||||
var KEY_TRANSLATIONS = {
|
||||
// LDL : Swagger
|
||||
|
@ -15,11 +17,12 @@ var KEY_TRANSLATIONS = {
|
|||
|
||||
/**
|
||||
* Correct key mismatches between LDL & Swagger.
|
||||
* Will modify original object.
|
||||
* Does not modify original object.
|
||||
* @param {Object} object Object on which to change keys.
|
||||
* @return {Object} Translated object.
|
||||
*/
|
||||
module.exports = function translateKeys(object) {
|
||||
object = _cloneDeep(object);
|
||||
Object.keys(KEY_TRANSLATIONS).forEach(function(LDLKey){
|
||||
var val = object[LDLKey];
|
||||
if (val) {
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
'use strict';
|
||||
|
||||
var modelHelper = require('../lib/model-helper');
|
||||
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({
|
||||
str: String, // 'string'
|
||||
num: Number, // {type: 'number', format: 'double'}
|
||||
date: Date, // {type: 'string', format: 'date'}
|
||||
bool: Boolean, // 'boolean'
|
||||
buf: Buffer // {type: 'string', format: 'byte'}
|
||||
});
|
||||
var props = def.properties;
|
||||
expect(props.str.type).to.equal('string');
|
||||
expect(props.num.type).to.equal('number');
|
||||
expect(props.num.format).to.equal('double');
|
||||
expect(props.date.type).to.equal('string');
|
||||
expect(props.bool.type).to.equal('boolean');
|
||||
expect(props.buf.type).to.equal('string');
|
||||
});
|
||||
it('converts string types', function() {
|
||||
var def = getDefinition({
|
||||
str: 'string', // 'string'
|
||||
num: 'number', // {type: 'number', format: 'double'}
|
||||
date: 'date', // {type: 'string', format: 'date'}
|
||||
bool: 'boolean', // 'boolean'
|
||||
buf: 'buffer' // {type: 'string', format: 'byte'}
|
||||
});
|
||||
var props = def.properties;
|
||||
expect(props.str.type).to.equal('string');
|
||||
expect(props.num.type).to.equal('number');
|
||||
expect(props.num.format).to.equal('double');
|
||||
expect(props.date.type).to.equal('string');
|
||||
expect(props.bool.type).to.equal('boolean');
|
||||
expect(props.buf.type).to.equal('string');
|
||||
});
|
||||
describe('array definitions', function() {
|
||||
// There are three types we want to checK:
|
||||
// [String]
|
||||
// ["string"],
|
||||
// [{type: String, ...}]
|
||||
it('converts [Constructor] type', function() {
|
||||
var def = getDefinition({
|
||||
array: [String]
|
||||
});
|
||||
var props = def.properties;
|
||||
expect(props.array.type).to.equal('array');
|
||||
expect(props.array.items.type).to.equal('string');
|
||||
});
|
||||
|
||||
it('converts ["string"] type', function() {
|
||||
var def = getDefinition({
|
||||
array: ['string']
|
||||
});
|
||||
var props = def.properties;
|
||||
expect(props.array.type).to.equal('array');
|
||||
expect(props.array.items.type).to.equal('string');
|
||||
});
|
||||
|
||||
it('converts [{type: "string", length: 64}] type', function() {
|
||||
var def = getDefinition({
|
||||
array: [{type: 'string', length: 64}]
|
||||
});
|
||||
var props = def.properties;
|
||||
expect(props.array.type).to.equal('array');
|
||||
expect(props.array.items.type).to.equal('string');
|
||||
});
|
||||
|
||||
it('converts [{type: "date"}] type (with `format`)', function() {
|
||||
var def = getDefinition({
|
||||
array: [{type: 'date'}]
|
||||
});
|
||||
var props = def.properties;
|
||||
expect(props.array.type).to.equal('array');
|
||||
expect(props.array.items.type).to.equal('string');
|
||||
expect(props.array.items.format).to.equal('date');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Simulates the format of a rmeoting class.
|
||||
function getDefinition(model) {
|
||||
Object.keys(model).forEach(function(name) {
|
||||
model[name] = {type: model[name]};
|
||||
});
|
||||
var aClass = {
|
||||
ctor: {
|
||||
definition: {
|
||||
name: 'testModel',
|
||||
properties: model
|
||||
}
|
||||
}
|
||||
};
|
||||
return modelHelper.generateModelDefinition(aClass).testModel;
|
||||
}
|
|
@ -99,7 +99,7 @@ describe('swagger definition', function() {
|
|||
if (err) return done(err);
|
||||
var data = res.body.models.product;
|
||||
expect(data.id).to.equal('product');
|
||||
expect(data.required.sort()).to.eql(['id', 'aNum', 'foo'].sort());
|
||||
expect(data.required.sort()).to.eql(['aNum', 'foo'].sort());
|
||||
expect(data.properties.foo.type).to.equal('string');
|
||||
expect(data.properties.bar.type).to.equal('string');
|
||||
expect(data.properties.aNum.type).to.equal('number');
|
||||
|
|
Loading…
Reference in New Issue