Skip to content
This repository was archived by the owner on Aug 4, 2023. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions middleware/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,55 @@ var isModelParameter = module.exports.isModelParameter = function (version, para
return isModel;
};

/*
* Gets the parameter values for the members of an object (recursively). This
* includes getting any applicable defaults based on the Swagger definition.
*/
function getObjectParameterValue(version, parameter, val, debug) {
if (version === '1.2') {
return undefined; //Doesn't support old formats
}
if (_.isUndefined(val) || !_.isObject(val)) {
val = {};
}
var foundDefault = false;
var nestedParams = parameter.properties;
if (_.isUndefined(nestedParams) && !_.isUndefined(parameter.schema)) {
nestedParams = parameter.schema.properties;
}

if (!_.isObject(nestedParams)) {
return undefined;
}

_.each(nestedParams, function (schemaParam, key) {
//
// Do we have a default for a missing value?
//
if (_.isUndefined(val[key]) && !_.isUndefined(schemaParam.default)) {
foundDefault = true;
val[key] = schemaParam.default;
debug(' Nested model default: %s =', key, schemaParam.default);
} else if (getParameterType(schemaParam) === 'object') {
//
// Go deeper
//
var newVal = getObjectParameterValue(version, schemaParam, val[key], debug);
if (!_.isUndefined(newVal)) {
val[key] = newVal;
foundDefault = true;
}
}
});

if (foundDefault) {
return val;
} else {
// Didn't change anything (except perhaps making val an object) so...
return undefined;
}
}

module.exports.getParameterValue = function (version, parameter, pathKeys, match, req, debug) {
var defaultVal = version === '1.2' ? parameter.defaultValue : parameter.default;
var paramLocation = version === '1.2' ? parameter.paramType : parameter.in;
Expand Down Expand Up @@ -111,6 +160,12 @@ module.exports.getParameterValue = function (version, parameter, pathKeys, match
// Use the default value when necessary
if (_.isUndefined(val) && !_.isUndefined(defaultVal)) {
val = defaultVal;
} else if (getParameterType(parameter) === 'object') {
// We need to look deeper for defaults
var newVal = getObjectParameterValue(version, parameter, val, debug);
if (!_.isUndefined(newVal)) {
val = newVal;
}
}

return val;
Expand Down
117 changes: 117 additions & 0 deletions test/2.0/test-middleware-swagger-metadata.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ var async = require('async');
var helpers = require('../helpers');
var path = require('path');
var petStoreJson = _.cloneDeep(require('../../samples/2.0/petstore.json'));
var nestingTestJson = _.cloneDeep(require('../test-swagger-definitions/test-nested-defaults.json'));
var pkg = require('../../package.json');
var request = require('supertest');
var spec = require('../../lib/helpers').getSpec('2.0');
Expand Down Expand Up @@ -248,6 +249,122 @@ describe('Swagger Metadata Middleware v2.0', function () {
.end(helpers.expectContent({id: 1, name: 'Top Dog'}, done));
});
});

/*
* Tests for setting defaults of parameters nested within an object (i.e. in
* the request body)
*/
describe('nested default parameters in body object', function () {
/*
* Before each test load the test Swagger definied for the tests
*/
var cTestJson;
beforeEach(function () {
cTestJson = _.cloneDeep(nestingTestJson);
});

it('should add 1 deep defaults', function (done) {
helpers.createServer([cTestJson], {
swaggerRouterOptions: {
controllers: {
testNest1: function (req, res, next) {
var body = req.swagger.params.testParam.value;
var expected = {
hasDefault: 'nest1'
};
assert.deepEqual(body, expected);

res.end('OK');

next();
}
}
}
}, function (app) {
request(app)
.post('/api/nest1')
.expect(200)
.end(helpers.expectContent('OK', done));
});
});

it('should not add 1 deep if no defaults', function (done) {
helpers.createServer([cTestJson], {
swaggerRouterOptions: {
controllers: {
testNest1NoDefault: function (req, res, next) {
var body = req.swagger.params.testParam.value;
var expected = {};
assert.deepEqual(body, expected);

res.end('OK');

next();
}
}
}
}, function (app) {
request(app)
.post('/api/nest1_NoDefault')
.expect(200)
.end(helpers.expectContent('OK', done));
});
});

it('should add 2 deep with defaults', function (done) {
helpers.createServer([cTestJson], {
swaggerRouterOptions: {
controllers: {
testNest2: function (req, res, next) {
var body = req.swagger.params.testParam.value;
var expected = {
nest1: {
hasDefault: 'nest1'
}
};
assert.deepEqual(body, expected);

res.end('OK');

next();
}
}
}
}, function (app) {
request(app)
.post('/api/nest2')
.expect(200)
.end(helpers.expectContent('OK', done));
});
});

it('should add 2 deep with partial defaults', function (done) {
helpers.createServer([cTestJson], {
swaggerRouterOptions: {
controllers: {
testNest2Default1: function (req, res, next) {
var body = req.swagger.params.testParam.value;
var expected = {
withDefault: {
hasDefault: 'nest1'
}
};
assert.deepEqual(body, expected);

res.end('OK');

next();
}
}
}
}, function (app) {
request(app)
.post('/api/nest2_default1')
.expect(200)
.end(helpers.expectContent('OK', done));
});
});
});

describe('non-multipart form parameters', function () {
it('should handle primitives', function (done) {
Expand Down
153 changes: 153 additions & 0 deletions test/test-swagger-definitions/test-nested-defaults.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
{
"swagger": "2.0",
"info": {
"version": "1.0.0",
"title": "Test nested default values",
"license": {
"name": "MIT"
}
},
"host": "example.com",
"basePath": "/api",
"schemes": [
"http"
],
"paths": {
"/nest1": {
"post": {
"operationId": "testNest1",
"summary": "Tests a 1 level of nesting with a default",
"parameters": [
{
"name": "testParam",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/Nest1"
}
}
],
"responses": {
"200": {
"description": "Created test response"
}
}
}
},
"/nest1_NoDefault": {
"post": {
"operationId": "testNest1NoDefault",
"summary": "Tests a 1 level of nesting without a default",
"parameters": [
{
"name": "testParam",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/Nest1_NoDefault"
}
}
],
"responses": {
"200": {
"description": "Created test response"
}
}
}
},
"/nest2": {
"post": {
"operationId": "testNest2",
"summary": "Tests 2 levels of nesting with a default at level 2",
"parameters": [
{
"name": "testParam",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/Nest2"
}
}
],
"responses": {
"200": {
"description": "Created test response"
}
}
}
},
"/nest2_default1": {
"post": {
"operationId": "testNest2Default1",
"summary": "Tests 2 levels of nesting with one default param, and one non-default",
"parameters": [
{
"name": "testParam",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/Nest2_Default1"
}
}
],
"responses": {
"200": {
"description": "Created test response"
}
}
}
}

},
"definitions": {
"Nest1": {
"type": "object",
"properties": {
"hasDefault": {
"type": "string",
"default": "nest1"
}
}
},
"Nest1_NoDefault": {
"type": "object",
"properties": {
"noDefault": {
"type": "string"
}
}
},
"Nest2": {
"properties": {
"nest1": {
"$ref": "#/definitions/Nest1"
}
}
},
"Nest2_Default1": {
"properties": {
"withDefault": {
"$ref": "#/definitions/Nest1"
},
"withoutDefault": {
"$ref": "#/definitions/Nest1_NoDefault"
}
}
}
},
"produces": [
"application/json"
],
"securityDefinitions": {
"oauth2": {
"type": "oauth2",
"scopes": {
"read": "Read access.",
"write": "Write access"
},
"flow": "accessCode",
"authorizationUrl": "http://petstore.swagger.wordnik.com/oauth/authorize",
"tokenUrl": "http://petstore.swagger.wordnik.com/oauth/token"
}
}
}