Skip to content
This repository has been archived by the owner on May 5, 2018. It is now read-only.

Commit

Permalink
Fix for invalid body parameter handling.
Browse files Browse the repository at this point in the history
* body parameters were being modified, making the module non idempotent.
* Major refactoring for body param validation was performed.
  • Loading branch information
jsdevel committed Feb 5, 2016
1 parent e3a6d5f commit 26ce238
Show file tree
Hide file tree
Showing 4 changed files with 149 additions and 15 deletions.
48 changes: 33 additions & 15 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,32 +23,37 @@ function validate(args) {
extendedErrorMapper(errorTransformer) :
toOpenapiValidationError;
var bodySchema = schemas.body;
var bodyValidationSchema;
var headersSchema = lowercasedHeaders(schemas.headers);
var pathSchema = schemas.path;
var querySchema = schemas.query;
var v = new JsonschemaValidator();
var isBodyRequired = args.parameters.filter(byBodyParameters).length > 0;

if (bodySchema) {
bodyValidationSchema = {
properties: {
body: bodySchema
}
};
}

if (args.schemas) {
if (Array.isArray(args.schemas)) {
args.schemas.forEach(function(schema) {
var id = schema.id;

if (id) {
var localSchemaPath;

if (bodySchema) {
localSchemaPath = LOCAL_DEFINITION_REGEX.exec(id);
}
var localSchemaPath = LOCAL_DEFINITION_REGEX.exec(id);

if (localSchemaPath) {
var localSchemas = bodySchema[localSchemaPath[1]];
if (localSchemaPath && bodyValidationSchema) {
var definitions = bodyValidationSchema[localSchemaPath[1]];

if (!localSchemas) {
localSchemas = bodySchema[localSchemaPath[1]] = {};
if (!definitions) {
definitions = bodyValidationSchema[localSchemaPath[1]] = {};
}

localSchemas[localSchemaPath[2]] = schema;
definitions[localSchemaPath[2]] = schema;
}

v.addSchema(schema, id);
Expand All @@ -57,7 +62,7 @@ function validate(args) {
}
});
} else if (bodySchema) {
bodySchema.definitions = args.schemas;
bodyValidationSchema.definitions = args.schemas;
}
}

Expand All @@ -69,7 +74,7 @@ function validate(args) {
if (bodySchema) {
if (req.body) {
try {
var validation = v.validate(req.body, bodySchema);
var validation = v.validate({body: req.body}, bodyValidationSchema);
errors.push.apply(errors, withAddedLocation('body', validation.errors));
} catch(e) {
e.location = 'body';
Expand Down Expand Up @@ -145,12 +150,25 @@ function lowercasedHeaders(headersSchema) {
}

function toOpenapiValidationError(error) {
return {
path: error.property.replace(/^instance\.?/, '') || error.argument,
return stripBodyInfo({
path: error.property.replace(
error.location === 'body' ?
/^instance\.body\.?/ :
/^instance\.?/, '') || error.argument,
errorCode: error.name + '.openapi.validation',
message: error.stack,
location: error.location
};
});
}

function stripBodyInfo(error) {
if (error.location === 'body') {
error.path = error.path.replace(/^body\./, '');
error.message = error.message.replace(/^instance\.body\./, 'instance.');
error.message = error.message.replace(/^instance\.body /, 'instance ');
}

return error;
}

function withAddedLocation(location, errors) {
Expand Down
37 changes: 37 additions & 0 deletions test/data-driven/accept-schema-ref-in-body.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
module.exports = {
validateArgs: {
parameters: [
{
in: 'body',
name: 'foo',
required: true,
schema: {
$ref: '#/definitions/Test1'
}
}
],

schemas: {
Test1: {
properties: {
foo: {
type: 'string'
}
},
required: ['foo']
}
}
},

requestMethod: 'post',

requestBody: {
foo: 'asdf'
},

path: '',

statusCode: 200,

responseBody: '"woot"'
};
45 changes: 45 additions & 0 deletions test/data-driven/fail-schema-ref-in-body.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
module.exports = {
validateArgs: {
parameters: [
{
in: 'body',
name: 'foo',
required: true,
schema: {
$ref: '#/definitions/Test1'
}
}
],

schemas: {
Test1: {
properties: {
foo: {
type: 'string'
}
},
required: ['foo']
}
}
},

requestMethod: 'post',

requestBody: {},

path: '',

statusCode: 400,

responseBody: {
'status': 400,
'errors': [
{
'path': 'foo',
'errorCode': 'required.openapi.validation',
'message': 'instance requires property "foo"',
'location': 'body'
}
]
}
};
34 changes: 34 additions & 0 deletions test/tests.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
var expect = require('chai').expect;
var sut = require('../');

describe(require('../package.json').name, function() {
it('should not modify the input parameters', function() {
var initialArguments = createArguments();
sut(initialArguments);
expect(initialArguments).to.eql(createArguments());
});

function createArguments() {
return {
parameters: [
{
in: 'body',
name: 'User',
schema: {
$ref: '#/definitions/User'
}
}
],

schemas: {
User: {
properties: {
name: {
type: 'string'
}
}
}
}
};
}
});

0 comments on commit 26ce238

Please sign in to comment.