Skip to content

Commit 8991d6e

Browse files
[FSSDK-8993] feat: switch odp event rest endpoint (#808)
* add support for odp browser event api * update browser odp event unit tests
1 parent 70bdda4 commit 8991d6e

File tree

8 files changed

+243
-112
lines changed

8 files changed

+243
-112
lines changed

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

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Copyright 2022, Optimizely
2+
* Copyright 2022-2023, Optimizely
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,7 +16,9 @@
1616

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

2123
const EVENT_SENDING_FAILURE_MESSAGE = 'ODP event send failed';
2224

@@ -33,6 +35,7 @@ export interface IOdpEventApiManager {
3335
export class OdpEventApiManager implements IOdpEventApiManager {
3436
private readonly logger: LogHandler;
3537
private readonly requestHandler: RequestHandler;
38+
private readonly isBrowser: boolean;
3639

3740
/**
3841
* Creates instance to access Optimizely Data Platform (ODP) REST API
@@ -42,6 +45,7 @@ export class OdpEventApiManager implements IOdpEventApiManager {
4245
constructor(requestHandler: RequestHandler, logger: LogHandler) {
4346
this.requestHandler = requestHandler;
4447
this.logger = logger;
48+
this.isBrowser = isBrowserContext()
4549
}
4650

4751
/**
@@ -64,14 +68,39 @@ export class OdpEventApiManager implements IOdpEventApiManager {
6468
return shouldRetry;
6569
}
6670

67-
const endpoint = `${apiHost}/v3/events`;
68-
const data = JSON.stringify(events, this.replacer);
71+
if (events.length > 1 && this.isBrowser) {
72+
this.logger.log(LogLevel.ERROR, `${EVENT_SENDING_FAILURE_MESSAGE} (browser only supports batch size 1)`);
73+
return shouldRetry;
74+
}
75+
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+
69103

70-
const method = 'POST';
71-
const headers = {
72-
'Content-Type': 'application/json',
73-
'x-api-key': apiKey,
74-
};
75104

76105
let statusCode = 0;
77106
try {

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

Lines changed: 24 additions & 25 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 } from './odp_utils';
25+
import { invalidOdpDataFound, isBrowserContext } from './odp_utils';
2626

2727
const MAX_RETRIES = 3;
2828
const DEFAULT_BATCH_SIZE = 10;
@@ -54,6 +54,8 @@ export interface IOdpEventManager {
5454
identifyUser(userId: string, vuid?: string): void;
5555

5656
sendEvent(event: OdpEvent): void;
57+
58+
flush(): void;
5759
}
5860

5961
/**
@@ -95,12 +97,12 @@ export class OdpEventManager implements IOdpEventManager {
9597
*/
9698
private readonly queueSize: number;
9799
/**
98-
* Maximum number of events to process at once
100+
* Maximum number of events to process at once. Ignored in browser context
99101
* @private
100102
*/
101103
private readonly batchSize: number;
102104
/**
103-
* Milliseconds between setTimeout() to process new batches
105+
* Milliseconds between setTimeout() to process new batches. Ignored in browser context
104106
* @private
105107
*/
106108
private readonly flushInterval: number;
@@ -140,27 +142,29 @@ export class OdpEventManager implements IOdpEventManager {
140142
this.clientEngine = clientEngine;
141143
this.clientVersion = clientVersion;
142144

143-
let defaultQueueSize = DEFAULT_BROWSER_QUEUE_SIZE;
144-
145-
try {
146-
// TODO: Consider refactoring to use typeof process and combine w/above line
147-
if (process) {
148-
defaultQueueSize = DEFAULT_SERVER_QUEUE_SIZE;
149-
}
150-
} catch (e) {
151-
// TODO: Create Browser and Non-Browser specific variants of ODP Event Manager to avoid this try/catch
152-
}
145+
const isBrowser = isBrowserContext();
153146

147+
const defaultQueueSize = isBrowser ? DEFAULT_BROWSER_QUEUE_SIZE : DEFAULT_SERVER_QUEUE_SIZE;
154148
this.queueSize = queueSize || defaultQueueSize;
155149
this.batchSize = batchSize || DEFAULT_BATCH_SIZE;
156-
if (flushInterval === 0) {
150+
151+
if (flushInterval === 0 || isBrowser) {
157152
// disable event batching
158153
this.batchSize = 1;
159154
this.flushInterval = 0;
160155
} else {
161156
this.flushInterval = flushInterval || DEFAULT_FLUSH_INTERVAL_MSECS;
162157
}
163158

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+
164168
this.state = STATE.STOPPED;
165169
}
166170

@@ -398,17 +402,12 @@ export class OdpEventManager implements IOdpEventManager {
398402
return true;
399403
}
400404

401-
try {
402-
if (process) {
403-
// if Node/server-side context, empty queue items before ready state
404-
this.logger.log(LogLevel.WARNING, 'ODPConfig not ready. Discarding events in queue.');
405-
this.queue = new Array<OdpEvent>();
406-
} else {
407-
// in Browser/client-side context, give debug message but leave events in queue
408-
this.logger.log(LogLevel.DEBUG, 'ODPConfig not ready. Leaving events in queue.');
409-
}
410-
} catch (e) {
411-
// TODO: Create Browser and Non-Browser specific variants of ODP Event Manager to avoid this try/catch
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
412411
this.logger.log(LogLevel.DEBUG, 'ODPConfig not ready. Leaving events in queue.');
413412
}
414413

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,14 @@ 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+
}

0 commit comments

Comments
 (0)