@@ -79,6 +79,8 @@ type InputKey = 'feature_key' | 'user_id' | 'variable_key' | 'experiment_key' |
7979
8080type StringInputs = Partial < Record < InputKey , unknown > > ;
8181
82+ type DecisionReasons = ( string | number ) [ ] ;
83+
8284export default class Optimizely implements Client {
8385 private isOptimizelyConfigValid : boolean ;
8486 private disposeOnUpdate : ( ( ) => void ) | null ;
@@ -1526,65 +1528,27 @@ export default class Optimizely implements Client {
15261528 }
15271529
15281530 /**
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.
15361540 */
1537- decideForKeys (
1541+ private generateDecision (
15381542 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 ]
15881552 const decisionSource = decisionObj . decisionSource ;
15891553 const experimentKey = decisionObj . experiment ?. key ?? null ;
15901554 const variationKey = decisionObj . variation ?. key ?? null ;
@@ -1599,7 +1563,7 @@ export default class Optimizely implements Client {
15991563 const variablesMap : { [ key : string ] : unknown } = { } ;
16001564 let decisionEventDispatched = false ;
16011565
1602- if ( ! allDecideOptions [ OptimizelyDecideOption . EXCLUDE_VARIABLES ] ) {
1566+ if ( ! options [ OptimizelyDecideOption . EXCLUDE_VARIABLES ] ) {
16031567 feature . variables . forEach ( variable => {
16041568 variablesMap [ variable . key ] = this . getFeatureVariableValueFromVariation (
16051569 key ,
@@ -1612,15 +1576,15 @@ export default class Optimizely implements Client {
16121576 }
16131577
16141578 if (
1615- ! allDecideOptions [ OptimizelyDecideOption . DISABLE_DECISION_EVENT ] &&
1579+ ! options [ OptimizelyDecideOption . DISABLE_DECISION_EVENT ] &&
16161580 ( decisionSource === DECISION_SOURCES . FEATURE_TEST ||
16171581 ( decisionSource === DECISION_SOURCES . ROLLOUT && projectConfig . getSendFlagDecisionsValue ( configObj ) ) )
16181582 ) {
16191583 this . sendImpressionEvent ( decisionObj , key , userId , flagEnabled , attributes ) ;
16201584 decisionEventDispatched = true ;
16211585 }
16221586
1623- const shouldIncludeReasons = allDecideOptions [ OptimizelyDecideOption . INCLUDE_REASONS ] ;
1587+ const shouldIncludeReasons = options [ OptimizelyDecideOption . INCLUDE_REASONS ] ;
16241588
16251589 let reportedReasons : string [ ] = [ ] ;
16261590 if ( shouldIncludeReasons ) {
@@ -1644,16 +1608,87 @@ export default class Optimizely implements Client {
16441608 decisionInfo : featureInfo ,
16451609 } ) ;
16461610
1647- if ( ! allDecideOptions [ OptimizelyDecideOption . ENABLED_FLAGS_ONLY ] || flagEnabled ) {
1648- decisionMap [ key ] = {
1611+ return {
16491612 variationKey : variationKey ,
16501613 enabled : flagEnabled ,
16511614 variables : variablesMap ,
16521615 ruleKey : experimentKey ,
16531616 flagKey : key ,
16541617 userContext : user ,
16551618 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 ,
16561672 } ;
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 ;
16571692 }
16581693 }
16591694
0 commit comments