Skip to content

Commit

Permalink
Rules refactor. Also closes #1827. Closes #1824. Closes #1823. Closes #…
Browse files Browse the repository at this point in the history
…1822. Closes #1778
  • Loading branch information
hueniverse committed May 29, 2019
1 parent d37378d commit ee93679
Show file tree
Hide file tree
Showing 32 changed files with 1,197 additions and 1,195 deletions.
15 changes: 14 additions & 1 deletion lib/about.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,22 @@ exports.describe = function (schema) {
if (keys.length === 1) {
arg = arg[keys[0]];
}
else {
const args = {};
for (const key of keys) {
const inner = arg[key];
if (inner !== undefined) {
args[key] = Ref.isRef(inner) ? inner.toString() : inner;
}
}

arg = args;
}
}

item.arg = Ref.isRef(arg) ? arg.toString() : arg;
if (arg !== undefined) {
item.arg = Ref.isRef(arg) ? arg.toString() : arg;
}
}

const options = test.options;
Expand Down
25 changes: 23 additions & 2 deletions lib/cast.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,15 @@ const Ref = require('./ref');
const internals = {};


exports.schema = function (Joi, config) {
exports.schema = function (Joi, config, options = {}) {

if (config !== undefined && config !== null && typeof config === 'object') {
if (options.appendPath) {
return internals.appendPath(Joi, config);
}

if (config !== undefined &&
config !== null &&
typeof config === 'object') {

if (config.isJoi) {
return config;
Expand Down Expand Up @@ -53,6 +59,21 @@ exports.schema = function (Joi, config) {
};


internals.appendPath = function (Joi, config) {

try {
return exports.schema(Joi, config);
}
catch (err) {
if (err.path !== undefined) {
err.message = `${err.message}(${err.path})`;
}

throw err;
}
};


exports.ref = function (id) {

return Ref.isRef(id) ? id : Ref.create(id);
Expand Down
2 changes: 1 addition & 1 deletion lib/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ exports.Err = class {
return `Error code "${this.type}" is not defined, your custom type is missing the correct language definition`;
}

let wrapArrays = localized && localized.messages && localized.messages.wrapArrays;
let wrapArrays = localized.messages && localized.messages.wrapArrays;
if (typeof wrapArrays !== 'boolean') {
wrapArrays = Language.errors.messages.wrapArrays;
}
Expand Down
22 changes: 4 additions & 18 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -157,16 +157,7 @@ internals.root = function () {

root.compile = function (schema) {

try {
return Cast.schema(this, schema);
}
catch (err) {
if (err.hasOwnProperty('path')) {
err.message = err.message + '(' + err.path + ')';
}

throw err;
}
return Cast.schema(this, schema, { appendPath: true });
};

root.assert = function (value, schema, message) {
Expand Down Expand Up @@ -364,6 +355,7 @@ internals.root = function () {
const ruleArgs = rule.params ?
(rule.params instanceof Any ? rule.params._inner.children.map((k) => k.key) : Object.keys(rule.params)) :
[];

const validateArgs = rule.params ? Cast.schema(this, rule.params) : null;

type.prototype[rule.name] = function (...rArgs) { // eslint-disable-line no-loop-func
Expand Down Expand Up @@ -393,10 +385,7 @@ internals.root = function () {
return rule.validate.call(this, arg, value, state, options);
};

schema = this._test(rule.name, arg, validate, {
description: rule.description,
hasRef
});
schema = this._test(rule.name, arg, validate, { description: rule.description, hasRef });
}
else {
schema = this.clone();
Expand All @@ -415,10 +404,7 @@ internals.root = function () {
return rule.validate.call(this, arg, value, state, options);
};

schema = schema._test(rule.name, arg, validate, {
description: rule.description,
hasRef
});
schema = schema._test(rule.name, arg, validate, { description: rule.description, hasRef });
}
}

Expand Down
6 changes: 4 additions & 2 deletions lib/language.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ exports.errors = {
base: 'must be a buffer or a string',
min: 'must be at least {{limit}} bytes',
max: 'must be less than or equal to {{limit}} bytes',
length: 'must be {{limit}} bytes'
length: 'must be {{limit}} bytes',
ref: 'references "{{ref}}" which is not a positive integer'
},
date: {
base: 'must be a number of milliseconds or valid date string',
Expand Down Expand Up @@ -102,7 +103,8 @@ exports.errors = {
}
},
type: 'must be an instance of "{{type}}"',
schema: 'must be a Joi instance'
schema: 'must be a Joi instance',
ref: 'references "{{ref}}" which is not a positive integer'
},
number: {
base: 'must be a number',
Expand Down
20 changes: 11 additions & 9 deletions lib/types/any.js
Original file line number Diff line number Diff line change
Expand Up @@ -561,18 +561,28 @@ module.exports = internals.Any = class {
for (const key in options.args) {
const resolver = options.refs[key];
if (resolver) {
const arg = options.args[key];
let arg = options.args[key];
if (Ref.isRef(arg)) {
rule.resolve.push(key);
}
else {
if (resolver.normalize) {
arg = resolver.normalize(arg);
options.args[key] = arg;
}

Hoek.assert(resolver.assert(arg), resolver.message);
}
}
}
}

const obj = this.clone();

if (options.override) {
obj._tests = obj._tests.filter((test) => test.name !== name);
}

obj._tests.push({ rule, name, arg: options.args });
return obj;
}
Expand All @@ -588,14 +598,6 @@ module.exports = internals.Any = class {
obj._tests.push({ func, name, arg, options });
return obj;
}

_testUnique(name, arg, func, options) {

const obj = this.clone();
obj._tests = obj._tests.filter((test) => test.name !== name);
obj._tests.push({ func, name, arg, options });
return obj;
}
};


Expand Down
114 changes: 31 additions & 83 deletions lib/types/array.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ const Hoek = require('@hapi/hoek');

const Any = require('./any');
const Cast = require('../cast');
const Ref = require('../ref');
const State = require('./state');
const Utils = require('../utils');

Expand Down Expand Up @@ -65,23 +64,29 @@ internals.Array = class extends Any {
!this._flags.sparse) {

// Clone the array so that we don't modify the original

if (wasArray) {
result.value = result.value.slice(0);
}

result.errors = this._checkItems(result.value, wasArray, state, options);

if (result.errors && wasArray && options.convert && this._flags.single) {
if (result.errors &&
wasArray &&
options.convert &&
this._flags.single) {

// Attempt a 2nd pass by putting the array inside one

// Attempt a 2nd pass by putting the array inside one.
const previousErrors = result.errors;

result.value = [result.value];
result.errors = this._checkItems(result.value, wasArray, state, options);

if (result.errors) {

// Restore previous errors and value since this didn't validate either.
// Restore previous errors and value since this didn't validate either

result.errors = previousErrors;
result.value = result.value[0];
}
Expand Down Expand Up @@ -411,101 +416,35 @@ internals.Array = class extends Any {

min(limit) {

const isRef = Ref.isRef(limit);

Hoek.assert((Number.isSafeInteger(limit) && limit >= 0) || isRef, 'limit must be a positive integer or reference');

return this._testUnique('min', limit, function (value, state, options) {

let compareTo;
if (isRef) {
compareTo = limit(state.reference || state.parent, value, options);

if (!(Number.isSafeInteger(compareTo) && compareTo >= 0)) {
return this.createError('array.ref', { ref: limit, value: compareTo }, state, options);
}
}
else {
compareTo = limit;
}

if (value.length >= compareTo) {
return value;
}

return this.createError('array.min', { limit, value }, state, options);
});
return this._length('min', limit, '>=');
}

max(limit) {

const isRef = Ref.isRef(limit);

Hoek.assert((Number.isSafeInteger(limit) && limit >= 0) || isRef, 'limit must be a positive integer or reference');

return this._testUnique('max', limit, function (value, state, options) {

let compareTo;
if (isRef) {
compareTo = limit(state.reference || state.parent, value, options);

if (!(Number.isSafeInteger(compareTo) && compareTo >= 0)) {
return this.createError('array.ref', { ref: limit.key }, state, options);
}
}
else {
compareTo = limit;
}

if (value.length <= compareTo) {
return value;
}

return this.createError('array.max', { limit, value }, state, options);
});
return this._length('max', limit, '<=');
}

length(limit) {

const isRef = Ref.isRef(limit);

Hoek.assert((Number.isSafeInteger(limit) && limit >= 0) || isRef, 'limit must be a positive integer or reference');

return this._testUnique('length', limit, function (value, state, options) {

let compareTo;
if (isRef) {
compareTo = limit(state.reference || state.parent, value, options);
return this._length('length', limit, '=');
}

if (!(Number.isSafeInteger(compareTo) && compareTo >= 0)) {
return this.createError('array.ref', { ref: limit.key }, state, options);
}
}
else {
compareTo = limit;
}
_length(name, limit, operator) {

if (value.length === compareTo) {
return value;
const refs = {
limit: {
assert: (value) => Number.isSafeInteger(value) && value >= 0,
code: 'array.ref',
message: 'limit must be a positive integer or reference'
}
};

return this.createError('array.length', { limit, value }, state, options);
});
return this._rule(name, { rule: 'length', refs, args: { limit }, operator, override: true });
}

has(schema) {

try {
schema = Cast.schema(this._currentJoi, schema);
}
catch (err) {
if (err.path !== undefined) {
err.message = `${err.message}(${err.path})`;
}

throw err;
}

schema = Cast.schema(this._currentJoi, schema, { appendPath: true });
return this._rule('has', { args: { schema } });
}

Expand Down Expand Up @@ -625,6 +564,15 @@ internals.Array.prototype._rules = {
return error('array.hasUnknown', null);
},

length: function (value, helpers, { limit }, { alias, operator, args }) {

if (Utils.compare(value.length, limit, operator)) {
return value;
}

return helpers.error('array.' + alias, { limit: args.limit, value });
},

unique: function (value, { state, options, error }, { settings }) {

const found = {
Expand Down
Loading

0 comments on commit ee93679

Please sign in to comment.