Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@eppo/js-client-sdk-common",
"version": "4.14.3",
"version": "4.14.4",
"description": "Common library for Eppo JavaScript SDKs (web, react native, and node)",
"main": "dist/index.js",
"files": [
Expand Down
17 changes: 17 additions & 0 deletions src/client/eppo-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -209,13 +209,17 @@ export default class EppoClient {
// noinspection JSUnusedGlobalSymbols
setFlagConfigurationStore(flagConfigurationStore: IConfigurationStore<Flag | ObfuscatedFlag>) {
this.flagConfigurationStore = flagConfigurationStore;

this.maybeUpdateConfigRequestor();
}

// noinspection JSUnusedGlobalSymbols
setBanditVariationConfigurationStore(
banditVariationConfigurationStore: IConfigurationStore<BanditVariation[]>,
) {
this.banditVariationConfigurationStore = banditVariationConfigurationStore;

this.maybeUpdateConfigRequestor();
}

/** Sets the EventDispatcher instance to use when tracking events with {@link track}. */
Expand Down Expand Up @@ -244,6 +248,19 @@ export default class EppoClient {
banditModelConfigurationStore: IConfigurationStore<BanditParameters>,
) {
this.banditModelConfigurationStore = banditModelConfigurationStore;

this.maybeUpdateConfigRequestor();
}

private maybeUpdateConfigRequestor() {
// Update the ConfigurationRequestor if it exists
if (this.configurationRequestor) {
this.configurationRequestor.setConfigurationStores(
this.flagConfigurationStore,
this.banditVariationConfigurationStore || null,
this.banditModelConfigurationStore || null,
);
}
}

// noinspection JSUnusedGlobalSymbols
Expand Down
69 changes: 68 additions & 1 deletion src/configuration-requestor.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
IUniversalFlagConfigResponse,
} from './http-client';
import { StoreBackedConfiguration } from './i-configuration';
import { BanditParameters, BanditVariation, Flag } from './interfaces';
import { BanditParameters, BanditVariation, Flag, VariationType } from './interfaces';

describe('ConfigurationRequestor', () => {
let flagStore: IConfigurationStore<Flag>;
Expand Down Expand Up @@ -610,7 +610,7 @@
banditModelStore,
);

const ufcResponse = {

Check warning on line 613 in src/configuration-requestor.spec.ts

View workflow job for this annotation

GitHub Actions / lint-test-sdk (18)

'ufcResponse' is assigned a value but never used

Check warning on line 613 in src/configuration-requestor.spec.ts

View workflow job for this annotation

GitHub Actions / lint-test-sdk (20)

'ufcResponse' is assigned a value but never used

Check warning on line 613 in src/configuration-requestor.spec.ts

View workflow job for this annotation

GitHub Actions / lint-test-sdk (22)

'ufcResponse' is assigned a value but never used

Check warning on line 613 in src/configuration-requestor.spec.ts

View workflow job for this annotation

GitHub Actions / lint-test-sdk (23)

'ufcResponse' is assigned a value but never used
flags: { test_flag: { key: 'test_flag', value: true } },
banditReferences: {
bandit: {
Expand Down Expand Up @@ -641,5 +641,72 @@
// expect(fetchSpy.mock.calls.length).toBe(initialFetchCount + 1);
});
});

describe('IConfigurationStore updates', () => {
it('should update configuration when stores are changed', async () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

// Create new stores
const newFlagStore = new MemoryOnlyConfigurationStore<Flag>();
const newBanditVariationStore = new MemoryOnlyConfigurationStore<BanditVariation[]>();
const newBanditModelStore = new MemoryOnlyConfigurationStore<BanditParameters>();

// Add a test flag to the new flag store
await newFlagStore.setEntries({
'test-flag': {
key: 'test-flag',
enabled: true,
variationType: VariationType.STRING,
variations: {
control: { key: 'control', value: 'control-value' },
treatment: { key: 'treatment', value: 'treatment-value' },
},
allocations: [
{
key: 'allocation-1',
rules: [],
splits: [
{
shards: [{ salt: '', ranges: [{ start: 0, end: 10000 }] }],
variationKey: 'treatment',
},
],
doLog: true,
},
],
totalShards: 10000,
},
});

await newBanditModelStore.setEntries({
'test-bandit': {
banditKey: 'test-bandt',
modelVersion: 'v123',
modelName: 'falcon',
modelData: {
coefficients: {},
gamma: 0,
defaultActionScore: 0,
actionProbabilityFloor: 0,
},
},
});

// Get the configuration and verify it has the test flag
const initialConfig = configurationRequestor.getConfiguration();
expect(initialConfig.getFlagKeys()).toEqual([]);
expect(Object.keys(initialConfig.getBandits())).toEqual([]);

// Update the stores
configurationRequestor.setConfigurationStores(
newFlagStore,
newBanditVariationStore,
newBanditModelStore,
);

// Get the configuration and verify it has the test flag
const config = configurationRequestor.getConfiguration();
expect(config.getFlagKeys()).toEqual(['test-flag']);
expect(Object.keys(config.getBandits())).toEqual(['test-bandit']);
});
});
});
});
30 changes: 24 additions & 6 deletions src/configuration-requestor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,13 @@ import { BanditVariation, BanditParameters, Flag, BanditReference } from './inte
// Requests AND stores flag configurations
export default class ConfigurationRequestor {
private banditModelVersions: string[] = [];
private readonly configuration: StoreBackedConfiguration;
private configuration: StoreBackedConfiguration;

constructor(
private readonly httpClient: IHttpClient,
private readonly flagConfigurationStore: IConfigurationStore<Flag>,
private readonly banditVariationConfigurationStore: IConfigurationStore<
BanditVariation[]
> | null,
private readonly banditModelConfigurationStore: IConfigurationStore<BanditParameters> | null,
private flagConfigurationStore: IConfigurationStore<Flag>,
private banditVariationConfigurationStore: IConfigurationStore<BanditVariation[]> | null,
private banditModelConfigurationStore: IConfigurationStore<BanditParameters> | null,
) {
this.configuration = new StoreBackedConfiguration(
this.flagConfigurationStore,
Expand All @@ -27,6 +25,26 @@ export default class ConfigurationRequestor {
);
}

/**
* Updates the configuration stores and recreates the StoreBackedConfiguration
*/
public setConfigurationStores(
flagConfigurationStore: IConfigurationStore<Flag>,
banditVariationConfigurationStore: IConfigurationStore<BanditVariation[]> | null,
banditModelConfigurationStore: IConfigurationStore<BanditParameters> | null,
): void {
this.flagConfigurationStore = flagConfigurationStore;
this.banditVariationConfigurationStore = banditVariationConfigurationStore;
this.banditModelConfigurationStore = banditModelConfigurationStore;

// Recreate the configuration with the new stores
this.configuration = new StoreBackedConfiguration(
this.flagConfigurationStore,
this.banditVariationConfigurationStore,
this.banditModelConfigurationStore,
);
}

public isFlagConfigExpired(): Promise<boolean> {
return this.flagConfigurationStore.isExpired();
}
Expand Down
Loading