@splitsoftware/splitio-commons 1.2.1-rc.1 → 1.2.1-rc.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cjs/evaluator/Engine.js +6 -6
- package/cjs/evaluator/combiners/and.js +1 -1
- package/cjs/evaluator/combiners/ifelseif.js +2 -2
- package/cjs/evaluator/condition/engineUtils.js +2 -2
- package/cjs/evaluator/condition/index.js +4 -4
- package/cjs/evaluator/index.js +5 -5
- package/cjs/evaluator/matchers/cont_all.js +1 -1
- package/cjs/evaluator/matchers/cont_any.js +1 -1
- package/cjs/evaluator/matchers/cont_str.js +1 -1
- package/cjs/evaluator/matchers/dependency.js +1 -1
- package/cjs/evaluator/matchers/eq_set.js +1 -1
- package/cjs/evaluator/matchers/ew.js +1 -1
- package/cjs/evaluator/matchers/part_of.js +1 -1
- package/cjs/evaluator/matchers/segment.js +1 -1
- package/cjs/evaluator/matchers/sw.js +1 -1
- package/cjs/evaluator/matchers/whitelist.js +1 -1
- package/cjs/evaluator/matchersTransform/index.js +12 -12
- package/cjs/evaluator/parser/index.js +6 -6
- package/cjs/evaluator/treatments/index.js +1 -1
- package/cjs/evaluator/value/index.js +1 -1
- package/cjs/evaluator/value/sanitize.js +4 -4
- package/cjs/integrations/browser.js +3 -3
- package/cjs/integrations/ga/GaToSplit.js +14 -14
- package/cjs/integrations/ga/GoogleAnalyticsToSplit.js +5 -3
- package/cjs/integrations/ga/SplitToGa.js +1 -1
- package/cjs/integrations/ga/SplitToGoogleAnalytics.js +4 -2
- package/cjs/listeners/browser.js +15 -11
- package/cjs/listeners/node.js +1 -1
- package/cjs/logger/constants.js +8 -3
- package/cjs/logger/index.js +2 -2
- package/cjs/logger/messages/debug.js +4 -4
- package/cjs/logger/messages/error.js +5 -4
- package/cjs/logger/messages/info.js +7 -5
- package/cjs/logger/messages/warn.js +1 -1
- package/cjs/logger/sdkLogger.js +1 -1
- package/cjs/readiness/readinessManager.js +2 -2
- package/cjs/readiness/sdkReadinessManager.js +4 -4
- package/cjs/sdkClient/client.js +12 -11
- package/cjs/sdkClient/clientAttributesDecoration.js +4 -4
- package/cjs/sdkClient/clientCS.js +3 -3
- package/cjs/sdkClient/clientInputValidation.js +20 -22
- package/cjs/sdkClient/sdkClient.js +3 -6
- package/cjs/sdkClient/sdkClientMethod.js +1 -1
- package/cjs/sdkClient/sdkClientMethodCS.js +6 -10
- package/cjs/sdkClient/sdkClientMethodCSWithTT.js +7 -15
- package/cjs/sdkFactory/index.js +14 -12
- package/cjs/sdkFactory/userConsentProps.js +37 -0
- package/cjs/sdkManager/index.js +11 -11
- package/cjs/services/splitApi.js +3 -2
- package/cjs/services/splitHttpClient.js +2 -2
- package/cjs/storages/KeyBuilder.js +2 -6
- package/cjs/storages/KeyBuilderCS.js +13 -3
- package/cjs/storages/KeyBuilderSS.js +1 -1
- package/cjs/storages/findLatencyIndex.js +1 -1
- package/cjs/storages/inLocalStorage/MySegmentsCacheInLocal.js +24 -4
- package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +12 -12
- package/cjs/storages/inLocalStorage/index.js +4 -4
- package/cjs/storages/inMemory/AttributesCacheInMemory.js +1 -1
- package/cjs/storages/inMemory/ImpressionCountsCacheInMemory.js +1 -1
- package/cjs/storages/inMemory/ImpressionsCacheInMemory.js +15 -1
- package/cjs/storages/inMemory/InMemoryStorage.js +1 -1
- package/cjs/storages/inMemory/InMemoryStorageCS.js +1 -1
- package/cjs/storages/inMemory/LatenciesCacheInMemory.js +1 -1
- package/cjs/storages/inMemory/MySegmentsCacheInMemory.js +1 -1
- package/cjs/storages/inMemory/SegmentsCacheInMemory.js +2 -2
- package/cjs/storages/inMemory/SplitsCacheInMemory.js +5 -5
- package/cjs/storages/inRedis/LatenciesCacheInRedis.js +1 -1
- package/cjs/storages/inRedis/RedisAdapter.js +24 -11
- package/cjs/storages/inRedis/SegmentsCacheInRedis.js +1 -1
- package/cjs/storages/inRedis/SplitsCacheInRedis.js +3 -3
- package/cjs/storages/inRedis/index.js +1 -1
- package/cjs/storages/pluggable/SegmentsCachePluggable.js +1 -1
- package/cjs/storages/pluggable/SplitsCachePluggable.js +3 -3
- package/cjs/storages/pluggable/inMemoryWrapper.js +4 -4
- package/cjs/storages/pluggable/index.js +6 -6
- package/cjs/sync/offline/LocalhostFromFile.js +1 -1
- package/cjs/sync/offline/LocalhostFromObject.js +1 -1
- package/cjs/sync/offline/splitsParser/parseCondition.js +1 -1
- package/cjs/sync/offline/splitsParser/splitsParserFromFile.js +13 -13
- package/cjs/sync/offline/splitsParser/splitsParserFromSettings.js +3 -3
- package/cjs/sync/offline/syncManagerOffline.js +1 -1
- package/cjs/sync/offline/syncTasks/fromObjectSyncTask.js +2 -2
- package/cjs/sync/polling/fetchers/segmentChangesFetcher.js +1 -1
- package/cjs/sync/polling/pollingManagerCS.js +6 -6
- package/cjs/sync/polling/pollingManagerSS.js +3 -3
- package/cjs/sync/polling/syncTasks/mySegmentsSyncTask.js +1 -1
- package/cjs/sync/polling/syncTasks/segmentsSyncTask.js +1 -1
- package/cjs/sync/polling/syncTasks/splitsSyncTask.js +1 -1
- package/cjs/sync/polling/updaters/mySegmentsUpdater.js +1 -1
- package/cjs/sync/polling/updaters/segmentChangesUpdater.js +2 -2
- package/cjs/sync/polling/updaters/splitChangesUpdater.js +2 -2
- package/cjs/sync/streaming/AuthClient/index.js +3 -3
- package/cjs/sync/streaming/SSEClient/index.js +2 -1
- package/cjs/sync/streaming/SSEHandler/NotificationParser.js +1 -1
- package/cjs/sync/streaming/SSEHandler/index.js +3 -3
- package/cjs/sync/streaming/mySegmentsV2utils.js +1 -1
- package/cjs/sync/streaming/pushManager.js +24 -17
- package/cjs/sync/submitters/eventsSyncTask.js +17 -5
- package/cjs/sync/submitters/impressionCountsSyncTask.js +1 -1
- package/cjs/sync/submitters/impressionsSyncTask.js +15 -3
- package/cjs/sync/submitters/metricsSyncTask.js +3 -3
- package/cjs/sync/submitters/submitterManager.js +4 -4
- package/cjs/sync/submitters/submitterSyncTask.js +1 -1
- package/cjs/sync/syncManagerOnline.js +12 -8
- package/cjs/trackers/eventTracker.js +11 -4
- package/cjs/trackers/impressionObserver/impressionObserverCS.js +1 -1
- package/cjs/trackers/impressionObserver/impressionObserverSS.js +1 -1
- package/cjs/trackers/impressionObserver/utils.js +8 -1
- package/cjs/trackers/impressionsTracker.js +9 -8
- package/cjs/utils/MinEvents.js +2 -1
- package/cjs/utils/consent.js +10 -0
- package/cjs/utils/constants/index.js +5 -1
- package/cjs/utils/inputValidation/apiKey.js +1 -1
- package/cjs/utils/inputValidation/attribute.js +4 -4
- package/cjs/utils/inputValidation/attributes.js +2 -2
- package/cjs/utils/inputValidation/event.js +1 -1
- package/cjs/utils/inputValidation/eventProperties.js +5 -5
- package/cjs/utils/inputValidation/eventValue.js +1 -1
- package/cjs/utils/inputValidation/key.js +6 -5
- package/cjs/utils/inputValidation/preloadedData.js +8 -8
- package/cjs/utils/inputValidation/split.js +1 -1
- package/cjs/utils/inputValidation/splits.js +2 -2
- package/cjs/utils/inputValidation/trafficType.js +1 -1
- package/cjs/utils/inputValidation/trafficTypeExistance.js +1 -1
- package/cjs/utils/jwt/index.js +1 -1
- package/cjs/utils/key/index.js +3 -3
- package/cjs/utils/lang/index.js +13 -4
- package/cjs/utils/lang/maps.js +16 -2
- package/cjs/utils/murmur3/common.js +1 -1
- package/cjs/utils/murmur3/murmur3.js +10 -10
- package/cjs/utils/murmur3/murmur3_128.js +1 -1
- package/cjs/utils/murmur3/murmur3_128_x86.js +37 -37
- package/cjs/utils/murmur3/murmur3_64.js +1 -1
- package/cjs/utils/settingsValidation/consent.js +16 -0
- package/cjs/utils/settingsValidation/impressionsMode.js +6 -6
- package/cjs/utils/settingsValidation/index.js +32 -14
- package/cjs/utils/settingsValidation/integrations/configurable.js +1 -1
- package/cjs/utils/settingsValidation/integrations/pluggable.js +1 -1
- package/cjs/utils/settingsValidation/localhost/builtin.js +2 -2
- package/cjs/utils/settingsValidation/logger/builtinLogger.js +3 -3
- package/cjs/utils/settingsValidation/logger/commons.js +1 -1
- package/cjs/utils/settingsValidation/logger/pluggableLogger.js +1 -1
- package/cjs/utils/settingsValidation/runtime.js +11 -0
- package/cjs/utils/settingsValidation/splitFilters.js +1 -1
- package/cjs/utils/settingsValidation/storage/storageCS.js +1 -1
- package/cjs/utils/timeTracker/index.js +3 -3
- package/esm/integrations/ga/GoogleAnalyticsToSplit.js +4 -2
- package/esm/integrations/ga/SplitToGoogleAnalytics.js +4 -2
- package/esm/listeners/browser.js +14 -10
- package/esm/logger/constants.js +5 -2
- package/esm/logger/messages/debug.js +3 -3
- package/esm/logger/messages/error.js +4 -3
- package/esm/logger/messages/info.js +6 -4
- package/esm/sdkClient/client.js +3 -2
- package/esm/sdkClient/clientCS.js +1 -1
- package/esm/sdkClient/clientInputValidation.js +6 -8
- package/esm/sdkClient/sdkClient.js +1 -4
- package/esm/sdkClient/sdkClientMethodCS.js +1 -5
- package/esm/sdkClient/sdkClientMethodCSWithTT.js +1 -9
- package/esm/sdkFactory/index.js +8 -6
- package/esm/sdkFactory/userConsentProps.js +33 -0
- package/esm/services/splitApi.js +2 -1
- package/esm/storages/KeyBuilder.js +2 -6
- package/esm/storages/KeyBuilderCS.js +11 -1
- package/esm/storages/inLocalStorage/MySegmentsCacheInLocal.js +23 -3
- package/esm/storages/inLocalStorage/index.js +1 -1
- package/esm/storages/inMemory/ImpressionsCacheInMemory.js +15 -1
- package/esm/storages/inMemory/InMemoryStorage.js +1 -1
- package/esm/storages/inMemory/InMemoryStorageCS.js +1 -1
- package/esm/storages/inRedis/RedisAdapter.js +15 -2
- package/esm/storages/pluggable/index.js +2 -2
- package/esm/sync/offline/splitsParser/splitsParserFromFile.js +1 -1
- package/esm/sync/polling/fetchers/segmentChangesFetcher.js +2 -2
- package/esm/sync/streaming/SSEClient/index.js +2 -1
- package/esm/sync/streaming/pushManager.js +9 -2
- package/esm/sync/submitters/eventsSyncTask.js +18 -6
- package/esm/sync/submitters/impressionsSyncTask.js +13 -1
- package/esm/sync/syncManagerOnline.js +11 -7
- package/esm/trackers/eventTracker.js +8 -1
- package/esm/trackers/impressionObserver/utils.js +7 -1
- package/esm/trackers/impressionsTracker.js +6 -5
- package/esm/utils/consent.js +6 -0
- package/esm/utils/constants/index.js +4 -0
- package/esm/utils/inputValidation/attributes.js +1 -1
- package/esm/utils/inputValidation/key.js +2 -1
- package/esm/utils/lang/index.js +12 -4
- package/esm/utils/lang/maps.js +14 -1
- package/esm/utils/settingsValidation/consent.js +12 -0
- package/esm/utils/settingsValidation/impressionsMode.js +7 -7
- package/esm/utils/settingsValidation/index.js +28 -10
- package/esm/utils/settingsValidation/runtime.js +7 -0
- package/package.json +8 -8
- package/src/evaluator/parser/index.ts +1 -1
- package/src/evaluator/types.ts +2 -2
- package/src/evaluator/value/index.ts +2 -2
- package/src/evaluator/value/sanitize.ts +2 -2
- package/src/integrations/ga/GoogleAnalyticsToSplit.ts +7 -4
- package/src/integrations/ga/SplitToGoogleAnalytics.ts +7 -4
- package/src/integrations/pluggable.ts +2 -2
- package/src/integrations/types.ts +5 -0
- package/src/listeners/browser.ts +13 -9
- package/src/logger/constants.ts +5 -2
- package/src/logger/messages/debug.ts +3 -3
- package/src/logger/messages/error.ts +4 -3
- package/src/logger/messages/info.ts +6 -4
- package/src/logger/types.ts +4 -0
- package/src/sdkClient/client.ts +3 -2
- package/src/sdkClient/clientCS.ts +1 -1
- package/src/sdkClient/clientInputValidation.ts +8 -7
- package/src/sdkClient/sdkClient.ts +2 -5
- package/src/sdkClient/sdkClientMethodCS.ts +1 -6
- package/src/sdkClient/sdkClientMethodCSWithTT.ts +2 -11
- package/src/sdkFactory/index.ts +9 -7
- package/src/sdkFactory/types.ts +2 -1
- package/src/sdkFactory/userConsentProps.ts +40 -0
- package/src/storages/KeyBuilder.ts +2 -6
- package/src/storages/KeyBuilderCS.ts +13 -1
- package/src/storages/inLocalStorage/MySegmentsCacheInLocal.ts +23 -3
- package/src/storages/inLocalStorage/index.ts +1 -1
- package/src/storages/inMemory/ImpressionsCacheInMemory.ts +22 -1
- package/src/storages/inMemory/InMemoryStorage.ts +1 -1
- package/src/storages/inMemory/InMemoryStorageCS.ts +1 -1
- package/src/storages/inRedis/RedisAdapter.ts +8 -2
- package/src/storages/pluggable/index.ts +2 -2
- package/src/storages/types.ts +6 -2
- package/src/sync/offline/splitsParser/splitsParserFromFile.ts +1 -1
- package/src/sync/streaming/SSEClient/index.ts +2 -1
- package/src/sync/streaming/pushManager.ts +11 -2
- package/src/sync/submitters/eventsSyncTask.ts +19 -6
- package/src/sync/submitters/impressionsSyncTask.ts +16 -1
- package/src/sync/syncManagerOnline.ts +13 -7
- package/src/sync/types.ts +4 -1
- package/src/trackers/eventTracker.ts +11 -3
- package/src/trackers/impressionObserver/utils.ts +8 -1
- package/src/trackers/impressionsTracker.ts +7 -8
- package/src/types.ts +22 -1
- package/src/utils/consent.ts +8 -0
- package/src/utils/constants/index.ts +5 -0
- package/src/utils/inputValidation/attributes.ts +1 -2
- package/src/utils/lang/index.ts +15 -4
- package/src/utils/lang/maps.ts +15 -1
- package/src/utils/settingsValidation/consent.ts +16 -0
- package/src/utils/settingsValidation/impressionsMode.ts +8 -8
- package/src/utils/settingsValidation/index.ts +29 -10
- package/src/utils/settingsValidation/runtime.ts +9 -0
- package/src/utils/settingsValidation/types.ts +12 -6
- package/types/evaluator/types.d.ts +2 -2
- package/types/evaluator/value/index.d.ts +1 -1
- package/types/evaluator/value/sanitize.d.ts +1 -1
- package/types/integrations/ga/GoogleAnalyticsToSplit.d.ts +2 -2
- package/types/integrations/ga/SplitToGoogleAnalytics.d.ts +2 -3
- package/types/integrations/types.d.ts +4 -0
- package/types/logger/constants.d.ts +5 -2
- package/types/logger/types.d.ts +4 -0
- package/types/sdkClient/clientAttributesDecoration.d.ts +1 -1
- package/types/sdkClient/clientInputValidation.d.ts +2 -3
- package/types/sdkFactory/types.d.ts +1 -1
- package/types/sdkFactory/userConsentProps.d.ts +6 -0
- package/types/storages/KeyBuilderCS.d.ts +2 -0
- package/types/storages/inMemory/ImpressionsCacheInMemory.d.ts +9 -0
- package/types/storages/inRedis/RedisAdapter.d.ts +1 -1
- package/types/storages/types.d.ts +3 -1
- package/types/sync/types.d.ts +3 -0
- package/types/trackers/eventTracker.d.ts +2 -2
- package/types/trackers/impressionObserver/utils.d.ts +4 -0
- package/types/trackers/impressionsTracker.d.ts +2 -3
- package/types/types.d.ts +22 -1
- package/types/utils/consent.d.ts +2 -0
- package/types/utils/constants/index.d.ts +3 -0
- package/types/utils/lang/index.d.ts +6 -1
- package/types/utils/lang/maps.d.ts +7 -0
- package/types/utils/settingsValidation/consent.d.ts +6 -0
- package/types/utils/settingsValidation/impressionsMode.d.ts +1 -1
- package/types/utils/settingsValidation/runtime/browser.d.ts +2 -0
- package/types/utils/settingsValidation/runtime/node.d.ts +2 -0
- package/types/utils/settingsValidation/runtime.d.ts +2 -0
- package/types/utils/settingsValidation/types.d.ts +12 -6
- package/types/utils/settingsValidation/userConsent.d.ts +5 -0
package/src/sdkFactory/types.ts
CHANGED
|
@@ -64,10 +64,11 @@ export interface ISdkFactoryParams {
|
|
|
64
64
|
serviceApi: ISplitApi | undefined) => ISignalListener, // Used by BrowserSignalListener
|
|
65
65
|
|
|
66
66
|
// @TODO review impressionListener and integrations interfaces. What about handling impressionListener as an integration ?
|
|
67
|
-
impressionListener?: SplitIO.IImpressionListener,
|
|
68
67
|
integrationsManagerFactory?: (params: IIntegrationFactoryParams) => IIntegrationManager | undefined,
|
|
69
68
|
|
|
70
69
|
// Impression observer factory. If provided, will be used for impressions dedupe
|
|
71
70
|
impressionsObserverFactory?: () => IImpressionObserver
|
|
72
71
|
|
|
72
|
+
// Optional function to assign additional properties to the factory instance
|
|
73
|
+
extraProps?: (settings: ISettings, syncManager?: ISyncManager) => object
|
|
73
74
|
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { ERROR_NOT_BOOLEAN, USER_CONSENT_UPDATED, USER_CONSENT_NOT_UPDATED } from '../logger/constants';
|
|
2
|
+
import { ISyncManager } from '../sync/types';
|
|
3
|
+
import { ISettings } from '../types';
|
|
4
|
+
import { CONSENT_GRANTED, CONSENT_DECLINED } from '../utils/constants';
|
|
5
|
+
import { isBoolean } from '../utils/lang';
|
|
6
|
+
|
|
7
|
+
// Extend client-side factory instances with user consent getter/setter
|
|
8
|
+
export function userConsentProps(settings: ISettings, syncManager?: ISyncManager) {
|
|
9
|
+
|
|
10
|
+
const log = settings.log;
|
|
11
|
+
|
|
12
|
+
return {
|
|
13
|
+
setUserConsent(consent: unknown) {
|
|
14
|
+
// validate input param
|
|
15
|
+
if (!isBoolean(consent)) {
|
|
16
|
+
log.warn(ERROR_NOT_BOOLEAN, ['setUserConsent']);
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const newConsentStatus = consent ? CONSENT_GRANTED : CONSENT_DECLINED;
|
|
21
|
+
|
|
22
|
+
if (settings.userConsent !== newConsentStatus) { // @ts-ignore, modify readonly prop
|
|
23
|
+
settings.userConsent = newConsentStatus;
|
|
24
|
+
|
|
25
|
+
if (consent) syncManager?.submitter?.start(); // resumes submitters if transitioning to GRANTED
|
|
26
|
+
else syncManager?.submitter?.stop(); // pauses submitters if transitioning to DECLINED
|
|
27
|
+
|
|
28
|
+
log.info(USER_CONSENT_UPDATED, [settings.userConsent, newConsentStatus]);
|
|
29
|
+
} else {
|
|
30
|
+
log.info(USER_CONSENT_NOT_UPDATED, [newConsentStatus]);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return true;
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
getUserConsent() {
|
|
37
|
+
return settings.userConsent;
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
}
|
|
@@ -1,15 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { startsWith } from '../utils/lang';
|
|
2
2
|
|
|
3
3
|
const everythingAtTheEnd = /[^.]+$/;
|
|
4
4
|
|
|
5
5
|
const DEFAULT_PREFIX = 'SPLITIO';
|
|
6
6
|
|
|
7
7
|
export function validatePrefix(prefix: unknown) {
|
|
8
|
-
return prefix
|
|
9
|
-
endsWith(prefix, '.' + DEFAULT_PREFIX) ?
|
|
10
|
-
prefix : // suffix already appended
|
|
11
|
-
prefix + '.' + DEFAULT_PREFIX : // append suffix
|
|
12
|
-
DEFAULT_PREFIX; // use default prefix if none is provided
|
|
8
|
+
return prefix ? prefix + '.SPLITIO' : 'SPLITIO';
|
|
13
9
|
}
|
|
14
10
|
|
|
15
11
|
export class KeyBuilder {
|
|
@@ -16,10 +16,22 @@ export class KeyBuilderCS extends KeyBuilder {
|
|
|
16
16
|
* @override
|
|
17
17
|
*/
|
|
18
18
|
buildSegmentNameKey(segmentName: string) {
|
|
19
|
-
return `${this.
|
|
19
|
+
return `${this.prefix}.${this.matchingKey}.segment.${segmentName}`;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
extractSegmentName(builtSegmentKeyName: string) {
|
|
23
|
+
const prefix = `${this.prefix}.${this.matchingKey}.segment.`;
|
|
24
|
+
|
|
25
|
+
if (startsWith(builtSegmentKeyName, prefix))
|
|
26
|
+
return builtSegmentKeyName.substr(prefix.length);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// @BREAKING: The key used to start with the matching key instead of the prefix, this was changed on version 10.17.3
|
|
30
|
+
buildOldSegmentNameKey(segmentName: string) {
|
|
31
|
+
return `${this.matchingKey}.${this.prefix}.segment.${segmentName}`;
|
|
32
|
+
}
|
|
33
|
+
// @BREAKING: The key used to start with the matching key instead of the prefix, this was changed on version 10.17.3
|
|
34
|
+
extractOldSegmentKey(builtSegmentKeyName: string) {
|
|
23
35
|
const prefix = `${this.matchingKey}.${this.prefix}.segment.`;
|
|
24
36
|
|
|
25
37
|
if (startsWith(builtSegmentKeyName, prefix))
|
|
@@ -67,9 +67,29 @@ export class MySegmentsCacheInLocal extends AbstractSegmentsCacheSync {
|
|
|
67
67
|
|
|
68
68
|
// Scan current values from localStorage
|
|
69
69
|
const storedSegmentNames = Object.keys(localStorage).reduce((accum, key) => {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
if (
|
|
70
|
+
let segmentName = this.keys.extractSegmentName(key);
|
|
71
|
+
|
|
72
|
+
if (segmentName) {
|
|
73
|
+
accum.push(segmentName);
|
|
74
|
+
} else {
|
|
75
|
+
// @BREAKING: This is only to clean up "old" keys. Remove this whole else code block.
|
|
76
|
+
segmentName = this.keys.extractOldSegmentKey(key);
|
|
77
|
+
|
|
78
|
+
if (segmentName) { // this was an old segment key, let's clean up.
|
|
79
|
+
const newSegmentKey = this.keys.buildSegmentNameKey(segmentName);
|
|
80
|
+
try {
|
|
81
|
+
// If the new format key is not there, create it.
|
|
82
|
+
if (!localStorage.getItem(newSegmentKey) && names.indexOf(segmentName) > -1) {
|
|
83
|
+
localStorage.setItem(newSegmentKey, DEFINED);
|
|
84
|
+
// we are migrating a segment, let's track it.
|
|
85
|
+
accum.push(segmentName);
|
|
86
|
+
}
|
|
87
|
+
localStorage.removeItem(key); // we migrated the current key, let's delete it.
|
|
88
|
+
} catch (e) {
|
|
89
|
+
this.log.error(e);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
73
93
|
|
|
74
94
|
return accum;
|
|
75
95
|
}, [] as string[]);
|
|
@@ -40,7 +40,7 @@ export function InLocalStorage(options: InLocalStorageOptions = {}): IStorageSyn
|
|
|
40
40
|
return {
|
|
41
41
|
splits: new SplitsCacheInLocal(log, keys, expirationTimestamp, params.splitFiltersValidation),
|
|
42
42
|
segments: new MySegmentsCacheInLocal(log, keys),
|
|
43
|
-
impressions: new ImpressionsCacheInMemory(),
|
|
43
|
+
impressions: new ImpressionsCacheInMemory(params.impressionsQueueSize),
|
|
44
44
|
impressionCounts: params.optimize ? new ImpressionCountsCacheInMemory() : undefined,
|
|
45
45
|
events: new EventsCacheInMemory(params.eventsQueueSize),
|
|
46
46
|
|
|
@@ -3,13 +3,34 @@ import { ImpressionDTO } from '../../types';
|
|
|
3
3
|
|
|
4
4
|
export class ImpressionsCacheInMemory implements IImpressionsCacheSync {
|
|
5
5
|
|
|
6
|
-
private
|
|
6
|
+
private onFullQueue?: () => void;
|
|
7
|
+
private readonly maxQueue: number;
|
|
8
|
+
private queue: ImpressionDTO[];
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
*
|
|
12
|
+
* @param impressionsQueueSize number of queued impressions to call onFullQueueCb.
|
|
13
|
+
* Default value is 0, that means no maximum value, in case we want to avoid this being triggered.
|
|
14
|
+
*/
|
|
15
|
+
constructor(impressionsQueueSize: number = 0) {
|
|
16
|
+
this.maxQueue = impressionsQueueSize;
|
|
17
|
+
this.queue = [];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
setOnFullQueueCb(cb: () => void) {
|
|
21
|
+
this.onFullQueue = cb;
|
|
22
|
+
}
|
|
7
23
|
|
|
8
24
|
/**
|
|
9
25
|
* Store impressions in sequential order
|
|
10
26
|
*/
|
|
11
27
|
track(data: ImpressionDTO[]) {
|
|
12
28
|
this.queue.push(...data);
|
|
29
|
+
|
|
30
|
+
// Check if the cache queue is full and we need to flush it.
|
|
31
|
+
if (this.maxQueue > 0 && this.queue.length >= this.maxQueue && this.onFullQueue) {
|
|
32
|
+
this.onFullQueue();
|
|
33
|
+
}
|
|
13
34
|
}
|
|
14
35
|
|
|
15
36
|
/**
|
|
@@ -16,7 +16,7 @@ export function InMemoryStorageFactory(params: IStorageFactoryParams): IStorageS
|
|
|
16
16
|
return {
|
|
17
17
|
splits: new SplitsCacheInMemory(),
|
|
18
18
|
segments: new SegmentsCacheInMemory(),
|
|
19
|
-
impressions: new ImpressionsCacheInMemory(),
|
|
19
|
+
impressions: new ImpressionsCacheInMemory(params.impressionsQueueSize),
|
|
20
20
|
impressionCounts: params.optimize ? new ImpressionCountsCacheInMemory() : undefined,
|
|
21
21
|
events: new EventsCacheInMemory(params.eventsQueueSize),
|
|
22
22
|
|
|
@@ -16,7 +16,7 @@ export function InMemoryStorageCSFactory(params: IStorageFactoryParams): IStorag
|
|
|
16
16
|
return {
|
|
17
17
|
splits: new SplitsCacheInMemory(),
|
|
18
18
|
segments: new MySegmentsCacheInMemory(),
|
|
19
|
-
impressions: new ImpressionsCacheInMemory(),
|
|
19
|
+
impressions: new ImpressionsCacheInMemory(params.impressionsQueueSize),
|
|
20
20
|
impressionCounts: params.optimize ? new ImpressionCountsCacheInMemory() : undefined,
|
|
21
21
|
events: new EventsCacheInMemory(params.eventsQueueSize),
|
|
22
22
|
|
|
@@ -164,6 +164,12 @@ export class RedisAdapter extends ioredis {
|
|
|
164
164
|
} else { // If it IS the string URL, that'll be the first param for ioredis.
|
|
165
165
|
result.unshift(options.url);
|
|
166
166
|
}
|
|
167
|
+
if (options.connectionTimeout) {
|
|
168
|
+
merge(opts, { connectTimeout: options.connectionTimeout });
|
|
169
|
+
}
|
|
170
|
+
if (options.tls) {
|
|
171
|
+
merge(opts, { tls: options.tls });
|
|
172
|
+
}
|
|
167
173
|
|
|
168
174
|
return result;
|
|
169
175
|
}
|
|
@@ -171,9 +177,9 @@ export class RedisAdapter extends ioredis {
|
|
|
171
177
|
/**
|
|
172
178
|
* Parses the options into what we care about.
|
|
173
179
|
*/
|
|
174
|
-
static _defineOptions({ connectionTimeout, operationTimeout, url, host, port, db, pass }: Record<string, any>) {
|
|
180
|
+
static _defineOptions({ connectionTimeout, operationTimeout, url, host, port, db, pass, tls }: Record<string, any>) {
|
|
175
181
|
const parsedOptions = {
|
|
176
|
-
connectionTimeout, operationTimeout, url, host, port, db, pass
|
|
182
|
+
connectionTimeout, operationTimeout, url, host, port, db, pass, tls
|
|
177
183
|
};
|
|
178
184
|
|
|
179
185
|
return merge({}, DEFAULT_OPTIONS, parsedOptions);
|
|
@@ -63,7 +63,7 @@ export function PluggableStorage(options: PluggableStorageOptions): IStorageAsyn
|
|
|
63
63
|
|
|
64
64
|
const prefix = validatePrefix(options.prefix);
|
|
65
65
|
|
|
66
|
-
function PluggableStorageFactory({ log, metadata, onReadyCb, mode, eventsQueueSize, optimize }: IStorageFactoryParams): IStorageAsync {
|
|
66
|
+
function PluggableStorageFactory({ log, metadata, onReadyCb, mode, eventsQueueSize, impressionsQueueSize, optimize }: IStorageFactoryParams): IStorageAsync {
|
|
67
67
|
const keys = new KeyBuilderSS(prefix, metadata);
|
|
68
68
|
const wrapper = wrapperAdapter(log, options.wrapper);
|
|
69
69
|
const isPartialConsumer = mode === CONSUMER_PARTIAL_MODE;
|
|
@@ -74,7 +74,7 @@ export function PluggableStorage(options: PluggableStorageOptions): IStorageAsyn
|
|
|
74
74
|
return {
|
|
75
75
|
splits: new SplitsCachePluggable(log, keys, wrapper),
|
|
76
76
|
segments: new SegmentsCachePluggable(log, keys, wrapper),
|
|
77
|
-
impressions: isPartialConsumer ? new ImpressionsCacheInMemory() : new ImpressionsCachePluggable(log, keys.buildImpressionsKey(), wrapper, metadata),
|
|
77
|
+
impressions: isPartialConsumer ? new ImpressionsCacheInMemory(impressionsQueueSize) : new ImpressionsCachePluggable(log, keys.buildImpressionsKey(), wrapper, metadata),
|
|
78
78
|
impressionCounts: optimize ? new ImpressionCountsCacheInMemory() : undefined,
|
|
79
79
|
events: isPartialConsumer ? promisifyEventsTrack(new EventsCacheInMemory(eventsQueueSize)) : new EventsCachePluggable(log, keys.buildEventsKey(), wrapper, metadata),
|
|
80
80
|
// @TODO add telemetry cache when required
|
package/src/storages/types.ts
CHANGED
|
@@ -298,7 +298,7 @@ export interface IRecorderCacheProducerSync<T> {
|
|
|
298
298
|
// @TODO names are inconsistent with spec
|
|
299
299
|
/* Checks if cache is empty. Returns true if the cache was just created or cleared */
|
|
300
300
|
isEmpty(): boolean
|
|
301
|
-
/*
|
|
301
|
+
/* Clears cache data */
|
|
302
302
|
clear(): void
|
|
303
303
|
/* Gets cache data */
|
|
304
304
|
state(): T
|
|
@@ -307,10 +307,13 @@ export interface IRecorderCacheProducerSync<T> {
|
|
|
307
307
|
|
|
308
308
|
export interface IImpressionsCacheSync extends IImpressionsCacheBase, IRecorderCacheProducerSync<ImpressionDTO[]> {
|
|
309
309
|
track(data: ImpressionDTO[]): void
|
|
310
|
+
/* Registers callback for full queue */
|
|
311
|
+
setOnFullQueueCb(cb: () => void): void
|
|
310
312
|
}
|
|
311
313
|
|
|
312
314
|
export interface IEventsCacheSync extends IEventsCacheBase, IRecorderCacheProducerSync<SplitIO.EventData[]> {
|
|
313
315
|
track(data: SplitIO.EventData, size?: number): boolean
|
|
316
|
+
/* Registers callback for full queue */
|
|
314
317
|
setOnFullQueueCb(cb: () => void): void
|
|
315
318
|
}
|
|
316
319
|
|
|
@@ -423,6 +426,7 @@ export type DataLoader = (storage: IStorageSync, matchingKey: string) => void
|
|
|
423
426
|
|
|
424
427
|
export interface IStorageFactoryParams {
|
|
425
428
|
log: ILogger,
|
|
429
|
+
impressionsQueueSize?: number,
|
|
426
430
|
eventsQueueSize?: number,
|
|
427
431
|
optimize?: boolean /* whether create the `impressionCounts` cache (OPTIMIZED impression mode) or not (DEBUG impression mode) */,
|
|
428
432
|
|
|
@@ -443,7 +447,7 @@ export interface IStorageFactoryParams {
|
|
|
443
447
|
export type StorageType = 'MEMORY' | 'LOCALSTORAGE' | 'REDIS' | 'PLUGGABLE';
|
|
444
448
|
|
|
445
449
|
export type IStorageSyncFactory = {
|
|
446
|
-
type: StorageType,
|
|
450
|
+
readonly type: StorageType,
|
|
447
451
|
(params: IStorageFactoryParams): IStorageSync
|
|
448
452
|
}
|
|
449
453
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { IEventSourceConstructor } from '../../../services/types';
|
|
2
2
|
import { ISettings } from '../../../types';
|
|
3
|
+
import { isString } from '../../../utils/lang';
|
|
3
4
|
import { IAuthTokenPushEnabled } from '../AuthClient/types';
|
|
4
5
|
import { ISSEClient, ISseEventHandler } from './types';
|
|
5
6
|
|
|
@@ -15,7 +16,7 @@ const CONTROL_CHANNEL_REGEX = /^control_/;
|
|
|
15
16
|
*/
|
|
16
17
|
function buildSSEHeaders(settings: ISettings) {
|
|
17
18
|
const headers: Record<string, string> = {
|
|
18
|
-
SplitSDKClientKey: settings.core.authorizationKey.slice(-4),
|
|
19
|
+
SplitSDKClientKey: isString(settings.core.authorizationKey) ? settings.core.authorizationKey.slice(-4) : '',
|
|
19
20
|
SplitSDKVersion: settings.version,
|
|
20
21
|
};
|
|
21
22
|
|
|
@@ -71,6 +71,7 @@ export function pushManagerFactory(
|
|
|
71
71
|
// It is used to halt the `connectPush` process if it was in progress.
|
|
72
72
|
let disconnected: boolean | undefined;
|
|
73
73
|
// flag that indicates a PUSH_NONRETRYABLE_ERROR, condition with which starting pushManager again is ignored.
|
|
74
|
+
// true if STREAMING_DISABLED control event, or 'pushEnabled: false', or non-recoverable SSE or Auth errors.
|
|
74
75
|
let disabled: boolean | undefined; // `disabled` implies `disconnected === true`
|
|
75
76
|
|
|
76
77
|
/** PushManager functions related to initialization */
|
|
@@ -296,12 +297,15 @@ export function pushManagerFactory(
|
|
|
296
297
|
// Expose Event Emitter functionality and Event constants
|
|
297
298
|
Object.create(pushEmitter),
|
|
298
299
|
{
|
|
299
|
-
// Stop/pause push mode
|
|
300
|
+
// Stop/pause push mode.
|
|
301
|
+
// It doesn't emit events. Neither PUSH_SUBSYSTEM_DOWN to start polling.
|
|
300
302
|
stop() {
|
|
301
303
|
disconnectPush(); // `handleNonRetryableError` cannot be used as `stop`, because it emits PUSH_SUBSYSTEM_DOWN event, which starts polling.
|
|
302
304
|
if (userKey) this.remove(userKey); // Necessary to properly resume streaming in client-side (e.g., RN SDK transition to foreground).
|
|
303
305
|
},
|
|
304
|
-
|
|
306
|
+
|
|
307
|
+
// Start/resume push mode.
|
|
308
|
+
// It eventually emits PUSH_SUBSYSTEM_DOWN, that starts polling, or PUSH_SUBSYSTEM_UP, that executes a syncAll
|
|
305
309
|
start() {
|
|
306
310
|
// Guard condition to avoid calling `connectPush` again if the `start` method is called multiple times or if push has been disabled.
|
|
307
311
|
if (disabled || disconnected === false) return;
|
|
@@ -311,6 +315,11 @@ export function pushManagerFactory(
|
|
|
311
315
|
else setTimeout(connectPush); // server-side runs in next cycle as in client-side, for consistency with client-side
|
|
312
316
|
},
|
|
313
317
|
|
|
318
|
+
// true/false if start or stop was called last respectively
|
|
319
|
+
isRunning(){
|
|
320
|
+
return disconnected === false;
|
|
321
|
+
},
|
|
322
|
+
|
|
314
323
|
// [Only for client-side]
|
|
315
324
|
add(userKey: string, mySegmentsSyncTask: ISegmentsSyncTask) {
|
|
316
325
|
const hash = hashUserKey(userKey);
|
|
@@ -3,7 +3,9 @@ import { IPostEventsBulk } from '../../services/types';
|
|
|
3
3
|
import { ISyncTask, ITimeTracker } from '../types';
|
|
4
4
|
import { submitterSyncTaskFactory } from './submitterSyncTask';
|
|
5
5
|
import { ILogger } from '../../logger/types';
|
|
6
|
-
import {
|
|
6
|
+
import { SUBMITTERS_PUSH_FULL_QUEUE } from '../../logger/constants';
|
|
7
|
+
|
|
8
|
+
const DATA_NAME = 'events';
|
|
7
9
|
|
|
8
10
|
/**
|
|
9
11
|
* Sync task that periodically posts tracked events
|
|
@@ -18,26 +20,37 @@ export function eventsSyncTaskFactory(
|
|
|
18
20
|
): ISyncTask {
|
|
19
21
|
|
|
20
22
|
// don't retry events.
|
|
21
|
-
const syncTask = submitterSyncTaskFactory(log, postEventsBulk, eventsCache, eventsPushRate,
|
|
23
|
+
const syncTask = submitterSyncTaskFactory(log, postEventsBulk, eventsCache, eventsPushRate, DATA_NAME, latencyTracker);
|
|
22
24
|
|
|
23
|
-
// Set a timer for the first push of events
|
|
25
|
+
// Set a timer for the first push window of events.
|
|
26
|
+
// Not implemented in the base submitter or sync task, since this feature is only used by the events submitter.
|
|
24
27
|
if (eventsFirstPushWindow > 0) {
|
|
28
|
+
let running = false;
|
|
25
29
|
let stopEventPublisherTimeout: ReturnType<typeof setTimeout>;
|
|
26
30
|
const originalStart = syncTask.start;
|
|
27
31
|
syncTask.start = () => {
|
|
32
|
+
running = true;
|
|
28
33
|
stopEventPublisherTimeout = setTimeout(originalStart, eventsFirstPushWindow);
|
|
29
34
|
};
|
|
30
35
|
const originalStop = syncTask.stop;
|
|
31
36
|
syncTask.stop = () => {
|
|
37
|
+
running = false;
|
|
32
38
|
clearTimeout(stopEventPublisherTimeout);
|
|
33
39
|
originalStop();
|
|
34
40
|
};
|
|
41
|
+
syncTask.isRunning = () => {
|
|
42
|
+
return running;
|
|
43
|
+
};
|
|
35
44
|
}
|
|
36
45
|
|
|
37
|
-
// register
|
|
46
|
+
// register events submitter to be executed when events cache is full
|
|
38
47
|
eventsCache.setOnFullQueueCb(() => {
|
|
39
|
-
|
|
40
|
-
|
|
48
|
+
if (syncTask.isRunning()) {
|
|
49
|
+
log.info(SUBMITTERS_PUSH_FULL_QUEUE, [DATA_NAME]);
|
|
50
|
+
syncTask.execute();
|
|
51
|
+
}
|
|
52
|
+
// If submitter is stopped (e.g., user consent declined or unknown, or app state offline), we don't send the data.
|
|
53
|
+
// Data will be sent when submitter is resumed.
|
|
41
54
|
});
|
|
42
55
|
|
|
43
56
|
return syncTask;
|
|
@@ -6,6 +6,9 @@ import { ImpressionDTO } from '../../types';
|
|
|
6
6
|
import { submitterSyncTaskFactory } from './submitterSyncTask';
|
|
7
7
|
import { ImpressionsPayload } from './types';
|
|
8
8
|
import { ILogger } from '../../logger/types';
|
|
9
|
+
import { SUBMITTERS_PUSH_FULL_QUEUE } from '../../logger/constants';
|
|
10
|
+
|
|
11
|
+
const DATA_NAME = 'impressions';
|
|
9
12
|
|
|
10
13
|
/**
|
|
11
14
|
* Converts `impressions` data from cache into request payload.
|
|
@@ -50,5 +53,17 @@ export function impressionsSyncTaskFactory(
|
|
|
50
53
|
): ISyncTask {
|
|
51
54
|
|
|
52
55
|
// retry impressions only once.
|
|
53
|
-
|
|
56
|
+
const syncTask = submitterSyncTaskFactory(log, postTestImpressionsBulk, impressionsCache, impressionsRefreshRate, DATA_NAME, latencyTracker, fromImpressionsCollector.bind(undefined, sendLabels), 1);
|
|
57
|
+
|
|
58
|
+
// register impressions submitter to be executed when impressions cache is full
|
|
59
|
+
impressionsCache.setOnFullQueueCb(() => {
|
|
60
|
+
if (syncTask.isRunning()) {
|
|
61
|
+
log.info(SUBMITTERS_PUSH_FULL_QUEUE, [DATA_NAME]);
|
|
62
|
+
syncTask.execute();
|
|
63
|
+
}
|
|
64
|
+
// If submitter is stopped (e.g., user consent declined or unknown, or app state offline), we don't send the data.
|
|
65
|
+
// Data will be sent when submitter is resumed.
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
return syncTask;
|
|
54
69
|
}
|
|
@@ -6,6 +6,7 @@ import { IPushManager } from './streaming/types';
|
|
|
6
6
|
import { IPollingManager, IPollingManagerCS } from './polling/types';
|
|
7
7
|
import { PUSH_SUBSYSTEM_UP, PUSH_SUBSYSTEM_DOWN } from './streaming/constants';
|
|
8
8
|
import { SYNC_START_POLLING, SYNC_CONTINUE_POLLING, SYNC_STOP_POLLING } from '../logger/constants';
|
|
9
|
+
import { isConsentGranted } from '../utils/consent';
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* Online SyncManager factory.
|
|
@@ -25,7 +26,7 @@ export function syncManagerOnlineFactory(
|
|
|
25
26
|
*/
|
|
26
27
|
return function (params: ISyncManagerFactoryParams): ISyncManagerCS {
|
|
27
28
|
|
|
28
|
-
const { log, streamingEnabled } = params
|
|
29
|
+
const { settings, settings: { log, streamingEnabled } } = params;
|
|
29
30
|
|
|
30
31
|
/** Polling Manager */
|
|
31
32
|
const pollingManager = pollingManagerFactory && pollingManagerFactory(params);
|
|
@@ -39,7 +40,6 @@ export function syncManagerOnlineFactory(
|
|
|
39
40
|
// It is not inyected as push and polling managers, because at the moment it is required
|
|
40
41
|
const submitter = submitterManagerFactory(params);
|
|
41
42
|
|
|
42
|
-
|
|
43
43
|
/** Sync Manager logic */
|
|
44
44
|
|
|
45
45
|
function startPolling() {
|
|
@@ -69,12 +69,18 @@ export function syncManagerOnlineFactory(
|
|
|
69
69
|
let startFirstTime = true; // flag to distinguish calling the `start` method for the first time, to support pausing and resuming the synchronization
|
|
70
70
|
|
|
71
71
|
return {
|
|
72
|
+
// Exposed for fine-grained control of synchronization.
|
|
73
|
+
// E.g.: user consent, app state changes (Page hide, Foreground/Background, Online/Offline).
|
|
74
|
+
pollingManager,
|
|
72
75
|
pushManager,
|
|
76
|
+
submitter,
|
|
73
77
|
|
|
74
78
|
/**
|
|
75
79
|
* Method used to start the syncManager for the first time, or resume it after being stopped.
|
|
76
80
|
*/
|
|
77
81
|
start() {
|
|
82
|
+
running = true;
|
|
83
|
+
|
|
78
84
|
// start syncing splits and segments
|
|
79
85
|
if (pollingManager) {
|
|
80
86
|
if (pushManager) {
|
|
@@ -90,21 +96,21 @@ export function syncManagerOnlineFactory(
|
|
|
90
96
|
}
|
|
91
97
|
|
|
92
98
|
// start periodic data recording (events, impressions, telemetry).
|
|
93
|
-
if (
|
|
94
|
-
running = true;
|
|
99
|
+
if (isConsentGranted(settings)) submitter.start();
|
|
95
100
|
},
|
|
96
101
|
|
|
97
102
|
/**
|
|
98
103
|
* Method used to stop/pause the syncManager.
|
|
99
104
|
*/
|
|
100
105
|
stop() {
|
|
106
|
+
running = false;
|
|
107
|
+
|
|
101
108
|
// stop syncing
|
|
102
109
|
if (pushManager) pushManager.stop();
|
|
103
110
|
if (pollingManager && pollingManager.isRunning()) pollingManager.stop();
|
|
104
111
|
|
|
105
112
|
// stop periodic data recording (events, impressions, telemetry).
|
|
106
|
-
|
|
107
|
-
running = false;
|
|
113
|
+
submitter.stop();
|
|
108
114
|
},
|
|
109
115
|
|
|
110
116
|
isRunning() {
|
|
@@ -112,7 +118,7 @@ export function syncManagerOnlineFactory(
|
|
|
112
118
|
},
|
|
113
119
|
|
|
114
120
|
flush() {
|
|
115
|
-
if (
|
|
121
|
+
if (isConsentGranted(settings)) return submitter.execute();
|
|
116
122
|
else return Promise.resolve();
|
|
117
123
|
},
|
|
118
124
|
|
package/src/sync/types.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { IPlatform } from '../sdkFactory/types';
|
|
|
3
3
|
import { ISplitApi } from '../services/types';
|
|
4
4
|
import { IStorageSync } from '../storages/types';
|
|
5
5
|
import { ISettings } from '../types';
|
|
6
|
+
import { IPollingManager } from './polling/types';
|
|
6
7
|
import { IPushManager } from './streaming/types';
|
|
7
8
|
|
|
8
9
|
export interface ITask<Input extends any[] = []> {
|
|
@@ -43,7 +44,9 @@ export interface ITimeTracker {
|
|
|
43
44
|
|
|
44
45
|
export interface ISyncManager extends ITask {
|
|
45
46
|
flush(): Promise<any>,
|
|
46
|
-
pushManager?: IPushManager
|
|
47
|
+
pushManager?: IPushManager,
|
|
48
|
+
pollingManager?: IPollingManager,
|
|
49
|
+
submitter?: ISyncTask
|
|
47
50
|
}
|
|
48
51
|
|
|
49
52
|
export interface ISyncManagerCS extends ISyncManager {
|
|
@@ -2,9 +2,10 @@ import { objectAssign } from '../utils/lang/objectAssign';
|
|
|
2
2
|
import { thenable } from '../utils/promise/thenable';
|
|
3
3
|
import { IEventsCacheBase } from '../storages/types';
|
|
4
4
|
import { IEventsHandler, IEventTracker } from './types';
|
|
5
|
-
import { SplitIO } from '../types';
|
|
6
|
-
import { ILogger } from '../logger/types';
|
|
5
|
+
import { ISettings, SplitIO } from '../types';
|
|
7
6
|
import { EVENTS_TRACKER_SUCCESS, ERROR_EVENTS_TRACKER } from '../logger/constants';
|
|
7
|
+
import { CONSENT_DECLINED } from '../utils/constants';
|
|
8
|
+
import { isStorageSync } from './impressionObserver/utils';
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* Event tracker stores events in cache and pass them to the integrations manager if provided.
|
|
@@ -13,11 +14,14 @@ import { EVENTS_TRACKER_SUCCESS, ERROR_EVENTS_TRACKER } from '../logger/constant
|
|
|
13
14
|
* @param integrationsManager optional event handler used for integrations
|
|
14
15
|
*/
|
|
15
16
|
export function eventTrackerFactory(
|
|
16
|
-
|
|
17
|
+
settings: ISettings,
|
|
17
18
|
eventsCache: IEventsCacheBase,
|
|
18
19
|
integrationsManager?: IEventsHandler
|
|
19
20
|
): IEventTracker {
|
|
20
21
|
|
|
22
|
+
const log = settings.log;
|
|
23
|
+
const isSync = isStorageSync(settings);
|
|
24
|
+
|
|
21
25
|
function queueEventsCallback(eventData: SplitIO.EventData, tracked: boolean) {
|
|
22
26
|
const { eventTypeId, trafficTypeName, key, value, timestamp, properties } = eventData;
|
|
23
27
|
// Logging every prop would be too much.
|
|
@@ -44,6 +48,10 @@ export function eventTrackerFactory(
|
|
|
44
48
|
|
|
45
49
|
return {
|
|
46
50
|
track(eventData: SplitIO.EventData, size?: number) {
|
|
51
|
+
if (settings.userConsent === CONSENT_DECLINED) {
|
|
52
|
+
return isSync ? false : Promise.resolve(false);
|
|
53
|
+
}
|
|
54
|
+
|
|
47
55
|
const tracked = eventsCache.track(eventData, size);
|
|
48
56
|
|
|
49
57
|
if (thenable(tracked)) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CONSUMER_PARTIAL_MODE, OPTIMIZED, PRODUCER_MODE, STANDALONE_MODE } from '../../utils/constants';
|
|
1
|
+
import { CONSUMER_MODE, CONSUMER_PARTIAL_MODE, OPTIMIZED, PRODUCER_MODE, STANDALONE_MODE } from '../../utils/constants';
|
|
2
2
|
import { ISettings } from '../../types';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -15,3 +15,10 @@ export function shouldBeOptimized(settings: ISettings) {
|
|
|
15
15
|
if (!shouldAddPt(settings)) return false;
|
|
16
16
|
return settings.sync.impressionsMode === OPTIMIZED ? true : false;
|
|
17
17
|
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Storage is async if mode is consumer or partial consumer
|
|
21
|
+
*/
|
|
22
|
+
export function isStorageSync(settings: ISettings) {
|
|
23
|
+
return [CONSUMER_MODE, CONSUMER_PARTIAL_MODE].indexOf(settings.mode) === -1 ? true : false;
|
|
24
|
+
}
|
|
@@ -5,8 +5,8 @@ import { IImpressionCountsCacheSync, IImpressionsCacheBase } from '../storages/t
|
|
|
5
5
|
import { IImpressionsHandler, IImpressionsTracker } from './types';
|
|
6
6
|
import { SplitIO, ImpressionDTO, ISettings } from '../types';
|
|
7
7
|
import { IImpressionObserver } from './impressionObserver/types';
|
|
8
|
-
import { ILogger } from '../logger/types';
|
|
9
8
|
import { IMPRESSIONS_TRACKER_SUCCESS, ERROR_IMPRESSIONS_TRACKER, ERROR_IMPRESSIONS_LISTENER } from '../logger/constants';
|
|
9
|
+
import { CONSENT_DECLINED } from '../utils/constants';
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Impressions tracker stores impressions in cache and pass them to the listener and integrations manager if provided.
|
|
@@ -19,22 +19,21 @@ import { IMPRESSIONS_TRACKER_SUCCESS, ERROR_IMPRESSIONS_TRACKER, ERROR_IMPRESSIO
|
|
|
19
19
|
* @param countsCache optional cache to save impressions count. If provided, impressions will be deduped (OPTIMIZED mode)
|
|
20
20
|
*/
|
|
21
21
|
export function impressionsTrackerFactory(
|
|
22
|
-
|
|
22
|
+
settings: ISettings,
|
|
23
23
|
impressionsCache: IImpressionsCacheBase,
|
|
24
|
-
|
|
25
|
-
// @TODO consider passing only an optional integrationsManager to handle impressions
|
|
26
|
-
{ runtime: { ip, hostname }, version }: Pick<ISettings, 'version' | 'runtime'>,
|
|
27
|
-
impressionListener?: SplitIO.IImpressionListener,
|
|
28
24
|
integrationsManager?: IImpressionsHandler,
|
|
29
|
-
|
|
30
25
|
// if observer is provided, it implies `shouldAddPreviousTime` flag (i.e., if impressions previous time should be added or not)
|
|
31
26
|
observer?: IImpressionObserver,
|
|
32
27
|
// if countsCache is provided, it implies `isOptimized` flag (i.e., if impressions should be deduped or not)
|
|
33
28
|
countsCache?: IImpressionCountsCacheSync
|
|
34
29
|
): IImpressionsTracker {
|
|
35
30
|
|
|
31
|
+
const { log, impressionListener, runtime: { ip, hostname }, version } = settings;
|
|
32
|
+
|
|
36
33
|
return {
|
|
37
34
|
track(impressions: ImpressionDTO[], attributes?: SplitIO.Attributes) {
|
|
35
|
+
if (settings.userConsent === CONSENT_DECLINED) return;
|
|
36
|
+
|
|
38
37
|
const impressionsCount = impressions.length;
|
|
39
38
|
|
|
40
39
|
const impressionsToStore: ImpressionDTO[] = []; // Track only the impressions that are going to be stored
|
|
@@ -85,7 +84,7 @@ export function impressionsTrackerFactory(
|
|
|
85
84
|
// integrationsManager.handleImpression does not throw errors
|
|
86
85
|
if (integrationsManager) integrationsManager.handleImpression(impressionData);
|
|
87
86
|
|
|
88
|
-
try { // An exception on the listeners should not break the SDK.
|
|
87
|
+
try { // @ts-ignore. An exception on the listeners should not break the SDK.
|
|
89
88
|
if (impressionListener) impressionListener.logImpression(impressionData);
|
|
90
89
|
} catch (err) {
|
|
91
90
|
log.error(ERROR_IMPRESSIONS_LISTENER, [err]);
|