Skip to content

Commit 2580ea0

Browse files
authored
fix(ats): [FSSDK-9023] Fix ODP Exports (#812)
Consolidated & patched external-facing interfaces for FSC testing. Additionally, also refactored internal implementations of Browser/Node-specific implementations to avoid using unnecessary implicit contextual checks.
1 parent 8991d6e commit 2580ea0

25 files changed

+6130
-33900
lines changed

packages/optimizely-sdk/lib/core/odp/odp_event_api_manager.ts

Lines changed: 14 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,7 @@
1616

1717
import { LogHandler, LogLevel } from '../../modules/logging';
1818
import { OdpEvent } from './odp_event';
19-
import { isBrowserContext } from './odp_utils';
2019
import { RequestHandler } from '../../utils/http_request_handler/http';
21-
import { ODP_EVENT_BROWSER_ENDPOINT } from '../../utils/enums';
2220

2321
const EVENT_SENDING_FAILURE_MESSAGE = 'ODP event send failed';
2422

@@ -32,10 +30,9 @@ export interface IOdpEventApiManager {
3230
/**
3331
* Concrete implementation for accessing the ODP REST API
3432
*/
35-
export class OdpEventApiManager implements IOdpEventApiManager {
33+
export abstract class OdpEventApiManager implements IOdpEventApiManager {
3634
private readonly logger: LogHandler;
3735
private readonly requestHandler: RequestHandler;
38-
private readonly isBrowser: boolean;
3936

4037
/**
4138
* Creates instance to access Optimizely Data Platform (ODP) REST API
@@ -45,7 +42,10 @@ export class OdpEventApiManager implements IOdpEventApiManager {
4542
constructor(requestHandler: RequestHandler, logger: LogHandler) {
4643
this.requestHandler = requestHandler;
4744
this.logger = logger;
48-
this.isBrowser = isBrowserContext()
45+
}
46+
47+
public getLogger(): LogHandler {
48+
return this.logger;
4949
}
5050

5151
/**
@@ -68,39 +68,11 @@ export class OdpEventApiManager implements IOdpEventApiManager {
6868
return shouldRetry;
6969
}
7070

71-
if (events.length > 1 && this.isBrowser) {
72-
this.logger.log(LogLevel.ERROR, `${EVENT_SENDING_FAILURE_MESSAGE} (browser only supports batch size 1)`);
71+
if (!this.shouldSendEvents(events)) {
7372
return shouldRetry;
7473
}
7574

76-
let method, endpoint, headers, data;
77-
78-
if (this.isBrowser) {
79-
method = 'GET';
80-
const event = events[0];
81-
const url = new URL(ODP_EVENT_BROWSER_ENDPOINT);
82-
event.identifiers.forEach((v, k) =>{
83-
url.searchParams.append(k, v);
84-
});
85-
event.data.forEach((v, k) =>{
86-
url.searchParams.append(k, v as string);
87-
});
88-
url.searchParams.append('tracker_id', apiKey);
89-
url.searchParams.append('event_type', event.type);
90-
url.searchParams.append('vdl_action', event.action);
91-
endpoint = url.toString();
92-
headers = {};
93-
} else {
94-
method = 'POST';
95-
endpoint = `${apiHost}/v3/events`;
96-
headers = {
97-
'Content-Type': 'application/json',
98-
'x-api-key': apiKey,
99-
};
100-
data = JSON.stringify(events, this.replacer);
101-
}
102-
103-
75+
const { method, endpoint, headers, data } = this.generateRequestData(apiHost, apiKey, events);
10476

10577
let statusCode = 0;
10678
try {
@@ -127,11 +99,12 @@ export class OdpEventApiManager implements IOdpEventApiManager {
12799
return shouldRetry;
128100
}
129101

130-
private replacer(_: unknown, value: unknown) {
131-
if (value instanceof Map) {
132-
return Object.fromEntries(value);
133-
} else {
134-
return value;
135-
}
102+
protected abstract shouldSendEvents(events: OdpEvent[]): boolean;
103+
104+
protected abstract generateRequestData(apiHost: string, apiKey: string, events: OdpEvent[]): {
105+
method: string,
106+
endpoint: string,
107+
headers: {[key: string]: string},
108+
data: string,
136109
}
137110
}

packages/optimizely-sdk/lib/core/odp/odp_event_manager.ts

Lines changed: 24 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import { ERROR_MESSAGES, ODP_USER_KEY, ODP_DEFAULT_EVENT_TYPE, ODP_EVENT_ACTION
2222
import { OdpEvent } from './odp_event';
2323
import { OdpConfig } from './odp_config';
2424
import { OdpEventApiManager } from './odp_event_api_manager';
25-
import { invalidOdpDataFound, isBrowserContext } from './odp_utils';
25+
import { invalidOdpDataFound } from './odp_utils';
2626

2727
const MAX_RETRIES = 3;
2828
const DEFAULT_BATCH_SIZE = 10;
@@ -61,16 +61,16 @@ export interface IOdpEventManager {
6161
/**
6262
* Concrete implementation of a manager for persisting events to the Optimizely Data Platform
6363
*/
64-
export class OdpEventManager implements IOdpEventManager {
64+
export abstract class OdpEventManager implements IOdpEventManager {
6565
/**
6666
* Current state of the event processor
6767
*/
6868
public state: STATE = STATE.STOPPED;
6969
/**
7070
* Queue for holding all events to be eventually dispatched
71-
* @private
71+
* @protected
7272
*/
73-
private queue = new Array<OdpEvent>();
73+
protected queue = new Array<OdpEvent>();
7474
/**
7575
* Identifier of the currently running timeout so clearCurrentTimeout() can be called
7676
* @private
@@ -93,19 +93,19 @@ export class OdpEventManager implements IOdpEventManager {
9393
private readonly logger: LogHandler;
9494
/**
9595
* Maximum queue size
96-
* @private
96+
* @protected
9797
*/
98-
private readonly queueSize: number;
98+
protected queueSize!: number;
9999
/**
100100
* Maximum number of events to process at once. Ignored in browser context
101-
* @private
101+
* @protected
102102
*/
103-
private readonly batchSize: number;
103+
protected batchSize!: number;
104104
/**
105105
* Milliseconds between setTimeout() to process new batches. Ignored in browser context
106-
* @private
106+
* @protected
107107
*/
108-
private readonly flushInterval: number;
108+
protected flushInterval!: number;
109109
/**
110110
* Type of execution context eg node, js, react
111111
* @private
@@ -141,33 +141,16 @@ export class OdpEventManager implements IOdpEventManager {
141141
this.logger = logger;
142142
this.clientEngine = clientEngine;
143143
this.clientVersion = clientVersion;
144-
145-
const isBrowser = isBrowserContext();
146-
147-
const defaultQueueSize = isBrowser ? DEFAULT_BROWSER_QUEUE_SIZE : DEFAULT_SERVER_QUEUE_SIZE;
148-
this.queueSize = queueSize || defaultQueueSize;
149-
this.batchSize = batchSize || DEFAULT_BATCH_SIZE;
150-
151-
if (flushInterval === 0 || isBrowser) {
152-
// disable event batching
153-
this.batchSize = 1;
154-
this.flushInterval = 0;
155-
} else {
156-
this.flushInterval = flushInterval || DEFAULT_FLUSH_INTERVAL_MSECS;
157-
}
158-
159-
if (isBrowser) {
160-
if (typeof batchSize !== 'undefined' && batchSize !== 1) {
161-
this.logger.log(LogLevel.WARNING, 'ODP event batch size must be 1 in the browser.');
162-
}
163-
if (typeof flushInterval !== 'undefined' && flushInterval !== 0) {
164-
this.logger.log(LogLevel.WARNING, 'ODP event flush interval must be 0 in the browser.');
165-
}
166-
}
167-
144+
this.initParams(batchSize, queueSize, flushInterval);
168145
this.state = STATE.STOPPED;
169146
}
170147

148+
protected abstract initParams(
149+
batchSize: number | undefined,
150+
queueSize: number | undefined,
151+
flushInterval: number | undefined,
152+
): void;
153+
171154
/**
172155
* Update ODP configuration settings.
173156
* @param newConfig New configuration to apply
@@ -401,19 +384,12 @@ export class OdpEventManager implements IOdpEventManager {
401384
if (this.odpConfig.isReady()) {
402385
return true;
403386
}
404-
405-
if (!isBrowserContext()) {
406-
// if Node/server-side context, empty queue items before ready state
407-
this.logger.log(LogLevel.WARNING, 'ODPConfig not ready. Discarding events in queue.');
408-
this.queue = new Array<OdpEvent>();
409-
} else {
410-
// in Browser/client-side context, give debug message but leave events in queue
411-
this.logger.log(LogLevel.DEBUG, 'ODPConfig not ready. Leaving events in queue.');
412-
}
413-
387+
this.discardEventsIfNeeded();
414388
return false;
415389
}
416390

391+
protected abstract discardEventsIfNeeded(): void;
392+
417393
/**
418394
* Add additional common data including an idempotent ID and execution context to event data
419395
* @param sourceData Existing event data to augment
@@ -430,4 +406,8 @@ export class OdpEventManager implements IOdpEventManager {
430406
sourceData.forEach((value, key) => data.set(key, value));
431407
return data;
432408
}
409+
410+
protected getLogger(): LogHandler {
411+
return this.logger;
412+
}
433413
}

packages/optimizely-sdk/lib/core/odp/odp_manager.ts

Lines changed: 10 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -14,51 +14,26 @@
1414
* limitations under the License.
1515
*/
1616

17-
import { BROWSER_CLIENT_VERSION, LOG_MESSAGES } from './../../utils/enums/index';
17+
import { LOG_MESSAGES } from './../../utils/enums/index';
1818
import { getLogger, LogHandler, LogLevel } from '../../modules/logging';
1919
import { ERROR_MESSAGES, ODP_USER_KEY } from '../../utils/enums';
2020

21-
import { RequestHandler } from './../../utils/http_request_handler/http';
22-
23-
import { LRUCache } from './../../utils/lru_cache/lru_cache';
24-
2521
import { VuidManager } from '../../plugins/vuid_manager';
2622

2723
import { OdpConfig } from './odp_config';
2824
import { OdpEventManager } from './odp_event_manager';
2925
import { OdpSegmentManager } from './odp_segment_manager';
30-
import { OdpSegmentApiManager } from './odp_segment_api_manager';
31-
import { OdpEventApiManager } from './odp_event_api_manager';
3226
import { OptimizelySegmentOption } from './optimizely_segment_option';
3327
import { invalidOdpDataFound } from './odp_utils';
3428
import { OdpEvent } from './odp_event';
35-
import { OdpOptions } from '../../shared_types';
36-
37-
/**
38-
* @param {LRUCache<string, string>[]} segmentLRUCache Cache to be used for storing segments.
39-
* @param {RequestHandler} segmentRequestHandler HTTP request handler that will be used by the ODP Segment Manager.
40-
* @param {RequestHandler} eventRequestHandler HTTP request handler that will be used by the ODP Event Manager.
41-
* @param {LogHandler} logger (Optional) Accepts custom LogHandler. Defaults to the default global LogHandler.
42-
* @param {string} clientEngine (Optional) String denoting specific client engine being used. Defaults to 'javascript-sdk'.
43-
* @param {string} clientVersion (Optional) String denoting specific client version. Defaults to current version value from package.json.
44-
* @param {OdpOptions} odpOptions (Optional) Configuration settings for various ODP options from segment cache size to event flush interval.
45-
*/
46-
interface OdpManagerConfig {
47-
segmentLRUCache: LRUCache<string, string[]>;
48-
segmentRequestHandler: RequestHandler;
49-
eventRequestHandler: RequestHandler;
50-
logger?: LogHandler;
51-
clientEngine?: string;
52-
clientVersion?: string;
53-
odpOptions?: OdpOptions;
54-
}
5529

5630
/**
5731
* Orchestrates segments manager, event manager, and ODP configuration
5832
*/
59-
export class OdpManager {
60-
enabled: boolean;
61-
logger: LogHandler;
33+
export abstract class OdpManager {
34+
initPromise?: Promise<void>;
35+
enabled = true;
36+
logger: LogHandler = getLogger();
6237
odpConfig: OdpConfig = new OdpConfig();
6338

6439
/**
@@ -73,54 +48,7 @@ export class OdpManager {
7348
*/
7449
public eventManager: OdpEventManager | undefined;
7550

76-
constructor({
77-
segmentLRUCache,
78-
segmentRequestHandler,
79-
eventRequestHandler,
80-
logger,
81-
clientEngine,
82-
clientVersion,
83-
odpOptions,
84-
}: OdpManagerConfig) {
85-
this.enabled = !odpOptions?.disabled;
86-
this.logger = logger || getLogger();
87-
88-
if (!this.enabled) {
89-
this.logger.log(LogLevel.INFO, LOG_MESSAGES.ODP_DISABLED);
90-
return;
91-
}
92-
93-
// Set up Segment Manager (Audience Segments GraphQL API Interface)
94-
if (odpOptions?.segmentManager) {
95-
this.segmentManager = odpOptions.segmentManager;
96-
this.segmentManager.updateSettings(this.odpConfig);
97-
} else {
98-
this.segmentManager = new OdpSegmentManager(
99-
this.odpConfig,
100-
segmentLRUCache,
101-
new OdpSegmentApiManager(segmentRequestHandler, this.logger)
102-
);
103-
}
104-
105-
// Set up Events Manager (Events REST API Interface)
106-
if (odpOptions?.eventManager) {
107-
this.eventManager = odpOptions.eventManager;
108-
this.eventManager.updateSettings(this.odpConfig);
109-
} else {
110-
this.eventManager = new OdpEventManager({
111-
odpConfig: this.odpConfig,
112-
apiManager: new OdpEventApiManager(eventRequestHandler, this.logger),
113-
logger: this.logger,
114-
clientEngine: clientEngine || 'javascript-sdk',
115-
clientVersion: clientVersion || BROWSER_CLIENT_VERSION,
116-
flushInterval: odpOptions?.eventFlushInterval,
117-
batchSize: odpOptions?.eventBatchSize,
118-
queueSize: odpOptions?.eventQueueSize,
119-
});
120-
}
121-
122-
this.eventManager.start();
123-
}
51+
constructor() {}
12452

12553
/**
12654
* Provides a method to update ODP Manager's ODP Config API Key, API Host, and Audience Segments
@@ -246,4 +174,8 @@ export class OdpManager {
246174

247175
this.eventManager.sendEvent(new OdpEvent(type, action, identifiers, data));
248176
}
177+
178+
public abstract isVuidEnabled(): boolean;
179+
180+
public abstract getVuid(): string | undefined;
249181
}

packages/optimizely-sdk/lib/core/odp/odp_utils.ts

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,3 @@ export function invalidOdpDataFound(data: Map<string, any>): boolean {
3030
});
3131
return foundInvalidValue;
3232
}
33-
34-
/**
35-
* Determine if the runtime environment is a browser
36-
* @returns True if in the browser
37-
* @private
38-
*/
39-
export function isBrowserContext(): boolean {
40-
return !(typeof process !== "undefined" &&
41-
process.versions != null &&
42-
process.versions.node != null);
43-
}

packages/optimizely-sdk/lib/export_types.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16+
17+
/**
18+
* This file contains a collection of all types to be externally exported.
19+
*/
20+
1621
export {
1722
UserAttributes,
1823
OptimizelyConfig,
@@ -38,4 +43,5 @@ export {
3843
ActivateListenerPayload,
3944
TrackListenerPayload,
4045
NotificationCenter,
46+
OptimizelySegmentOption,
4147
} from './shared_types';

0 commit comments

Comments
 (0)