Skip to content

Commit 50e66aa

Browse files
committed
combine new and old constructors
1 parent e20ef73 commit 50e66aa

File tree

4 files changed

+290
-53
lines changed

4 files changed

+290
-53
lines changed
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
import {
2+
IConfigurationStore,
3+
ObfuscatedFlag,
4+
Flag,
5+
EventDispatcher,
6+
} from '@eppo/js-client-sdk-common';
7+
import * as td from 'testdouble';
8+
9+
import { clientOptionsToParameters } from './client-options-converter';
10+
import { IClientOptions } from './i-client-config';
11+
import { sdkName, sdkVersion } from './sdk-data';
12+
13+
describe('clientOptionsToParameters', () => {
14+
const mockStore = td.object<IConfigurationStore<Flag | ObfuscatedFlag>>();
15+
16+
it('converts basic client options', () => {
17+
const options: IClientOptions = {
18+
sdkKey: 'test-key',
19+
baseUrl: 'https://test.eppo.cloud',
20+
assignmentLogger: { logAssignment: jest.fn() },
21+
};
22+
23+
const result = clientOptionsToParameters(options, mockStore);
24+
25+
expect(result.isObfuscated).toBe(true);
26+
expect(result.flagConfigurationStore).toBeDefined();
27+
expect(result.configurationRequestParameters).toEqual({
28+
apiKey: 'test-key',
29+
baseUrl: 'https://test.eppo.cloud',
30+
sdkName,
31+
sdkVersion,
32+
numInitialRequestRetries: undefined,
33+
numPollRequestRetries: undefined,
34+
pollingIntervalMs: undefined,
35+
requestTimeoutMs: undefined,
36+
pollAfterFailedInitialization: undefined,
37+
pollAfterSuccessfulInitialization: undefined,
38+
throwOnFailedInitialization: undefined,
39+
skipInitialPoll: undefined,
40+
});
41+
});
42+
43+
it('uses provided flag configuration store', () => {
44+
const options: IClientOptions = {
45+
sdkKey: 'test-key',
46+
assignmentLogger: { logAssignment: jest.fn() },
47+
};
48+
49+
const result = clientOptionsToParameters(options, mockStore);
50+
51+
expect(result.flagConfigurationStore).toBe(mockStore);
52+
});
53+
54+
it('converts client options with event ingestion config', () => {
55+
const options: IClientOptions = {
56+
sdkKey: 'test-key',
57+
assignmentLogger: { logAssignment: jest.fn() },
58+
};
59+
const mockDispatcher: EventDispatcher = td.object<EventDispatcher>();
60+
61+
const result = clientOptionsToParameters(options, mockStore, mockDispatcher);
62+
63+
expect(result.eventDispatcher).toBeDefined();
64+
});
65+
66+
it('converts client options with polling configuration', () => {
67+
const options: IClientOptions = {
68+
sdkKey: 'test-key',
69+
assignmentLogger: { logAssignment: jest.fn() },
70+
pollingIntervalMs: 30000,
71+
pollAfterSuccessfulInitialization: true,
72+
pollAfterFailedInitialization: true,
73+
skipInitialRequest: true,
74+
};
75+
76+
const result = clientOptionsToParameters(options, mockStore);
77+
78+
expect(result.configurationRequestParameters).toMatchObject({
79+
pollingIntervalMs: 30000,
80+
pollAfterSuccessfulInitialization: true,
81+
pollAfterFailedInitialization: true,
82+
skipInitialPoll: true,
83+
});
84+
});
85+
86+
it('converts client options with retry configuration', () => {
87+
const options: IClientOptions = {
88+
sdkKey: 'test-key',
89+
assignmentLogger: { logAssignment: jest.fn() },
90+
requestTimeoutMs: 5000,
91+
numInitialRequestRetries: 3,
92+
numPollRequestRetries: 2,
93+
};
94+
95+
const result = clientOptionsToParameters(options, mockStore);
96+
97+
expect(result.configurationRequestParameters).toMatchObject({
98+
requestTimeoutMs: 5000,
99+
numInitialRequestRetries: 3,
100+
numPollRequestRetries: 2,
101+
});
102+
});
103+
104+
it('handles undefined optional parameters', () => {
105+
const options: IClientOptions = {
106+
sdkKey: 'test-key',
107+
assignmentLogger: { logAssignment: jest.fn() },
108+
};
109+
110+
const result = clientOptionsToParameters(options, mockStore);
111+
112+
expect(result.configurationRequestParameters).toMatchObject({
113+
baseUrl: undefined,
114+
pollingIntervalMs: undefined,
115+
requestTimeoutMs: undefined,
116+
numInitialRequestRetries: undefined,
117+
numPollRequestRetries: undefined,
118+
});
119+
});
120+
121+
it('includes sdk metadata', () => {
122+
const options: IClientOptions = {
123+
sdkKey: 'test-key',
124+
assignmentLogger: { logAssignment: jest.fn() },
125+
};
126+
127+
const result = clientOptionsToParameters(options, mockStore);
128+
129+
expect(result.configurationRequestParameters).toMatchObject({
130+
sdkName,
131+
sdkVersion,
132+
});
133+
});
134+
});

src/client-options-converter.ts

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import {
2+
BanditParameters,
3+
BanditVariation,
4+
EventDispatcher,
5+
Flag,
6+
FlagConfigurationRequestParameters,
7+
IConfigurationStore,
8+
ObfuscatedFlag,
9+
} from '@eppo/js-client-sdk-common';
10+
11+
import { IClientOptions } from './i-client-config';
12+
import { sdkName, sdkVersion } from './sdk-data';
13+
14+
export type EppoClientParameters = {
15+
// Dispatcher for arbitrary, application-level events (not to be confused with Eppo specific assignment
16+
// or bandit events). These events are application-specific and captures by EppoClient#track API.
17+
eventDispatcher?: EventDispatcher;
18+
flagConfigurationStore: IConfigurationStore<Flag | ObfuscatedFlag>;
19+
banditVariationConfigurationStore?: IConfigurationStore<BanditVariation[]>;
20+
banditModelConfigurationStore?: IConfigurationStore<BanditParameters>;
21+
configurationRequestParameters?: FlagConfigurationRequestParameters;
22+
isObfuscated?: boolean;
23+
};
24+
25+
/**
26+
* Converts IClientOptions to EppoClientParameters
27+
* @internal
28+
*/
29+
export function clientOptionsToParameters(
30+
options: IClientOptions,
31+
flagConfigurationStore: IConfigurationStore<Flag>,
32+
eventDispatcher?: EventDispatcher,
33+
): EppoClientParameters {
34+
const parameters: EppoClientParameters = {
35+
flagConfigurationStore,
36+
isObfuscated: true,
37+
};
38+
39+
parameters.eventDispatcher = eventDispatcher;
40+
41+
// Always include configuration request parameters
42+
parameters.configurationRequestParameters = {
43+
apiKey: options.sdkKey,
44+
sdkVersion, // dynamically picks up version.
45+
sdkName, // Hardcoded to `js-client-sdk`
46+
baseUrl: options.baseUrl,
47+
requestTimeoutMs: options.requestTimeoutMs,
48+
numInitialRequestRetries: options.numInitialRequestRetries,
49+
numPollRequestRetries: options.numPollRequestRetries,
50+
pollAfterSuccessfulInitialization: options.pollAfterSuccessfulInitialization,
51+
pollAfterFailedInitialization: options.pollAfterFailedInitialization,
52+
pollingIntervalMs: options.pollingIntervalMs,
53+
throwOnFailedInitialization: options.throwOnFailedInitialization,
54+
skipInitialPoll: options.skipInitialRequest,
55+
};
56+
57+
return parameters;
58+
}

src/index.spec.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ import { ServingStoreUpdateStrategy } from './isolatable-hybrid.store';
4141

4242
import {
4343
EppoJSClient,
44-
EppoJSClientV2,
4544
EppoPrecomputedJSClient,
4645
getConfigUrl,
4746
getInstance,
@@ -431,11 +430,10 @@ describe('decoupled initialization', () => {
431430
jest.restoreAllMocks();
432431
});
433432

434-
435433
it('should be independent of the singleton', async () => {
436434
const apiOptions: IApiOptions = { sdkKey: '<MY SDK KEY>' };
437435
const options: IClientOptions = { ...apiOptions, assignmentLogger: mockLogger };
438-
const isolatedClient = new EppoJSClientV2(options);
436+
const isolatedClient = new EppoJSClient(options);
439437

440438
expect(isolatedClient).not.toEqual(getInstance());
441439
await isolatedClient.waitForReady();
@@ -455,7 +453,7 @@ describe('decoupled initialization', () => {
455453
it('initializes on instantiation and notifies when ready', async () => {
456454
const apiOptions: IApiOptions = { sdkKey: '<MY SDK KEY>', baseUrl };
457455
const options: IClientOptions = { ...apiOptions, assignmentLogger: mockLogger };
458-
const client = new EppoJSClientV2(options);
456+
const client = new EppoJSClient(options);
459457

460458
expect(client.getStringAssignment(flagKey, 'subject-10', {}, 'default-value')).toEqual(
461459
'default-value',
@@ -529,7 +527,7 @@ describe('decoupled initialization', () => {
529527
);
530528
expect(callCount).toBe(1);
531529

532-
const myClient2 = new EppoJSClientV2({ ...commonOptions, sdkKey: API_KEY_2 });
530+
const myClient2 = new EppoJSClient({ ...commonOptions, sdkKey: API_KEY_2 });
533531
await myClient2.waitForReady();
534532
expect(callCount).toBe(2);
535533

@@ -540,7 +538,7 @@ describe('decoupled initialization', () => {
540538
'variant-2',
541539
);
542540

543-
const myClient3 = new EppoJSClientV2({ ...commonOptions, sdkKey: API_KEY_3 });
541+
const myClient3 = new EppoJSClient({ ...commonOptions, sdkKey: API_KEY_3 });
544542
await myClient3.waitForReady();
545543

546544
expect(singleton.getStringAssignment(flagKey, 'subject-10', {}, 'default-value')).toEqual(

0 commit comments

Comments
 (0)