Skip to content

Commit b76e8d8

Browse files
Provide a customer persistence store (FF-2085) (#65)
* bump to commons 3.0.6; add LocalStorageBackedAsyncStore (FF-1980) * Provide a customer persistence store (FF-2085) * rename to persistentStore * docs
1 parent 6d08baa commit b76e8d8

7 files changed

+57
-9
lines changed

docs/js-client-sdk.iclientconfig.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,10 @@ export interface IClientConfig
2121
| [baseUrl?](./js-client-sdk.iclientconfig.baseurl.md) | | string | _(Optional)_ Base URL of the Eppo API. Clients should use the default setting in most cases. |
2222
| [numInitialRequestRetries?](./js-client-sdk.iclientconfig.numinitialrequestretries.md) | | number | _(Optional)_ Number of additional times the initial configuration request will be attempted if it fails. This is the request typically synchronously waited (via await) for completion. A small wait will be done between requests. (Default: 1) |
2323
| [numPollRequestRetries?](./js-client-sdk.iclientconfig.numpollrequestretries.md) | | number | _(Optional)_ Number of additional times polling for updated configurations will be attempted before giving up. Polling is done after a successful initial request. Subsequent attempts are done using an exponential backoff. (Default: 7) |
24+
| [persistentStore?](./js-client-sdk.iclientconfig.persistentstore.md) | | IAsyncStore<Flag> | _(Optional)_ A custom class to use for storing flag configurations. This is useful for cases where you want to use a different storage mechanism than the default storage provided by the SDK. |
2425
| [pollAfterFailedInitialization?](./js-client-sdk.iclientconfig.pollafterfailedinitialization.md) | | boolean | _(Optional)_ Poll for new configurations even if the initial configuration request failed. (default: false) |
2526
| [pollAfterSuccessfulInitialization?](./js-client-sdk.iclientconfig.pollaftersuccessfulinitialization.md) | | boolean | _(Optional)_ Poll for new configurations (every 30 seconds) after successfully requesting the initial configuration. (default: false) |
2627
| [requestTimeoutMs?](./js-client-sdk.iclientconfig.requesttimeoutms.md) | | number | _(Optional)_ \* Timeout in milliseconds for the HTTPS request for the experiment configuration. (Default: 5000) |
28+
| [skipInitialRequest?](./js-client-sdk.iclientconfig.skipinitialrequest.md) | | boolean | _(Optional)_ Skip the request for new configurations during initialization. (default: false) |
2729
| [throwOnFailedInitialization?](./js-client-sdk.iclientconfig.throwonfailedinitialization.md) | | boolean | _(Optional)_ Throw an error if unable to fetch an initial configuration during initialization. (default: true) |
2830

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
2+
3+
[Home](./index.md) &gt; [@eppo/js-client-sdk](./js-client-sdk.md) &gt; [IClientConfig](./js-client-sdk.iclientconfig.md) &gt; [persistentStore](./js-client-sdk.iclientconfig.persistentstore.md)
4+
5+
## IClientConfig.persistentStore property
6+
7+
A custom class to use for storing flag configurations. This is useful for cases where you want to use a different storage mechanism than the default storage provided by the SDK.
8+
9+
**Signature:**
10+
11+
```typescript
12+
persistentStore?: IAsyncStore<Flag>;
13+
```
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
2+
3+
[Home](./index.md) &gt; [@eppo/js-client-sdk](./js-client-sdk.md) &gt; [IClientConfig](./js-client-sdk.iclientconfig.md) &gt; [skipInitialRequest](./js-client-sdk.iclientconfig.skipinitialrequest.md)
4+
5+
## IClientConfig.skipInitialRequest property
6+
7+
Skip the request for new configurations during initialization. (default: false)
8+
9+
**Signature:**
10+
11+
```typescript
12+
skipInitialRequest?: boolean;
13+
```

js-client-sdk.api.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@
55
```ts
66

77
import { EppoClient } from '@eppo/js-client-sdk-common';
8+
import { Flag } from '@eppo/js-client-sdk-common';
89
import { IAssignmentEvent } from '@eppo/js-client-sdk-common';
910
import { IAssignmentLogger } from '@eppo/js-client-sdk-common';
11+
import { IAsyncStore } from '@eppo/js-client-sdk-common';
1012
import { IEppoClient } from '@eppo/js-client-sdk-common';
1113

1214
// @public
@@ -41,9 +43,11 @@ export interface IClientConfig {
4143
baseUrl?: string;
4244
numInitialRequestRetries?: number;
4345
numPollRequestRetries?: number;
46+
persistentStore?: IAsyncStore<Flag>;
4447
pollAfterFailedInitialization?: boolean;
4548
pollAfterSuccessfulInitialization?: boolean;
4649
requestTimeoutMs?: number;
50+
skipInitialRequest?: boolean;
4751
throwOnFailedInitialization?: boolean;
4852
}
4953

src/configuration-factory.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,20 @@
11
import {
22
Flag,
33
HybridConfigurationStore,
4+
IAsyncStore,
45
IConfigurationStore,
56
MemoryOnlyConfigurationStore,
67
MemoryStore,
78
} from '@eppo/js-client-sdk-common';
89

910
import { LocalStorageBackedAsyncStore } from './local-storage';
1011

11-
export function configurationStorageFactory(): IConfigurationStore<Flag> {
12-
if (hasWindowLocalStorage()) {
12+
export function configurationStorageFactory(
13+
persistenceStore?: IAsyncStore<Flag>,
14+
): IConfigurationStore<Flag> {
15+
if (persistenceStore) {
16+
return new HybridConfigurationStore(new MemoryStore<Flag>(), persistenceStore);
17+
} else if (hasWindowLocalStorage()) {
1318
// fallback to window.localStorage if available
1419
return new HybridConfigurationStore(
1520
new MemoryStore<Flag>(),

src/index.spec.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -563,8 +563,7 @@ describe('initialization options', () => {
563563

564564
it('returns empty assignments pre-initialization by default', async () => {
565565
returnUfc = () => mockConfigResponse;
566-
const client = getInstance();
567-
expect(client.getStringAssignment(flagKey, 'subject-10', {}, 'default-value')).toBe(
566+
expect(getInstance().getStringAssignment(flagKey, 'subject-10', {}, 'default-value')).toBe(
568567
'default-value',
569568
);
570569
// don't await
@@ -573,12 +572,14 @@ describe('initialization options', () => {
573572
baseUrl,
574573
assignmentLogger: mockLogger,
575574
});
576-
expect(client.getStringAssignment(flagKey, 'subject', {}, 'default-value')).toBe(
575+
expect(getInstance().getStringAssignment(flagKey, 'subject', {}, 'default-value')).toBe(
577576
'default-value',
578577
);
579578
// Advance time so a poll happened and check again
580579
await jest.advanceTimersByTimeAsync(POLL_INTERVAL_MS);
581-
expect(client.getStringAssignment(flagKey, 'subject', {}, 'default-value')).toBe('control');
580+
expect(getInstance().getStringAssignment(flagKey, 'subject', {}, 'default-value')).toBe(
581+
'control',
582+
);
582583
});
583584
});
584585
});

src/index.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import {
44
IEppoClient,
55
EppoClient,
66
FlagConfigurationRequestParameters,
7+
Flag,
8+
IAsyncStore,
79
} from '@eppo/js-client-sdk-common';
810

911
import { configurationStorageFactory } from './configuration-factory';
@@ -65,6 +67,13 @@ export interface IClientConfig {
6567
*/
6668
numPollRequestRetries?: number;
6769

70+
/**
71+
* A custom class to use for storing flag configurations.
72+
* This is useful for cases where you want to use a different storage mechanism
73+
* than the default storage provided by the SDK.
74+
*/
75+
persistentStore?: IAsyncStore<Flag>;
76+
6877
/**
6978
* Skip the request for new configurations during initialization. (default: false)
7079
*/
@@ -73,14 +82,12 @@ export interface IClientConfig {
7382

7483
export { IAssignmentLogger, IAssignmentEvent, IEppoClient } from '@eppo/js-client-sdk-common';
7584

76-
const localStorage = configurationStorageFactory();
77-
7885
/**
7986
* Client for assigning experiment variations.
8087
* @public
8188
*/
8289
export class EppoJSClient extends EppoClient {
83-
public static instance: EppoJSClient = new EppoJSClient(localStorage, undefined, true);
90+
public static instance: EppoJSClient;
8491
public static initialized = false;
8592

8693
public getStringAssignment(
@@ -154,6 +161,9 @@ export async function init(config: IClientConfig): Promise<IEppoClient> {
154161
EppoJSClient.instance.stopPolling();
155162
}
156163

164+
const configurationStore = configurationStorageFactory(config.persistentStore);
165+
EppoJSClient.instance = new EppoJSClient(configurationStore, undefined, true);
166+
157167
const requestConfiguration: FlagConfigurationRequestParameters = {
158168
apiKey: config.apiKey,
159169
sdkName,

0 commit comments

Comments
 (0)