@adaptivestone/framework 3.0.20 → 3.0.22
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 +11 -0
- package/commands/GetOpenApiJson.js +1 -1
- package/config/redis.js +1 -0
- package/helpers/redis/clearNamespace.js +14 -0
- package/modules/AbstractController.js +6 -7
- package/package.json +9 -4
- package/services/cache/Cache.js +14 -6
- package/services/http/middleware/RateLimiter.js +17 -5
- package/tests/setup.js +26 -0
package/.env.example
CHANGED
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
### 3.1.0 // NEXT
|
|
2
2
|
|
|
3
3
|
[NEW] new comand to generate open API documentation (wip)
|
|
4
|
+
[NEW] coverage report
|
|
5
|
+
|
|
6
|
+
### 3.0.22
|
|
7
|
+
|
|
8
|
+
[UPDATE] updated deps
|
|
9
|
+
[UPDATE] cast function now can be ASYNC too
|
|
10
|
+
|
|
11
|
+
### 3.0.21
|
|
12
|
+
|
|
13
|
+
[UPDATE] updated redis to v4
|
|
14
|
+
[NEW] updates tests to have own namespace on redis
|
|
4
15
|
|
|
5
16
|
### 3.0.20
|
|
6
17
|
|
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;
|
|
@@ -110,8 +110,6 @@ class AbstractController extends Base {
|
|
|
110
110
|
|
|
111
111
|
const middlewaresInfo = parseMiddlewares(this.constructor.middleware);
|
|
112
112
|
const routesInfo = [];
|
|
113
|
-
let routeObjectClone = {};
|
|
114
|
-
const routeObjests = [];
|
|
115
113
|
|
|
116
114
|
/**
|
|
117
115
|
* Register controller middleware
|
|
@@ -240,9 +238,12 @@ class AbstractController extends Base {
|
|
|
240
238
|
errors: errorAnswer,
|
|
241
239
|
});
|
|
242
240
|
}
|
|
243
|
-
req.appInfo.request = routeObject.request.cast(
|
|
244
|
-
|
|
245
|
-
|
|
241
|
+
req.appInfo.request = await routeObject.request.cast(
|
|
242
|
+
bodyAndQuery,
|
|
243
|
+
{
|
|
244
|
+
stripUnknown: true,
|
|
245
|
+
},
|
|
246
|
+
);
|
|
246
247
|
}
|
|
247
248
|
req.body = new Proxy(req.body, {
|
|
248
249
|
get: (target, prop) => {
|
|
@@ -280,8 +281,6 @@ class AbstractController extends Base {
|
|
|
280
281
|
});
|
|
281
282
|
},
|
|
282
283
|
);
|
|
283
|
-
|
|
284
|
-
routeObjectClone = merge({}, routeObject);
|
|
285
284
|
}
|
|
286
285
|
}
|
|
287
286
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adaptivestone/framework",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.22",
|
|
4
4
|
"description": "Adaptive stone node js framework",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"engines": {
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"scripts": {
|
|
15
15
|
"dev": "nodemon ./index.js",
|
|
16
16
|
"prod": "nodemon ./cluster.js",
|
|
17
|
-
"test": "jest",
|
|
17
|
+
"test": "jest --coverage=true",
|
|
18
18
|
"codestyle": "prettier --check '**/*.(js|jsx|ts|tsx|json|css|scss|md)'",
|
|
19
19
|
"prepare": "husky install",
|
|
20
20
|
"cli": "node cliCommand"
|
|
@@ -24,7 +24,12 @@
|
|
|
24
24
|
"./tests/setup.js"
|
|
25
25
|
],
|
|
26
26
|
"testEnvironment": "node",
|
|
27
|
-
"verbose": true
|
|
27
|
+
"verbose": true,
|
|
28
|
+
"collectCoverage": true,
|
|
29
|
+
"coverageReporters": [
|
|
30
|
+
"text",
|
|
31
|
+
"text-summary"
|
|
32
|
+
]
|
|
28
33
|
},
|
|
29
34
|
"author": "Andrey Logunov",
|
|
30
35
|
"license": "MIT",
|
|
@@ -47,7 +52,7 @@
|
|
|
47
52
|
"prettier": "^2.3.2",
|
|
48
53
|
"pug": "^3.0.2",
|
|
49
54
|
"rate-limiter-flexible": "^2.2.4",
|
|
50
|
-
"redis": "^3.1
|
|
55
|
+
"redis": "^4.3.1",
|
|
51
56
|
"winston": "^3.3.3",
|
|
52
57
|
"winston-transport-sentry-node": "^2.0.0",
|
|
53
58
|
"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({
|
|
@@ -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
|
});
|