Skip to content

Commit 260432c

Browse files
authored
feat!: remove ctx transformer, add provider hooks (open-feature#148)
Add provider hooks, update ordering BREAKING CHANGE: context transformer and related interfaces removed. Signed-off-by: Todd Baert <[email protected]> Signed-off-by: Todd Baert <[email protected]>
1 parent fde134f commit 260432c

File tree

5 files changed

+689
-613
lines changed

5 files changed

+689
-613
lines changed

src/client.ts

Lines changed: 9 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ import {
1010
FlagValueType,
1111
Hook,
1212
HookContext,
13+
Provider,
1314
ResolutionDetails,
14-
TransformingProvider,
1515
} from './types';
1616

1717
type OpenFeatureClientOptions = {
@@ -27,7 +27,7 @@ export class OpenFeatureClient implements Client {
2727
constructor(
2828
// we always want the client to use the current provider,
2929
// so pass a function to always access the currently registered one.
30-
private readonly providerAccessor: () => TransformingProvider<unknown>,
30+
private readonly providerAccessor: () => Provider,
3131
options: OpenFeatureClientOptions,
3232
context: EvaluationContext = {}
3333
) {
@@ -148,7 +148,7 @@ export class OpenFeatureClient implements Client {
148148
resolver: (
149149
flagKey: string,
150150
defaultValue: T,
151-
transformedContext: unknown,
151+
context: EvaluationContext,
152152
options: FlagEvaluationOptions | undefined
153153
) => Promise<ResolutionDetails<T>>,
154154
defaultValue: T,
@@ -158,13 +158,14 @@ export class OpenFeatureClient implements Client {
158158
): Promise<EvaluationDetails<T>> {
159159
// merge global, client, and evaluation context
160160

161-
const allHooks = [...OpenFeature.hooks, ...this.hooks, ...(options.hooks || [])];
161+
const allHooks = [...OpenFeature.hooks, ...this.hooks, ...(options.hooks || []), ...(this.provider.hooks || [])];
162162
const allHooksReversed = [...allHooks].reverse();
163163

164164
// merge global and client contexts
165-
const globalAndClientContext = {
165+
const mergedContext = {
166166
...OpenFeature.context,
167167
...this.context,
168+
...invocationContext,
168169
};
169170

170171
// this reference cannot change during the course of evaluation
@@ -175,26 +176,14 @@ export class OpenFeatureClient implements Client {
175176
flagValueType: flagType,
176177
clientMetadata: this.metadata,
177178
providerMetadata: OpenFeature.providerMetadata,
178-
context: globalAndClientContext,
179+
context: mergedContext,
179180
};
180181

181182
try {
182-
const mergedHookContext = await this.beforeHooks(allHooks, hookContext, options);
183-
184-
// merge context in order: global, client, hook, invocation
185-
const mergedContext = {
186-
...mergedHookContext,
187-
...invocationContext,
188-
};
189-
190-
// if a transformer is defined, run it to prepare the context
191-
const transformedContext =
192-
typeof this.provider.contextTransformer === 'function'
193-
? await this.provider.contextTransformer(mergedContext)
194-
: mergedContext;
183+
const frozenContext = await this.beforeHooks(allHooks, hookContext, options);
195184

196185
// run the referenced resolver, binding the provider.
197-
const resolution = await resolver.call(this.provider, flagKey, defaultValue, transformedContext, options);
186+
const resolution = await resolver.call(this.provider, flagKey, defaultValue, frozenContext, options);
198187

199188
const evaluationDetails = {
200189
...resolution,

src/open-feature.ts

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,6 @@
11
import { OpenFeatureClient } from './client';
22
import { NOOP_PROVIDER } from './no-op-provider';
3-
import {
4-
Client,
5-
EvaluationContext,
6-
EvaluationLifeCycle,
7-
FlagValue,
8-
Hook,
9-
NonTransformingProvider,
10-
Provider,
11-
TransformingProvider,
12-
} from './types';
3+
import { Client, EvaluationContext, EvaluationLifeCycle, FlagValue, Hook, Provider } from './types';
134

145
// use a symbol as a key for the global singleton
156
const GLOBAL_OPENFEATURE_API_KEY = Symbol.for('@openfeature/js.api');
@@ -36,7 +27,7 @@ class OpenFeatureAPI implements EvaluationLifeCycle {
3627
}
3728

3829
getClient(name?: string, version?: string, context?: EvaluationContext): Client {
39-
return new OpenFeatureClient(() => this._provider as TransformingProvider<unknown>, { name, version }, context);
30+
return new OpenFeatureClient(() => this._provider, { name, version }, context);
4031
}
4132

4233
get providerMetadata() {
@@ -51,7 +42,7 @@ class OpenFeatureAPI implements EvaluationLifeCycle {
5142
return this._hooks;
5243
}
5344

54-
setProvider(provider: TransformingProvider<unknown> | NonTransformingProvider) {
45+
setProvider(provider: Provider) {
5546
this._provider = provider;
5647
}
5748

src/types.ts

Lines changed: 13 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -104,11 +104,12 @@ export interface Features {
104104
}
105105

106106
/**
107-
* Function which transforms the EvaluationContext to a type useful for the provider.
107+
* Interface that providers must implement to resolve flag values for their particular
108+
* backend or vendor.
109+
*
110+
* Implementation for resolving all the required flag types must be defined.
108111
*/
109-
export type ContextTransformer<T = unknown> = (context: EvaluationContext) => T;
110-
111-
interface GenericProvider<T> {
112+
export interface Provider extends Pick<Partial<EvaluationLifeCycle>, 'hooks'> {
112113
readonly metadata: ProviderMetadata;
113114

114115
/**
@@ -117,7 +118,7 @@ interface GenericProvider<T> {
117118
resolveBooleanEvaluation(
118119
flagKey: string,
119120
defaultValue: boolean,
120-
transformedContext: T,
121+
context: EvaluationContext,
121122
options: FlagEvaluationOptions | undefined
122123
): Promise<ResolutionDetails<boolean>>;
123124

@@ -127,7 +128,7 @@ interface GenericProvider<T> {
127128
resolveStringEvaluation(
128129
flagKey: string,
129130
defaultValue: string,
130-
transformedContext: T,
131+
context: EvaluationContext,
131132
options: FlagEvaluationOptions | undefined
132133
): Promise<ResolutionDetails<string>>;
133134

@@ -137,7 +138,7 @@ interface GenericProvider<T> {
137138
resolveNumberEvaluation(
138139
flagKey: string,
139140
defaultValue: number,
140-
transformedContext: T,
141+
context: EvaluationContext,
141142
options: FlagEvaluationOptions | undefined
142143
): Promise<ResolutionDetails<number>>;
143144

@@ -147,40 +148,17 @@ interface GenericProvider<T> {
147148
resolveObjectEvaluation<U extends object>(
148149
flagKey: string,
149150
defaultValue: U,
150-
transformedContext: T,
151+
context: EvaluationContext,
151152
options: FlagEvaluationOptions | undefined
152153
): Promise<ResolutionDetails<U>>;
153154
}
154155

155-
export type NonTransformingProvider = GenericProvider<EvaluationContext>;
156-
157-
export interface TransformingProvider<T> extends GenericProvider<T> {
158-
contextTransformer: ContextTransformer<Promise<T> | T> | undefined;
159-
}
160-
161-
/**
162-
* Interface that providers must implement to resolve flag values for their particular
163-
* backend or vendor.
164-
*
165-
* Implementation for resolving all the required flag types must be defined.
166-
*
167-
* Additionally, a ContextTransformer function that transforms the OpenFeature context to the requisite user/context/attribute representation (typeof T)
168-
* may also be implemented. This function will run immediately before the flag value resolver functions, appropriately transforming the context.
169-
*/
170-
export type Provider<T extends EvaluationContext | unknown = EvaluationContext> = T extends EvaluationContext
171-
? NonTransformingProvider
172-
: TransformingProvider<T>;
173-
174156
export interface EvaluationLifeCycle {
175157
addHooks(...hooks: Hook[]): void;
176158
get hooks(): Hook[];
177159
clearHooks(): void;
178160
}
179161

180-
export interface ProviderOptions<T = unknown> {
181-
contextTransformer?: ContextTransformer<T>;
182-
}
183-
184162
export enum StandardResolutionReasons {
185163
/**
186164
* Indicates that the feature flag is targeting
@@ -204,20 +182,20 @@ export enum StandardResolutionReasons {
204182
* similar functions in the Client */
205183
DEFAULT = 'DEFAULT',
206184
/**
207-
* Indicates that the feature flag evaluated to a
185+
* Indicates that the feature flag evaluated to a
208186
* static value, for example, the default value for the flag
209-
*
187+
*
210188
* Note: Typically means that no dynamic evaluation has been
211189
* executed for the feature flag
212190
*/
213-
STATIC = 'STATIC',
191+
STATIC = 'STATIC',
214192
/**
215193
* Indicates an unknown issue occurred during evaluation
216194
*/
217195
UNKNOWN = 'UNKNOWN',
218196
/**
219197
* Indicates that an error occurred during evaluation
220-
*
198+
*
221199
* Note: The `errorCode`-field contains the details of this error
222200
*/
223201
ERROR = 'ERROR',

0 commit comments

Comments
 (0)