route-helper: add `responseMessages`
Add a default "success" response message, the status code is 200 or 204 depending on whether the method returns any data. Append any error messages as specified in the `errors` property of method's remoting metadata. Move the description of operation's return type to the "success" response message. Include error message models in the API models.
This commit is contained in:
parent
9a6bd35df7
commit
d05dcb71df
|
@ -123,21 +123,40 @@ var routeHelper = module.exports = {
|
||||||
|
|
||||||
debug('route %j', route);
|
debug('route %j', route);
|
||||||
|
|
||||||
|
var responseDoc = modelHelper.LDLPropToSwaggerDataType(returns);
|
||||||
|
|
||||||
|
// Note: Swagger Spec does not provide a way how to specify
|
||||||
|
// that the responseModel is "array of X". However,
|
||||||
|
// Swagger UI converts Arrays to the item types anyways,
|
||||||
|
// therefore it should be ok to do the same here.
|
||||||
|
var responseModel = responseDoc.type === 'array' ?
|
||||||
|
responseDoc.items.type : responseDoc.type;
|
||||||
|
|
||||||
|
var responseMessages = [{
|
||||||
|
code: route.returns && route.returns.length ? 200 : 204,
|
||||||
|
message: 'Request was successful',
|
||||||
|
responseModel: responseModel
|
||||||
|
}];
|
||||||
|
|
||||||
|
if (route.errors) {
|
||||||
|
responseMessages.push.apply(responseMessages, route.errors);
|
||||||
|
}
|
||||||
|
|
||||||
var apiDoc = {
|
var apiDoc = {
|
||||||
path: routeHelper.convertPathFragments(route.path),
|
path: routeHelper.convertPathFragments(route.path),
|
||||||
// Create the operation doc. Use `extendWithType` to add the necessary
|
// Create the operation doc.
|
||||||
// `items` and `format` fields.
|
// Note that we are not calling `extendWithType`, as the response type
|
||||||
operations: [routeHelper.extendWithType({
|
// is specified in the first response message.
|
||||||
|
operations: [{
|
||||||
method: routeHelper.convertVerb(route.verb),
|
method: routeHelper.convertVerb(route.verb),
|
||||||
// [rfeng] Swagger UI doesn't escape '.' for jQuery selector
|
// [rfeng] Swagger UI doesn't escape '.' for jQuery selector
|
||||||
nickname: route.method.replace(/\./g, '_'),
|
nickname: route.method.replace(/\./g, '_'),
|
||||||
parameters: accepts,
|
parameters: accepts,
|
||||||
// TODO(schoon) - We don't have descriptions for this yet.
|
responseMessages: responseMessages,
|
||||||
responseMessages: [],
|
|
||||||
summary: typeConverter.convertText(route.description),
|
summary: typeConverter.convertText(route.description),
|
||||||
notes: typeConverter.convertText(route.notes),
|
notes: typeConverter.convertText(route.notes),
|
||||||
deprecated: route.deprecated
|
deprecated: route.deprecated
|
||||||
}, returns)]
|
}]
|
||||||
};
|
};
|
||||||
|
|
||||||
return apiDoc;
|
return apiDoc;
|
||||||
|
|
|
@ -96,7 +96,9 @@ function Swagger(loopbackApplication, swaggerApp, opts) {
|
||||||
|
|
||||||
addTypeToModels(routeDoc.type);
|
addTypeToModels(routeDoc.type);
|
||||||
|
|
||||||
// TODO(bajtos) handle types used by responseMessages
|
routeDoc.responseMessages.forEach(function(msg) {
|
||||||
|
addTypeToModels(msg.responseModel);
|
||||||
|
});
|
||||||
|
|
||||||
function addTypeToModels(name) {
|
function addTypeToModels(name) {
|
||||||
if (!name || name === 'void') return;
|
if (!name || name === 'void') return;
|
||||||
|
|
|
@ -13,7 +13,8 @@ describe('route-helper', function() {
|
||||||
{ arg: 'avg', type: 'number' }
|
{ arg: 'avg', type: 'number' }
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
expect(doc.operations[0].type).to.equal('object');
|
expect(doc.operations[0].type).to.equal(undefined);
|
||||||
|
expect(getResponseType(doc.operations[0])).to.equal('object');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('converts path params when they exist in the route name', function() {
|
it('converts path params when they exist in the route name', function() {
|
||||||
|
@ -60,19 +61,12 @@ describe('route-helper', function() {
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
var opDoc = doc.operations[0];
|
var opDoc = doc.operations[0];
|
||||||
expect(opDoc.type).to.equal('array');
|
// Note: swagger-ui treat arrays of X the same way as object X
|
||||||
expect(opDoc.items).to.eql({type: 'customType'});
|
expect(getResponseType(opDoc)).to.equal('customType');
|
||||||
});
|
|
||||||
|
|
||||||
it('correctly converts return types (format)', function() {
|
// NOTE(bajtos) this would be the case if there was a single response type
|
||||||
var doc = createAPIDoc({
|
// expect(opDoc.type).to.equal('array');
|
||||||
returns: [
|
// expect(opDoc.items).to.eql({type: 'customType'});
|
||||||
{arg: 'data', type: 'buffer'}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
var opDoc = doc.operations[0];
|
|
||||||
expect(opDoc.type).to.equal('string');
|
|
||||||
expect(opDoc.format).to.equal('byte');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('includes `notes` metadata', function() {
|
it('includes `notes` metadata', function() {
|
||||||
|
@ -151,12 +145,45 @@ describe('route-helper', function() {
|
||||||
.to.have.property('enum').eql([1,2,3]);
|
.to.have.property('enum').eql([1,2,3]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('preserves `enum` returns arg metadata', function() {
|
it('includes the default response message with code 200', function() {
|
||||||
var doc = createAPIDoc({
|
var doc = createAPIDoc({
|
||||||
returns: [{ name: 'arg', root: true, type: 'number', enum: [1,2,3] }]
|
returns: [{ name: 'result', type: 'object', root: true }]
|
||||||
|
});
|
||||||
|
expect(doc.operations[0].responseMessages).to.eql([
|
||||||
|
{
|
||||||
|
code: 200,
|
||||||
|
message: 'Request was successful',
|
||||||
|
responseModel: 'object'
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('uses the response code 204 when `returns` is empty', function() {
|
||||||
|
var doc = createAPIDoc({
|
||||||
|
returns: []
|
||||||
|
});
|
||||||
|
expect(doc.operations[0].responseMessages).to.eql([
|
||||||
|
{
|
||||||
|
code: 204,
|
||||||
|
message: 'Request was successful',
|
||||||
|
responseModel: 'void'
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('includes custom error response in `responseMessages`', function() {
|
||||||
|
var doc = createAPIDoc({
|
||||||
|
errors: [{
|
||||||
|
code: 422,
|
||||||
|
message: 'Validation failed',
|
||||||
|
responseModel: 'ValidationError'
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
expect(doc.operations[0].responseMessages[1]).to.eql({
|
||||||
|
code: 422,
|
||||||
|
message: 'Validation failed',
|
||||||
|
responseModel: 'ValidationError'
|
||||||
});
|
});
|
||||||
expect(doc.operations[0])
|
|
||||||
.to.have.property('enum').eql([1,2,3]);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -168,3 +195,7 @@ function createAPIDoc(def) {
|
||||||
method: 'test.get'
|
method: 'test.get'
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getResponseType(operationDoc) {
|
||||||
|
return operationDoc.responseMessages[0].responseModel;
|
||||||
|
}
|
||||||
|
|
|
@ -185,6 +185,24 @@ describe('swagger definition', function() {
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('includes `responseMessages` models', function(done) {
|
||||||
|
var app = createLoopbackAppWithModel();
|
||||||
|
loopback.createModel('ValidationError');
|
||||||
|
givenSharedMethod(app.models.Product, 'setImage', {
|
||||||
|
errors: [{
|
||||||
|
code: '422',
|
||||||
|
message: 'Validation failed',
|
||||||
|
responseModel: 'ValidationError'
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
mountExplorer(app);
|
||||||
|
|
||||||
|
getAPIDeclaration(app, 'products').end(function(err, res) {
|
||||||
|
expect(Object.keys(res.body.models)).to.include('ValidationError');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Cross-origin resource sharing', function() {
|
describe('Cross-origin resource sharing', function() {
|
||||||
|
|
Loading…
Reference in New Issue