From f8013d54f555fb79d413adb94b8d0876a0cb11fe Mon Sep 17 00:00:00 2001 From: Ardalan Amini Date: Fri, 28 Dec 2018 00:37:08 +0330 Subject: [PATCH] Added ability to use filter in the first level --- CHANGELOG.md | 4 ++ README.md | 8 ++-- package-lock.json | 103 +++++++++++++++++++++++------------------ package.json | 11 +++-- src/builders/filter.ts | 28 +++++++---- test/index/filter.ts | 64 +++++++++++++++++++++++++ test/update/update.ts | 35 +++++++------- 7 files changed, 175 insertions(+), 78 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8642231..3fca9cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,10 @@ --- +## [v1.6.0](https://github.com/foxifyjs/foxify-restify-odin/releases/tag/v1.6.0) - *(2018-12-28)* + +- :star2: Added ability to use `has` filter in the first `and` level + ## [v1.5.0](https://github.com/foxifyjs/foxify-restify-odin/releases/tag/v1.5.0) - *(2018-12-16)* - :zap: Added `Odin`'s `whereHas` compatibility diff --git a/README.md b/README.md index 8b361ca..d4fdf4b 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ Easily restify odin databases - [Node.js](https://nodejs.org/en/download) `8.12` or higher is required. - [foxify](https://github.com/foxifyjs/foxify) `0.10.20` or higher is required. -- [@foxify/odin](https://github.com/foxifyjs/odin) `0.6.0` or higher is required. +- [@foxify/odin](https://github.com/foxifyjs/odin) `0.8.0` or higher is required. ### Installation @@ -183,7 +183,7 @@ possible operators: qs.stringify({ include: [ "relation1", - "relation2", + "relation2.subRelation1.subRelation2", ] }) ``` @@ -218,13 +218,13 @@ qs.stringify({ ## Versioning -We use [SemVer](http://semver.org) for versioning. For the versions available, see the [tags on this repository](https://github.com/foxifyjs/foxify/tags). +We use [SemVer](http://semver.org) for versioning. For the versions available, see the [tags on this repository](https://github.com/foxifyjs/foxify-restify-odin/tags). ## Authors - **Ardalan Amini** - *Owner/Developer* - [@ardalanamini](https://github.com/ardalanamini) -See also the list of [contributors](https://github.com/foxifyjs/foxify/contributors) who participated in this project. +See also the list of [contributors](https://github.com/foxifyjs/foxify-restify-odin/contributors) who participated in this project. ## License diff --git a/package-lock.json b/package-lock.json index 7166dba..86d6681 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "foxify-restify-odin", - "version": "1.5.0", + "version": "1.6.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -103,15 +103,15 @@ } }, "@foxify/odin": { - "version": "0.7.5", - "resolved": "https://registry.npmjs.org/@foxify/odin/-/odin-0.7.5.tgz", - "integrity": "sha512-+UmE9e+GZbYcgr9H4ZP3PyelmUsp7v6tqKFXo9VbFg8IB1O5bvOYyZOh4E2CmPAexp2dr2IVxst6yQ/cusBtGQ==", + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@foxify/odin/-/odin-0.8.0.tgz", + "integrity": "sha512-hEjFCIPgIK38suHDVBeRPx7IO3IO4xaLibX/vtJHjVGtcmlt7tDtRKZUocXekSO9rQ90CGZfXwRiVeTTCLxk6w==", "dev": true, "requires": { "@types/graphql": "^0.13.4", "@types/graphql-iso-date": "^3.3.1", - "@types/mongodb": "^3.1.17", - "@types/node": "^10.12.15", + "@types/mongodb": "^3.1.18", + "@types/node": "^10.12.18", "async": "^2.6.1", "caller-id": "^0.1.0", "deasync": "^0.1.14", @@ -123,13 +123,23 @@ }, "dependencies": { "@types/node": { - "version": "10.12.15", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.15.tgz", - "integrity": "sha512-9kROxduaN98QghwwHmxXO2Xz3MaWf+I1sLVAA6KJDF5xix+IyXVhds0MAfdNwtcpSrzhaTsNB0/jnL86fgUhqA==", + "version": "10.12.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.18.tgz", + "integrity": "sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ==", "dev": true } } }, + "@foxify/schema": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@foxify/schema/-/schema-1.0.1.tgz", + "integrity": "sha512-4skI8btazW5uV9AamsvmOe/6v0BCkppxlfgb1LWt2fKTr5muZCsiD/17FufCbvQs1f7U3xrffufvBuaZ8lwwTw==", + "dev": true, + "requires": { + "prototyped.js": "^0.21.0", + "verifications": "^0.3.0" + } + }, "@types/body-parser": { "version": "1.17.0", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.17.0.tgz", @@ -180,9 +190,9 @@ "dev": true }, "@types/mongodb": { - "version": "3.1.17", - "resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.1.17.tgz", - "integrity": "sha512-u6tSIpfdsgK74aE0TuyqZYhHscw+gHs6dQNSsFUTFXubhhxCqovmV3nJRS0YKSw0sfqbzUgGzbG5+yorUPRnFg==", + "version": "3.1.18", + "resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.1.18.tgz", + "integrity": "sha512-8m8yvrDagesNNJdOIMk+g0b5z/sW48FwNp4GS1tRcHMKfQ/o40WsFXOXSfYY7y4xkutPFGQKGb4/GpzGFhpnqw==", "dev": true, "requires": { "@types/bson": "*", @@ -986,7 +996,7 @@ }, "bindings": { "version": "1.2.1", - "resolved": "http://registry.npmjs.org/bindings/-/bindings-1.2.1.tgz", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.2.1.tgz", "integrity": "sha1-FK1hE4EtLTfXLme0ystLtyZQXxE=", "dev": true }, @@ -1392,10 +1402,13 @@ } }, "commander": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz", - "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==", - "dev": true + "version": "2.8.1", + "resolved": "http://registry.npmjs.org/commander/-/commander-2.8.1.tgz", + "integrity": "sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=", + "dev": true, + "requires": { + "graceful-readlink": ">= 1.0.0" + } }, "commondir": { "version": "1.0.1", @@ -1645,6 +1658,12 @@ } } }, + "dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=", + "dev": true + }, "deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", @@ -2176,9 +2195,9 @@ } }, "p-limit": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.0.0.tgz", - "integrity": "sha512-fl5s52lI5ahKCernzzIyAP0QAZbGIovtVHGwpcu1Jr/EpzLVDI2myISHwGqK7m8uQFugVWSrbxH7XnhGtvEc+A==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.1.0.tgz", + "integrity": "sha512-NhURkNcrVB+8hNfLuysU8enY5xn2KXphsHBaC2YmRNTZRc7RWusw6apSpdEj3jo4CMb6W9nrF6tTnsJsJeyu6g==", "dev": true, "requires": { "p-try": "^2.0.0" @@ -2862,9 +2881,9 @@ "dev": true }, "get-port": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/get-port/-/get-port-4.0.0.tgz", - "integrity": "sha512-Yy3yNI2oShgbaWg4cmPhWjkZfktEvpKI09aDX4PZzNtlU9obuYrX7x2mumQsrNxlF+Ls7OtMQW/u+X4s896bOQ==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-4.1.0.tgz", + "integrity": "sha512-4/fqAYrzrzOiqDrdeZRKXGdTGgbkfTEumGlNQPeP6Jy8w0PzN9mzeNQ3XgHaTNie8pQ3hOUkrwlZt2Fzk5H9mA==", "dev": true }, "get-stream": { @@ -4540,14 +4559,15 @@ } }, "mongodb-memory-server": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/mongodb-memory-server/-/mongodb-memory-server-2.8.0.tgz", - "integrity": "sha512-PKa56QdqyyO2srfba7hzUSmSJMrARXnBAbTWtJFkg8McPA07ufKtqb+iW2DOuOJYKo0k55ni7V+eCdN/sUu7+Q==", + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/mongodb-memory-server/-/mongodb-memory-server-2.9.1.tgz", + "integrity": "sha512-SmAJMTiD3X4sY6neu+UsBAmbiyK3IikvZhf9unF0VdC3YE+vMkTcClbx1DuGVWQmimGPSt9d0odFLUmgKS3I/A==", "dev": true, "requires": { - "@babel/runtime": "^7.1.2", + "@babel/runtime": "^7.2.0", "debug": "^4.1.0", "decompress": "^4.2.0", + "dedent": "^0.7.0", "find-cache-dir": "^2.0.0", "get-port": "^4.0.0", "getos": "^3.1.1", @@ -4560,9 +4580,9 @@ }, "dependencies": { "debug": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.0.tgz", - "integrity": "sha512-heNPJUJIqC+xB6ayLAMHaIrmN9HKa7aQO8MGqKpvCA+uJYVcvR6l5kgdrhRuwPFHU7P5/A1w0BjByPHwpfTDKg==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", "dev": true, "requires": { "ms": "^2.1.1" @@ -5818,17 +5838,6 @@ "dev": true, "requires": { "commander": "~2.8.1" - }, - "dependencies": { - "commander": { - "version": "2.8.1", - "resolved": "http://registry.npmjs.org/commander/-/commander-2.8.1.tgz", - "integrity": "sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=", - "dev": true, - "requires": { - "graceful-readlink": ">= 1.0.0" - } - } } }, "semver": { @@ -6516,9 +6525,9 @@ "dev": true }, "tslint": { - "version": "5.11.0", - "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.11.0.tgz", - "integrity": "sha1-mPMMAurjzecAYgHkwzywi0hYHu0=", + "version": "5.12.0", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.12.0.tgz", + "integrity": "sha512-CKEcH1MHUBhoV43SA/Jmy1l24HJJgI0eyLbBNSRyFlsQvb9v6Zdq+Nz2vEOH00nC5SUx4SneJ59PZUS/ARcokQ==", "dev": true, "requires": { "babel-code-frame": "^6.22.0", @@ -6535,6 +6544,12 @@ "tsutils": "^2.27.2" }, "dependencies": { + "commander": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz", + "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==", + "dev": true + }, "tsutils": { "version": "2.29.0", "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", diff --git a/package.json b/package.json index 226a808..8b7e3d7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "foxify-restify-odin", - "version": "1.5.0", + "version": "1.6.0", "description": "Easily restify odin databases", "author": "Ardalan Amini [https://github.com/ardalanamini]", "license": "MIT", @@ -38,11 +38,12 @@ "qs": "^6.6.0" }, "peerDependencies": { - "@foxify/odin": ">=0.7.5", + "@foxify/odin": ">=0.8.0", "foxify": ">=0.10.20" }, "devDependencies": { - "@foxify/odin": "^0.7.5", + "@foxify/odin": "^0.8.0", + "@foxify/schema": "^1.0.1", "@types/body-parser": "^1.17.0", "@types/jest": "^23.3.10", "@types/qs": "^6.5.1", @@ -52,9 +53,9 @@ "jest": "^23.6.0", "jest-environment-node": "^23.4.0", "mongodb": "^3.1.10", - "mongodb-memory-server": "^2.8.0", + "mongodb-memory-server": "^2.9.1", "ts-jest": "^23.10.5", - "tslint": "^5.11.0", + "tslint": "^5.12.0", "tslint-config-airbnb": "^5.11.1", "typescript": "^3.2.2" }, diff --git a/src/builders/filter.ts b/src/builders/filter.ts index ff9cad0..0888303 100644 --- a/src/builders/filter.ts +++ b/src/builders/filter.ts @@ -40,14 +40,25 @@ const operate = (query: typeof Odin, field: string, operator: Operator, value: a } }; -const and = (query: any, filters: Array, had: boolean): any => filters - .reduce((prev, curr) => prev.where((q: any) => filter(q, curr, had)), query); +const and = ( + query: any, filters: Array, had: boolean, level: number, +): any => filters + .reduce( + (prev, curr) => { + if ((curr as any).has) return filter(prev, curr, had, level); -const or = (query: any, filters: Array, had: boolean): any => filters.reduce( + return prev.where((q: any) => filter(q, curr, had, level + 1)); + }, + query, + ); + +const or = ( + query: any, filters: Array, had: boolean, level: number, +): any => filters.reduce( (prev, curr, index) => { - if (index === 0) return prev.where((q: any) => filter(q, curr, had)); + if (index === 0) return prev.where((q: any) => filter(q, curr, had, level + 1)); - return prev.orWhere((q: any) => filter(q, curr, had)); + return prev.orWhere((q: any) => filter(q, curr, had, level + 1)); }, query, ); @@ -66,23 +77,24 @@ const has = ( ); }; -const filter = (model: any, filters: any, had = false) => { +const filter = (model: any, filters: any, had = false, level = 0) => { if (filters.and) { if (filters.or || filters.has) { throw new TypeError("filter can only have one of [\"and\", \"or\", \"has\"]"); } - return and(model, filters.and, had); + return and(model, filters.and, had, level); } if (filters.or) { if (filters.has) throw new TypeError("filter can only have one of [\"and\", \"or\", \"has\"]"); - return or(model, filters.or, had); + return or(model, filters.or, had, level); } if (filters.has) { if (had) throw new Error("Can't use \"has\" inside has"); + if (level !== 0) throw new Error("Can't use \"has\" at this level"); return has(model, filters.has); } diff --git a/test/index/filter.ts b/test/index/filter.ts index 23425ee..a67e3f7 100644 --- a/test/index/filter.ts +++ b/test/index/filter.ts @@ -367,6 +367,34 @@ it("Should filter [has]", async () => { }); }); +it("Should filter [has inside first level and]", async () => { + expect.assertions(1); + + const app = new Foxify(); + + app.use(restify(User)); + + const result = await app.inject(`/users?${stringify( + { + filter: { + and: [ + { + has: "age", + }, + ], + }, + }, + )}`); + + const users = ITEMS.filter(({ username }) => ITEMS2.any(item => item.username === username)); + + expect(JSON.parse(result.body)) + .toEqual({ + users, + meta: { limit: 10, page: 0, count: users.length, total_count: users.length }, + }); +}); + it("Should filter [whereHas]", async () => { expect.assertions(1); @@ -398,3 +426,39 @@ it("Should filter [whereHas]", async () => { meta: { limit: 10, page: 0, count: users.length, total_count: users.length }, }); }); + +it("Should filter [whereHas inside first level and]", async () => { + expect.assertions(1); + + const app = new Foxify(); + + app.use(restify(User)); + + const result = await app.inject(`/users?${stringify( + { + filter: { + and: [ + { + has: { + relation: "age", + filter: { + field: "age", + operator: "gte", + value: 25, + }, + }, + }, + ], + }, + }, + )}`); + + const users = ITEMS + .filter(({ username }) => ITEMS2.any(item => item.username === username && item.age >= 25)); + + expect(JSON.parse(result.body)) + .toEqual({ + users, + meta: { limit: 10, page: 0, count: users.length, total_count: users.length }, + }); +}); diff --git a/test/update/update.ts b/test/update/update.ts index 2cce416..b31e516 100644 --- a/test/update/update.ts +++ b/test/update/update.ts @@ -42,16 +42,31 @@ Odin.Connect({ }, }); +const Types = Odin.Types; + +class User extends Odin { + public static schema = { + email: Types.string.email.required, + username: Types.string.alphanum.min(3).required, + age: Types.number.min(18).required, + name: { + first: Types.string.min(3).required, + last: Types.string.min(3), + }, + }; +} + beforeAll((done) => { - Odin.DB.collection(TABLE).insert(ITEMS, (err) => { + User.insert(ITEMS, (err) => { if (err) throw err; - Odin.DB.collection(TABLE).get((err2, items) => { + User.lean().get((err2, items: any[]) => { if (err2) throw err2; ITEMS.length = 0; - ITEMS.push(...items); + ITEMS.push(...items.map(item => ({ + ...item, created_at: item.created_at.toISOString() }))); done(); }); @@ -78,20 +93,6 @@ afterAll((done) => { }); }); -const Types = Odin.Types; - -class User extends Odin { - public static schema = { - email: Types.string.email.required, - username: Types.string.alphanum.min(3).required, - age: Types.number.min(18).required, - name: { - first: Types.string.min(3).required, - last: Types.string.min(3), - }, - }; -} - it("Should update the user by the given id", async () => { expect.assertions(2);