Skip to content

Commit 84aec0c

Browse files
committed
fix: properly match AJV paths with slashes
AJV error paths are JSON Pointers. These paths are converted to dot-notation in JSON Forms. However escaped JSON Pointer characters were not decoded. Therefore errors for properties containing one of the escaped characters were not properly matched. Background information: In JSON Pointers two characters need to be escaped: '~' is converted to '~0' and '/' is converted to '~1'. Therefore properties containing either '~' or '/' were not properly matched to errors before this fix. Fixes #2146
1 parent fa055c2 commit 84aec0c

File tree

2 files changed

+33
-9
lines changed

2 files changed

+33
-9
lines changed

packages/core/src/reducers/core.ts

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ import {
4545
UPDATE_CORE,
4646
UpdateCoreAction,
4747
} from '../actions';
48-
import { createAjv, isOneOfEnumSchema, Reducer } from '../util';
48+
import { createAjv, decode, isOneOfEnumSchema, Reducer } from '../util';
4949
import type { JsonSchema, UISchemaElement } from '../models';
5050

5151
export const validate = (
@@ -356,13 +356,9 @@ const getInvalidProperty = (error: ErrorObject): string | undefined => {
356356
};
357357

358358
export const getControlPath = (error: ErrorObject) => {
359-
const dataPath = (error as any).dataPath;
360-
// older AJV version
361-
if (dataPath) {
362-
return dataPath.replace(/\//g, '.').substr(1);
363-
}
364-
// dataPath was renamed to instancePath in AJV v8
365-
let controlPath: string = error.instancePath;
359+
// Up until AJV v7 the path property was called 'dataPath'
360+
// With AJV v8 the property was renamed to 'instancePath'
361+
let controlPath = (error as any).dataPath || error.instancePath || '';
366362

367363
// change '/' chars to '.'
368364
controlPath = controlPath.replace(/\//g, '.');
@@ -374,6 +370,9 @@ export const getControlPath = (error: ErrorObject) => {
374370

375371
// remove '.' chars at the beginning of paths
376372
controlPath = controlPath.replace(/^./, '');
373+
374+
// decode JSON Pointer escape sequences
375+
controlPath = decode(controlPath);
377376
return controlPath;
378377
};
379378

packages/core/test/reducers/core.test.ts

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
THE SOFTWARE.
2424
*/
2525
import test from 'ava';
26-
import Ajv from 'ajv';
26+
import Ajv, { ErrorObject } from 'ajv';
2727
import { coreReducer } from '../../src/reducers';
2828
import {
2929
init,
@@ -39,6 +39,7 @@ import {
3939
JsonFormsCore,
4040
validate,
4141
subErrorsAt,
42+
getControlPath,
4243
} from '../../src/reducers/core';
4344

4445
import { cloneDeep } from 'lodash';
@@ -1812,3 +1813,27 @@ test('core reducer - setSchema - schema with id', (t) => {
18121813
const after: JsonFormsCore = coreReducer(before, setSchema(updatedSchema));
18131814
t.is(after.schema.properties.animal.minLength, 5);
18141815
});
1816+
1817+
test('core reducer helpers - getControlPath - converts JSON Pointer notation to dot notation', (t) => {
1818+
const errorObject = { instancePath: '/group/name' } as ErrorObject;
1819+
const controlPath = getControlPath(errorObject);
1820+
t.is(controlPath, 'group.name');
1821+
});
1822+
1823+
test('core reducer helpers - getControlPath - fallback to AJV <=7 errors', (t) => {
1824+
const errorObject = { dataPath: '/group/name' } as unknown as ErrorObject;
1825+
const controlPath = getControlPath(errorObject);
1826+
t.is(controlPath, 'group.name');
1827+
});
1828+
1829+
test('core reducer helpers - getControlPath - fallback to AJV <=7 errors does not crash for empty paths', (t) => {
1830+
const errorObject = { dataPath: '' } as unknown as ErrorObject;
1831+
const controlPath = getControlPath(errorObject);
1832+
t.is(controlPath, '');
1833+
});
1834+
1835+
test('core reducer helpers - getControlPath - decodes JSON Pointer escape sequences', (t) => {
1836+
const errorObject = { instancePath: '/~0group/~1name' } as ErrorObject;
1837+
const controlPath = getControlPath(errorObject);
1838+
t.is(controlPath, '~group./name');
1839+
});

0 commit comments

Comments
 (0)