Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 6 additions & 10 deletions packages/optimizely-sdk/lib/core/odp/odp_event_manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,10 @@ 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 { IOdpEventApiManager } from './odp_event_api_manager';
import { invalidOdpDataFound } from './odp_utils';

const MAX_RETRIES = 3;
const DEFAULT_BATCH_SIZE = 10;
const DEFAULT_FLUSH_INTERVAL_MSECS = 1000;
const DEFAULT_BROWSER_QUEUE_SIZE = 100;
const DEFAULT_SERVER_QUEUE_SIZE = 10000;

/**
* Event dispatcher's execution states
Expand All @@ -51,7 +47,7 @@ export interface IOdpEventManager {

registerVuid(vuid: string): void;

identifyUser(userId: string, vuid?: string): void;
identifyUser(userId?: string, vuid?: string): void;

sendEvent(event: OdpEvent): void;

Expand Down Expand Up @@ -85,7 +81,7 @@ export abstract class OdpEventManager implements IOdpEventManager {
* REST API Manager used to send the events
* @private
*/
private readonly apiManager: OdpEventApiManager;
private readonly apiManager: IOdpEventApiManager;
/**
* Handler for recording execution logs
* @private
Expand All @@ -100,7 +96,7 @@ export abstract class OdpEventManager implements IOdpEventManager {
* Maximum number of events to process at once. Ignored in browser context
* @protected
*/
protected batchSize!: number;
protected batchSize!: number;
/**
* Milliseconds between setTimeout() to process new batches. Ignored in browser context
* @protected
Expand Down Expand Up @@ -128,7 +124,7 @@ export abstract class OdpEventManager implements IOdpEventManager {
flushInterval,
}: {
odpConfig: OdpConfig;
apiManager: OdpEventApiManager;
apiManager: IOdpEventApiManager;
logger: LogHandler;
clientEngine: string;
clientVersion: string;
Expand All @@ -148,7 +144,7 @@ export abstract class OdpEventManager implements IOdpEventManager {
protected abstract initParams(
batchSize: number | undefined,
queueSize: number | undefined,
flushInterval: number | undefined,
flushInterval: number | undefined
): void;

/**
Expand Down
52 changes: 36 additions & 16 deletions packages/optimizely-sdk/lib/core/odp/odp_manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,29 @@ import { ERROR_MESSAGES, ODP_USER_KEY } from '../../utils/enums';
import { VuidManager } from '../../plugins/vuid_manager';

import { OdpConfig } from './odp_config';
import { OdpEventManager } from './odp_event_manager';
import { OdpSegmentManager } from './odp_segment_manager';
import { IOdpEventManager } from './odp_event_manager';
import { IOdpSegmentManager } from './odp_segment_manager';
import { OptimizelySegmentOption } from './optimizely_segment_option';
import { invalidOdpDataFound } from './odp_utils';
import { OdpEvent } from './odp_event';

export interface IOdpManager {
enabled: boolean;
segmentManager: IOdpSegmentManager | undefined;
eventManager: IOdpEventManager | undefined;
updateSettings({ apiKey, apiHost, segmentsToCheck }: OdpConfig): boolean;
close(): void;
fetchQualifiedSegments(userId: string, options?: Array<OptimizelySegmentOption>): Promise<string[] | null>;
identifyUser(userId?: string, vuid?: string): void;
sendEvent({ type, action, identifiers, data }: OdpEvent): void;
isVuidEnabled(): boolean;
getVuid(): string | undefined;
}

/**
* Orchestrates segments manager, event manager, and ODP configuration
*/
export abstract class OdpManager {
export abstract class OdpManager implements IOdpManager {
initPromise?: Promise<void>;
enabled = true;
logger: LogHandler = getLogger();
Expand All @@ -40,20 +53,20 @@ export abstract class OdpManager {
* ODP Segment Manager which provides an interface to the remote ODP server (GraphQL API) for audience segments mapping.
* It fetches all qualified segments for the given user context and manages the segments cache for all user contexts.
*/
public segmentManager: OdpSegmentManager | undefined;
segmentManager: IOdpSegmentManager | undefined;

/**
* ODP Event Manager which provides an interface to the remote ODP server (REST API) for events.
* It will queue all pending events (persistent) and send them (in batches of up to 10 events) to the ODP server when possible.
*/
public eventManager: OdpEventManager | undefined;
eventManager: IOdpEventManager | undefined;

constructor() {}

/**
* Provides a method to update ODP Manager's ODP Config API Key, API Host, and Audience Segments
*/
public updateSettings({ apiKey, apiHost, segmentsToCheck }: OdpConfig): boolean {
updateSettings({ apiKey, apiHost, segmentsToCheck }: OdpConfig): boolean {
if (!this.enabled) {
return false;
}
Expand Down Expand Up @@ -85,7 +98,7 @@ export abstract class OdpManager {
/**
* Attempts to stop the current instance of ODP Manager's event manager, if it exists and is running.
*/
public close(): void {
close(): void {
if (!this.enabled) {
return;
}
Expand All @@ -100,10 +113,7 @@ export abstract class OdpManager {
* @param {Array<OptimizelySegmentOption>} options - An array of OptimizelySegmentOption used to ignore and/or reset the cache.
* @returns {Promise<string[] | null>} A promise holding either a list of qualified segments or null.
*/
public async fetchQualifiedSegments(
userId: string,
options: Array<OptimizelySegmentOption> = []
): Promise<string[] | null> {
async fetchQualifiedSegments(userId: string, options: Array<OptimizelySegmentOption> = []): Promise<string[] | null> {
if (!this.enabled) {
this.logger.log(LogLevel.ERROR, ERROR_MESSAGES.ODP_NOT_ENABLED);
return null;
Expand All @@ -127,7 +137,7 @@ export abstract class OdpManager {
* @param {string} vuid (Optional) Secondary unique identifier of a target user, primarily used by client SDKs.
* @returns
*/
public identifyUser(userId?: string, vuid?: string): void {
identifyUser(userId?: string, vuid?: string): void {
if (!this.enabled) {
this.logger.log(LogLevel.DEBUG, LOG_MESSAGES.ODP_IDENTIFY_FAILED_ODP_DISABLED);
return;
Expand Down Expand Up @@ -155,7 +165,13 @@ export abstract class OdpManager {
* Sends an event to the ODP Server via the ODP Events API
* @param {OdpEvent} > ODP Event to send to event manager
*/
public sendEvent({ type, action, identifiers, data }: OdpEvent): void {
sendEvent({ type, action, identifiers, data }: OdpEvent): void {
let mType = type;

if (typeof mType !== 'string' || mType === '') {
mType = 'fullstack';
}

if (!this.enabled) {
throw new Error(ERROR_MESSAGES.ODP_NOT_ENABLED);
}
Expand All @@ -172,10 +188,14 @@ export abstract class OdpManager {
throw new Error(ERROR_MESSAGES.ODP_SEND_EVENT_FAILED_EVENT_MANAGER_MISSING);
}

this.eventManager.sendEvent(new OdpEvent(type, action, identifiers, data));
if (typeof action !== 'string' || action === '') {
throw new Error('ODP action is not valid (cannot be empty).');
}

this.eventManager.sendEvent(new OdpEvent(mType, action, identifiers, data));
}

public abstract isVuidEnabled(): boolean;
abstract isVuidEnabled(): boolean;

public abstract getVuid(): string | undefined;
abstract getVuid(): string | undefined;
}
36 changes: 26 additions & 10 deletions packages/optimizely-sdk/lib/core/odp/odp_segment_manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,26 @@

import { getLogger, LogHandler, LogLevel } from '../../modules/logging';
import { ERROR_MESSAGES, ODP_USER_KEY } from '../../utils/enums';
import { LRUCache } from '../../utils/lru_cache';
import { OdpSegmentApiManager } from './odp_segment_api_manager';
import { ICache } from '../../utils/lru_cache';
import { IOdpSegmentApiManager } from './odp_segment_api_manager';
import { OdpConfig } from './odp_config';
import { OptimizelySegmentOption } from './optimizely_segment_option';

export interface IOdpSegmentManager {
fetchQualifiedSegments(
userKey: ODP_USER_KEY,
userValue: string,
options: Array<OptimizelySegmentOption>
): Promise<string[] | null>;
reset(): void;
makeCacheKey(userKey: string, userValue: string): string;
updateSettings(config: OdpConfig): void;
}

/**
* Schedules connections to ODP for audience segmentation and caches the results.
*/
export class OdpSegmentManager {
export class OdpSegmentManager implements IOdpSegmentManager {
/**
* ODP configuration settings in used
* @private
Expand All @@ -35,21 +46,21 @@ export class OdpSegmentManager {
* Holds cached audience segments
* @private
*/
private _segmentsCache: LRUCache<string, string[]>;
private _segmentsCache: ICache<string, string[]>;

/**
* Getter for private segments cache
* @public
*/
public get segmentsCache(): LRUCache<string, string[]> {
public get segmentsCache(): ICache<string, string[]> {
return this._segmentsCache;
}

/**
* GraphQL API Manager used to fetch segments
* @private
*/
private odpSegmentApiManager: OdpSegmentApiManager;
private odpSegmentApiManager: IOdpSegmentApiManager;

/**
* Handler for recording execution logs
Expand All @@ -59,8 +70,8 @@ export class OdpSegmentManager {

constructor(
odpConfig: OdpConfig,
segmentsCache: LRUCache<string, string[]>,
odpSegmentApiManager: OdpSegmentApiManager,
segmentsCache: ICache<string, string[]>,
odpSegmentApiManager: IOdpSegmentApiManager,
logger?: LogHandler
) {
this.odpConfig = odpConfig;
Expand Down Expand Up @@ -100,7 +111,9 @@ export class OdpSegmentManager {
const ignoreCache = options.includes(OptimizelySegmentOption.IGNORE_CACHE);
const resetCache = options.includes(OptimizelySegmentOption.RESET_CACHE);

if (resetCache) this.reset();
if (resetCache) {
this.reset();
}

if (!ignoreCache && !resetCache) {
const cachedSegments = this._segmentsCache.lookup(cacheKey);
Expand All @@ -121,7 +134,9 @@ export class OdpSegmentManager {
segmentsToCheck
);

if (segments && !ignoreCache) this._segmentsCache.save({ key: cacheKey, value: segments });
if (segments && !ignoreCache) {
this._segmentsCache.save({ key: cacheKey, value: segments });
}

return segments;
}
Expand Down Expand Up @@ -149,5 +164,6 @@ export class OdpSegmentManager {
*/
public updateSettings(config: OdpConfig): void {
this.odpConfig = config;
this._segmentsCache.reset();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@

// Options for defining behavior of OdpSegmentManager's caching mechanism when calling fetchSegments()
export enum OptimizelySegmentOption {
IGNORE_CACHE,
RESET_CACHE,
IGNORE_CACHE = 'IGNORE_CACHE',
RESET_CACHE = 'RESET_CACHE',
}
Loading