From cfa02b44d44ec513b55383abd556cdca43925577 Mon Sep 17 00:00:00 2001 From: Raju Ahmed Date: Fri, 7 Feb 2025 02:37:33 +0600 Subject: [PATCH 1/5] a --- lib/entrypoint.test-d.ts | 15 ++++++ .../event_processor_factory.browser.ts | 6 +-- .../event_processor_factory.node.ts | 6 +-- .../event_processor_factory.react_native.ts | 6 +-- .../event_processor_factory.ts | 21 +++++++- lib/odp/odp_manager_factory.browser.spec.ts | 43 ++++++++-------- lib/odp/odp_manager_factory.browser.ts | 6 +-- lib/odp/odp_manager_factory.node.spec.ts | 51 ++++++++++--------- lib/odp/odp_manager_factory.node.ts | 6 +-- .../odp_manager_factory.react_native.spec.ts | 51 ++++++++++--------- lib/odp/odp_manager_factory.react_native.ts | 6 +-- lib/odp/odp_manager_factory.ts | 16 ++++++ lib/optimizely/index.spec.ts | 3 +- .../config_manager_factory.browser.spec.ts | 15 +++--- .../config_manager_factory.browser.ts | 6 +-- .../config_manager_factory.node.spec.ts | 15 +++--- .../config_manager_factory.node.ts | 6 +-- ...onfig_manager_factory.react_native.spec.ts | 17 ++++--- .../config_manager_factory.react_native.ts | 8 +-- lib/project_config/config_manager_factory.ts | 22 +++++++- package.json | 2 +- vitest.config.mts | 1 + 22 files changed, 206 insertions(+), 122 deletions(-) create mode 100644 lib/entrypoint.test-d.ts diff --git a/lib/entrypoint.test-d.ts b/lib/entrypoint.test-d.ts new file mode 100644 index 000000000..e952f293e --- /dev/null +++ b/lib/entrypoint.test-d.ts @@ -0,0 +1,15 @@ +import { expectTypeOf } from 'vitest'; + +import * as browserEntrypoint from './index.browser'; +import * as nodeEntrypoint from './index.node'; +import * as reactNativeEntrypoint from './index.react_native'; + +import { Config, Client } from './shared_types'; + +export type Entrypoint = { + createInstance: (config: Config) => Client | null; +} + +expectTypeOf(browserEntrypoint).toMatchTypeOf(); +expectTypeOf(nodeEntrypoint).toMatchTypeOf(); +expectTypeOf(reactNativeEntrypoint).toMatchTypeOf(); diff --git a/lib/event_processor/event_processor_factory.browser.ts b/lib/event_processor/event_processor_factory.browser.ts index b6651ed70..96f19b7fe 100644 --- a/lib/event_processor/event_processor_factory.browser.ts +++ b/lib/event_processor/event_processor_factory.browser.ts @@ -18,7 +18,7 @@ import { getForwardingEventProcessor } from './forwarding_event_processor'; import { EventDispatcher } from './event_dispatcher/event_dispatcher'; import { EventProcessor } from './event_processor'; import { EventWithId } from './batch_event_processor'; -import { getBatchEventProcessor, BatchEventProcessorOptions } from './event_processor_factory'; +import { getOpaqueBatchEventProcessor, BatchEventProcessorOptions, OpaqueEventProcessor } from './event_processor_factory'; import defaultEventDispatcher from './event_dispatcher/default_dispatcher.browser'; import sendBeaconEventDispatcher from './event_dispatcher/send_beacon_dispatcher.browser'; import { LocalStorageCache } from '../utils/cache/local_storage_cache.browser'; @@ -35,7 +35,7 @@ const identity = (v: T): T => v; export const createBatchEventProcessor = ( options: BatchEventProcessorOptions -): EventProcessor => { +): OpaqueEventProcessor => { const localStorageCache = new LocalStorageCache(); const eventStore = new SyncPrefixCache( localStorageCache, EVENT_STORE_PREFIX, @@ -43,7 +43,7 @@ export const createBatchEventProcessor = ( identity, ); - return getBatchEventProcessor({ + return getOpaqueBatchEventProcessor({ eventDispatcher: options.eventDispatcher || defaultEventDispatcher, closingEventDispatcher: options.closingEventDispatcher || (options.eventDispatcher ? undefined : sendBeaconEventDispatcher), diff --git a/lib/event_processor/event_processor_factory.node.ts b/lib/event_processor/event_processor_factory.node.ts index 6c57272bc..d95ab031d 100644 --- a/lib/event_processor/event_processor_factory.node.ts +++ b/lib/event_processor/event_processor_factory.node.ts @@ -17,7 +17,7 @@ import { getForwardingEventProcessor } from './forwarding_event_processor'; import { EventDispatcher } from './event_dispatcher/event_dispatcher'; import { EventProcessor } from './event_processor'; import defaultEventDispatcher from './event_dispatcher/default_dispatcher.node'; -import { BatchEventProcessorOptions, FAILED_EVENT_RETRY_INTERVAL, getBatchEventProcessor, getPrefixEventStore } from './event_processor_factory'; +import { BatchEventProcessorOptions, FAILED_EVENT_RETRY_INTERVAL, getOpaqueBatchEventProcessor, getPrefixEventStore, OpaqueEventProcessor } from './event_processor_factory'; export const createForwardingEventProcessor = ( eventDispatcher: EventDispatcher = defaultEventDispatcher, @@ -28,10 +28,10 @@ export const createForwardingEventProcessor = ( export const createBatchEventProcessor = ( options: BatchEventProcessorOptions -): EventProcessor => { +): OpaqueEventProcessor => { const eventStore = options.eventStore ? getPrefixEventStore(options.eventStore) : undefined; - return getBatchEventProcessor({ + return getOpaqueBatchEventProcessor({ eventDispatcher: options.eventDispatcher || defaultEventDispatcher, closingEventDispatcher: options.closingEventDispatcher, flushInterval: options.flushInterval, diff --git a/lib/event_processor/event_processor_factory.react_native.ts b/lib/event_processor/event_processor_factory.react_native.ts index ce300ac79..24f054fad 100644 --- a/lib/event_processor/event_processor_factory.react_native.ts +++ b/lib/event_processor/event_processor_factory.react_native.ts @@ -17,7 +17,7 @@ import { getForwardingEventProcessor } from './forwarding_event_processor'; import { EventDispatcher } from './event_dispatcher/event_dispatcher'; import { EventProcessor } from './event_processor'; import defaultEventDispatcher from './event_dispatcher/default_dispatcher.browser'; -import { BatchEventProcessorOptions, getBatchEventProcessor, getPrefixEventStore } from './event_processor_factory'; +import { BatchEventProcessorOptions, getOpaqueBatchEventProcessor, getPrefixEventStore, OpaqueEventProcessor } from './event_processor_factory'; import { EVENT_STORE_PREFIX, FAILED_EVENT_RETRY_INTERVAL } from './event_processor_factory'; import { AsyncPrefixCache } from '../utils/cache/cache'; import { BatchEventProcessor, EventWithId } from './batch_event_processor'; @@ -48,10 +48,10 @@ const getDefaultEventStore = () => { export const createBatchEventProcessor = ( options: BatchEventProcessorOptions -): EventProcessor => { +): OpaqueEventProcessor => { const eventStore = options.eventStore ? getPrefixEventStore(options.eventStore) : getDefaultEventStore(); - return getBatchEventProcessor({ + return getOpaqueBatchEventProcessor({ eventDispatcher: options.eventDispatcher || defaultEventDispatcher, closingEventDispatcher: options.closingEventDispatcher, flushInterval: options.flushInterval, diff --git a/lib/event_processor/event_processor_factory.ts b/lib/event_processor/event_processor_factory.ts index 70f1b6310..20e7797d2 100644 --- a/lib/event_processor/event_processor_factory.ts +++ b/lib/event_processor/event_processor_factory.ts @@ -46,6 +46,12 @@ export const getPrefixEventStore = (cache: Cache): Cache => } }; +const eventProcessorSymbol: unique symbol = Symbol(); + +export type OpaqueEventProcessor = { + [eventProcessorSymbol]: unknown; +}; + export type BatchEventProcessorOptions = { eventDispatcher?: EventDispatcher; closingEventDispatcher?: EventDispatcher; @@ -118,4 +124,17 @@ export const getBatchEventProcessor = ( eventStore, startupLogs, }); -}; +} + +export const getOpaqueBatchEventProcessor = ( + options: BatchEventProcessorFactoryOptions, + EventProcessorConstructor: typeof BatchEventProcessor = BatchEventProcessor +): OpaqueEventProcessor => { + return { + [eventProcessorSymbol]: getBatchEventProcessor(options, EventProcessorConstructor), + }; +} + +export const extractEventProcessor = (eventProcessor: OpaqueEventProcessor): EventProcessor => { + return eventProcessor[eventProcessorSymbol] as EventProcessor; +} diff --git a/lib/odp/odp_manager_factory.browser.spec.ts b/lib/odp/odp_manager_factory.browser.spec.ts index 534046f94..16b4183c8 100644 --- a/lib/odp/odp_manager_factory.browser.spec.ts +++ b/lib/odp/odp_manager_factory.browser.spec.ts @@ -19,29 +19,32 @@ vi.mock('../utils/http_request_handler/request_handler.browser', () => { }); vi.mock('./odp_manager_factory', () => { - return { getOdpManager: vi.fn().mockImplementation(() => ({})) }; + return { + getOdpManager: vi.fn().mockImplementation(() => ({})), + getOpaqueOdpManager: vi.fn().mockImplementation(() => ({})) + }; }); import { describe, it, expect, beforeEach, vi } from 'vitest'; -import { getOdpManager, OdpManagerOptions } from './odp_manager_factory'; +import { getOpaqueOdpManager, OdpManagerOptions } from './odp_manager_factory'; import { BROWSER_DEFAULT_API_TIMEOUT, createOdpManager } from './odp_manager_factory.browser'; import { BrowserRequestHandler } from '../utils/http_request_handler/request_handler.browser'; import { pixelApiRequestGenerator } from './event_manager/odp_event_api_manager'; describe('createOdpManager', () => { const MockBrowserRequestHandler = vi.mocked(BrowserRequestHandler); - const mockGetOdpManager = vi.mocked(getOdpManager); + const mockGetOpaqueOdpManager = vi.mocked(getOpaqueOdpManager); beforeEach(() => { MockBrowserRequestHandler.mockClear(); - mockGetOdpManager.mockClear(); + mockGetOpaqueOdpManager.mockClear(); }); it('should use BrowserRequestHandler with the provided timeout as the segment request handler', () => { const odpManager = createOdpManager({ segmentsApiTimeout: 3456 }); - expect(odpManager).toBe(mockGetOdpManager.mock.results[0].value); - const { segmentRequestHandler } = mockGetOdpManager.mock.calls[0][0]; + expect(odpManager).toBe(mockGetOpaqueOdpManager.mock.results[0].value); + const { segmentRequestHandler } = mockGetOpaqueOdpManager.mock.calls[0][0]; expect(segmentRequestHandler).toBe(MockBrowserRequestHandler.mock.instances[0]); const requestHandlerOptions = MockBrowserRequestHandler.mock.calls[0][0]; expect(requestHandlerOptions?.timeout).toBe(3456); @@ -49,8 +52,8 @@ describe('createOdpManager', () => { it('should use BrowserRequestHandler with the browser default timeout as the segment request handler', () => { const odpManager = createOdpManager({}); - expect(odpManager).toBe(mockGetOdpManager.mock.results[0].value); - const { segmentRequestHandler } = mockGetOdpManager.mock.calls[0][0]; + expect(odpManager).toBe(mockGetOpaqueOdpManager.mock.results[0].value); + const { segmentRequestHandler } = mockGetOpaqueOdpManager.mock.calls[0][0]; expect(segmentRequestHandler).toBe(MockBrowserRequestHandler.mock.instances[0]); const requestHandlerOptions = MockBrowserRequestHandler.mock.calls[0][0]; expect(requestHandlerOptions?.timeout).toBe(BROWSER_DEFAULT_API_TIMEOUT); @@ -58,8 +61,8 @@ describe('createOdpManager', () => { it('should use BrowserRequestHandler with the provided timeout as the event request handler', () => { const odpManager = createOdpManager({ eventApiTimeout: 2345 }); - expect(odpManager).toBe(mockGetOdpManager.mock.results[0].value); - const { eventRequestHandler } = mockGetOdpManager.mock.calls[0][0]; + expect(odpManager).toBe(mockGetOpaqueOdpManager.mock.results[0].value); + const { eventRequestHandler } = mockGetOpaqueOdpManager.mock.calls[0][0]; expect(eventRequestHandler).toBe(MockBrowserRequestHandler.mock.instances[1]); const requestHandlerOptions = MockBrowserRequestHandler.mock.calls[1][0]; expect(requestHandlerOptions?.timeout).toBe(2345); @@ -67,8 +70,8 @@ describe('createOdpManager', () => { it('should use BrowserRequestHandler with the browser default timeout as the event request handler', () => { const odpManager = createOdpManager({}); - expect(odpManager).toBe(mockGetOdpManager.mock.results[0].value); - const { eventRequestHandler } = mockGetOdpManager.mock.calls[0][0]; + expect(odpManager).toBe(mockGetOpaqueOdpManager.mock.results[0].value); + const { eventRequestHandler } = mockGetOpaqueOdpManager.mock.calls[0][0]; expect(eventRequestHandler).toBe(MockBrowserRequestHandler.mock.instances[1]); const requestHandlerOptions = MockBrowserRequestHandler.mock.calls[1][0]; expect(requestHandlerOptions?.timeout).toBe(BROWSER_DEFAULT_API_TIMEOUT); @@ -76,22 +79,22 @@ describe('createOdpManager', () => { it('should use batchSize 1 if batchSize is not provided', () => { const odpManager = createOdpManager({}); - expect(odpManager).toBe(mockGetOdpManager.mock.results[0].value); - const { eventBatchSize } = mockGetOdpManager.mock.calls[0][0]; + expect(odpManager).toBe(mockGetOpaqueOdpManager.mock.results[0].value); + const { eventBatchSize } = mockGetOpaqueOdpManager.mock.calls[0][0]; expect(eventBatchSize).toBe(1); }); it('should use batchSize 1 event if some other batchSize value is provided', () => { const odpManager = createOdpManager({ eventBatchSize: 99 }); - expect(odpManager).toBe(mockGetOdpManager.mock.results[0].value); - const { eventBatchSize } = mockGetOdpManager.mock.calls[0][0]; + expect(odpManager).toBe(mockGetOpaqueOdpManager.mock.results[0].value); + const { eventBatchSize } = mockGetOpaqueOdpManager.mock.calls[0][0]; expect(eventBatchSize).toBe(1); }); it('uses the pixel api request generator', () => { const odpManager = createOdpManager({ }); - expect(odpManager).toBe(mockGetOdpManager.mock.results[0].value); - const { eventRequestGenerator } = mockGetOdpManager.mock.calls[0][0]; + expect(odpManager).toBe(mockGetOpaqueOdpManager.mock.results[0].value); + const { eventRequestGenerator } = mockGetOpaqueOdpManager.mock.calls[0][0]; expect(eventRequestGenerator).toBe(pixelApiRequestGenerator); }); @@ -106,7 +109,7 @@ describe('createOdpManager', () => { userAgentParser: {} as any, }; const odpManager = createOdpManager(options); - expect(odpManager).toBe(mockGetOdpManager.mock.results[0].value); - expect(mockGetOdpManager).toHaveBeenNthCalledWith(1, expect.objectContaining(options)); + expect(odpManager).toBe(mockGetOpaqueOdpManager.mock.results[0].value); + expect(mockGetOpaqueOdpManager).toHaveBeenNthCalledWith(1, expect.objectContaining(options)); }); }); diff --git a/lib/odp/odp_manager_factory.browser.ts b/lib/odp/odp_manager_factory.browser.ts index f70dfe976..2090dcb87 100644 --- a/lib/odp/odp_manager_factory.browser.ts +++ b/lib/odp/odp_manager_factory.browser.ts @@ -17,11 +17,11 @@ import { BrowserRequestHandler } from '../utils/http_request_handler/request_handler.browser'; import { pixelApiRequestGenerator } from './event_manager/odp_event_api_manager'; import { OdpManager } from './odp_manager'; -import { getOdpManager, OdpManagerOptions } from './odp_manager_factory'; +import { getOpaqueOdpManager, OdpManagerOptions, OpaqueOdpManager } from './odp_manager_factory'; export const BROWSER_DEFAULT_API_TIMEOUT = 10_000; -export const createOdpManager = (options: OdpManagerOptions): OdpManager => { +export const createOdpManager = (options: OdpManagerOptions): OpaqueOdpManager => { const segmentRequestHandler = new BrowserRequestHandler({ timeout: options.segmentsApiTimeout || BROWSER_DEFAULT_API_TIMEOUT, }); @@ -30,7 +30,7 @@ export const createOdpManager = (options: OdpManagerOptions): OdpManager => { timeout: options.eventApiTimeout || BROWSER_DEFAULT_API_TIMEOUT, }); - return getOdpManager({ + return getOpaqueOdpManager({ ...options, eventBatchSize: 1, segmentRequestHandler, diff --git a/lib/odp/odp_manager_factory.node.spec.ts b/lib/odp/odp_manager_factory.node.spec.ts index 491fd7520..f89d6ce94 100644 --- a/lib/odp/odp_manager_factory.node.spec.ts +++ b/lib/odp/odp_manager_factory.node.spec.ts @@ -19,29 +19,32 @@ vi.mock('../utils/http_request_handler/request_handler.node', () => { }); vi.mock('./odp_manager_factory', () => { - return { getOdpManager: vi.fn().mockImplementation(() => ({})) }; + return { + getOdpManager: vi.fn().mockImplementation(() => ({})), + getOpaqueOdpManager: vi.fn().mockImplementation(() => ({})) + }; }); import { describe, it, expect, beforeEach, vi } from 'vitest'; -import { getOdpManager, OdpManagerOptions } from './odp_manager_factory'; +import { getOpaqueOdpManager, OdpManagerOptions } from './odp_manager_factory'; import { NODE_DEFAULT_API_TIMEOUT, NODE_DEFAULT_BATCH_SIZE, NODE_DEFAULT_FLUSH_INTERVAL, createOdpManager } from './odp_manager_factory.node'; import { NodeRequestHandler } from '../utils/http_request_handler/request_handler.node'; import { eventApiRequestGenerator } from './event_manager/odp_event_api_manager'; describe('createOdpManager', () => { const MockNodeRequestHandler = vi.mocked(NodeRequestHandler); - const mockGetOdpManager = vi.mocked(getOdpManager); + const mockGetOpaqueOdpManager = vi.mocked(getOpaqueOdpManager); beforeEach(() => { MockNodeRequestHandler.mockClear(); - mockGetOdpManager.mockClear(); + mockGetOpaqueOdpManager.mockClear(); }); it('should use NodeRequestHandler with the provided timeout as the segment request handler', () => { const odpManager = createOdpManager({ segmentsApiTimeout: 3456 }); - expect(odpManager).toBe(mockGetOdpManager.mock.results[0].value); - const { segmentRequestHandler } = mockGetOdpManager.mock.calls[0][0]; + expect(odpManager).toBe(mockGetOpaqueOdpManager.mock.results[0].value); + const { segmentRequestHandler } = mockGetOpaqueOdpManager.mock.calls[0][0]; expect(segmentRequestHandler).toBe(MockNodeRequestHandler.mock.instances[0]); const requestHandlerOptions = MockNodeRequestHandler.mock.calls[0][0]; expect(requestHandlerOptions?.timeout).toBe(3456); @@ -49,8 +52,8 @@ describe('createOdpManager', () => { it('should use NodeRequestHandler with the node default timeout as the segment request handler', () => { const odpManager = createOdpManager({}); - expect(odpManager).toBe(mockGetOdpManager.mock.results[0].value); - const { segmentRequestHandler } = mockGetOdpManager.mock.calls[0][0]; + expect(odpManager).toBe(mockGetOpaqueOdpManager.mock.results[0].value); + const { segmentRequestHandler } = mockGetOpaqueOdpManager.mock.calls[0][0]; expect(segmentRequestHandler).toBe(MockNodeRequestHandler.mock.instances[0]); const requestHandlerOptions = MockNodeRequestHandler.mock.calls[0][0]; expect(requestHandlerOptions?.timeout).toBe(NODE_DEFAULT_API_TIMEOUT); @@ -58,8 +61,8 @@ describe('createOdpManager', () => { it('should use NodeRequestHandler with the provided timeout as the event request handler', () => { const odpManager = createOdpManager({ eventApiTimeout: 2345 }); - expect(odpManager).toBe(mockGetOdpManager.mock.results[0].value); - const { eventRequestHandler } = mockGetOdpManager.mock.calls[0][0]; + expect(odpManager).toBe(mockGetOpaqueOdpManager.mock.results[0].value); + const { eventRequestHandler } = mockGetOpaqueOdpManager.mock.calls[0][0]; expect(eventRequestHandler).toBe(MockNodeRequestHandler.mock.instances[1]); const requestHandlerOptions = MockNodeRequestHandler.mock.calls[1][0]; expect(requestHandlerOptions?.timeout).toBe(2345); @@ -67,8 +70,8 @@ describe('createOdpManager', () => { it('should use NodeRequestHandler with the node default timeout as the event request handler', () => { const odpManager = createOdpManager({}); - expect(odpManager).toBe(mockGetOdpManager.mock.results[0].value); - const { eventRequestHandler } = mockGetOdpManager.mock.calls[0][0]; + expect(odpManager).toBe(mockGetOpaqueOdpManager.mock.results[0].value); + const { eventRequestHandler } = mockGetOpaqueOdpManager.mock.calls[0][0]; expect(eventRequestHandler).toBe(MockNodeRequestHandler.mock.instances[1]); const requestHandlerOptions = MockNodeRequestHandler.mock.calls[1][0]; expect(requestHandlerOptions?.timeout).toBe(NODE_DEFAULT_API_TIMEOUT); @@ -76,36 +79,36 @@ describe('createOdpManager', () => { it('uses the event api request generator', () => { const odpManager = createOdpManager({ }); - expect(odpManager).toBe(mockGetOdpManager.mock.results[0].value); - const { eventRequestGenerator } = mockGetOdpManager.mock.calls[0][0]; + expect(odpManager).toBe(mockGetOpaqueOdpManager.mock.results[0].value); + const { eventRequestGenerator } = mockGetOpaqueOdpManager.mock.calls[0][0]; expect(eventRequestGenerator).toBe(eventApiRequestGenerator); }); it('should use the provided eventBatchSize', () => { const odpManager = createOdpManager({ eventBatchSize: 99 }); - expect(odpManager).toBe(mockGetOdpManager.mock.results[0].value); - const { eventBatchSize } = mockGetOdpManager.mock.calls[0][0]; + expect(odpManager).toBe(mockGetOpaqueOdpManager.mock.results[0].value); + const { eventBatchSize } = mockGetOpaqueOdpManager.mock.calls[0][0]; expect(eventBatchSize).toBe(99); }); it('should use the node default eventBatchSize if none provided', () => { const odpManager = createOdpManager({}); - expect(odpManager).toBe(mockGetOdpManager.mock.results[0].value); - const { eventBatchSize } = mockGetOdpManager.mock.calls[0][0]; + expect(odpManager).toBe(mockGetOpaqueOdpManager.mock.results[0].value); + const { eventBatchSize } = mockGetOpaqueOdpManager.mock.calls[0][0]; expect(eventBatchSize).toBe(NODE_DEFAULT_BATCH_SIZE); }); it('should use the provided eventFlushInterval', () => { const odpManager = createOdpManager({ eventFlushInterval: 9999 }); - expect(odpManager).toBe(mockGetOdpManager.mock.results[0].value); - const { eventFlushInterval } = mockGetOdpManager.mock.calls[0][0]; + expect(odpManager).toBe(mockGetOpaqueOdpManager.mock.results[0].value); + const { eventFlushInterval } = mockGetOpaqueOdpManager.mock.calls[0][0]; expect(eventFlushInterval).toBe(9999); }); it('should use the node default eventFlushInterval if none provided', () => { const odpManager = createOdpManager({}); - expect(odpManager).toBe(mockGetOdpManager.mock.results[0].value); - const { eventFlushInterval } = mockGetOdpManager.mock.calls[0][0]; + expect(odpManager).toBe(mockGetOpaqueOdpManager.mock.results[0].value); + const { eventFlushInterval } = mockGetOpaqueOdpManager.mock.calls[0][0]; expect(eventFlushInterval).toBe(NODE_DEFAULT_FLUSH_INTERVAL); }); @@ -119,7 +122,7 @@ describe('createOdpManager', () => { userAgentParser: {} as any, }; const odpManager = createOdpManager(options); - expect(odpManager).toBe(mockGetOdpManager.mock.results[0].value); - expect(mockGetOdpManager).toHaveBeenNthCalledWith(1, expect.objectContaining(options)); + expect(odpManager).toBe(mockGetOpaqueOdpManager.mock.results[0].value); + expect(mockGetOpaqueOdpManager).toHaveBeenNthCalledWith(1, expect.objectContaining(options)); }); }); diff --git a/lib/odp/odp_manager_factory.node.ts b/lib/odp/odp_manager_factory.node.ts index f2438dbd9..35d223462 100644 --- a/lib/odp/odp_manager_factory.node.ts +++ b/lib/odp/odp_manager_factory.node.ts @@ -17,13 +17,13 @@ import { NodeRequestHandler } from '../utils/http_request_handler/request_handler.node'; import { eventApiRequestGenerator } from './event_manager/odp_event_api_manager'; import { OdpManager } from './odp_manager'; -import { getOdpManager, OdpManagerOptions } from './odp_manager_factory'; +import { getOpaqueOdpManager, OdpManagerOptions, OpaqueOdpManager } from './odp_manager_factory'; export const NODE_DEFAULT_API_TIMEOUT = 10_000; export const NODE_DEFAULT_BATCH_SIZE = 10; export const NODE_DEFAULT_FLUSH_INTERVAL = 1000; -export const createOdpManager = (options: OdpManagerOptions): OdpManager => { +export const createOdpManager = (options: OdpManagerOptions): OpaqueOdpManager => { const segmentRequestHandler = new NodeRequestHandler({ timeout: options.segmentsApiTimeout || NODE_DEFAULT_API_TIMEOUT, }); @@ -32,7 +32,7 @@ export const createOdpManager = (options: OdpManagerOptions): OdpManager => { timeout: options.eventApiTimeout || NODE_DEFAULT_API_TIMEOUT, }); - return getOdpManager({ + return getOpaqueOdpManager({ ...options, segmentRequestHandler, eventRequestHandler, diff --git a/lib/odp/odp_manager_factory.react_native.spec.ts b/lib/odp/odp_manager_factory.react_native.spec.ts index 640e9cf4e..fd703d362 100644 --- a/lib/odp/odp_manager_factory.react_native.spec.ts +++ b/lib/odp/odp_manager_factory.react_native.spec.ts @@ -19,29 +19,32 @@ vi.mock('../utils/http_request_handler/request_handler.browser', () => { }); vi.mock('./odp_manager_factory', () => { - return { getOdpManager: vi.fn().mockImplementation(() => ({})) }; + return { + getOdpManager: vi.fn().mockImplementation(() => ({})), + getOpaqueOdpManager: vi.fn().mockImplementation(() => ({})), + }; }); import { describe, it, expect, beforeEach, vi } from 'vitest'; -import { getOdpManager, OdpManagerOptions } from './odp_manager_factory'; +import { getOpaqueOdpManager, OdpManagerOptions } from './odp_manager_factory'; import { RN_DEFAULT_API_TIMEOUT, RN_DEFAULT_BATCH_SIZE, RN_DEFAULT_FLUSH_INTERVAL, createOdpManager } from './odp_manager_factory.react_native'; import { BrowserRequestHandler } from '../utils/http_request_handler/request_handler.browser' import { eventApiRequestGenerator } from './event_manager/odp_event_api_manager'; describe('createOdpManager', () => { const MockBrowserRequestHandler = vi.mocked(BrowserRequestHandler); - const mockGetOdpManager = vi.mocked(getOdpManager); + const mockGetOpaqueOdpManager = vi.mocked(getOpaqueOdpManager); beforeEach(() => { MockBrowserRequestHandler.mockClear(); - mockGetOdpManager.mockClear(); + mockGetOpaqueOdpManager.mockClear(); }); it('should use BrowserRequestHandler with the provided timeout as the segment request handler', () => { const odpManager = createOdpManager({ segmentsApiTimeout: 3456 }); - expect(odpManager).toBe(mockGetOdpManager.mock.results[0].value); - const { segmentRequestHandler } = mockGetOdpManager.mock.calls[0][0]; + expect(odpManager).toBe(mockGetOpaqueOdpManager.mock.results[0].value); + const { segmentRequestHandler } = mockGetOpaqueOdpManager.mock.calls[0][0]; expect(segmentRequestHandler).toBe(MockBrowserRequestHandler.mock.instances[0]); const requestHandlerOptions = MockBrowserRequestHandler.mock.calls[0][0]; expect(requestHandlerOptions?.timeout).toBe(3456); @@ -49,8 +52,8 @@ describe('createOdpManager', () => { it('should use BrowserRequestHandler with the node default timeout as the segment request handler', () => { const odpManager = createOdpManager({}); - expect(odpManager).toBe(mockGetOdpManager.mock.results[0].value); - const { segmentRequestHandler } = mockGetOdpManager.mock.calls[0][0]; + expect(odpManager).toBe(mockGetOpaqueOdpManager.mock.results[0].value); + const { segmentRequestHandler } = mockGetOpaqueOdpManager.mock.calls[0][0]; expect(segmentRequestHandler).toBe(MockBrowserRequestHandler.mock.instances[0]); const requestHandlerOptions = MockBrowserRequestHandler.mock.calls[0][0]; expect(requestHandlerOptions?.timeout).toBe(RN_DEFAULT_API_TIMEOUT); @@ -58,8 +61,8 @@ describe('createOdpManager', () => { it('should use BrowserRequestHandler with the provided timeout as the event request handler', () => { const odpManager = createOdpManager({ eventApiTimeout: 2345 }); - expect(odpManager).toBe(mockGetOdpManager.mock.results[0].value); - const { eventRequestHandler } = mockGetOdpManager.mock.calls[0][0]; + expect(odpManager).toBe(mockGetOpaqueOdpManager.mock.results[0].value); + const { eventRequestHandler } = mockGetOpaqueOdpManager.mock.calls[0][0]; expect(eventRequestHandler).toBe(MockBrowserRequestHandler.mock.instances[1]); const requestHandlerOptions = MockBrowserRequestHandler.mock.calls[1][0]; expect(requestHandlerOptions?.timeout).toBe(2345); @@ -67,8 +70,8 @@ describe('createOdpManager', () => { it('should use BrowserRequestHandler with the node default timeout as the event request handler', () => { const odpManager = createOdpManager({}); - expect(odpManager).toBe(mockGetOdpManager.mock.results[0].value); - const { eventRequestHandler } = mockGetOdpManager.mock.calls[0][0]; + expect(odpManager).toBe(mockGetOpaqueOdpManager.mock.results[0].value); + const { eventRequestHandler } = mockGetOpaqueOdpManager.mock.calls[0][0]; expect(eventRequestHandler).toBe(MockBrowserRequestHandler.mock.instances[1]); const requestHandlerOptions = MockBrowserRequestHandler.mock.calls[1][0]; expect(requestHandlerOptions?.timeout).toBe(RN_DEFAULT_API_TIMEOUT); @@ -76,36 +79,36 @@ describe('createOdpManager', () => { it('uses the event api request generator', () => { const odpManager = createOdpManager({ }); - expect(odpManager).toBe(mockGetOdpManager.mock.results[0].value); - const { eventRequestGenerator } = mockGetOdpManager.mock.calls[0][0]; + expect(odpManager).toBe(mockGetOpaqueOdpManager.mock.results[0].value); + const { eventRequestGenerator } = mockGetOpaqueOdpManager.mock.calls[0][0]; expect(eventRequestGenerator).toBe(eventApiRequestGenerator); }); it('should use the provided eventBatchSize', () => { const odpManager = createOdpManager({ eventBatchSize: 99 }); - expect(odpManager).toBe(mockGetOdpManager.mock.results[0].value); - const { eventBatchSize } = mockGetOdpManager.mock.calls[0][0]; + expect(odpManager).toBe(mockGetOpaqueOdpManager.mock.results[0].value); + const { eventBatchSize } = mockGetOpaqueOdpManager.mock.calls[0][0]; expect(eventBatchSize).toBe(99); }); it('should use the react_native default eventBatchSize if none provided', () => { const odpManager = createOdpManager({}); - expect(odpManager).toBe(mockGetOdpManager.mock.results[0].value); - const { eventBatchSize } = mockGetOdpManager.mock.calls[0][0]; + expect(odpManager).toBe(mockGetOpaqueOdpManager.mock.results[0].value); + const { eventBatchSize } = mockGetOpaqueOdpManager.mock.calls[0][0]; expect(eventBatchSize).toBe(RN_DEFAULT_BATCH_SIZE); }); it('should use the provided eventFlushInterval', () => { const odpManager = createOdpManager({ eventFlushInterval: 9999 }); - expect(odpManager).toBe(mockGetOdpManager.mock.results[0].value); - const { eventFlushInterval } = mockGetOdpManager.mock.calls[0][0]; + expect(odpManager).toBe(mockGetOpaqueOdpManager.mock.results[0].value); + const { eventFlushInterval } = mockGetOpaqueOdpManager.mock.calls[0][0]; expect(eventFlushInterval).toBe(9999); }); it('should use the react_native default eventFlushInterval if none provided', () => { const odpManager = createOdpManager({}); - expect(odpManager).toBe(mockGetOdpManager.mock.results[0].value); - const { eventFlushInterval } = mockGetOdpManager.mock.calls[0][0]; + expect(odpManager).toBe(mockGetOpaqueOdpManager.mock.results[0].value); + const { eventFlushInterval } = mockGetOpaqueOdpManager.mock.calls[0][0]; expect(eventFlushInterval).toBe(RN_DEFAULT_FLUSH_INTERVAL); }); @@ -119,7 +122,7 @@ describe('createOdpManager', () => { userAgentParser: {} as any, }; const odpManager = createOdpManager(options); - expect(odpManager).toBe(mockGetOdpManager.mock.results[0].value); - expect(mockGetOdpManager).toHaveBeenNthCalledWith(1, expect.objectContaining(options)); + expect(odpManager).toBe(mockGetOpaqueOdpManager.mock.results[0].value); + expect(mockGetOpaqueOdpManager).toHaveBeenNthCalledWith(1, expect.objectContaining(options)); }); }); diff --git a/lib/odp/odp_manager_factory.react_native.ts b/lib/odp/odp_manager_factory.react_native.ts index 1ba0bcc5c..854ba32bc 100644 --- a/lib/odp/odp_manager_factory.react_native.ts +++ b/lib/odp/odp_manager_factory.react_native.ts @@ -17,13 +17,13 @@ import { BrowserRequestHandler } from '../utils/http_request_handler/request_handler.browser'; import { eventApiRequestGenerator } from './event_manager/odp_event_api_manager'; import { OdpManager } from './odp_manager'; -import { getOdpManager, OdpManagerOptions } from './odp_manager_factory'; +import { getOpaqueOdpManager, OdpManagerOptions, OpaqueOdpManager } from './odp_manager_factory'; export const RN_DEFAULT_API_TIMEOUT = 10_000; export const RN_DEFAULT_BATCH_SIZE = 10; export const RN_DEFAULT_FLUSH_INTERVAL = 1000; -export const createOdpManager = (options: OdpManagerOptions): OdpManager => { +export const createOdpManager = (options: OdpManagerOptions): OpaqueOdpManager => { const segmentRequestHandler = new BrowserRequestHandler({ timeout: options.segmentsApiTimeout || RN_DEFAULT_API_TIMEOUT, }); @@ -32,7 +32,7 @@ export const createOdpManager = (options: OdpManagerOptions): OdpManager => { timeout: options.eventApiTimeout || RN_DEFAULT_API_TIMEOUT, }); - return getOdpManager({ + return getOpaqueOdpManager({ ...options, segmentRequestHandler, eventRequestHandler, diff --git a/lib/odp/odp_manager_factory.ts b/lib/odp/odp_manager_factory.ts index 31d908df1..12d229d4b 100644 --- a/lib/odp/odp_manager_factory.ts +++ b/lib/odp/odp_manager_factory.ts @@ -34,6 +34,12 @@ export const DEFAULT_EVENT_MAX_RETRIES = 5; export const DEFAULT_EVENT_MIN_BACKOFF = 1000; export const DEFAULT_EVENT_MAX_BACKOFF = 32_000; +const odpManagerSymbol: unique symbol = Symbol(); + +export type OpaqueOdpManager = { + [odpManagerSymbol]: unknown; +}; + export type OdpManagerOptions = { segmentsCache?: Cache; segmentsCacheSize?: number; @@ -93,3 +99,13 @@ export const getOdpManager = (options: OdpManagerFactoryOptions): OdpManager => userAgentParser: options.userAgentParser, }); }; + +export const getOpaqueOdpManager = (options: OdpManagerFactoryOptions): OpaqueOdpManager => { + return { + [odpManagerSymbol]: getOdpManager(options), + }; +}; + +export const extractOdpManager = (manager: OpaqueOdpManager): OdpManager => { + return manager[odpManagerSymbol] as OdpManager; +} diff --git a/lib/optimizely/index.spec.ts b/lib/optimizely/index.spec.ts index cb5210915..593cb84ba 100644 --- a/lib/optimizely/index.spec.ts +++ b/lib/optimizely/index.spec.ts @@ -24,6 +24,7 @@ import { LoggerFacade } from '../logging/logger'; import { createProjectConfig } from '../project_config/project_config'; import { getMockLogger } from '../tests/mock/mock_logger'; import { createOdpManager } from '../odp/odp_manager_factory.node'; +import { extractOdpManager } from '../odp/odp_manager_factory'; describe('Optimizely', () => { const eventDispatcher = { @@ -31,7 +32,7 @@ describe('Optimizely', () => { }; const eventProcessor = getForwardingEventProcessor(eventDispatcher); - const odpManager = createOdpManager({}); + const odpManager = extractOdpManager(createOdpManager({})); const logger = getMockLogger(); it('should pass disposable options to the respective services', () => { diff --git a/lib/project_config/config_manager_factory.browser.spec.ts b/lib/project_config/config_manager_factory.browser.spec.ts index 843111fb4..9dfa7bced 100644 --- a/lib/project_config/config_manager_factory.browser.spec.ts +++ b/lib/project_config/config_manager_factory.browser.spec.ts @@ -19,6 +19,7 @@ import { describe, it, expect, beforeEach, vi } from 'vitest'; vi.mock('./config_manager_factory', () => { return { getPollingConfigManager: vi.fn().mockReturnValueOnce({ foo: 'bar' }), + getOpaquePollingConfigManager: vi.fn().mockReturnValueOnce({ foo: 'bar' }), }; }); @@ -27,17 +28,17 @@ vi.mock('../utils/http_request_handler/request_handler.browser', () => { return { BrowserRequestHandler }; }); -import { getPollingConfigManager, PollingConfigManagerConfig, PollingConfigManagerFactoryOptions } from './config_manager_factory'; +import { getOpaquePollingConfigManager, PollingConfigManagerConfig, PollingConfigManagerFactoryOptions } from './config_manager_factory'; import { createPollingProjectConfigManager } from './config_manager_factory.browser'; import { BrowserRequestHandler } from '../utils/http_request_handler/request_handler.browser'; import { getMockSyncCache } from '../tests/mock/mock_cache'; describe('createPollingConfigManager', () => { - const mockGetPollingConfigManager = vi.mocked(getPollingConfigManager); + const mockGetOpaquePollingConfigManager = vi.mocked(getOpaquePollingConfigManager); const MockBrowserRequestHandler = vi.mocked(BrowserRequestHandler); beforeEach(() => { - mockGetPollingConfigManager.mockClear(); + mockGetOpaquePollingConfigManager.mockClear(); MockBrowserRequestHandler.mockClear(); }); @@ -47,7 +48,7 @@ describe('createPollingConfigManager', () => { }; const projectConfigManager = createPollingProjectConfigManager(config); - expect(Object.is(projectConfigManager, mockGetPollingConfigManager.mock.results[0].value)).toBe(true); + expect(Object.is(projectConfigManager, mockGetOpaquePollingConfigManager.mock.results[0].value)).toBe(true); }); it('uses an instance of BrowserRequestHandler as requestHandler', () => { @@ -56,7 +57,7 @@ describe('createPollingConfigManager', () => { }; const projectConfigManager = createPollingProjectConfigManager(config); - expect(Object.is(mockGetPollingConfigManager.mock.calls[0][0].requestHandler, MockBrowserRequestHandler.mock.instances[0])).toBe(true); + expect(Object.is(mockGetOpaquePollingConfigManager.mock.calls[0][0].requestHandler, MockBrowserRequestHandler.mock.instances[0])).toBe(true); }); it('uses uses autoUpdate = false by default', () => { @@ -65,7 +66,7 @@ describe('createPollingConfigManager', () => { }; const projectConfigManager = createPollingProjectConfigManager(config); - expect(mockGetPollingConfigManager.mock.calls[0][0].autoUpdate).toBe(false); + expect(mockGetOpaquePollingConfigManager.mock.calls[0][0].autoUpdate).toBe(false); }); it('uses the provided options', () => { @@ -81,6 +82,6 @@ describe('createPollingConfigManager', () => { }; const projectConfigManager = createPollingProjectConfigManager(config); - expect(mockGetPollingConfigManager).toHaveBeenNthCalledWith(1, expect.objectContaining(config)); + expect(mockGetOpaquePollingConfigManager).toHaveBeenNthCalledWith(1, expect.objectContaining(config)); }); }); diff --git a/lib/project_config/config_manager_factory.browser.ts b/lib/project_config/config_manager_factory.browser.ts index 8a5433bd5..0a96affd5 100644 --- a/lib/project_config/config_manager_factory.browser.ts +++ b/lib/project_config/config_manager_factory.browser.ts @@ -14,14 +14,14 @@ * limitations under the License. */ -import { getPollingConfigManager, PollingConfigManagerConfig } from './config_manager_factory'; +import { getOpaquePollingConfigManager, OpaqueConfigManager, PollingConfigManagerConfig } from './config_manager_factory'; import { BrowserRequestHandler } from '../utils/http_request_handler/request_handler.browser'; import { ProjectConfigManager } from './project_config_manager'; -export const createPollingProjectConfigManager = (config: PollingConfigManagerConfig): ProjectConfigManager => { +export const createPollingProjectConfigManager = (config: PollingConfigManagerConfig): OpaqueConfigManager => { const defaultConfig = { autoUpdate: false, requestHandler: new BrowserRequestHandler(), }; - return getPollingConfigManager({ ...defaultConfig, ...config }); + return getOpaquePollingConfigManager({ ...defaultConfig, ...config }); }; diff --git a/lib/project_config/config_manager_factory.node.spec.ts b/lib/project_config/config_manager_factory.node.spec.ts index 41dedd4f5..c0631a63b 100644 --- a/lib/project_config/config_manager_factory.node.spec.ts +++ b/lib/project_config/config_manager_factory.node.spec.ts @@ -19,6 +19,7 @@ import { describe, it, expect, beforeEach, vi } from 'vitest'; vi.mock('./config_manager_factory', () => { return { getPollingConfigManager: vi.fn().mockReturnValueOnce({ foo: 'bar' }), + getOpaquePollingConfigManager: vi.fn().mockReturnValueOnce({ foo: 'bar' }), }; }); @@ -27,17 +28,17 @@ vi.mock('../utils/http_request_handler/request_handler.node', () => { return { NodeRequestHandler }; }); -import { getPollingConfigManager, PollingConfigManagerConfig } from './config_manager_factory'; +import { getOpaquePollingConfigManager, PollingConfigManagerConfig } from './config_manager_factory'; import { createPollingProjectConfigManager } from './config_manager_factory.node'; import { NodeRequestHandler } from '../utils/http_request_handler/request_handler.node'; import { getMockSyncCache } from '../tests/mock/mock_cache'; describe('createPollingConfigManager', () => { - const mockGetPollingConfigManager = vi.mocked(getPollingConfigManager); + const mockGetOpaquePollingConfigManager = vi.mocked(getOpaquePollingConfigManager); const MockNodeRequestHandler = vi.mocked(NodeRequestHandler); beforeEach(() => { - mockGetPollingConfigManager.mockClear(); + mockGetOpaquePollingConfigManager.mockClear(); MockNodeRequestHandler.mockClear(); }); @@ -47,7 +48,7 @@ describe('createPollingConfigManager', () => { }; const projectConfigManager = createPollingProjectConfigManager(config); - expect(Object.is(projectConfigManager, mockGetPollingConfigManager.mock.results[0].value)).toBe(true); + expect(Object.is(projectConfigManager, mockGetOpaquePollingConfigManager.mock.results[0].value)).toBe(true); }); it('uses an instance of NodeRequestHandler as requestHandler', () => { @@ -56,7 +57,7 @@ describe('createPollingConfigManager', () => { }; const projectConfigManager = createPollingProjectConfigManager(config); - expect(Object.is(mockGetPollingConfigManager.mock.calls[0][0].requestHandler, MockNodeRequestHandler.mock.instances[0])).toBe(true); + expect(Object.is(mockGetOpaquePollingConfigManager.mock.calls[0][0].requestHandler, MockNodeRequestHandler.mock.instances[0])).toBe(true); }); it('uses uses autoUpdate = true by default', () => { @@ -65,7 +66,7 @@ describe('createPollingConfigManager', () => { }; const projectConfigManager = createPollingProjectConfigManager(config); - expect(mockGetPollingConfigManager.mock.calls[0][0].autoUpdate).toBe(true); + expect(mockGetOpaquePollingConfigManager.mock.calls[0][0].autoUpdate).toBe(true); }); it('uses the provided options', () => { @@ -81,6 +82,6 @@ describe('createPollingConfigManager', () => { }; const projectConfigManager = createPollingProjectConfigManager(config); - expect(mockGetPollingConfigManager).toHaveBeenNthCalledWith(1, expect.objectContaining(config)); + expect(mockGetOpaquePollingConfigManager).toHaveBeenNthCalledWith(1, expect.objectContaining(config)); }); }); diff --git a/lib/project_config/config_manager_factory.node.ts b/lib/project_config/config_manager_factory.node.ts index 241927c5a..58ac126bc 100644 --- a/lib/project_config/config_manager_factory.node.ts +++ b/lib/project_config/config_manager_factory.node.ts @@ -14,15 +14,15 @@ * limitations under the License. */ -import { getPollingConfigManager, PollingConfigManagerConfig } from "./config_manager_factory"; +import { getOpaquePollingConfigManager, OpaqueConfigManager, PollingConfigManagerConfig } from "./config_manager_factory"; import { NodeRequestHandler } from "../utils/http_request_handler/request_handler.node"; import { ProjectConfigManager } from "./project_config_manager"; import { DEFAULT_URL_TEMPLATE, DEFAULT_AUTHENTICATED_URL_TEMPLATE } from './constant'; -export const createPollingProjectConfigManager = (config: PollingConfigManagerConfig): ProjectConfigManager => { +export const createPollingProjectConfigManager = (config: PollingConfigManagerConfig): OpaqueConfigManager => { const defaultConfig = { autoUpdate: true, requestHandler: new NodeRequestHandler(), }; - return getPollingConfigManager({ ...defaultConfig, ...config }); + return getOpaquePollingConfigManager({ ...defaultConfig, ...config }); }; diff --git a/lib/project_config/config_manager_factory.react_native.spec.ts b/lib/project_config/config_manager_factory.react_native.spec.ts index 6550fa0c6..52411861d 100644 --- a/lib/project_config/config_manager_factory.react_native.spec.ts +++ b/lib/project_config/config_manager_factory.react_native.spec.ts @@ -39,6 +39,7 @@ async function mockRequireAsyncStorage() { vi.mock('./config_manager_factory', () => { return { getPollingConfigManager: vi.fn().mockReturnValueOnce({ foo: 'bar' }), + getOpaquePollingConfigManager: vi.fn().mockRejectedValueOnce({ foo: 'bar' }), }; }); @@ -56,19 +57,19 @@ vi.mock('../utils/cache/async_storage_cache.react_native', async (importOriginal return { AsyncStorageCache: MockAsyncStorageCache }; }); -import { getPollingConfigManager, PollingConfigManagerConfig } from './config_manager_factory'; +import { getOpaquePollingConfigManager, getPollingConfigManager, PollingConfigManagerConfig } from './config_manager_factory'; import { createPollingProjectConfigManager } from './config_manager_factory.react_native'; import { BrowserRequestHandler } from '../utils/http_request_handler/request_handler.browser'; import { AsyncStorageCache } from '../utils/cache/async_storage_cache.react_native'; import { getMockSyncCache } from '../tests/mock/mock_cache'; describe('createPollingConfigManager', () => { - const mockGetPollingConfigManager = vi.mocked(getPollingConfigManager); + const mockGetOpaquePollingConfigManager = vi.mocked(getOpaquePollingConfigManager); const MockBrowserRequestHandler = vi.mocked(BrowserRequestHandler); const MockAsyncStorageCache = vi.mocked(AsyncStorageCache); beforeEach(() => { - mockGetPollingConfigManager.mockClear(); + mockGetOpaquePollingConfigManager.mockClear(); MockBrowserRequestHandler.mockClear(); MockAsyncStorageCache.mockClear(); }); @@ -79,7 +80,7 @@ describe('createPollingConfigManager', () => { }; const projectConfigManager = createPollingProjectConfigManager(config); - expect(Object.is(projectConfigManager, mockGetPollingConfigManager.mock.results[0].value)).toBe(true); + expect(Object.is(projectConfigManager, mockGetOpaquePollingConfigManager.mock.results[0].value)).toBe(true); }); it('uses an instance of BrowserRequestHandler as requestHandler', () => { @@ -91,7 +92,7 @@ describe('createPollingConfigManager', () => { expect( Object.is( - mockGetPollingConfigManager.mock.calls[0][0].requestHandler, + mockGetOpaquePollingConfigManager.mock.calls[0][0].requestHandler, MockBrowserRequestHandler.mock.instances[0] ) ).toBe(true); @@ -104,7 +105,7 @@ describe('createPollingConfigManager', () => { createPollingProjectConfigManager(config); - expect(mockGetPollingConfigManager.mock.calls[0][0].autoUpdate).toBe(true); + expect(mockGetOpaquePollingConfigManager.mock.calls[0][0].autoUpdate).toBe(true); }); it('uses an instance of ReactNativeAsyncStorageCache for caching by default', () => { @@ -115,7 +116,7 @@ describe('createPollingConfigManager', () => { createPollingProjectConfigManager(config); expect( - Object.is(mockGetPollingConfigManager.mock.calls[0][0].cache, MockAsyncStorageCache.mock.instances[0]) + Object.is(mockGetOpaquePollingConfigManager.mock.calls[0][0].cache, MockAsyncStorageCache.mock.instances[0]) ).toBe(true); }); @@ -133,7 +134,7 @@ describe('createPollingConfigManager', () => { createPollingProjectConfigManager(config); - expect(mockGetPollingConfigManager).toHaveBeenNthCalledWith(1, expect.objectContaining(config)); + expect(mockGetOpaquePollingConfigManager).toHaveBeenNthCalledWith(1, expect.objectContaining(config)); }); it('Should not throw error if a cache is present in the config, and async storage is not available', async () => { diff --git a/lib/project_config/config_manager_factory.react_native.ts b/lib/project_config/config_manager_factory.react_native.ts index 6edcbbe3f..8ea480595 100644 --- a/lib/project_config/config_manager_factory.react_native.ts +++ b/lib/project_config/config_manager_factory.react_native.ts @@ -14,17 +14,19 @@ * limitations under the License. */ -import { getPollingConfigManager, PollingConfigManagerConfig } from "./config_manager_factory"; +import { getOpaquePollingConfigManager, PollingConfigManagerConfig } from "./config_manager_factory"; import { BrowserRequestHandler } from "../utils/http_request_handler/request_handler.browser"; import { ProjectConfigManager } from "./project_config_manager"; import { AsyncStorageCache } from "../utils/cache/async_storage_cache.react_native"; -export const createPollingProjectConfigManager = (config: PollingConfigManagerConfig): ProjectConfigManager => { +import { OpaqueConfigManager } from "./config_manager_factory"; + +export const createPollingProjectConfigManager = (config: PollingConfigManagerConfig): OpaqueConfigManager => { const defaultConfig = { autoUpdate: true, requestHandler: new BrowserRequestHandler(), cache: config.cache || new AsyncStorageCache(), }; - return getPollingConfigManager({ ...defaultConfig, ...config }); + return getOpaquePollingConfigManager({ ...defaultConfig, ...config }); }; diff --git a/lib/project_config/config_manager_factory.ts b/lib/project_config/config_manager_factory.ts index 141952148..3ce76918b 100644 --- a/lib/project_config/config_manager_factory.ts +++ b/lib/project_config/config_manager_factory.ts @@ -26,6 +26,12 @@ import { StartupLog } from "../service"; import { MIN_UPDATE_INTERVAL, UPDATE_INTERVAL_BELOW_MINIMUM_MESSAGE } from './constant'; import { LogLevel } from '../logging/logger' +const configManagerSymbol: unique symbol = Symbol(); + +export type OpaqueConfigManager = { + [configManagerSymbol]: unknown; +}; + export type StaticConfigManagerConfig = { datafile: string, jsonSchemaValidator?: Transformer, @@ -33,8 +39,10 @@ export type StaticConfigManagerConfig = { export const createStaticProjectConfigManager = ( config: StaticConfigManagerConfig -): ProjectConfigManager => { - return new ProjectConfigManagerImpl(config); +): OpaqueConfigManager => { + return { + [configManagerSymbol]: new ProjectConfigManagerImpl(config), + } }; export type PollingConfigManagerConfig = { @@ -87,3 +95,13 @@ export const getPollingConfigManager = ( jsonSchemaValidator: opt.jsonSchemaValidator, }); }; + +export const getOpaquePollingConfigManager = (opt: PollingConfigManagerFactoryOptions): OpaqueConfigManager => { + return { + [configManagerSymbol]: getPollingConfigManager(opt), + }; +}; + +export const extractConfigManager = (opaqueConfigManager: OpaqueConfigManager): ProjectConfigManager => { + return opaqueConfigManager[configManagerSymbol] as ProjectConfigManager; +}; diff --git a/package.json b/package.json index 2d97998df..e7a8fbd77 100644 --- a/package.json +++ b/package.json @@ -73,7 +73,7 @@ "clean": "rm -rf dist", "clean:win": "(if exist dist rd /s/q dist)", "lint": "tsc --noEmit && eslint 'lib/**/*.js' 'lib/**/*.ts'", - "test-vitest": "tsc --noEmit --p tsconfig.spec.json && vitest run", + "test-vitest": "vitest run", "test-mocha": "TS_NODE_COMPILER_OPTIONS='{\"module\": \"commonjs\" }' mocha -r ts-node/register -r tsconfig-paths/register -r lib/tests/exit_on_unhandled_rejection.js 'lib/**/*.tests.ts' 'lib/**/*.tests.js'", "test": "npm run test-mocha && npm run test-vitest", "posttest": "npm run lint", diff --git a/vitest.config.mts b/vitest.config.mts index 05669feb1..584eeb60d 100644 --- a/vitest.config.mts +++ b/vitest.config.mts @@ -28,6 +28,7 @@ export default defineConfig({ environment: 'happy-dom', include: ['**/*.spec.ts'], typecheck: { + enabled: true, tsconfig: 'tsconfig.spec.json', }, }, From 3be3d9b614720d922150cece9d026e60a1496f29 Mon Sep 17 00:00:00 2001 From: Raju Ahmed Date: Sat, 8 Feb 2025 00:32:31 +0600 Subject: [PATCH 2/5] upd --- .../event_processor_factory.browser.ts | 11 ++++++++--- .../event_processor_factory.node.ts | 15 ++++++++++----- .../event_processor_factory.react_native.ts | 12 +++++++++--- lib/event_processor/event_processor_factory.ts | 10 +++++++--- 4 files changed, 34 insertions(+), 14 deletions(-) diff --git a/lib/event_processor/event_processor_factory.browser.ts b/lib/event_processor/event_processor_factory.browser.ts index 96f19b7fe..0ce034dc7 100644 --- a/lib/event_processor/event_processor_factory.browser.ts +++ b/lib/event_processor/event_processor_factory.browser.ts @@ -18,7 +18,12 @@ import { getForwardingEventProcessor } from './forwarding_event_processor'; import { EventDispatcher } from './event_dispatcher/event_dispatcher'; import { EventProcessor } from './event_processor'; import { EventWithId } from './batch_event_processor'; -import { getOpaqueBatchEventProcessor, BatchEventProcessorOptions, OpaqueEventProcessor } from './event_processor_factory'; +import { + getOpaqueBatchEventProcessor, + BatchEventProcessorOptions, + OpaqueEventProcessor, + wrapEventProcessor, +} from './event_processor_factory'; import defaultEventDispatcher from './event_dispatcher/default_dispatcher.browser'; import sendBeaconEventDispatcher from './event_dispatcher/send_beacon_dispatcher.browser'; import { LocalStorageCache } from '../utils/cache/local_storage_cache.browser'; @@ -27,8 +32,8 @@ import { EVENT_STORE_PREFIX, FAILED_EVENT_RETRY_INTERVAL } from './event_process export const createForwardingEventProcessor = ( eventDispatcher: EventDispatcher = defaultEventDispatcher, -): EventProcessor => { - return getForwardingEventProcessor(eventDispatcher); +): OpaqueEventProcessor => { + return wrapEventProcessor(getForwardingEventProcessor(eventDispatcher)); }; const identity = (v: T): T => v; diff --git a/lib/event_processor/event_processor_factory.node.ts b/lib/event_processor/event_processor_factory.node.ts index d95ab031d..4671ce3a3 100644 --- a/lib/event_processor/event_processor_factory.node.ts +++ b/lib/event_processor/event_processor_factory.node.ts @@ -15,17 +15,22 @@ */ import { getForwardingEventProcessor } from './forwarding_event_processor'; import { EventDispatcher } from './event_dispatcher/event_dispatcher'; -import { EventProcessor } from './event_processor'; import defaultEventDispatcher from './event_dispatcher/default_dispatcher.node'; -import { BatchEventProcessorOptions, FAILED_EVENT_RETRY_INTERVAL, getOpaqueBatchEventProcessor, getPrefixEventStore, OpaqueEventProcessor } from './event_processor_factory'; +import { + BatchEventProcessorOptions, + FAILED_EVENT_RETRY_INTERVAL, + getOpaqueBatchEventProcessor, + getPrefixEventStore, + OpaqueEventProcessor, + wrapEventProcessor, +} from './event_processor_factory'; export const createForwardingEventProcessor = ( eventDispatcher: EventDispatcher = defaultEventDispatcher, -): EventProcessor => { - return getForwardingEventProcessor(eventDispatcher); +): OpaqueEventProcessor => { + return wrapEventProcessor(getForwardingEventProcessor(eventDispatcher)); }; - export const createBatchEventProcessor = ( options: BatchEventProcessorOptions ): OpaqueEventProcessor => { diff --git a/lib/event_processor/event_processor_factory.react_native.ts b/lib/event_processor/event_processor_factory.react_native.ts index 24f054fad..fefb3f816 100644 --- a/lib/event_processor/event_processor_factory.react_native.ts +++ b/lib/event_processor/event_processor_factory.react_native.ts @@ -17,7 +17,13 @@ import { getForwardingEventProcessor } from './forwarding_event_processor'; import { EventDispatcher } from './event_dispatcher/event_dispatcher'; import { EventProcessor } from './event_processor'; import defaultEventDispatcher from './event_dispatcher/default_dispatcher.browser'; -import { BatchEventProcessorOptions, getOpaqueBatchEventProcessor, getPrefixEventStore, OpaqueEventProcessor } from './event_processor_factory'; +import { + BatchEventProcessorOptions, + getOpaqueBatchEventProcessor, + getPrefixEventStore, + OpaqueEventProcessor, + wrapEventProcessor, +} from './event_processor_factory'; import { EVENT_STORE_PREFIX, FAILED_EVENT_RETRY_INTERVAL } from './event_processor_factory'; import { AsyncPrefixCache } from '../utils/cache/cache'; import { BatchEventProcessor, EventWithId } from './batch_event_processor'; @@ -27,8 +33,8 @@ import { isAvailable as isNetInfoAvailable } from '../utils/import.react_native/ export const createForwardingEventProcessor = ( eventDispatcher: EventDispatcher = defaultEventDispatcher, -): EventProcessor => { - return getForwardingEventProcessor(eventDispatcher); +): OpaqueEventProcessor => { + return wrapEventProcessor(getForwardingEventProcessor(eventDispatcher)); }; const identity = (v: T): T => v; diff --git a/lib/event_processor/event_processor_factory.ts b/lib/event_processor/event_processor_factory.ts index 20e7797d2..fe7f838f7 100644 --- a/lib/event_processor/event_processor_factory.ts +++ b/lib/event_processor/event_processor_factory.ts @@ -126,13 +126,17 @@ export const getBatchEventProcessor = ( }); } +export const wrapEventProcessor = (eventProcessor: EventProcessor): OpaqueEventProcessor => { + return { + [eventProcessorSymbol]: eventProcessor, + }; +} + export const getOpaqueBatchEventProcessor = ( options: BatchEventProcessorFactoryOptions, EventProcessorConstructor: typeof BatchEventProcessor = BatchEventProcessor ): OpaqueEventProcessor => { - return { - [eventProcessorSymbol]: getBatchEventProcessor(options, EventProcessorConstructor), - }; + return wrapEventProcessor(getBatchEventProcessor(options, EventProcessorConstructor)); } export const extractEventProcessor = (eventProcessor: OpaqueEventProcessor): EventProcessor => { From 870510dec8d18c4d6b75d94359730a5572554d1e Mon Sep 17 00:00:00 2001 From: Raju Ahmed Date: Thu, 13 Feb 2025 22:10:25 +0600 Subject: [PATCH 3/5] update --- lib/client_factory.ts | 60 +++++++++++ lib/entrypoint.test-d.ts | 13 ++- .../event_processor_factory.browser.spec.ts | 65 ++++++------ .../event_processor_factory.node.spec.ts | 73 +++++++------ ...ent_processor_factory.react_native.spec.ts | 77 +++++++------- lib/index.browser.tests.js | 100 ++++++++---------- lib/index.browser.ts | 81 +++----------- lib/index.node.tests.js | 8 +- lib/index.node.ts | 47 ++------ lib/index.react_native.spec.ts | 20 ++-- lib/index.react_native.ts | 55 ++-------- lib/logging/logger_factory.ts | 9 +- lib/project_config/config_manager_factory.ts | 6 ++ lib/shared_types.ts | 13 ++- lib/utils/enums/index.ts | 2 - lib/vuid/vuid_manager_factory.browser.spec.ts | 15 +-- lib/vuid/vuid_manager_factory.browser.ts | 8 +- lib/vuid/vuid_manager_factory.node.ts | 5 +- .../vuid_manager_factory.react_native.spec.ts | 15 +-- lib/vuid/vuid_manager_factory.react_native.ts | 8 +- lib/vuid/vuid_manager_factory.ts | 17 +++ 21 files changed, 333 insertions(+), 364 deletions(-) create mode 100644 lib/client_factory.ts diff --git a/lib/client_factory.ts b/lib/client_factory.ts new file mode 100644 index 000000000..04a5567ed --- /dev/null +++ b/lib/client_factory.ts @@ -0,0 +1,60 @@ +import { LoggerFacade } from "./logging/logger"; +import { Client, Config } from "./shared_types"; +import { Maybe } from "./utils/type"; +import configValidator from './utils/config_validator'; +import { extractLogger } from "./logging/logger_factory"; +import { extractErrorNotifier } from "./error/error_notifier_factory"; +import { extractConfigManager } from "./project_config/config_manager_factory"; +import { extractEventProcessor } from "./event_processor/event_processor_factory"; +import { extractOdpManager } from "./odp/odp_manager_factory"; +import { extractVuidManager } from "./vuid/vuid_manager_factory"; + +import { CLIENT_VERSION, JAVASCRIPT_CLIENT_ENGINE } from "./utils/enums"; +import Optimizely from "./optimizely"; + +export const getOptimizelyInstance = (config: Config): Client | null => { + let logger: Maybe; + + try { + logger = config.logger ? extractLogger(config.logger) : undefined; + + configValidator.validate(config); + + const { + clientEngine, + clientVersion, + jsonSchemaValidator, + userProfileService, + defaultDecideOptions, + disposable, + } = config; + + const errorNotifier = config.errorNotifier ? extractErrorNotifier(config.errorNotifier) : undefined; + + const projectConfigManager = extractConfigManager(config.projectConfigManager); + const eventProcessor = config.eventProcessor ? extractEventProcessor(config.eventProcessor) : undefined; + const odpManager = config.odpManager ? extractOdpManager(config.odpManager) : undefined; + const vuidManager = config.vuidManager ? extractVuidManager(config.vuidManager) : undefined; + + const optimizelyOptions = { + clientEngine: clientEngine || JAVASCRIPT_CLIENT_ENGINE, + clientVersion: clientVersion || CLIENT_VERSION, + jsonSchemaValidator, + userProfileService, + defaultDecideOptions, + disposable, + logger, + errorNotifier, + projectConfigManager, + eventProcessor, + odpManager, + vuidManager, + }; + + return new Optimizely(optimizelyOptions); + } catch (e) { + console.log('got error ', e); + logger?.error(e); + return null; + } +} diff --git a/lib/entrypoint.test-d.ts b/lib/entrypoint.test-d.ts index e952f293e..5ed9bb14a 100644 --- a/lib/entrypoint.test-d.ts +++ b/lib/entrypoint.test-d.ts @@ -10,6 +10,13 @@ export type Entrypoint = { createInstance: (config: Config) => Client | null; } -expectTypeOf(browserEntrypoint).toMatchTypeOf(); -expectTypeOf(nodeEntrypoint).toMatchTypeOf(); -expectTypeOf(reactNativeEntrypoint).toMatchTypeOf(); + +// these type tests will be fixed in a future PR + +// expectTypeOf(browserEntrypoint).toEqualTypeOf(); +// expectTypeOf(nodeEntrypoint).toEqualTypeOf(); +// expectTypeOf(reactNativeEntrypoint).toEqualTypeOf(); + +// expectTypeOf(browserEntrypoint).toEqualTypeOf(nodeEntrypoint); +// expectTypeOf(browserEntrypoint).toEqualTypeOf(reactNativeEntrypoint); +// expectTypeOf(nodeEntrypoint).toEqualTypeOf(reactNativeEntrypoint); diff --git a/lib/event_processor/event_processor_factory.browser.spec.ts b/lib/event_processor/event_processor_factory.browser.spec.ts index b0b636efb..dcc7ce497 100644 --- a/lib/event_processor/event_processor_factory.browser.spec.ts +++ b/lib/event_processor/event_processor_factory.browser.spec.ts @@ -30,8 +30,11 @@ vi.mock('./event_processor_factory', async (importOriginal) => { const getBatchEventProcessor = vi.fn().mockImplementation(() => { return {}; }); + const getOpaqueBatchEventProcessor = vi.fn().mockImplementation(() => { + return {}; + }); const original: any = await importOriginal(); - return { ...original, getBatchEventProcessor }; + return { ...original, getBatchEventProcessor, getOpaqueBatchEventProcessor }; }); vi.mock('../utils/cache/local_storage_cache.browser', () => { @@ -47,11 +50,11 @@ import defaultEventDispatcher from './event_dispatcher/default_dispatcher.browse import { LocalStorageCache } from '../utils/cache/local_storage_cache.browser'; import { SyncPrefixCache } from '../utils/cache/cache'; import { createForwardingEventProcessor, createBatchEventProcessor } from './event_processor_factory.browser'; -import { EVENT_STORE_PREFIX, FAILED_EVENT_RETRY_INTERVAL } from './event_processor_factory'; +import { EVENT_STORE_PREFIX, extractEventProcessor, FAILED_EVENT_RETRY_INTERVAL } from './event_processor_factory'; import sendBeaconEventDispatcher from './event_dispatcher/send_beacon_dispatcher.browser'; import { getForwardingEventProcessor } from './forwarding_event_processor'; import browserDefaultEventDispatcher from './event_dispatcher/default_dispatcher.browser'; -import { getBatchEventProcessor } from './event_processor_factory'; +import { getOpaqueBatchEventProcessor } from './event_processor_factory'; describe('createForwardingEventProcessor', () => { const mockGetForwardingEventProcessor = vi.mocked(getForwardingEventProcessor); @@ -65,14 +68,14 @@ describe('createForwardingEventProcessor', () => { dispatchEvent: vi.fn(), }; - const processor = createForwardingEventProcessor(eventDispatcher); + const processor = extractEventProcessor(createForwardingEventProcessor(eventDispatcher)); expect(Object.is(processor, mockGetForwardingEventProcessor.mock.results[0].value)).toBe(true); expect(mockGetForwardingEventProcessor).toHaveBeenNthCalledWith(1, eventDispatcher); }); it('uses the browser default event dispatcher if none is provided', () => { - const processor = createForwardingEventProcessor(); + const processor = extractEventProcessor(createForwardingEventProcessor()); expect(Object.is(processor, mockGetForwardingEventProcessor.mock.results[0].value)).toBe(true); expect(mockGetForwardingEventProcessor).toHaveBeenNthCalledWith(1, browserDefaultEventDispatcher); @@ -80,20 +83,20 @@ describe('createForwardingEventProcessor', () => { }); describe('createBatchEventProcessor', () => { - const mockGetBatchEventProcessor = vi.mocked(getBatchEventProcessor); + const mockGetOpaqueBatchEventProcessor = vi.mocked(getOpaqueBatchEventProcessor); const MockLocalStorageCache = vi.mocked(LocalStorageCache); const MockSyncPrefixCache = vi.mocked(SyncPrefixCache); beforeEach(() => { - mockGetBatchEventProcessor.mockClear(); + mockGetOpaqueBatchEventProcessor.mockClear(); MockLocalStorageCache.mockClear(); MockSyncPrefixCache.mockClear(); }); it('uses LocalStorageCache and SyncPrefixCache to create eventStore', () => { const processor = createBatchEventProcessor({}); - expect(Object.is(processor, mockGetBatchEventProcessor.mock.results[0].value)).toBe(true); - const eventStore = mockGetBatchEventProcessor.mock.calls[0][0].eventStore; + expect(Object.is(processor, mockGetOpaqueBatchEventProcessor.mock.results[0].value)).toBe(true); + const eventStore = mockGetOpaqueBatchEventProcessor.mock.calls[0][0].eventStore; expect(Object.is(eventStore, MockSyncPrefixCache.mock.results[0].value)).toBe(true); const [cache, prefix, transformGet, transformSet] = MockSyncPrefixCache.mock.calls[0]; @@ -111,14 +114,14 @@ describe('createBatchEventProcessor', () => { }; const processor = createBatchEventProcessor({ eventDispatcher }); - expect(Object.is(processor, mockGetBatchEventProcessor.mock.results[0].value)).toBe(true); - expect(mockGetBatchEventProcessor.mock.calls[0][0].eventDispatcher).toBe(eventDispatcher); + expect(Object.is(processor, mockGetOpaqueBatchEventProcessor.mock.results[0].value)).toBe(true); + expect(mockGetOpaqueBatchEventProcessor.mock.calls[0][0].eventDispatcher).toBe(eventDispatcher); }); it('uses the default browser event dispatcher if none is provided', () => { const processor = createBatchEventProcessor({ }); - expect(Object.is(processor, mockGetBatchEventProcessor.mock.results[0].value)).toBe(true); - expect(mockGetBatchEventProcessor.mock.calls[0][0].eventDispatcher).toBe(defaultEventDispatcher); + expect(Object.is(processor, mockGetOpaqueBatchEventProcessor.mock.results[0].value)).toBe(true); + expect(mockGetOpaqueBatchEventProcessor.mock.calls[0][0].eventDispatcher).toBe(defaultEventDispatcher); }); it('uses the provided closingEventDispatcher', () => { @@ -127,8 +130,8 @@ describe('createBatchEventProcessor', () => { }; const processor = createBatchEventProcessor({ closingEventDispatcher }); - expect(Object.is(processor, mockGetBatchEventProcessor.mock.results[0].value)).toBe(true); - expect(mockGetBatchEventProcessor.mock.calls[0][0].closingEventDispatcher).toBe(closingEventDispatcher); + expect(Object.is(processor, mockGetOpaqueBatchEventProcessor.mock.results[0].value)).toBe(true); + expect(mockGetOpaqueBatchEventProcessor.mock.calls[0][0].closingEventDispatcher).toBe(closingEventDispatcher); }); it('does not use any closingEventDispatcher if eventDispatcher is provided but closingEventDispatcher is not', () => { @@ -137,45 +140,45 @@ describe('createBatchEventProcessor', () => { }; const processor = createBatchEventProcessor({ eventDispatcher }); - expect(Object.is(processor, mockGetBatchEventProcessor.mock.results[0].value)).toBe(true); - expect(mockGetBatchEventProcessor.mock.calls[0][0].closingEventDispatcher).toBe(undefined); + expect(Object.is(processor, mockGetOpaqueBatchEventProcessor.mock.results[0].value)).toBe(true); + expect(mockGetOpaqueBatchEventProcessor.mock.calls[0][0].closingEventDispatcher).toBe(undefined); }); it('uses the default sendBeacon event dispatcher if neither eventDispatcher nor closingEventDispatcher is provided', () => { const processor = createBatchEventProcessor({ }); - expect(Object.is(processor, mockGetBatchEventProcessor.mock.results[0].value)).toBe(true); - expect(mockGetBatchEventProcessor.mock.calls[0][0].closingEventDispatcher).toBe(sendBeaconEventDispatcher); + expect(Object.is(processor, mockGetOpaqueBatchEventProcessor.mock.results[0].value)).toBe(true); + expect(mockGetOpaqueBatchEventProcessor.mock.calls[0][0].closingEventDispatcher).toBe(sendBeaconEventDispatcher); }); it('uses the provided flushInterval', () => { const processor1 = createBatchEventProcessor({ flushInterval: 2000 }); - expect(Object.is(processor1, mockGetBatchEventProcessor.mock.results[0].value)).toBe(true); - expect(mockGetBatchEventProcessor.mock.calls[0][0].flushInterval).toBe(2000); + expect(Object.is(processor1, mockGetOpaqueBatchEventProcessor.mock.results[0].value)).toBe(true); + expect(mockGetOpaqueBatchEventProcessor.mock.calls[0][0].flushInterval).toBe(2000); const processor2 = createBatchEventProcessor({ }); - expect(Object.is(processor2, mockGetBatchEventProcessor.mock.results[1].value)).toBe(true); - expect(mockGetBatchEventProcessor.mock.calls[1][0].flushInterval).toBe(undefined); + expect(Object.is(processor2, mockGetOpaqueBatchEventProcessor.mock.results[1].value)).toBe(true); + expect(mockGetOpaqueBatchEventProcessor.mock.calls[1][0].flushInterval).toBe(undefined); }); it('uses the provided batchSize', () => { const processor1 = createBatchEventProcessor({ batchSize: 20 }); - expect(Object.is(processor1, mockGetBatchEventProcessor.mock.results[0].value)).toBe(true); - expect(mockGetBatchEventProcessor.mock.calls[0][0].batchSize).toBe(20); + expect(Object.is(processor1, mockGetOpaqueBatchEventProcessor.mock.results[0].value)).toBe(true); + expect(mockGetOpaqueBatchEventProcessor.mock.calls[0][0].batchSize).toBe(20); const processor2 = createBatchEventProcessor({ }); - expect(Object.is(processor2, mockGetBatchEventProcessor.mock.results[1].value)).toBe(true); - expect(mockGetBatchEventProcessor.mock.calls[1][0].batchSize).toBe(undefined); + expect(Object.is(processor2, mockGetOpaqueBatchEventProcessor.mock.results[1].value)).toBe(true); + expect(mockGetOpaqueBatchEventProcessor.mock.calls[1][0].batchSize).toBe(undefined); }); it('uses maxRetries value of 5', () => { const processor = createBatchEventProcessor({ }); - expect(Object.is(processor, mockGetBatchEventProcessor.mock.results[0].value)).toBe(true); - expect(mockGetBatchEventProcessor.mock.calls[0][0].retryOptions?.maxRetries).toBe(5); + expect(Object.is(processor, mockGetOpaqueBatchEventProcessor.mock.results[0].value)).toBe(true); + expect(mockGetOpaqueBatchEventProcessor.mock.calls[0][0].retryOptions?.maxRetries).toBe(5); }); it('uses the default failedEventRetryInterval', () => { const processor = createBatchEventProcessor({ }); - expect(Object.is(processor, mockGetBatchEventProcessor.mock.results[0].value)).toBe(true); - expect(mockGetBatchEventProcessor.mock.calls[0][0].failedEventRetryInterval).toBe(FAILED_EVENT_RETRY_INTERVAL); + expect(Object.is(processor, mockGetOpaqueBatchEventProcessor.mock.results[0].value)).toBe(true); + expect(mockGetOpaqueBatchEventProcessor.mock.calls[0][0].failedEventRetryInterval).toBe(FAILED_EVENT_RETRY_INTERVAL); }); }); diff --git a/lib/event_processor/event_processor_factory.node.spec.ts b/lib/event_processor/event_processor_factory.node.spec.ts index 31001400f..487230748 100644 --- a/lib/event_processor/event_processor_factory.node.spec.ts +++ b/lib/event_processor/event_processor_factory.node.spec.ts @@ -28,8 +28,11 @@ vi.mock('./event_processor_factory', async (importOriginal) => { const getBatchEventProcessor = vi.fn().mockImplementation(() => { return {}; }); + const getOpaqueBatchEventProcessor = vi.fn().mockImplementation(() => { + return {}; + }); const original: any = await importOriginal(); - return { ...original, getBatchEventProcessor }; + return { ...original, getBatchEventProcessor, getOpaqueBatchEventProcessor }; }); vi.mock('../utils/cache/async_storage_cache.react_native', () => { @@ -43,8 +46,8 @@ vi.mock('../utils/cache/cache', () => { import { createBatchEventProcessor, createForwardingEventProcessor } from './event_processor_factory.node'; import { getForwardingEventProcessor } from './forwarding_event_processor'; import nodeDefaultEventDispatcher from './event_dispatcher/default_dispatcher.node'; -import { EVENT_STORE_PREFIX, FAILED_EVENT_RETRY_INTERVAL } from './event_processor_factory'; -import { getBatchEventProcessor } from './event_processor_factory'; +import { EVENT_STORE_PREFIX, extractEventProcessor, FAILED_EVENT_RETRY_INTERVAL } from './event_processor_factory'; +import { getOpaqueBatchEventProcessor } from './event_processor_factory'; import { AsyncCache, AsyncPrefixCache, SyncCache, SyncPrefixCache } from '../utils/cache/cache'; import { AsyncStorageCache } from '../utils/cache/async_storage_cache.react_native'; @@ -60,14 +63,14 @@ describe('createForwardingEventProcessor', () => { dispatchEvent: vi.fn(), }; - const processor = createForwardingEventProcessor(eventDispatcher); + const processor = extractEventProcessor(createForwardingEventProcessor(eventDispatcher)); expect(Object.is(processor, mockGetForwardingEventProcessor.mock.results[0].value)).toBe(true); expect(mockGetForwardingEventProcessor).toHaveBeenNthCalledWith(1, eventDispatcher); }); it('uses the node default event dispatcher if none is provided', () => { - const processor = createForwardingEventProcessor(); + const processor = extractEventProcessor(createForwardingEventProcessor()); expect(Object.is(processor, mockGetForwardingEventProcessor.mock.results[0].value)).toBe(true); expect(mockGetForwardingEventProcessor).toHaveBeenNthCalledWith(1, nodeDefaultEventDispatcher); @@ -75,13 +78,13 @@ describe('createForwardingEventProcessor', () => { }); describe('createBatchEventProcessor', () => { - const mockGetBatchEventProcessor = vi.mocked(getBatchEventProcessor); + const mockGetOpaqueBatchEventProcessor = vi.mocked(getOpaqueBatchEventProcessor); const MockAsyncStorageCache = vi.mocked(AsyncStorageCache); const MockSyncPrefixCache = vi.mocked(SyncPrefixCache); const MockAsyncPrefixCache = vi.mocked(AsyncPrefixCache); beforeEach(() => { - mockGetBatchEventProcessor.mockClear(); + mockGetOpaqueBatchEventProcessor.mockClear(); MockAsyncStorageCache.mockClear(); MockSyncPrefixCache.mockClear(); MockAsyncPrefixCache.mockClear(); @@ -90,8 +93,8 @@ describe('createBatchEventProcessor', () => { it('uses no default event store if no eventStore is provided', () => { const processor = createBatchEventProcessor({}); - expect(Object.is(processor, mockGetBatchEventProcessor.mock.results[0].value)).toBe(true); - const eventStore = mockGetBatchEventProcessor.mock.calls[0][0].eventStore; + expect(Object.is(processor, mockGetOpaqueBatchEventProcessor.mock.results[0].value)).toBe(true); + const eventStore = mockGetOpaqueBatchEventProcessor.mock.calls[0][0].eventStore; expect(eventStore).toBe(undefined); }); @@ -101,9 +104,9 @@ describe('createBatchEventProcessor', () => { } as SyncCache; const processor = createBatchEventProcessor({ eventStore }); - expect(Object.is(processor, mockGetBatchEventProcessor.mock.results[0].value)).toBe(true); + expect(Object.is(processor, mockGetOpaqueBatchEventProcessor.mock.results[0].value)).toBe(true); - expect(mockGetBatchEventProcessor.mock.calls[0][0].eventStore).toBe(MockSyncPrefixCache.mock.results[0].value); + expect(mockGetOpaqueBatchEventProcessor.mock.calls[0][0].eventStore).toBe(MockSyncPrefixCache.mock.results[0].value); const [cache, prefix, transformGet, transformSet] = MockSyncPrefixCache.mock.calls[0]; expect(cache).toBe(eventStore); @@ -120,9 +123,9 @@ describe('createBatchEventProcessor', () => { } as AsyncCache; const processor = createBatchEventProcessor({ eventStore }); - expect(Object.is(processor, mockGetBatchEventProcessor.mock.results[0].value)).toBe(true); + expect(Object.is(processor, mockGetOpaqueBatchEventProcessor.mock.results[0].value)).toBe(true); - expect(mockGetBatchEventProcessor.mock.calls[0][0].eventStore).toBe(MockAsyncPrefixCache.mock.results[0].value); + expect(mockGetOpaqueBatchEventProcessor.mock.calls[0][0].eventStore).toBe(MockAsyncPrefixCache.mock.results[0].value); const [cache, prefix, transformGet, transformSet] = MockAsyncPrefixCache.mock.calls[0]; expect(cache).toBe(eventStore); @@ -140,14 +143,14 @@ describe('createBatchEventProcessor', () => { }; const processor = createBatchEventProcessor({ eventDispatcher }); - expect(Object.is(processor, mockGetBatchEventProcessor.mock.results[0].value)).toBe(true); - expect(mockGetBatchEventProcessor.mock.calls[0][0].eventDispatcher).toBe(eventDispatcher); + expect(Object.is(processor, mockGetOpaqueBatchEventProcessor.mock.results[0].value)).toBe(true); + expect(mockGetOpaqueBatchEventProcessor.mock.calls[0][0].eventDispatcher).toBe(eventDispatcher); }); it('uses the default node event dispatcher if none is provided', () => { const processor = createBatchEventProcessor({ }); - expect(Object.is(processor, mockGetBatchEventProcessor.mock.results[0].value)).toBe(true); - expect(mockGetBatchEventProcessor.mock.calls[0][0].eventDispatcher).toBe(nodeDefaultEventDispatcher); + expect(Object.is(processor, mockGetOpaqueBatchEventProcessor.mock.results[0].value)).toBe(true); + expect(mockGetOpaqueBatchEventProcessor.mock.calls[0][0].eventDispatcher).toBe(nodeDefaultEventDispatcher); }); it('uses the provided closingEventDispatcher', () => { @@ -156,49 +159,49 @@ describe('createBatchEventProcessor', () => { }; const processor = createBatchEventProcessor({ closingEventDispatcher }); - expect(Object.is(processor, mockGetBatchEventProcessor.mock.results[0].value)).toBe(true); - expect(mockGetBatchEventProcessor.mock.calls[0][0].closingEventDispatcher).toBe(closingEventDispatcher); + expect(Object.is(processor, mockGetOpaqueBatchEventProcessor.mock.results[0].value)).toBe(true); + expect(mockGetOpaqueBatchEventProcessor.mock.calls[0][0].closingEventDispatcher).toBe(closingEventDispatcher); const processor2 = createBatchEventProcessor({ }); - expect(Object.is(processor2, mockGetBatchEventProcessor.mock.results[1].value)).toBe(true); - expect(mockGetBatchEventProcessor.mock.calls[1][0].closingEventDispatcher).toBe(undefined); + expect(Object.is(processor2, mockGetOpaqueBatchEventProcessor.mock.results[1].value)).toBe(true); + expect(mockGetOpaqueBatchEventProcessor.mock.calls[1][0].closingEventDispatcher).toBe(undefined); }); it('uses the provided flushInterval', () => { const processor1 = createBatchEventProcessor({ flushInterval: 2000 }); - expect(Object.is(processor1, mockGetBatchEventProcessor.mock.results[0].value)).toBe(true); - expect(mockGetBatchEventProcessor.mock.calls[0][0].flushInterval).toBe(2000); + expect(Object.is(processor1, mockGetOpaqueBatchEventProcessor.mock.results[0].value)).toBe(true); + expect(mockGetOpaqueBatchEventProcessor.mock.calls[0][0].flushInterval).toBe(2000); const processor2 = createBatchEventProcessor({ }); - expect(Object.is(processor2, mockGetBatchEventProcessor.mock.results[1].value)).toBe(true); - expect(mockGetBatchEventProcessor.mock.calls[1][0].flushInterval).toBe(undefined); + expect(Object.is(processor2, mockGetOpaqueBatchEventProcessor.mock.results[1].value)).toBe(true); + expect(mockGetOpaqueBatchEventProcessor.mock.calls[1][0].flushInterval).toBe(undefined); }); it('uses the provided batchSize', () => { const processor1 = createBatchEventProcessor({ batchSize: 20 }); - expect(Object.is(processor1, mockGetBatchEventProcessor.mock.results[0].value)).toBe(true); - expect(mockGetBatchEventProcessor.mock.calls[0][0].batchSize).toBe(20); + expect(Object.is(processor1, mockGetOpaqueBatchEventProcessor.mock.results[0].value)).toBe(true); + expect(mockGetOpaqueBatchEventProcessor.mock.calls[0][0].batchSize).toBe(20); const processor2 = createBatchEventProcessor({ }); - expect(Object.is(processor2, mockGetBatchEventProcessor.mock.results[1].value)).toBe(true); - expect(mockGetBatchEventProcessor.mock.calls[1][0].batchSize).toBe(undefined); + expect(Object.is(processor2, mockGetOpaqueBatchEventProcessor.mock.results[1].value)).toBe(true); + expect(mockGetOpaqueBatchEventProcessor.mock.calls[1][0].batchSize).toBe(undefined); }); it('uses maxRetries value of 10', () => { const processor = createBatchEventProcessor({ }); - expect(Object.is(processor, mockGetBatchEventProcessor.mock.results[0].value)).toBe(true); - expect(mockGetBatchEventProcessor.mock.calls[0][0].retryOptions?.maxRetries).toBe(10); + expect(Object.is(processor, mockGetOpaqueBatchEventProcessor.mock.results[0].value)).toBe(true); + expect(mockGetOpaqueBatchEventProcessor.mock.calls[0][0].retryOptions?.maxRetries).toBe(10); }); it('uses no failed event retry if an eventStore is not provided', () => { const processor = createBatchEventProcessor({ }); - expect(Object.is(processor, mockGetBatchEventProcessor.mock.results[0].value)).toBe(true); - expect(mockGetBatchEventProcessor.mock.calls[0][0].failedEventRetryInterval).toBe(undefined); + expect(Object.is(processor, mockGetOpaqueBatchEventProcessor.mock.results[0].value)).toBe(true); + expect(mockGetOpaqueBatchEventProcessor.mock.calls[0][0].failedEventRetryInterval).toBe(undefined); }); it('uses the default failedEventRetryInterval if an eventStore is provided', () => { const processor = createBatchEventProcessor({ eventStore: {} as any }); - expect(Object.is(processor, mockGetBatchEventProcessor.mock.results[0].value)).toBe(true); - expect(mockGetBatchEventProcessor.mock.calls[0][0].failedEventRetryInterval).toBe(FAILED_EVENT_RETRY_INTERVAL); + expect(Object.is(processor, mockGetOpaqueBatchEventProcessor.mock.results[0].value)).toBe(true); + expect(mockGetOpaqueBatchEventProcessor.mock.calls[0][0].failedEventRetryInterval).toBe(FAILED_EVENT_RETRY_INTERVAL); }); }); diff --git a/lib/event_processor/event_processor_factory.react_native.spec.ts b/lib/event_processor/event_processor_factory.react_native.spec.ts index 30e300dc9..131654a79 100644 --- a/lib/event_processor/event_processor_factory.react_native.spec.ts +++ b/lib/event_processor/event_processor_factory.react_native.spec.ts @@ -29,8 +29,11 @@ vi.mock('./event_processor_factory', async importOriginal => { const getBatchEventProcessor = vi.fn().mockImplementation(() => { return {}; }); + const getOpaqueBatchEventProcessor = vi.fn().mockImplementation(() => { + return {}; + }); const original: any = await importOriginal(); - return { ...original, getBatchEventProcessor }; + return { ...original, getBatchEventProcessor, getOpaqueBatchEventProcessor }; }); vi.mock('../utils/cache/async_storage_cache.react_native', () => { @@ -74,8 +77,8 @@ async function mockRequireNetInfo() { import { createForwardingEventProcessor, createBatchEventProcessor } from './event_processor_factory.react_native'; import { getForwardingEventProcessor } from './forwarding_event_processor'; import defaultEventDispatcher from './event_dispatcher/default_dispatcher.browser'; -import { EVENT_STORE_PREFIX, FAILED_EVENT_RETRY_INTERVAL, getPrefixEventStore } from './event_processor_factory'; -import { getBatchEventProcessor } from './event_processor_factory'; +import { EVENT_STORE_PREFIX, extractEventProcessor, FAILED_EVENT_RETRY_INTERVAL, getPrefixEventStore } from './event_processor_factory'; +import { getOpaqueBatchEventProcessor } from './event_processor_factory'; import { AsyncCache, AsyncPrefixCache, SyncCache, SyncPrefixCache } from '../utils/cache/cache'; import { AsyncStorageCache } from '../utils/cache/async_storage_cache.react_native'; import { ReactNativeNetInfoEventProcessor } from './batch_event_processor.react_native'; @@ -95,14 +98,14 @@ describe('createForwardingEventProcessor', () => { dispatchEvent: vi.fn(), }; - const processor = createForwardingEventProcessor(eventDispatcher); + const processor = extractEventProcessor(createForwardingEventProcessor(eventDispatcher)); expect(Object.is(processor, mockGetForwardingEventProcessor.mock.results[0].value)).toBe(true); expect(mockGetForwardingEventProcessor).toHaveBeenNthCalledWith(1, eventDispatcher); }); it('uses the browser default event dispatcher if none is provided', () => { - const processor = createForwardingEventProcessor(); + const processor = extractEventProcessor(createForwardingEventProcessor()); expect(Object.is(processor, mockGetForwardingEventProcessor.mock.results[0].value)).toBe(true); expect(mockGetForwardingEventProcessor).toHaveBeenNthCalledWith(1, defaultEventDispatcher); @@ -110,14 +113,14 @@ describe('createForwardingEventProcessor', () => { }); describe('createBatchEventProcessor', () => { - const mockGetBatchEventProcessor = vi.mocked(getBatchEventProcessor); + const mockGetOpaqueBatchEventProcessor = vi.mocked(getOpaqueBatchEventProcessor); const MockAsyncStorageCache = vi.mocked(AsyncStorageCache); const MockSyncPrefixCache = vi.mocked(SyncPrefixCache); const MockAsyncPrefixCache = vi.mocked(AsyncPrefixCache); beforeEach(() => { isNetInfoAvailable = false; - mockGetBatchEventProcessor.mockClear(); + mockGetOpaqueBatchEventProcessor.mockClear(); MockAsyncStorageCache.mockClear(); MockSyncPrefixCache.mockClear(); MockAsyncPrefixCache.mockClear(); @@ -126,22 +129,22 @@ describe('createBatchEventProcessor', () => { it('returns an instance of ReacNativeNetInfoEventProcessor if netinfo can be required', async () => { isNetInfoAvailable = true; const processor = createBatchEventProcessor({}); - expect(Object.is(processor, mockGetBatchEventProcessor.mock.results[0].value)).toBe(true); - expect(mockGetBatchEventProcessor.mock.calls[0][1]).toBe(ReactNativeNetInfoEventProcessor); + expect(Object.is(processor, mockGetOpaqueBatchEventProcessor.mock.results[0].value)).toBe(true); + expect(mockGetOpaqueBatchEventProcessor.mock.calls[0][1]).toBe(ReactNativeNetInfoEventProcessor); }); it('returns an instance of BatchEventProcessor if netinfo cannot be required', async () => { isNetInfoAvailable = false; const processor = createBatchEventProcessor({}); - expect(Object.is(processor, mockGetBatchEventProcessor.mock.results[0].value)).toBe(true); - expect(mockGetBatchEventProcessor.mock.calls[0][1]).toBe(BatchEventProcessor); + expect(Object.is(processor, mockGetOpaqueBatchEventProcessor.mock.results[0].value)).toBe(true); + expect(mockGetOpaqueBatchEventProcessor.mock.calls[0][1]).toBe(BatchEventProcessor); }); it('uses AsyncStorageCache and AsyncPrefixCache to create eventStore if no eventStore is provided', () => { const processor = createBatchEventProcessor({}); - expect(Object.is(processor, mockGetBatchEventProcessor.mock.results[0].value)).toBe(true); - const eventStore = mockGetBatchEventProcessor.mock.calls[0][0].eventStore; + expect(Object.is(processor, mockGetOpaqueBatchEventProcessor.mock.results[0].value)).toBe(true); + const eventStore = mockGetOpaqueBatchEventProcessor.mock.calls[0][0].eventStore; expect(Object.is(eventStore, MockAsyncPrefixCache.mock.results[0].value)).toBe(true); const [cache, prefix, transformGet, transformSet] = MockAsyncPrefixCache.mock.calls[0]; @@ -195,9 +198,9 @@ describe('createBatchEventProcessor', () => { } as SyncCache; const processor = createBatchEventProcessor({ eventStore }); - expect(Object.is(processor, mockGetBatchEventProcessor.mock.results[0].value)).toBe(true); + expect(Object.is(processor, mockGetOpaqueBatchEventProcessor.mock.results[0].value)).toBe(true); - expect(mockGetBatchEventProcessor.mock.calls[0][0].eventStore).toBe(MockSyncPrefixCache.mock.results[0].value); + expect(mockGetOpaqueBatchEventProcessor.mock.calls[0][0].eventStore).toBe(MockSyncPrefixCache.mock.results[0].value); const [cache, prefix, transformGet, transformSet] = MockSyncPrefixCache.mock.calls[0]; expect(cache).toBe(eventStore); @@ -214,9 +217,9 @@ describe('createBatchEventProcessor', () => { } as AsyncCache; const processor = createBatchEventProcessor({ eventStore }); - expect(Object.is(processor, mockGetBatchEventProcessor.mock.results[0].value)).toBe(true); + expect(Object.is(processor, mockGetOpaqueBatchEventProcessor.mock.results[0].value)).toBe(true); - expect(mockGetBatchEventProcessor.mock.calls[0][0].eventStore).toBe(MockAsyncPrefixCache.mock.results[0].value); + expect(mockGetOpaqueBatchEventProcessor.mock.calls[0][0].eventStore).toBe(MockAsyncPrefixCache.mock.results[0].value); const [cache, prefix, transformGet, transformSet] = MockAsyncPrefixCache.mock.calls[0]; expect(cache).toBe(eventStore); @@ -233,14 +236,14 @@ describe('createBatchEventProcessor', () => { }; const processor = createBatchEventProcessor({ eventDispatcher }); - expect(Object.is(processor, mockGetBatchEventProcessor.mock.results[0].value)).toBe(true); - expect(mockGetBatchEventProcessor.mock.calls[0][0].eventDispatcher).toBe(eventDispatcher); + expect(Object.is(processor, mockGetOpaqueBatchEventProcessor.mock.results[0].value)).toBe(true); + expect(mockGetOpaqueBatchEventProcessor.mock.calls[0][0].eventDispatcher).toBe(eventDispatcher); }); it('uses the default browser event dispatcher if none is provided', () => { const processor = createBatchEventProcessor({}); - expect(Object.is(processor, mockGetBatchEventProcessor.mock.results[0].value)).toBe(true); - expect(mockGetBatchEventProcessor.mock.calls[0][0].eventDispatcher).toBe(defaultEventDispatcher); + expect(Object.is(processor, mockGetOpaqueBatchEventProcessor.mock.results[0].value)).toBe(true); + expect(mockGetOpaqueBatchEventProcessor.mock.calls[0][0].eventDispatcher).toBe(defaultEventDispatcher); }); it('uses the provided closingEventDispatcher', () => { @@ -249,43 +252,43 @@ describe('createBatchEventProcessor', () => { }; const processor = createBatchEventProcessor({ closingEventDispatcher }); - expect(Object.is(processor, mockGetBatchEventProcessor.mock.results[0].value)).toBe(true); - expect(mockGetBatchEventProcessor.mock.calls[0][0].closingEventDispatcher).toBe(closingEventDispatcher); + expect(Object.is(processor, mockGetOpaqueBatchEventProcessor.mock.results[0].value)).toBe(true); + expect(mockGetOpaqueBatchEventProcessor.mock.calls[0][0].closingEventDispatcher).toBe(closingEventDispatcher); const processor2 = createBatchEventProcessor({}); - expect(Object.is(processor2, mockGetBatchEventProcessor.mock.results[1].value)).toBe(true); - expect(mockGetBatchEventProcessor.mock.calls[1][0].closingEventDispatcher).toBe(undefined); + expect(Object.is(processor2, mockGetOpaqueBatchEventProcessor.mock.results[1].value)).toBe(true); + expect(mockGetOpaqueBatchEventProcessor.mock.calls[1][0].closingEventDispatcher).toBe(undefined); }); it('uses the provided flushInterval', () => { const processor1 = createBatchEventProcessor({ flushInterval: 2000 }); - expect(Object.is(processor1, mockGetBatchEventProcessor.mock.results[0].value)).toBe(true); - expect(mockGetBatchEventProcessor.mock.calls[0][0].flushInterval).toBe(2000); + expect(Object.is(processor1, mockGetOpaqueBatchEventProcessor.mock.results[0].value)).toBe(true); + expect(mockGetOpaqueBatchEventProcessor.mock.calls[0][0].flushInterval).toBe(2000); const processor2 = createBatchEventProcessor({}); - expect(Object.is(processor2, mockGetBatchEventProcessor.mock.results[1].value)).toBe(true); - expect(mockGetBatchEventProcessor.mock.calls[1][0].flushInterval).toBe(undefined); + expect(Object.is(processor2, mockGetOpaqueBatchEventProcessor.mock.results[1].value)).toBe(true); + expect(mockGetOpaqueBatchEventProcessor.mock.calls[1][0].flushInterval).toBe(undefined); }); it('uses the provided batchSize', () => { const processor1 = createBatchEventProcessor({ batchSize: 20 }); - expect(Object.is(processor1, mockGetBatchEventProcessor.mock.results[0].value)).toBe(true); - expect(mockGetBatchEventProcessor.mock.calls[0][0].batchSize).toBe(20); + expect(Object.is(processor1, mockGetOpaqueBatchEventProcessor.mock.results[0].value)).toBe(true); + expect(mockGetOpaqueBatchEventProcessor.mock.calls[0][0].batchSize).toBe(20); const processor2 = createBatchEventProcessor({}); - expect(Object.is(processor2, mockGetBatchEventProcessor.mock.results[1].value)).toBe(true); - expect(mockGetBatchEventProcessor.mock.calls[1][0].batchSize).toBe(undefined); + expect(Object.is(processor2, mockGetOpaqueBatchEventProcessor.mock.results[1].value)).toBe(true); + expect(mockGetOpaqueBatchEventProcessor.mock.calls[1][0].batchSize).toBe(undefined); }); it('uses maxRetries value of 5', () => { const processor = createBatchEventProcessor({}); - expect(Object.is(processor, mockGetBatchEventProcessor.mock.results[0].value)).toBe(true); - expect(mockGetBatchEventProcessor.mock.calls[0][0].retryOptions?.maxRetries).toBe(5); + expect(Object.is(processor, mockGetOpaqueBatchEventProcessor.mock.results[0].value)).toBe(true); + expect(mockGetOpaqueBatchEventProcessor.mock.calls[0][0].retryOptions?.maxRetries).toBe(5); }); it('uses the default failedEventRetryInterval', () => { const processor = createBatchEventProcessor({}); - expect(Object.is(processor, mockGetBatchEventProcessor.mock.results[0].value)).toBe(true); - expect(mockGetBatchEventProcessor.mock.calls[0][0].failedEventRetryInterval).toBe(FAILED_EVENT_RETRY_INTERVAL); + expect(Object.is(processor, mockGetOpaqueBatchEventProcessor.mock.results[0].value)).toBe(true); + expect(mockGetOpaqueBatchEventProcessor.mock.calls[0][0].failedEventRetryInterval).toBe(FAILED_EVENT_RETRY_INTERVAL); }); }); diff --git a/lib/index.browser.tests.js b/lib/index.browser.tests.js index 5fb84a30f..3ea249904 100644 --- a/lib/index.browser.tests.js +++ b/lib/index.browser.tests.js @@ -18,10 +18,12 @@ import sinon from 'sinon'; import Optimizely from './optimizely'; import testData from './tests/test_data'; import packageJSON from '../package.json'; -import optimizelyFactory from './index.browser'; +import * as optimizelyFactory from './index.browser'; import configValidator from './utils/config_validator'; import { getMockProjectConfigManager } from './tests/mock/mock_project_config_manager'; import { createProjectConfig } from './project_config/project_config'; +import { wrapConfigManager } from './project_config/config_manager_factory'; +import { wrapLogger } from './logging/logger_factory'; class MockLocalStorage { store = {}; @@ -69,13 +71,17 @@ var getLogger = () => ({ describe('javascript-sdk (Browser)', function() { var clock; + + before(() => { + window.addEventListener = () => {}; + // sinon.spy(window, 'addEventListener') + }); + beforeEach(function() { - sinon.stub(optimizelyFactory.eventDispatcher, 'dispatchEvent'); clock = sinon.useFakeTimers(new Date()); }); afterEach(function() { - optimizelyFactory.eventDispatcher.dispatchEvent.restore(); clock.restore(); }); @@ -103,7 +109,6 @@ describe('javascript-sdk (Browser)', function() { }); afterEach(function() { - optimizelyFactory.__internalResetRetryState(); mockLogger.error.restore(); configValidator.validate.restore(); delete global.XMLHttpRequest; @@ -114,7 +119,7 @@ describe('javascript-sdk (Browser)', function() { // logic, not the dispatcher. Refactor accordingly. // it('should invoke resendPendingEvents at most once', function() { // var optlyInstance = optimizelyFactory.createInstance({ - // projectConfigManager: getMockProjectConfigManager(), + // projectConfigManager: wrapConfigManager(getMockProjectConfigManager()), // errorHandler: fakeErrorHandler, // logger: silentLogger, // }); @@ -122,7 +127,7 @@ describe('javascript-sdk (Browser)', function() { // sinon.assert.calledOnce(LocalStoragePendingEventsDispatcher.prototype.sendPendingEvents); // optlyInstance = optimizelyFactory.createInstance({ - // projectConfigManager: getMockProjectConfigManager(), + // projectConfigManager: wrapConfigManager(getMockProjectConfigManager()), // errorHandler: fakeErrorHandler, // logger: silentLogger, // }); @@ -135,18 +140,17 @@ describe('javascript-sdk (Browser)', function() { configValidator.validate.throws(new Error('INVALID_CONFIG_OR_SOMETHING')); assert.doesNotThrow(function() { var optlyInstance = optimizelyFactory.createInstance({ - projectConfigManager: getMockProjectConfigManager(), - logger: mockLogger, + projectConfigManager: wrapConfigManager(getMockProjectConfigManager()), + logger: wrapLogger(mockLogger), }); }); }); it('should create an instance of optimizely', function() { var optlyInstance = optimizelyFactory.createInstance({ - projectConfigManager: getMockProjectConfigManager(), + projectConfigManager: wrapConfigManager(getMockProjectConfigManager()), errorHandler: fakeErrorHandler, - eventDispatcher: fakeEventDispatcher, - logger: mockLogger, + logger: wrapLogger(mockLogger), }); assert.instanceOf(optlyInstance, Optimizely); @@ -155,10 +159,9 @@ describe('javascript-sdk (Browser)', function() { it('should set the JavaScript client engine and version', function() { var optlyInstance = optimizelyFactory.createInstance({ - projectConfigManager: getMockProjectConfigManager(), + projectConfigManager: wrapConfigManager(getMockProjectConfigManager()), errorHandler: fakeErrorHandler, - eventDispatcher: fakeEventDispatcher, - logger: mockLogger, + logger: wrapLogger(mockLogger), }); assert.equal('javascript-sdk', optlyInstance.clientEngine); @@ -168,22 +171,19 @@ describe('javascript-sdk (Browser)', function() { it('should allow passing of "react-sdk" as the clientEngine', function() { var optlyInstance = optimizelyFactory.createInstance({ clientEngine: 'react-sdk', - projectConfigManager: getMockProjectConfigManager(), + projectConfigManager: wrapConfigManager(getMockProjectConfigManager()), errorHandler: fakeErrorHandler, - eventDispatcher: fakeEventDispatcher, - logger: mockLogger, + logger: wrapLogger(mockLogger), }); assert.equal('react-sdk', optlyInstance.clientEngine); }); it('should activate with provided event dispatcher', function() { var optlyInstance = optimizelyFactory.createInstance({ - projectConfigManager: getMockProjectConfigManager({ + projectConfigManager: wrapConfigManager(getMockProjectConfigManager({ initConfig: createProjectConfig(testData.getTestProjectConfig()), - }), - errorHandler: fakeErrorHandler, - eventDispatcher: optimizelyFactory.eventDispatcher, - logger: mockLogger, + })), + logger: wrapLogger(mockLogger), }); var activate = optlyInstance.activate('testExperiment', 'testUser'); assert.strictEqual(activate, 'control'); @@ -191,12 +191,10 @@ describe('javascript-sdk (Browser)', function() { it('should be able to set and get a forced variation', function() { var optlyInstance = optimizelyFactory.createInstance({ - projectConfigManager: getMockProjectConfigManager({ + projectConfigManager: wrapConfigManager(getMockProjectConfigManager({ initConfig: createProjectConfig(testData.getTestProjectConfig()), - }), - errorHandler: fakeErrorHandler, - eventDispatcher: optimizelyFactory.eventDispatcher, - logger: mockLogger, + })), + logger: wrapLogger(mockLogger), }); var didSetVariation = optlyInstance.setForcedVariation('testExperiment', 'testUser', 'control'); @@ -208,12 +206,10 @@ describe('javascript-sdk (Browser)', function() { it('should be able to set and unset a forced variation', function() { var optlyInstance = optimizelyFactory.createInstance({ - projectConfigManager: getMockProjectConfigManager({ + projectConfigManager: wrapConfigManager(getMockProjectConfigManager({ initConfig: createProjectConfig(testData.getTestProjectConfig()), - }), - errorHandler: fakeErrorHandler, - eventDispatcher: optimizelyFactory.eventDispatcher, - logger: mockLogger, + })), + logger: wrapLogger(mockLogger), }); var didSetVariation = optlyInstance.setForcedVariation('testExperiment', 'testUser', 'control'); @@ -231,12 +227,10 @@ describe('javascript-sdk (Browser)', function() { it('should be able to set multiple experiments for one user', function() { var optlyInstance = optimizelyFactory.createInstance({ - projectConfigManager: getMockProjectConfigManager({ + projectConfigManager: wrapConfigManager(getMockProjectConfigManager({ initConfig: createProjectConfig(testData.getTestProjectConfig()), - }), - errorHandler: fakeErrorHandler, - eventDispatcher: optimizelyFactory.eventDispatcher, - logger: mockLogger, + })), + logger: wrapLogger(mockLogger), }); var didSetVariation = optlyInstance.setForcedVariation('testExperiment', 'testUser', 'control'); @@ -258,12 +252,10 @@ describe('javascript-sdk (Browser)', function() { it('should be able to set multiple experiments for one user, and unset one', function() { var optlyInstance = optimizelyFactory.createInstance({ - projectConfigManager: getMockProjectConfigManager({ + projectConfigManager: wrapConfigManager(getMockProjectConfigManager({ initConfig: createProjectConfig(testData.getTestProjectConfig()), - }), - errorHandler: fakeErrorHandler, - eventDispatcher: optimizelyFactory.eventDispatcher, - logger: mockLogger, + })), + logger: wrapLogger(mockLogger), }); var didSetVariation = optlyInstance.setForcedVariation('testExperiment', 'testUser', 'control'); @@ -288,12 +280,10 @@ describe('javascript-sdk (Browser)', function() { it('should be able to set multiple experiments for one user, and reset one', function() { var optlyInstance = optimizelyFactory.createInstance({ - projectConfigManager: getMockProjectConfigManager({ + projectConfigManager: wrapConfigManager(getMockProjectConfigManager({ initConfig: createProjectConfig(testData.getTestProjectConfig()), - }), - errorHandler: fakeErrorHandler, - eventDispatcher: optimizelyFactory.eventDispatcher, - logger: mockLogger, + })), + logger: wrapLogger(mockLogger), }); var didSetVariation = optlyInstance.setForcedVariation('testExperiment', 'testUser', 'control'); @@ -322,12 +312,10 @@ describe('javascript-sdk (Browser)', function() { it('should override bucketing when setForcedVariation is called', function() { var optlyInstance = optimizelyFactory.createInstance({ - projectConfigManager: getMockProjectConfigManager({ + projectConfigManager: wrapConfigManager(getMockProjectConfigManager({ initConfig: createProjectConfig(testData.getTestProjectConfig()), - }), - errorHandler: fakeErrorHandler, - eventDispatcher: optimizelyFactory.eventDispatcher, - logger: mockLogger, + })), + logger: wrapLogger(mockLogger), }); var didSetVariation = optlyInstance.setForcedVariation('testExperiment', 'testUser', 'control'); @@ -345,12 +333,10 @@ describe('javascript-sdk (Browser)', function() { it('should override bucketing when setForcedVariation is called for a not running experiment', function() { var optlyInstance = optimizelyFactory.createInstance({ - projectConfigManager: getMockProjectConfigManager({ + projectConfigManager: wrapConfigManager(getMockProjectConfigManager({ initConfig: createProjectConfig(testData.getTestProjectConfig()), - }), - errorHandler: fakeErrorHandler, - eventDispatcher: optimizelyFactory.eventDispatcher, - logger: mockLogger, + })), + logger: wrapLogger(mockLogger), }); var didSetVariation = optlyInstance.setForcedVariation( diff --git a/lib/index.browser.ts b/lib/index.browser.ts index bdb10fe42..4190a4b9f 100644 --- a/lib/index.browser.ts +++ b/lib/index.browser.ts @@ -33,14 +33,9 @@ import { extractLogger, createLogger } from './logging/logger_factory'; import { extractErrorNotifier, createErrorNotifier } from './error/error_notifier_factory'; import { LoggerFacade } from './logging/logger'; import { Maybe } from './utils/type'; +import { getOptimizelyInstance } from './client_factory'; -const DEFAULT_EVENT_BATCH_SIZE = 10; -const DEFAULT_EVENT_FLUSH_INTERVAL = 1000; // Unit is ms, default is 1s -const DEFAULT_EVENT_MAX_QUEUE_SIZE = 10000; - -let hasRetriedEvents = false; - /** * Creates an instance of the Optimizely class * @param {Config} config @@ -48,59 +43,27 @@ let hasRetriedEvents = false; * null on error */ const createInstance = function(config: Config): Client | null { - let logger: Maybe; - - try { - configValidator.validate(config); - - const { clientEngine, clientVersion } = config; - logger = config.logger ? extractLogger(config.logger) : undefined; - const errorNotifier = config.errorNotifier ? extractErrorNotifier(config.errorNotifier) : undefined; - - const optimizelyOptions = { - ...config, - clientEngine: clientEngine || enums.JAVASCRIPT_CLIENT_ENGINE, - clientVersion: clientVersion || enums.CLIENT_VERSION, - logger, - errorNotifier, - }; - - const optimizely = new Optimizely(optimizelyOptions); - - try { - if (typeof window.addEventListener === 'function') { - const unloadEvent = 'onpagehide' in window ? 'pagehide' : 'unload'; - window.addEventListener( - unloadEvent, - () => { - optimizely.close(); - }, - false - ); - } - // eslint-disable-next-line @typescript-eslint/no-explicit-any - } catch (e) { - logger?.error(UNABLE_TO_ATTACH_UNLOAD, e.message); - } - - return optimizely; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - } catch (e) { - logger?.error(e); - return null; + const client = getOptimizelyInstance(config); + + if (client) { + const unloadEvent = 'onpagehide' in window ? 'pagehide' : 'unload'; + window.addEventListener( + unloadEvent, + () => { + client.close(); + }, + ); } -}; -const __internalResetRetryState = function(): void { - hasRetriedEvents = false; + return client; }; + export { defaultEventDispatcher as eventDispatcher, - sendBeaconEventDispatcher, + // sendBeaconEventDispatcher, enums, createInstance, - __internalResetRetryState, OptimizelyDecideOption, UserAgentParser as IUserAgentParser, getUserAgentParser, @@ -115,20 +78,4 @@ export { export * from './common_exports'; -export default { - ...commonExports, - eventDispatcher: defaultEventDispatcher, - sendBeaconEventDispatcher, - enums, - createInstance, - __internalResetRetryState, - OptimizelyDecideOption, - getUserAgentParser, - createPollingProjectConfigManager, - createForwardingEventProcessor, - createBatchEventProcessor, - createOdpManager, - createVuidManager, -}; - export * from './export_types'; diff --git a/lib/index.node.tests.js b/lib/index.node.tests.js index f35903418..0146fffab 100644 --- a/lib/index.node.tests.js +++ b/lib/index.node.tests.js @@ -18,9 +18,11 @@ import sinon from 'sinon'; import Optimizely from './optimizely'; import testData from './tests/test_data'; -import optimizelyFactory from './index.node'; +import * as optimizelyFactory from './index.node'; import configValidator from './utils/config_validator'; import { getMockProjectConfigManager } from './tests/mock/mock_project_config_manager'; +import { wrapConfigManager } from './project_config/config_manager_factory'; +import { wrapLogger } from './logging/logger_factory'; var createLogger = () => ({ debug: () => {}, @@ -72,8 +74,8 @@ describe('optimizelyFactory', function() { configValidator.validate.throws(new Error('INVALID_CONFIG_OR_SOMETHING')); assert.doesNotThrow(function() { var optlyInstance = optimizelyFactory.createInstance({ - projectConfigManager: getMockProjectConfigManager(), - logger: fakeLogger, + projectConfigManager: wrapConfigManager(getMockProjectConfigManager()), + logger: wrapLogger(fakeLogger), }); }); // sinon.assert.calledOnce(fakeLogger.error); diff --git a/lib/index.node.ts b/lib/index.node.ts index c0d7b41db..d3959c75c 100644 --- a/lib/index.node.ts +++ b/lib/index.node.ts @@ -14,10 +14,7 @@ * limitations under the License. */ -// import { getLogger, setErrorHandler, getErrorHandler, LogLevel, setLogHandler, setLogLevel } from './modules/logging'; -import Optimizely from './optimizely'; import * as enums from './utils/enums'; -import configValidator from './utils/config_validator'; import defaultEventDispatcher from './event_processor/event_dispatcher/default_dispatcher.node'; import { createNotificationCenter } from './notification_center'; import { OptimizelyDecideOption, Client, Config } from './shared_types'; @@ -31,10 +28,7 @@ import { extractErrorNotifier, createErrorNotifier } from './error/error_notifie import { Maybe } from './utils/type'; import { LoggerFacade } from './logging/logger'; import { ErrorNotifier } from './error/error_notifier'; - -const DEFAULT_EVENT_BATCH_SIZE = 10; -const DEFAULT_EVENT_FLUSH_INTERVAL = 30000; // Unit is ms, default is 30s -const DEFAULT_EVENT_MAX_QUEUE_SIZE = 10000; +import { getOptimizelyInstance } from './client_factory'; /** * Creates an instance of the Optimizely class @@ -43,29 +37,12 @@ const DEFAULT_EVENT_MAX_QUEUE_SIZE = 10000; * null on error */ const createInstance = function(config: Config): Client | null { - let logger: Maybe; - - try { - configValidator.validate(config); - - const { clientEngine, clientVersion } = config; - logger = config.logger ? extractLogger(config.logger) : undefined; - const errorNotifier = config.errorNotifier ? extractErrorNotifier(config.errorNotifier) : undefined; - - const optimizelyOptions = { - ...config, - clientEngine: clientEngine || enums.NODE_CLIENT_ENGINE, - clientVersion: clientVersion || enums.CLIENT_VERSION, - logger, - errorNotifier, - }; - - return new Optimizely(optimizelyOptions); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - } catch (e) { - logger?.error(e); - return null; + const nodeConfig = { + ...config, + clientEnging: config.clientEngine || enums.NODE_CLIENT_ENGINE, } + + return getOptimizelyInstance(nodeConfig); }; /** @@ -87,17 +64,5 @@ export { export * from './common_exports'; -export default { - ...commonExports, - eventDispatcher: defaultEventDispatcher, - enums, - createInstance, - OptimizelyDecideOption, - createPollingProjectConfigManager, - createForwardingEventProcessor, - createBatchEventProcessor, - createOdpManager, - createVuidManager, -}; export * from './export_types'; diff --git a/lib/index.react_native.spec.ts b/lib/index.react_native.spec.ts index 42ba24821..d091e889a 100644 --- a/lib/index.react_native.spec.ts +++ b/lib/index.react_native.spec.ts @@ -18,11 +18,13 @@ import { describe, beforeEach, afterEach, it, expect, vi } from 'vitest'; import Optimizely from './optimizely'; import testData from './tests/test_data'; import packageJSON from '../package.json'; -import optimizelyFactory from './index.react_native'; +import * as optimizelyFactory from './index.react_native'; import configValidator from './utils/config_validator'; import { getMockProjectConfigManager } from './tests/mock/mock_project_config_manager'; import { createProjectConfig } from './project_config/project_config'; import { getMockLogger } from './tests/mock/mock_logger'; +import { wrapConfigManager } from './project_config/config_manager_factory'; +import { wrapLogger } from './logging/logger_factory'; vi.mock('@react-native-community/netinfo'); vi.mock('react-native-get-random-values') @@ -39,10 +41,10 @@ describe('javascript-sdk/react-native', () => { }); describe('APIs', () => { - it('should expose logger, errorHandler, eventDispatcher and enums', () => { - expect(optimizelyFactory.eventDispatcher).toBeDefined(); - expect(optimizelyFactory.enums).toBeDefined(); - }); + // it('should expose logger, errorHandler, eventDispatcher and enums', () => { + // expect(optimizelyFactory.eventDispatcher).toBeDefined(); + // expect(optimizelyFactory.enums).toBeDefined(); + // }); describe('createInstance', () => { const fakeErrorHandler = { handleError: function() {} }; @@ -70,17 +72,17 @@ describe('javascript-sdk/react-native', () => { }); expect(function() { const optlyInstance = optimizelyFactory.createInstance({ - projectConfigManager: getMockProjectConfigManager(), + projectConfigManager: wrapConfigManager(getMockProjectConfigManager()), // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - logger: mockLogger, + logger: wrapLogger(mockLogger), }); }).not.toThrow(); }); it('should create an instance of optimizely', () => { const optlyInstance = optimizelyFactory.createInstance({ - projectConfigManager: getMockProjectConfigManager(), + projectConfigManager: wrapConfigManager(getMockProjectConfigManager()), // errorHandler: fakeErrorHandler, // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore @@ -95,7 +97,7 @@ describe('javascript-sdk/react-native', () => { it('should set the React Native JS client engine and javascript SDK version', () => { const optlyInstance = optimizelyFactory.createInstance({ - projectConfigManager: getMockProjectConfigManager(), + projectConfigManager: wrapConfigManager(getMockProjectConfigManager()), // errorHandler: fakeErrorHandler, // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore diff --git a/lib/index.react_native.ts b/lib/index.react_native.ts index 243d1fea3..f135d40ba 100644 --- a/lib/index.react_native.ts +++ b/lib/index.react_native.ts @@ -13,8 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -import * as enums from './utils/enums'; import Optimizely from './optimizely'; import configValidator from './utils/config_validator'; import defaultEventDispatcher from './event_processor/event_dispatcher/default_dispatcher.browser'; @@ -32,10 +30,8 @@ import { Maybe } from './utils/type'; import { LoggerFacade } from './logging/logger'; import { extractLogger, createLogger } from './logging/logger_factory'; import { extractErrorNotifier, createErrorNotifier } from './error/error_notifier_factory'; - -const DEFAULT_EVENT_BATCH_SIZE = 10; -const DEFAULT_EVENT_FLUSH_INTERVAL = 1000; // Unit is ms, default is 1s -const DEFAULT_EVENT_MAX_QUEUE_SIZE = 10000; +import { getOptimizelyInstance } from './client_factory'; +import { REACT_NATIVE_JS_CLIENT_ENGINE } from './utils/enums'; /** * Creates an instance of the Optimizely class @@ -44,35 +40,12 @@ const DEFAULT_EVENT_MAX_QUEUE_SIZE = 10000; * null on error */ const createInstance = function(config: Config): Client | null { - let logger: Maybe; - - try { - configValidator.validate(config); - - const { clientEngine, clientVersion } = config; - - logger = config.logger ? extractLogger(config.logger) : undefined; - const errorNotifier = config.errorNotifier ? extractErrorNotifier(config.errorNotifier) : undefined; - - const optimizelyOptions = { - ...config, - clientEngine: clientEngine || enums.REACT_NATIVE_JS_CLIENT_ENGINE, - clientVersion: clientVersion || enums.CLIENT_VERSION, - logger, - errorNotifier, - }; - - // If client engine is react, convert it to react native. - if (optimizelyOptions.clientEngine === enums.REACT_CLIENT_ENGINE) { - optimizelyOptions.clientEngine = enums.REACT_NATIVE_CLIENT_ENGINE; - } - - return new Optimizely(optimizelyOptions); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - } catch (e) { - logger?.error(e); - return null; + const rnConfig = { + ...config, + clientEngine: config.clientEngine || REACT_NATIVE_JS_CLIENT_ENGINE, } + + return getOptimizelyInstance(rnConfig); }; /** @@ -80,7 +53,6 @@ const createInstance = function(config: Config): Client | null { */ export { defaultEventDispatcher as eventDispatcher, - enums, createInstance, OptimizelyDecideOption, createPollingProjectConfigManager, @@ -94,17 +66,4 @@ export { export * from './common_exports'; -export default { - ...commonExports, - eventDispatcher: defaultEventDispatcher, - enums, - createInstance, - OptimizelyDecideOption, - createPollingProjectConfigManager, - createForwardingEventProcessor, - createBatchEventProcessor, - createOdpManager, - createVuidManager, -}; - export * from './export_types'; diff --git a/lib/logging/logger_factory.ts b/lib/logging/logger_factory.ts index 09d3440fc..0b4335d01 100644 --- a/lib/logging/logger_factory.ts +++ b/lib/logging/logger_factory.ts @@ -97,6 +97,13 @@ export const createLogger = (config: LoggerConfig): OpaqueLogger => { }; }; +export const wrapLogger = (logger: OptimizelyLogger): OpaqueLogger => { + return { + [loggerSymbol]: logger, + }; +}; + export const extractLogger = (logger: OpaqueLogger): OptimizelyLogger => { return logger[loggerSymbol] as OptimizelyLogger; -} +}; + diff --git a/lib/project_config/config_manager_factory.ts b/lib/project_config/config_manager_factory.ts index 3ce76918b..6f01c2589 100644 --- a/lib/project_config/config_manager_factory.ts +++ b/lib/project_config/config_manager_factory.ts @@ -102,6 +102,12 @@ export const getOpaquePollingConfigManager = (opt: PollingConfigManagerFactoryOp }; }; +export const wrapConfigManager = (configManager: ProjectConfigManager): OpaqueConfigManager => { + return { + [configManagerSymbol]: configManager, + }; +}; + export const extractConfigManager = (opaqueConfigManager: OpaqueConfigManager): ProjectConfigManager => { return opaqueConfigManager[configManagerSymbol] as ProjectConfigManager; }; diff --git a/lib/shared_types.ts b/lib/shared_types.ts index 0cb41e6d0..13bf965cb 100644 --- a/lib/shared_types.ts +++ b/lib/shared_types.ts @@ -35,13 +35,16 @@ import { DefaultOdpEventApiManager } from './odp/event_manager/odp_event_api_man import { OdpEventManager } from './odp/event_manager/odp_event_manager'; import { OdpManager } from './odp/odp_manager'; import { ProjectConfig } from './project_config/project_config'; -import { ProjectConfigManager } from './project_config/project_config_manager'; +import { OpaqueConfigManager } from './project_config/config_manager_factory'; import { EventDispatcher } from './event_processor/event_dispatcher/event_dispatcher'; import { EventProcessor } from './event_processor/event_processor'; import { VuidManager } from './vuid/vuid_manager'; import { ErrorNotifier } from './error/error_notifier'; import { OpaqueLogger } from './logging/logger_factory'; import { OpaqueErrorNotifier } from './error/error_notifier_factory'; +import { OpaqueEventProcessor } from './event_processor/event_processor_factory'; +import { OpaqueOdpManager } from './odp/odp_manager_factory'; +import { OpaqueVuidManager } from './vuid/vuid_manager_factory'; export { EventDispatcher } from './event_processor/event_dispatcher/event_dispatcher'; export { EventProcessor } from './event_processor/event_processor'; @@ -342,8 +345,8 @@ export interface TrackListenerPayload extends ListenerPayload { * For compatibility with the previous declaration file */ export interface Config { - projectConfigManager: ProjectConfigManager; - eventProcessor?: EventProcessor; + projectConfigManager: OpaqueConfigManager; + eventProcessor?: OpaqueEventProcessor; // The object to validate against the schema jsonSchemaValidator?: { validate(jsonObject: unknown): boolean; @@ -356,8 +359,8 @@ export interface Config { defaultDecideOptions?: OptimizelyDecideOption[]; clientEngine?: string; clientVersion?: string; - odpManager?: OdpManager; - vuidManager?: VuidManager; + odpManager?: OpaqueOdpManager; + vuidManager?: OpaqueVuidManager; disposable?: boolean; } diff --git a/lib/utils/enums/index.ts b/lib/utils/enums/index.ts index 1c867fbbf..573857d00 100644 --- a/lib/utils/enums/index.ts +++ b/lib/utils/enums/index.ts @@ -41,8 +41,6 @@ export const CONTROL_ATTRIBUTES = { export const JAVASCRIPT_CLIENT_ENGINE = 'javascript-sdk'; export const NODE_CLIENT_ENGINE = 'node-sdk'; -export const REACT_CLIENT_ENGINE = 'react-sdk'; -export const REACT_NATIVE_CLIENT_ENGINE = 'react-native-sdk'; export const REACT_NATIVE_JS_CLIENT_ENGINE = 'react-native-js-sdk'; export const CLIENT_VERSION = '5.3.4'; diff --git a/lib/vuid/vuid_manager_factory.browser.spec.ts b/lib/vuid/vuid_manager_factory.browser.spec.ts index d4a7c2c72..805064c4b 100644 --- a/lib/vuid/vuid_manager_factory.browser.spec.ts +++ b/lib/vuid/vuid_manager_factory.browser.spec.ts @@ -33,8 +33,9 @@ import { getMockSyncCache } from '../tests/mock/mock_cache'; import { createVuidManager } from './vuid_manager_factory.browser'; import { LocalStorageCache } from '../utils/cache/local_storage_cache.browser'; import { DefaultVuidManager, VuidCacheManager } from './vuid_manager'; +import { extractVuidManager } from './vuid_manager_factory'; -describe('createVuidManager', () => { +describe('extractVuidManager(createVuidManager', () => { const MockVuidCacheManager = vi.mocked(VuidCacheManager); const MockLocalStorageCache = vi.mocked(LocalStorageCache); const MockDefaultVuidManager = vi.mocked(DefaultVuidManager); @@ -45,24 +46,24 @@ describe('createVuidManager', () => { }); it('should pass the enableVuid option to the DefaultVuidManager', () => { - const manager = createVuidManager({ enableVuid: true }); + const manager = extractVuidManager(createVuidManager({ enableVuid: true })); expect(manager).toBe(MockDefaultVuidManager.mock.instances[0]); expect(MockDefaultVuidManager.mock.calls[0][0].enableVuid).toBe(true); - const manager2 = createVuidManager({ enableVuid: false }); + const manager2 = extractVuidManager(createVuidManager({ enableVuid: false })); expect(manager2).toBe(MockDefaultVuidManager.mock.instances[1]); expect(MockDefaultVuidManager.mock.calls[1][0].enableVuid).toBe(false); }); it('should use the provided cache', () => { const cache = getMockSyncCache(); - const manager = createVuidManager({ enableVuid: true, vuidCache: cache }); + const manager = extractVuidManager(createVuidManager({ enableVuid: true, vuidCache: cache })); expect(manager).toBe(MockDefaultVuidManager.mock.instances[0]); expect(MockDefaultVuidManager.mock.calls[0][0].vuidCache).toBe(cache); }); it('should use a LocalStorageCache if no cache is provided', () => { - const manager = createVuidManager({ enableVuid: true }); + const manager = extractVuidManager(createVuidManager({ enableVuid: true })); expect(manager).toBe(MockDefaultVuidManager.mock.instances[0]); const usedCache = MockDefaultVuidManager.mock.calls[0][0].vuidCache; @@ -70,8 +71,8 @@ describe('createVuidManager', () => { }); it('should use a single VuidCacheManager instance for all VuidManager instances', () => { - const manager1 = createVuidManager({ enableVuid: true }); - const manager2 = createVuidManager({ enableVuid: true }); + const manager1 = extractVuidManager(createVuidManager({ enableVuid: true })); + const manager2 = extractVuidManager(createVuidManager({ enableVuid: true })); expect(manager1).toBe(MockDefaultVuidManager.mock.instances[0]); expect(manager2).toBe(MockDefaultVuidManager.mock.instances[1]); expect(MockVuidCacheManager.mock.instances.length).toBe(1); diff --git a/lib/vuid/vuid_manager_factory.browser.ts b/lib/vuid/vuid_manager_factory.browser.ts index cf8df6a44..97e94dc2e 100644 --- a/lib/vuid/vuid_manager_factory.browser.ts +++ b/lib/vuid/vuid_manager_factory.browser.ts @@ -15,14 +15,14 @@ */ import { DefaultVuidManager, VuidCacheManager, VuidManager } from './vuid_manager'; import { LocalStorageCache } from '../utils/cache/local_storage_cache.browser'; -import { VuidManagerOptions } from './vuid_manager_factory'; +import { OpaqueVuidManager, VuidManagerOptions, wrapVuidManager } from './vuid_manager_factory'; export const vuidCacheManager = new VuidCacheManager(); -export const createVuidManager = (options: VuidManagerOptions): VuidManager => { - return new DefaultVuidManager({ +export const createVuidManager = (options: VuidManagerOptions): OpaqueVuidManager => { + return wrapVuidManager(new DefaultVuidManager({ vuidCacheManager, vuidCache: options.vuidCache || new LocalStorageCache(), enableVuid: options.enableVuid - }); + })); } diff --git a/lib/vuid/vuid_manager_factory.node.ts b/lib/vuid/vuid_manager_factory.node.ts index ebc7fd373..e8de3e564 100644 --- a/lib/vuid/vuid_manager_factory.node.ts +++ b/lib/vuid/vuid_manager_factory.node.ts @@ -14,11 +14,10 @@ * limitations under the License. */ import { VuidManager } from './vuid_manager'; -import { VuidManagerOptions } from './vuid_manager_factory'; +import { OpaqueVuidManager, VuidManagerOptions } from './vuid_manager_factory'; export const VUID_IS_NOT_SUPPORTED_IN_NODEJS= 'VUID is not supported in Node.js environment'; -export const createVuidManager = (options: VuidManagerOptions): VuidManager => { +export const createVuidManager = (options: VuidManagerOptions): OpaqueVuidManager => { throw new Error(VUID_IS_NOT_SUPPORTED_IN_NODEJS); }; - diff --git a/lib/vuid/vuid_manager_factory.react_native.spec.ts b/lib/vuid/vuid_manager_factory.react_native.spec.ts index 22920c099..8057946e3 100644 --- a/lib/vuid/vuid_manager_factory.react_native.spec.ts +++ b/lib/vuid/vuid_manager_factory.react_native.spec.ts @@ -34,8 +34,9 @@ import { createVuidManager } from './vuid_manager_factory.react_native'; import { AsyncStorageCache } from '../utils/cache/async_storage_cache.react_native'; import { DefaultVuidManager, VuidCacheManager } from './vuid_manager'; +import { extractVuidManager } from './vuid_manager_factory'; -describe('createVuidManager', () => { +describe('extractVuidManager(createVuidManager', () => { const MockVuidCacheManager = vi.mocked(VuidCacheManager); const MockAsyncStorageCache = vi.mocked(AsyncStorageCache); const MockDefaultVuidManager = vi.mocked(DefaultVuidManager); @@ -46,24 +47,24 @@ describe('createVuidManager', () => { }); it('should pass the enableVuid option to the DefaultVuidManager', () => { - const manager = createVuidManager({ enableVuid: true }); + const manager = extractVuidManager(createVuidManager({ enableVuid: true })); expect(manager).toBe(MockDefaultVuidManager.mock.instances[0]); expect(MockDefaultVuidManager.mock.calls[0][0].enableVuid).toBe(true); - const manager2 = createVuidManager({ enableVuid: false }); + const manager2 = extractVuidManager(createVuidManager({ enableVuid: false })); expect(manager2).toBe(MockDefaultVuidManager.mock.instances[1]); expect(MockDefaultVuidManager.mock.calls[1][0].enableVuid).toBe(false); }); it('should use the provided cache', () => { const cache = getMockAsyncCache(); - const manager = createVuidManager({ enableVuid: true, vuidCache: cache }); + const manager = extractVuidManager(createVuidManager({ enableVuid: true, vuidCache: cache })); expect(manager).toBe(MockDefaultVuidManager.mock.instances[0]); expect(MockDefaultVuidManager.mock.calls[0][0].vuidCache).toBe(cache); }); it('should use a AsyncStorageCache if no cache is provided', () => { - const manager = createVuidManager({ enableVuid: true }); + const manager = extractVuidManager(createVuidManager({ enableVuid: true })); expect(manager).toBe(MockDefaultVuidManager.mock.instances[0]); const usedCache = MockDefaultVuidManager.mock.calls[0][0].vuidCache; @@ -71,8 +72,8 @@ describe('createVuidManager', () => { }); it('should use a single VuidCacheManager instance for all VuidManager instances', () => { - const manager1 = createVuidManager({ enableVuid: true }); - const manager2 = createVuidManager({ enableVuid: true }); + const manager1 = extractVuidManager(createVuidManager({ enableVuid: true })); + const manager2 = extractVuidManager(createVuidManager({ enableVuid: true })); expect(manager1).toBe(MockDefaultVuidManager.mock.instances[0]); expect(manager2).toBe(MockDefaultVuidManager.mock.instances[1]); expect(MockVuidCacheManager.mock.instances.length).toBe(1); diff --git a/lib/vuid/vuid_manager_factory.react_native.ts b/lib/vuid/vuid_manager_factory.react_native.ts index 6eba4c9f2..51b3f754b 100644 --- a/lib/vuid/vuid_manager_factory.react_native.ts +++ b/lib/vuid/vuid_manager_factory.react_native.ts @@ -15,14 +15,14 @@ */ import { DefaultVuidManager, VuidCacheManager, VuidManager } from './vuid_manager'; import { AsyncStorageCache } from '../utils/cache/async_storage_cache.react_native'; -import { VuidManagerOptions } from './vuid_manager_factory'; +import { OpaqueVuidManager, VuidManagerOptions, wrapVuidManager } from './vuid_manager_factory'; export const vuidCacheManager = new VuidCacheManager(); -export const createVuidManager = (options: VuidManagerOptions): VuidManager => { - return new DefaultVuidManager({ +export const createVuidManager = (options: VuidManagerOptions): OpaqueVuidManager => { + return wrapVuidManager(new DefaultVuidManager({ vuidCacheManager, vuidCache: options.vuidCache || new AsyncStorageCache(), enableVuid: options.enableVuid - }); + })); } diff --git a/lib/vuid/vuid_manager_factory.ts b/lib/vuid/vuid_manager_factory.ts index ab2264242..61ac36966 100644 --- a/lib/vuid/vuid_manager_factory.ts +++ b/lib/vuid/vuid_manager_factory.ts @@ -15,8 +15,25 @@ */ import { Cache } from '../utils/cache/cache'; +import { VuidManager } from './vuid_manager'; export type VuidManagerOptions = { vuidCache?: Cache; enableVuid?: boolean; } + +const vuidManagerSymbol: unique symbol = Symbol(); + +export type OpaqueVuidManager = { + [vuidManagerSymbol]: unknown; +}; + +export const extractVuidManager = (opaqueVuidManager: OpaqueVuidManager): VuidManager => { + return opaqueVuidManager[vuidManagerSymbol] as VuidManager; +}; + +export const wrapVuidManager = (vuidManager: VuidManager): OpaqueVuidManager => { + return { + [vuidManagerSymbol]: vuidManager + } +}; From 4b8ff1f0936fe7ea3648e94c43c4e35a253434e0 Mon Sep 17 00:00:00 2001 From: Raju Ahmed Date: Thu, 13 Feb 2025 22:14:13 +0600 Subject: [PATCH 4/5] copyright --- lib/client_factory.ts | 16 ++++++++++++++++ lib/entrypoint.test-d.ts | 16 ++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/lib/client_factory.ts b/lib/client_factory.ts index 04a5567ed..9b016fb4b 100644 --- a/lib/client_factory.ts +++ b/lib/client_factory.ts @@ -1,3 +1,19 @@ +/** + * Copyright 2025, Optimizely + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import { LoggerFacade } from "./logging/logger"; import { Client, Config } from "./shared_types"; import { Maybe } from "./utils/type"; diff --git a/lib/entrypoint.test-d.ts b/lib/entrypoint.test-d.ts index 5ed9bb14a..f408688b2 100644 --- a/lib/entrypoint.test-d.ts +++ b/lib/entrypoint.test-d.ts @@ -1,3 +1,19 @@ +/** + * Copyright 2025, Optimizely + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import { expectTypeOf } from 'vitest'; import * as browserEntrypoint from './index.browser'; From 1af7fcddb9fb30b4947450a79156bf6e6bbaaa5b Mon Sep 17 00:00:00 2001 From: Raju Ahmed Date: Thu, 13 Feb 2025 22:15:00 +0600 Subject: [PATCH 5/5] rem --- lib/client_factory.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/client_factory.ts b/lib/client_factory.ts index 9b016fb4b..898df5575 100644 --- a/lib/client_factory.ts +++ b/lib/client_factory.ts @@ -69,7 +69,6 @@ export const getOptimizelyInstance = (config: Config): Client | null => { return new Optimizely(optimizelyOptions); } catch (e) { - console.log('got error ', e); logger?.error(e); return null; }