Skip to content

Commit

Permalink
Apply link.concat() on resolved schema. Closes #2101
Browse files Browse the repository at this point in the history
  • Loading branch information
hueniverse committed Sep 6, 2019
1 parent 742b69d commit aa374d7
Show file tree
Hide file tree
Showing 7 changed files with 94 additions and 6 deletions.
5 changes: 5 additions & 0 deletions API.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@
- [`function.minArity(n)`](#funcminarityn)
- [`link(ref)` - inherits from `Any`](#linkref---inherits-from-any)
- [`link.ref(ref)`](#linkrefref)
- [`link.concat(schema)`](#linkconcatschema)
- [`number` - inherits from `Any`](#number---inherits-from-any)
- [`number.greater(limit)`](#numbergreaterlimit)
- [`number.integer()`](#numberinteger)
Expand Down Expand Up @@ -2212,6 +2213,10 @@ await number.validateAsync(5);

Possible validation errors: [`number.base`](#numberbase), [`number.infinity`](#numberinfinity)

#### `link.concat(schema)`

Same as [`any.concat()`](#anyconcatschema) but the schema is merged after the link is resolved which allows merging with schemas of the same type as the resolved link. Will throw an exception during validation if the merged types are not compatible.

#### `number.greater(limit)`

Specifies that the value must be greater than `limit` or a reference.
Expand Down
7 changes: 7 additions & 0 deletions lib/base.js
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ internals.Base = class {

prefs(prefs) {

Assert(prefs, 'Missing preferences');
Assert(prefs.context === undefined, 'Cannot override context');
Assert(prefs.externals === undefined, 'Cannot override externals');
Assert(prefs.warnings === undefined, 'Cannot override warnings');
Expand Down Expand Up @@ -808,6 +809,12 @@ internals.Base = class {
for (let i = 0; i < this.$_terms.whens.length; ++i) {
const when = this.$_terms.whens[i];

if (when.concat) {
whens.push(when.concat);
ids.push(`${i}.concat`);
continue;
}

const input = when.ref ? when.ref.resolve(value, state, prefs) : value;
const tests = when.is ? [when] : when.switch;
const before = ids.length;
Expand Down
2 changes: 1 addition & 1 deletion lib/extend.js
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ internals.build = function (child, parent) {

return function (obj, desc) {

return child(parent(obj, desc), desc);
return parent(child(obj, desc), desc);
};
};

Expand Down
2 changes: 2 additions & 0 deletions lib/types/alternatives.js
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,8 @@ internals.errors = function (failures, { error, state }) {
return { errors: error('alternatives.types', { types: [...valids] }) };
}

// Single complex error

if (complex.length === 1) {
return { errors: complex[0].report };
}
Expand Down
13 changes: 11 additions & 2 deletions lib/types/any.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,8 +135,11 @@ module.exports = Base.extend({

if (key === 'whens') {
for (const value of values) {
const { ref, is, not, then, otherwise } = value;
if (ref) {
const { ref, is, not, then, otherwise, concat } = value;
if (concat) {
obj = obj.concat(concat);
}
else if (ref) {
obj = obj.when(ref, { is, not, then, otherwise, switch: value.switch, break: value.break });
}
else {
Expand All @@ -146,6 +149,12 @@ module.exports = Base.extend({

continue;
}

if (key === 'shared') {
for (const value of values) {
obj = obj.shared(value);
}
}
}

return obj;
Expand Down
11 changes: 9 additions & 2 deletions lib/types/link.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,16 @@ module.exports = Any.extend({

Assert(this.$_terms.link, 'Uninitialized link schema');
Assert(Common.isSchema(source), 'Invalid schema object');
Assert(source.type === 'any', 'Cannot merge type link with another type:', source.type);
Assert(source.type !== 'link', 'Cannot merge type link with another link');

return this.$_super.concat(source);
const obj = this.clone();

if (!obj.$_terms.whens) {
obj.$_terms.whens = [];
}

obj.$_terms.whens.push({ concat: source });
return obj.$_mutateRebuild();
}
},

Expand Down
60 changes: 59 additions & 1 deletion test/types/link.js
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,7 @@ describe('link', () => {

it('errors on concat of link to link', () => {

expect(() => Joi.link('..').concat(Joi.link('..'))).to.throw('Cannot merge type link with another type: link');
expect(() => Joi.link('..').concat(Joi.link('..'))).to.throw('Cannot merge type link with another link');
});

it('combines link with any', () => {
Expand All @@ -359,6 +359,64 @@ describe('link', () => {
expect(a.validate({ x: {} }).error).to.not.exist();
expect(a.concat(b).validate({ x: {} }).error).to.be.an.error('"x" is not allowed');
});

it('applies concat after ref resolved', () => {

const shared = Joi.object({
a: Joi.number()
})
.id('shared');

const schema = Joi.object({
x: Joi.link('#shared')
.concat(Joi.object({ a: 3 }))
})
.shared(shared);

Helper.validate(schema, [
[{ x: { a: 3 } }, true],
[{ x: { a: 2 } }, false, null, {
message: '"x.a" must be [3]',
details: [{
message: '"x.a" must be [3]',
path: ['x', 'a'],
type: 'any.only',
context: { label: 'x.a', value: 2, key: 'a', valids: [3] }
}]
}]
]);
});

it('applies concat after ref resolved (with when)', () => {

const shared = Joi.object({
a: Joi.number()
})
.id('shared');

const schema = Joi.object({
w: Joi.boolean(),
x: Joi.link('#shared')
.when('w', { then: Joi.object({ a: 4 }) })
.concat(Joi.object({ a: Joi.valid(3) }))
})
.shared(shared);

Helper.validate(schema, [
[{ x: { a: 3 } }, true],
[{ w: true, x: { a: 3 } }, true],
[{ w: true, x: { a: 4 } }, true],
[{ x: { a: 2 } }, false, null, {
message: '"x.a" must be [3]',
details: [{
message: '"x.a" must be [3]',
path: ['x', 'a'],
type: 'any.only',
context: { label: 'x.a', value: 2, key: 'a', valids: [3] }
}]
}]
]);
});
});

describe('describe()', () => {
Expand Down

0 comments on commit aa374d7

Please sign in to comment.