Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FSSDK-10090] Refactor ODP integration #920

Merged
merged 24 commits into from
Apr 5, 2024
Merged
Show file tree
Hide file tree
Changes from 21 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
112 changes: 37 additions & 75 deletions lib/core/odp/odp_config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright 2022-2023, Optimizely
* Copyright 2022-2024, Optimizely
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -19,90 +19,29 @@ import { checkArrayEquality } from '../../utils/fns';
export class OdpConfig {
/**
* Host of ODP audience segments API.
* @private
*/
private _apiHost: string;

/**
* Getter to retrieve the ODP server host
* @public
*/
get apiHost(): string {
return this._apiHost;
}
readonly apiHost: string;
mikechu-optimizely marked this conversation as resolved.
Show resolved Hide resolved

/**
* Public API key for the ODP account from which the audience segments will be fetched (optional).
* @private
*/
private _apiKey: string;

/**
* Getter to retrieve the ODP API key
* @public
*/
get apiKey(): string {
return this._apiKey;
}
readonly apiKey: string;

/**
* Url for sending events via pixel.
* @private
*/
private _pixelUrl: string;

/**
* Getter to retrieve the ODP pixel URL
* @public
*/
get pixelUrl(): string {
return this._pixelUrl;
}
readonly pixelUrl: string;

/**
* All ODP segments used in the current datafile (associated with apiHost/apiKey).
* @private
*/
private _segmentsToCheck: string[];

/**
* Getter for ODP segments to check
* @public
*/
get segmentsToCheck(): string[] {
return this._segmentsToCheck;
}

constructor(apiKey?: string, apiHost?: string, pixelUrl?: string, segmentsToCheck?: string[]) {
this._apiKey = apiKey ?? '';
this._apiHost = apiHost ?? '';
this._pixelUrl = pixelUrl ?? '';
this._segmentsToCheck = segmentsToCheck ?? [];
}

/**
* Update the ODP configuration details
* @param {OdpConfig} config New ODP Config to potentially update self with
* @returns true if configuration was updated successfully
*/
update(config: OdpConfig): boolean {
if (this.equals(config)) {
return false;
} else {
if (config.apiKey) this._apiKey = config.apiKey;
if (config.apiHost) this._apiHost = config.apiHost;
if (config.pixelUrl) this._pixelUrl = config.pixelUrl;
if (config.segmentsToCheck) this._segmentsToCheck = config.segmentsToCheck;
readonly segmentsToCheck: string[];

return true;
}
}

/**
* Determines if ODP configuration has the minimum amount of information
*/
isReady(): boolean {
return !!this._apiKey && !!this._apiHost;
constructor(apiKey: string, apiHost: string, pixelUrl: string, segmentsToCheck: string[]) {
this.apiKey = apiKey;
this.apiHost = apiHost;
this.pixelUrl = pixelUrl;
this.segmentsToCheck = segmentsToCheck;
}

/**
Expand All @@ -112,10 +51,33 @@ export class OdpConfig {
*/
equals(configToCompare: OdpConfig): boolean {
return (
this._apiHost === configToCompare._apiHost &&
this._apiKey === configToCompare._apiKey &&
this._pixelUrl === configToCompare._pixelUrl &&
checkArrayEquality(this.segmentsToCheck, configToCompare._segmentsToCheck)
this.apiHost === configToCompare.apiHost &&
this.apiKey === configToCompare.apiKey &&
this.pixelUrl === configToCompare.pixelUrl &&
checkArrayEquality(this.segmentsToCheck, configToCompare.segmentsToCheck)
);
}
}

export type OdpNotIntegratedConfig = {
readonly integrated: false;
}

export type OdpIntegratedConfig = {
readonly integrated: true;
readonly odpConfig: OdpConfig;
}

export const odpIntegrationEquals = (config1: OdpIntegrationConfig, config2: OdpIntegrationConfig): boolean => {
mikechu-optimizely marked this conversation as resolved.
Show resolved Hide resolved
if (config1.integrated !== config2.integrated) {
return false;
}

if (config1.integrated && config2.integrated) {
return config1.odpConfig.equals(config2.odpConfig);
}

return true;
}

export type OdpIntegrationConfig = OdpNotIntegratedConfig | OdpIntegratedConfig;
30 changes: 6 additions & 24 deletions lib/core/odp/odp_event_api_manager.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright 2022-2023, Optimizely
* Copyright 2022-2024, Optimizely
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -18,16 +18,15 @@ import { LogHandler, LogLevel } from '../../modules/logging';
import { OdpEvent } from './odp_event';
import { RequestHandler } from '../../utils/http_request_handler/http';
import { OdpConfig } from './odp_config';
import { ERROR_MESSAGES } from '../../utils/enums';

const EVENT_SENDING_FAILURE_MESSAGE = 'ODP event send failed';
export const ODP_CONFIG_NOT_READY_MESSAGE = 'ODP config not ready';

/**
* Manager for communicating with the Optimizely Data Platform REST API
*/
export interface IOdpEventApiManager {
sendEvents(events: OdpEvent[]): Promise<boolean>;
updateSettings(odpConfig: OdpConfig): void;
sendEvents(odpConfig: OdpConfig, events: OdpEvent[]): Promise<boolean>;
}

/**
Expand All @@ -46,11 +45,6 @@ export abstract class OdpEventApiManager implements IOdpEventApiManager {
*/
private readonly requestHandler: RequestHandler;

/**
* ODP configuration settings for identifying the target API and segments
*/
protected odpConfig?: OdpConfig;

/**
* Creates instance to access Optimizely Data Platform (ODP) REST API
* @param requestHandler Desired request handler for testing
Expand All @@ -61,14 +55,6 @@ export abstract class OdpEventApiManager implements IOdpEventApiManager {
this.logger = logger;
}

/**
* Updates odpConfig of the api manager instance
* @param odpConfig
*/
updateSettings(odpConfig: OdpConfig): void {
this.odpConfig = odpConfig;
}

getLogger(): LogHandler {
return this.logger;
}
Expand All @@ -78,14 +64,9 @@ export abstract class OdpEventApiManager implements IOdpEventApiManager {
* @param events ODP events to send
* @returns Retry is true - if network or server error (5xx), otherwise false
*/
async sendEvents(events: OdpEvent[]): Promise<boolean> {
async sendEvents(odpConfig: OdpConfig, events: OdpEvent[]): Promise<boolean> {
mikechu-optimizely marked this conversation as resolved.
Show resolved Hide resolved
let shouldRetry = false;

if (!this.odpConfig?.isReady()) {
this.logger.log(LogLevel.ERROR, `${EVENT_SENDING_FAILURE_MESSAGE} (${ODP_CONFIG_NOT_READY_MESSAGE})`);
return shouldRetry;
}

if (events.length === 0) {
this.logger.log(LogLevel.ERROR, `${EVENT_SENDING_FAILURE_MESSAGE} (no events)`);
return shouldRetry;
Expand All @@ -95,7 +76,7 @@ export abstract class OdpEventApiManager implements IOdpEventApiManager {
return shouldRetry;
}

const { method, endpoint, headers, data } = this.generateRequestData(events);
const { method, endpoint, headers, data } = this.generateRequestData(odpConfig, events);

let statusCode = 0;
try {
Expand Down Expand Up @@ -125,6 +106,7 @@ export abstract class OdpEventApiManager implements IOdpEventApiManager {
protected abstract shouldSendEvents(events: OdpEvent[]): boolean;

protected abstract generateRequestData(
odpConfig: OdpConfig,
events: OdpEvent[]
): {
method: string;
Expand Down
Loading
Loading