-
Notifications
You must be signed in to change notification settings - Fork 60
feat(openfeature): DatadogOfflineOpenFeatureProvider [PR3] (FFL-2689, FFL-2690) #1332
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
Merged
Merged
Changes from all commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
2b8a643
feat(openfeature): add DatadogOfflineOpenFeatureProvider (FFL-2689, F…
btthomas 89849cf
refactor(openfeature): extract DatadogCoreOpenFeatureProvider base
btthomas 43ac1b5
fix(openfeature): address PR3 review (empty-context stamping, events,…
btthomas 7c5daa8
feat(openfeature): add offline provider example to example apps (FFL-…
btthomas 67447e0
refactor(openfeature): ignore mismatched context in offline provider …
btthomas b2be3f3
refactor(openfeature): address PR3 review findings (docs, events, emp…
btthomas c1acba5
fix(openfeature): reject initialize when the loaded offline config is…
btthomas a5a21e8
chore(openfeature): remove provider_set_configuration plan file
btthomas f18e2c9
refactor(openfeature): error on context mismatch in offline provider …
btthomas a3716b4
fix(example): use distinct clientNames for the online and offline pro…
btthomas da4497e
docs(openfeature): clarify offline context recovery, domains, and compat
btthomas 367d9dc
test(openfeature): strengthen offline provider assertions + domain cases
btthomas 4f4f7f5
chore(openfeature): require web-sdk ^1.8.0; tighten offline docs
btthomas a316444
test(openfeature): cover the documented domain setup order
btthomas File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| import React, {useState} from 'react'; | ||
| import { | ||
| View, | ||
| Text, | ||
| Switch, | ||
| ActivityIndicator, | ||
| StyleSheet, | ||
| } from 'react-native'; | ||
|
|
||
| import {setFlagsProvider} from './flagsProvider'; | ||
| import type {FlagsSource} from './flagsProvider'; | ||
|
|
||
| /** | ||
| * A runtime switch between the offline (bundled, no network) and online (CDN) flags | ||
| * providers. Toggling re-sets the OpenFeature provider, which re-renders any `<FeatureFlag>`. | ||
| */ | ||
| export const FlagsSourceToggle = ({ | ||
| initialSource = 'offline', | ||
| }: { | ||
| initialSource?: FlagsSource; | ||
| }) => { | ||
| const [offline, setOffline] = useState(initialSource === 'offline'); | ||
| const [busy, setBusy] = useState(false); | ||
|
|
||
| const onToggle = async (nextOffline: boolean) => { | ||
| setBusy(true); | ||
| try { | ||
| await setFlagsProvider(nextOffline ? 'offline' : 'online'); | ||
| setOffline(nextOffline); | ||
| } finally { | ||
| setBusy(false); | ||
| } | ||
| }; | ||
|
|
||
| return ( | ||
| <View style={styles.container}> | ||
| <Text style={styles.label}> | ||
| Flags source: {offline ? 'offline (bundled)' : 'online (CDN)'} | ||
| </Text> | ||
| <Switch | ||
| accessibilityLabel="flags_source_toggle" | ||
| value={offline} | ||
| onValueChange={onToggle} | ||
| disabled={busy} | ||
| /> | ||
| {busy ? <ActivityIndicator style={styles.spinner} /> : null} | ||
| </View> | ||
| ); | ||
| }; | ||
|
|
||
| const styles = StyleSheet.create({ | ||
| container: { | ||
| flexDirection: 'row', | ||
| alignItems: 'center', | ||
| marginTop: 8, | ||
| }, | ||
| label: { | ||
| marginRight: 10, | ||
| }, | ||
| spinner: { | ||
| marginLeft: 10, | ||
| }, | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| import { | ||
| DatadogOpenFeatureProvider, | ||
| DatadogOfflineOpenFeatureProvider, | ||
| configurationFromString, | ||
| } from '@datadog/mobile-react-native-openfeature'; | ||
| import {OpenFeature} from '@openfeature/react-sdk'; | ||
|
|
||
| import {buildSampleWire} from './sampleOfflineConfiguration'; | ||
|
|
||
| export type FlagsSource = 'online' | 'offline'; | ||
|
|
||
| /** | ||
| * Select which OpenFeature provider backs flag evaluations, and (re)set it at runtime. | ||
| * | ||
| * - `offline`: loads a bundled `ConfigurationWire` into `DatadogOfflineOpenFeatureProvider` | ||
| * **before** setting it, so flags resolve immediately with no network request. | ||
| * - `online`: the standard `DatadogOpenFeatureProvider`, which fetches assignments from the CDN. | ||
| * | ||
| * The two providers use distinct `clientName`s so each is backed by its own `FlagsClient`. | ||
| * Sharing one client across the offline and online modes is unsupported (an online fetch would | ||
| * discard the offline configuration). | ||
| * | ||
| * `DdFlags.enable()` must have been called once before this (it enables the native feature). | ||
| */ | ||
| export const setFlagsProvider = async (source: FlagsSource): Promise<void> => { | ||
| if (source === 'offline') { | ||
| const provider = new DatadogOfflineOpenFeatureProvider({ | ||
| clientName: 'offline', | ||
| }); | ||
| provider.setConfiguration( | ||
| configurationFromString(buildSampleWire()), | ||
| ); | ||
| await OpenFeature.setProviderAndWait(provider); | ||
| return; | ||
| } | ||
|
|
||
| await OpenFeature.setProviderAndWait( | ||
| new DatadogOpenFeatureProvider({clientName: 'online'}), | ||
| ); | ||
| }; |
50 changes: 50 additions & 0 deletions
50
example-new-architecture/flags/sampleOfflineConfiguration.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| // The flag key shared with the online example, so the UI is comparable across providers. | ||
| export const OFFLINE_FLAG_KEY = 'rn-sdk-test-boolean-flag'; | ||
|
|
||
| export type OfflineWireContext = {targetingKey?: string} & Record< | ||
| string, | ||
| string | number | boolean | ||
| >; | ||
|
|
||
| // The evaluation context the bundled configuration is precomputed for. Because the wire | ||
| // carries its own context, the app does not need to call `OpenFeature.setContext` for the | ||
| // offline flow. | ||
| export const DEFAULT_OFFLINE_CONTEXT: OfflineWireContext = { | ||
| targetingKey: 'example-offline-user', | ||
| }; | ||
|
|
||
| /** | ||
| * Build a bundled `ConfigurationWire` v1 string for the offline example. | ||
| * | ||
| * Mirrors the shape the Datadog Flags CDN returns, but is bundled with the app so the demo | ||
| * is fully offline — it never hits the network. Flip `variationValue` to `false` to confirm | ||
| * the flag's fallback renders. | ||
| */ | ||
| export const buildSampleWire = ( | ||
| context: OfflineWireContext = DEFAULT_OFFLINE_CONTEXT, | ||
| variationValue = true, | ||
| ): string => | ||
| JSON.stringify({ | ||
| version: 1, | ||
| precomputed: { | ||
| context, | ||
| response: JSON.stringify({ | ||
| data: { | ||
| attributes: { | ||
| obfuscated: false, | ||
| flags: { | ||
| [OFFLINE_FLAG_KEY]: { | ||
| variationType: 'boolean', | ||
| variationValue, | ||
| variationKey: String(variationValue), | ||
| allocationKey: 'offline-example-alloc', | ||
| reason: 'STATIC', | ||
| doLog: true, | ||
| extraLogging: {}, | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| }), | ||
| }, | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| import React, { useState } from 'react'; | ||
| import { View, Text, Switch, ActivityIndicator, StyleSheet } from 'react-native'; | ||
|
|
||
| import { setFlagsProvider } from '../flags/flagsProvider'; | ||
| import type { FlagsSource } from '../flags/flagsProvider'; | ||
|
|
||
| /** | ||
| * A runtime switch between the offline (bundled, no network) and online (CDN) flags | ||
| * providers. Toggling re-sets the OpenFeature provider, which re-renders any `<FeatureFlag>`. | ||
| */ | ||
| export const FlagsSourceToggle = ({ | ||
| initialSource = 'offline' | ||
| }: { | ||
| initialSource?: FlagsSource; | ||
| }) => { | ||
| const [offline, setOffline] = useState(initialSource === 'offline'); | ||
| const [busy, setBusy] = useState(false); | ||
|
|
||
| const onToggle = async (nextOffline: boolean) => { | ||
| setBusy(true); | ||
| try { | ||
| await setFlagsProvider(nextOffline ? 'offline' : 'online'); | ||
| setOffline(nextOffline); | ||
| } finally { | ||
| setBusy(false); | ||
| } | ||
| }; | ||
|
|
||
| return ( | ||
| <View style={styles.container}> | ||
| <Text style={styles.label}> | ||
| Flags source: {offline ? 'offline (bundled)' : 'online (CDN)'} | ||
| </Text> | ||
| <Switch | ||
| accessibilityLabel="flags_source_toggle" | ||
| value={offline} | ||
| onValueChange={onToggle} | ||
| disabled={busy} | ||
| /> | ||
| {busy ? <ActivityIndicator style={styles.spinner} /> : null} | ||
| </View> | ||
| ); | ||
| }; | ||
|
|
||
| const styles = StyleSheet.create({ | ||
| container: { | ||
| flexDirection: 'row', | ||
| alignItems: 'center', | ||
| marginBottom: 12 | ||
| }, | ||
| label: { | ||
| marginRight: 10 | ||
| }, | ||
| spinner: { | ||
| marginLeft: 10 | ||
| } | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| import { | ||
| DatadogOpenFeatureProvider, | ||
| DatadogOfflineOpenFeatureProvider, | ||
| configurationFromString | ||
| } from '@datadog/mobile-react-native-openfeature'; | ||
| import { OpenFeature } from '@openfeature/react-sdk'; | ||
|
|
||
| import { buildSampleWire } from './sampleOfflineConfiguration'; | ||
| import type { OfflineWireContext } from './sampleOfflineConfiguration'; | ||
|
|
||
| export type FlagsSource = 'online' | 'offline'; | ||
|
|
||
| /** | ||
| * Select which OpenFeature provider backs flag evaluations, and (re)set it at runtime. | ||
| * | ||
| * - `offline`: loads a bundled `ConfigurationWire` into `DatadogOfflineOpenFeatureProvider` | ||
| * **before** setting it, so flags resolve immediately with no network request. | ||
| * - `online`: the standard `DatadogOpenFeatureProvider`, which fetches assignments from the CDN. | ||
| * | ||
| * The two providers use distinct `clientName`s so each is backed by its own `FlagsClient`. | ||
| * Sharing one client across the offline and online modes is unsupported (an online fetch would | ||
| * discard the offline configuration). | ||
| * | ||
| * `DdFlags.enable()` must have been called once before this (it enables the native feature). | ||
| */ | ||
| export const setFlagsProvider = async ( | ||
| source: FlagsSource, | ||
| offlineContext?: OfflineWireContext | ||
| ): Promise<void> => { | ||
| if (source === 'offline') { | ||
| const provider = new DatadogOfflineOpenFeatureProvider({ | ||
| clientName: 'offline' | ||
| }); | ||
| provider.setConfiguration( | ||
| configurationFromString(buildSampleWire(offlineContext)) | ||
| ); | ||
| await OpenFeature.setProviderAndWait(provider); | ||
| return; | ||
| } | ||
|
|
||
| await OpenFeature.setProviderAndWait( | ||
| new DatadogOpenFeatureProvider({ clientName: 'online' }) | ||
| ); | ||
| }; | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps replacing the return with an else would look a bit cleaner.