@@ -14,7 +14,7 @@ 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 { Configuration } from '../configuration' ;
17
+ import { Configuration , PrecomputedConfig } from '../configuration' ;
18
18
import ConfigurationRequestor from '../configuration-requestor' ;
19
19
import { ConfigurationStore } from '../configuration-store' ;
20
20
import { ISyncStore } from '../configuration-store/configuration-store' ;
@@ -40,7 +40,13 @@ import {
40
40
DEFAULT_ENABLE_BANDITS ,
41
41
} from '../constants' ;
42
42
import { EppoValue } from '../eppo_value' ;
43
- import { Evaluator , FlagEvaluation , noneResult , overrideResult } from '../evaluator' ;
43
+ import {
44
+ Evaluator ,
45
+ FlagEvaluation ,
46
+ FlagEvaluationWithoutDetails ,
47
+ noneResult ,
48
+ overrideResult ,
49
+ } from '../evaluator' ;
44
50
import { BoundedEventQueue } from '../events/bounded-event-queue' ;
45
51
import EventDispatcher from '../events/event-dispatcher' ;
46
52
import NoOpEventDispatcher from '../events/no-op-event-dispatcher' ;
@@ -53,6 +59,7 @@ import FetchHttpClient from '../http-client';
53
59
import {
54
60
BanditModelData ,
55
61
FormatEnum ,
62
+ IObfuscatedPrecomputedBandit ,
56
63
IPrecomputedBandit ,
57
64
PrecomputedFlag ,
58
65
Variation ,
@@ -80,6 +87,8 @@ import {
80
87
import { ConfigurationPoller } from '../configuration-poller' ;
81
88
import { ConfigurationFeed , ConfigurationSource } from '../configuration-feed' ;
82
89
import { BroadcastChannel } from '../broadcast' ;
90
+ import { getMD5Hash } from '../obfuscation' ;
91
+ import { decodePrecomputedBandit , decodePrecomputedFlag } from '../decoding' ;
83
92
84
93
export interface IAssignmentDetails < T extends Variation [ 'value' ] | object > {
85
94
variation : T ;
@@ -1297,6 +1306,19 @@ export default class EppoClient {
1297
1306
) ;
1298
1307
}
1299
1308
1309
+ const precomputed = config . getPrecomputedConfiguration ( ) ;
1310
+ if ( precomputed && precomputed . subjectKey === subjectKey ) {
1311
+ // Short-circuit evaluation if we have a matching precomputed configuration.
1312
+ return this . evaluatePrecomputedAssignment (
1313
+ precomputed ,
1314
+ flagKey ,
1315
+ subjectKey ,
1316
+ subjectAttributes ,
1317
+ expectedVariationType ,
1318
+ flagEvaluationDetailsBuilder ,
1319
+ ) ;
1320
+ }
1321
+
1300
1322
const flag = config . getFlag ( flagKey ) ;
1301
1323
1302
1324
if ( flag === null ) {
@@ -1311,7 +1333,7 @@ export default class EppoClient {
1311
1333
subjectKey ,
1312
1334
subjectAttributes ,
1313
1335
flagEvaluationDetails ,
1314
- config . getFlagsConfiguration ( ) ?. response . environment . name ?? '' ,
1336
+ config . getFlagsConfiguration ( ) ?. response . format ?? '' ,
1315
1337
) ;
1316
1338
}
1317
1339
@@ -1371,6 +1393,90 @@ export default class EppoClient {
1371
1393
return result ;
1372
1394
}
1373
1395
1396
+ private evaluatePrecomputedAssignment (
1397
+ precomputed : PrecomputedConfig ,
1398
+ flagKey : string ,
1399
+ subjectKey : string ,
1400
+ subjectAttributes : Attributes ,
1401
+ expectedVariationType : VariationType | undefined ,
1402
+ flagEvaluationDetailsBuilder : FlagEvaluationDetailsBuilder ,
1403
+ ) : FlagEvaluation {
1404
+ const obfuscatedKey = getMD5Hash ( flagKey , precomputed . response . salt ) ;
1405
+ const obfuscatedFlag : PrecomputedFlag | undefined = precomputed . response . flags [ obfuscatedKey ] ;
1406
+ const obfuscatedBandit : IObfuscatedPrecomputedBandit | undefined =
1407
+ precomputed . response . bandits [ obfuscatedKey ] ;
1408
+ const flag = obfuscatedFlag && decodePrecomputedFlag ( obfuscatedFlag ) ;
1409
+ const bandit = obfuscatedBandit && decodePrecomputedBandit ( obfuscatedBandit ) ;
1410
+
1411
+ if ( ! flag ) {
1412
+ logger . warn ( `${ loggerPrefix } No assigned variation. Flag not found: ${ flagKey } ` ) ;
1413
+ // note: this is different from the Python SDK, which returns None instead
1414
+ const flagEvaluationDetails = flagEvaluationDetailsBuilder . buildForNoneResult (
1415
+ 'FLAG_UNRECOGNIZED_OR_DISABLED' ,
1416
+ `Unrecognized or disabled flag: ${ flagKey } ` ,
1417
+ ) ;
1418
+ return noneResult (
1419
+ flagKey ,
1420
+ subjectKey ,
1421
+ subjectAttributes ,
1422
+ flagEvaluationDetails ,
1423
+ precomputed . response . format ,
1424
+ ) ;
1425
+ }
1426
+
1427
+ if ( ! checkTypeMatch ( expectedVariationType , flag . variationType ) ) {
1428
+ const errorMessage = `Variation value does not have the correct type. Found ${ flag . variationType } , but expected ${ expectedVariationType } for flag ${ flagKey } ` ;
1429
+ if ( this . isGracefulFailureMode ) {
1430
+ const flagEvaluationDetails = flagEvaluationDetailsBuilder . buildForNoneResult (
1431
+ 'TYPE_MISMATCH' ,
1432
+ errorMessage ,
1433
+ ) ;
1434
+ return noneResult (
1435
+ flagKey ,
1436
+ subjectKey ,
1437
+ subjectAttributes ,
1438
+ flagEvaluationDetails ,
1439
+ precomputed . response . format ,
1440
+ ) ;
1441
+ }
1442
+ throw new TypeError ( errorMessage ) ;
1443
+ }
1444
+
1445
+ const result : FlagEvaluation = {
1446
+ flagKey,
1447
+ format : precomputed . response . format ,
1448
+ subjectKey : precomputed . subjectKey ,
1449
+ subjectAttributes : precomputed . subjectAttributes
1450
+ ? ensureNonContextualSubjectAttributes ( precomputed . subjectAttributes )
1451
+ : { } ,
1452
+ variation : {
1453
+ key : flag . variationKey ?? '' ,
1454
+ value : flag . variationValue ,
1455
+ } ,
1456
+ allocationKey : flag . allocationKey ?? '' ,
1457
+ extraLogging : flag . extraLogging ?? { } ,
1458
+ doLog : flag . doLog ,
1459
+ entityId : null ,
1460
+ flagEvaluationDetails : {
1461
+ environmentName : precomputed . response . environment ?. name ?? '' ,
1462
+ flagEvaluationCode : 'MATCH' ,
1463
+ flagEvaluationDescription : 'Matched precomputed flag' ,
1464
+ variationKey : flag . variationKey ?? null ,
1465
+ variationValue : flag . variationValue ,
1466
+ banditKey : null ,
1467
+ banditAction : null ,
1468
+ configFetchedAt : precomputed . fetchedAt ?? '' ,
1469
+ configPublishedAt : precomputed . response . createdAt ,
1470
+ matchedRule : null ,
1471
+ matchedAllocation : null ,
1472
+ unmatchedAllocations : [ ] ,
1473
+ unevaluatedAllocations : [ ] ,
1474
+ } ,
1475
+ } ;
1476
+
1477
+ return result ;
1478
+ }
1479
+
1374
1480
/**
1375
1481
* Enqueues an arbitrary event. Events must have a type and a payload.
1376
1482
*/
0 commit comments