jxp 2.12.4 → 2.13.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.
@@ -0,0 +1,12 @@
1
+ # Caching
2
+
3
+ Enable caching to speed up your queries. Caching is disabled by default. Enable caching through the config:
4
+
5
+ ```JSON
6
+ {
7
+ "cache": {
8
+ "enabled": true,
9
+ "ttl": 3600
10
+ }
11
+ }
12
+ ```
package/libs/cache.js ADDED
@@ -0,0 +1,47 @@
1
+ const NodeCache = require('node-cache');
2
+ const config = require('config');
3
+ const cache = new NodeCache({ stdTTL: config.cache?.ttl || 5 * 60 });
4
+
5
+ const generateKey = (req) => {
6
+ const url = req.url
7
+ return url
8
+ }
9
+
10
+ const set = (req, res, next) => {
11
+ if (!config.cache?.enabled) return next();
12
+ const key = generateKey(req)
13
+ cache.set(key, res.result)
14
+ next()
15
+ }
16
+
17
+ const get = (req, res, next) => {
18
+ if (!config.cache?.enabled) return next();
19
+ const key = generateKey(req)
20
+ const cached = cache.get(key)
21
+ if (cached) {
22
+ res.header('jxp-cache', 'hit')
23
+ res.result = cached
24
+ return res.send(res.result)
25
+ }
26
+ res.header('jxp-cache', 'miss')
27
+ next()
28
+ }
29
+
30
+ const clear = (req, res, next) => {
31
+ if (!config.cache?.enabled) return next();
32
+ cache.flushAll()
33
+ next()
34
+ }
35
+
36
+ const stats = (req, res, next) => {
37
+ if (!config.cache?.enabled) return {};
38
+ res.result = cache.getStats()
39
+ next()
40
+ }
41
+
42
+ module.exports = {
43
+ set,
44
+ get,
45
+ clear,
46
+ stats
47
+ }
package/libs/jxp.js CHANGED
@@ -15,6 +15,7 @@ const modeldir = require("./modeldir");
15
15
  const query_manipulation = require("./query_manipulation");
16
16
  const corsMiddleware = require('restify-cors-middleware2');
17
17
  const json2csv = require('json2csv').parse;
18
+ const cache = require("./cache");
18
19
  global.JXPSchema = require("./schema");
19
20
 
20
21
  var models = {};
@@ -202,7 +203,7 @@ const actionGetOne = async (req, res) => {
202
203
  console.time(opname);
203
204
  try {
204
205
  const data = await getOne(req.Model, req.params.item_id, req.query, { user: res.user });
205
- res.send({ data });
206
+ res.result = { data };
206
207
  if (debug) console.timeEnd(opname);
207
208
  } catch(err) {
208
209
  console.error(new Date(), err);
@@ -908,7 +909,9 @@ const JXP = function(options) {
908
909
  security.login,
909
910
  security.auth,
910
911
  config.pre_hooks.get,
912
+ cache.get,
911
913
  actionGet,
914
+ cache.set,
912
915
  outputJSON
913
916
  );
914
917
  server.get(
@@ -916,8 +919,12 @@ const JXP = function(options) {
916
919
  middlewareModel,
917
920
  security.login,
918
921
  security.auth,
922
+ cache.get,
919
923
  config.pre_hooks.getOne,
920
- actionGetOne
924
+ cache.get,
925
+ actionGetOne,
926
+ cache.set,
927
+ outputJSON
921
928
  );
922
929
  server.post(
923
930
  "/api/:modelname",
@@ -927,6 +934,7 @@ const JXP = function(options) {
927
934
  middlewarePasswords,
928
935
  config.pre_hooks.post,
929
936
  actionPost,
937
+ cache.clear,
930
938
  (req, res, next) => {
931
939
  next();
932
940
  },
@@ -940,6 +948,7 @@ const JXP = function(options) {
940
948
  middlewareCheckAdmin,
941
949
  config.pre_hooks.put,
942
950
  actionPut,
951
+ cache.clear,
943
952
  );
944
953
  server.del(
945
954
  "/api/:modelname/:item_id",
@@ -948,6 +957,7 @@ const JXP = function(options) {
948
957
  security.auth,
949
958
  config.pre_hooks.delete,
950
959
  actionDelete,
960
+ cache.clear,
951
961
  );
952
962
 
953
963
  // Count
@@ -957,7 +967,9 @@ const JXP = function(options) {
957
967
  security.login,
958
968
  security.auth,
959
969
  config.pre_hooks.get,
970
+ cache.get,
960
971
  actionCount,
972
+ cache.set,
961
973
  outputJSON
962
974
  );
963
975
 
@@ -1009,6 +1021,7 @@ const JXP = function(options) {
1009
1021
  middlewareCheckAdmin,
1010
1022
  config.pre_hooks.update,
1011
1023
  actionUpdate,
1024
+ cache.clear,
1012
1025
  );
1013
1026
 
1014
1027
  /* Batch routes - ROLLED BACK FOR NOW */
@@ -1020,21 +1033,24 @@ const JXP = function(options) {
1020
1033
  middlewareModel,
1021
1034
  security.login,
1022
1035
  security.auth,
1023
- actionCall
1036
+ actionCall,
1037
+ cache.clear,
1024
1038
  );
1025
1039
  server.post(
1026
1040
  "/call/:modelname/:method_name",
1027
1041
  middlewareModel,
1028
1042
  security.login,
1029
1043
  security.auth,
1030
- actionCall
1044
+ actionCall,
1045
+ cache.clear,
1031
1046
  );
1032
1047
  server.get(
1033
1048
  "/call/:modelname/:item_id/:method_name",
1034
1049
  middlewareModel,
1035
1050
  security.login,
1036
1051
  security.auth,
1037
- actionCallItem
1052
+ actionCallItem,
1053
+ cache.clear,
1038
1054
  );
1039
1055
 
1040
1056
  /* Login and authentication */
@@ -1081,6 +1097,11 @@ const JXP = function(options) {
1081
1097
 
1082
1098
  /* Websocket */
1083
1099
  server.on("upgrade", ws.upgrade)
1100
+
1101
+ /* Cache */
1102
+ server.get("/cache/stats", cache.stats, outputJSON);
1103
+ server.get("/cache/clear", cache.clear, outputJSON);
1104
+
1084
1105
  return server;
1085
1106
  };
1086
1107
 
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.12.4",
4
+ "version": "2.13.0",
5
5
  "private": false,
6
6
  "main": "libs/jxp.js",
7
7
  "scripts": {
@@ -45,6 +45,7 @@
45
45
  "mongoose": "6.8.4",
46
46
  "mongoose-friendly": "^0.1.4",
47
47
  "morgan": "^1.10.0",
48
+ "node-cache": "^5.1.2",
48
49
  "nodemailer": "^6.9.0",
49
50
  "nodemailer-smtp-transport": "^2.7.4",
50
51
  "path": "^0.12.7",
package/test/test.js CHANGED
@@ -127,7 +127,6 @@ describe('Test', () => {
127
127
  .get("/api/user")
128
128
  .auth(init.email, init.password)
129
129
  .end((err, res) => {
130
- // console.log(res.body);
131
130
  res.should.have.status(200);
132
131
  res.body.data.should.be.an('array');
133
132
  done();
@@ -166,7 +165,6 @@ describe('Test', () => {
166
165
  chai.request(server)
167
166
  .get(`/api/user`)
168
167
  .end((err, res) => {
169
- // console.log(res.body);
170
168
  res.should.have.status(403);
171
169
  done();
172
170
  });
@@ -374,7 +372,6 @@ describe('Test', () => {
374
372
  .get(`/api/test/${post_id}?populate=link`)
375
373
  .auth(init.email, init.password)
376
374
  .end((err, res) => {
377
- // console.log(res.body);
378
375
  res.should.have.status(200);
379
376
  res.body.should.have.property("data");
380
377
  res.body.data.should.have.property("link");
@@ -391,7 +388,6 @@ describe('Test', () => {
391
388
  .get(`/api/test/${post_id}?populate=other_link`)
392
389
  .auth(init.email, init.password)
393
390
  .end((err, res) => {
394
- // console.log(res.body);
395
391
  res.should.have.status(200);
396
392
  res.body.should.have.property("data");
397
393
  res.body.data.should.have.property("other_link")
@@ -408,7 +404,6 @@ describe('Test', () => {
408
404
  .get(`/api/test?autopopulate=true`)
409
405
  .auth(init.email, init.password)
410
406
  .end((err, res) => {
411
- // console.log(res.body);
412
407
  res.should.have.status(200);
413
408
  res.body.data[0].should.have.property("link")
414
409
  res.body.data[0].link.should.be.an('object');
@@ -426,7 +421,6 @@ describe('Test', () => {
426
421
  .get(`/api/test/${post_id}?autopopulate=true`)
427
422
  .auth(init.email, init.password)
428
423
  .end((err, res) => {
429
- // console.log(res.body);
430
424
  res.should.have.status(200);
431
425
  res.body.data.should.have.property("link")
432
426
  res.body.data.link.should.be.an('object');
@@ -444,7 +438,6 @@ describe('Test', () => {
444
438
  .get(`/api/test/${post_id}?populate=link`)
445
439
  .auth(init.email, init.password)
446
440
  .end((err, res) => {
447
- console.log(res.body);
448
441
  res.should.have.status(200);
449
442
  res.body.should.have.property("data");
450
443
  res.body.data.should.have.property("link")
@@ -573,7 +566,6 @@ describe('Test', () => {
573
566
  .get(`/api/test?populate=array_link`)
574
567
  .auth(init.email, init.password)
575
568
  .end((err, res) => {
576
- // console.log(res.body);
577
569
  res.should.have.status(200);
578
570
  res.body.data[0].should.have.property("array_link");
579
571
  res.body.data[0].array_link.should.be.an("array");
@@ -727,7 +719,6 @@ describe('Test', () => {
727
719
  .send({ query })
728
720
  .end((err, res) => {
729
721
  res.should.have.status(200);
730
- // console.log(res.body);
731
722
  res.body.data.should.be.an('array');
732
723
  res.body.data[0].should.have.property("_id");
733
724
  res.body.data[0].should.have.property("count");
@@ -876,7 +867,6 @@ describe('Test', () => {
876
867
  .del(`/api/test/${post_id}`)
877
868
  .auth(init.email, init.password)
878
869
  .end((err, res) => {
879
- // console.log(res.body);
880
870
  res.should.have.status(200);
881
871
  res.body.status.should.equal('ok');
882
872
  done();
@@ -887,7 +877,6 @@ describe('Test', () => {
887
877
  .get(`/api/test/${post_id}`)
888
878
  .auth(init.email, init.password)
889
879
  .end((err, res) => {
890
- // console.log(res.body);
891
880
  res.should.have.status(404);
892
881
  res.body.message.should.equal(`Document ${post_id} is deleted on Test`);
893
882
  res.body.code.should.equal('NotFound');
@@ -992,7 +981,6 @@ describe('Test', () => {
992
981
  .auth(init.email, init.password)
993
982
  .end((err, res) => {
994
983
  res.should.have.status(409);
995
- // console.log(res.body.message);
996
984
  res.body.message.should.equal(`Parent link item exists in test/link_id`);
997
985
  done();
998
986
  });
@@ -1002,7 +990,6 @@ describe('Test', () => {
1002
990
  .del(`/api/link/${link_id}?_cascade=1`)
1003
991
  .auth(init.email, init.password)
1004
992
  .end((err, res) => {
1005
- // console.log(res.body, res.status);
1006
993
  res.should.have.status(200);
1007
994
  res.body.status.should.equal('ok');
1008
995
  done();
@@ -1133,12 +1120,66 @@ describe('Test', () => {
1133
1120
  bar: "Throw an error"
1134
1121
  })
1135
1122
  .end((err, res) => {
1136
- // console.log(res.body);
1137
- console.log(res.headers);
1138
1123
  res.should.have.status(418);
1139
1124
  res.body.message.should.equal(`I'm a teapot`);
1140
1125
  done();
1141
1126
  });
1142
1127
  })
1143
1128
  });
1129
+
1130
+ describe("Caching", () => {
1131
+ it ("should give us cache stats", (done) => {
1132
+ chai.request(server)
1133
+ .get("/cache/stats")
1134
+ .end((err, res) => {
1135
+ // console.log(res.body);
1136
+ res.should.have.status(200);
1137
+ res.body.should.have.property("hits");
1138
+ done();
1139
+ });
1140
+ })
1141
+ it ("should clear the cache stats", (done) => {
1142
+ chai.request(server)
1143
+ .get("/cache/clear")
1144
+ .end((err, res) => {
1145
+ console.log(res.body);
1146
+ res.should.have.status(200);
1147
+ done();
1148
+ });
1149
+ });
1150
+ it ("should get an uncached request", (done) => {
1151
+ chai.request(server)
1152
+ .get("/api/test")
1153
+ .auth(init.email, init.password)
1154
+ .end((err, res) => {
1155
+ res.should.have.status(200);
1156
+ res.headers.should.have.property("jxp-cache");
1157
+ res.headers["jxp-cache"].should.equal("miss");
1158
+ done();
1159
+ });
1160
+ });
1161
+ it ("should get an cached request", (done) => {
1162
+ chai.request(server)
1163
+ .get("/api/test")
1164
+ .auth(init.email, init.password)
1165
+ .end((err, res) => {
1166
+ res.should.have.status(200);
1167
+ res.headers.should.have.property("jxp-cache");
1168
+ res.headers["jxp-cache"].should.equal("hit");
1169
+ done();
1170
+ });
1171
+ });
1172
+ it ("should give us cache stats", (done) => {
1173
+ chai.request(server)
1174
+ .get("/cache/stats")
1175
+ .end((err, res) => {
1176
+ // console.log(res.body);
1177
+ res.should.have.status(200);
1178
+ res.body.should.have.property("hits");
1179
+ res.body.hits.should.equal(1);
1180
+ res.body.misses.should.equal(1);
1181
+ done();
1182
+ });
1183
+ })
1184
+ });
1144
1185
  });