Skip to content

Commit 3921202

Browse files
committed
add sort order to validations
fixes #48
1 parent 648269f commit 3921202

File tree

7 files changed

+69
-20
lines changed

7 files changed

+69
-20
lines changed

src/array.js

+5-5
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ inherits(ArraySchema, MixedSchema, {
7272
return value
7373
}
7474

75-
let result = value.map((item, key) => {
75+
let validations = value.map((item, key) => {
7676
var path = (options.path || '') + '[' + key + ']'
7777

7878
// object._validate note for isStrict explanation
@@ -84,11 +84,11 @@ inherits(ArraySchema, MixedSchema, {
8484
return true
8585
})
8686

87-
result = endEarly
88-
? Promise.all(result).catch(scopeError(value))
89-
: collectErrors(result, value, options.path, errors)
87+
validations = endEarly
88+
? Promise.all(validations).catch(scopeError(value))
89+
: collectErrors({ validations, value, errors, path: options.path })
9090

91-
return result.then(() => value)
91+
return validations.then(() => value)
9292
})
9393
},
9494

src/mixed.js

+5-2
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,17 @@ let notEmpty = value => !isAbsent(value);
1616
function runValidations(validations, endEarly, value, path) {
1717
return endEarly
1818
? Promise.all(validations)
19-
: _.collectErrors(validations, value, path)
19+
: _.collectErrors({ validations, value, path })
2020
}
2121

2222
function extractTestParams(name, message, test, useCallback) {
2323
var opts = name;
2424

2525
if (typeof message === 'function')
26-
test = message, message = locale.default;
26+
test = message, message = locale.default, name = null;
27+
28+
if (typeof name === 'function')
29+
test = name, message = locale.default, name = null;
2730

2831
if (typeof name === 'string' || name === null)
2932
opts = { name, test, message, useCallback, exclusive: false }

src/object.js

+10-6
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ var MixedSchema = require('./mixed')
66
, split = require('property-expr').split
77
, Ref = require('./util/reference')
88
, c = require('case')
9+
, sortByFields = require('./util/sortByFields')
910
, {
1011
isObject
1112
, transform
@@ -21,12 +22,12 @@ c.type('altCamel', function(str) {
2122
return idx === 0 ? result : (str.substr(0, idx) + result)
2223
})
2324

24-
2525
let scopeError = value => err => {
2626
err.value = value
2727
throw err
2828
}
2929

30+
3031
module.exports = ObjectSchema
3132

3233
function ObjectSchema(spec) {
@@ -135,7 +136,7 @@ inherits(ObjectSchema, MixedSchema, {
135136
return value
136137
}
137138

138-
let result = this._nodes.map((key) => {
139+
let validations = this._nodes.map((key) => {
139140
var path = (opts.path ? (opts.path + '.') : '') + key
140141
, field = this.fields[key]
141142
, innerOptions = { ...opts, key, path, parent: value };
@@ -153,11 +154,14 @@ inherits(ObjectSchema, MixedSchema, {
153154
return true
154155
})
155156

156-
result = endEarly
157-
? Promise.all(result).catch(scopeError(value))
158-
: collectErrors(result, value, opts.path, errors)
157+
validations = endEarly
158+
? Promise.all(validations).catch(scopeError(value))
159+
: collectErrors({ validations, value, errors,
160+
path: opts.path,
161+
sort: sortByFields(this)
162+
})
159163

160-
return result.then(() => value)
164+
return validations.then(() => value)
161165
})
162166
},
163167

src/util/_.js

+10-5
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,21 @@ function settled(promises){
1919
return Promise.all(promises.map(settle))
2020
}
2121

22-
function collectErrors(promises, value, path, errors = []){
22+
function collectErrors({ validations, value, path, errors = [], sort }){
2323
// unwrap aggregate errors
2424
errors = errors.inner && errors.inner.length
2525
? errors.inner : [].concat(errors)
2626

27-
return settled(promises).then( results => {
28-
errors = results.reduce(
29-
(arr, r) => !r.fulfilled ? arr.concat(r.value) : arr, errors)
27+
return settled(validations).then(results => {
28+
let nestedErrors = results
29+
.filter(r => !r.fulfilled)
30+
.reduce((arr, r) => arr.concat(r.value), [])
3031

31-
if ( errors.length )
32+
if (sort) nestedErrors.sort(sort)
33+
//show parent errors after the nested ones: name.first, name
34+
errors = nestedErrors.concat(errors)
35+
36+
if (errors.length)
3237
throw new ValidationError(errors, value, path)
3338
})
3439
}

src/util/sortByFields.js

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
2+
3+
function findIndex(arr, err) {
4+
let idx = Infinity;
5+
arr.some((key, ii) => {
6+
if (err.path.indexOf(key) !== -1) {
7+
idx = ii
8+
return true
9+
}
10+
});
11+
12+
return idx
13+
}
14+
15+
module.exports = function sortByFields(schema) {
16+
let keys = Object.keys(schema.fields);
17+
return (a, b) => {
18+
return findIndex(keys, a) - findIndex(keys, b)
19+
}
20+
}

test/array.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ describe('Array types', function(){
138138
err.value.should.eql([{ str: '' }])
139139

140140
err.errors.length.should.equal(2)
141-
err.errors.should.eql(['oops', '[0].str is a required field'])
141+
err.errors.should.eql(['[0].str is a required field', 'oops' ])
142142
})
143143
])
144144
})

test/object.js

+18-1
Original file line numberDiff line numberDiff line change
@@ -482,11 +482,28 @@ describe('Object types', function(){
482482
.then(function(err) {
483483
err.value.should.eql({ nest: { str: '' } })
484484
err.errors.length.should.equal(2)
485-
err.errors.should.eql(['oops', 'nest.str is a required field'])
485+
err.errors.should.eql(['nest.str is a required field', 'oops'])
486486
})
487487
])
488488
})
489489

490+
it('should sort errors by insertion order', async () => {
491+
var inst = object({
492+
foo: string().test('foo', function() {
493+
return new Promise(resolve => setTimeout(() => resolve(false), 10))
494+
}),
495+
bar: string().required()
496+
})
497+
498+
let err = await inst.validate(
499+
{ foo: 'foo', bar: null },
500+
{ abortEarly: false }).should.rejected;
501+
502+
err.errors.should.eql([
503+
'foo is invalid',
504+
'bar is a required field'
505+
])
506+
})
490507

491508
it('should respect recursive', function(){
492509
var inst = object({

0 commit comments

Comments
 (0)