@sera4/essentia 1.0.38 → 1.0.40

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/cache/index.js ADDED
@@ -0,0 +1,188 @@
1
+ "use strict"
2
+
3
+ import { promisify } from "util";
4
+
5
+ const isEmpty = obj => [Object, Array].includes((obj || {}).constructor) && !Object.entries((obj || {})).length;
6
+
7
+ class CacheConfigError extends Error {
8
+ constructor(m) {
9
+ super(m);
10
+ }
11
+ }
12
+
13
+ /* Redis cache helper
14
+ * Example use: Place the following code in your project to setup redis use
15
+ *
16
+ * import { cache } from "@sera4/essentia";
17
+ * const cacheHelper = cache({ password: .., database: .., host: .., port: .. }, "service:"));
18
+ *
19
+ */
20
+
21
+ const convertRedisKeyValueToJson = (v) => {
22
+ return (v && v.length > 0 && v[0] !== null) ? JSON.parse(v) : null;
23
+ }
24
+
25
+ const convertRedisHashToJson = (v) => {
26
+ if (v) {
27
+ var result = {};
28
+ for(var k in v) {
29
+ if (v.hasOwnProperty(k)) {
30
+ result[k] = JSON.parse(v[k]);
31
+ }
32
+ }
33
+ return result;
34
+ } else {
35
+ return null;
36
+ }
37
+ }
38
+
39
+ function convertJsonToRedisValue(j) {
40
+ var result = {};
41
+ for (var key in j) {
42
+ if (j.hasOwnProperty(key)) {
43
+ result[key] = JSON.stringify(j[key]);
44
+ }
45
+ }
46
+ return result;
47
+ }
48
+
49
+ class Cache {
50
+ constructor(config, servicePrefix) {
51
+ if ((config == undefined) || isEmpty(config)) {
52
+ throw new CacheConfigError("Invalid cache configuration");
53
+ }
54
+
55
+ if (servicePrefix == null) {
56
+ throw new CacheConfigError("Invalid service prefix");
57
+ }
58
+
59
+ if (config.fake) {
60
+ // used in testing
61
+ this.redisClient = require("fakeredis").createClient("fake backend")
62
+ } else {
63
+ this.redisClient = require("redis").createClient(config);
64
+ }
65
+
66
+ this.redisClient.on("error", (err) => {
67
+ logger.error("Cache/Redis error: ", err);
68
+ });
69
+
70
+ this.servicePrefix = servicePrefix;
71
+ }
72
+
73
+ ping() {
74
+ return this.redisClient.ping();
75
+ }
76
+
77
+ properKey(key, path) {
78
+ let p = this.servicePrefix + key
79
+ if (path)
80
+ p += path
81
+ return p
82
+ }
83
+
84
+ writeHash(path, key, value) {
85
+ return this.redisClient.hsetAsync(properKey(key), key, value);
86
+ }
87
+
88
+ writeHashBlocking(path, key, value) {
89
+ return this.redisClient.hset(properKey(key), key, value);
90
+ }
91
+
92
+ // write multiple hash keys and stringify every value
93
+ writeMultiHash(path, j) {
94
+ return this.redisClient.hmsetAsync(properKey(key), convertJsonToRedisValue(j));
95
+ }
96
+
97
+ readHashKey(obj, key, autoConvert=true) {
98
+ var k = autoConvert;
99
+
100
+ return new Promise((resolve, reject) => {
101
+ this.redisClient.hget(properKey(key, obj), key, (e, r) => {
102
+ if (e) {
103
+ logger.error("Redis hmget error:", e);
104
+ reject(e);
105
+ } else {
106
+ resolve(k ? convertRedisKeyValueToJson(r) : r);
107
+ }
108
+ });
109
+ });
110
+ }
111
+
112
+ readHash(path) {
113
+ return new Promise((resolve, reject) => {
114
+ this.redisClient.hgetall(properKey(key, path), (e, r) => {
115
+ if (e) {
116
+ console.log("Redis hget error:", e);
117
+ reject(e);
118
+ } else {
119
+ resolve(convertRedisHashToJson(r));
120
+ }
121
+ });
122
+ });
123
+ }
124
+
125
+ deleteHashKey(hash, key) {
126
+ return new Promise((resolve, reject) => {
127
+ this.redisClient.hdel(properKey(key, hash), key, (err) => {
128
+ if (err) {
129
+ logger.error("Redis hdel error:", err);
130
+ reject(err);
131
+ } else {
132
+ resolve();
133
+ }
134
+ });
135
+ });
136
+ }
137
+
138
+ get(key) {
139
+ return this.redisClient.getAsync(properKey(key));
140
+ }
141
+
142
+ getAsync(key) {
143
+ return promisify(this.redisClient.get(key)).bind(this.redisClient)
144
+ }
145
+
146
+
147
+ /* a single key write with optional expiry */
148
+ // returns bluebird promise
149
+ writeKey(key, value, expiry) {
150
+ key = this.servicePrefix + key;
151
+ if (expiry) {
152
+ return this.redisClient.setAsync(key, value, "EX", expiry);
153
+ } else {
154
+ return this.redisClient.setAsync(key, value);
155
+ }
156
+ }
157
+
158
+ deleteKey(key) {
159
+ return redisClient.delAsync(properKey(key));
160
+ }
161
+
162
+ expire(path, expiry) {
163
+ return this.redisClient.expire(path, expiry);
164
+ }
165
+
166
+ exists(path) {
167
+ return this.redisClient.exists(path);
168
+ }
169
+
170
+ deleteAllKeys() {
171
+ return new Promise((resolve, reject) => {
172
+ if(process.env.NODE_ENV === "production") {
173
+ throw new Error("Clearing system cache is not permitted in production");
174
+ }
175
+ this.redisClient.flushdb((err, res) => {
176
+ if (err) {
177
+ reject(err);
178
+ } else {
179
+ resolve(res);
180
+ }
181
+ });
182
+ });
183
+ }
184
+ }
185
+
186
+ export default function(config, servicePrefix) {
187
+ return new Cache(config, servicePrefix)
188
+ }
package/health/index.js CHANGED
@@ -35,7 +35,8 @@ class HealthCheck {
35
35
  this.healthChecks = [];
36
36
  }
37
37
 
38
- /* call this with an async function, must support wait capability
38
+ /* Deprecated and replaced by setupCheck
39
+ * call this with an async function, must support wait capability
39
40
  *
40
41
  * const dbCheck = async () => {
41
42
  * return { name: "db", "result": "success", critical: false };
@@ -62,12 +63,15 @@ class HealthCheck {
62
63
  return router
63
64
  }
64
65
 
65
- // v2 version of health check
66
+ // v2 version of health check, replaced addCheck
67
+ // example:
68
+ // const checkRedisFcn = async () => { await cache.ping().then(() => true) }
69
+ // setupCheck({ name: "redis", checkResult: false, critical: false, healthFunction: checkRedisFcn });
66
70
  setupCheck(config) {
67
71
  this.healthChecks.push(config);
68
72
  }
69
73
 
70
- /* performs health checks and returns 200 if true and 500 if not */
74
+ /* performs health checks and returns 200 if true and 500 if not */
71
75
  async checkHealth(req, res) {
72
76
  const checkFormat = pathHelper.extname(req.baseUrl);
73
77
 
@@ -82,7 +86,7 @@ class HealthCheck {
82
86
  } else {
83
87
  let res = { name: hc.name, critical: hc.critical }
84
88
  try {
85
- return await hc.healthFunction();
89
+ await hc.healthFunction();
86
90
  return { ...res, "result": "success" };
87
91
  } catch(e) {
88
92
  return { ...res, "result": "fail", status: e.status }
@@ -126,16 +130,16 @@ class HealthCheck {
126
130
  ... payload,
127
131
  "healthy": false,
128
132
  "message": "fail",
129
- "services": [result]
133
+ "services": result
130
134
  });
131
- } else if (status === 200) {
132
- res.status(status).json(payload);
133
- } else if (nonCriticalFailure) {
135
+ } else if (nonCriticalFail) {
134
136
  res.status(status).json({
135
137
  ... payload,
136
138
  "message": "fail",
137
- "services": [result]
139
+ "services": result
138
140
  });
141
+ } else if (status === 200) {
142
+ res.status(status).json(payload);
139
143
  }
140
144
  } else {
141
145
  let status_message;
@@ -148,6 +152,7 @@ class HealthCheck {
148
152
  res.status(status).send(status_message);
149
153
  }
150
154
  } catch(error) {
155
+ console.debug("error: ", error)
151
156
  res.status(500).send();
152
157
  }
153
158
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sera4/essentia",
3
- "version": "1.0.38",
3
+ "version": "1.0.40",
4
4
  "description": "A library of utilities for Teleporte Web Services",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/package.tar.gz CHANGED
Binary file