Open thomaspoignant opened 1 month ago
Hello there!
To better understand, are you looking into adding this as an extra initialization option in the JavaScript / TypeScript client provider, or did you have something else in mind?
e.g. something like:
const goFeatureFlagWebProvider = new GoFeatureFlagWebProvider({
endpoint: endpoint,
listener: (key, value) => {
datadogRum.addFeatureFlagEvaluation(key, value);
},
}, logger);
Hello @mbezhanov, Yes, something like that could work. I haven't looked at it in detail yet, but this sounds like an elegant solution.
I'll give it a shot.
/assign-me
👋 Hey @mbezhanov, thanks for your interest in this issue! 🎉
⚠ Note that this issue will become unassigned if it isn't closed within 10 days.
🔧 A maintainer can also add the 📌 Pinned label to prevent it from being unassigned automatically.
Just want to make sure I'm on the right track here. Consider the following potential changes made to the go-feature-flag-web-provider.ts
file in the open-telemetry/js-sdk-contrib
repo: https://github.com/mbezhanov/js-sdk-contrib/commit/9308bff9d37ef187f0d3496b6d9e01bf0e9542cb
This allows us to initialize the provider in the following way:
const goFeatureFlagWebProvider = new GoFeatureFlagWebProvider({
endpoint,
listener: (key, value) => {
datadogRum.addFeatureFlagEvaluation(key, value)
}
}, logger);
Then, if we assume we have the following flags defined in the relay proxy:
some-boolean-flag:
variations:
A: true
B: false
targeting:
- query: ff eq true
percentage:
A: 50
B: 50
defaultRule:
variation: A
some-numeric-flag:
variations:
A: 3
B: 5
C: 7
targeting:
- query: ff eq true
percentage:
A: 30
B: 30
C: 40
defaultRule:
variation: A
some-string-flag:
variations:
A: "foo"
B: "bar"
C: "baz"
targeting:
- query: ff eq true
percentage:
A: 30
B: 30
C: 40
defaultRule:
variation: A
And we perform similar evaluations in our code:
if (client.getBooleanValue('some-boolean-flag', false)) {
// ...
}
if (client.getStringValue('some-string-flag', 'qux')) {
// ...
}
if (client.getNumberValue('some-numeric-flag', 9))) {
// ...
}
The following information gets displayed in Datadog:
Is that sufficient?
That looks great regarding results, but I was wondering if we should not leverage hooks for that.
Having a built-in provider hook can probably make it work. But in the same way I don't want to tight the provider to a Datadog dependency. So I am not 100% sure of the best solution here 🤔
To clarify this a little bit, there is no direct dependency on @datadog/browser-rum in @openfeature/go-feature-flag-web-provider itself.
So in a JS app, the entire code goes somewhat like this:
import {datadogRum} from "@datadog/browser-rum";
import {GoFeatureFlagWebProvider} from "@openfeature/go-feature-flag-web-provider";
import {OpenFeature} from "@openfeature/web-sdk";
// init the RUM browser SDK
datadogRum.init({
// . . .
enableExperimentalFeatures: ['feature_flags'],
});
// . . .
// init the GO Feature Flag web provider
const goFeatureFlagWebProvider = new GoFeatureFlagWebProvider({
// . . .
listener: (key, value) => {
datadogRum.addFeatureFlagEvaluation(key, value)
}
}, logger);
// . . .
// init OpenFeature client
await OpenFeature.setContext(ctx);
OpenFeature.setProvider(goFeatureFlagWebProvider);
const client = OpenFeature.getClient();
// . . .
// evaluate some feature flag
if (client.getBooleanValue('some-boolean-flag', false)) {
// . . .
}
This mirrors some of the other listeners listed on the Datadog integrations page in the documentation.
listener
can effectively be anything:
const goFeatureFlagWebProvider = new GoFeatureFlagWebProvider({
endpoint: endpoint,
listener: (key, value) => {
console.log(key, value)
}
}, logger);
The way I understand it, with hooks we would define a new Hook
class somewhere in our application:
import {EvaluationDetails, FlagValue, Hook, HookContext} from '@openfeature/web-sdk';
import {RumPublicApi} from "@datadog/browser-rum-core";
export class DatadogHook implements Hook {
private _datadogRum;
constructor(datadogRum: RumPublicApi ) {
this._datadogRum = datadogRum;
}
after(hookContext: HookContext, evaluationDetails: EvaluationDetails<FlagValue>) {
this._datadogRum.addFeatureFlagEvaluation(evaluationDetails.flagKey, evaluationDetails.value)
}
}
And then we'll instruct the OpenFeature SDK to use that hook as follows:
import {datadogRum} from "@datadog/browser-rum";
import {DatadogHook} from "./datadog-hook";
import {GoFeatureFlagWebProvider} from "@openfeature/go-feature-flag-web-provider";
import {OpenFeature} from "@openfeature/web-sdk";
// init the RUM browser SDK
datadogRum.init({
// . . .
enableExperimentalFeatures: ['feature_flags'],
});
// . . .
// init the GO Feature Flag web provider
const goFeatureFlagWebProvider = new GoFeatureFlagWebProvider({
// . . .
}, logger);
// . . .
// init OpenFeature client and add hook
await OpenFeature.setContext(ctx);
OpenFeature.setProvider(goFeatureFlagWebProvider);
const client = OpenFeature.getClient();
client.addHooks(new DatadogHook(datadogRum))
// . . .
// evaluate some feature flag
if (client.getBooleanValue('some-boolean-flag', false)) {
// . . .
}
In that case, it doesn't seem that any changes are necessary in the @openfeature/go-feature-flag-web-provider package itself.
Am I understanding this correctly, or did you have something else in mind?
I'll unassign myself for now, but I'll be ready to take over again whenever you need me :slightly_smiling_face:
Requirements
Datadog is now supporting RUM integration for feature flags (https://docs.datadoghq.com/real_user_monitoring/guide/setup-feature-flag-data-collection/?tab=browser).
It will be great to be able to integrate GO Feature Flag with Datadog. This will probably be through an OpenFeature Hook.