@withjoy/limiter 0.1.3 → 0.1.4-test
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 +4 -0
- package/limitd-redis/LICENSE +21 -0
- package/limitd-redis/README.md +183 -0
- package/limitd-redis/docker-compose.yml +11 -0
- package/limitd-redis/index.js +2 -0
- package/limitd-redis/lib/cb.js +45 -0
- package/limitd-redis/lib/client.js +135 -0
- package/limitd-redis/lib/db.js +501 -0
- package/limitd-redis/lib/db_ping.js +106 -0
- package/limitd-redis/lib/put.lua +31 -0
- package/limitd-redis/lib/take.lua +48 -0
- package/limitd-redis/lib/utils.js +116 -0
- package/limitd-redis/lib/validation.js +64 -0
- package/limitd-redis/node_modules/lru-cache/LICENSE +15 -0
- package/limitd-redis/node_modules/lru-cache/README.md +158 -0
- package/limitd-redis/node_modules/lru-cache/index.js +468 -0
- package/limitd-redis/node_modules/lru-cache/package.json +74 -0
- package/limitd-redis/node_modules/ms/index.js +162 -0
- package/limitd-redis/node_modules/ms/license.md +21 -0
- package/limitd-redis/node_modules/ms/package.json +73 -0
- package/limitd-redis/node_modules/ms/readme.md +59 -0
- package/limitd-redis/node_modules/yallist/LICENSE +15 -0
- package/limitd-redis/node_modules/yallist/README.md +204 -0
- package/limitd-redis/node_modules/yallist/iterator.js +7 -0
- package/limitd-redis/node_modules/yallist/package.json +65 -0
- package/limitd-redis/node_modules/yallist/yallist.js +370 -0
- package/limitd-redis/opslevel.yml +6 -0
- package/limitd-redis/package-lock.json +3484 -0
- package/limitd-redis/package.json +31 -0
- package/limitd-redis/test/cb.tests.js +124 -0
- package/limitd-redis/test/client.tests.js +194 -0
- package/limitd-redis/test/db.tests.js +1318 -0
- package/limitd-redis/test/validation.tests.js +124 -0
- package/limiter.js +57 -1
- package/package.json +3 -2
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
const assert = require('chai').assert;
|
|
2
|
+
|
|
3
|
+
const { validateParams } = require('../lib/validation');
|
|
4
|
+
|
|
5
|
+
describe('validation', () => {
|
|
6
|
+
describe('validateParameters', () => {
|
|
7
|
+
|
|
8
|
+
const buckets = {
|
|
9
|
+
user: {
|
|
10
|
+
size: 10
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
describe('when providing invalid parameters', () => {
|
|
15
|
+
const invalidParameterSets = [
|
|
16
|
+
{
|
|
17
|
+
result: {
|
|
18
|
+
message: 'params are required',
|
|
19
|
+
code: 101
|
|
20
|
+
}
|
|
21
|
+
}, {
|
|
22
|
+
params: {},
|
|
23
|
+
result: {
|
|
24
|
+
message: 'type is required',
|
|
25
|
+
code: 102
|
|
26
|
+
}
|
|
27
|
+
}, {
|
|
28
|
+
params: {
|
|
29
|
+
type: 'ip'
|
|
30
|
+
},
|
|
31
|
+
result: {
|
|
32
|
+
message: 'undefined bucket type ip',
|
|
33
|
+
code: 103
|
|
34
|
+
}
|
|
35
|
+
}, {
|
|
36
|
+
params: {
|
|
37
|
+
type: 'user'
|
|
38
|
+
},
|
|
39
|
+
result: {
|
|
40
|
+
message: 'key is required',
|
|
41
|
+
code: 104
|
|
42
|
+
}
|
|
43
|
+
}, {
|
|
44
|
+
params: {
|
|
45
|
+
type: 'user',
|
|
46
|
+
key: 'tenant|username',
|
|
47
|
+
configOverride: 5
|
|
48
|
+
},
|
|
49
|
+
result: {
|
|
50
|
+
message: 'configuration overrides must be an object',
|
|
51
|
+
code: 105
|
|
52
|
+
}
|
|
53
|
+
}, {
|
|
54
|
+
params: {
|
|
55
|
+
type: 'user',
|
|
56
|
+
key: 'tenant|username',
|
|
57
|
+
configOverride: {}
|
|
58
|
+
},
|
|
59
|
+
result: {
|
|
60
|
+
message: 'configuration overrides must provide either a size or interval',
|
|
61
|
+
code: 106
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
];
|
|
65
|
+
|
|
66
|
+
invalidParameterSets.forEach(testcase => {
|
|
67
|
+
it(`Should return a validation error, code ${testcase.result.code}`, () => {
|
|
68
|
+
const result = validateParams(testcase.params, buckets);
|
|
69
|
+
assert.strictEqual(result.name, 'LimitdRedisValidationError');
|
|
70
|
+
assert.strictEqual(result.message, testcase.result.message);
|
|
71
|
+
assert.deepEqual(result.extra, { code: testcase.result.code });
|
|
72
|
+
assert.exists(result.stack);
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
describe('when providing valid parameters', () => {
|
|
78
|
+
const validParameterSerts = [
|
|
79
|
+
{
|
|
80
|
+
params: {
|
|
81
|
+
type: 'user',
|
|
82
|
+
key: 'tenant|username',
|
|
83
|
+
},
|
|
84
|
+
name: 'type and key params'
|
|
85
|
+
}, {
|
|
86
|
+
params: {
|
|
87
|
+
type: 'user',
|
|
88
|
+
key: 'tenant|username',
|
|
89
|
+
configOverride: {
|
|
90
|
+
size: 77
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
name: 'configOverride with size'
|
|
94
|
+
}, {
|
|
95
|
+
params: {
|
|
96
|
+
type: 'user',
|
|
97
|
+
key: 'tenant|username',
|
|
98
|
+
configOverride: {
|
|
99
|
+
per_hour: 300
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
name: 'configOverride with interval'
|
|
103
|
+
}, {
|
|
104
|
+
params: {
|
|
105
|
+
type: 'user',
|
|
106
|
+
key: 'tenant|username',
|
|
107
|
+
configOverride: {
|
|
108
|
+
size: 30,
|
|
109
|
+
per_hour: 300
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
name: 'configOverride with size and interval'
|
|
113
|
+
},
|
|
114
|
+
];
|
|
115
|
+
|
|
116
|
+
validParameterSerts.forEach(testcase => {
|
|
117
|
+
it(`Should not cause a validation error for ${testcase.name}`, () => {
|
|
118
|
+
const result = validateParams(testcase.params, buckets);
|
|
119
|
+
assert.isUndefined(result);
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
});
|
package/limiter.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
var LimitdRedis = require("limitd-redis"); // name to redis matter
|
|
4
|
+
var z = require('zod');
|
|
4
5
|
|
|
5
6
|
const defaultBuckets = {
|
|
6
7
|
emailsByEventId: { size: 3000 },
|
|
@@ -37,6 +38,56 @@ class Operation {
|
|
|
37
38
|
// global singleton
|
|
38
39
|
let connection = null;
|
|
39
40
|
|
|
41
|
+
const LIMITER_CIRCUIT_BREAKER_DEFAULTS = {
|
|
42
|
+
timeout: "0.50s",
|
|
43
|
+
maxFailures: 50,
|
|
44
|
+
cooldown: "1s",
|
|
45
|
+
maxCooldown: "3s",
|
|
46
|
+
name: "limitr",
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const LIMITER_RETRY_DEFAULTS = {
|
|
50
|
+
retries: 3,
|
|
51
|
+
minTimeout: 10,
|
|
52
|
+
maxTimeout: 30,
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Circuit Breaker configuration for limiter. derived from limitd-redis package
|
|
57
|
+
* https://github.com/auth0/limitd-redis/blob/03ba193fe436c5b887ff410d4f2623512ee5c9e2/lib/client.js#L11C1-L12C1
|
|
58
|
+
*/
|
|
59
|
+
const limiterCircuitBreakerConfig = z
|
|
60
|
+
.object({
|
|
61
|
+
timeout: z.coerce
|
|
62
|
+
.string()
|
|
63
|
+
.default(LIMITER_CIRCUIT_BREAKER_DEFAULTS.timeout),
|
|
64
|
+
maxFailures: z.coerce
|
|
65
|
+
.number()
|
|
66
|
+
.default(LIMITER_CIRCUIT_BREAKER_DEFAULTS.maxFailures),
|
|
67
|
+
cooldown: z.coerce
|
|
68
|
+
.string()
|
|
69
|
+
.default(LIMITER_CIRCUIT_BREAKER_DEFAULTS.cooldown),
|
|
70
|
+
maxCooldown: z.coerce
|
|
71
|
+
.string()
|
|
72
|
+
.default(LIMITER_CIRCUIT_BREAKER_DEFAULTS.maxCooldown),
|
|
73
|
+
name: z.coerce.string().default(LIMITER_CIRCUIT_BREAKER_DEFAULTS.name),
|
|
74
|
+
})
|
|
75
|
+
.strict()
|
|
76
|
+
.default(LIMITER_CIRCUIT_BREAKER_DEFAULTS);
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Limiter Retry configuration for limiter. derived from limitd-redis package
|
|
80
|
+
* https://github.com/auth0/limitd-redis/blob/03ba193fe436c5b887ff410d4f2623512ee5c9e2/lib/client.js#L22
|
|
81
|
+
*/
|
|
82
|
+
const limiterRetryConfig = z
|
|
83
|
+
.object({
|
|
84
|
+
retries: z.coerce.number().default(LIMITER_RETRY_DEFAULTS.retries),
|
|
85
|
+
minTimeout: z.coerce.number().default(LIMITER_RETRY_DEFAULTS.minTimeout),
|
|
86
|
+
maxTimeout: z.coerce.number().default(LIMITER_RETRY_DEFAULTS.maxTimeout),
|
|
87
|
+
})
|
|
88
|
+
.strict()
|
|
89
|
+
.default(LIMITER_RETRY_DEFAULTS);
|
|
90
|
+
|
|
40
91
|
class Limiter {
|
|
41
92
|
// static defaultBuckets;
|
|
42
93
|
// static pickDefaultBuckets(keys) { ... };
|
|
@@ -53,7 +104,7 @@ class Limiter {
|
|
|
53
104
|
*/
|
|
54
105
|
connect() {
|
|
55
106
|
return new Promise((resolve, reject) => {
|
|
56
|
-
|
|
107
|
+
let buckets = this._config.buckets || {};
|
|
57
108
|
buckets = {
|
|
58
109
|
...defaultBuckets,
|
|
59
110
|
...buckets,
|
|
@@ -64,6 +115,9 @@ class Limiter {
|
|
|
64
115
|
uri: this._config.limitdUrl,
|
|
65
116
|
buckets,
|
|
66
117
|
keyPrefix,
|
|
118
|
+
circuitbreaker: this._config.circuitbreaker,
|
|
119
|
+
commandTimeout: this._config.commandTimeout,
|
|
120
|
+
retry: this._config.retry,
|
|
67
121
|
});
|
|
68
122
|
connection.db.on('ready', () => {
|
|
69
123
|
return resolve(connection);
|
|
@@ -199,5 +253,7 @@ Limiter.pickDefaultBuckets = function pickDefaultBuckets(keyArray) {
|
|
|
199
253
|
}
|
|
200
254
|
return buckets;
|
|
201
255
|
};
|
|
256
|
+
Limiter.limiterCircuitBreakerConfig = limiterCircuitBreakerConfig;
|
|
257
|
+
Limiter.limiterRetryConfig = limiterRetryConfig;
|
|
202
258
|
|
|
203
259
|
module.exports = Limiter;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@withjoy/limiter",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4-test",
|
|
4
4
|
"description": "Api Rate limiter",
|
|
5
5
|
"main": "limiter.js",
|
|
6
6
|
"scripts": {
|
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
"author": "services@withjoy.com",
|
|
11
11
|
"license": "MIT",
|
|
12
12
|
"dependencies": {
|
|
13
|
-
"limitd-redis": "
|
|
13
|
+
"limitd-redis": "file:./limitd-redis",
|
|
14
|
+
"zod": "^3.22.4"
|
|
14
15
|
},
|
|
15
16
|
"devDependencies": {
|
|
16
17
|
"jest": "^24.9.0"
|