Skip to content

Commit cbfdb29

Browse files
Merge branch 'development' into SDKS-8407_baseline
2 parents f6fa56e + 9cd4e33 commit cbfdb29

File tree

11 files changed

+50
-38
lines changed

11 files changed

+50
-38
lines changed

CHANGES.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
2.0.0 (September XX, 2024)
2+
- Added `factory.destroy()` method, which invokes the `destroy` method on all SDK clients created by the factory.
23
- Added support for targeting rules based on large segments.
34
- BREAKING CHANGES:
45
- Updated default flag spec version to 1.2.

src/sdkClient/__tests__/sdkClientMethod.spec.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ const paramMocks = [
1414
sdkReadinessManager: { sdkStatus: jest.fn(), readinessManager: { destroy: jest.fn() } },
1515
signalListener: undefined,
1616
settings: { mode: CONSUMER_MODE, log: loggerMock, core: { authorizationKey: 'sdk key '} },
17-
telemetryTracker: telemetryTrackerFactory()
17+
telemetryTracker: telemetryTrackerFactory(),
18+
clients: {}
1819
},
1920
// SyncManager (i.e., Sync SDK) and Signal listener
2021
{
@@ -23,7 +24,8 @@ const paramMocks = [
2324
sdkReadinessManager: { sdkStatus: jest.fn(), readinessManager: { destroy: jest.fn() } },
2425
signalListener: { stop: jest.fn() },
2526
settings: { mode: STANDALONE_MODE, log: loggerMock, core: { authorizationKey: 'sdk key '} },
26-
telemetryTracker: telemetryTrackerFactory()
27+
telemetryTracker: telemetryTrackerFactory(),
28+
clients: {}
2729
}
2830
];
2931

src/sdkClient/__tests__/sdkClientMethodCS.spec.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ const params = {
4545
syncManager: syncManagerMock,
4646
signalListener: { stop: jest.fn() },
4747
settings: settingsWithKey,
48-
telemetryTracker: telemetryTrackerFactory()
48+
telemetryTracker: telemetryTrackerFactory(),
49+
clients: {}
4950
};
5051

5152
const invalidAttributes = [
@@ -71,6 +72,7 @@ describe('sdkClientMethodCSFactory', () => {
7172
partialStorages.length = 0;
7273
partialSdkReadinessManagers.length = 0;
7374
partialSyncManagers.length = 0;
75+
params.clients = {};
7476
});
7577

7678
// list of factory functions and their types (whether it ignores TT or not)

src/sdkClient/identity.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { SplitIO } from '../types';
2+
3+
export function buildInstanceId(key: SplitIO.SplitKey, trafficType?: string) { // @ts-ignore
4+
return `${key.matchingKey ? key.matchingKey : key}-${key.bucketingKey ? key.bucketingKey : key}-${trafficType ? trafficType : ''}`;
5+
}

src/sdkClient/sdkClient.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,11 +66,11 @@ export function sdkClientFactory(params: ISdkFactoryContext, isSharedClient?: bo
6666
syncManager && syncManager.stop();
6767

6868
return __flush().then(() => {
69-
// Cleanup event listeners
70-
signalListener && signalListener.stop();
71-
72-
// @TODO stop only if last client is destroyed
73-
if (uniqueKeysTracker) uniqueKeysTracker.stop();
69+
// For main client, cleanup event listeners and scheduled jobs
70+
if (!isSharedClient) {
71+
signalListener && signalListener.stop();
72+
uniqueKeysTracker && uniqueKeysTracker.stop();
73+
}
7474

7575
// Cleanup storage
7676
return storage.destroy();

src/sdkClient/sdkClientMethod.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,15 @@ import { RETRIEVE_CLIENT_DEFAULT } from '../logger/constants';
44
import { ISdkFactoryContext } from '../sdkFactory/types';
55

66
/**
7-
* Factory of client method for server-side SDKs (ISDK and IAsyncSDK)
7+
* Factory of client method for server-side SDKs
88
*/
99
export function sdkClientMethodFactory(params: ISdkFactoryContext): () => SplitIO.IClient | SplitIO.IAsyncClient {
1010
const log = params.settings.log;
1111
const clientInstance = sdkClientFactory(params);
1212

13+
// Only one client in server-side without bound key
14+
params.clients[''] = clientInstance;
15+
1316
return function client() {
1417
if (arguments.length > 0) {
1518
throw new Error('Shared Client not supported by the storage mechanism. Create isolated instances instead.');

src/sdkClient/sdkClientMethodCS.ts

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,14 @@ import { objectAssign } from '../utils/lang/objectAssign';
88
import { RETRIEVE_CLIENT_DEFAULT, NEW_SHARED_CLIENT, RETRIEVE_CLIENT_EXISTING, LOG_PREFIX_CLIENT_INSTANTIATION } from '../logger/constants';
99
import { SDK_SEGMENTS_ARRIVED } from '../readiness/constants';
1010
import { ISdkFactoryContext } from '../sdkFactory/types';
11-
12-
function buildInstanceId(key: SplitIO.SplitKey) {
13-
// @ts-ignore
14-
return `${key.matchingKey ? key.matchingKey : key}-${key.bucketingKey ? key.bucketingKey : key}-`;
15-
}
11+
import { buildInstanceId } from './identity';
1612

1713
/**
1814
* Factory of client method for the client-side API variant where TT is ignored.
1915
* Therefore, clients don't have a bound TT for the track method.
2016
*/
2117
export function sdkClientMethodCSFactory(params: ISdkFactoryContext): (key?: SplitIO.SplitKey) => SplitIO.ICsClient {
22-
const { storage, syncManager, sdkReadinessManager, settings: { core: { key }, log } } = params;
18+
const { clients, storage, syncManager, sdkReadinessManager, settings: { core: { key }, log } } = params;
2319

2420
const mainClientInstance = clientCSDecorator(
2521
log,
@@ -31,8 +27,7 @@ export function sdkClientMethodCSFactory(params: ISdkFactoryContext): (key?: Spl
3127
const defaultInstanceId = buildInstanceId(parsedDefaultKey);
3228

3329
// Cache instances created per factory.
34-
const clientInstances: Record<string, SplitIO.ICsClient> = {};
35-
clientInstances[defaultInstanceId] = mainClientInstance;
30+
clients[defaultInstanceId] = mainClientInstance;
3631

3732
return function client(key?: SplitIO.SplitKey) {
3833
if (key === undefined) {
@@ -48,7 +43,7 @@ export function sdkClientMethodCSFactory(params: ISdkFactoryContext): (key?: Spl
4843

4944
const instanceId = buildInstanceId(validKey);
5045

51-
if (!clientInstances[instanceId]) {
46+
if (!clients[instanceId]) {
5247
const matchingKey = getMatching(validKey);
5348

5449
const sharedSdkReadiness = sdkReadinessManager.shared();
@@ -70,13 +65,12 @@ export function sdkClientMethodCSFactory(params: ISdkFactoryContext): (key?: Spl
7065

7166
// As shared clients reuse all the storage information, we don't need to check here if we
7267
// will use offline or online mode. We should stick with the original decision.
73-
clientInstances[instanceId] = clientCSDecorator(
68+
clients[instanceId] = clientCSDecorator(
7469
log,
7570
sdkClientFactory(objectAssign({}, params, {
7671
sdkReadinessManager: sharedSdkReadiness,
7772
storage: sharedStorage || storage,
7873
syncManager: sharedSyncManager,
79-
signalListener: undefined, // only the main client "destroy" method stops the signal listener
8074
}), true) as SplitIO.IClient,
8175
validKey
8276
);
@@ -88,6 +82,6 @@ export function sdkClientMethodCSFactory(params: ISdkFactoryContext): (key?: Spl
8882
log.debug(RETRIEVE_CLIENT_EXISTING);
8983
}
9084

91-
return clientInstances[instanceId];
85+
return clients[instanceId] as SplitIO.ICsClient;
9286
};
9387
}

src/sdkClient/sdkClientMethodCSWithTT.ts

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,15 @@ import { objectAssign } from '../utils/lang/objectAssign';
99
import { RETRIEVE_CLIENT_DEFAULT, NEW_SHARED_CLIENT, RETRIEVE_CLIENT_EXISTING, LOG_PREFIX_CLIENT_INSTANTIATION } from '../logger/constants';
1010
import { SDK_SEGMENTS_ARRIVED } from '../readiness/constants';
1111
import { ISdkFactoryContext } from '../sdkFactory/types';
12-
13-
function buildInstanceId(key: SplitIO.SplitKey, trafficType?: string) {
14-
// @ts-ignore
15-
return `${key.matchingKey ? key.matchingKey : key}-${key.bucketingKey ? key.bucketingKey : key}-${trafficType !== undefined ? trafficType : ''}`;
16-
}
12+
import { buildInstanceId } from './identity';
1713

1814
/**
1915
* Factory of client method for the client-side (browser) variant of the Isomorphic JS SDK,
2016
* where clients can have a bound TT for the track method, which is provided via the settings
2117
* (default client) or the client method (shared clients).
2218
*/
2319
export function sdkClientMethodCSFactory(params: ISdkFactoryContext): (key?: SplitIO.SplitKey, trafficType?: string) => SplitIO.ICsClient {
24-
const { storage, syncManager, sdkReadinessManager, settings: { core: { key, trafficType }, log } } = params;
20+
const { clients, storage, syncManager, sdkReadinessManager, settings: { core: { key, trafficType }, log } } = params;
2521

2622
const mainClientInstance = clientCSDecorator(
2723
log,
@@ -34,8 +30,7 @@ export function sdkClientMethodCSFactory(params: ISdkFactoryContext): (key?: Spl
3430
const defaultInstanceId = buildInstanceId(parsedDefaultKey, trafficType);
3531

3632
// Cache instances created per factory.
37-
const clientInstances: Record<string, SplitIO.ICsClient> = {};
38-
clientInstances[defaultInstanceId] = mainClientInstance;
33+
clients[defaultInstanceId] = mainClientInstance;
3934

4035
return function client(key?: SplitIO.SplitKey, trafficType?: string) {
4136
if (key === undefined) {
@@ -58,7 +53,7 @@ export function sdkClientMethodCSFactory(params: ISdkFactoryContext): (key?: Spl
5853
}
5954
const instanceId = buildInstanceId(validKey, validTrafficType);
6055

61-
if (!clientInstances[instanceId]) {
56+
if (!clients[instanceId]) {
6257
const matchingKey = getMatching(validKey);
6358

6459
const sharedSdkReadiness = sdkReadinessManager.shared();
@@ -80,13 +75,12 @@ export function sdkClientMethodCSFactory(params: ISdkFactoryContext): (key?: Spl
8075

8176
// As shared clients reuse all the storage information, we don't need to check here if we
8277
// will use offline or online mode. We should stick with the original decision.
83-
clientInstances[instanceId] = clientCSDecorator(
78+
clients[instanceId] = clientCSDecorator(
8479
log,
8580
sdkClientFactory(objectAssign({}, params, {
8681
sdkReadinessManager: sharedSdkReadiness,
8782
storage: sharedStorage || storage,
8883
syncManager: sharedSyncManager,
89-
signalListener: undefined, // only the main client "destroy" method stops the signal listener
9084
}), true) as SplitIO.IClient,
9185
validKey,
9286
validTrafficType
@@ -99,6 +93,6 @@ export function sdkClientMethodCSFactory(params: ISdkFactoryContext): (key?: Spl
9993
log.debug(RETRIEVE_CLIENT_EXISTING);
10094
}
10195

102-
return clientInstances[instanceId];
96+
return clients[instanceId] as SplitIO.ICsClient;
10397
};
10498
}

src/sdkFactory/index.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { sdkReadinessManagerFactory } from '../readiness/sdkReadinessManager';
33
import { impressionsTrackerFactory } from '../trackers/impressionsTracker';
44
import { eventTrackerFactory } from '../trackers/eventTracker';
55
import { telemetryTrackerFactory } from '../trackers/telemetryTracker';
6-
import { SplitIO } from '../types';
6+
import { IBasicClient, SplitIO } from '../types';
77
import { validateAndTrackApiKey } from '../utils/inputValidation/apiKey';
88
import { createLoggerAPI } from '../logger/sdkLogger';
99
import { NEW_FACTORY, RETRIEVE_MANAGER } from '../logger/constants';
@@ -48,7 +48,7 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ICsSDK | SplitIO.
4848
},
4949
});
5050
// @TODO add support for dataloader: `if (params.dataLoader) params.dataLoader(storage);`
51-
51+
const clients: Record<string, IBasicClient> = {};
5252
const telemetryTracker = telemetryTrackerFactory(storage.telemetry, platform.now);
5353
const integrationsManager = integrationsManagerFactory && integrationsManagerFactory({ settings, storage, telemetryTracker });
5454

@@ -73,7 +73,7 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ICsSDK | SplitIO.
7373
// splitApi is used by SyncManager and Browser signal listener
7474
const splitApi = splitApiFactory && splitApiFactory(settings, platform, telemetryTracker);
7575

76-
const ctx: ISdkFactoryContext = { splitApi, eventTracker, impressionsTracker, telemetryTracker, uniqueKeysTracker, sdkReadinessManager, readiness, settings, storage, platform };
76+
const ctx: ISdkFactoryContext = { clients, splitApi, eventTracker, impressionsTracker, telemetryTracker, uniqueKeysTracker, sdkReadinessManager, readiness, settings, storage, platform };
7777

7878
const syncManager = syncManagerFactory && syncManagerFactory(ctx as ISdkFactoryContextSync);
7979
ctx.syncManager = syncManager;
@@ -105,5 +105,9 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ICsSDK | SplitIO.
105105
Logger: createLoggerAPI(log),
106106

107107
settings,
108+
109+
destroy() {
110+
return Promise.all(Object.keys(clients).map(key => clients[key].destroy())).then(() => {});
111+
}
108112
}, extraProps && extraProps(ctx));
109113
}

src/sdkFactory/types.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { IStorageAsync, IStorageSync, IStorageFactoryParams } from '../storages/
88
import { ISyncManager } from '../sync/types';
99
import { IImpressionObserver } from '../trackers/impressionObserver/types';
1010
import { IImpressionsTracker, IEventTracker, ITelemetryTracker, IFilterAdapter, IUniqueKeysTracker } from '../trackers/types';
11-
import { SplitIO, ISettings, IEventEmitter } from '../types';
11+
import { SplitIO, ISettings, IEventEmitter, IBasicClient } from '../types';
1212

1313
/**
1414
* Environment related dependencies.
@@ -49,6 +49,7 @@ export interface ISdkFactoryContext {
4949
signalListener?: ISignalListener
5050
splitApi?: ISplitApi
5151
syncManager?: ISyncManager,
52+
clients: Record<string, IBasicClient>,
5253
}
5354

5455
export interface ISdkFactoryContextSync extends ISdkFactoryContext {

src/types.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -426,7 +426,7 @@ export interface IStatusInterface extends IEventEmitter {
426426
* @interface IBasicClient
427427
* @extends IStatusInterface
428428
*/
429-
interface IBasicClient extends IStatusInterface {
429+
export interface IBasicClient extends IStatusInterface {
430430
/**
431431
* Flush data
432432
* @function flush
@@ -459,6 +459,12 @@ interface IBasicSDK {
459459
* @property Logger
460460
*/
461461
Logger: ILoggerAPI
462+
/**
463+
* Destroy all the clients created by this factory.
464+
* @function destroy
465+
* @returns {Promise<void>}
466+
*/
467+
destroy(): Promise<void>
462468
}
463469
/****** Exposed namespace ******/
464470
/**

0 commit comments

Comments
 (0)