Skip to content

Commit 6ea96bc

Browse files
committed
feat: add max depth config
1 parent fe743f2 commit 6ea96bc

File tree

2 files changed

+106
-63
lines changed

2 files changed

+106
-63
lines changed

src/index.spec.ts

Lines changed: 99 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import type { JsonValue, ObjectHashContext, Patch } from './index';
2-
import { generateJSONPatch, pathInfo } from './index';
3-
import { applyPatch, deepClone } from 'fast-json-patch';
4-
import { assert, expect } from 'chai';
1+
import type {JsonValue, ObjectHashContext, Patch} from './index';
2+
import {generateJSONPatch, pathInfo} from './index';
3+
import {applyPatch, deepClone} from 'fast-json-patch';
4+
import {assert, expect} from 'chai';
55

66
type Title = string;
77
type Before = JsonValue;
@@ -18,9 +18,9 @@ const jsonValues = {
1818
primitiveNumberZero: 0,
1919
primitiveBooleanTrue: true,
2020
primitiveBooleanFalse: false,
21-
jsonObjectWithFlatPropertiesAndStringValues: { a: 'a', b: 'b', c: 'c' },
22-
jsonObjectWithFlatPropertiesAndNumberValues: { a: 3, b: 2, c: 1 },
23-
jsonObjectWithFlatPropertiesAndMixedValues: { a: true, b: 'b', c: 12 },
21+
jsonObjectWithFlatPropertiesAndStringValues: {a: 'a', b: 'b', c: 'c'},
22+
jsonObjectWithFlatPropertiesAndNumberValues: {a: 3, b: 2, c: 1},
23+
jsonObjectWithFlatPropertiesAndMixedValues: {a: true, b: 'b', c: 12},
2424
} as const;
2525

2626
describe('a generate json patch function', () => {
@@ -47,12 +47,12 @@ describe('a generate json patch function', () => {
4747
'adds root array elements',
4848
[1, 2, 3],
4949
[1, 2, 3, 4],
50-
[{ op: 'add', path: '/3', value: 4 }],
50+
[{op: 'add', path: '/3', value: 4}],
5151
],
5252
[
5353
'adds root object property',
54-
{ a: 'a', b: 'b' },
55-
{ a: 'a', b: 'b', c: 'c' },
54+
{a: 'a', b: 'b'},
55+
{a: 'a', b: 'b', c: 'c'},
5656
[
5757
{
5858
op: 'add',
@@ -65,38 +65,38 @@ describe('a generate json patch function', () => {
6565
'removes root array elements',
6666
[1, 2, 3, 4],
6767
[1, 2, 3],
68-
[{ op: 'remove', path: '/3' }],
68+
[{op: 'remove', path: '/3'}],
6969
],
7070
[
7171
'removes root object property',
72-
{ a: 'a', b: 'b', c: 'c' },
73-
{ a: 'a', b: 'b' },
74-
[{ op: 'remove', path: '/c' }],
72+
{a: 'a', b: 'b', c: 'c'},
73+
{a: 'a', b: 'b'},
74+
[{op: 'remove', path: '/c'}],
7575
],
7676
[
7777
'replaces root number values',
7878
1,
7979
2,
80-
[{ op: 'replace', path: '', value: 2 }],
80+
[{op: 'replace', path: '', value: 2}],
8181
],
8282
[
8383
'replaces root string values',
8484
'hello',
8585
'world',
86-
[{ op: 'replace', path: '', value: 'world' }],
86+
[{op: 'replace', path: '', value: 'world'}],
8787
],
8888
[
8989
'replaces root boolean values',
9090
true,
9191
false,
92-
[{ op: 'replace', path: '', value: false }],
92+
[{op: 'replace', path: '', value: false}],
9393
],
9494

9595
['replaces root empty arrays', [], [], []],
9696
[
9797
'replaces root object property',
98-
{ a: 'a', b: 'b' },
99-
{ a: 'a', b: 'c' },
98+
{a: 'a', b: 'b'},
99+
{a: 'a', b: 'c'},
100100
[
101101
{
102102
op: 'replace',
@@ -109,12 +109,12 @@ describe('a generate json patch function', () => {
109109
'replaces root array elements',
110110
[1, 2, 3],
111111
[1, 2, 4],
112-
[{ op: 'replace', path: '/2', value: 4 }],
112+
[{op: 'replace', path: '/2', value: 4}],
113113
],
114114
[
115115
'replaces an obj prop with an array property',
116-
{ prop: { hello: 'world' } },
117-
{ prop: ['hello', 'world'] },
116+
{prop: {hello: 'world'}},
117+
{prop: ['hello', 'world']},
118118
[
119119
{
120120
op: 'replace',
@@ -125,20 +125,20 @@ describe('a generate json patch function', () => {
125125
],
126126
[
127127
'replaces an array prop with an obj property',
128-
{ prop: ['hello', 'world'] },
129-
{ prop: { hello: 'world' } },
128+
{prop: ['hello', 'world']},
129+
{prop: {hello: 'world'}},
130130
[
131131
{
132132
op: 'replace',
133133
path: '/prop',
134-
value: { hello: 'world' },
134+
value: {hello: 'world'},
135135
},
136136
],
137137
],
138138
[
139139
'replaces a deep nested object property',
140-
{ root: { first: { second: { third: 'before' } } } },
141-
{ root: { first: { second: { third: 'after' } } } },
140+
{root: {first: {second: {third: 'before'}}}},
141+
{root: {first: {second: {third: 'after'}}}},
142142
[
143143
{
144144
op: 'replace',
@@ -153,13 +153,13 @@ describe('a generate json patch function', () => {
153153
root: {
154154
first: [
155155
{},
156-
{ second: { third: 'before', list: ['hello', 'world'] } },
156+
{second: {third: 'before', list: ['hello', 'world']}},
157157
],
158158
},
159159
},
160160
{
161161
root: {
162-
first: [{}, { second: { third: 'after', list: ['hello', 'world'] } }],
162+
first: [{}, {second: {third: 'after', list: ['hello', 'world']}}],
163163
},
164164
},
165165
[
@@ -172,8 +172,8 @@ describe('a generate json patch function', () => {
172172
],
173173
[
174174
'detects several changes on arrays by reference',
175-
{ root: [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }] },
176-
{ root: [{ id: 4 }, { id: 3 }, { id: 2 }] },
175+
{root: [{id: 1}, {id: 2}, {id: 3}, {id: 4}]},
176+
{root: [{id: 4}, {id: 3}, {id: 2}]},
177177
[
178178
{
179179
op: 'remove',
@@ -210,8 +210,8 @@ describe('a generate json patch function', () => {
210210

211211
describe('with an array value hash function', () => {
212212
it('throws when objectHash is not a function', () => {
213-
const before = [{ id: 1, paramOne: 'before' }];
214-
const after = [{ id: 2, paramOne: 'after' }];
213+
const before = [{id: 1, paramOne: 'before'}];
214+
const after = [{id: 2, paramOne: 'after'}];
215215

216216
assert.throws(() =>
217217
generateJSONPatch(before, after, {
@@ -223,12 +223,12 @@ describe('a generate json patch function', () => {
223223

224224
it('handles changes with change and move on the same property', () => {
225225
const before = [
226-
{ id: 1, paramOne: 'future', paramTwo: 'past' },
227-
{ id: 2, paramOne: 'current' },
226+
{id: 1, paramOne: 'future', paramTwo: 'past'},
227+
{id: 2, paramOne: 'current'},
228228
];
229229
const after = [
230-
{ id: 2, paramOne: 'current' },
231-
{ id: 1, paramOne: 'current' },
230+
{id: 2, paramOne: 'current'},
231+
{id: 1, paramOne: 'current'},
232232
];
233233

234234
const patch = generateJSONPatch(before, after, {
@@ -239,19 +239,19 @@ describe('a generate json patch function', () => {
239239

240240
const patched = doPatch(before, patch);
241241
expect(patched).to.be.eql([
242-
{ id: 2, paramOne: 'current' },
243-
{ id: 1, paramOne: 'current' },
242+
{id: 2, paramOne: 'current'},
243+
{id: 1, paramOne: 'current'},
244244
]);
245245
});
246246

247247
it('handles changes on array objects with different shape', () => {
248-
const before = [{ id: 1, paramOne: 'current' }];
248+
const before = [{id: 1, paramOne: 'current'}];
249249
const after = [
250250
{
251251
id: 1,
252252
paramOne: 'future',
253253
paramTwo: 'past',
254-
paramThree: { nested: 'some text' },
254+
paramThree: {nested: 'some text'},
255255
},
256256
];
257257

@@ -268,7 +268,7 @@ describe('a generate json patch function', () => {
268268
id: 1,
269269
paramOne: 'future',
270270
paramTwo: 'past',
271-
paramThree: { nested: 'some text' },
271+
paramThree: {nested: 'some text'},
272272
},
273273
]);
274274
});
@@ -487,7 +487,7 @@ describe('a generate json patch function', () => {
487487
objectHash: function (obj: any) {
488488
return `${obj.id}`;
489489
},
490-
array: { ignoreMove: true },
490+
array: {ignoreMove: true},
491491
});
492492

493493
const patched = doPatch(before, patch);
@@ -514,10 +514,10 @@ describe('a generate json patch function', () => {
514514
type: 'Granada',
515515
colors: ['red', 'silver', 'yellow'],
516516
engine: [
517-
{ name: 'Cologne V6 2.6', hp: 125 },
518-
{ name: 'Cologne V6 2.0', hp: 90 },
519-
{ name: 'Cologne V6 2.3', hp: 108 },
520-
{ name: 'Essex V6 3.0', hp: 150 },
517+
{name: 'Cologne V6 2.6', hp: 125},
518+
{name: 'Cologne V6 2.0', hp: 90},
519+
{name: 'Cologne V6 2.3', hp: 108},
520+
{name: 'Essex V6 3.0', hp: 150},
521521
],
522522
};
523523

@@ -526,16 +526,16 @@ describe('a generate json patch function', () => {
526526
type: 'Granada',
527527
colors: ['red', 'silver', 'yellow'],
528528
engine: [
529-
{ name: 'Essex V6 3.0', hp: 138 },
530-
{ name: 'Cologne V6 2.6', hp: 125 },
531-
{ name: 'Cologne V6 2.0', hp: 90 },
532-
{ name: 'Cologne V6 2.3', hp: 108 },
529+
{name: 'Essex V6 3.0', hp: 138},
530+
{name: 'Cologne V6 2.6', hp: 125},
531+
{name: 'Cologne V6 2.0', hp: 90},
532+
{name: 'Cologne V6 2.3', hp: 108},
533533
],
534534
};
535535

536536
const patch = generateJSONPatch(before, after, {
537537
objectHash: function (value: JsonValue, context: ObjectHashContext) {
538-
const { length, last } = pathInfo(context.path);
538+
const {length, last} = pathInfo(context.path);
539539
if (length === 2 && last === 'engine') {
540540
// @ts-ignore
541541
return value?.name;
@@ -548,8 +548,8 @@ describe('a generate json patch function', () => {
548548
expect(patched).to.be.eql(after);
549549

550550
expect(patch).to.be.eql([
551-
{ op: 'replace', path: '/engine/3/hp', value: 138 },
552-
{ op: 'move', from: '/engine/3', path: '/engine/0' },
551+
{op: 'replace', path: '/engine/3/hp', value: 138},
552+
{op: 'move', from: '/engine/3', path: '/engine/0'},
553553
]);
554554
});
555555
});
@@ -584,7 +584,7 @@ describe('a generate json patch function', () => {
584584
expect(patched).to.be.eql({
585585
id: 1,
586586
paramOne: 'after',
587-
paramTwo: { ignoreMe: 'before', doNotIgnoreMe: 'after' },
587+
paramTwo: {ignoreMe: 'before', doNotIgnoreMe: 'after'},
588588
});
589589
});
590590

@@ -627,14 +627,14 @@ describe('a generate json patch function', () => {
627627
paramTwo: {
628628
ignoreMe: 'before',
629629
doNotIgnoreMe: 'after',
630-
two: { ignoreMe: 'after' },
630+
two: {ignoreMe: 'after'},
631631
},
632632
});
633633

634634
expect(patch).to.eql([
635-
{ op: 'replace', path: '/paramOne', value: 'after' },
636-
{ op: 'replace', path: '/paramTwo/doNotIgnoreMe', value: 'after' },
637-
{ op: 'replace', path: '/paramTwo/two/ignoreMe', value: 'after' },
635+
{op: 'replace', path: '/paramOne', value: 'after'},
636+
{op: 'replace', path: '/paramTwo/doNotIgnoreMe', value: 'after'},
637+
{op: 'replace', path: '/paramTwo/two/ignoreMe', value: 'after'},
638638
]);
639639
});
640640

@@ -648,11 +648,49 @@ describe('a generate json patch function', () => {
648648
expect(patched).to.be.eql([1]);
649649

650650
expect(patch).to.eql([
651-
{ op: 'remove', path: '/2' },
652-
{ op: 'remove', path: '/1' },
651+
{op: 'remove', path: '/2'},
652+
{op: 'remove', path: '/1'},
653653
]);
654654
});
655655
});
656+
657+
describe('with maxDepth config', () => {
658+
const before = {
659+
firstLevel: {
660+
secondLevel: {
661+
thirdLevel: {
662+
fourthLevel: "hello-world"
663+
},
664+
thirdLevelTwo: "hello"
665+
}
666+
}
667+
}
668+
669+
const after = {
670+
firstLevel: {
671+
secondLevel: {
672+
thirdLevel: {
673+
fourthLevel: "hello-brave-new-world"
674+
},
675+
thirdLevelTwo: "hello"
676+
}
677+
}
678+
}
679+
680+
const patch = generateJSONPatch(before, after, {maxDepth: 3});
681+
expect(patch).to.eql([
682+
{
683+
op: 'replace',
684+
path: '/firstLevel/secondLevel',
685+
value: {
686+
thirdLevel: {
687+
fourthLevel: "hello-brave-new-world"
688+
},
689+
thirdLevelTwo: "hello"
690+
}
691+
}
692+
]);
693+
})
656694
});
657695

658696
function doPatch(json: JsonValue, patch: Patch) {

src/index.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ export type JsonPatchConfig = {
7171
objectHash?: ObjectHash;
7272
propertyFilter?: PropertyFilter;
7373
array?: { ignoreMove?: boolean };
74+
maxDepth?: number
7475
};
7576

7677
export const defaultObjectHash: ObjectHash = (obj, context) => {
@@ -82,7 +83,7 @@ export function generateJSONPatch(
8283
after: JsonValue,
8384
config: JsonPatchConfig = {}
8485
): Patch {
85-
const { objectHash = defaultObjectHash, propertyFilter } = config;
86+
const { objectHash = defaultObjectHash, propertyFilter, maxDepth = Infinity } = config;
8687
const patch: Patch = [];
8788
const hasPropertyFilter = typeof propertyFilter === 'function';
8889

@@ -181,7 +182,11 @@ export function generateJSONPatch(
181182
compareArrays(leftValue, rightValue, newPath);
182183
} else if (isJsonObject(rightValue)) {
183184
if (isJsonObject(leftValue)) {
184-
compareObjects(newPath, leftValue, rightValue);
185+
if(maxDepth <= path.split('/').length) {
186+
patch.push({ op: 'replace', path: path, value: rightJsonValue });
187+
} else {
188+
compareObjects(newPath, leftValue, rightValue);
189+
}
185190
} else if (leftJsonValue.hasOwnProperty(rightKey)) {
186191
patch.push({ op: 'replace', path: newPath, value: rightValue });
187192
} else {

0 commit comments

Comments
 (0)