From 8f484cd7c0735291ad183eefb7d7aaf244cd7242 Mon Sep 17 00:00:00 2001
From: typicode
Date: Tue, 11 Oct 2016 10:43:49 +0200
Subject: [PATCH 001/478] Update README.md
Thanks to @istommao
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index bb70667de..77d366233 100644
--- a/README.md
+++ b/README.md
@@ -271,7 +271,7 @@ You can add your middlewares from the CLI using `--middlewares` option:
```js
// first.js
module.exports = function (req, res, next) {
- res.Header('X-Hello', 'World')
+ res.header('X-Hello', 'World')
}
```
From fddd4507057029bf0ee741e63b744862034caf25 Mon Sep 17 00:00:00 2001
From: mbaer3000
Date: Thu, 13 Oct 2016 11:42:15 +0200
Subject: [PATCH 002/478] fix middlewares example (#385)
---
README.md | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 77d366233..1270880a2 100644
--- a/README.md
+++ b/README.md
@@ -269,9 +269,10 @@ Now you can access resources using additional routes.
You can add your middlewares from the CLI using `--middlewares` option:
```js
-// first.js
+// hello.js
module.exports = function (req, res, next) {
res.header('X-Hello', 'World')
+ next()
}
```
From 7a619bcc7b62fdd437102c247e0a8cb908f3e506 Mon Sep 17 00:00:00 2001
From: typicode
Date: Tue, 25 Oct 2016 21:42:36 +0200
Subject: [PATCH 003/478] Update README.md
---
README.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/README.md b/README.md
index 1270880a2..27cf3f4dd 100644
--- a/README.md
+++ b/README.md
@@ -51,6 +51,8 @@ Also when doing requests, its good to know that
$ npm install -g json-server
```
+__Beta__ if you want to try `v0.9.0-beta` [[README](https://github.com/typicode/json-server/tree/next)], you can use the following command `npm install -g json-server@next`
+
## Routes
Based on the previous `db.json` file, here are all the default routes. You can also add [other routes](#add-routes) using `--routes`.
From 0dbb5ba1a79a94b4248ed2c8501ffd74f66726e7 Mon Sep 17 00:00:00 2001
From: typicode
Date: Sun, 30 Oct 2016 13:29:28 +0100
Subject: [PATCH 004/478] Update README.md
---
README.md | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 27cf3f4dd..5652de895 100644
--- a/README.md
+++ b/README.md
@@ -51,7 +51,15 @@ Also when doing requests, its good to know that
$ npm install -g json-server
```
-__Beta__ if you want to try `v0.9.0-beta` [[README](https://github.com/typicode/json-server/tree/next)], you can use the following command `npm install -g json-server@next`
+### Beta
+
+To try [v0.9.0-beta](https://github.com/typicode/json-server/tree/next)
+
+```bash
+$ npm install -g json-server@next
+```
+
+
## Routes
From 2b93bf5113aa2d2e1a36449613e3e8c7ec4eb13a Mon Sep 17 00:00:00 2001
From: Vahid Panjganj
Date: Wed, 2 Nov 2016 22:33:55 +0000
Subject: [PATCH 005/478] Exposing `Link` in the header instead of `Links`
(#394)
- fix the small typo `Links` to `Link`
- update relevant tests
---
src/server/router/plural.js | 2 +-
test/server/plural.js | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/server/router/plural.js b/src/server/router/plural.js
index 4d8d2c0ae..2b7d266b1 100644
--- a/src/server/router/plural.js
+++ b/src/server/router/plural.js
@@ -153,7 +153,7 @@ module.exports = function (db, name) {
// Slice result
if (_end || _limit || _page) {
res.setHeader('X-Total-Count', chain.size())
- res.setHeader('Access-Control-Expose-Headers', 'X-Total-Count' + (_page ? ', Links' : ''))
+ res.setHeader('Access-Control-Expose-Headers', 'X-Total-Count' + (_page ? ', Link' : ''))
}
if (_page) {
diff --git a/test/server/plural.js b/test/server/plural.js
index b747155cb..7bddd09db 100644
--- a/test/server/plural.js
+++ b/test/server/plural.js
@@ -261,7 +261,7 @@ describe('Server', function () {
.get('/list?_page=2')
.expect('Content-Type', /json/)
.expect('x-total-count', db.list.length.toString())
- .expect('Access-Control-Expose-Headers', 'X-Total-Count, Links')
+ .expect('Access-Control-Expose-Headers', 'X-Total-Count, Link')
.expect(db.list.slice(10, 20))
.expect(200, done)
})
@@ -281,7 +281,7 @@ describe('Server', function () {
.expect('Content-Type', /json/)
.expect('x-total-count', db.list.length.toString())
.expect('link', link)
- .expect('Access-Control-Expose-Headers', 'X-Total-Count, Links')
+ .expect('Access-Control-Expose-Headers', 'X-Total-Count, Link')
.expect(db.list.slice(1, 2))
.expect(200, done)
})
From 1f89edb6656f2a414c195c1266f06b04e7a562ca Mon Sep 17 00:00:00 2001
From: typicode
Date: Thu, 3 Nov 2016 09:17:41 +0100
Subject: [PATCH 006/478] Update CHANGELOG
---
CHANGELOG.md | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 56c0b769e..a5ade0086 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,9 @@
# Change Log
+## [0.8.23][2016-11-31]
+
+* Fix `Links` header
+
## [0.8.22][2016-10-04]
* Fix `Links` header issue when using `_page`
From c6d0a572026cc95da0485b747a11f012b2d5081d Mon Sep 17 00:00:00 2001
From: typicode
Date: Thu, 3 Nov 2016 09:19:26 +0100
Subject: [PATCH 007/478] Update CHANGELOG.md
---
CHANGELOG.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a5ade0086..b1ad26c7a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,6 @@
# Change Log
-## [0.8.23][2016-11-31]
+## [0.8.23][2016-11-03]
* Fix `Links` header
From b2228581c8fdbf15b09a8a5f4139125e6fe9355e Mon Sep 17 00:00:00 2001
From: typicode
Date: Thu, 3 Nov 2016 09:23:57 +0100
Subject: [PATCH 008/478] 0.8.23
---
package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/package.json b/package.json
index 8db4b769c..f0f2f896a 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "json-server",
- "version": "0.8.22",
+ "version": "0.8.23",
"description": "Serves JSON files through REST routes.",
"main": "./src/server/index.js",
"bin": "./bin/index.js",
From 0861dfe581a65c8cd0154d47b3ce9542cf0fc71e Mon Sep 17 00:00:00 2001
From: typicode
Date: Thu, 3 Nov 2016 12:48:52 +0100
Subject: [PATCH 009/478] Update .travis.yml
---
.travis.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.travis.yml b/.travis.yml
index e986bdd0f..29d5864eb 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -2,4 +2,4 @@ sudo: false
language: node_js
node_js:
- "stable"
- - "0.12"
+# - "0.12"
From 44bdfb490a986e9666abc2c4da5acd2b61b053cc Mon Sep 17 00:00:00 2001
From: typicode
Date: Thu, 10 Nov 2016 12:57:48 +0100
Subject: [PATCH 010/478] Update README.md
---
README.md | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 5652de895..28bcc58db 100644
--- a/README.md
+++ b/README.md
@@ -354,7 +354,15 @@ server.listen(3000, function () {
$ node server.js
```
-For an in-memory database, you can pass an object to `jsonServer.router()`.
+The path you provide to the `jsonServer.router` function is relative to the directory from where you launch your node process. If you run the above code from another directory, it’s better to use an absolute path:
+
+```js
+var path = require('path')
+var router = jsonServer.router(path.join(__dirname, 'db.json'))
+```
+
+For an in-memory database, simply pass an object to `jsonServer.router()`.
+
Please note also that `jsonServer.router()` can be used in existing Express projects.
#### Custom routes example
From 2b26630ac6379fba77eb104b22e83b41a004b52e Mon Sep 17 00:00:00 2001
From: typicode
Date: Sat, 12 Nov 2016 01:59:43 +0100
Subject: [PATCH 011/478] v0.9.0 (#404)
* Remove automatic type conversion
* Remove body-parser from default middlewares
* Ignore lib
* ES2015
* Use shortid
* Add babel-register
* Update paths to ./lib
* Add .npmignore
* Update bin
* temp fix
* Fix bin
* Add message when creating default db
* Use fs.watch
* Fix operator existence test
* Fix 0.12 tests
* Update dependencies
* Update
* Increase timeout
* Fix 0.12 support
* 0.9.0-beta.1
* Fix missing example.json issue
* 0.9.0-beta.2
* Update message
* Update CHANGELOG.md
* Update lowdb dependency
* Add error message
* Update README.md
* Add database validation
* Update
* Update
* Fix tests
* Update
---
.babelrc | 2 +-
.gitignore | 1 +
.npmignore | 1 +
CHANGELOG.md | 12 +
README.md | 31 ++-
bin/index.js | 2 +-
package.json | 42 ++--
src/cli/bin.js | 1 +
src/cli/example.json | 9 +
src/cli/index.js | 12 +-
src/cli/run.js | 138 +++++-----
src/cli/utils/is.js | 16 +-
src/cli/utils/load.js | 31 ++-
src/server/body-parser.js | 6 +
src/server/common.js | 9 -
src/server/defaults.js | 43 ++--
src/server/index.js | 11 +-
src/server/mixins.js | 51 ++--
src/server/rewriter.js | 20 +-
src/server/router/index.js | 60 ++---
src/server/router/nested.js | 21 +-
src/server/router/plural.js | 165 ++++++------
src/server/router/singular.js | 6 +-
src/server/router/validate-data.js | 26 ++
src/server/utils.js | 24 +-
test/cli/index.js | 253 ++++++++++---------
test/mocha.opts | 3 +
test/server/mixins.js | 66 +++--
test/server/plural.js | 388 +++++++++++++++--------------
test/server/singular.js | 15 +-
test/server/utils.js | 28 +--
test/server/validate-data.js | 24 ++
32 files changed, 828 insertions(+), 689 deletions(-)
create mode 100644 .npmignore
create mode 100644 src/cli/bin.js
create mode 100644 src/cli/example.json
create mode 100644 src/server/body-parser.js
delete mode 100644 src/server/common.js
create mode 100644 src/server/router/validate-data.js
create mode 100644 test/mocha.opts
create mode 100644 test/server/validate-data.js
diff --git a/.babelrc b/.babelrc
index 3c078e9f9..45001da4a 100644
--- a/.babelrc
+++ b/.babelrc
@@ -1,5 +1,5 @@
{
"presets": [
- "es2015"
+ ["es2015", { "loose": true }]
]
}
diff --git a/.gitignore b/.gitignore
index 835d515cb..0c475f8ea 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,6 @@
**/*.log
node_modules
tmp
+lib
.DS_Store
.idea
diff --git a/.npmignore b/.npmignore
new file mode 100644
index 000000000..e8310385c
--- /dev/null
+++ b/.npmignore
@@ -0,0 +1 @@
+src
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b1ad26c7a..1b4cbfdd8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,17 @@
# Change Log
+## [0.9.0][2016-11-11]
+
+* Shorter `uuid`
+* No automatic conversion of strings to boolean or integer
+* Create a default `db.json` file if it doesn't exist
+* Fix
+ * [#361](https://github.com/typicode/json-server/issues/361)
+ * [#363](https://github.com/typicode/json-server/issues/363) [#365](https://github.com/typicode/json-server/issues/365)
+ * [#374](https://github.com/typicode/json-server/issues/374)
+ * [#383](https://github.com/typicode/json-server/issues/383)
+* Updated dependencies and codebase to ES6
+
## [0.8.23][2016-11-03]
* Fix `Links` header
diff --git a/README.md b/README.md
index 28bcc58db..37c7db91a 100644
--- a/README.md
+++ b/README.md
@@ -97,10 +97,14 @@ GET /comments?author.name=typicode
### Paginate
-Add `_page` and in the `Link` header you'll get `first`, `prev`, `next` and `last` links
+Use `_page` and optionally `_limit` to paginate returned data.
+
+In the `Link` header you'll get `first`, `prev`, `next` and `last` links.
+
```
GET /posts?_page=7
+GET /posts?_page=7&_limit=20
```
_10 items are returned by default_
@@ -247,16 +251,21 @@ module.exports = function() {
$ json-server index.js
```
-__Tip__ use modules like [faker](https://github.com/Marak/faker.js), [casual](https://github.com/boo1ean/casual) or [chance](https://github.com/victorquinn/chancejs).
+__Tip__ use modules like [Faker](https://github.com/Marak/faker.js), [Casual](https://github.com/boo1ean/casual), [Chance](https://github.com/victorquinn/chancejs) or [JSON Schema Faker](https://github.com/json-schema-faker/json-schema-faker).
+
+### HTTPS
-### Add routes
+There's many way to set up SSL in development. One simple way though is to use [hotel](https://github.com/typicode/hotel).
-Create a `routes.json` file. Pay attention to start every route with /.
+### Add custom routes
+
+Create a `routes.json` file. Pay attention to start every route with `/`.
```json
{
"/api/": "/",
- "/blog/:resource/:id/show": "/:resource/:id"
+ "/blog/:resource/:id/show": "/:resource/:id",
+ "/blog/:category": "/posts/:id?category=:category"
}
```
@@ -268,10 +277,11 @@ json-server db.json --routes routes.json
Now you can access resources using additional routes.
-```bash
-/api/posts
-/api/posts/1
-/blog/posts/1/show
+```sh
+/api/posts # → /posts
+/api/posts/1 # → /posts/1
+/blog/posts/1/show # → /posts/1
+/blog/javascript # → /posts?category=javascript
```
### Add middlewares
@@ -383,6 +393,9 @@ server.get('/echo', function (req, res) {
res.jsonp(req.query)
})
+// To handle POST, PUT and PATCH you need to use a body-parser
+// You can use the one used by JSON Server
+server.use(jsonServer.bodyParser)
server.use(function (req, res, next) {
if (req.method === 'POST') {
req.body.createdAt = Date.now()
diff --git a/bin/index.js b/bin/index.js
index 633765557..af3551aa9 100755
--- a/bin/index.js
+++ b/bin/index.js
@@ -1,2 +1,2 @@
#!/usr/bin/env node
-require('../src/cli')()
+require('../lib/cli/bin')
diff --git a/package.json b/package.json
index f0f2f896a..9bab48524 100644
--- a/package.json
+++ b/package.json
@@ -1,8 +1,8 @@
{
"name": "json-server",
- "version": "0.8.23",
+ "version": "0.9.0-beta.2",
"description": "Serves JSON files through REST routes.",
- "main": "./src/server/index.js",
+ "main": "./lib/server/index.js",
"bin": "./bin/index.js",
"directories": {
"test": "test"
@@ -10,46 +10,46 @@
"dependencies": {
"body-parser": "^1.15.2",
"chalk": "^1.1.3",
- "chokidar": "^1.6.0",
"compression": "^1.6.0",
"connect-pause": "^0.1.0",
"cors": "^2.3.0",
"errorhandler": "^1.2.0",
"express": "^4.9.5",
"lodash": "^4.11.2",
- "lowdb": "^0.13.1",
+ "lowdb": "^0.14.0",
"method-override": "^2.1.2",
"morgan": "^1.3.1",
- "node-uuid": "^1.4.2",
"object-assign": "^4.0.1",
"pluralize": "^3.0.0",
"request": "^2.72.0",
"server-destroy": "^1.0.1",
- "underscore-db": "^0.10.0",
+ "shortid": "^2.2.6",
+ "underscore-db": "^0.12.0",
"update-notifier": "^1.0.2",
- "yargs": "^4.2.0"
+ "yargs": "^6.0.0"
},
"devDependencies": {
"babel-cli": "^6.10.1",
- "babel-preset-es2015": "^6.9.0",
- "cross-env": "^1.0.8",
+ "babel-preset-es2015": "^6.16.0",
+ "babel-register": "^6.16.3",
+ "cross-env": "^2.0.1",
"husky": "^0.11.4",
"mkdirp": "^0.5.1",
- "mocha": "^2.2.4",
+ "mocha": "^3.1.2",
"os-tmpdir": "^1.0.1",
"rimraf": "^2.5.2",
- "server-ready": "^0.2.0",
- "standard": "^7.1.2",
- "supertest": "^1.2.0",
+ "server-ready": "^0.3.1",
+ "standard": "^8.3.0",
+ "supertest": "^2.0.0",
"temp-write": "^2.1.0"
},
"scripts": {
- "test": "npm run test:cli && npm run test:server && standard",
- "test:cli": "cross-env NODE_ENV=test mocha -R spec test/cli/*.js",
- "test:server": "cross-env NODE_ENV=test mocha -R spec test/server/*.js",
+ "test": "npm run test:cli && npm run test:server && standard --fix",
+ "test:cli": "npm run build && cross-env NODE_ENV=test mocha test/cli/*.js",
+ "test:server": "cross-env NODE_ENV=test mocha test/server/*.js",
"start": "node bin",
"prepush": "npm t",
- "build": "babel src -d lib"
+ "build": "babel src -d lib --copy-files"
},
"repository": {
"type": "git",
@@ -76,5 +76,11 @@
"bugs": {
"url": "https://github.com/typicode/json-server/issues"
},
- "homepage": "https://github.com/typicode/json-server"
+ "homepage": "https://github.com/typicode/json-server",
+ "standard": {
+ "fix": true,
+ "env": {
+ "mocha": true
+ }
+ }
}
diff --git a/src/cli/bin.js b/src/cli/bin.js
new file mode 100644
index 000000000..2b8efefce
--- /dev/null
+++ b/src/cli/bin.js
@@ -0,0 +1 @@
+require('./')()
diff --git a/src/cli/example.json b/src/cli/example.json
new file mode 100644
index 000000000..4d3941aea
--- /dev/null
+++ b/src/cli/example.json
@@ -0,0 +1,9 @@
+{
+ "posts": [
+ { "id": 1, "title": "json-server", "author": "typicode" }
+ ],
+ "comments": [
+ { "id": 1, "body": "some comment", "postId": 1 }
+ ],
+ "profile": { "name": "typicode" }
+}
\ No newline at end of file
diff --git a/src/cli/index.js b/src/cli/index.js
index 29c7f719e..8f62da1b9 100644
--- a/src/cli/index.js
+++ b/src/cli/index.js
@@ -1,12 +1,12 @@
-var updateNotifier = require('update-notifier')
-var yargs = require('yargs')
-var run = require('./run')
-var pkg = require('../../package.json')
+const updateNotifier = require('update-notifier')
+const yargs = require('yargs')
+const run = require('./run')
+const pkg = require('../../package.json')
module.exports = function () {
- updateNotifier({ pkg: pkg }).notify()
+ updateNotifier({ pkg }).notify()
- var argv = yargs
+ const argv = yargs
.config('config')
.usage('$0 [options] ')
.options({
diff --git a/src/cli/run.js b/src/cli/run.js
index 975fbce93..648974c2d 100644
--- a/src/cli/run.js
+++ b/src/cli/run.js
@@ -1,22 +1,22 @@
-var fs = require('fs')
-var path = require('path')
-var _ = require('lodash')
-var chalk = require('chalk')
-var chokidar = require('chokidar')
-var enableDestroy = require('server-destroy')
-var pause = require('connect-pause')
-var is = require('./utils/is')
-var load = require('./utils/load')
-var jsonServer = require('../server')
+const fs = require('fs')
+const path = require('path')
+const _ = require('lodash')
+const chalk = require('chalk')
+const enableDestroy = require('server-destroy')
+const pause = require('connect-pause')
+const is = require('./utils/is')
+const load = require('./utils/load')
+const example = require('./example.json')
+const jsonServer = require('../server')
function prettyPrint (argv, object, rules) {
- var host = argv.host === '0.0.0.0' ? 'localhost' : argv.host
- var port = argv.port
- var root = 'http://' + host + ':' + port
+ const host = argv.host === '0.0.0.0' ? 'localhost' : argv.host
+ const port = argv.port
+ const root = `http://${host}:${port}`
console.log()
console.log(chalk.bold(' Resources'))
- for (var prop in object) {
+ for (let prop in object) {
console.log(' ' + root + '/' + prop)
}
@@ -35,15 +35,23 @@ function prettyPrint (argv, object, rules) {
}
function createApp (source, object, routes, middlewares, argv) {
- var app = jsonServer.create()
+ const app = jsonServer.create()
- var router = jsonServer.router(
- is.JSON(source)
- ? source
- : object
- )
+ let router
- var defaultsOpts = {
+ try {
+ router = jsonServer.router(
+ is.JSON(source)
+ ? source
+ : object
+ )
+ } catch (e) {
+ console.log()
+ console.error(chalk.red(e.message.replace(/^/gm, ' ')))
+ process.exit(1)
+ }
+
+ const defaultsOpts = {
logger: !argv.quiet,
readOnly: argv.readOnly,
noCors: argv.noCors,
@@ -54,11 +62,11 @@ function createApp (source, object, routes, middlewares, argv) {
defaultsOpts.static = path.join(process.cwd(), argv.static)
}
- var defaults = jsonServer.defaults(defaultsOpts)
+ const defaults = jsonServer.defaults(defaultsOpts)
app.use(defaults)
if (routes) {
- var rewriter = jsonServer.rewriter(routes)
+ const rewriter = jsonServer.rewriter(routes)
app.use(rewriter)
}
@@ -78,18 +86,18 @@ function createApp (source, object, routes, middlewares, argv) {
}
module.exports = function (argv) {
- var source = argv._[0]
- var app
- var server
+ const source = argv._[0]
+ let app
+ let server
if (!fs.existsSync(argv.snapshots)) {
- console.log('Error: snapshots directory ' + argv.snapshots + ' doesn\'t exist')
+ console.log(`Error: snapshots directory ${argv.snapshots} doesn't exist`)
process.exit(1)
}
// noop log fn
if (argv.quiet) {
- console.log = function () {}
+ console.log = () => {}
}
console.log()
@@ -97,20 +105,30 @@ module.exports = function (argv) {
function start (cb) {
console.log()
+
+ // Be nice and create a default db.json if it doesn't exist
+ if (is.JSON(source) && !fs.existsSync(source)) {
+ console.log(chalk.yellow(` Oops, ${source} doesn't seem to exist`))
+ console.log(chalk.yellow(` Creating ${source} with some default data`))
+ console.log()
+ fs.writeFileSync(source, JSON.stringify(example, null, 2))
+ }
+
console.log(chalk.gray(' Loading', source))
// Load JSON, JS or HTTP database
- load(source, function (err, data) {
+ load(source, (err, data) => {
if (err) throw err
// Load additional routes
+ let routes
if (argv.routes) {
console.log(chalk.gray(' Loading', argv.routes))
- var routes = JSON.parse(fs.readFileSync(argv.routes))
+ routes = JSON.parse(fs.readFileSync(argv.routes))
}
// Load middlewares
- var middlewares
+ let middlewares
if (argv.middlewares) {
middlewares = argv.middlewares.map(function (m) {
console.log(chalk.gray(' Loading', m))
@@ -136,7 +154,7 @@ module.exports = function (argv) {
}
// Start server
- start(function () {
+ start(() => {
// Snapshot
console.log(
chalk.gray(' Type s + enter at any time to create a snapshot of the database')
@@ -144,13 +162,13 @@ module.exports = function (argv) {
process.stdin.resume()
process.stdin.setEncoding('utf8')
- process.stdin.on('data', function (chunk) {
+ process.stdin.on('data', (chunk) => {
if (chunk.trim().toLowerCase() === 's') {
- var filename = 'db-' + Date.now() + '.json'
- var file = path.join(argv.snapshots, filename)
- var state = app.db.getState()
+ const filename = 'db-' + Date.now() + '.json'
+ const file = path.join(argv.snapshots, filename)
+ const state = app.db.getState()
fs.writeFileSync(file, JSON.stringify(state, null, 2), 'utf-8')
- console.log(' Saved snapshot to ' + path.relative(process.cwd(), file) + '\n')
+ console.log(` Saved snapshot to ${path.relative(process.cwd(), file)}\n`)
}
})
@@ -158,43 +176,45 @@ module.exports = function (argv) {
if (argv.watch) {
console.log(chalk.gray(' Watching...'))
console.log()
- var source = argv._[0]
+ const source = argv._[0]
// Can't watch URL
if (is.URL(source)) throw new Error('Can\'t watch URL')
// Watch .js or .json file
// Since lowdb uses atomic writing, directory is watched instead of file
- chokidar
- .watch(path.dirname(source))
- .on('change', function (file) {
- if (path.resolve(file) === path.resolve(source)) {
- if (is.JSON(file)) {
- var obj = JSON.parse(fs.readFileSync(file))
- // Compare .json file content with in memory database
- var isDatabaseDifferent = !_.isEqual(obj, app.db.getState())
- if (isDatabaseDifferent) {
- console.log(chalk.gray(' ' + file + ' has changed, reloading...'))
- server && server.destroy()
- start()
- }
- } else {
- console.log(chalk.gray(' ' + file + ' has changed, reloading...'))
+ const watchedDir = path.dirname(source)
+ fs.watch(watchedDir, (event, file) => {
+ const watchedFile = path.resolve(watchedDir, file)
+ if (watchedFile === path.resolve(source)) {
+ if (is.JSON(watchedFile)) {
+ var obj = JSON.parse(fs.readFileSync(watchedFile))
+ // Compare .json file content with in memory database
+ var isDatabaseDifferent = !_.isEqual(obj, app.db.getState())
+ if (isDatabaseDifferent) {
+ console.log(chalk.gray(` ${source} has changed, reloading...`))
server && server.destroy()
start()
}
+ } else {
+ console.log(chalk.gray(` ${source} has changed, reloading...`))
+ server && server.destroy()
+ start()
}
- })
+ }
+ })
// Watch routes
if (argv.routes) {
- chokidar
- .watch(argv.routes)
- .on('change', function (file) {
- console.log(chalk.gray(' ' + file + ' has changed, reloading...'))
+ const watchedDir = path.dirname(argv.routes)
+ fs.watch(watchedDir, (event, file) => {
+ const watchedFile = path.resolve(watchedDir, file)
+ if (watchedFile === path.resolve(argv.routes)) {
+ console.log(chalk.gray(` ${argv.routes} has changed, reloading...`))
server && server.destroy()
start()
- })
+ }
+ })
}
}
})
diff --git a/src/cli/utils/is.js b/src/cli/utils/is.js
index a59da210a..2f5b8dfd2 100644
--- a/src/cli/utils/is.js
+++ b/src/cli/utils/is.js
@@ -1,17 +1,17 @@
module.exports = {
- JSON: isJSON,
- JS: isJS,
- URL: isURL
+ JSON,
+ JS,
+ URL
}
-function isJSON (s) {
- return !isURL(s) && /\.json$/.test(s)
+function JSON (s) {
+ return !URL(s) && /\.json$/.test(s)
}
-function isJS (s) {
- return !isURL(s) && /\.js$/.test(s)
+function JS (s) {
+ return !URL(s) && /\.js$/.test(s)
}
-function isURL (s) {
+function URL (s) {
return /^(http|https):/.test(s)
}
diff --git a/src/cli/utils/load.js b/src/cli/utils/load.js
index 28a1e94cb..e14a2a547 100644
--- a/src/cli/utils/load.js
+++ b/src/cli/utils/load.js
@@ -1,30 +1,37 @@
-var path = require('path')
-var request = require('request')
-var low = require('lowdb')
-var fileAsync = require('lowdb/lib/file-async')
-var is = require('./is')
+const path = require('path')
+const request = require('request')
+const low = require('lowdb')
+const fileAsync = require('lowdb/lib/file-async')
+const is = require('./is')
module.exports = function (source, cb) {
- var data
-
if (is.URL(source)) {
- request({ url: source, json: true }, function (err, response) {
+ // Load remote data
+ const opts = {
+ url: source,
+ json: true
+ }
+
+ request(opts, (err, response) => {
if (err) return cb(err)
cb(null, response.body)
})
} else if (is.JS(source)) {
- var filename = path.resolve(source)
+ // Clear cache
+ const filename = path.resolve(source)
delete require.cache[filename]
- var dataFn = require(filename)
+ const dataFn = require(filename)
if (typeof dataFn !== 'function') {
throw new Error('The database is a JavaScript file but the export is not a function.')
}
- data = dataFn()
+ // Run dataFn to generate data
+ const data = dataFn()
cb(null, data)
} else if (is.JSON(source)) {
- data = low(source, { storage: fileAsync }).getState()
+ // Load JSON using lowdb
+ const data = low(source, { storage: fileAsync }).getState()
cb(null, data)
} else {
throw new Error('Unsupported source ' + source)
diff --git a/src/server/body-parser.js b/src/server/body-parser.js
new file mode 100644
index 000000000..eeeda211e
--- /dev/null
+++ b/src/server/body-parser.js
@@ -0,0 +1,6 @@
+const bodyParser = require('body-parser')
+
+module.exports = [
+ bodyParser.json({limit: '10mb', extended: false}),
+ bodyParser.urlencoded({extended: false})
+]
diff --git a/src/server/common.js b/src/server/common.js
deleted file mode 100644
index 26577f02d..000000000
--- a/src/server/common.js
+++ /dev/null
@@ -1,9 +0,0 @@
-var bodyParser = require('body-parser')
-var methodOverride = require('method-override')
-
-// common middlewares used in ./defaults.js and ./router/index.js
-module.exports = [
- bodyParser.json({limit: '10mb', extended: false}),
- bodyParser.urlencoded({extended: false}),
- methodOverride()
-]
diff --git a/src/server/defaults.js b/src/server/defaults.js
index 6cbd60645..44265a389 100644
--- a/src/server/defaults.js
+++ b/src/server/defaults.js
@@ -1,23 +1,22 @@
-var fs = require('fs')
-var path = require('path')
-var express = require('express')
-var logger = require('morgan')
-var cors = require('cors')
-var compression = require('compression')
-var errorhandler = require('errorhandler')
-var objectAssign = require('object-assign')
-var common = require('./common')
+const fs = require('fs')
+const path = require('path')
+const express = require('express')
+const logger = require('morgan')
+const cors = require('cors')
+const compression = require('compression')
+const errorhandler = require('errorhandler')
+const objectAssign = require('object-assign')
module.exports = function (opts) {
- var userDir = path.join(process.cwd(), 'public')
- var defaultDir = path.join(__dirname, 'public')
- var staticDir = fs.existsSync(userDir)
+ const userDir = path.join(process.cwd(), 'public')
+ const defaultDir = path.join(__dirname, 'public')
+ const staticDir = fs.existsSync(userDir)
? userDir
: defaultDir
opts = objectAssign({ logger: true, static: staticDir }, opts)
- var arr = []
+ const arr = []
// Compress all requests
if (!opts.noGzip) {
@@ -26,12 +25,14 @@ module.exports = function (opts) {
// Logger
if (opts.logger) {
- arr.push(logger('dev', {
- skip: function (req, res) {
- return process.env.NODE_ENV === 'test' ||
+ arr.push(
+ logger('dev', {
+ skip: (req) => (
+ process.env.NODE_ENV === 'test' ||
req.path === '/favicon.ico'
- }
- }))
+ )
+ })
+ )
}
// Enable CORS for all the requests, including static files
@@ -49,7 +50,7 @@ module.exports = function (opts) {
// No cache for IE
// https://support.microsoft.com/en-us/kb/234067
- arr.push(function (req, res, next) {
+ arr.push((req, res, next) => {
res.header('Cache-Control', 'no-cache')
res.header('Pragma', 'no-cache')
res.header('Expires', '-1')
@@ -58,7 +59,7 @@ module.exports = function (opts) {
// Read-only
if (opts.readOnly) {
- arr.push(function (req, res, next) {
+ arr.push((req, res, next) => {
if (req.method === 'GET') {
next() // Continue
} else {
@@ -67,5 +68,5 @@ module.exports = function (opts) {
})
}
- return arr.concat(common)
+ return arr
}
diff --git a/src/server/index.js b/src/server/index.js
index 9a74caf58..dd98979be 100644
--- a/src/server/index.js
+++ b/src/server/index.js
@@ -1,12 +1,9 @@
-var express = require('express')
+const express = require('express')
module.exports = {
- create: function () {
- var server = express()
- server.set('json spaces', 2)
- return server
- },
+ create: () => express().set('json spaces', 2),
defaults: require('./defaults'),
router: require('./router'),
- rewriter: require('./rewriter')
+ rewriter: require('./rewriter'),
+ bodyParser: require('./body-parser')
}
diff --git a/src/server/mixins.js b/src/server/mixins.js
index 43dab7f18..1f980df73 100644
--- a/src/server/mixins.js
+++ b/src/server/mixins.js
@@ -1,28 +1,28 @@
-var uuid = require('node-uuid')
-var pluralize = require('pluralize')
+const shortid = require('shortid')
+const pluralize = require('pluralize')
module.exports = {
- getRemovable: getRemovable,
- createId: createId,
- deepQuery: deepQuery
+ getRemovable,
+ createId,
+ deepQuery
}
// Returns document ids that have unsatisfied relations
// Example: a comment that references a post that doesn't exist
function getRemovable (db) {
- var _ = this
- var removable = []
- _.each(db, function (coll, collName) {
- _.each(coll, function (doc) {
- _.each(doc, function (value, key) {
+ const _ = this
+ const removable = []
+ _.each(db, (coll, collName) => {
+ _.each(coll, (doc) => {
+ _.each(doc, (value, key) => {
if (/Id$/.test(key)) {
- var refName = pluralize.plural(key.slice(0, -2))
+ const refName = pluralize.plural(key.slice(0, -2))
// Test if table exists
if (db[refName]) {
// Test if references is defined in table
- var ref = _.getById(db[refName], value)
+ const ref = _.getById(db[refName], value)
if (_.isUndefined(ref)) {
- removable.push({name: collName, id: doc.id})
+ removable.push({ name: collName, id: doc.id })
}
}
}
@@ -36,36 +36,31 @@ function getRemovable (db) {
// Return incremented id or uuid
// Used to override underscore-db's createId with utils.createId
function createId (coll) {
- var _ = this
- var idProperty = _.__id()
+ const _ = this
+ const idProperty = _.__id()
if (_.isEmpty(coll)) {
return 1
} else {
- var id = _.maxBy(coll, function (doc) {
- return doc[idProperty]
- })[idProperty]
+ let id = _(coll).maxBy(idProperty)[idProperty]
- if (_.isFinite(id)) {
- // Increment integer id
- return ++id
- } else {
- // Generate string id
- return uuid()
- }
+ // Increment integer id or generate string id
+ return _.isFinite(id)
+ ? ++id
+ : shortid.generate()
}
}
function deepQuery (value, q) {
- var _ = this
+ const _ = this
if (value && q) {
if (_.isArray(value)) {
- for (var i = 0; i < value.length; i++) {
+ for (let i = 0; i < value.length; i++) {
if (_.deepQuery(value[i], q)) {
return true
}
}
} else if (_.isObject(value) && !_.isArray(value)) {
- for (var k in value) {
+ for (let k in value) {
if (_.deepQuery(value[k], q)) {
return true
}
diff --git a/src/server/rewriter.js b/src/server/rewriter.js
index 3549c32a2..718220f37 100644
--- a/src/server/rewriter.js
+++ b/src/server/rewriter.js
@@ -1,16 +1,16 @@
-var express = require('express')
-var url = require('url')
-var _ = require('lodash')
+const express = require('express')
+const url = require('url')
+const _ = require('lodash')
-module.exports = function (routes) {
- var router = express.Router()
+module.exports = (routes) => {
+ const router = express.Router()
- Object.keys(routes).forEach(function (route) {
+ Object.keys(routes).forEach((route) => {
if (route.indexOf(':') !== -1) {
- router.all(route, function (req, res, next) {
+ router.all(route, (req, res, next) => {
// Rewrite target url using params
- var target = routes[route]
- for (var param in req.params) {
+ let target = routes[route]
+ for (let param in req.params) {
target = target.replace(':' + param, req.params[param])
}
req.url = target
@@ -21,7 +21,7 @@ module.exports = function (routes) {
next()
})
} else {
- router.all(route + '*', function (req, res, next) {
+ router.all(route + '*', (req, res, next) => {
// Rewrite url by replacing prefix
req.url = req.url.replace(route, routes[route])
next()
diff --git a/src/server/router/index.js b/src/server/router/index.js
index 481d00a38..7a7ad6eaf 100644
--- a/src/server/router/index.js
+++ b/src/server/router/index.js
@@ -1,23 +1,26 @@
-var express = require('express')
-var _ = require('lodash')
-var _db = require('underscore-db')
-var low = require('lowdb')
-var fileAsync = require('lowdb/lib/file-async')
-var plural = require('./plural')
-var nested = require('./nested')
-var singular = require('./singular')
-var mixins = require('../mixins')
-var common = require('../common')
-
-module.exports = function (source) {
+const express = require('express')
+const methodOverride = require('method-override')
+const _ = require('lodash')
+const _db = require('underscore-db')
+const low = require('lowdb')
+const fileAsync = require('lowdb/lib/file-async')
+const bodyParser = require('../body-parser')
+const validateData = require('./validate-data')
+const plural = require('./plural')
+const nested = require('./nested')
+const singular = require('./singular')
+const mixins = require('../mixins')
+
+module.exports = (source) => {
// Create router
- var router = express.Router()
+ const router = express.Router()
// Add middlewares
- router.use(common)
+ router.use(methodOverride())
+ router.use(bodyParser)
// Create database
- var db
+ let db
if (_.isObject(source)) {
db = low()
db.setState(source)
@@ -25,6 +28,8 @@ module.exports = function (source) {
db = low(source, { storage: fileAsync })
}
+ validateData(db.getState())
+
// Add underscore-db methods to db
db._.mixin(_db)
@@ -35,40 +40,39 @@ module.exports = function (source) {
router.db = db
// Expose render
- router.render = function (req, res) {
+ router.render = (req, res) => {
res.jsonp(res.locals.data)
}
// GET /db
- function showDatabase (req, res, next) {
+ router.get('/db', (req, res) => {
res.jsonp(db.getState())
- }
-
- router.get('/db', showDatabase)
+ })
+ // Handle /:parent/:parentId/:resource
router.use(nested())
// Create routes
- db.forEach(function (value, key) {
+ db.forEach((value, key) => {
if (_.isPlainObject(value)) {
- router.use('/' + key, singular(db, key))
+ router.use(`/${key}`, singular(db, key))
return
}
if (_.isArray(value)) {
- router.use('/' + key, plural(db, key))
+ router.use(`/${key}`, plural(db, key))
return
}
- var msg =
- 'Type of "' + key + '" (' + typeof value + ') ' +
- (_.isObject(source) ? '' : 'in ' + source) + ' is not supported. ' +
+ const msg =
+ `Type of "${key}" (${typeof value}) ` +
+ (_.isObject(source) ? '' : `in ${source}`) + ' is not supported. ' +
'Use objects or arrays of objects.'
throw new Error(msg)
}).value()
- router.use(function (req, res) {
+ router.use((req, res) => {
if (!res.locals.data) {
res.status(404)
res.locals.data = {}
@@ -77,7 +81,7 @@ module.exports = function (source) {
router.render(req, res)
})
- router.use(function (err, req, res, next) {
+ router.use((err, req, res, next) => {
console.error(err.stack)
res.status(500).send(err.stack)
})
diff --git a/src/server/router/nested.js b/src/server/router/nested.js
index a3edcc2e1..61a1a5926 100644
--- a/src/server/router/nested.js
+++ b/src/server/router/nested.js
@@ -1,23 +1,22 @@
-var express = require('express')
-var pluralize = require('pluralize')
-var utils = require('../utils')
+const express = require('express')
+const pluralize = require('pluralize')
-module.exports = function () {
- var router = express.Router()
+module.exports = () => {
+ const router = express.Router()
// Rewrite URL (/:resource/:id/:nested -> /:nested) and request query
function get (req, res, next) {
- var prop = pluralize.singular(req.params.resource)
- req.query[prop + 'Id'] = utils.toNative(req.params.id)
- req.url = '/' + req.params.nested
+ const prop = pluralize.singular(req.params.resource)
+ req.query[`${prop}Id`] = req.params.id
+ req.url = `/${req.params.nested}`
next()
}
// Rewrite URL (/:resource/:id/:nested -> /:nested) and request body
function post (req, res, next) {
- var prop = pluralize.singular(req.params.resource)
- req.body[prop + 'Id'] = utils.toNative(req.params.id)
- req.url = '/' + req.params.nested
+ const prop = pluralize.singular(req.params.resource)
+ req.body[`${prop}Id`] = req.params.id
+ req.url = `/${req.params.nested}`
next()
}
diff --git a/src/server/router/plural.js b/src/server/router/plural.js
index 2b7d266b1..24ecf850c 100644
--- a/src/server/router/plural.js
+++ b/src/server/router/plural.js
@@ -1,20 +1,21 @@
-var express = require('express')
-var _ = require('lodash')
-var pluralize = require('pluralize')
-var utils = require('../utils')
+const url = require('url')
+const express = require('express')
+const _ = require('lodash')
+const pluralize = require('pluralize')
+const utils = require('../utils')
-module.exports = function (db, name) {
+module.exports = (db, name) => {
// Create router
- var router = express.Router()
+ const router = express.Router()
// Embed function used in GET /name and GET /name/id
function embed (resource, e) {
e && [].concat(e)
- .forEach(function (externalResource) {
+ .forEach((externalResource) => {
if (db.get(externalResource).value) {
- var query = {}
- var singularResource = pluralize.singular(name)
- query[singularResource + 'Id'] = resource.id
+ const query = {}
+ const singularResource = pluralize.singular(name)
+ query[`${singularResource}Id`] = resource.id
resource[externalResource] = db.get(externalResource).filter(query).value()
}
})
@@ -23,17 +24,22 @@ module.exports = function (db, name) {
// Expand function used in GET /name and GET /name/id
function expand (resource, e) {
e && [].concat(e)
- .forEach(function (innerResource) {
- var plural = pluralize(innerResource)
+ .forEach((innerResource) => {
+ const plural = pluralize(innerResource)
if (db.get(plural).value()) {
- var prop = innerResource + 'Id'
+ const prop = `${innerResource}Id`
resource[innerResource] = db.get(plural).getById(resource[prop]).value()
}
})
}
function getFullURL (req) {
- return req.protocol + '://' + req.get('host') + req.originalUrl
+ const root = url.format({
+ protocol: req.protocol,
+ host: req.get('host')
+ })
+
+ return `${root}${req.originalUrl}`
}
// GET /name
@@ -44,19 +50,19 @@ module.exports = function (db, name) {
// GET /name?_embed=&_expand=
function list (req, res, next) {
// Resource chain
- var chain = db.get(name)
+ let chain = db.get(name)
// Remove q, _start, _end, ... from req.query to avoid filtering using those
// parameters
- var q = req.query.q
- var _start = req.query._start
- var _end = req.query._end
- var _page = req.query._page
- var _sort = req.query._sort
- var _order = req.query._order
- var _limit = req.query._limit
- var _embed = req.query._embed
- var _expand = req.query._expand
+ let q = req.query.q
+ let _start = req.query._start
+ let _end = req.query._end
+ let _page = req.query._page
+ let _sort = req.query._sort
+ let _order = req.query._order
+ let _limit = req.query._limit
+ let _embed = req.query._embed
+ let _expand = req.query._expand
delete req.query.q
delete req.query._start
delete req.query._end
@@ -68,17 +74,17 @@ module.exports = function (db, name) {
// Automatically delete query parameters that can't be found
// in the database
- Object.keys(req.query).forEach(function (query) {
- var arr = db.get(name).value()
- for (var i in arr) {
+ Object.keys(req.query).forEach((query) => {
+ const arr = db.get(name).value()
+ for (let i in arr) {
if (
_.has(arr[i], query) ||
query === 'callback' ||
query === '_' ||
- query.indexOf('_lte') !== -1 ||
- query.indexOf('_gte') !== -1 ||
- query.indexOf('_ne') !== -1 ||
- query.indexOf('_like') !== -1
+ /_lte$/.test(query) ||
+ /_gte$/.test(query) ||
+ /_ne$/.test(query) ||
+ /_like$/.test(query)
) return
}
delete req.query[query]
@@ -88,9 +94,9 @@ module.exports = function (db, name) {
// Full-text search
q = q.toLowerCase()
- chain = chain.filter(function (obj) {
- for (var key in obj) {
- var value = obj[key]
+ chain = chain.filter((obj) => {
+ for (let key in obj) {
+ const value = obj[key]
if (db._.deepQuery(value, q)) {
return true
}
@@ -98,41 +104,41 @@ module.exports = function (db, name) {
})
}
- Object.keys(req.query).forEach(function (key) {
+ Object.keys(req.query).forEach((key) => {
// Don't take into account JSONP query parameters
// jQuery adds a '_' query parameter too
if (key !== 'callback' && key !== '_') {
// Always use an array, in case req.query is an array
- var arr = [].concat(req.query[key])
+ const arr = [].concat(req.query[key])
- chain = chain.filter(function (element) {
+ chain = chain.filter((element) => {
return arr
- .map(utils.toNative)
.map(function (value) {
- var isDifferent = key.indexOf('_ne') !== -1
- var isRange = key.indexOf('_lte') !== -1 || key.indexOf('_gte') !== -1
- var isLike = key.indexOf('_like') !== -1
- var path = key.replace(/(_lte|_gte|_ne|_like)$/, '')
- var elementValue = _.get(element, path)
+ 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 elementValue = _.get(element, path)
+
+ if (!elementValue) {
+ return
+ }
if (isRange) {
- var isLowerThan = key.indexOf('_gte') !== -1
+ const isLowerThan = /_gte$/.test(key)
- if (isLowerThan) {
- return value <= elementValue
- } else {
- return value >= elementValue
- }
+ return isLowerThan
+ ? value <= elementValue
+ : value >= elementValue
} else if (isDifferent) {
- return value !== elementValue
+ return value !== elementValue.toString()
} else if (isLike) {
- return new RegExp(value, 'i').test(elementValue)
+ return new RegExp(value, 'i').test(elementValue.toString())
} else {
- return _.matchesProperty(key, value)(element)
+ return value === elementValue.toString()
}
- }).reduce(function (a, b) {
- return a || b
})
+ .reduce((a, b) => a || b)
})
}
})
@@ -160,9 +166,9 @@ module.exports = function (db, name) {
_page = parseInt(_page, 10)
_page = _page >= 1 ? _page : 1
_limit = parseInt(_limit, 10) || 10
- var page = utils.getPage(chain.value(), _page, _limit)
- var links = {}
- var fullURL = getFullURL(req)
+ const page = utils.getPage(chain.value(), _page, _limit)
+ const links = {}
+ const fullURL = getFullURL(req)
if (page.first) {
links.first = fullURL.replace('page=' + page.current, 'page=' + page.first)
@@ -207,24 +213,25 @@ module.exports = function (db, name) {
// GET /name/:id
// GET /name/:id?_embed=&_expand
function show (req, res, next) {
- var _embed = req.query._embed
- var _expand = req.query._expand
- var id = utils.toNative(req.params.id)
- var resource = db.get(name).getById(id).value()
+ const _embed = req.query._embed
+ const _expand = req.query._expand
+ const resource = db.get(name)
+ .getById(req.params.id)
+ .value()
if (resource) {
// Clone resource to avoid making changes to the underlying object
- resource = _.cloneDeep(resource)
+ const clone = _.cloneDeep(resource)
// Embed other resources based on resource id
// /posts/1?_embed=comments
- embed(resource, _embed)
+ embed(clone, _embed)
// Expand inner resources based on id
// /posts/1?_expand=user
- expand(resource, _expand)
+ expand(clone, _expand)
- res.locals.data = resource
+ res.locals.data = clone
}
next()
@@ -232,11 +239,7 @@ module.exports = function (db, name) {
// POST /name
function create (req, res, next) {
- for (var key in req.body) {
- req.body[key] = utils.toNative(req.body[key])
- }
-
- var resource = db.get(name)
+ const resource = db.get(name)
.insert(req.body)
.value()
@@ -248,18 +251,14 @@ module.exports = function (db, name) {
// PUT /name/:id
// PATCH /name/:id
function update (req, res, next) {
- for (var key in req.body) {
- req.body[key] = utils.toNative(req.body[key])
- }
-
- var id = utils.toNative(req.params.id)
- var chain = db.get(name)
+ const id = req.params.id
+ let chain = db.get(name)
chain = req.method === 'PATCH'
? chain.updateById(id, req.body)
: chain.replaceById(id, req.body)
- var resource = chain.value()
+ const resource = chain.value()
if (resource) {
res.locals.data = resource
@@ -270,13 +269,17 @@ module.exports = function (db, name) {
// DELETE /name/:id
function destroy (req, res, next) {
- var resource = db.get(name).removeById(utils.toNative(req.params.id)).value()
+ const resource = db.get(name)
+ .removeById(req.params.id)
+ .value()
// Remove dependents documents
- var removable = db._.getRemovable(db.getState())
+ const removable = db._.getRemovable(db.getState())
- _.each(removable, function (item) {
- db.get(item.name).removeById(item.id).value()
+ removable.forEach((item) => {
+ db.get(item.name)
+ .removeById(item.id)
+ .value()
})
if (resource) {
diff --git a/src/server/router/singular.js b/src/server/router/singular.js
index c5c629198..f33bbda09 100644
--- a/src/server/router/singular.js
+++ b/src/server/router/singular.js
@@ -1,7 +1,7 @@
-var express = require('express')
+const express = require('express')
-module.exports = function (db, name) {
- var router = express.Router()
+module.exports = (db, name) => {
+ const router = express.Router()
function show (req, res, next) {
res.locals.data = db.get(name).value()
diff --git a/src/server/router/validate-data.js b/src/server/router/validate-data.js
new file mode 100644
index 000000000..5bb5dbc1e
--- /dev/null
+++ b/src/server/router/validate-data.js
@@ -0,0 +1,26 @@
+const _ = require('lodash')
+
+function validateKey (key) {
+ if (key.indexOf('/') !== -1) {
+ const msg = [
+ `Oops, found / character in database property '${key}'.`,
+ '',
+ '/ aren\'t supported, if you want to tweak default routes, see',
+ 'https://github.com/typicode/json-server/tree/next#add-custom-routes'
+ ].join('\n')
+ throw new Error(msg)
+ }
+}
+
+module.exports = (obj) => {
+ if (_.isPlainObject(obj)) {
+ Object
+ .keys(obj)
+ .forEach(validateKey)
+ } else {
+ throw new Error(
+ `Data must be an object. Found ${typeof obj}.` +
+ 'See https://github.com/typicode/json-server for example.'
+ )
+ }
+}
diff --git a/src/server/utils.js b/src/server/utils.js
index 8f8307a96..f7a5c9a50 100644
--- a/src/server/utils.js
+++ b/src/server/utils.js
@@ -1,27 +1,5 @@
module.exports = {
- toNative: toNative,
- getPage: getPage
-}
-
-// Turns string to native.
-// Example:
-// 'true' -> true
-// '1' -> 1
-function toNative (value) {
- if (typeof value === 'string') {
- if (
- value === '' ||
- value.trim() !== value ||
- (value.length > 1 && value[0] === '0')
- ) {
- return value
- } else if (value === 'true' || value === 'false') {
- return value === 'true'
- } else if (!isNaN(+value)) {
- return +value
- }
- }
- return value
+ getPage
}
function getPage (array, page, perPage) {
diff --git a/test/cli/index.js b/test/cli/index.js
index ce9657c48..f822f6d61 100644
--- a/test/cli/index.js
+++ b/test/cli/index.js
@@ -1,70 +1,73 @@
-var fs = require('fs')
-var path = require('path')
-var cp = require('child_process')
-var assert = require('assert')
-var supertest = require('supertest')
-var osTmpdir = require('os-tmpdir')
-var tempWrite = require('temp-write')
-var mkdirp = require('mkdirp')
-var rimraf = require('rimraf')
-var express = require('express')
-var serverReady = require('server-ready')
-var pkg = require('../../package.json')
-
-var PORT = 3100
+const fs = require('fs')
+const path = require('path')
+const cp = require('child_process')
+const assert = require('assert')
+const supertest = require('supertest')
+const osTmpdir = require('os-tmpdir')
+const tempWrite = require('temp-write')
+const mkdirp = require('mkdirp')
+const rimraf = require('rimraf')
+const express = require('express')
+const serverReady = require('server-ready')
+
+let PORT = 3100
+
+const middlewareFiles = {
+ en: './fixtures/middlewares/en.js',
+ jp: './fixtures/middlewares/jp.js'
+}
+
+const bin = path.join(__dirname, '../../lib/cli/bin')
function cli (args) {
- var bin = path.join(__dirname, '../..', pkg.bin)
- return cp.spawn('node', [bin, '-p', PORT].concat(args), {
+ return cp.spawn('node', ['--', bin, '-p', PORT].concat(args), {
cwd: __dirname,
stdio: ['pipe', process.stdout, process.stderr]
})
}
-/* global beforeEach, afterEach, describe, it */
-
-describe('cli', function () {
- var child
- var request
- var dbFile
- var routesFile
- var middlewareFiles = {
- en: './fixtures/middlewares/en.js',
- jp: './fixtures/middlewares/jp.js'
- }
-
- beforeEach(function () {
- dbFile = tempWrite.sync(JSON.stringify({
- posts: [
- { id: 1 },
- { _id: 2 }
- ]
- }), 'db.json')
-
- routesFile = tempWrite.sync(JSON.stringify({
- '/blog/': '/'
- }), 'routes.json')
+describe('cli', () => {
+ let child
+ let request
+ let dbFile
+ let routesFile
+
+ beforeEach(() => {
+ dbFile = tempWrite.sync(
+ JSON.stringify({
+ posts: [
+ { id: 1 },
+ { _id: 2 }
+ ]
+ }),
+ 'db.json'
+ )
+
+ routesFile = tempWrite.sync(
+ JSON.stringify({ '/blog/': '/' }),
+ 'routes.json'
+ )
++PORT
- request = supertest('http://localhost/' + PORT)
+ request = supertest(`http://localhost:${PORT}`)
})
- afterEach(function () {
- child.kill()
+ afterEach(() => {
+ child.kill('SIGKILL')
})
- describe('db.json', function () {
- beforeEach(function (done) {
- child = cli([dbFile])
+ describe('db.json', () => {
+ beforeEach((done) => {
+ child = cli([ dbFile ])
serverReady(PORT, done)
})
- it('should support JSON file', function (done) {
+ it('should support JSON file', (done) => {
request.get('/posts').expect(200, done)
})
- it('should send CORS headers', function (done) {
- var origin = 'http://example.com/'
+ it('should send CORS headers', (done) => {
+ const origin = 'http://example.com/'
request.get('/posts')
.set('Origin', origin)
@@ -72,12 +75,12 @@ describe('cli', function () {
.expect(200, done)
})
- it('should update JSON file', function (done) {
+ it('should update JSON file', (done) => {
request.post('/posts')
.send({ title: 'hello' })
- .end(function () {
- setTimeout(function () {
- var str = fs.readFileSync(dbFile, 'utf8')
+ .end(() => {
+ setTimeout(() => {
+ const str = fs.readFileSync(dbFile, 'utf8')
assert(str.indexOf('hello') !== -1)
done()
}, 1000)
@@ -85,118 +88,122 @@ describe('cli', function () {
})
})
- describe('seed.js', function () {
- beforeEach(function (done) {
- child = cli(['fixtures/seed.js'])
+ describe('seed.js', () => {
+ beforeEach((done) => {
+ child = cli([ 'fixtures/seed.js' ])
serverReady(PORT, done)
})
- it('should support JS file', function (done) {
+ it('should support JS file', (done) => {
request.get('/posts').expect(200, done)
})
})
- describe('http://localhost:8080/db', function () {
- beforeEach(function (done) {
- var fakeServer = express()
- fakeServer.get('/db', function (req, res) {
+ describe('http://localhost:8080/db', () => {
+ beforeEach((done) => {
+ const fakeServer = express()
+ fakeServer.get('/db', (req, res) => {
res.jsonp({ posts: [] })
})
- fakeServer.listen(8080, function () {
- child = cli(['http://localhost:8080/db'])
+ fakeServer.listen(8080, () => {
+ child = cli([ 'http://localhost:8080/db' ])
serverReady(PORT, done)
})
})
- it('should support URL file', function (done) {
+ it('should support URL file', (done) => {
request.get('/posts').expect(200, done)
})
})
- describe('db.json -r routes.json -m middleware.js -i _id --read-only', function () {
- beforeEach(function (done) {
- child = cli([dbFile, '-r', routesFile, '-m', middlewareFiles.en, '-i', '_id', '--read-only'])
+ describe('db.json -r routes.json -m middleware.js -i _id --read-only', () => {
+ beforeEach((done) => {
+ child = cli([ dbFile, '-r', routesFile, '-m', middlewareFiles.en, '-i', '_id', '--read-only' ])
serverReady(PORT, done)
})
- it('should use routes.json and _id as the identifier', function (done) {
+ it('should use routes.json and _id as the identifier', (done) => {
request.get('/blog/posts/2').expect(200, done)
})
- it('should apply middlewares', function (done) {
+ it('should apply middlewares', (done) => {
request.get('/blog/posts/2').expect('X-Hello', 'World', done)
})
- it('should allow only GET requests', function (done) {
+ it('should allow only GET requests', (done) => {
request.post('/blog/posts').expect(403, done)
})
})
- describe('db.json -m first-middleware.js second-middleware.js', function () {
- beforeEach(function (done) {
- child = cli([dbFile, '-m', middlewareFiles.en, middlewareFiles.jp])
+ describe('db.json -m first-middleware.js second-middleware.js', () => {
+ beforeEach((done) => {
+ child = cli([ dbFile, '-m', middlewareFiles.en, middlewareFiles.jp ])
serverReady(PORT, done)
})
- it('should apply all middlewares', function (done) {
+ it('should apply all middlewares', (done) => {
request.get('/posts')
.expect('X-Hello', 'World')
.expect('X-Konnichiwa', 'Sekai', done)
})
})
- describe('db.json -d 1000', function () {
- beforeEach(function (done) {
- child = cli([dbFile, '-d', 1000])
+ describe('db.json -d 1000', () => {
+ beforeEach((done) => {
+ child = cli([ dbFile, '-d', 1000 ])
serverReady(PORT, done)
})
- it('should delay response', function (done) {
- var start = new Date()
+ it('should delay response', (done) => {
+ const start = new Date()
request.get('/posts').expect(200, function (err) {
- var end = new Date()
- done(end - start > 1000 ? err : new Error('Request wasn\'t delayed'))
+ const end = new Date()
+ done(
+ end - start > 1000
+ ? err
+ : new Error('Request wasn\'t delayed')
+ )
})
})
})
- describe('db.json -s fixtures/public -S /some/path/snapshots', function () {
- var snapshotsDir = path.join(osTmpdir(), 'snapshots')
- var publicDir = 'fixtures/public'
+ describe('db.json -s fixtures/public -S /some/path/snapshots', () => {
+ const snapshotsDir = path.join(osTmpdir(), 'snapshots')
+ const publicDir = 'fixtures/public'
- beforeEach(function (done) {
+ beforeEach((done) => {
rimraf.sync(snapshotsDir)
mkdirp.sync(snapshotsDir)
- child = cli([dbFile, '-s', publicDir, '-S', snapshotsDir])
- serverReady(PORT, function () {
+ child = cli([ dbFile, '-s', publicDir, '-S', snapshotsDir ])
+ serverReady(PORT, () => {
child.stdin.write('s\n')
setTimeout(done, 100)
})
})
- it('should serve fixtures/public', function (done) {
+ it('should serve fixtures/public', (done) => {
request.get('/').expect(/Hello/, done)
})
- it('should save a snapshot in snapshots dir', function () {
+ it('should save a snapshot in snapshots dir', () => {
assert.equal(fs.readdirSync(snapshotsDir).length, 1)
})
})
- describe('fixtures/seed.json --no-cors=true', function () {
- beforeEach(function (done) {
- child = cli(['fixtures/seed.js', '--no-cors=true'])
+ describe('fixtures/seed.json --no-cors=true', () => {
+ beforeEach((done) => {
+ child = cli([ 'fixtures/seed.js', '--no-cors=true' ])
serverReady(PORT, done)
})
- it('should not send Access-Control-Allow-Origin headers', function (done) {
- var origin = 'http://example.com/'
+ it('should not send Access-Control-Allow-Origin headers', (done) => {
+ const origin = 'http://example.com/'
request.get('/posts')
.set('Origin', origin)
.expect(200)
- .end(function (err, res) {
+ .end((err, res) => {
if (err) {
done(err)
} if ('access-control-allow-origin' in res.headers) {
@@ -208,13 +215,13 @@ describe('cli', function () {
})
})
- describe('fixtures/seed.json --no-gzip=true', function () {
- beforeEach(function (done) {
- child = cli(['fixtures/seed.js', '--no-gzip=true'])
+ describe('fixtures/seed.json --no-gzip=true', () => {
+ beforeEach((done) => {
+ child = cli([ 'fixtures/seed.js', '--no-gzip=true' ])
serverReady(PORT, done)
})
- it('should not set Content-Encoding to gzip', function (done) {
+ it('should not set Content-Encoding to gzip', (done) => {
request.get('/posts')
.expect(200)
.end(function (err, res) {
@@ -229,39 +236,55 @@ describe('cli', function () {
})
})
- describe('--watch db.json -r routes.json', function () {
- beforeEach(function (done) {
- child = cli(['--watch', dbFile, '-r', routesFile])
+ describe('--watch db.json -r routes.json', () => {
+ beforeEach((done) => {
+ child = cli([ dbFile, '-r', routesFile, '--watch' ])
serverReady(PORT, done)
})
- it('should watch db file', function (done) {
+ it('should watch db file', (done) => {
fs.writeFileSync(dbFile, JSON.stringify({ foo: [] }))
- setTimeout(function () {
+ setTimeout(() => {
request.get('/foo').expect(200, done)
}, 1000)
})
- it('should watch routes file', function (done) {
- // Can be very slow
- this.timeout(10000)
+ it('should watch routes file', (done) => {
fs.writeFileSync(routesFile, JSON.stringify({ '/api/': '/' }))
- setTimeout(function () {
+ setTimeout(() => {
request.get('/api/posts').expect(200, done)
- }, 9000)
+ }, 1000)
})
})
- describe('db.json --config some-config.json', function (done) {
- beforeEach(function (done) {
- child = cli([dbFile, '--config', 'fixtures/config.json'])
+ describe('non existent db.json', () => {
+ beforeEach((done) => {
+ fs.unlinkSync(dbFile)
+ child = cli([ dbFile ])
serverReady(PORT, done)
})
- it('should apply all middlewares', function (done) {
- request.get('/posts')
- .expect('X-Hello', 'World')
- .expect('X-Konnichiwa', 'Sekai', done)
+ it('should create JSON file if it doesn\'t exist', (done) => {
+ request.get('/posts').expect(200, done)
+ })
+ })
+
+ describe('db.json with error', () => {
+ beforeEach(() => {
+ dbFile = tempWrite.sync(
+ JSON.stringify({ 'a/b': [] }),
+ 'db-error.json'
+ )
+ })
+
+ it('should exit with an error', (done) => {
+ child = cli([ dbFile ])
+ child.on('exit', (code) => {
+ if (code === 1) {
+ return done()
+ }
+ return done(new Error('should exit with error code'))
+ })
})
})
})
diff --git a/test/mocha.opts b/test/mocha.opts
new file mode 100644
index 000000000..82a4ddb26
--- /dev/null
+++ b/test/mocha.opts
@@ -0,0 +1,3 @@
+--compilers js:babel-register
+--reporter spec
+--timeout 5000
\ No newline at end of file
diff --git a/test/server/mixins.js b/test/server/mixins.js
index 395230085..8707fd0c0 100644
--- a/test/server/mixins.js
+++ b/test/server/mixins.js
@@ -1,34 +1,52 @@
-var assert = require('assert')
-var _ = require('lodash')
-var _db = require('underscore-db')
-var mixins = require('../../src/server/mixins')
+const assert = require('assert')
+const _ = require('lodash')
+const _db = require('underscore-db')
+const mixins = require('../../src/server/mixins')
-/* global describe, it */
+describe('mixins', () => {
+ let db
-describe('mixins', function () {
- describe('getRemovable', function () {
- it('should return removable documents', function () {
- var db = {
- posts: [
- {id: 1, comment: 1}
- ],
- comments: [
- {id: 1, postId: 1},
- // Comments below references a post that doesn't exist
- {id: 2, postId: 2},
- {id: 3, postId: 2}
- ]
- }
+ before(() => {
+ _.mixin(_db)
+ _.mixin(mixins)
+ })
- var expected = [
- {name: 'comments', id: 2},
- {name: 'comments', id: 3}
+ beforeEach(() => {
+ db = {
+ posts: [
+ { id: 1, comment: 1 }
+ ],
+ comments: [
+ { id: 1, postId: 1 },
+ // Comments below references a post that doesn't exist
+ { id: 2, postId: 2 },
+ { id: 3, postId: 2 }
+ ],
+ photos: [
+ { id: '1' },
+ { id: '2' }
]
+ }
+ })
- _.mixin(_db)
- _.mixin(mixins)
+ describe('getRemovable', () => {
+ it('should return removable documents', () => {
+ const expected = [
+ { name: 'comments', id: 2 },
+ { name: 'comments', id: 3 }
+ ]
assert.deepEqual(_.getRemovable(db), expected)
})
})
+
+ describe('createId', () => {
+ it('should return a new id', () => {
+ assert.equal(_.createId(db.comments), 4)
+ })
+
+ it('should return a new uuid', () => {
+ assert.notEqual(_.createId(db.photos), 3)
+ })
+ })
})
diff --git a/test/server/plural.js b/test/server/plural.js
index 7bddd09db..1cd47748c 100644
--- a/test/server/plural.js
+++ b/test/server/plural.js
@@ -1,43 +1,46 @@
-var assert = require('assert')
-var _ = require('lodash')
-var request = require('supertest')
-var jsonServer = require('../../src/server')
-
-/* global beforeEach, describe, it */
-describe('Server', function () {
- var server
- var router
- var db
-
- beforeEach(function () {
+const assert = require('assert')
+const _ = require('lodash')
+const request = require('supertest')
+const jsonServer = require('../../src/server')
+
+describe('Server', () => {
+ let server
+ let router
+ let db
+
+ beforeEach(() => {
db = {}
db.posts = [
- {id: 1, body: 'foo'},
- {id: 2, body: 'bar'}
+ { id: 1, body: 'foo' },
+ { id: 2, body: 'bar' }
]
db.tags = [
- {id: 1, body: 'Technology'},
- {id: 2, body: 'Photography'},
- {id: 3, body: 'photo'}
+ { id: 1, body: 'Technology' },
+ { id: 2, body: 'Photography' },
+ { id: 3, body: 'photo' }
]
db.users = [
- {id: 1, username: 'Jim'},
- {id: 2, username: 'George'}
+ { id: 1, username: 'Jim', tel: '0123' },
+ { id: 2, username: 'George', tel: '123' }
]
db.comments = [
- {id: 1, body: 'foo', published: true, postId: 1, userId: 1},
- {id: 2, body: 'bar', published: false, postId: 1, userId: 2},
- {id: 3, body: 'baz', published: false, postId: 2, userId: 1},
- {id: 4, body: 'qux', published: true, postId: 2, userId: 2},
- {id: 5, body: 'quux', published: false, postId: 2, userId: 1}
+ { id: 1, body: 'foo', published: true, postId: 1, userId: 1 },
+ { id: 2, body: 'bar', published: false, postId: 1, userId: 2 },
+ { id: 3, body: 'baz', published: false, postId: 2, userId: 1 },
+ { id: 4, body: 'qux', published: true, postId: 2, userId: 2 },
+ { id: 5, body: 'quux', published: false, postId: 2, userId: 1 }
]
db.refs = [
- {id: 'abcd-1234', url: 'http://example.com/', postId: 1, userId: 1}
+ { id: 'abcd-1234', url: 'http://example.com/', postId: 1, userId: 1 }
+ ]
+
+ db.stringIds = [
+ { id: '1234' }
]
db.deep = [
@@ -46,27 +49,27 @@ describe('Server', function () {
]
db.nested = [
- {resource: {name: 'dewey'}},
- {resource: {name: 'cheatem'}},
- {resource: {name: 'howe'}}
+ { resource: {name: 'dewey'} },
+ { resource: {name: 'cheatem'} },
+ { resource: {name: 'howe'} }
]
db.list = [
- {id: 1},
- {id: 2},
- {id: 3},
- {id: 4},
- {id: 5},
- {id: 6},
- {id: 7},
- {id: 8},
- {id: 9},
- {id: 10},
- {id: 11},
- {id: 12},
- {id: 13},
- {id: 14},
- {id: 15}
+ { id: 1 },
+ { id: 2 },
+ { id: 3 },
+ { id: 4 },
+ { id: 5 },
+ { id: 6 },
+ { id: 7 },
+ { id: 8 },
+ { id: 9 },
+ { id: 10 },
+ { id: 11 },
+ { id: 12 },
+ { id: 13 },
+ { id: 14 },
+ { id: 15 }
]
server = jsonServer.create()
@@ -80,8 +83,8 @@ describe('Server', function () {
server.use(router)
})
- describe('GET /db', function () {
- it('should respond with json and full database', function (done) {
+ describe('GET /db', () => {
+ it('should respond with json and full database', (done) => {
request(server)
.get('/db')
.expect('Content-Type', /json/)
@@ -90,8 +93,8 @@ describe('Server', function () {
})
})
- describe('GET /:resource', function () {
- it('should respond with json and corresponding resources', function (done) {
+ describe('GET /:resource', () => {
+ it('should respond with json and corresponding resources', (done) => {
request(server)
.get('/posts')
.set('Origin', 'http://example.com/')
@@ -102,39 +105,47 @@ describe('Server', function () {
.expect(200, done)
})
- it('should respond with 404 if resource is not found', function (done) {
+ it('should respond with 404 if resource is not found', (done) => {
request(server)
.get('/undefined')
.expect(404, done)
})
})
- describe('GET /:resource?attr=&attr=', function () {
- it('should respond with json and filter resources', function (done) {
+ describe('GET /:resource?attr=&attr=', () => {
+ it('should respond with json and filter resources', (done) => {
request(server)
.get('/comments?postId=1&published=true')
.expect('Content-Type', /json/)
- .expect([db.comments[0]])
+ .expect([ db.comments[0] ])
.expect(200, done)
})
- it('should support multiple filters', function (done) {
+ it('should be strict', (done) => {
+ request(server)
+ .get('/users?tel=123')
+ .expect('Content-Type', /json/)
+ .expect([ db.users[1] ])
+ .expect(200, done)
+ })
+
+ it('should support multiple filters', (done) => {
request(server)
.get('/comments?id=1&id=2')
.expect('Content-Type', /json/)
- .expect([db.comments[0], db.comments[1]])
+ .expect([ db.comments[0], db.comments[1] ])
.expect(200, done)
})
- it('should support deep filter', function (done) {
+ it('should support deep filter', (done) => {
request(server)
.get('/deep?a.b=1')
.expect('Content-Type', /json/)
- .expect([db.deep[0]])
+ .expect([ db.deep[0] ])
.expect(200, done)
})
- it('should ignore JSONP query parameters callback and _ ', function (done) {
+ it('should ignore JSONP query parameters callback and _ ', (done) => {
request(server)
.get('/comments?callback=1&_=1')
.expect('Content-Type', /text/)
@@ -142,7 +153,7 @@ describe('Server', function () {
.expect(200, done)
})
- it('should ignore unknown query parameters', function (done) {
+ it('should ignore unknown query parameters', (done) => {
request(server)
.get('/comments?foo=1&bar=2')
.expect('Content-Type', /json/)
@@ -151,16 +162,16 @@ describe('Server', function () {
})
})
- describe('GET /:resource?q=', function () {
- it('should respond with json and make a full-text search', function (done) {
+ describe('GET /:resource?q=', () => {
+ it('should respond with json and make a full-text search', (done) => {
request(server)
.get('/tags?q=pho')
.expect('Content-Type', /json/)
- .expect([db.tags[1], db.tags[2]])
+ .expect([ db.tags[1], db.tags[2] ])
.expect(200, done)
})
- it('should respond with json and make a deep full-text search', function (done) {
+ it('should respond with json and make a deep full-text search', (done) => {
request(server)
.get('/deep?q=1')
.expect('Content-Type', /json/)
@@ -168,25 +179,25 @@ describe('Server', function () {
.expect(200, done)
})
- it('should return an empty array when nothing is matched', function (done) {
+ it('should return an empty array when nothing is matched', (done) => {
request(server)
.get('/tags?q=nope')
.expect('Content-Type', /json/)
- .expect([])
+ .expect([ ])
.expect(200, done)
})
- it('should support other query parameters', function (done) {
+ it('should support other query parameters', (done) => {
request(server)
.get('/comments?q=qu&published=true')
.expect('Content-Type', /json/)
- .expect([db.comments[3]])
+ .expect([ db.comments[3] ])
.expect(200, done)
})
})
- describe('GET /:resource?_end=', function () {
- it('should respond with a sliced array', function (done) {
+ describe('GET /:resource?_end=', () => {
+ it('should respond with a sliced array', (done) => {
request(server)
.get('/comments?_end=2')
.expect('Content-Type', /json/)
@@ -197,24 +208,24 @@ describe('Server', function () {
})
})
- describe('GET /:resource?_sort=', function () {
- it('should respond with json and sort on a field', function (done) {
+ describe('GET /:resource?_sort=', () => {
+ it('should respond with json and sort on a field', (done) => {
request(server)
.get('/tags?_sort=body')
.expect('Content-Type', /json/)
- .expect([db.tags[1], db.tags[0], db.tags[2]])
+ .expect([ db.tags[1], db.tags[0], db.tags[2] ])
.expect(200, done)
})
- it('should reverse sorting with _order=DESC', function (done) {
+ it('should reverse sorting with _order=DESC', (done) => {
request(server)
.get('/tags?_sort=body&_order=DESC')
.expect('Content-Type', /json/)
- .expect([db.tags[2], db.tags[0], db.tags[1]])
+ .expect([ db.tags[2], db.tags[0], db.tags[1] ])
.expect(200, done)
})
- it('should sort on numerical field', function (done) {
+ it('should sort on numerical field', (done) => {
request(server)
.get('/posts?_sort=id&_order=DESC')
.expect('Content-Type', /json/)
@@ -222,41 +233,41 @@ describe('Server', function () {
.expect(200, done)
})
- it('should sort on nested field', function (done) {
+ it('should sort on nested field', (done) => {
request(server)
.get('/nested?_sort=resource.name')
.expect('Content-Type', /json/)
- .expect([db.nested[1], db.nested[0], db.nested[2]])
+ .expect([ db.nested[1], db.nested[0], db.nested[2] ])
.expect(200, done)
})
})
- describe('GET /:resource?_start=&_end=', function () {
- it('should respond with a sliced array', function (done) {
+ describe('GET /:resource?_start=&_end=', () => {
+ it('should respond with a sliced array', (done) => {
request(server)
.get('/comments?_start=1&_end=2')
.expect('Content-Type', /json/)
- .expect('x-total-count', db.comments.length.toString())
+ .expect('X-Total-Count', db.comments.length.toString())
.expect('Access-Control-Expose-Headers', 'X-Total-Count')
.expect(db.comments.slice(1, 2))
.expect(200, done)
})
})
- describe('GET /:resource?_start=&_limit=', function () {
- it('should respond with a limited array', function (done) {
+ describe('GET /:resource?_start=&_limit=', () => {
+ it('should respond with a limited array', (done) => {
request(server)
.get('/comments?_start=1&_limit=1')
.expect('Content-Type', /json/)
- .expect('x-total-count', db.comments.length.toString())
+ .expect('X-Total-Count', db.comments.length.toString())
.expect('Access-Control-Expose-Headers', 'X-Total-Count')
.expect(db.comments.slice(1, 2))
.expect(200, done)
})
})
- describe('GET /:resource?_page=', function () {
- it('should paginate', function (done) {
+ describe('GET /:resource?_page=', () => {
+ it('should paginate', (done) => {
request(server)
.get('/list?_page=2')
.expect('Content-Type', /json/)
@@ -267,9 +278,9 @@ describe('Server', function () {
})
})
- describe('GET /:resource?_page=&_limit=', function () {
- it('should paginate with a custom limit', function (done) {
- var link = [
+ describe('GET /:resource?_page=&_limit=', () => {
+ it('should paginate with a custom limit', (done) => {
+ const link = [
'; rel="first"',
'; rel="prev"',
'; rel="next"',
@@ -287,8 +298,8 @@ describe('Server', function () {
})
})
- describe('GET /:resource?attr_gte=&attr_lte=', function () {
- it('should respond with a limited array', function (done) {
+ describe('GET /:resource?attr_gte=&attr_lte=', () => {
+ it('should respond with a limited array', (done) => {
request(server)
.get('/comments?id_gte=2&id_lte=3')
.expect('Content-Type', /json/)
@@ -297,8 +308,8 @@ describe('Server', function () {
})
})
- describe('GET /:resource?attr_ne=', function () {
- it('should respond with a limited array', function (done) {
+ describe('GET /:resource?attr_ne=', () => {
+ it('should respond with a limited array', (done) => {
request(server)
.get('/comments?id_ne=1')
.expect('Content-Type', /json/)
@@ -307,8 +318,8 @@ describe('Server', function () {
})
})
- describe('GET /:resource?attr_like=', function () {
- it('should respond with an array that matches the like operator (case insensitive)', function (done) {
+ describe('GET /:resource?attr_like=', () => {
+ it('should respond with an array that matches the like operator (case insensitive)', (done) => {
request(server)
.get('/tags?body_like=photo')
.expect('Content-Type', /json/)
@@ -320,8 +331,8 @@ describe('Server', function () {
})
})
- describe('GET /:parent/:parentId/:resource', function () {
- it('should respond with json and corresponding nested resources', function (done) {
+ describe('GET /:parent/:parentId/:resource', () => {
+ it('should respond with json and corresponding nested resources', (done) => {
request(server)
.get('/posts/1/comments')
.expect('Content-Type', /json/)
@@ -333,8 +344,8 @@ describe('Server', function () {
})
})
- describe('GET /:resource/:id', function () {
- it('should respond with json and corresponding resource', function (done) {
+ describe('GET /:resource/:id', () => {
+ it('should respond with json and corresponding resource', (done) => {
request(server)
.get('/posts/1')
.expect('Content-Type', /json/)
@@ -342,7 +353,7 @@ describe('Server', function () {
.expect(200, done)
})
- it('should support string id, respond with json and corresponding resource', function (done) {
+ it('should support string id, respond with json and corresponding resource', (done) => {
request(server)
.get('/refs/abcd-1234')
.expect('Content-Type', /json/)
@@ -350,7 +361,15 @@ describe('Server', function () {
.expect(200, done)
})
- it('should respond with 404 if resource is not found', function (done) {
+ it('should support integer id as string', (done) => {
+ request(server)
+ .get('/stringIds/1234')
+ .expect('Content-Type', /json/)
+ .expect(db.stringIds[0])
+ .expect(200, done)
+ })
+
+ it('should respond with 404 if resource is not found', (done) => {
request(server)
.get('/posts/9001')
.expect('Content-Type', /json/)
@@ -359,11 +378,11 @@ describe('Server', function () {
})
})
- describe('GET /:resource?_embed=', function () {
- it('should respond with corresponding resources and embedded resources', function (done) {
- var posts = _.cloneDeep(db.posts)
- posts[0].comments = [db.comments[0], db.comments[1]]
- posts[1].comments = [db.comments[2], db.comments[3], db.comments[4]]
+ describe('GET /:resource?_embed=', () => {
+ it('should respond with corresponding resources and embedded resources', (done) => {
+ const posts = _.cloneDeep(db.posts)
+ posts[0].comments = [ db.comments[0], db.comments[1] ]
+ posts[1].comments = [ db.comments[2], db.comments[3], db.comments[4] ]
request(server)
.get('/posts?_embed=comments')
.expect('Content-Type', /json/)
@@ -372,12 +391,12 @@ describe('Server', function () {
})
})
- describe('GET /:resource?_embed&_embed=', function () {
- it('should respond with corresponding resources and embedded resources', function (done) {
- var posts = _.cloneDeep(db.posts)
- posts[0].comments = [db.comments[0], db.comments[1]]
- posts[0].refs = [db.refs[0]]
- posts[1].comments = [db.comments[2], db.comments[3], db.comments[4]]
+ describe('GET /:resource?_embed&_embed=', () => {
+ it('should respond with corresponding resources and embedded resources', (done) => {
+ const posts = _.cloneDeep(db.posts)
+ posts[0].comments = [ db.comments[0], db.comments[1] ]
+ posts[0].refs = [ db.refs[0] ]
+ posts[1].comments = [ db.comments[2], db.comments[3], db.comments[4] ]
posts[1].refs = []
request(server)
.get('/posts?_embed=comments&_embed=refs')
@@ -387,34 +406,34 @@ describe('Server', function () {
})
})
- describe('GET /:resource/:id?_embed=', function () {
- it('should respond with corresponding resources and embedded resources', function (done) {
- var posts = db.posts[0]
- posts.comments = [db.comments[0], db.comments[1]]
+ describe('GET /:resource/:id?_embed=', () => {
+ it('should respond with corresponding resources and embedded resources', (done) => {
+ const post = _.cloneDeep(db.posts[0])
+ post.comments = [ db.comments[0], db.comments[1] ]
request(server)
.get('/posts/1?_embed=comments')
.expect('Content-Type', /json/)
- .expect(posts)
+ .expect(post)
.expect(200, done)
})
})
- describe('GET /:resource/:id?_embed=&_embed=', function () {
- it('should respond with corresponding resource and embedded resources', function (done) {
- var posts = db.posts[0]
- posts.comments = [db.comments[0], db.comments[1]]
- posts.refs = [db.refs[0]]
+ describe('GET /:resource/:id?_embed=&_embed=', () => {
+ it('should respond with corresponding resource and embedded resources', (done) => {
+ const post = _.cloneDeep(db.posts[0])
+ post.comments = [ db.comments[0], db.comments[1] ]
+ post.refs = [db.refs[0]]
request(server)
.get('/posts/1?_embed=comments&_embed=refs')
.expect('Content-Type', /json/)
- .expect(posts)
+ .expect(post)
.expect(200, done)
})
})
- describe('GET /:resource?_expand=', function () {
- it('should respond with corresponding resource and expanded inner resources', function (done) {
- var refs = _.cloneDeep(db.refs)
+ describe('GET /:resource?_expand=', () => {
+ it('should respond with corresponding resource and expanded inner resources', (done) => {
+ const refs = _.cloneDeep(db.refs)
refs[0].post = db.posts[0]
request(server)
.get('/refs?_expand=post')
@@ -424,21 +443,21 @@ describe('Server', function () {
})
})
- describe('GET /:resource/:id?_expand=', function () {
- it('should respond with corresponding resource and expanded inner resources', function (done) {
- var comments = db.comments[0]
- comments.post = db.posts[0]
+ describe('GET /:resource/:id?_expand=', () => {
+ it('should respond with corresponding resource and expanded inner resources', (done) => {
+ const comment = _.cloneDeep(db.comments[0])
+ comment.post = db.posts[0]
request(server)
.get('/comments/1?_expand=post')
.expect('Content-Type', /json/)
- .expect(comments)
+ .expect(comment)
.expect(200, done)
})
})
- describe('GET /:resource?_expand=&_expand', function () {
- it('should respond with corresponding resource and expanded inner resources', function (done) {
- var refs = _.cloneDeep(db.refs)
+ describe('GET /:resource?_expand=&_expand', () => {
+ it('should respond with corresponding resource and expanded inner resources', (done) => {
+ const refs = _.cloneDeep(db.refs)
refs[0].post = db.posts[0]
refs[0].user = db.users[0]
request(server)
@@ -449,9 +468,9 @@ describe('Server', function () {
})
})
- describe('GET /:resource/:id?_expand=&_expand=', function () {
- it('should respond with corresponding resource and expanded inner resources', function (done) {
- var comments = db.comments[0]
+ describe('GET /:resource/:id?_expand=&_expand=', () => {
+ it('should respond with corresponding resource and expanded inner resources', (done) => {
+ const comments = db.comments[0]
comments.post = db.posts[0]
comments.user = db.users[0]
request(server)
@@ -462,46 +481,49 @@ describe('Server', function () {
})
})
- describe('POST /:resource', function () {
+ describe('POST /:resource', () => {
it('should respond with json, create a resource and increment id',
- function (done) {
+ (done) => {
request(server)
.post('/posts')
- .send({body: 'foo', booleanValue: 'true', integerValue: '1'})
+ .send({body: 'foo', booleanValue: true, integerValue: 1})
.expect('Content-Type', /json/)
.expect({id: 3, body: 'foo', booleanValue: true, integerValue: 1})
.expect(201)
- .end(function (err, res) {
+ .end((err, res) => {
if (err) return done(err)
assert.equal(db.posts.length, 3)
done()
})
- })
+ }
+ )
it('should support x-www-form-urlencoded',
- function (done) {
+ (done) => {
request(server)
.post('/posts')
.type('form')
- .send({body: 'foo'})
+ .send({body: 'foo', booleanValue: true, integerValue: 1})
.expect('Content-Type', /json/)
- .expect({id: 3, body: 'foo'})
+ // x-www-form-urlencoded will convert to string
+ .expect({id: 3, body: 'foo', booleanValue: 'true', integerValue: '1'})
.expect(201)
- .end(function (err, res) {
+ .end((err, res) => {
if (err) return done(err)
assert.equal(db.posts.length, 3)
done()
})
- })
+ }
+ )
it('should respond with json, create a resource and generate string id',
- function (done) {
+ (done) => {
request(server)
.post('/refs')
.send({url: 'http://foo.com/', postId: '1'})
.expect('Content-Type', /json/)
.expect(201)
- .end(function (err, res) {
+ .end((err, res) => {
if (err) return done(err)
assert.equal(db.refs.length, 2)
done()
@@ -509,8 +531,8 @@ describe('Server', function () {
})
})
- describe('POST /:parent/:parentId/:resource', function () {
- it('should respond with json and set parentId', function (done) {
+ describe('POST /:parent/:parentId/:resource', () => {
+ it('should respond with json and set parentId', (done) => {
request(server)
.post('/posts/1/comments')
.send({body: 'foo'})
@@ -520,17 +542,17 @@ describe('Server', function () {
})
})
- describe('PUT /:resource/:id', function () {
- it('should respond with json and replace resource', function (done) {
+ describe('PUT /:resource/:id', () => {
+ it('should respond with json and replace resource', (done) => {
var post = {id: 1, booleanValue: true, integerValue: 1}
request(server)
.put('/posts/1')
// body property omitted to test that the resource is replaced
- .send({id: 1, booleanValue: 'true', integerValue: '1'})
+ .send(post)
.expect('Content-Type', /json/)
.expect(post)
.expect(200)
- .end(function (err, res) {
+ .end((err, res) => {
if (err) return done(err)
// assert it was created in database too
assert.deepEqual(db.posts[0], post)
@@ -538,25 +560,25 @@ describe('Server', function () {
})
})
- it('should respond with 404 if resource is not found', function (done) {
+ it('should respond with 404 if resource is not found', (done) => {
request(server)
.put('/posts/9001')
- .send({id: 1, body: 'bar', booleanValue: 'true', integerValue: '1'})
+ .send({id: 1, body: 'bar'})
.expect('Content-Type', /json/)
.expect({})
.expect(404, done)
})
})
- describe('PATCH /:resource/:id', function () {
- it('should respond with json and update resource', function (done) {
+ describe('PATCH /:resource/:id', () => {
+ it('should respond with json and update resource', (done) => {
request(server)
.patch('/posts/1')
.send({body: 'bar'})
.expect('Content-Type', /json/)
.expect({id: 1, body: 'bar'})
.expect(200)
- .end(function (err, res) {
+ .end((err, res) => {
if (err) return done(err)
// assert it was created in database too
assert.deepEqual(db.posts[0], {id: 1, body: 'bar'})
@@ -564,7 +586,7 @@ describe('Server', function () {
})
})
- it('should respond with 404 if resource is not found', function (done) {
+ it('should respond with 404 if resource is not found', (done) => {
request(server)
.patch('/posts/9001')
.send({body: 'bar'})
@@ -574,13 +596,13 @@ describe('Server', function () {
})
})
- describe('DELETE /:resource/:id', function () {
- it('should respond with empty data, destroy resource and dependent resources', function (done) {
+ describe('DELETE /:resource/:id', () => {
+ it('should respond with empty data, destroy resource and dependent resources', (done) => {
request(server)
.del('/posts/1')
.expect({})
.expect(200)
- .end(function (err, res) {
+ .end((err, res) => {
if (err) return done(err)
assert.equal(db.posts.length, 1)
assert.equal(db.comments.length, 3)
@@ -588,7 +610,7 @@ describe('Server', function () {
})
})
- it('should respond with 404 if resource is not found', function (done) {
+ it('should respond with 404 if resource is not found', (done) => {
request(server)
.del('/posts/9001')
.expect('Content-Type', /json/)
@@ -597,9 +619,9 @@ describe('Server', function () {
})
})
- describe('Static routes', function () {
- describe('GET /', function () {
- it('should respond with html', function (done) {
+ describe('Static routes', () => {
+ describe('GET /', () => {
+ it('should respond with html', (done) => {
request(server)
.get('/')
.expect(/You're successfully running JSON Server/)
@@ -607,8 +629,8 @@ describe('Server', function () {
})
})
- describe('GET /stylesheets/style.css', function () {
- it('should respond with css', function (done) {
+ describe('GET /stylesheets/style.css', () => {
+ it('should respond with css', (done) => {
request(server)
.get('/stylesheets/style.css')
.expect('Content-Type', /css/)
@@ -617,14 +639,14 @@ describe('Server', function () {
})
})
- describe('Database state', function () {
- it('should be accessible', function () {
+ describe('Database state', () => {
+ it('should be accessible', () => {
assert(router.db.getState())
})
})
- describe('Responses', function () {
- it('should have no cache headers (for IE)', function (done) {
+ describe('Responses', () => {
+ it('should have no cache headers (for IE)', (done) => {
request(server)
.get('/db')
.expect('Cache-Control', 'no-cache')
@@ -634,15 +656,15 @@ describe('Server', function () {
})
})
- describe('Rewriter', function () {
- it('should rewrite using prefix', function (done) {
+ describe('Rewriter', () => {
+ it('should rewrite using prefix', (done) => {
request(server)
.get('/api/posts/1')
.expect(db.posts[0])
.end(done)
})
- it('should rewrite using params', function (done) {
+ it('should rewrite using params', (done) => {
request(server)
.get('/blog/posts/1/show')
.expect(db.posts[0])
@@ -657,16 +679,16 @@ describe('Server', function () {
})
})
- describe('router.render', function (done) {
- beforeEach(function () {
- router.render = function (req, res) {
+ describe('router.render', (done) => {
+ beforeEach(() => {
+ router.render = (req, res) => {
res.jsonp({
data: res.locals.data
})
}
})
- it('should be possible to wrap response', function (done) {
+ it('should be possible to wrap response', (done) => {
request(server)
.get('/posts/1')
.expect('Content-Type', /json/)
@@ -675,8 +697,8 @@ describe('Server', function () {
})
})
- describe('router.db._.id', function (done) {
- beforeEach(function () {
+ describe('router.db._.id', (done) => {
+ beforeEach(() => {
router.db.setState({
posts: [
{ _id: 1 }
@@ -686,7 +708,7 @@ describe('Server', function () {
router.db._.id = '_id'
})
- it('should be possible to GET using a different id property', function (done) {
+ it('should be possible to GET using a different id property', (done) => {
request(server)
.get('/posts/1')
.expect('Content-Type', /json/)
@@ -694,12 +716,12 @@ describe('Server', function () {
.expect(200, done)
})
- it('should be possible to POST using a different id property', function (done) {
+ it('should be possible to POST using a different id property', (done) => {
request(server)
.post('/posts')
.send({ body: 'hello' })
.expect('Content-Type', /json/)
- .expect({_id: 2, body: 'hello'})
+ .expect({ _id: 2, body: 'hello' })
.expect(201, done)
})
})
diff --git a/test/server/singular.js b/test/server/singular.js
index 617bc3749..509a4785e 100644
--- a/test/server/singular.js
+++ b/test/server/singular.js
@@ -1,11 +1,10 @@
-var request = require('supertest')
-var jsonServer = require('../../src/server')
+const request = require('supertest')
+const jsonServer = require('../../src/server')
-/* global beforeEach, describe, it */
describe('Server', function () {
- var server
- var router
- var db
+ let server
+ let router
+ let db
beforeEach(function () {
db = {}
@@ -32,7 +31,7 @@ describe('Server', function () {
describe('POST /:resource', function () {
it('should create resource', function (done) {
- var user = { name: 'bar' }
+ const user = { name: 'bar' }
request(server)
.post('/user')
.send(user)
@@ -43,7 +42,7 @@ describe('Server', function () {
describe('PUT /:resource', function () {
it('should update resource', function (done) {
- var user = { name: 'bar' }
+ const user = { name: 'bar' }
request(server)
.put('/user')
.send(user)
diff --git a/test/server/utils.js b/test/server/utils.js
index 4036f58aa..a611db22e 100644
--- a/test/server/utils.js
+++ b/test/server/utils.js
@@ -1,30 +1,10 @@
-var assert = require('assert')
-var utils = require('../../src/server/utils')
-
-/* global describe, it */
+const assert = require('assert')
+const utils = require('../../src/server/utils')
describe('utils', function () {
- describe('toNative', function () {
- it('should convert string to native type', function () {
- // should convert
- assert.strictEqual(utils.toNative('1'), 1)
- assert.strictEqual(utils.toNative('0'), 0)
- assert.strictEqual(utils.toNative('true'), true)
- // should not convert
- assert.strictEqual(utils.toNative(''), '')
- assert.strictEqual(utils.toNative('\t\n'), '\t\n')
- assert.strictEqual(utils.toNative('1 '), '1 ')
- assert.strictEqual(utils.toNative('01'), '01')
- assert.strictEqual(utils.toNative(' 1'), ' 1')
- assert.strictEqual(utils.toNative('string'), 'string')
- assert.strictEqual(utils.toNative(1), 1)
- assert.strictEqual(utils.toNative(true), true)
- })
- })
-
describe('getPage', function () {
- var array = [1, 2, 3, 4, 5]
- var perPage = 2
+ const array = [1, 2, 3, 4, 5]
+ const perPage = 2
it('should return first page', function () {
assert.deepEqual(
diff --git a/test/server/validate-data.js b/test/server/validate-data.js
new file mode 100644
index 000000000..808721576
--- /dev/null
+++ b/test/server/validate-data.js
@@ -0,0 +1,24 @@
+const assert = require('assert')
+const validateData = require('../../src/server/router/validate-data')
+
+describe('validateData', () => {
+ it('should throw an error if data contains /', () => {
+ assert.throws(
+ () => validateData({ 'a/b': [] }),
+ /found \//
+ )
+ })
+
+ it('should throw an error if data is an array', () => {
+ assert.throws(
+ () => validateData([]),
+ /must be an object/
+ )
+ })
+
+ it('shouldn\'t throw an error', () => {
+ assert.doesNotThrow(
+ () => validateData({ a: [] })
+ )
+ })
+})
From 526ff59aff86d4477cbaed90e8bd248c0f793e37 Mon Sep 17 00:00:00 2001
From: typicode
Date: Sat, 12 Nov 2016 02:01:26 +0100
Subject: [PATCH 012/478] 0.9.0
---
package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/package.json b/package.json
index 9bab48524..42fe72337 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "json-server",
- "version": "0.9.0-beta.2",
+ "version": "0.9.0",
"description": "Serves JSON files through REST routes.",
"main": "./lib/server/index.js",
"bin": "./bin/index.js",
From d46cbd20f008a2b98ed775a605cee8a6e46559e3 Mon Sep 17 00:00:00 2001
From: typicode
Date: Sat, 12 Nov 2016 02:22:30 +0100
Subject: [PATCH 013/478] Add prepublish
---
package.json | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/package.json b/package.json
index 42fe72337..29deed0bb 100644
--- a/package.json
+++ b/package.json
@@ -49,7 +49,8 @@
"test:server": "cross-env NODE_ENV=test mocha test/server/*.js",
"start": "node bin",
"prepush": "npm t",
- "build": "babel src -d lib --copy-files"
+ "build": "babel src -d lib --copy-files",
+ "prepublish": "npm run build"
},
"repository": {
"type": "git",
From 1cb4e3e27ebd87ad94f7a8cabe13bab72c4dad85 Mon Sep 17 00:00:00 2001
From: typicode
Date: Sat, 12 Nov 2016 02:24:44 +0100
Subject: [PATCH 014/478] Update README.md
---
README.md | 10 ----------
1 file changed, 10 deletions(-)
diff --git a/README.md b/README.md
index 37c7db91a..e5df52995 100644
--- a/README.md
+++ b/README.md
@@ -51,16 +51,6 @@ Also when doing requests, its good to know that
$ npm install -g json-server
```
-### Beta
-
-To try [v0.9.0-beta](https://github.com/typicode/json-server/tree/next)
-
-```bash
-$ npm install -g json-server@next
-```
-
-
-
## Routes
Based on the previous `db.json` file, here are all the default routes. You can also add [other routes](#add-routes) using `--routes`.
From a42373eaf29e91f11217d16beb41a19452f54f73 Mon Sep 17 00:00:00 2001
From: typicode
Date: Sat, 12 Nov 2016 02:42:06 +0100
Subject: [PATCH 015/478] Add markdown-toc
---
package.json | 2 ++
1 file changed, 2 insertions(+)
diff --git a/package.json b/package.json
index 29deed0bb..7ebe09036 100644
--- a/package.json
+++ b/package.json
@@ -34,6 +34,7 @@
"babel-register": "^6.16.3",
"cross-env": "^2.0.1",
"husky": "^0.11.4",
+ "markdown-toc": "^0.13.0",
"mkdirp": "^0.5.1",
"mocha": "^3.1.2",
"os-tmpdir": "^1.0.1",
@@ -50,6 +51,7 @@
"start": "node bin",
"prepush": "npm t",
"build": "babel src -d lib --copy-files",
+ "toc": "markdown-toc -i README.md",
"prepublish": "npm run build"
},
"repository": {
From 4be7538412a7648509084fd83e8a862f1b104586 Mon Sep 17 00:00:00 2001
From: typicode
Date: Sat, 12 Nov 2016 02:42:29 +0100
Subject: [PATCH 016/478] Update README.md
---
README.md | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 51 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index e5df52995..f78223220 100644
--- a/README.md
+++ b/README.md
@@ -8,9 +8,57 @@ Created with <3 for front-end developers who need a quick back-end for prototypi
* [JSONPlaceholder - Live running version](http://jsonplaceholder.typicode.com)
See also:
-* :hotel: [hotel - Get local domains in seconds](https://github.com/typicode/hotel)
+* :hotel: [hotel - Start apps from your browser and get local dev domains in seconds](https://github.com/typicode/hotel)
* :dog: [husky - Git hooks made easy](https://github.com/typicode/husky)
+## Table of contents
+
+
+
+
+
+- [Example](#example)
+- [Install](#install)
+- [Routes](#routes)
+ * [Plural routes](#plural-routes)
+ * [Singular routes](#singular-routes)
+ * [Filter](#filter)
+ * [Paginate](#paginate)
+ * [Sort](#sort)
+ * [Slice](#slice)
+ * [Operators](#operators)
+ * [Full-text search](#full-text-search)
+ * [Relationships](#relationships)
+ * [Database](#database)
+ * [Homepage](#homepage)
+- [Extras](#extras)
+ * [Static file server](#static-file-server)
+ * [Alternative port](#alternative-port)
+ * [Access from anywhere](#access-from-anywhere)
+ * [Remote schema](#remote-schema)
+ * [Generate random data](#generate-random-data)
+ * [HTTPS](#https)
+ * [Add custom routes](#add-custom-routes)
+ * [Add middlewares](#add-middlewares)
+ * [CLI usage](#cli-usage)
+ * [Module](#module)
+ + [Simple example](#simple-example)
+ + [Custom routes example](#custom-routes-example)
+ + [Access control example](#access-control-example)
+ + [Custom output example](#custom-output-example)
+ + [Rewriter example](#rewriter-example)
+ + [Mounting JSON Server on another endpoint example](#mounting-json-server-on-another-endpoint-example)
+ * [Deployment](#deployment)
+- [Links](#links)
+ * [Video](#video)
+ * [Articles](#articles)
+ * [Third-party tools](#third-party-tools)
+- [License](#license)
+
+
+
+
+
## Example
Create a `db.json` file
@@ -39,7 +87,8 @@ Now if you go to [http://localhost:3000/posts/1](), you'll get
{ "id": 1, "title": "json-server", "author": "typicode" }
```
-Also when doing requests, its good to know that
+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 wil be ignored. Only a value set in a POST request wil be respected, but only if not already taken.
From b5eec153f7ca9551338e3b884c0d140f93cde42e Mon Sep 17 00:00:00 2001
From: typicode
Date: Thu, 17 Nov 2016 13:22:53 +0100
Subject: [PATCH 017/478] Update package.json
---
package.json | 3 +++
1 file changed, 3 insertions(+)
diff --git a/package.json b/package.json
index 7ebe09036..2fdee8d1d 100644
--- a/package.json
+++ b/package.json
@@ -85,5 +85,8 @@
"env": {
"mocha": true
}
+ },
+ "engines": {
+ "node": ">= 0.12"
}
}
From eca6b763f2b904e6c0739f14cd4f9eb04a7c2a27 Mon Sep 17 00:00:00 2001
From: Jan Trienes
Date: Mon, 21 Nov 2016 02:24:35 +0100
Subject: [PATCH 018/478] fix filter for boolean value false (#412) (#413)
---
src/server/router/plural.js | 2 +-
test/server/plural.js | 8 ++++++++
2 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/src/server/router/plural.js b/src/server/router/plural.js
index 24ecf850c..b949465b0 100644
--- a/src/server/router/plural.js
+++ b/src/server/router/plural.js
@@ -120,7 +120,7 @@ module.exports = (db, name) => {
const path = key.replace(/(_lte|_gte|_ne|_like)$/, '')
const elementValue = _.get(element, path)
- if (!elementValue) {
+ if (elementValue == null) {
return
}
diff --git a/test/server/plural.js b/test/server/plural.js
index 1cd47748c..fa0b751b9 100644
--- a/test/server/plural.js
+++ b/test/server/plural.js
@@ -194,6 +194,14 @@ describe('Server', () => {
.expect([ db.comments[3] ])
.expect(200, done)
})
+
+ it('should support filtering by boolean value false', (done) => {
+ request(server)
+ .get('/comments?published=false')
+ .expect('Content-Type', /json/)
+ .expect([ db.comments[1], db.comments[2], db.comments[4] ])
+ .expect(200, done)
+ })
})
describe('GET /:resource?_end=', () => {
From b5e4fcc9d2fe4167642fb4cf1f99532392374d5c Mon Sep 17 00:00:00 2001
From: typicode
Date: Mon, 21 Nov 2016 02:39:04 +0100
Subject: [PATCH 019/478] Update underscore-db dependency
---
package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/package.json b/package.json
index 2fdee8d1d..c807ef608 100644
--- a/package.json
+++ b/package.json
@@ -24,7 +24,7 @@
"request": "^2.72.0",
"server-destroy": "^1.0.1",
"shortid": "^2.2.6",
- "underscore-db": "^0.12.0",
+ "underscore-db": "^0.12.1",
"update-notifier": "^1.0.2",
"yargs": "^6.0.0"
},
From f54de70738d7f8a95892f7ba285facc3dd98a01a Mon Sep 17 00:00:00 2001
From: typicode
Date: Mon, 21 Nov 2016 02:39:29 +0100
Subject: [PATCH 020/478] Update test
---
src/server/router/plural.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/server/router/plural.js b/src/server/router/plural.js
index b949465b0..0c8b1173b 100644
--- a/src/server/router/plural.js
+++ b/src/server/router/plural.js
@@ -120,7 +120,7 @@ module.exports = (db, name) => {
const path = key.replace(/(_lte|_gte|_ne|_like)$/, '')
const elementValue = _.get(element, path)
- if (elementValue == null) {
+ if (elementValue === undefined) {
return
}
From 6051dde5f41cf58eb30e7154ea14cfafba23ee6a Mon Sep 17 00:00:00 2001
From: typicode
Date: Mon, 21 Nov 2016 02:43:18 +0100
Subject: [PATCH 021/478] Update CHANGELOG.md
---
CHANGELOG.md | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1b4cbfdd8..8e1505d97 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,10 @@
# Change Log
+## [0.9.1][2016-11-21]
+
+* Fix
+ * [#412](https://github.com/typicode/json-server/issues/412)
+
## [0.9.0][2016-11-11]
* Shorter `uuid`
From 05e147427df6d7c1f64ca17cf1247acb787dc44d Mon Sep 17 00:00:00 2001
From: typicode
Date: Mon, 21 Nov 2016 02:43:35 +0100
Subject: [PATCH 022/478] 0.9.1
---
package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/package.json b/package.json
index c807ef608..5781fd0bd 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "json-server",
- "version": "0.9.0",
+ "version": "0.9.1",
"description": "Serves JSON files through REST routes.",
"main": "./lib/server/index.js",
"bin": "./bin/index.js",
From 16577f1bfd937b95e29bb542cb51cae82c79757e Mon Sep 17 00:00:00 2001
From: typicode
Date: Mon, 21 Nov 2016 14:57:01 +0100
Subject: [PATCH 023/478] Update CHANGELOG.md
---
CHANGELOG.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8e1505d97..dea8a3c1b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,7 @@
* Fix
* [#412](https://github.com/typicode/json-server/issues/412)
+ * [#451]https://github.com/typicode/json-server/issues/411
## [0.9.0][2016-11-11]
From d3883f91430461827d4e5698d498770c1fb0120b Mon Sep 17 00:00:00 2001
From: typicode
Date: Mon, 21 Nov 2016 14:57:11 +0100
Subject: [PATCH 024/478] Update CHANGELOG.md
---
CHANGELOG.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index dea8a3c1b..8b90412d7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,7 +4,7 @@
* Fix
* [#412](https://github.com/typicode/json-server/issues/412)
- * [#451]https://github.com/typicode/json-server/issues/411
+ * [#451](https://github.com/typicode/json-server/issues/411)
## [0.9.0][2016-11-11]
From 6e75107638896430969dd3031bec4c0d823f9a0e Mon Sep 17 00:00:00 2001
From: typicode
Date: Tue, 22 Nov 2016 12:59:17 +0100
Subject: [PATCH 025/478] Update README.md
---
README.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/README.md b/README.md
index f78223220..4a9b8a73e 100644
--- a/README.md
+++ b/README.md
@@ -522,6 +522,7 @@ You can deploy JSON Server. For example, [JSONPlaceholder](http://jsonplaceholde
* [ng-admin: Add an AngularJS admin GUI to any RESTful API](http://marmelab.com/blog/2014/09/15/easy-backend-for-your-restful-api.html)
* [Fast prototyping using Restangular and Json-server](http://glebbahmutov.com/blog/fast-prototyping-using-restangular-and-json-server/)
* [Create a Mock REST API in Seconds for Prototyping your Frontend](https://coligo.io/create-mock-rest-api-with-json-server/)
+* [No API? No Problem! Rapid Development via Mock APIs](https://medium.com/@housecor/rapid-development-via-mock-apis-e559087be066#.93d7w8oro)
### Third-party tools
From edbb8603007ac57ed98155758844002b6b344e9e Mon Sep 17 00:00:00 2001
From: Sam Blowes
Date: Fri, 25 Nov 2016 14:56:20 +0000
Subject: [PATCH 026/478] Fix invalid HTML, Improve formatting (#422)
---
src/server/public/index.html | 18 ++++++++----------
1 file changed, 8 insertions(+), 10 deletions(-)
diff --git a/src/server/public/index.html b/src/server/public/index.html
index ecd16b493..bdac2a7ad 100644
--- a/src/server/public/index.html
+++ b/src/server/public/index.html
@@ -26,19 +26,17 @@ Routes
Here are the resources that JSON Server has loaded:
-
-
-
-
+
+
You can view database current state at any time:
-
-
+
+
You can use any HTTP verbs (GET, POST, PUT, PATCH and DELETE) and access your resources from anywhere
using CORS and JSONP.
From 3f355b3adabc3f62d4400e0f09906ce8fb32a267 Mon Sep 17 00:00:00 2001
From: typicode
Date: Sun, 27 Nov 2016 20:12:38 +0100
Subject: [PATCH 027/478] Check if file is not null when watching dir
---
src/cli/run.js | 38 ++++++++++++++++++++++----------------
1 file changed, 22 insertions(+), 16 deletions(-)
diff --git a/src/cli/run.js b/src/cli/run.js
index 648974c2d..1dc7fd1a4 100644
--- a/src/cli/run.js
+++ b/src/cli/run.js
@@ -185,21 +185,25 @@ module.exports = function (argv) {
// Since lowdb uses atomic writing, directory is watched instead of file
const watchedDir = path.dirname(source)
fs.watch(watchedDir, (event, file) => {
- const watchedFile = path.resolve(watchedDir, file)
- if (watchedFile === path.resolve(source)) {
- if (is.JSON(watchedFile)) {
- var obj = JSON.parse(fs.readFileSync(watchedFile))
- // Compare .json file content with in memory database
- var isDatabaseDifferent = !_.isEqual(obj, app.db.getState())
- if (isDatabaseDifferent) {
+ // https://github.com/typicode/json-server/issues/420
+ // file can be null
+ if (file) {
+ const watchedFile = path.resolve(watchedDir, file)
+ if (watchedFile === path.resolve(source)) {
+ if (is.JSON(watchedFile)) {
+ var obj = JSON.parse(fs.readFileSync(watchedFile))
+ // Compare .json file content with in memory database
+ var isDatabaseDifferent = !_.isEqual(obj, app.db.getState())
+ if (isDatabaseDifferent) {
+ console.log(chalk.gray(` ${source} has changed, reloading...`))
+ server && server.destroy()
+ start()
+ }
+ } else {
console.log(chalk.gray(` ${source} has changed, reloading...`))
server && server.destroy()
start()
}
- } else {
- console.log(chalk.gray(` ${source} has changed, reloading...`))
- server && server.destroy()
- start()
}
}
})
@@ -208,11 +212,13 @@ module.exports = function (argv) {
if (argv.routes) {
const watchedDir = path.dirname(argv.routes)
fs.watch(watchedDir, (event, file) => {
- const watchedFile = path.resolve(watchedDir, file)
- if (watchedFile === path.resolve(argv.routes)) {
- console.log(chalk.gray(` ${argv.routes} has changed, reloading...`))
- server && server.destroy()
- start()
+ if (file) {
+ const watchedFile = path.resolve(watchedDir, file)
+ if (watchedFile === path.resolve(argv.routes)) {
+ console.log(chalk.gray(` ${argv.routes} has changed, reloading...`))
+ server && server.destroy()
+ start()
+ }
}
})
}
From fdab5ee4d0cd4a21693b86631aeb3f93344f2999 Mon Sep 17 00:00:00 2001
From: typicode
Date: Mon, 28 Nov 2016 08:55:14 +0100
Subject: [PATCH 028/478] Create appveyor.yml
---
appveyor.yml | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
create mode 100644 appveyor.yml
diff --git a/appveyor.yml b/appveyor.yml
new file mode 100644
index 000000000..5fef32bbf
--- /dev/null
+++ b/appveyor.yml
@@ -0,0 +1,21 @@
+# Test against this version of Node.js
+environment:
+ nodejs_version: "7"
+
+# Install scripts. (runs after repo cloning)
+install:
+ # Get the latest stable version of Node.js
+ - ps: Install-Product node $env:nodejs_version
+ # install modules
+ - npm install
+
+# Post-install test scripts.
+test_script:
+ # Output useful info for debugging.
+ - node --version
+ - npm --version
+ # run tests
+ - npm test
+
+# Don't actually build.
+build: off
From 2ce7cd37b289e6e286667262f223939ecf46c954 Mon Sep 17 00:00:00 2001
From: Sam Blowes
Date: Mon, 28 Nov 2016 11:09:58 +0000
Subject: [PATCH 029/478] Fix broken links (#426)
---
README.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index 4a9b8a73e..945a9aaf4 100644
--- a/README.md
+++ b/README.md
@@ -102,7 +102,7 @@ $ npm install -g json-server
## Routes
-Based on the previous `db.json` file, here are all the default routes. You can also add [other routes](#add-routes) using `--routes`.
+Based on the previous `db.json` file, here are all the default routes. You can also add [other routes](#add-custom-routes) using `--routes`.
### Plural routes
@@ -211,7 +211,7 @@ GET /comments?_expand=post
GET /comments/1?_expand=post
```
-To get or create nested resources (by default one level, [add routes](#add-routes) for more)
+To get or create nested resources (by default one level, [add custom routes](#add-custom-routes) for more)
```
GET /posts/1/comments
From c527e1ef87ec79818c7c1376e8451676782e4f6e Mon Sep 17 00:00:00 2001
From: typicode
Date: Mon, 28 Nov 2016 13:30:43 +0100
Subject: [PATCH 030/478] Fix nohup issue
---
CHANGELOG.md | 5 +++++
src/cli/run.js | 6 +++++-
2 files changed, 10 insertions(+), 1 deletion(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8b90412d7..4179153a3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,10 @@
# Change Log
+## [Unreleased][Unreleased]
+
+* Fix [#221](https://github.com/typicode/json-server/issues/221) nohup support
+* Fix [#420](https://github.com/typicode/json-server/issues/420) TypeError when watching db.json
+
## [0.9.1][2016-11-21]
* Fix
diff --git a/src/cli/run.js b/src/cli/run.js
index 1dc7fd1a4..49a5fd3e9 100644
--- a/src/cli/run.js
+++ b/src/cli/run.js
@@ -160,7 +160,11 @@ module.exports = function (argv) {
chalk.gray(' Type s + enter at any time to create a snapshot of the database')
)
- process.stdin.resume()
+ // Support nohup
+ // https://github.com/typicode/json-server/issues/221
+ process.stdin.on('error', (err) => {
+ console.log(' Error, can\'t read from stdin')
+ })
process.stdin.setEncoding('utf8')
process.stdin.on('data', (chunk) => {
if (chunk.trim().toLowerCase() === 's') {
From b6b48faceea09a145da3e3d0b64567bce6f5b33a Mon Sep 17 00:00:00 2001
From: typicode
Date: Mon, 28 Nov 2016 13:33:29 +0100
Subject: [PATCH 031/478] Fix standard error
---
src/cli/run.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/cli/run.js b/src/cli/run.js
index 49a5fd3e9..92fcbd5c9 100644
--- a/src/cli/run.js
+++ b/src/cli/run.js
@@ -162,7 +162,7 @@ module.exports = function (argv) {
// Support nohup
// https://github.com/typicode/json-server/issues/221
- process.stdin.on('error', (err) => {
+ process.stdin.on('error', () => {
console.log(' Error, can\'t read from stdin')
})
process.stdin.setEncoding('utf8')
From cb1d474ac63690bd3db9fd76b142a4fe9d9d1b62 Mon Sep 17 00:00:00 2001
From: typicode
Date: Mon, 28 Nov 2016 14:07:30 +0100
Subject: [PATCH 032/478] Update run.js
---
src/cli/run.js | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/cli/run.js b/src/cli/run.js
index 92fcbd5c9..9c739097b 100644
--- a/src/cli/run.js
+++ b/src/cli/run.js
@@ -163,7 +163,8 @@ module.exports = function (argv) {
// Support nohup
// https://github.com/typicode/json-server/issues/221
process.stdin.on('error', () => {
- console.log(' Error, can\'t read from stdin')
+ console.log(` Error, can't read from stdin`)
+ console.log(` Creating a snapshot from the CLI won't be possible`)
})
process.stdin.setEncoding('utf8')
process.stdin.on('data', (chunk) => {
From 814bae7969b226f9cd69fecc59d99b8012323893 Mon Sep 17 00:00:00 2001
From: typicode
Date: Tue, 29 Nov 2016 08:54:28 +0100
Subject: [PATCH 033/478] 0.9.2
---
package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/package.json b/package.json
index 5781fd0bd..8886f1ceb 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "json-server",
- "version": "0.9.1",
+ "version": "0.9.2",
"description": "Serves JSON files through REST routes.",
"main": "./lib/server/index.js",
"bin": "./bin/index.js",
From 060d194b044835bf7eb5adfec0eca72ee9b193fe Mon Sep 17 00:00:00 2001
From: typicode
Date: Tue, 29 Nov 2016 07:56:14 +0100
Subject: [PATCH 034/478] Update CHANGELOG.md
---
CHANGELOG.md | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4179153a3..8e9fdbb9b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,9 +1,9 @@
# Change Log
-## [Unreleased][Unreleased]
+## [0.9.2][2016-11-29]
-* Fix [#221](https://github.com/typicode/json-server/issues/221) nohup support
-* Fix [#420](https://github.com/typicode/json-server/issues/420) TypeError when watching db.json
+* Fix [#221](https://github.com/typicode/json-server/issues/221) `nohup` support
+* Fix [#420](https://github.com/typicode/json-server/issues/420) TypeError when watching `db.json`
## [0.9.1][2016-11-21]
From 485378d22203778f839e4f3ff66072d4d6f157b6 Mon Sep 17 00:00:00 2001
From: zhangbiaoguang
Date: Tue, 29 Nov 2016 18:55:30 +0800
Subject: [PATCH 035/478] fixed the bug on windows: SyntaxError-Unexpected end
of input
---
src/cli/run.js | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/src/cli/run.js b/src/cli/run.js
index 648974c2d..ebdbd2c05 100644
--- a/src/cli/run.js
+++ b/src/cli/run.js
@@ -188,7 +188,14 @@ module.exports = function (argv) {
const watchedFile = path.resolve(watchedDir, file)
if (watchedFile === path.resolve(source)) {
if (is.JSON(watchedFile)) {
- var obj = JSON.parse(fs.readFileSync(watchedFile))
+ var obj = null;
+ try {
+ obj = JSON.parse(fs.readFileSync(watchedFile));
+ } catch (e) {
+ console.log('The format of the json file is not right, please check.');
+ console.dir(e);
+ return;
+ }
// Compare .json file content with in memory database
var isDatabaseDifferent = !_.isEqual(obj, app.db.getState())
if (isDatabaseDifferent) {
From f2f96514e5984962237103f0adc8f4d2e084922f Mon Sep 17 00:00:00 2001
From: typicode
Date: Tue, 6 Dec 2016 13:05:28 +0100
Subject: [PATCH 036/478] Update README.md
---
README.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/README.md b/README.md
index 945a9aaf4..1916c1aa2 100644
--- a/README.md
+++ b/README.md
@@ -530,6 +530,7 @@ You can deploy JSON Server. For example, [JSONPlaceholder](http://jsonplaceholde
* [Docker JSON Server](https://github.com/clue/docker-json-server)
* [JSON Server GUI](https://github.com/naholyr/json-server-gui)
* [JSON file generator](https://github.com/dfsq/json-server-init)
+* [JSON Server extension](https://github.com/maty21/json-server-extension)
## License
From 46a5259584fde5860f8c58b9c125947617cded76 Mon Sep 17 00:00:00 2001
From: typicode
Date: Tue, 6 Dec 2016 23:43:26 +0100
Subject: [PATCH 037/478] Add stricter tests
---
src/server/router/plural.js | 1 +
test/server/plural.js | 11 ++++++++---
2 files changed, 9 insertions(+), 3 deletions(-)
diff --git a/src/server/router/plural.js b/src/server/router/plural.js
index 0c8b1173b..42a5e03be 100644
--- a/src/server/router/plural.js
+++ b/src/server/router/plural.js
@@ -253,6 +253,7 @@ module.exports = (db, name) => {
function update (req, res, next) {
const id = req.params.id
let chain = db.get(name)
+ console.log(req.body)
chain = req.method === 'PATCH'
? chain.updateById(id, req.body)
diff --git a/test/server/plural.js b/test/server/plural.js
index fa0b751b9..d9993303d 100644
--- a/test/server/plural.js
+++ b/test/server/plural.js
@@ -551,10 +551,11 @@ describe('Server', () => {
})
describe('PUT /:resource/:id', () => {
- it('should respond with json and replace resource', (done) => {
+ it.only('should respond with json and replace resource', (done) => {
var post = {id: 1, booleanValue: true, integerValue: 1}
request(server)
.put('/posts/1')
+ .set('Accept', 'application/json')
// body property omitted to test that the resource is replaced
.send(post)
.expect('Content-Type', /json/)
@@ -562,8 +563,11 @@ describe('Server', () => {
.expect(200)
.end((err, res) => {
if (err) return done(err)
+ // TODO find a "supertest" way to test this
+ // https://github.com/typicode/json-server/issues/396
+ assert.deepStrictEqual(res.body, post)
// assert it was created in database too
- assert.deepEqual(db.posts[0], post)
+ assert.deepStrictEqual(db.posts[0], post)
done()
})
})
@@ -588,8 +592,9 @@ describe('Server', () => {
.expect(200)
.end((err, res) => {
if (err) return done(err)
+ assert.deepStrictEqual(res.body, post)
// assert it was created in database too
- assert.deepEqual(db.posts[0], {id: 1, body: 'bar'})
+ assert.deepStrictEqual(db.posts[0], {id: 1, body: 'bar'})
done()
})
})
From 3df272dbe331a0ea5703b724717b1c9df1955af1 Mon Sep 17 00:00:00 2001
From: typicode
Date: Tue, 6 Dec 2016 23:49:21 +0100
Subject: [PATCH 038/478] Update package.json
---
package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/package.json b/package.json
index 8886f1ceb..be3d73a6e 100644
--- a/package.json
+++ b/package.json
@@ -24,7 +24,7 @@
"request": "^2.72.0",
"server-destroy": "^1.0.1",
"shortid": "^2.2.6",
- "underscore-db": "^0.12.1",
+ "underscore-db": "^0.12.2",
"update-notifier": "^1.0.2",
"yargs": "^6.0.0"
},
From d0596e1386214d4211a30700e951e209ead852b2 Mon Sep 17 00:00:00 2001
From: typicode
Date: Tue, 6 Dec 2016 23:51:01 +0100
Subject: [PATCH 039/478] clean
---
src/server/router/plural.js | 1 -
test/server/plural.js | 2 +-
2 files changed, 1 insertion(+), 2 deletions(-)
diff --git a/src/server/router/plural.js b/src/server/router/plural.js
index 42a5e03be..0c8b1173b 100644
--- a/src/server/router/plural.js
+++ b/src/server/router/plural.js
@@ -253,7 +253,6 @@ module.exports = (db, name) => {
function update (req, res, next) {
const id = req.params.id
let chain = db.get(name)
- console.log(req.body)
chain = req.method === 'PATCH'
? chain.updateById(id, req.body)
diff --git a/test/server/plural.js b/test/server/plural.js
index d9993303d..6408796e1 100644
--- a/test/server/plural.js
+++ b/test/server/plural.js
@@ -551,7 +551,7 @@ describe('Server', () => {
})
describe('PUT /:resource/:id', () => {
- it.only('should respond with json and replace resource', (done) => {
+ it('should respond with json and replace resource', (done) => {
var post = {id: 1, booleanValue: true, integerValue: 1}
request(server)
.put('/posts/1')
From 24b2c9f1b6c8db28950a619000d84faf9c4b2df8 Mon Sep 17 00:00:00 2001
From: typicode
Date: Tue, 6 Dec 2016 23:52:26 +0100
Subject: [PATCH 040/478] Update CHANGELOG.md
---
CHANGELOG.md | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8e9fdbb9b..2ac401867 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,9 @@
# Change Log
+## [0.9.3][unreleased]
+
+* Fix [#396](https://github.com/typicode/json-server/issues/396)
+
## [0.9.2][2016-11-29]
* Fix [#221](https://github.com/typicode/json-server/issues/221) `nohup` support
From d6528665a13b9a8e925337b3ea2c8159e0cbb693 Mon Sep 17 00:00:00 2001
From: typicode
Date: Wed, 7 Dec 2016 09:28:33 +0100
Subject: [PATCH 041/478] Update CHANGELOG.md
---
CHANGELOG.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2ac401867..89ce05272 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,8 +1,8 @@
# Change Log
-## [0.9.3][unreleased]
+## [0.9.3][2016-12-07]
-* Fix [#396](https://github.com/typicode/json-server/issues/396)
+* Fix [#396](https://github.com/typicode/json-server/issues/396) PUT/PATCH saves the updated item with an id that has been converted to string
## [0.9.2][2016-11-29]
From 84493e07db7d5b9a165c3075fdd4a61127fc9f20 Mon Sep 17 00:00:00 2001
From: typicode
Date: Wed, 7 Dec 2016 09:28:52 +0100
Subject: [PATCH 042/478] 0.9.3
---
package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/package.json b/package.json
index be3d73a6e..c6216dfc4 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "json-server",
- "version": "0.9.2",
+ "version": "0.9.3",
"description": "Serves JSON files through REST routes.",
"main": "./lib/server/index.js",
"bin": "./bin/index.js",
From b2bcabb71e273a2b655d71a89a9a4b5378b73450 Mon Sep 17 00:00:00 2001
From: typicode
Date: Wed, 7 Dec 2016 09:39:44 +0100
Subject: [PATCH 043/478] Fix PATCH test
---
test/server/plural.js | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/test/server/plural.js b/test/server/plural.js
index 6408796e1..9a0918b04 100644
--- a/test/server/plural.js
+++ b/test/server/plural.js
@@ -584,17 +584,19 @@ describe('Server', () => {
describe('PATCH /:resource/:id', () => {
it('should respond with json and update resource', (done) => {
+ var partial = {body: 'bar'}
+ var post = {id: 1, body: 'bar'}
request(server)
.patch('/posts/1')
- .send({body: 'bar'})
+ .send(partial)
.expect('Content-Type', /json/)
- .expect({id: 1, body: 'bar'})
+ .expect(post)
.expect(200)
.end((err, res) => {
if (err) return done(err)
assert.deepStrictEqual(res.body, post)
// assert it was created in database too
- assert.deepStrictEqual(db.posts[0], {id: 1, body: 'bar'})
+ assert.deepStrictEqual(db.posts[0], post)
done()
})
})
From 427e6add1fc0bcc8d9f27dd00ba28e587d9cd89a Mon Sep 17 00:00:00 2001
From: typicode
Date: Wed, 7 Dec 2016 09:41:42 +0100
Subject: [PATCH 044/478] 0.9.4
---
package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/package.json b/package.json
index c6216dfc4..8527da58c 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "json-server",
- "version": "0.9.3",
+ "version": "0.9.4",
"description": "Serves JSON files through REST routes.",
"main": "./lib/server/index.js",
"bin": "./bin/index.js",
From 7c402a855665713c131dd14e72dc25eb0a62f417 Mon Sep 17 00:00:00 2001
From: typicode
Date: Wed, 7 Dec 2016 09:43:34 +0100
Subject: [PATCH 045/478] Update package.json
---
package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/package.json b/package.json
index 8527da58c..c6216dfc4 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "json-server",
- "version": "0.9.4",
+ "version": "0.9.3",
"description": "Serves JSON files through REST routes.",
"main": "./lib/server/index.js",
"bin": "./bin/index.js",
From 202a4160980361e025e952c4b8f27a1404f24d5a Mon Sep 17 00:00:00 2001
From: Ahmed Ayoub
Date: Thu, 8 Dec 2016 22:53:16 +0100
Subject: [PATCH 046/478] update req.query when rewrite without params (#432)
* update req.query when rewrite without params #431
* add tests for rewrite rules with query and without paramas for PR #432
* [typo] add tests for rewrite rules with query and without paramas for PR #432
---
src/server/rewriter.js | 10 +++++-----
test/server/plural.js | 13 ++++++++++++-
2 files changed, 17 insertions(+), 6 deletions(-)
diff --git a/src/server/rewriter.js b/src/server/rewriter.js
index 718220f37..a48e91069 100644
--- a/src/server/rewriter.js
+++ b/src/server/rewriter.js
@@ -1,7 +1,9 @@
const express = require('express')
const url = require('url')
const _ = require('lodash')
-
+function updateQueryString(target,sourceUrl) {
+ return !!~sourceUrl.indexOf('?') ? _.assign(target, url.parse(sourceUrl, true).query) : {};
+}
module.exports = (routes) => {
const router = express.Router()
@@ -14,16 +16,14 @@ module.exports = (routes) => {
target = target.replace(':' + param, req.params[param])
}
req.url = target
- if (target.indexOf('?')) {
- // create query from target
- _.assign(req.query, url.parse(target, true).query)
- }
+ req.query = updateQueryString(req.query,req.url)
next()
})
} else {
router.all(route + '*', (req, res, next) => {
// Rewrite url by replacing prefix
req.url = req.url.replace(route, routes[route])
+ req.query = updateQueryString(req.query,req.url)
next()
})
}
diff --git a/test/server/plural.js b/test/server/plural.js
index 9a0918b04..25103b67d 100644
--- a/test/server/plural.js
+++ b/test/server/plural.js
@@ -78,7 +78,9 @@ describe('Server', () => {
server.use(jsonServer.rewriter({
'/api/': '/',
'/blog/posts/:id/show': '/posts/:id',
- '/comments/special/:userId-:body': '/comments/?userId=:userId&body=:body'
+ '/comments/special/:userId-:body': '/comments/?userId=:userId&body=:body',
+ '/firstpostwithcomments': '/posts/1?_embed=comments'
+
}))
server.use(router)
})
@@ -686,6 +688,15 @@ describe('Server', () => {
.end(done)
})
+ it('should rewrite using query without params', function (done) {
+ const expectedPost = _.cloneDeep(db.posts[0])
+ expectedPost.comments = [ db.comments[0], db.comments[1] ]
+ request(server)
+ .get('/firstpostwithcomments')
+ .expect(expectedPost)
+ .end(done)
+ })
+
it('should rewrite using params and query', function (done) {
request(server)
.get('/comments/special/1-quux')
From 760c37d288dfbe3ba4a18b24036de1eb31324292 Mon Sep 17 00:00:00 2001
From: typicode
Date: Thu, 8 Dec 2016 22:55:35 +0100
Subject: [PATCH 047/478] Update CHANGELOG.md
---
CHANGELOG.md | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 89ce05272..a87051abc 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,9 @@
# Change Log
+## [0.9.4][2016-12-08]
+
+* Improve rewriter [#431](https://github.com/typicode/json-server/issues/431)
+
## [0.9.3][2016-12-07]
* Fix [#396](https://github.com/typicode/json-server/issues/396) PUT/PATCH saves the updated item with an id that has been converted to string
From 0bac0352587ff58ac494b6390840f4309a75de82 Mon Sep 17 00:00:00 2001
From: typicode
Date: Thu, 8 Dec 2016 23:29:43 +0100
Subject: [PATCH 048/478] Update
---
package.json | 1 +
src/cli/run.js | 22 ++++++++++++++--------
src/server/rewriter.js | 8 ++++----
3 files changed, 19 insertions(+), 12 deletions(-)
diff --git a/package.json b/package.json
index c6216dfc4..d574c5179 100644
--- a/package.json
+++ b/package.json
@@ -15,6 +15,7 @@
"cors": "^2.3.0",
"errorhandler": "^1.2.0",
"express": "^4.9.5",
+ "json-parse-helpfulerror": "^1.0.3",
"lodash": "^4.11.2",
"lowdb": "^0.14.0",
"method-override": "^2.1.2",
diff --git a/src/cli/run.js b/src/cli/run.js
index fc78cfd22..a67d7303b 100644
--- a/src/cli/run.js
+++ b/src/cli/run.js
@@ -1,5 +1,6 @@
const fs = require('fs')
const path = require('path')
+const jph = require('json-parse-helpfulerror')
const _ = require('lodash')
const chalk = require('chalk')
const enableDestroy = require('server-destroy')
@@ -189,25 +190,30 @@ module.exports = function (argv) {
// Watch .js or .json file
// Since lowdb uses atomic writing, directory is watched instead of file
const watchedDir = path.dirname(source)
+ let readError = false
fs.watch(watchedDir, (event, file) => {
-<<<<<<< HEAD
// https://github.com/typicode/json-server/issues/420
// file can be null
if (file) {
const watchedFile = path.resolve(watchedDir, file)
if (watchedFile === path.resolve(source)) {
if (is.JSON(watchedFile)) {
- var obj
+ let obj
try {
- obj = JSON.parse(fs.readFileSync(watchedFile))
+ obj = jph.parse(fs.readFileSync(watchedFile))
+ if (readError) {
+ console.log(chalk.green(` Read error has been fixed :)`))
+ readError = false
+ }
} catch (e) {
- console.log('Error reading JSON file');
- console.dir(e);
- return;
+ readError = true
+ console.log(chalk.red(` Error reading ${watchedFile}`))
+ console.error(e.message)
+ return
}
-
+
// Compare .json file content with in memory database
- var isDatabaseDifferent = !_.isEqual(obj, app.db.getState())
+ const isDatabaseDifferent = !_.isEqual(obj, app.db.getState())
if (isDatabaseDifferent) {
console.log(chalk.gray(` ${source} has changed, reloading...`))
server && server.destroy()
diff --git a/src/server/rewriter.js b/src/server/rewriter.js
index a48e91069..e94ceb90e 100644
--- a/src/server/rewriter.js
+++ b/src/server/rewriter.js
@@ -1,8 +1,8 @@
const express = require('express')
const url = require('url')
const _ = require('lodash')
-function updateQueryString(target,sourceUrl) {
- return !!~sourceUrl.indexOf('?') ? _.assign(target, url.parse(sourceUrl, true).query) : {};
+function updateQueryString (target, sourceUrl) {
+ return ~sourceUrl.indexOf('?') ? _.assign(target, url.parse(sourceUrl, true).query) : {}
}
module.exports = (routes) => {
const router = express.Router()
@@ -16,14 +16,14 @@ module.exports = (routes) => {
target = target.replace(':' + param, req.params[param])
}
req.url = target
- req.query = updateQueryString(req.query,req.url)
+ req.query = updateQueryString(req.query, req.url)
next()
})
} else {
router.all(route + '*', (req, res, next) => {
// Rewrite url by replacing prefix
req.url = req.url.replace(route, routes[route])
- req.query = updateQueryString(req.query,req.url)
+ req.query = updateQueryString(req.query, req.url)
next()
})
}
From 2960b273376772b2479f488cf6915cad155e975a Mon Sep 17 00:00:00 2001
From: typicode
Date: Thu, 8 Dec 2016 23:30:13 +0100
Subject: [PATCH 049/478] Update
---
.gitignore | 1 +
1 file changed, 1 insertion(+)
diff --git a/.gitignore b/.gitignore
index 0c475f8ea..306cc31e5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,3 +4,4 @@ tmp
lib
.DS_Store
.idea
+db.json
\ No newline at end of file
From 47e76434103b87cf3550650b01bcca25fd11dbe5 Mon Sep 17 00:00:00 2001
From: typicode
Date: Thu, 8 Dec 2016 23:41:52 +0100
Subject: [PATCH 050/478] Update CHANGELOG.md
---
CHANGELOG.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a87051abc..0c99e38ca 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,7 @@
## [0.9.4][2016-12-08]
* Improve rewriter [#431](https://github.com/typicode/json-server/issues/431)
+* Improve watch mode [#427](https://github.com/typicode/json-server/pull/427)
## [0.9.3][2016-12-07]
From 442746efcc5c7fb9cb6453f649e33c7a256260ff Mon Sep 17 00:00:00 2001
From: typicode
Date: Thu, 8 Dec 2016 23:47:41 +0100
Subject: [PATCH 051/478] 0.9.4
---
package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/package.json b/package.json
index d574c5179..fa5c23b5f 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "json-server",
- "version": "0.9.3",
+ "version": "0.9.4",
"description": "Serves JSON files through REST routes.",
"main": "./lib/server/index.js",
"bin": "./bin/index.js",
From bb64e719b71a048d1f8a03e9724a167cb8d6abb5 Mon Sep 17 00:00:00 2001
From: typicode
Date: Sun, 25 Dec 2016 07:44:36 +0100
Subject: [PATCH 052/478] Update rewriter
---
src/server/rewriter.js | 4 ++++
test/server/plural.js | 21 ++++++++++++++-------
2 files changed, 18 insertions(+), 7 deletions(-)
diff --git a/src/server/rewriter.js b/src/server/rewriter.js
index e94ceb90e..d7f3c7471 100644
--- a/src/server/rewriter.js
+++ b/src/server/rewriter.js
@@ -7,6 +7,10 @@ function updateQueryString (target, sourceUrl) {
module.exports = (routes) => {
const router = express.Router()
+ router.get('/__rules', (req, res) => {
+ res.json(routes)
+ })
+
Object.keys(routes).forEach((route) => {
if (route.indexOf(':') !== -1) {
router.all(route, (req, res, next) => {
diff --git a/test/server/plural.js b/test/server/plural.js
index 25103b67d..8a70ec8a4 100644
--- a/test/server/plural.js
+++ b/test/server/plural.js
@@ -7,6 +7,12 @@ describe('Server', () => {
let server
let router
let db
+ const rewriterRules = {
+ '/api/': '/',
+ '/blog/posts/:id/show': '/posts/:id',
+ '/comments/special/:userId-:body': '/comments/?userId=:userId&body=:body',
+ '/firstpostwithcomments': '/posts/1?_embed=comments'
+ }
beforeEach(() => {
db = {}
@@ -75,13 +81,7 @@ describe('Server', () => {
server = jsonServer.create()
router = jsonServer.router(db)
server.use(jsonServer.defaults())
- server.use(jsonServer.rewriter({
- '/api/': '/',
- '/blog/posts/:id/show': '/posts/:id',
- '/comments/special/:userId-:body': '/comments/?userId=:userId&body=:body',
- '/firstpostwithcomments': '/posts/1?_embed=comments'
-
- }))
+ server.use(jsonServer.rewriter(rewriterRules))
server.use(router)
})
@@ -703,6 +703,13 @@ describe('Server', () => {
.expect([db.comments[4]])
.end(done)
})
+
+ it('should expose routes', (done) => {
+ request(server)
+ .get('/__rules')
+ .expect(rewriterRules)
+ .end(done)
+ })
})
describe('router.render', (done) => {
From ad9cbdd1ae4c68c1909c4d938285d1d3fc9d5d0d Mon Sep 17 00:00:00 2001
From: typicode
Date: Sun, 25 Dec 2016 18:49:09 +0100
Subject: [PATCH 053/478] Add failing test
---
test/server/plural.js | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/test/server/plural.js b/test/server/plural.js
index 8a70ec8a4..a144ca617 100644
--- a/test/server/plural.js
+++ b/test/server/plural.js
@@ -11,7 +11,8 @@ describe('Server', () => {
'/api/': '/',
'/blog/posts/:id/show': '/posts/:id',
'/comments/special/:userId-:body': '/comments/?userId=:userId&body=:body',
- '/firstpostwithcomments': '/posts/1?_embed=comments'
+ '/firstpostwithcomments': '/posts/1?_embed=comments',
+ '/articles?_id=:id': '/posts/:id'
}
beforeEach(() => {
@@ -704,6 +705,14 @@ describe('Server', () => {
.end(done)
})
+ // TODO
+ // it('should rewrite query params', (done) => {
+ // request(server)
+ // .get('/articles?_id=1')
+ // .expect(db.posts[0])
+ // .end(done)
+ // })
+
it('should expose routes', (done) => {
request(server)
.get('/__rules')
From 69321be00fe85d026a88e7f30d464bd51afcee2f Mon Sep 17 00:00:00 2001
From: typicode
Date: Sun, 25 Dec 2016 19:12:38 +0100
Subject: [PATCH 054/478] Display custom routes
---
package.json | 2 +-
src/server/public/index.html | 29 ++++++++++++++++++++++-------
2 files changed, 23 insertions(+), 8 deletions(-)
diff --git a/package.json b/package.json
index fa5c23b5f..6ef35be00 100644
--- a/package.json
+++ b/package.json
@@ -49,7 +49,7 @@
"test": "npm run test:cli && npm run test:server && standard --fix",
"test:cli": "npm run build && cross-env NODE_ENV=test mocha test/cli/*.js",
"test:server": "cross-env NODE_ENV=test mocha test/server/*.js",
- "start": "node bin",
+ "start": "babel-node src/cli/bin",
"prepush": "npm t",
"build": "babel src -d lib --copy-files",
"toc": "markdown-toc -i README.md",
diff --git a/src/server/public/index.html b/src/server/public/index.html
index bdac2a7ad..83a644b90 100644
--- a/src/server/public/index.html
+++ b/src/server/public/index.html
@@ -27,7 +27,9 @@ Routes
Here are the resources that JSON Server has loaded:
-
+
+
+
You can view database current state at any time:
@@ -36,10 +38,10 @@ Routes
db
-
+
You can use any HTTP verbs (GET, POST, PUT, PATCH and DELETE) and access your resources from anywhere
- using CORS and JSONP.
+ using CORS or JSONP.
Documentation
@@ -61,17 +63,30 @@ Issues
-
-
+
+