This module validates a request from a swagger-node-express application using the existing swagger-node-express objects, parameters, and models following the swagger specification 1.2. It returns an array of JavaScript Error objects if there are any validation errors. For now, it only uses the message property of the Error object which, using lo-dash or Underscore.js, can be got easily via
var errors = _.pluck(_.pluck([VALIDATION RETURN], 'error'), 'message');)
Using NPM, include the swagger-validation
module in your package.json
dependencies.
{
...
"dependencies": {
"swagger-validation": "~1.0",
...
}
...
}
There are few different ways to use this module within swagger-node-express depending on what you are trying to accomplish.
The benefit of this is that, by default, all methods will have their request validated against the parameters specified automatically.
// add this to the swagger definition, usually defined in the app.js
swagger.addMiddleware(function(req, res, spec, models) {
var ret = validate(spec, req, models);
if(ret.length) {
var errors = _.pluck(_.pluck(ret, 'error'), 'message');
var message = 'validation failure - ' + errors.join();
return { 'code' : 400, 'message': message };
}
});
(NOTE: As of 8-7-2014, this is still a pull request of swagger and has not been approved. As such, this implementation WILL change if / when it gets pulled into swagger-node-express).
For the following method (using the swagger-application "test application" inside swagger-node-express)
exports.findById = {
'spec': {
description : "Operations about pets",
path : "/pet/{petId}",
method: "GET",
summary : "Find pet by ID",
notes : "Returns a pet based on ID",
type : "Pet",
nickname : "getPetById",
produces : ["application/json"],
parameters : [param.path("petId", "ID of pet that needs to be fetched", "string")],
responseMessages : [swe.invalid('id'), swe.notFound('pet')]
},
'action': function (req,res) {
if (!req.params.petId) {
throw swe.invalid('id'); }
var id = parseInt(req.params.petId);
var pet = petData.getPetById(id);
if(pet) res.send(JSON.stringify(pet));
else throw swe.notFound('pet',res);
}
};
change it to
exports.findById = {
'spec': {
description : "Operations about pets",
path : "/pet/{petId}",
method: "GET",
summary : "Find pet by ID",
notes : "Returns a pet based on ID",
type : "Pet",
nickname : "getPetById",
produces : ["application/json"],
parameters : [param.path("petId", "ID of pet that needs to be fetched", "string")],
responseMessages : [swe.invalid('id'), swe.notFound('pet')]
},
'action': function (req,res) {
var validate = require('swagger-validation');
var models = require("./models.js");
var _ = require('lodash');
// models are only needed if this is intended to validate an object
var ret = validate(exports.findById.spec, req, models);
if(ret.length) {
var errors = _.pluck(_.pluck(ret, 'error'), 'message');
res.send(JSON.stringify({
'message': 'validation failure - ' + errors.join(),
'code': 400
}), 400);
return;
}
if (!req.params.petId) {
throw swe.invalid('id'); }
var id = parseInt(req.params.petId);
var pet = petData.getPetById(id);
if(pet) res.send(JSON.stringify(pet));
else throw swe.notFound('pet',res);
}
};
or, for a little cleaner approach:
exports.findById = {
'spec': {
description : "Operations about pets",
path : "/pet/{petId}",
method: "GET",
summary : "Find pet by ID",
notes : "Returns a pet based on ID",
type : "Pet",
nickname : "getPetById",
produces : ["application/json"],
parameters : [param.path("petId", "ID of pet that needs to be fetched", "integer")],
responseMessages : [swe.invalid('id'), swe.notFound('pet')]
},
'action': function (req,res) {
validateReq(req, res, exports.findById.spec, function() {
if (!req.params.petId) {
throw swe.invalid('id'); }
var id = parseInt(req.params.petId);
var pet = petData.getPetById(id);
if(pet) res.send(JSON.stringify(pet));
else throw swe.notFound('pet',res);
});
}
};
// put this somewhere else, either in the same file or put it in a
// separate module using the standard module.exports Node convention
var validate = require('swagger-validation');
var _ = require('lodash');
var models = require("./models.js");
function validateReq(req, res, spec, func) {
var ret = validate(spec, req, models);
if(ret.length) {
var errors = _.pluck(_.pluck(ret, 'error'), 'message');
res.send(JSON.stringify({
'message': 'validation failure - ' + errors.join(),
'code': 400
}), 400);
return;
}
func();
}
While this would have the same benefit as the first one that, by default, all methods will have their request validated against the parameters specified automatically, this is non-standard and can lead to unintended consequences. This will be deprecated / removed once the pull request specified above gets pulled in.
// /swagger-node-express/lib/swagger.js
// lines 418 - 420 currently have
else {
callback(req, res, next);
}
// change it to
else {
var validate = require('swagger-validation');
var ret = validate(spec, req, self.allModels);
if(ret.length) {
var errors = _.pluck(_.pluck(ret, 'error'), 'message');
res.send(JSON.stringify({
'message': 'validation failure - ' + errors.join(),
'code': 400
}), 400);
return;
}
callback(req, res, next);
}
Type | Format | Description |
---|---|---|
array |
This checks that each value inside the array corresponds to the type that was specified. It doesn't check that the array contains 'empty' values, even if the array parameter is required as spec doesn't have a way to say all values inside the array are required. While the spec says uniqueItems marks the array to be treated like a set instead of an array (and not that this is invalid if it isn't unique), it does have the potential to lead to an unintentional and unintended loss of data, so this throws a validation error that what you are passing isn't unique over just allowing the non-unique data to be lost. As such, if all the items passed their validation, check for uniqueness. This only validates uniqueness after all the items in the array are validated. If "nothing" was passed into the validate function and it's required with no default value, then this will throw a parameter is required error. |
|
boolean |
This only handles native boolean types or converting from true / false strings, as the concept is not uniform for other types (ie, if it's a number, should it be 0 = false and 1 = true or should any non-zero number be true). However, this only handles strings that are the string representation in JavaScript of their boolean counterparts, so True, TRUE, etc. will not validate. If "nothing" was passed into the validate function and it's required with no default value, then this will throw a parameter is required error. |
|
integer |
This allows all forms of a number (so 2, 2.0, 2e0, 0x2). As a hex value COULD be the hex representation of an actual integer (and JavaScript parses it for us anyway), allow JavaScript to treat hex numbers in the way it wants to. Additionally, if a minimum or maximum is defined this ensures the value is greater than the minimum (if minimum defined) or less than the maximum (if maximum defined). If "nothing" was passed into the validate function and it's required with no default value, then this will throw a parameter is required error. |
|
integer |
int32 |
This allows all forms of a number (so 2, 2.0, 2e0, 0x2). As a hex value COULD be the hex representation of an actual integer (and JavaScript parses it for us anyway), allow JavaScript to treat hex numbers in the way it wants to. Additionally, if a minimum or maximum is defined this ensures the value is greater than the minimum (if minimum defined) or less than the maximum (if maximum defined). If "nothing" was passed into the validate function and it's required with no default value, then this will throw a parameter is required error. |
integer |
int64 |
This allows all forms of a number (so 2, 2.0, 2.2, 2e0, 0x2). As a hex value COULD be the hex representation of an actual number (and JavaScript parses it for us anyway), allow JavaScript to treat hex numbers in the way it wants to. Additionally, if a minimum or maximum is defined this ensures the value is greater than the minimum (if minimum defined) or less than the maximum (if maximum defined). This does have issues with edge case validation (such as Number.MAX_VALUE + 1) as, per IEEE-754 2008 §4.3.1 spec, JavaScript does rounding during addition, so essentially, Number.MAX_VALUE + 1 will equal Number.MAX_VALUE not Number.Infinity. There isn't anything we can do about this as it is correct, per spec, but it isn't intuitive. If "nothing" was passed into the validate function and it's required with no default value, then this will throw a parameter is required error. |
file |
This has no type validation, but it is valid. If "nothing" was passed into the validate function and it's required with no default value, then this will throw a parameter is required error. |
|
number |
This allows all forms of a number (so 2, 2.0, 2.2, 2e0, 0x2). As a hex value COULD be the hex representation of an actual number (and JavaScript parses it for us anyway), allow JavaScript to treat hex numbers in the way it wants to. Additionally, if a minimum or maximum is defined this ensures the value is greater than the minimum (if minimum defined) or less than the maximum (if maximum defined). This does have issues with edge case validation (such as Number.MAX_VALUE + 1) as, per IEEE-754 2008 §4.3.1 spec, JavaScript does rounding during addition, so essentially, Number.MAX_VALUE + 1 will equal Number.MAX_VALUE not Number.Infinity. There isn't anything we can do about this as it is correct, per spec, but it isn't intuitive. If "nothing" was passed into the validate function and it's required with no default value, then this will throw a parameter is required error. |
|
number |
float |
This allows all forms of a number (so 2, 2.0, 2.2, 2e0, 0x2). As a hex value COULD be the hex representation of an actual number (and JavaScript parses it for us anyway), allow JavaScript to treat hex numbers in the way it wants to. Additionally, if a minimum or maximum is defined this ensures the value is greater than the minimum (if minimum defined) or less than the maximum (if maximum defined). This does have issues with edge case validation (such as Number.MAX_VALUE + 1) as, per IEEE-754 2008 §4.3.1 spec, JavaScript does rounding during addition, so essentially, Number.MAX_VALUE + 1 will equal Number.MAX_VALUE not Number.Infinity. There isn't anything we can do about this as it is correct, per spec, but it isn't intuitive. If "nothing" was passed into the validate function and it's required with no default value, then this will throw a parameter is required error. |
number |
double |
This allows all forms of a number (so 2, 2.0, 2.2, 2e0, 0x2). As a hex value COULD be the hex representation of an actual number (and JavaScript parses it for us anyway), allow JavaScript to treat hex numbers in the way it wants to. Additionally, if a minimum or maximum is defined this ensures the value is greater than the minimum (if minimum defined) or less than the maximum (if maximum defined). This does have issues with edge case validation (such as Number.MAX_VALUE + 1) as, per IEEE-754 2008 §4.3.1 spec, JavaScript does rounding during addition, so essentially, Number.MAX_VALUE + 1 will equal Number.MAX_VALUE not Number.Infinity. There isn't anything we can do about this as it is correct, per spec, but it isn't intuitive. If "nothing" was passed into the validate function and it's required with no default value, then this will throw a parameter is required error. |
object |
This checks that the value is a valid Object by iterating through each property on the associated model and calling out to the respective validation method to validate that property. After validating the properties on this object's model, it will recursively look to see if any other models have this model in their subType array. If so, it will validate those properties as well. It will continue to do this until no more types are found in the subType array. If "nothing" was passed into the validate function and it's required with no default value, then this will throw a parameter is required error. |
|
string |
If an enum is defined this ensures that the value is inside the enum list (which is case-sensitive). If "nothing" was passed into the validate function and it's required with no default value, then this will throw a parameter is required error. |
|
string |
byte |
This has no type validation, but it is valid. If "nothing" was passed into the validate function and it's required with no default value, then this will throw a parameter is required error. |
string |
date |
This has no type validation, but it is valid. There is no definitive definition in the swagger spec as to what constitutes a valid date or date-time (more than likely due to the varied formats a date could have). Even using moment.js and something like moment-parseformat or the native Date object has the potential to lead to false positives. Without additional spec definition into what defines a date or date-time (or, ideally, the spec adds in a format for date / date time that can be used in a date validation function), this is too varied to be handled by the validation framework. If "nothing" was passed into the validate function and it's required with no default value, then this will throw a parameter is required error. |
string |
date-time |
This has no type validation, but it is valid. There is no definitive definition in the swagger spec as to what constitutes a valid date or date-time (more than likely due to the varied formats a date could have). Even using moment.js and something like moment-parseformat or the native Date object has the potential to lead to false positives. Without additional spec definition into what defines a date or date-time (or, ideally, the spec adds in a format for date / date time that can be used in a date validation function), this is too varied to be handled by the validation framework. If "nothing" was passed into the validate function and it's required with no default value, then this will throw a parameter is required error. |
Full documentation of how all code works in swagger-validation is available in both markdown and HTML format (using jsdoc) in the /docs sub-folder of this project.
(The MIT License)
Copyright (c) 2014 Wonderlic, Inc. [email protected]
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.