New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

praetorian

Package Overview
Dependencies
Maintainers
1
Versions
26
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

praetorian - npm Package Compare versions

Comparing version 0.0.2 to 0.0.3

lib/configuration.js

372

lib/praetorian.js
/**
* @name /.js
* @name /lib/praetorian.js
* @description Validates all inputs against a required structure and returns any errors that it finds

@@ -10,36 +10,125 @@ * @since Fri Oct 04 12:20:21 BST 2013

// get some stuff we need to support Praetorian
var _ = require( 'underscore' ), moment = require( 'moment' );
var praetorian = function() {};
var _ = require( 'lodash' ), moment = require( 'moment' );
var praetorian = function( options ) {
// create a shorthand then export the Praetorian object for NodeJS
// default options
this.options = {
automaticTypeConversion: true
}
// if there's any overriding options, blat them over the defaults
if( _.isObject( options ) ) {
this.options = _.extend( this.options, options );
}
};
// create a shorthand then export the Praetorian object for Nodejs
exports = module.exports = praetorian;
praetorian.prototype.errors = [];
praetorian.prototype.validate = function( json, schema, callback ) {
praetorian.prototype.validate = function( data, structure, callback ) {
// grab the options from the protoype and make them available inside this function
var options = this.options;
function check( field, data, message ) {
// store some state for this check() call
// types we accept
var primitiveTypes = {
ARRAY: 'array',
BOOLEAN: 'boolean',
DECIMAL: 'decimal',
INTEGER: 'integer',
OBJECT: 'object',
STRING: 'string',
DATE: 'date' // ISO 8601
}
/*
* An array of objects that define all the errors generated by the json against the schema
*/
var errors = [];
var messagInvalidTypeMustBeArray = 'Invalid type, expected Array';
var messagInvalidTypeMustBeObject = 'Invalid type, expected Object';
var addError = function( element, message ) {
errors.push( { 'element': element, 'message': message } );
}
// little closure to manage rule adherence
var rules = function( data ) {
// state for lifetime of the rules call (rules are chainable)
var check = {
hasError: false,
field: field,
data: data == null || ( isNaN( data ) && data.length == undefined ) ? '' : data + '',
message: message
data: data == null || ( isNaN( data ) && data.length == undefined ) ? '' : data,
error: null
}
return {
isInteger: function() {
// console.log( 'isInteger() on ', check.data );
if( !_.isNumber( check.data ) ) {
check.error = 'Not a number';
}
return this;
},
isString: function() {
console.log( 'isString() check.data', check.data );
// if not a string...
// maybe this should return stuff?
// console.log( 'add an error to Praetorian', self.praetorian.errors.push( 'error' ) );
// console.log( 'isString() on ', check.data );
if( !_.isString( check.data ) ) {
check.error = 'Not a string';
}
return this;
},
isInteger: function() {
console.log( 'isInteger() check.data', check.data );
this.error = 'Not an integer';
isDate: function() {
// console.log( 'isDate() on ', check.data );
// few checks on the date here:
// 1. has the user passed in too many or too few days for the selected month/year combination (factors in leap years)
// 2. is the month too high or low (>12 or <1)
// 3. date must be YYYY-MM-DD format
if( _.isString( check.data ) ) {
var date = check.data.split( '-' );
// must have 3 parts
if( date.length === 3 ) {
var selectedYear = date[0];
// parseInt incase we have preceeding zeros (we don't want em)
var selectedMonth = parseInt( date[1] );
var selectedDay = parseInt( date[2] );
// how long is the selected month?
var daysInMonth = new Date( selectedYear, selectedMonth, 0 ).getDate();
// have we got too many days?
if( selectedDay > daysInMonth ) {
check.error = 'Too many days for month';
} else if( selectedDay < 1 ) {
// have we got too few days?
check.error ='Too few days for month';
}
// check months is not greater than 12...
if( selectedMonth > 12 ) {
check.error = 'Month value too high';
} else if( selectedMonth < 1 ) {
// and not less than 1...
check.error = 'Month value too low';
}
} else {
// split on hyphen failed to produce 3 parts
check.error = 'Invalid date format';
}
} else {
// not a string :/
check.error = 'Invalid date format';
}
return this;
},
go: function() {
return this.error;
lengthBetween: function( min, max ) {
// console.log( 'lengthBetween() on ', check.data );
if ( check.data.length < min ) {
check.error = 'Value is too short';
} else if ( ( max !== undefined ) && ( check.data.length > max ) ) {
check.error = 'Value is too long';
}
return this;
},
execute: function( callback ) {
callback( check.error );
}

@@ -49,14 +138,155 @@ };

// if the schema says the value should be boolean, integer or decimal, make sure it is
var forceType = function( schema, value ) {
// by default, always pass back what came in so if we can't match it, stuff doesn't go missing
var result = value;
switch( schema.type ) {
case 'boolean':
// true booleans can stay as they are, if we match string bools, boolean them up bitches
result = ( value === 'true' || value === 'false' ) ? !!value: value;
break;
case 'integer':
result = !_.isNaN( parseInt( value ) ) ? parseInt( value ): value;
break;
case 'decimal':
result = !_.isNaN( parseFloat( value ) ) ? parseFloat( value ): value;
break;
default:
break;
}
return result;
}
// check all the fields that are "required" by the schema are present
// if we hit a complex type (object, array), only recurse if data is passed for a sub-schema
var requiredJson = function( json, schema, elementPrefix ){
// check json also matches the schema requirements
_.each( schema, function( levelValue, levelKey ) {
// is this field "required" (explicit true, don't want people passing no 1 or "true" shit)
if( ( levelValue.required === true ) && ( _.isUndefined( json[levelKey] ) ) ) {
// BOOO!, #fail
addError( elementPrefix + levelKey, 'Required but not found' );
} else {
// "required" is false or undefined, ignore and continue
}
// if the json actually has a value...
if( json[levelKey] ) {
// schema says type ARRAY and we have "items" or OBJECT and we have "properties" we continue
if( ( !_.isUndefined( levelValue.items ) ) && ( levelValue.type === primitiveTypes.ARRAY ) && _.isArray( json[levelKey] ) ) {
_.each( json[levelKey], function( jsonValue, jsonKey ) {
requiredJson( jsonValue, levelValue.items, elementPrefix + levelKey + '[' + jsonKey + '].' );
} );
} else if( ( !_.isUndefined( levelValue.properties ) ) && ( levelValue.type === primitiveTypes.OBJECT ) && _.isObject( json[levelKey] ) ) {
requiredJson( json[levelKey], levelValue.properties, elementPrefix + levelKey + '.' );
}
}
} );
}
var parseJson = function( json, schema, schemaType, elementPrefix ) {
// for this level, loop over all the json and see if its required by the schema
_.each( json, function( levelValue, levelKey ) {
// is it a node in the schema?
if( !schema[levelKey] ) {
// it's not, can it
delete json[levelKey];
} else {
// only convert types if the consumer is cool with that
if( options.automaticTypeConversion ) {
// type conversion for primitive types that might get passed as a string
json[levelKey] = forceType( schema[levelKey], levelValue );
}
// start doing stuff
if( schema[levelKey].type === primitiveTypes.ARRAY ) {
/* Array */
// check its ACTUALLY an array
if( _.isArray( json[levelKey] ) ) {
// loop each element in the array and parse those as individual objects against their schema
_.each( json[levelKey], function( arrayElement, loopCount ) {
parseJson( arrayElement, schema[levelKey].items, schema[levelKey].type, levelKey + '[' + loopCount + '].' );
} );
} else {
// ERROR: this value should be an ARRAY but it's not (object of some other description?)
addError( elementPrefix + levelKey, messagInvalidTypeMustBeArray );
}
} else if( schema[levelKey].type === primitiveTypes.OBJECT ) {
/* Object */
// must be an object (not null or undefined or anything silly)
if( _.isObject( json[levelKey] ) && !_.isArray( json[levelKey] ) && !_.isNull( json[levelKey] ) && !_.isUndefined( json[levelKey] ) ) {
parseJson( json[levelKey], schema[levelKey].properties, schema[levelKey].type, levelKey + '.' );
} else {
// ERROR: this value should be an OBJECT but its an array
addError( elementPrefix + levelKey, messagInvalidTypeMustBeObject );
}
} else if( ( !_.isUndefined( schema[levelKey].validation ) ) && ( !_.isUndefined( schema[levelKey].validation.rules ) ) ) {
// Handle anything else which has rules here (primitives? string, number...)
_.each( schema[levelKey].validation.rules, function( rule, ruleKey ) {
var validator = new rules( json[levelKey] );
switch( ruleKey ) {
case 'type':
// basic type checking
switch( rule ) {
case 'string':
validator.isString();
break;
case 'integer':
validator.isInteger();
break;
case 'date':
validator.isDate();
break;
}
break;
case 'lengthBetween': // max and min (inclusive)
validator.lengthBetween( rule.min, rule.max );
break;
default:
// case not handled
break;
}
// execute the validation routines and push any errors onto the Praetorian stack
validator.execute( function( err ) {
if( err ){
addError( elementPrefix + levelKey, err );
}
} );
} );
} else {
// no validation found, whatevs...
}
}
} );
}
try {
// console.log( 'Praetorian.check()' );
// console.log( self.data );
// console.log( self.structure );
// ensure data and structure are both objects
if( !_.isObject( data ) ) {
throw new TypeError( 'parameter "data" is not [object]' );
// ensure json and schema are both objects
if( !_.isObject( json ) ) {
throw new TypeError( 'parameter "json" is not [object]' );
}
if( !_.isObject( structure ) ) {
throw new TypeError( 'parameter "structure" is not [object]' );
if( !_.isObject( schema ) ) {
throw new TypeError( 'parameter "schema" is not [object]' );
}

@@ -67,55 +297,41 @@ if( !_.isFunction( callback ) ) {

// -1. Do we treat the whole thing as a stack? Or reusable "function"
// #-1. Do we treat the whole thing as a stack? Or reusable "function"
// 0. benchmark against Doorman for validating a data set against a structure
// 1. remove all junk data (recursive) keys that dont exist in the structure
// 2. ensure items that are present (if REQUIRED) and are of the correct type
// #1. remove all junk data (recursive) keys that dont exist in the structure
// #2. ensure items that are present (if REQUIRED) and are of the correct type
// 3. check for ITEMS
// 4. check for STRUCTURES
// 5. what happens when we find an OBJECT? i.e. roomInfo on POST book
// 6. deal with error handling (pass back up stack, modified data)
// #5. what happens when we find an OBJECT? i.e. roomInfo on POST book
// #6. deal with error handling (pass back up stack, modified data)
// 7. individual validation routines
// 8. config for Praetorian, build Doorman config in to somewhere but can be overridable :/
// 9. isValidDate, isNumber etc, can we use Underscore (isFinite)?
// 10. Identical objects are equal. `0 === -0`, but they aren't identical.
// #10. Identical objects are equal. `0 === -0`, but they aren't identical.
// 11. better error messageing structure (consolidated)
// 12. requirements() for outputting errors from a stack
// 13. figure out what sort of return structures we will provide
// #13. figure out what sort of return structures we will provide
// #14. what is the difference between ARRAY and ITEMS
// #15. list types, OBJECT ARRAY, STRUCTURE, ITEMS, CHECKSUM
// #16. in Revolver, book roomInfo is ARRAY but is passed as [object]...
// 17. requirements needs to recurse (see test/index.js test)
// #18. transpose underscore to lodash
// 19. turn off default type conversion
// 20. rules passed in on construction but don't sit inside Praetorian module, basic types should be basked in + date
// 21. type should always be checked as a validation routine itself, validation then works afterwards
// 22. internationalisation, abstract errors to a lib GB and default settings go to GB
// #23. why does index.js throw 2 errors [location && dave] only when new field "dave" was added?
// console.log( 'this', this );
// loop over all data and remove anything that isnt required
_.each( data, function( data, dataKey ) {
if( _.isUndefined( structure[dataKey] ) ) {
// console.log( dataKey + ' is not a needed, data binned' );
// the key is not required at this level, remove it
delete data[dataKey];
} else if( ( !_.isUndefined( structure[dataKey].validation ) ) && ( !_.isUndefined( structure[dataKey].validation.rules ) ) ) {
// console.log( dataKey + ' is present but no suggested validation' );
// console.log( 'rulez', structure[dataKey].validation.rules , data );
// push these onto an error stack??
console.log( 'go()', check( dataKey, data ).isString().isInteger().go() );
parseJson( json, schema, undefined, '' );
} else {
// "required" but no validation prescribed
}
requiredJson( json, schema, '' );
} );
// console.log( 'final self.hasProblem', this.praetorian.errors );
// check we have all required fields
_.each( structure, function( structure, structureKey ) {
// is this field "required"
if( ( structure['required'] ) && ( _.isUndefined( data[structureKey] ) ) ) {
// BOOO!, #fail
throw "get yo shit together";
} else {
// "required" is false or undefined, ignore and continue
}
} );
// send the decent data back
callback( null, data );
if( errors.length > 0 ) {
// send the errors back
callback( errors );
} else {
// send the decent json back
callback( null, json );
}
} catch ( err ) {

@@ -134,3 +350,3 @@

praetorian.prototype.requirements = function( structure, callback ) {
praetorian.prototype.requirements = function( schema, callback ) {

@@ -140,6 +356,6 @@ try {

var results = [];
if( structure == null ) return results;
if( schema == null ) return results;
function addRequirement( message, field ) {
// returns the field "requirement" details in a consistent structure
// returns the field "requirement" details in a consistent schema
return {

@@ -152,4 +368,4 @@ 'example': message,

if( !_.isObject( structure ) ) {
throw new TypeError( 'parameter "structure" is not [object]' );
if( !_.isObject( schema ) ) {
throw new TypeError( 'parameter "schema" is not [object]' );
}

@@ -160,3 +376,3 @@ if( !_.isFunction( callback ) ) {

_.each( structure, function( field, fieldKey ) {
_.each( schema, function( field, fieldKey ) {
// add some details to the requirements

@@ -163,0 +379,0 @@ results[fieldKey] = addRequirement( ( field.validation && field.validation.example ) ? field.validation.example : 'No example given', field );

{
"name": "praetorian",
"version": "0.0.2",
"description": "A structured JSON parser and validator",
"main": "index.js",
"directories": {
"test": "./test",
"lib": "./lib"
},
"dependencies": {
"underscore": "1.5.x",
"moment": "2.0.0"
},
"devDependencies": {
"vows": "0.7.0"
},
"author": {
"name": "Kevin Hodges",
"email": "kevin.hodges@holidayextras.com"
},
"license": "MIT"
"name": "praetorian",
"version": "0.0.3",
"description": "A structured JSON parser and validator",
"main": "index.js",
"license": "MIT",
"directories": {
"test": "./test",
"lib": "./lib"
},
"dependencies": {
"moment": "2.0.0",
"lodash": "~2.3.0"
},
"devDependencies": {
"vows": "0.7.0"
},
"author": {
"name": "Kevin Hodges",
"email": "kevin.hodges@holidayextras.com"
},
"repository": {
"type": "git",
"url": "http://github.com/kevinhodges/praetorian.git"
}
}
Praetorian
==========
==
Praetorian is a structured JSON validator. It take both a JSON data set and a structure and tells you if anything, whats wrong with it.
Version
--
0.0.2 (in development)
Installation
--
```sh
$ npm install praetorian
```
Schema
--
Example:
```sh
{
"shield": {
"required": true
"validation": {
}
},
"weapon": {
"required": true
"type": "array",
"items": {
"sword": {
"validation": {
}
},
"dagger": {
"required": true
}
}
},
"helmet": {
"type": "object",
"properties": {
"noseGuard": {
"required": true
},
"chinStrap": {
}
}
}
}
```
Notes:
* By specifying "type", Praetorian will automatically cast values
* Objects have "properties"
* Arrays have "items"
* Validation configuration should be used to ensure any type dependencies are met
Usage
--
```sh
var Praetorian = require( '../index' );
praetorian = new Praetorian();
// pass your data and structure in like this
praetorian.validate( data, structure, function( err, data ) {
if( err ) {
console.log( 'check err', err );
// requirements will tell you for the passed structure
// how to fulfill the validation
praetorian.requirements( structure, function( err, data ) {
if( err ) {
// console.log( 'requirements err', err );
} else {
// console.log( 'requirements success', data );
}
} );
} else {
console.log( 'check success', data );
}
} );
```
Testing
--
To run the test harness do the following:
```sh
cd praetorian
node test/sanity.js
```
License
--
[MIT](http://en.wikipedia.org/wiki/MIT_License "MIT")
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc