Skip to content

Commit

Permalink
feat: support _is_null and _is_not_null operators
Browse files Browse the repository at this point in the history
  • Loading branch information
zlargon committed Dec 5, 2023
1 parent 3a259d1 commit a2cd204
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 7 deletions.
16 changes: 14 additions & 2 deletions README.md
Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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`
Expand Down
23 changes: 23 additions & 0 deletions __tests__/server/plural.js
Expand Up @@ -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 },
Expand Down Expand Up @@ -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)
Expand Down
20 changes: 15 additions & 5 deletions src/server/router/plural.js
Expand Up @@ -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) ||
Expand Down Expand Up @@ -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)
Expand Down

0 comments on commit a2cd204

Please sign in to comment.