Giter Site home page Giter Site logo

swagger-validation's Introduction

Validation for swagger-node-express

Build Status NPM version Dependency Status

Quick Description

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');)

Installation

Using NPM, include the swagger-validation module in your package.json dependencies.

{
	...
	"dependencies": {
		"swagger-validation": "~1.0",
		...
	}
	...
}

Adding validation to swagger-node-express

There are few different ways to use this module within swagger-node-express depending on what you are trying to accomplish.

Use the middleware component of Swagger

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).

Validate each method individually

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();
}

Modify swagger-node-express directly

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);
    }
        

Types of validation

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.

Documentation

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.

License

(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.

swagger-validation's People

Contributors

wonderlic-chrisk avatar wonderlic-stephene avatar

Watchers

James Cloos avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.