diff --git a/packages/optimizely-sdk/lib/core/odp/odp_event_api_manager.ts b/packages/optimizely-sdk/lib/core/odp/odp_event_api_manager.ts index b5b3c69a8..d067855e1 100644 --- a/packages/optimizely-sdk/lib/core/odp/odp_event_api_manager.ts +++ b/packages/optimizely-sdk/lib/core/odp/odp_event_api_manager.ts @@ -16,9 +16,7 @@ import { LogHandler, LogLevel } from '../../modules/logging'; import { OdpEvent } from './odp_event'; -import { isBrowserContext } from './odp_utils'; import { RequestHandler } from '../../utils/http_request_handler/http'; -import { ODP_EVENT_BROWSER_ENDPOINT } from '../../utils/enums'; const EVENT_SENDING_FAILURE_MESSAGE = 'ODP event send failed'; @@ -32,10 +30,9 @@ export interface IOdpEventApiManager { /** * Concrete implementation for accessing the ODP REST API */ -export class OdpEventApiManager implements IOdpEventApiManager { +export abstract class OdpEventApiManager implements IOdpEventApiManager { private readonly logger: LogHandler; private readonly requestHandler: RequestHandler; - private readonly isBrowser: boolean; /** * Creates instance to access Optimizely Data Platform (ODP) REST API @@ -45,7 +42,10 @@ export class OdpEventApiManager implements IOdpEventApiManager { constructor(requestHandler: RequestHandler, logger: LogHandler) { this.requestHandler = requestHandler; this.logger = logger; - this.isBrowser = isBrowserContext() + } + + public getLogger(): LogHandler { + return this.logger; } /** @@ -68,39 +68,11 @@ export class OdpEventApiManager implements IOdpEventApiManager { return shouldRetry; } - if (events.length > 1 && this.isBrowser) { - this.logger.log(LogLevel.ERROR, `${EVENT_SENDING_FAILURE_MESSAGE} (browser only supports batch size 1)`); + if (!this.shouldSendEvents(events)) { return shouldRetry; } - let method, endpoint, headers, data; - - if (this.isBrowser) { - method = 'GET'; - const event = events[0]; - const url = new URL(ODP_EVENT_BROWSER_ENDPOINT); - event.identifiers.forEach((v, k) =>{ - url.searchParams.append(k, v); - }); - event.data.forEach((v, k) =>{ - url.searchParams.append(k, v as string); - }); - url.searchParams.append('tracker_id', apiKey); - url.searchParams.append('event_type', event.type); - url.searchParams.append('vdl_action', event.action); - endpoint = url.toString(); - headers = {}; - } else { - method = 'POST'; - endpoint = `${apiHost}/v3/events`; - headers = { - 'Content-Type': 'application/json', - 'x-api-key': apiKey, - }; - data = JSON.stringify(events, this.replacer); - } - - + const { method, endpoint, headers, data } = this.generateRequestData(apiHost, apiKey, events); let statusCode = 0; try { @@ -127,11 +99,12 @@ export class OdpEventApiManager implements IOdpEventApiManager { return shouldRetry; } - private replacer(_: unknown, value: unknown) { - if (value instanceof Map) { - return Object.fromEntries(value); - } else { - return value; - } + protected abstract shouldSendEvents(events: OdpEvent[]): boolean; + + protected abstract generateRequestData(apiHost: string, apiKey: string, events: OdpEvent[]): { + method: string, + endpoint: string, + headers: {[key: string]: string}, + data: string, } } diff --git a/packages/optimizely-sdk/lib/core/odp/odp_event_manager.ts b/packages/optimizely-sdk/lib/core/odp/odp_event_manager.ts index 249a772ca..ed1457d0a 100644 --- a/packages/optimizely-sdk/lib/core/odp/odp_event_manager.ts +++ b/packages/optimizely-sdk/lib/core/odp/odp_event_manager.ts @@ -22,7 +22,7 @@ import { ERROR_MESSAGES, ODP_USER_KEY, ODP_DEFAULT_EVENT_TYPE, ODP_EVENT_ACTION import { OdpEvent } from './odp_event'; import { OdpConfig } from './odp_config'; import { OdpEventApiManager } from './odp_event_api_manager'; -import { invalidOdpDataFound, isBrowserContext } from './odp_utils'; +import { invalidOdpDataFound } from './odp_utils'; const MAX_RETRIES = 3; const DEFAULT_BATCH_SIZE = 10; @@ -61,16 +61,16 @@ export interface IOdpEventManager { /** * Concrete implementation of a manager for persisting events to the Optimizely Data Platform */ -export class OdpEventManager implements IOdpEventManager { +export abstract class OdpEventManager implements IOdpEventManager { /** * Current state of the event processor */ public state: STATE = STATE.STOPPED; /** * Queue for holding all events to be eventually dispatched - * @private + * @protected */ - private queue = new Array(); + protected queue = new Array(); /** * Identifier of the currently running timeout so clearCurrentTimeout() can be called * @private @@ -93,19 +93,19 @@ export class OdpEventManager implements IOdpEventManager { private readonly logger: LogHandler; /** * Maximum queue size - * @private + * @protected */ - private readonly queueSize: number; + protected queueSize!: number; /** * Maximum number of events to process at once. Ignored in browser context - * @private + * @protected */ - private readonly batchSize: number; + protected batchSize!: number; /** * Milliseconds between setTimeout() to process new batches. Ignored in browser context - * @private + * @protected */ - private readonly flushInterval: number; + protected flushInterval!: number; /** * Type of execution context eg node, js, react * @private @@ -141,33 +141,16 @@ export class OdpEventManager implements IOdpEventManager { this.logger = logger; this.clientEngine = clientEngine; this.clientVersion = clientVersion; - - const isBrowser = isBrowserContext(); - - const defaultQueueSize = isBrowser ? DEFAULT_BROWSER_QUEUE_SIZE : DEFAULT_SERVER_QUEUE_SIZE; - this.queueSize = queueSize || defaultQueueSize; - this.batchSize = batchSize || DEFAULT_BATCH_SIZE; - - if (flushInterval === 0 || isBrowser) { - // disable event batching - this.batchSize = 1; - this.flushInterval = 0; - } else { - this.flushInterval = flushInterval || DEFAULT_FLUSH_INTERVAL_MSECS; - } - - if (isBrowser) { - if (typeof batchSize !== 'undefined' && batchSize !== 1) { - this.logger.log(LogLevel.WARNING, 'ODP event batch size must be 1 in the browser.'); - } - if (typeof flushInterval !== 'undefined' && flushInterval !== 0) { - this.logger.log(LogLevel.WARNING, 'ODP event flush interval must be 0 in the browser.'); - } - } - + this.initParams(batchSize, queueSize, flushInterval); this.state = STATE.STOPPED; } + protected abstract initParams( + batchSize: number | undefined, + queueSize: number | undefined, + flushInterval: number | undefined, + ): void; + /** * Update ODP configuration settings. * @param newConfig New configuration to apply @@ -401,19 +384,12 @@ export class OdpEventManager implements IOdpEventManager { if (this.odpConfig.isReady()) { return true; } - - if (!isBrowserContext()) { - // if Node/server-side context, empty queue items before ready state - this.logger.log(LogLevel.WARNING, 'ODPConfig not ready. Discarding events in queue.'); - this.queue = new Array(); - } else { - // in Browser/client-side context, give debug message but leave events in queue - this.logger.log(LogLevel.DEBUG, 'ODPConfig not ready. Leaving events in queue.'); - } - + this.discardEventsIfNeeded(); return false; } + protected abstract discardEventsIfNeeded(): void; + /** * Add additional common data including an idempotent ID and execution context to event data * @param sourceData Existing event data to augment @@ -430,4 +406,8 @@ export class OdpEventManager implements IOdpEventManager { sourceData.forEach((value, key) => data.set(key, value)); return data; } + + protected getLogger(): LogHandler { + return this.logger; + } } diff --git a/packages/optimizely-sdk/lib/core/odp/odp_manager.ts b/packages/optimizely-sdk/lib/core/odp/odp_manager.ts index 002405968..6c6b4ad56 100644 --- a/packages/optimizely-sdk/lib/core/odp/odp_manager.ts +++ b/packages/optimizely-sdk/lib/core/odp/odp_manager.ts @@ -14,51 +14,25 @@ * limitations under the License. */ -import { BROWSER_CLIENT_VERSION, LOG_MESSAGES } from './../../utils/enums/index'; +import { LOG_MESSAGES } from './../../utils/enums/index'; import { getLogger, LogHandler, LogLevel } from '../../modules/logging'; import { ERROR_MESSAGES, ODP_USER_KEY } from '../../utils/enums'; -import { RequestHandler } from './../../utils/http_request_handler/http'; - -import { LRUCache } from './../../utils/lru_cache/lru_cache'; - import { VuidManager } from '../../plugins/vuid_manager'; import { OdpConfig } from './odp_config'; import { OdpEventManager } from './odp_event_manager'; import { OdpSegmentManager } from './odp_segment_manager'; -import { OdpSegmentApiManager } from './odp_segment_api_manager'; -import { OdpEventApiManager } from './odp_event_api_manager'; import { OptimizelySegmentOption } from './optimizely_segment_option'; import { invalidOdpDataFound } from './odp_utils'; import { OdpEvent } from './odp_event'; -import { OdpOptions } from '../../shared_types'; - -/** - * @param {LRUCache[]} segmentLRUCache Cache to be used for storing segments. - * @param {RequestHandler} segmentRequestHandler HTTP request handler that will be used by the ODP Segment Manager. - * @param {RequestHandler} eventRequestHandler HTTP request handler that will be used by the ODP Event Manager. - * @param {LogHandler} logger (Optional) Accepts custom LogHandler. Defaults to the default global LogHandler. - * @param {string} clientEngine (Optional) String denoting specific client engine being used. Defaults to 'javascript-sdk'. - * @param {string} clientVersion (Optional) String denoting specific client version. Defaults to current version value from package.json. - * @param {OdpOptions} odpOptions (Optional) Configuration settings for various ODP options from segment cache size to event flush interval. - */ -interface OdpManagerConfig { - segmentLRUCache: LRUCache; - segmentRequestHandler: RequestHandler; - eventRequestHandler: RequestHandler; - logger?: LogHandler; - clientEngine?: string; - clientVersion?: string; - odpOptions?: OdpOptions; -} /** * Orchestrates segments manager, event manager, and ODP configuration */ -export class OdpManager { - enabled: boolean; - logger: LogHandler; +export abstract class OdpManager { + enabled = true; + logger: LogHandler = getLogger(); odpConfig: OdpConfig = new OdpConfig(); /** @@ -73,54 +47,7 @@ export class OdpManager { */ public eventManager: OdpEventManager | undefined; - constructor({ - segmentLRUCache, - segmentRequestHandler, - eventRequestHandler, - logger, - clientEngine, - clientVersion, - odpOptions, - }: OdpManagerConfig) { - this.enabled = !odpOptions?.disabled; - this.logger = logger || getLogger(); - - if (!this.enabled) { - this.logger.log(LogLevel.INFO, LOG_MESSAGES.ODP_DISABLED); - return; - } - - // Set up Segment Manager (Audience Segments GraphQL API Interface) - if (odpOptions?.segmentManager) { - this.segmentManager = odpOptions.segmentManager; - this.segmentManager.updateSettings(this.odpConfig); - } else { - this.segmentManager = new OdpSegmentManager( - this.odpConfig, - segmentLRUCache, - new OdpSegmentApiManager(segmentRequestHandler, this.logger) - ); - } - - // Set up Events Manager (Events REST API Interface) - if (odpOptions?.eventManager) { - this.eventManager = odpOptions.eventManager; - this.eventManager.updateSettings(this.odpConfig); - } else { - this.eventManager = new OdpEventManager({ - odpConfig: this.odpConfig, - apiManager: new OdpEventApiManager(eventRequestHandler, this.logger), - logger: this.logger, - clientEngine: clientEngine || 'javascript-sdk', - clientVersion: clientVersion || BROWSER_CLIENT_VERSION, - flushInterval: odpOptions?.eventFlushInterval, - batchSize: odpOptions?.eventBatchSize, - queueSize: odpOptions?.eventQueueSize, - }); - } - - this.eventManager.start(); - } + constructor() {} /** * Provides a method to update ODP Manager's ODP Config API Key, API Host, and Audience Segments @@ -246,4 +173,8 @@ export class OdpManager { this.eventManager.sendEvent(new OdpEvent(type, action, identifiers, data)); } + + public abstract isVuidEnabled(): boolean; + + public abstract getVuid(): string | undefined; } diff --git a/packages/optimizely-sdk/lib/core/odp/odp_utils.ts b/packages/optimizely-sdk/lib/core/odp/odp_utils.ts index bf5fd7d05..875b7e091 100644 --- a/packages/optimizely-sdk/lib/core/odp/odp_utils.ts +++ b/packages/optimizely-sdk/lib/core/odp/odp_utils.ts @@ -30,13 +30,3 @@ export function invalidOdpDataFound(data: Map): boolean { }); return foundInvalidValue; } - -/** - * TODO: Consider moving this into general utilities - * Determine if the runtime environment is a browser - * @returns True if in the browser - * @private - */ -export function isBrowserContext(): boolean { - return !(typeof process !== 'undefined' && process.versions != null && process.versions.node != null); -} diff --git a/packages/optimizely-sdk/lib/index.browser.tests.js b/packages/optimizely-sdk/lib/index.browser.tests.js index 25cc05185..d99664627 100644 --- a/packages/optimizely-sdk/lib/index.browser.tests.js +++ b/packages/optimizely-sdk/lib/index.browser.tests.js @@ -28,9 +28,8 @@ import OptimizelyUserContext from './optimizely_user_context'; import { LOG_MESSAGES, ODP_EVENT_ACTION, ODP_EVENT_BROWSER_ENDPOINT } from './utils/enums'; import { BrowserOdpManager } from './plugins/odp_manager/index.browser'; import { OdpConfig } from './core/odp/odp_config'; -import { OdpEventManager } from './core/odp/odp_event_manager'; -import { OdpEventApiManager } from './core/odp/odp_event_api_manager'; -import { isBrowserContext } from './core/odp/odp_utils'; +import { BrowserOdpEventManager } from './plugins/odp/event_manager/index.browser'; +import { BrowserOdpEventApiManager } from './plugins/odp/event_api_manager/index.browser'; var LocalStoragePendingEventsDispatcher = eventProcessor.LocalStoragePendingEventsDispatcher; @@ -824,9 +823,6 @@ describe('javascript-sdk (Browser)', function() { }); it('should log a warning when attempting to use an event batch size other than 1', async () => { - if (!isBrowserContext()) { - return; - } const client = optimizelyFactory.createInstance({ datafile: testData.getOdpIntegratedConfigWithSegments(), errorHandler: fakeErrorHandler, @@ -852,12 +848,9 @@ describe('javascript-sdk (Browser)', function() { }); it('should send an odp event to the browser endpoint', async () => { - if (!isBrowserContext()) { - return; - } const odpConfig = new OdpConfig(); - const apiManager = new OdpEventApiManager(mockRequestHandler, logger); - const eventManager = new OdpEventManager({ + const apiManager = new BrowserOdpEventApiManager(mockRequestHandler, logger); + const eventManager = new BrowserOdpEventManager({ odpConfig, apiManager, logger, diff --git a/packages/optimizely-sdk/lib/index.node.ts b/packages/optimizely-sdk/lib/index.node.ts index 56ba69c71..20b977f94 100644 --- a/packages/optimizely-sdk/lib/index.node.ts +++ b/packages/optimizely-sdk/lib/index.node.ts @@ -1,5 +1,5 @@ /**************************************************************************** - * Copyright 2016-2017, 2019-2022 Optimizely, Inc. and contributors * + * Copyright 2016-2017, 2019-2023 Optimizely, Inc. and contributors * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * @@ -13,14 +13,7 @@ * See the License for the specific language governing permissions and * * limitations under the License. * ***************************************************************************/ -import { - getLogger, - setErrorHandler, - getErrorHandler, - LogLevel, - setLogHandler, - setLogLevel -} from './modules/logging'; +import { getLogger, setErrorHandler, getErrorHandler, LogLevel, setLogHandler, setLogLevel } from './modules/logging'; import Optimizely from './optimizely'; import * as enums from './utils/enums'; import * as loggerPlugin from './plugins/logger'; @@ -32,6 +25,7 @@ import { createNotificationCenter } from './core/notification_center'; import { createEventProcessor } from './plugins/event_processor'; import { OptimizelyDecideOption, Client, Config } from './shared_types'; import { createHttpPollingDatafileManager } from './plugins/datafile_manager/http_polling_datafile_manager'; +import { NodeOdpManager } from './plugins/odp_manager/index.node'; const logger = getLogger(); setLogLevel(LogLevel.ERROR); @@ -69,7 +63,7 @@ const createInstance = function(config: Config): Client | null { configValidator.validate(config); isValidInstance = true; // eslint-disable-next-line @typescript-eslint/no-explicit-any - } catch (ex: any) { + } catch (ex) { if (hasLogger) { logger.error(ex); } else { @@ -100,9 +94,9 @@ const createInstance = function(config: Config): Client | null { dispatcher: config.eventDispatcher || defaultEventDispatcher, flushInterval: eventFlushInterval, batchSize: eventBatchSize, - maxQueueSize: config.eventMaxQueueSize || DEFAULT_EVENT_MAX_QUEUE_SIZE, + maxQueueSize: config.eventMaxQueueSize || DEFAULT_EVENT_MAX_QUEUE_SIZE, notificationCenter, - } + }; const eventProcessor = createEventProcessor(eventProcessorConfig); @@ -112,14 +106,17 @@ const createInstance = function(config: Config): Client | null { eventProcessor, logger, errorHandler, - datafileManager: config.sdkKey ? createHttpPollingDatafileManager(config.sdkKey, logger, config.datafile, config.datafileOptions) : undefined, + datafileManager: config.sdkKey + ? createHttpPollingDatafileManager(config.sdkKey, logger, config.datafile, config.datafileOptions) + : undefined, notificationCenter, isValidInstance: isValidInstance, + odpManager: new NodeOdpManager({ logger, odpOptions: config.odpOptions }), }; return new Optimizely(optimizelyOptions); // eslint-disable-next-line @typescript-eslint/no-explicit-any - } catch (e: any) { + } catch (e) { logger.error(e); return null; } @@ -150,4 +147,4 @@ export default { OptimizelyDecideOption, }; -export * from './export_types' +export * from './export_types'; diff --git a/packages/optimizely-sdk/lib/optimizely/index.browser.ts b/packages/optimizely-sdk/lib/optimizely/index.browser.ts deleted file mode 100644 index 092950845..000000000 --- a/packages/optimizely-sdk/lib/optimizely/index.browser.ts +++ /dev/null @@ -1,62 +0,0 @@ -/**************************************************************************** - * Copyright 2023, Optimizely, Inc. and contributors * - * * - * 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 OptimizelyUserContext from '../optimizely_user_context'; -import Optimizely from '.'; -import { OptimizelyOptions, UserAttributes } from '../shared_types'; -import { BrowserOdpManager } from '../plugins/odp_manager/index.browser'; - -export default class BrowserOptimizely extends Optimizely { - constructor(config: OptimizelyOptions) { - super(config); - } - - /** - * @override - * Creates a context of the user for which decision APIs will be called. - * - * A user context will be created successfully even when the SDK is not fully configured yet, however, - * for just the browser variant ODP Manager is expected to have been instantiated already in order to - * access the stored vuid. - * - * @param {string} userId (Optional) The user ID to be used for bucketing. Defaults to VUID. - * @param {UserAttributes} attributes (Optional) Arbitrary attributes map. - * @return {OptimizelyUserContext|null} An OptimizelyUserContext associated with this OptimizelyClient or - * null if provided inputs are invalid - */ - createUserContext(userId?: string, attributes?: UserAttributes): OptimizelyUserContext | null { - const userIdentifier = userId || this.getVuid(); - - if (!userIdentifier) { - return null; - } - - return super.createUserContext(userIdentifier, attributes); - } - - /** - * @returns {string|undefined} Currently provisioned VUID from local ODP Manager or undefined if - * ODP Manager has not been instantiated yet for any reason. - */ - getVuid(): string | undefined { - if (!this.odpManager) { - this.logger?.error('Unable to get VUID - ODP Manager is not instantiated yet.'); - return undefined; - } - - return (this.odpManager as BrowserOdpManager).vuid; - } -} diff --git a/packages/optimizely-sdk/lib/optimizely/index.tests.js b/packages/optimizely-sdk/lib/optimizely/index.tests.js index c7d63aecc..7b1f30dc3 100644 --- a/packages/optimizely-sdk/lib/optimizely/index.tests.js +++ b/packages/optimizely-sdk/lib/optimizely/index.tests.js @@ -10091,7 +10091,6 @@ describe('lib/optimizely', function() { describe('odp', () => { var optlyInstanceWithOdp; var bucketStub; - var fakeDecisionResponse; var notificationCenter = createNotificationCenter({ logger: createdLogger, errorHandler }); var eventProcessor = createForwardingEventProcessor(eventDispatcher, notificationCenter); var createdLogger = logger.createLogger({ diff --git a/packages/optimizely-sdk/lib/optimizely/index.ts b/packages/optimizely-sdk/lib/optimizely/index.ts index 97a0959f8..8f353de9c 100644 --- a/packages/optimizely-sdk/lib/optimizely/index.ts +++ b/packages/optimizely-sdk/lib/optimizely/index.ts @@ -23,8 +23,6 @@ import { OdpManager } from '../core/odp/odp_manager'; import { OdpConfig } from '../core/odp/odp_config'; import { OdpEvent } from '../core/odp/odp_event'; import { OptimizelySegmentOption } from '../core/odp/optimizely_segment_option'; -import { BrowserOdpManager } from '../plugins/odp_manager/index.browser'; -import { isBrowserContext } from '../core/odp/odp_utils'; import { UserAttributes, @@ -1450,7 +1448,7 @@ export default class Optimizely { createUserContext(userId?: string, attributes?: UserAttributes): OptimizelyUserContext | null { let userIdentifier; - if (isBrowserContext() && !userId) { + if (this.odpManager?.isVuidEnabled() && !userId) { userIdentifier = userId || this.getVuid(); } else { userIdentifier = userId; @@ -1743,16 +1741,16 @@ export default class Optimizely { * ODP Manager has not been instantiated yet for any reason. */ getVuid(): string | undefined { - if (!isBrowserContext()) { - this.logger.log(LOG_LEVEL.WARNING, 'getVuid() unavailable in non-browser contexts', MODULE_NAME); - return undefined; - } - if (!this.odpManager) { this.logger?.error('Unable to get VUID - ODP Manager is not instantiated yet.'); return undefined; } - - return (this.odpManager as BrowserOdpManager).vuid; + + if (!this.odpManager.isVuidEnabled()) { + this.logger.log(LOG_LEVEL.WARNING, 'getVuid() unavailable for this platform', MODULE_NAME); + return undefined; + } + + return this.odpManager.getVuid(); } } diff --git a/packages/optimizely-sdk/lib/plugins/odp/event_api_manager/index.browser.ts b/packages/optimizely-sdk/lib/plugins/odp/event_api_manager/index.browser.ts new file mode 100644 index 000000000..ab561c664 --- /dev/null +++ b/packages/optimizely-sdk/lib/plugins/odp/event_api_manager/index.browser.ts @@ -0,0 +1,38 @@ +import { OdpEvent } from "../../../core/odp/odp_event"; +import { OdpEventApiManager } from "../../../core/odp/odp_event_api_manager"; +import { LogHandler, LogLevel } from '../../../modules/logging'; +import { ODP_EVENT_BROWSER_ENDPOINT } from '../../../utils/enums'; + +const EVENT_SENDING_FAILURE_MESSAGE = 'ODP event send failed'; + +export class BrowserOdpEventApiManager extends OdpEventApiManager { + protected shouldSendEvents(events: OdpEvent[]): boolean { + if (events.length <= 1) { + return true; + } + this.getLogger().log(LogLevel.ERROR, `${EVENT_SENDING_FAILURE_MESSAGE} (browser only supports batch size 1)`); + return false; + } + + protected generateRequestData(apiHost: string, apiKey: string, events: OdpEvent[]): { method: string; endpoint: string; headers: { [key: string]: string; }; data: string; } { + const method = 'GET'; + const event = events[0]; + const url = new URL(ODP_EVENT_BROWSER_ENDPOINT); + event.identifiers.forEach((v, k) =>{ + url.searchParams.append(k, v); + }); + event.data.forEach((v, k) =>{ + url.searchParams.append(k, v as string); + }); + url.searchParams.append('tracker_id', apiKey); + url.searchParams.append('event_type', event.type); + url.searchParams.append('vdl_action', event.action); + const endpoint = url.toString(); + return { + method, + endpoint, + headers: {}, + data: "", + }; + } +} \ No newline at end of file diff --git a/packages/optimizely-sdk/lib/plugins/odp/event_api_manager/index.node.ts b/packages/optimizely-sdk/lib/plugins/odp/event_api_manager/index.node.ts new file mode 100644 index 000000000..06dc79e8d --- /dev/null +++ b/packages/optimizely-sdk/lib/plugins/odp/event_api_manager/index.node.ts @@ -0,0 +1,28 @@ +import { OdpEvent } from "../../../core/odp/odp_event"; +import { OdpEventApiManager } from "../../../core/odp/odp_event_api_manager"; + +export class NodeOdpEventApiManager extends OdpEventApiManager { + protected shouldSendEvents(events: OdpEvent[]): boolean { + return true; + } + + protected generateRequestData(apiHost: string, apiKey: string, events: OdpEvent[]): { method: string; endpoint: string; headers: { [key: string]: string; }; data: string; } { + return { + method: 'POST', + endpoint: `${apiHost}/v3/events`, + headers: { + 'Content-Type': 'application/json', + 'x-api-key': apiKey, + }, + data: JSON.stringify(events, this.replacer), + } + } + + private replacer(_: unknown, value: unknown) { + if (value instanceof Map) { + return Object.fromEntries(value); + } else { + return value; + } + } +} \ No newline at end of file diff --git a/packages/optimizely-sdk/lib/plugins/odp/event_manager/index.browser.ts b/packages/optimizely-sdk/lib/plugins/odp/event_manager/index.browser.ts new file mode 100644 index 000000000..9ace87c16 --- /dev/null +++ b/packages/optimizely-sdk/lib/plugins/odp/event_manager/index.browser.ts @@ -0,0 +1,27 @@ +import { OdpEventManager } from "../../../../lib/core/odp/odp_event_manager"; +import { LogLevel } from '../../../modules/logging'; + +const DEFAULT_BROWSER_QUEUE_SIZE = 100; + +export class BrowserOdpEventManager extends OdpEventManager { + protected initParams(batchSize: number | undefined, queueSize: number | undefined, flushInterval: number | undefined): void { + this.queueSize = queueSize || DEFAULT_BROWSER_QUEUE_SIZE; + + // disable event batching for browser + this.batchSize = 1; + this.flushInterval = 0; + + if (typeof batchSize !== 'undefined' && batchSize !== 1) { + this.getLogger().log(LogLevel.WARNING, 'ODP event batch size must be 1 in the browser.'); + } + + if (typeof flushInterval !== 'undefined' && flushInterval !== 0) { + this.getLogger().log(LogLevel.WARNING, 'ODP event flush interval must be 0 in the browser.'); + } + } + + protected discardEventsIfNeeded(): void { + // in Browser/client-side context, give debug message but leave events in queue + this.getLogger().log(LogLevel.DEBUG, 'ODPConfig not ready. Leaving events in queue.'); + } +} \ No newline at end of file diff --git a/packages/optimizely-sdk/lib/plugins/odp/event_manager/index.node.ts b/packages/optimizely-sdk/lib/plugins/odp/event_manager/index.node.ts new file mode 100644 index 000000000..83ddf0296 --- /dev/null +++ b/packages/optimizely-sdk/lib/plugins/odp/event_manager/index.node.ts @@ -0,0 +1,29 @@ + +import { OdpEvent } from "../../../../lib/core/odp/odp_event"; +import { OdpEventManager } from "../../../../lib/core/odp/odp_event_manager"; +import { LogLevel } from "../../../../lib/modules/logging"; + +const DEFAULT_BATCH_SIZE = 10; +const DEFAULT_FLUSH_INTERVAL_MSECS = 1000; +const DEFAULT_SERVER_QUEUE_SIZE = 10000; + +export class NodeOdpEventManager extends OdpEventManager { + protected initParams(batchSize: number | undefined, queueSize: number | undefined, flushInterval: number | undefined): void { + this.queueSize = queueSize || DEFAULT_SERVER_QUEUE_SIZE; + this.batchSize = batchSize || DEFAULT_BATCH_SIZE; + + if (flushInterval === 0) { + // disable event batching + this.batchSize = 1; + this.flushInterval = 0; + } else { + this.flushInterval = flushInterval || DEFAULT_FLUSH_INTERVAL_MSECS; + } + } + + protected discardEventsIfNeeded(): void { + // if Node/server-side context, empty queue items before ready state + this.getLogger().log(LogLevel.WARNING, 'ODPConfig not ready. Discarding events in queue.'); + this.queue = new Array(); + } +} \ No newline at end of file diff --git a/packages/optimizely-sdk/lib/plugins/odp_manager/index.browser.ts b/packages/optimizely-sdk/lib/plugins/odp_manager/index.browser.ts index d625e3030..46f099010 100644 --- a/packages/optimizely-sdk/lib/plugins/odp_manager/index.browser.ts +++ b/packages/optimizely-sdk/lib/plugins/odp_manager/index.browser.ts @@ -21,6 +21,7 @@ import { ODP_USER_KEY, REQUEST_TIMEOUT_ODP_SEGMENTS_MS, REQUEST_TIMEOUT_ODP_EVENTS_MS, + LOG_MESSAGES, } from '../../utils/enums'; import { getLogger, LogHandler, LogLevel } from '../../modules/logging'; @@ -35,6 +36,10 @@ import { VuidManager } from './../vuid_manager/index'; import { OdpManager } from '../../core/odp/odp_manager'; import { OdpEvent } from '../../core/odp/odp_event'; import { OdpOptions } from '../../shared_types'; +import { BrowserOdpEventApiManager } from '../odp/event_api_manager/index.browser'; +import { BrowserOdpEventManager } from '../odp/event_manager/index.browser'; +import { OdpSegmentManager } from '../../core/odp/odp_segment_manager'; +import { OdpSegmentApiManager } from '../../core/odp/odp_segment_api_manager'; interface BrowserOdpManagerConfig { logger?: LogHandler; @@ -48,7 +53,15 @@ export class BrowserOdpManager extends OdpManager { initPromise?: Promise; constructor({ logger, odpOptions }: BrowserOdpManagerConfig) { - const browserLogger = logger || getLogger('BrowserOdpManager'); + super(); + + this.logger = logger || getLogger(); + + if (odpOptions?.disabled) { + this.enabled = false; + this.logger.log(LogLevel.INFO, LOG_MESSAGES.ODP_DISABLED); + return; + } const browserClientEngine = JAVASCRIPT_CLIENT_ENGINE; const browserClientVersion = BROWSER_CLIENT_VERSION; @@ -59,38 +72,57 @@ export class BrowserOdpManager extends OdpManager { customSegmentRequestHandler = odpOptions.segmentsRequestHandler; } else { customSegmentRequestHandler = new BrowserRequestHandler( - browserLogger, + this.logger, odpOptions?.segmentsApiTimeout || REQUEST_TIMEOUT_ODP_SEGMENTS_MS ); } + // Set up Segment Manager (Audience Segments GraphQL API Interface) + if (odpOptions?.segmentManager) { + this.segmentManager = odpOptions.segmentManager; + this.segmentManager.updateSettings(this.odpConfig); + } else { + this.segmentManager = new OdpSegmentManager( + this.odpConfig, + odpOptions?.segmentsCache || + new BrowserLRUCache({ + maxSize: odpOptions?.segmentsCacheSize, + timeout: odpOptions?.segmentsCacheTimeout, + }), + new OdpSegmentApiManager(customSegmentRequestHandler, this.logger) + ); + } + let customEventRequestHandler; if (odpOptions?.eventRequestHandler) { customEventRequestHandler = odpOptions.eventRequestHandler; } else { customEventRequestHandler = new BrowserRequestHandler( - browserLogger, + this.logger, odpOptions?.eventApiTimeout || REQUEST_TIMEOUT_ODP_EVENTS_MS ); } - super({ - segmentLRUCache: - odpOptions?.segmentsCache || - new BrowserLRUCache({ - maxSize: odpOptions?.segmentsCacheSize, - timeout: odpOptions?.segmentsCacheTimeout, - }), - segmentRequestHandler: customSegmentRequestHandler, - eventRequestHandler: customEventRequestHandler, - logger: browserLogger, - clientEngine: browserClientEngine, - clientVersion: browserClientVersion, - odpOptions, - }); + // Set up Events Manager (Events REST API Interface) + if (odpOptions?.eventManager) { + this.eventManager = odpOptions.eventManager; + this.eventManager.updateSettings(this.odpConfig); + } else { + this.eventManager = new BrowserOdpEventManager({ + odpConfig: this.odpConfig, + apiManager: new BrowserOdpEventApiManager(customEventRequestHandler, this.logger), + logger: this.logger, + clientEngine: browserClientEngine, + clientVersion: browserClientVersion, + flushInterval: odpOptions?.eventFlushInterval, + batchSize: odpOptions?.eventBatchSize, + queueSize: odpOptions?.eventQueueSize, + }); + } + + this.eventManager!.start(); - this.logger = browserLogger; this.initPromise = this.initializeVuid(BrowserOdpManager.cache).catch(e => { this.logger.log(this.enabled ? LogLevel.ERROR : LogLevel.DEBUG, e); }); @@ -160,4 +192,12 @@ export class BrowserOdpManager extends OdpManager { super.sendEvent({ type, action, identifiers: identifiersWithVuid, data }); } + + public isVuidEnabled(): boolean { + return true; + } + + public getVuid(): string | undefined { + return this.vuid; + } } diff --git a/packages/optimizely-sdk/lib/plugins/odp_manager/index.node.ts b/packages/optimizely-sdk/lib/plugins/odp_manager/index.node.ts index 9b6e1a657..4fe3a9260 100644 --- a/packages/optimizely-sdk/lib/plugins/odp_manager/index.node.ts +++ b/packages/optimizely-sdk/lib/plugins/odp_manager/index.node.ts @@ -18,13 +18,23 @@ import { NodeRequestHandler } from '../../utils/http_request_handler/node_reques import { ServerLRUCache } from './../../utils/lru_cache/server_lru_cache'; +import { getLogger, LogHandler, LogLevel } from '../../modules/logging'; +import { + LOG_MESSAGES, + NODE_CLIENT_ENGINE, + NODE_CLIENT_VERSION, + REQUEST_TIMEOUT_ODP_EVENTS_MS, + REQUEST_TIMEOUT_ODP_SEGMENTS_MS, +} from '../../utils/enums'; + import { OdpManager } from '../../core/odp/odp_manager'; -import { getLogger, LogHandler } from '../../modules/logging'; -import { NODE_CLIENT_ENGINE, NODE_CLIENT_VERSION } from '../../utils/enums'; import { OdpOptions } from '../../../lib/shared_types'; +import { NodeOdpEventApiManager } from '../odp/event_api_manager/index.node'; +import { NodeOdpEventManager } from '../odp/event_manager/index.node'; +import { OdpSegmentManager } from '../../core/odp/odp_segment_manager'; +import { OdpSegmentApiManager } from '../../core/odp/odp_segment_api_manager'; interface NodeOdpManagerConfig { - disable: boolean; logger?: LogHandler; odpOptions?: OdpOptions; } @@ -35,25 +45,82 @@ interface NodeOdpManagerConfig { */ export class NodeOdpManager extends OdpManager { constructor({ logger, odpOptions }: NodeOdpManagerConfig) { - const nodeLogger = logger || getLogger(); + super(); + + this.logger = logger || getLogger(); + + if (odpOptions?.disabled) { + this.enabled = false; + this.logger.log(LogLevel.INFO, LOG_MESSAGES.ODP_DISABLED); + return; + } - const nodeRequestHandler = new NodeRequestHandler(nodeLogger); const nodeClientEngine = NODE_CLIENT_ENGINE; const nodeClientVersion = NODE_CLIENT_VERSION; - super({ - segmentLRUCache: + let customSegmentRequestHandler; + + if (odpOptions?.segmentsRequestHandler) { + customSegmentRequestHandler = odpOptions.segmentsRequestHandler; + } else { + customSegmentRequestHandler = new NodeRequestHandler( + this.logger, + odpOptions?.segmentsApiTimeout || REQUEST_TIMEOUT_ODP_SEGMENTS_MS + ); + } + + // Set up Segment Manager (Audience Segments GraphQL API Interface) + if (odpOptions?.segmentManager) { + this.segmentManager = odpOptions.segmentManager; + this.segmentManager.updateSettings(this.odpConfig); + } else { + this.segmentManager = new OdpSegmentManager( + this.odpConfig, odpOptions?.segmentsCache || - new ServerLRUCache({ - maxSize: odpOptions?.segmentsCacheSize, - timeout: odpOptions?.segmentsCacheTimeout, - }), - segmentRequestHandler: nodeRequestHandler, - eventRequestHandler: nodeRequestHandler, - logger: nodeLogger, - clientEngine: nodeClientEngine, - clientVersion: nodeClientVersion, - odpOptions, - }); + new ServerLRUCache({ + maxSize: odpOptions?.segmentsCacheSize, + timeout: odpOptions?.segmentsCacheTimeout, + }), + new OdpSegmentApiManager(customSegmentRequestHandler, this.logger) + ); + } + + let customEventRequestHandler; + + if (odpOptions?.eventRequestHandler) { + customEventRequestHandler = odpOptions.eventRequestHandler; + } else { + customEventRequestHandler = new NodeRequestHandler( + this.logger, + odpOptions?.eventApiTimeout || REQUEST_TIMEOUT_ODP_EVENTS_MS + ); + } + + // Set up Events Manager (Events REST API Interface) + if (odpOptions?.eventManager) { + this.eventManager = odpOptions.eventManager; + this.eventManager.updateSettings(this.odpConfig); + } else { + this.eventManager = new NodeOdpEventManager({ + odpConfig: this.odpConfig, + apiManager: new NodeOdpEventApiManager(customEventRequestHandler, this.logger), + logger: this.logger, + clientEngine: nodeClientEngine, + clientVersion: nodeClientVersion, + flushInterval: odpOptions?.eventFlushInterval, + batchSize: odpOptions?.eventBatchSize, + queueSize: odpOptions?.eventQueueSize, + }); + } + + this.eventManager!.start(); + } + + public isVuidEnabled(): boolean { + return false; + } + + public getVuid(): string | undefined { + return undefined; } } diff --git a/packages/optimizely-sdk/tests/odpEventApiManager.spec.ts b/packages/optimizely-sdk/tests/odpEventApiManager.spec.ts index 49492a8c6..b9dabfd4b 100644 --- a/packages/optimizely-sdk/tests/odpEventApiManager.spec.ts +++ b/packages/optimizely-sdk/tests/odpEventApiManager.spec.ts @@ -1,5 +1,5 @@ /** - * Copyright 2022, Optimizely + * Copyright 2022-2023, Optimizely * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ import { anyString, anything, instance, mock, resetCalls, verify, when } from 'ts-mockito'; import { LogHandler, LogLevel } from '../lib/modules/logging'; -import { OdpEventApiManager } from '../lib/core/odp/odp_event_api_manager'; +import { NodeOdpEventApiManager } from '../lib/plugins/odp/event_api_manager/index.node'; import { OdpEvent } from '../lib/core/odp/odp_event'; import { RequestHandler } from '../lib/utils/http_request_handler/http'; @@ -36,7 +36,7 @@ const ODP_EVENTS = [ new OdpEvent('t2', 'a2', new Map([['id-key-2', 'id-value-2']]), data2), ]; -describe('OdpEventApiManager', () => { +describe('NodeOdpEventApiManager', () => { let mockLogger: LogHandler; let mockRequestHandler: RequestHandler; @@ -50,7 +50,7 @@ describe('OdpEventApiManager', () => { resetCalls(mockRequestHandler); }); - const managerInstance = () => new OdpEventApiManager(instance(mockRequestHandler), instance(mockLogger)); + const managerInstance = () => new NodeOdpEventApiManager(instance(mockRequestHandler), instance(mockLogger)); const abortableRequest = (statusCode: number, body: string) => { return { abort: () => {}, diff --git a/packages/optimizely-sdk/tests/odpEventManager.spec.ts b/packages/optimizely-sdk/tests/odpEventManager.spec.ts index 944dc156e..7321cfc47 100644 --- a/packages/optimizely-sdk/tests/odpEventManager.spec.ts +++ b/packages/optimizely-sdk/tests/odpEventManager.spec.ts @@ -17,9 +17,10 @@ import { ODP_EVENT_ACTION, ODP_DEFAULT_EVENT_TYPE } from './../lib/utils/enums/index'; import { OdpConfig } from '../lib/core/odp/odp_config'; -import { OdpEventManager, STATE } from '../lib/core/odp/odp_event_manager'; +import { STATE } from '../lib/core/odp/odp_event_manager'; +import { NodeOdpEventManager as OdpEventManager } from '../lib/plugins/odp/event_manager/index.node'; import { anything, capture, instance, mock, resetCalls, spy, verify, when } from 'ts-mockito'; -import { OdpEventApiManager } from '../lib/core/odp/odp_event_api_manager'; +import { NodeOdpEventApiManager as OdpEventApiManager } from '../lib/plugins/odp/event_api_manager/index.node'; import { LogHandler, LogLevel } from '../lib/modules/logging'; import { OdpEvent } from '../lib/core/odp/odp_event'; import { RequestHandler } from '../lib/utils/http_request_handler/http'; diff --git a/packages/optimizely-sdk/tests/odpManager.browser.spec.ts b/packages/optimizely-sdk/tests/odpManager.browser.spec.ts index 17514d890..fc0e09d42 100644 --- a/packages/optimizely-sdk/tests/odpManager.browser.spec.ts +++ b/packages/optimizely-sdk/tests/odpManager.browser.spec.ts @@ -26,8 +26,8 @@ import { BrowserLRUCache } from './../lib/utils/lru_cache/browser_lru_cache'; import { BrowserOdpManager } from './../lib/plugins/odp_manager/index.browser'; import { OdpOptions } from './../lib/shared_types'; import { OdpConfig } from '../lib/core/odp/odp_config'; -import { OdpEventApiManager } from '../lib/core/odp/odp_event_api_manager'; -import { OdpEventManager, STATE } from '../lib/core/odp/odp_event_manager'; +import { BrowserOdpEventApiManager } from '../lib/plugins/odp/event_api_manager/index.browser'; +import { BrowserOdpEventManager } from '../lib/plugins/odp/event_manager/index.browser'; import { OdpSegmentManager } from './../lib/core/odp/odp_segment_manager'; import { OdpSegmentApiManager } from '../lib/core/odp/odp_segment_api_manager'; import { VuidManager } from '../lib/plugins/vuid_manager'; @@ -57,11 +57,11 @@ describe('OdpManager', () => { let mockRequestHandler: RequestHandler; let fakeRequestHandler: RequestHandler; - let mockEventApiManager: OdpEventApiManager; - let fakeEventApiManager: OdpEventApiManager; + let mockEventApiManager: BrowserOdpEventApiManager; + let fakeEventApiManager: BrowserOdpEventApiManager; - let mockEventManager: OdpEventManager; - let fakeEventManager: OdpEventManager; + let mockEventManager: BrowserOdpEventManager; + let fakeEventManager: BrowserOdpEventManager; let mockSegmentApiManager: OdpSegmentApiManager; let fakeSegmentApiManager: OdpSegmentApiManager; @@ -80,8 +80,8 @@ describe('OdpManager', () => { fakeLogger = instance(mockLogger); fakeRequestHandler = instance(mockRequestHandler); - mockEventApiManager = mock(); - mockEventManager = mock(); + mockEventApiManager = mock(); + mockEventManager = mock(); mockSegmentApiManager = mock(); mockSegmentManager = mock(); mockBrowserOdpManager = mock(); @@ -261,8 +261,9 @@ describe('OdpManager', () => { browserOdpManager.vuid = undefined; const invalidOdpEvent = new OdpEvent(ODP_DEFAULT_EVENT_TYPE, ODP_EVENT_ACTION.INITIALIZED, undefined); - await expect(browserOdpManager.sendEvent(invalidOdpEvent)) - .rejects.toThrow(ERROR_MESSAGES.ODP_SEND_EVENT_FAILED_VUID_MISSING) + await expect(browserOdpManager.sendEvent(invalidOdpEvent)).rejects.toThrow( + ERROR_MESSAGES.ODP_SEND_EVENT_FAILED_VUID_MISSING + ); }); describe('Populates BrowserOdpManager correctly with all odpOptions', () => { @@ -439,8 +440,7 @@ describe('OdpManager', () => { }); it('Browser default Events API Request Handler timeout should be used when odpOptions does not include eventsApiTimeout', () => { - const odpOptions: OdpOptions = { - }; + const odpOptions: OdpOptions = {}; const browserOdpManager = new BrowserOdpManager({ odpOptions, @@ -450,7 +450,7 @@ describe('OdpManager', () => { expect(browserOdpManager.eventManager.apiManager.requestHandler.timeout).toBe(10000); }); - it('Custom odpOptions.eventFlushInterval overrides default Event Manager flush interval', () => { + it('Custom odpOptions.eventFlushInterval cannot override the default Event Manager flush interval', () => { const odpOptions: OdpOptions = { eventFlushInterval: 4000, }; @@ -460,7 +460,7 @@ describe('OdpManager', () => { }); // @ts-ignore - expect(browserOdpManager.eventManager.flushInterval).toBe(4000); + expect(browserOdpManager.eventManager.flushInterval).toBe(0); // Note: Browser flush interval is always 0 due to use of Pixel API }); it('Default ODP event flush interval is used when odpOptions does not include eventFlushInterval', () => { @@ -471,7 +471,7 @@ describe('OdpManager', () => { }); // @ts-ignore - expect(browserOdpManager.eventManager.flushInterval).toBe(1000); + expect(browserOdpManager.eventManager.flushInterval).toBe(0); }); it('ODP event batch size set to one when odpOptions.eventFlushInterval set to 0', () => { @@ -490,7 +490,7 @@ describe('OdpManager', () => { expect(browserOdpManager.eventManager.batchSize).toBe(1); }); - it('Custom odpOptions.eventBatchSize overrides default Event Manager batch size', () => { + it('Custom odpOptions.eventBatchSize does not override default Event Manager batch size', () => { const odpOptions: OdpOptions = { eventBatchSize: 2, }; @@ -500,7 +500,7 @@ describe('OdpManager', () => { }); // @ts-ignore - expect(browserOdpManager.eventManager.batchSize).toBe(2); + expect(browserOdpManager.eventManager.batchSize).toBe(1); // Note: Browser event batch size is always 1 due to use of Pixel API }); it('Custom odpOptions.eventQueueSize overrides default Event Manager queue size', () => { @@ -550,7 +550,7 @@ describe('OdpManager', () => { const fakeClientEngine = 'test-javascript-sdk'; const fakeClientVersion = '1.2.3'; - const customEventManager = new OdpEventManager({ + const customEventManager = new BrowserOdpEventManager({ odpConfig, apiManager: fakeEventApiManager, logger: fakeLogger, @@ -580,9 +580,9 @@ describe('OdpManager', () => { const fakeClientEngine = 'test-javascript-sdk'; const fakeClientVersion = '1.2.3'; - const customEventManager = new OdpEventManager({ + const customEventManager = new BrowserOdpEventManager({ odpConfig, - apiManager: new OdpEventApiManager(new BrowserRequestHandler(fakeLogger, 1), fakeLogger), + apiManager: new BrowserOdpEventApiManager(new BrowserRequestHandler(fakeLogger, 1), fakeLogger), logger: fakeLogger, clientEngine: fakeClientEngine, clientVersion: fakeClientVersion, @@ -617,7 +617,7 @@ describe('OdpManager', () => { expect(browserOdpManager.eventManager.batchSize).toBe(1); // @ts-ignore - expect(browserOdpManager.eventManager.flushInterval).toBe(1); + expect(browserOdpManager.eventManager.flushInterval).toBe(0); // Note: Browser event flush interval will always be 0 due to use of Pixel API // @ts-ignore expect(browserOdpManager.eventManager.queueSize).toBe(1); @@ -652,10 +652,10 @@ describe('OdpManager', () => { expect(browserOdpManager.segmentManager.odpSegmentApiManager.requestHandler.timeout).toBe(4); // @ts-ignore - expect(browserOdpManager.eventManager.batchSize).toBe(4); + expect(browserOdpManager.eventManager.batchSize).toBe(1); // Note: Browser batch size will always be 1 due to use of Pixel API // @ts-ignore - expect(browserOdpManager.eventManager.flushInterval).toBe(4); + expect(browserOdpManager.eventManager.flushInterval).toBe(0); // Note: Browser event flush interval will always be 0 due to use of Pixel API // @ts-ignore expect(browserOdpManager.eventManager.queueSize).toBe(4); diff --git a/packages/optimizely-sdk/tests/odpManager.spec.ts b/packages/optimizely-sdk/tests/odpManager.spec.ts index 9fd12605d..87009135e 100644 --- a/packages/optimizely-sdk/tests/odpManager.spec.ts +++ b/packages/optimizely-sdk/tests/odpManager.spec.ts @@ -1,4 +1,3 @@ -import { LRUCache } from './../lib/utils/lru_cache/lru_cache'; /** * Copyright 2023, Optimizely * @@ -25,10 +24,10 @@ import { LogHandler, LogLevel } from '../lib/modules/logging'; import { RequestHandler } from '../lib/utils/http_request_handler/http'; import { BrowserLRUCache } from './../lib/utils/lru_cache/browser_lru_cache'; -import { OdpManager } from './../lib/core/odp/odp_manager'; +import { NodeOdpManager as OdpManager } from './../lib/plugins/odp_manager/index.node'; import { OdpConfig } from '../lib/core/odp/odp_config'; -import { OdpEventApiManager } from '../lib/core/odp/odp_event_api_manager'; -import { OdpEventManager } from '../lib/core/odp/odp_event_manager'; +import { NodeOdpEventApiManager as OdpEventApiManager } from '../lib/plugins/odp/event_api_manager/index.node'; +import { NodeOdpEventManager as OdpEventManager } from '../lib/plugins/odp/event_manager/index.node'; import { OdpSegmentManager } from './../lib/core/odp/odp_segment_manager'; import { OdpSegmentApiManager } from '../lib/core/odp/odp_segment_api_manager'; import { ServerLRUCache } from '../lib/utils/lru_cache'; @@ -90,23 +89,21 @@ describe('OdpManager', () => { const odpManagerInstance = (config?: OdpConfig) => new OdpManager({ - segmentLRUCache: new ServerLRUCache(), - segmentRequestHandler: defaultRequestHandler, - eventRequestHandler: defaultRequestHandler, odpOptions: { eventManager, segmentManager, + segmentsRequestHandler: defaultRequestHandler, + eventRequestHandler: defaultRequestHandler, }, }); it('should drop relevant calls when OdpManager is initialized with the disabled flag', async () => { const odpManager = new OdpManager({ - segmentLRUCache: new ServerLRUCache(), - segmentRequestHandler: defaultRequestHandler, - eventRequestHandler: defaultRequestHandler, logger, odpOptions: { disabled: true, + segmentsRequestHandler: defaultRequestHandler, + eventRequestHandler: defaultRequestHandler, }, }); verify(mockLogger.log(LogLevel.INFO, LOG_MESSAGES.ODP_DISABLED)).once(); @@ -140,10 +137,9 @@ describe('OdpManager', () => { it('should use new settings in event manager when ODP Config is updated', async () => { const odpManager = new OdpManager({ - segmentLRUCache: new ServerLRUCache(), - segmentRequestHandler: defaultRequestHandler, - eventRequestHandler: defaultRequestHandler, odpOptions: { + segmentsRequestHandler: defaultRequestHandler, + eventRequestHandler: defaultRequestHandler, eventManager: new OdpEventManager({ odpConfig, apiManager: eventApiManager, @@ -176,11 +172,10 @@ describe('OdpManager', () => { it('should use new settings in segment manager when ODP Config is updated', async () => { const odpManager = new OdpManager({ - segmentLRUCache: new ServerLRUCache(), - segmentRequestHandler: defaultRequestHandler, - eventRequestHandler: defaultRequestHandler, odpOptions: { segmentManager: new OdpSegmentManager(odpConfig, new BrowserLRUCache(), segmentApiManager), + segmentsRequestHandler: defaultRequestHandler, + eventRequestHandler: defaultRequestHandler, }, }); @@ -205,10 +200,11 @@ describe('OdpManager', () => { expect(odpManagerA.eventManager).not.toBe(null); const odpManagerB = new OdpManager({ - segmentLRUCache: new ServerLRUCache(), - segmentRequestHandler: defaultRequestHandler, - eventRequestHandler: defaultRequestHandler, logger, + odpOptions: { + segmentsRequestHandler: defaultRequestHandler, + eventRequestHandler: defaultRequestHandler, + }, }); expect(odpManagerB.eventManager).not.toBe(null); }); @@ -218,9 +214,10 @@ describe('OdpManager', () => { expect(odpManagerA.segmentManager).not.toBe(null); const odpManagerB = new OdpManager({ - segmentLRUCache: new ServerLRUCache(), - segmentRequestHandler: defaultRequestHandler, - eventRequestHandler: defaultRequestHandler, + odpOptions: { + segmentsRequestHandler: defaultRequestHandler, + eventRequestHandler: defaultRequestHandler, + }, }); expect(odpManagerB.eventManager).not.toBe(null); });