Skip to content

Commit c34f847

Browse files
feat: [do not re-init and perform a network request if the singleton … (#95)
* feat: [do not re-init and perform a network request if the singleton has already been init] (FF-2921) * Adjust unit tests * update log
1 parent be41540 commit c34f847

File tree

3 files changed

+88
-3
lines changed

3 files changed

+88
-3
lines changed

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@eppo/js-client-sdk",
3-
"version": "3.4.0",
3+
"version": "3.5.0",
44
"description": "Eppo SDK for client-side JavaScript applications",
55
"main": "dist/index.js",
66
"files": [
@@ -61,4 +61,4 @@
6161
"dependencies": {
6262
"@eppo/js-client-sdk-common": "4.0.0"
6363
}
64-
}
64+
}

src/index.spec.ts

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ describe('EppoJSClient E2E test', () => {
191191
apiKey,
192192
baseUrl,
193193
assignmentLogger: mockLogger,
194+
forceReinitialize: true,
194195
});
195196
});
196197

@@ -335,6 +336,7 @@ describe('EppoJSClient E2E test', () => {
335336
apiKey,
336337
baseUrl,
337338
assignmentLogger: mockLogger,
339+
forceReinitialize: true,
338340
});
339341
});
340342

@@ -494,6 +496,61 @@ describe('initialization options', () => {
494496
expect(callCount).toBe(2);
495497
});
496498

499+
it('do not reinitialize if already initialized', async () => {
500+
let callCount = 0;
501+
502+
global.fetch = jest.fn(() => {
503+
callCount += 1;
504+
return Promise.resolve({
505+
ok: true,
506+
status: 200,
507+
json: () => Promise.resolve(mockConfigResponse),
508+
});
509+
}) as jest.Mock;
510+
511+
await init({
512+
apiKey,
513+
baseUrl,
514+
assignmentLogger: mockLogger,
515+
});
516+
517+
await init({
518+
apiKey,
519+
baseUrl,
520+
assignmentLogger: mockLogger,
521+
});
522+
523+
expect(callCount).toBe(1);
524+
});
525+
526+
it('force reinitialize', async () => {
527+
let callCount = 0;
528+
529+
global.fetch = jest.fn(() => {
530+
callCount += 1;
531+
return Promise.resolve({
532+
ok: true,
533+
status: 200,
534+
json: () => Promise.resolve(mockConfigResponse),
535+
});
536+
}) as jest.Mock;
537+
538+
await init({
539+
apiKey,
540+
baseUrl,
541+
assignmentLogger: mockLogger,
542+
});
543+
544+
await init({
545+
apiKey,
546+
baseUrl,
547+
assignmentLogger: mockLogger,
548+
forceReinitialize: true,
549+
});
550+
551+
expect(callCount).toBe(2);
552+
});
553+
497554
it('polls after successful init if configured to do so', async () => {
498555
let callCount = 0;
499556

@@ -723,6 +780,7 @@ describe('initialization options', () => {
723780
apiKey,
724781
baseUrl,
725782
assignmentLogger: mockLogger,
783+
forceReinitialize: true,
726784
});
727785

728786
expect(fetchCallCount).toBe(1);
@@ -735,6 +793,7 @@ describe('initialization options', () => {
735793
baseUrl: 'https://thisisabaddomainforthistest.com',
736794
assignmentLogger: mockLogger,
737795
useExpiredCache: true,
796+
forceReinitialize: true,
738797
});
739798

740799
// Should serve assignment from cache before fetch even fails
@@ -754,6 +813,7 @@ describe('initialization options', () => {
754813
baseUrl: 'https://thisisabaddomainforthistest.com',
755814
assignmentLogger: mockLogger,
756815
useExpiredCache: true,
816+
forceReinitialize: true,
757817
}),
758818
).rejects.toThrow();
759819
});
@@ -854,6 +914,7 @@ describe('initialization options', () => {
854914
baseUrl,
855915
assignmentLogger: mockLogger,
856916
updateOnFetch,
917+
forceReinitialize: true,
857918
});
858919

859920
expect(fetchCallCount).toBe(1);
@@ -871,6 +932,7 @@ describe('initialization options', () => {
871932
assignmentLogger: mockLogger,
872933
updateOnFetch,
873934
useExpiredCache: true,
935+
forceReinitialize: true,
874936
});
875937

876938
// Should serve assignment from cache before fetch completes
@@ -900,6 +962,7 @@ describe('initialization options', () => {
900962
assignmentLogger: mockLogger,
901963
updateOnFetch,
902964
maxCacheAgeSeconds: fetchResolveDelayMs * 10,
965+
forceReinitialize: true,
903966
});
904967

905968
// No fetch will have been kicked off because of valid cache; previously fetched values will be served
@@ -942,6 +1005,7 @@ describe('initialization options', () => {
9421005
apiKey,
9431006
baseUrl,
9441007
assignmentLogger: mockLogger,
1008+
forceReinitialize: true,
9451009
});
9461010
});
9471011

@@ -955,6 +1019,7 @@ describe('initialization options', () => {
9551019
apiKey,
9561020
baseUrl,
9571021
assignmentLogger: mockLogger,
1022+
forceReinitialize: true,
9581023
});
9591024
expect(getInstance().getStringAssignment(flagKey, 'subject', {}, 'default-value')).toBe(
9601025
'default-value',

src/index.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,11 @@ export interface IClientConfig {
113113
* than the default storage provided by the SDK.
114114
*/
115115
persistentStore?: IAsyncStore<Flag>;
116+
117+
/**
118+
* Force reinitialize the SDK if it is already initialized.
119+
*/
120+
forceReinitialize?: boolean;
116121
}
117122

118123
export interface IClientConfigSync {
@@ -381,8 +386,23 @@ export async function init(config: IClientConfig): Promise<EppoClient> {
381386
validation.validateNotBlank(config.apiKey, 'API key required');
382387
let initializationError: Error | undefined;
383388
const instance = EppoJSClient.instance;
384-
const { apiKey, persistentStore, baseUrl, maxCacheAgeSeconds, updateOnFetch } = config;
389+
const { apiKey, persistentStore, baseUrl, maxCacheAgeSeconds, updateOnFetch, forceReinitialize } =
390+
config;
385391
try {
392+
if (EppoJSClient.initialized) {
393+
if (forceReinitialize) {
394+
applicationLogger.warn(
395+
'Eppo SDK is already initialized, reinitializing since forceReinitialize is true.',
396+
);
397+
EppoJSClient.initialized = false;
398+
} else {
399+
applicationLogger.warn(
400+
'Eppo SDK is already initialized, skipping reinitialization since forceReinitialize is false.',
401+
);
402+
return instance;
403+
}
404+
}
405+
386406
// If any existing instances; ensure they are not polling
387407
instance.stopPolling();
388408
// Set up assignment logger and cache

0 commit comments

Comments
 (0)