Skip to content

Conversation

@yugisu-flux
Copy link

@yugisu-flux yugisu-flux commented Jan 15, 2026

What does this PR do?

  • Implement a Datadog Flags OpenFeature provider for React Native
  • Update the example apps to use the new OpenFeature provider
  • Refactor the Flags SDK: improve the documentation, remove some usage rough edges, update some data models

What's left:

  • Update the Old Architecture example app
  • Add a README to the OpenFeature provider's folder
  • Update LICENSE-3rdparty.csv

Motivation

Datadog Flags SDKs for all platforms are supposed to be consumable through OpenFeature SDKs.

https://datadoghq.atlassian.net/browse/FFL-908

Additional Notes

Anything else we should know when reviewing?

Review checklist (to be filled by reviewers)

  • Feature or bugfix MUST have appropriate tests
  • Make sure you discussed the feature or bugfix with the maintaining team in an Issue
  • Make sure each commit and the PR mention the Issue number (cf the CONTRIBUTING doc)
  • If this PR is auto-generated, please make sure also to manually update the code related to the change

@yugisu-flux yugisu-flux self-assigned this Jan 15, 2026
@yugisu-flux yugisu-flux requested a review from a team as a code owner January 15, 2026 16:41
@yugisu-flux yugisu-flux requested a review from typotter January 15, 2026 16:44
…-implement-flags-react-native-open-feature-provider
Copy link

@typotter typotter left a comment

Choose a reason for hiding this comment

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

Nothing major, but some things to consider. 1 blocker (calling DDFlags.enable in the initialize method)

<ActivityIndicator />
</SafeAreaView>
}>
<OpenFeatureProvider suspendUntilReady>

Choose a reason for hiding this comment

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

🧑‍🍳

<Section title={greetingFlag.value.greeting}>
The title of this section is based on the <Text style={styles.highlight}>{greetingFlag.flagKey}</Text> feature flag.{'\n\n'}

If it's different from "Default greeting", then it is coming from the feature flag evaluation.

Choose a reason for hiding this comment

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

👍
consider adding the details.evaluation_reason here as well.

import type { FlagsClient } from './FlagsClient';

export type DdFlagsType = {
export interface DdFlagsType {

Choose a reason for hiding this comment

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

👍

Comment on lines +109 to +115
export type PrimitiveValue = null | boolean | string | number;
type JsonObject = { [key: string]: JsonValue };
type JsonArray = JsonValue[];
/**
* Represents a JSON node value.
*/
export type JsonValue = PrimitiveValue | JsonObject | JsonArray;

Choose a reason for hiding this comment

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

🧑‍🍳

* This is typically a user ID, session ID, or device ID. The targeting key is used
* by the feature flag service to determine which variation to serve.
*
* Pass an empty string if you don't have such an identifier.

Choose a reason for hiding this comment

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

a null (or undefined) value for the targetingKey is a valid state. Recommending to pass an empty string is good advice, mind you, so I agree with the suggestion.

The nuance is that the targeting key is only needed if there is experimental/traffic sharding in the matching flag allocation. In the future we may also allow for custom targeting using field(s) from the evaluation context.

@@ -0,0 +1,86 @@
{
"name": "@datadog/openfeature-react-native",

Choose a reason for hiding this comment

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

Suggested change
"name": "@datadog/openfeature-react-native",
"name": "@datadog/mobile-react-native-openfeature",

based on the naming pattern of other packages in the repo.

}

async initialize(context: OFEvaluationContext = {}): Promise<void> {
await DdFlags.enable(this.options);

Choose a reason for hiding this comment

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

I don't think init should enable the DDFlags. We are enabling in the sample app, where the other SDK features are also enabled. Trying to initialize before the module is enabled is an error state in the mobile sdk spec. The doc details how to recover/proceed in such cases.

private options: DatadogProviderOptions;
private flagsClient: FlagsClient | undefined;

constructor(options: DatadogProviderOptions = {}) {

Choose a reason for hiding this comment

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

In the android SDK, we wrote a constructor that takes a FlagsClient to wrap instead of passing around the clientName. Not suggesting to change it here, but something to consider

}
}

const toDdContext = (context: OFEvaluationContext): DdEvaluationContext => {

Choose a reason for hiding this comment

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

Is it worth it to add tests for these converters?

* Controls whether the feature flag evaluation feature is enabled.
*/
enabled: boolean;
export interface FlagsConfiguration {

Choose a reason for hiding this comment

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

Breaking change?

Choose a reason for hiding this comment

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

not a breaking change! (flags not merged to develop yet, right?)

reason,
variant,
flagMetadata: allocationKey ? { allocationKey } : undefined,
errorCode: errorCode as ErrorCode | undefined,

Choose a reason for hiding this comment

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

This cast will become unsafe if the DD and OF errorCodes diverge. Consider an explicit converter/map or some other means of type safety

clientName?: string;
}

export class DatadogProvider implements Provider {

Choose a reason for hiding this comment

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

Is there anything to do on shutdown here? The Provider interface also has an onClose method for when the provider is shutdown

Comment on lines 163 to 164
/**
* An error tha occurs during feature flag evaluation.

Choose a reason for hiding this comment

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

(while you're here)

Suggested change
/**
* An error tha occurs during feature flag evaluation.
/**
* An error that occurs during feature flag evaluation.

};
}

export type PrimitiveValue = null | boolean | string | number;

Choose a reason for hiding this comment

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

Do these types need to be exported from the module also?


if (!isPrimitiveValue) {
InternalLog.log(
`Non-primitive context value under "${key}" is not supported. Omitting this atribute from the evaluation context.`,

Choose a reason for hiding this comment

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

Suggested change
`Non-primitive context value under "${key}" is not supported. Omitting this atribute from the evaluation context.`,
`Non-primitive context value under "${key}" is not supported. Omitting this attribute from the evaluation context.`,

newContext: OFEvaluationContext
): Promise<void> {
if (!this.flagsClient) {
throw new Error(

Choose a reason for hiding this comment

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

Should this be an OpenFeature ProviderNotReadyError?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants