Skip to content

Commit

Permalink
fix: wrong context passed to named providers (#752)
Browse files Browse the repository at this point in the history
Fixed an issue with implementations of
#704 where the wrong context
was passed to namespaced providers.

---------

Signed-off-by: Todd Baert <[email protected]>
  • Loading branch information
toddbaert authored Jan 11, 2024
1 parent 072d2c7 commit b6adbba
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 5 deletions.
2 changes: 1 addition & 1 deletion packages/client/src/client/open-feature-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ export class OpenFeatureClient implements Client {
const allHooksReversed = [...allHooks].reverse();

const context = {
...OpenFeature.getContext(),
...OpenFeature.getContext(this?.options?.name),
};

// this reference cannot change during the course of evaluation
Expand Down
2 changes: 1 addition & 1 deletion packages/client/src/open-feature.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ export class OpenFeatureAPI extends OpenFeatureCommonAPI<Provider, Hook> impleme
* @param {string} clientName The name to identify the client
* @returns {EvaluationContext} Evaluation context
*/
getContext(clientName: string): EvaluationContext;
getContext(clientName?: string): EvaluationContext;
getContext(nameOrUndefined?: string): EvaluationContext {
const clientName = stringOrUndefined(nameOrUndefined);
if (clientName) {
Expand Down
60 changes: 57 additions & 3 deletions packages/client/test/evaluation-context.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,12 @@ class MockProvider implements Provider {
return Promise.resolve();
}

resolveBooleanEvaluation(): ResolutionDetails<boolean> {
throw new Error('Not implemented');
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
resolveBooleanEvaluation = jest.fn((flagKey: string, defaultValue: boolean, context: EvaluationContext) => {
return {
value: true
};
});

resolveNumberEvaluation(): ResolutionDetails<number> {
throw new Error('Not implemented');
Expand Down Expand Up @@ -76,6 +79,57 @@ describe('Evaluation Context', () => {
expect(OpenFeature.getContext(clientName)).toEqual(globalContext);
});

it('should call onContextChange for appropriate provider with appropriate context', async () => {
const globalContext: EvaluationContext = { scope: 'global' };
const testContext: EvaluationContext = { scope: 'test' };
const clientName = 'appropriateProviderTest';
const defaultProvider = new MockProvider();
const provider1 = new MockProvider();

await OpenFeature.setProviderAndWait(defaultProvider);
await OpenFeature.setProviderAndWait(clientName, provider1);

// Spy on context changed handlers of both providers
const defaultProviderSpy = jest.spyOn(defaultProvider, 'onContextChange');
const provider1Spy = jest.spyOn(provider1, 'onContextChange');

await OpenFeature.setContext(globalContext);
await OpenFeature.setContext(clientName, testContext);

// provider one should get global and specific context calls
expect(defaultProviderSpy).toHaveBeenCalledWith({}, globalContext);
expect(provider1Spy).toHaveBeenCalledWith(globalContext, testContext);
});

it('should pass correct context to resolver', async () => {
const globalContext: EvaluationContext = { scope: 'global' };
const testContext: EvaluationContext = { scope: 'test' };
const clientName = 'correctContextTest';
const defaultProvider = new MockProvider();
const provider1 = new MockProvider();

await OpenFeature.setProviderAndWait(defaultProvider);
await OpenFeature.setProviderAndWait(clientName, provider1);

// Spy on boolean resolvers of both providers
const defaultProviderSpy = jest.spyOn(defaultProvider, 'resolveBooleanEvaluation');
const provider1Spy = jest.spyOn(provider1, 'resolveBooleanEvaluation');

await OpenFeature.setContext(globalContext);
await OpenFeature.setContext(clientName, testContext);

const defaultClient = OpenFeature.getClient();
const provider1Client = OpenFeature.getClient(clientName);

const flagName = 'some-flag';
defaultClient.getBooleanValue(flagName, false);
provider1Client.getBooleanValue(flagName, false);

// provider one should get global and specific context
expect(defaultProviderSpy).toHaveBeenCalledWith(flagName, false, globalContext, expect.anything());
expect(provider1Spy).toHaveBeenCalledWith(flagName, false, testContext, expect.anything());
});

it('should only call a providers onContextChange once when clearing context', async () => {
const globalContext: EvaluationContext = { scope: 'global' };
const testContext: EvaluationContext = { scope: 'test' };
Expand Down

0 comments on commit b6adbba

Please sign in to comment.