Code Monkey home page Code Monkey logo

ratify's Introduction

ratify

A Hapi plugin for validating the schema of path, query, request body, and response body params using JSON-schema, while providing documenation for your end points via Swagger

Build Status Coverage Status Code Climate NPM version Dependency Status

NPM

Contributing

This module makes use of a Makefile for building/testing purposes. After obtaining a copy of the repo, run the following commands to make sure everything is in working condition before you start your work:

make install
make test

Before committing a change to your fork/branch, run the following commands to make sure nothing is broken:

make test
make test-cov

Don't forget to bump the version in the package.json using the semver spec as a guide for which part to bump. Submit a pull request when your work is complete.

Notes:

  • Please do your best to ensure the code coverage does not drop. If new unit tests are required to maintain the same level of coverage, please include those in your pull request.
  • Please follow the same coding/formatting practices that have been established in the module.

Installation

npm install ratify

Usage

To install this plugin on your Hapi server, do something similar to this:

var Hapi = require('hapi');
var server = new Hapi.Server();

var ratifyOptions = {};

server.register({ register: require('ratify'), options: ratifyOptions }, function(err) {
	if (err) {
		console.log('error', 'Failed loading plugin: ratify');
	}
});

Plugin Options

auth

Used to add authentication to the swagger routes that get created by the plugin. Valid values are described here under the auth property.

Defaults to false

baseUrl

The protocol, hostname, and port where the application is running.

Defaults to 'http://localhost'

startingPath

The path at which all of the swagger routes begin at. This is the endpoint you would pass to an instance of the swagger UI.

Defaults to '/api-docs'

apiVersion

The version of your API.

Defaults to ''

responseContentTypes

A collection of valid response types returned by your services.

Defaults to ['application/json']

swaggerHooks

An object in which the property names represent swagger generated elements and the values must be functions to be invoked to customize how those elements are processed.

Possible values:

  • params: function(params, route, type)
  • operation: function(operation, route, resourceType, path)
  • routeNameGroup: function(route)

errorReporters

An object in which the property keys represent elements that can be validated ("headers", "query", "path", "payload", "response") and the values are initialized ZSchemaErrors instances to be used to report those errors.

Parameter Validation

Once your server is set to use ratify, you can specify route-specific validations in each route config like so:

var route = {
	method: 'GET',
	path: '/foo/{bar}',
	config: {
		handler: function(request, reply) {

		},
		plugins: {
			ratify: {
				path: {
					// path parameters schema
				},
				query: {
					// query parameters schema
				},
				headers: {
					// header parameters schema
				},
				payload: {
					// request payload schema
				},
				response: {
					schema: {
						// response payload schema
					},
					sample: 100, // percentage of responses to test against the schema
					failAction: 'log' // action to take when schena validation fails. Valid options are; 'log' and 'error'
				}

			}
		}
	}
};
server.route(route);

All schemas should follow the JSON schema specification.

Notes: In addition to the JSON schema defined types, ratify allows you to specify "file" as a payload type. If this is specified, no validation against JSON schema is performed, but swagger documentation will still be provided.

Type Conversion

In the process of validating the properties based on the schema, ratify will attempt to convert path, header, and query params to the type defined in the schema. For example, if you have a query paramter called limit and it's type is number, since all query parameters are parsed as strings by Hapi, ratify will convert the string to a number.

Ratify can also specifically convert query parameters that are intended to be arrays. For example, both of the following query strings will result in a property called types having an array value:

  • ?types=first&types=second&types=third
  • ?types[0]=first&types[2]=third&types[1]=second

Result:

{
	types: ['first', 'second', 'third']
}

Swagger Documentation

Ratify automatically generates routes that produce JSON in the format of the Swagger API Specification. In order to ge tthe most of the documentation, it's best to ensure there are descriptions to all your parameters as allowed by the JSON schema spec.

Version Compatibility

Currently compatible with: Hapi 10.x.x (Node v4)

  • 0.2.x - Hapi 1.x.x
  • 0.3.x - Don't use!
  • 0.4.x - Hapi 4.x.x
  • 0.6.x - Hapi 6.x.x
  • 0.7.x - Hapi 7.x.x
  • 0.8.x - Hapi 8.x.x
  • 0.10.x - Hapi 9.x.x
  • 1.x.x - Hapi 10.x.x (Node v4)
  • 2.x.x - Hapi 11.x.x
  • 3.x.x - Hapi 16.x.x

ratify's People

Contributors

barryhammen avatar bitdeli-chef avatar dschenkelman avatar hernanhht avatar mac- avatar siacomuzzi avatar skeary-immt avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

ratify's Issues

remove array query string parsing?

It looks like hapi is using the qs module which should parse arrays in query strings properly. Look into removing the convertArraysInQueryString method in RouteSchemaManager.js

Include field information in errors

Issue

Error messages on failed types do not include property name.

Example

Currently, the following validation:

ratify: {
  payload: {
    type: "object",
    properties: {
      outer: {
        type: "object",
        properties: {
          inner:{
            type: "string"
          }
        }
      }
    }
  }
}

Together with this payload:

{
  "outer": {
    "inner": false
  }
}

Results in this response:

{
    "statusCode": 400,
    "error": "Bad Request",
    "message": "payload parameters validation error: invalid type: boolean (expected string)"
}

Proposal

The available information when creating the error message is:

{ code: 'INVALID_TYPE',
  message: 'invalid type: boolean (expected string)',
  path: '#/outer/inner',
  params: { expected: 'string', type: 'boolean' } }

The current code looks like this:

if (!report.valid) {
  errorMessages = 'payload parameters validation error: ' +
          _.map(report.errors, function(error) { console.log(error); return error.message; }).join(', ');
  return next(plugin.hapi.error.badRequest(errorMessages));
}

If it were updated to something like this it would provide more detailed error messages:

if (!report.valid) {
  errorMessages = 'payload parameters validation error: ' +
      _.map(report.errors, function(error) { 
        var message = error.message;
        message += ' - on ';
        // handle array case
        message += error.path.substr(2).replace(/\//g, ".").replace(/\.\[/g, "[")
        return message;
      }).join(', ');

Is there a reason why response validation requires an object?

I believe schemas like this one should be usable:

response: {
    schema: {
        // response payload schema
        type: 'string'
    },
    sample: 100, // percentage of responses to test against the schema
    failAction: 'log' // action to take when schena validation fails. Valid options are; 'log' and 'error'
}

IMO it should be up to the schema whether something is a valid response or not. Thoughts?

https://github.com/mac-/ratify/blob/master/lib/RouteSchemaManager.js#L267-L269

Include error description (if any) in when reporting validation errors

Right now the following code is being used to report errors:
https://github.com/mac-/ratify/blob/master/lib/RequestValidator.js#L28-L29

A recent minor change in z-schema zaggino/z-schema#53 provides additional information for failed string patterns (regex) through the error.description property.

It would be very useful if that property was available in the error message. For example:

errorMessages = 'path parameters validation error: ' +
  _.map(report.errors, function(error) { 
    var message = error.message;
    if (error.description){
      message += '. Details: ' + error.description
    }

    return message;
}).join(', ');

Convert strings formatted as date-time to Date objects?

Hi @mac-, would you accept a PR to automatically convert strings defined with date-time formats to Date object? This is useful, for example, when saving Date objects to MongoDB - otherwise they are persisted as strings by default.

Current behavior is here:

convertValueFromStringToType = function(value, type) {

Seems this would be a breaking change. The only other JSON schema string format that has an equivalent Node type is uri, which we might want to convert automatically as well. Let me know what you think.

Thanks!

Documentation to register plugin with options is not accurate for Hapi 6.x.x

It reads:

var ratifyOptions = {};

server.pack.register({ plugin: require('ratify') }, ratifyOptions, function(err) {
    if (err) {
        console.log('error', 'Failed loading plugin: ratify');
    }
});

It should be:

var ratifyOptions = {};

server.pack.register({ plugin: require('ratify'), options: ratifyOptions }, function(err) {
    if (err) {
        console.log('error', 'Failed loading plugin: ratify');
    }
});

Consistency with route configurations

I've been exploring a few different options for automatic schema validation and API documentation. I noticed both lout and hapi-swagger support the following configuration for routes:

server.route({
    method: "GET",
    path: "/something/{somethingId}",
    config: {
        description: "Get's a something",
        tags: ["api"],
        validate: {
            params: {
                somethingId: Joi.string()
            },
            headers: Joi.object({
                "authorization": Joi.string().required()
            }).options({ allowUnknown: true })
        },
        response: {
            schema: Joi.object({

            })
        }
    },
    handler: function (request, reply) {
        // ...
    }
});

But ratify seems to require a different type of configuration structure with everything under a plugins and then a ratify object.

Is there a reason for the different structure? Would it be possible to re-use the same structure that others use that don't specify the ratify plugin explicitly? I mostly just want an easy way to keep things consistent but have the option of changing out documentation and / or validations.

Upgrade dependency: z-schema

Upgrade to latest version (3.x.x).

Some ramifications:

  • no longer an async compile option
  • new constructor options

swagger-js-codegen not working on api doc root

Hi,

I'm not sure if this is an issue with ratify or something else, but when I try to generate code with swagger-js-codegen and pointing to the root documentation generated by ratify it generate an error (TypeError: Cannot read property 'forEach' of undefined)
But if I point to a specific route in the doc the generation works fine, but this would mean I need to generate seperatly each routes witch is not what I want.

This is what I have when pointing to the api-docs:
{ apiVersion: '0.1.0', swaggerVersion: '1.2', apis: [ { path: '/api/1.0.0/users' }, { path: '/api/1.0.0/connexion/checkAndDecodeSession' }, { path: '/api/1.0.0/connexion/login' }, { path: '/api/1.0.0/connexion/logout' } ] }

At least if /api/1.0.0/connexion/* could all be grouped into a single end point /api/1.0.0/connexion. Maybe I'm doing something wrong because I don't understand why they are generated a separated routes. According to my understanding checkAndDecodeSession, login and logout should be methods of connexion endpoint and not individual endpoints. Is there any way to help ratify to defined the correct endpoint ?

Thanks for your help.

findPackageJson in SwaggerManager.js loops infinitely when used in iron-node

This is the function code:

    findPackageJson = function(startingDirectory) {
        if (!startingDirectory) {
            return false;
        }
        if (fs.existsSync(startingDirectory + '/package.json')) {
            return startingDirectory + '/package.json';
        }
        console.log(startingDirectory);
        return findPackageJson(startingDirectory.replace(/\/[^\/]+?$/g, ''));
    },

In my example, the startingDirectory is cli.js.

fs.existsSync(startingDirectory + '/package.json') is false, and then startingDirectory.replace(/\/[^\/]+?$/g, '') is cli.js again.

how to use references

Hi,

I'm trying to use this plugin but I'm new to hapi, nodejs, and json-schema.
I have created 2 files, one describe an element, and the other an array of the first element. I'm trying to use the $ref property but without success.
I'm not sure of what I should put in it. By default I should specify the id of the first element, but as it's not in the same file it's not defined. As the files are not online yet I can't use a fixed url either. So I tried with relative path (./element.json) or with "file://" syntax but with no success yet. Maybe I'm not is the good working directory.

Could someone tell me if such a think is possible with ratify, and how to achieve this ?

Thanks.

Getting invalid register options error

When trying to register the plugin using the following code:

var ratifyOptions = {
  apiVersion: 'v2',
  startingPath: '/api'
};

server.pack.register({ plugin: require('ratify') }, ratifyOptions, function(err) {
  if (err) {
    console.log('error', 'Failed loading plugin: ratify');
  }
});

I get the following error message (removed beginning of paths):

hapi/node_modules/hapi/node_modules/hoek/lib/index.js:425
    throw new Error(msgs.join(' ') || 'Unknown error');
          ^
Error: Invalid register options  {
  "startingPath" [1]: "/api",
  "apiVersion" [2]: "v2"
}

[1] apiVersion is not allowed
[2] startingPath is not allowed
    at Object.exports.assert (/node_modules/hapi/node_modules/hoek/lib/index.js:425:11)
    at Object.exports.assert (/node_modules/hapi/lib/schema.js:15:10)
    at internals.Pack._plugin (/node_modules/hapi/lib/pack.js:271:12)
    at hapi/node_modules/hapi/lib/pack.js:260:14
    at iterate (/node_modules/hapi/node_modules/items/lib/index.js:35:13)
    at Object.exports.serial (/node_modules/hapi/node_modules/items/lib/index.js:38:9)
    at internals.Pack._register (/node_modules/hapi/lib/pack.js:258:11)
    at internals.Pack.register (/node_modules/hapi/lib/pack.js:179:17)
    at Object.<anonymous> (index.js:37:13)
    at Module._compile (module.js:456:26)

The validation that seems to be failing is this one.

Is that the correct way to provide the options?

Node v6 - TypeError thrown when validating parsed querystring objects

Core node module querystring was updated in Node v6 to return plain objects that do not prototypically extend from the JavaScript Object.

According to the Node docs:

This means that the typical Object methods such as obj.toString(), obj.hasOwnProperty(), and others are not defined and will not work.

See Node.js PR here:
nodejs/node#6055

breaking changes list for v5-v6:
https://github.com/nodejs/node/wiki/Breaking-changes-between-v5-and-v6#querystring

As a result of this change, this packages validation of parsed query strings throws an error on this line when the var object is a parsed query string:

https://github.com/mac-/ratify/blob/master/lib/RouteSchemaManager.js#L117

Global sample rate for response validation

Hi,

First of all thanks for this great plugin. I'm just starting to use it and it seems very great.
I have a simple question please, is it possible to set the sample rate for response validation globaly ?

The main reason is that in 90% case I want a 100% validation in test/dev but lower in production. This setting is usally global so it would be great to set it in one single place. This would reduce the code as I won't have to set it on every path but only when I want a specific sample rate (for some critics route maybe).
The same option would be great for failAction.

Thanks a lot.

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.