Skip to content

Commit 2a20458

Browse files
Merge pull request #388 from splitio/development
Release v2.1.0
2 parents 5eb0027 + af3ef14 commit 2a20458

36 files changed

+221
-263
lines changed

CHANGES.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
2.1.0 (January 17, 2025)
2+
- Added support for the new impressions tracking toggle available on feature flags, both respecting the setting and including the new field being returned on `SplitView` type objects. Read more in our docs.
3+
14
2.0.3 (January 9, 2025)
25
- Bugfixing - Properly handle rejected promises when using targeting rules with segment matchers in consumer modes (e.g., Redis and Pluggable storages).
36

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@splitsoftware/splitio-commons",
3-
"version": "2.0.3",
3+
"version": "2.1.0",
44
"description": "Split JavaScript SDK common components",
55
"main": "cjs/index.js",
66
"module": "esm/index.js",

src/dtos/types.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,8 @@ export interface ISplit {
208208
configurations?: {
209209
[treatmentName: string]: string
210210
},
211-
sets?: string[]
211+
sets?: string[],
212+
impressionsDisabled?: boolean
212213
}
213214

214215
// Split definition used in offline mode

src/evaluator/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,12 +156,14 @@ function getEvaluation(
156156
return evaluation.then(result => {
157157
result.changeNumber = split.getChangeNumber();
158158
result.config = splitJSON.configurations && splitJSON.configurations[result.treatment] || null;
159+
result.impressionsDisabled = splitJSON.impressionsDisabled;
159160

160161
return result;
161162
});
162163
} else {
163164
evaluation.changeNumber = split.getChangeNumber(); // Always sync and optional
164165
evaluation.config = splitJSON.configurations && splitJSON.configurations[evaluation.treatment] || null;
166+
evaluation.impressionsDisabled = splitJSON.impressionsDisabled;
165167
}
166168
}
167169

src/evaluator/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export interface IEvaluation {
2525
config?: string | null
2626
}
2727

28-
export type IEvaluationResult = IEvaluation & { treatment: string }
28+
export type IEvaluationResult = IEvaluation & { treatment: string; impressionsDisabled?: boolean }
2929

3030
export type ISplitEvaluator = (log: ILogger, key: SplitIO.SplitKey, splitName: string, attributes: SplitIO.Attributes | undefined, storage: IStorageSync | IStorageAsync) => MaybeThenable<IEvaluation>
3131

src/listeners/browser.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import { IResponse, ISplitApi } from '../services/types';
88
import { ISettings } from '../types';
99
import SplitIO from '../../types/splitio';
1010
import { ImpressionsPayload } from '../sync/submitters/types';
11-
import { OPTIMIZED, DEBUG, NONE } from '../utils/constants';
1211
import { objectAssign } from '../utils/lang/objectAssign';
1312
import { CLEANUP_REGISTERING, CLEANUP_DEREGISTERING } from '../logger/constants';
1413
import { ISyncManager } from '../sync/types';
@@ -78,10 +77,9 @@ export class BrowserSignalListener implements ISignalListener {
7877

7978
// Flush impressions & events data if there is user consent
8079
if (isConsentGranted(this.settings)) {
81-
const sim = this.settings.sync.impressionsMode;
8280
const extraMetadata = {
8381
// sim stands for Sync/Split Impressions Mode
84-
sim: sim === OPTIMIZED ? OPTIMIZED : sim === DEBUG ? DEBUG : NONE
82+
sim: this.settings.sync.impressionsMode
8583
};
8684

8785
this._flushData(events + '/testImpressions/beacon', this.storage.impressions, this.serviceApi.postTestImpressionsBulk, this.fromImpressionsCollector, extraMetadata);

src/sdkClient/__tests__/sdkClientMethod.spec.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ const paramMocks = [
1616
signalListener: undefined,
1717
settings: { mode: CONSUMER_MODE, log: loggerMock, core: { authorizationKey: 'sdk key '} },
1818
telemetryTracker: telemetryTrackerFactory(),
19-
clients: {}
19+
clients: {},
20+
uniqueKeysTracker: { start: jest.fn(), stop: jest.fn() }
2021
},
2122
// SyncManager (i.e., Sync SDK) and Signal listener
2223
{
@@ -26,7 +27,8 @@ const paramMocks = [
2627
signalListener: { stop: jest.fn() },
2728
settings: { mode: STANDALONE_MODE, log: loggerMock, core: { authorizationKey: 'sdk key '} },
2829
telemetryTracker: telemetryTrackerFactory(),
29-
clients: {}
30+
clients: {},
31+
uniqueKeysTracker: { start: jest.fn(), stop: jest.fn() }
3032
}
3133
];
3234

@@ -70,6 +72,7 @@ test.each(paramMocks)('sdkClientMethodFactory', (params, done: any) => {
7072
client.destroy().then(() => {
7173
expect(params.sdkReadinessManager.readinessManager.destroy).toBeCalledTimes(1);
7274
expect(params.storage.destroy).toBeCalledTimes(1);
75+
expect(params.uniqueKeysTracker.stop).toBeCalledTimes(1);
7376

7477
if (params.syncManager) {
7578
expect(params.syncManager.stop).toBeCalledTimes(1);

src/sdkClient/__tests__/sdkClientMethodCS.spec.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ const params = {
4646
settings: settingsWithKey,
4747
telemetryTracker: telemetryTrackerFactory(),
4848
clients: {},
49+
uniqueKeysTracker: { start: jest.fn(), stop: jest.fn() }
4950
};
5051

5152
const invalidAttributes = [
@@ -95,6 +96,7 @@ describe('sdkClientMethodCSFactory', () => {
9596
expect(params.syncManager.stop).toBeCalledTimes(1);
9697
expect(params.syncManager.flush).toBeCalledTimes(1);
9798
expect(params.signalListener.stop).toBeCalledTimes(1);
99+
expect(params.uniqueKeysTracker.stop).toBeCalledTimes(1);
98100
});
99101

100102
});

src/sdkClient/client.ts

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { IMPRESSION, IMPRESSION_QUEUEING } from '../logger/constants';
1111
import { ISdkFactoryContext } from '../sdkFactory/types';
1212
import { isConsumerMode } from '../utils/settingsValidation/mode';
1313
import { Method } from '../sync/submitters/types';
14+
import { ImpressionDecorated } from '../trackers/types';
1415

1516
const treatmentNotReady = { treatment: CONTROL, label: SDK_NOT_READY };
1617

@@ -34,11 +35,11 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
3435
const stopTelemetryTracker = telemetryTracker.trackEval(withConfig ? TREATMENT_WITH_CONFIG : TREATMENT);
3536

3637
const wrapUp = (evaluationResult: IEvaluationResult) => {
37-
const queue: SplitIO.ImpressionDTO[] = [];
38+
const queue: ImpressionDecorated[] = [];
3839
const treatment = processEvaluation(evaluationResult, featureFlagName, key, attributes, withConfig, methodName, queue);
3940
impressionsTracker.track(queue, attributes);
4041

41-
stopTelemetryTracker(queue[0] && queue[0].label);
42+
stopTelemetryTracker(queue[0] && queue[0].imp.label);
4243
return treatment;
4344
};
4445

@@ -59,14 +60,14 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
5960
const stopTelemetryTracker = telemetryTracker.trackEval(withConfig ? TREATMENTS_WITH_CONFIG : TREATMENTS);
6061

6162
const wrapUp = (evaluationResults: Record<string, IEvaluationResult>) => {
62-
const queue: SplitIO.ImpressionDTO[] = [];
63+
const queue: ImpressionDecorated[] = [];
6364
const treatments: Record<string, SplitIO.Treatment | SplitIO.TreatmentWithConfig> = {};
6465
Object.keys(evaluationResults).forEach(featureFlagName => {
6566
treatments[featureFlagName] = processEvaluation(evaluationResults[featureFlagName], featureFlagName, key, attributes, withConfig, methodName, queue);
6667
});
6768
impressionsTracker.track(queue, attributes);
6869

69-
stopTelemetryTracker(queue[0] && queue[0].label);
70+
stopTelemetryTracker(queue[0] && queue[0].imp.label);
7071
return treatments;
7172
};
7273

@@ -87,15 +88,15 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
8788
const stopTelemetryTracker = telemetryTracker.trackEval(method);
8889

8990
const wrapUp = (evaluationResults: Record<string, IEvaluationResult>) => {
90-
const queue: SplitIO.ImpressionDTO[] = [];
91+
const queue: ImpressionDecorated[] = [];
9192
const treatments: Record<string, SplitIO.Treatment | SplitIO.TreatmentWithConfig> = {};
9293
const evaluations = evaluationResults;
9394
Object.keys(evaluations).forEach(featureFlagName => {
9495
treatments[featureFlagName] = processEvaluation(evaluations[featureFlagName], featureFlagName, key, attributes, withConfig, methodName, queue);
9596
});
9697
impressionsTracker.track(queue, attributes);
9798

98-
stopTelemetryTracker(queue[0] && queue[0].label);
99+
stopTelemetryTracker(queue[0] && queue[0].imp.label);
99100
return treatments;
100101
};
101102

@@ -128,24 +129,27 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
128129
attributes: SplitIO.Attributes | undefined,
129130
withConfig: boolean,
130131
invokingMethodName: string,
131-
queue: SplitIO.ImpressionDTO[]
132+
queue: ImpressionDecorated[]
132133
): SplitIO.Treatment | SplitIO.TreatmentWithConfig {
133134
const matchingKey = getMatching(key);
134135
const bucketingKey = getBucketing(key);
135136

136-
const { treatment, label, changeNumber, config = null } = evaluation;
137+
const { treatment, label, changeNumber, config = null, impressionsDisabled } = evaluation;
137138
log.info(IMPRESSION, [featureFlagName, matchingKey, treatment, label]);
138139

139140
if (validateSplitExistence(log, readinessManager, featureFlagName, label, invokingMethodName)) {
140141
log.info(IMPRESSION_QUEUEING);
141142
queue.push({
142-
feature: featureFlagName,
143-
keyName: matchingKey,
144-
treatment,
145-
time: Date.now(),
146-
bucketingKey,
147-
label,
148-
changeNumber: changeNumber as number
143+
imp: {
144+
feature: featureFlagName,
145+
keyName: matchingKey,
146+
treatment,
147+
time: Date.now(),
148+
bucketingKey,
149+
label,
150+
changeNumber: changeNumber as number,
151+
},
152+
disabled: impressionsDisabled
149153
});
150154
}
151155

src/sdkClient/sdkClient.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ export function sdkClientFactory(params: ISdkFactoryContext, isSharedClient?: bo
6161
releaseApiKey(settings.core.authorizationKey);
6262
telemetryTracker.sessionLength();
6363
signalListener && signalListener.stop();
64-
uniqueKeysTracker && uniqueKeysTracker.stop();
64+
uniqueKeysTracker.stop();
6565
}
6666

6767
// Stop background jobs

src/sdkFactory/index.ts

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { strategyDebugFactory } from '../trackers/strategy/strategyDebug';
1313
import { strategyOptimizedFactory } from '../trackers/strategy/strategyOptimized';
1414
import { strategyNoneFactory } from '../trackers/strategy/strategyNone';
1515
import { uniqueKeysTrackerFactory } from '../trackers/uniqueKeysTracker';
16-
import { NONE, OPTIMIZED } from '../utils/constants';
16+
import { DEBUG, OPTIMIZED } from '../utils/constants';
1717

1818
/**
1919
* Modular SDK factory
@@ -59,21 +59,16 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ISDK | SplitIO.IA
5959
const integrationsManager = integrationsManagerFactory && integrationsManagerFactory({ settings, storage, telemetryTracker });
6060

6161
const observer = impressionsObserverFactory();
62-
const uniqueKeysTracker = impressionsMode === NONE ? uniqueKeysTrackerFactory(log, storage.uniqueKeys!, filterAdapterFactory && filterAdapterFactory()) : undefined;
63-
64-
let strategy;
65-
switch (impressionsMode) {
66-
case OPTIMIZED:
67-
strategy = strategyOptimizedFactory(observer, storage.impressionCounts!);
68-
break;
69-
case NONE:
70-
strategy = strategyNoneFactory(storage.impressionCounts!, uniqueKeysTracker!);
71-
break;
72-
default:
73-
strategy = strategyDebugFactory(observer);
74-
}
62+
const uniqueKeysTracker = uniqueKeysTrackerFactory(log, storage.uniqueKeys, filterAdapterFactory && filterAdapterFactory());
63+
64+
const noneStrategy = strategyNoneFactory(storage.impressionCounts, uniqueKeysTracker);
65+
const strategy = impressionsMode === OPTIMIZED ?
66+
strategyOptimizedFactory(observer, storage.impressionCounts) :
67+
impressionsMode === DEBUG ?
68+
strategyDebugFactory(observer) :
69+
noneStrategy;
7570

76-
const impressionsTracker = impressionsTrackerFactory(settings, storage.impressions, strategy, whenInit, integrationsManager, storage.telemetry);
71+
const impressionsTracker = impressionsTrackerFactory(settings, storage.impressions, noneStrategy, strategy, whenInit, integrationsManager, storage.telemetry);
7772
const eventTracker = eventTrackerFactory(settings, storage.events, whenInit, integrationsManager, storage.telemetry);
7873

7974
// splitApi is used by SyncManager and Browser signal listener
@@ -99,7 +94,7 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ISDK | SplitIO.IA
9994
// We will just log and allow for the SDK to end up throwing an SDK_TIMEOUT event for devs to handle.
10095
validateAndTrackApiKey(log, settings.core.authorizationKey);
10196
readiness.init();
102-
uniqueKeysTracker && uniqueKeysTracker.start();
97+
uniqueKeysTracker.start();
10398
syncManager && syncManager.start();
10499
signalListener && signalListener.start();
105100

src/sdkFactory/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ export interface ISdkFactoryContext {
4646
eventTracker: IEventTracker,
4747
telemetryTracker: ITelemetryTracker,
4848
storage: IStorageSync | IStorageAsync,
49-
uniqueKeysTracker?: IUniqueKeysTracker,
49+
uniqueKeysTracker: IUniqueKeysTracker,
5050
signalListener?: ISignalListener
5151
splitApi?: ISplitApi
5252
syncManager?: ISyncManager,

src/sdkManager/__tests__/mocks/output.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,6 @@
88
"on": "\"color\": \"green\""
99
},
1010
"sets": ["set_a"],
11-
"defaultTreatment": "off"
11+
"defaultTreatment": "off",
12+
"impressionsDisabled": false
1213
}

src/sdkManager/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ function objectToView(splitObject: ISplit | null): SplitIO.SplitView | null {
3131
treatments: collectTreatments(splitObject),
3232
configs: splitObject.configurations || {},
3333
sets: splitObject.sets || [],
34-
defaultTreatment: splitObject.defaultTreatment
34+
defaultTreatment: splitObject.defaultTreatment,
35+
impressionsDisabled: splitObject.impressionsDisabled === true
3536
};
3637
}
3738

src/storages/inLocalStorage/index.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { MySegmentsCacheInLocal } from './MySegmentsCacheInLocal';
1010
import { DEFAULT_CACHE_EXPIRATION_IN_MILLIS } from '../../utils/constants/browser';
1111
import { InMemoryStorageCSFactory } from '../inMemory/InMemoryStorageCS';
1212
import { LOG_PREFIX } from './constants';
13-
import { DEBUG, NONE, STORAGE_LOCALSTORAGE } from '../../utils/constants';
13+
import { STORAGE_LOCALSTORAGE } from '../../utils/constants';
1414
import { shouldRecordTelemetry, TelemetryCacheInMemory } from '../inMemory/TelemetryCacheInMemory';
1515
import { UniqueKeysCacheInMemoryCS } from '../inMemory/UniqueKeysCacheInMemoryCS';
1616
import { getMatching } from '../../utils/key';
@@ -34,7 +34,7 @@ export function InLocalStorage(options: InLocalStorageOptions = {}): IStorageSyn
3434
return InMemoryStorageCSFactory(params);
3535
}
3636

37-
const { settings, settings: { log, scheduler: { impressionsQueueSize, eventsQueueSize, }, sync: { impressionsMode } } } = params;
37+
const { settings, settings: { log, scheduler: { impressionsQueueSize, eventsQueueSize } } } = params;
3838
const matchingKey = getMatching(settings.core.key);
3939
const keys = new KeyBuilderCS(prefix, matchingKey);
4040
const expirationTimestamp = Date.now() - DEFAULT_CACHE_EXPIRATION_IN_MILLIS;
@@ -48,10 +48,10 @@ export function InLocalStorage(options: InLocalStorageOptions = {}): IStorageSyn
4848
segments,
4949
largeSegments,
5050
impressions: new ImpressionsCacheInMemory(impressionsQueueSize),
51-
impressionCounts: impressionsMode !== DEBUG ? new ImpressionCountsCacheInMemory() : undefined,
51+
impressionCounts: new ImpressionCountsCacheInMemory(),
5252
events: new EventsCacheInMemory(eventsQueueSize),
5353
telemetry: shouldRecordTelemetry(params) ? new TelemetryCacheInMemory(splits, segments) : undefined,
54-
uniqueKeys: impressionsMode === NONE ? new UniqueKeysCacheInMemoryCS() : undefined,
54+
uniqueKeys: new UniqueKeysCacheInMemoryCS(),
5555

5656
destroy() { },
5757

@@ -66,6 +66,7 @@ export function InLocalStorage(options: InLocalStorageOptions = {}): IStorageSyn
6666
impressionCounts: this.impressionCounts,
6767
events: this.events,
6868
telemetry: this.telemetry,
69+
uniqueKeys: this.uniqueKeys,
6970

7071
destroy() { }
7172
};

src/storages/inMemory/InMemoryStorage.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { ImpressionsCacheInMemory } from './ImpressionsCacheInMemory';
44
import { EventsCacheInMemory } from './EventsCacheInMemory';
55
import { IStorageFactoryParams, IStorageSync } from '../types';
66
import { ImpressionCountsCacheInMemory } from './ImpressionCountsCacheInMemory';
7-
import { DEBUG, LOCALHOST_MODE, NONE, STORAGE_MEMORY } from '../../utils/constants';
7+
import { LOCALHOST_MODE, STORAGE_MEMORY } from '../../utils/constants';
88
import { shouldRecordTelemetry, TelemetryCacheInMemory } from './TelemetryCacheInMemory';
99
import { UniqueKeysCacheInMemory } from './UniqueKeysCacheInMemory';
1010

@@ -14,7 +14,7 @@ import { UniqueKeysCacheInMemory } from './UniqueKeysCacheInMemory';
1414
* @param params - parameters required by EventsCacheSync
1515
*/
1616
export function InMemoryStorageFactory(params: IStorageFactoryParams): IStorageSync {
17-
const { settings: { scheduler: { impressionsQueueSize, eventsQueueSize, }, sync: { impressionsMode, __splitFiltersValidation } } } = params;
17+
const { settings: { scheduler: { impressionsQueueSize, eventsQueueSize, }, sync: { __splitFiltersValidation } } } = params;
1818

1919
const splits = new SplitsCacheInMemory(__splitFiltersValidation);
2020
const segments = new SegmentsCacheInMemory();
@@ -23,10 +23,10 @@ export function InMemoryStorageFactory(params: IStorageFactoryParams): IStorageS
2323
splits,
2424
segments,
2525
impressions: new ImpressionsCacheInMemory(impressionsQueueSize),
26-
impressionCounts: impressionsMode !== DEBUG ? new ImpressionCountsCacheInMemory() : undefined,
26+
impressionCounts: new ImpressionCountsCacheInMemory(),
2727
events: new EventsCacheInMemory(eventsQueueSize),
2828
telemetry: shouldRecordTelemetry(params) ? new TelemetryCacheInMemory(splits, segments) : undefined,
29-
uniqueKeys: impressionsMode === NONE ? new UniqueKeysCacheInMemory() : undefined,
29+
uniqueKeys: new UniqueKeysCacheInMemory(),
3030

3131
destroy() { }
3232
};
@@ -37,8 +37,8 @@ export function InMemoryStorageFactory(params: IStorageFactoryParams): IStorageS
3737
const noopTrack = () => true;
3838
storage.impressions.track = noopTrack;
3939
storage.events.track = noopTrack;
40-
if (storage.impressionCounts) storage.impressionCounts.track = noopTrack;
41-
if (storage.uniqueKeys) storage.uniqueKeys.track = noopTrack;
40+
storage.impressionCounts.track = noopTrack;
41+
storage.uniqueKeys.track = noopTrack;
4242
}
4343

4444
return storage;

0 commit comments

Comments
 (0)