Skip to content

Commit e15bf8c

Browse files
chore: Refactor SDK structure (#473)
<!-- Please use this template for your pull request. --> <!-- Please use the sections that you need and delete other sections --> ## This PR Splits up the types file and restructures the SDK by functionalities. Feeling quite good with the structure now. The PR became quite huge, but I could even move one or two more things (for the client) to the shared implementation. @toddbaert @beeme1mr Please give feedback if you think the structure is completely off or something seems odd to you🙂 ### Related Issues <!-- add here the GitHub issue that this PR resolves if applicable --> Fixes #437 Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
1 parent 4f86c01 commit e15bf8c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+1050
-1165
lines changed

Diff for: packages/client/src/client/client.ts

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { ClientMetadata, EvaluationLifeCycle, Eventing, ManageLogger } from '@openfeature/shared';
2+
import { Features } from '../evaluation';
3+
4+
export interface Client extends EvaluationLifeCycle<Client>, Features, ManageLogger<Client>, Eventing {
5+
readonly metadata: ClientMetadata;
6+
}

Diff for: packages/client/src/client/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from './client';
2+
export * from './open-feature-client';

Diff for: packages/client/src/client.ts renamed to packages/client/src/client/open-feature-client.ts

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { Client } from './client';
2+
import { Provider } from '../provider';
13
import {
24
ClientMetadata,
35
ErrorCode,
@@ -6,6 +8,7 @@ import {
68
EventHandler,
79
FlagValue,
810
FlagValueType,
11+
Hook,
912
HookContext,
1013
JsonValue,
1114
Logger,
@@ -17,8 +20,8 @@ import {
1720
SafeLogger,
1821
StandardResolutionReasons,
1922
} from '@openfeature/shared';
20-
import { OpenFeature } from './open-feature';
21-
import { Client, FlagEvaluationOptions, Hook, Provider } from './types';
23+
import { OpenFeature } from '../open-feature';
24+
import { FlagEvaluationOptions } from '../evaluation';
2225

2326
type OpenFeatureClientOptions = {
2427
name?: string;

Diff for: packages/client/src/evaluation/evaluation.ts

+118
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
import { EvaluationDetails, Hook, HookHints, JsonValue } from '@openfeature/shared';
2+
3+
export interface FlagEvaluationOptions {
4+
hooks?: Hook[];
5+
hookHints?: HookHints;
6+
}
7+
8+
export interface Features {
9+
/**
10+
* Performs a flag evaluation that returns a boolean.
11+
* @param {string} flagKey The flag key uniquely identifies a particular flag
12+
* @param {boolean} defaultValue The value returned if an error occurs
13+
* @param {FlagEvaluationOptions} options Additional flag evaluation options
14+
* @returns {boolean} Flag evaluation response
15+
*/
16+
getBooleanValue(flagKey: string, defaultValue: boolean, options?: FlagEvaluationOptions): boolean;
17+
18+
/**
19+
* Performs a flag evaluation that a returns an evaluation details object.
20+
* @param {string} flagKey The flag key uniquely identifies a particular flag
21+
* @param {boolean} defaultValue The value returned if an error occurs
22+
* @param {FlagEvaluationOptions} options Additional flag evaluation options
23+
* @returns {EvaluationDetails<boolean>} Flag evaluation details response
24+
*/
25+
getBooleanDetails(
26+
flagKey: string,
27+
defaultValue: boolean,
28+
options?: FlagEvaluationOptions
29+
): EvaluationDetails<boolean>;
30+
31+
/**
32+
* Performs a flag evaluation that returns a string.
33+
* @param {string} flagKey The flag key uniquely identifies a particular flag
34+
* @template {string} T A optional generic argument constraining the string
35+
* @param {T} defaultValue The value returned if an error occurs
36+
* @param {FlagEvaluationOptions} options Additional flag evaluation options
37+
* @returns {T} Flag evaluation response
38+
*/
39+
getStringValue(flagKey: string, defaultValue: string, options?: FlagEvaluationOptions): string;
40+
41+
getStringValue<T extends string = string>(flagKey: string, defaultValue: T, options?: FlagEvaluationOptions): T;
42+
43+
/**
44+
* Performs a flag evaluation that a returns an evaluation details object.
45+
* @param {string} flagKey The flag key uniquely identifies a particular flag
46+
* @template {string} T A optional generic argument constraining the string
47+
* @param {T} defaultValue The value returned if an error occurs
48+
* @param {FlagEvaluationOptions} options Additional flag evaluation options
49+
* @returns {EvaluationDetails<T>} Flag evaluation details response
50+
*/
51+
getStringDetails(flagKey: string, defaultValue: string, options?: FlagEvaluationOptions): EvaluationDetails<string>;
52+
53+
getStringDetails<T extends string = string>(
54+
flagKey: string,
55+
defaultValue: T,
56+
options?: FlagEvaluationOptions
57+
): EvaluationDetails<T>;
58+
59+
/**
60+
* Performs a flag evaluation that returns a number.
61+
* @param {string} flagKey The flag key uniquely identifies a particular flag
62+
* @template {number} T A optional generic argument constraining the number
63+
* @param {T} defaultValue The value returned if an error occurs
64+
* @param {FlagEvaluationOptions} options Additional flag evaluation options
65+
* @returns {T} Flag evaluation response
66+
*/
67+
getNumberValue(flagKey: string, defaultValue: number, options?: FlagEvaluationOptions): number;
68+
69+
getNumberValue<T extends number = number>(flagKey: string, defaultValue: T, options?: FlagEvaluationOptions): T;
70+
71+
/**
72+
* Performs a flag evaluation that a returns an evaluation details object.
73+
* @param {string} flagKey The flag key uniquely identifies a particular flag
74+
* @template {number} T A optional generic argument constraining the number
75+
* @param {T} defaultValue The value returned if an error occurs
76+
* @param {FlagEvaluationOptions} options Additional flag evaluation options
77+
* @returns {Promise<EvaluationDetails<T>>} Flag evaluation details response
78+
*/
79+
getNumberDetails(flagKey: string, defaultValue: number, options?: FlagEvaluationOptions): EvaluationDetails<number>;
80+
81+
getNumberDetails<T extends number = number>(
82+
flagKey: string,
83+
defaultValue: T,
84+
options?: FlagEvaluationOptions
85+
): EvaluationDetails<T>;
86+
87+
/**
88+
* Performs a flag evaluation that returns an object.
89+
* @param {string} flagKey The flag key uniquely identifies a particular flag
90+
* @template {JsonValue} T A optional generic argument describing the structure
91+
* @param {T} defaultValue The value returned if an error occurs
92+
* @param {FlagEvaluationOptions} options Additional flag evaluation options
93+
* @returns {Promise<T>} Flag evaluation response
94+
*/
95+
getObjectValue(flagKey: string, defaultValue: JsonValue, options?: FlagEvaluationOptions): JsonValue;
96+
97+
getObjectValue<T extends JsonValue = JsonValue>(flagKey: string, defaultValue: T, options?: FlagEvaluationOptions): T;
98+
99+
/**
100+
* Performs a flag evaluation that a returns an evaluation details object.
101+
* @param {string} flagKey The flag key uniquely identifies a particular flag
102+
* @template {JsonValue} T A optional generic argument describing the structure
103+
* @param {T} defaultValue The value returned if an error occurs
104+
* @param {FlagEvaluationOptions} options Additional flag evaluation options
105+
* @returns {Promise<EvaluationDetails<T>>} Flag evaluation details response
106+
*/
107+
getObjectDetails(
108+
flagKey: string,
109+
defaultValue: JsonValue,
110+
options?: FlagEvaluationOptions
111+
): EvaluationDetails<JsonValue>;
112+
113+
getObjectDetails<T extends JsonValue = JsonValue>(
114+
flagKey: string,
115+
defaultValue: T,
116+
options?: FlagEvaluationOptions
117+
): EvaluationDetails<T>;
118+
}

Diff for: packages/client/src/evaluation/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './evaluation';

Diff for: packages/client/src/index.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
export * from './client';
2-
export * from './no-op-provider';
2+
export * from './provider';
3+
export * from './evaluation';
34
export * from './open-feature';
4-
export * from './types';
5-
export * from '@openfeature/shared';
5+
export * from '@openfeature/shared';

Diff for: packages/client/src/open-feature.ts

+8-25
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
import { EvaluationContext, FlagValue, Logger, OpenFeatureCommonAPI, SafeLogger } from '@openfeature/shared';
2-
import { OpenFeatureClient } from './client';
3-
import { NOOP_PROVIDER } from './no-op-provider';
4-
import { Client, Hook, Provider } from './types';
1+
import { EvaluationContext, ManageContext, OpenFeatureCommonAPI } from '@openfeature/shared';
2+
import { Client, OpenFeatureClient } from './client';
3+
import { NOOP_PROVIDER, Provider } from './provider';
54

65
// use a symbol as a key for the global singleton
76
const GLOBAL_OPENFEATURE_API_KEY = Symbol.for('@openfeature/web-sdk/api');
@@ -11,8 +10,7 @@ type OpenFeatureGlobal = {
1110
};
1211
const _globalThis = globalThis as OpenFeatureGlobal;
1312

14-
export class OpenFeatureAPI extends OpenFeatureCommonAPI<Provider> {
15-
protected _hooks: Hook[] = [];
13+
export class OpenFeatureAPI extends OpenFeatureCommonAPI<Provider> implements ManageContext<Promise<void>> {
1614
protected _defaultProvider: Provider = NOOP_PROVIDER;
1715

1816
// eslint-disable-next-line @typescript-eslint/no-empty-function
@@ -36,31 +34,16 @@ export class OpenFeatureAPI extends OpenFeatureCommonAPI<Provider> {
3634
return instance;
3735
}
3836

39-
setLogger(logger: Logger): this {
40-
this._logger = new SafeLogger(logger);
41-
return this;
42-
}
43-
44-
addHooks(...hooks: Hook<FlagValue>[]): this {
45-
this._hooks = [...this._hooks, ...hooks];
46-
return this;
47-
}
48-
49-
getHooks(): Hook<FlagValue>[] {
50-
return this._hooks;
51-
}
52-
53-
clearHooks(): this {
54-
this._hooks = [];
55-
return this;
56-
}
57-
5837
async setContext(context: EvaluationContext): Promise<void> {
5938
const oldContext = this._context;
6039
this._context = context;
6140
await this._defaultProvider?.onContextChange?.(oldContext, context);
6241
}
6342

43+
getContext(): EvaluationContext {
44+
return this._context;
45+
}
46+
6447
/**
6548
* A factory function for creating new named OpenFeature clients. Clients can contain
6649
* their own state (e.g. logger, hook, context). Multiple clients can be used

Diff for: packages/client/src/provider/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from './provider';
2+
export * from './no-op-provider';

Diff for: packages/client/src/no-op-provider.ts renamed to packages/client/src/provider/no-op-provider.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { JsonValue, ProviderStatus, ResolutionDetails } from '@openfeature/shared';
2-
import { Provider } from './types';
2+
import { Provider } from './provider';
33

44
const REASON_NO_OP = 'No-op';
55

Diff for: packages/client/src/provider/provider.ts

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { CommonProvider, EvaluationContext, Hook, JsonValue, Logger, ResolutionDetails } from '@openfeature/shared';
2+
3+
/**
4+
* Interface that providers must implement to resolve flag values for their particular
5+
* backend or vendor.
6+
*
7+
* Implementation for resolving all the required flag types must be defined.
8+
*/
9+
export interface Provider extends CommonProvider {
10+
/**
11+
* A provider hook exposes a mechanism for provider authors to register hooks
12+
* to tap into various stages of the flag evaluation lifecycle. These hooks can
13+
* be used to perform side effects and mutate the context for purposes of the
14+
* provider. Provider hooks are not configured or controlled by the application author.
15+
*/
16+
readonly hooks?: Hook[];
17+
18+
/**
19+
* A handler function to reconcile changes when the static context.
20+
* Called by the SDK when the context is changed.
21+
* @param oldContext
22+
* @param newContext
23+
*/
24+
onContextChange?(oldContext: EvaluationContext, newContext: EvaluationContext): Promise<void>;
25+
26+
/**
27+
* Resolve a boolean flag and its evaluation details.
28+
*/
29+
resolveBooleanEvaluation(
30+
flagKey: string,
31+
defaultValue: boolean,
32+
context: EvaluationContext,
33+
logger: Logger
34+
): ResolutionDetails<boolean>;
35+
36+
/**
37+
* Resolve a string flag and its evaluation details.
38+
*/
39+
resolveStringEvaluation(
40+
flagKey: string,
41+
defaultValue: string,
42+
context: EvaluationContext,
43+
logger: Logger
44+
): ResolutionDetails<string>;
45+
46+
/**
47+
* Resolve a numeric flag and its evaluation details.
48+
*/
49+
resolveNumberEvaluation(
50+
flagKey: string,
51+
defaultValue: number,
52+
context: EvaluationContext,
53+
logger: Logger
54+
): ResolutionDetails<number>;
55+
56+
/**
57+
* Resolve and parse an object flag and its evaluation details.
58+
*/
59+
resolveObjectEvaluation<T extends JsonValue>(
60+
flagKey: string,
61+
defaultValue: T,
62+
context: EvaluationContext,
63+
logger: Logger
64+
): ResolutionDetails<T>;
65+
}

0 commit comments

Comments
 (0)