Skip to content

Atlantis-Software/offshore-validator

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

offshore-validator

npm version Build Status Coverage Status NSP Status Dependencies Status

offshore-validator is a javascript library that lets you define strict types.

This makes it really useful for things like:

  • Form validation on the client or server
  • Ensuring compliance with an API
  • Ensuring that the proper arguments were passed into a function or command-line script
  • Validating objects before storing them in a data store
  • Normalizing polymorphic behaviors

Adds support for strongly typed arguments, like http://lea.verou.me/2011/05/strongly-typed-javascript/, but goes a step further by adding support for array and object sub-validation. It's also the core validation library for the offshore ecosystem.

(Built on top of the great work with https://github.com/chriso/validator.js)

Installation

Client-side

<script type='text/javscript' src="/js/offshore-validator.js"></script>

node.js

npm install offshore-validator

Basic Usage

var offshore-validator = require('offshore-validator');

var userData = 'some string';

// This will guarantee that userData is a string
// If it's not, an error will be thrown
userData = offshore-validator(userData).to('string');

// If you want to handle the error instead of throwing it, use a callback
offshore-validator('something').to("string", function (err) {
  // Err is an error object with a subset of the original data that didn't pass
  // Specifying a callback will prevent an error from being thrown
});

Objects

// Limit data to match these requirements
var requirements = offshore-validator({
  name: 'string',
  avatar: {
    path: 'string'
    name: 'string',
    size: 'int',
    type: 'string'
  }
});

// Unvalidated data from the user
var userData = {
  name: 'Elvis',
  avatar: {
    path: '/tmp/2Gf8ahagjg42.jpg',
    name: '2Gf8ahagjg42.jpg',
    size: 382944
    type: 'image/jpeg'
  }
};

// Verify that the userData at least contains your requirements
// It can have EXTRA keys, but it MUST have the keys you specify
offshore-validator(userData).to(requirements);

Custom rules

Offshore-validator also supports custom validation rules.

// Define a compound validation rule using offshore-validator types
offshore-validator.define('file').as({
  name: 'string',
  type: 'string',
  size: 'int',
  type: 'int'
});

// Define a custom rule using a function
offshore-validator.define('supportedFruit').as(function (fruit) {
  return fruit === 'orange' || fruit === 'apple' || fruit === 'grape';
});


// you can use your new validation rules like any standard offshore-validator data type:
offshore-validator(someUserData).to({
  name: 'string',
  avatar: 'file'
});

offshore-validator(someUserData).to({
  fruit: 'supportedFruit'
});

We bundled a handful of useful defaults:

offshore-validator(someUserData).to({
  id: 'int',
  name: 'string',
  phone: 'phone',
  creditcard: 'creditcard',
  joinDate: 'date',
  email: 'email',
  twitterHandle: 'twitter'
});

The example below demonstrates the complete list of supported default data types:

offshore-validator(userData).to({
  id: 'int',
  name: 'string',
  phone: 'phone',
  creditcard: 'creditcard',
  joinDate: 'date',
  email: 'email',
  twitterHandle: 'twitter',
  homepage: 'url',
  
  // This requires any data
  someData: {},
  
  // This will require a list of >=0 hex colors
  favoriteColors: ['htmlcolor'],
  
  // This will require a list of >=0 badge objects, as defined:
  badges: [{
    name: 'string',
    // This will require a list of privilege objects, as defined:
    privileges: [{
      id: 'int'
      permission: 'string'
    }]
  }]
});

Functions

TODO: Support for functions is incomplete. If you'd like to contribute, please reach out at @balderdashy!

It also has built-in usage to verify the arguments of a function. This lets you be confident that the arguments are what you expect.

$.get = offshore-validator($.get).usage(
  // You can specify multiple usages
  ['urlish',{}, 'function'],
  ['urlish','function'],
  ['urlish',{}],
  ['urlish']
);

// The following usage will throw an error because agasdg is not urlish
$.get('agasdg', {}, function (){})

// You can use the same callback from above in your definition to handle the error yourself
$.get = offshore-validator($.get).usage(
  ['urlish',{}, 'function'],
  ['urlish','function'],
  ['urlish',{}],
  ['urlish'],
  function (err) {
  // Do something about the error here
});

Multiple usages and Argument normalization

But sometimes you want to support several different argument structures.
And to do that, you have to, either explicitly or implicitly, name those arguments so your function can know which one was which, irrespective of how the arguments are specified. Here's how you specify multiple usages:

// This will create a copy of the function that only allows usage that explicitly declares an id
var getById = offshore-validator(
  function (args) {
    // the args object is constructed based on the arguments and usage you define below
    $.get(args.endpoint, {
      id: args.id
    }, args.done);
  })
  
  // Here you can define your named arguments as temporal custom data types, 
  // each with their OWN validation rules
  .args({
    endpoint: 'urlish',
    id: 'int'
    done: 'function'
  })
  
  // To pass valiation, the user arguments must match at least one of the usages below
  // and each argument must pass its validation definition above
  .usage(
    ['endpoint', 'id', 'done'],
    
    // Callback is optional
    ['endpoint', 'id']
  );

Call it any way you want

Now the cool part. You can call your new function any of the following ways:

$.getById('/user',3,cb);
$.getById('/user',3);

Default values

You can also specify default values. If it's not required, if a value listed in defaults is undefined, the default value will be substituted. A value should not have a default AND be required-- one or the other.

Here's an example for an object's keys:

offshore-validator(myObj)
  .to({
    id: 'int'
    name: 'string',
    friends: [{
      id: 'int'
    }]
  })
  .defaults({
    name: 'Anonymous',
    friends: [{
      id: 'int',
      name: 'Anonymous'
    }]
  })

And here's an example for a function's arguments:

offshore-validator(myFunction)
  .args({
    id: 'int',
    options: {}
  })
  .defaults({
    
  }),
  .usage(
    ['id'],
    ['options']
    ['id', 'options']
  );

Asynchronous Usage / Promises

Offshore-validator can also help you normalize your synchronous and asynchronous functions into a uniform api. It allows you to support both last-argument-callback (Node standard) and promise usage out of the box.

TODO

Express/Connect Usage

app.use(require('offshore-validator'));

// ...

function createUser (req,res,next) {
  // Any errors will be handled by Express/Connect
  var params = req.offshore-validator.to({
    id: 'int',
    name: 'string'
  });
  
  
  // Do stuff here
  // This could be anything-- we chose to demonstrate a create method 
  // in this case from our favorite ORM, Offshore (https://github.com/Atlantis-Software/offshore)
   async.auto([
        
      // Create the user itself
      user: User.create(params).done,
      
      // Grant permission for the user to administer itself
      permission: Permission.create({
        targetModel : 'user',
        targetId    : params.id,
        UserId      : params.id,
      }).done
      
    ], function (err, results) {
    
      // Just basic usage, but this prevents you from dealing with non-existent values and null pointers
      // both when providing a consistent API on the server-side 
      // AND when marshalling server-sent data on the client-side
      // i.e. this sucks: user.friends && user.friends.length && user.friends[0] && user.friends[0].id
      var user = res.offshore-validator(results.user).to({
        id: 'int',
        name: 'string',
        email: 'email',
        friends: [{
          id: 'int',
          name: 'string',
          email: 'string'
        }]
      });
      
      // Respond with JSON
      // Could just pass the user object, 
      // but in this case we're demonstrating a custom mapping 
      // (like you might use to implement a custom, predefined API)
      // You can safely know all the .'s you're using won't result in any errors, since you validated this above
      res.json({
        user_id           : user.id,
        user_full_name    : user.name,
        user_email_address: user.email,
        friends           : user.friends
      });
  });
  
}

Tests

npm test

The MIT License (MIT)

Copyright © 2012- Mike McNeil Copyright © 2015- Atlantis Software

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.

Packages

No packages published

Languages

  • JavaScript 100.0%