@@ -14,20 +14,19 @@ import { AssignmentCache } from '../cache/abstract-assignment-cache';
14
14
import { LRUInMemoryAssignmentCache } from '../cache/lru-in-memory-assignment-cache' ;
15
15
import { NonExpiringInMemoryAssignmentCache } from '../cache/non-expiring-in-memory-cache-assignment' ;
16
16
import { TLRUInMemoryAssignmentCache } from '../cache/tlru-in-memory-assignment-cache' ;
17
+ import ConfigurationRequestor from '../configuration-requestor' ;
18
+ import { IConfigurationStore , ISyncStore } from '../configuration-store/configuration-store' ;
17
19
import {
18
20
ConfigurationWireV1 ,
19
21
IConfigurationWire ,
20
22
IPrecomputedConfiguration ,
21
23
PrecomputedConfiguration ,
22
- } from '../configuration' ;
23
- import ConfigurationRequestor from '../configuration-requestor' ;
24
- import { IConfigurationStore , ISyncStore } from '../configuration-store/configuration-store' ;
24
+ } from '../configuration-wire-types' ;
25
25
import {
26
26
DEFAULT_INITIAL_CONFIG_REQUEST_RETRIES ,
27
27
DEFAULT_POLL_CONFIG_REQUEST_RETRIES ,
28
28
DEFAULT_POLL_INTERVAL_MS ,
29
29
DEFAULT_REQUEST_TIMEOUT_MS ,
30
- OBFUSCATED_FORMATS ,
31
30
} from '../constants' ;
32
31
import { decodeFlag } from '../decoding' ;
33
32
import { EppoValue } from '../eppo_value' ;
@@ -41,13 +40,12 @@ import {
41
40
} from '../flag-evaluation-details-builder' ;
42
41
import { FlagEvaluationError } from '../flag-evaluation-error' ;
43
42
import FetchHttpClient from '../http-client' ;
43
+ import { IConfiguration , StoreBackedConfiguration } from '../i-configuration' ;
44
44
import {
45
45
BanditModelData ,
46
46
BanditParameters ,
47
47
BanditVariation ,
48
- ConfigDetails ,
49
48
Flag ,
50
- FormatEnum ,
51
49
IPrecomputedBandit ,
52
50
ObfuscatedFlag ,
53
51
PrecomputedFlag ,
@@ -135,6 +133,7 @@ export default class EppoClient {
135
133
private configObfuscatedCache ?: boolean ;
136
134
private requestPoller ?: IPoller ;
137
135
private readonly evaluator = new Evaluator ( ) ;
136
+ private configurationRequestor ?: ConfigurationRequestor ;
138
137
139
138
constructor ( {
140
139
eventDispatcher = new NoOpEventDispatcher ( ) ,
@@ -164,6 +163,16 @@ export default class EppoClient {
164
163
this . expectObfuscated = isObfuscated ;
165
164
}
166
165
166
+ private getConfiguration ( ) : IConfiguration {
167
+ return this . configurationRequestor
168
+ ? this . configurationRequestor . getConfiguration ( )
169
+ : new StoreBackedConfiguration (
170
+ this . flagConfigurationStore ,
171
+ this . banditVariationConfigurationStore ,
172
+ this . banditModelConfigurationStore ,
173
+ ) ;
174
+ }
175
+
167
176
private maybeWarnAboutObfuscationMismatch ( configObfuscated : boolean ) {
168
177
// Don't warn again if we did on the last check.
169
178
if ( configObfuscated !== this . expectObfuscated && ! this . obfuscationMismatchWarningIssued ) {
@@ -177,11 +186,14 @@ export default class EppoClient {
177
186
}
178
187
}
179
188
180
- private isObfuscated ( ) {
189
+ /**
190
+ * This method delegates to the configuration to determine whether it is obfuscated, then caches the actual
191
+ * obfuscation state and issues a warning if it hasn't already.
192
+ * This method can be removed with the next major update when the @deprecated setIsObfuscated is removed
193
+ */
194
+ private isObfuscated ( config : IConfiguration ) {
181
195
if ( this . configObfuscatedCache === undefined ) {
182
- this . configObfuscatedCache = OBFUSCATED_FORMATS . includes (
183
- this . flagConfigurationStore . getFormat ( ) ?? FormatEnum . SERVER ,
184
- ) ;
196
+ this . configObfuscatedCache = config . isObfuscated ( ) ;
185
197
}
186
198
this . maybeWarnAboutObfuscationMismatch ( this . configObfuscatedCache ) ;
187
199
return this . configObfuscatedCache ;
@@ -307,7 +319,7 @@ export default class EppoClient {
307
319
) ;
308
320
309
321
const pollingCallback = async ( ) => {
310
- if ( await this . flagConfigurationStore . isExpired ( ) ) {
322
+ if ( await configurationRequestor . isFlagConfigExpired ( ) ) {
311
323
this . configObfuscatedCache = undefined ;
312
324
return configurationRequestor . fetchAndStoreConfigurations ( ) ;
313
325
}
@@ -624,13 +636,14 @@ export default class EppoClient {
624
636
actions : BanditActions ,
625
637
defaultAction : string ,
626
638
) : string {
639
+ const config = this . getConfiguration ( ) ;
627
640
let result : string | null = null ;
628
641
629
- const flagBanditVariations = this . banditVariationConfigurationStore ?. get ( flagKey ) ;
642
+ const flagBanditVariations = config . getFlagBanditVariations ( flagKey ) ;
630
643
const banditKey = flagBanditVariations ?. at ( 0 ) ?. key ;
631
644
632
645
if ( banditKey ) {
633
- const banditParameters = this . getBandit ( banditKey ) ;
646
+ const banditParameters = config . getBandit ( banditKey ) ;
634
647
if ( banditParameters ) {
635
648
const contextualSubjectAttributes = ensureContextualSubjectAttributes ( subjectAttributes ) ;
636
649
const actionsWithContextualAttributes = ensureActionsWithContextualAttributes ( actions ) ;
@@ -653,11 +666,13 @@ export default class EppoClient {
653
666
actions : BanditActions ,
654
667
defaultValue : string ,
655
668
) : IAssignmentDetails < string > {
669
+ const config = this . getConfiguration ( ) ;
656
670
let variation = defaultValue ;
657
671
let action : string | null = null ;
658
672
659
673
// Initialize with a generic evaluation details. This will mutate as the function progresses.
660
674
let evaluationDetails : IFlagEvaluationDetails = this . newFlagEvaluationDetailsBuilder (
675
+ config ,
661
676
flagKey ,
662
677
) . buildForNoneResult (
663
678
'ASSIGNMENT_ERROR' ,
@@ -681,7 +696,7 @@ export default class EppoClient {
681
696
// Check if the assigned variation is an active bandit
682
697
// Note: the reason for non-bandit assignments include the subject being bucketed into a non-bandit variation or
683
698
// a rollout having been done.
684
- const bandit = this . findBanditByVariation ( flagKey , variation ) ;
699
+ const bandit = config . getFlagVariationBandit ( flagKey , variation ) ;
685
700
686
701
if ( ! bandit ) {
687
702
return { variation, action : null , evaluationDetails } ;
@@ -909,13 +924,14 @@ export default class EppoClient {
909
924
subjectKey : string ,
910
925
subjectAttributes : Attributes = { } ,
911
926
) : Record < FlagKey , PrecomputedFlag > {
912
- const configDetails = this . getConfigDetails ( ) ;
927
+ const config = this . getConfiguration ( ) ;
928
+ const configDetails = config . getFlagConfigDetails ( ) ;
913
929
const flagKeys = this . getFlagKeys ( ) ;
914
930
const flags : Record < FlagKey , PrecomputedFlag > = { } ;
915
931
916
932
// Evaluate all the enabled flags for the user
917
933
flagKeys . forEach ( ( flagKey ) => {
918
- const flag = this . getFlag ( flagKey ) ;
934
+ const flag = this . getNormalizedFlag ( config , flagKey ) ;
919
935
if ( ! flag ) {
920
936
logger . debug ( `${ loggerPrefix } No assigned variation. Flag does not exist.` ) ;
921
937
return ;
@@ -927,7 +943,7 @@ export default class EppoClient {
927
943
configDetails ,
928
944
subjectKey ,
929
945
subjectAttributes ,
930
- this . isObfuscated ( ) ,
946
+ this . isObfuscated ( config ) ,
931
947
) ;
932
948
933
949
// allocationKey is set along with variation when there is a result. this check appeases typescript below
@@ -965,13 +981,15 @@ export default class EppoClient {
965
981
banditActions : Record < FlagKey , BanditActions > = { } ,
966
982
salt ?: string ,
967
983
) : string {
968
- const configDetails = this . getConfigDetails ( ) ;
984
+ const config = this . getConfiguration ( ) ;
985
+ const configDetails = config . getFlagConfigDetails ( ) ;
969
986
970
987
const subjectContextualAttributes = ensureContextualSubjectAttributes ( subjectAttributes ) ;
971
988
const subjectFlatAttributes = ensureNonContextualSubjectAttributes ( subjectAttributes ) ;
972
989
const flags = this . getAllAssignments ( subjectKey , subjectFlatAttributes ) ;
973
990
974
991
const bandits = this . computeBanditsForFlags (
992
+ config ,
975
993
subjectKey ,
976
994
subjectContextualAttributes ,
977
995
banditActions ,
@@ -1011,8 +1029,9 @@ export default class EppoClient {
1011
1029
) : FlagEvaluation {
1012
1030
validateNotBlank ( subjectKey , 'Invalid argument: subjectKey cannot be blank' ) ;
1013
1031
validateNotBlank ( flagKey , 'Invalid argument: flagKey cannot be blank' ) ;
1032
+ const config = this . getConfiguration ( ) ;
1014
1033
1015
- const flagEvaluationDetailsBuilder = this . newFlagEvaluationDetailsBuilder ( flagKey ) ;
1034
+ const flagEvaluationDetailsBuilder = this . newFlagEvaluationDetailsBuilder ( config , flagKey ) ;
1016
1035
const overrideVariation = this . overrideStore ?. get ( flagKey ) ;
1017
1036
if ( overrideVariation ) {
1018
1037
return overrideResult (
@@ -1024,8 +1043,8 @@ export default class EppoClient {
1024
1043
) ;
1025
1044
}
1026
1045
1027
- const configDetails = this . getConfigDetails ( ) ;
1028
- const flag = this . getFlag ( flagKey ) ;
1046
+ const configDetails = config . getFlagConfigDetails ( ) ;
1047
+ const flag = this . getNormalizedFlag ( config , flagKey ) ;
1029
1048
1030
1049
if ( flag === null ) {
1031
1050
logger . warn ( `${ loggerPrefix } No assigned variation. Flag not found: ${ flagKey } ` ) ;
@@ -1077,7 +1096,7 @@ export default class EppoClient {
1077
1096
) ;
1078
1097
}
1079
1098
1080
- const isObfuscated = this . isObfuscated ( ) ;
1099
+ const isObfuscated = this . isObfuscated ( config ) ;
1081
1100
const result = this . evaluator . evaluateFlag (
1082
1101
flag ,
1083
1102
configDetails ,
@@ -1114,9 +1133,12 @@ export default class EppoClient {
1114
1133
} ) ;
1115
1134
}
1116
1135
1117
- private newFlagEvaluationDetailsBuilder ( flagKey : string ) : FlagEvaluationDetailsBuilder {
1118
- const flag = this . getFlag ( flagKey ) ;
1119
- const configDetails = this . getConfigDetails ( ) ;
1136
+ private newFlagEvaluationDetailsBuilder (
1137
+ config : IConfiguration ,
1138
+ flagKey : string ,
1139
+ ) : FlagEvaluationDetailsBuilder {
1140
+ const flag = this . getNormalizedFlag ( config , flagKey ) ;
1141
+ const configDetails = config . getFlagConfigDetails ( ) ;
1120
1142
return new FlagEvaluationDetailsBuilder (
1121
1143
configDetails . configEnvironment . name ,
1122
1144
flag ?. allocations ?? [ ] ,
@@ -1125,35 +1147,17 @@ export default class EppoClient {
1125
1147
) ;
1126
1148
}
1127
1149
1128
- private getConfigDetails ( ) : ConfigDetails {
1129
- return {
1130
- configFetchedAt : this . flagConfigurationStore . getConfigFetchedAt ( ) ?? '' ,
1131
- configPublishedAt : this . flagConfigurationStore . getConfigPublishedAt ( ) ?? '' ,
1132
- configEnvironment : this . flagConfigurationStore . getEnvironment ( ) ?? {
1133
- name : '' ,
1134
- } ,
1135
- configFormat : this . flagConfigurationStore . getFormat ( ) ?? '' ,
1136
- } ;
1137
- }
1138
-
1139
- private getFlag ( flagKey : string ) : Flag | null {
1140
- return this . isObfuscated ( )
1141
- ? this . getObfuscatedFlag ( flagKey )
1142
- : this . flagConfigurationStore . get ( flagKey ) ;
1150
+ private getNormalizedFlag ( config : IConfiguration , flagKey : string ) : Flag | null {
1151
+ return this . isObfuscated ( config )
1152
+ ? this . getObfuscatedFlag ( config , flagKey )
1153
+ : config . getFlag ( flagKey ) ;
1143
1154
}
1144
1155
1145
- private getObfuscatedFlag ( flagKey : string ) : Flag | null {
1146
- const flag : ObfuscatedFlag | null = this . flagConfigurationStore . get (
1147
- getMD5Hash ( flagKey ) ,
1148
- ) as ObfuscatedFlag ;
1156
+ private getObfuscatedFlag ( config : IConfiguration , flagKey : string ) : Flag | null {
1157
+ const flag : ObfuscatedFlag | null = config . getFlag ( getMD5Hash ( flagKey ) ) as ObfuscatedFlag ;
1149
1158
return flag ? decodeFlag ( flag ) : null ;
1150
1159
}
1151
1160
1152
- private getBandit ( banditKey : string ) : BanditParameters | null {
1153
- // Upstreams for this SDK do not yet support obfuscating bandits, so no `isObfuscated` check here.
1154
- return this . banditModelConfigurationStore ?. get ( banditKey ) ?? null ;
1155
- }
1156
-
1157
1161
// noinspection JSUnusedGlobalSymbols
1158
1162
getFlagKeys ( ) {
1159
1163
/**
@@ -1162,16 +1166,11 @@ export default class EppoClient {
1162
1166
*
1163
1167
* Note that it is generally not a good idea to preload all flag configurations.
1164
1168
*/
1165
- return this . flagConfigurationStore . getKeys ( ) ;
1169
+ return this . getConfiguration ( ) . getFlagKeys ( ) ;
1166
1170
}
1167
1171
1168
1172
isInitialized ( ) {
1169
- return (
1170
- this . flagConfigurationStore . isInitialized ( ) &&
1171
- ( ! this . banditVariationConfigurationStore ||
1172
- this . banditVariationConfigurationStore . isInitialized ( ) ) &&
1173
- ( ! this . banditModelConfigurationStore || this . banditModelConfigurationStore . isInitialized ( ) )
1174
- ) ;
1173
+ return this . getConfiguration ( ) . isInitialized ( ) ;
1175
1174
}
1176
1175
1177
1176
/** @deprecated Use `setAssignmentLogger` */
@@ -1237,7 +1236,7 @@ export default class EppoClient {
1237
1236
}
1238
1237
1239
1238
getFlagConfigurations ( ) : Record < string , Flag > {
1240
- return this . flagConfigurationStore . entries ( ) ;
1239
+ return this . getConfiguration ( ) . getFlags ( ) ;
1241
1240
}
1242
1241
1243
1242
private flushQueuedEvents < T > ( eventQueue : BoundedEventQueue < T > , logFunction ?: ( event : T ) => void ) {
@@ -1305,13 +1304,14 @@ export default class EppoClient {
1305
1304
1306
1305
private buildLoggerMetadata ( ) : Record < string , unknown > {
1307
1306
return {
1308
- obfuscated : this . isObfuscated ( ) ,
1307
+ obfuscated : this . isObfuscated ( this . getConfiguration ( ) ) ,
1309
1308
sdkLanguage : 'javascript' ,
1310
1309
sdkLibVersion : LIB_VERSION ,
1311
1310
} ;
1312
1311
}
1313
1312
1314
1313
private computeBanditsForFlags (
1314
+ config : IConfiguration ,
1315
1315
subjectKey : string ,
1316
1316
subjectAttributes : ContextAttributes ,
1317
1317
banditActions : Record < FlagKey , BanditActions > ,
@@ -1325,6 +1325,7 @@ export default class EppoClient {
1325
1325
if ( flagVariation ) {
1326
1326
// Precompute a bandit, if there is one matching this variation.
1327
1327
const precomputedResult = this . getPrecomputedBandit (
1328
+ config ,
1328
1329
flagKey ,
1329
1330
flagVariation . variationValue ,
1330
1331
subjectKey ,
@@ -1339,27 +1340,15 @@ export default class EppoClient {
1339
1340
return banditResults ;
1340
1341
}
1341
1342
1342
- private findBanditByVariation ( flagKey : string , variationValue : string ) : BanditParameters | null {
1343
- const banditVariations = this . banditVariationConfigurationStore ?. get ( flagKey ) ;
1344
- const banditKey = banditVariations ?. find (
1345
- ( banditVariation ) => banditVariation . variationValue === variationValue ,
1346
- ) ?. key ;
1347
-
1348
- if ( banditKey ) {
1349
- // Retrieve the model parameters for the bandit
1350
- return this . getBandit ( banditKey ) ;
1351
- }
1352
- return null ;
1353
- }
1354
-
1355
1343
private getPrecomputedBandit (
1344
+ config : IConfiguration ,
1356
1345
flagKey : string ,
1357
1346
variationValue : string ,
1358
1347
subjectKey : string ,
1359
1348
subjectAttributes : ContextAttributes ,
1360
1349
banditActions : BanditActions ,
1361
1350
) : IPrecomputedBandit | null {
1362
- const bandit = this . findBanditByVariation ( flagKey , variationValue ) ;
1351
+ const bandit = config . getFlagVariationBandit ( flagKey , variationValue ) ;
1363
1352
if ( ! bandit ) {
1364
1353
return null ;
1365
1354
}
0 commit comments