jxp 2.11.2 → 2.12.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/README.md CHANGED
@@ -35,4 +35,8 @@ JXP is an opinionated RESTful API framework that lets you make an API just by de
35
35
  * [Roles and Permissions](https://jxp.readthedocs.io/en/v2.0.0/permissions)
36
36
  * [Hooks](https://jxp.readthedocs.io/en/v2.0.0/hooks)
37
37
  * [Special Features](https://jxp.readthedocs.io/en/v2.0.0/special)
38
- * [Changelog](https://jxp.readthedocs.io/en/v2.0.0/changelog)
38
+ * [Changelog](https://jxp.readthedocs.io/en/v2.0.0/changelog)
39
+
40
+ ## Installing from source
41
+
42
+ `npm i --legacy-peer-deps`
package/bin/server.js CHANGED
@@ -196,17 +196,23 @@ const mongoose = require("mongoose");
196
196
  const JXP = require("../libs/jxp");
197
197
  const config = require("config");
198
198
  require("dotenv").config();
199
+ const pkg = require("../package.json");
199
200
 
200
201
  config.callbacks = {
201
- post: function(modelname, item, user) {
202
- // console.log("Post callback");
203
- },
204
- put: function(modelname, item, user) {
205
- // console.log("Put callback");
206
- },
207
- delete: function(modelname, item, user, opts) {
208
- // console.log("Delete callback");
209
- }
202
+ // Examples:
203
+ // post: function(modelname, item, user) {
204
+ // console.log("Post callback");
205
+ // },
206
+ // put: function(modelname, item, user) {
207
+ // console.log("Put callback");
208
+ // },
209
+ // delete: function(modelname, item, user, opts) {
210
+ // console.log("Delete callback");
211
+ // }
212
+
213
+ post: function() {},
214
+ put: function() {},
215
+ delete: function() {}
210
216
  };
211
217
 
212
218
  config.pre_hooks = {
@@ -259,7 +265,7 @@ var server = new JXP(config);
259
265
  let port = process.env.NODE_DOCKER_PORT || process.env.PORT || config.port || 4001;
260
266
  if (process.env.NODE_ENV === "test") port = 4005;
261
267
  server.listen(port, function() {
262
- console.log('%s listening at %s', server.name, server.url);
268
+ console.log('%s listening at %s', `${pkg.name} v${pkg.version}`, server.url);
263
269
  });
264
270
 
265
271
  module.exports = server; // For testing
package/libs/docs.js CHANGED
@@ -6,6 +6,7 @@ const md = require('jstransformer')(require('jstransformer-markdown-it'));
6
6
  const util = require('util');
7
7
  const readFile = util.promisify(fs.readFile);
8
8
  const schema_description = require("./schema_description");
9
+ const errors = require("restify-errors");
9
10
 
10
11
  class Docs {
11
12
  constructor(opts) {
@@ -32,7 +33,7 @@ class Docs {
32
33
  res.end();
33
34
  } catch(err) {
34
35
  console.error(err);
35
- return res.send(500, { status: "error", error: err, message: err.toString() });
36
+ return errors.InternalServerError({ message: err.toString() });
36
37
  }
37
38
  }
38
39
 
@@ -42,20 +43,20 @@ class Docs {
42
43
  res.send(models);
43
44
  } catch (err) {
44
45
  console.error(err);
45
- return res.send(500, { status: "error", error: err, message: err.toString() });
46
+ return errors.InternalServerError({ message: err.toString() });
46
47
  }
47
48
  }
48
49
 
49
50
  metaModel(req, res, next) {
50
51
  try {
51
52
  if (!req.Model) {
52
- return res.send(404, { status: "error", error: "Model not found" })
53
+ return errors.NotFoundError({ status: "error", error: "Model not found" })
53
54
  }
54
55
  res.send(req.Model.schema.paths);
55
56
  next();
56
57
  } catch (err) {
57
58
  console.error(err);
58
- return res.send(500, { status: "error", error: err, message: err.toString() });
59
+ return errors.InternalServerError({ message: err.toString() });
59
60
  }
60
61
  }
61
62
 
@@ -64,7 +65,7 @@ class Docs {
64
65
  res.send(this.models);
65
66
  next();
66
67
  } catch(err) {
67
- res.send(500, { status: "error", error: err, message: err.toString() });
68
+ errors.InternalServerError({ message: err.toString() });
68
69
  }
69
70
  }
70
71
 
@@ -73,7 +74,7 @@ class Docs {
73
74
  this.renderTemplate(res, "index", {});
74
75
  next();
75
76
  } catch(err) {
76
- res.send(500, { status: "error", error: err, message: err.toString() });
77
+ errors.InternalServerError({ message: err.toString() });
77
78
  }
78
79
  }
79
80
 
@@ -83,7 +84,7 @@ class Docs {
83
84
  const md_contents = md.render(body.toString()).body;
84
85
  this.renderTemplate(res, "md", { md_contents });
85
86
  } catch(err) {
86
- res.send(500, { status: "error", error: err, message: err.toString() });
87
+ errors.InternalServerError({ message: err.toString() });
87
88
  }
88
89
  }
89
90
 
@@ -97,7 +98,7 @@ class Docs {
97
98
  this.renderTemplate(res, "model", { model, fields, perms });
98
99
  next();
99
100
  } catch(err) {
100
- res.send(500, { status: "error", error: err, message: err.toString() });
101
+ errors.InternalServerError({ message: err.toString() });
101
102
  }
102
103
  }
103
104
  }
package/libs/groups.js CHANGED
@@ -1,4 +1,6 @@
1
- var Groups = null;
1
+ const errors = require("restify-errors");
2
+
3
+ let Groups = null;
2
4
 
3
5
  const init = config => {
4
6
  const path = require("path");
@@ -10,13 +12,11 @@ const actionPut = async (req, res) => {
10
12
  const group = req.body.group;
11
13
  if (!group) {
12
14
  console.error("Group required");
13
- res.send(400, "Group required");
14
- return;
15
+ return errors.BadRequestError("Group required");
15
16
  }
16
17
  if (!user_id) {
17
18
  console.error("user_id required");
18
- res.send(400, "user_id required");
19
- return;
19
+ return errors.BadRequestError("user_id required", { message: "user_id is required" });
20
20
  }
21
21
  try {
22
22
  let userGroup = await Groups.findOne({ user_id: user_id });
@@ -39,8 +39,7 @@ const actionPut = async (req, res) => {
39
39
  res.send(await userGroup.save());
40
40
  } catch(err) {
41
41
  console.error(err);
42
- res.send(500, err);
43
- return;
42
+ return errors.InternalServerError({ message: err.toString()});
44
43
  }
45
44
  };
46
45
 
@@ -49,13 +48,11 @@ const actionPost = async (req, res) => {
49
48
  const group = req.body.group;
50
49
  if (!group) {
51
50
  console.error("Group required");
52
- res.send(400, "Group required");
53
- return;
51
+ return errors.BadRequestError("Group required");
54
52
  }
55
53
  if (!user_id) {
56
54
  console.error("user_id required");
57
- res.send(400, "user_id required");
58
- return;
55
+ return errors.BadRequestError("user_id required", { message: "user_id is required" });
59
56
  }
60
57
  try {
61
58
  let userGroup = await Groups.findOne({ user_id: user_id });
@@ -78,8 +75,7 @@ const actionPost = async (req, res) => {
78
75
  res.send(await userGroup.save());
79
76
  } catch (err) {
80
77
  console.error(err);
81
- res.send(500, err);
82
- return;
78
+ return errors.InternalServerError({ message: err.toString()});
83
79
  }
84
80
  };
85
81
 
@@ -87,8 +83,7 @@ const actionGet = async (req, res) => {
87
83
  var user_id = req.params.user_id;
88
84
  if (!user_id) {
89
85
  console.error("user_id required");
90
- res.send(400, "user_id required");
91
- return;
86
+ return errors.BadRequestError("user_id required", { message: "user_id is required" });
92
87
  }
93
88
  try {
94
89
  let userGroup = await Groups.findOne({ user_id });
@@ -99,8 +94,7 @@ const actionGet = async (req, res) => {
99
94
  res.send(userGroup);
100
95
  } catch (err) {
101
96
  console.error(err);
102
- res.send(500, err);
103
- return;
97
+ return errors.InternalServerError({ message: err.toString()});
104
98
  }
105
99
  };
106
100
 
@@ -108,19 +102,16 @@ const actionDelete = async (req, res) => {
108
102
  const user_id = req.params.user_id;
109
103
  const group = req.query.group;
110
104
  if (!group) {
111
- res.send(400, "Group required");
112
- return;
105
+ return errors.BadRequestError("Group required");
113
106
  }
114
107
  if (!user_id) {
115
108
  console.error("user_id required");
116
- res.send(400, "user_id required");
117
- return;
109
+ return errors.BadRequestError("user_id required", { message: "user_id is required" });
118
110
  }
119
111
  try {
120
112
  let userGroup = await Groups.findOne({ user_id });
121
113
  if (!userGroup) {
122
- res.send(400, "User not found");
123
- return;
114
+ return errors.BadRequestError("User not found");
124
115
  }
125
116
  let i = userGroup.groups.indexOf(group);
126
117
  if (i > -1) {
@@ -129,8 +120,7 @@ const actionDelete = async (req, res) => {
129
120
  res.send(await userGroup.save());
130
121
  } catch (err) {
131
122
  console.error(err);
132
- res.send(500, err);
133
- return;
123
+ return errors.InternalServerError({ message: err.toString() });
134
124
  }
135
125
  };
136
126
 
package/libs/jxp.js CHANGED
@@ -1,3 +1,4 @@
1
+ const errors = require("restify-errors");
1
2
  const restify = require("restify");
2
3
  const path = require("path");
3
4
  const security = require("./security");
@@ -29,7 +30,7 @@ const middlewareModel = (req, res, next) => {
29
30
  return next();
30
31
  } catch (err) {
31
32
  console.error(new Date, err);
32
- return res.send(404, { status: "error", error: err, message: `Model ${modelname} not found` });
33
+ throw new errors.NotFoundError("Not Found", { message: `Model ${modelname} not found` });
33
34
  }
34
35
  };
35
36
 
@@ -50,8 +51,13 @@ const middlewareCheckAdmin = (req, res, next) => {
50
51
 
51
52
  // Outputs whatever is in res.result as JSON
52
53
  const outputJSON = (req, res, next) => {
53
- res.send(res.result);
54
- next();
54
+ try {
55
+ res.send(res.result);
56
+ next();
57
+ } catch (err) {
58
+ console.error(new Date(), err);
59
+ throw new errors.InternalServerError("Error generating output", { message: err.toString() });
60
+ }
55
61
  }
56
62
 
57
63
  // Outputs whatever is in res.result as CSV
@@ -59,7 +65,7 @@ const outputCSV = (req, res, next) => {
59
65
  const json2csv = require('json2csv').parse;
60
66
  const opts = { "flatten": true };
61
67
  if (!res.result.data) {
62
- return res.send(500, "Not CSVable data");
68
+ throw new errors.InternalServerError("Error generating output", { message: "Not CSVable data" });
63
69
  }
64
70
  try {
65
71
  const data = res.result.data.map(row => row._doc);
@@ -75,7 +81,7 @@ const outputCSV = (req, res, next) => {
75
81
  next();
76
82
  } catch (err) {
77
83
  console.error(err);
78
- res.send(500, { status: "error", error: err, message: err.toString() });
84
+ throw new errors.InternalServerError("Internal Error", { message: err.toString() });
79
85
  }
80
86
  }
81
87
 
@@ -95,8 +101,7 @@ const actionGet = async (req, res) => {
95
101
  filters = parseFilter(req.query.filter);
96
102
  } catch (err) {
97
103
  console.trace(new Date(), err);
98
- res.send(500, { status: "error", error: err, message: err.toString() });
99
- return;
104
+ throw new errors.InternalServerError("Internal Error", { message: err.toString() });
100
105
  }
101
106
  let search = parseSearch(req.query.search);
102
107
  for (let i in search) {
@@ -118,7 +123,7 @@ const actionGet = async (req, res) => {
118
123
  qcount = req.Model.find({ $text: { $search: req.query.search }});
119
124
  }
120
125
  if (res.user) {
121
- q.options({ user: res.user });
126
+ q.options = ({ user: res.user });
122
127
  }
123
128
  try {
124
129
  let count = await req.Model.estimatedDocumentCount();
@@ -185,7 +190,8 @@ const actionGet = async (req, res) => {
185
190
  } catch(err) {
186
191
  console.error(new Date(), err);
187
192
  if (debug) console.timeEnd(opname);
188
- res.send(500, { status: "error", message: err.toString() });
193
+ if (err.code) throw err;
194
+ throw new errors.InternalServerError("Internal Error", { message: err.toString() });
189
195
  }
190
196
  };
191
197
 
@@ -199,11 +205,8 @@ const actionGetOne = async (req, res) => {
199
205
  } catch(err) {
200
206
  console.error(new Date(), err);
201
207
  if (debug) console.timeEnd(opname);
202
- if (err.msg) {
203
- res.send(err.code || 500, { status: "error", message: err.msg });
204
- } else {
205
- res.send(err.code || 500, { status: "error", message: err.toString() });
206
- }
208
+ if (err.code) throw err;
209
+ throw new errors.InternalServerError("Internal Error", { message: err.toString() });
207
210
  }
208
211
  };
209
212
 
@@ -233,7 +236,7 @@ const actionPost = async (req, res) => {
233
236
  } catch (err) {
234
237
  console.error(new Date(), err);
235
238
  if (debug) console.timeEnd(opname);
236
- res.send(500, { status: "error", message: err.toString() });
239
+ throw new errors.InternalServerError("Internal Error", { message: err.toString() });
237
240
  }
238
241
  };
239
242
 
@@ -244,8 +247,7 @@ const actionPut = async (req, res) => {
244
247
  let item = await req.Model.findById(req.params.item_id);
245
248
  if (!item) {
246
249
  console.error(new Date(), "Document not found");
247
- res.send(404, { status: "error", message: "Document not found" });
248
- return;
250
+ throw new errors.NotFoundError("Not Found", { message: "Document not found" });
249
251
  }
250
252
  _populateItem(item, datamunging.deserialize(req.body));
251
253
  _versionItem(item);
@@ -269,8 +271,7 @@ const actionPut = async (req, res) => {
269
271
  } catch (err) {
270
272
  console.error(new Date(), err);
271
273
  if (debug) console.timeEnd(opname);
272
- res.send(500, { status: "error", message: err.toString() });
273
- return;
274
+ throw new errors.InternalServerError("Internal Error", { message: err.toString() });
274
275
  }
275
276
  };
276
277
 
@@ -295,8 +296,7 @@ const actionUpdate = async (req, res) => {
295
296
  } catch (err) {
296
297
  console.error(new Date(), err);
297
298
  if (debug) console.timeEnd(opname);
298
- res.send(500, { status: "error", message: err.toString() });
299
- return;
299
+ throw new errors.InternalServerError("Internal Error", { message: err.toString() });
300
300
  }
301
301
  };
302
302
 
@@ -310,8 +310,7 @@ const actionDelete = async (req, res) => {
310
310
  let item = await req.Model.findById(req.params.item_id);
311
311
  if (!item) {
312
312
  console.error(new Date(), "Couldn't find item for delete");
313
- res.send(404, "Could not find document");
314
- return;
313
+ throw new errors.NotFoundError("Not Found", { message: "Couldn't find item for delete" });
315
314
  }
316
315
  // Get linked models
317
316
  const linked_models = [];
@@ -340,8 +339,7 @@ const actionDelete = async (req, res) => {
340
339
  await models[linked_model.modelname].updateMany(q, { _deleted: true });
341
340
  }
342
341
  } else {
343
- res.send(409, { status: "error", message: `Parent link item exists in ${linked_model.modelname}/${linked_model.field}` });
344
- return;
342
+ throw new errors.ConflictError("Conflict Error", { message: `Parent link item exists in ${linked_model.modelname}/${linked_model.field}` });
345
343
  }
346
344
  }
347
345
  }
@@ -374,8 +372,8 @@ const actionDelete = async (req, res) => {
374
372
  } catch(err) {
375
373
  console.error(new Date(), err);
376
374
  if (debug) console.timeEnd(opname);
377
- res.send(500, { status: "error", message: err.toString() });
378
- return;
375
+ if (err.code) throw err;
376
+ throw new errors.InternalServerError("Internal Error", { message: err.toString() });
379
377
  }
380
378
  };
381
379
 
@@ -394,8 +392,7 @@ const actionCount = async (req, res) => {
394
392
  filters = parseFilter(req.query.filter);
395
393
  } catch (err) {
396
394
  console.trace(new Date(), err);
397
- res.send(500, { status: "error", error: err, message: err.toString() });
398
- return;
395
+ throw new errors.InternalServerError("Internal Error", { message: err.toString() });
399
396
  }
400
397
  let search = parseSearch(req.query.search);
401
398
  for (let i in search) {
@@ -411,7 +408,8 @@ const actionCount = async (req, res) => {
411
408
  } catch(err) {
412
409
  console.error(new Date(), err);
413
410
  if (debug) console.timeEnd(opname);
414
- res.send(500, { status: "error", message: err.toString() });
411
+ if (err.code) throw err;
412
+ throw new errors.InternalServerError("Internal Error", { message: err.toString() });
415
413
  }
416
414
  };
417
415
 
@@ -424,7 +422,8 @@ const actionCall = async (req, res) => {
424
422
  res.json(result);
425
423
  } catch(err) {
426
424
  console.error(new Date(), err);
427
- res.send(500, { status: "error", message: err.toString() });
425
+ if (err.code) throw err;
426
+ throw new errors.InternalServerError("Internal Error", { message: err.toString() });
428
427
  }
429
428
  };
430
429
 
@@ -432,16 +431,15 @@ const actionCallItem = async (req, res) => {
432
431
  try {
433
432
  const item = req.Model.findById(req.params.item_id);
434
433
  if (!item) {
435
- res.send(404, "Document not found for " + req.params.method_name);
436
- return;
434
+ throw new errors.NotFoundError("Not Found", { message: "Couldn't find item for call" });
437
435
  }
438
436
  req.params.__user = res.user || null;
439
437
  const result = await req.Model[req.params.method_name](item);
440
438
  res.json(result);
441
-
442
439
  } catch(err) {
443
440
  console.trace(err);
444
- res.send(500, { status: "error", message: err.toString() });
441
+ if (err.code) throw err;
442
+ throw new errors.InternalServerError("Internal Error", { message: err.toString() });
445
443
  }
446
444
  };
447
445
 
@@ -449,7 +447,7 @@ const actionCallItem = async (req, res) => {
449
447
  const actionQuery = async (req, res) => {
450
448
  if (!req.body || !req.body.query || typeof req.body.query !== "object") {
451
449
  console.error("query missing or not of type object")
452
- return res.send(500, { status: "error", message: "query missing or not of type object" });
450
+ throw new errors.InternalServerError("Query missing", { message: "query missing or not of type object" });
453
451
  }
454
452
  const opname = `query ${req.modelname} ${ops++}`;
455
453
  console.time(opname);
@@ -518,7 +516,8 @@ const actionQuery = async (req, res) => {
518
516
  } catch(err) {
519
517
  console.error(new Date(), err);
520
518
  if (debug) console.timeEnd(opname);
521
- res.send(500, { status: "error", message: err.toString() });
519
+ if (err.code) throw err;
520
+ throw new errors.InternalServerError("Internal Error", { message: err.toString() });
522
521
  }
523
522
  };
524
523
 
@@ -527,7 +526,7 @@ const actionAggregate = async (req, res) => {
527
526
  let query = (req.body.query) ? req.body.query : req.body; // Don't require to embed in query anymore
528
527
  if (!query || !Array.isArray(query)) {
529
528
  console.error("query missing or not of type array")
530
- return res.send(500, { status: "error", message: "query missing or not of type array" });
529
+ throw new errors.InternalServerError("Query missing", { message: "query missing or not of type array" });
531
530
  }
532
531
  query = query_manipulation.fix_query(query);
533
532
  const opname = `aggregate ${req.modelname} ${ops++}`;
@@ -545,7 +544,7 @@ const actionAggregate = async (req, res) => {
545
544
  } catch (err) {
546
545
  console.error(new Date(), err);
547
546
  if (debug) console.timeEnd(opname);
548
- res.send(500, { status: "error", message: err.toString() });
547
+ throw new errors.InternalServerError("Internal Error", { message: err.toString() });
549
548
  }
550
549
  };
551
550
 
@@ -553,7 +552,7 @@ const actionAggregate = async (req, res) => {
553
552
  const actionBulkWrite = async (req, res) => {
554
553
  if (!req.body || !Array.isArray(req.body)) {
555
554
  console.error("query missing or not of type array")
556
- return res.send(500, { status: "error", message: "query missing or not of type array" });
555
+ throw new errors.InternalServerError("Query Missing", { message: "query missing or not of type array" });
557
556
  }
558
557
  const opname = `bulkwrite ${req.modelname} ${ops++}`;
559
558
  console.time(opname);
@@ -568,7 +567,7 @@ const actionBulkWrite = async (req, res) => {
568
567
  } catch (err) {
569
568
  console.error(new Date(), err);
570
569
  if (debug) console.timeEnd(opname);
571
- res.send(500, { status: "error", message: err.toString() });
570
+ throw new errors.InternalServerError("Internal Error", { message: err.toString() });
572
571
  }
573
572
  };
574
573
 
@@ -629,11 +628,11 @@ const getOne = async (Model, item_id, params, options) => {
629
628
  var item = await query.exec();
630
629
  if (!item) {
631
630
  // console.error("Could not find document");
632
- return Promise.reject({ code: 404, msg: "Could not find document" });
631
+ throw new errors.NotFoundError("Could not find document", { message: "Could not find document" });
633
632
  }
634
633
  if (item._deleted && !params.showDeleted) {
635
634
  // console.error("Document is deleted");
636
- return Promise.reject({ code: 404, msg: "Document is deleted" });
635
+ throw new errors.NotFoundError("Document is deleted", { message: "Document is deleted" });
637
636
  }
638
637
  item = item.toObject();
639
638
  //Don't ever return passwords
@@ -641,7 +640,8 @@ const getOne = async (Model, item_id, params, options) => {
641
640
  return item;
642
641
  } catch(err) {
643
642
  console.error(err);
644
- return Promise.reject({ code: 500, msg: err });
643
+ if (err.code) throw err;
644
+ throw new errors.InternalServerError("Internal Error", { message: err.toString() });
645
645
  }
646
646
  };
647
647
 
package/libs/login.js CHANGED
@@ -3,13 +3,12 @@ const jwt = require("jsonwebtoken");
3
3
  const bcrypt = require('bcryptjs');
4
4
  const security = require("../libs/security");
5
5
  const nodemailer = require('nodemailer');
6
+ const errors = require("restify-errors");
6
7
  const smtpTransport = require('nodemailer-smtp-transport');
7
- var User = null;
8
- var APIKey = null;
8
+ const path = require("path");
9
+ let User = null;
9
10
 
10
11
  var init = function (config) {
11
- var path = require("path");
12
- APIKey = require(path.join(config.model_dir, 'apikey_model'));
13
12
  User = require(path.join(config.model_dir, 'user_model'));
14
13
  security.init(config);
15
14
  };
@@ -30,12 +29,11 @@ const recover = async (req, res) => {
30
29
  const email = req.body.email;
31
30
  if (!email) {
32
31
  console.error("Missing email parameter");
33
- res.send(400, { status: "fail", message: "Missing email parameter" });
34
- return;
32
+ throw new errors.BadRequestError("Bad Request", { status: "fail", message: "Missing email parameter" });
35
33
  }
36
34
  const user = await User.findOne({ email });
37
35
  if (!user) {
38
- throw ("Could not find email");
36
+ throw new errors.NotFoundError("User Not Found", { status: "fail", message: "User not found" });
39
37
  }
40
38
  const result = await security.generateApiKey(user._id);
41
39
  const token = jwt.sign({ apikey: result.apikey, email: user.email, id: user._id }, req.config.shared_secret, { expiresIn: "2d" });
@@ -57,26 +55,27 @@ const recover = async (req, res) => {
57
55
  });
58
56
  res.send({ status: "ok", message: "Sent recovery email" });
59
57
  } catch (err) {
60
- res.send(403, { status: "fail", message: "Unauthorized", err });
58
+ if (err.code) throw err;
59
+ throw new errors.UnauthorizedError("Unauthorized", { status: "fail", message: "Unauthorized", info: err });
61
60
  }
62
61
  }
63
62
 
64
63
  const logout = async (req, res) => {
65
64
  try {
66
- if (!res.user) throw("You don't seem to be logged in");
65
+ if (!res.user) throw new errors.ForbiddenError("Forbidden", { message: "You don't seem to be logged in" });
67
66
  await security.revokeToken(res.user._id);
68
67
  res.send({ status: "ok", message: "User logged out" });
69
68
  } catch(err) {
70
69
  console.error(err);
71
- res.send(500, { status: "error", error: err });
70
+ if (err.code) throw err;
71
+ throw new errors.InternalServerError("Server Error", { status: "error", message: err.toString() });
72
72
  }
73
73
  }
74
74
 
75
75
  const oauth = (req, res, next) => { // Log in through an OAuth2 provider, defined in config.js
76
76
  const provider_config = req.config.oauth[req.params.provider];
77
77
  if (!provider_config) {
78
- res.send(500, req.params.provider + " config not defined");
79
- return;
78
+ throw new errors.InternalServerError("Internal Server Error", { message: req.params.provider + " config not defined" });
80
79
  }
81
80
  const state = Math.random().toString(36).substring(7);
82
81
  const uri = `${provider_config.auth_uri}?client_id=${provider_config.app_id}&redirect_uri=${req.config.url}/login/oauth/callback/${req.params.provider}&scope=${provider_config.scope}&state=${state}&response_type=code`;
@@ -149,8 +148,7 @@ const login = async (req, res) => {
149
148
  }
150
149
  if ((!password) || (!email)) {
151
150
  console.error(new Date(), "Missing email or password parameters");
152
- res.send(404, { status: "fail", message: "Missing email or password parameters" });
153
- return;
151
+ return errors.ForbiddenError({ status: "fail", message: "Missing email or password parameters" });
154
152
  }
155
153
  try {
156
154
  const user = await User.findOne({ email });
@@ -171,28 +169,25 @@ const login = async (req, res) => {
171
169
  provider: token.provider,
172
170
  });
173
171
  } catch (err) {
174
- res.send(401, { status: "fail", message: "Authentication failed", err });
175
172
  console.error(new Date(), `Authentication failed`, ip, err);
176
- return;
173
+ if (err.code) throw err;
174
+ return errors.ForbiddenError({ status: "fail", message: "Authentication failed" });
177
175
  }
178
176
  }
179
177
 
180
178
  const getJWT = async (req, res) => {
181
179
  var user = null;
182
180
  if (!res.user.admin) {
183
- res.send(403, { status: "fail", message: "Unauthorized" });
184
- return;
181
+ throw new errors.UnauthorizedError("Unauthorized", { status: "fail", message: "Unauthorized" });
185
182
  }
186
183
  var email = req.params.email || req.body.email;
187
184
  if (!email) {
188
- res.send(400, { status: "fail", message: "Email required" });
189
- return;
185
+ throw new errors.BadRequestError("Bad Request", { status: "fail", message: "Email required" });
190
186
  }
191
187
  try {
192
188
  const result = await User.findOne({ email: email });
193
189
  if (!result || !result._id) {
194
- res.send(404, { status: "fail", message: "User not found" });
195
- return;
190
+ throw new errors.NotFoundError("Not Found", { status: "fail", message: "User not found" });
196
191
  }
197
192
  user = result;
198
193
  try {
@@ -202,11 +197,11 @@ const getJWT = async (req, res) => {
202
197
  });
203
198
  res.send({ email: user.email, token: token });
204
199
  } catch (err) {
205
- res.send(403, { status: "fail", message: "Unauthorized" });
206
- return;
200
+ throw new errors.UnauthorizedError("Unauthorized", { status: "fail", message: "Unauthorized" });
207
201
  }
208
202
  } catch (err) {
209
- res.send(500, { status: "error", error: err });
203
+ if (err.code) throw err;
204
+ throw new errors.InternalServerError("Internal Server Error", { status: "error", message: err.toString() });
210
205
  }
211
206
  }
212
207
 
@@ -9,7 +9,7 @@ const getModelFileContents = (model_dir) => {
9
9
  return reject(err);
10
10
  }
11
11
  let models = [];
12
- for (file of files) {
12
+ for (const file of files) {
13
13
  const modelname = path.basename(file, ".js").replace("_model", "");
14
14
  try {
15
15
  const modelobj = require(path.join(model_dir, file));
package/libs/security.js CHANGED
@@ -1,6 +1,7 @@
1
1
  const bcrypt = require("bcryptjs");
2
2
  const randToken = require("rand-token");
3
3
  const path = require("path");
4
+ const errors = require("restify-errors");
4
5
  var APIKey = null;
5
6
  var Token = null;
6
7
  var Groups = null;
@@ -17,10 +18,6 @@ const init = function(config) {
17
18
  if (config.url) provider = config.url;
18
19
  };
19
20
 
20
- const fail = function (res, code, message) {
21
- res.send(code, { status: "error", message });
22
- };
23
-
24
21
  const basicAuthData = function(req) {
25
22
  if (!req.headers.authorization) {
26
23
  return false;
@@ -51,7 +48,7 @@ const basicAuth = async ba => {
51
48
  return user;
52
49
  } catch(err) {
53
50
  console.error(new Date(), err);
54
- return Promise.reject(err);
51
+ throw err;
55
52
  }
56
53
  };
57
54
 
@@ -84,7 +81,7 @@ const bearerAuth = async t => {
84
81
  return user;
85
82
  } catch (err) {
86
83
  console.error(err);
87
- return Promise.reject(err);
84
+ throw err;
88
85
  }
89
86
  }
90
87
 
@@ -98,7 +95,7 @@ const apiKeyAuth = async apikey => {
98
95
  return user;
99
96
  } catch(err) {
100
97
  console.error(new Date(), err);
101
- return Promise.reject(err);
98
+ throw err;
102
99
  }
103
100
  };
104
101
 
@@ -109,7 +106,7 @@ const getGroups = async user_id => {
109
106
  return groups;
110
107
  } catch(err) {
111
108
  console.error(new Date(), err);
112
- return Promise.reject(err);
109
+ throw err;
113
110
  }
114
111
  };
115
112
 
@@ -131,7 +128,7 @@ const generateApiKey = async user_id => {
131
128
  return apikey;
132
129
  } catch(err) {
133
130
  console.error(new Date(), err);
134
- return Promise.reject(err);
131
+ throw err;
135
132
  }
136
133
  };
137
134
 
@@ -156,16 +153,21 @@ const generateToken = async user_id => {
156
153
  return token;
157
154
  } catch (err) {
158
155
  console.error(new Date(), err);
159
- return Promise.reject(err);
156
+ throw err;
160
157
  }
161
158
  };
162
159
 
163
160
  const ensureToken = async user_id => {
164
- const token = await Token.findOne({ user_id, provider }).sort({ createdAt: -1 }).exec();
165
- if (tokenIsValid(token)) {
166
- return token;
161
+ try {
162
+ const token = await Token.findOne({ user_id, provider }).sort({ createdAt: -1 }).exec();
163
+ if (tokenIsValid(token)) {
164
+ return token;
165
+ }
166
+ return await generateToken(user_id);
167
+ } catch (err) {
168
+ console.error(new Date(), err);
169
+ throw err;
167
170
  }
168
- return await generateToken(user_id);
169
171
  }
170
172
 
171
173
  const refreshToken = async user_id => {
@@ -175,7 +177,7 @@ const refreshToken = async user_id => {
175
177
 
176
178
  const revokeToken = async user_id => {
177
179
  await Token.deleteOne({ user_id, provider });
178
- return true;
180
+ return;
179
181
  }
180
182
 
181
183
  const generateRefreshToken = async user_id => {
@@ -187,7 +189,7 @@ const generateRefreshToken = async user_id => {
187
189
  return refreshtoken;
188
190
  } catch (err) {
189
191
  console.error(new Date(), err);
190
- return Promise.reject(err);
192
+ throw err;
191
193
  }
192
194
  };
193
195
 
@@ -201,7 +203,7 @@ const ensureRefreshToken = async user_id => {
201
203
 
202
204
  const revokeRefreshToken = async user_id => {
203
205
  await RefreshToken.deleteMany({ user_id });
204
- return true;
206
+ return;
205
207
  }
206
208
 
207
209
  const refresh = async (req, res) => {
@@ -226,7 +228,8 @@ const refresh = async (req, res) => {
226
228
  }
227
229
  } catch (err) {
228
230
  console.error(err);
229
- return fail(res, 403, err);
231
+ if (err.code) throw err;
232
+ throw new errors.ForbiddenError("Forbidden", { message: err.toString() });
230
233
  }
231
234
  }
232
235
 
@@ -241,7 +244,8 @@ const login = async (req, res) => {
241
244
  res = Object.assign(res, authenticate_result);
242
245
  } catch(err) {
243
246
  console.error(err);
244
- return fail(res, 403, err);
247
+ if (err.code) throw err;
248
+ throw new errors.ForbiddenError("Forbidden", { message: err.toString() });
245
249
  }
246
250
  };
247
251
 
@@ -250,40 +254,37 @@ const authenticate = async req => {
250
254
  if (!req.query.apikey && !req.headers.authorization && !(req.headers["X-API-Key"] || req.headers["x-api-key"])) {
251
255
  return false;
252
256
  }
253
- try {
254
- if (req.headers.authorization && req.headers.authorization.trim().toLowerCase().indexOf("basic") === 0) {
255
- // Basic Auth
256
- user = await basicAuth(basicAuthData(req));
257
- } else if (req.headers.authorization && req.headers.authorization.trim().toLowerCase().indexOf("bearer") === 0) {
258
- // Token Auth
259
- user = await bearerAuth(bearerAuthData(req));
260
- } else if (req.query.apikey) {
261
- user = await apiKeyAuth(req.query.apikey);
262
- } else if (req.headers["X-API-Key"] || req.headers["x-api-key"]) {
263
- // API Key
264
- user = await apiKeyAuth(req.query.apikey || req.headers["X-API-Key"] || req.headers["x-api-key"])
265
- } else {
266
- throw ("Could not find any way to authenticate");
267
- }
268
- if (!user) {
269
- throw ("Could not find user");
270
- }
271
- return {
272
- token: await ensureToken(user._id),
273
- refresh_token: await ensureRefreshToken(user._id),
274
- groups: await getGroups(user._id),
275
- user
276
- }
277
- } catch (err) {
278
- return Promise.reject(err);
257
+ if (req.headers.authorization && req.headers.authorization.trim().toLowerCase().indexOf("basic") === 0) {
258
+ // Basic Auth
259
+ user = await basicAuth(basicAuthData(req));
260
+ } else if (req.headers.authorization && req.headers.authorization.trim().toLowerCase().indexOf("bearer") === 0) {
261
+ // Token Auth
262
+ user = await bearerAuth(bearerAuthData(req));
263
+ } else if (req.query.apikey) {
264
+ user = await apiKeyAuth(req.query.apikey);
265
+ } else if (req.headers["X-API-Key"] || req.headers["x-api-key"]) {
266
+ // API Key
267
+ user = await apiKeyAuth(req.query.apikey || req.headers["X-API-Key"] || req.headers["x-api-key"])
268
+ } else {
269
+ throw ("Could not find any way to authenticate");
279
270
  }
271
+ if (!user) {
272
+ throw ("Could not find user");
273
+ }
274
+ return {
275
+ token: await ensureToken(user._id),
276
+ refresh_token: await ensureRefreshToken(user._id),
277
+ groups: await getGroups(user._id),
278
+ user
279
+ }
280
+
280
281
  }
281
282
 
282
283
  const auth = async (req, res) => {
283
284
  // Check against model as to whether we're allowed to edit this model
284
285
  if (!req.Model) {
285
286
  console.error("Model missing");
286
- return fail(res, 500, "Model missing");
287
+ throw new errors.InternalServerError("Model missing", { message: "Model missing" });
287
288
  }
288
289
  try {
289
290
  var method = null;
@@ -298,12 +299,13 @@ const auth = async (req, res) => {
298
299
  method = "d";
299
300
  } else {
300
301
  console.error("Unsupported operation", req.method);
301
- return fail(res, 500, "Unsupported operation: " + req.method);
302
+ throw new errors.InternalServerError("Unsupported operation", { message: "Unsupported operation: " + req.method});
302
303
  }
303
- await check_perms(res.user, res.groups, req.Model, method, req.params.item_id);
304
+ return await check_perms(res.user, res.groups, req.Model, method, req.params.item_id);
304
305
  } catch(err) {
305
306
  console.error(err);
306
- return fail(res, 403, { status: "Unauthorized", error: err });
307
+ if (err.code) throw err;
308
+ throw new errors.ForbiddenError("Forbidden", { message: err.toString() });
307
309
  }
308
310
  };
309
311
 
@@ -316,7 +318,8 @@ const bulkAuth = async (req, res,) => {
316
318
  await check_perms(res.user, res.groups, req.Model, "d");
317
319
  } catch (err) {
318
320
  console.error(err);
319
- return fail(res, 403, { status: "Unauthorized", error: err });
321
+ if (err.code) throw err;
322
+ throw new errors.ForbiddenError("Forbidden", { message: err.toString() });
320
323
  }
321
324
  };
322
325
 
@@ -326,52 +329,53 @@ const check_perms = async (user, groups, model, method, item_id) => {
326
329
  //If no perms are set, then this isn't an available model
327
330
  if (!perms.admin) {
328
331
  console.error("Model permissions not set correctly - add an admin section");
329
- throw("Model permissions not set correctly - add an admin section");
332
+ throw new errors.InternalServerError("Model permissions not set correctly - add an admin section", { message: "Model permissions not set correctly - add an admin section" });
330
333
  }
331
334
  //First check if "all" is able to do this. If so, let's get on with it.
332
- if (perms.all) {
335
+ if (perms.all && perms.all.length) {
333
336
  if (perms.all.indexOf(method) !== -1) {
334
- return true;
337
+ return;
335
338
  }
336
339
  }
337
340
  //This isn't an 'all' situation, so let's bail if the user isn't logged in
338
341
  if (!user) {
339
- throw("Unauthorized");
342
+ throw new errors.ForbiddenError("User not logged in", { message: "User not logged in" });
340
343
  }
341
344
  //Let's check perms in this order - admin, user, group, owner
342
345
  //Admin check
343
346
  if (user.admin && perms.admin && perms.admin.includes(method)) {
344
347
  // console.log("Matched permission 'admin':" + method);
345
- return true;
348
+ return;
346
349
  }
347
350
  //User check
348
351
  if (perms.user && perms.user.includes(method)) {
349
352
  // console.log("Matched permission 'user':" + method);
350
- return true;
353
+ return;
351
354
  }
352
355
  //Group check
353
356
  for (let group of groups) {
354
357
  if (perms[group] && perms[group].includes(method) ) {
355
358
  // console.log("Matched permission '" + group + "':" + method);
356
- return true;
359
+ return;
357
360
  }
358
361
  }
359
362
  //Owner check
360
363
  if (!item_id) throw (`Authorization failed - ${method}`);
361
364
  const item = await model.findById(item_id);
362
- if (item && item._owner_id && item._owner_id.toString() == user._id.toString() && (perms.owner && perms.owner.includes(method))) return true;
365
+ if (item && item._owner_id && item._owner_id.toString() == user._id.toString() && (perms.owner && perms.owner.includes(method))) return;
363
366
  throw ("Authorization failed");
364
367
  } catch (err) {
365
- return Promise.reject(err);
368
+ if (err.code) throw err;
369
+ throw new errors.ForbiddenError("Forbidden", { message: err.toString() });
366
370
  }
367
371
  }
368
372
 
369
373
  const admin_only = (req, res, next) => { // Chain after login
370
374
  if (!res.user) {
371
- return fail(res, 403, "Unauthorized");
375
+ throw new errors.ForbiddenError("Unauthorized", { message: "Unauthorized" });
372
376
  }
373
377
  if (!res.user.admin) {
374
- return fail(res, 403, "Unauthorized");
378
+ throw new errors.ForbiddenError("Unauthorized", { message: "Unauthorized" });
375
379
  }
376
380
  next();
377
381
  }
package/libs/setup.js CHANGED
@@ -1,5 +1,6 @@
1
1
  const rand_token = require("rand-token");
2
2
  const path = require("path");
3
+ const errors = require("restify-errors");
3
4
  const security = require("./security");
4
5
  const ObjectID = require('mongodb').ObjectID;
5
6
  var User = null;
@@ -9,15 +10,15 @@ const init = config => {
9
10
  User = require(path.join(config.model_dir, "user_model"));
10
11
  }
11
12
 
12
- const checkUserDoesNotExist = async (req, res) => {
13
+ const checkUserDoesNotExist = async () => {
13
14
  try {
14
15
  const count = await User.countDocuments();
15
16
  if (count) {
16
- return res.send(403, { status: "failed", error: "Cannot setup if user exists" });
17
+ throw new errors.ConflictError("Conflict Error", { status: "failed", error: "Cannot setup if user exists" });
17
18
  }
18
19
  } catch(err) {
19
20
  console.error(err);
20
- res.send(500, { status: "error", error: err.message });
21
+ throw new errors.InternalServerError("Internal Server Error", { status: "error", error: err.message });
21
22
  }
22
23
  };
23
24
 
@@ -46,7 +47,7 @@ const setup = async (req, res) => {
46
47
  });
47
48
  } catch(err) {
48
49
  console.error(err);
49
- res.send(500, { status: "error", error: err.message });
50
+ throw new errors.InternalServerError("Internal Server Error", { status: "error", error: err.message });
50
51
  }
51
52
  };
52
53
 
@@ -76,7 +77,7 @@ const data_setup = async (req, res) => {
76
77
  res.send({ status: "success", results });
77
78
  } catch(err) {
78
79
  console.error(err);
79
- res.send(500, { status: "error", error: err.message });
80
+ throw new errors.InternalServerError("Internal Server Error", { status: "error", error: err.message });
80
81
  }
81
82
  }
82
83
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "jxp",
3
3
  "description:": "An opinionated RESTful API library based on Mongoose and Restify. Make an API by just writing Mongoose models.",
4
- "version": "2.11.2",
4
+ "version": "2.12.1",
5
5
  "private": false,
6
6
  "main": "libs/jxp.js",
7
7
  "scripts": {
@@ -39,7 +39,7 @@
39
39
  "jstransformer-markdown-it": "^3.0.0",
40
40
  "jxp-helper": "^1.4.0",
41
41
  "mkdirp": "^1.0.4",
42
- "mongoose": "6.7.3",
42
+ "mongoose": "6.7.5",
43
43
  "mongoose-friendly": "^0.1.4",
44
44
  "morgan": "^1.10.0",
45
45
  "nodemailer": "^6.8.0",
@@ -49,8 +49,9 @@
49
49
  "querystring": "^0.2.1",
50
50
  "rand-token": "^1.0.1",
51
51
  "readline-sync": "^1.4.10",
52
- "restify": "^9.0.0",
52
+ "restify": "^10.0.0",
53
53
  "restify-cors-middleware": "^1.1.1",
54
+ "restify-errors": "^8.0.2",
54
55
  "traverse": "^0.6.7",
55
56
  "underscore": "^1.13.6",
56
57
  "ws": "^8.11.0"
package/test/init.js CHANGED
@@ -13,7 +13,7 @@ const security = require("../libs/security");
13
13
 
14
14
  const empty = async model => {
15
15
  try {
16
- const result = await model.deleteMany({});
16
+ await model.deleteMany({});
17
17
  } catch(err) {
18
18
  console.error(err);
19
19
  throw(err);
@@ -3,14 +3,13 @@ const config = require("config");
3
3
  const path = require("path");
4
4
 
5
5
  var model_dir = config.model_dir || path.join(process.cwd(), "./models");
6
- const Test = require(path.join(model_dir, "test_model"));
6
+ require(path.join(model_dir, "test_model"));
7
7
 
8
8
  var chai = require('chai');
9
9
  var chaiHttp = require('chai-http');
10
10
  var should = chai.should();
11
11
 
12
12
  var init = require("./init");
13
-
14
13
  var server = require("../bin/server");
15
14
 
16
15
  chai.use(chaiHttp);
@@ -27,6 +26,7 @@ const mock_data = {
27
26
  describe('Setup', () => {
28
27
  beforeEach(async function() {
29
28
  await init.empty_user_collections();
29
+ console.log("Emptied collections");
30
30
  })
31
31
 
32
32
  describe("setup", () => {
@@ -35,6 +35,7 @@ describe('Setup', () => {
35
35
  .post("/setup")
36
36
  .send({ email: init.email, password: init.password })
37
37
  .end((err, res) => {
38
+ // console.log(res);
38
39
  res.should.have.status(200);
39
40
  res.body.status.should.equal('success');
40
41
  res.body.name.should.equal('admin');
package/test/test.js CHANGED
@@ -121,6 +121,7 @@ describe('Test', () => {
121
121
  .get("/api/user")
122
122
  .auth(init.email, init.password)
123
123
  .end((err, res) => {
124
+ // console.log(res.body);
124
125
  res.should.have.status(200);
125
126
  res.body.data.should.be.an('array');
126
127
  done();
@@ -159,6 +160,7 @@ describe('Test', () => {
159
160
  chai.request(server)
160
161
  .get(`/api/user`)
161
162
  .end((err, res) => {
163
+ // console.log(res.body);
162
164
  res.should.have.status(403);
163
165
  done();
164
166
  });
@@ -565,7 +567,7 @@ describe('Test', () => {
565
567
  .get(`/api/test?populate=array_link`)
566
568
  .auth(init.email, init.password)
567
569
  .end((err, res) => {
568
- // console.log(res.body.data);
570
+ // console.log(res.body);
569
571
  res.should.have.status(200);
570
572
  res.body.data[0].should.have.property("array_link");
571
573
  res.body.data[0].array_link.should.be.an("array");
@@ -708,7 +710,7 @@ describe('Test', () => {
708
710
  var query = [
709
711
  {
710
712
  $match: {
711
- "_id": `ObjectId(\"${objectid}\")`
713
+ "_id": `ObjectId("${objectid}")`
712
714
  }
713
715
  },
714
716
  { $group: { _id: null, count: { $sum: 1 } } }
@@ -719,7 +721,7 @@ describe('Test', () => {
719
721
  .send({ query })
720
722
  .end((err, res) => {
721
723
  res.should.have.status(200);
722
- console.log(res.body);
724
+ // console.log(res.body);
723
725
  res.body.data.should.be.an('array');
724
726
  res.body.data[0].should.have.property("_id");
725
727
  res.body.data[0].should.have.property("count");
@@ -832,21 +834,21 @@ describe('Test', () => {
832
834
  });
833
835
  });
834
836
  });
835
- it("should $push to an array", done => {
836
- chai.request(server)
837
- .patch("/api/test/" + post_id)
838
- .auth(init.email, init.password)
839
- .send({ $push: { shmack: "fah" } })
840
- .end((err, res) => {
841
- console.log(res.body.data);
842
- res.should.have.status(200);
843
- res.body.data.should.be.an('object');
844
- res.body.data.should.have.property("shmack")
845
- res.body.data.shmack.should.be.an("array");
846
- res.body.data.shmack.should.have.length(4);
847
- done();
848
- });
849
- });
837
+ // it("should $push to an array", done => {
838
+ // chai.request(server)
839
+ // .patch("/api/test/" + post_id)
840
+ // .auth(init.email, init.password)
841
+ // .send({ $push: { shmack: "fah" } })
842
+ // .end((err, res) => {
843
+ // // console.log(res.body.data);
844
+ // res.should.have.status(200);
845
+ // res.body.data.should.be.an('object');
846
+ // res.body.data.should.have.property("shmack")
847
+ // res.body.data.shmack.should.be.an("array");
848
+ // res.body.data.shmack.should.have.length(4);
849
+ // done();
850
+ // });
851
+ // });
850
852
  });
851
853
 
852
854
  describe("Models", () => {
@@ -868,6 +870,7 @@ describe('Test', () => {
868
870
  .del(`/api/test/${post_id}`)
869
871
  .auth(init.email, init.password)
870
872
  .end((err, res) => {
873
+ console.log(res.body);
871
874
  res.should.have.status(200);
872
875
  res.body.status.should.equal('ok');
873
876
  done();
@@ -878,9 +881,10 @@ describe('Test', () => {
878
881
  .get(`/api/test/${post_id}`)
879
882
  .auth(init.email, init.password)
880
883
  .end((err, res) => {
884
+ console.log(res.body);
881
885
  res.should.have.status(404);
882
886
  res.body.message.should.equal('Document is deleted');
883
- res.body.status.should.equal('error');
887
+ res.body.code.should.equal('NotFound');
884
888
  done();
885
889
  });
886
890
  });
@@ -982,8 +986,7 @@ describe('Test', () => {
982
986
  .auth(init.email, init.password)
983
987
  .end((err, res) => {
984
988
  res.should.have.status(409);
985
- res.body.message.should.equal(`Parent link item exists in test/link_id`);
986
- res.body.status.should.equal('error');
989
+ res.body.message.should.equal(`Conflict Error`);
987
990
  done();
988
991
  });
989
992
  });
@@ -992,7 +995,7 @@ describe('Test', () => {
992
995
  .del(`/api/link/${link_id}?_cascade=1`)
993
996
  .auth(init.email, init.password)
994
997
  .end((err, res) => {
995
- console.log(res.body, res.status);
998
+ // console.log(res.body, res.status);
996
999
  res.should.have.status(200);
997
1000
  res.body.status.should.equal('ok');
998
1001
  done();
@@ -1004,7 +1007,7 @@ describe('Test', () => {
1004
1007
  .auth(init.email, init.password)
1005
1008
  .end((err, res) => {
1006
1009
  res.should.have.status(404);
1007
- res.body.status.should.equal('error');
1010
+ res.body.message.should.equal('Document is deleted');
1008
1011
  done();
1009
1012
  });
1010
1013
  });
@@ -1060,8 +1063,7 @@ describe('Test', () => {
1060
1063
  .auth(init.email, init.password)
1061
1064
  .end((err, res) => {
1062
1065
  res.should.have.status(409);
1063
- res.body.message.should.equal(`Parent link item exists in test/link_id`);
1064
- res.body.status.should.equal('error');
1066
+ res.body.message.should.equal(`Conflict Error`);
1065
1067
  done();
1066
1068
  });
1067
1069
  });
@@ -1087,7 +1089,6 @@ describe('Test', () => {
1087
1089
  });
1088
1090
 
1089
1091
  describe("/Filter with +", () => {
1090
- let plus_user_id = null;
1091
1092
  it("it should POST a user with a + in email", (done) => {
1092
1093
  var user = {
1093
1094
  name: "Plus User",
@@ -1100,7 +1101,6 @@ describe('Test', () => {
1100
1101
  .end((err, res) => {
1101
1102
  res.should.have.status(200);
1102
1103
  res.body.data.should.have.property("_id");
1103
- plus_user_id = res.body.data._id;
1104
1104
  done();
1105
1105
  });
1106
1106
  });