From 78c5d926fa4f1733eaba852577f5307bbb4ab864 Mon Sep 17 00:00:00 2001 From: benco1967 Date: Tue, 18 Jul 2017 21:15:07 +0200 Subject: [PATCH 1/7] use of a swith instead of if else, faster and more readable --- index.js | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/index.js b/index.js index cf977ef..e6fe3e0 100644 --- a/index.js +++ b/index.js @@ -3,7 +3,8 @@ var toDot = require('jsonpath-to-dot'); module.exports = function(patches){ var update = {}; patches.map(function(p){ - if(p.op === 'add'){ + switch(p.op) { + case 'add': var path = toDot(p.path), parts = path.split('.'); @@ -47,16 +48,18 @@ module.exports = function(patches){ update.$push[path] = p.value; } } - } - else if(p.op === 'remove'){ + break; + case 'remove': if(!update.$unset) update.$unset = {}; update.$unset[toDot(p.path)] = 1; - } - else if(p.op === 'replace'){ + break; + case 'replace': if(!update.$set) update.$set = {}; update.$set[toDot(p.path)] = p.value; - } - else if(p.op !== 'test') { + break; + case 'test': + break; + default: throw new Error('Unsupported Operation! op = ' + p.op); } }); From 1a5b64ce74753f0be1b098ae4105bf7ac4ea507e Mon Sep 17 00:00:00 2001 From: benco1967 Date: Tue, 18 Jul 2017 21:18:29 +0200 Subject: [PATCH 2/7] use same initialization pattern for every nodes --- index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index e6fe3e0..81ed86b 100644 --- a/index.js +++ b/index.js @@ -50,11 +50,11 @@ module.exports = function(patches){ } break; case 'remove': - if(!update.$unset) update.$unset = {}; + update.$unset = update.$unset || {}; update.$unset[toDot(p.path)] = 1; break; case 'replace': - if(!update.$set) update.$set = {}; + update.$set = update.$set || {}; update.$set[toDot(p.path)] = p.value; break; case 'test': From 38ffe05dfe5cd20090d423b3f2b719ea6fb58e0e Mon Sep 17 00:00:00 2001 From: benco1967 Date: Tue, 18 Jul 2017 21:26:32 +0200 Subject: [PATCH 3/7] remove useless code --- index.js | 1 - 1 file changed, 1 deletion(-) diff --git a/index.js b/index.js index 81ed86b..a5e9eae 100644 --- a/index.js +++ b/index.js @@ -17,7 +17,6 @@ module.exports = function(patches){ if (update.$push[key]) { if (!isNaN(update.$push[key].$position)) { $position = update.$push[key].$position; - delete update.$push[key].$position; } if (!update.$push[key].$each) { From 15287fb596442e02f0cc6579ed36924fea1ab837 Mon Sep 17 00:00:00 2001 From: Benco1967 Date: Tue, 18 Jul 2017 21:49:22 +0200 Subject: [PATCH 4/7] according to the rfc6901 the path for array should has a valid position index or '-', this last one means to add to the end of the array --- index.js | 10 ++++++---- test/index.test.js | 8 ++++---- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/index.js b/index.js index a5e9eae..677fcbd 100644 --- a/index.js +++ b/index.js @@ -8,8 +8,10 @@ module.exports = function(patches){ var path = toDot(p.path), parts = path.split('.'); - var key = parts[0]; - var $position = parts[1] && parseInt(parts[1], 10); + var lastPart = parts[parts.length - 1]; + var addToEnd = lastPart === '-'; + var key = parts.slice(0, -1).join('.'); + var $position = lastPart && parseInt(lastPart, 10); update.$push = update.$push || {}; @@ -42,9 +44,9 @@ module.exports = function(patches){ $each: [update.$push[key]] }; } - update.$push[path].$each.push(p.value); + update.$push[addToEnd ? key : path].$each.push(p.value); } else { - update.$push[path] = p.value; + update.$push[addToEnd ? key : path] = p.value; } } break; diff --git a/test/index.test.js b/test/index.test.js index 8e04ac1..12f1217 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -7,7 +7,7 @@ describe('jsonpatch to mongodb', function() { it('should work with single add', function() { var patches = [{ op: 'add', - path: '/name', + path: '/name/-', value: 'dave' }]; @@ -70,15 +70,15 @@ describe('jsonpatch to mongodb', function() { it('should work with multiple adds', function() { var patches = [{ op: 'add', - path: '/name', + path: '/name/-', value: 'dave' },{ op: 'add', - path: '/name', + path: '/name/-', value: 'bob' },{ op: 'add', - path: '/name', + path: '/name/-', value: 'john' }]; From a4d333fb01972b349aa32aa2d66925aa01475cc1 Mon Sep 17 00:00:00 2001 From: BenCo1967 Date: Wed, 19 Jul 2017 09:40:12 +0200 Subject: [PATCH 5/7] add is not possible without position rfc6901 says that add op is not possible without position index or '-' --- index.js | 8 +++++--- test/index.test.js | 10 ++++++++++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index 677fcbd..efdf541 100644 --- a/index.js +++ b/index.js @@ -37,17 +37,19 @@ module.exports = function(patches){ $position: $position }; } - } else { + } else if(addToEnd) { if (update.$push[key]) { if (!update.$push[key].$each) { update.$push[key] = { $each: [update.$push[key]] }; } - update.$push[addToEnd ? key : path].$each.push(p.value); + update.$push[key].$each.push(p.value); } else { - update.$push[addToEnd ? key : path] = p.value; + update.$push[key] = p.value; } + } else { + throw new Error("Unsupported Operation! can't use add op without position"); } break; case 'remove': diff --git a/test/index.test.js b/test/index.test.js index 12f1217..dab40f7 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -135,6 +135,16 @@ describe('jsonpatch to mongodb', function() { assert.deepEqual(toMongodb(patches), expected); }); + it('should blow up on add without position', function() { + var patches = [{ + op: 'add', + path: '/name', + value: 'dave' + }]; + + chai.expect(function(){toMongodb(patches)}).to.throw("Unsupported Operation! can't use add op without position"); + }); + it('should blow up on move', function() { var patches = [{ op: 'move', From ad2cc1986185cab25bf2e3b6c6e0acd09b186c49 Mon Sep 17 00:00:00 2001 From: BenCo1967 Date: Wed, 19 Jul 2017 12:24:06 +0200 Subject: [PATCH 6/7] add/$push improvments added items must be contiguous, if we add a item in the first position and in the third position, we can't do it in only one $push => throws error add op can't mixe position index and '-', as we don't know the length of the array where the itmes will be pushed, we can't mixed index positions and '-' position (added to end) => throws error --- index.js | 46 +++++++++-------- test/index.test.js | 120 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 141 insertions(+), 25 deletions(-) diff --git a/index.js b/index.js index efdf541..e7e7df3 100644 --- a/index.js +++ b/index.js @@ -8,45 +8,43 @@ module.exports = function(patches){ var path = toDot(p.path), parts = path.split('.'); - var lastPart = parts[parts.length - 1]; - var addToEnd = lastPart === '-'; + var positionPart = parts.length > 1 && parts[parts.length - 1]; + var addToEnd = positionPart === '-'; var key = parts.slice(0, -1).join('.'); - var $position = lastPart && parseInt(lastPart, 10); + var $position = positionPart && parseInt(positionPart, 10) || null; update.$push = update.$push || {}; - if (!isNaN($position)) { - if (update.$push[key]) { - if (!isNaN(update.$push[key].$position)) { - $position = update.$push[key].$position; - } - - if (!update.$push[key].$each) { - update.$push[key] = { - $each: [ - update.$push[key] - ] - }; - } - - update.$push[key].$each.push(p.value); - update.$push[key].$position = $position; - } else { + if ($position !== null) { + if (update.$push[key] === undefined) { update.$push[key] = { $each: [p.value], $position: $position }; + } else { + if (update.$push[key] === null || update.$push[key].$position === undefined) { + throw new Error("Unsupported Operation! can't use add op with mixed positions"); + } + var posDiff = $position - update.$push[key].$position; + if (posDiff > update.$push[key].$each.length) { + throw new Error("Unsupported Operation! can use add op only with contiguous positions"); + } + update.$push[key].$each.splice(posDiff, 0, p.value); + update.$push[key].$position = Math.min($position, update.$push[key].$position); } } else if(addToEnd) { - if (update.$push[key]) { - if (!update.$push[key].$each) { + if (update.$push[key] === undefined) { + update.$push[key] = p.value; + } else { + if (update.$push[key] === null || update.$push[key].$each === undefined) { update.$push[key] = { $each: [update.$push[key]] }; } + if (update.$push[key].$position !== undefined) { + throw new Error("Unsupported Operation! can't use add op with mixed positions"); + } update.$push[key].$each.push(p.value); - } else { - update.$push[key] = p.value; } } else { throw new Error("Unsupported Operation! can't use add op without position"); diff --git a/test/index.test.js b/test/index.test.js index dab40f7..10e3103 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -50,6 +50,10 @@ describe('jsonpatch to mongodb', function() { op: 'add', path: '/name/2', value: 'bob' + }, { + op: 'add', + path: '/name/2', + value: 'john' }]; var expected = { @@ -57,6 +61,7 @@ describe('jsonpatch to mongodb', function() { name: { $each: [ 'dave', + 'john', 'bob' ], $position: 1 @@ -67,6 +72,30 @@ describe('jsonpatch to mongodb', function() { assert.deepEqual(toMongodb(patches), expected); }); + it('should work with multiple adds in reverse position', function() { + var patches = [{ + op: 'add', + path: '/name/1', + value: 'dave' + },{ + op: 'add', + path: '/name/1', + value: 'bob' + },{ + op: 'add', + path: '/name/1', + value: 'john' + }]; + + var expected = { + $push: { + name: {$each: ['john', 'bob', 'dave'], $position: 1} + } + }; + + assert.deepEqual(toMongodb(patches), expected); + }); + it('should work with multiple adds', function() { var patches = [{ op: 'add', @@ -91,6 +120,54 @@ describe('jsonpatch to mongodb', function() { assert.deepEqual(toMongodb(patches), expected); }); + it('should work with multiple adds with some null at the end', function() { + var patches = [{ + op: 'add', + path: '/name/-', + value: null + },{ + op: 'add', + path: '/name/-', + value: 'bob' + },{ + op: 'add', + path: '/name/-', + value: null + }]; + + var expected = { + $push: { + name: {$each: [null, 'bob', null]} + } + }; + + assert.deepEqual(toMongodb(patches), expected); + }); + + it('should work with multiple adds with some null and position', function() { + var patches = [{ + op: 'add', + path: '/name/1', + value: null + },{ + op: 'add', + path: '/name/1', + value: 'bob' + },{ + op: 'add', + path: '/name/1', + value: null + }]; + + var expected = { + $push: { + name: {$each: [null, 'bob', null], $position: 1} + } + }; + + assert.deepEqual(toMongodb(patches), expected); + }); + it('should work with remove', function() { var patches = [{ op: 'remove', @@ -135,6 +212,48 @@ describe('jsonpatch to mongodb', function() { assert.deepEqual(toMongodb(patches), expected); }); + it('blow up on adds with non contiguous positions', function() { + var patches = [{ + op: 'add', + path: '/name/1', + value: 'bob' + },{ + op: 'add', + path: '/name/3', + value: 'john' + }]; + + chai.expect(function(){toMongodb(patches)}).to.throw("Unsupported Operation! can use add op only with contiguous positions"); + }); + + it('blow up on adds with mixed position 1', function() { + var patches = [{ + op: 'add', + path: '/name/1', + value: 'bob' + },{ + op: 'add', + path: '/name/-', + value: 'john' + }]; + + chai.expect(function(){toMongodb(patches)}).to.throw("Unsupported Operation! can't use add op with mixed positions"); + }); + + it('blow up on adds with mixed position 2', function() { + var patches = [{ + op: 'add', + path: '/name/-', + value: 'bob' + },{ + op: 'add', + path: '/name/1', + value: 'john' + }]; + + chai.expect(function(){toMongodb(patches)}).to.throw("Unsupported Operation! can't use add op with mixed positions"); + }); + it('should blow up on add without position', function() { var patches = [{ op: 'add', @@ -153,7 +272,6 @@ describe('jsonpatch to mongodb', function() { }]; chai.expect(function(){toMongodb(patches)}).to.throw('Unsupported Operation! op = move'); - }); From 8f625376129acedd14c3e4825c9dd24866dcee1a Mon Sep 17 00:00:00 2001 From: BenCo1967 Date: Wed, 19 Jul 2017 15:05:19 +0200 Subject: [PATCH 7/7] handle the escaped characters in jsonpath ~0 <=> ~ and ~1 <=> / were forgotten no more dependency to jsonpath-to-dot --- index.js | 4 +++- package.json | 4 +--- test/index.test.js | 16 ++++++++++++++++ 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/index.js b/index.js index e7e7df3..8c7027c 100644 --- a/index.js +++ b/index.js @@ -1,4 +1,6 @@ -var toDot = require('jsonpath-to-dot'); +function toDot(path) { + return path.replace(/^\//, '').replace(/\//g, '.').replace(/~1/g, '/').replace(/~0/g, '~'); +} module.exports = function(patches){ var update = {}; diff --git a/package.json b/package.json index 42fac0a..129dbe3 100644 --- a/package.json +++ b/package.json @@ -19,9 +19,7 @@ "scripts": { "test": "mocha" }, - "dependencies": { - "jsonpath-to-dot": "~0.2.0" - }, + "dependencies": {}, "devDependencies": { "chai": "^3.5.0", "mocha": "~3.1.2" diff --git a/test/index.test.js b/test/index.test.js index 10e3103..f868581 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -20,6 +20,22 @@ describe('jsonpatch to mongodb', function() { assert.deepEqual(toMongodb(patches), expected); }); + it('should work with escaped characters', function() { + var patches = [{ + op: 'replace', + path: '/foo~1bar~0', + value: 'dave' + }]; + + var expected = { + $set: { + "foo/bar~": 'dave' + } + }; + + assert.deepEqual(toMongodb(patches), expected); + }); + it('should work with array set', function() { var patches = [{ op: 'add',