Skip to content

feat: Added getFeatureVariableJson, getFeatureVariable and getAllFeatureVariables #53

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

Merged
merged 6 commits into from
Jul 8, 2020
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -484,7 +484,7 @@ The following type definitions are used in the `ReactSDKClient` interface:
- `onUserUpdate(handler: (userInfo: User) => void): () => void` Subscribe a callback to be called when this instance's current user changes. Returns a function that will unsubscribe the callback.
- `activate(experimentKey: string, overrideUserId?: string, overrideAttributes?: UserAttributes): string | null` Activate an experiment, and return the variation for the given user.
- `getVariation(experimentKey: string, overrideUserId?: string, overrideAttributes?: UserAttributes): string | null` Return the variation for the given experiment and user.
- `getFeatureVariables(featureKey: string, overrideUserId?: string, overrideAttributes?: UserAttributes): VariableValuesObject`: Decide and return variable values for the given feature and user
- `getFeatureVariables(featureKey: string, overrideUserId?: string, overrideAttributes?: UserAttributes): VariableValuesObject`: Decide and return variable values for the given feature and user <br /> <b>Warning:</b> Deprecated since 2.1.0 <br /> `getAllFeatureVariables` is added in JavaScript SDK which is similarly returning all the feature variables, but it sends only single notification of type `all-feature-variables` instead of sending for each variable. As `getFeatureVariables` was added when this functionality wasn't provided by `JavaScript SDK`, so there is no need of it now and it would be removed in next major release
- `getFeatureVariableString(featureKey: string, variableKey: string, overrideUserId?: string, overrideAttributes?: optimizely.UserAttributes): string | null`: Decide and return the variable value for the given feature, variable, and user
- `getFeatureVariableInteger(featureKey: string, variableKey: string, overrideUserId?: string, overrideAttributes?: UserAttributes): number | null` Decide and return the variable value for the given feature, variable, and user
- `getFeatureVariableBoolean(featureKey: string, variableKey: string, overrideUserId?: string, overrideAttributes?: UserAttributes): boolean | null` Decide and return the variable value for the given feature, variable, and user
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
},
"dependencies": {
"@optimizely/js-sdk-logging": "^0.1.0",
"@optimizely/optimizely-sdk": "4.0.0",
"@optimizely/optimizely-sdk": "4.1.0",
"hoist-non-react-statics": "^3.3.0",
"prop-types": "^15.6.2",
"utility-types": "^2.1.0 || ^3.0.0"
Expand Down
171 changes: 171 additions & 0 deletions src/client.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ describe('ReactSDKClient', () => {
getForcedVariation: jest.fn(() => null),
getFeatureVariableBoolean: jest.fn(() => null),
getFeatureVariableDouble: jest.fn(() => null),
getFeatureVariableJSON: jest.fn(() => null),
getAllFeatureVariables: jest.fn(() => { return {} }),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, interesting that getAllFeatureVariables was not here before. I guess it should be added, but does it affect any test?

getFeatureVariable: jest.fn(() => null),
getFeatureVariableInteger: jest.fn(() => null),
getFeatureVariableString: jest.fn(() => null),
getOptimizelyConfig: jest.fn(() => null),
Expand Down Expand Up @@ -358,6 +361,68 @@ describe('ReactSDKClient', () => {
expect(mockInnerClient.getFeatureVariableDouble).toBeCalledWith('feat1', 'dvar1', 'user2', { bar: 'baz' });
});

it('can use pre-set and override user for getFeatureVariableJSON', () => {
const mockFn = mockInnerClient.getFeatureVariableJSON as jest.Mock;
mockFn.mockReturnValue({
num_buttons: 0,
text: 'default value',
});
let result = instance.getFeatureVariableJSON('feat1', 'dvar1');
expect(result).toEqual({
num_buttons: 0,
text: 'default value',
});
expect(mockFn).toBeCalledTimes(1);
expect(mockFn).toBeCalledWith('feat1', 'dvar1', 'user1', {
foo: 'bar',
});
mockFn.mockReset();
mockFn.mockReturnValue({
num_buttons: 0,
text: 'variable value',
});
result = instance.getFeatureVariableJSON('feat1', 'dvar1', 'user2', {
bar: 'baz',
});
expect(result).toEqual({
num_buttons: 0,
text: 'variable value',
});
expect(mockInnerClient.getFeatureVariableJSON).toBeCalledTimes(1);
expect(mockInnerClient.getFeatureVariableJSON).toBeCalledWith('feat1', 'dvar1', 'user2', { bar: 'baz' });
});

it('can use pre-set and override user for getFeatureVariable', () => {
const mockFn = mockInnerClient.getFeatureVariable as jest.Mock;
mockFn.mockReturnValue({
num_buttons: 0,
text: 'default value',
});
let result = instance.getFeatureVariable('feat1', 'dvar1', 'user1');
expect(result).toEqual({
num_buttons: 0,
text: 'default value',
});
expect(mockFn).toBeCalledTimes(1);
expect(mockFn).toBeCalledWith('feat1', 'dvar1', 'user1', {
foo: 'bar',
});
mockFn.mockReset();
mockFn.mockReturnValue({
num_buttons: 0,
text: 'variable value',
});
result = instance.getFeatureVariable('feat1', 'dvar1', 'user2', {
bar: 'baz',
});
expect(result).toEqual({
num_buttons: 0,
text: 'variable value',
});
expect(mockInnerClient.getFeatureVariable).toBeCalledTimes(1);
expect(mockInnerClient.getFeatureVariable).toBeCalledWith('feat1', 'dvar1', 'user2', { bar: 'baz' });
});

it('can use pre-set and override user for setForcedVariation', () => {
const mockFn = mockInnerClient.setForcedVariation as jest.Mock;
mockFn.mockReturnValue(true);
Expand Down Expand Up @@ -398,6 +463,7 @@ describe('ReactSDKClient', () => {
anyClient.getFeatureVariableString.mockReturnValue(null);
anyClient.getFeatureVariableInteger.mockReturnValue(null);
anyClient.getFeatureVariableDouble.mockReturnValue(null);
anyClient.getFeatureVariableJSON.mockReturnValue(null);
const instance = createInstance(config);
const result = instance.getFeatureVariables('feat1');
expect(result).toEqual({});
Expand Down Expand Up @@ -427,6 +493,10 @@ describe('ReactSDKClient', () => {
type: 'double',
key: 'dvar',
},
{
type: 'json',
key: 'jvar',
},
],
},
},
Expand All @@ -437,6 +507,9 @@ describe('ReactSDKClient', () => {
anyClient.getFeatureVariableString.mockReturnValue('whatsup');
anyClient.getFeatureVariableInteger.mockReturnValue(10);
anyClient.getFeatureVariableDouble.mockReturnValue(-10.5);
anyClient.getFeatureVariableJSON.mockReturnValue({
value: 'json value'
});
const instance = createInstance(config);
instance.setUser({
id: 'user1123',
Expand All @@ -447,7 +520,105 @@ describe('ReactSDKClient', () => {
svar: 'whatsup',
ivar: 10,
dvar: -10.5,
jvar: {
value: 'json value'
}
});
});
});

describe('getAllFeatureVariables', () => {
it('returns an empty object when the inner SDK returns no variables', () => {
const anyClient = mockInnerClient.getAllFeatureVariables as jest.Mock;
anyClient.mockReturnValue({});
const instance = createInstance(config);
const result = instance.getAllFeatureVariables('feat1', 'user1');
expect(result).toEqual({});
});

it('returns an object with variables of all types returned from the inner sdk ', () => {
const anyClient = mockInnerClient.getAllFeatureVariables as jest.Mock;
anyClient.mockReturnValue({
bvar: true,
svar: 'whatsup',
ivar: 10,
dvar: -10.5,
jvar: {
value: 'json value'
}
});
const instance = createInstance(config);
instance.setUser({
id: 'user1123',
});
const result = instance.getAllFeatureVariables('feat1', 'user1');
expect(result).toEqual({
bvar: true,
svar: 'whatsup',
ivar: 10,
dvar: -10.5,
jvar: {
value: 'json value'
}
});
});

it('can use pre-set and override user for getAllFeatureVariables', () => {
const mockFn = mockInnerClient.getAllFeatureVariables as jest.Mock;
mockFn.mockReturnValue({
bvar: true,
svar: 'whatsup',
ivar: 10,
dvar: -10.5,
jvar: {
value: 'json value'
}
});
const instance = createInstance(config);
instance.setUser({
id: 'user1',
attributes: {
foo: 'bar',
},
});
let result = instance.getAllFeatureVariables('feat1', 'user1');
expect(result).toEqual({
bvar: true,
svar: 'whatsup',
ivar: 10,
dvar: -10.5,
jvar: {
value: 'json value'
}
});
expect(mockFn).toBeCalledTimes(1);
expect(mockFn).toBeCalledWith('feat1', 'user1', {
foo: 'bar',
});
mockFn.mockReset();
mockFn.mockReturnValue({
bvar: false,
svar: 'another var',
ivar: 11,
dvar: -11.5,
jvar: {
value: 'json another value'
}
});
result = instance.getAllFeatureVariables('feat1', 'user2', {
bar: 'baz',
});
expect(result).toEqual({
bvar: false,
svar: 'another var',
ivar: 11,
dvar: -11.5,
jvar: {
value: 'json another value'
}
});
expect(mockInnerClient.getAllFeatureVariables).toBeCalledTimes(1);
expect(mockInnerClient.getAllFeatureVariables).toBeCalledWith('feat1', 'user2', { bar: 'baz' });
});
});
});
Expand Down
113 changes: 112 additions & 1 deletion src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import * as optimizely from '@optimizely/optimizely-sdk';
import * as logging from '@optimizely/js-sdk-logging';
import { UserAttributes } from "@optimizely/optimizely-sdk";

const logger = logging.getLogger('ReactSDK');

Expand Down Expand Up @@ -89,6 +90,26 @@ export interface ReactSDKClient extends optimizely.Client {
overrideAttributes?: optimizely.UserAttributes
): number | null;

getFeatureVariableJSON(
featureKey: string,
variableKey: string,
overrideUserId?: string,
overrideAttributes?: optimizely.UserAttributes,
): unknown

getFeatureVariable(
featureKey: string,
variableKey: string,
overrideUserId: string,
overrideAttributes?: optimizely.UserAttributes
): unknown

getAllFeatureVariables(
featureKey: string,
overrideUserId: string,
overrideAttributes?: optimizely.UserAttributes
): { [variableKey: string]: unknown }

isFeatureEnabled(
featureKey: string,
overrideUserId?: string,
Expand Down Expand Up @@ -305,8 +326,14 @@ class OptimizelyReactSDKClient implements ReactSDKClient {
}

/**
* @deprecated since 2.1.0
* getAllFeatureVariables is added in JavaScript SDK which is similarly returning all the feature variables, but
* it sends only single notification of type "all-feature-variables" instead of sending for each variable.
* As getFeatureVariables was added when this functionality wasn't provided by JavaScript SDK, so there is no
* need of it now and it would be removed in next major release
*
* Get all variables for a feature, regardless of the feature being enabled/disabled
* @param {string} feature
* @param {string} featureKey
* @param {string} [overrideUserId]
* @param {optimizely.UserAttributes} [overrideAttributes]
* @returns {VariableValuesObject}
Expand Down Expand Up @@ -354,6 +381,10 @@ class OptimizelyReactSDKClient implements ReactSDKClient {
case 'double':
variableObj[key] = this._client.getFeatureVariableDouble(featureKey, key, userId, userAttributes);
break;

case 'json':
variableObj[key] = this._client.getFeatureVariableJSON(featureKey, key, userId, userAttributes);
break;
}
});

Expand Down Expand Up @@ -452,6 +483,86 @@ class OptimizelyReactSDKClient implements ReactSDKClient {
return this._client.getFeatureVariableDouble(feature, variable, user.id, user.attributes);
}

/**
* Returns value for the given json variable attached to the given feature
* flag
* @param {string} feature
* @param {string} variable
* @param {string} [overrideUserId]
* @param {optimizely.UserAttributes} [overrideAttributes]
* @returns {(unknown | null)}
* @memberof OptimizelyReactSDKClient
*/
public getFeatureVariableJSON(
feature: string,
variable: string,
overrideUserId?: string,
overrideAttributes?: optimizely.UserAttributes
): unknown {
const user = this.getUserContextWithOverrides(overrideUserId, overrideAttributes);
if (user.id === null) {
return null
}
return this._client.getFeatureVariableJSON(
feature,
variable,
user.id,
user.attributes,
);
}

/**
* Returns dynamically-typed value of the variable attached to the given
* feature flag. Returns null if the feature key or variable key is invalid.
* @param {string} featureKey
* @param {string} variableKey
* @param {string} [overrideUserId]
* @param {optimizely.UserAttributes} [overrideAttributes]
* @returns {(unknown | null)}
* @memberof OptimizelyReactSDKClient
*/
getFeatureVariable(
featureKey: string,
variableKey: string,
overrideUserId: string,
overrideAttributes?: optimizely.UserAttributes
): unknown {
const user = this.getUserContextWithOverrides(overrideUserId, overrideAttributes);
if (user.id === null) {
return null
}
return this._client.getFeatureVariable(
featureKey,
variableKey,
user.id,
user.attributes,
);
}

/**
* Returns values for all the variables attached to the given feature flag
* @param {string} featureKey
* @param {string} overrideUserId
* @param {optimizely.UserAttributes} [overrideAttributes]
* @returns {({ [variableKey: string]: unknown } | null)}
* @memberof OptimizelyReactSDKClient
*/
getAllFeatureVariables(
featureKey: string,
overrideUserId: string,
overrideAttributes?: optimizely.UserAttributes
): { [variableKey: string]: unknown } {
const user = this.getUserContextWithOverrides(overrideUserId, overrideAttributes);
if (user.id === null) {
return {};
}
return this._client.getAllFeatureVariables(
featureKey,
user.id,
user.attributes,
);
}

/**
* Get an array of all enabled features
* @param {string} [overrideUserId]
Expand Down
Loading