Skip to content
Merged
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
5 changes: 4 additions & 1 deletion packages/browser/src/openfeature/exposures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,10 @@ export function createExposureLoggingHook(

return {
shutdown: () => exposuresBatch.stop(),
after: (hookContext: HookContext, details: EvaluationDetails<FlagValue>) => {
finally: (hookContext: HookContext, details: EvaluationDetails<FlagValue>) => {
if (details.errorCode || details.reason === 'ERROR' || details.variant == null) {
return
}
const timestamp = timeStampNow()
const exposureEvent = createExposureEvent(getEvaluationContext(hookContext.context), details)
if (!exposureEvent) {
Expand Down
19 changes: 16 additions & 3 deletions packages/browser/src/openfeature/flagEvaluations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,13 @@ import {
Observable,
} from '@datadog/browser-core'
import { FlagEvaluationAggregator, type FlagEvaluationEvent } from '@datadog/flagging-core'
import type { EvaluationContext, EvaluationDetails, FlagValue, HookContext } from '@openfeature/web-sdk'
import {
ErrorCode,
type EvaluationContext,
type EvaluationDetails,
type FlagValue,
type HookContext,
} from '@openfeature/web-sdk'
import type { FlaggingTrackingConfiguration } from '../domain/configuration'
import { validateAndBuildFlaggingTrackingConfiguration } from '../domain/configuration'
import type { DatadogTrackingHooks, DatadogTrackingHooksOptions, ManagedTrackingHook } from './tracking'
Expand Down Expand Up @@ -77,9 +83,16 @@ export function createFlagEvalEVPHook(
flagEvaluationBatch.stop()
}
},
after: (hookContext: HookContext, details: EvaluationDetails<FlagValue>) => {
// Failed evaluations skip `after`; `finally` receives the result returned to the application.
finally: (hookContext: HookContext, details: EvaluationDetails<FlagValue>) => {
try {
aggregator.addEvaluation(getEvaluationContext(hookContext.context), details)
// OpenFeature can copy a customer hook's error.code without validation.
Comment thread
sameerank marked this conversation as resolved.
// Restrict telemetry to standard OpenFeature error codes.
const errorCode =
details.errorCode === undefined || Object.values(ErrorCode).includes(details.errorCode)
? details.errorCode
: ErrorCode.GENERAL
aggregator.addEvaluation(getEvaluationContext(hookContext.context), details, errorCode)
} catch (error) {
addTelemetryDebug('Error adding evaluation to aggregator', {
'error.message': error instanceof Error ? error.message : String(error),
Expand Down
6 changes: 3 additions & 3 deletions packages/browser/src/openfeature/provider-tracking.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,14 @@ export function createProviderTracking({
}

function withTrackingContext(hook: Hook, getTrackingContext: (context: EvaluationContext) => EvaluationContext): Hook {
if (!hook.after) {
if (!hook.finally) {
return hook
}

return {
...hook,
after: (hookContext, details, hookHints) =>
hook.after?.(
finally: (hookContext, details, hookHints) =>
hook.finally?.(
{
...hookContext,
context: getTrackingContext(hookContext.context),
Expand Down
9 changes: 6 additions & 3 deletions packages/browser/src/openfeature/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,9 @@ export class DatadogProvider extends DatadogProviderBase {
// result before calling the resolver and subsequent hooks. Return this stored context, not a
// fresh RUM lookup, so targeting, flag configuration, and telemetry stay on the same identity.
/** Effective context associated with the active flags configuration. */
private evaluationContext: EvaluationContext = {}
private evaluationContext: EvaluationContext | undefined
/** Request context used for tracking until a configuration has been accepted. */
private requestedEvaluationContext: EvaluationContext = {}

status: ProviderStatus

Expand Down Expand Up @@ -108,7 +110,7 @@ export class DatadogProvider extends DatadogProviderBase {
options,
configuration: this.configuration,
enabledByDefault: true,
getTrackingContext: () => this.evaluationContext,
getTrackingContext: () => this.evaluationContext ?? this.requestedEvaluationContext,
})
this.hooks = this.tracking.hooks
this.exposureCache = this.tracking.exposureCache
Expand Down Expand Up @@ -140,6 +142,7 @@ export class DatadogProvider extends DatadogProviderBase {

private setContext(context: EvaluationContext): Promise<void> {
const evaluationContext = this.isRumIntegrationEnabled ? enrichEvaluationContextWithRumUser(context) : context
this.requestedEvaluationContext = evaluationContext

if (this.status === ProviderStatus.NOT_READY) {
// we're initializing, no status changes necessary
Expand Down Expand Up @@ -280,7 +283,7 @@ export class DatadogProvider extends DatadogProviderBase {
type,
flagKey,
defaultValue,
this.evaluationContext
this.evaluationContext ?? {}
)
}
}
4 changes: 2 additions & 2 deletions packages/browser/src/openfeature/rumIntegration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ export function enrichEvaluationContextWithRumUser(context: EvaluationContext):

export function createRumTrackingHook(): Hook {
return {
after: (_hookContext: HookContext, details: EvaluationDetails<FlagValue>) => {
if (details.variant == null) {
finally: (_hookContext: HookContext, details: EvaluationDetails<FlagValue>) => {
if (details.errorCode || details.reason === 'ERROR' || details.variant == null) {
return
}
const globalObject = getGlobalObject<{ DD_RUM?: DDRum }>()
Expand Down
2 changes: 1 addition & 1 deletion packages/browser/src/openfeature/tracking.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export function createTrackingHookController(
let lifecycle = Promise.resolve()

return {
hooks: [{ after: (...args) => activeHook?.after?.(...args) }],
hooks: [{ finally: (...args) => activeHook?.finally?.(...args) }],
initialize: () => {
// Serialize setup and teardown so shutdown also waits for a pending cache read.
lifecycle = lifecycle.then(() =>
Expand Down
Loading
Loading