exframe-cache-manager 2.1.3 → 2.2.1
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/Dockerfile +4 -2
- package/docker-compose.yml +0 -1
- package/index.cjs +5 -0
- package/index.mjs +3 -0
- package/package.json +13 -5
- package/{index.js → src/cacheManager/index.js} +37 -12
- package/test/cacheManager.test.js +45 -11
package/Dockerfile
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
FROM node:
|
|
1
|
+
FROM node:24-alpine
|
|
2
2
|
|
|
3
3
|
LABEL maintainer=Exzeo
|
|
4
4
|
|
|
@@ -10,8 +10,10 @@ COPY ./package.json /app/package.json
|
|
|
10
10
|
COPY . /app
|
|
11
11
|
|
|
12
12
|
WORKDIR /app
|
|
13
|
-
RUN npm install
|
|
13
|
+
RUN npm install --ignore-scripts
|
|
14
14
|
RUN npm run peers
|
|
15
15
|
|
|
16
|
+
USER node
|
|
17
|
+
|
|
16
18
|
# Override the command, to run the test instead of the application
|
|
17
19
|
CMD ["npm", "run", "unit-test"]
|
package/docker-compose.yml
CHANGED
package/index.cjs
ADDED
package/index.mjs
ADDED
package/package.json
CHANGED
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "exframe-cache-manager",
|
|
3
|
-
"version": "2.1
|
|
3
|
+
"version": "2.2.1",
|
|
4
4
|
"description": "Managing the cache",
|
|
5
5
|
"main": "index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"import": "./index.mjs",
|
|
10
|
+
"require": "./index.cjs",
|
|
11
|
+
"default": "./index.mjs"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
6
14
|
"config": {
|
|
7
15
|
"reporter": "mocha-exzeo-reporter"
|
|
8
16
|
},
|
|
@@ -24,14 +32,14 @@
|
|
|
24
32
|
"exframe-service": "1.x"
|
|
25
33
|
},
|
|
26
34
|
"dependencies": {
|
|
27
|
-
"@types/cache-manager": "^
|
|
35
|
+
"@types/cache-manager": "^4.0.6",
|
|
28
36
|
"cache-manager": "^3.6.0",
|
|
29
37
|
"cache-manager-redis-store": "^2.0.0",
|
|
30
|
-
"exframe-metrics": "^1.
|
|
38
|
+
"exframe-metrics": "^1.5.0",
|
|
31
39
|
"exframe-utilities": "^1.1.0"
|
|
32
40
|
},
|
|
33
41
|
"devDependencies": {
|
|
34
|
-
"chai": "^
|
|
42
|
+
"chai": "^6",
|
|
35
43
|
"eslint": "*",
|
|
36
44
|
"eslint-config-exzeo": "*",
|
|
37
45
|
"eslint-plugin-import": "*",
|
|
@@ -46,5 +54,5 @@
|
|
|
46
54
|
"url": "https://bitbucket.org/exzeo-usa/exframe",
|
|
47
55
|
"directory": "packages/exframe-cache-manager"
|
|
48
56
|
},
|
|
49
|
-
"gitHead": "
|
|
57
|
+
"gitHead": "1e58908906f3408a98bf3e81546feb516db1412d"
|
|
50
58
|
}
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
3
|
+
import util from 'node:util';
|
|
4
|
+
import { setTimeout as waitTimeout } from 'node:timers/promises';
|
|
5
|
+
import CacheManager from 'cache-manager';
|
|
6
|
+
import RedisStore from 'cache-manager-redis-store';
|
|
7
|
+
import { lazyInstrument } from 'exframe-metrics';
|
|
8
|
+
import health from 'exframe-health';
|
|
9
|
+
import service from 'exframe-service';
|
|
10
|
+
import { generateShortId } from 'exframe-utilities';
|
|
11
|
+
|
|
12
|
+
const DEFAULT_TTL = 600;
|
|
11
13
|
|
|
12
14
|
const db = 0;
|
|
13
15
|
|
|
@@ -24,7 +26,7 @@ function cachemanager(options) {
|
|
|
24
26
|
|
|
25
27
|
const redisCache = CacheManager.caching({
|
|
26
28
|
store: RedisStore,
|
|
27
|
-
ttl:
|
|
29
|
+
ttl: DEFAULT_TTL,
|
|
28
30
|
db,
|
|
29
31
|
...options
|
|
30
32
|
});
|
|
@@ -98,6 +100,30 @@ function cachemanager(options) {
|
|
|
98
100
|
return value;
|
|
99
101
|
},
|
|
100
102
|
|
|
103
|
+
/**
|
|
104
|
+
* Set the item if it does not exist yet. If the item already exists, do not set and return null.
|
|
105
|
+
* @template T
|
|
106
|
+
* @param {String} key
|
|
107
|
+
* @param {T} value
|
|
108
|
+
* @param {Number} ttl
|
|
109
|
+
* @returns {Promise<T|null>}
|
|
110
|
+
*/
|
|
111
|
+
async setItemIfNotExists(key, value, ttl) {
|
|
112
|
+
const ttlToSet = ttl ?? (process.env.REDIS_CACHE_TTL
|
|
113
|
+
? Number(process.env.REDIS_CACHE_TTL)
|
|
114
|
+
: DEFAULT_TTL);
|
|
115
|
+
|
|
116
|
+
return new Promise((resolve, reject) => {
|
|
117
|
+
redisClient.set(key, value, 'NX', 'EX', ttlToSet, (error, result) => {
|
|
118
|
+
if (error) {
|
|
119
|
+
return reject(error);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
resolve(result === 'OK' ? value : null);
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
},
|
|
126
|
+
|
|
101
127
|
/**
|
|
102
128
|
*
|
|
103
129
|
* @param {string} key
|
|
@@ -226,9 +252,8 @@ function cachemanager(options) {
|
|
|
226
252
|
return instance;
|
|
227
253
|
}
|
|
228
254
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
};
|
|
255
|
+
// eslint-disable-next-line import/prefer-default-export
|
|
256
|
+
export { cachemanager };
|
|
232
257
|
|
|
233
258
|
/**
|
|
234
259
|
* @typedef {RedisCacheManagerOptions & { store?: any, ttl?: number, db?: number }} Options
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
3
|
+
import { expect } from 'chai';
|
|
4
|
+
import { createSandbox, match, stub as _stub } from 'sinon';
|
|
5
|
+
import service from 'exframe-service';
|
|
6
|
+
import health from 'exframe-health';
|
|
7
|
+
import { setTimeout as waitTimeout } from 'node:timers/promises';
|
|
8
|
+
|
|
9
|
+
import cacheManager from '../index.mjs';
|
|
9
10
|
|
|
10
11
|
const { prometheusClient } = service;
|
|
11
12
|
const userId = 'auth0|1234567890';
|
|
@@ -25,7 +26,7 @@ context('Test User Profile Middleware', () => {
|
|
|
25
26
|
let sinonInstance;
|
|
26
27
|
|
|
27
28
|
beforeEach(() => {
|
|
28
|
-
sinonInstance =
|
|
29
|
+
sinonInstance = createSandbox();
|
|
29
30
|
});
|
|
30
31
|
|
|
31
32
|
afterEach(() => {
|
|
@@ -37,7 +38,7 @@ context('Test User Profile Middleware', () => {
|
|
|
37
38
|
|
|
38
39
|
const instance = cacheManager.create(options);
|
|
39
40
|
expect(healthAddSpy.calledOnce).to.equal(true);
|
|
40
|
-
expect(healthAddSpy.calledWithMatch(`redis-${instance.id}`,
|
|
41
|
+
expect(healthAddSpy.calledWithMatch(`redis-${instance.id}`, match.func, { promotionTimeout: 60000 })).to.be.true;
|
|
41
42
|
});
|
|
42
43
|
|
|
43
44
|
it('registers a service resource with the instance ID in the name, onSignal function, and order of "last"', () => {
|
|
@@ -45,8 +46,8 @@ context('Test User Profile Middleware', () => {
|
|
|
45
46
|
|
|
46
47
|
const instance = cacheManager.create(options);
|
|
47
48
|
expect(registerSpy.calledOnce).to.equal(true);
|
|
48
|
-
expect(registerSpy.calledWithMatch(`exframe-cache-manager-${instance.id}`,
|
|
49
|
-
onSignal:
|
|
49
|
+
expect(registerSpy.calledWithMatch(`exframe-cache-manager-${instance.id}`, match({
|
|
50
|
+
onSignal: match.func,
|
|
50
51
|
order: 'last'
|
|
51
52
|
}))).to.be.true;
|
|
52
53
|
});
|
|
@@ -69,6 +70,39 @@ context('Test User Profile Middleware', () => {
|
|
|
69
70
|
await app.flushCache({ log: { info: (info) => { console.log(info); } } }, cacheKey);
|
|
70
71
|
});
|
|
71
72
|
|
|
73
|
+
it('validates the setItemIfNotExists function on the basis of key and value', async () => {
|
|
74
|
+
const data = await app.setItemIfNotExists(cacheKey, 'value1');
|
|
75
|
+
expect(data).to.be.ok.and.to.eql('value1');
|
|
76
|
+
|
|
77
|
+
const metrics = prometheusClient.register.getMetricsAsArray().filter(({ name }) => name.includes('setItemIfNotExists'));
|
|
78
|
+
expect(metrics).to.be.an('array').and.to.have.lengthOf(1);
|
|
79
|
+
|
|
80
|
+
await app.flushCache({ log: { info: (info) => { console.log(info); } } }, cacheKey);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('validates the setItemIfNotExists function on the basis of key, value, and ttl', async () => {
|
|
84
|
+
const data = await app.setItemIfNotExists(cacheKey, 'value1', 1000);
|
|
85
|
+
expect(data).to.be.ok.and.to.eql('value1');
|
|
86
|
+
|
|
87
|
+
const metrics = prometheusClient.register.getMetricsAsArray().filter(({ name }) => name.includes('setItemIfNotExists'));
|
|
88
|
+
expect(metrics).to.be.an('array').and.to.have.lengthOf(1);
|
|
89
|
+
|
|
90
|
+
await app.flushCache({ log: { info: (info) => { console.log(info); } } }, cacheKey);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('validates the setItemIfNotExists function returns null if key is already set', async () => {
|
|
94
|
+
const data1 = await app.setItemIfNotExists(cacheKey, 'value1', 1000);
|
|
95
|
+
expect(data1).to.be.ok.and.to.eql('value1');
|
|
96
|
+
|
|
97
|
+
const data2 = await app.setItemIfNotExists(cacheKey, 'value2');
|
|
98
|
+
expect(data2).to.eql(null);
|
|
99
|
+
|
|
100
|
+
const metrics = prometheusClient.register.getMetricsAsArray().filter(({ name }) => name.includes('setItemIfNotExists'));
|
|
101
|
+
expect(metrics).to.be.an('array').and.to.have.lengthOf(1);
|
|
102
|
+
|
|
103
|
+
await app.flushCache({ log: { info: (info) => { console.log(info); } } }, cacheKey);
|
|
104
|
+
});
|
|
105
|
+
|
|
72
106
|
it('validate the getItem function on the basis of key', async () => {
|
|
73
107
|
await app.setItem(cacheKey, 'someValue');
|
|
74
108
|
const value = await app.getItem(cacheKey);
|
|
@@ -94,7 +128,7 @@ context('Test User Profile Middleware', () => {
|
|
|
94
128
|
await waitTimeout(5000);
|
|
95
129
|
return 'finished';
|
|
96
130
|
};
|
|
97
|
-
const stub =
|
|
131
|
+
const stub = _stub(app.redisCache, 'get').callsFake(fake);
|
|
98
132
|
|
|
99
133
|
await app.setItem(cacheKey, 'someValue');
|
|
100
134
|
const context = { log: { info: (info) => { console.log(info); } } };
|