Socket
Socket
Sign inDemoInstall

joi

Package Overview
Dependencies
Maintainers
3
Versions
238
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

joi - npm Package Compare versions

Comparing version 1.2.3 to 2.0.0

languages/en-us.json

0

CONTRIBUTING.md

@@ -0,0 +0,0 @@ # How to contribute

109

lib/errors.js
// Load modules
var Messages = require('./messages');
var Utils = require('./utils');

@@ -12,38 +11,28 @@

module.exports = internals.Errors = function (object, config) {
exports.process = function (errors, object, options) {
this._object = object;
this._values = [];
config = config || {};
this._messages = new Messages({ languagePath: config.languagePath } );
};
if (!errors || !errors.length) {
return null;
}
var resources = require(options.languagePath);
internals.Errors.prototype.add = function (message, path) {
var values = [];
for (var i = 0, il = errors.length; i < il; ++i) {
var item = errors[i];
var message = Utils.reach(resources, item.type);
if (message) {
message = message.replace(/\{\{\s*([^\s}]+?)\s*\}\}/ig, function (match, name) { return Utils.reach(item.context, name); });
}
else {
message = item.context.key;
}
this._values.push({
message: message,
path: path
});
};
values.push({ message: message, path: item.path });
}
internals.Errors.prototype.addLocalized = function (type, key, replacements, path) {
var message = this._messages.print(type, key, replacements);
Utils.assert(message, 'No message resource found in languages');
this._values.push({
message: message,
path: path
});
return new internals.ValidationError(values, object);
};
internals.Errors.prototype.toError = function () {
return this._values.length > 0 ? new internals.ValidationError(this._values, this._object) : null;
};
internals.ValidationError = function (errors, object) {

@@ -66,7 +55,3 @@

if (message) {
message += ', ';
}
message += error.message;
message += (message ? '. ' : '') + error.message;
});

@@ -82,32 +67,50 @@

var errorObject = Utils.clone(this._object || {});
var hasTemplate = false;
var obj = Utils.clone(this._object || {});
this._errors.forEach(function (error) {
var el = this._errors.length;
for (var e = el - 1; e >= 0; --e) { // Reverse order to process deepest child first
var error = this._errors[e];
var path = error.path.split('.');
var ref = obj;
for (var i = 0, il = path.length; i < il && ref; ++i) {
var seg = path[i];
if (i + 1 === il) {
var value = ref[seg];
if (value !== undefined) {
delete ref[seg];
ref[seg + '_$key$_' + (e + 1) + '_$end$_'] = value;
}
else {
ref['_$miss$_' + seg + '|' + (e + 1) + '_$end$_'] = '__missing__';
}
var paths = error.path.split('.');
var result = errorObject;
for (var i = 0, il = paths.length; i < il; ++i) {
if (i + 1 === il) {
hasTemplate = true;
var pathMessage = typeof result[paths[i]] === 'object' ? JSON.stringify(result[paths[i]]) : result[paths[i]];
result[paths[i]] = '{{red}}' + pathMessage + '{{/}}{{br}}' + error.message + '{{/}}';
break;
}
result = result[paths[i]];
ref = ref[seg];
}
}
var annotated = JSON.stringify(obj, null, 2);
annotated = annotated.replace(/_\$key\$_(\d+)_\$end\$_\"/g, function ($0, $1) {
return '" \u001b[31m[' + $1 + ']\u001b[0m';
});
var annotated = JSON.stringify(errorObject, null, 1);
this.message = annotated.replace(/\"_\$miss\$_([^\|]+)\|(\d+)_\$end\$_\"\: \"__missing__\"/g, function ($0, $1, $2) {
if (hasTemplate) {
annotated = annotated.replace(/\{\{\/\}\}/gi, '\u001b[0m');
annotated = annotated.replace(/\{\{red\}\}/gi, '\u001b[41m');
annotated = annotated.replace(/\{\{br\}\}/gi, '"\n\t"\u001b[31m');
return '\u001b[41m"' + $1 + '"\u001b[0m\u001b[31m [' + $2 + ']: -- missing --\u001b[0m';
});
this.message += '\n\u001b[31m';
for (e = 0; e < el; ++e) {
this.message += '\n[' + (e + 1) + '] ' + this._errors[e].message;
}
this.message = annotated;
this.message += '\u001b[0m';
return this;
};
};

@@ -5,3 +5,3 @@ // Load modules

var Utils = require('./utils');
var Types = require('./types');
var Any = require('./any');

@@ -14,131 +14,66 @@

exports.Types = exports.types = Types;
exports.Utils = exports.utils = Utils;
internals.create = function (Type) {
return function (arg) {
exports.settings = {
skipFunctions: false,
saveConversions: false,
skipConversions: false,
stripExtraKeys: false,
allowExtraKeys: false
return new Type(arg);
};
};
internals.joiConfig = {
skipFunctions: 'boolean',
saveConversions: 'boolean',
skipConversions: 'boolean',
stripExtraKeys: 'boolean',
allowExtraKeys: 'boolean',
languagePath: 'string'
};
exports.any = internals.create(Any);
exports.array = internals.create(require('./array'));
exports.boolean = internals.create(require('./boolean'));
exports.date = internals.create(require('./date'));
exports.func = internals.create(require('./function'));
exports.number = internals.create(require('./number'));
exports.object = internals.create(require('./object'));
exports.string = internals.create(require('./string'));
exports.validate = function (object, config) {
exports.types = exports.Types = {
any: exports.any,
array: exports.array,
boolean: exports.boolean,
date: exports.date,
func: exports.func,
number: exports.number,
object: exports.object,
string: exports.string
};
var settings = Utils.clone(exports.settings);
var localSettings = {};
if (!(config instanceof Types.Base)) {
var names = Object.keys(config);
for (var i = 0, il = names.length; i < il; ++ i) {
// Backwards compatibility
var name = names[i];
exports.types.Any = exports.types.any;
exports.types.Array = exports.types.array;
exports.types.Boolean = exports.types.boolean;
exports.types.Date = exports.types.date;
exports.types.Function = exports.types.func;
exports.types.Number = exports.types.number;
exports.types.Object = exports.types.object;
exports.types.String = exports.types.string;
if (typeof config[name] === internals.joiConfig[name]) {
localSettings[name] = settings[name] = config[name];
delete config[name];
}
}
}
var skipFunctions = settings.skipFunctions;
var stripExtraKeys = settings.stripExtraKeys;
var allowExtraKeys = settings.allowExtraKeys;
var errors = new Errors(object, settings);
exports.validate = function (object, schema, options) {
var processConfig = function () {
Utils.assert(typeof schema === 'object', 'Schema must be an object');
if (config instanceof Types.Base) {
return validateKeyConfig(config, null, object);
}
if (schema instanceof Any) {
return schema.validate(object, options);
}
var unprocessedObject = Utils.clone(object || {});
var keys = Object.keys(config || {});
for (var i = 0, il = keys.length; i < il; ++ i) {
return exports.object(schema).validate(object, options);
};
var key = keys[i];
var keyConfig = config[key];
var value = object ? object[key] : object;
delete unprocessedObject[key];
validateKeyConfig(keyConfig, key, value);
}
exports.describe = function (schema) {
Object.keys(unprocessedObject).forEach(function (unprocessedKey) {
Utils.assert(typeof schema === 'object', 'Schema must be an object');
var unprocessedValueType = typeof unprocessedObject[unprocessedKey];
if ((!skipFunctions || unprocessedValueType !== 'function') && unprocessedValueType !== 'undefined') {
if (stripExtraKeys && allowExtraKeys) {
delete object[unprocessedKey]
}
else if (!allowExtraKeys) {
errors.addLocalized('base.unknown', unprocessedKey, null, unprocessedKey);
}
}
});
};
var validateKeyConfig = function (keyConfig, key, value) {
var converted;
if (Array.isArray(keyConfig)) {
var localErrors = new Errors(object, settings);
for (var i = 0, il = keyConfig.length; i < il; ++i) {
converted = convertType(keyConfig[i], key, value);
if (converted && keyConfig[i].validate(converted.value, object, key, localErrors, key)) {
return true;
}
}
localErrors._values.map(function (error) {
errors.add(error.message, error.path);
});
return false;
}
converted = convertType(keyConfig, key, value);
return converted && keyConfig.validate(converted.value, object, key, errors, key);
};
var convertType = function (keyConfig, key, value) {
var T = Types[keyConfig.type];
var converter = T && T().convert || null;
if (!settings.skipConversions && typeof converter === 'function') {
value = converter(value);
if (settings.saveConversions && key !== null) {
object[key] = value;
}
return { value: value };
}
return { value: value }; // If no convert function then just return the value
};
processConfig();
var names = Object.keys(localSettings);
for (var i = 0, il = names.length; i < il; ++ i) {
var name = names[i];
config[name] = localSettings[name];
if (schema instanceof Any) {
return schema._describe();
}
return errors.toError();
return exports.object(schema)._describe();
};

@@ -26,15 +26,2 @@ // Load modules

exports.mixin = function (self, parent) {
for (var i in parent.prototype) {
if (parent.prototype.hasOwnProperty(i)) {
if (!self.hasOwnProperty(i)) {
self[i] = parent.prototype[i];
}
}
}
return self;
};
exports.inherits = Util.inherits;
{
"name": "joi",
"description": "Object schema validation",
"version": "1.2.3",
"version": "2.0.0",
"repository": "git://github.com/spumko/joi",

@@ -6,0 +6,0 @@ "main": "index",

@@ -1,39 +0,63 @@

<a href="https://github.com/spumko"><img src="https://raw.github.com/spumko/spumko/master/images/from.png" align="right" /></a>
<a href="https://github.com/spumko"><img src="https://raw.github.com/spumko/spumko/master/images/from.png" align="right" /></a>
![joi Logo](https://raw.github.com/spumko/joi/master/images/joi.png)
Object schema validation
Object schema description language and validator for JavaScript objects.
Current version: **2.x.x**
[![Build Status](https://secure.travis-ci.org/spumko/joi.png)](http://travis-ci.org/spumko/joi)
# Table of Contents
## Table of Contents
<img src="https://raw.github.com/spumko/joi/master/images/validation.png" align="right" />
* [Introduction](#introduction "Introduction")
* [Type Registry](#type-registry "Type Registry")
* [Constraints](#constraints "Constraints")
* [BaseType](#basetype "BaseType")
* [String](#string "String")
* [Number](#number "Number")
* [Boolean](#boolean "Boolean")
* [Array](#array "Array")
* [Object](#object "Object")
* [Function](#function "Function")
* [Any](#any "Any")
* [Usage](#usage "Usage")
* [Config Syntax](#config-syntax "Config Syntax")
* [Evaluation Order](#evaluation-order "Evaluation Order")
* [Special Options](#special-options "Special Options")
* [Security Considerations](#security-considerations "Security Considerations")
* [Examples](#examples "Examples")
* [References](#references "References")
* [Reference A: Other Types](#reference-a-other-types "Reference A: Other Types")
- [Example](#example)
- [Usage](#usage)
- [`validate(value, schema, options)`](#validatevalue-schema-options)
- [`any()`](#any)
- [`any.allow(value)`](#anyallowvalue)
- [`any.valid(value)`](#anyvalidvalue)
- [`any.invalid(value)`](#anyinvalidvalue)
- [`any.required()`](#anyrequired)
- [`any.optional()`](#anyoptional)
- [`any.nullOk()`](#anynullok)
- [`any.with(peer)`](#anywithpeer)
- [`any.without(peer)`](#anywithoutpeer)
- [`any.xor(peer)`](#anyxorpeer)
- [`description(desc)`](#descriptiondesc)
- [`any.notes(notes)`](#anynotesnotes)
- [`any.tags(tags)`](#anytagstags)
- [`any.options(options)`](#anyoptionsoptions)
- [`any.strict()`](#anystrict)
- [`any.rename(to, [options])`](#anyrenameto-options)
- [`array()`](#array)
- [`array.emptyOk()`](#arrayemptyok)
- [`array.includes(type)`](#arrayincludestype)
- [`array.excludes(type)`](#arrayexcludestype)
- [`array.min(limit)`](#arrayminlimit)
- [`array.max(limit)`](#arraymaxlimit)
- [`array.length(limit)`](#arraylengthlimit)
- [`boolean()`](#boolean)
- [`date()`](#date)
- [`date.min(date)`](#datemindate)
- [`date.max(date)`](#datemaxdate)
- [`func()`](#func)
- [`number()`](#number)
- [`number.min(limit)`](#numberminlimit)
- [`number.max(limit)`](#numbermaxlimit)
- [`number.integer()`](#numberinteger)
- [`object(schema)`](#objectschema)
- [`string()`](#string)
- [`string.emptyOk()`](#stringemptyok)
- [`string.insensitive()`](#stringinsensitive)
- [`string.min(limit)`](#stringminlimit)
- [`string.max(limit)`](#stringmaxlimit)
- [`string.length(limit)`](#stringlengthlimit)
- [`string.regex(pattern)`](#stringregexpattern)
- [`string.alphanum()`](#stringalphanum)
- [`string.token()`](#stringtoken)
- [`string.email()`](#stringemail)
# Example
# Introduction
The **joi** validation system is used to validate JavaScript objects based on a rich descriptive schema.
Schema validation is the process of ensuring that objects match pre-defined expectations.
For example, the following schema:
```javascript

@@ -43,549 +67,591 @@ var Joi = require('joi');

var schema = {
username: Joi.types.String().alphanum().min(3).max(30).with('birthyear').required(),
password: Joi.types.String().regex(/[a-zA-Z0-9]{3,30}/).without('access_token'),
access_token: Joi.types.String(),
birthyear: Joi.types.Number().min(1850).max(2012),
email: Joi.types.String().email()
username: Joi.string().alphanum().min(3).max(30).with('birthyear').required(),
password: Joi.string().regex(/[a-zA-Z0-9]{3,30}/).without('access_token'),
access_token: [Joi.string(), Joi.number()],
birthyear: Joi.number().integer().min(1900).max(2013),
email: Joi.string().email()
};
var err = Joi.validate({ username: 'abc', birthyear: 1994 }, schema); // err === null -> valid
```
defines these constraints:
* 'username'
The above schema defines the following constraints:
* `username`
* a required string
* must contain only alphanumeric characters
* at least 3 chars long but no more than 30
* must be accompanied by 'birthyear' (logical AND)
* 'password'
* at least 3 characters long but no more than 30
* must be accompanied by `birthyear`
* `password`
* an optional string
* must satisfy the custom regex
* cannot appear together with 'access_token'
* 'access_token'
* an optional, unconstrained string
* 'birthyear'
* an integer between 1850 and 2012
* 'email'
* cannot appear together with `access_token`
* `access_token`
* an optional, unconstrained string or number
* `birthyear`
* an integer between 1900 and 2013
* `email`
* a valid email address string
The above constraints point out some non-obvious features:
* Keys are optional by default
* Strings are by default utf-8 encoded
* relationships are defined in an additive fashion
* "X.join(Y), Y.join(Z)" is the same as requiring all three to be present: "X AND Y AND Z"
* Likewise "X.xor(Y), Y.xor(Z)" => requires that only one of three be present: "X XOR Y XOR Z"
* .regex may or may not override other string-related constraints (.alphanum, .min, .max)
** constraints are evaluated in order
* order of chained functions matter
** ".min(0).max(100).min(1)" sets the min to 1, overwriting the result of the first min call
** if ".regex(/[a-z]{0,3}/)" and ".max(50)" both supplied, only the overlap is valid (length 3 or less = valid)
# Usage
Below is an example of how to validate an object against the above schema:
Usage is a two steps process. First, a schema is constructed using the provided types and constraints:
```javascript
var err = Joi.validate(obj, schema);
// err will be set if the object failed to validate against the schema
var schema = {
a: Joi.string()
};
```
# Type Registry
Then the value is validated against the schema:
The Types object is pre-populated with a mutable list of JavaScript's valid data types. However, for convenience, the registry also includes subset helpers with common constraints already applied. For a list of helpers see the following sections...
```javascript
var err = Joi.validate({ a: 'a string' }, schema);
```
* [String](#string "String")
* [Number](#number "Number")
* [Boolean](#boolean "Boolean")
* [Array](#array "Array")
* [Object](#object "Object")
* [Function](#function "Function")
* [Any](#any "Any")
If the value is valid, `null` is returned, otherwise an `Error` object.
Any custom, user-defined data type is derived from one of the base types (although it may also combine additional types for sub-elements). Thus, there are two valid ways of creating your own types.
The schema can be a plain JavaScript object where every key is assigned a **joi** type, or it can be a **joi** type directly:
The first method is to add the type directly to the Type Registry. This makes the new type explicitly available as a base Type.
```javascript
var IntDef = _.extends({}, Number, function () {
// Constructor
return this.integer();
});
Types.set("Int", IntDef);
var Int = Types.Int;
var schema = Joi.string().min(10);
```
The second, simpler, and more acceptable method is to alias the new Type within the config file.
If the schema is a **joi** type, the `schema.validate(value)` can be called directly on the type. When passing a non-type schema object,
the module converts it internally to an object() type equivalent to:
```javascript
var PositiveInt = Number().integer().min(0)
PositiveInt.max(999999);
var schema = Joi.object({
a: Joi.string()
});
```
Thus, subsequent calls to the new "type" will behave as fully registered types in much less code.
When validating a schema:
* Keys are optional by default.
* Strings are utf-8 encoded by default.
* Rules are defined in an additive fashion and evaluated in order after whitelist and blacklist checks.
*Note: The first method may eventually be deprecated. Then, the Type Registry becomes non-mutable which simplies the logic significantly.*
### `validate(value, schema, options)`
*Note: See "Reference A" before suggesting a pre-included Type for the Type Registry.*
Validates a value using the given schema and options where:
- `value` - the value being validated.
- `schema` - the validation schema. Can be a **joi** type object or a plain object where every key is assigned a **joi** type object.
- `options` - an optional object with the following optional keys:
- `abortEarly` - when `true`, stops validation on the first error, otherwise returns all the errors found. Defaults to `true`.
- `convert` - when `true`, attepmts to cast values to the required types (e.g. a string to a number). Defaults to `true`.
- `modify` - when `true`, converted values are written back to the provided value (only when value is an object). Defaults to `false`.
- `allowUnknown` - when `true`, allows object to contain unknown keys which are ignored. Defaults to `false`.
- `skipFunctions` - when `true`, ignores unknown keys with a function value. Defaults to `false`.
- `stripUnknown` - when `true`, unknown keys are deleted (only when value is an object). Defaults to `false`.
- `languagePath` - the location of the language file used to localize error messages. Defaults to `'languages/en-us.json'`.
## Constraints
Constraints are functions that restrict the input value in some way.
By default, all without explicit constraints, Types are optional.
### Implementation
```javascript
var schema = {
username: Joi.types.String().min(6).max(30).allow('admin').deny('Administrator'),
a: Joi.number()
};
```
The above example demonstrates that even though the username has a minimum length of 6, extra constraints can be appended that allow 'admin' to be used as a username. Likewise, even though 'Administrator' would be allowed by the other constraints, it is explicitly denied by the _'deny'_ constraint.
var value = {
a: '123'
};
### By Type
var err = Joi.validate(value, schema, { modify: true });
#### BaseType
// err -> null
// value.a -> 123 (number, not string)
```
All types inherit the following builtin constraints:
### `any()`
##### BaseType.required()
Generates a schema object that matches any data type.
Specifies that the input may not be undefined (unspecified).
```javascript
var any = Joi.any();
any.valid('a');
##### BaseType.allow(value)
var err = any.validate('a');
```
Specifies that the input may equal this value. This is type specific, so you cannot allow a number on a string type and vice-versa.
#### `any.allow(value)`
This function is idempotent.
Whitelists a value where:
- `value` - the allowed value which can be of any type and will be matched against the validated value before applying any other rules.
`value` can be an array of values, or multiple values can be passed as individual arguments.
*Note: This function does not verify that value is the correct type.*
```javascript
var schema = {
a: Joi.any().allow('a'),
b: Joi.any().allow('b', 'B'),
c: Joi.any().allow(['c', 'C'])
};
```
##### BaseType.deny(value)
#### `any.valid(value)`
Specifies that the input may NOT equal this value.
Adds the provided values into the allowed whitelist and marks them as the only valid values allowed where:
- `value` - the allowed value which can be of any type and will be matched against the validated value before applying any other rules.
`value` can be an array of values, or multiple values can be passed as individual arguments.
This function is idempotent.
```javascript
var schema = {
a: Joi.any().valid('a'),
b: Joi.any().valid('b', 'B'),
c: Joi.any().valid(['c', 'C'])
};
```
*Note: This function does not verify that value is the correct type.*
#### `any.invalid(value)`
##### Basetype.valid(a1[, a2, ...])
Blacklists a value where:
- `value` - the forbidden value which can be of any type and will be matched against the validated value before applying any other rules.
`value` can be an array of values, or multiple values can be passed as individual arguments.
Specifies an arbitrary number of valid values for this input.
```javascript
var schema = {
a: Joi.any().invalid('a'),
b: Joi.any().invalid('b', 'B'),
c: Joi.any().invalid(['c', 'C'])
};
```
If no inputs are supplied, it returns an Error.
#### `any.required()`
If one or more of inputs given do not match the basic type, an Error is raised.
Marks a key as required which will not allow `undefined` as value. All keys are optional by default.
##### Basetype.invalid(a1[, a2, ...])
```javascript
var schema = {
a: Joi.any().required()
};
```
Specifies an arbitrary number of invalid values for this input.
#### `any.optional()`
If no inputs are supplied, it returns an Error.
Marks a key as optional which will allow `undefined` as values. Used to annotate the schema for readability as all keys are optional by default.
If one or more of inputs given do not match the basic type, an Error is raised.
```javascript
var schema = {
a: Joi.any().optional()
};
```
##### BaseType.with(a1[, a2, ...])
#### `any.nullOk()`
Specifies an arbitrary number of inputs that must also be supplied (a1..an) with this input.
Allows `null` as valid value. Note that while `undefined` is a valid value for an optional key, `null` is not and must be explicitly allowed.
*Note: This may or may not have aliases in the final version (.join, .with, .and... etc)*
```javascript
var schema = {
a: Joi.any().nullOk()
};
```
##### BaseType.without(a1[, a2, ...])
#### `any.with(peer)`
Specifies an arbitrary number of inputs that cannot exist alongside this input (logical XOR).
Requires the presence of another key whenever this value is present where:
- `peer` - the required key name that must appear together with the current value. `peer` can be an array of values, or multiple values can be
passed as individual arguments.
*Note: This may or may not have aliases in the final version (.disjoin, .without, .xor... etc)*
```javascript
var schema = {
a: Joi.any().with('b'),
b: Joi.any()
};
```
##### BaseType.nullOk()
#### `any.without(peer)`
Specifies that the value is allowed to be null.
Forbids the presence of another key whenever this value is present where:
- `peer` - the forbidden key name that must not appear together with the current value. `peer` can be an array of values, or multiple values can be
passed as individual arguments.
##### BaseType.rename(to[, options])
```javascript
var schema = {
a: Joi.any().without('b'),
b: Joi.any()
};
```
Specifies a key to rename the current parameter to.
#### `any.xor(peer)`
Options take the form of an object with the follow default values:
Defines an exclusive relationship with another key where this or one of the peers is required but not at the same time where:
- `peer` - the exclusive key name that must not appear together with the current value but where one of them is required. `peer` can be an array
of values, or multiple values can be passed as individual arguments.
```javascript
{
deleteOrig: false,
allowMult: false,
allowOverwrite: false
}
var schema = {
a: Joi.any().xor('b'),
b: Joi.any()
};
```
The option "deleteOrig" specifies whether or not to delete the original key of the param (effectively a permanent "move").
#### `description(desc)`
The option "allowMult" specifies whether or not multiple parameters can be renamed to a single key.
Annotates the key where:
- `desc` - the description string.
The option "allowOverwrite" specifies whether or not the rename function can overwrite existing keys.
```javascript
var schema = Joi.any().description('this key will match anything you give it');
```
#### `any.notes(notes)`
#### String
Annotates the key where:
- `notes` - the notes string or array of strings.
Strings, by default, match JavaScript Strings. They are typically unbounded in length unless limited by interpreter. They are encoded in UTF-8 (this is true in Node.js at least). They may contain any allowable characters in the specified encoding.
```javascript
var schema = Joi.any().notes(['this is special', 'this is important']);
```
The Type Registry's implementation of String also includes some builtin constraints:
#### `any.tags(tags)`
##### String.emptyOk()
Annotates the key where:
- `tags` - the tag string or array of strings.
Specifies that the input may be equal to '' (the empty string).
```javascript
var schema = Joi.any().tags(['api', 'user']);
```
##### String.min(n)
#### `any.options(options)`
Specifies a minimum length for this input string, inclusive.
Overrides the global `validate()` options for the current key and any sub-key where:
- `options` - an object with the same optional keys as [`Joi.validate(value, schema, options)`](#joivalidatevalue-schema-options).
If n is not specified, it returns an Error.
```javascript
var schema = {
a: Joi.any().options({ modify: true })
};
```
If n is not a non-negative integer, it returns an Error.
#### `any.strict()`
##### String.max(n)
Sets the `options.convert` options to `false` which prevent type casting for the current key and any child keys.
Specifies a maximum length for this input string, inclusive.
```javascript
var schema = {
a: Joi.any().strinct()
};
```
If n is not specified, it returns an Error.
#### `any.rename(to, [options])`
If n is not a positive integer, it returns an Error.
Renames a key to another name where:
- `to` - the new key name.
- `options` - an optional object with the following optional keys:
- `move` - if `true`, deletes the old key name, otherwise both old and new keys are kept. Defaults to `false`.
- `multiple` - if `true`, allows renaming multiple keys to the same destination where the last rename wins. Defaults to `false`.
- `override` - if `true`, allows renaming a key over an existing key. Defaults to `false`.
##### String.alphanum()
### `array()`
Specifies that this input may only consist of alphanumeric characters.
Generates a schema object that matches an array data type.
##### String.regex(pattern)
Supports the following subset of the [`any()`](#any) type:
- `required()`
- `optional()`
- `nullOk()`
- `with(peer)`
- `without(peer)`
- `xor(peer)`
- `description(desc)`
- `notes(notes)`
- `tags(tags)`
- `options(options)`
- `rename(to, [options])`
Specifies that this input matches the given RegExp pattern.
```javascript
var array = Joi.array();
array.includes(Joi.string().valid('a', 'b'));
If pattern is not specified, it returns an Error.
var err = array.validate(['a', 'b', 'a']);
```
If pattern is not a valid RegExp object, it returns an error.
#### `array.emptyOk()`
##### String.email()
Allows the value to be assigned an empty array.
Specifies that this input is a valid email string.
```javascript
var schema = {
a: Joi.array().emptyOk()
};
```
##### String.date()
#### `array.includes(type)`
Specifies that this input is a valid Date string (locale string but also accepts unix timestamp in milliseconds).
List the types allowed for the array values where:
- `type` - a **joi** schema object to validate each array item against. `type` can be an array of values, or multiple values can be passed as individual arguments.
##### String.encoding(enc)
```javascript
var schema = {
a: Joi.array().includes(Joi.string(), Joi.number())
};
```
Specifies an explicit encoding for this input string.
#### `array.excludes(type)`
*Warning: This may or may not be included in the final version. A better solution may be to forcibly convert from the encoding specified by enc to utf-8. However, this is not always possible (i.e. UTF-16 converting to UTF-8 would truncate a lot of characters).*
List the types forbidden for the array values where:
- `type` - a **joi** schema object to validate each array item against. `type` can be an array of values, or multiple values can be passed as individual arguments.
```javascript
var schema = {
a: Joi.array().excludes(Joi.object())
};
```
#### Number
#### `array.min(limit)`
##### Number.integer()
Specifies the minimum number of items in the array where:
- `limit` - the lowest number of array items allowed.
Specifies that this input be a valid integer.
```javascript
var schema = {
a: Joi.array().min(2)
};
```
##### Number.float()
#### `array.max(limit)`
Specifies that this input be a valid float or double.
Specifies the maximum number of items in the array where:
- `limit` - the highest number of array items allowed.
##### Number.min(n)
```javascript
var schema = {
a: Joi.array().max(10)
};
```
Specifies a minimum value for this input, inclusive.
#### `array.length(limit)`
If n is not specified, it returns an Error.
Specifies the exact number of items in the array where:
- `limit` - the number of array items allowed.
If n is not an integer, it returns an Error.
```javascript
var schema = {
a: Joi.array().length(5)
};
```
##### Number.max(n)
### `boolean()`
Specifies a maximum value for this input, inclusive.
Generates a schema object that matches a boolean data type (as well as the strings 'true', 'false', 'yes', and 'no').
If n is not specified, it returns an Error.
Supports the same methods of the [`any()`](#any) type.
If n is not an integer, it returns an Error.
```javascript
var boolean = Joi.boolean();
boolean.nullOk();
var err = any.validate(true);
```
#### Boolean
### `date()`
Boolean values accept a case-insensitive string parameter. If the value is "true", true is returned. Otherwise, false is returned.
Generates a schema object that matches a date type (as well as a JavaScript date string or number of milliseconds).
*Note: Boolean has no special methods other than those inherited from BaseType*
Supports the same methods of the [`any()`](#any) type.
```javascript
var date = Joi.date();
date.min('12-20-2012');
#### Array
**Note**
Array values take the querystring form of
var err = date.validate('12-21-2012');
```
?cars=1&cars=2
```
and get converted to
```
{ cars: [ '1', '2' ] }
```
by the server.
*Note: Array has no special methods other than those inherited from BaseType*
#### `date.min(date)`
Specifies the oldest date allowed where:
- `date` - the oldest date allowed.
##### Array.includes(n1, n2, ...)
```javascript
var schema = {
a: Joi.date().min('1-1-1974')
};
```
Specifies allowed types for the array value to include. The values of n1, n2, ... are Type Registry constraints (usually of other types).
#### `date.max(date)`
##### Array.excludes(n1, n2, ...)
Specifies the latest date allowed where:
- `date` - the latest date allowed.
Specifies allowed types for the array value to exclude. The values of n1, n2, ... are Type Registry constraints (usually of other types).
```javascript
var schema = {
a: Joi.date().max('12-31-2020')
};
```
### `func()`
#### Object
Generates a schema object that matches a function type.
##### Object.allowOtherKeys()
Supports the same methods of the [`any()`](#any) type.
Will cause any unknown keys in the object being validated to not cause the object to be invalid.
```javascript
var func = Joi.func();
func.nullOk();
var err = func.validate(function () {});
```
#### Function
### `number()`
Function types accept any value that is a function.
Generates a schema object that matches a number data type (as well as strings that can be converted to numbers).
*Note: Function has no special methods other than those inherited from BaseType*
Supports the same methods of the [`any()`](#any) type.
```javascript
var number = Joi.number();
number.min(1).max(10).integer();
#### Any
var err = number.validate(5);
```
Accept any type of value where the value is not null. By default the value must not be null but is allowed to be undefined. To change this behavior use either the _'required'_ or _'nullOk'_ methods.
#### `number.min(limit)`
*Note: Any has no special methods other than those inherited from BaseType*
Specifies the minimum value where:
- `limit` - the minimum value allowed.
## Usage
```javascript
var schema = {
a: Joi.number().min(2)
};
```
### Config Syntax
#### `number.max(limit)`
In Hapi's routes configuration array, the routes are listed as JavaScript objects. Route objects may include an optional "query" key, the value of which should be an object. This object should associate querystring input names to validation constraints.
Specifies the maximum value where:
- `limit` - the maximum value allowed.
```javascript
var queryObj = {
input_name: constraints
var schema = {
a: Joi.number().max(10)
};
```
In the above code example, "input_name" must conform to typical JavaScript object key constraints (no spaces, no quotes unless escaped and surrounded by quotes, etc).
#### `number.integer()`
In place of "constraints", there should be a combination of constraints. The combination of constraints must be formed starting from a valid base type. The base type may be followed by zero or more pre-defined constraint functions chained consecutively. These combinations can be pre-combined into "alias" variables that may also be followed by zero or more pre-defined constraint functions chained consecutively. An example is shown below:
Requires the number to be an integer (no floating point).
```javascript
Base().constraint_one().constraint_two()...
BaseAlias = Base().constraint()
BaseAlias.constraint_one().constraint_two()...
var schema = {
a: Joi.number().integer()
};
```
Constraint functions may accept optional and arbitrary parameters.
### `object(schema)`
Every call must have its own `Base()` prefix. This creates a new validation object. Otherwise, it will retain settings from any prior constraints.
Generates a schema object that matches an object data type (as well as JSON strings that parsed into objects) where:
- `schema` - optional object where each key is assinged a **joi** type object. If the schema is `{}` no keys allowed.
Defaults to 'undefined' which allows any child key.
### Evaluation Order
Supports the same methods of the [`any()`](#any) type.
#### Overrides
```javascript
var object = Joi.object({
a: number.min(1).max(10).integer()
});
Each constraint is evaluated independantly and in order of chaining. In some cases, a subsequent constraint may override a prior constraint:
```javascript
String.required().optional() # This input will be considered optional
String.required(false).required() # This input will be considered required
var err = object.validate({ a: 5 });
```
Constraints that can override modify the query validation state upon the function's evocation. The actual evaluation is performed at the end of the chain (or once the entire querystring validation is finished).
### `string()`
These constraint functions are special cases:
* required/optional
* with/without
* rename
Generates a schema object that matches a string data type.
Rename is always performed at the end of the chain.
Supports the same methods of the [`any()`](#any) type.
```javascript
var string = Joi.string();
string.min(1).max(10);
###### With/Without
var err = string.validate('12345');
```
Below is an example of a schema that is likely to be used for defining a username/password constraint. Notice that _'with'_ is used on the _'username'_ to indicate that _'password'_ is required to appear whenever _'username'_ exists. Similarly, _'password'_ has a constraint indicating that the key _'access_token'_ must not exist when _'password'_ exists.
#### `string.emptyOk()`
Allows the value to be assigned an empty string.
```javascript
username: Joi.types.String().with('password'),
password: Joi.types.String().without('access_token')
var schema = {
a: Joi.string().emptyOk()
};
```
#### Overrules
#### `string.insensitive()`
Yet, in another case, a prior constraint may overrule a subsequent constraint:
Allows the value to match any whitelist of blacklist item in a case insensitive comparison.
```javascript
Types.String().max(5).max(10) # This input cannot be larger than 5 characters
Types.String().max(3).regex(/.{0,5}/) # This input cannot be larger than 3 characters
var schema = {
a: Joi.string().valid('a').insensitive()
};
```
This should apply to all other constraints that do not override.
#### `string.min(limit)`
Specifies the minimum number string characters where:
- `limit` - the minimum number of string characters required.
```javascript
var schema = {
a: Joi.string().min(2)
};
```
## Special Options
#### `string.max(limit)`
Joi has special settings that will modify certain behaviors.
Specifies the maximum number of string characters where:
- `limit` - the maximum number of string characters allowed.
### Global
```javascript
var schema = {
a: Joi.string().max(10)
};
```
#### Skip Functions
#### `string.length(limit)`
On occasion, an object must be validated which contains functions as properties. To force Joi to ignore validation on such functions, use the `skipFunctions` option:
Specifies the exact string length required where:
- `limit` - the required string length.
Joi.settings.skipFunctions = true;
```javascript
var schema = {
a: Joi.string().length(5)
};
```
#### `string.regex(pattern)`
#### Save Conversions
Defines a regular expression rule where:
- `pattern` - a regular expression object the string value must match against.
Through the process of validation, some inputs will be converted to accommodate the various constraint functions. For example, if an input is of type Joi.Types.Number() but is defined as a string, the validator will convert to Number during validation. This does not persist and does not affect the original input.
```javascript
var schema = {
a: Joi.string().regex(\^[abc]+$\)
};
```
To force Joi to save the conversion, use the `saveConversions` option:
#### `string.alphanum()`
Joi.settings.saveConversions = true;
Requires the string value to only contain a-z, A-Z, and 0-9.
#### Skip Conversions
By default Joi tries to parse and convert object's values into correct type. You might want to disable this behaviour e.g. when you are validating program's internal objects instead of user input.
To force Joi to not convert object values, use the `skipConversions` option:
Joi.settings.skipConversions = true;
#### Allow Extra Keys & Strip Extra Keys
By default Joi will throw an error on keys that are not specified in the configuration object.
To force Joi to not throw errors when it encounters an unknown key, use the `allowExtraKeys` option:
Joi.settings.allowExtraKeys = true;
If you'd like Joi to remove the unknown keys from the object, enable both the `stripExtraKeys` option and the `allowExtraKeys` option:
Joi.settings.allowExtraKeys = true;
Joi.settings.stripExtraKeys = true;
### Local
All global options may be overridden by specifying the option directly on the schema object.
#### Custom Messages
Joi error messages can be updated and replaced with localized versions. Use the `languagePath` option to specify a file path to a JSON file that contains error messages. Each message supports a mustache style template with the following keys:
- `{{key}}` - the schema property that fails validation
- `{{value}}` - the invalid value assigned to the key
- `{{validTypes}}` - an optional list of acceptable inputs
```javascript
var S = Joi.Types.String();
var config = {
username: S().alphanum().min(3).max(30),
password: S().regex(/[a-zA-Z0-9]{3,30}/),
languagePath: Path.join(__dirname, 'languages', 'en-US.json')
var schema = {
a: Joi.string().alphanum()
};
Joi.validate({
username: 'ab',
password: '1'
}, config);
```
#### `string.token()`
### Type-Specific
Requires the string value to only contain a-z, A-Z, 0-9, and underscore _.
#### Short Circuit Breakout
When validating an input for a specific type with lots of constraints, Joi will, by default, return error immediately upon the first error condition. In some rare cases, iterating through all of the constraint functions is actually ideal (to identify all the reasons why an input is invalid at once). To force Joi to evaluate all constraints, use the `shortCircuit` option:
var S = Joi.Types.String();
S.options.shortCircuit = false;
var schema = {
nickname: S().valid('Silly').min(2)
}
schema.nickname.validate('o', null, null, errors) // => populates errors with all failing constraints
// alternative way
var input = { amount: 2.5 };
var schema = { amount: T.Number().integer().min(3).max(5).noShortCircuit() };
Joi.validate(input, schema);
#### Non-Exclusive Valid
The `.valid` constraint is currently exclusive - if the input is NOT one of the values passed to `.valid`, the validator returns false. In the event this is too strict, use the hidden `__allowOnly` option.
var S = Joi.Types.String();
S.__allowOnly = false;
var schema = {
username: S().valid('walmart')
}
schema.username.validate('test') // => this returns true
## Security Considerations
Encodings could potentially play a role in security - some strings in one encoding, when exec()'d in another encoding could execute malicious code. If this type of validation is enabled, it will likely provide little to no explicit protection for developers. Developers could unintentionally (and even worse, unknowingly) expose a significant security risk.
## Examples
### Validating username and password
```javascript
var Joi = require('joi');
var schema = {
username: Joi.types.String().alphanum().min(3).max(30).required(),
password: Joi.types.String().regex(/[a-zA-Z0-9]{3,30}/).required(),
a: Joi.string().token()
};
var invalidObj = { username: 'roger' };
var validObj = { username: 'roger', password: 'pa55word' };
var err = Joi.validate(invalidObj, schema);
if (err) throw err;
var err = Joi.validate(validObj, schema);
if (err) throw err;
```
Executing the above code outputs the following:
```
Error: [ValidationError]: the value of `password` is not allowed to be undefined
```
#### `string.email()`
### Validating a number
Requires the string value to be a valid email address.
```javascript
var Joi = require('joi');
var schema = {
num: Joi.types.Number().required()
a: Joi.string().email()
};
var obj = { num: '1' };
var err = Joi.validate(obj, schema);
if (err) throw err;
else console.log('Success!');
```
Executing the above code outputs the following:
```
Success!
```
## References
### Reference A: Other Types
#### "null"
The "null" variable is considered to be of type "object". An alias could easily be added for this type if necessary. However, for the purposes of querystring validation, this appears to be unnecessary.
#### "undefined"
Unlike null, undefined is its own type with its own special properties. For the purposes of querystring validation, any blank or indefinite inputs will appear as blank strings (""). As far as I know, there is no way to force the undefined object into the querystring. Thus, unless otherwise proven, "undefined" will not be included in the Type Registry.

@@ -20,41 +20,15 @@ // Load modules

var it = Lab.test;
var T = Joi.types;
var Validate = require('./helper');
describe('#validate', function () {
describe('Joi', function () {
var config1 = {
a: Joi.types.Number().min(0).max(3),
b: Joi.types.String().valid('a', 'b', 'c'),
c: Joi.types.String().email().optional()
};
it('validates object', function (done) {
var config2 = {
d: [Joi.types.String(), Joi.types.Boolean()],
e: [Joi.types.Number(), Joi.types.Object()]
};
var schema = {
a: Joi.number().min(0).max(3).without('none'),
b: Joi.string().valid('a', 'b', 'c'),
c: Joi.string().email().optional()
};
var config3 = {
f: [Joi.types.Number(), Joi.types.Boolean()],
g: [Joi.types.String(), Joi.types.Object()]
};
var config4 = {
h: Joi.types.Number(),
i: Joi.types.String(),
j: Joi.types.Object()
};
var config5 = {
txt: Joi.types.String().xor('upc'),
upc: Joi.types.String().xor('txt')
};
var config6 = {
txt: Joi.types.String().required().without('upc'),
upc: Joi.types.String().required().without('txt')
};
it('should validate object successfully', function (done) {
var obj = {

@@ -65,4 +39,4 @@ a: 1,

};
var err = Joi.validate(obj, config1);
var err = Joi.validate(obj, schema);
expect(err).to.not.exist;

@@ -72,6 +46,5 @@ done();

it('should validate null', function (done) {
it('validates null', function (done) {
var err = Joi.validate(null, config1);
var err = Joi.validate(null, Joi.string());
expect(err).to.exist;

@@ -81,25 +54,29 @@ done();

it('should validate xor statements', function (done) {
it('validates xor', function (done) {
expect(Joi.validate({ upc: null }, config5)).to.not.be.null;
expect(Joi.validate({ upc: 'test' }, config5)).to.be.null;
expect(Joi.validate({ txt: null }, config5)).to.not.be.null;
expect(Joi.validate({ txt: 'test' }, config5)).to.be.null;
var schema = Joi.object({
txt: Joi.string().xor('upc'),
upc: Joi.string().xor('txt')
});
var err = Joi.validate({ upc: null, txt: null }, config5);
expect(err).to.not.be.null;
expect(err.message).to.contain('the value of txt must exist without upc');
expect(err.message).to.contain('the value of upc must exist without txt');
var err = Joi.validate({ upc: null, txt: null }, schema, { abortEarly: false });
expect(err.message).to.equal('txt conflict with exclusive peer upc. upc conflict with exclusive peer txt');
expect(Joi.validate({ txt: 'test', upc: 'test' }, config5)).to.not.be.null;
expect(Joi.validate({ txt: 'test', upc: null }, config5)).to.be.null;
expect(Joi.validate({ txt: 'test', upc: '' }, config5)).to.be.null;
expect(Joi.validate({ txt: '', upc: 'test' }, config5)).to.be.null;
expect(Joi.validate({ txt: null, upc: 'test' }, config5)).to.be.null;
expect(Joi.validate({ txt: undefined, upc: 'test' }, config5)).to.be.null;
expect(Joi.validate({ txt: 'test', upc: undefined }, config5)).to.be.null;
expect(Joi.validate({ txt: 'test', upc: '' }, config5)).to.be.null;
expect(Joi.validate({ txt: 'test', upc: null }, config5)).to.be.null;
expect(Joi.validate({ txt: '', upc: undefined }, config5)).to.not.be.null;
expect(Joi.validate({ txt: '', upc: '' }, config5)).to.not.be.null;
Validate(schema, [
[{ upc: null }, false],
[{ upc: 'test' }, true],
[{ txt: null }, false],
[{ txt: 'test' }, true],
[{ txt: 'test', upc: null }, true],
[{ txt: 'test', upc: '' }, true],
[{ txt: '', upc: 'test' }, true],
[{ txt: null, upc: 'test' }, true],
[{ txt: undefined, upc: 'test' }, true],
[{ txt: 'test', upc: undefined }, true],
[{ txt: 'test', upc: '' }, true],
[{ txt: 'test', upc: null }, true],
[{ txt: '', upc: undefined }, false],
[{ txt: '', upc: '' }, false],
[{ txt: 'test', upc: 'test' }, false]
]);

@@ -109,25 +86,26 @@ done();

it('should validate required without statements like xor', function (done) {
it('validates an array of valid types', function (done) {
expect(Joi.validate({ upc: null }, config6)).to.not.be.null;
expect(Joi.validate({ upc: 'test' }, config6)).to.be.null;
expect(Joi.validate({ txt: null }, config6)).to.not.be.null;
expect(Joi.validate({ txt: 'test' }, config6)).to.be.null;
expect(Joi.validate({ upc: null, txt: null }, config6)).to.not.be.null;
var schema = {
auth: [
Joi.object({
mode: Joi.string().valid('required', 'optional', 'try').nullOk()
}).nullOk(),
Joi.string(),
Joi.boolean()
]
};
var err = Joi.validate({ txt: 'test', upc: 'test' }, config6);
expect(err).to.not.be.null;
expect(err.message).to.contain('the value of txt must exist without upc');
expect(err.message).to.contain('the value of upc must exist without txt');
var err = Joi.validate({ auth: { mode: 'none' } }, schema);
expect(err).to.exist;
expect(err.message).to.equal('the value of mode must be one of required, optional, try, null. the value of auth must be a string. the value of auth must be a boolean');
expect(Joi.validate({ txt: 'test', upc: null }, config6)).to.be.null;
expect(Joi.validate({ txt: 'test', upc: '' }, config6)).to.be.null;
expect(Joi.validate({ txt: '', upc: 'test' }, config6)).to.be.null;
expect(Joi.validate({ txt: null, upc: 'test' }, config6)).to.be.null;
expect(Joi.validate({ txt: undefined, upc: 'test' }, config6)).to.be.null;
expect(Joi.validate({ txt: 'test', upc: undefined }, config6)).to.be.null;
expect(Joi.validate({ txt: 'test', upc: '' }, config6)).to.be.null;
expect(Joi.validate({ txt: 'test', upc: null }, config6)).to.be.null;
expect(Joi.validate({ txt: '', upc: undefined }, config6)).to.not.be.null;
expect(Joi.validate({ txt: '', upc: '' }, config6)).to.not.be.null;
Validate(schema, [
[{ auth: { mode: 'try' } }, true],
[{ something: undefined }, false],
[{ auth: { something: undefined } }, false],
[{ auth: null }, true],
[{ auth: true }, true],
[{ auth: 123 }, false]
]);

@@ -137,68 +115,92 @@ done();

it('should validate an array of valid types', function (done) {
it('validates an array of string with valid', function (done) {
var config = {
auth: [
Joi.types.Object({
mode: T.String().valid('required', 'optional', 'try').nullOk()
}).nullOk(),
T.String().nullOk(),
T.Boolean().nullOk()
]
var schema = {
brand: Joi.array().includes(Joi.string().valid('amex', 'visa'))
};
var err = Joi.validate({ auth: { mode: 'none' } }, config);
expect(err).to.not.be.null;
expect(Joi.validate({ brand: ['amex'] }, schema)).to.not.exist;
expect(Joi.validate({ brand: ['visa', 'mc'] }, schema)).to.exist;
done();
});
expect(err.message).to.contain('the value of mode must be one of undefined, try, optional, required, null');
expect(err.message).to.contain('the value of auth must be a string');
expect(err.message).to.contain('the value of auth must be a boolean');
it('validates pre and post convert value', function (done) {
expect(Joi.validate({ auth: { mode: 'try' } }, config)).to.be.null;
expect(Joi.validate({ something: undefined }, config)).to.be.null;
expect(Joi.validate({ auth: { something: undefined } }, config)).to.be.null;
var schema = Joi.number().valid(5);
Validate(schema, [
[5, true],
['5', true]
]);
done();
});
it('should validate config where the root item is a joi type', function (done) {
it('does not change object when validation fails', function (done) {
expect(Joi.validate(true, T.Boolean().nullOk())).to.be.null;
expect(Joi.validate({ auth: { mode: 'try' } }, T.Object())).to.be.null;
var schema = {
a: Joi.number().valid(2)
};
var err = Joi.validate(true, T.Object());
expect(err).to.not.be.null;
expect(err.message).to.contain('the value of <root> must be an object');
var obj = {
a: '5'
};
err = Joi.validate(true, T.String());
expect(err).to.not.be.null;
expect(err.message).to.contain('the value of <root> must be a string');
expect(Joi.validate(obj, schema, { modify: true })).to.exist;
expect(obj.a).to.equal('5');
done();
});
expect(Joi.validate('test@test.com', T.String().email())).to.be.null;
expect(Joi.validate({ param: 'item'}, T.Object({ param: T.String().required() }, true))).to.be.null;
it('invalidates pre and post convert value', function (done) {
var schema = Joi.number().invalid(5);
Validate(schema, [
[5, false],
['5', false]
]);
done();
});
it('should validate config where the root item is a joi Object and saveConversions setting is enabled', function (done) {
Joi.settings.saveConversions = true;
it('invalidates missing peers', function (done) {
var config = T.Object({
a: T.String()
});
var schema = {
username: Joi.string().with('password'),
password: Joi.string().without('access_token')
};
expect(Joi.validate({ a: 'okay' }, config)).to.be.null;
var err = Joi.validate({ username: 'bob' }, schema);
expect(err).to.exist;
done();
});
Joi.settings.saveConversions = false;
it('validates config where the root item is a joi type', function (done) {
expect(Joi.validate(true, Joi.boolean().nullOk())).to.be.null;
expect(Joi.validate({ auth: { mode: 'try' } }, Joi.object())).to.be.null;
var err = Joi.validate(true, Joi.object());
expect(err.message).to.contain('the value of <root> must be an object');
err = Joi.validate(true, Joi.string());
expect(err.message).to.contain('the value of <root> must be a string');
expect(Joi.validate('test@test.com', Joi.string().email())).to.be.null;
expect(Joi.validate({ param: 'item' }, Joi.object({ param: Joi.string().required() }))).to.be.null;
done();
});
it('should support setting the saveConversions setting locally', function (done) {
it('validates config where the root item is a joi Object and modify setting is enabled', function (done) {
expect(Joi.settings.saveConversions).to.equal(false);
var config = Joi.object({
a: Joi.string()
});
expect(Joi.validate({ a: 'okay' }, config, { modify: true })).to.be.null;
done();
});
it('converts string to number in a schema', function (done) {
var config = {
a: T.Number(),
saveConversions: true
a: Joi.number()
};

@@ -209,48 +211,55 @@

expect(Joi.validate(original, config)).to.be.null;
expect(Joi.validate(original, config, { modify: true })).to.be.null;
expect(validated).to.deep.equal(original);
done();
});
expect(Joi.settings.saveConversions).to.equal(false);
it('moves a key', function (done) {
var schema = {
a: Joi.number().rename('b', { move: true })
};
var obj = { a: 10 };
var err = Joi.validate(obj, schema);
expect(err).to.not.exist;
expect(obj.a).to.not.exist;
expect(obj.b).to.equal(10);
done();
});
it('should not alter valid top level objects when saveConversions setting is enabled', function (done) {
Joi.settings.saveConversions = true;
it('does not alter valid top level objects when modify setting is enabled', function (done) {
var config = T.Object({
a: T.String()
var config = Joi.object({
a: Joi.string()
});
var original = { a: 'okay' };
var original = { a: 'okay' };
var validated = { a: 'okay' };
expect(Joi.validate(validated, config)).to.be.null;
expect(Joi.validate(validated, config, { modify: true })).to.be.null;
expect(validated).to.deep.equal(original);
Joi.settings.saveConversions = false;
done();
});
it('should allow unknown keys in objects if no schema was given', function (done) {
it('allows unknown keys in objects if no schema was given', function (done) {
expect(Joi.validate({ foo: 'bar' }, T.Object())).to.not.exist;
expect(Joi.validate({ foo: 'bar' }, Joi.object())).to.not.exist;
done();
});
it('should fail on unkown keys in objects if a schema was given', function (done) {
it('fails on unknown keys in objects if a schema was given', function (done) {
var err = Joi.validate({ foo: 'bar' }, T.Object({}));
var err = Joi.validate({ foo: 'bar' }, Joi.object({}));
expect(err).to.exist;
expect(err.message).to.contain('the key (foo) is not allowed');
expect(err.message).to.equal('the keys foo are not allowed');
err = Joi.validate({ foo: 'bar' }, {});
expect(err).to.exist;
expect(err.message).to.contain('the key (foo) is not allowed');
expect(err.message).to.equal('the keys foo are not allowed');
err = Joi.validate({ foo: 'bar' }, { other: T.Number() });
err = Joi.validate({ foo: 'bar' }, { other: Joi.number() });
expect(err).to.exist;
expect(err.message).to.contain('the key (foo) is not allowed');
expect(err.message).to.equal('the keys foo are not allowed');

@@ -260,7 +269,7 @@ done();

it('should validate an unknown option', function (done) {
it('validates an unknown option', function (done) {
var config = {
auth: Joi.types.Object({
mode: T.String().valid('required', 'optional', 'try').nullOk()
auth: Joi.object({
mode: Joi.string().valid('required', 'optional', 'try').nullOk()
}).nullOk()

@@ -271,7 +280,7 @@ };

expect(err).to.not.be.null;
expect(err.message).to.contain('the key (unknown) is not allowed');
expect(err.message).to.contain('the keys unknown are not allowed');
err = Joi.validate({ something: false }, config);
expect(err).to.not.be.null;
expect(err.message).to.contain('the key (something) is not allowed');
expect(err.message).to.contain('the keys something are not allowed');

@@ -281,42 +290,2 @@ done();

it('should work with complex configs', function (done) {
var config = {
handler: [T.Object(), T.Function(), T.String().valid('notFound')],
payload: T.String().valid('stream', 'raw', 'parse').nullOk(),
response: T.Object({
schema: T.Object().nullOk(),
sample: T.Number(),
failAction: T.String().valid('error', 'log', 'ignore')
}).nullOk().allow(true).allow(false),
auth: [
T.Object({
mode: T.String().valid(['required', 'optional', 'try']).nullOk(),
scope: T.String().nullOk(),
tos: T.Number().nullOk(),
entity: T.String().nullOk(),
strategy: T.String().nullOk(),
strategies: T.Array().nullOk(),
payload: T.String().nullOk()
}).nullOk(),
T.Boolean().allow(false).nullOk(),
T.String().nullOk()
],
cache: T.Object({
mode: T.String().valid(['server+client', 'client+server', 'client', 'server']),
segment: T.String(),
privacy: T.String().valid('default', 'public', 'private'),
expiresIn: T.Number().xor('expiresAt'),
expiresAt: T.String(),
staleIn: T.Number().with('staleTimeout'),
staleTimeout: T.Number().with('staleIn')
}).nullOk()
};
expect(Joi.validate({ payload: 'raw' }, config)).to.be.null;
expect(Joi.validate({ auth: { mode: 'required', payload: 'required' }, payload: 'raw' }, config)).to.be.null;
expect(Joi.validate({ handler: internals.item, cache: { expiresIn: 20000, staleIn: 10000, staleTimeout: 500 } }, config)).to.be.null;
done();
});
it('validates required key with multiple options', function (done) {

@@ -326,7 +295,7 @@

module: [
T.Object({
compile: T.Function().required(),
execute: T.Function()
Joi.object({
compile: Joi.func().required(),
execute: Joi.func()
}).required(),
T.String().required()
Joi.string().required()
]

@@ -351,7 +320,7 @@ };

it('should not require optional numbers', function (done) {
it('does not require optional numbers', function (done) {
var config = {
position: T.Number(),
suggestion: T.String()
position: Joi.number(),
suggestion: Joi.string()
};

@@ -365,7 +334,7 @@

it('should not require optional objects', function (done) {
it('does not require optional objects', function (done) {
var config = {
position: T.Number(),
suggestion: T.Object()
position: Joi.number(),
suggestion: Joi.object()
};

@@ -379,4 +348,9 @@

it('should validate object successfully when config has an array of types', function (done) {
it('validates object successfully when config has an array of types', function (done) {
var schema = {
f: [Joi.number(), Joi.boolean()],
g: [Joi.string(), Joi.object()]
};
var obj = {

@@ -386,3 +360,3 @@ f: true,

};
var err = Joi.validate(obj, config3);
var err = Joi.validate(obj, schema);

@@ -393,4 +367,10 @@ expect(err).to.not.exist;

it('should validate object successfully when config allows for optional key and key is missing', function (done) {
it('validates object successfully when config allows for optional key and key is missing', function (done) {
var schema = {
h: Joi.number(),
i: Joi.string(),
j: Joi.object()
};
var obj = {

@@ -400,3 +380,3 @@ h: 12,

};
var err = Joi.validate(obj, config4);
var err = Joi.validate(obj, schema);

@@ -407,4 +387,10 @@ expect(err).to.not.exist;

it('should fail validation', function (done) {
it('fails validation', function (done) {
var schema = {
a: Joi.number().min(0).max(3),
b: Joi.string().valid('a', 'b', 'c'),
c: Joi.string().email().optional()
};
var obj = {

@@ -415,3 +401,3 @@ a: 10,

};
var err = Joi.validate(obj, config1);
var err = Joi.validate(obj, schema);

@@ -422,4 +408,10 @@ expect(err).to.exist;

it('should fail validation when the wrong types are supplied', function (done) {
it('fails validation when the wrong types are supplied', function (done) {
var schema = {
a: Joi.number().min(0).max(3),
b: Joi.string().valid('a', 'b', 'c'),
c: Joi.string().email().optional()
};
var obj = {

@@ -430,3 +422,3 @@ a: 'a',

};
var err = Joi.validate(obj, config1);
var err = Joi.validate(obj, schema);

@@ -437,3 +429,3 @@ expect(err).to.exist;

it('should fail validation when missing a required parameter', function (done) {
it('fails validation when missing a required parameter', function (done) {

@@ -443,3 +435,3 @@ var obj = {

};
var err = Joi.validate(obj, { a: Joi.types.String().required() });
var err = Joi.validate(obj, { a: Joi.string().required() });

@@ -450,3 +442,3 @@ expect(err).to.exist;

it('should fail validation when missing a required parameter within an object config', function (done) {
it('fails validation when missing a required parameter within an object config', function (done) {

@@ -456,3 +448,3 @@ var obj = {

};
var err = Joi.validate(obj, { a: Joi.types.Object({ b: Joi.types.String().required() }) });
var err = Joi.validate(obj, { a: Joi.object({ b: Joi.string().required() }) });

@@ -463,8 +455,8 @@ expect(err).to.exist;

it('should fail validation when parameter is required to be an object but is given as string', function (done) {
it('fails validation when parameter is required to be an object but is given as string', function (done) {
var obj = {
a: "a string"
a: 'a string'
};
var err = Joi.validate(obj, { a: Joi.types.Object({ b: Joi.types.String().required() }) });
var err = Joi.validate(obj, { a: Joi.object({ b: Joi.string().required() }) });
expect(err).to.exist;

@@ -474,3 +466,3 @@ done();

it('should pass validation when parameter is required to be an object and is given correctly as a json string', function (done) {
it('validates when parameter is required to be an object and is given correctly as a json string', function (done) {

@@ -480,3 +472,3 @@ var obj = {

};
var err = Joi.validate(obj, { a: Joi.types.Object({ b: Joi.types.String().required() }) });
var err = Joi.validate(obj, { a: Joi.object({ b: Joi.string().required() }) });
expect(err).to.be.null;

@@ -486,3 +478,3 @@ done();

it('should fail validation when parameter is required to be an object but is given as a json string that is incorrect (number instead of string)', function (done) {
it('fails validation when parameter is required to be an object but is given as a json string that is incorrect (number instead of string)', function (done) {

@@ -492,3 +484,3 @@ var obj = {

};
var err = Joi.validate(obj, { a: Joi.types.Object({ b: Joi.types.String().required() }) });
var err = Joi.validate(obj, { a: Joi.object({ b: Joi.string().required() }) });
expect(err).to.exist;

@@ -498,3 +490,3 @@ done();

it('should fail validation when parameter is required to be an Array but is given as string', function (done) {
it('fails validation when parameter is required to be an Array but is given as string', function (done) {

@@ -504,3 +496,3 @@ var obj = {

};
var err = Joi.validate(obj, { a: Joi.types.Array() });
var err = Joi.validate(obj, { a: Joi.array() });
expect(err).to.exist;

@@ -510,3 +502,3 @@ done();

it('should pass validation when parameter is required to be an Array and is given correctly as a json string', function (done) {
it('validates when parameter is required to be an Array and is given correctly as a json string', function (done) {

@@ -516,3 +508,3 @@ var obj = {

};
var err = Joi.validate(obj, { a: Joi.types.Array() });
var err = Joi.validate(obj, { a: Joi.array() });
expect(err).to.be.null;

@@ -522,3 +514,3 @@ done();

it('should fail validation when parameter is required to be an Array but is given as a json that is incorrect (object instead of array)', function (done) {
it('fails validation when parameter is required to be an Array but is given as a json that is incorrect (object instead of array)', function (done) {

@@ -528,3 +520,3 @@ var obj = {

};
var err = Joi.validate(obj, { a: Joi.types.Object({ b: Joi.types.String().required() }) });
var err = Joi.validate(obj, { a: Joi.object({ b: Joi.string().required() }) });
expect(err).to.exist;

@@ -534,4 +526,9 @@ done();

it('should fail validation when config is an array and fails', function (done) {
it('fails validation when config is an array and fails', function (done) {
var schema = {
d: [Joi.string(), Joi.boolean()],
e: [Joi.number(), Joi.object()]
};
var obj = {

@@ -541,3 +538,3 @@ d: 10,

};
var err = Joi.validate(obj, config2);
var err = Joi.validate(obj, schema);

@@ -548,4 +545,9 @@ expect(err).to.exist;

it('should fail validation when config is an array and fails with extra keys', function (done) {
it('fails validation when config is an array and fails with extra keys', function (done) {
var schema = {
d: [Joi.string(), Joi.boolean()],
e: [Joi.number(), Joi.object()]
};
var obj = {

@@ -555,3 +557,3 @@ a: 10,

};
var err = Joi.validate(obj, config2);
var err = Joi.validate(obj, schema);

@@ -562,10 +564,13 @@ expect(err).to.exist;

it('should fail validation with extra keys', function (done) {
it('fails validation with extra keys', function (done) {
var schema = {
a: Joi.number(),
};
var obj = {
a: 1,
b: 'a',
d: 'c'
};
var err = Joi.validate(obj, config1);
var err = Joi.validate(obj, schema);

@@ -576,9 +581,9 @@ expect(err).to.exist;

it('should pass string validation of missing optional key with regex string condition', function (done) {
it('validates missing optional key with string condition', function (done) {
var rules = {
key: Joi.types.String().alphanum(false).min(8)
var schema = {
key: Joi.string().alphanum(false).min(8)
};
var err = Joi.validate({}, rules);
var err = Joi.validate({}, schema);
expect(err).to.not.exist;

@@ -588,6 +593,9 @@ done();

it('should pass validation with extra keys and remove them when stripExtraKeys is set', function (done) {
it('validates with extra keys and remove them when stripUnknown is set', function (done) {
Joi.settings.stripExtraKeys = true;
Joi.settings.allowExtraKeys = true;
var schema = {
a: Joi.number().min(0).max(3),
b: Joi.string().valid('a', 'b', 'c'),
c: Joi.string().email().optional()
};

@@ -599,16 +607,16 @@ var obj = {

};
var err = Joi.validate(obj, config1);
var err = Joi.validate(obj, schema, { stripUnknown: true, allowUnknown: true });
expect(err).to.be.null;
expect(obj).to.deep.equal({a: 1, b: 'a'});
Joi.settings.stripExtraKeys = false;
Joi.settings.allowExtraKeys = false;
expect(obj).to.deep.equal({ a: 1, b: 'a' });
done();
});
it('should pass validation with extra keys when allowExtraKeys is set', function (done) {
it('should pass validation with extra keys when allowUnknown is set', function (done) {
Joi.settings.allowExtraKeys = true;
var schema = {
a: Joi.number().min(0).max(3),
b: Joi.string().valid('a', 'b', 'c'),
c: Joi.string().email().optional()
};

@@ -620,20 +628,14 @@ var obj = {

};
var err = Joi.validate(obj, config1);
var err = Joi.validate(obj, schema, { allowUnknown: true });
expect(err).to.be.null;
expect(obj).to.deep.equal({a: 1, b: 'a', d: 'c'});
Joi.settings.allowExtraKeys = false;
expect(obj).to.deep.equal({ a: 1, b: 'a', d: 'c' });
done();
});
it('should pass validation with extra keys set locally', function (done) {
it('should pass validation with extra keys set', function (done) {
expect(Joi.settings.stripExtraKeys).to.equal(false);
var localConfig = {
a: Joi.types.Number().min(0).max(3),
b: Joi.types.String().valid('a', 'b', 'c'),
allowExtraKeys: true
a: Joi.number().min(0).max(3),
b: Joi.string().valid('a', 'b', 'c'),
};

@@ -646,14 +648,11 @@

};
var err = Joi.validate(obj, localConfig);
var err = Joi.validate(obj, localConfig, { allowUnknown: true });
expect(err).to.be.null;
expect(obj).to.deep.equal({a: 1, b: 'a', d: 'c'});
expect(Joi.settings.stripExtraKeys).to.equal(false);
expect(obj).to.deep.equal({ a: 1, b: 'a', d: 'c' });
err = Joi.validate(obj, localConfig);
err = Joi.validate(obj, localConfig, { allowUnknown: true });
expect(err).to.be.null;
expect(obj).to.deep.equal({a: 1, b: 'a', d: 'c'});
expect(Joi.settings.stripExtraKeys).to.equal(false);
expect(obj).to.deep.equal({ a: 1, b: 'a', d: 'c' });
done();

@@ -665,9 +664,5 @@ });

expect(Joi.settings.stripExtraKeys).to.equal(false);
var localConfig = {
a: Joi.types.Number().min(0).max(3),
b: Joi.types.String().valid('a', 'b', 'c'),
stripExtraKeys: true,
allowExtraKeys: true
a: Joi.number().min(0).max(3),
b: Joi.string().valid('a', 'b', 'c')
};

@@ -680,13 +675,11 @@

};
var err = Joi.validate(obj, localConfig);
var err = Joi.validate(obj, localConfig, { stripUnknown: true, allowUnknown: true });
expect(err).to.be.null;
expect(obj).to.deep.equal({a: 1, b: 'a'});
expect(Joi.settings.stripExtraKeys).to.equal(false);
expect(obj).to.deep.equal({ a: 1, b: 'a' });
err = Joi.validate(obj, localConfig);
err = Joi.validate(obj, localConfig, { stripUnknown: true, allowUnknown: true });
expect(err).to.be.null;
expect(obj).to.deep.equal({a: 1, b: 'a'});
expect(Joi.settings.stripExtraKeys).to.equal(false);
expect(obj).to.deep.equal({ a: 1, b: 'a' });

@@ -698,4 +691,3 @@ done();

Joi.settings.skipFunctions = true;
var schema = { username: Joi.types.String() };
var schema = Joi.object({ username: Joi.string() }).options({ skipFunctions: true });
var input = { username: 'test', func: function () { } };

@@ -705,3 +697,2 @@ var err = Joi.validate(input, schema);

expect(err).to.not.exist;
Joi.settings.skipFunctions = false;
done();

@@ -712,31 +703,27 @@ });

Joi.settings.skipFunctions = false;
var schema = { username: Joi.types.String() };
var schema = { username: Joi.string() };
var input = { username: 'test', func: function () { } };
var err = Joi.validate(input, schema);
var err = Joi.validate(input, schema, { skipFunctions: false });
expect(err).to.exist;
expect(err.message).to.contain('the key (func) is not allowed');
expect(err.message).to.contain('the keys func are not allowed');
done();
});
it('should work when the saveConversions setting is enabled', function (done) {
it('should work when the modify setting is enabled', function (done) {
Joi.settings.saveConversions = true;
var schema = { item: Joi.types.Number() };
var schema = { item: Joi.number() };
var input = { item: '1' };
var err = Joi.validate(input, schema);
var err = Joi.validate(input, schema, { modify: true });
expect(err).to.not.exist;
expect(input.item).to.equal(1);
Joi.settings.saveConversions = false;
done();
});
it('should work when the saveConversions setting is disabled', function (done) {
it('should work when the modify setting is disabled', function (done) {
Joi.settings.saveConversions = false;
var schema = { item: Joi.types.Number() };
var schema = { item: Joi.number() };
var input = { item: '1' };
var err = Joi.validate(input, schema);
var err = Joi.validate(input, schema, { modify: false });

@@ -748,23 +735,10 @@ expect(err).to.not.exist;

it('should display correct processed pluralization messsage when skipFunctions is enabled', function (done) {
it('should not convert values when convert is false', function (done) {
Joi.settings.skipFunctions = true;
var schema = { username: Joi.types.String() };
var input = { username: 'test', item1: 'test', 'item2': 'test' };
var err = Joi.validate(input, schema);
expect(err).to.exist;
Joi.settings.skipFunctions = false;
done();
});
it('should not convert values when skipConversions is set', function (done) {
Joi.settings.skipConversions = true;
var schema = {
arr: Joi.types.Array().includes(Joi.types.String())
arr: Joi.array().includes(Joi.string())
};
var input = { arr: 'foo' };
var err = Joi.validate(input, schema);
var err = Joi.validate(input, schema, { convert: false });

@@ -775,37 +749,34 @@ expect(err).to.exist;

it('should fail validation when a child object has an invalid string value but object traversal isn\'t complete', function (done) {
it('full errors when abortEarly is false', function (done) {
var input = { method: 'GET', path: '/', config: { payload: 'something' } };
var err = Joi.validate(input, internals.routeSchema);
var schema = {
a: Joi.string(),
b: Joi.string()
};
expect(err).to.exist;
done();
});
var input = { a: 1, b: 2 };
it('validation errors should provide an annotated message when making the error annotated', function (done) {
var errOne = Joi.validate(input, schema);
var errFull = Joi.validate(input, schema, { abortEarly: false });
var input = { method: 'GET', path: '/', config: { payload: 'something' } };
var err = Joi.validate(input, internals.routeSchema);
err.annotated();
expect(err.message).to.contain('\u001b[0m');
expect(errOne).to.exist
expect(errFull).to.exist
expect(errFull._errors.length).to.be.greaterThan(errOne._errors.length);
done();
});
it('there should be more validation errors when short circuit is disabled', function (done) {
it('supports custom errors when validating types', function (done) {
var input = { a: 1, b: 2 };
var schema = {
email: Joi.string().email(),
date: Joi.date(),
alphanum: Joi.string().alphanum(),
min: Joi.string().min(3),
max: Joi.string().max(3),
required: Joi.string().required().without('xor'),
xor: Joi.string().without('required'),
renamed: Joi.string().rename('required').valid('456'),
notEmpty: Joi.string().required()
};
var resultWithShortCircuit = Joi.validate(input, T.Object({}));
var resultWithoutShortCircuit = Joi.validate(input, T.Object({}).noShortCircuit());
expect(resultWithShortCircuit).to.exist
expect(resultWithoutShortCircuit).to.exist
expect(resultWithoutShortCircuit._errors.length).to.be.greaterThan(resultWithShortCircuit._errors.length);
done();
});
it('should support custom errors when validating types', function (done) {
var input = {

@@ -823,44 +794,221 @@ email: 'invalid-email',

var err = Joi.validate(input, schema, { abortEarly: false, languagePath: Path.join(__dirname, 'languages', 'en-us.json') });
expect(err).to.exist;
expect(err.message).to.equal('19. 18. 16. 14. 15. 7. 7. 11. 3');
done();
});
it('returns key when language file missing item', function (done) {
var input = {
notEmpty: ''
};
var schema = {
email: T.String().email(),
date: T.String().date(),
alphanum: T.String().alphanum(),
min: T.String().min(3),
max: T.String().max(3),
required: T.String().required().without('xor'),
xor: T.String().without('required'),
renamed: T.String().rename('required'),
notEmpty: T.String().required(),
languagePath: Path.join(__dirname, 'languages', 'en-US.json')
notEmpty: Joi.string().required()
};
var err = Joi.validate(input, schema);
var err = Joi.validate(input, schema, { languagePath: Path.join(__dirname, 'languages', 'empty.json') });
expect(err).to.exist;
expect(err.message).to.contain('The `email` field must be a valid e-mail address.');
expect(err.message).to.contain('The `date` field must be a valid date.');
expect(err.message).to.contain('The `alphanum` field failed one or more validation constraints.');
expect(err.message).to.contain('The `min` field must be at least 3 characters long.');
expect(err.message).to.contain('The `max` field may not exceed 3 characters.');
expect(err.message).to.contain('The `required` field must be omitted if `xor` is specified.');
expect(err.message).to.contain('`required` is already assigned to the `renamed` field.');
expect(err.message).to.contain('Invalid value for `notEmpty`: `empty`.');
expect(err.message).to.equal('notEmpty');
done();
});
it('annotates error', function (done) {
var object = {
a: 'm',
y: {
b: {
c: 10
}
}
};
var schema = {
a: Joi.string().valid('a', 'b', 'c', 'd'),
y: Joi.object({
u: Joi.string().valid(['e', 'f', 'g', 'h']).required(),
b: Joi.string().valid('i', 'j').allow(false),
d: Joi.object({
x: Joi.string().valid('k', 'l').required(),
c: Joi.number()
})
})
};
var err = Joi.validate(object, schema, { abortEarly: false });
expect(err).to.exist;
err.annotated();
expect(err.message).to.equal('{\n \"y\": {\n \"b\" \u001b[31m[5]\u001b[0m: {\n \"c\": 10\n },\n \u001b[41m\"b\"\u001b[0m\u001b[31m [4]: -- missing --\u001b[0m,\n \u001b[41m\"u\"\u001b[0m\u001b[31m [3]: -- missing --\u001b[0m,\n \u001b[41m\"u\"\u001b[0m\u001b[31m [2]: -- missing --\u001b[0m\n },\n \"a\" \u001b[31m[1]\u001b[0m: \"m\"\n}\n\u001b[31m\n[1] the value of a must be one of a, b, c, d\n[2] the value of u is not allowed to be undefined\n[3] the value of u must be one of e, f, g, h\n[4] the value of b must be one of i, j, false\n[5] the value of b must be a string\u001b[0m');
done();
});
internals.routeSchema = {
method: T.String().invalid('head').required(),
path: T.String().required(),
handler: [T.Object(), T.Function(), T.String().valid('notFound')],
config: T.Object({
handler: [T.Object(), T.Function(), T.String().valid('notFound')],
payload: T.String().valid(['stream', 'raw', 'parse']),
response: T.Object({
schema: T.Object().nullOk(),
sample: T.Number(),
failAction: T.String().valid(['error', 'log', 'ignore'])
})
})
};
describe('#describe', function () {
var schema = {
sub: {
email: Joi.string().email(),
date: Joi.date(),
child: Joi.object({
alphanum: Joi.string().alphanum()
}),
},
min: [Joi.number(), Joi.string().min(3)],
max: Joi.string().max(3),
required: Joi.string().required().without('xor'),
xor: Joi.string().without('required'),
renamed: Joi.string().rename('required').valid('456'),
notEmpty: Joi.string().required().description('a').notes('b').tags('c')
};
var result = {
type: 'object',
flags: {
insensitive: false,
allowOnly: false
},
valids: [undefined],
invalids: [null],
children: {
sub: {
type: 'object',
flags: {
insensitive: false,
allowOnly: false
},
valids: [undefined],
invalids: [null],
children: {
email: {
type: 'string',
flags: {
insensitive: false,
allowOnly: false
},
valids: [undefined],
invalids: [null, ''],
rules: [{ name: 'email' }]
},
date: {
type: 'date',
flags: {
insensitive: false,
allowOnly: false
},
valids: [undefined],
invalids: [null]
},
child: {
type: 'object',
flags: {
insensitive: false,
allowOnly: false
},
valids: [undefined],
invalids: [null],
children: {
alphanum: {
type: 'string',
flags: {
insensitive: false,
allowOnly: false
},
valids: [undefined],
invalids: [null, ''],
rules: [{ name: 'alphanum' }]
}
}
}
}
},
min: [
{
type: 'number',
flags: {
insensitive: false,
allowOnly: false
},
valids: [undefined],
invalids: [null]
},
{
type: 'string',
flags: {
insensitive: false,
allowOnly: false
},
valids: [undefined],
invalids: [null, ''],
rules: [{ name: 'min', arg: 3 }]
}
],
max: {
type: 'string',
flags: {
insensitive: false,
allowOnly: false
},
valids: [undefined],
invalids: [null, ''],
rules: [{ name: 'max', arg: 3 }]
},
required: {
type: 'string',
flags: {
insensitive: false,
allowOnly: false
},
invalids: [null, '', undefined],
rules: [{ name: 'without', arg: ['xor'] }]
},
xor: {
type: 'string',
flags: {
insensitive: false,
allowOnly: false
},
valids: [undefined],
invalids: [null, ''],
rules: [{ name: 'without', arg: ['required'] }]
},
renamed: {
type: 'string',
flags: {
insensitive: false,
allowOnly: true
},
valids: [undefined, '456'],
invalids: [null, '']
},
notEmpty: {
type: 'string',
flags: {
insensitive: false,
allowOnly: false
},
invalids: [null, '', undefined],
description: 'a',
notes: ['b'],
tags: ['c']
}
}
};
it('describes schema', function (done) {
var description = Joi.describe(schema);
expect(description).to.deep.equal(result);
done();
});
it('describes schema with object', function (done) {
var description = Joi.describe(Joi.object(schema));
expect(description).to.deep.equal(result);
done();
});
});
});

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

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