From 41f0ef0bd62184fe3b94a7c41e0670637076cc99 Mon Sep 17 00:00:00 2001 From: Scott Newcomer Date: Sat, 11 Jan 2020 08:39:50 -0800 Subject: [PATCH] Collect errors correctly (#399) --- addon/-private/validated-changeset.ts | 19 +++++++---------- addon/types/index.ts | 4 ++-- addon/utils/get-key-values.ts | 30 +++++++++++++++++++++++++++ tests/unit/changeset-test.js | 20 ++++++++++++++++++ 4 files changed, 59 insertions(+), 14 deletions(-) diff --git a/addon/-private/validated-changeset.ts b/addon/-private/validated-changeset.ts index 4c36c32f..68ea70aa 100644 --- a/addon/-private/validated-changeset.ts +++ b/addon/-private/validated-changeset.ts @@ -4,7 +4,7 @@ import { isEqual } from '@ember/utils'; import Change from 'ember-changeset/-private/change'; -import { getKeyValues } from 'ember-changeset/utils/get-key-values'; +import { getKeyValues, getKeyErrorValues } from 'ember-changeset/utils/get-key-values'; import { notifierForEvent } from 'ember-changeset/-private/evented'; import Err from 'ember-changeset/-private/err'; import normalizeObject from 'ember-changeset/utils/normalize-object'; @@ -147,20 +147,15 @@ export class BufferedChangeset implements IChangeset { return getKeyValues(obj); } - // TODO: iterate and find all leaf errors - // can only provide leaf key + /** + * @property errors + * @type {Array} + */ get errors() { let obj = this[ERRORS]; - function transform(e: Err) { - return { value: e.value, validation: e.validation }; - } - - return keys(obj).map(key => { - let { value, validation } = transform(obj[key]); - - return { key, value, validation }; - }); + // [{ key, validation, value }, ...] + return getKeyErrorValues(obj); } get change() { diff --git a/addon/types/index.ts b/addon/types/index.ts index 6bcc75b7..45aac628 100644 --- a/addon/types/index.ts +++ b/addon/types/index.ts @@ -70,8 +70,8 @@ export interface ChangesetDef { _runningValidations: RunningValidations, _bareChanges: { [s: string]: any }, - changes: any, // { key: string; value: any; }[], //ComputedProperty, - errors: { key: string; value: any; validation: ValidationErr | ValidationErr[] }[], //ComputedProperty, + changes: object[], + errors: object[], error: object, change: object, data: object, diff --git a/addon/utils/get-key-values.ts b/addon/utils/get-key-values.ts index a435aa37..d085cc1b 100644 --- a/addon/utils/get-key-values.ts +++ b/addon/utils/get-key-values.ts @@ -1,4 +1,5 @@ import isObject from './is-object'; +import Err from '../-private/err'; /** * traverse through target and return leaf nodes with `value` property and key as 'person.name' @@ -25,3 +26,32 @@ export function getKeyValues>(obj: T, keysUpToValu return map; } + +/** + * traverse through target and return leaf nodes with `value` property and key as 'person.name' + * + * @method getKeyErrorValues + * @return {Array} [{ key: 'person.name', validation: '', value: '' }] + */ +export function getKeyErrorValues>( + obj: T, + keysUpToValue: string[] = [] +): object[] { + let map = []; + + for (let key in obj) { + keysUpToValue.push(key); + + if (obj[key] && isObject(obj[key])) { + if (Object.prototype.hasOwnProperty.call(obj[key], 'value') && obj[key] as any instanceof Err) { + map.push({ key: keysUpToValue.join('.'), validation: obj[key].validation, value: obj[key].value }); + // stop collecting keys + keysUpToValue = []; + } else if (key !== 'value') { + map.push(...getKeyErrorValues(obj[key], keysUpToValue)); + } + } + } + + return map; +} diff --git a/tests/unit/changeset-test.js b/tests/unit/changeset-test.js index 8df844c6..646dda16 100644 --- a/tests/unit/changeset-test.js +++ b/tests/unit/changeset-test.js @@ -133,6 +133,26 @@ module('Unit | Utility | changeset', function(hooks) { /** * #errors */ + test('#errors returns the error object and keeps changes', async function(assert) { + let dummyChangeset = new Changeset(dummyModel, dummyValidator); + let expectedResult = [{ key: 'name', validation: 'too short', value: 'a' }]; + dummyChangeset.set('name', 'a'); + + assert.deepEqual(dummyChangeset.errors, expectedResult, 'should return errors object'); + assert.deepEqual(dummyChangeset.get('errors'), expectedResult, 'should return nested errors'); + assert.deepEqual(dummyChangeset.get('errors.name'), expectedResult.name, 'should return nested errors'); + assert.deepEqual(dummyChangeset.change, { name: 'a' }, 'should return change object'); + assert.deepEqual(dummyChangeset.get('change.name'), 'a', 'should return nested change'); + assert.deepEqual(dummyChangeset.get('change').name, 'a', 'should return nested change'); + }); + + test('can get nested values in the errors object', function(assert) { + let dummyChangeset = new Changeset(dummyModel, dummyValidator); + dummyChangeset.set('org.usa.ny', ''); + + let expectedErrors = [{ key: 'org.usa.ny', validation: 'must be present', value: '' }]; + assert.deepEqual(dummyChangeset.get('errors'), expectedErrors, 'should return errors object for `org.usa.ny` key'); + }); /** * #changes