Skip to content

Commit bc69201

Browse files
[FSSDK-9726] AAT gap fill (#221)
* chore: only yarn install during container create * chore: add copilot & chat to dev container * fix: add missing getVuid method * test: add unit tests for getVuid * fix: user context not saving to this.userContext * feat: add getQualifedSegments * test(wip): adding getQualifedSegments tests * chore: lint fix * test: fix test & implementation * fix: requsted PR adjustments * test: update tests per PR request * feat: add getUserContext * chore: lint fixes --------- Co-authored-by: Mike Chu <[email protected]>
1 parent c4afe04 commit bc69201

File tree

3 files changed

+117
-20
lines changed

3 files changed

+117
-20
lines changed

.devcontainer/devcontainer.json

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
{
22
"name": "React SDK",
3-
43
"image": "mcr.microsoft.com/devcontainers/javascript-node:1-18-bullseye",
5-
6-
"postCreateCommand": "npm install -g npm && yarn install",
7-
4+
"postCreateCommand": "yarn install",
85
"customizations": {
96
"vscode": {
107
"extensions": [
@@ -14,7 +11,8 @@
1411
"Gruntfuggly.todo-tree",
1512
"github.vscode-github-actions",
1613
"Orta.vscode-jest",
17-
"ms-vscode.test-adapter-converter"
14+
"ms-vscode.test-adapter-converter",
15+
"GitHub.copilot-chat"
1816
],
1917
"settings": {
2018
"files.eol": "\n"

src/client.spec.ts

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ describe('ReactSDKClient', () => {
269269
await instance.setUser({
270270
id: 'xxfueaojfe8&86',
271271
});
272-
await instance.onReady()
272+
await instance.onReady();
273273

274274
await instance.setUser({
275275
id: 'xxfueaojfe8&87',
@@ -1624,4 +1624,73 @@ describe('ReactSDKClient', () => {
16241624
expect(mockInnerClient.sendOdpEvent).toHaveBeenCalledTimes(1);
16251625
});
16261626
});
1627+
1628+
describe('getVuid', () => {
1629+
const vuidFormat = /^vuid_[a-f0-9]{27}$/;
1630+
let instance: ReactSDKClient;
1631+
1632+
beforeEach(async () => {
1633+
instance = createInstance(config);
1634+
});
1635+
1636+
it('should return undefined if client is null', () => {
1637+
// @ts-ignore
1638+
instance._client = null;
1639+
1640+
const vuid = instance.getVuid();
1641+
1642+
expect(vuid).toBeUndefined();
1643+
});
1644+
1645+
it('should return a valid vuid', async () => {
1646+
const validVuid = 'vuid_8de3bb278fce47f6b000cadc1ac';
1647+
const mockGetVuid = mockInnerClient.getVuid as jest.Mock;
1648+
mockGetVuid.mockReturnValue(validVuid);
1649+
1650+
const vuid = instance.getVuid();
1651+
1652+
expect(vuid).toMatch(vuidFormat);
1653+
expect(vuid).toEqual(validVuid);
1654+
});
1655+
});
1656+
1657+
describe('getUserContext', () => {
1658+
let instance: ReactSDKClient;
1659+
1660+
beforeEach(async () => {
1661+
instance = createInstance(config);
1662+
});
1663+
1664+
it('should log a warning and return null if client is not defined', () => {
1665+
// @ts-ignore
1666+
instance._client = null;
1667+
1668+
instance.getUserContext();
1669+
1670+
expect(logger.warn).toHaveBeenCalledTimes(1);
1671+
expect(logger.warn).toBeCalledWith("Unable to get user context because Optimizely client failed to initialize.");
1672+
});
1673+
1674+
1675+
it('should log a warning and return null if setUser is not called first', () => {
1676+
instance.getUserContext();
1677+
1678+
expect(logger.warn).toHaveBeenCalledTimes(1);
1679+
expect(logger.warn).toBeCalledWith("Unable to get user context because user was not set.");
1680+
});
1681+
1682+
it('should return a userContext if setUser is called', () => {
1683+
instance.setUser({
1684+
id: 'user1',
1685+
attributes: {
1686+
foo: 'bar',
1687+
},
1688+
});
1689+
1690+
const currentUserContext = instance.getUserContext();
1691+
1692+
expect(logger.warn).toHaveBeenCalledTimes(0);
1693+
expect(currentUserContext).not.toBeNull();
1694+
});
1695+
});
16271696
});

src/client.ts

Lines changed: 44 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,10 @@ const default_user: UserInfo = {
4949
attributes: {},
5050
};
5151

52-
export interface ReactSDKClient extends Omit<optimizely.Client, 'createUserContext' | 'getVuid'> {
52+
export interface ReactSDKClient extends Omit<optimizely.Client, 'createUserContext'> {
5353
user: UserInfo;
5454

55-
onReady(opts?: { timeout?: number }): Promise<any>;
55+
onReady(opts?: { timeout?: number; }): Promise<any>;
5656
setUser(userInfo: UserInfo): Promise<void>;
5757
onUserUpdate(handler: OnUserUpdateHandler): DisposeFn;
5858
isReady(): boolean;
@@ -123,7 +123,7 @@ export interface ReactSDKClient extends Omit<optimizely.Client, 'createUserConte
123123
featureKey: string,
124124
overrideUserId: string,
125125
overrideAttributes?: optimizely.UserAttributes
126-
): { [variableKey: string]: unknown } | null;
126+
): { [variableKey: string]: unknown; } | null;
127127

128128
isFeatureEnabled(
129129
featureKey: string,
@@ -159,14 +159,14 @@ export interface ReactSDKClient extends Omit<optimizely.Client, 'createUserConte
159159
options?: optimizely.OptimizelyDecideOption[],
160160
overrideUserId?: string,
161161
overrideAttributes?: optimizely.UserAttributes
162-
): { [key: string]: OptimizelyDecision };
162+
): { [key: string]: OptimizelyDecision; };
163163

164164
decideForKeys(
165165
keys: string[],
166166
options?: optimizely.OptimizelyDecideOption[],
167167
overrideUserId?: string,
168168
overrideAttributes?: optimizely.UserAttributes
169-
): { [key: string]: OptimizelyDecision };
169+
): { [key: string]: OptimizelyDecision; };
170170

171171
setForcedDecision(
172172
decisionContext: optimizely.OptimizelyDecisionContext,
@@ -180,9 +180,13 @@ export interface ReactSDKClient extends Omit<optimizely.Client, 'createUserConte
180180
getForcedDecision(decisionContext: optimizely.OptimizelyDecisionContext): optimizely.OptimizelyForcedDecision | null;
181181

182182
fetchQualifiedSegments(options?: optimizely.OptimizelySegmentOption[]): Promise<boolean>;
183+
184+
getUserContext(): optimizely.OptimizelyUserContext | null;
185+
186+
getVuid(): string | undefined;
183187
}
184188

185-
export const DEFAULT_ON_READY_TIMEOUT = 5000;
189+
export const DEFAULT_ON_READY_TIMEOUT = 5_000;
186190

187191
class OptimizelyReactSDKClient implements ReactSDKClient {
188192
private userContext: optimizely.OptimizelyUserContext | null = null;
@@ -291,7 +295,7 @@ class OptimizelyReactSDKClient implements ReactSDKClient {
291295
return this.isUsingSdkKey;
292296
}
293297

294-
public onReady(config: { timeout?: number } = {}): Promise<OnReadyResult> {
298+
public onReady(config: { timeout?: number; } = {}): Promise<OnReadyResult> {
295299
let timeoutId: number | undefined;
296300
let timeout: number = DEFAULT_ON_READY_TIMEOUT;
297301
if (config && config.timeout !== undefined) {
@@ -326,6 +330,24 @@ class OptimizelyReactSDKClient implements ReactSDKClient {
326330
});
327331
}
328332

333+
public getUserContext(): optimizely.OptimizelyUserContext | null {
334+
if (!this._client) {
335+
logger.warn(
336+
'Unable to get user context because Optimizely client failed to initialize.'
337+
);
338+
return null;
339+
}
340+
341+
if (!this.userContext) {
342+
logger.warn(
343+
'Unable to get user context because user was not set.'
344+
);
345+
return null;
346+
}
347+
348+
return this.userContext;
349+
}
350+
329351
public getUserContextInstance(userInfo: UserInfo): optimizely.OptimizelyUserContext | null {
330352
if (!this._client) {
331353
logger.warn(
@@ -501,7 +523,7 @@ class OptimizelyReactSDKClient implements ReactSDKClient {
501523
options: optimizely.OptimizelyDecideOption[] = [],
502524
overrideUserId?: string,
503525
overrideAttributes?: optimizely.UserAttributes
504-
): { [key: string]: OptimizelyDecision } {
526+
): { [key: string]: OptimizelyDecision; } {
505527
if (!this._client) {
506528
logger.warn('Unable to evaluate features for keys because Optimizely client failed to initialize.');
507529
return {};
@@ -517,7 +539,7 @@ class OptimizelyReactSDKClient implements ReactSDKClient {
517539
const optlyUserContext = this.getUserContextInstance(user);
518540
if (optlyUserContext) {
519541
return Object.entries(optlyUserContext.decideForKeys(keys, options)).reduce(
520-
(decisions: { [key: string]: OptimizelyDecision }, [key, decision]) => {
542+
(decisions: { [key: string]: OptimizelyDecision; }, [key, decision]) => {
521543
decisions[key] = {
522544
...decision,
523545
userContext: {
@@ -537,7 +559,7 @@ class OptimizelyReactSDKClient implements ReactSDKClient {
537559
options: optimizely.OptimizelyDecideOption[] = [],
538560
overrideUserId?: string,
539561
overrideAttributes?: optimizely.UserAttributes
540-
): { [key: string]: OptimizelyDecision } {
562+
): { [key: string]: OptimizelyDecision; } {
541563
if (!this._client) {
542564
logger.warn('Unable to evaluate all feature decisions because Optimizely client is not initialized.');
543565
return {};
@@ -553,7 +575,7 @@ class OptimizelyReactSDKClient implements ReactSDKClient {
553575
const optlyUserContext = this.getUserContextInstance(user);
554576
if (optlyUserContext) {
555577
return Object.entries(optlyUserContext.decideAll(options)).reduce(
556-
(decisions: { [key: string]: OptimizelyDecision }, [key, decision]) => {
578+
(decisions: { [key: string]: OptimizelyDecision; }, [key, decision]) => {
557579
decisions[key] = {
558580
...decision,
559581
userContext: {
@@ -1033,7 +1055,7 @@ class OptimizelyReactSDKClient implements ReactSDKClient {
10331055
featureKey: string,
10341056
overrideUserId: string,
10351057
overrideAttributes?: optimizely.UserAttributes
1036-
): { [variableKey: string]: unknown } | null {
1058+
): { [variableKey: string]: unknown; } | null {
10371059
if (!this._client) {
10381060
logger.warn(
10391061
'Unable to get all feature variables from feature "%s" because Optimizely client failed to initialize.',
@@ -1162,7 +1184,7 @@ class OptimizelyReactSDKClient implements ReactSDKClient {
11621184
* Cleanup method for killing an running timers and flushing eventQueue
11631185
* @returns {Promise<{ success: boolean; reason?: string }>}
11641186
*/
1165-
public close(): Promise<{ success: boolean; reason?: string }> {
1187+
public close(): Promise<{ success: boolean; reason?: string; }> {
11661188
if (!this._client) {
11671189
/**
11681190
* Note:
@@ -1171,7 +1193,7 @@ class OptimizelyReactSDKClient implements ReactSDKClient {
11711193
* - If we resolve as "false", then the cleanup for timers and the event queue will never trigger.
11721194
* - Not triggering cleanup may lead to memory leaks and other inefficiencies.
11731195
*/
1174-
return new Promise<{ success: boolean; reason: string }>((resolve, reject) =>
1196+
return new Promise<{ success: boolean; reason: string; }>((resolve, reject) =>
11751197
resolve({
11761198
success: true,
11771199
reason: 'Optimizely client is not initialized.',
@@ -1227,6 +1249,14 @@ class OptimizelyReactSDKClient implements ReactSDKClient {
12271249

12281250
this.client?.sendOdpEvent(action, type, identifiers, data);
12291251
}
1252+
1253+
public getVuid(): string | undefined {
1254+
if (!this._client) {
1255+
logger.warn('Unable to get VUID because Optimizely client failed to initialize.');
1256+
return undefined;
1257+
}
1258+
return this._client.getVuid();
1259+
}
12301260
}
12311261

12321262
export function createInstance(config: optimizely.Config): ReactSDKClient {

0 commit comments

Comments
 (0)