From 4da8ae37da833955d32b7af060f6ab015370e428 Mon Sep 17 00:00:00 2001 From: John Szwaronek Date: Sat, 3 Feb 2018 14:22:35 -0500 Subject: [PATCH 1/2] Added mongoKeys hook. --- lib/services/index.js | 2 + lib/services/mongo-keys.js | 58 ++++++++++++++++++++ package-lock.json | 43 +++++++++++++++ package.json | 1 + tests/services/exposed.js | 1 + tests/services/mongo-keys.test.js | 90 +++++++++++++++++++++++++++++++ 6 files changed, 195 insertions(+) create mode 100755 lib/services/mongo-keys.js create mode 100755 tests/services/mongo-keys.test.js diff --git a/lib/services/index.js b/lib/services/index.js index 6bef42d4..b4b4e953 100755 --- a/lib/services/index.js +++ b/lib/services/index.js @@ -26,6 +26,7 @@ const keep = require('./keep'); const keepQuery = require('./keep-query'); const lowerCase = require('./lower-case'); const makeCallingParams = require('./make-calling-params'); +const mongoKeys = require('./mongo-keys'); const paramsForServer = require('./params-for-server'); const paramsFromClient = require('./params-from-client'); const populate = require('./populate'); @@ -82,6 +83,7 @@ module.exports = Object.assign({ callbackToPromise, keepQuery, lowerCase, makeCallingParams, + mongoKeys, paramsForServer, paramsFromClient, populate, diff --git a/lib/services/mongo-keys.js b/lib/services/mongo-keys.js new file mode 100755 index 00000000..f07781bf --- /dev/null +++ b/lib/services/mongo-keys.js @@ -0,0 +1,58 @@ + +const traverse = require('traverse'); +const { ObjectID } = require('mongodb'); + +module.exports = function mongoKeys (ObjectID, keyFields) { + keyFields = Array.isArray(keyFields) ? keyFields : [keyFields]; + const keyLeaves = []; + + const keysInfo = keyFields.map(field => { + const fieldNames = field.split('.'); + const leaf = fieldNames.slice(-1)[0]; + keyLeaves.push(leaf); + + return { leaf, len: fieldNames.length, path: JSON.stringify(fieldNames) }; + }); + + return context => { + checkContext(context, 'before', 'find', 'mongoKeys'); + const query = context.params.query || {}; + + traverse(query).forEach(function (node) { + const typeofNode = typeof node; + const key = this.key; + const path = this.path; + + if (keyLeaves.indexOf(key) === -1) return; + + keysInfo.forEach(info => { + if (info.leaf === key && info.len <= path.length) { + const endPath = path.slice(-info.len); + if (JSON.stringify(endPath) === info.path) { + if (typeofNode === 'object' && node !== null && !Array.isArray(node)) { + // { keyPath: { ... } } + const actualProps = Object.keys(node); + const onlyProp = actualProps[0]; + + if (actualProps.length === 1 && onlyProp === '$in') { + // { keyPath: { $in: [...] } } + const newNode = { $in: wrapValue(node[onlyProp], true) }; + this.update(newNode); + } + } else if (typeofNode === 'string' || typeofNode === 'number') { + // { keyPath: '111111111111' } + const newNode = wrapValue(node, true); + this.update(newNode); + } + } + } + }); + }); + + return context; + }; +}; + +function wrapValue (value) { + return Array.isArray(value) ? value.map(val => new ObjectID(val)) : new ObjectID(value); +} diff --git a/package-lock.json b/package-lock.json index eb42dd36..a141969c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -382,6 +382,12 @@ "integrity": "sha1-81HTKWnTL6XXpVZxVCY9korjvR8=", "dev": true }, + "bson": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/bson/-/bson-1.0.4.tgz", + "integrity": "sha1-k8ENOeqltYQVy8QFLz5T5WKwtyw=", + "dev": true + }, "builtin-modules": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", @@ -2275,6 +2281,25 @@ } } }, + "mongodb": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.0.2.tgz", + "integrity": "sha512-E50FmpSQchZAimn2uPIegoNoH9UQYR1yiGHtQPmmg8/Ekc97w6owHoqaBoz+assnd9V5LxMzmQ/VEWMsQMgZhQ==", + "dev": true, + "requires": { + "mongodb-core": "3.0.2" + } + }, + "mongodb-core": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-3.0.2.tgz", + "integrity": "sha512-p1B0qwFQUw6C1OlFJnrOJp8KaX7MuGoogRbTaupRt0y+pPRkMllHWtE9V6i1CDtTvI3/3sy2sQwqWez7zuXEAA==", + "dev": true, + "requires": { + "bson": "1.0.4", + "require_optional": "1.0.1" + } + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -2658,6 +2683,24 @@ "resolve-from": "1.0.1" } }, + "require_optional": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz", + "integrity": "sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g==", + "dev": true, + "requires": { + "resolve-from": "2.0.0", + "semver": "5.4.1" + }, + "dependencies": { + "resolve-from": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", + "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=", + "dev": true + } + } + }, "resolve": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.4.0.tgz", diff --git a/package.json b/package.json index 625e434a..0f702455 100755 --- a/package.json +++ b/package.json @@ -65,6 +65,7 @@ "feathers-tests-fake-app-users": "^1.0.0", "istanbul": "^1.1.0-alpha.1", "mocha": "^3.1.2", + "mongodb": "3.0.2", "semistandard": "^11.0.0", "shx": "^0.2.2", "sift": "^4.0.0" diff --git a/tests/services/exposed.js b/tests/services/exposed.js index 1de4337b..045764ba 100755 --- a/tests/services/exposed.js +++ b/tests/services/exposed.js @@ -29,6 +29,7 @@ const hookNames = [ 'keepQuery', 'lowerCase', 'makeCallingParams', + 'mongoKeys', 'paramsForServer', 'paramsFromClient', 'populate', diff --git a/tests/services/mongo-keys.test.js b/tests/services/mongo-keys.test.js new file mode 100755 index 00000000..31724b74 --- /dev/null +++ b/tests/services/mongo-keys.test.js @@ -0,0 +1,90 @@ + +const { assert } = require('chai'); +const { ObjectID } = require('mongodb'); +const { mongoKeys } = require('../../lib/services'); + +const s0 = '000000000000'; +const s1 = '111111111111'; +const s2 = '222222222222'; +const s5 = '555555555555'; +const s8 = '888888888888'; + +describe('services mongoKeys', () => { + it('{ a: s1, c: s0 }', () => { + const newQuery = wrapper( + ['a'], + { a: s1, c: s0 } + ); + + assert.instanceOf(newQuery.a, ObjectID, '"a" not ObjectID'); + assert.isString(newQuery.c, '"c" not a string'); + }); + + it('{ a: { b: s0 }, c: s0 }', () => { + const newQuery = wrapper( + ['a'], + { a: { b: s0 }, c: s0 } + ); + + assert.isString(newQuery.a.b, '"a.b" not a string'); + assert.isString(newQuery.c, '"c" not a string'); + }); + + it('{ a: { $in: [s1, s2] }, c: s0 }', () => { + const newQuery = wrapper( + ['a'], + { a: { $in: [s1, s2] }, c: s0 } + ); + + assert.instanceOf(newQuery.a.$in[0], ObjectID, '"a.$in[0]" not ObjectID'); + assert.instanceOf(newQuery.a.$in[1], ObjectID, '"a.$in[1]" not ObjectID'); + assert.isString(newQuery.c, '"c" not a string'); + }); + + it('{ a: s1, b: \'111111111111\', c: s0 }', () => { + const newQuery = wrapper( + ['a', 'b'], + { a: s1, b: s2, c: s0 } + ); + + assert.instanceOf(newQuery.a, ObjectID, '"a" not ObjectID'); + assert.instanceOf(newQuery.b, ObjectID, '"b" not ObjectID'); + assert.isString(newQuery.c, '"c" not a string'); + }); + + it('{ a: { x: s8 } }', () => { + const newQuery = wrapper( + ['a.x'], + { a: { x: s8 } } + ); + + assert.instanceOf(newQuery.a.x, ObjectID, '"a.x" not ObjectID'); + }); + + it('{ $or: [{ a: { x: s8 } }, { b: s5 }, { c: s0 }], d: s0 }', () => { + const newQuery = wrapper( + ['a.x', 'b'], + { $or: [{ a: { x: s8 } }, { b: s5 }, { c: s0 }], d: s0 } + ); + + assert.instanceOf(newQuery.$or[0].a.x, ObjectID, '"$or[0].a.x" not ObjectID'); + assert.instanceOf(newQuery.$or[1].b, ObjectID, '"$or[1].b" not ObjectID'); + assert.isString(newQuery.$or[2].c, '"$or[2].c" not a string'); + assert.isString(newQuery.d, '"d" not a string'); + }); + + it('{ $or: [{ a: { x: s8 } }, { a: s5 }] } - questionable', () => { + const newQuery = wrapper( + ['a', 'a.x'], + { $or: [{ a: { x: s8 } }, { a: s5 }] } + ); + + assert.instanceOf(newQuery.$or[0].a.x, ObjectID, '"$or[0].a.x" not ObjectID'); + assert.instanceOf(newQuery.$or[1].a, ObjectID, '"$or[1].a" not ObjectID'); + }); +}); + +function wrapper (keys, query) { + const newContext = mongoKeys(ObjectID, keys)({ params: { query } }); + return newContext.params.query; +} From 7d173822803bed2a11f9a7a9cd5e2aeafcaa3c21 Mon Sep 17 00:00:00 2001 From: John Szwaronek Date: Sat, 3 Feb 2018 14:32:59 -0500 Subject: [PATCH 2/2] Added checkContext to mongoKeys. --- lib/services/mongo-keys.js | 1 + tests/services/mongo-keys.test.js | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/services/mongo-keys.js b/lib/services/mongo-keys.js index f07781bf..85e36daf 100755 --- a/lib/services/mongo-keys.js +++ b/lib/services/mongo-keys.js @@ -1,6 +1,7 @@ const traverse = require('traverse'); const { ObjectID } = require('mongodb'); +const checkContext = require('./check-context'); module.exports = function mongoKeys (ObjectID, keyFields) { keyFields = Array.isArray(keyFields) ? keyFields : [keyFields]; diff --git a/tests/services/mongo-keys.test.js b/tests/services/mongo-keys.test.js index 31724b74..ca789802 100755 --- a/tests/services/mongo-keys.test.js +++ b/tests/services/mongo-keys.test.js @@ -85,6 +85,6 @@ describe('services mongoKeys', () => { }); function wrapper (keys, query) { - const newContext = mongoKeys(ObjectID, keys)({ params: { query } }); + const newContext = mongoKeys(ObjectID, keys)({ params: { query }, type: 'before', method: 'find' }); return newContext.params.query; }