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