diff --git a/packages/shared/openfeature-server-common/src/BaseOpenFeatureProvider.ts b/packages/shared/openfeature-server-common/src/BaseOpenFeatureProvider.ts index afcabe3045..7c24ec9c19 100644 --- a/packages/shared/openfeature-server-common/src/BaseOpenFeatureProvider.ts +++ b/packages/shared/openfeature-server-common/src/BaseOpenFeatureProvider.ts @@ -25,6 +25,8 @@ import { translateTrackingEventDetails } from './translateTrackingEventDetails'; /** * Create a ResolutionDetails for an evaluation that produced a type different * than the expected type. + * @param value The default value to populate the ResolutionDetails with. + * @returns A ResolutionDetails with the default value. */ function wrongTypeResult(value: T): ResolutionDetails { return { @@ -56,10 +58,22 @@ export interface BaseProviderConfig { export abstract class BaseOpenFeatureProvider< TClient extends OpenFeatureLDClientContract = OpenFeatureLDClientContract, > implements Provider { + /** + * Metadata reported to OpenFeature. The provider name is supplied by each + * concrete subclass via the {@link BaseProviderConfig.providerName} field. + */ readonly metadata: ProviderMetadata; + /** + * The OpenFeature paradigm this provider runs on. LaunchDarkly server-side + * SDKs always run on the server. + */ readonly runsOn: Paradigm = 'server'; + /** + * Event emitter used to surface OpenFeature provider events such as + * {@link ProviderEvents.ConfigurationChanged}. + */ readonly events = new OpenFeatureEventEmitter(); private _client?: TClient; @@ -102,6 +116,11 @@ export abstract class BaseOpenFeatureProvider< }); } + /** + * Called by OpenFeature once the provider is registered. Waits for the + * underlying LDClient to finish initialization, or rethrows the error that + * was captured if client construction failed. + */ // eslint-disable-next-line @typescript-eslint/no-unused-vars async initialize(context?: EvaluationContext): Promise { if (!this._client) { @@ -113,6 +132,19 @@ export abstract class BaseOpenFeatureProvider< await this._client.waitForInitialization({ timeout: this._initTimeoutSeconds }); } + /** + * Determines the boolean variation of a feature flag for a context, along with information about + * how it was calculated. + * + * If the flag does not evaluate to a boolean value, then the defaultValue will be returned. + * + * @param flagKey The unique key of the feature flag. + * @param defaultValue The default value of the flag, to be used if the value is not available + * from LaunchDarkly. + * @param context The context requesting the flag. The client will generate an analytics event to + * register this context with LaunchDarkly if the context does not already exist. + * @returns A promise which will resolve to a ResolutionDetails. + */ async resolveBooleanEvaluation( flagKey: string, defaultValue: boolean, @@ -129,6 +161,19 @@ export abstract class BaseOpenFeatureProvider< return wrongTypeResult(defaultValue); } + /** + * Determines the string variation of a feature flag for a context, along with information about + * how it was calculated. + * + * If the flag does not evaluate to a string value, then the defaultValue will be returned. + * + * @param flagKey The unique key of the feature flag. + * @param defaultValue The default value of the flag, to be used if the value is not available + * from LaunchDarkly. + * @param context The context requesting the flag. The client will generate an analytics event to + * register this context with LaunchDarkly if the context does not already exist. + * @returns A promise which will resolve to a ResolutionDetails. + */ async resolveStringEvaluation( flagKey: string, defaultValue: string, @@ -145,6 +190,19 @@ export abstract class BaseOpenFeatureProvider< return wrongTypeResult(defaultValue); } + /** + * Determines the numeric variation of a feature flag for a context, along with information about + * how it was calculated. + * + * If the flag does not evaluate to a numeric value, then the defaultValue will be returned. + * + * @param flagKey The unique key of the feature flag. + * @param defaultValue The default value of the flag, to be used if the value is not available + * from LaunchDarkly. + * @param context The context requesting the flag. The client will generate an analytics event to + * register this context with LaunchDarkly if the context does not already exist. + * @returns A promise which will resolve to a ResolutionDetails. + */ async resolveNumberEvaluation( flagKey: string, defaultValue: number, @@ -161,6 +219,19 @@ export abstract class BaseOpenFeatureProvider< return wrongTypeResult(defaultValue); } + /** + * Determines the object variation of a feature flag for a context, along with information about + * how it was calculated. + * + * If the flag does not evaluate to a JSON object value, then the defaultValue will be returned. + * + * @param flagKey The unique key of the feature flag. + * @param defaultValue The default value of the flag, to be used if the value is not available + * from LaunchDarkly. + * @param context The context requesting the flag. The client will generate an analytics event to + * register this context with LaunchDarkly if the context does not already exist. + * @returns A promise which will resolve to a ResolutionDetails. + */ async resolveObjectEvaluation( flagKey: string, defaultValue: U, @@ -177,14 +248,28 @@ export abstract class BaseOpenFeatureProvider< return wrongTypeResult(defaultValue); } + /** + * Returns the OpenFeature hooks registered for this provider. The + * LaunchDarkly provider does not currently register any provider-level + * hooks, so this always returns an empty array. + */ get hooks(): Hook[] { return []; } + /** + * Get the LDClient instance used by this provider. + * + * @returns The client for this provider. + */ getClient(): TClient { return this._client!; } + /** + * Called by OpenFeature when it needs to close the provider. This will flush + * events from the LDClient and then close it. + */ async onClose(): Promise { try { await this._client?.flush(); @@ -193,6 +278,13 @@ export abstract class BaseOpenFeatureProvider< } } + /** + * Track a user action or application state, usually representing a business objective or outcome. + * @param trackingEventName The name of the event, which may correspond to a metric + * in Experimentation. + * @param context The context to track. + * @param trackingEventDetails Optional additional information to associate with the event. + */ track( trackingEventName: string, context: EvaluationContext, diff --git a/packages/shared/openfeature-server-common/src/OpenFeatureLDClientContract.ts b/packages/shared/openfeature-server-common/src/OpenFeatureLDClientContract.ts index 96e3290eb0..e71a37f178 100644 --- a/packages/shared/openfeature-server-common/src/OpenFeatureLDClientContract.ts +++ b/packages/shared/openfeature-server-common/src/OpenFeatureLDClientContract.ts @@ -5,17 +5,49 @@ import type { LDContext, LDEvaluationDetail, LDFlagValue } from '@launchdarkly/j * Any LaunchDarkly server-side LDClient should satisfy this interface. */ export interface OpenFeatureLDClientContract { + /** + * Evaluate a flag and return the value along with details of how it was + * calculated. + * + * @param key The unique key of the feature flag. + * @param context The context to evaluate the flag against. + * @param defaultValue The value to return if the flag cannot be evaluated. + * @returns A promise which resolves to the evaluation detail. + */ variationDetail( key: string, context: LDContext, defaultValue: LDFlagValue, ): Promise; + /** + * Wait for the underlying client to finish initialization. + * + * @param options Optional initialization options. The `timeout` field is + * the maximum number of seconds to wait. + */ waitForInitialization(options?: { timeout: number }): Promise; + /** + * Flush any pending analytics events to LaunchDarkly. Called by the provider + * during {@link Provider.onClose}. + */ flush(): Promise; + /** + * Shut down the client and release any resources it holds. Called by the + * provider during {@link Provider.onClose} after {@link flush}. + */ close(): void; + /** + * Track a custom event for the given context. + * + * @param key The name of the event. + * @param context The context to associate with the event. + * @param data Optional additional information to attach to the event. + * @param metricValue Optional numeric metric value to associate with the + * event. + */ track(key: string, context: LDContext, data?: any, metricValue?: number): void; } diff --git a/packages/shared/openfeature-server-common/src/translateContext.ts b/packages/shared/openfeature-server-common/src/translateContext.ts index e194ab937c..b2a0f29fea 100644 --- a/packages/shared/openfeature-server-common/src/translateContext.ts +++ b/packages/shared/openfeature-server-common/src/translateContext.ts @@ -104,6 +104,7 @@ function translateContextCommon( /** * Convert an OpenFeature evaluation context into an LDContext. + * @param logger Logger to use when warnings or errors are encountered during translation. * @param evalContext The OpenFeature evaluation context to translate. * @returns An LDContext based on the evaluation context. */