From ecf184d495b43fb127fd44a4451f6b078a1f745d Mon Sep 17 00:00:00 2001 From: Sameeran Kunche Date: Thu, 5 Dec 2024 09:28:37 -0800 Subject: [PATCH 01/32] Create offlinePrecomputedInit method --- src/index.ts | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/src/index.ts b/src/index.ts index 954c417..50cce36 100644 --- a/src/index.ts +++ b/src/index.ts @@ -17,6 +17,7 @@ import { ObfuscatedFlag, BoundedEventQueue, validation, + PrecomputedFlag, } from '@eppo/js-client-sdk-common'; import { assignmentCacheFactory } from './cache/assignment-cache-factory'; @@ -624,3 +625,52 @@ function newEventDispatcher(sdkKey: string): EventDispatcher { typeof window !== 'undefined' ? new BrowserNetworkStatusListener() : emptyNetworkStatusListener; return newDefaultEventDispatcher(eventQueue, networkStatusListener, sdkKey); } + +/** + * Initializes the Eppo precomputed client with configuration parameters. + * + * The purpose is for use-cases where the precomputed assignments are available from an external process + * that can bootstrap the SDK. + * + * This method should be called once on application startup. + * + * @param config - client configuration + * @returns a singleton precomputed client instance + * @public + */ +export interface IPrecomputedClientConfigSync { + precomputedAssignments: Record; + assignmentLogger?: IAssignmentLogger; + throwOnFailedInitialization?: boolean; +} + +export function offlinePrecomputedInit( + config: IPrecomputedClientConfigSync, +): EppoPrecomputedClient { + const throwOnFailedInitialization = config.throwOnFailedInitialization ?? true; + + try { + const memoryOnlyPrecomputedStore = precomputedFlagsStorageFactory(); + memoryOnlyPrecomputedStore + .setEntries(config.precomputedAssignments) + .catch((err) => + applicationLogger.warn('Error setting precomputed assignments for memory-only store', err), + ); + + EppoPrecomputedJSClient.instance.setPrecomputedFlagStore(memoryOnlyPrecomputedStore); + + if (config.assignmentLogger) { + EppoPrecomputedJSClient.instance.setAssignmentLogger(config.assignmentLogger); + } + } catch (error) { + applicationLogger.warn( + 'Eppo SDK encountered an error initializing precomputed client, assignment calls will return the default value and not be logged', + ); + if (throwOnFailedInitialization) { + throw error; + } + } + + EppoPrecomputedJSClient.initialized = true; + return EppoPrecomputedJSClient.instance; +} From 80340ca5758786888db415aa2e76833dfb90781d Mon Sep 17 00:00:00 2001 From: Sameeran Kunche Date: Sun, 8 Dec 2024 23:00:20 -0800 Subject: [PATCH 02/32] Adjust config init format to have a precompute object --- package.json | 2 +- src/i-client-config.ts | 12 ++++++++++-- src/index.spec.ts | 6 ++++-- src/index.ts | 34 ++++++++++++++++++++++++---------- 4 files changed, 39 insertions(+), 15 deletions(-) diff --git a/package.json b/package.json index 3d92aac..0ce2e98 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ "webpack-cli": "^4.10.0" }, "dependencies": { - "@eppo/js-client-sdk-common": "^4.5.4" + "@eppo/js-client-sdk-common": "^4.6.1" }, "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" } diff --git a/src/i-client-config.ts b/src/i-client-config.ts index 0c149fd..72f0546 100644 --- a/src/i-client-config.ts +++ b/src/i-client-config.ts @@ -63,10 +63,10 @@ interface IBaseRequestConfig { } /** - * Configuration for Eppo precomputed client initialization + * Configuration for precomputed flag assignments * @public */ -export interface IPrecomputedClientConfig extends IBaseRequestConfig { +export interface IPrecompute { /** * Subject key to use for precomputed flag assignments. */ @@ -78,6 +78,14 @@ export interface IPrecomputedClientConfig extends IBaseRequestConfig { subjectAttributes?: Record; } +/** + * Configuration for Eppo precomputed client initialization + * @public + */ +export interface IPrecomputedClientConfig extends IBaseRequestConfig { + precompute: IPrecompute; +} + /** * Configuration for regular client initialization * @public diff --git a/src/index.spec.ts b/src/index.spec.ts index 791a35a..6760013 100644 --- a/src/index.spec.ts +++ b/src/index.spec.ts @@ -1127,8 +1127,10 @@ describe('EppoPrecomputedJSClient E2E test', () => { apiKey: 'dummy', baseUrl: 'http://127.0.0.1:4000', assignmentLogger: mockLogger, - subjectKey: 'test-subject', - subjectAttributes: { attr1: 'value1' }, + precompute: { + subjectKey: 'test-subject', + subjectAttributes: { attr1: 'value1' }, + }, }); }); diff --git a/src/index.ts b/src/index.ts index 50cce36..5404b1a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -32,7 +32,7 @@ import { } from './configuration-factory'; import BrowserNetworkStatusListener from './events/browser-network-status-listener'; import LocalStorageBackedNamedEventQueue from './events/local-storage-backed-named-event-queue'; -import { IClientConfig, IPrecomputedClientConfig } from './i-client-config'; +import { IClientConfig, IPrecompute, IPrecomputedClientConfig } from './i-client-config'; import { sdkName, sdkVersion } from './sdk-data'; /** @@ -557,13 +557,12 @@ export async function precomputedInit( config: IPrecomputedClientConfig, ): Promise { validation.validateNotBlank(config.apiKey, 'API key required'); - validation.validateNotBlank(config.subjectKey, 'Subject key required'); + validation.validateNotBlank(config.precompute.subjectKey, 'Subject key required'); const instance = EppoPrecomputedJSClient.instance; const { apiKey, - subjectKey, - subjectAttributes = {}, + precompute: { subjectKey, subjectAttributes = {} }, baseUrl, requestTimeoutMs, numInitialRequestRetries, @@ -583,10 +582,7 @@ export async function precomputedInit( sdkName, sdkVersion, baseUrl, - precompute: { - subjectKey, - subjectAttributes, - }, + precompute: { subjectKey, subjectAttributes }, requestTimeoutMs, numInitialRequestRetries, numPollRequestRetries, @@ -596,7 +592,7 @@ export async function precomputedInit( throwOnFailedInitialization: true, // always use true here as underlying instance fetch is surrounded by try/catch skipInitialPoll: skipInitialRequest, }; - instance.setPrecomputedFlagsRequestParameters(precomputedFlagsRequestParameters); + instance.setSubjectAndPrecomputedFlagsRequestParameters(precomputedFlagsRequestParameters); await instance.fetchPrecomputedFlags(); @@ -639,11 +635,24 @@ function newEventDispatcher(sdkKey: string): EventDispatcher { * @public */ export interface IPrecomputedClientConfigSync { + precompute: IPrecompute; precomputedAssignments: Record; assignmentLogger?: IAssignmentLogger; throwOnFailedInitialization?: boolean; } +/** + * Initializes the Eppo precomputed client with configuration parameters. + * + * The purpose is for use-cases where the precomputed assignments are available from an external process + * that can bootstrap the SDK. + * + * This method should be called once on application startup. + * + * @param config - precomputed client configuration + * @returns a singleton precomputed client instance + * @public + */ export function offlinePrecomputedInit( config: IPrecomputedClientConfigSync, ): EppoPrecomputedClient { @@ -657,7 +666,12 @@ export function offlinePrecomputedInit( applicationLogger.warn('Error setting precomputed assignments for memory-only store', err), ); - EppoPrecomputedJSClient.instance.setPrecomputedFlagStore(memoryOnlyPrecomputedStore); + const { subjectKey, subjectAttributes = {} } = config.precompute; + EppoPrecomputedJSClient.instance.setSubjectAndPrecomputedFlagStore( + subjectKey, + subjectAttributes, + memoryOnlyPrecomputedStore, + ); if (config.assignmentLogger) { EppoPrecomputedJSClient.instance.setAssignmentLogger(config.assignmentLogger); From ce4bf21d8edea2a313721ad12075e13e6bd57f94 Mon Sep 17 00:00:00 2001 From: Sameeran Kunche Date: Mon, 9 Dec 2024 00:11:10 -0800 Subject: [PATCH 03/32] v3.9.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0ce2e98..a8045cc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@eppo/js-client-sdk", - "version": "3.8.2", + "version": "3.9.0", "description": "Eppo SDK for client-side JavaScript applications", "main": "dist/index.js", "files": [ From 3fdd774290a0f10019d38772effcbe00bbb98352 Mon Sep 17 00:00:00 2001 From: Sameeran Kunche Date: Mon, 9 Dec 2024 00:11:52 -0800 Subject: [PATCH 04/32] Bump minor version --- ...mputedclientconfigsync.assignmentlogger.md | 11 +++ ...client-sdk.iprecomputedclientconfigsync.md | 97 +++++++++++++++++++ ...clientconfigsync.precomputedassignments.md | 11 +++ ...tconfigsync.throwonfailedinitialization.md | 11 +++ docs/js-client-sdk.md | 24 +++++ docs/js-client-sdk.offlineprecomputedinit.md | 49 ++++++++++ 6 files changed, 203 insertions(+) create mode 100644 docs/js-client-sdk.iprecomputedclientconfigsync.assignmentlogger.md create mode 100644 docs/js-client-sdk.iprecomputedclientconfigsync.md create mode 100644 docs/js-client-sdk.iprecomputedclientconfigsync.precomputedassignments.md create mode 100644 docs/js-client-sdk.iprecomputedclientconfigsync.throwonfailedinitialization.md create mode 100644 docs/js-client-sdk.offlineprecomputedinit.md diff --git a/docs/js-client-sdk.iprecomputedclientconfigsync.assignmentlogger.md b/docs/js-client-sdk.iprecomputedclientconfigsync.assignmentlogger.md new file mode 100644 index 0000000..ba011e3 --- /dev/null +++ b/docs/js-client-sdk.iprecomputedclientconfigsync.assignmentlogger.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@eppo/js-client-sdk](./js-client-sdk.md) > [IPrecomputedClientConfigSync](./js-client-sdk.iprecomputedclientconfigsync.md) > [assignmentLogger](./js-client-sdk.iprecomputedclientconfigsync.assignmentlogger.md) + +## IPrecomputedClientConfigSync.assignmentLogger property + +**Signature:** + +```typescript +assignmentLogger?: IAssignmentLogger; +``` diff --git a/docs/js-client-sdk.iprecomputedclientconfigsync.md b/docs/js-client-sdk.iprecomputedclientconfigsync.md new file mode 100644 index 0000000..32dbb93 --- /dev/null +++ b/docs/js-client-sdk.iprecomputedclientconfigsync.md @@ -0,0 +1,97 @@ + + +[Home](./index.md) > [@eppo/js-client-sdk](./js-client-sdk.md) > [IPrecomputedClientConfigSync](./js-client-sdk.iprecomputedclientconfigsync.md) + +## IPrecomputedClientConfigSync interface + +Initializes the Eppo precomputed client with configuration parameters. + +The purpose is for use-cases where the precomputed assignments are available from an external process that can bootstrap the SDK. + +This method should be called once on application startup. + +**Signature:** + +```typescript +export interface IPrecomputedClientConfigSync +``` + +## Properties + + + + + +
+ +Property + + + + +Modifiers + + + + +Type + + + + +Description + + +
+ +[assignmentLogger?](./js-client-sdk.iprecomputedclientconfigsync.assignmentlogger.md) + + + + + + + +IAssignmentLogger + + + + +_(Optional)_ + + +
+ +[precomputedAssignments](./js-client-sdk.iprecomputedclientconfigsync.precomputedassignments.md) + + + + + + + +Record<string, PrecomputedFlag> + + + + + +
+ +[throwOnFailedInitialization?](./js-client-sdk.iprecomputedclientconfigsync.throwonfailedinitialization.md) + + + + + + + +boolean + + + + +_(Optional)_ + + +
diff --git a/docs/js-client-sdk.iprecomputedclientconfigsync.precomputedassignments.md b/docs/js-client-sdk.iprecomputedclientconfigsync.precomputedassignments.md new file mode 100644 index 0000000..61d30c6 --- /dev/null +++ b/docs/js-client-sdk.iprecomputedclientconfigsync.precomputedassignments.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@eppo/js-client-sdk](./js-client-sdk.md) > [IPrecomputedClientConfigSync](./js-client-sdk.iprecomputedclientconfigsync.md) > [precomputedAssignments](./js-client-sdk.iprecomputedclientconfigsync.precomputedassignments.md) + +## IPrecomputedClientConfigSync.precomputedAssignments property + +**Signature:** + +```typescript +precomputedAssignments: Record; +``` diff --git a/docs/js-client-sdk.iprecomputedclientconfigsync.throwonfailedinitialization.md b/docs/js-client-sdk.iprecomputedclientconfigsync.throwonfailedinitialization.md new file mode 100644 index 0000000..e20f953 --- /dev/null +++ b/docs/js-client-sdk.iprecomputedclientconfigsync.throwonfailedinitialization.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@eppo/js-client-sdk](./js-client-sdk.md) > [IPrecomputedClientConfigSync](./js-client-sdk.iprecomputedclientconfigsync.md) > [throwOnFailedInitialization](./js-client-sdk.iprecomputedclientconfigsync.throwonfailedinitialization.md) + +## IPrecomputedClientConfigSync.throwOnFailedInitialization property + +**Signature:** + +```typescript +throwOnFailedInitialization?: boolean; +``` diff --git a/docs/js-client-sdk.md b/docs/js-client-sdk.md index bdaed9d..1ccf3b0 100644 --- a/docs/js-client-sdk.md +++ b/docs/js-client-sdk.md @@ -138,6 +138,15 @@ The purpose is for use-cases where the configuration is available from an extern This method should be called once on application startup. + + + +[offlinePrecomputedInit(config)](./js-client-sdk.offlineprecomputedinit.md) + + + + + @@ -197,5 +206,20 @@ Configuration interface for synchronous client initialization. Configuration for Eppo precomputed client initialization + + + +[IPrecomputedClientConfigSync](./js-client-sdk.iprecomputedclientconfigsync.md) + + + + +Initializes the Eppo precomputed client with configuration parameters. + +The purpose is for use-cases where the precomputed assignments are available from an external process that can bootstrap the SDK. + +This method should be called once on application startup. + + diff --git a/docs/js-client-sdk.offlineprecomputedinit.md b/docs/js-client-sdk.offlineprecomputedinit.md new file mode 100644 index 0000000..7ec3e54 --- /dev/null +++ b/docs/js-client-sdk.offlineprecomputedinit.md @@ -0,0 +1,49 @@ + + +[Home](./index.md) > [@eppo/js-client-sdk](./js-client-sdk.md) > [offlinePrecomputedInit](./js-client-sdk.offlineprecomputedinit.md) + +## offlinePrecomputedInit() function + +**Signature:** + +```typescript +export declare function offlinePrecomputedInit(config: IPrecomputedClientConfigSync): EppoPrecomputedClient; +``` + +## Parameters + + + +
+ +Parameter + + + + +Type + + + + +Description + + +
+ +config + + + + +[IPrecomputedClientConfigSync](./js-client-sdk.iprecomputedclientconfigsync.md) + + + + + +
+**Returns:** + +EppoPrecomputedClient + From 2cbe3e57c6ae7746e840ed1f43c8d13f0501d32a Mon Sep 17 00:00:00 2001 From: Sameeran Kunche Date: Mon, 9 Dec 2024 00:35:58 -0800 Subject: [PATCH 05/32] Test for offlinePrecomputedInit --- src/index.spec.ts | 56 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/src/index.spec.ts b/src/index.spec.ts index 6760013..d0ae539 100644 --- a/src/index.spec.ts +++ b/src/index.spec.ts @@ -1179,3 +1179,59 @@ describe('EppoPrecomputedJSClient E2E test', () => { }); }); }); + +describe('offlinePrecomputedInit', () => { + let mockLogger: IAssignmentLogger; + + beforeEach(() => { + mockLogger = td.object(); + // Reset the static instance before each test + jest.isolateModules(() => { + // eslint-disable-next-line @typescript-eslint/no-var-requires + require('./index'); + }); + }); + + const mockPrecomputedAssignments = { + 'test-flag': { + allocationKey: 'allocation-123', + variationKey: 'variation-123', + variationType: VariationType.STRING, + variationValue: 'test-value', + extraLogging: {}, + doLog: true, + }, + }; + + it('initializes with precomputed assignments', () => { + const client = offlinePrecomputedInit({ + precompute: { + subjectKey: 'test-subject', + subjectAttributes: { attr1: 'value1' }, + }, + precomputedAssignments: mockPrecomputedAssignments, + assignmentLogger: mockLogger, + }); + + expect(client.getStringAssignment('test-flag', 'default')).toBe('test-value'); + expect(td.explain(mockLogger.logAssignment).callCount).toBe(1); + expect(td.explain(mockLogger.logAssignment).calls[0]?.args[0]).toMatchObject({ + subject: 'test-subject', + featureFlag: 'test-flag', + allocation: 'allocation-123', + variation: 'variation-123', + subjectAttributes: { attr1: 'value1' }, + }); + }); + + it('initializes with empty subject attributes', () => { + const client = offlinePrecomputedInit({ + precompute: { + subjectKey: 'test-subject', + }, + precomputedAssignments: mockPrecomputedAssignments, + }); + + expect(client.getStringAssignment('test-flag', 'default')).toBe('test-value'); + }); +}); From acbafaa18aecf7f51632e87834ce73b5f1933d39 Mon Sep 17 00:00:00 2001 From: Sameeran Kunche Date: Mon, 9 Dec 2024 14:48:21 -0800 Subject: [PATCH 06/32] Fix tests --- .DS_Store | Bin 0 -> 6148 bytes src/index.spec.ts | 1 + src/index.ts | 8 +++++++- yarn.lock | 8 ++++---- 4 files changed, 12 insertions(+), 5 deletions(-) create mode 100644 .DS_Store diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..576bddecb4f4ddfad4e513f63662750b9e6c2d72 GIT binary patch literal 6148 zcmeHK-AcnS6i&A3GKSC#6)ywc4xB#-!<$m)3s}($mD$>%#oCOvvlnB~OMM|9#pm&y zBn8KIE#l6B6@v8{PHgP2 z1AcpxlQ=K>{V(2Vwl+4~qAj}O-hWo5Uj)T8_k!#ett+LHu-1d{Iv$rJ zXZu{GMG&XsOc%t_7*g(T;xtmFr>1F?>ssFgL`QT+&hBj1@5z(n!MrDDgCnh<&gUJm zvwv`QIeJN6Q}u4hx5nF+dCu1H{0(Ghj~!(Oq{U zX#K false, onNetworkStatusChange: () => {} }; const networkStatusListener = typeof window !== 'undefined' ? new BrowserNetworkStatusListener() : emptyNetworkStatusListener; - return newDefaultEventDispatcher(eventQueue, networkStatusListener, sdkKey); + return newDefaultEventDispatcher( + eventQueue as NamedEventQueue, + networkStatusListener, + sdkKey, + ); } /** diff --git a/yarn.lock b/yarn.lock index 2c0b7e4..945b3b2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -380,10 +380,10 @@ resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== -"@eppo/js-client-sdk-common@^4.5.4": - version "4.5.4" - resolved "https://registry.yarnpkg.com/@eppo/js-client-sdk-common/-/js-client-sdk-common-4.5.4.tgz#089e0eb28ec2fd567f8ed2ec516b1b93f2ceb473" - integrity sha512-q3E9BTOcyAuPxWm9MqdqqV9uhaO4v8QW5d6tyDqQVZXC1hAgiMynAHH2DcuSEfE3v4s3JT0xjwzqkrcGNRCRlg== +"@eppo/js-client-sdk-common@^4.6.1": + version "4.6.1" + resolved "https://registry.yarnpkg.com/@eppo/js-client-sdk-common/-/js-client-sdk-common-4.6.1.tgz#3b23ed51052b8a5a8c720c47e04adab428675639" + integrity sha512-vKKBSFVO1ogouvSMuNWEHYKZnSZwI6M5TkXmk0peAwkUYMVD1Otuzcp29qPcvjeuLsD+/oja1eywW2MzWibHMQ== dependencies: buffer "npm:@eppo/buffer@6.2.0" js-base64 "^3.7.7" From e3f35a5d6aacd69b8a753a9ecbf3c45f108d8860 Mon Sep 17 00:00:00 2001 From: Sameeran Kunche Date: Tue, 10 Dec 2024 15:53:32 -0800 Subject: [PATCH 07/32] Update common version --- js-client-sdk.api.md | 22 ++++++++++++++++++++-- package.json | 2 +- yarn.lock | 8 ++++---- 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/js-client-sdk.api.md b/js-client-sdk.api.md index 41fb970..b58803b 100644 --- a/js-client-sdk.api.md +++ b/js-client-sdk.api.md @@ -19,6 +19,7 @@ import { IAssignmentLogger } from '@eppo/js-client-sdk-common'; import { IAsyncStore } from '@eppo/js-client-sdk-common'; import { IContainerExperiment } from '@eppo/js-client-sdk-common'; import { ObfuscatedFlag } from '@eppo/js-client-sdk-common'; +import { PrecomputedFlag } from '@eppo/js-client-sdk-common'; // @public export function buildStorageKeySuffix(apiKey: string): string; @@ -142,8 +143,22 @@ export function init(config: IClientConfig): Promise; // @public export interface IPrecomputedClientConfig extends IBaseRequestConfig { - subjectAttributes?: Record; - subjectKey: string; + // Warning: (ae-forgotten-export) The symbol "IPrecompute" needs to be exported by the entry point index.d.ts + // + // (undocumented) + precompute: IPrecompute; +} + +// @public +export interface IPrecomputedClientConfigSync { + // (undocumented) + assignmentLogger?: IAssignmentLogger; + // (undocumented) + precompute: IPrecompute; + // (undocumented) + precomputedAssignments: Record; + // (undocumented) + throwOnFailedInitialization?: boolean; } export { ObfuscatedFlag } @@ -151,6 +166,9 @@ export { ObfuscatedFlag } // @public export function offlineInit(config: IClientConfigSync): EppoClient; +// @public +export function offlinePrecomputedInit(config: IPrecomputedClientConfigSync): EppoPrecomputedClient; + // @public export function precomputedInit(config: IPrecomputedClientConfig): Promise; diff --git a/package.json b/package.json index a8045cc..b74db11 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ "webpack-cli": "^4.10.0" }, "dependencies": { - "@eppo/js-client-sdk-common": "^4.6.1" + "@eppo/js-client-sdk-common": "^4.6.2" }, "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" } diff --git a/yarn.lock b/yarn.lock index 945b3b2..31c7434 100644 --- a/yarn.lock +++ b/yarn.lock @@ -380,10 +380,10 @@ resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== -"@eppo/js-client-sdk-common@^4.6.1": - version "4.6.1" - resolved "https://registry.yarnpkg.com/@eppo/js-client-sdk-common/-/js-client-sdk-common-4.6.1.tgz#3b23ed51052b8a5a8c720c47e04adab428675639" - integrity sha512-vKKBSFVO1ogouvSMuNWEHYKZnSZwI6M5TkXmk0peAwkUYMVD1Otuzcp29qPcvjeuLsD+/oja1eywW2MzWibHMQ== +"@eppo/js-client-sdk-common@^4.6.2": + version "4.6.2" + resolved "https://registry.yarnpkg.com/@eppo/js-client-sdk-common/-/js-client-sdk-common-4.6.2.tgz#18b845e1411a9edf861a52e935a17d25ce5055af" + integrity sha512-jXxu3zaRd74IKepYj/s1TSMtRqp5pNLqgle7aD4PqfVGxmCFiBU81Jd/v9px7YzLId/8uUrQvLgIm6GAmTJMxg== dependencies: buffer "npm:@eppo/buffer@6.2.0" js-base64 "^3.7.7" From b0f73a7a858fa424d73c06802d32d327627a7efe Mon Sep 17 00:00:00 2001 From: Sameeran Kunche Date: Tue, 10 Dec 2024 15:54:12 -0800 Subject: [PATCH 08/32] Docs update --- .../js-client-sdk.iprecomputedclientconfig.md | 25 ++----------------- ...dk.iprecomputedclientconfig.precompute.md} | 8 +++--- ...ecomputedclientconfig.subjectattributes.md | 13 ---------- ...client-sdk.iprecomputedclientconfigsync.md | 17 +++++++++++++ ...iprecomputedclientconfigsync.precompute.md | 11 ++++++++ docs/js-client-sdk.md | 6 +++++ docs/js-client-sdk.offlineprecomputedinit.md | 10 ++++++++ 7 files changed, 49 insertions(+), 41 deletions(-) rename docs/{js-client-sdk.iprecomputedclientconfig.subjectkey.md => js-client-sdk.iprecomputedclientconfig.precompute.md} (52%) delete mode 100644 docs/js-client-sdk.iprecomputedclientconfig.subjectattributes.md create mode 100644 docs/js-client-sdk.iprecomputedclientconfigsync.precompute.md diff --git a/docs/js-client-sdk.iprecomputedclientconfig.md b/docs/js-client-sdk.iprecomputedclientconfig.md index 7e394c3..37f20e7 100644 --- a/docs/js-client-sdk.iprecomputedclientconfig.md +++ b/docs/js-client-sdk.iprecomputedclientconfig.md @@ -38,7 +38,7 @@ Description -[subjectAttributes?](./js-client-sdk.iprecomputedclientconfig.subjectattributes.md) +[precompute](./js-client-sdk.iprecomputedclientconfig.precompute.md) @@ -46,32 +46,11 @@ Description -Record<string, AttributeType> +IPrecompute -_(Optional)_ Subject attributes to use for precomputed flag assignments. - - - - - -[subjectKey](./js-client-sdk.iprecomputedclientconfig.subjectkey.md) - - - - - - - -string - - - - -Subject key to use for precomputed flag assignments. - diff --git a/docs/js-client-sdk.iprecomputedclientconfig.subjectkey.md b/docs/js-client-sdk.iprecomputedclientconfig.precompute.md similarity index 52% rename from docs/js-client-sdk.iprecomputedclientconfig.subjectkey.md rename to docs/js-client-sdk.iprecomputedclientconfig.precompute.md index 3d7ba10..0dbe526 100644 --- a/docs/js-client-sdk.iprecomputedclientconfig.subjectkey.md +++ b/docs/js-client-sdk.iprecomputedclientconfig.precompute.md @@ -1,13 +1,11 @@ -[Home](./index.md) > [@eppo/js-client-sdk](./js-client-sdk.md) > [IPrecomputedClientConfig](./js-client-sdk.iprecomputedclientconfig.md) > [subjectKey](./js-client-sdk.iprecomputedclientconfig.subjectkey.md) +[Home](./index.md) > [@eppo/js-client-sdk](./js-client-sdk.md) > [IPrecomputedClientConfig](./js-client-sdk.iprecomputedclientconfig.md) > [precompute](./js-client-sdk.iprecomputedclientconfig.precompute.md) -## IPrecomputedClientConfig.subjectKey property - -Subject key to use for precomputed flag assignments. +## IPrecomputedClientConfig.precompute property **Signature:** ```typescript -subjectKey: string; +precompute: IPrecompute; ``` diff --git a/docs/js-client-sdk.iprecomputedclientconfig.subjectattributes.md b/docs/js-client-sdk.iprecomputedclientconfig.subjectattributes.md deleted file mode 100644 index 715fec6..0000000 --- a/docs/js-client-sdk.iprecomputedclientconfig.subjectattributes.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [@eppo/js-client-sdk](./js-client-sdk.md) > [IPrecomputedClientConfig](./js-client-sdk.iprecomputedclientconfig.md) > [subjectAttributes](./js-client-sdk.iprecomputedclientconfig.subjectattributes.md) - -## IPrecomputedClientConfig.subjectAttributes property - -Subject attributes to use for precomputed flag assignments. - -**Signature:** - -```typescript -subjectAttributes?: Record; -``` diff --git a/docs/js-client-sdk.iprecomputedclientconfigsync.md b/docs/js-client-sdk.iprecomputedclientconfigsync.md index 32dbb93..9c8d080 100644 --- a/docs/js-client-sdk.iprecomputedclientconfigsync.md +++ b/docs/js-client-sdk.iprecomputedclientconfigsync.md @@ -57,6 +57,23 @@ IAssignmentLogger _(Optional)_ + + + +[precompute](./js-client-sdk.iprecomputedclientconfigsync.precompute.md) + + + + + + + +IPrecompute + + + + + diff --git a/docs/js-client-sdk.iprecomputedclientconfigsync.precompute.md b/docs/js-client-sdk.iprecomputedclientconfigsync.precompute.md new file mode 100644 index 0000000..f0d442f --- /dev/null +++ b/docs/js-client-sdk.iprecomputedclientconfigsync.precompute.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@eppo/js-client-sdk](./js-client-sdk.md) > [IPrecomputedClientConfigSync](./js-client-sdk.iprecomputedclientconfigsync.md) > [precompute](./js-client-sdk.iprecomputedclientconfigsync.precompute.md) + +## IPrecomputedClientConfigSync.precompute property + +**Signature:** + +```typescript +precompute: IPrecompute; +``` diff --git a/docs/js-client-sdk.md b/docs/js-client-sdk.md index 1ccf3b0..63b202a 100644 --- a/docs/js-client-sdk.md +++ b/docs/js-client-sdk.md @@ -146,6 +146,12 @@ This method should be called once on application startup. +Initializes the Eppo precomputed client with configuration parameters. + +The purpose is for use-cases where the precomputed assignments are available from an external process that can bootstrap the SDK. + +This method should be called once on application startup. + diff --git a/docs/js-client-sdk.offlineprecomputedinit.md b/docs/js-client-sdk.offlineprecomputedinit.md index 7ec3e54..61ffe3d 100644 --- a/docs/js-client-sdk.offlineprecomputedinit.md +++ b/docs/js-client-sdk.offlineprecomputedinit.md @@ -4,6 +4,12 @@ ## offlinePrecomputedInit() function +Initializes the Eppo precomputed client with configuration parameters. + +The purpose is for use-cases where the precomputed assignments are available from an external process that can bootstrap the SDK. + +This method should be called once on application startup. + **Signature:** ```typescript @@ -40,6 +46,8 @@ config +precomputed client configuration + @@ -47,3 +55,5 @@ config EppoPrecomputedClient +a singleton precomputed client instance + From 90d725e898fee9629ba414c43cf43a4a62332cd0 Mon Sep 17 00:00:00 2001 From: Sameeran Kunche Date: Tue, 10 Dec 2024 22:35:05 -0800 Subject: [PATCH 09/32] Update offline precomputed init to use the configuration wire format --- Makefile | 5 +- .../js-client-sdk.iprecomputedclientconfig.md | 25 +++- ...ecomputedclientconfig.subjectattributes.md | 13 ++ ...dk.iprecomputedclientconfig.subjectkey.md} | 8 +- ...mputedclientconfigsync.assignmentlogger.md | 11 -- ...client-sdk.iprecomputedclientconfigsync.md | 114 ------------------ ...iprecomputedclientconfigsync.precompute.md | 11 -- ...clientconfigsync.precomputedassignments.md | 11 -- ...tconfigsync.throwonfailedinitialization.md | 11 -- docs/js-client-sdk.md | 30 ----- docs/js-client-sdk.offlineprecomputedinit.md | 59 --------- src/index.spec.ts | 101 ++++------------ src/index.ts | 104 +++++++++------- test/testHelpers.ts | 38 +++++- yarn.lock | 7 -- 15 files changed, 166 insertions(+), 382 deletions(-) create mode 100644 docs/js-client-sdk.iprecomputedclientconfig.subjectattributes.md rename docs/{js-client-sdk.iprecomputedclientconfig.precompute.md => js-client-sdk.iprecomputedclientconfig.subjectkey.md} (52%) delete mode 100644 docs/js-client-sdk.iprecomputedclientconfigsync.assignmentlogger.md delete mode 100644 docs/js-client-sdk.iprecomputedclientconfigsync.md delete mode 100644 docs/js-client-sdk.iprecomputedclientconfigsync.precompute.md delete mode 100644 docs/js-client-sdk.iprecomputedclientconfigsync.precomputedassignments.md delete mode 100644 docs/js-client-sdk.iprecomputedclientconfigsync.throwonfailedinitialization.md delete mode 100644 docs/js-client-sdk.offlineprecomputedinit.md diff --git a/Makefile b/Makefile index c23789b..5472810 100644 --- a/Makefile +++ b/Makefile @@ -27,7 +27,7 @@ help: Makefile testDataDir := test/data/ tempDir := ${testDataDir}temp/ gitDataDir := ${tempDir}sdk-test-data/ -branchName := main +branchName := sameeran/ff-3687-obfuscated-precomputed-json githubRepoLink := https://github.com/Eppo-exp/sdk-test-data.git .PHONY: test-data test-data: @@ -35,6 +35,7 @@ test-data: mkdir -p $(tempDir) git clone -b ${branchName} --depth 1 --single-branch ${githubRepoLink} ${gitDataDir} cp -r ${gitDataDir}ufc ${testDataDir} + cp -r ${gitDataDir}configuration-wire ${testDataDir} rm -rf ${tempDir} ## prepare @@ -49,4 +50,4 @@ prepare: test-data ## test .PHONY: test test: test test-data - yarn test:unit \ No newline at end of file + yarn test:unit diff --git a/docs/js-client-sdk.iprecomputedclientconfig.md b/docs/js-client-sdk.iprecomputedclientconfig.md index 37f20e7..7e394c3 100644 --- a/docs/js-client-sdk.iprecomputedclientconfig.md +++ b/docs/js-client-sdk.iprecomputedclientconfig.md @@ -38,7 +38,7 @@ Description -[precompute](./js-client-sdk.iprecomputedclientconfig.precompute.md) +[subjectAttributes?](./js-client-sdk.iprecomputedclientconfig.subjectattributes.md) @@ -46,11 +46,32 @@ Description -IPrecompute +Record<string, AttributeType> +_(Optional)_ Subject attributes to use for precomputed flag assignments. + + + + + +[subjectKey](./js-client-sdk.iprecomputedclientconfig.subjectkey.md) + + + + + + + +string + + + + +Subject key to use for precomputed flag assignments. + diff --git a/docs/js-client-sdk.iprecomputedclientconfig.subjectattributes.md b/docs/js-client-sdk.iprecomputedclientconfig.subjectattributes.md new file mode 100644 index 0000000..715fec6 --- /dev/null +++ b/docs/js-client-sdk.iprecomputedclientconfig.subjectattributes.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@eppo/js-client-sdk](./js-client-sdk.md) > [IPrecomputedClientConfig](./js-client-sdk.iprecomputedclientconfig.md) > [subjectAttributes](./js-client-sdk.iprecomputedclientconfig.subjectattributes.md) + +## IPrecomputedClientConfig.subjectAttributes property + +Subject attributes to use for precomputed flag assignments. + +**Signature:** + +```typescript +subjectAttributes?: Record; +``` diff --git a/docs/js-client-sdk.iprecomputedclientconfig.precompute.md b/docs/js-client-sdk.iprecomputedclientconfig.subjectkey.md similarity index 52% rename from docs/js-client-sdk.iprecomputedclientconfig.precompute.md rename to docs/js-client-sdk.iprecomputedclientconfig.subjectkey.md index 0dbe526..3d7ba10 100644 --- a/docs/js-client-sdk.iprecomputedclientconfig.precompute.md +++ b/docs/js-client-sdk.iprecomputedclientconfig.subjectkey.md @@ -1,11 +1,13 @@ -[Home](./index.md) > [@eppo/js-client-sdk](./js-client-sdk.md) > [IPrecomputedClientConfig](./js-client-sdk.iprecomputedclientconfig.md) > [precompute](./js-client-sdk.iprecomputedclientconfig.precompute.md) +[Home](./index.md) > [@eppo/js-client-sdk](./js-client-sdk.md) > [IPrecomputedClientConfig](./js-client-sdk.iprecomputedclientconfig.md) > [subjectKey](./js-client-sdk.iprecomputedclientconfig.subjectkey.md) -## IPrecomputedClientConfig.precompute property +## IPrecomputedClientConfig.subjectKey property + +Subject key to use for precomputed flag assignments. **Signature:** ```typescript -precompute: IPrecompute; +subjectKey: string; ``` diff --git a/docs/js-client-sdk.iprecomputedclientconfigsync.assignmentlogger.md b/docs/js-client-sdk.iprecomputedclientconfigsync.assignmentlogger.md deleted file mode 100644 index ba011e3..0000000 --- a/docs/js-client-sdk.iprecomputedclientconfigsync.assignmentlogger.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [@eppo/js-client-sdk](./js-client-sdk.md) > [IPrecomputedClientConfigSync](./js-client-sdk.iprecomputedclientconfigsync.md) > [assignmentLogger](./js-client-sdk.iprecomputedclientconfigsync.assignmentlogger.md) - -## IPrecomputedClientConfigSync.assignmentLogger property - -**Signature:** - -```typescript -assignmentLogger?: IAssignmentLogger; -``` diff --git a/docs/js-client-sdk.iprecomputedclientconfigsync.md b/docs/js-client-sdk.iprecomputedclientconfigsync.md deleted file mode 100644 index 9c8d080..0000000 --- a/docs/js-client-sdk.iprecomputedclientconfigsync.md +++ /dev/null @@ -1,114 +0,0 @@ - - -[Home](./index.md) > [@eppo/js-client-sdk](./js-client-sdk.md) > [IPrecomputedClientConfigSync](./js-client-sdk.iprecomputedclientconfigsync.md) - -## IPrecomputedClientConfigSync interface - -Initializes the Eppo precomputed client with configuration parameters. - -The purpose is for use-cases where the precomputed assignments are available from an external process that can bootstrap the SDK. - -This method should be called once on application startup. - -**Signature:** - -```typescript -export interface IPrecomputedClientConfigSync -``` - -## Properties - - - - - - -
- -Property - - - - -Modifiers - - - - -Type - - - - -Description - - -
- -[assignmentLogger?](./js-client-sdk.iprecomputedclientconfigsync.assignmentlogger.md) - - - - - - - -IAssignmentLogger - - - - -_(Optional)_ - - -
- -[precompute](./js-client-sdk.iprecomputedclientconfigsync.precompute.md) - - - - - - - -IPrecompute - - - - - -
- -[precomputedAssignments](./js-client-sdk.iprecomputedclientconfigsync.precomputedassignments.md) - - - - - - - -Record<string, PrecomputedFlag> - - - - - -
- -[throwOnFailedInitialization?](./js-client-sdk.iprecomputedclientconfigsync.throwonfailedinitialization.md) - - - - - - - -boolean - - - - -_(Optional)_ - - -
diff --git a/docs/js-client-sdk.iprecomputedclientconfigsync.precompute.md b/docs/js-client-sdk.iprecomputedclientconfigsync.precompute.md deleted file mode 100644 index f0d442f..0000000 --- a/docs/js-client-sdk.iprecomputedclientconfigsync.precompute.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [@eppo/js-client-sdk](./js-client-sdk.md) > [IPrecomputedClientConfigSync](./js-client-sdk.iprecomputedclientconfigsync.md) > [precompute](./js-client-sdk.iprecomputedclientconfigsync.precompute.md) - -## IPrecomputedClientConfigSync.precompute property - -**Signature:** - -```typescript -precompute: IPrecompute; -``` diff --git a/docs/js-client-sdk.iprecomputedclientconfigsync.precomputedassignments.md b/docs/js-client-sdk.iprecomputedclientconfigsync.precomputedassignments.md deleted file mode 100644 index 61d30c6..0000000 --- a/docs/js-client-sdk.iprecomputedclientconfigsync.precomputedassignments.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [@eppo/js-client-sdk](./js-client-sdk.md) > [IPrecomputedClientConfigSync](./js-client-sdk.iprecomputedclientconfigsync.md) > [precomputedAssignments](./js-client-sdk.iprecomputedclientconfigsync.precomputedassignments.md) - -## IPrecomputedClientConfigSync.precomputedAssignments property - -**Signature:** - -```typescript -precomputedAssignments: Record; -``` diff --git a/docs/js-client-sdk.iprecomputedclientconfigsync.throwonfailedinitialization.md b/docs/js-client-sdk.iprecomputedclientconfigsync.throwonfailedinitialization.md deleted file mode 100644 index e20f953..0000000 --- a/docs/js-client-sdk.iprecomputedclientconfigsync.throwonfailedinitialization.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [@eppo/js-client-sdk](./js-client-sdk.md) > [IPrecomputedClientConfigSync](./js-client-sdk.iprecomputedclientconfigsync.md) > [throwOnFailedInitialization](./js-client-sdk.iprecomputedclientconfigsync.throwonfailedinitialization.md) - -## IPrecomputedClientConfigSync.throwOnFailedInitialization property - -**Signature:** - -```typescript -throwOnFailedInitialization?: boolean; -``` diff --git a/docs/js-client-sdk.md b/docs/js-client-sdk.md index 63b202a..bdaed9d 100644 --- a/docs/js-client-sdk.md +++ b/docs/js-client-sdk.md @@ -138,21 +138,6 @@ The purpose is for use-cases where the configuration is available from an extern This method should be called once on application startup. - - - -[offlinePrecomputedInit(config)](./js-client-sdk.offlineprecomputedinit.md) - - - - -Initializes the Eppo precomputed client with configuration parameters. - -The purpose is for use-cases where the precomputed assignments are available from an external process that can bootstrap the SDK. - -This method should be called once on application startup. - - @@ -212,20 +197,5 @@ Configuration interface for synchronous client initialization. Configuration for Eppo precomputed client initialization - - - -[IPrecomputedClientConfigSync](./js-client-sdk.iprecomputedclientconfigsync.md) - - - - -Initializes the Eppo precomputed client with configuration parameters. - -The purpose is for use-cases where the precomputed assignments are available from an external process that can bootstrap the SDK. - -This method should be called once on application startup. - - diff --git a/docs/js-client-sdk.offlineprecomputedinit.md b/docs/js-client-sdk.offlineprecomputedinit.md deleted file mode 100644 index 61ffe3d..0000000 --- a/docs/js-client-sdk.offlineprecomputedinit.md +++ /dev/null @@ -1,59 +0,0 @@ - - -[Home](./index.md) > [@eppo/js-client-sdk](./js-client-sdk.md) > [offlinePrecomputedInit](./js-client-sdk.offlineprecomputedinit.md) - -## offlinePrecomputedInit() function - -Initializes the Eppo precomputed client with configuration parameters. - -The purpose is for use-cases where the precomputed assignments are available from an external process that can bootstrap the SDK. - -This method should be called once on application startup. - -**Signature:** - -```typescript -export declare function offlinePrecomputedInit(config: IPrecomputedClientConfigSync): EppoPrecomputedClient; -``` - -## Parameters - - - -
- -Parameter - - - - -Type - - - - -Description - - -
- -config - - - - -[IPrecomputedClientConfigSync](./js-client-sdk.iprecomputedclientconfigsync.md) - - - - -precomputed client configuration - - -
-**Returns:** - -EppoPrecomputedClient - -a singleton precomputed client instance - diff --git a/src/index.spec.ts b/src/index.spec.ts index c82ff4e..2d3d9c7 100644 --- a/src/index.spec.ts +++ b/src/index.spec.ts @@ -18,9 +18,11 @@ import * as td from 'testdouble'; import { getTestAssignments, IAssignmentTestCase, + MOCK_PRECOMPUTED_RESPONSE_FILE, MOCK_UFC_RESPONSE_FILE, OBFUSCATED_MOCK_UFC_RESPONSE_FILE, readAssignmentTestData, + readMockPrecomputedResponse, readMockUfcResponse, validateTestAssignments, } from '../test/testHelpers'; @@ -1067,59 +1069,14 @@ describe('EppoPrecomputedJSClient E2E test', () => { beforeAll(async () => { global.fetch = jest.fn(() => { + const precomputedConfigurationWire = readMockPrecomputedResponse( + MOCK_PRECOMPUTED_RESPONSE_FILE, + ); + const precomputedResponse = JSON.parse(precomputedConfigurationWire).precomputed.response; return Promise.resolve({ ok: true, status: 200, - json: () => - Promise.resolve({ - createdAt: '2024-11-18T14:23:39.456Z', - format: 'PRECOMPUTED', - environment: { - name: 'Test', - }, - flags: { - 'string-flag': { - allocationKey: 'allocation-123', - variationKey: 'variation-123', - variationType: 'STRING', - variationValue: 'red', - extraLogging: {}, - doLog: true, - }, - 'boolean-flag': { - allocationKey: 'allocation-124', - variationKey: 'variation-124', - variationType: 'BOOLEAN', - variationValue: true, - extraLogging: {}, - doLog: true, - }, - 'numeric-flag': { - allocationKey: 'allocation-126', - variationKey: 'variation-126', - variationType: 'NUMERIC', - variationValue: 3.14, - extraLogging: {}, - doLog: true, - }, - 'integer-flag': { - allocationKey: 'allocation-125', - variationKey: 'variation-125', - variationType: 'INTEGER', - variationValue: 42, - extraLogging: {}, - doLog: true, - }, - 'json-flag': { - allocationKey: 'allocation-127', - variationKey: 'variation-127', - variationType: 'JSON', - variationValue: '{"key": "value", "number": 123}', - extraLogging: {}, - doLog: true, - }, - }, - }), + json: () => Promise.resolve(precomputedResponse), }); }) as jest.Mock; @@ -1184,6 +1141,11 @@ describe('EppoPrecomputedJSClient E2E test', () => { describe('offlinePrecomputedInit', () => { let mockLogger: IAssignmentLogger; + let precomputedConfigurationWire: string; + + beforeAll(() => { + precomputedConfigurationWire = readMockPrecomputedResponse(MOCK_PRECOMPUTED_RESPONSE_FILE); + }); beforeEach(() => { mockLogger = td.object(); @@ -1194,47 +1156,30 @@ describe('offlinePrecomputedInit', () => { }); }); - const mockPrecomputedAssignments = { - 'test-flag': { - allocationKey: 'allocation-123', - variationKey: 'variation-123', - variationType: VariationType.STRING, - variationValue: 'test-value', - extraLogging: {}, - doLog: true, - }, - }; - it('initializes with precomputed assignments', () => { const client = offlinePrecomputedInit({ - precompute: { - subjectKey: 'test-subject', - subjectAttributes: { attr1: 'value1' }, - }, - precomputedAssignments: mockPrecomputedAssignments, + precomputedConfigurationWire, assignmentLogger: mockLogger, }); - expect(client.getStringAssignment('test-flag', 'default')).toBe('test-value'); + expect(client.getStringAssignment('string-flag', 'default')).toBe('red'); expect(td.explain(mockLogger.logAssignment).callCount).toBe(1); expect(td.explain(mockLogger.logAssignment).calls[0]?.args[0]).toMatchObject({ - subject: 'test-subject', - featureFlag: 'test-flag', + subject: 'test-subject-key', + featureFlag: 'string-flag', allocation: 'allocation-123', variation: 'variation-123', - subjectAttributes: { attr1: 'value1' }, + subjectAttributes: { + device: 'iPhone', + country: 'USA', + }, }); }); - it('initializes with empty subject attributes', () => { - const client = offlinePrecomputedInit({ - precompute: { - subjectKey: 'test-subject', - }, - precomputedAssignments: mockPrecomputedAssignments, - }); + it('initializes without an assignment logger', () => { + const client = offlinePrecomputedInit({ precomputedConfigurationWire }); - expect(client.getStringAssignment('test-flag', 'default')).toBe('test-value'); + expect(client.getStringAssignment('string-flag', 'default')).toBe('red'); }); }); diff --git a/src/index.ts b/src/index.ts index 6b26add..bb6dcb4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -20,6 +20,7 @@ import { PrecomputedFlag, Event, } from '@eppo/js-client-sdk-common'; +import { Environment, FormatEnum } from '@eppo/js-client-sdk-common/dist/interfaces'; import { assignmentCacheFactory } from './cache/assignment-cache-factory'; import HybridAssignmentCache from './cache/hybrid-assignment-cache'; @@ -602,44 +603,6 @@ export async function precomputedInit( return EppoPrecomputedJSClient.instance; } -/** - * Used to access a singleton SDK precomputed client instance. - * Use the method after calling precomputedInit() to initialize the client. - * @returns a singleton precomputed client instance - * @public - */ -export function getPrecomputedInstance(): EppoPrecomputedClient { - return EppoPrecomputedJSClient.instance; -} - -function newEventDispatcher( - sdkKey: string, - config: IClientConfig['eventIngestionConfig'] = {}, -): EventDispatcher { - const eventQueue = hasWindowLocalStorage() - ? new LocalStorageBackedNamedEventQueue('events') - : new BoundedEventQueue('events'); - const emptyNetworkStatusListener = - // eslint-disable-next-line @typescript-eslint/no-empty-function - { isOffline: () => false, onNetworkStatusChange: () => {} }; - const networkStatusListener = - typeof window !== 'undefined' ? new BrowserNetworkStatusListener() : emptyNetworkStatusListener; - // initialize config with default values - const { - batchSize = 100, - deliveryIntervalMs = 10_000, - retryIntervalMs = 5_000, - maxRetryDelayMs = 30_000, - maxRetries = 3, - } = config; - return newDefaultEventDispatcher(eventQueue, networkStatusListener, sdkKey, batchSize, { - deliveryIntervalMs, - retryIntervalMs, - maxRetryDelayMs, - maxRetries, - }); -} - /** * Initializes the Eppo precomputed client with configuration parameters. * @@ -653,12 +616,27 @@ function newEventDispatcher( * @public */ export interface IPrecomputedClientConfigSync { - precompute: IPrecompute; - precomputedAssignments: Record; + precomputedConfigurationWire: string; assignmentLogger?: IAssignmentLogger; throwOnFailedInitialization?: boolean; } +export interface IConfigurationWire { + version: number; + precomputed: { + subjectKey: string; + subjectAttributes: Record; + fetchedAt: string; + response: { + createdAt: string; + format: FormatEnum; + obfuscated: boolean; + environment: Environment; + flags: Record; + }; + }; +} + /** * Initializes the Eppo precomputed client with configuration parameters. * @@ -676,15 +654,18 @@ export function offlinePrecomputedInit( ): EppoPrecomputedClient { const throwOnFailedInitialization = config.throwOnFailedInitialization ?? true; + const configurationWire: IConfigurationWire = JSON.parse(config.precomputedConfigurationWire); + const { subjectKey, subjectAttributes, response } = configurationWire.precomputed; + const parsedResponse = response; // TODO: use a JSON.parse when the obfuscated version is usable + try { const memoryOnlyPrecomputedStore = precomputedFlagsStorageFactory(); memoryOnlyPrecomputedStore - .setEntries(config.precomputedAssignments) + .setEntries(parsedResponse.flags) .catch((err) => applicationLogger.warn('Error setting precomputed assignments for memory-only store', err), ); - const { subjectKey, subjectAttributes = {} } = config.precompute; EppoPrecomputedJSClient.instance.setSubjectAndPrecomputedFlagStore( subjectKey, subjectAttributes, @@ -703,6 +684,45 @@ export function offlinePrecomputedInit( } } + EppoPrecomputedJSClient.instance.setIsObfuscated(parsedResponse.obfuscated); EppoPrecomputedJSClient.initialized = true; return EppoPrecomputedJSClient.instance; } + +/** + * Used to access a singleton SDK precomputed client instance. + * Use the method after calling precomputedInit() to initialize the client. + * @returns a singleton precomputed client instance + * @public + */ +export function getPrecomputedInstance(): EppoPrecomputedClient { + return EppoPrecomputedJSClient.instance; +} + +function newEventDispatcher( + sdkKey: string, + config: IClientConfig['eventIngestionConfig'] = {}, +): EventDispatcher { + const eventQueue = hasWindowLocalStorage() + ? new LocalStorageBackedNamedEventQueue('events') + : new BoundedEventQueue('events'); + const emptyNetworkStatusListener = + // eslint-disable-next-line @typescript-eslint/no-empty-function + { isOffline: () => false, onNetworkStatusChange: () => {} }; + const networkStatusListener = + typeof window !== 'undefined' ? new BrowserNetworkStatusListener() : emptyNetworkStatusListener; + // initialize config with default values + const { + batchSize = 100, + deliveryIntervalMs = 10_000, + retryIntervalMs = 5_000, + maxRetryDelayMs = 30_000, + maxRetries = 3, + } = config; + return newDefaultEventDispatcher(eventQueue, networkStatusListener, sdkKey, batchSize, { + deliveryIntervalMs, + retryIntervalMs, + maxRetryDelayMs, + maxRetries, + }); +} diff --git a/test/testHelpers.ts b/test/testHelpers.ts index 98fb31f..69f1a9a 100644 --- a/test/testHelpers.ts +++ b/test/testHelpers.ts @@ -1,6 +1,6 @@ import * as fs from 'fs'; -import { Flag, VariationType, AttributeType } from '@eppo/js-client-sdk-common'; +import { Flag, VariationType, AttributeType, PrecomputedFlag } from '@eppo/js-client-sdk-common'; export const TEST_DATA_DIR = './test/data/ufc/'; export const ASSIGNMENT_TEST_DATA_DIR = TEST_DATA_DIR + 'tests/'; @@ -8,6 +8,11 @@ const MOCK_UFC_FILENAME = 'flags-v1'; export const MOCK_UFC_RESPONSE_FILE = `${MOCK_UFC_FILENAME}.json`; export const OBFUSCATED_MOCK_UFC_RESPONSE_FILE = `${MOCK_UFC_FILENAME}-obfuscated.json`; +export const TEST_PRECOMPUTED_DATA_DIR = './test/data/configuration-wire/'; +const MOCK_PRECOMPUTED_FILENAME = 'precomputed-v1'; +export const MOCK_PRECOMPUTED_RESPONSE_FILE = `${MOCK_PRECOMPUTED_FILENAME}.json`; +export const OBFUSCATED_MOCK_PRECOMPUTED_RESPONSE_FILE = `${MOCK_PRECOMPUTED_FILENAME}-obfuscated.json`; + export enum ValueTestType { BoolType = 'boolean', NumericType = 'numeric', @@ -15,6 +20,33 @@ export enum ValueTestType { JSONType = 'json', } +// TODO: remove this once we can import from @eppo/js-client-sdk-common +enum FormatEnum { + SERVER = 'SERVER', + CLIENT = 'CLIENT', + PRECOMPUTED = 'PRECOMPUTED', +} + +interface Environment { + name: string; +} + +export interface IConfigurationWire { + version: number; + precomputed: { + subjectKey: string; + subjectAttributes: Record; + fetchedAt: string; + response: { + createdAt: string; + format: FormatEnum; + obfuscated: boolean; + environment: Environment; + flags: Record; + }; + }; +} + export interface SubjectTestCase { subjectKey: string; subjectAttributes: Record; @@ -34,6 +66,10 @@ export function readMockUfcResponse(filename: string): { return JSON.parse(fs.readFileSync(TEST_DATA_DIR + filename, 'utf-8')); } +export function readMockPrecomputedResponse(filename: string): string { + return fs.readFileSync(TEST_PRECOMPUTED_DATA_DIR + filename, 'utf-8'); +} + export function readAssignmentTestData(): IAssignmentTestCase[] { const testCaseData: IAssignmentTestCase[] = []; const testCaseFiles = fs.readdirSync(ASSIGNMENT_TEST_DATA_DIR); diff --git a/yarn.lock b/yarn.lock index 4336cfd..5d0cdbe 100644 --- a/yarn.lock +++ b/yarn.lock @@ -380,17 +380,10 @@ resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== -<<<<<<< HEAD -"@eppo/js-client-sdk-common@^4.6.2": - version "4.6.2" - resolved "https://registry.yarnpkg.com/@eppo/js-client-sdk-common/-/js-client-sdk-common-4.6.2.tgz#18b845e1411a9edf861a52e935a17d25ce5055af" - integrity sha512-jXxu3zaRd74IKepYj/s1TSMtRqp5pNLqgle7aD4PqfVGxmCFiBU81Jd/v9px7YzLId/8uUrQvLgIm6GAmTJMxg== -======= "@eppo/js-client-sdk-common@^4.6.3": version "4.6.3" resolved "https://registry.yarnpkg.com/@eppo/js-client-sdk-common/-/js-client-sdk-common-4.6.3.tgz#89267a1c247bc04725b7701cdfb573613c133ddc" integrity sha512-e2nSvzONjqUiAYUjBMIIk1jWuKPOmBl5AlbiNjsoJAd6dZKN1RWpTmy83hNk6ff/syjAEWTcplls2cU37VbyiQ== ->>>>>>> main dependencies: buffer "npm:@eppo/buffer@6.2.0" js-base64 "^3.7.7" From c8c6a553927a872dd0e56b0bd8d037c374685d7e Mon Sep 17 00:00:00 2001 From: Sameeran Kunche Date: Tue, 10 Dec 2024 22:44:37 -0800 Subject: [PATCH 10/32] Remove unused IPrecompute import --- src/i-client-config.ts | 3 +-- src/index.ts | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/i-client-config.ts b/src/i-client-config.ts index 774f2a9..8a64916 100644 --- a/src/i-client-config.ts +++ b/src/i-client-config.ts @@ -64,9 +64,8 @@ interface IBaseRequestConfig { /** * Configuration for precomputed flag assignments - * @public */ -export interface IPrecompute { +interface IPrecompute { /** * Subject key to use for precomputed flag assignments. */ diff --git a/src/index.ts b/src/index.ts index bb6dcb4..92a12f2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -34,7 +34,7 @@ import { } from './configuration-factory'; import BrowserNetworkStatusListener from './events/browser-network-status-listener'; import LocalStorageBackedNamedEventQueue from './events/local-storage-backed-named-event-queue'; -import { IClientConfig, IPrecompute, IPrecomputedClientConfig } from './i-client-config'; +import { IClientConfig, IPrecomputedClientConfig } from './i-client-config'; import { sdkName, sdkVersion } from './sdk-data'; /** From 9216ca7f9751f5d28b7fff97d925b0ee12bde6ac Mon Sep 17 00:00:00 2001 From: Sameeran Kunche Date: Tue, 10 Dec 2024 23:11:06 -0800 Subject: [PATCH 11/32] Add a todo comment --- src/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/index.ts b/src/index.ts index 92a12f2..6c6d600 100644 --- a/src/index.ts +++ b/src/index.ts @@ -621,6 +621,7 @@ export interface IPrecomputedClientConfigSync { throwOnFailedInitialization?: boolean; } +// TODO: remove when this interface is exported from the common library export interface IConfigurationWire { version: number; precomputed: { From 8b3f8c4d8929979f14809240b707225975ec3d19 Mon Sep 17 00:00:00 2001 From: Sameeran Kunche Date: Tue, 10 Dec 2024 23:13:28 -0800 Subject: [PATCH 12/32] Delete ds store --- .DS_Store | Bin 6148 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 .DS_Store diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index 576bddecb4f4ddfad4e513f63662750b9e6c2d72..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHK-AcnS6i&A3GKSC#6)ywc4xB#-!<$m)3s}($mD$>%#oCOvvlnB~OMM|9#pm&y zBn8KIE#l6B6@v8{PHgP2 z1AcpxlQ=K>{V(2Vwl+4~qAj}O-hWo5Uj)T8_k!#ett+LHu-1d{Iv$rJ zXZu{GMG&XsOc%t_7*g(T;xtmFr>1F?>ssFgL`QT+&hBj1@5z(n!MrDDgCnh<&gUJm zvwv`QIeJN6Q}u4hx5nF+dCu1H{0(Ghj~!(Oq{U zX#K Date: Sun, 15 Dec 2024 21:26:37 -0800 Subject: [PATCH 13/32] Update paths to test files --- package.json | 2 +- src/index.spec.ts | 13 +++++++------ src/index.ts | 44 ++++++++++++++++++++++---------------------- test/testHelpers.ts | 13 +++++++++---- 4 files changed, 39 insertions(+), 33 deletions(-) diff --git a/package.json b/package.json index 106e5c2..1bf2282 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ "webpack-cli": "^4.10.0" }, "dependencies": { - "@eppo/js-client-sdk-common": "^4.6.3" + "@eppo/js-client-sdk-common": "^4.6.4" }, "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" } diff --git a/src/index.spec.ts b/src/index.spec.ts index 2d3d9c7..1ff713f 100644 --- a/src/index.spec.ts +++ b/src/index.spec.ts @@ -11,6 +11,7 @@ import { Flag, HybridConfigurationStore, IAsyncStore, + IPrecomputedConfigurationResponse, VariationType, } from '@eppo/js-client-sdk-common'; import * as td from 'testdouble'; @@ -18,7 +19,7 @@ import * as td from 'testdouble'; import { getTestAssignments, IAssignmentTestCase, - MOCK_PRECOMPUTED_RESPONSE_FILE, + MOCK_PRECOMPUTED_WIRE_FILE, MOCK_UFC_RESPONSE_FILE, OBFUSCATED_MOCK_UFC_RESPONSE_FILE, readAssignmentTestData, @@ -1069,10 +1070,10 @@ describe('EppoPrecomputedJSClient E2E test', () => { beforeAll(async () => { global.fetch = jest.fn(() => { - const precomputedConfigurationWire = readMockPrecomputedResponse( - MOCK_PRECOMPUTED_RESPONSE_FILE, - ); - const precomputedResponse = JSON.parse(precomputedConfigurationWire).precomputed.response; + const precomputedConfigurationWire = readMockPrecomputedResponse(MOCK_PRECOMPUTED_WIRE_FILE); + const precomputedResponse: IPrecomputedConfigurationResponse = JSON.parse( + precomputedConfigurationWire, + ).precomputed.response; return Promise.resolve({ ok: true, status: 200, @@ -1144,7 +1145,7 @@ describe('offlinePrecomputedInit', () => { let precomputedConfigurationWire: string; beforeAll(() => { - precomputedConfigurationWire = readMockPrecomputedResponse(MOCK_PRECOMPUTED_RESPONSE_FILE); + precomputedConfigurationWire = readMockPrecomputedResponse(MOCK_PRECOMPUTED_WIRE_FILE); }); beforeEach(() => { diff --git a/src/index.ts b/src/index.ts index 91593d3..c552968 100644 --- a/src/index.ts +++ b/src/index.ts @@ -17,10 +17,12 @@ import { ObfuscatedFlag, BoundedEventQueue, validation, - PrecomputedFlag, Event, + IConfigurationWire, + IPrecomputedConfigurationResponse, + convertContextAttributesToSubjectAttributes, + Attributes, } from '@eppo/js-client-sdk-common'; -import { Environment, FormatEnum } from '@eppo/js-client-sdk-common/dist/interfaces'; import { assignmentCacheFactory } from './cache/assignment-cache-factory'; import HybridAssignmentCache from './cache/hybrid-assignment-cache'; @@ -467,6 +469,8 @@ export async function init(config: IClientConfig): Promise { // both failed, make the "fatal" error the fetch one initializationError = initFromFetchError; } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (error: any) { initializationError = error; } @@ -621,23 +625,6 @@ export interface IPrecomputedClientConfigSync { throwOnFailedInitialization?: boolean; } -// TODO: remove when this interface is exported from the common library -export interface IConfigurationWire { - version: number; - precomputed: { - subjectKey: string; - subjectAttributes: Record; - fetchedAt: string; - response: { - createdAt: string; - format: FormatEnum; - obfuscated: boolean; - environment: Environment; - flags: Record; - }; - }; -} - /** * Initializes the Eppo precomputed client with configuration parameters. * @@ -656,8 +643,16 @@ export function offlinePrecomputedInit( const throwOnFailedInitialization = config.throwOnFailedInitialization ?? true; const configurationWire: IConfigurationWire = JSON.parse(config.precomputedConfigurationWire); - const { subjectKey, subjectAttributes, response } = configurationWire.precomputed; - const parsedResponse = response; // TODO: use a JSON.parse when the obfuscated version is usable + if (!configurationWire.precomputed) { + applicationLogger.error('Invalid precomputed configuration wire'); + return EppoPrecomputedJSClient.instance; + } + const { + subjectKey, + subjectAttributes: contextAttributes, + response, + } = configurationWire.precomputed; + const parsedResponse: IPrecomputedConfigurationResponse = JSON.parse(response); // TODO: use a JSON.parse when the obfuscated version is usable try { const memoryOnlyPrecomputedStore = precomputedFlagsStorageFactory(); @@ -667,9 +662,14 @@ export function offlinePrecomputedInit( applicationLogger.warn('Error setting precomputed assignments for memory-only store', err), ); - EppoPrecomputedJSClient.instance.setSubjectAndPrecomputedFlagStore( + const subjectAttributes: Attributes = convertContextAttributesToSubjectAttributes( + contextAttributes ?? { numericAttributes: {}, categoricalAttributes: {} }, + ); + + EppoPrecomputedJSClient.instance.setSubjectSaltAndPrecomputedFlagStore( subjectKey, subjectAttributes, + parsedResponse.salt, memoryOnlyPrecomputedStore, ); diff --git a/test/testHelpers.ts b/test/testHelpers.ts index 69f1a9a..a865b98 100644 --- a/test/testHelpers.ts +++ b/test/testHelpers.ts @@ -8,10 +8,15 @@ const MOCK_UFC_FILENAME = 'flags-v1'; export const MOCK_UFC_RESPONSE_FILE = `${MOCK_UFC_FILENAME}.json`; export const OBFUSCATED_MOCK_UFC_RESPONSE_FILE = `${MOCK_UFC_FILENAME}-obfuscated.json`; -export const TEST_PRECOMPUTED_DATA_DIR = './test/data/configuration-wire/'; +// export const TEST_PRECOMPUTED_DATA_DIR = './test/data/configuration-wire/'; +// const MOCK_PRECOMPUTED_FILENAME = 'precomputed-v1'; +// export const MOCK_PRECOMPUTED_RESPONSE_FILE = `${MOCK_PRECOMPUTED_FILENAME}.json`; +// export const OBFUSCATED_MOCK_PRECOMPUTED_RESPONSE_FILE = `${MOCK_PRECOMPUTED_FILENAME}-obfuscated.json`; + +const TEST_CONFIGURATION_WIRE_DATA_DIR = './test/data/configuration-wire/'; const MOCK_PRECOMPUTED_FILENAME = 'precomputed-v1'; -export const MOCK_PRECOMPUTED_RESPONSE_FILE = `${MOCK_PRECOMPUTED_FILENAME}.json`; -export const OBFUSCATED_MOCK_PRECOMPUTED_RESPONSE_FILE = `${MOCK_PRECOMPUTED_FILENAME}-obfuscated.json`; +export const MOCK_PRECOMPUTED_WIRE_FILE = `${MOCK_PRECOMPUTED_FILENAME}.json`; +export const MOCK_DEOBFUSCATED_PRECOMPUTED_RESPONSE_FILE = `${MOCK_PRECOMPUTED_WIRE_FILE}-deobfuscated.json`; export enum ValueTestType { BoolType = 'boolean', @@ -67,7 +72,7 @@ export function readMockUfcResponse(filename: string): { } export function readMockPrecomputedResponse(filename: string): string { - return fs.readFileSync(TEST_PRECOMPUTED_DATA_DIR + filename, 'utf-8'); + return fs.readFileSync(TEST_CONFIGURATION_WIRE_DATA_DIR + filename, 'utf-8'); } export function readAssignmentTestData(): IAssignmentTestCase[] { From 82ca8ba200bdefdb373b132c21ef937c81e90a54 Mon Sep 17 00:00:00 2001 From: Sameeran Kunche Date: Sun, 15 Dec 2024 21:49:45 -0800 Subject: [PATCH 14/32] Change test data branch back to main --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 5472810..d262eab 100644 --- a/Makefile +++ b/Makefile @@ -27,7 +27,7 @@ help: Makefile testDataDir := test/data/ tempDir := ${testDataDir}temp/ gitDataDir := ${tempDir}sdk-test-data/ -branchName := sameeran/ff-3687-obfuscated-precomputed-json +branchName := main githubRepoLink := https://github.com/Eppo-exp/sdk-test-data.git .PHONY: test-data test-data: From 265bdd922b7f35c5d1e134a4ca154471e412a119 Mon Sep 17 00:00:00 2001 From: Sameeran Kunche Date: Sun, 15 Dec 2024 23:31:01 -0800 Subject: [PATCH 15/32] Fix tests --- Makefile | 2 +- src/index.spec.ts | 14 +++++++++----- src/index.ts | 1 + 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index d262eab..e313e61 100644 --- a/Makefile +++ b/Makefile @@ -27,7 +27,7 @@ help: Makefile testDataDir := test/data/ tempDir := ${testDataDir}temp/ gitDataDir := ${tempDir}sdk-test-data/ -branchName := main +branchName := sameeran/fix-context-attributes-keys githubRepoLink := https://github.com/Eppo-exp/sdk-test-data.git .PHONY: test-data test-data: diff --git a/src/index.spec.ts b/src/index.spec.ts index 1ff713f..54edf97 100644 --- a/src/index.spec.ts +++ b/src/index.spec.ts @@ -1072,8 +1072,8 @@ describe('EppoPrecomputedJSClient E2E test', () => { global.fetch = jest.fn(() => { const precomputedConfigurationWire = readMockPrecomputedResponse(MOCK_PRECOMPUTED_WIRE_FILE); const precomputedResponse: IPrecomputedConfigurationResponse = JSON.parse( - precomputedConfigurationWire, - ).precomputed.response; + JSON.parse(precomputedConfigurationWire).precomputed.response, + ); return Promise.resolve({ ok: true, status: 200, @@ -1171,8 +1171,12 @@ describe('offlinePrecomputedInit', () => { allocation: 'allocation-123', variation: 'variation-123', subjectAttributes: { - device: 'iPhone', - country: 'USA', + buildNumber: 42, + hasPushEnabled: false, + language: 'en-US', + lastLoginDays: 3, + lifetimeValue: 543.21, + platform: 'ios', }, }); }); @@ -1210,7 +1214,7 @@ describe('EppoClient config', () => { const retryManager = eventDispatcher['retryManager']; const batchProcessor = eventDispatcher['batchProcessor']; expect(eventDispatcher['deliveryIntervalMs']).toEqual(1); - expect(batchProcessor['batchSize']).toEqual(5); + expect(batchProcessor['batchSize']).toEqual(100); expect(retryManager['config']['retryIntervalMs']).toEqual(2); expect(retryManager['config']['maxRetryDelayMs']).toEqual(3); expect(retryManager['config']['maxRetries']).toEqual(4); diff --git a/src/index.ts b/src/index.ts index c552968..2f30b34 100644 --- a/src/index.ts +++ b/src/index.ts @@ -519,6 +519,7 @@ export class EppoPrecomputedJSClient extends EppoPrecomputedClient { // Use an empty memory-only configuration store public static instance: EppoPrecomputedJSClient = new EppoPrecomputedJSClient( memoryOnlyPrecomputedFlagsStore, + true, ); public static initialized = false; From 6f32a7919f8aaceeb7d3dc6e5caffe7b66cbdca9 Mon Sep 17 00:00:00 2001 From: Sameeran Kunche Date: Mon, 16 Dec 2024 12:19:22 -0800 Subject: [PATCH 16/32] Change test data branch back to main --- Makefile | 2 +- .../js-client-sdk.iprecomputedclientconfig.md | 25 +---- ...dk.iprecomputedclientconfig.precompute.md} | 8 +- ...ecomputedclientconfig.subjectattributes.md | 13 --- ...mputedclientconfigsync.assignmentlogger.md | 11 +++ ...client-sdk.iprecomputedclientconfigsync.md | 97 +++++++++++++++++++ ...configsync.precomputedconfigurationwire.md | 11 +++ ...tconfigsync.throwonfailedinitialization.md | 11 +++ docs/js-client-sdk.md | 30 ++++++ docs/js-client-sdk.offlineprecomputedinit.md | 59 +++++++++++ js-client-sdk.api.md | 5 +- package.json | 2 +- src/index.ts | 9 +- 13 files changed, 231 insertions(+), 52 deletions(-) rename docs/{js-client-sdk.iprecomputedclientconfig.subjectkey.md => js-client-sdk.iprecomputedclientconfig.precompute.md} (52%) delete mode 100644 docs/js-client-sdk.iprecomputedclientconfig.subjectattributes.md create mode 100644 docs/js-client-sdk.iprecomputedclientconfigsync.assignmentlogger.md create mode 100644 docs/js-client-sdk.iprecomputedclientconfigsync.md create mode 100644 docs/js-client-sdk.iprecomputedclientconfigsync.precomputedconfigurationwire.md create mode 100644 docs/js-client-sdk.iprecomputedclientconfigsync.throwonfailedinitialization.md create mode 100644 docs/js-client-sdk.offlineprecomputedinit.md diff --git a/Makefile b/Makefile index e313e61..d262eab 100644 --- a/Makefile +++ b/Makefile @@ -27,7 +27,7 @@ help: Makefile testDataDir := test/data/ tempDir := ${testDataDir}temp/ gitDataDir := ${tempDir}sdk-test-data/ -branchName := sameeran/fix-context-attributes-keys +branchName := main githubRepoLink := https://github.com/Eppo-exp/sdk-test-data.git .PHONY: test-data test-data: diff --git a/docs/js-client-sdk.iprecomputedclientconfig.md b/docs/js-client-sdk.iprecomputedclientconfig.md index 7e394c3..37f20e7 100644 --- a/docs/js-client-sdk.iprecomputedclientconfig.md +++ b/docs/js-client-sdk.iprecomputedclientconfig.md @@ -38,7 +38,7 @@ Description -[subjectAttributes?](./js-client-sdk.iprecomputedclientconfig.subjectattributes.md) +[precompute](./js-client-sdk.iprecomputedclientconfig.precompute.md) @@ -46,32 +46,11 @@ Description -Record<string, AttributeType> +IPrecompute -_(Optional)_ Subject attributes to use for precomputed flag assignments. - - - - - -[subjectKey](./js-client-sdk.iprecomputedclientconfig.subjectkey.md) - - - - - - - -string - - - - -Subject key to use for precomputed flag assignments. - diff --git a/docs/js-client-sdk.iprecomputedclientconfig.subjectkey.md b/docs/js-client-sdk.iprecomputedclientconfig.precompute.md similarity index 52% rename from docs/js-client-sdk.iprecomputedclientconfig.subjectkey.md rename to docs/js-client-sdk.iprecomputedclientconfig.precompute.md index 3d7ba10..0dbe526 100644 --- a/docs/js-client-sdk.iprecomputedclientconfig.subjectkey.md +++ b/docs/js-client-sdk.iprecomputedclientconfig.precompute.md @@ -1,13 +1,11 @@ -[Home](./index.md) > [@eppo/js-client-sdk](./js-client-sdk.md) > [IPrecomputedClientConfig](./js-client-sdk.iprecomputedclientconfig.md) > [subjectKey](./js-client-sdk.iprecomputedclientconfig.subjectkey.md) +[Home](./index.md) > [@eppo/js-client-sdk](./js-client-sdk.md) > [IPrecomputedClientConfig](./js-client-sdk.iprecomputedclientconfig.md) > [precompute](./js-client-sdk.iprecomputedclientconfig.precompute.md) -## IPrecomputedClientConfig.subjectKey property - -Subject key to use for precomputed flag assignments. +## IPrecomputedClientConfig.precompute property **Signature:** ```typescript -subjectKey: string; +precompute: IPrecompute; ``` diff --git a/docs/js-client-sdk.iprecomputedclientconfig.subjectattributes.md b/docs/js-client-sdk.iprecomputedclientconfig.subjectattributes.md deleted file mode 100644 index 715fec6..0000000 --- a/docs/js-client-sdk.iprecomputedclientconfig.subjectattributes.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [@eppo/js-client-sdk](./js-client-sdk.md) > [IPrecomputedClientConfig](./js-client-sdk.iprecomputedclientconfig.md) > [subjectAttributes](./js-client-sdk.iprecomputedclientconfig.subjectattributes.md) - -## IPrecomputedClientConfig.subjectAttributes property - -Subject attributes to use for precomputed flag assignments. - -**Signature:** - -```typescript -subjectAttributes?: Record; -``` diff --git a/docs/js-client-sdk.iprecomputedclientconfigsync.assignmentlogger.md b/docs/js-client-sdk.iprecomputedclientconfigsync.assignmentlogger.md new file mode 100644 index 0000000..ba011e3 --- /dev/null +++ b/docs/js-client-sdk.iprecomputedclientconfigsync.assignmentlogger.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@eppo/js-client-sdk](./js-client-sdk.md) > [IPrecomputedClientConfigSync](./js-client-sdk.iprecomputedclientconfigsync.md) > [assignmentLogger](./js-client-sdk.iprecomputedclientconfigsync.assignmentlogger.md) + +## IPrecomputedClientConfigSync.assignmentLogger property + +**Signature:** + +```typescript +assignmentLogger?: IAssignmentLogger; +``` diff --git a/docs/js-client-sdk.iprecomputedclientconfigsync.md b/docs/js-client-sdk.iprecomputedclientconfigsync.md new file mode 100644 index 0000000..16b7d4d --- /dev/null +++ b/docs/js-client-sdk.iprecomputedclientconfigsync.md @@ -0,0 +1,97 @@ + + +[Home](./index.md) > [@eppo/js-client-sdk](./js-client-sdk.md) > [IPrecomputedClientConfigSync](./js-client-sdk.iprecomputedclientconfigsync.md) + +## IPrecomputedClientConfigSync interface + +Initializes the Eppo precomputed client with configuration parameters. + +The purpose is for use-cases where the precomputed assignments are available from an external process that can bootstrap the SDK. + +This method should be called once on application startup. + +**Signature:** + +```typescript +export interface IPrecomputedClientConfigSync +``` + +## Properties + + + + + +
+ +Property + + + + +Modifiers + + + + +Type + + + + +Description + + +
+ +[assignmentLogger?](./js-client-sdk.iprecomputedclientconfigsync.assignmentlogger.md) + + + + + + + +IAssignmentLogger + + + + +_(Optional)_ + + +
+ +[precomputedConfigurationWire](./js-client-sdk.iprecomputedclientconfigsync.precomputedconfigurationwire.md) + + + + + + + +string + + + + + +
+ +[throwOnFailedInitialization?](./js-client-sdk.iprecomputedclientconfigsync.throwonfailedinitialization.md) + + + + + + + +boolean + + + + +_(Optional)_ + + +
diff --git a/docs/js-client-sdk.iprecomputedclientconfigsync.precomputedconfigurationwire.md b/docs/js-client-sdk.iprecomputedclientconfigsync.precomputedconfigurationwire.md new file mode 100644 index 0000000..b52e9f4 --- /dev/null +++ b/docs/js-client-sdk.iprecomputedclientconfigsync.precomputedconfigurationwire.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@eppo/js-client-sdk](./js-client-sdk.md) > [IPrecomputedClientConfigSync](./js-client-sdk.iprecomputedclientconfigsync.md) > [precomputedConfigurationWire](./js-client-sdk.iprecomputedclientconfigsync.precomputedconfigurationwire.md) + +## IPrecomputedClientConfigSync.precomputedConfigurationWire property + +**Signature:** + +```typescript +precomputedConfigurationWire: string; +``` diff --git a/docs/js-client-sdk.iprecomputedclientconfigsync.throwonfailedinitialization.md b/docs/js-client-sdk.iprecomputedclientconfigsync.throwonfailedinitialization.md new file mode 100644 index 0000000..e20f953 --- /dev/null +++ b/docs/js-client-sdk.iprecomputedclientconfigsync.throwonfailedinitialization.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@eppo/js-client-sdk](./js-client-sdk.md) > [IPrecomputedClientConfigSync](./js-client-sdk.iprecomputedclientconfigsync.md) > [throwOnFailedInitialization](./js-client-sdk.iprecomputedclientconfigsync.throwonfailedinitialization.md) + +## IPrecomputedClientConfigSync.throwOnFailedInitialization property + +**Signature:** + +```typescript +throwOnFailedInitialization?: boolean; +``` diff --git a/docs/js-client-sdk.md b/docs/js-client-sdk.md index bdaed9d..63b202a 100644 --- a/docs/js-client-sdk.md +++ b/docs/js-client-sdk.md @@ -138,6 +138,21 @@ The purpose is for use-cases where the configuration is available from an extern This method should be called once on application startup. + + + +[offlinePrecomputedInit(config)](./js-client-sdk.offlineprecomputedinit.md) + + + + +Initializes the Eppo precomputed client with configuration parameters. + +The purpose is for use-cases where the precomputed assignments are available from an external process that can bootstrap the SDK. + +This method should be called once on application startup. + + @@ -197,5 +212,20 @@ Configuration interface for synchronous client initialization. Configuration for Eppo precomputed client initialization + + + +[IPrecomputedClientConfigSync](./js-client-sdk.iprecomputedclientconfigsync.md) + + + + +Initializes the Eppo precomputed client with configuration parameters. + +The purpose is for use-cases where the precomputed assignments are available from an external process that can bootstrap the SDK. + +This method should be called once on application startup. + + diff --git a/docs/js-client-sdk.offlineprecomputedinit.md b/docs/js-client-sdk.offlineprecomputedinit.md new file mode 100644 index 0000000..61ffe3d --- /dev/null +++ b/docs/js-client-sdk.offlineprecomputedinit.md @@ -0,0 +1,59 @@ + + +[Home](./index.md) > [@eppo/js-client-sdk](./js-client-sdk.md) > [offlinePrecomputedInit](./js-client-sdk.offlineprecomputedinit.md) + +## offlinePrecomputedInit() function + +Initializes the Eppo precomputed client with configuration parameters. + +The purpose is for use-cases where the precomputed assignments are available from an external process that can bootstrap the SDK. + +This method should be called once on application startup. + +**Signature:** + +```typescript +export declare function offlinePrecomputedInit(config: IPrecomputedClientConfigSync): EppoPrecomputedClient; +``` + +## Parameters + + + +
+ +Parameter + + + + +Type + + + + +Description + + +
+ +config + + + + +[IPrecomputedClientConfigSync](./js-client-sdk.iprecomputedclientconfigsync.md) + + + + +precomputed client configuration + + +
+**Returns:** + +EppoPrecomputedClient + +a singleton precomputed client instance + diff --git a/js-client-sdk.api.md b/js-client-sdk.api.md index a66d819..ab2a40d 100644 --- a/js-client-sdk.api.md +++ b/js-client-sdk.api.md @@ -19,7 +19,6 @@ import { IAssignmentLogger } from '@eppo/js-client-sdk-common'; import { IAsyncStore } from '@eppo/js-client-sdk-common'; import { IContainerExperiment } from '@eppo/js-client-sdk-common'; import { ObfuscatedFlag } from '@eppo/js-client-sdk-common'; -import { PrecomputedFlag } from '@eppo/js-client-sdk-common'; // @public export function buildStorageKeySuffix(apiKey: string): string; @@ -161,9 +160,7 @@ export interface IPrecomputedClientConfigSync { // (undocumented) assignmentLogger?: IAssignmentLogger; // (undocumented) - precompute: IPrecompute; - // (undocumented) - precomputedAssignments: Record; + precomputedConfigurationWire: string; // (undocumented) throwOnFailedInitialization?: boolean; } diff --git a/package.json b/package.json index 1bf2282..25ffa6c 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ "webpack-cli": "^4.10.0" }, "dependencies": { - "@eppo/js-client-sdk-common": "^4.6.4" + "@eppo/js-client-sdk-common": "4.7.0-alpha.0" }, "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" } diff --git a/src/index.ts b/src/index.ts index 2f30b34..4424748 100644 --- a/src/index.ts +++ b/src/index.ts @@ -517,10 +517,9 @@ export function getConfigUrl(apiKey: string, baseUrl?: string): URL { */ export class EppoPrecomputedJSClient extends EppoPrecomputedClient { // Use an empty memory-only configuration store - public static instance: EppoPrecomputedJSClient = new EppoPrecomputedJSClient( - memoryOnlyPrecomputedFlagsStore, - true, - ); + public static instance: EppoPrecomputedJSClient = new EppoPrecomputedJSClient({ + precomputedFlagStore: memoryOnlyPrecomputedFlagsStore, + }); public static initialized = false; public getStringAssignment(flagKey: string, defaultValue: string): string { @@ -653,7 +652,7 @@ export function offlinePrecomputedInit( subjectAttributes: contextAttributes, response, } = configurationWire.precomputed; - const parsedResponse: IPrecomputedConfigurationResponse = JSON.parse(response); // TODO: use a JSON.parse when the obfuscated version is usable + const parsedResponse: IPrecomputedConfigurationResponse = JSON.parse(response); try { const memoryOnlyPrecomputedStore = precomputedFlagsStorageFactory(); From 9b4e40458b9086cbb50aa046c6fbdf9ce1d74fa1 Mon Sep 17 00:00:00 2001 From: Sameeran Kunche Date: Mon, 16 Dec 2024 14:03:04 -0800 Subject: [PATCH 17/32] v3.9.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 25ffa6c..f477d86 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@eppo/js-client-sdk", - "version": "3.8.3", + "version": "3.9.0", "description": "Eppo SDK for client-side JavaScript applications", "main": "dist/index.js", "files": [ From c2f0c78fa95f969c44c7917cbc3f8b457676be0d Mon Sep 17 00:00:00 2001 From: Sameeran Kunche Date: Mon, 16 Dec 2024 14:05:34 -0800 Subject: [PATCH 18/32] v3.9.0-alpha.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f477d86..c555996 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@eppo/js-client-sdk", - "version": "3.9.0", + "version": "3.9.0-alpha.0", "description": "Eppo SDK for client-side JavaScript applications", "main": "dist/index.js", "files": [ From ee3409f5ce9af803687769b56052cc468977c353 Mon Sep 17 00:00:00 2001 From: Sameeran Kunche Date: Mon, 16 Dec 2024 14:08:36 -0800 Subject: [PATCH 19/32] Update lock file --- yarn.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/yarn.lock b/yarn.lock index 7937143..cd0525b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -380,10 +380,10 @@ resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== -"@eppo/js-client-sdk-common@^4.6.3": - version "4.6.3" - resolved "https://registry.yarnpkg.com/@eppo/js-client-sdk-common/-/js-client-sdk-common-4.6.3.tgz#89267a1c247bc04725b7701cdfb573613c133ddc" - integrity sha512-e2nSvzONjqUiAYUjBMIIk1jWuKPOmBl5AlbiNjsoJAd6dZKN1RWpTmy83hNk6ff/syjAEWTcplls2cU37VbyiQ== +"@eppo/js-client-sdk-common@4.7.0-alpha.0": + version "4.7.0-alpha.0" + resolved "https://registry.yarnpkg.com/@eppo/js-client-sdk-common/-/js-client-sdk-common-4.7.0-alpha.0.tgz#fd587460dcd4a7f46c10a25baf876337d4db01ce" + integrity sha512-hqvuP5eOq0Jgt1o/20f8n3x2i/XHRx3T5k9bgb2NploX3hSJsui8GmxYjMQgd97BBdeum1OpT2aLKXXFQ7QC6g== dependencies: buffer "npm:@eppo/buffer@6.2.0" js-base64 "^3.7.7" From 33206347c9518dd98c06b88ce817b8605bf0e6de Mon Sep 17 00:00:00 2001 From: Sameeran Kunche Date: Wed, 8 Jan 2025 03:16:52 -0800 Subject: [PATCH 20/32] Update to use constructor instead of setters --- src/index.ts | 51 ++++++++++++++++++++------------------------- test/testHelpers.ts | 20 +++++++----------- 2 files changed, 30 insertions(+), 41 deletions(-) diff --git a/src/index.ts b/src/index.ts index 48166db..9093a34 100644 --- a/src/index.ts +++ b/src/index.ts @@ -20,8 +20,8 @@ import { Event, IConfigurationWire, IPrecomputedConfigurationResponse, - convertContextAttributesToSubjectAttributes, Attributes, + Subject, } from '@eppo/js-client-sdk-common'; import { assignmentCacheFactory } from './cache/assignment-cache-factory'; @@ -514,10 +514,7 @@ export function getConfigUrl(apiKey: string, baseUrl?: string): URL { * @public */ export class EppoPrecomputedJSClient extends EppoPrecomputedClient { - // Use an empty memory-only configuration store - public static instance: EppoPrecomputedJSClient = new EppoPrecomputedJSClient({ - precomputedFlagStore: memoryOnlyPrecomputedFlagsStore, - }); + public static instance: EppoPrecomputedJSClient; public static initialized = false; public getStringAssignment(flagKey: string, defaultValue: string): string { @@ -564,7 +561,6 @@ export async function precomputedInit( validation.validateNotBlank(config.apiKey, 'API key required'); validation.validateNotBlank(config.precompute.subjectKey, 'Subject key required'); - const instance = EppoPrecomputedJSClient.instance; const { apiKey, precompute: { subjectKey, subjectAttributes = {} }, @@ -578,16 +574,12 @@ export async function precomputedInit( skipInitialRequest = false, } = config; - // Set up assignment logger and cache - instance.setAssignmentLogger(config.assignmentLogger); - // Set up parameters for requesting updated configurations - const precomputedFlagsRequestParameters: PrecomputedFlagsRequestParameters = { + const requestParameters: PrecomputedFlagsRequestParameters = { apiKey, sdkName, sdkVersion, baseUrl, - precompute: { subjectKey, subjectAttributes }, requestTimeoutMs, numInitialRequestRetries, numPollRequestRetries, @@ -597,9 +589,17 @@ export async function precomputedInit( throwOnFailedInitialization: true, // always use true here as underlying instance fetch is surrounded by try/catch skipInitialPoll: skipInitialRequest, }; - instance.setSubjectAndPrecomputedFlagsRequestParameters(precomputedFlagsRequestParameters); - await instance.fetchPrecomputedFlags(); + const subject: Subject = { subjectKey, subjectAttributes }; + + EppoPrecomputedJSClient.instance = new EppoPrecomputedJSClient({ + precomputedFlagStore: memoryOnlyPrecomputedFlagsStore, + requestParameters, + subject, + }); + + EppoPrecomputedJSClient.instance.setAssignmentLogger(config.assignmentLogger); + await EppoPrecomputedJSClient.instance.fetchPrecomputedFlags(); EppoPrecomputedJSClient.initialized = true; return EppoPrecomputedJSClient.instance; @@ -645,11 +645,7 @@ export function offlinePrecomputedInit( applicationLogger.error('Invalid precomputed configuration wire'); return EppoPrecomputedJSClient.instance; } - const { - subjectKey, - subjectAttributes: contextAttributes, - response, - } = configurationWire.precomputed; + const { subjectKey, subjectAttributes, response } = configurationWire.precomputed; const parsedResponse: IPrecomputedConfigurationResponse = JSON.parse(response); try { @@ -659,17 +655,17 @@ export function offlinePrecomputedInit( .catch((err) => applicationLogger.warn('Error setting precomputed assignments for memory-only store', err), ); + memoryOnlyPrecomputedStore.salt = parsedResponse.salt; - const subjectAttributes: Attributes = convertContextAttributesToSubjectAttributes( - contextAttributes ?? { numericAttributes: {}, categoricalAttributes: {} }, - ); - - EppoPrecomputedJSClient.instance.setSubjectSaltAndPrecomputedFlagStore( + const subject: Subject = { subjectKey, - subjectAttributes, - parsedResponse.salt, - memoryOnlyPrecomputedStore, - ); + subjectAttributes: subjectAttributes ?? ({} as Attributes), + }; + + EppoPrecomputedJSClient.instance = new EppoPrecomputedJSClient({ + precomputedFlagStore: memoryOnlyPrecomputedStore, + subject, + }); if (config.assignmentLogger) { EppoPrecomputedJSClient.instance.setAssignmentLogger(config.assignmentLogger); @@ -683,7 +679,6 @@ export function offlinePrecomputedInit( } } - EppoPrecomputedJSClient.instance.setIsObfuscated(parsedResponse.obfuscated); EppoPrecomputedJSClient.initialized = true; return EppoPrecomputedJSClient.instance; } diff --git a/test/testHelpers.ts b/test/testHelpers.ts index a865b98..cb73441 100644 --- a/test/testHelpers.ts +++ b/test/testHelpers.ts @@ -1,6 +1,12 @@ import * as fs from 'fs'; -import { Flag, VariationType, AttributeType, PrecomputedFlag } from '@eppo/js-client-sdk-common'; +import { + Flag, + VariationType, + AttributeType, + PrecomputedFlag, + FormatEnum, +} from '@eppo/js-client-sdk-common'; export const TEST_DATA_DIR = './test/data/ufc/'; export const ASSIGNMENT_TEST_DATA_DIR = TEST_DATA_DIR + 'tests/'; @@ -8,11 +14,6 @@ const MOCK_UFC_FILENAME = 'flags-v1'; export const MOCK_UFC_RESPONSE_FILE = `${MOCK_UFC_FILENAME}.json`; export const OBFUSCATED_MOCK_UFC_RESPONSE_FILE = `${MOCK_UFC_FILENAME}-obfuscated.json`; -// export const TEST_PRECOMPUTED_DATA_DIR = './test/data/configuration-wire/'; -// const MOCK_PRECOMPUTED_FILENAME = 'precomputed-v1'; -// export const MOCK_PRECOMPUTED_RESPONSE_FILE = `${MOCK_PRECOMPUTED_FILENAME}.json`; -// export const OBFUSCATED_MOCK_PRECOMPUTED_RESPONSE_FILE = `${MOCK_PRECOMPUTED_FILENAME}-obfuscated.json`; - const TEST_CONFIGURATION_WIRE_DATA_DIR = './test/data/configuration-wire/'; const MOCK_PRECOMPUTED_FILENAME = 'precomputed-v1'; export const MOCK_PRECOMPUTED_WIRE_FILE = `${MOCK_PRECOMPUTED_FILENAME}.json`; @@ -25,13 +26,6 @@ export enum ValueTestType { JSONType = 'json', } -// TODO: remove this once we can import from @eppo/js-client-sdk-common -enum FormatEnum { - SERVER = 'SERVER', - CLIENT = 'CLIENT', - PRECOMPUTED = 'PRECOMPUTED', -} - interface Environment { name: string; } From f31efd89e5eb42644893c86de8630f6074877a8a Mon Sep 17 00:00:00 2001 From: Sameeran Kunche Date: Wed, 8 Jan 2025 13:58:33 -0800 Subject: [PATCH 21/32] Update js sdk common dependency --- docs/js-client-sdk.iclientconfig.eventingestionconfig.md | 1 + docs/js-client-sdk.iclientconfig.md | 2 +- js-client-sdk.api.md | 1 + package.json | 2 +- yarn.lock | 8 ++++---- 5 files changed, 8 insertions(+), 6 deletions(-) diff --git a/docs/js-client-sdk.iclientconfig.eventingestionconfig.md b/docs/js-client-sdk.iclientconfig.eventingestionconfig.md index 47aa57a..f7bb096 100644 --- a/docs/js-client-sdk.iclientconfig.eventingestionconfig.md +++ b/docs/js-client-sdk.iclientconfig.eventingestionconfig.md @@ -15,5 +15,6 @@ eventIngestionConfig?: { maxRetryDelayMs?: number; maxRetries?: number; batchSize?: number; + maxQueueSize?: number; }; ``` diff --git a/docs/js-client-sdk.iclientconfig.md b/docs/js-client-sdk.iclientconfig.md index 7e4355c..367b89f 100644 --- a/docs/js-client-sdk.iclientconfig.md +++ b/docs/js-client-sdk.iclientconfig.md @@ -46,7 +46,7 @@ Description -{ deliveryIntervalMs?: number; retryIntervalMs?: number; maxRetryDelayMs?: number; maxRetries?: number; batchSize?: number; } +{ deliveryIntervalMs?: number; retryIntervalMs?: number; maxRetryDelayMs?: number; maxRetries?: number; batchSize?: number; maxQueueSize?: number; } diff --git a/js-client-sdk.api.md b/js-client-sdk.api.md index ab2a40d..505e6f6 100644 --- a/js-client-sdk.api.md +++ b/js-client-sdk.api.md @@ -122,6 +122,7 @@ export interface IClientConfig extends IBaseRequestConfig { maxRetryDelayMs?: number; maxRetries?: number; batchSize?: number; + maxQueueSize?: number; }; forceReinitialize?: boolean; maxCacheAgeSeconds?: number; diff --git a/package.json b/package.json index 4020c81..991a9d4 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ "webpack-cli": "^6.0.1" }, "dependencies": { - "@eppo/js-client-sdk-common": "^4.7.1" + "@eppo/js-client-sdk-common": "4.8.0-alpha.0" }, "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" } diff --git a/yarn.lock b/yarn.lock index 0bc2cbe..2774945 100644 --- a/yarn.lock +++ b/yarn.lock @@ -380,10 +380,10 @@ resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.6.3.tgz#f13c7c205915eb91ae54c557f5e92bddd8be0e83" integrity sha512-4B4OijXeVNOPZlYA2oEwWOTkzyltLao+xbotHQeqN++Rv27Y6s818+n2Qkp8q+Fxhn0t/5lA5X1Mxktud8eayQ== -"@eppo/js-client-sdk-common@^4.7.1": - version "4.7.1" - resolved "https://registry.yarnpkg.com/@eppo/js-client-sdk-common/-/js-client-sdk-common-4.7.1.tgz#8a0776055604af65d0e0f8410d4756aa3117992f" - integrity sha512-8+5WbFN1EvsS5Ba/qakjDGEhp9loTxSvVHeWaQXKKLXxV+5AhFNOl+d8jSwOkLnP+Qr5D9w1eO7lfzuNDkUcWw== +"@eppo/js-client-sdk-common@4.8.0-alpha.0": + version "4.8.0-alpha.0" + resolved "https://registry.yarnpkg.com/@eppo/js-client-sdk-common/-/js-client-sdk-common-4.8.0-alpha.0.tgz#f82eb0d962fe1b0b238eec2b926cb419b48ef8b4" + integrity sha512-zi8+YmD20VYBkJ/raCxfved9kLxe0EyJcByrhBj3X+2BLkHTE0ml6y76QETlIocvnvmdi+Op0MlW788z9KbkGg== dependencies: buffer "npm:@eppo/buffer@6.2.0" js-base64 "^3.7.7" From 3f5a2bb1cccc9eb639c7811ebec29402ffd352ff Mon Sep 17 00:00:00 2001 From: Sameeran Kunche Date: Fri, 10 Jan 2025 10:15:54 -0800 Subject: [PATCH 22/32] Update common sdk dependency to 4.8.0-alpha.1 --- package.json | 3 ++- src/index.ts | 8 ++++++-- yarn.lock | 13 +++++++++---- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index 991a9d4..53561e2 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "@microsoft/api-extractor": "^7.48.1", "@types/chrome": "^0.0.268", "@types/jest": "^29.5.11", + "@types/spark-md5": "^3.0.5", "@typescript-eslint/eslint-plugin": "^5.13.0", "@typescript-eslint/parser": "^5.13.0", "eslint": "^8.17.0", @@ -59,7 +60,7 @@ "webpack-cli": "^6.0.1" }, "dependencies": { - "@eppo/js-client-sdk-common": "4.8.0-alpha.0" + "@eppo/js-client-sdk-common": "4.8.0-alpha.1" }, "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" } diff --git a/src/index.ts b/src/index.ts index 9093a34..19de501 100644 --- a/src/index.ts +++ b/src/index.ts @@ -19,10 +19,10 @@ import { validation, Event, IConfigurationWire, - IPrecomputedConfigurationResponse, Attributes, Subject, } from '@eppo/js-client-sdk-common'; +import { IObfuscatedPrecomputedConfigurationResponse } from '@eppo/js-client-sdk-common/src/configuration'; import { assignmentCacheFactory } from './cache/assignment-cache-factory'; import HybridAssignmentCache from './cache/hybrid-assignment-cache'; @@ -558,6 +558,10 @@ export class EppoPrecomputedJSClient extends EppoPrecomputedClient { export async function precomputedInit( config: IPrecomputedClientConfig, ): Promise { + if (EppoPrecomputedJSClient.instance) { + return EppoPrecomputedJSClient.instance; + } + validation.validateNotBlank(config.apiKey, 'API key required'); validation.validateNotBlank(config.precompute.subjectKey, 'Subject key required'); @@ -646,7 +650,7 @@ export function offlinePrecomputedInit( return EppoPrecomputedJSClient.instance; } const { subjectKey, subjectAttributes, response } = configurationWire.precomputed; - const parsedResponse: IPrecomputedConfigurationResponse = JSON.parse(response); + const parsedResponse: IObfuscatedPrecomputedConfigurationResponse = JSON.parse(response); try { const memoryOnlyPrecomputedStore = precomputedFlagsStorageFactory(); diff --git a/yarn.lock b/yarn.lock index 2774945..93813b2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -380,10 +380,10 @@ resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.6.3.tgz#f13c7c205915eb91ae54c557f5e92bddd8be0e83" integrity sha512-4B4OijXeVNOPZlYA2oEwWOTkzyltLao+xbotHQeqN++Rv27Y6s818+n2Qkp8q+Fxhn0t/5lA5X1Mxktud8eayQ== -"@eppo/js-client-sdk-common@4.8.0-alpha.0": - version "4.8.0-alpha.0" - resolved "https://registry.yarnpkg.com/@eppo/js-client-sdk-common/-/js-client-sdk-common-4.8.0-alpha.0.tgz#f82eb0d962fe1b0b238eec2b926cb419b48ef8b4" - integrity sha512-zi8+YmD20VYBkJ/raCxfved9kLxe0EyJcByrhBj3X+2BLkHTE0ml6y76QETlIocvnvmdi+Op0MlW788z9KbkGg== +"@eppo/js-client-sdk-common@4.8.0-alpha.1": + version "4.8.0-alpha.1" + resolved "https://registry.yarnpkg.com/@eppo/js-client-sdk-common/-/js-client-sdk-common-4.8.0-alpha.1.tgz#3c5c5f9fad5aa4ba582f0f9ea09b5ee60456e4cf" + integrity sha512-sB/3D/aj4TvTw44mxb2hWNGB22FrJzvHrQxQ6eGGeTBZkrBJz4wnl3WcvVjiiNPKBJpKxYGs5C0kM/++IHfH6A== dependencies: buffer "npm:@eppo/buffer@6.2.0" js-base64 "^3.7.7" @@ -1053,6 +1053,11 @@ resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.8.tgz#8268a8c57a3e4abd25c165ecd36237db7948a55e" integrity sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ== +"@types/spark-md5@^3.0.5": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@types/spark-md5/-/spark-md5-3.0.5.tgz#eddec8639217e518c26e9e221ff56bf5f5f5c900" + integrity sha512-lWf05dnD42DLVKQJZrDHtWFidcLrHuip01CtnC2/S6AMhX4t9ZlEUj4iuRlAnts0PQk7KESOqKxeGE/b6sIPGg== + "@types/stack-utils@^2.0.0": version "2.0.1" resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c" From 5f249516e886c3eaf00cf3bd1904a2d61687c74c Mon Sep 17 00:00:00 2001 From: Sameeran Kunche Date: Fri, 10 Jan 2025 10:24:12 -0800 Subject: [PATCH 23/32] Comply with throwOnFailedInitialization --- src/index.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/index.ts b/src/index.ts index 19de501..4027995 100644 --- a/src/index.ts +++ b/src/index.ts @@ -646,8 +646,13 @@ export function offlinePrecomputedInit( const configurationWire: IConfigurationWire = JSON.parse(config.precomputedConfigurationWire); if (!configurationWire.precomputed) { - applicationLogger.error('Invalid precomputed configuration wire'); - return EppoPrecomputedJSClient.instance; + const errorMessage = 'Invalid precomputed configuration wire'; + if (throwOnFailedInitialization) { + throw new Error(errorMessage); + } else { + applicationLogger.error(errorMessage); + return EppoPrecomputedJSClient.instance; + } } const { subjectKey, subjectAttributes, response } = configurationWire.precomputed; const parsedResponse: IObfuscatedPrecomputedConfigurationResponse = JSON.parse(response); From 72c6ac34a4960f679081a164deb4fae68cc37379 Mon Sep 17 00:00:00 2001 From: Sameeran Kunche Date: Fri, 10 Jan 2025 10:40:15 -0800 Subject: [PATCH 24/32] Remove unneeded reset the static instance before each test --- src/index.spec.ts | 5 ----- src/index.ts | 5 +++-- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/index.spec.ts b/src/index.spec.ts index fe08aad..340713b 100644 --- a/src/index.spec.ts +++ b/src/index.spec.ts @@ -1149,11 +1149,6 @@ describe('offlinePrecomputedInit', () => { beforeEach(() => { mockLogger = td.object(); - // Reset the static instance before each test - jest.isolateModules(() => { - // eslint-disable-next-line @typescript-eslint/no-var-requires - require('./index'); - }); }); it('initializes with precomputed assignments', () => { diff --git a/src/index.ts b/src/index.ts index 4027995..98c9d81 100644 --- a/src/index.ts +++ b/src/index.ts @@ -22,6 +22,7 @@ import { Attributes, Subject, } from '@eppo/js-client-sdk-common'; +import { loggerPrefix } from '@eppo/js-client-sdk-common/dist/application-logger'; import { IObfuscatedPrecomputedConfigurationResponse } from '@eppo/js-client-sdk-common/src/configuration'; import { assignmentCacheFactory } from './cache/assignment-cache-factory'; @@ -646,7 +647,7 @@ export function offlinePrecomputedInit( const configurationWire: IConfigurationWire = JSON.parse(config.precomputedConfigurationWire); if (!configurationWire.precomputed) { - const errorMessage = 'Invalid precomputed configuration wire'; + const errorMessage = `${loggerPrefix} Invalid precomputed configuration wire`; if (throwOnFailedInitialization) { throw new Error(errorMessage); } else { @@ -681,7 +682,7 @@ export function offlinePrecomputedInit( } } catch (error) { applicationLogger.warn( - 'Eppo SDK encountered an error initializing precomputed client, assignment calls will return the default value and not be logged', + `${loggerPrefix} Encountered an error initializing precomputed client, assignment calls will return the default value and not be logged`, ); if (throwOnFailedInitialization) { throw error; From 5c3712ecf3f578b9b354177c6dba252070ae85af Mon Sep 17 00:00:00 2001 From: Sameeran Kunche Date: Fri, 10 Jan 2025 10:50:11 -0800 Subject: [PATCH 25/32] Make documentation consistent with the interface --- src/index.spec.ts | 12 ++++++------ src/index.ts | 17 ++++++++--------- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/src/index.spec.ts b/src/index.spec.ts index 340713b..b57bb55 100644 --- a/src/index.spec.ts +++ b/src/index.spec.ts @@ -1069,9 +1069,9 @@ describe('EppoPrecomputedJSClient E2E test', () => { beforeAll(async () => { global.fetch = jest.fn(() => { - const precomputedConfigurationWire = readMockPrecomputedResponse(MOCK_PRECOMPUTED_WIRE_FILE); + const precomputedConfiguration = readMockPrecomputedResponse(MOCK_PRECOMPUTED_WIRE_FILE); const precomputedResponse: IPrecomputedConfigurationResponse = JSON.parse( - JSON.parse(precomputedConfigurationWire).precomputed.response, + JSON.parse(precomputedConfiguration).precomputed.response, ); return Promise.resolve({ ok: true, @@ -1141,10 +1141,10 @@ describe('EppoPrecomputedJSClient E2E test', () => { describe('offlinePrecomputedInit', () => { let mockLogger: IAssignmentLogger; - let precomputedConfigurationWire: string; + let precomputedConfiguration: string; beforeAll(() => { - precomputedConfigurationWire = readMockPrecomputedResponse(MOCK_PRECOMPUTED_WIRE_FILE); + precomputedConfiguration = readMockPrecomputedResponse(MOCK_PRECOMPUTED_WIRE_FILE); }); beforeEach(() => { @@ -1153,7 +1153,7 @@ describe('offlinePrecomputedInit', () => { it('initializes with precomputed assignments', () => { const client = offlinePrecomputedInit({ - precomputedConfigurationWire, + precomputedConfiguration, assignmentLogger: mockLogger, }); @@ -1176,7 +1176,7 @@ describe('offlinePrecomputedInit', () => { }); it('initializes without an assignment logger', () => { - const client = offlinePrecomputedInit({ precomputedConfigurationWire }); + const client = offlinePrecomputedInit({ precomputedConfiguration }); expect(client.getStringAssignment('string-flag', 'default')).toBe('red'); }); diff --git a/src/index.ts b/src/index.ts index 98c9d81..28e5141 100644 --- a/src/index.ts +++ b/src/index.ts @@ -611,19 +611,18 @@ export async function precomputedInit( } /** - * Initializes the Eppo precomputed client with configuration parameters. + * Configuration parameters for initializing the Eppo precomputed client. * - * The purpose is for use-cases where the precomputed assignments are available from an external process - * that can bootstrap the SDK. + * This interface is used for cases where precomputed assignments are available + * from an external process that can bootstrap the SDK client. * - * This method should be called once on application startup. - * - * @param config - client configuration - * @returns a singleton precomputed client instance + * @property precomputedConfiguration - The configuration as a string to bootstrap the client. + * @property assignmentLogger - Optional logger for assignment events. + * @property throwOnFailedInitialization - Optional flag to throw an error if initialization fails. * @public */ export interface IPrecomputedClientConfigSync { - precomputedConfigurationWire: string; + precomputedConfiguration: string; assignmentLogger?: IAssignmentLogger; throwOnFailedInitialization?: boolean; } @@ -645,7 +644,7 @@ export function offlinePrecomputedInit( ): EppoPrecomputedClient { const throwOnFailedInitialization = config.throwOnFailedInitialization ?? true; - const configurationWire: IConfigurationWire = JSON.parse(config.precomputedConfigurationWire); + const configurationWire: IConfigurationWire = JSON.parse(config.precomputedConfiguration); if (!configurationWire.precomputed) { const errorMessage = `${loggerPrefix} Invalid precomputed configuration wire`; if (throwOnFailedInitialization) { From d71e589d43900f779ccad4b969a043f268f63811 Mon Sep 17 00:00:00 2001 From: Sameeran Kunche Date: Fri, 10 Jan 2025 10:54:12 -0800 Subject: [PATCH 26/32] Remove unnecessary casting to Attribute --- src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index 28e5141..f815e0d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -668,7 +668,7 @@ export function offlinePrecomputedInit( const subject: Subject = { subjectKey, - subjectAttributes: subjectAttributes ?? ({} as Attributes), + subjectAttributes: subjectAttributes ?? {}, }; EppoPrecomputedJSClient.instance = new EppoPrecomputedJSClient({ From e094f8e35504a01889ef4efa804995d23cca7441 Mon Sep 17 00:00:00 2001 From: Sameeran Kunche Date: Fri, 10 Jan 2025 12:11:47 -0800 Subject: [PATCH 27/32] Don't include logger prefix in thrown error --- src/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/index.ts b/src/index.ts index f815e0d..b9e9dfd 100644 --- a/src/index.ts +++ b/src/index.ts @@ -646,11 +646,11 @@ export function offlinePrecomputedInit( const configurationWire: IConfigurationWire = JSON.parse(config.precomputedConfiguration); if (!configurationWire.precomputed) { - const errorMessage = `${loggerPrefix} Invalid precomputed configuration wire`; + const errorMessage = 'Invalid precomputed configuration wire'; if (throwOnFailedInitialization) { throw new Error(errorMessage); } else { - applicationLogger.error(errorMessage); + applicationLogger.error(`${loggerPrefix} ${errorMessage}`); return EppoPrecomputedJSClient.instance; } } From 27b413bc2d8c8aed791829ebdc1b513d76c6c216 Mon Sep 17 00:00:00 2001 From: Sameeran Kunche Date: Fri, 10 Jan 2025 15:33:57 -0800 Subject: [PATCH 28/32] Add a shutdown function and test --- src/index.spec.ts | 30 ++++++++++++++++++++++++++++++ src/index.ts | 10 ++++++++++ 2 files changed, 40 insertions(+) diff --git a/src/index.spec.ts b/src/index.spec.ts index b57bb55..7a1798d 100644 --- a/src/index.spec.ts +++ b/src/index.spec.ts @@ -5,6 +5,7 @@ import { createHash } from 'crypto'; import { + applicationLogger, AssignmentCache, constants, EppoClient, @@ -1151,6 +1152,10 @@ describe('offlinePrecomputedInit', () => { mockLogger = td.object(); }); + afterEach(() => { + td.reset(); + }); + it('initializes with precomputed assignments', () => { const client = offlinePrecomputedInit({ precomputedConfiguration, @@ -1180,6 +1185,31 @@ describe('offlinePrecomputedInit', () => { expect(client.getStringAssignment('string-flag', 'default')).toBe('red'); }); + + it('logs a warning on re-initialization', () => { + td.replace(applicationLogger, 'warn'); + // First initialization there is no client to spy on, so we only test that no warning is logged + offlinePrecomputedInit({ + precomputedConfiguration, + assignmentLogger: mockLogger, + }); + td.verify( + applicationLogger.warn(td.matchers.contains('Precomputed client is being re-initialized.')), + { times: 0 }, + ); + // Replace instance with a mock and check that shutdown is called on re-initialization + const mockInstance = td.object(); + EppoPrecomputedJSClient.instance = mockInstance; + offlinePrecomputedInit({ + precomputedConfiguration, + assignmentLogger: mockLogger, + }); + td.verify(mockInstance.stopPolling(), { times: 1 }); + td.verify( + applicationLogger.warn(td.matchers.contains('Precomputed client is being re-initialized.')), + { times: 1 }, + ); + }); }); describe('EppoClient config', () => { diff --git a/src/index.ts b/src/index.ts index b9e9dfd..09e6188 100644 --- a/src/index.ts +++ b/src/index.ts @@ -671,6 +671,7 @@ export function offlinePrecomputedInit( subjectAttributes: subjectAttributes ?? {}, }; + shutdownEppoPrecomputedClient(); EppoPrecomputedJSClient.instance = new EppoPrecomputedJSClient({ precomputedFlagStore: memoryOnlyPrecomputedStore, subject, @@ -692,6 +693,15 @@ export function offlinePrecomputedInit( return EppoPrecomputedJSClient.instance; } +function shutdownEppoPrecomputedClient() { + if (EppoPrecomputedJSClient.instance) { + // Perform necessary cleanup, such as stopping polling or logging + EppoPrecomputedJSClient.instance.stopPolling(); + EppoPrecomputedJSClient.initialized = false; + applicationLogger.warn(`${loggerPrefix} Precomputed client is being re-initialized.`); + } +} + /** * Used to access a singleton SDK precomputed client instance. * Use the method after calling precomputedInit() to initialize the client. From 70a066762f8ef25e4cb43685dbe0023ed24a375f Mon Sep 17 00:00:00 2001 From: Sameeran Kunche Date: Fri, 10 Jan 2025 15:42:02 -0800 Subject: [PATCH 29/32] Remove comment --- src/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index 09e6188..066a27f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -695,7 +695,6 @@ export function offlinePrecomputedInit( function shutdownEppoPrecomputedClient() { if (EppoPrecomputedJSClient.instance) { - // Perform necessary cleanup, such as stopping polling or logging EppoPrecomputedJSClient.instance.stopPolling(); EppoPrecomputedJSClient.initialized = false; applicationLogger.warn(`${loggerPrefix} Precomputed client is being re-initialized.`); From a2db546d49ad3b4a82e1243473cc6b3618c100e2 Mon Sep 17 00:00:00 2001 From: Sameeran Kunche Date: Fri, 10 Jan 2025 16:16:59 -0800 Subject: [PATCH 30/32] v3.9.2-alpha.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 53561e2..60b7fb9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@eppo/js-client-sdk", - "version": "3.9.1", + "version": "3.9.2-alpha.0", "description": "Eppo SDK for client-side JavaScript applications", "main": "dist/index.js", "files": [ From a53962dc302ab5945d50849525eee3c67dd6746b Mon Sep 17 00:00:00 2001 From: Sameeran Kunche Date: Fri, 10 Jan 2025 16:50:53 -0800 Subject: [PATCH 31/32] Remove application logger input --- docs/js-client-sdk.iprecomputedclientconfigsync.md | 8 ++++---- ...tedclientconfigsync.precomputedconfiguration.md | 11 +++++++++++ ...lientconfigsync.precomputedconfigurationwire.md | 11 ----------- docs/js-client-sdk.md | 6 +++--- js-client-sdk.api.md | 2 +- src/index.ts | 14 ++++++-------- 6 files changed, 25 insertions(+), 27 deletions(-) create mode 100644 docs/js-client-sdk.iprecomputedclientconfigsync.precomputedconfiguration.md delete mode 100644 docs/js-client-sdk.iprecomputedclientconfigsync.precomputedconfigurationwire.md diff --git a/docs/js-client-sdk.iprecomputedclientconfigsync.md b/docs/js-client-sdk.iprecomputedclientconfigsync.md index 16b7d4d..2d334c3 100644 --- a/docs/js-client-sdk.iprecomputedclientconfigsync.md +++ b/docs/js-client-sdk.iprecomputedclientconfigsync.md @@ -4,11 +4,11 @@ ## IPrecomputedClientConfigSync interface -Initializes the Eppo precomputed client with configuration parameters. +Configuration parameters for initializing the Eppo precomputed client. -The purpose is for use-cases where the precomputed assignments are available from an external process that can bootstrap the SDK. +This interface is used for cases where precomputed assignments are available from an external process that can bootstrap the SDK client. -This method should be called once on application startup. + precomputedConfiguration - The configuration as a string to bootstrap the client. assignmentLogger - Optional logger for assignment events. throwOnFailedInitialization - Optional flag to throw an error if initialization fails. **Signature:** @@ -60,7 +60,7 @@ _(Optional)_ -[precomputedConfigurationWire](./js-client-sdk.iprecomputedclientconfigsync.precomputedconfigurationwire.md) +[precomputedConfiguration](./js-client-sdk.iprecomputedclientconfigsync.precomputedconfiguration.md) diff --git a/docs/js-client-sdk.iprecomputedclientconfigsync.precomputedconfiguration.md b/docs/js-client-sdk.iprecomputedclientconfigsync.precomputedconfiguration.md new file mode 100644 index 0000000..6f6726b --- /dev/null +++ b/docs/js-client-sdk.iprecomputedclientconfigsync.precomputedconfiguration.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@eppo/js-client-sdk](./js-client-sdk.md) > [IPrecomputedClientConfigSync](./js-client-sdk.iprecomputedclientconfigsync.md) > [precomputedConfiguration](./js-client-sdk.iprecomputedclientconfigsync.precomputedconfiguration.md) + +## IPrecomputedClientConfigSync.precomputedConfiguration property + +**Signature:** + +```typescript +precomputedConfiguration: string; +``` diff --git a/docs/js-client-sdk.iprecomputedclientconfigsync.precomputedconfigurationwire.md b/docs/js-client-sdk.iprecomputedclientconfigsync.precomputedconfigurationwire.md deleted file mode 100644 index b52e9f4..0000000 --- a/docs/js-client-sdk.iprecomputedclientconfigsync.precomputedconfigurationwire.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [@eppo/js-client-sdk](./js-client-sdk.md) > [IPrecomputedClientConfigSync](./js-client-sdk.iprecomputedclientconfigsync.md) > [precomputedConfigurationWire](./js-client-sdk.iprecomputedclientconfigsync.precomputedconfigurationwire.md) - -## IPrecomputedClientConfigSync.precomputedConfigurationWire property - -**Signature:** - -```typescript -precomputedConfigurationWire: string; -``` diff --git a/docs/js-client-sdk.md b/docs/js-client-sdk.md index 63b202a..38730ac 100644 --- a/docs/js-client-sdk.md +++ b/docs/js-client-sdk.md @@ -220,11 +220,11 @@ Configuration for Eppo precomputed client initialization -Initializes the Eppo precomputed client with configuration parameters. +Configuration parameters for initializing the Eppo precomputed client. -The purpose is for use-cases where the precomputed assignments are available from an external process that can bootstrap the SDK. +This interface is used for cases where precomputed assignments are available from an external process that can bootstrap the SDK client. -This method should be called once on application startup. + precomputedConfiguration - The configuration as a string to bootstrap the client. assignmentLogger - Optional logger for assignment events. throwOnFailedInitialization - Optional flag to throw an error if initialization fails. diff --git a/js-client-sdk.api.md b/js-client-sdk.api.md index 505e6f6..a6adacc 100644 --- a/js-client-sdk.api.md +++ b/js-client-sdk.api.md @@ -161,7 +161,7 @@ export interface IPrecomputedClientConfigSync { // (undocumented) assignmentLogger?: IAssignmentLogger; // (undocumented) - precomputedConfigurationWire: string; + precomputedConfiguration: string; // (undocumented) throwOnFailedInitialization?: boolean; } diff --git a/src/index.ts b/src/index.ts index 066a27f..3da1a7a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -19,10 +19,8 @@ import { validation, Event, IConfigurationWire, - Attributes, Subject, } from '@eppo/js-client-sdk-common'; -import { loggerPrefix } from '@eppo/js-client-sdk-common/dist/application-logger'; import { IObfuscatedPrecomputedConfigurationResponse } from '@eppo/js-client-sdk-common/src/configuration'; import { assignmentCacheFactory } from './cache/assignment-cache-factory'; @@ -616,9 +614,9 @@ export async function precomputedInit( * This interface is used for cases where precomputed assignments are available * from an external process that can bootstrap the SDK client. * - * @property precomputedConfiguration - The configuration as a string to bootstrap the client. - * @property assignmentLogger - Optional logger for assignment events. - * @property throwOnFailedInitialization - Optional flag to throw an error if initialization fails. + * @param precomputedConfiguration - The configuration as a string to bootstrap the client. + * @param assignmentLogger - Optional logger for assignment events. + * @param throwOnFailedInitialization - Optional flag to throw an error if initialization fails. * @public */ export interface IPrecomputedClientConfigSync { @@ -650,7 +648,7 @@ export function offlinePrecomputedInit( if (throwOnFailedInitialization) { throw new Error(errorMessage); } else { - applicationLogger.error(`${loggerPrefix} ${errorMessage}`); + applicationLogger.error('[Eppo SDK] ${errorMessage}'); return EppoPrecomputedJSClient.instance; } } @@ -682,7 +680,7 @@ export function offlinePrecomputedInit( } } catch (error) { applicationLogger.warn( - `${loggerPrefix} Encountered an error initializing precomputed client, assignment calls will return the default value and not be logged`, + '[Eppo SDK] Encountered an error initializing precomputed client, assignment calls will return the default value and not be logged', ); if (throwOnFailedInitialization) { throw error; @@ -697,7 +695,7 @@ function shutdownEppoPrecomputedClient() { if (EppoPrecomputedJSClient.instance) { EppoPrecomputedJSClient.instance.stopPolling(); EppoPrecomputedJSClient.initialized = false; - applicationLogger.warn(`${loggerPrefix} Precomputed client is being re-initialized.`); + applicationLogger.warn('[Eppo SDK] Precomputed client is being re-initialized.'); } } From 8612f588bb1c7fe496bcd270070c193e5d719d21 Mon Sep 17 00:00:00 2001 From: Sameeran Kunche Date: Fri, 10 Jan 2025 17:14:05 -0800 Subject: [PATCH 32/32] Fix test --- src/index.spec.ts | 1 + src/index.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/index.spec.ts b/src/index.spec.ts index 7a1798d..660780b 100644 --- a/src/index.spec.ts +++ b/src/index.spec.ts @@ -1188,6 +1188,7 @@ describe('offlinePrecomputedInit', () => { it('logs a warning on re-initialization', () => { td.replace(applicationLogger, 'warn'); + EppoPrecomputedJSClient.initialized = false; // First initialization there is no client to spy on, so we only test that no warning is logged offlinePrecomputedInit({ precomputedConfiguration, diff --git a/src/index.ts b/src/index.ts index 3da1a7a..e1af51e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -692,7 +692,7 @@ export function offlinePrecomputedInit( } function shutdownEppoPrecomputedClient() { - if (EppoPrecomputedJSClient.instance) { + if (EppoPrecomputedJSClient.instance && EppoPrecomputedJSClient.initialized) { EppoPrecomputedJSClient.instance.stopPolling(); EppoPrecomputedJSClient.initialized = false; applicationLogger.warn('[Eppo SDK] Precomputed client is being re-initialized.');