@@ -3,6 +3,11 @@ import { v4 as randomUUID } from 'uuid';
3
3
import ApiEndpoints from '../api-endpoints' ;
4
4
import { logger } from '../application-logger' ;
5
5
import { IAssignmentEvent , IAssignmentLogger } from '../assignment-logger' ;
6
+ import {
7
+ ensureActionsWithContextualAttributes ,
8
+ ensureContextualSubjectAttributes ,
9
+ ensureNonContextualSubjectAttributes ,
10
+ } from '../attributes' ;
6
11
import { BanditEvaluator } from '../bandit-evaluator' ;
7
12
import { IBanditEvent , IBanditLogger } from '../bandit-logger' ;
8
13
import { AssignmentCache } from '../cache/abstract-assignment-cache' ;
@@ -535,9 +540,8 @@ export default class EppoClient {
535
540
if ( banditKey ) {
536
541
const banditParameters = this . banditModelConfigurationStore ?. get ( banditKey ) ;
537
542
if ( banditParameters ) {
538
- const contextualSubjectAttributes =
539
- this . ensureContextualSubjectAttributes ( subjectAttributes ) ;
540
- const actionsWithContextualAttributes = this . ensureActionsWithContextualAttributes ( actions ) ;
543
+ const contextualSubjectAttributes = ensureContextualSubjectAttributes ( subjectAttributes ) ;
544
+ const actionsWithContextualAttributes = ensureActionsWithContextualAttributes ( actions ) ;
541
545
542
546
result = this . banditEvaluator . evaluateBestBanditAction (
543
547
contextualSubjectAttributes ,
@@ -571,7 +575,7 @@ export default class EppoClient {
571
575
// Get the assigned variation for the flag with a possible bandit
572
576
// Note for getting assignments, we don't care about context
573
577
const nonContextualSubjectAttributes =
574
- this . ensureNonContextualSubjectAttributes ( subjectAttributes ) ;
578
+ ensureNonContextualSubjectAttributes ( subjectAttributes ) ;
575
579
const { variation : assignedVariation , evaluationDetails : assignmentEvaluationDetails } =
576
580
this . getStringAssignmentDetails (
577
581
flagKey ,
@@ -683,8 +687,8 @@ export default class EppoClient {
683
687
}
684
688
685
689
const banditModelData = banditParameters . modelData ;
686
- const contextualSubjectAttributes = this . ensureContextualSubjectAttributes ( subjectAttributes ) ;
687
- const actionsWithContextualAttributes = this . ensureActionsWithContextualAttributes ( actions ) ;
690
+ const contextualSubjectAttributes = ensureContextualSubjectAttributes ( subjectAttributes ) ;
691
+ const actionsWithContextualAttributes = ensureActionsWithContextualAttributes ( actions ) ;
688
692
const banditEvaluation = this . banditEvaluator . evaluateBandit (
689
693
flagKey ,
690
694
subjectKey ,
@@ -715,79 +719,6 @@ export default class EppoClient {
715
719
return action ;
716
720
}
717
721
718
- private ensureNonContextualSubjectAttributes (
719
- subjectAttributes : BanditSubjectAttributes ,
720
- ) : Attributes {
721
- let result : Attributes ;
722
- if ( this . isInstanceOfContextualAttributes ( subjectAttributes ) ) {
723
- const contextualSubjectAttributes = subjectAttributes as ContextAttributes ;
724
- result = {
725
- ...contextualSubjectAttributes . numericAttributes ,
726
- ...contextualSubjectAttributes . categoricalAttributes ,
727
- } ;
728
- } else {
729
- // Attributes are non-contextual
730
- result = subjectAttributes as Attributes ;
731
- }
732
- return result ;
733
- }
734
-
735
- private ensureContextualSubjectAttributes (
736
- subjectAttributes : BanditSubjectAttributes ,
737
- ) : ContextAttributes {
738
- if ( this . isInstanceOfContextualAttributes ( subjectAttributes ) ) {
739
- return subjectAttributes as ContextAttributes ;
740
- } else {
741
- return this . deduceAttributeContext ( subjectAttributes as Attributes ) ;
742
- }
743
- }
744
-
745
- private ensureActionsWithContextualAttributes (
746
- actions : BanditActions ,
747
- ) : Record < string , ContextAttributes > {
748
- let result : Record < string , ContextAttributes > = { } ;
749
- if ( Array . isArray ( actions ) ) {
750
- // no context
751
- actions . forEach ( ( action ) => {
752
- result [ action ] = { numericAttributes : { } , categoricalAttributes : { } } ;
753
- } ) ;
754
- } else if ( ! Object . values ( actions ) . every ( this . isInstanceOfContextualAttributes ) ) {
755
- // Actions have non-contextual attributes; bucket based on number or not
756
- Object . entries ( actions ) . forEach ( ( [ action , attributes ] ) => {
757
- result [ action ] = this . deduceAttributeContext ( attributes ) ;
758
- } ) ;
759
- } else {
760
- // Actions already have contextual attributes
761
- result = actions as Record < string , ContextAttributes > ;
762
- }
763
- return result ;
764
- }
765
-
766
- private isInstanceOfContextualAttributes ( attributes : unknown ) : boolean {
767
- return Boolean (
768
- typeof attributes === 'object' &&
769
- attributes && // exclude null
770
- 'numericAttributes' in attributes &&
771
- 'categoricalAttributes' in attributes ,
772
- ) ;
773
- }
774
-
775
- private deduceAttributeContext ( attributes : Attributes ) : ContextAttributes {
776
- const contextualAttributes : ContextAttributes = {
777
- numericAttributes : { } ,
778
- categoricalAttributes : { } ,
779
- } ;
780
- Object . entries ( attributes ) . forEach ( ( [ attribute , value ] ) => {
781
- const isNumeric = typeof value === 'number' && isFinite ( value ) ;
782
- if ( isNumeric ) {
783
- contextualAttributes . numericAttributes [ attribute ] = value ;
784
- } else {
785
- contextualAttributes . categoricalAttributes [ attribute ] = value as AttributeType ;
786
- }
787
- } ) ;
788
- return contextualAttributes ;
789
- }
790
-
791
722
private logBanditAction ( banditEvent : IBanditEvent ) : void {
792
723
// First we check if this bandit action has been logged before
793
724
const subjectKey = banditEvent . subject ;
@@ -944,8 +875,8 @@ export default class EppoClient {
944
875
) : string {
945
876
const configDetails = this . getConfigDetails ( ) ;
946
877
947
- const subjectContextualAttributes = this . ensureContextualSubjectAttributes ( subjectAttributes ) ;
948
- const subjectFlatAttributes = this . ensureNonContextualSubjectAttributes ( subjectAttributes ) ;
878
+ const subjectContextualAttributes = ensureContextualSubjectAttributes ( subjectAttributes ) ;
879
+ const subjectFlatAttributes = ensureNonContextualSubjectAttributes ( subjectAttributes ) ;
949
880
const flags = this . getAllAssignments ( subjectKey , subjectFlatAttributes ) ;
950
881
951
882
const precomputedConfig : IPrecomputedConfiguration = obfuscated
@@ -1095,6 +1026,7 @@ export default class EppoClient {
1095
1026
configPublishedAt : this . flagConfigurationStore . getConfigPublishedAt ( ) ?? '' ,
1096
1027
configEnvironment : this . flagConfigurationStore . getEnvironment ( ) ?? { name : '' } ,
1097
1028
configFormat : this . flagConfigurationStore . getFormat ( ) ?? '' ,
1029
+ salt : this . flagConfigurationStore . salt ,
1098
1030
} ;
1099
1031
}
1100
1032
0 commit comments