json-server 0.9.4 → 0.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.babelrc +6 -1
- package/.travis.yml +1 -1
- package/CHANGELOG.md +22 -2
- package/README.md +44 -31
- package/db.json +4 -15
- package/lib/cli/index.js +1 -1
- package/lib/cli/run.js +59 -63
- package/lib/cli/utils/is.js +3 -3
- package/lib/cli/utils/load.js +1 -1
- package/lib/server/mixins.js +4 -4
- package/lib/server/public/index.html +46 -72
- package/lib/server/public/main.js +39 -0
- package/lib/server/public/style.css +73 -0
- package/lib/server/rewriter.js +4 -0
- package/lib/server/router/get-full-url.js +12 -0
- package/lib/server/router/index.js +7 -9
- package/lib/server/router/nested.js +4 -4
- package/lib/server/router/plural.js +52 -53
- package/lib/server/router/singular.js +9 -1
- package/lib/server/router/validate-data.js +2 -4
- package/lib/server/router/write.js +8 -0
- package/lib/server/utils.js +1 -1
- package/package.json +14 -13
- package/routes.json +5 -0
- package/test/mocha.opts +2 -2
- package/test/server/mixins.js +2 -2
- package/test/server/plural.js +280 -248
- package/test/server/singular.js +18 -18
- package/test/server/utils.js +8 -8
- package/lib/server/public/stylesheets/style.css +0 -16
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
html {
|
|
2
|
+
font-size: 70%;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
body {
|
|
6
|
+
display: flex;
|
|
7
|
+
min-height: 100vh;
|
|
8
|
+
flex-direction: column;
|
|
9
|
+
padding:0;
|
|
10
|
+
margin: 0;
|
|
11
|
+
color: #333;
|
|
12
|
+
letter-spacing: 0;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
header {
|
|
16
|
+
padding-top: 2.0rem;
|
|
17
|
+
border-bottom: 1px solid #EEE;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
header a, header a:hover {
|
|
21
|
+
text-decoration: none;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
main {
|
|
25
|
+
flex: 1;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
footer {
|
|
29
|
+
padding-top: 2.5rem;
|
|
30
|
+
border-top: 1px solid #EEE;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
h4 {
|
|
34
|
+
margin-top: 4rem;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
a {
|
|
38
|
+
color: #0275d8;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
a:hover {
|
|
42
|
+
color: #014c8c;
|
|
43
|
+
text-decoration: underline;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
table {
|
|
47
|
+
margin-left: 0;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
td {
|
|
51
|
+
border: 0;
|
|
52
|
+
padding: 0 1em .5em 0;
|
|
53
|
+
color: #014c8c;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
td:first-child {
|
|
57
|
+
width: 1%;
|
|
58
|
+
white-space: nowrap;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
ul {
|
|
62
|
+
list-style-position: inside;
|
|
63
|
+
padding-left: 0;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
li {
|
|
67
|
+
list-style-type: none;
|
|
68
|
+
margin-bottom: .2rem;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
code {
|
|
72
|
+
border-radius: 0;
|
|
73
|
+
}
|
package/lib/server/rewriter.js
CHANGED
|
@@ -9,6 +9,10 @@ function updateQueryString(target, sourceUrl) {
|
|
|
9
9
|
module.exports = function (routes) {
|
|
10
10
|
var router = express.Router();
|
|
11
11
|
|
|
12
|
+
router.get('/__rules', function (req, res) {
|
|
13
|
+
res.json(routes);
|
|
14
|
+
});
|
|
15
|
+
|
|
12
16
|
Object.keys(routes).forEach(function (route) {
|
|
13
17
|
if (route.indexOf(':') !== -1) {
|
|
14
18
|
router.all(route, function (req, res, next) {
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
|
|
4
|
-
|
|
5
3
|
var express = require('express');
|
|
6
4
|
var methodOverride = require('method-override');
|
|
7
5
|
var _ = require('lodash');
|
|
8
|
-
var
|
|
6
|
+
var lodashId = require('lodash-id');
|
|
9
7
|
var low = require('lowdb');
|
|
10
|
-
var fileAsync = require('lowdb/lib/file-async');
|
|
8
|
+
var fileAsync = require('lowdb/lib/storages/file-async');
|
|
11
9
|
var bodyParser = require('../body-parser');
|
|
12
10
|
var validateData = require('./validate-data');
|
|
13
11
|
var plural = require('./plural');
|
|
@@ -34,8 +32,8 @@ module.exports = function (source) {
|
|
|
34
32
|
|
|
35
33
|
validateData(db.getState());
|
|
36
34
|
|
|
37
|
-
// Add
|
|
38
|
-
db._.mixin(
|
|
35
|
+
// Add lodash-id methods to db
|
|
36
|
+
db._.mixin(lodashId);
|
|
39
37
|
|
|
40
38
|
// Add specific mixins
|
|
41
39
|
db._.mixin(mixins);
|
|
@@ -59,16 +57,16 @@ module.exports = function (source) {
|
|
|
59
57
|
// Create routes
|
|
60
58
|
db.forEach(function (value, key) {
|
|
61
59
|
if (_.isPlainObject(value)) {
|
|
62
|
-
router.use(
|
|
60
|
+
router.use(`/${key}`, singular(db, key));
|
|
63
61
|
return;
|
|
64
62
|
}
|
|
65
63
|
|
|
66
64
|
if (_.isArray(value)) {
|
|
67
|
-
router.use(
|
|
65
|
+
router.use(`/${key}`, plural(db, key));
|
|
68
66
|
return;
|
|
69
67
|
}
|
|
70
68
|
|
|
71
|
-
var msg =
|
|
69
|
+
var msg = `Type of "${key}" (${typeof value}) ` + (_.isObject(source) ? '' : `in ${source}`) + ' is not supported. ' + 'Use objects or arrays of objects.';
|
|
72
70
|
|
|
73
71
|
throw new Error(msg);
|
|
74
72
|
}).value();
|
|
@@ -9,16 +9,16 @@ module.exports = function () {
|
|
|
9
9
|
// Rewrite URL (/:resource/:id/:nested -> /:nested) and request query
|
|
10
10
|
function get(req, res, next) {
|
|
11
11
|
var prop = pluralize.singular(req.params.resource);
|
|
12
|
-
req.query[prop
|
|
13
|
-
req.url =
|
|
12
|
+
req.query[`${prop}Id`] = req.params.id;
|
|
13
|
+
req.url = `/${req.params.nested}`;
|
|
14
14
|
next();
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
// Rewrite URL (/:resource/:id/:nested -> /:nested) and request body
|
|
18
18
|
function post(req, res, next) {
|
|
19
19
|
var prop = pluralize.singular(req.params.resource);
|
|
20
|
-
req.body[prop
|
|
21
|
-
req.url =
|
|
20
|
+
req.body[`${prop}Id`] = req.params.id;
|
|
21
|
+
req.url = `/${req.params.nested}`;
|
|
22
22
|
next();
|
|
23
23
|
}
|
|
24
24
|
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var url = require('url');
|
|
4
3
|
var express = require('express');
|
|
5
4
|
var _ = require('lodash');
|
|
6
5
|
var pluralize = require('pluralize');
|
|
6
|
+
var write = require('./write');
|
|
7
|
+
var getFullURL = require('./get-full-url');
|
|
7
8
|
var utils = require('../utils');
|
|
8
9
|
|
|
9
10
|
module.exports = function (db, name) {
|
|
@@ -16,7 +17,7 @@ module.exports = function (db, name) {
|
|
|
16
17
|
if (db.get(externalResource).value) {
|
|
17
18
|
var query = {};
|
|
18
19
|
var singularResource = pluralize.singular(name);
|
|
19
|
-
query[singularResource
|
|
20
|
+
query[`${singularResource}Id`] = resource.id;
|
|
20
21
|
resource[externalResource] = db.get(externalResource).filter(query).value();
|
|
21
22
|
}
|
|
22
23
|
});
|
|
@@ -27,21 +28,12 @@ module.exports = function (db, name) {
|
|
|
27
28
|
e && [].concat(e).forEach(function (innerResource) {
|
|
28
29
|
var plural = pluralize(innerResource);
|
|
29
30
|
if (db.get(plural).value()) {
|
|
30
|
-
var prop = innerResource
|
|
31
|
+
var prop = `${innerResource}Id`;
|
|
31
32
|
resource[innerResource] = db.get(plural).getById(resource[prop]).value();
|
|
32
33
|
}
|
|
33
34
|
});
|
|
34
35
|
}
|
|
35
36
|
|
|
36
|
-
function getFullURL(req) {
|
|
37
|
-
var root = url.format({
|
|
38
|
-
protocol: req.protocol,
|
|
39
|
-
host: req.get('host')
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
return '' + root + req.originalUrl;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
37
|
// GET /name
|
|
46
38
|
// GET /name?q=
|
|
47
39
|
// GET /name?attr=&attr=
|
|
@@ -84,6 +76,10 @@ module.exports = function (db, name) {
|
|
|
84
76
|
|
|
85
77
|
if (q) {
|
|
86
78
|
// Full-text search
|
|
79
|
+
if (Array.isArray(q)) {
|
|
80
|
+
q = q[0];
|
|
81
|
+
}
|
|
82
|
+
|
|
87
83
|
q = q.toLowerCase();
|
|
88
84
|
|
|
89
85
|
chain = chain.filter(function (obj) {
|
|
@@ -100,52 +96,49 @@ module.exports = function (db, name) {
|
|
|
100
96
|
// Don't take into account JSONP query parameters
|
|
101
97
|
// jQuery adds a '_' query parameter too
|
|
102
98
|
if (key !== 'callback' && key !== '_') {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
99
|
+
// Always use an array, in case req.query is an array
|
|
100
|
+
var arr = [].concat(req.query[key]);
|
|
101
|
+
|
|
102
|
+
chain = chain.filter(function (element) {
|
|
103
|
+
return arr.map(function (value) {
|
|
104
|
+
var isDifferent = /_ne$/.test(key);
|
|
105
|
+
var isRange = /_lte$/.test(key) || /_gte$/.test(key);
|
|
106
|
+
var isLike = /_like$/.test(key);
|
|
107
|
+
var path = key.replace(/(_lte|_gte|_ne|_like)$/, '');
|
|
108
|
+
// get item value based on path
|
|
109
|
+
// i.e post.title -> 'foo'
|
|
110
|
+
var elementValue = _.get(element, path);
|
|
111
|
+
|
|
112
|
+
// Prevent toString() failing on undefined or null values
|
|
113
|
+
if (elementValue === undefined || elementValue === null) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (isRange) {
|
|
118
|
+
var isLowerThan = /_gte$/.test(key);
|
|
119
|
+
|
|
120
|
+
return isLowerThan ? value <= elementValue : value >= elementValue;
|
|
121
|
+
} else if (isDifferent) {
|
|
122
|
+
return value !== elementValue.toString();
|
|
123
|
+
} else if (isLike) {
|
|
124
|
+
return new RegExp(value, 'i').test(elementValue.toString());
|
|
125
|
+
} else {
|
|
126
|
+
return value === elementValue.toString();
|
|
127
|
+
}
|
|
128
|
+
}).reduce(function (a, b) {
|
|
129
|
+
return a || b;
|
|
133
130
|
});
|
|
134
|
-
})
|
|
131
|
+
});
|
|
135
132
|
}
|
|
136
133
|
});
|
|
137
134
|
|
|
138
135
|
// Sort
|
|
139
136
|
if (_sort) {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
return _.get(element, _sort);
|
|
137
|
+
var _sortSet = _sort.split(',');
|
|
138
|
+
var _orderSet = (_order || '').split(',').map(function (s) {
|
|
139
|
+
return s.toLowerCase();
|
|
144
140
|
});
|
|
145
|
-
|
|
146
|
-
if (_order === 'DESC') {
|
|
147
|
-
chain = chain.reverse();
|
|
148
|
-
}
|
|
141
|
+
chain = chain.orderBy(_sortSet, _orderSet);
|
|
149
142
|
}
|
|
150
143
|
|
|
151
144
|
// Slice result
|
|
@@ -229,8 +222,12 @@ module.exports = function (db, name) {
|
|
|
229
222
|
function create(req, res, next) {
|
|
230
223
|
var resource = db.get(name).insert(req.body).value();
|
|
231
224
|
|
|
225
|
+
res.setHeader('Access-Control-Expose-Headers', 'Location');
|
|
226
|
+
res.location(`${getFullURL(req)}/${resource.id}`);
|
|
227
|
+
|
|
232
228
|
res.status(201);
|
|
233
229
|
res.locals.data = resource;
|
|
230
|
+
|
|
234
231
|
next();
|
|
235
232
|
}
|
|
236
233
|
|
|
@@ -269,9 +266,11 @@ module.exports = function (db, name) {
|
|
|
269
266
|
next();
|
|
270
267
|
}
|
|
271
268
|
|
|
272
|
-
|
|
269
|
+
var w = write(db);
|
|
270
|
+
|
|
271
|
+
router.route('/').get(list).post(create, w);
|
|
273
272
|
|
|
274
|
-
router.route('/:id').get(show).put(update).patch(update).delete(destroy);
|
|
273
|
+
router.route('/:id').get(show).put(update, w).patch(update, w).delete(destroy, w);
|
|
275
274
|
|
|
276
275
|
return router;
|
|
277
276
|
};
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var express = require('express');
|
|
4
|
+
var write = require('./write');
|
|
5
|
+
var getFullURL = require('./get-full-url');
|
|
4
6
|
|
|
5
7
|
module.exports = function (db, name) {
|
|
6
8
|
var router = express.Router();
|
|
@@ -13,6 +15,10 @@ module.exports = function (db, name) {
|
|
|
13
15
|
function create(req, res, next) {
|
|
14
16
|
db.set(name, req.body).value();
|
|
15
17
|
res.locals.data = db.get(name).value();
|
|
18
|
+
|
|
19
|
+
res.setHeader('Access-Control-Expose-Headers', 'Location');
|
|
20
|
+
res.location(`${getFullURL(req)}`);
|
|
21
|
+
|
|
16
22
|
res.status(201);
|
|
17
23
|
next();
|
|
18
24
|
}
|
|
@@ -28,7 +34,9 @@ module.exports = function (db, name) {
|
|
|
28
34
|
next();
|
|
29
35
|
}
|
|
30
36
|
|
|
31
|
-
|
|
37
|
+
var w = write(db);
|
|
38
|
+
|
|
39
|
+
router.route('/').get(show).post(create, w).put(update, w).patch(update, w);
|
|
32
40
|
|
|
33
41
|
return router;
|
|
34
42
|
};
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
|
|
4
|
-
|
|
5
3
|
var _ = require('lodash');
|
|
6
4
|
|
|
7
5
|
function validateKey(key) {
|
|
8
6
|
if (key.indexOf('/') !== -1) {
|
|
9
|
-
var msg = [
|
|
7
|
+
var 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/#add-custom-routes'].join('\n');
|
|
10
8
|
throw new Error(msg);
|
|
11
9
|
}
|
|
12
10
|
}
|
|
@@ -15,6 +13,6 @@ module.exports = function (obj) {
|
|
|
15
13
|
if (_.isPlainObject(obj)) {
|
|
16
14
|
Object.keys(obj).forEach(validateKey);
|
|
17
15
|
} else {
|
|
18
|
-
throw new Error(
|
|
16
|
+
throw new Error(`Data must be an object. Found ${typeof obj}.` + 'See https://github.com/typicode/json-server for example.');
|
|
19
17
|
}
|
|
20
18
|
};
|
package/lib/server/utils.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "json-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.1",
|
|
4
4
|
"description": "Serves JSON files through REST routes.",
|
|
5
5
|
"main": "./lib/server/index.js",
|
|
6
6
|
"bin": "./bin/index.js",
|
|
@@ -17,7 +17,8 @@
|
|
|
17
17
|
"express": "^4.9.5",
|
|
18
18
|
"json-parse-helpfulerror": "^1.0.3",
|
|
19
19
|
"lodash": "^4.11.2",
|
|
20
|
-
"
|
|
20
|
+
"lodash-id": "^0.13.0",
|
|
21
|
+
"lowdb": "^0.15.0",
|
|
21
22
|
"method-override": "^2.1.2",
|
|
22
23
|
"morgan": "^1.3.1",
|
|
23
24
|
"object-assign": "^4.0.1",
|
|
@@ -25,35 +26,35 @@
|
|
|
25
26
|
"request": "^2.72.0",
|
|
26
27
|
"server-destroy": "^1.0.1",
|
|
27
28
|
"shortid": "^2.2.6",
|
|
28
|
-
"underscore-db": "^0.12.2",
|
|
29
29
|
"update-notifier": "^1.0.2",
|
|
30
30
|
"yargs": "^6.0.0"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
|
-
"babel-cli": "^6.
|
|
34
|
-
"babel-preset-
|
|
33
|
+
"babel-cli": "^6.24.0",
|
|
34
|
+
"babel-preset-env": "^1.3.2",
|
|
35
35
|
"babel-register": "^6.16.3",
|
|
36
36
|
"cross-env": "^2.0.1",
|
|
37
|
-
"husky": "^0.
|
|
37
|
+
"husky": "^0.13.0",
|
|
38
38
|
"markdown-toc": "^0.13.0",
|
|
39
39
|
"mkdirp": "^0.5.1",
|
|
40
|
-
"mocha": "^3.
|
|
40
|
+
"mocha": "^3.2.0",
|
|
41
41
|
"os-tmpdir": "^1.0.1",
|
|
42
|
+
"pkg-ok": "^1.0.1",
|
|
42
43
|
"rimraf": "^2.5.2",
|
|
43
44
|
"server-ready": "^0.3.1",
|
|
44
45
|
"standard": "^8.3.0",
|
|
45
|
-
"supertest": "^
|
|
46
|
+
"supertest": "^3.0.0",
|
|
46
47
|
"temp-write": "^2.1.0"
|
|
47
48
|
},
|
|
48
49
|
"scripts": {
|
|
49
|
-
"test": "npm run test:cli && npm run test:server && standard
|
|
50
|
+
"test": "npm run test:cli && npm run test:server && standard",
|
|
50
51
|
"test:cli": "npm run build && cross-env NODE_ENV=test mocha test/cli/*.js",
|
|
51
52
|
"test:server": "cross-env NODE_ENV=test mocha test/server/*.js",
|
|
52
|
-
"start": "node bin",
|
|
53
|
-
"
|
|
53
|
+
"start": "babel-node src/cli/bin",
|
|
54
|
+
"precommit": "npm test",
|
|
54
55
|
"build": "babel src -d lib --copy-files",
|
|
55
56
|
"toc": "markdown-toc -i README.md",
|
|
56
|
-
"prepublish": "npm run build"
|
|
57
|
+
"prepublish": "npm run build && pkg-ok"
|
|
57
58
|
},
|
|
58
59
|
"repository": {
|
|
59
60
|
"type": "git",
|
|
@@ -88,6 +89,6 @@
|
|
|
88
89
|
}
|
|
89
90
|
},
|
|
90
91
|
"engines": {
|
|
91
|
-
"node": ">=
|
|
92
|
+
"node": ">= 4"
|
|
92
93
|
}
|
|
93
94
|
}
|
package/routes.json
ADDED
package/test/mocha.opts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
--
|
|
1
|
+
--require babel-register
|
|
2
2
|
--reporter spec
|
|
3
|
-
--timeout 5000
|
|
3
|
+
--timeout 5000
|
package/test/server/mixins.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
const assert = require('assert')
|
|
2
2
|
const _ = require('lodash')
|
|
3
|
-
const
|
|
3
|
+
const lodashId = require('lodash-id')
|
|
4
4
|
const mixins = require('../../src/server/mixins')
|
|
5
5
|
|
|
6
6
|
describe('mixins', () => {
|
|
7
7
|
let db
|
|
8
8
|
|
|
9
9
|
before(() => {
|
|
10
|
-
_.mixin(
|
|
10
|
+
_.mixin(lodashId)
|
|
11
11
|
_.mixin(mixins)
|
|
12
12
|
})
|
|
13
13
|
|