@@ -79,6 +79,8 @@ type InputKey = 'feature_key' | 'user_id' | 'variable_key' | 'experiment_key' |
79
79
80
80
type StringInputs = Partial < Record < InputKey , unknown > > ;
81
81
82
+ type DecisionReasons = ( string | number ) [ ] ;
83
+
82
84
export default class Optimizely implements Client {
83
85
private isOptimizelyConfigValid : boolean ;
84
86
private disposeOnUpdate : ( ( ) => void ) | null ;
@@ -1526,65 +1528,27 @@ export default class Optimizely implements Client {
1526
1528
}
1527
1529
1528
1530
/**
1529
- * Returns an object of decision results for multiple flag keys and a user context.
1530
- * If the SDK finds an error for a key, the response will include a decision for the key showing reasons for the error.
1531
- * The SDK will always return an object of decisions. When it cannot process requests, it will return an empty object after logging the errors.
1532
- * @param {OptimizelyUserContext } user A user context associated with this OptimizelyClient
1533
- * @param {string[] } keys An array of flag keys for which decisions will be made.
1534
- * @param {OptimizelyDecideOption[] } options An array of options for decision-making.
1535
- * @return {[key: string]: OptimizelyDecision } An object of decision results mapped by flag keys.
1531
+ * Makes a decision for a given feature key.
1532
+ *
1533
+ * @param {OptimizelyUserContext } user - The user context associated with this Optimizely client.
1534
+ * @param {string } key - The feature key for which a decision will be made.
1535
+ * @param {DecisionObj } decisionObj - The decision object containing decision details.
1536
+ * @param {DecisionReasons[] } reasons - An array of reasons for the decision.
1537
+ * @param {Record<string, boolean> } options - A map of options for decision-making.
1538
+ * @param {projectConfig.ProjectConfig } configObj - The project configuration object.
1539
+ * @returns {OptimizelyDecision } - The decision object for the feature flag.
1536
1540
*/
1537
- decideForKeys (
1541
+ private generateDecision (
1538
1542
user : OptimizelyUserContext ,
1539
- keys : string [ ] ,
1540
- options : OptimizelyDecideOption [ ] = [ ]
1541
- ) : { [ key : string ] : OptimizelyDecision } {
1542
- const decisionMap : { [ key : string ] : OptimizelyDecision } = { } ;
1543
- const configObj = this . projectConfigManager . getConfig ( )
1544
-
1545
- if ( ! this . isValidInstance ( ) || ! configObj ) {
1546
- this . logger . log ( LOG_LEVEL . ERROR , LOG_MESSAGES . INVALID_OBJECT , MODULE_NAME , 'decideForKeys' ) ;
1547
- return decisionMap ;
1548
- }
1549
- if ( keys . length === 0 ) {
1550
- return decisionMap ;
1551
- }
1552
-
1553
- const allDecideOptions = this . getAllDecideOptions ( options ) ;
1554
-
1555
- for ( const key of keys ) {
1556
- const feature = configObj . featureKeyMap [ key ] ;
1557
- if ( ! feature ) {
1558
- this . logger . log ( LOG_LEVEL . ERROR , ERROR_MESSAGES . FEATURE_NOT_IN_DATAFILE , MODULE_NAME , key ) ;
1559
- decisionMap [ key ] = newErrorDecision ( key , user , [ sprintf ( DECISION_MESSAGES . FLAG_KEY_INVALID , key ) ] ) ;
1560
- continue
1561
- }
1562
-
1563
- const userId = user . getUserId ( ) ;
1564
- const attributes = user . getAttributes ( ) ;
1565
- const reasons : ( string | number ) [ ] [ ] = [ ] ;
1566
- const forcedDecisionResponse = this . decisionService . findValidatedForcedDecision ( configObj , user , key ) ;
1567
- reasons . push ( ...forcedDecisionResponse . reasons ) ;
1568
- const variation = forcedDecisionResponse . result ;
1569
- let decisionObj : DecisionObj ;
1570
-
1571
- if ( variation ) {
1572
- decisionObj = {
1573
- experiment : null ,
1574
- variation : variation ,
1575
- decisionSource : DECISION_SOURCES . FEATURE_TEST ,
1576
- } ;
1577
- } else {
1578
- const decisionVariation = this . decisionService . getVariationForFeature (
1579
- configObj ,
1580
- feature ,
1581
- user ,
1582
- allDecideOptions
1583
- ) ;
1584
- reasons . push ( ...decisionVariation . reasons ) ;
1585
- decisionObj = decisionVariation . result ;
1586
- }
1587
-
1543
+ key : string ,
1544
+ decisionObj : DecisionObj ,
1545
+ reasons : DecisionReasons [ ] ,
1546
+ options : Record < string , boolean > ,
1547
+ configObj : projectConfig . ProjectConfig ,
1548
+ ) : OptimizelyDecision {
1549
+ const userId = user . getUserId ( )
1550
+ const attributes = user . getAttributes ( )
1551
+ const feature = configObj . featureKeyMap [ key ]
1588
1552
const decisionSource = decisionObj . decisionSource ;
1589
1553
const experimentKey = decisionObj . experiment ?. key ?? null ;
1590
1554
const variationKey = decisionObj . variation ?. key ?? null ;
@@ -1599,7 +1563,7 @@ export default class Optimizely implements Client {
1599
1563
const variablesMap : { [ key : string ] : unknown } = { } ;
1600
1564
let decisionEventDispatched = false ;
1601
1565
1602
- if ( ! allDecideOptions [ OptimizelyDecideOption . EXCLUDE_VARIABLES ] ) {
1566
+ if ( ! options [ OptimizelyDecideOption . EXCLUDE_VARIABLES ] ) {
1603
1567
feature . variables . forEach ( variable => {
1604
1568
variablesMap [ variable . key ] = this . getFeatureVariableValueFromVariation (
1605
1569
key ,
@@ -1612,15 +1576,15 @@ export default class Optimizely implements Client {
1612
1576
}
1613
1577
1614
1578
if (
1615
- ! allDecideOptions [ OptimizelyDecideOption . DISABLE_DECISION_EVENT ] &&
1579
+ ! options [ OptimizelyDecideOption . DISABLE_DECISION_EVENT ] &&
1616
1580
( decisionSource === DECISION_SOURCES . FEATURE_TEST ||
1617
1581
( decisionSource === DECISION_SOURCES . ROLLOUT && projectConfig . getSendFlagDecisionsValue ( configObj ) ) )
1618
1582
) {
1619
1583
this . sendImpressionEvent ( decisionObj , key , userId , flagEnabled , attributes ) ;
1620
1584
decisionEventDispatched = true ;
1621
1585
}
1622
1586
1623
- const shouldIncludeReasons = allDecideOptions [ OptimizelyDecideOption . INCLUDE_REASONS ] ;
1587
+ const shouldIncludeReasons = options [ OptimizelyDecideOption . INCLUDE_REASONS ] ;
1624
1588
1625
1589
let reportedReasons : string [ ] = [ ] ;
1626
1590
if ( shouldIncludeReasons ) {
@@ -1644,16 +1608,87 @@ export default class Optimizely implements Client {
1644
1608
decisionInfo : featureInfo ,
1645
1609
} ) ;
1646
1610
1647
- if ( ! allDecideOptions [ OptimizelyDecideOption . ENABLED_FLAGS_ONLY ] || flagEnabled ) {
1648
- decisionMap [ key ] = {
1611
+ return {
1649
1612
variationKey : variationKey ,
1650
1613
enabled : flagEnabled ,
1651
1614
variables : variablesMap ,
1652
1615
ruleKey : experimentKey ,
1653
1616
flagKey : key ,
1654
1617
userContext : user ,
1655
1618
reasons : reportedReasons ,
1619
+ } ;
1620
+ }
1621
+
1622
+ /**
1623
+ * Returns an object of decision results for multiple flag keys and a user context.
1624
+ * If the SDK finds an error for a key, the response will include a decision for the key showing reasons for the error.
1625
+ * The SDK will always return an object of decisions. When it cannot process requests, it will return an empty object after logging the errors.
1626
+ * @param {OptimizelyUserContext } user A user context associated with this OptimizelyClient
1627
+ * @param {string[] } keys An array of flag keys for which decisions will be made.
1628
+ * @param {OptimizelyDecideOption[] } options An array of options for decision-making.
1629
+ * @return {[key: string]: OptimizelyDecision } An object of decision results mapped by flag keys.
1630
+ */
1631
+ decideForKeys (
1632
+ user : OptimizelyUserContext ,
1633
+ keys : string [ ] ,
1634
+ options : OptimizelyDecideOption [ ] = [ ]
1635
+ ) : Record < string , OptimizelyDecision > {
1636
+ const decisionMap : Record < string , OptimizelyDecision > = { } ;
1637
+ const flagDecisions : Record < string , DecisionObj > = { } ;
1638
+ const decisionReasonsMap : Record < string , DecisionReasons [ ] > = { } ;
1639
+ const flagsWithoutForcedDecision = [ ] ;
1640
+ const validKeys = [ ] ;
1641
+
1642
+ const configObj = this . projectConfigManager . getConfig ( )
1643
+
1644
+ if ( ! this . isValidInstance ( ) || ! configObj ) {
1645
+ this . logger . log ( LOG_LEVEL . ERROR , LOG_MESSAGES . INVALID_OBJECT , MODULE_NAME , 'decideForKeys' ) ;
1646
+ return decisionMap ;
1647
+ }
1648
+ if ( keys . length === 0 ) {
1649
+ return decisionMap ;
1650
+ }
1651
+
1652
+ const allDecideOptions = this . getAllDecideOptions ( options ) ;
1653
+
1654
+ for ( const key of keys ) {
1655
+ const feature = configObj . featureKeyMap [ key ] ;
1656
+ if ( ! feature ) {
1657
+ this . logger . log ( LOG_LEVEL . ERROR , ERROR_MESSAGES . FEATURE_NOT_IN_DATAFILE , MODULE_NAME , key ) ;
1658
+ decisionMap [ key ] = newErrorDecision ( key , user , [ sprintf ( DECISION_MESSAGES . FLAG_KEY_INVALID , key ) ] ) ;
1659
+ continue
1660
+ }
1661
+
1662
+ validKeys . push ( key ) ;
1663
+ const forcedDecisionResponse = this . decisionService . findValidatedForcedDecision ( configObj , user , key ) ;
1664
+ decisionReasonsMap [ key ] = forcedDecisionResponse . reasons
1665
+ const variation = forcedDecisionResponse . result ;
1666
+
1667
+ if ( variation ) {
1668
+ flagDecisions [ key ] = {
1669
+ experiment : null ,
1670
+ variation : variation ,
1671
+ decisionSource : DECISION_SOURCES . FEATURE_TEST ,
1656
1672
} ;
1673
+ } else {
1674
+ flagsWithoutForcedDecision . push ( feature )
1675
+ }
1676
+ }
1677
+
1678
+ const decisionList = this . decisionService . getVariationsForFeatureList ( configObj , flagsWithoutForcedDecision , user , allDecideOptions ) ;
1679
+
1680
+ for ( let i = 0 ; i < flagsWithoutForcedDecision . length ; i ++ ) {
1681
+ const key = flagsWithoutForcedDecision [ i ] . key ;
1682
+ const decision = decisionList [ i ] ;
1683
+ flagDecisions [ key ] = decision . result ;
1684
+ decisionReasonsMap [ key ] = [ ...decisionReasonsMap [ key ] , ...decision . reasons ] ;
1685
+ }
1686
+
1687
+ for ( const validKey of validKeys ) {
1688
+ const decision = this . generateDecision ( user , validKey , flagDecisions [ validKey ] , decisionReasonsMap [ validKey ] , allDecideOptions , configObj ) ;
1689
+
1690
+ if ( ! allDecideOptions [ OptimizelyDecideOption . ENABLED_FLAGS_ONLY ] || decision . enabled ) {
1691
+ decisionMap [ validKey ] = decision ;
1657
1692
}
1658
1693
}
1659
1694
0 commit comments