Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

validation: fix add support validating query objects that do not extend Object.prototype #52

Merged
merged 2 commits into from Dec 1, 2016
Merged
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
52 changes: 26 additions & 26 deletions lib/RouteSchemaManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,29 @@ var RouteSchemaManager = function(options) {
return schemas;
},

convertValueFromStringToType = function(value, type) {
if (typeof(value) !== 'string' || type === 'string') {
return value;
}
if (type === 'integer' || type === 'number') {
// fastest (and more reliable) way to convert strings to numbers
var convertedVal = 1 * value;
// make sure that if our schema calls for an integer, that there is no decimal
if (convertedVal || convertedVal === 0 && (type === 'number' || (value.indexOf('.') === -1))) {
return convertedVal;
}
}
else if (type === 'boolean') {
if (value === 'true') {
return true;
}
else if (value === 'false') {
return false;
}
}
return value;
},

convertPropertyTypesToMatchSchema = function(object, schema, forceArrayConversion) {
// in some cases (query params), we want to force a value to be an array that contains that value,
// if the schema expects an array of strings, numbers, integers, or booleans
Expand All @@ -114,7 +137,7 @@ var RouteSchemaManager = function(options) {
var i, prop;
if (schema.type === 'object' && typeof(object) === 'object' && schema.properties) {
for (prop in schema.properties) {
if (schema.properties.hasOwnProperty(prop) && object.hasOwnProperty(prop)) {
if (schema.properties.hasOwnProperty(prop) && Object.hasOwnProperty.call(object, prop)) {
object[prop] = convertPropertyTypesToMatchSchema(object[prop], schema.properties[prop], forceArrayConversion);
}
}
Expand All @@ -135,34 +158,11 @@ var RouteSchemaManager = function(options) {
}
},

convertValueFromStringToType = function(value, type) {
if (typeof(value) !== 'string' || type === 'string') {
return value;
}
if (type === 'integer' || type === 'number') {
// fastest (and more reliable) way to convert strings to numbers
var convertedVal = 1 * value;
// make sure that if our schema calls for an integer, that there is no decimal
if (convertedVal || convertedVal === 0 && (type === 'number' || (value.indexOf('.') === -1))) {
return convertedVal;
}
}
else if (type === 'boolean') {
if (value === 'true') {
return true;
}
else if (value === 'false') {
return false;
}
}
return value;
},

convertArraysInQueryString = function(queryObj) {
var prop, newProp, idx,
arraySyntaxRegex = /\[\d+\]$/;
for (prop in queryObj) {
if (queryObj.hasOwnProperty(prop)) {
if (Object.hasOwnProperty.call(queryObj, prop)) {
if (arraySyntaxRegex.test(prop)) {
newProp = prop.substring(0, prop.lastIndexOf('['));
queryObj[newProp] = queryObj[newProp] || [];
Expand Down Expand Up @@ -284,4 +284,4 @@ var RouteSchemaManager = function(options) {
};
};

module.exports = RouteSchemaManager;
module.exports = RouteSchemaManager;
57 changes: 29 additions & 28 deletions lib/SwaggerManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,22 +30,19 @@ var SwaggerManager = function(options) {
});
},

swaggerParamTypeMap = {
path: 'path',
query: 'query',
payload: 'body',
headers: 'header'
},

getRoutesGroupedByPath = function(routes) {
return _.groupBy(routes, function(item) {
return item.path;
});
},

getApplicationVersion = function() {
var executingFile = process.argv[1];
var packageLoc = findPackageJson(executingFile.replace(/\/[^\/]+?$/g, ''));

if (packageLoc) {
return require(packageLoc).version;
}
return 'unknown';
},

findPackageJson = function(startingDirectory) {
if (!startingDirectory) {
return false;
Expand All @@ -56,6 +53,16 @@ var SwaggerManager = function(options) {
return findPackageJson(startingDirectory.replace(/\/[^\/]+?$/g, ''));
},

getApplicationVersion = function() {
var executingFile = process.argv[1];
var packageLoc = findPackageJson(executingFile.replace(/\/[^\/]+?$/g, ''));

if (packageLoc) {
return require(packageLoc).version;
}
return 'unknown';
},

getSwaggerParams = function(route, type, operationNickname) {
var params = [], prop, param;
if (swaggerParamTypeMap[type] &&
Expand Down Expand Up @@ -128,6 +135,18 @@ var SwaggerManager = function(options) {
return params;
},

setOperationConsumes = function (payloadParameters, route, operation) {
if (!payloadParameters.length) { //if no payload parameters, no consumes attribute
return;
}

if (route.settings.payload && route.settings.payload.allow && route.settings.payload.parse) {
operation.consumes = route.settings.payload.allow;
} else {
operation.consumes = consumesDefaults;
}
},

getSwaggerOperationForRoute = function(route, resourceType, path) {
var pathParts = path.split('/'),
regex = /^\{.+\}$/,
Expand Down Expand Up @@ -201,18 +220,6 @@ var SwaggerManager = function(options) {
return operation;
},

setOperationConsumes = function (payloadParameters, route, operation) {
if (!payloadParameters.length) { //if no payload parameters, no consumes attribute
return;
}

if (route.settings.payload && route.settings.payload.allow && route.settings.payload.parse) {
operation.consumes = route.settings.payload.allow;
} else {
operation.consumes = consumesDefaults;
}
},

getModelForRoute = function(route, modelKind, modelGetter){
var plugins = route.settings.plugins;
var model;
Expand Down Expand Up @@ -276,12 +283,6 @@ var SwaggerManager = function(options) {
});
},

swaggerParamTypeMap = {
path: 'path',
query: 'query',
payload: 'body',
headers: 'header'
},
defaultResourceListingModel = {
apiVersion: options.apiVersion || getApplicationVersion().split('.')[0],
swaggerVersion: '1.2',
Expand Down
45 changes: 15 additions & 30 deletions test/RouteSchemaManager.tests.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// headings were generated by http://patorjk.com/software/taag/#p=display&f=Colossal&t=ValidateResponse
var querystring = require('querystring');
var assert = require('assert'),
RouteSchemaManager = require('../lib/RouteSchemaManager'),
rsmConfig = {
Expand Down Expand Up @@ -286,15 +287,10 @@ describe('RouteSchemaManager Unit Tests', function() {

it('should validate query params for route with no query schema successfully', function() {
var routeSchemaManager = new RouteSchemaManager(rsmConfig),
query = querystring.parse('string=fnord&array[0]=fnord1&array[1]=fnord2'),
mockRequest = {
_route: mockRoute2,
query: {
string: 'fnord',
array: [
'fnord1',
'fnord2'
]
}
query: query
};
routeSchemaManager.initializeRoutes(mockRoute1.server.info.uri, mockRoutes);
var report = routeSchemaManager.validateQuery(mockRequest);
Expand All @@ -303,12 +299,10 @@ describe('RouteSchemaManager Unit Tests', function() {

it('should validate query params successfully while converting properties to arrays if defined as such by schema', function() {
var routeSchemaManager = new RouteSchemaManager(rsmConfig),
query = querystring.parse('string=fnord&array=fnord1'),
mockRequest = {
_route: mockRoute1,
query: {
string: 'fnord',
array: 'fnord1'
}
query: query
};
routeSchemaManager.initializeRoutes(mockRoute1.server.info.uri, mockRoutes);
var report = routeSchemaManager.validateQuery(mockRequest);
Expand All @@ -318,13 +312,10 @@ describe('RouteSchemaManager Unit Tests', function() {
it('should validate query params successfully while converting properties to arrays (from objects) if defined as such by schema', function() {

var routeSchemaManager = new RouteSchemaManager(rsmConfig),
query = querystring.parse('string=fnord&array[0]=fnord1&array[1]=fnord2'),
mockRequest = {
_route: mockRoute1,
query: {
string: 'fnord',
'array[0]': 'fnord1',
'array[1]': 'fnord2'
}
query: query
};
routeSchemaManager.initializeRoutes(mockRoute1.server.info.uri, mockRoutes);
var report = routeSchemaManager.validateQuery(mockRequest);
Expand All @@ -334,12 +325,10 @@ describe('RouteSchemaManager Unit Tests', function() {
it('should validate query params successfully while converting properties to booleans (true) if defined as such by schema', function() {

var routeSchemaManager = new RouteSchemaManager(rsmConfig),
query = querystring.parse('string=fnord&bool=true'),
mockRequest = {
_route: mockRoute1,
query: {
string: 'fnord',
bool: 'true'
}
query: query
};
routeSchemaManager.initializeRoutes(mockRoute1.server.info.uri, mockRoutes);
var report = routeSchemaManager.validateQuery(mockRequest);
Expand All @@ -348,12 +337,10 @@ describe('RouteSchemaManager Unit Tests', function() {

it('should validate query params successfully while converting properties to booleans (false) if defined as such by schema', function() {
var routeSchemaManager = new RouteSchemaManager(rsmConfig),
query = querystring.parse('string=fnord&bool=false'),
mockRequest = {
_route: mockRoute1,
query: {
string: 'fnord',
bool: 'false'
}
query: query
};
routeSchemaManager.initializeRoutes(mockRoute1.server.info.uri, mockRoutes);
var report = routeSchemaManager.validateQuery(mockRequest);
Expand All @@ -363,20 +350,17 @@ describe('RouteSchemaManager Unit Tests', function() {
it('should not validate query params successfully while avoiding the conversion of properties to different types when the types cannot be coerced to the type defined in the schema', function() {

var routeSchemaManager = new RouteSchemaManager(rsmConfig),
query = querystring.parse('string=fnord&array[fnord]=1&bool=truefalse'),
mockRequest = {
_route: mockRoute1,
query: {
string: 'fnord',
array: { fnord: 1 },
bool: 'truefalse'
}
query: query
};
routeSchemaManager.initializeRoutes(mockRoute1.server.info.uri, mockRoutes);
var report = routeSchemaManager.validateQuery(mockRequest);
assert(!report.valid, 'query obj should not be valid');
});

it('should fail validation of query params', function() {
it('should fail validation of query params if parsed object has incorrect type', function() {

var routeSchemaManager = new RouteSchemaManager(rsmConfig),
mockRequest = {
Expand All @@ -390,6 +374,7 @@ describe('RouteSchemaManager Unit Tests', function() {
assert(!report.valid, 'query obj should not be valid');
assert(report.errors, 'errors obj should be valid');
});

});

/*
Expand Down