Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add _is_null and _is_not_null operators #1467

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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