1
+ import {
2
+ InvalidContextError ,
3
+ ParseError ,
4
+ StandardResolutionReasons ,
5
+ TargetingKeyMissingError ,
6
+ } from '@openfeature/core' ;
7
+ import {
8
+ EvaluationFailureErrorCode ,
9
+ EvaluationRequest ,
10
+ EvaluationResponse ,
11
+ OFREPApi ,
12
+ OFREPApiFetchError ,
13
+ OFREPApiTooManyRequestsError ,
14
+ OFREPApiUnauthorizedError ,
15
+ OFREPForbiddenError ,
16
+ RequestOptions ,
17
+ handleEvaluationError ,
18
+ isEvaluationFailureResponse ,
19
+ isEvaluationSuccessResponse ,
20
+ } from '@openfeature/ofrep-core' ;
1
21
import {
2
22
ClientProviderEvents ,
3
23
EvaluationContext ,
@@ -15,30 +35,10 @@ import {
15
35
ResolutionDetails ,
16
36
TypeMismatchError ,
17
37
} from '@openfeature/web-sdk' ;
18
- import { OFREPWebProviderOptions } from './model/ofrep-web-provider-options' ;
19
- import {
20
- EvaluationFailureErrorCode ,
21
- EvaluationRequest ,
22
- EvaluationResponse ,
23
- handleEvaluationError ,
24
- isEvaluationFailureResponse ,
25
- isEvaluationSuccessResponse ,
26
- OFREPApi ,
27
- OFREPApiFetchError ,
28
- OFREPApiTooManyRequestsError ,
29
- OFREPApiUnauthorizedError ,
30
- OFREPForbiddenError ,
31
- RequestOptions ,
32
- } from '@openfeature/ofrep-core' ;
33
- import {
34
- InvalidContextError ,
35
- ParseError ,
36
- StandardResolutionReasons ,
37
- TargetingKeyMissingError ,
38
- } from '@openfeature/core' ;
39
- import { isResolutionError , ResolutionError } from './model/resolution-error' ;
40
38
import { BulkEvaluationStatus , EvaluateFlagsResponse } from './model/evaluate-flags-response' ;
41
- import { InMemoryCache } from './model/in-memory-cache' ;
39
+ import { FlagCache } from './model/in-memory-cache' ;
40
+ import { OFREPWebProviderOptions } from './model/ofrep-web-provider-options' ;
41
+ import { isResolutionError } from './model/resolution-error' ;
42
42
43
43
export class OFREPWebProvider implements Provider {
44
44
DEFAULT_POLL_INTERVAL = 30000 ;
@@ -47,19 +47,18 @@ export class OFREPWebProvider implements Provider {
47
47
name : 'OpenFeature Remote Evaluation Protocol Web Provider' ,
48
48
} ;
49
49
readonly runsOn = 'client' ;
50
- events = new OpenFeatureEventEmitter ( ) ;
51
- hooks ?: Hook [ ] | undefined ;
50
+ readonly events = new OpenFeatureEventEmitter ( ) ;
51
+ readonly hooks ?: Hook [ ] | undefined ;
52
52
53
53
// logger is the Open Feature logger to use
54
54
private _logger ?: Logger ;
55
55
// _options is the options used to configure the provider.
56
56
private _options : OFREPWebProviderOptions ;
57
-
58
57
private _ofrepAPI : OFREPApi ;
59
58
private _etag : string | null ;
60
59
private _pollingInterval : number ;
61
60
private _retryPollingAfter : Date | undefined ;
62
- private _cache : InMemoryCache = { } ;
61
+ private _flagCache : FlagCache = { } ;
63
62
private _context : EvaluationContext | undefined ;
64
63
private _pollingIntervalId ?: number ;
65
64
@@ -71,11 +70,18 @@ export class OFREPWebProvider implements Provider {
71
70
this . _pollingInterval = this . _options . pollInterval ?? this . DEFAULT_POLL_INTERVAL ;
72
71
}
73
72
73
+ /**
74
+ * Returns a shallow copy of the flag cache, which is updated at initialization/context-change/configuration-change once the flags are re-evaluated.
75
+ */
76
+ get flagCache ( ) : FlagCache {
77
+ return { ...this . _flagCache } ;
78
+ }
79
+
74
80
/**
75
81
* Initialize the provider, it will evaluate the flags and start the polling if it is not disabled.
76
82
* @param context - the context to use for the evaluation
77
83
*/
78
- async initialize ? ( context ?: EvaluationContext | undefined ) : Promise < void > {
84
+ async initialize ( context ?: EvaluationContext | undefined ) : Promise < void > {
79
85
try {
80
86
this . _context = context ;
81
87
await this . _evaluateFlags ( context ) ;
@@ -92,19 +98,37 @@ export class OFREPWebProvider implements Provider {
92
98
throw error ;
93
99
}
94
100
}
95
-
96
- resolveBooleanEvaluation ( flagKey : string ) : ResolutionDetails < boolean > {
101
+ /* eslint-disable @typescript-eslint/no-unused-vars*/
102
+ /* to make overrides easier we keep these unused vars */
103
+ resolveBooleanEvaluation (
104
+ flagKey : string ,
105
+ defaultValue : boolean ,
106
+ context : EvaluationContext ,
107
+ ) : ResolutionDetails < boolean > {
97
108
return this . evaluate ( flagKey , 'boolean' ) ;
98
109
}
99
- resolveStringEvaluation ( flagKey : string ) : ResolutionDetails < string > {
110
+ resolveStringEvaluation (
111
+ flagKey : string ,
112
+ defaultValue : string ,
113
+ context : EvaluationContext ,
114
+ ) : ResolutionDetails < string > {
100
115
return this . evaluate ( flagKey , 'string' ) ;
101
116
}
102
- resolveNumberEvaluation ( flagKey : string ) : ResolutionDetails < number > {
117
+ resolveNumberEvaluation (
118
+ flagKey : string ,
119
+ defaultValue : number ,
120
+ context : EvaluationContext ,
121
+ ) : ResolutionDetails < number > {
103
122
return this . evaluate ( flagKey , 'number' ) ;
104
123
}
105
- resolveObjectEvaluation < T extends JsonValue > ( flagKey : string ) : ResolutionDetails < T > {
124
+ resolveObjectEvaluation < T extends JsonValue > (
125
+ flagKey : string ,
126
+ defaultValue : T ,
127
+ context : EvaluationContext ,
128
+ ) : ResolutionDetails < T > {
106
129
return this . evaluate ( flagKey , 'object' ) ;
107
130
}
131
+ /* eslint-enable @typescript-eslint/no-unused-vars */
108
132
109
133
/**
110
134
* onContextChange is called when the context changes, it will re-evaluate the flags with the new context
@@ -183,7 +207,7 @@ export class OFREPWebProvider implements Provider {
183
207
}
184
208
185
209
const bulkSuccessResp = response . value ;
186
- const newCache : InMemoryCache = { } ;
210
+ const newCache : FlagCache = { } ;
187
211
188
212
bulkSuccessResp . flags ?. forEach ( ( evalResp : EvaluationResponse ) => {
189
213
if ( isEvaluationFailureResponse ( evalResp ) ) {
@@ -203,8 +227,8 @@ export class OFREPWebProvider implements Provider {
203
227
} ;
204
228
}
205
229
} ) ;
206
- const listUpdatedFlags = this . _getListUpdatedFlags ( this . _cache , newCache ) ;
207
- this . _cache = newCache ;
230
+ const listUpdatedFlags = this . _getListUpdatedFlags ( this . _flagCache , newCache ) ;
231
+ this . _flagCache = newCache ;
208
232
this . _etag = response . httpResponse ?. headers . get ( 'etag' ) ;
209
233
return { status : BulkEvaluationStatus . SUCCESS_WITH_CHANGES , flags : listUpdatedFlags } ;
210
234
} catch ( error ) {
@@ -222,7 +246,7 @@ export class OFREPWebProvider implements Provider {
222
246
* @param newCache
223
247
* @private
224
248
*/
225
- private _getListUpdatedFlags ( oldCache : InMemoryCache , newCache : InMemoryCache ) : string [ ] {
249
+ private _getListUpdatedFlags ( oldCache : FlagCache , newCache : FlagCache ) : string [ ] {
226
250
const changedKeys : string [ ] = [ ] ;
227
251
const oldKeys = Object . keys ( oldCache ) ;
228
252
const newKeys = Object . keys ( newCache ) ;
@@ -251,7 +275,7 @@ export class OFREPWebProvider implements Provider {
251
275
* @private
252
276
*/
253
277
private evaluate < T extends FlagValue > ( flagKey : string , type : string ) : ResolutionDetails < T > {
254
- const resolved = this . _cache [ flagKey ] ;
278
+ const resolved = this . _flagCache [ flagKey ] ;
255
279
if ( ! resolved ) {
256
280
throw new FlagNotFoundError ( `flag key ${ flagKey } not found in cache` ) ;
257
281
}
0 commit comments