Skip to content
23 changes: 23 additions & 0 deletions src/client.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1558,4 +1558,27 @@ describe('ReactSDKClient', () => {
});
});
});

describe('sendOdpEvent', () => {
let instance: ReactSDKClient;
beforeEach(() => {
instance = createInstance(config);
});

it('should throw error when action param is falsy', async () => {
const badValues = ['', ' '];
badValues.forEach(item => {
instance.sendOdpEvent(item);
});

expect(logger.error).toHaveBeenCalledTimes(badValues.length);
expect(logger.error).toBeCalledWith('ODP action is not valid (cannot be empty).');
});

it('should call sendOdpEvent once', async () => {
instance.sendOdpEvent('test');

expect(mockInnerClient.sendOdpEvent).toHaveBeenCalledTimes(1);
});
});
});
81 changes: 43 additions & 38 deletions src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,11 +177,6 @@ export interface ReactSDKClient extends Omit<optimizely.Client, 'createUserConte
export const DEFAULT_ON_READY_TIMEOUT = 5000;

class OptimizelyReactSDKClient implements ReactSDKClient {
public initialConfig: optimizely.Config;
public user: UserInfo = {
id: null,
attributes: {},
};
private userContext: optimizely.OptimizelyUserContext | null = null;
private userPromiseResolver: (user: UserInfo) => void;
private userPromise: Promise<OnReadyResult>;
Expand All @@ -207,6 +202,12 @@ class OptimizelyReactSDKClient implements ReactSDKClient {
// promise keeping track of async requests for initializing client instance
private dataReadyPromise: Promise<OnReadyResult>;

public initialConfig: optimizely.Config;
public user: UserInfo = {
id: null,
attributes: {},
};

/**
* Creates an instance of OptimizelyReactSDKClient.
* @param {optimizely.Config} [config={}]
Expand Down Expand Up @@ -258,15 +259,29 @@ class OptimizelyReactSDKClient implements ReactSDKClient {
}
}

getIsReadyPromiseFulfilled(): boolean {
protected getUserContextWithOverrides(
overrideUserId?: string,
overrideAttributes?: optimizely.UserAttributes
): UserInfo {
const finalUserId: string | null = overrideUserId === undefined ? this.user.id : overrideUserId;
const finalUserAttributes: optimizely.UserAttributes | undefined =
overrideAttributes === undefined ? this.user.attributes : overrideAttributes;

return {
id: finalUserId,
attributes: finalUserAttributes,
};
}

public getIsReadyPromiseFulfilled(): boolean {
return this.isReadyPromiseFulfilled;
}

getIsUsingSdkKey(): boolean {
public getIsUsingSdkKey(): boolean {
return this.isUsingSdkKey;
}

onReady(config: { timeout?: number } = {}): Promise<OnReadyResult> {
public onReady(config: { timeout?: number } = {}): Promise<OnReadyResult> {
let timeoutId: number | undefined;
let timeout: number = DEFAULT_ON_READY_TIMEOUT;
if (config && config.timeout !== undefined) {
Expand All @@ -291,7 +306,7 @@ class OptimizelyReactSDKClient implements ReactSDKClient {
});
}

getUserContextInstance(userInfo: UserInfo): optimizely.OptimizelyUserContext | null {
public getUserContextInstance(userInfo: UserInfo): optimizely.OptimizelyUserContext | null {
if (!this._client) {
logger.warn(
'Unable to get user context for user id "%s" because Optimizely client failed to initialize.',
Expand Down Expand Up @@ -323,7 +338,7 @@ class OptimizelyReactSDKClient implements ReactSDKClient {
return null;
}

async fetchQualifiedSegments(): Promise<boolean> {
public async fetchQualifiedSegments(): Promise<boolean> {
if (!this.userContext) {
logger.warn('Unable to fetch qualified segments for user because Optimizely client failed to initialize.');
return false;
Expand All @@ -332,7 +347,7 @@ class OptimizelyReactSDKClient implements ReactSDKClient {
return await this.userContext.fetchQualifiedSegments();
}

setUser(userInfo: UserInfo): void {
public setUser(userInfo: UserInfo): void {
// TODO add check for valid user
if (userInfo.id) {
this.user.id = userInfo.id;
Expand Down Expand Up @@ -360,7 +375,7 @@ class OptimizelyReactSDKClient implements ReactSDKClient {
this.onUserUpdateHandlers.forEach(handler => handler(this.user));
}

onUserUpdate(handler: OnUserUpdateHandler): DisposeFn {
public onUserUpdate(handler: OnUserUpdateHandler): DisposeFn {
this.onUserUpdateHandlers.push(handler);

return () => {
Expand All @@ -377,7 +392,7 @@ class OptimizelyReactSDKClient implements ReactSDKClient {
* @param {OnForcedVariationsUpdateHandler} handler
* @returns {DisposeFn}
*/
onForcedVariationsUpdate(handler: OnForcedVariationsUpdateHandler): DisposeFn {
public onForcedVariationsUpdate(handler: OnForcedVariationsUpdateHandler): DisposeFn {
this.onForcedVariationsUpdateHandlers.push(handler);

return (): void => {
Expand All @@ -388,7 +403,7 @@ class OptimizelyReactSDKClient implements ReactSDKClient {
};
}

isReady(): boolean {
public isReady(): boolean {
// React SDK Instance only becomes ready when both JS SDK client and the user info is ready.
return this.isUserReady && this.isClientReady;
}
Expand Down Expand Up @@ -952,7 +967,7 @@ class OptimizelyReactSDKClient implements ReactSDKClient {
* @returns {(unknown | null)}
* @memberof OptimizelyReactSDKClient
*/
getFeatureVariable(
public getFeatureVariable(
featureKey: string,
variableKey: string,
overrideUserId: string,
Expand Down Expand Up @@ -989,7 +1004,7 @@ class OptimizelyReactSDKClient implements ReactSDKClient {
* @returns {({ [variableKey: string]: unknown } | null)}
* @memberof OptimizelyReactSDKClient
*/
getAllFeatureVariables(
public getAllFeatureVariables(
featureKey: string,
overrideUserId: string,
overrideAttributes?: optimizely.UserAttributes
Expand Down Expand Up @@ -1174,33 +1189,23 @@ class OptimizelyReactSDKClient implements ReactSDKClient {
return this._client.notificationCenter;
}

protected getUserContextWithOverrides(
overrideUserId?: string,
overrideAttributes?: optimizely.UserAttributes
): UserInfo {
const finalUserId: string | null = overrideUserId === undefined ? this.user.id : overrideUserId;
const finalUserAttributes: optimizely.UserAttributes | undefined =
overrideAttributes === undefined ? this.user.attributes : overrideAttributes;

return {
id: finalUserId,
attributes: finalUserAttributes,
};
}

// TODO: discuss if we want to expose these method and provide implementation
getVuid(): string | undefined {
// TODO: this is tobe removed in future once the js-sdk gets updated
public getVuid(): string | undefined {
return undefined;
}

// TODO: discuss if we want to expose these method and provide implementation
sendOdpEvent(
public sendOdpEvent(
action: string,
type: string | undefined,
identifiers: Map<string, string> | undefined,
data: Map<string, unknown> | undefined
type?: string,
identifiers?: Map<string, string>,
data?: Map<string, unknown>
): void {
// no-op
if (!action || !action.trim()) {
logger.error('ODP action is not valid (cannot be empty).');
return;
}

this.client?.sendOdpEvent(action, type, identifiers, data);
}
}

Expand Down