diff --git a/package-lock.json b/package-lock.json index 9459fc73..73687c88 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@splitsoftware/splitio-commons", - "version": "1.3.1", + "version": "1.3.2-rc.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index ba8520d5..987ecb67 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@splitsoftware/splitio-commons", - "version": "1.3.1", + "version": "1.3.2-rc.1", "description": "Split Javascript SDK common components", "main": "cjs/index.js", "module": "esm/index.js", diff --git a/src/sdkFactory/index.ts b/src/sdkFactory/index.ts index 40f5c45e..9bba6f95 100644 --- a/src/sdkFactory/index.ts +++ b/src/sdkFactory/index.ts @@ -1,11 +1,10 @@ -import { ISdkFactoryParams } from './types'; +import { ISdkFactoryContext, ISdkFactoryContextSync, ISdkFactoryParams } from './types'; import { sdkReadinessManagerFactory } from '../readiness/sdkReadinessManager'; import { impressionsTrackerFactory } from '../trackers/impressionsTracker'; import { eventTrackerFactory } from '../trackers/eventTracker'; import { telemetryTrackerFactory } from '../trackers/telemetryTracker'; -import { IStorageFactoryParams, IStorageSync } from '../storages/types'; +import { IStorageFactoryParams } from '../storages/types'; import { SplitIO } from '../types'; -import { ISplitApi } from '../services/types'; import { getMatching } from '../utils/key'; import { shouldBeOptimized } from '../trackers/impressionObserver/utils'; import { validateAndTrackApiKey } from '../utils/inputValidation/apiKey'; @@ -32,7 +31,7 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ICsSDK | SplitIO. validateAndTrackApiKey(log, settings.core.authorizationKey); const sdkReadinessManager = sdkReadinessManagerFactory(log, platform.EventEmitter, settings.startup.readyTimeout); - const readinessManager = sdkReadinessManager.readinessManager; + const readiness = sdkReadinessManager.readinessManager; // @TODO consider passing the settings object, so that each storage access only what it needs const storageFactoryParams: IStorageFactoryParams = { @@ -51,8 +50,8 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ICsSDK | SplitIO. // or partial consumer mode, where it only has submitters, and therefore it doesn't emit readiness events. onReadyCb: (error) => { if (error) return; // Don't emit SDK_READY if storage failed to connect. Error message is logged by wrapperAdapter - readinessManager.splits.emit(SDK_SPLITS_ARRIVED); - readinessManager.segments.emit(SDK_SEGMENTS_ARRIVED); + readiness.splits.emit(SDK_SPLITS_ARRIVED); + readiness.segments.emit(SDK_SEGMENTS_ARRIVED); }, metadata: metadataBuilder(settings), log @@ -72,20 +71,15 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ICsSDK | SplitIO. // splitApi is used by SyncManager and Browser signal listener const splitApi = splitApiFactory && splitApiFactory(settings, platform, telemetryTracker); - const syncManager = syncManagerFactory && syncManagerFactory({ - settings, - splitApi: splitApi as ISplitApi, - storage: storage as IStorageSync, - readiness: sdkReadinessManager.readinessManager, - platform, - telemetryTracker - }); - - // signal listener + const ctx: ISdkFactoryContext = { splitApi, eventTracker, impressionsTracker, telemetryTracker, sdkReadinessManager, readiness, settings, storage, platform }; + + const syncManager = syncManagerFactory && syncManagerFactory(ctx as ISdkFactoryContextSync); + ctx.syncManager = syncManager; + const signalListener = SignalListener && new SignalListener(syncManager, settings, storage, splitApi); + ctx.signalListener = signalListener; - // Sdk client and manager - const ctx = { eventTracker, impressionsTracker, telemetryTracker, sdkReadinessManager, settings, storage, syncManager, signalListener }; + // SDK client and manager const clientMethod = sdkClientMethodFactory(ctx); const managerInstance = sdkManagerFactory(log, storage.splits, sdkReadinessManager); diff --git a/src/sdkFactory/types.ts b/src/sdkFactory/types.ts index 94223532..f041c863 100644 --- a/src/sdkFactory/types.ts +++ b/src/sdkFactory/types.ts @@ -1,25 +1,14 @@ import { IIntegrationManager, IIntegrationFactoryParams } from '../integrations/types'; import { ISignalListener } from '../listeners/types'; import { ILogger } from '../logger/types'; -import { ISdkReadinessManager } from '../readiness/types'; +import { IReadinessManager, ISdkReadinessManager } from '../readiness/types'; import { IFetch, ISplitApi, IEventSourceConstructor } from '../services/types'; import { IStorageAsync, IStorageSync, ISplitsCacheSync, ISplitsCacheAsync, IStorageFactoryParams } from '../storages/types'; -import { ISyncManager, ISyncManagerFactoryParams } from '../sync/types'; +import { ISyncManager } from '../sync/types'; import { IImpressionObserver } from '../trackers/impressionObserver/types'; import { IImpressionsTracker, IEventTracker, ITelemetryTracker } from '../trackers/types'; import { SplitIO, ISettings, IEventEmitter } from '../types'; -export interface ISdkFactoryContext { - storage: IStorageSync | IStorageAsync, - sdkReadinessManager: ISdkReadinessManager, - settings: ISettings - impressionsTracker: IImpressionsTracker, - eventTracker: IEventTracker, - telemetryTracker: ITelemetryTracker - signalListener?: ISignalListener - syncManager?: ISyncManager, -} - /** * Environment related dependencies. * These getters are called a fixed number of times per factory instantiation. @@ -32,6 +21,32 @@ export interface IPlatform { now?: () => number } +export interface ISdkFactoryContext { + platform: IPlatform, + sdkReadinessManager: ISdkReadinessManager, + readiness: IReadinessManager, + settings: ISettings + impressionsTracker: IImpressionsTracker, + eventTracker: IEventTracker, + telemetryTracker: ITelemetryTracker, + storage: IStorageSync | IStorageAsync, + signalListener?: ISignalListener + splitApi?: ISplitApi + syncManager?: ISyncManager, +} + +export interface ISdkFactoryContextSync extends ISdkFactoryContext { + storage: IStorageSync, + splitApi: ISplitApi + syncManager: ISyncManager, +} + +export interface ISdkFactoryContextAsync extends ISdkFactoryContext { + storage: IStorageAsync, + splitApi: undefined, + syncManager: undefined +} + /** * Object parameter with the modules required to create an SDK factory instance */ @@ -54,7 +69,7 @@ export interface ISdkFactoryParams { // SyncManager factory. // Not required when providing an asynchronous storage (consumer mode), but required in standalone mode to avoid SDK timeout. // It can create an offline or online sync manager, with or without streaming support. - syncManagerFactory?: (params: ISyncManagerFactoryParams) => ISyncManager, + syncManagerFactory?: (params: ISdkFactoryContextSync) => ISyncManager, // Sdk manager factory sdkManagerFactory: ( diff --git a/src/storages/inRedis/__tests__/SegmentsCacheInRedis.spec.ts b/src/storages/inRedis/__tests__/SegmentsCacheInRedis.spec.ts index e230fbc0..15d81e6b 100644 --- a/src/storages/inRedis/__tests__/SegmentsCacheInRedis.spec.ts +++ b/src/storages/inRedis/__tests__/SegmentsCacheInRedis.spec.ts @@ -36,6 +36,7 @@ describe('SEGMENTS CACHE IN REDIS', () => { expect(await cache.isInSegment('mocked-segment', 'd')).toBe(true); expect(await cache.isInSegment('mocked-segment', 'e')).toBe(true); + await cache.clear(); await connection.quit(); }); @@ -53,6 +54,7 @@ describe('SEGMENTS CACHE IN REDIS', () => { ['s1', 's2', 's3', 's4'].forEach(s => expect(segments.indexOf(s) !== -1).toBe(true)); + await cache.clear(); await connection.quit(); }); diff --git a/src/sync/offline/syncManagerOffline.ts b/src/sync/offline/syncManagerOffline.ts index 253493f2..cd5c435d 100644 --- a/src/sync/offline/syncManagerOffline.ts +++ b/src/sync/offline/syncManagerOffline.ts @@ -1,9 +1,10 @@ -import { ISyncManager, ISyncManagerCS, ISyncManagerFactoryParams } from '../types'; +import { ISyncManager, ISyncManagerCS } from '../types'; import { fromObjectSyncTaskFactory } from './syncTasks/fromObjectSyncTask'; import { objectAssign } from '../../utils/lang/objectAssign'; import { ISplitsParser } from './splitsParser/types'; import { IReadinessManager } from '../../readiness/types'; import { SDK_SEGMENTS_ARRIVED } from '../../readiness/constants'; +import { ISdkFactoryContextSync } from '../../sdkFactory/types'; function flush() { return Promise.resolve(); @@ -17,7 +18,7 @@ function flush() { */ export function syncManagerOfflineFactory( splitsParserFactory: () => ISplitsParser -): (params: ISyncManagerFactoryParams) => ISyncManagerCS { +): (params: ISdkFactoryContextSync) => ISyncManagerCS { /** * SyncManager factory for modular SDK @@ -26,7 +27,7 @@ export function syncManagerOfflineFactory( settings, readiness, storage, - }: ISyncManagerFactoryParams): ISyncManagerCS { + }: ISdkFactoryContextSync): ISyncManagerCS { return objectAssign( fromObjectSyncTaskFactory(splitsParserFactory(), storage, readiness, settings), diff --git a/src/sync/polling/pollingManagerCS.ts b/src/sync/polling/pollingManagerCS.ts index 05f7f793..e8e98142 100644 --- a/src/sync/polling/pollingManagerCS.ts +++ b/src/sync/polling/pollingManagerCS.ts @@ -7,14 +7,14 @@ import { splitsSyncTaskFactory } from './syncTasks/splitsSyncTask'; import { getMatching } from '../../utils/key'; import { SDK_SPLITS_ARRIVED, SDK_SEGMENTS_ARRIVED } from '../../readiness/constants'; import { POLLING_SMART_PAUSING, POLLING_START, POLLING_STOP } from '../../logger/constants'; -import { ISyncManagerFactoryParams } from '../types'; +import { ISdkFactoryContextSync } from '../../sdkFactory/types'; /** * Expose start / stop mechanism for polling data from services. * For client-side API with multiple clients. */ export function pollingManagerCSFactory( - params: ISyncManagerFactoryParams + params: ISdkFactoryContextSync ): IPollingManagerCS { const { splitApi, storage, readiness, settings } = params; diff --git a/src/sync/polling/pollingManagerSS.ts b/src/sync/polling/pollingManagerSS.ts index 2c7d3ec9..90f252a4 100644 --- a/src/sync/polling/pollingManagerSS.ts +++ b/src/sync/polling/pollingManagerSS.ts @@ -3,13 +3,13 @@ import { segmentsSyncTaskFactory } from './syncTasks/segmentsSyncTask'; import { IPollingManager, ISegmentsSyncTask, ISplitsSyncTask } from './types'; import { thenable } from '../../utils/promise/thenable'; import { POLLING_START, POLLING_STOP, LOG_PREFIX_SYNC_POLLING } from '../../logger/constants'; -import { ISyncManagerFactoryParams } from '../types'; +import { ISdkFactoryContextSync } from '../../sdkFactory/types'; /** * Expose start / stop mechanism for pulling data from services. */ export function pollingManagerSSFactory( - params: ISyncManagerFactoryParams + params: ISdkFactoryContextSync ): IPollingManager { const { splitApi, storage, readiness, settings } = params; diff --git a/src/sync/streaming/pushManager.ts b/src/sync/streaming/pushManager.ts index 329aa7bf..9392401b 100644 --- a/src/sync/streaming/pushManager.ts +++ b/src/sync/streaming/pushManager.ts @@ -18,8 +18,8 @@ import { isInBitmap, parseBitmap, parseKeyList } from './mySegmentsV2utils'; import { ISet, _Set } from '../../utils/lang/sets'; import { Hash64, hash64 } from '../../utils/murmur3/murmur3_64'; import { IAuthTokenPushEnabled } from './AuthClient/types'; -import { ISyncManagerFactoryParams } from '../types'; import { TOKEN_REFRESH, AUTH_REJECTION } from '../../utils/constants'; +import { ISdkFactoryContextSync } from '../../sdkFactory/types'; /** * PushManager factory: @@ -27,7 +27,7 @@ import { TOKEN_REFRESH, AUTH_REJECTION } from '../../utils/constants'; * - for client-side, with support for multiple clients, if key is provided in settings */ export function pushManagerFactory( - params: ISyncManagerFactoryParams, + params: ISdkFactoryContextSync, pollingManager: IPollingManager, ): IPushManager | undefined { diff --git a/src/sync/submitters/__tests__/eventsSubmitter.spec.ts b/src/sync/submitters/__tests__/eventsSubmitter.spec.ts index a17ec656..16b26bc1 100644 --- a/src/sync/submitters/__tests__/eventsSubmitter.spec.ts +++ b/src/sync/submitters/__tests__/eventsSubmitter.spec.ts @@ -6,19 +6,28 @@ import { loggerMock } from '../../../logger/__tests__/sdkLogger.mock'; describe('Events submitter', () => { let __onFullQueueCb: () => void; - const postEventsBulkMock = jest.fn(); const eventsCacheMock = { isEmpty: jest.fn(() => true), setOnFullQueueCb: jest.fn(function (onFullQueueCb) { __onFullQueueCb = onFullQueueCb; }) }; + const params = { + settings: { + log: loggerMock, + scheduler: { eventsPushRate: 30000 }, + startup: { eventsFirstPushWindow: 0 } + }, + splitApi: { postEventsBulkMock: jest.fn() }, + storage: { events: eventsCacheMock } + }; beforeEach(() => { eventsCacheMock.isEmpty.mockClear(); }); test('with eventsFirstPushWindow', async () => { - const eventsFirstPushWindow = 20; // @ts-ignore - const eventsSubmitter = eventsSubmitterFactory(loggerMock, postEventsBulkMock, eventsCacheMock, 30000, eventsFirstPushWindow); + const eventsFirstPushWindow = 20; + params.settings.startup.eventsFirstPushWindow = eventsFirstPushWindow; // @ts-ignore + const eventsSubmitter = eventsSubmitterFactory(params); eventsSubmitter.start(); expect(eventsSubmitter.isRunning()).toEqual(true); // Submitter should be flagged as running @@ -40,8 +49,9 @@ describe('Events submitter', () => { }); test('without eventsFirstPushWindow', async () => { - // @ts-ignore - const eventsSubmitter = eventsSubmitterFactory(loggerMock, postEventsBulkMock, eventsCacheMock, 30000); + const eventsFirstPushWindow = 0; + params.settings.startup.eventsFirstPushWindow = eventsFirstPushWindow; // @ts-ignore + const eventsSubmitter = eventsSubmitterFactory(params); eventsSubmitter.start(); expect(eventsSubmitter.isRunning()).toEqual(true); // Submitter should be flagged as running diff --git a/src/sync/submitters/__tests__/telemetrySubmitter.spec.ts b/src/sync/submitters/__tests__/telemetrySubmitter.spec.ts index 9a9175cf..cbf65e06 100644 --- a/src/sync/submitters/__tests__/telemetrySubmitter.spec.ts +++ b/src/sync/submitters/__tests__/telemetrySubmitter.spec.ts @@ -2,6 +2,7 @@ import { telemetrySubmitterFactory } from '../telemetrySubmitter'; import { InMemoryStorageFactory } from '../../../storages/inMemory/InMemoryStorage'; import { SDK_READY, SDK_READY_FROM_CACHE } from '../../../readiness/constants'; import { fullSettings } from '../../../utils/settingsValidation/__tests__/settings.mocks'; +import { ISyncTask } from '../../types'; describe('Telemetry submitter', () => { @@ -26,7 +27,7 @@ describe('Telemetry submitter', () => { }; test('submits metrics/usage periodically', async () => { // @ts-ignore - const telemetrySubmitter = telemetrySubmitterFactory(params); + const telemetrySubmitter = telemetrySubmitterFactory(params) as ISyncTask; const popLatenciesSpy = jest.spyOn(params.storage.telemetry!, 'popLatencies'); telemetrySubmitter.start(); @@ -48,7 +49,7 @@ describe('Telemetry submitter', () => { }); test('submits metrics/config when SDK is ready', async () => { // @ts-ignore - const telemetrySubmitter = telemetrySubmitterFactory(params); + const telemetrySubmitter = telemetrySubmitterFactory(params) as ISyncTask; const recordTimeUntilReadyFromCacheSpy = jest.spyOn(params.storage.telemetry!, 'recordTimeUntilReadyFromCache'); const recordTimeUntilReadySpy = jest.spyOn(params.storage.telemetry!, 'recordTimeUntilReady'); diff --git a/src/sync/submitters/eventsSubmitter.ts b/src/sync/submitters/eventsSubmitter.ts index 84ce404f..998b7aba 100644 --- a/src/sync/submitters/eventsSubmitter.ts +++ b/src/sync/submitters/eventsSubmitter.ts @@ -1,30 +1,28 @@ -import { IEventsCacheSync } from '../../storages/types'; -import { IPostEventsBulk } from '../../services/types'; import { submitterFactory, firstPushWindowDecorator } from './submitter'; -import { ILogger } from '../../logger/types'; import { SUBMITTERS_PUSH_FULL_QUEUE } from '../../logger/constants'; +import { ISdkFactoryContextSync } from '../../sdkFactory/types'; const DATA_NAME = 'events'; /** * Submitter that periodically posts tracked events */ -export function eventsSubmitterFactory( - log: ILogger, - postEventsBulk: IPostEventsBulk, - eventsCache: IEventsCacheSync, - eventsPushRate: number, - eventsFirstPushWindow: number, -) { +export function eventsSubmitterFactory(params: ISdkFactoryContextSync) { + + const { + settings: { log, scheduler: { eventsPushRate }, startup: { eventsFirstPushWindow } }, + splitApi: { postEventsBulk }, + storage: { events }, + } = params; // don't retry events. - let submitter = submitterFactory(log, postEventsBulk, eventsCache, eventsPushRate, DATA_NAME); + let submitter = submitterFactory(log, postEventsBulk, events, eventsPushRate, DATA_NAME); // Set a timer for the first push window of events. if (eventsFirstPushWindow > 0) submitter = firstPushWindowDecorator(submitter, eventsFirstPushWindow); // register events submitter to be executed when events cache is full - eventsCache.setOnFullQueueCb(() => { + events.setOnFullQueueCb(() => { if (submitter.isRunning()) { log.info(SUBMITTERS_PUSH_FULL_QUEUE, [DATA_NAME]); submitter.execute(); diff --git a/src/sync/submitters/impressionCountsSubmitter.ts b/src/sync/submitters/impressionCountsSubmitter.ts index 78fc6588..48131021 100644 --- a/src/sync/submitters/impressionCountsSubmitter.ts +++ b/src/sync/submitters/impressionCountsSubmitter.ts @@ -1,8 +1,6 @@ -import { IPostTestImpressionsCount } from '../../services/types'; -import { IImpressionCountsCacheSync } from '../../storages/types'; +import { ISdkFactoryContextSync } from '../../sdkFactory/types'; import { submitterFactory } from './submitter'; import { ImpressionCountsPayload } from './types'; -import { ILogger } from '../../logger/types'; /** * Converts `impressionCounts` data from cache into request payload. @@ -33,12 +31,16 @@ const IMPRESSIONS_COUNT_RATE = 1800000; // 30 minutes /** * Submitter that periodically posts impression counts */ -export function impressionCountsSubmitterFactory( - log: ILogger, - postTestImpressionsCount: IPostTestImpressionsCount, - impressionCountsCache: IImpressionCountsCacheSync, -) { - - // retry impressions counts only once. - return submitterFactory(log, postTestImpressionsCount, impressionCountsCache, IMPRESSIONS_COUNT_RATE, 'impression counts', fromImpressionCountsCollector, 1); +export function impressionCountsSubmitterFactory(params: ISdkFactoryContextSync) { + + const { + settings: { log }, + splitApi: { postTestImpressionsCount }, + storage: { impressionCounts } + } = params; + + if (impressionCounts) { + // retry impressions counts only once. + return submitterFactory(log, postTestImpressionsCount, impressionCounts, IMPRESSIONS_COUNT_RATE, 'impression counts', fromImpressionCountsCollector, 1); + } } diff --git a/src/sync/submitters/impressionsSubmitter.ts b/src/sync/submitters/impressionsSubmitter.ts index 3a01f6f2..e54b5f1f 100644 --- a/src/sync/submitters/impressionsSubmitter.ts +++ b/src/sync/submitters/impressionsSubmitter.ts @@ -1,11 +1,9 @@ import { groupBy, forOwn } from '../../utils/lang'; -import { IPostTestImpressionsBulk } from '../../services/types'; -import { IImpressionsCacheSync } from '../../storages/types'; import { ImpressionDTO } from '../../types'; import { submitterFactory } from './submitter'; import { ImpressionsPayload } from './types'; -import { ILogger } from '../../logger/types'; import { SUBMITTERS_PUSH_FULL_QUEUE } from '../../logger/constants'; +import { ISdkFactoryContextSync } from '../../sdkFactory/types'; const DATA_NAME = 'impressions'; @@ -42,19 +40,19 @@ export function fromImpressionsCollector(sendLabels: boolean, data: ImpressionDT /** * Submitter that periodically posts impressions data */ -export function impressionsSubmitterFactory( - log: ILogger, - postTestImpressionsBulk: IPostTestImpressionsBulk, - impressionsCache: IImpressionsCacheSync, - impressionsRefreshRate: number, - sendLabels = false, -) { +export function impressionsSubmitterFactory(params: ISdkFactoryContextSync) { + + const { + settings: { log, scheduler: { impressionsRefreshRate }, core: { labelsEnabled } }, + splitApi: { postTestImpressionsBulk }, + storage: { impressions } + } = params; // retry impressions only once. - const syncTask = submitterFactory(log, postTestImpressionsBulk, impressionsCache, impressionsRefreshRate, DATA_NAME, fromImpressionsCollector.bind(undefined, sendLabels), 1); + const syncTask = submitterFactory(log, postTestImpressionsBulk, impressions, impressionsRefreshRate, DATA_NAME, fromImpressionsCollector.bind(undefined, labelsEnabled), 1); // register impressions submitter to be executed when impressions cache is full - impressionsCache.setOnFullQueueCb(() => { + impressions.setOnFullQueueCb(() => { if (syncTask.isRunning()) { log.info(SUBMITTERS_PUSH_FULL_QUEUE, [DATA_NAME]); syncTask.execute(); diff --git a/src/sync/submitters/submitterManager.ts b/src/sync/submitters/submitterManager.ts index fd3cab7b..523e5ab5 100644 --- a/src/sync/submitters/submitterManager.ts +++ b/src/sync/submitters/submitterManager.ts @@ -2,18 +2,20 @@ import { syncTaskComposite } from '../syncTaskComposite'; import { eventsSubmitterFactory } from './eventsSubmitter'; import { impressionsSubmitterFactory } from './impressionsSubmitter'; import { impressionCountsSubmitterFactory } from './impressionCountsSubmitter'; -import { ISyncManagerFactoryParams } from '../types'; -import { ISyncManagerFactoryParamsWithTelemetry, telemetrySubmitterFactory } from './telemetrySubmitter'; +import { telemetrySubmitterFactory } from './telemetrySubmitter'; +import { ISdkFactoryContextSync } from '../../sdkFactory/types'; -export function submitterManagerFactory(params: ISyncManagerFactoryParams) { +export function submitterManagerFactory(params: ISdkFactoryContextSync) { - const { settings, storage, splitApi } = params; - const log = settings.log; const submitters = [ - impressionsSubmitterFactory(log, splitApi.postTestImpressionsBulk, storage.impressions, settings.scheduler.impressionsRefreshRate, settings.core.labelsEnabled), - eventsSubmitterFactory(log, splitApi.postEventsBulk, storage.events, settings.scheduler.eventsPushRate, settings.startup.eventsFirstPushWindow) + impressionsSubmitterFactory(params), + eventsSubmitterFactory(params) ]; - if (storage.impressionCounts) submitters.push(impressionCountsSubmitterFactory(log, splitApi.postTestImpressionsCount, storage.impressionCounts)); - if (storage.telemetry) submitters.push(telemetrySubmitterFactory(params as ISyncManagerFactoryParamsWithTelemetry)); + + const impressionCountsSubmitter = impressionCountsSubmitterFactory(params); + if (impressionCountsSubmitter) submitters.push(impressionCountsSubmitter); + const telemetrySubmitter = telemetrySubmitterFactory(params); + if (telemetrySubmitter) submitters.push(telemetrySubmitter); + return syncTaskComposite(submitters); } diff --git a/src/sync/submitters/telemetrySubmitter.ts b/src/sync/submitters/telemetrySubmitter.ts index dbebd972..5d1ab125 100644 --- a/src/sync/submitters/telemetrySubmitter.ts +++ b/src/sync/submitters/telemetrySubmitter.ts @@ -1,4 +1,4 @@ -import { ITelemetryCacheSync } from '../../storages/types'; +import { ISegmentsCacheSync, ISplitsCacheSync, ITelemetryCacheSync } from '../../storages/types'; import { submitterFactory, firstPushWindowDecorator } from './submitter'; import { TelemetryUsageStatsPayload, TelemetryConfigStatsPayload } from './types'; import { QUEUED, DEDUPED, DROPPED, CONSUMER_MODE, CONSUMER_ENUM, STANDALONE_MODE, CONSUMER_PARTIAL_MODE, STANDALONE_ENUM, CONSUMER_PARTIAL_ENUM, OPTIMIZED, DEBUG, DEBUG_ENUM, OPTIMIZED_ENUM } from '../../utils/constants'; @@ -6,15 +6,13 @@ import { SDK_READY, SDK_READY_FROM_CACHE } from '../../readiness/constants'; import { ISettings } from '../../types'; import { base } from '../../utils/settingsValidation'; import { usedKeysMap } from '../../utils/inputValidation/apiKey'; -import { ISyncManagerFactoryParams } from '../types'; import { timer } from '../../utils/timeTracker/timer'; - -export type ISyncManagerFactoryParamsWithTelemetry = ISyncManagerFactoryParams & { storage: { telemetry: ITelemetryCacheSync } } +import { ISdkFactoryContextSync } from '../../sdkFactory/types'; /** * Converts data from telemetry cache into /metrics/usage request payload. */ -export function telemetryCacheStatsAdapter({ splits, segments, telemetry }: ISyncManagerFactoryParamsWithTelemetry['storage']) { +export function telemetryCacheStatsAdapter(telemetry: ITelemetryCacheSync, splits: ISplitsCacheSync, segments: ISegmentsCacheSync) { return { isEmpty() { return false; }, // There is always data in telemetry cache clear() { }, // No-op @@ -69,7 +67,7 @@ function getRedundantActiveFactories() { /** * Converts data from telemetry cache and settings into /metrics/config request payload. */ -export function telemetryCacheConfigAdapter(settings: ISettings, telemetryCache: ITelemetryCacheSync) { +export function telemetryCacheConfigAdapter(telemetry: ITelemetryCacheSync, settings: ISettings) { return { isEmpty() { return false; }, clear() { }, @@ -102,10 +100,10 @@ export function telemetryCacheConfigAdapter(settings: ISettings, telemetryCache: hP: false, // @TODO proxy not supported aF: getActiveFactories(), rF: getRedundantActiveFactories(), - tR: telemetryCache.getTimeUntilReady() as number, - tC: telemetryCache.getTimeUntilReadyFromCache(), - nR: telemetryCache.getNonReadyUsage(), - t: telemetryCache.popTags(), + tR: telemetry.getTimeUntilReady() as number, + tC: telemetry.getTimeUntilReadyFromCache(), + nR: telemetry.getNonReadyUsage(), + t: telemetry.popTags(), i: settings.integrations && settings.integrations.map(int => int.type), }; } @@ -115,25 +113,28 @@ export function telemetryCacheConfigAdapter(settings: ISettings, telemetryCache: /** * Submitter that periodically posts telemetry data */ -export function telemetrySubmitterFactory(params: ISyncManagerFactoryParamsWithTelemetry) { - const { settings, settings: { log, scheduler: { telemetryRefreshRate } }, storage, splitApi, platform: { now }, readiness } = params; +export function telemetrySubmitterFactory(params: ISdkFactoryContextSync) { + const { storage: { splits, segments, telemetry } } = params; + if (!telemetry) return; // No submitter created if telemetry cache is not defined + + const { settings, settings: { log, scheduler: { telemetryRefreshRate } }, splitApi, platform: { now }, readiness } = params; const startTime = timer(now || Date.now); const submitter = firstPushWindowDecorator( - submitterFactory(log, splitApi.postMetricsUsage, telemetryCacheStatsAdapter(storage), telemetryRefreshRate, 'telemetry stats', undefined, 0, true), + submitterFactory(log, splitApi.postMetricsUsage, telemetryCacheStatsAdapter(telemetry, splits, segments), telemetryRefreshRate, 'telemetry stats', undefined, 0, true), telemetryRefreshRate ); readiness.gate.once(SDK_READY_FROM_CACHE, () => { - storage.telemetry.recordTimeUntilReadyFromCache(startTime()); + telemetry.recordTimeUntilReadyFromCache(startTime()); }); readiness.gate.once(SDK_READY, () => { - storage.telemetry.recordTimeUntilReady(startTime()); + telemetry.recordTimeUntilReady(startTime()); // Post config data when the SDK is ready and if the telemetry submitter was started if (submitter.isRunning()) { - const postMetricsConfigTask = submitterFactory(log, splitApi.postMetricsConfig, telemetryCacheConfigAdapter(settings, storage.telemetry), 0, 'telemetry config', undefined, 0, true); + const postMetricsConfigTask = submitterFactory(log, splitApi.postMetricsConfig, telemetryCacheConfigAdapter(telemetry, settings), 0, 'telemetry config', undefined, 0, true); postMetricsConfigTask.execute(); } }); diff --git a/src/sync/syncManagerOnline.ts b/src/sync/syncManagerOnline.ts index 88c1516e..61f0603d 100644 --- a/src/sync/syncManagerOnline.ts +++ b/src/sync/syncManagerOnline.ts @@ -1,4 +1,4 @@ -import { ISyncManagerCS, ISyncManagerFactoryParams } from './types'; +import { ISyncManagerCS } from './types'; import { submitterManagerFactory } from './submitters/submitterManager'; import { IReadinessManager } from '../readiness/types'; import { IStorageSync } from '../storages/types'; @@ -8,6 +8,7 @@ import { PUSH_SUBSYSTEM_UP, PUSH_SUBSYSTEM_DOWN } from './streaming/constants'; import { SYNC_START_POLLING, SYNC_CONTINUE_POLLING, SYNC_STOP_POLLING } from '../logger/constants'; import { isConsentGranted } from '../consent'; import { POLLING, STREAMING, SYNC_MODE_UPDATE } from '../utils/constants'; +import { ISdkFactoryContextSync } from '../sdkFactory/types'; /** * Online SyncManager factory. @@ -18,14 +19,14 @@ import { POLLING, STREAMING, SYNC_MODE_UPDATE } from '../utils/constants'; * @param pushManagerFactory optional to build a SyncManager with or without streaming support */ export function syncManagerOnlineFactory( - pollingManagerFactory?: (params: ISyncManagerFactoryParams) => IPollingManager, - pushManagerFactory?: (params: ISyncManagerFactoryParams, pollingManager: IPollingManager) => IPushManager | undefined, -): (params: ISyncManagerFactoryParams) => ISyncManagerCS { + pollingManagerFactory?: (params: ISdkFactoryContextSync) => IPollingManager, + pushManagerFactory?: (params: ISdkFactoryContextSync, pollingManager: IPollingManager) => IPushManager | undefined, +): (params: ISdkFactoryContextSync) => ISyncManagerCS { /** * SyncManager factory for modular SDK */ - return function (params: ISyncManagerFactoryParams): ISyncManagerCS { + return function (params: ISdkFactoryContextSync): ISyncManagerCS { const { settings, settings: { log, streamingEnabled }, telemetryTracker } = params; diff --git a/src/sync/types.ts b/src/sync/types.ts index 40229c02..22f51eb9 100644 --- a/src/sync/types.ts +++ b/src/sync/types.ts @@ -1,9 +1,5 @@ import { IReadinessManager } from '../readiness/types'; -import { IPlatform } from '../sdkFactory/types'; -import { ISplitApi } from '../services/types'; import { IStorageSync } from '../storages/types'; -import { ITelemetryTracker } from '../trackers/types'; -import { ISettings } from '../types'; import { IPollingManager } from './polling/types'; import { IPushManager } from './streaming/types'; @@ -49,12 +45,3 @@ export interface ISyncManager extends ITask { export interface ISyncManagerCS extends ISyncManager { shared(matchingKey: string, readinessManager: IReadinessManager, storage: IStorageSync): ISyncManager | undefined } - -export interface ISyncManagerFactoryParams { - settings: ISettings, - readiness: IReadinessManager, - storage: IStorageSync, - splitApi: ISplitApi, - platform: IPlatform, - telemetryTracker: ITelemetryTracker -} diff --git a/src/types.ts b/src/types.ts index 37379932..4ea5ce31 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,10 +1,11 @@ import { ISplitFiltersValidation } from './dtos/types'; import { IIntegration, IIntegrationFactoryParams } from './integrations/types'; import { ILogger } from './logger/types'; +import { ISdkFactoryContext } from './sdkFactory/types'; /* eslint-disable no-use-before-define */ import { IStorageFactoryParams, IStorageSync, IStorageAsync, IStorageSyncFactory, IStorageAsyncFactory } from './storages/types'; -import { ISyncManagerFactoryParams, ISyncManagerCS } from './sync/types'; +import { ISyncManagerCS } from './sync/types'; /** * Reduced version of NodeJS.EventEmitter interface with the minimal methods used by the SDK @@ -622,7 +623,7 @@ export namespace SplitIO { */ export type LocalhostFactory = { type: 'LocalhostFromObject' | 'LocalhostFromFile' - (params: ISyncManagerFactoryParams): ISyncManagerCS + (params: ISdkFactoryContext): ISyncManagerCS } /** * Impression listener interface. This is the interface that needs to be implemented