Skip to content

Commit 8f94626

Browse files
authored
Merge pull request #1 from codefresh-io/cf-node-server-sdk-redis-feat
feat: adjust node-server-sdk-redis for codefresh
2 parents 00a57b5 + 07205e6 commit 8f94626

File tree

9 files changed

+11201
-17
lines changed

9 files changed

+11201
-17
lines changed

.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
package-lock.json
2-
yarn.lock
32
dist/
43
node_modules/
54
**/*.tsbuildinfo

.nvmrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
20.18.0

packages/shared/akamai-edgeworker-sdk/src/__tests__/index.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// eslint-disable-next-line import/extensions
12
import { EdgeProvider, init, LDLogger, LDMultiKindContext, LDSingleKindContext } from '../..';
23
import * as testData from './testData.json';
34

packages/store/node-server-sdk-redis/package.json

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
2-
"name": "@launchdarkly/node-server-sdk-redis",
3-
"version": "3.0.11",
2+
"name": "@codefresh-io/launchdarkly-node-server-sdk-redis",
3+
"version": "3.0.11-codefresh.2",
44
"description": "Redis-backed feature store for the LaunchDarkly Server-Side SDK for Node.js",
55
"homepage": "https://github.com/launchdarkly/js-core/tree/main/packages/store/node-server-sdk-redis",
66
"repository": {
@@ -11,7 +11,8 @@
1111
"main": "./dist/src/index.js",
1212
"types": "./dist/src/index.d.ts",
1313
"files": [
14-
"dist"
14+
"dist",
15+
"yarn.lock"
1516
],
1617
"keywords": [
1718
"launchdarkly",
@@ -36,6 +37,7 @@
3637
"@launchdarkly/node-server-sdk": "8.2.4",
3738
"@trivago/prettier-plugin-sort-imports": "^4.1.1",
3839
"@types/jest": "^29.4.0",
40+
"@types/redis-errors": "^1.2.3",
3941
"@typescript-eslint/eslint-plugin": "^6.1.0",
4042
"@typescript-eslint/parser": "^6.1.0",
4143
"eslint": "^8.45.0",

packages/store/node-server-sdk-redis/src/RedisClientState.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ export default class RedisClientState {
6262
});
6363

6464
client.on('reconnecting', (delay: number) => {
65+
this.connected = false;
6566
this.attempt += 1;
6667
logger?.info(
6768
`Attempting to reconnect to redis (attempt # ${this.attempt}, delay: ${delay}ms)`,

packages/store/node-server-sdk-redis/src/RedisCore.ts

Lines changed: 52 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { MaxRetriesPerRequestError } from 'ioredis/built/errors';
2+
13
import { interfaces, LDLogger } from '@launchdarkly/node-server-sdk';
24

35
import RedisClientState from './RedisClientState';
@@ -29,6 +31,7 @@ export default class RedisCore implements interfaces.PersistentDataStore {
2931
constructor(
3032
private readonly state: RedisClientState,
3133
private readonly logger?: LDLogger,
34+
private readonly localFeatureStore?: any,
3235
) {
3336
this.initedKey = this.state.prefixedKey('$inited');
3437
}
@@ -99,6 +102,50 @@ export default class RedisCore implements interfaces.PersistentDataStore {
99102
});
100103
}
101104

105+
#serializeItems(kind: any, itemsObj: Record<string, any>) {
106+
const serializedItemsObj: Record<string, any> = {};
107+
Object.keys(itemsObj).forEach((key) => {
108+
const value = itemsObj[key];
109+
serializedItemsObj[key] = kind.serialize(value).serializedItem;
110+
});
111+
return serializedItemsObj;
112+
}
113+
114+
#prepareArray(values: Record<string, string>) {
115+
const results: interfaces.KeyedItem<string, interfaces.SerializedItemDescriptor>[] = [];
116+
Object.keys(values).forEach((key) => {
117+
const value = values[key];
118+
// When getting we do not populate version and deleted.
119+
// The SDK will have to deserialize to access these values.
120+
results.push({ key, item: { version: 0, deleted: false, serializedItem: value } });
121+
});
122+
return results;
123+
}
124+
125+
#useItemsFromCodefresh(
126+
kind: interfaces.PersistentStoreDataKind,
127+
callback: (
128+
descriptors: interfaces.KeyedItem<string, interfaces.SerializedItemDescriptor>[] | undefined,
129+
) => void,
130+
) {
131+
this.localFeatureStore().then(
132+
(items: any) => {
133+
let localResults;
134+
if (kind.namespace === 'features') {
135+
localResults = items.features;
136+
} else {
137+
localResults = items.segments;
138+
}
139+
const serializedItems = this.#serializeItems(kind, localResults);
140+
callback(this.#prepareArray(serializedItems));
141+
},
142+
(error: any) => {
143+
console.log(error);
144+
callback(undefined);
145+
},
146+
);
147+
}
148+
102149
getAll(
103150
kind: interfaces.PersistentStoreDataKind,
104151
callback: (
@@ -107,22 +154,17 @@ export default class RedisCore implements interfaces.PersistentDataStore {
107154
): void {
108155
if (!this.state.isConnected() && !this.state.isInitialConnection()) {
109156
this.logger?.warn('Attempted to fetch all keys while Redis connection is down');
110-
callback(undefined);
111-
return;
157+
this.#useItemsFromCodefresh(kind, callback);
112158
}
113159

114160
this.state.getClient().hgetall(this.state.prefixedKey(kind.namespace), (err, values) => {
115161
if (err) {
116162
this.logger?.error(`Error fetching '${kind.namespace}' from Redis ${err}`);
163+
if (err instanceof MaxRetriesPerRequestError) {
164+
this.#useItemsFromCodefresh(kind, callback);
165+
}
117166
} else if (values) {
118-
const results: interfaces.KeyedItem<string, interfaces.SerializedItemDescriptor>[] = [];
119-
Object.keys(values).forEach((key) => {
120-
const value = values[key];
121-
// When getting we do not populate version and deleted.
122-
// The SDK will have to deserialize to access these values.
123-
results.push({ key, item: { version: 0, deleted: false, serializedItem: value } });
124-
});
125-
callback(results);
167+
callback(this.#prepareArray(values));
126168
} else {
127169
callback(undefined);
128170
}

packages/store/node-server-sdk-redis/src/RedisFeatureStore.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,10 @@ export default class RedisFeatureStore implements LDFeatureStore {
2323
constructor(
2424
options?: LDRedisOptions,
2525
private readonly logger?: LDLogger,
26+
localFeatureStore?: any,
2627
) {
2728
this.wrapper = new PersistentDataStoreWrapper(
28-
new RedisCore(new RedisClientState(options, logger), logger),
29+
new RedisCore(new RedisClientState(options, logger), logger, localFeatureStore),
2930
TtlFromOptions(options),
3031
);
3132
}

packages/store/node-server-sdk-redis/src/RedisFeatureStoreFactory.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,10 @@ import RedisFeatureStore from './RedisFeatureStore';
2323
* @returns
2424
* A factory function suitable for use in the SDK configuration (LDOptions).
2525
*/
26-
export default function RedisFeatureStoreFactory(options?: LDRedisOptions) {
26+
export default function RedisFeatureStoreFactory(
27+
options?: LDRedisOptions,
28+
localFeatureStore?: any,
29+
) {
2730
return (config: LDClientContext) =>
28-
new RedisFeatureStore(options, config.basicConfiguration.logger);
31+
new RedisFeatureStore(options, config.basicConfiguration.logger, localFeatureStore);
2932
}

0 commit comments

Comments
 (0)