Skip to content

Commit cab78cb

Browse files
authored
Update for updated common SDK (#92)
* old tests passing * upgrade package; handle entrie() rename * feedback from PR * bump version
1 parent 47927fd commit cab78cb

10 files changed

+61
-41
lines changed

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@eppo/js-client-sdk",
3-
"version": "3.2.2",
3+
"version": "3.3.0",
44
"description": "Eppo SDK for client-side JavaScript applications",
55
"main": "dist/index.js",
66
"files": [
@@ -56,6 +56,6 @@
5656
"webpack-cli": "^4.10.0"
5757
},
5858
"dependencies": {
59-
"@eppo/js-client-sdk-common": "3.3.3"
59+
"@eppo/js-client-sdk-common": "3.5.0"
6060
}
61-
}
61+
}

src/cache/chrome-storage-assignment-cache.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ export default class ChromeStorageAssignmentCache implements BulkReadAssignmentC
2020
this.storage.set(assignmentCacheKeyToString(entry), assignmentCacheValueToString(entry));
2121
}
2222

23-
has(_: AssignmentCacheEntry): boolean {
23+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
24+
has(_entry: AssignmentCacheEntry): boolean {
2425
throw new Error(
2526
'This should never be called for ChromeStorageAssignmentCache, use getEntries() instead.',
2627
);

src/chrome-storage-engine.spec.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ describe('ChromeStorageStore', () => {
9090
it('should get entries', async () => {
9191
fakeStore[fakeStoreContentsKey] = JSON.stringify(mockEntries);
9292

93-
const entries = await chromeStore.getEntries();
93+
const entries = await chromeStore.entries();
9494
expect(entries).toEqual(mockEntries);
9595
});
9696

@@ -107,17 +107,17 @@ describe('ChromeStorageStore', () => {
107107
const chromeStoreB = new StringValuedAsyncStore(chromeStorageEngineB, 1);
108108

109109
await chromeStoreA.setEntries({ theKey: 'A' });
110-
expect(await chromeStoreA.getEntries()).toEqual({ theKey: 'A' });
110+
expect(await chromeStoreA.entries()).toEqual({ theKey: 'A' });
111111
expect(await chromeStoreA.isExpired()).toBe(false);
112-
expect(await chromeStoreB.getEntries()).toEqual({});
112+
expect(await chromeStoreB.entries()).toEqual({});
113113
expect(await chromeStoreB.isExpired()).toBe(true);
114114

115115
await jest.advanceTimersByTimeAsync(2000);
116116

117117
await chromeStoreB.setEntries({ theKey: 'B' });
118-
expect(await chromeStoreA.getEntries()).toEqual({ theKey: 'A' });
118+
expect(await chromeStoreA.entries()).toEqual({ theKey: 'A' });
119119
expect(await chromeStoreA.isExpired()).toBe(true);
120-
expect(await chromeStoreB.getEntries()).toEqual({ theKey: 'B' });
120+
expect(await chromeStoreB.entries()).toEqual({ theKey: 'B' });
121121
expect(await chromeStoreB.isExpired()).toBe(false);
122122
});
123123
});

src/configuration-factory.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ describe('configurationStorageFactory', () => {
1818
set: jest.fn(),
1919
isInitialized: jest.fn().mockReturnValue(true),
2020
isExpired: jest.fn().mockReturnValue(false),
21-
getEntries: jest.fn().mockReturnValue({}),
21+
entries: jest.fn().mockReturnValue({}),
2222
setEntries: jest.fn(),
2323
};
2424
const result = configurationStorageFactory({

src/index.spec.ts

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
constants,
1111
HybridConfigurationStore,
1212
IAsyncStore,
13+
AssignmentCache,
1314
} from '@eppo/js-client-sdk-common';
1415
import * as td from 'testdouble';
1516

@@ -200,7 +201,7 @@ describe('EppoJSClient E2E test', () => {
200201
});
201202

202203
afterEach(() => {
203-
globalClient.setLogger(mockLogger);
204+
globalClient.setAssignmentLogger(mockLogger);
204205
td.reset();
205206
});
206207

@@ -224,8 +225,15 @@ describe('EppoJSClient E2E test', () => {
224225
return mockObfuscatedUfcFlagConfig;
225226
});
226227

228+
const mockAssignmentCache = td.object<AssignmentCache>();
229+
td.when(mockAssignmentCache.has(td.matchers.anything())).thenReturn(false);
230+
td.when(mockAssignmentCache.set(td.matchers.anything())).thenReturn();
231+
globalClient.useCustomAssignmentCache(mockAssignmentCache);
232+
233+
const mockLogger = td.object<IAssignmentLogger>();
234+
globalClient.setAssignmentLogger(mockLogger);
235+
227236
const subjectAttributes = { foo: 3 };
228-
globalClient.setLogger(mockLogger);
229237
const assignment = globalClient.getStringAssignment(
230238
flagKey,
231239
'subject-10',
@@ -235,12 +243,12 @@ describe('EppoJSClient E2E test', () => {
235243

236244
expect(assignment).toEqual('variant-1');
237245
expect(td.explain(mockLogger.logAssignment).callCount).toEqual(1);
238-
expect(td.explain(mockLogger?.logAssignment).calls[0]?.args[0].subject).toEqual('subject-10');
239-
expect(td.explain(mockLogger?.logAssignment).calls[0]?.args[0].featureFlag).toEqual(flagKey);
240-
expect(td.explain(mockLogger?.logAssignment).calls[0]?.args[0].experiment).toEqual(
246+
expect(td.explain(mockLogger.logAssignment).calls[0]?.args[0].subject).toEqual('subject-10');
247+
expect(td.explain(mockLogger.logAssignment).calls[0]?.args[0].featureFlag).toEqual(flagKey);
248+
expect(td.explain(mockLogger.logAssignment).calls[0]?.args[0].experiment).toEqual(
241249
`${flagKey}-${allocationKey}`,
242250
);
243-
expect(td.explain(mockLogger?.logAssignment).calls[0]?.args[0].allocation).toEqual(
251+
expect(td.explain(mockLogger.logAssignment).calls[0]?.args[0].allocation).toEqual(
244252
`${allocationKey}`,
245253
);
246254
});
@@ -255,7 +263,7 @@ describe('EppoJSClient E2E test', () => {
255263
return mockObfuscatedUfcFlagConfig;
256264
});
257265
const subjectAttributes = { foo: 3 };
258-
globalClient.setLogger(mockLogger);
266+
globalClient.setAssignmentLogger(mockLogger);
259267
const assignment = globalClient.getStringAssignment(
260268
flagKey,
261269
'subject-10',
@@ -626,7 +634,7 @@ describe('initialization options', () => {
626634
async isExpired() {
627635
return true;
628636
},
629-
async getEntries() {
637+
async entries() {
630638
return {
631639
'old-key': mockObfuscatedUfcFlagConfig,
632640
};
@@ -773,7 +781,7 @@ describe('initialization options', () => {
773781
async isExpired() {
774782
return true; // triggers a fetch
775783
},
776-
async getEntries() {
784+
async entries() {
777785
return new Promise((resolve) => {
778786
setTimeout(() => {
779787
storeLoaded = true;

src/index.ts

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ export {
135135
export { ChromeStorageEngine } from './chrome-storage-engine';
136136

137137
// Instantiate the configuration store with memory-only implementation.
138-
const configurationStore = configurationStorageFactory({
138+
const flagConfigurationStore = configurationStorageFactory({
139139
forceMemoryOnly: true,
140140
});
141141

@@ -147,7 +147,13 @@ export class EppoJSClient extends EppoClient {
147147
// Ensure that the client is instantiated during class loading.
148148
// Use an empty memory-only configuration store until the `init` method is called,
149149
// to avoid serving stale data to the user.
150-
public static instance: EppoJSClient = new EppoJSClient(configurationStore, undefined, true);
150+
public static instance: EppoJSClient = new EppoJSClient(
151+
flagConfigurationStore,
152+
undefined,
153+
undefined,
154+
undefined,
155+
true,
156+
);
151157
public static initialized = false;
152158

153159
public getStringAssignment(
@@ -244,16 +250,20 @@ export function offlineInit(config: IClientConfigSync): IEppoClient {
244250
const memoryOnlyConfigurationStore = configurationStorageFactory({
245251
forceMemoryOnly: true,
246252
});
247-
memoryOnlyConfigurationStore.setEntries(config.flagsConfiguration);
248-
EppoJSClient.instance.setConfigurationStore(memoryOnlyConfigurationStore);
253+
memoryOnlyConfigurationStore
254+
.setEntries(config.flagsConfiguration)
255+
.catch((err) =>
256+
applicationLogger.warn('Error setting flags for memory-only configuration store', err),
257+
);
258+
EppoJSClient.instance.setFlagConfigurationStore(memoryOnlyConfigurationStore);
249259

250260
// Allow the caller to override the default obfuscated mode, which is false
251261
// since the purpose of this method is to bootstrap the SDK from an external source,
252262
// which is likely a server that has not-obfuscated flag values.
253263
EppoJSClient.instance.setIsObfuscated(isObfuscated);
254264

255265
if (config.assignmentLogger) {
256-
EppoJSClient.instance.setLogger(config.assignmentLogger);
266+
EppoJSClient.instance.setAssignmentLogger(config.assignmentLogger);
257267
}
258268

259269
// There is no SDK key in the offline context.
@@ -262,7 +272,7 @@ export function offlineInit(config: IClientConfigSync): IEppoClient {
262272
// As this is a synchronous initialization,
263273
// we are unable to call the async `init` method on the assignment cache
264274
// which loads the assignment cache from the browser's storage.
265-
// Therefore there is no purpose trying to use a persistent assignment cache.
275+
// Therefore, there is no purpose trying to use a persistent assignment cache.
266276
const assignmentCache = assignmentCacheFactory({
267277
storageKeySuffix,
268278
forceMemoryOnly: true,
@@ -296,7 +306,7 @@ export async function init(config: IClientConfig): Promise<IEppoClient> {
296306
// If any existing instances; ensure they are not polling
297307
instance.stopPolling();
298308
// Set up assignment logger and cache
299-
instance.setLogger(config.assignmentLogger);
309+
instance.setAssignmentLogger(config.assignmentLogger);
300310
// Default to obfuscated mode when requesting configuration from the server.
301311
instance.setIsObfuscated(true);
302312

@@ -318,7 +328,7 @@ export async function init(config: IClientConfig): Promise<IEppoClient> {
318328
storageKeySuffix,
319329
},
320330
);
321-
instance.setConfigurationStore(configurationStore);
331+
instance.setFlagConfigurationStore(configurationStore);
322332

323333
// instantiate and init assignment cache
324334
const assignmentCache = assignmentCacheFactory({

src/isolatable-hybrid.store.spec.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,14 @@ describe('IsolatableHybridConfigurationStore', () => {
88
get: jest.fn(),
99
getKeys: jest.fn(),
1010
isInitialized: jest.fn(),
11+
entries: jest.fn(),
1112
setEntries: jest.fn(),
1213
};
1314

1415
const asyncStoreMock = {
15-
getEntries: jest.fn(),
1616
isInitialized: jest.fn(),
1717
isExpired: jest.fn(),
18+
entries: jest.fn(),
1819
setEntries: jest.fn(),
1920
};
2021

@@ -36,7 +37,7 @@ describe('IsolatableHybridConfigurationStore', () => {
3637
it('should initialize the serving store with entries from the persistent store', async () => {
3738
const entries = { key1: 'value1', key2: 'value2' };
3839
asyncStoreMock.isInitialized.mockReturnValue(true);
39-
asyncStoreMock.getEntries.mockResolvedValue(entries);
40+
asyncStoreMock.entries.mockResolvedValue(entries);
4041

4142
await store.init();
4243

@@ -86,7 +87,7 @@ describe('IsolatableHybridConfigurationStore', () => {
8687
asyncStoreMock.isInitialized.mockReturnValue(true);
8788
asyncStoreMock.isExpired.mockReturnValue(false);
8889
const initialEntries = { key1: 'init1', key2: 'init2' };
89-
asyncStoreMock.getEntries.mockResolvedValue(initialEntries);
90+
asyncStoreMock.entries.mockResolvedValue(initialEntries);
9091

9192
await store.init();
9293
// Expect sync store to be initialized with the empty async store

src/local-storage.store.spec.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,13 @@ describe('LocalStorageStore', () => {
2929

3030
it('returns empty object if entry is not present', async () => {
3131
const store = new StringValuedAsyncStore<ITestEntry>(localStorageEngine);
32-
expect(await store.getEntries()).toEqual({});
32+
expect(await store.entries()).toEqual({});
3333
});
3434

3535
it('returns stored entries', async () => {
3636
const store = new StringValuedAsyncStore<ITestEntry>(localStorageEngine);
3737
await store.setEntries({ key1: config1, key2: config2 });
38-
expect(await store.getEntries()).toEqual({ key1: config1, key2: config2 });
38+
expect(await store.entries()).toEqual({ key1: config1, key2: config2 });
3939
});
4040

4141
it('is always expired without cooldown', async () => {
@@ -67,17 +67,17 @@ describe('LocalStorageStore', () => {
6767
const storeB = new StringValuedAsyncStore(localStorageEngineEngineB, 1);
6868

6969
await storeA.setEntries({ theKey: 'A' });
70-
expect(await storeA.getEntries()).toEqual({ theKey: 'A' });
70+
expect(await storeA.entries()).toEqual({ theKey: 'A' });
7171
expect(await storeA.isExpired()).toBe(false);
72-
expect(await storeB.getEntries()).toEqual({});
72+
expect(await storeB.entries()).toEqual({});
7373
expect(await storeB.isExpired()).toBe(true);
7474

7575
await jest.advanceTimersByTimeAsync(2000);
7676

7777
await storeB.setEntries({ theKey: 'B' });
78-
expect(await storeA.getEntries()).toEqual({ theKey: 'A' });
78+
expect(await storeA.entries()).toEqual({ theKey: 'A' });
7979
expect(await storeA.isExpired()).toBe(true);
80-
expect(await storeB.getEntries()).toEqual({ theKey: 'B' });
80+
expect(await storeB.entries()).toEqual({ theKey: 'B' });
8181
expect(await storeB.isExpired()).toBe(false);
8282
});
8383
});

src/string-valued.store.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ export class StringValuedAsyncStore<T> implements IAsyncStore<T> {
4141
return isExpired;
4242
}
4343

44-
public async getEntries(): Promise<Record<string, T>> {
44+
public async entries(): Promise<Record<string, T>> {
4545
const contentsJsonString = await this.storageEngine.getContentsJsonString();
4646
return contentsJsonString ? JSON.parse(contentsJsonString) : {};
4747
}

yarn.lock

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -380,10 +380,10 @@
380380
resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70"
381381
integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==
382382

383-
"@eppo/js-client-sdk-common@3.3.3":
384-
version "3.3.3"
385-
resolved "https://registry.yarnpkg.com/@eppo/js-client-sdk-common/-/js-client-sdk-common-3.3.3.tgz#aec92ac7bd2415708fe7dc9667a9c1fb18ba9c3a"
386-
integrity sha512-i57MkWSBc/KmT4G9y31541bPotn4nnktRbZv1f3E+W58TfG4NBU0FUhY86n98CTqQ2OBoAwkCMntWede4v4rTQ==
383+
"@eppo/js-client-sdk-common@3.5.0":
384+
version "3.5.0"
385+
resolved "https://registry.yarnpkg.com/@eppo/js-client-sdk-common/-/js-client-sdk-common-3.5.0.tgz#456616067a7044707828eea596cf6915d9c21c71"
386+
integrity sha512-uCzPXRq7Z7Ir8a9XDz0YU3TP5F1vKYtKqXSjRjqViDSBBfbdTkHBNh1zeA3hEdpppjQKZHExbV1Stl0rmNWznw==
387387
dependencies:
388388
js-base64 "^3.7.7"
389389
md5 "^2.3.0"

0 commit comments

Comments
 (0)