json-server 0.13.0 → 0.15.0

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.
@@ -1,31 +1,39 @@
1
- 'use strict';
1
+ "use strict";
2
2
 
3
3
  const nanoid = require('nanoid');
4
+
4
5
  const pluralize = require('pluralize');
5
6
 
6
7
  module.exports = {
7
8
  getRemovable,
8
9
  createId,
9
- deepQuery
10
-
11
- // Returns document ids that have unsatisfied relations
10
+ deepQuery // Returns document ids that have unsatisfied relations
12
11
  // Example: a comment that references a post that doesn't exist
13
- };function getRemovable(db, opts) {
12
+
13
+ };
14
+
15
+ function getRemovable(db, opts) {
14
16
  const _ = this;
17
+
15
18
  const removable = [];
19
+
16
20
  _.each(db, (coll, collName) => {
17
21
  _.each(coll, doc => {
18
22
  _.each(doc, (value, key) => {
19
23
  if (new RegExp(`${opts.foreignKeySuffix}$`).test(key)) {
20
24
  // Remove foreign key suffix and pluralize it
21
25
  // Example postId -> posts
22
- const refName = pluralize.plural(key.replace(new RegExp(`${opts.foreignKeySuffix}$`), ''));
23
- // Test if table exists
26
+ const refName = pluralize.plural(key.replace(new RegExp(`${opts.foreignKeySuffix}$`), '')); // Test if table exists
27
+
24
28
  if (db[refName]) {
25
29
  // Test if references is defined in table
26
30
  const ref = _.getById(db[refName], value);
31
+
27
32
  if (_.isUndefined(ref)) {
28
- removable.push({ name: collName, id: doc.id });
33
+ removable.push({
34
+ name: collName,
35
+ id: doc.id
36
+ });
29
37
  }
30
38
  }
31
39
  }
@@ -34,25 +42,28 @@ module.exports = {
34
42
  });
35
43
 
36
44
  return removable;
37
- }
38
-
39
- // Return incremented id or uuid
45
+ } // Return incremented id or uuid
40
46
  // Used to override lodash-id's createId with utils.createId
47
+
48
+
41
49
  function createId(coll) {
42
50
  const _ = this;
51
+
43
52
  const idProperty = _.__id();
53
+
44
54
  if (_.isEmpty(coll)) {
45
55
  return 1;
46
56
  } else {
47
- let id = _(coll).maxBy(idProperty)[idProperty];
57
+ let id = _(coll).maxBy(idProperty)[idProperty]; // Increment integer id or generate string id
58
+
48
59
 
49
- // Increment integer id or generate string id
50
60
  return _.isFinite(id) ? ++id : nanoid(7);
51
61
  }
52
62
  }
53
63
 
54
64
  function deepQuery(value, q) {
55
65
  const _ = this;
66
+
56
67
  if (value && q) {
57
68
  if (_.isArray(value)) {
58
69
  for (let i = 0; i < value.length; i++) {
@@ -1,18 +1,16 @@
1
- 'use strict';
1
+ "use strict";
2
2
 
3
3
  const express = require('express');
4
+
4
5
  const rewrite = require('express-urlrewrite');
5
6
 
6
7
  module.exports = routes => {
7
8
  const router = express.Router();
8
-
9
9
  router.get('/__rules', (req, res) => {
10
10
  res.json(routes);
11
11
  });
12
-
13
12
  Object.keys(routes).forEach(key => {
14
13
  router.use(rewrite(key, routes[key]));
15
14
  });
16
-
17
15
  return router;
18
16
  };
@@ -1,4 +1,4 @@
1
- 'use strict';
1
+ "use strict";
2
2
 
3
3
  const pause = require('connect-pause');
4
4
 
@@ -6,6 +6,7 @@ module.exports = function delay(req, res, next) {
6
6
  // NOTE: for some reason unknown to me, if the default is 0, the tests seems to add 2 seconds
7
7
  // NOTE: to each test, a default value of 1 does not seem to be effected by that issue
8
8
  const _delay = !isNaN(parseFloat(req.query._delay)) ? parseFloat(req.query._delay) : 1;
9
+
9
10
  delete req.query._delay;
10
11
  pause(_delay)(req, res, next);
11
12
  };
@@ -1,4 +1,4 @@
1
- 'use strict';
1
+ "use strict";
2
2
 
3
3
  const url = require('url');
4
4
 
@@ -7,6 +7,5 @@ module.exports = function getFullURL(req) {
7
7
  protocol: req.protocol,
8
8
  host: req.get('host')
9
9
  });
10
-
11
10
  return `${root}${req.originalUrl}`;
12
11
  };
@@ -1,63 +1,70 @@
1
- 'use strict';
1
+ "use strict";
2
2
 
3
3
  const express = require('express');
4
+
4
5
  const methodOverride = require('method-override');
6
+
5
7
  const _ = require('lodash');
8
+
6
9
  const lodashId = require('lodash-id');
10
+
7
11
  const low = require('lowdb');
8
- const fileAsync = require('lowdb/lib/storages/file-async');
12
+
13
+ const Memory = require('lowdb/adapters/Memory');
14
+
15
+ const FileSync = require('lowdb/adapters/FileSync');
16
+
9
17
  const bodyParser = require('../body-parser');
18
+
10
19
  const validateData = require('./validate-data');
20
+
11
21
  const plural = require('./plural');
22
+
12
23
  const nested = require('./nested');
24
+
13
25
  const singular = require('./singular');
26
+
14
27
  const mixins = require('../mixins');
15
28
 
16
- module.exports = (source, opts = { foreignKeySuffix: 'Id' }) => {
17
- // Create router
18
- const router = express.Router();
29
+ module.exports = (db, opts = {
30
+ foreignKeySuffix: 'Id',
31
+ _isFake: false
32
+ }) => {
33
+ if (typeof db === 'string') {
34
+ db = low(new FileSync(db));
35
+ } else if (!_.has(db, '__chain__') || !_.has(db, '__wrapped__')) {
36
+ db = low(new Memory()).setState(db);
37
+ } // Create router
38
+
39
+
40
+ const router = express.Router(); // Add middlewares
19
41
 
20
- // Add middlewares
21
42
  router.use(methodOverride());
22
43
  router.use(bodyParser);
44
+ validateData(db.getState()); // Add lodash-id methods to db
23
45
 
24
- // Create database
25
- let db;
26
- if (_.isObject(source)) {
27
- db = low();
28
- db.setState(source);
29
- } else {
30
- db = low(source, { storage: fileAsync });
31
- }
46
+ db._.mixin(lodashId); // Add specific mixins
32
47
 
33
- validateData(db.getState());
34
48
 
35
- // Add lodash-id methods to db
36
- db._.mixin(lodashId);
49
+ db._.mixin(mixins); // Expose database
37
50
 
38
- // Add specific mixins
39
- db._.mixin(mixins);
40
51
 
41
- // Expose database
42
- router.db = db;
52
+ router.db = db; // Expose render
43
53
 
44
- // Expose render
45
54
  router.render = (req, res) => {
46
55
  res.jsonp(res.locals.data);
47
- };
56
+ }; // GET /db
57
+
48
58
 
49
- // GET /db
50
59
  router.get('/db', (req, res) => {
51
60
  res.jsonp(db.getState());
52
- });
61
+ }); // Handle /:parent/:parentId/:resource
53
62
 
54
- // Handle /:parent/:parentId/:resource
55
- router.use(nested(opts));
63
+ router.use(nested(opts)); // Create routes
56
64
 
57
- // Create routes
58
65
  db.forEach((value, key) => {
59
66
  if (_.isPlainObject(value)) {
60
- router.use(`/${key}`, singular(db, key));
67
+ router.use(`/${key}`, singular(db, key, opts));
61
68
  return;
62
69
  }
63
70
 
@@ -66,16 +73,13 @@ module.exports = (source, opts = { foreignKeySuffix: 'Id' }) => {
66
73
  return;
67
74
  }
68
75
 
69
- var sourceMessage = '';
70
- if (!_.isObject(source)) {
71
- sourceMessage = `in ${source}`;
72
- }
76
+ var sourceMessage = ''; // if (!_.isObject(source)) {
77
+ // sourceMessage = `in ${source}`
78
+ // }
73
79
 
74
80
  const msg = `Type of "${key}" (${typeof value}) ${sourceMessage} is not supported. ` + `Use objects or arrays of objects.`;
75
-
76
81
  throw new Error(msg);
77
82
  }).value();
78
-
79
83
  router.use((req, res) => {
80
84
  if (!res.locals.data) {
81
85
  res.status(404);
@@ -84,11 +88,9 @@ module.exports = (source, opts = { foreignKeySuffix: 'Id' }) => {
84
88
 
85
89
  router.render(req, res);
86
90
  });
87
-
88
91
  router.use((err, req, res, next) => {
89
92
  console.error(err.stack);
90
93
  res.status(500).send(err.stack);
91
94
  });
92
-
93
95
  return router;
94
96
  };
@@ -1,22 +1,23 @@
1
- 'use strict';
1
+ "use strict";
2
2
 
3
3
  const express = require('express');
4
+
4
5
  const pluralize = require('pluralize');
6
+
5
7
  const delay = require('./delay');
6
8
 
7
9
  module.exports = opts => {
8
10
  const router = express.Router();
9
- router.use(delay);
11
+ router.use(delay); // Rewrite URL (/:resource/:id/:nested -> /:nested) and request query
10
12
 
11
- // Rewrite URL (/:resource/:id/:nested -> /:nested) and request query
12
13
  function get(req, res, next) {
13
14
  const prop = pluralize.singular(req.params.resource);
14
15
  req.query[`${prop}${opts.foreignKeySuffix}`] = req.params.id;
15
16
  req.url = `/${req.params.nested}`;
16
17
  next();
17
- }
18
+ } // Rewrite URL (/:resource/:id/:nested -> /:nested) and request body
19
+
18
20
 
19
- // Rewrite URL (/:resource/:id/:nested -> /:nested) and request body
20
21
  function post(req, res, next) {
21
22
  const prop = pluralize.singular(req.params.resource);
22
23
  req.body[`${prop}${opts.foreignKeySuffix}`] = req.params.id;
@@ -1,19 +1,28 @@
1
- 'use strict';
1
+ "use strict";
2
+
3
+ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } return target; }
4
+
5
+ function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
2
6
 
3
7
  const express = require('express');
8
+
4
9
  const _ = require('lodash');
10
+
5
11
  const pluralize = require('pluralize');
12
+
6
13
  const write = require('./write');
14
+
7
15
  const getFullURL = require('./get-full-url');
16
+
8
17
  const utils = require('../utils');
18
+
9
19
  const delay = require('./delay');
10
20
 
11
21
  module.exports = (db, name, opts) => {
12
22
  // Create router
13
23
  const router = express.Router();
14
- router.use(delay);
24
+ router.use(delay); // Embed function used in GET /name and GET /name/id
15
25
 
16
- // Embed function used in GET /name and GET /name/id
17
26
  function embed(resource, e) {
18
27
  e && [].concat(e).forEach(externalResource => {
19
28
  if (db.get(externalResource).value) {
@@ -23,31 +32,31 @@ module.exports = (db, name, opts) => {
23
32
  resource[externalResource] = db.get(externalResource).filter(query).value();
24
33
  }
25
34
  });
26
- }
35
+ } // Expand function used in GET /name and GET /name/id
36
+
27
37
 
28
- // Expand function used in GET /name and GET /name/id
29
38
  function expand(resource, e) {
30
39
  e && [].concat(e).forEach(innerResource => {
31
40
  const plural = pluralize(innerResource);
41
+
32
42
  if (db.get(plural).value()) {
33
43
  const prop = `${innerResource}${opts.foreignKeySuffix}`;
34
44
  resource[innerResource] = db.get(plural).getById(resource[prop]).value();
35
45
  }
36
46
  });
37
- }
38
-
39
- // GET /name
47
+ } // GET /name
40
48
  // GET /name?q=
41
49
  // GET /name?attr=&attr=
42
50
  // GET /name?_end=&
43
51
  // GET /name?_start=&_end=&
44
52
  // GET /name?_embed=&_expand=
53
+
54
+
45
55
  function list(req, res, next) {
46
56
  // Resource chain
47
- let chain = db.get(name);
48
-
49
- // Remove q, _start, _end, ... from req.query to avoid filtering using those
57
+ let chain = db.get(name); // Remove q, _start, _end, ... from req.query to avoid filtering using those
50
58
  // parameters
59
+
51
60
  let q = req.query.q;
52
61
  let _start = req.query._start;
53
62
  let _end = req.query._end;
@@ -64,15 +73,16 @@ module.exports = (db, name, opts) => {
64
73
  delete req.query._order;
65
74
  delete req.query._limit;
66
75
  delete req.query._embed;
67
- delete req.query._expand;
68
-
69
- // Automatically delete query parameters that can't be found
76
+ delete req.query._expand; // Automatically delete query parameters that can't be found
70
77
  // in the database
78
+
71
79
  Object.keys(req.query).forEach(query => {
72
80
  const arr = db.get(name).value();
81
+
73
82
  for (let i in arr) {
74
83
  if (_.has(arr[i], query) || query === 'callback' || query === '_' || /_lte$/.test(query) || /_gte$/.test(query) || /_ne$/.test(query) || /_like$/.test(query)) return;
75
84
  }
85
+
76
86
  delete req.query[query];
77
87
  });
78
88
 
@@ -83,10 +93,10 @@ module.exports = (db, name, opts) => {
83
93
  }
84
94
 
85
95
  q = q.toLowerCase();
86
-
87
96
  chain = chain.filter(obj => {
88
97
  for (let key in obj) {
89
98
  const value = obj[key];
99
+
90
100
  if (db._.deepQuery(value, q)) {
91
101
  return true;
92
102
  }
@@ -100,25 +110,23 @@ module.exports = (db, name, opts) => {
100
110
  if (key !== 'callback' && key !== '_') {
101
111
  // Always use an array, in case req.query is an array
102
112
  const arr = [].concat(req.query[key]);
103
-
104
113
  chain = chain.filter(element => {
105
114
  return arr.map(function (value) {
106
115
  const isDifferent = /_ne$/.test(key);
107
116
  const isRange = /_lte$/.test(key) || /_gte$/.test(key);
108
117
  const isLike = /_like$/.test(key);
109
- const path = key.replace(/(_lte|_gte|_ne|_like)$/, '');
110
- // get item value based on path
118
+ const path = key.replace(/(_lte|_gte|_ne|_like)$/, ''); // get item value based on path
111
119
  // i.e post.title -> 'foo'
112
- const elementValue = _.get(element, path);
113
120
 
114
- // Prevent toString() failing on undefined or null values
121
+ const elementValue = _.get(element, path); // Prevent toString() failing on undefined or null values
122
+
123
+
115
124
  if (elementValue === undefined || elementValue === null) {
116
125
  return;
117
126
  }
118
127
 
119
128
  if (isRange) {
120
129
  const isLowerThan = /_gte$/.test(key);
121
-
122
130
  return isLowerThan ? value <= elementValue : value >= elementValue;
123
131
  } else if (isDifferent) {
124
132
  return value !== elementValue.toString();
@@ -130,16 +138,17 @@ module.exports = (db, name, opts) => {
130
138
  }).reduce((a, b) => a || b);
131
139
  });
132
140
  }
133
- });
141
+ }); // Sort
134
142
 
135
- // Sort
136
143
  if (_sort) {
137
144
  const _sortSet = _sort.split(',');
145
+
138
146
  const _orderSet = (_order || '').split(',').map(s => s.toLowerCase());
147
+
139
148
  chain = chain.orderBy(_sortSet, _orderSet);
140
- }
149
+ } // Slice result
150
+
141
151
 
142
- // Slice result
143
152
  if (_end || _limit || _page) {
144
153
  res.setHeader('X-Total-Count', chain.size());
145
154
  res.setHeader('Access-Control-Expose-Headers', `X-Total-Count${_page ? ', Link' : ''}`);
@@ -179,20 +188,19 @@ module.exports = (db, name, opts) => {
179
188
  _start = parseInt(_start, 10) || 0;
180
189
  _limit = parseInt(_limit, 10);
181
190
  chain = chain.slice(_start, _start + _limit);
182
- }
191
+ } // embed and expand
192
+
183
193
 
184
- // embed and expand
185
194
  chain = chain.cloneDeep().forEach(function (element) {
186
195
  embed(element, _embed);
187
196
  expand(element, _expand);
188
197
  });
189
-
190
198
  res.locals.data = chain.value();
191
199
  next();
192
- }
193
-
194
- // GET /name/:id
200
+ } // GET /name/:id
195
201
  // GET /name/:id?_embed=&_expand
202
+
203
+
196
204
  function show(req, res, next) {
197
205
  const _embed = req.query._embed;
198
206
  const _expand = req.query._expand;
@@ -200,61 +208,84 @@ module.exports = (db, name, opts) => {
200
208
 
201
209
  if (resource) {
202
210
  // Clone resource to avoid making changes to the underlying object
203
- const clone = _.cloneDeep(resource);
204
-
205
- // Embed other resources based on resource id
211
+ const clone = _.cloneDeep(resource); // Embed other resources based on resource id
206
212
  // /posts/1?_embed=comments
207
- embed(clone, _embed);
208
213
 
209
- // Expand inner resources based on id
214
+
215
+ embed(clone, _embed); // Expand inner resources based on id
210
216
  // /posts/1?_expand=user
211
- expand(clone, _expand);
212
217
 
218
+ expand(clone, _expand);
213
219
  res.locals.data = clone;
214
220
  }
215
221
 
216
222
  next();
217
- }
223
+ } // POST /name
224
+
218
225
 
219
- // POST /name
220
226
  function create(req, res, next) {
221
- const resource = db.get(name).insert(req.body).value();
227
+ let resource;
228
+
229
+ if (opts._isFake) {
230
+ const id = db.get(name).createId().value();
231
+ resource = _objectSpread({}, req.body, {
232
+ id
233
+ });
234
+ } else {
235
+ resource = db.get(name).insert(req.body).value();
236
+ }
222
237
 
223
238
  res.setHeader('Access-Control-Expose-Headers', 'Location');
224
239
  res.location(`${getFullURL(req)}/${resource.id}`);
225
-
226
240
  res.status(201);
227
241
  res.locals.data = resource;
228
-
229
242
  next();
230
- }
231
-
232
- // PUT /name/:id
243
+ } // PUT /name/:id
233
244
  // PATCH /name/:id
245
+
246
+
234
247
  function update(req, res, next) {
235
248
  const id = req.params.id;
236
- let chain = db.get(name);
249
+ let resource;
237
250
 
238
- chain = req.method === 'PATCH' ? chain.updateById(id, req.body) : chain.replaceById(id, req.body);
251
+ if (opts._isFake) {
252
+ resource = db.get(name).getById(id).value();
239
253
 
240
- const resource = chain.value();
254
+ if (req.method === 'PATCH') {
255
+ resource = _objectSpread({}, resource, req.body);
256
+ } else {
257
+ resource = _objectSpread({}, req.body, {
258
+ id: resource.id
259
+ });
260
+ }
261
+ } else {
262
+ let chain = db.get(name);
263
+ chain = req.method === 'PATCH' ? chain.updateById(id, req.body) : chain.replaceById(id, req.body);
264
+ resource = chain.value();
265
+ }
241
266
 
242
267
  if (resource) {
243
268
  res.locals.data = resource;
244
269
  }
245
270
 
246
271
  next();
247
- }
272
+ } // DELETE /name/:id
273
+
248
274
 
249
- // DELETE /name/:id
250
275
  function destroy(req, res, next) {
251
- const resource = db.get(name).removeById(req.params.id).value();
276
+ let resource;
252
277
 
253
- // Remove dependents documents
254
- const removable = db._.getRemovable(db.getState(), opts);
255
- removable.forEach(item => {
256
- db.get(item.name).removeById(item.id).value();
257
- });
278
+ if (opts._isFake) {
279
+ resource = db.get(name).value();
280
+ } else {
281
+ resource = db.get(name).removeById(req.params.id).value(); // Remove dependents documents
282
+
283
+ const removable = db._.getRemovable(db.getState(), opts);
284
+
285
+ removable.forEach(item => {
286
+ db.get(item.name).removeById(item.id).value();
287
+ });
288
+ }
258
289
 
259
290
  if (resource) {
260
291
  res.locals.data = {};
@@ -264,10 +295,7 @@ module.exports = (db, name, opts) => {
264
295
  }
265
296
 
266
297
  const w = write(db);
267
-
268
298
  router.route('/').get(list).post(create, w);
269
-
270
299
  router.route('/:id').get(show).put(update, w).patch(update, w).delete(destroy, w);
271
-
272
300
  return router;
273
301
  };
@@ -1,11 +1,18 @@
1
- 'use strict';
1
+ "use strict";
2
+
3
+ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } return target; }
4
+
5
+ function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
2
6
 
3
7
  const express = require('express');
8
+
4
9
  const write = require('./write');
10
+
5
11
  const getFullURL = require('./get-full-url');
12
+
6
13
  const delay = require('./delay');
7
14
 
8
- module.exports = (db, name) => {
15
+ module.exports = (db, name, opts) => {
9
16
  const router = express.Router();
10
17
  router.use(delay);
11
18
 
@@ -15,30 +22,41 @@ module.exports = (db, name) => {
15
22
  }
16
23
 
17
24
  function create(req, res, next) {
18
- db.set(name, req.body).value();
19
- res.locals.data = db.get(name).value();
25
+ if (opts._isFake) {
26
+ res.locals.data = req.body;
27
+ } else {
28
+ db.set(name, req.body).value();
29
+ res.locals.data = db.get(name).value();
30
+ }
20
31
 
21
32
  res.setHeader('Access-Control-Expose-Headers', 'Location');
22
33
  res.location(`${getFullURL(req)}`);
23
-
24
34
  res.status(201);
25
35
  next();
26
36
  }
27
37
 
28
38
  function update(req, res, next) {
29
- if (req.method === 'PUT') {
30
- db.set(name, req.body).value();
39
+ if (opts._isFake) {
40
+ if (req.method === 'PUT') {
41
+ res.locals.data = req.body;
42
+ } else {
43
+ const resource = db.get(name).value();
44
+ res.locals.data = _objectSpread({}, resource, req.body);
45
+ }
31
46
  } else {
32
- db.get(name).assign(req.body).value();
47
+ if (req.method === 'PUT') {
48
+ db.set(name, req.body).value();
49
+ } else {
50
+ db.get(name).assign(req.body).value();
51
+ }
52
+
53
+ res.locals.data = db.get(name).value();
33
54
  }
34
55
 
35
- res.locals.data = db.get(name).value();
36
56
  next();
37
57
  }
38
58
 
39
59
  const w = write(db);
40
-
41
60
  router.route('/').get(show).post(create, w).put(update, w).patch(update, w);
42
-
43
61
  return router;
44
62
  };
@@ -1,4 +1,4 @@
1
- 'use strict';
1
+ "use strict";
2
2
 
3
3
  const _ = require('lodash');
4
4
 
@@ -8,8 +8,8 @@ function getPage(array, page, perPage) {
8
8
  var obj = {};
9
9
  var start = (page - 1) * perPage;
10
10
  var end = page * perPage;
11
-
12
11
  obj.items = array.slice(start, end);
12
+
13
13
  if (obj.items.length === 0) {
14
14
  return obj;
15
15
  }