Skip to content
Open
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
10 changes: 10 additions & 0 deletions clientlibs/js/src/DataService/DataService.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,16 @@ describe('DataService', () => {
});
});

describe('#clearFeatureFlags', () => {
it('should clear previously set feature flags', () => {
dataService.setFeatureFlags(['testFlagKey']);

dataService.clearFeatureFlags();

expect(dataService.getFeatureFlags()).toBeNull();
});
});

describe('#rotateAssignmentList', () => {
it('should return the rotated assignment list', () => {
const assignmentList: IExperimentAssignmentv5 = {
Expand Down
4 changes: 4 additions & 0 deletions clientlibs/js/src/DataService/DataService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ export class DataService {
this.featureFlags = featureFlags;
}

clearFeatureFlags() {
this.featureFlags = null;
}

public rotateAssignmentList(assignment: IExperimentAssignmentv5) {
if (assignment.assignedCondition.length > 1) {
assignment.assignedCondition.push(assignment.assignedCondition.shift());
Expand Down
52 changes: 52 additions & 0 deletions clientlibs/js/src/UpGradeClient/UpgradeClient.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,58 @@ describe('UpgradeClient', () => {

expect(ApiService.prototype.setFeatureFlagUserGroupsForSession).toHaveBeenCalledWith(undefined, undefined);
});

it('should clear the cached feature flags', () => {
ApiService.prototype.setFeatureFlagUserGroupsForSession = jest.fn();
const clearFeatureFlags = jest.spyOn(DataService.prototype, 'clearFeatureFlags');

upgradeClient.setFeatureFlagUserGroupsForSession({
groupsForSession: { classId: ['classB'] },
includeStoredUserGroups: false,
});

expect(clearFeatureFlags).toHaveBeenCalled();
clearFeatureFlags.mockRestore();
});

it('should not clear the cached feature flags when the options are invalid', () => {
ApiService.prototype.setFeatureFlagUserGroupsForSession = jest.fn();
const clearFeatureFlags = jest.spyOn(DataService.prototype, 'clearFeatureFlags');

expect(() => {
upgradeClient.setFeatureFlagUserGroupsForSession({ groupsForSession: null, includeStoredUserGroups: false });
}).toThrow();

expect(clearFeatureFlags).not.toHaveBeenCalled();
clearFeatureFlags.mockRestore();
});

it('should cause the next getAllFeatureFlags call to refetch against the new groups', async () => {
// the real DataService cache is left unmocked here, since invalidating it is the behavior under test
ApiService.prototype.setFeatureFlagUserGroupsForSession = jest.fn();
const getAllFeatureFlags = jest
.spyOn(ApiService.prototype, 'getAllFeatureFlags')
.mockResolvedValue(['classAFlag']);

// baseline: the first call fetches and caches, the second is served from the cache
expect(await upgradeClient.getAllFeatureFlags()).toEqual(['classAFlag']);
expect(await upgradeClient.getAllFeatureFlags()).toEqual(['classAFlag']);
expect(getAllFeatureFlags).toHaveBeenCalledTimes(1);

getAllFeatureFlags.mockResolvedValue(['classBFlag']);
upgradeClient.setFeatureFlagUserGroupsForSession({
groupsForSession: { classId: ['classB'] },
includeStoredUserGroups: false,
});

// the cleared cache forces exactly one refetch, whose result becomes the new cached value
expect(await upgradeClient.getAllFeatureFlags()).toEqual(['classBFlag']);
expect(getAllFeatureFlags).toHaveBeenCalledTimes(2);
expect(await upgradeClient.getAllFeatureFlags()).toEqual(['classBFlag']);
expect(getAllFeatureFlags).toHaveBeenCalledTimes(2);

getAllFeatureFlags.mockRestore();
});
});

describe('#getAllExperimentConditions', () => {
Expand Down
9 changes: 8 additions & 1 deletion clientlibs/js/src/UpGradeClient/UpgradeClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,11 @@ export default class UpgradeClient {
* Note: This is a convenience method, this can also be set directly in the constructor of UpgradeClient.
* See example usage in the constructor documentation.
*
* Note: Calling this clears any cached feature flags, since those were resolved against the previous
* groups. The next `getAllFeatureFlags()`/`hasFeatureFlag()` call will refetch against the new groups.
* If you need to retain flags for the previous groups, capture the array returned by
* `getAllFeatureFlags()` before switching.
*
* @example
* ```typescript
*
Expand Down Expand Up @@ -211,6 +216,8 @@ export default class UpgradeClient {
featureFlagOptions?.groupsForSession,
featureFlagOptions?.includeStoredUserGroups
);

this.dataService.clearFeatureFlags();
}

/**
Expand Down Expand Up @@ -515,7 +522,7 @@ export default class UpgradeClient {
*/

async getAllFeatureFlags(options = { ignoreCache: false }): Promise<string[]> {
let response = options.ignoreCache ? null : await this.dataService.getFeatureFlags();
let response = options.ignoreCache ? null : this.dataService.getFeatureFlags();
if (response == null) {
response = await this.apiService.getAllFeatureFlags();
if (Array.isArray(response)) {
Expand Down