From 231853eddea2378c88c3467232289166cf87939e Mon Sep 17 00:00:00 2001 From: Leon Huang Date: Tue, 5 Dec 2023 08:16:37 -0500 Subject: [PATCH] feat: support _is_null and _is_not_null operators --- README.md | 16 ++++++++++++++-- __tests__/server/plural.js | 23 +++++++++++++++++++++++ src/server/router/plural.js | 20 +++++++++++++++----- 3 files changed, 52 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index fa1e16a65..00e1c649b 100644 --- a/README.md +++ b/README.md @@ -105,7 +105,7 @@ __Please help me build OSS__ 👉 [GitHub Sponsors](https://github.com/sponsors/ ## Getting started -Install JSON Server +Install JSON Server ``` npm install -g json-server @@ -142,7 +142,7 @@ Also when doing requests, it's good to know that: - If you make POST, PUT, PATCH or DELETE requests, changes will be automatically and safely saved to `db.json` using [lowdb](https://github.com/typicode/lowdb). - Your request body JSON should be object enclosed, just like the GET output. (for example `{"name": "Foobar"}`) - Id values are not mutable. Any `id` value in the body of your PUT or PATCH request will be ignored. Only a value set in a POST request will be respected, but only if not already taken. -- A POST, PUT or PATCH request should include a `Content-Type: application/json` header to use the JSON in the request body. Otherwise it will return a 2XX status code, but without changes being made to the data. +- A POST, PUT or PATCH request should include a `Content-Type: application/json` header to use the JSON in the request body. Otherwise it will return a 2XX status code, but without changes being made to the data. ## Routes @@ -239,6 +239,18 @@ Add `_like` to filter (RegExp supported) GET /posts?title_like=server ``` +Add `_is_null` for getting a null value + +``` +GET /todos?completeDate_is_null +``` + +Add `_is_not_null` for getting a non-null value + +``` +GET /todos?completeDate_is_not_null +``` + ### Full-text search Add `q` diff --git a/__tests__/server/plural.js b/__tests__/server/plural.js index 67043cc9b..9a2a464f3 100644 --- a/__tests__/server/plural.js +++ b/__tests__/server/plural.js @@ -42,6 +42,13 @@ describe('Server', () => { { id: 5, body: 'quux', published: false, postId: 2, userId: 1 }, ] + db.todos = [ + { id: 1, completeDate: null }, + { id: 2, completeDate: null }, + { id: 3, completeDate: '2022-11-20T13:02:47.210Z' }, + { id: 4, completeDate: '2023-12-05T17:58:17.420Z' }, + ] + db.buyers = [ { id: 1, name: 'Aileen', country: 'Colombia', total: 100 }, { id: 2, name: 'Barney', country: 'Colombia', total: 200 }, @@ -305,6 +312,22 @@ describe('Server', () => { }) }) + describe('GET /:resource?attr_is_null', () => { + test('should respond with null value array', () => + request(server) + .get('/todos?completeDate_is_null') + .expect('Content-Type', /json/) + .expect(200, db.todos.slice(0, 2))) + }) + + describe('GET /:resource?attr_is_not_null', () => { + test('should respond with non-null value array', () => + request(server) + .get('/todos?completeDate_is_not_null') + .expect('Content-Type', /json/) + .expect(200, db.todos.slice(2))) + }) + describe('GET /:resource?attr_gte=&attr_lte=', () => { test('should respond with a limited array', () => request(server) diff --git a/src/server/router/plural.js b/src/server/router/plural.js index 0e73dc399..23fd77b29 100644 --- a/src/server/router/plural.js +++ b/src/server/router/plural.js @@ -81,6 +81,8 @@ module.exports = (db, name, opts) => { _.has(arr[i], query) || query === 'callback' || query === '_' || + /_is_null$/.test(query) || + /_is_not_null$/.test(query) || /_lte$/.test(query) || /_gte$/.test(query) || /_ne$/.test(query) || @@ -117,22 +119,30 @@ module.exports = (db, name, opts) => { // Always use an array, in case req.query is an array const arr = [].concat(req.query[key]) + const isNull = /_is_null$/.test(key) + const isNotNull = /_is_not_null$/.test(key) const isDifferent = /_ne$/.test(key) const isRange = /_lte$/.test(key) || /_gte$/.test(key) const isLike = /_like$/.test(key) - const path = key.replace(/(_lte|_gte|_ne|_like)$/, '') + const path = key.replace( + /(_is_null|_is_not_null|_lte|_gte|_ne|_like)$/, + '', + ) chain = chain.filter((element) => { return arr - .map(function (value) { + .map((value) => { // get item value based on path // i.e post.title -> 'foo' const elementValue = _.get(element, path) + const isElementValueNull = + typeof elementValue === 'undefined' || elementValue === null + + if (isNull) return isElementValueNull + if (isNotNull) return !isElementValueNull // Prevent toString() failing on undefined or null values - if (elementValue === undefined || elementValue === null) { - return undefined - } + if (isElementValueNull) return undefined if (isRange) { const isLowerThan = /_gte$/.test(key)