Skip to content
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

ref(flags): support single-client tracking in OpenFeatureIntegration #14838

Closed
wants to merge 8 commits into from
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ sentryTest('Basic test with eviction, update, and no async tasks', async ({ getL
await page.goto(url);

await page.evaluate(bufferSize => {
const client = (window as any).initialize();
const client = (window as any).openFeatureClient;
for (let i = 1; i <= bufferSize; i++) {
client.getBooleanValue(`feat${i}`, false);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,25 @@
import * as Sentry from '@sentry/browser';

window.openFeatureClient = {
_hooks: [],

getBooleanValue(flag, value) {
this._hooks.forEach(hook => {
hook.error({ flagKey: flag, defaultValue: false }, new Error('flag eval error'));
});
return value;
},

addHooks(...hooks) {
this._hooks = [...this._hooks, ...hooks];
},
};

window.Sentry = Sentry;
window.sentryOpenFeatureIntegration = Sentry.openFeatureIntegration();
window.sentryOpenFeatureIntegration = Sentry.openFeatureIntegration(window.openFeatureClient);

Sentry.init({
dsn: 'https://[email protected]/1337',
sampleRate: 1.0,
integrations: [window.sentryOpenFeatureIntegration],
});

window.initialize = () => {
return {
getBooleanValue(flag, value) {
let hook = new Sentry.OpenFeatureIntegrationHook();
hook.error({ flagKey: flag, defaultValue: false }, new Error('flag eval error'));
return value;
},
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ sentryTest('Flag evaluation error hook', async ({ getLocalTestUrl, page }) => {
await page.goto(url);

await page.evaluate(bufferSize => {
const client = (window as any).initialize();
const client = (window as any).openFeatureClient;
for (let i = 1; i <= bufferSize; i++) {
client.getBooleanValue(`feat${i}`, false);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,25 @@
import * as Sentry from '@sentry/browser';

window.openFeatureClient = {
_hooks: [],

getBooleanValue(flag, value) {
this._hooks.forEach(hook => {
hook.after(null, { flagKey: flag, value: value });
});
return value;
},

addHooks(...hooks) {
this._hooks = [...this._hooks, ...hooks];
},
};

window.Sentry = Sentry;
window.sentryOpenFeatureIntegration = Sentry.openFeatureIntegration();
window.sentryOpenFeatureIntegration = Sentry.openFeatureIntegration(window.openFeatureClient);

Sentry.init({
dsn: 'https://[email protected]/1337',
sampleRate: 1.0,
integrations: [window.sentryOpenFeatureIntegration],
});

window.initialize = () => {
return {
getBooleanValue(flag, value) {
let hook = new Sentry.OpenFeatureIntegrationHook();
hook.after(null, { flagKey: flag, value: value });
return value;
},
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ sentryTest('Flag evaluations in forked scopes are stored separately.', async ({
await page.evaluate(() => {
const Sentry = (window as any).Sentry;
const errorButton = document.querySelector('#error') as HTMLButtonElement;
const client = (window as any).initialize();
const client = (window as any).openFeatureClient;

client.getBooleanValue('shared', true);

Expand Down
2 changes: 1 addition & 1 deletion packages/browser/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,4 @@ export {
type FeatureFlagsIntegration,
} from './integrations/featureFlags';
export { launchDarklyIntegration, buildLaunchDarklyFlagUsedHandler } from './integrations/featureFlags/launchdarkly';
export { openFeatureIntegration, OpenFeatureIntegrationHook } from './integrations/featureFlags/openfeature';
export { openFeatureIntegration } from './integrations/featureFlags/openfeature';
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export { openFeatureIntegration, OpenFeatureIntegrationHook } from './integration';
export { openFeatureIntegration } from './integration';
Original file line number Diff line number Diff line change
@@ -1,30 +1,48 @@
/**
* OpenFeature integration.
*
* Add the openFeatureIntegration() function call to your integration lists.
* Add the integration hook to your OpenFeature object.
* - OpenFeature.getClient().addHooks(new OpenFeatureIntegrationHook());
*/
import type { Client, Event, EventHint, IntegrationFn } from '@sentry/core';
import type { EvaluationDetails, HookContext, HookHints, JsonValue, OpenFeatureHook } from './types';
import type { EvaluationDetails, HookContext, HookHints, JsonValue, OpenFeatureClient, OpenFeatureHook } from './types';

import { defineIntegration } from '@sentry/core';
import { copyFlagsFromScopeToEvent, insertFlagToScope } from '../../../utils/featureFlags';

export const openFeatureIntegration = defineIntegration(() => {
/**
* Sentry integration for capturing feature flags from the OpenFeature SDK.
*
* See the [feature flag documentation](https://develop.sentry.dev/sdk/expected-features/#feature-flags) for more information.
*
* @example
* ```
* import * as Sentry from '@sentry/browser';
* import { OpenFeature } from '@openfeature/web-sdk';
*
* OpenFeature.setProvider(new MyProviderOfChoice());
* const client = OpenFeature.getClient();
* const openFeatureIntegration = Sentry.openFeatureIntegration({openFeatureClient: client});
*
* Sentry.init({
* dsn: '___PUBLIC_DSN___',
* integrations: [openFeatureIntegration]
* });
* ```
*/
export const openFeatureIntegration = defineIntegration((openFeatureClient: OpenFeatureClient) => {
return {
name: 'OpenFeature',

processEvent(event: Event, _hint: EventHint, _client: Client): Event {
return copyFlagsFromScopeToEvent(event);
},

setupOnce() {
openFeatureClient.addHooks(new OpenFeatureIntegrationHook());
},
};
}) satisfies IntegrationFn;

/**
* OpenFeature Hook class implementation.
* OpenFeatureHook class implementation. Listens for flag evaluations and
* updates the `flags` context in our Sentry scope.
*/
export class OpenFeatureIntegrationHook implements OpenFeatureHook {
class OpenFeatureIntegrationHook implements OpenFeatureHook {
/**
* Successful evaluation result.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,7 @@ export interface BaseHook<T extends FlagValue = FlagValue, BeforeHookReturn = un
finally?(hookContext: Readonly<HookContext<T>>, hookHints?: HookHints): HooksReturn;
}
export type OpenFeatureHook = BaseHook<FlagValue, void, void>;

export interface OpenFeatureClient {
addHooks(...hooks: OpenFeatureHook[]): OpenFeatureClient;
}
Loading