Skip to content

Commit c4b8373

Browse files
authored
[FSSDK-9562] refactor odp event api manager to handle odp config (#844)
1 parent 4cea2f1 commit c4b8373

File tree

8 files changed

+193
-77
lines changed

8 files changed

+193
-77
lines changed

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

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,17 @@
1717
import { LogHandler, LogLevel } from '../../modules/logging';
1818
import { OdpEvent } from './odp_event';
1919
import { RequestHandler } from '../../utils/http_request_handler/http';
20+
import { OdpConfig } from './odp_config';
2021

2122
const EVENT_SENDING_FAILURE_MESSAGE = 'ODP event send failed';
23+
export const ODP_CONFIG_NOT_READY_MESSAGE = 'ODP config not ready';
2224

2325
/**
2426
* Manager for communicating with the Optimizely Data Platform REST API
2527
*/
2628
export interface IOdpEventApiManager {
27-
sendEvents(apiKey: string, apiHost: string, events: OdpEvent[]): Promise<boolean>;
29+
sendEvents(events: OdpEvent[]): Promise<boolean>;
30+
updateSettings(odpConfig: OdpConfig): void;
2831
}
2932

3033
/**
@@ -43,6 +46,11 @@ export abstract class OdpEventApiManager implements IOdpEventApiManager {
4346
*/
4447
private readonly requestHandler: RequestHandler;
4548

49+
/**
50+
* ODP configuration settings for identifying the target API and segments
51+
*/
52+
protected odpConfig?: OdpConfig;
53+
4654
/**
4755
* Creates instance to access Optimizely Data Platform (ODP) REST API
4856
* @param requestHandler Desired request handler for testing
@@ -53,22 +61,28 @@ export abstract class OdpEventApiManager implements IOdpEventApiManager {
5361
this.logger = logger;
5462
}
5563

64+
/**
65+
* Updates odpConfig of the api manager instance
66+
* @param odpConfig
67+
*/
68+
updateSettings(odpConfig: OdpConfig): void {
69+
this.odpConfig = odpConfig;
70+
}
71+
5672
getLogger(): LogHandler {
5773
return this.logger;
5874
}
5975

6076
/**
6177
* Service for sending ODP events to REST API
62-
* @param apiKey ODP public key
63-
* @param apiHost Host of ODP endpoint
6478
* @param events ODP events to send
6579
* @returns Retry is true - if network or server error (5xx), otherwise false
6680
*/
67-
async sendEvents(apiKey: string, apiHost: string, events: OdpEvent[]): Promise<boolean> {
81+
async sendEvents(events: OdpEvent[]): Promise<boolean> {
6882
let shouldRetry = false;
6983

70-
if (!apiKey || !apiHost) {
71-
this.logger.log(LogLevel.ERROR, `${EVENT_SENDING_FAILURE_MESSAGE} (Parameters apiKey or apiHost invalid)`);
84+
if (!this.odpConfig?.isReady()) {
85+
this.logger.log(LogLevel.ERROR, `${EVENT_SENDING_FAILURE_MESSAGE} (${ODP_CONFIG_NOT_READY_MESSAGE})`);
7286
return shouldRetry;
7387
}
7488

@@ -81,7 +95,7 @@ export abstract class OdpEventApiManager implements IOdpEventApiManager {
8195
return shouldRetry;
8296
}
8397

84-
const { method, endpoint, headers, data } = this.generateRequestData(apiHost, apiKey, events);
98+
const { method, endpoint, headers, data } = this.generateRequestData(events);
8599

86100
let statusCode = 0;
87101
try {
@@ -111,8 +125,6 @@ export abstract class OdpEventApiManager implements IOdpEventApiManager {
111125
protected abstract shouldSendEvents(events: OdpEvent[]): boolean;
112126

113127
protected abstract generateRequestData(
114-
apiHost: string,
115-
apiKey: string,
116128
events: OdpEvent[]
117129
): {
118130
method: string;

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,8 @@ export abstract class OdpEventManager implements IOdpEventManager {
149149
this.clientVersion = clientVersion;
150150
this.initParams(batchSize, queueSize, flushInterval);
151151
this.state = STATE.STOPPED;
152+
153+
this.apiManager.updateSettings(odpConfig);
152154
}
153155

154156
protected abstract initParams(
@@ -163,6 +165,7 @@ export abstract class OdpEventManager implements IOdpEventManager {
163165
*/
164166
updateSettings(newConfig: OdpConfig): void {
165167
this.odpConfig = newConfig;
168+
this.apiManager.updateSettings(newConfig);
166169
}
167170

168171
/**
@@ -340,7 +343,7 @@ export abstract class OdpEventManager implements IOdpEventManager {
340343
*/
341344
private makeAndSend1Batch(): void {
342345
const batch = new Array<OdpEvent>();
343-
346+
344347
// remove a batch from the queue
345348
for (let count = 0; count < this.batchSize; count += 1) {
346349
const event = this.queue.shift();
@@ -357,7 +360,7 @@ export abstract class OdpEventManager implements IOdpEventManager {
357360
let shouldRetry: boolean;
358361
let attemptNumber = 0;
359362
do {
360-
shouldRetry = await this.apiManager.sendEvents(this.odpConfig.apiKey, this.odpConfig.apiHost, batch);
363+
shouldRetry = await this.apiManager.sendEvents(batch);
361364
attemptNumber += 1;
362365
} while (shouldRetry && attemptNumber < MAX_RETRIES);
363366
});

packages/optimizely-sdk/lib/index.browser.tests.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1111,7 +1111,7 @@ describe('javascript-sdk (Browser)', function() {
11111111

11121112
clock.tick(100);
11131113

1114-
const [, , events] = apiManager.sendEvents.getCall(0).args;
1114+
const [events] = apiManager.sendEvents.getCall(0).args;
11151115
const [firstEvent] = events;
11161116
assert.equal(firstEvent.action, 'client_initialized');
11171117
assert.equal(firstEvent.type, 'fullstack');

packages/optimizely-sdk/lib/plugins/odp/event_api_manager/index.browser.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { OdpEvent } from '../../../core/odp/odp_event';
22
import { OdpEventApiManager } from '../../../core/odp/odp_event_api_manager';
33
import { LogLevel } from '../../../modules/logging';
44
import { ODP_EVENT_BROWSER_ENDPOINT } from '../../../utils/enums';
5+
import { ODP_CONFIG_NOT_READY_MESSAGE } from '../../../core/odp/odp_event_api_manager';
56

67
const EVENT_SENDING_FAILURE_MESSAGE = 'ODP event send failed';
78

@@ -15,10 +16,15 @@ export class BrowserOdpEventApiManager extends OdpEventApiManager {
1516
}
1617

1718
protected generateRequestData(
18-
apiHost: string,
19-
apiKey: string,
2019
events: OdpEvent[]
2120
): { method: string; endpoint: string; headers: { [key: string]: string }; data: string } {
21+
// the caller should ensure odpConfig is ready before calling
22+
if (!this.odpConfig?.isReady()) {
23+
this.getLogger().log(LogLevel.ERROR, ODP_CONFIG_NOT_READY_MESSAGE);
24+
throw new Error(ODP_CONFIG_NOT_READY_MESSAGE);
25+
}
26+
27+
const apiKey = this.odpConfig.apiKey;
2228
const method = 'GET';
2329
const event = events[0];
2430
const url = new URL(ODP_EVENT_BROWSER_ENDPOINT);

packages/optimizely-sdk/lib/plugins/odp/event_api_manager/index.node.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,24 @@
11
import { OdpEvent } from '../../../core/odp/odp_event';
22
import { OdpEventApiManager } from '../../../core/odp/odp_event_api_manager';
3-
3+
import { LogLevel } from '../../../modules/logging';
4+
import { ODP_CONFIG_NOT_READY_MESSAGE } from '../../../core/odp/odp_event_api_manager';
45
export class NodeOdpEventApiManager extends OdpEventApiManager {
56
protected shouldSendEvents(events: OdpEvent[]): boolean {
67
return true;
78
}
89

910
protected generateRequestData(
10-
apiHost: string,
11-
apiKey: string,
1211
events: OdpEvent[]
1312
): { method: string; endpoint: string; headers: { [key: string]: string }; data: string } {
13+
// the caller should ensure odpConfig is ready before calling
14+
if (!this.odpConfig?.isReady()) {
15+
this.getLogger().log(LogLevel.ERROR, ODP_CONFIG_NOT_READY_MESSAGE);
16+
throw new Error(ODP_CONFIG_NOT_READY_MESSAGE);
17+
}
18+
19+
const apiHost = this.odpConfig.apiHost;
20+
const apiKey = this.odpConfig.apiKey;
21+
1422
return {
1523
method: 'POST',
1624
endpoint: `${apiHost}/v3/events`,
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"typeAcquisition": {
3+
"include": [
4+
"jest"
5+
]
6+
}
7+
}

packages/optimizely-sdk/tests/odpEventApiManager.spec.ts

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,13 @@
1616

1717
/// <reference types="jest" />
1818

19-
import { anyString, anything, instance, mock, resetCalls, verify, when } from 'ts-mockito';
19+
import { anyString, anything, capture, instance, mock, resetCalls, verify, when } from 'ts-mockito';
2020
import { LogHandler, LogLevel } from '../lib/modules/logging';
2121
import { NodeOdpEventApiManager } from '../lib/plugins/odp/event_api_manager/index.node';
2222
import { OdpEvent } from '../lib/core/odp/odp_event';
2323
import { RequestHandler } from '../lib/utils/http_request_handler/http';
24+
import { OdpConfig } from '../lib/core/odp/odp_config';
2425

25-
const VALID_ODP_PUBLIC_KEY = 'not-real-api-key';
26-
const ODP_REST_API_HOST = 'https://events.example.com/v2/api';
2726
const data1 = new Map<string, unknown>();
2827
data1.set('key11', 'value-1');
2928
data1.set('key12', true);
@@ -36,6 +35,11 @@ const ODP_EVENTS = [
3635
new OdpEvent('t2', 'a2', new Map([['id-key-2', 'id-value-2']]), data2),
3736
];
3837

38+
const API_KEY = 'test-api-key';
39+
const API_HOST = 'https://odp.example.com';
40+
41+
const odpConfig = new OdpConfig(API_KEY, API_HOST, []);
42+
3943
describe('NodeOdpEventApiManager', () => {
4044
let mockLogger: LogHandler;
4145
let mockRequestHandler: RequestHandler;
@@ -50,7 +54,12 @@ describe('NodeOdpEventApiManager', () => {
5054
resetCalls(mockRequestHandler);
5155
});
5256

53-
const managerInstance = () => new NodeOdpEventApiManager(instance(mockRequestHandler), instance(mockLogger));
57+
const managerInstance = () => {
58+
const manager = new NodeOdpEventApiManager(instance(mockRequestHandler), instance(mockLogger));
59+
manager.updateSettings(odpConfig);
60+
return manager;
61+
}
62+
5463
const abortableRequest = (statusCode: number, body: string) => {
5564
return {
5665
abort: () => {},
@@ -68,7 +77,7 @@ describe('NodeOdpEventApiManager', () => {
6877
);
6978
const manager = managerInstance();
7079

71-
const shouldRetry = await manager.sendEvents(VALID_ODP_PUBLIC_KEY, ODP_REST_API_HOST, ODP_EVENTS);
80+
const shouldRetry = await manager.sendEvents(ODP_EVENTS);
7281

7382
expect(shouldRetry).toBe(false);
7483
verify(mockLogger.log(anything(), anyString())).never();
@@ -80,7 +89,7 @@ describe('NodeOdpEventApiManager', () => {
8089
);
8190
const manager = managerInstance();
8291

83-
const shouldRetry = await manager.sendEvents(VALID_ODP_PUBLIC_KEY, ODP_REST_API_HOST, ODP_EVENTS);
92+
const shouldRetry = await manager.sendEvents(ODP_EVENTS);
8493

8594
expect(shouldRetry).toBe(false);
8695
verify(mockLogger.log(LogLevel.ERROR, 'ODP event send failed (400)')).once();
@@ -92,7 +101,7 @@ describe('NodeOdpEventApiManager', () => {
92101
);
93102
const manager = managerInstance();
94103

95-
const shouldRetry = await manager.sendEvents(VALID_ODP_PUBLIC_KEY, ODP_REST_API_HOST, ODP_EVENTS);
104+
const shouldRetry = await manager.sendEvents(ODP_EVENTS);
96105

97106
expect(shouldRetry).toBe(true);
98107
verify(mockLogger.log(LogLevel.ERROR, 'ODP event send failed (500)')).once();
@@ -105,9 +114,37 @@ describe('NodeOdpEventApiManager', () => {
105114
});
106115
const manager = managerInstance();
107116

108-
const shouldRetry = await manager.sendEvents(VALID_ODP_PUBLIC_KEY, ODP_REST_API_HOST, ODP_EVENTS);
117+
const shouldRetry = await manager.sendEvents(ODP_EVENTS);
109118

110119
expect(shouldRetry).toBe(true);
111120
verify(mockLogger.log(LogLevel.ERROR, 'ODP event send failed (Request timed out)')).once();
112121
});
122+
123+
it('should send events to updated host on settings update', async () => {
124+
when(mockRequestHandler.makeRequest(anything(), anything(), anything(), anything())).thenReturn({
125+
abort: () => {},
126+
responsePromise: Promise.reject(new Error('Request timed out')),
127+
});
128+
129+
const manager = managerInstance();
130+
131+
await manager.sendEvents(ODP_EVENTS);
132+
133+
const updatedOdpConfig = new OdpConfig(
134+
'updated-key',
135+
'https://updatedhost.test',
136+
['updated-seg'],
137+
)
138+
139+
manager.updateSettings(updatedOdpConfig);
140+
await manager.sendEvents(ODP_EVENTS);
141+
142+
verify(mockRequestHandler.makeRequest(anything(), anything(), anything(), anything())).twice();
143+
144+
const [initUrl] = capture(mockRequestHandler.makeRequest).first();
145+
expect(initUrl).toEqual(`${API_HOST}/v3/events`);
146+
147+
const [finalUrl] = capture(mockRequestHandler.makeRequest).last();
148+
expect(finalUrl).toEqual(`${updatedOdpConfig.apiHost}/v3/events`);
149+
});
113150
});

0 commit comments

Comments
 (0)