diff --git a/src/jsonData.js b/src/jsonData.js index 9331748..8e1229f 100644 --- a/src/jsonData.js +++ b/src/jsonData.js @@ -352,10 +352,11 @@ export function simplifyPatch(patch: JSONPatch) { * @param {string} path * @param {JSONData} value * @param {{before?: string}} [options] + * @param {number} [id] Optional id for the new item * @return {{data: JSONData, revert: JSONPatch}} * @private */ -export function add (data: JSONData, path: string, value: JSONData, options) { +export function add (data: JSONData, path: string, value: JSONData, options, id = getId()) { const pathArray = parseJSONPointer(path) const parentPath = pathArray.slice(0, pathArray.length - 1) const dataPath = toDataPath(data, parentPath) @@ -366,7 +367,7 @@ export function add (data: JSONData, path: string, value: JSONData, options) { let updatedData if (parent.type === 'Array') { const newItem = { - id: getId(), // TODO: create a unique id within current id's instead of using a global, ever incrementing id + id, // TODO: create a unique id within current id's instead of using a global, ever incrementing id value } updatedData = insertAt(data, dataPath.concat('items', prop), newItem) @@ -380,8 +381,7 @@ export function add (data: JSONData, path: string, value: JSONData, options) { } else { // insert new item - const newId = getId() - const newProp = { id: newId, name: prop, value } + const newProp = { id, name: prop, value } const index = (options && typeof options.before === 'string') ? findPropertyIndex(object, options.before) // insert before : object.props.length // append @@ -441,13 +441,17 @@ export function copy (data: JSONData, path: string, from: string, options) { */ export function move (data: JSONData, path: string, from: string, options) { const fromArray = parseJSONPointer(from) - const dataValue = getIn(data, toDataPath(data, fromArray)) + const prop = getIn(data, allButLast(toDataPath(data, fromArray))) + const dataValue = prop.value + const id = prop.id // we want to use the existing id in case the move is a renaming a property + // FIXME: only reuse the existing id when move is renaming a property in the same object - const parentPath = fromArray.slice(0, fromArray.length - 1) - const parent = getIn(data, toDataPath(data, parentPath)) + const parentPathFrom = allButLast(fromArray) + const parent = getIn(data, toDataPath(data, parentPathFrom)) const result1 = remove(data, from) - const result2 = add(result1.data, path, dataValue, options) + const result2 = add(result1.data, path, dataValue, options, id) + // FIXME: passing id as parameter is ugly, make that redundant (use replace instead of remove/add? (that would give less predictive output :( )) const before = result1.revert[0].jsoneditor.before const beforeNeeded = (parent.type === 'Object' && before) @@ -706,11 +710,11 @@ export function transform (data: JSONData, callback: RecurseCallback) { * Recursively transform JSONData * @param {JSONData} value * @param {Path} path - * @param {JSONData | null} root The root object, object at path=[] + * @param {JSONData} root The root object, object at path=[] * @param {function(value: JSONData, path: Path, root: JSONData)} callback * @return {JSONData} Returns the transformed data */ -function recurseTransform (value: JSONData, path: Path, root?: JSONData, callback: RecurseCallback) : JSONData{ +function recurseTransform (value: JSONData, path: Path, root: JSONData, callback: RecurseCallback) : JSONData{ let updatedValue = callback(value, path, root) if (value.type === 'Array') { diff --git a/test/jsonData.test.js b/test/jsonData.test.js index ec73803..a54dc38 100644 --- a/test/jsonData.test.js +++ b/test/jsonData.test.js @@ -766,34 +766,35 @@ test('jsonpatch copy', t => { ]) }) -// test('jsonpatch copy (create new ids)', t => { -// const json = { foo: { bar: 42 } } -// const patch = [ -// {op: 'copy', from: '/foo', path: '/copied'} -// ] -// -// const data = jsonToData(json) -// const objectId = data.id -// const fooId = data.props[0].value.id -// const barId = data.props[0].value.props[0].value.id -// -// const patchedData = patchData(data, patch).data -// const patchedObjectId = patchedData.id -// const patchedFooId = patchedData.props[0].value.id -// const patchedBarId = patchedData.props[0].value.props[0].value.id -// const patchedCopiedId = patchedData.props[1].value.id -// const patchedCopiedBarId = patchedData.props[1].value.props[0].value.id -// -// t.is(patchedData.props[0].name, 'foo') -// t.is(patchedData.props[1].name, 'copied') -// -// t.is(patchedObjectId, objectId, 'same object id') -// t.is(patchedFooId, fooId, 'same foo id') -// t.is(patchedBarId, barId, 'same bar id') -// t.not(patchedCopiedId, patchedFooId, 'different copied foo id') -// t.not(patchedCopiedBarId, patchedBarId, 'different copied bar id') -// }) -// +test('jsonpatch copy (keeps the same ids)', t => { + const json = { foo: { bar: 42 } } + const patch = [ + {op: 'copy', from: '/foo', path: '/copied'} + ] + + const data = jsonToData(json) + const fooId = data.props[0].id + const barId = data.props[0].value.props[0].id + + const patchedData = patchData(data, patch).data + const patchedFooId = patchedData.props[0].id + const patchedBarId = patchedData.props[0].value.props[0].id + const copiedId = patchedData.props[1].id + const patchedCopiedBarId = patchedData.props[1].value.props[0].id + + t.is(patchedData.props[0].name, 'foo') + t.is(patchedData.props[1].name, 'copied') + + t.is(patchedFooId, fooId, 'same foo id') + t.is(patchedBarId, barId, 'same bar id') + + t.not(copiedId, fooId, 'different id of property copied') + + // The id's of the copied childs are the same, that's okish, they will not bite each other + // FIXME: better solution for id's either always unique, or unique per object/array + t.is(patchedCopiedBarId, patchedBarId, 'same copied bar id') +}) + test('jsonpatch move', t => { const json = { arr: [1,2,3], @@ -874,11 +875,15 @@ test('jsonpatch move and replace', t => { ] const data = jsonToData(json) + const result = patchData(data, patch) const patchedData = result.data const revert = result.revert const patchedJson = dataToJson(patchedData) + // id of the replaced B must be kept intact + t.is(patchedData.props[0].id, data.props[1].id) + replaceIds(patchedData) t.deepEqual(patchedData, { "type": "Object", @@ -958,15 +963,11 @@ test('jsonpatch move (keep id intact)', t => { ] const data = jsonToData(json) - const objectId = data.id - const valueId = data.props[0].value.id + const valueId = data.props[0].id const patchedData = patchData(data, patch).data + const patchedValueId = patchedData.props[0].id - const patchedObjectId = patchedData.id - const patchedValueId = patchedData.props[0].value.id - - t.is(patchedObjectId, objectId) t.is(patchedValueId, valueId) }) @@ -983,10 +984,9 @@ test('jsonpatch move and replace (keep ids intact)', t => { t.is(data.props[1].name, 'b') const patchedData = patchData(data, patch).data - const patchedBId = patchedData.props[0].id t.is(patchedData.props[0].name, 'b') - t.is(patchedBId, bId) + t.is(patchedData.props[0].id, bId) }) test('jsonpatch test (ok)', t => {