Extended immutableJSONPatch with options fromJSON, toJSON, clone

This commit is contained in:
jos 2018-08-29 22:23:30 +02:00
parent dc814a3aa5
commit 410353c86f
9 changed files with 143 additions and 72 deletions

View File

@ -159,7 +159,7 @@ class App extends Component {
handlePatch = (patch, revert) => { handlePatch = (patch, revert) => {
this.log('onPatch patch=', patch, ', revert=', revert) this.log('onPatch patch=', patch, ', revert=', revert)
window.immutableJsonPatch = patch window.immutableJSONPatch = patch
window.revert = revert window.revert = revert
} }

View File

@ -66,7 +66,7 @@
name: 'myObject', name: 'myObject',
onPatch: function (patch, revert) { onPatch: function (patch, revert) {
log('onPatch patch=', patch, ', revert=', revert) log('onPatch patch=', patch, ', revert=', revert)
window.immutableJsonPatch = patch window.immutableJSONPatch = patch
window.revert = revert window.revert = revert
}, },
onPatchText: function (patch, revert) { onPatchText: function (patch, revert) {

View File

@ -3,7 +3,7 @@
import { sort } from './actions' import { sort } from './actions'
import { createAssertEqualEson } from './utils/assertEqualEson' import { createAssertEqualEson } from './utils/assertEqualEson'
import { ID, syncEson } from './eson' import { ID, syncEson } from './eson'
import { immutableJsonPatch } from './immutableJsonPatch' import { immutableJSONPatch } from './immutableJSONPatch'
const assertEqualEson = createAssertEqualEson(expect) const assertEqualEson = createAssertEqualEson(expect)
@ -20,14 +20,14 @@ const assertEqualEson = createAssertEqualEson(expect)
it('sort root Array', () => { it('sort root Array', () => {
const eson = syncEson([1,3,2]) const eson = syncEson([1,3,2])
assertEqualEson(immutableJsonPatch(eson, sort(eson, [])).json, syncEson([1,2,3])) assertEqualEson(immutableJSONPatch(eson, sort(eson, [])).json, syncEson([1,2,3]))
assertEqualEson(immutableJsonPatch(eson, sort(eson, [], 'asc')).json, syncEson([1,2,3])) assertEqualEson(immutableJSONPatch(eson, sort(eson, [], 'asc')).json, syncEson([1,2,3]))
assertEqualEson(immutableJsonPatch(eson, sort(eson, [], 'desc')).json, syncEson([3,2,1])) assertEqualEson(immutableJSONPatch(eson, sort(eson, [], 'desc')).json, syncEson([3,2,1]))
}) })
it('sort nested Array', () => { it('sort nested Array', () => {
const eson = syncEson({arr: [4,1,8,5,3,9,2,7,6]}) const eson = syncEson({arr: [4,1,8,5,3,9,2,7,6]})
const actual = immutableJsonPatch(eson, sort(eson, ['arr'])).json const actual = immutableJSONPatch(eson, sort(eson, ['arr'])).json
const expected = syncEson({arr: [1,2,3,4,5,6,7,8,9]}) const expected = syncEson({arr: [1,2,3,4,5,6,7,8,9]})
assertEqualEson(actual, expected) assertEqualEson(actual, expected)
}) })
@ -35,7 +35,7 @@ it('sort nested Array', () => {
it('sort nested Array reverse order', () => { it('sort nested Array reverse order', () => {
// no order provided -> order ascending, but if nothing changes, order descending // no order provided -> order ascending, but if nothing changes, order descending
const eson = syncEson({arr: [1,2,3,4,5,6,7,8,9]}) const eson = syncEson({arr: [1,2,3,4,5,6,7,8,9]})
const actual = immutableJsonPatch(eson, sort(eson, ['arr'])).json const actual = immutableJSONPatch(eson, sort(eson, ['arr'])).json
const expected = syncEson({arr: [9,8,7,6,5,4,3,2,1]}) const expected = syncEson({arr: [9,8,7,6,5,4,3,2,1]})
assertEqualEson(actual, expected) assertEqualEson(actual, expected)

View File

@ -7,7 +7,7 @@ import { createFindKeyBinding } from '../utils/keyBindings'
import { KEY_BINDINGS } from '../constants' import { KEY_BINDINGS } from '../constants'
import ModeButton from './menu/ModeButton' import ModeButton from './menu/ModeButton'
import { immutableJsonPatch } from '../immutableJsonPatch' import { immutableJSONPatch } from '../immutableJSONPatch'
const AJV_OPTIONS = { const AJV_OPTIONS = {
allErrors: true, allErrors: true,
@ -330,7 +330,7 @@ export default class TextMode extends Component {
patch (operations) { patch (operations) {
const json = this.get() const json = this.get()
const result = immutableJsonPatch(json, operations) const result = immutableJSONPatch(json, operations)
this.set(result.data) this.set(result.data)

View File

@ -48,16 +48,15 @@ import {
} from './utils/domSelector' } from './utils/domSelector'
import { createFindKeyBinding } from '../utils/keyBindings' import { createFindKeyBinding } from '../utils/keyBindings'
import { KEY_BINDINGS } from '../constants' import { KEY_BINDINGS } from '../constants'
import { immutableJsonPatch } from '../immutableJsonPatch' import { immutableJSONPatch } from '../immutableJSONPatch'
import { import {
applyErrors, applySelection, contentsFromPaths, applyErrors, applySelection, contentsFromPaths,
expand, expand,
EXPANDED, EXPANDED,
expandPath, expandPath, immutableESONPatch,
nextSearchResult, pathsFromSelection, previousSearchResult, nextSearchResult, pathsFromSelection, previousSearchResult,
search, search,
syncEson, syncEson
toEsonPatchOperation
} from '../eson' } from '../eson'
const AJV_OPTIONS = { const AJV_OPTIONS = {
@ -904,8 +903,8 @@ export default class TreeMode extends PureComponent {
const historyIndex = this.state.historyIndex const historyIndex = this.state.historyIndex
const historyItem = history[historyIndex] const historyItem = history[historyIndex]
const jsonResult = immutableJsonPatch(this.state.json, historyItem.undo) const jsonResult = immutableJSONPatch(this.state.json, historyItem.undo)
const esonResult = immutableJsonPatch(this.state.eson, historyItem.undo.map(toEsonPatchOperation)) const esonResult = immutableESONPatch(this.state.eson, historyItem.undo)
// FIXME: apply search // FIXME: apply search
this.setState({ this.setState({
@ -925,8 +924,8 @@ export default class TreeMode extends PureComponent {
const historyIndex = this.state.historyIndex - 1 const historyIndex = this.state.historyIndex - 1
const historyItem = history[historyIndex] const historyItem = history[historyIndex]
const jsonResult = immutableJsonPatch(this.state.json, historyItem.redo) const jsonResult = immutableJSONPatch(this.state.json, historyItem.redo)
const esonResult = immutableJsonPatch(this.state.eson, historyItem.undo.map(toEsonPatchOperation)) const esonResult = immutableESONPatch(this.state.eson, historyItem.redo)
// FIXME: apply search // FIXME: apply search
this.setState({ this.setState({
@ -954,8 +953,8 @@ export default class TreeMode extends PureComponent {
console.log('patch', operations) // TODO: cleanup console.log('patch', operations) // TODO: cleanup
const jsonResult = immutableJsonPatch(this.state.json, operations) const jsonResult = immutableJSONPatch(this.state.json, operations)
const esonResult = immutableJsonPatch(this.state.eson, operations.map(toEsonPatchOperation)) const esonResult = immutableESONPatch(this.state.eson, operations)
if (this.props.history !== false) { if (this.props.history !== false) {
// update data and store history // update data and store history

View File

@ -7,6 +7,7 @@ import isEmpty from 'lodash/isEmpty'
import isEqual from 'lodash/isEqual' import isEqual from 'lodash/isEqual'
import naturalSort from 'javascript-natural-sort' import naturalSort from 'javascript-natural-sort'
import times from 'lodash/times' import times from 'lodash/times'
import { immutableJSONPatch } from './immutableJSONPatch'
export const ID = typeof Symbol === 'function' ? Symbol('id') : '@jsoneditor-id' export const ID = typeof Symbol === 'function' ? Symbol('id') : '@jsoneditor-id'
export const TYPE = typeof Symbol === 'function' ? Symbol('type') : '@jsoneditor-type' // 'object', 'array', 'value', or 'undefined' export const TYPE = typeof Symbol === 'function' ? Symbol('type') : '@jsoneditor-type' // 'object', 'array', 'value', or 'undefined'
@ -529,14 +530,19 @@ export function pathsFromSelection (eson, selection) {
} }
/** /**
* Convert the value of a JSON Patch action into a ESON object * Apply a JSON patch document to an ESON object.
* @param {JSONPatchOperation} operation * - Applies meta information to added values
* @returns {ESONPatchOperation} * - Reckons with creating unique id's when duplicating data
* @param eson
* @param operations
* @returns {{json: JSON, revert: JSONPatchDocument, error: (Error|null)}}
*/ */
export function toEsonPatchOperation (operation) { export function immutableESONPatch (eson, operations) {
return ('value' in operation) return immutableJSONPatch(eson, operations, {
? setIn(operation, ['value'], syncEson(operation.value)) fromJSON: (value, previousEson) => syncEson(value, previousEson),
: operation toJSON: (eson) => eson[VALUE],
clone: (value) => setIn(value, [ID], createId())
})
} }
// TODO: comment // TODO: comment

View File

@ -4,15 +4,22 @@ import initial from 'lodash/initial'
import { setIn, getIn, deleteIn, insertAt, existsIn } from './utils/immutabilityHelpers' import { setIn, getIn, deleteIn, insertAt, existsIn } from './utils/immutabilityHelpers'
import { parseJSONPointer, compileJSONPointer } from './jsonPointer' import { parseJSONPointer, compileJSONPointer } from './jsonPointer'
const DEFAULT_OPTIONS = {
fromJSON: (json, previousObject) => json,
toJSON: (object) => object,
clone: (object) => object
}
/** /**
* Apply a patch to a JSON object * Apply a patch to a JSON object
* The original JSON object will not be changed, * The original JSON object will not be changed,
* instead, the patch is applied in an immutable way * instead, the patch is applied in an immutable way
* @param {JSON} json * @param {JSON} json
* @param {JSONPatchDocument} operations Array with JSON patch actions * @param {JSONPatchDocument} operations Array with JSON patch actions
* @param {JSONPatchOptions} [options]
* @return {{json: JSON, revert: JSONPatchDocument, error: Error | null}} * @return {{json: JSON, revert: JSONPatchDocument, error: Error | null}}
*/ */
export function immutableJsonPatch (json, operations) { export function immutableJSONPatch (json, operations, options = DEFAULT_OPTIONS) {
let updatedJson = json let updatedJson = json
let revert = [] let revert = []
@ -23,14 +30,14 @@ export function immutableJsonPatch (json, operations) {
switch (operation.op) { switch (operation.op) {
case 'add': { case 'add': {
const result = add(updatedJson, path, operation.value) const result = add(updatedJson, path, operation.value, options)
updatedJson = result.json updatedJson = result.json
revert = result.revert.concat(revert) revert = result.revert.concat(revert)
break break
} }
case 'remove': { case 'remove': {
const result = remove(updatedJson, path) const result = remove(updatedJson, path, options)
updatedJson = result.json updatedJson = result.json
revert = result.revert.concat(revert) revert = result.revert.concat(revert)
@ -38,7 +45,7 @@ export function immutableJsonPatch (json, operations) {
} }
case 'replace': { case 'replace': {
const result = replace(updatedJson, path, operation.value) const result = replace(updatedJson, path, operation.value, options)
updatedJson = result.json updatedJson = result.json
revert = result.revert.concat(revert) revert = result.revert.concat(revert)
@ -54,7 +61,7 @@ export function immutableJsonPatch (json, operations) {
} }
} }
const result = copy(updatedJson, path, from) const result = copy(updatedJson, path, from, options)
updatedJson = result.json updatedJson = result.json
revert = result.revert.concat(revert) revert = result.revert.concat(revert)
@ -70,7 +77,7 @@ export function immutableJsonPatch (json, operations) {
} }
} }
const result = move(updatedJson, path, from) const result = move(updatedJson, path, from, options)
updatedJson = result.json updatedJson = result.json
revert = result.revert.concat(revert) revert = result.revert.concat(revert)
@ -79,7 +86,7 @@ export function immutableJsonPatch (json, operations) {
case 'test': { case 'test': {
// when a test fails, cancel the whole patch and return the error // when a test fails, cancel the whole patch and return the error
const error = test(updatedJson, path, operation.value) const error = test(updatedJson, path, operation.value, options)
if (error) { if (error) {
return { json, revert: [], error} return { json, revert: [], error}
} }
@ -110,13 +117,15 @@ export function immutableJsonPatch (json, operations) {
* @param {JSON} json * @param {JSON} json
* @param {Path} path * @param {Path} path
* @param {JSON} value * @param {JSON} value
* @param {JSONPatchOptions} [options]
* @return {{json: JSON, revert: JSONPatchDocument}} * @return {{json: JSON, revert: JSONPatchDocument}}
*/ */
export function replace (json, path, value) { export function replace (json, path, value, options) {
const oldValue = getIn(json, path) const oldValue = getIn(json, path)
const newValue = options.fromJSON(value, oldValue)
return { return {
json: setIn(json, path, value), json: setIn(json, path, newValue),
revert: [{ revert: [{
op: 'replace', op: 'replace',
path: compileJSONPointer(path), path: compileJSONPointer(path),
@ -129,9 +138,10 @@ export function replace (json, path, value) {
* Remove an item or property * Remove an item or property
* @param {JSON} json * @param {JSON} json
* @param {Path} path * @param {Path} path
* @param {JSONPatchOptions} [options]
* @return {{json: JSON, revert: JSONPatchDocument}} * @return {{json: JSON, revert: JSONPatchDocument}}
*/ */
export function remove (json, path) { export function remove (json, path, options) {
const oldValue = getIn(json, path) const oldValue = getIn(json, path)
return { return {
@ -139,7 +149,7 @@ export function remove (json, path) {
revert: [{ revert: [{
op: 'add', op: 'add',
path: compileJSONPointer(path), path: compileJSONPointer(path),
value: oldValue value: options.toJSON(oldValue)
}] }]
} }
} }
@ -148,27 +158,28 @@ export function remove (json, path) {
* @param {JSON} json * @param {JSON} json
* @param {Path} path * @param {Path} path
* @param {JSON} value * @param {JSON} value
* @param {JSONPatchOptions} [options]
* @return {{json: JSON, revert: JSONPatchDocument}} * @return {{json: JSON, revert: JSONPatchDocument}}
* @private * @private
*/ */
export function add (json, path, value) { export function add (json, path, value, options) {
const resolvedPath = resolvePathIndex(json, path) const resolvedPath = resolvePathIndex(json, path)
const parent = getIn(json, initial(path)) const parent = getIn(json, initial(path))
const parentIsArray = Array.isArray(parent) const parentIsArray = Array.isArray(parent)
const oldValue = getIn(json, resolvedPath)
const newValue = options.fromJSON(value, oldValue)
const updatedJson = parentIsArray const updatedJson = parentIsArray
? insertAt(json, resolvedPath, value) ? insertAt(json, resolvedPath, newValue)
: setIn(json, resolvedPath, value) : setIn(json, resolvedPath, newValue)
if (!parentIsArray && existsIn(json, resolvedPath)) { if (!parentIsArray && existsIn(json, resolvedPath)) {
const oldValue = getIn(json, resolvedPath)
return { return {
json: updatedJson, json: updatedJson,
revert: [{ revert: [{
op: 'replace', op: 'replace',
path: compileJSONPointer(resolvedPath), path: compileJSONPointer(resolvedPath),
value: oldValue value: options.toJSON(oldValue)
}] }]
} }
} }
@ -188,13 +199,20 @@ export function add (json, path, value) {
* @param {JSON} json * @param {JSON} json
* @param {Path} path * @param {Path} path
* @param {Path} from * @param {Path} from
* @param {JSONPatchOptions} [options]
* @return {{json: JSON, revert: ESONPatchDocument}} * @return {{json: JSON, revert: ESONPatchDocument}}
* @private * @private
*/ */
export function copy (json, path, from) { export function copy (json, path, from, options) {
const value = getIn(json, from) const value = options.clone
? options.clone(getIn(json, from))
: options.fromJSON(options.toJSON(getIn(json, from)), undefined)
return add(json, path, value) return add(json, path, value, {
fromJSON: DEFAULT_OPTIONS.fromJSON,
toJSON: options.toJSON,
clone: options.clone
})
} }
/** /**
@ -202,17 +220,18 @@ export function copy (json, path, from) {
* @param {JSON} json * @param {JSON} json
* @param {Path} path * @param {Path} path
* @param {Path} from * @param {Path} from
* @param {JSONPatchOptions} [options]
* @return {{json: JSON, revert: ESONPatchDocument}} * @return {{json: JSON, revert: ESONPatchDocument}}
* @private * @private
*/ */
export function move (json, path, from) { export function move (json, path, from, options) {
const resolvedPath = resolvePathIndex(json, path) const resolvedPath = resolvePathIndex(json, path)
const parent = getIn(json, initial(path)) const parent = getIn(json, initial(path))
const parentIsArray = Array.isArray(parent) const parentIsArray = Array.isArray(parent)
const oldValue = getIn(json, path) const oldValue = getIn(json, path)
const value = getIn(json, from) const value = getIn(json, from)
const removedJson = remove(json, from).json const removedJson = remove(json, from, options).json
const updatedJson = parentIsArray const updatedJson = parentIsArray
? insertAt(removedJson, resolvedPath, value) ? insertAt(removedJson, resolvedPath, value)
: setIn(removedJson, resolvedPath, value) : setIn(removedJson, resolvedPath, value)
@ -230,7 +249,7 @@ export function move (json, path, from) {
{ {
op: 'add', op: 'add',
path: compileJSONPointer(resolvedPath), path: compileJSONPointer(resolvedPath),
value: oldValue value: options.toJSON(oldValue)
} }
] ]
} }
@ -254,10 +273,11 @@ export function move (json, path, from) {
* Throws an error when the test fails. * Throws an error when the test fails.
* @param {JSON} json * @param {JSON} json
* @param {Path} path * @param {Path} path
* @param {*} value * @param {JSON} value
* @param {JSONPatchOptions} [options]
* @return {null | Error} Returns an error when the tests, returns null otherwise * @return {null | Error} Returns an error when the tests, returns null otherwise
*/ */
export function test (json, path, value) { export function test (json, path, value, options) {
if (value === undefined) { if (value === undefined) {
return new Error('Test failed, no value provided') return new Error('Test failed, no value provided')
} }
@ -266,7 +286,7 @@ export function test (json, path, value) {
return new Error('Test failed, path not found') return new Error('Test failed, path not found')
} }
const actualValue = getIn(json, path) const actualValue = options.toJSON(getIn(json, path))
if (!isEqual(actualValue, value)) { if (!isEqual(actualValue, value)) {
return new Error('Test failed, value differs') return new Error('Test failed, value differs')
} }

View File

@ -1,6 +1,6 @@
'use strict' 'use strict'
import { immutableJsonPatch } from './immutableJsonPatch' import { immutableJSONPatch } from './immutableJSONPatch'
test('test toBe', () => { test('test toBe', () => {
const a = { x: 2 } const a = { x: 2 }
@ -22,7 +22,7 @@ test('jsonpatch add', () => {
{op: 'add', path: '/obj/b', value: {foo: 'bar'}} {op: 'add', path: '/obj/b', value: {foo: 'bar'}}
] ]
const result = immutableJsonPatch(json, patch) const result = immutableJSONPatch(json, patch)
expect(result.json).toEqual({ expect(result.json).toEqual({
arr: [1,2,3], arr: [1,2,3],
@ -44,7 +44,7 @@ test('jsonpatch add: insert in matrix', () => {
{op: 'add', path: '/arr/1', value: 4} {op: 'add', path: '/arr/1', value: 4}
] ]
const result = immutableJsonPatch(json, patch) const result = immutableJSONPatch(json, patch)
expect(result.json).toEqual({ expect(result.json).toEqual({
arr: [1,4,2,3], arr: [1,4,2,3],
@ -66,7 +66,7 @@ test('jsonpatch add: append to matrix', () => {
{op: 'add', path: '/arr/-', value: 4} {op: 'add', path: '/arr/-', value: 4}
] ]
const result = immutableJsonPatch(json, patch) const result = immutableJSONPatch(json, patch)
expect(result.json).toEqual({ expect(result.json).toEqual({
arr: [1,2,3,4], arr: [1,2,3,4],
@ -90,7 +90,7 @@ test('jsonpatch remove', () => {
{op: 'remove', path: '/arr/1'}, {op: 'remove', path: '/arr/1'},
] ]
const result = immutableJsonPatch(json, patch) const result = immutableJSONPatch(json, patch)
expect(result.json).toEqual({ expect(result.json).toEqual({
arr: [1,3], arr: [1,3],
@ -103,7 +103,7 @@ test('jsonpatch remove', () => {
]) ])
// test revert // test revert
const result2 = immutableJsonPatch(result.json, result.revert) const result2 = immutableJSONPatch(result.json, result.revert)
expect(result2.json).toEqual(json) expect(result2.json).toEqual(json)
expect(result2.revert).toEqual(patch) expect(result2.revert).toEqual(patch)
@ -122,7 +122,7 @@ test('jsonpatch replace', () => {
{op: 'replace', path: '/arr/1', value: 200}, {op: 'replace', path: '/arr/1', value: 200},
] ]
const result = immutableJsonPatch(json, patch) const result = immutableJSONPatch(json, patch)
expect(result.json).toEqual({ expect(result.json).toEqual({
arr: [1,200,3], arr: [1,200,3],
@ -135,7 +135,7 @@ test('jsonpatch replace', () => {
]) ])
// test revert // test revert
const result2 = immutableJsonPatch(result.json, result.revert) const result2 = immutableJSONPatch(result.json, result.revert)
expect(result2.json).toEqual(json) expect(result2.json).toEqual(json)
expect(result2.revert).toEqual([ expect(result2.revert).toEqual([
@ -155,7 +155,7 @@ test('jsonpatch copy', () => {
{op: 'copy', from: '/obj', path: '/arr/2'}, {op: 'copy', from: '/obj', path: '/arr/2'},
] ]
const result = immutableJsonPatch(json, patch) const result = immutableJSONPatch(json, patch)
expect(result.json).toEqual({ expect(result.json).toEqual({
arr: [1, 2, {a:4}, 3], arr: [1, 2, {a:4}, 3],
@ -166,7 +166,7 @@ test('jsonpatch copy', () => {
]) ])
// test revert // test revert
const result2 = immutableJsonPatch(result.json, result.revert) const result2 = immutableJSONPatch(result.json, result.revert)
expect(result2.json).toEqual(json) expect(result2.json).toEqual(json)
expect(result2.revert).toEqual([ expect(result2.revert).toEqual([
@ -187,7 +187,7 @@ test('jsonpatch move', () => {
{op: 'move', from: '/obj', path: '/arr/2'}, {op: 'move', from: '/obj', path: '/arr/2'},
] ]
const result = immutableJsonPatch(json, patch) const result = immutableJSONPatch(json, patch)
expect(result.error).toEqual(null) expect(result.error).toEqual(null)
expect(result.json).toEqual({ expect(result.json).toEqual({
@ -199,7 +199,7 @@ test('jsonpatch move', () => {
]) ])
// test revert // test revert
const result2 = immutableJsonPatch(result.json, result.revert) const result2 = immutableJSONPatch(result.json, result.revert)
expect(result2.json).toEqual(json) expect(result2.json).toEqual(json)
expect(result2.revert).toEqual(patch) expect(result2.revert).toEqual(patch)
@ -214,7 +214,7 @@ test('jsonpatch move and replace', () => {
{op: 'move', from: '/a', path: '/b'}, {op: 'move', from: '/a', path: '/b'},
] ]
const result = immutableJsonPatch(json, patch) const result = immutableJSONPatch(json, patch)
expect(result.json).toEqual({ b : 2 }) expect(result.json).toEqual({ b : 2 })
expect(result.revert).toEqual([ expect(result.revert).toEqual([
@ -223,7 +223,7 @@ test('jsonpatch move and replace', () => {
]) ])
// test revert // test revert
const result2 = immutableJsonPatch(result.json, result.revert) const result2 = immutableJSONPatch(result.json, result.revert)
expect(result2.json).toEqual(json) expect(result2.json).toEqual(json)
expect(result2.revert).toEqual([ expect(result2.revert).toEqual([
@ -243,7 +243,7 @@ test('jsonpatch move and replace (nested)', () => {
{op: 'move', from: '/obj', path: '/arr'}, {op: 'move', from: '/obj', path: '/arr'},
] ]
const result = immutableJsonPatch(json, patch) const result = immutableJSONPatch(json, patch)
expect(result.json).toEqual({ expect(result.json).toEqual({
arr: {a:4}, arr: {a:4},
@ -255,7 +255,7 @@ test('jsonpatch move and replace (nested)', () => {
]) ])
// test revert // test revert
const result2 = immutableJsonPatch(result.json, result.revert) const result2 = immutableJSONPatch(result.json, result.revert)
expect(result2.json).toEqual(json) expect(result2.json).toEqual(json)
expect(result2.revert).toEqual([ expect(result2.revert).toEqual([
@ -277,7 +277,7 @@ test('jsonpatch test (ok)', () => {
{op: 'add', path: '/added', value: 'ok'} {op: 'add', path: '/added', value: 'ok'}
] ]
const result = immutableJsonPatch(json, patch) const result = immutableJSONPatch(json, patch)
expect(result.json).toEqual({ expect(result.json).toEqual({
arr: [1,2,3], arr: [1,2,3],
@ -300,7 +300,7 @@ test('jsonpatch test (fail: path not found)', () => {
{op: 'add', path: '/added', value: 'ok'} {op: 'add', path: '/added', value: 'ok'}
] ]
const result = immutableJsonPatch(json, patch) const result = immutableJSONPatch(json, patch)
// patch shouldn't be applied // patch shouldn't be applied
expect(result.json).toEqual({ expect(result.json).toEqual({
@ -322,7 +322,7 @@ test('jsonpatch test (fail: value not equal)', () => {
{op: 'add', path: '/added', value: 'ok'} {op: 'add', path: '/added', value: 'ok'}
] ]
const result = immutableJsonPatch(json, patch) const result = immutableJSONPatch(json, patch)
// patch shouldn't be applied // patch shouldn't be applied
expect(result.json).toEqual({ expect(result.json).toEqual({
@ -332,3 +332,41 @@ test('jsonpatch test (fail: value not equal)', () => {
expect(result.revert).toEqual([]) expect(result.revert).toEqual([])
expect(result.error.toString()).toEqual('Error: Test failed, value differs') expect(result.error.toString()).toEqual('Error: Test failed, value differs')
}) })
test('jsonpatch options', () => {
const json = {
arr: [1,2,3],
obj: {a : 2}
}
const patch = [
{op: 'add', path: '/obj/a', value: 4 }
]
const result = immutableJSONPatch(json, patch, {
fromJSON: function (value, previousObject) {
return { value, previousObject }
},
toJSON: value => value
})
expect(result.json).toEqual({
arr: [1,2,3],
obj: {a : { value: 4, previousObject: 2 }}
})
const patch2 = [
{op: 'add', path: '/obj/b', value: 4 }
]
const result2 = immutableJSONPatch(json, patch2, {
fromJSON: function (value, previousObject) {
return { value, previousObject }
},
toJSON: value => value
})
expect(result2.json).toEqual({
arr: [1,2,3],
obj: {a : 2, b: { value: 4, previousObject: undefined }}
})
})
// TODO: test all operations with JSONPatchOptions (not just add)

View File

@ -73,6 +73,14 @@
* @typedef {JSONPatchOperation[]} JSONPatchDocument * @typedef {JSONPatchOperation[]} JSONPatchDocument
*/ */
/**
* @typedef {{
* fromJSON: function(json: JSON, previousObject: * | undefined),
* toJSON: function(object: *),
* clone: function(object: *)
* }} JSONPatchOptions
*/
/** /**
* @typedef {{ * @typedef {{
* op: 'add' | 'remove' | 'replace' | 'copy' | 'move' | 'test', * op: 'add' | 'remove' | 'replace' | 'copy' | 'move' | 'test',