Skip to content

Commit 3979bb4

Browse files
committed
Enhance description. Closes #1606
1 parent 7c04274 commit 3979bb4

File tree

12 files changed

+93
-55
lines changed

12 files changed

+93
-55
lines changed

lib/about.js

+12-11
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,20 @@
33
const Hoek = require('@hapi/hoek');
44

55
const Ref = require('./ref');
6+
const Settings = require('./settings');
67

78

89
const internals = {
9-
flags: ['empty', 'default', 'lazy', 'label']
10+
flags: ['empty', 'default', 'lazy', 'label'],
11+
exclude: ['lazy', 'label']
1012
};
1113

1214

1315
exports.describe = function (schema) {
1416

1517
const description = {
16-
type: schema._type
18+
type: schema._type,
19+
[Settings.symbols.schema]: schema
1720
};
1821

1922
const flags = Object.keys(schema._flags);
@@ -26,7 +29,7 @@ exports.describe = function (schema) {
2629
}
2730
else if (flag === 'default') {
2831
if (Ref.isRef(schema._flags[flag])) {
29-
description.flags[flag] = schema._flags[flag].toString();
32+
description.flags[flag] = schema._flags[flag].describe();
3033
}
3134
else if (typeof schema._flags[flag] === 'function') {
3235
description.flags[flag] = {
@@ -38,9 +41,7 @@ exports.describe = function (schema) {
3841
description.flags[flag] = schema._flags[flag];
3942
}
4043
}
41-
else if (flag !== 'lazy' &&
42-
flag !== 'label') {
43-
44+
else if (!internals.exclude.includes(flag)) {
4445
description.flags[flag] = schema._flags[flag];
4546
}
4647
}
@@ -86,15 +87,15 @@ exports.describe = function (schema) {
8687
if (valids.length) {
8788
description.valids = valids.map((v) => {
8889

89-
return Ref.isRef(v) ? v.toString() : v;
90+
return Ref.isRef(v) ? v.describe() : v;
9091
});
9192
}
9293

9394
const invalids = schema._invalids.values();
9495
if (invalids.length) {
9596
description.invalids = invalids.map((v) => {
9697

97-
return Ref.isRef(v) ? v.toString() : v;
98+
return Ref.isRef(v) ? v.describe() : v;
9899
});
99100
}
100101

@@ -115,7 +116,7 @@ exports.describe = function (schema) {
115116
for (const key of keys) {
116117
const inner = arg[key];
117118
if (inner !== undefined) {
118-
args[key] = Ref.isRef(inner) ? inner.toString() : inner;
119+
args[key] = Ref.isRef(inner) ? inner.describe() : inner;
119120
}
120121
}
121122

@@ -124,7 +125,7 @@ exports.describe = function (schema) {
124125
}
125126

126127
if (arg !== undefined) {
127-
item.arg = Ref.isRef(arg) ? arg.toString() : arg;
128+
item.arg = Ref.isRef(arg) ? arg.describe() : arg;
128129
}
129130
}
130131

@@ -135,7 +136,7 @@ exports.describe = function (schema) {
135136
const keys = Object.keys(test.arg);
136137
for (const key of keys) {
137138
const value = test.arg[key];
138-
item.arg[key] = Ref.isRef(value) ? value.toString() : value;
139+
item.arg[key] = Ref.isRef(value) ? value.describe() : value;
139140
}
140141
}
141142

lib/index.js

+2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ const Cast = require('./cast');
77
const Errors = require('./errors');
88
const Lazy = require('./types/lazy');
99
const Ref = require('./ref');
10+
const Settings = require('./settings');
1011
const Validator = require('./validator');
1112
const Utils = require('./utils');
1213

@@ -462,6 +463,7 @@ internals.root = function () {
462463
root.extensionsSchema = internals.array.items(internals.object, internals.func.arity(1)).strict();
463464

464465
root.version = require('../package.json').version;
466+
root.schema = Settings.symbols.schema;
465467

466468
return root;
467469
};

lib/ref.js

+12-3
Original file line numberDiff line numberDiff line change
@@ -21,25 +21,34 @@ exports.create = function (key, options) {
2121
const contextPrefix = settings.contextPrefix || '$';
2222
const separator = settings.separator || '.';
2323

24-
ref.isContext = (key[0] === contextPrefix);
24+
ref.isContext = key[0] === contextPrefix;
2525
if (!ref.isContext &&
2626
key[0] === separator) {
2727

2828
key = key.slice(1);
2929
settings.self = true;
3030
}
3131

32-
ref.key = (ref.isContext ? key.slice(1) : key);
32+
ref.key = ref.isContext ? key.slice(1) : key;
3333
ref.path = ref.key.split(separator);
3434
ref.depth = ref.path.length;
3535
ref.root = ref.path[0];
3636
ref.isJoi = true;
3737

38-
ref.toString = function () {
38+
ref.toString = () => {
3939

4040
return (ref.isContext ? 'context:' : 'ref:') + ref.key;
4141
};
4242

43+
ref.describe = () => {
44+
45+
return {
46+
type: ref.isContext ? 'context' : 'ref',
47+
key: ref.key,
48+
path: ref.path
49+
};
50+
};
51+
4352
return ref;
4453
};
4554

lib/settings.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ exports.defaults = {
2222

2323

2424
exports.symbols = {
25-
settingsCache: Symbol('settingsCache')
25+
settingsCache: Symbol('settingsCache'),
26+
schema: Symbol('schema')
2627
};
2728

2829

lib/types/alternatives.js

+2-6
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,7 @@ internals.Alternatives = class extends Any {
180180
describe() {
181181

182182
const description = super.describe();
183+
183184
const alternatives = [];
184185
for (let i = 0; i < this._inner.matches.length; ++i) {
185186
const item = this._inner.matches[i];
@@ -193,12 +194,7 @@ internals.Alternatives = class extends Any {
193194

194195
// when()
195196

196-
const when = item.is ? {
197-
ref: item.ref.toString(),
198-
is: item.is.describe()
199-
} : {
200-
peek: item.peek.describe()
201-
};
197+
const when = item.is ? { ref: item.ref.describe(), is: item.is.describe() } : { peek: item.peek.describe() };
202198

203199
if (item.then) {
204200
when.then = item.then.describe();

lib/types/lazy.js

+11
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,17 @@ internals.Lazy = class extends Any {
5050
return schema._validate(value, state, options);
5151
}
5252

53+
// About
54+
55+
describe() {
56+
57+
const description = super.describe();
58+
description.schema = this._flags.lazy;
59+
return description;
60+
}
61+
62+
// Rules
63+
5364
set(fn, options) {
5465

5566
Hoek.assert(typeof fn === 'function', 'You must provide a function as first argument');

lib/types/object.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ internals.Object = class extends Any {
257257

258258
rule.arg = {
259259
schema: rule.arg.schema.describe(),
260-
ref: rule.arg.ref.toString()
260+
ref: rule.arg.ref
261261
};
262262
}
263263
}

test/index.js

+19-4
Original file line numberDiff line numberDiff line change
@@ -2440,7 +2440,11 @@ describe('Joi', () => {
24402440
defaultRef: Joi.string().default(defaultRef, 'not here'),
24412441
defaultFn: Joi.string().default(defaultFn, 'not here'),
24422442
defaultDescribedFn: Joi.string().default(defaultDescribedFn, 'described test')
2443-
}).options({ abortEarly: false, convert: false }).rename('renamed', 'required').without('required', 'xor').without('xor', 'required');
2443+
})
2444+
.options({ abortEarly: false, convert: false })
2445+
.rename('renamed', 'required')
2446+
.without('required', 'xor')
2447+
.without('xor', 'required');
24442448

24452449
const result = {
24462450
type: 'object',
@@ -2537,7 +2541,11 @@ describe('Joi', () => {
25372541
defaultRef: {
25382542
type: 'string',
25392543
flags: {
2540-
default: 'ref:xor'
2544+
default: {
2545+
type: 'ref',
2546+
key: 'xor',
2547+
path: ['xor']
2548+
}
25412549
},
25422550
invalids: ['']
25432551
},
@@ -2595,7 +2603,7 @@ describe('Joi', () => {
25952603

25962604
const description = schema.describe();
25972605
expect(description).to.equal(result);
2598-
expect(description.children.defaultRef.flags.default).to.equal('ref:xor');
2606+
expect(description.children.defaultRef.flags.default).to.equal({ type: 'ref', key: 'xor', path: ['xor'] });
25992607
expect(description.children.defaultFn.flags.default.description).to.equal('testing');
26002608
expect(description.children.defaultDescribedFn.flags.default.description).to.equal('described test');
26012609
});
@@ -2620,6 +2628,13 @@ describe('Joi', () => {
26202628
const description = Joi.allow(null).describe();
26212629
expect(description.invalids).to.not.exist();
26222630
});
2631+
2632+
it('includes schemas in description)', () => {
2633+
2634+
const description = Joi.describe(schema);
2635+
expect(description).to.equal(result);
2636+
expect(description[Joi.schema]).to.equal(schema);
2637+
});
26232638
});
26242639

26252640
describe('assert()', () => {
@@ -3772,7 +3787,7 @@ describe('Joi', () => {
37723787
expect(schema.describe()).to.equal({
37733788
type: 'myType',
37743789
rules: [
3775-
{ name: 'foo', arg: { bar: 'bar', baz: 42, qux: 'ref:a.b', quux: 'context:c.d' } }
3790+
{ name: 'foo', arg: { bar: 'bar', baz: 42, qux: { type: 'ref', key: 'a.b', path: ['a', 'b'] }, quux: { type: 'context', key: 'c.d', path: ['c', 'd'] } } }
37763791
]
37773792
});
37783793
});

test/ref.js

+21-21
Original file line numberDiff line numberDiff line change
@@ -621,56 +621,56 @@ describe('ref', () => {
621621
type: 'any',
622622
flags: {
623623
allowOnly: true,
624-
default: 'ref:a.b'
624+
default: { type: 'ref', key: 'a.b', path: ['a', 'b'] }
625625
},
626-
invalids: ['context:b.c'],
627-
valids: ['ref:a.b']
626+
invalids: [{ type: 'context', key: 'b.c', path: ['b', 'c'] }],
627+
valids: [{ type: 'ref', key: 'a.b', path: ['a', 'b'] }]
628628
},
629629
alternatives: [{
630-
ref: 'ref:a.b',
630+
ref: { type: 'ref', key: 'a.b', path: ['a', 'b'] },
631631
is: {
632632
type: 'date',
633633
rules: [
634-
{ name: 'min', arg: 'ref:a.b' },
635-
{ name: 'max', arg: 'ref:a.b' }
634+
{ name: 'min', arg: { type: 'ref', key: 'a.b', path: ['a', 'b'] } },
635+
{ name: 'max', arg: { type: 'ref', key: 'a.b', path: ['a', 'b'] } }
636636
]
637637
},
638638
then: {
639639
type: 'number',
640-
flags: { allowOnly: true, default: 'ref:a.b', unsafe: false },
641-
valids: ['ref:a.b'],
642-
invalids: ['context:b.c', Infinity, -Infinity],
640+
flags: { allowOnly: true, default: { type: 'ref', key: 'a.b', path: ['a', 'b'] }, unsafe: false },
641+
valids: [{ type: 'ref', key: 'a.b', path: ['a', 'b'] }],
642+
invalids: [{ type: 'context', key: 'b.c', path: ['b', 'c'] }, Infinity, -Infinity],
643643
rules: [
644-
{ name: 'min', arg: 'ref:a.b' },
645-
{ name: 'max', arg: 'ref:a.b' },
646-
{ name: 'greater', arg: 'ref:a.b' },
647-
{ name: 'less', arg: 'ref:a.b' }
644+
{ name: 'min', arg: { type: 'ref', key: 'a.b', path: ['a', 'b'] } },
645+
{ name: 'max', arg: { type: 'ref', key: 'a.b', path: ['a', 'b'] } },
646+
{ name: 'greater', arg: { type: 'ref', key: 'a.b', path: ['a', 'b'] } },
647+
{ name: 'less', arg: { type: 'ref', key: 'a.b', path: ['a', 'b'] } }
648648
]
649649
},
650650
otherwise: {
651651
type: 'object',
652-
flags: { allowOnly: true, default: 'ref:a.b' },
653-
valids: ['ref:a.b'],
654-
invalids: ['context:b.c'],
652+
flags: { allowOnly: true, default: { type: 'ref', key: 'a.b', path: ['a', 'b'] } },
653+
valids: [{ type: 'ref', key: 'a.b', path: ['a', 'b'] }],
654+
invalids: [{ type: 'context', key: 'b.c', path: ['b', 'c'] }],
655655
rules: [{
656656
name: 'assert',
657657
arg: {
658658
schema: {
659659
type: 'any',
660660
flags: { allowOnly: true },
661-
valids: ['ref:a.b']
661+
valids: [{ type: 'ref', key: 'a.b', path: ['a', 'b'] }]
662662
},
663-
ref: 'ref:a.b'
663+
ref: { type: 'ref', key: 'a.b', path: ['a', 'b'] }
664664
}
665665
}],
666666
children: {
667667
a: {
668668
type: 'string',
669669
invalids: [''],
670670
rules: [
671-
{ name: 'min', arg: { limit: 'ref:a.b' } },
672-
{ name: 'max', arg: { limit: 'ref:a.b' } },
673-
{ name: 'length', arg: { limit: 'ref:a.b' } }
671+
{ name: 'min', arg: { limit: { type: 'ref', key: 'a.b', path: ['a', 'b'] } } },
672+
{ name: 'max', arg: { limit: { type: 'ref', key: 'a.b', path: ['a', 'b'] } } },
673+
{ name: 'length', arg: { limit: { type: 'ref', key: 'a.b', path: ['a', 'b'] } } }
674674
]
675675
}
676676
},

test/types/alternatives.js

+6-5
Original file line numberDiff line numberDiff line change
@@ -1149,7 +1149,7 @@ describe('alternatives', () => {
11491149
falsy: [false],
11501150
valids: [true]
11511151
},
1152-
ref: 'ref:a',
1152+
ref: { type: 'ref', key: 'a', path: ['a'] },
11531153
then: {
11541154
type: 'string',
11551155
flags: {
@@ -1164,6 +1164,7 @@ describe('alternatives', () => {
11641164
}
11651165
}]
11661166
});
1167+
11671168
expect(labeled.describe()).to.equal({
11681169
base: { type: 'any' },
11691170
flags: { presence: 'ignore' },
@@ -1177,7 +1178,7 @@ describe('alternatives', () => {
11771178
falsy: [false],
11781179
valids: [true]
11791180
},
1180-
ref: 'ref:a',
1181+
ref: { type: 'ref', key: 'a', path: ['a'] },
11811182
then: {
11821183
type: 'string',
11831184
flags: {
@@ -1217,7 +1218,7 @@ describe('alternatives', () => {
12171218
type: 'alternatives',
12181219
alternatives: [
12191220
{
1220-
ref: 'ref:b',
1221+
ref: { type: 'ref', key: 'b', path: ['b'] },
12211222
is: {
12221223
type: 'number',
12231224
flags: {
@@ -1280,7 +1281,7 @@ describe('alternatives', () => {
12801281
type: 'alternatives',
12811282
alternatives: [
12821283
{
1283-
ref: 'ref:b',
1284+
ref: { type: 'ref', key: 'b', path: ['b'] },
12841285
is: {
12851286
type: 'number',
12861287
flags: {
@@ -1335,7 +1336,7 @@ describe('alternatives', () => {
13351336
type: 'alternatives',
13361337
alternatives: [
13371338
{
1338-
ref: 'ref:b',
1339+
ref: { type: 'ref', key: 'b', path: ['b'] },
13391340
is: {
13401341
type: 'number',
13411342
flags: {

0 commit comments

Comments
 (0)