@adaptivestone/framework 3.0.19 → 3.0.21
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/.env.example +3 -0
- package/CHANGELOG.md +7 -2
- package/commands/GetOpenApiJson.js +1 -1
- package/config/redis.js +1 -0
- package/helpers/redis/clearNamespace.js +14 -0
- package/modules/AbstractController.js +8 -0
- package/package.json +2 -2
- package/services/cache/Cache.js +14 -6
- package/services/http/middleware/RateLimiter.js +17 -5
- package/tests/setup.js +27 -1
package/.env.example
CHANGED
package/CHANGELOG.md
CHANGED
package/config/redis.js
CHANGED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
async function clearNamespace(redisClient, namespace) {
|
|
2
|
+
const deletedKeys = [];
|
|
3
|
+
|
|
4
|
+
const keys = await redisClient.sendCommand(['keys', `*${namespace}*`]);
|
|
5
|
+
|
|
6
|
+
if (keys && keys.length > 0) {
|
|
7
|
+
for (const key of keys) {
|
|
8
|
+
deletedKeys.push(redisClient.sendCommand(['del', key]));
|
|
9
|
+
}
|
|
10
|
+
await Promise.all(deletedKeys);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
module.exports = clearNamespace;
|
|
@@ -253,6 +253,14 @@ class AbstractController extends Base {
|
|
|
253
253
|
},
|
|
254
254
|
});
|
|
255
255
|
|
|
256
|
+
if (!routeObject.handler) {
|
|
257
|
+
this.logger.error(`Route object have no handler defined`);
|
|
258
|
+
return res.status(500).json({
|
|
259
|
+
message:
|
|
260
|
+
'Platform error 2. Please check later or contact support',
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
|
|
256
264
|
if (routeObject.handler.constructor.name !== 'AsyncFunction') {
|
|
257
265
|
const error =
|
|
258
266
|
"Handler should be AsyncFunction. Perhabs you miss 'async' of function declaration?";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adaptivestone/framework",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.21",
|
|
4
4
|
"description": "Adaptive stone node js framework",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"engines": {
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"prettier": "^2.3.2",
|
|
48
48
|
"pug": "^3.0.2",
|
|
49
49
|
"rate-limiter-flexible": "^2.2.4",
|
|
50
|
-
"redis": "^3.1
|
|
50
|
+
"redis": "^4.3.1",
|
|
51
51
|
"winston": "^3.3.3",
|
|
52
52
|
"winston-transport-sentry-node": "^2.0.0",
|
|
53
53
|
"yup": "^0.32.9"
|
package/services/cache/Cache.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
const redis = require('redis');
|
|
2
|
-
const { promisify } = require('util');
|
|
3
2
|
const Base = require('../../modules/Base');
|
|
4
3
|
|
|
5
4
|
class Cache extends Base {
|
|
@@ -10,7 +9,12 @@ class Cache extends Base {
|
|
|
10
9
|
// we should support multiple cashe same time
|
|
11
10
|
super(app);
|
|
12
11
|
const conf = this.app.getConfig('redis');
|
|
13
|
-
this.redisClient = redis.createClient(
|
|
12
|
+
this.redisClient = redis.createClient({
|
|
13
|
+
url: conf.url,
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
this.redisNamespace = conf.namespace;
|
|
17
|
+
|
|
14
18
|
this.redisClient.on('error', (error, b, c) => {
|
|
15
19
|
this.logger.error(error, b, c);
|
|
16
20
|
});
|
|
@@ -20,11 +24,15 @@ class Cache extends Base {
|
|
|
20
24
|
this.app.events.on('shutdown', () => {
|
|
21
25
|
this.redisClient.quit();
|
|
22
26
|
});
|
|
23
|
-
|
|
27
|
+
|
|
24
28
|
this.promiseMapping = new Map();
|
|
25
29
|
}
|
|
26
30
|
|
|
27
|
-
async getSetValue(
|
|
31
|
+
async getSetValue(keyValue, onNotFound, storeTime = 60 * 5) {
|
|
32
|
+
if (!this.redisClient.isOpen) {
|
|
33
|
+
await this.redisClient.connect();
|
|
34
|
+
}
|
|
35
|
+
const key = `${this.redisNamespace}-${keyValue}`;
|
|
28
36
|
// 5 mins default
|
|
29
37
|
let resolve = null;
|
|
30
38
|
if (this.promiseMapping.has(key)) {
|
|
@@ -38,11 +46,11 @@ class Cache extends Base {
|
|
|
38
46
|
}),
|
|
39
47
|
);
|
|
40
48
|
|
|
41
|
-
let result = await this.
|
|
49
|
+
let result = await this.redisClient.get(key);
|
|
42
50
|
if (!result) {
|
|
43
51
|
this.logger.verbose(`getSetValueFromCache not found for key ${key}`);
|
|
44
52
|
result = await onNotFound();
|
|
45
|
-
this.redisClient.
|
|
53
|
+
this.redisClient.setEx(key, storeTime, JSON.stringify(result));
|
|
46
54
|
} else {
|
|
47
55
|
this.logger.verbose(
|
|
48
56
|
`getSetValueFromCache FROM CACHE key ${key}, value ${result}`,
|
|
@@ -16,9 +16,10 @@ class RateLimiter extends AbstractMiddleware {
|
|
|
16
16
|
|
|
17
17
|
constructor(app, params) {
|
|
18
18
|
super(app, params);
|
|
19
|
+
const routeParams = params;
|
|
19
20
|
const limiterOptions = this.app.getConfig('rateLimiter');
|
|
20
|
-
this.finalOptions = merge(limiterOptions, params);
|
|
21
21
|
|
|
22
|
+
this.finalOptions = merge(limiterOptions, routeParams);
|
|
22
23
|
this.limiter = null;
|
|
23
24
|
|
|
24
25
|
switch (this.finalOptions.driver) {
|
|
@@ -47,7 +48,16 @@ class RateLimiter extends AbstractMiddleware {
|
|
|
47
48
|
|
|
48
49
|
initRedisLimiter() {
|
|
49
50
|
const redisConfig = this.app.getConfig('redis');
|
|
50
|
-
const redisClient = redis.createClient(
|
|
51
|
+
const redisClient = redis.createClient({
|
|
52
|
+
url: redisConfig.url,
|
|
53
|
+
legacyMode: true,
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// TODO: change it
|
|
57
|
+
(async () => {
|
|
58
|
+
await redisClient.connect();
|
|
59
|
+
})();
|
|
60
|
+
|
|
51
61
|
redisClient.on('error', (error, b, c) => {
|
|
52
62
|
this.logger.error(error, b, c);
|
|
53
63
|
});
|
|
@@ -55,8 +65,8 @@ class RateLimiter extends AbstractMiddleware {
|
|
|
55
65
|
this.logger.info('Redis connection success');
|
|
56
66
|
});
|
|
57
67
|
|
|
58
|
-
this.app.events.on('shutdown', () => {
|
|
59
|
-
redisClient.
|
|
68
|
+
this.app.events.on('shutdown', async () => {
|
|
69
|
+
await redisClient.disconnect();
|
|
60
70
|
});
|
|
61
71
|
|
|
62
72
|
return new RateLimiterRedis({
|
|
@@ -100,7 +110,9 @@ class RateLimiter extends AbstractMiddleware {
|
|
|
100
110
|
);
|
|
101
111
|
}
|
|
102
112
|
|
|
103
|
-
const
|
|
113
|
+
const { namespace } = this.app.getConfig('redis');
|
|
114
|
+
|
|
115
|
+
const consumeKey = `${namespace}-${this.gerenateConsumeKey(req)}`;
|
|
104
116
|
|
|
105
117
|
const consumeResult = await this.limiter
|
|
106
118
|
.consume(consumeKey, this.finalOptions.consumePoints)
|
package/tests/setup.js
CHANGED
|
@@ -4,8 +4,11 @@ const mongoose = require('mongoose');
|
|
|
4
4
|
|
|
5
5
|
let mongoMemoryServerInstance;
|
|
6
6
|
const path = require('path');
|
|
7
|
+
const redis = require('redis');
|
|
7
8
|
const Server = require('../server');
|
|
8
9
|
|
|
10
|
+
const clearRadisNamespace = require('../helpers/redis/clearNamespace');
|
|
11
|
+
|
|
9
12
|
jest.setTimeout(1000000);
|
|
10
13
|
beforeAll(async () => {
|
|
11
14
|
mongoMemoryServerInstance = await MongoMemoryReplSet.create({
|
|
@@ -15,7 +18,7 @@ beforeAll(async () => {
|
|
|
15
18
|
await mongoMemoryServerInstance.waitUntilRunning();
|
|
16
19
|
process.env.LOGGER_CONSOLE_LEVEL = 'error';
|
|
17
20
|
const connectionStringMongo = await mongoMemoryServerInstance.getUri();
|
|
18
|
-
console.info('MONGO_URI: ', connectionStringMongo);
|
|
21
|
+
// console.info('MONGO_URI: ', connectionStringMongo);
|
|
19
22
|
global.server = new Server({
|
|
20
23
|
folders: {
|
|
21
24
|
config: process.env.TEST_FOLDER_CONFIG || path.resolve('./config'),
|
|
@@ -63,6 +66,27 @@ beforeAll(async () => {
|
|
|
63
66
|
}
|
|
64
67
|
await global.server.startServer();
|
|
65
68
|
});
|
|
69
|
+
|
|
70
|
+
beforeEach(() => {
|
|
71
|
+
if (global.server) {
|
|
72
|
+
const key = `test-${Math.random().toString(36).substring(7)}`;
|
|
73
|
+
global.server.app.updateConfig('redis', {
|
|
74
|
+
namespace: key,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
afterEach(async () => {
|
|
80
|
+
if (global.server) {
|
|
81
|
+
const { url, namespace } = global.server.getConfig('redis');
|
|
82
|
+
const redisClient = redis.createClient({ url });
|
|
83
|
+
|
|
84
|
+
await redisClient.connect();
|
|
85
|
+
await clearRadisNamespace(redisClient, namespace);
|
|
86
|
+
await redisClient.disconnect();
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
|
|
66
90
|
afterAll(async () => {
|
|
67
91
|
if (global.server) {
|
|
68
92
|
global.server.app.httpServer.shutdown();
|
|
@@ -72,7 +96,9 @@ afterAll(async () => {
|
|
|
72
96
|
if (typeof global.testSetup.afterAll === 'function') {
|
|
73
97
|
await global.testSetup.afterAll();
|
|
74
98
|
}
|
|
99
|
+
|
|
75
100
|
await mongoose.disconnect();
|
|
76
101
|
await mongoMemoryServerInstance.stop();
|
|
102
|
+
|
|
77
103
|
// }, 2000);
|
|
78
104
|
});
|