@@ -2,7 +2,7 @@ import { createHash } from 'crypto';
2
2
3
3
import { IExperimentConfiguration } from './experiment/experiment-configuration' ;
4
4
import ExperimentConfigurationRequestor from './experiment/experiment-configuration-requestor' ;
5
- import { Rule } from './experiment/ rule' ;
5
+ import { Rule } from './rule' ;
6
6
import { matchesAnyRule } from './rule_evaluator' ;
7
7
import { getShard , isShardInRange } from './shard' ;
8
8
import { validateNotBlank } from './validation' ;
@@ -15,48 +15,57 @@ export interface IEppoClient {
15
15
/**
16
16
* Maps a subject to a variation for a given experiment.
17
17
*
18
+ * @param subjectKey an identifier of the experiment subject, for example a user ID.
18
19
* @param experimentKey experiment identifier
20
+ * @param subjectAttributes optional attributes associated with the subject, for example name and email.
21
+ * The subject attributes are used for evaluating any targeting rules tied to the experiment.
19
22
* @returns a variation value if the subject is part of the experiment sample, otherwise null
20
23
* @public
21
24
*/
22
- getAssignment ( experimentKey : string ) : string ;
25
+ getAssignment (
26
+ subjectKey : string ,
27
+ experimentKey : string ,
28
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
29
+ subjectAttributes ?: Record < string , any > ,
30
+ ) : string ;
23
31
}
24
32
25
33
export default class EppoClient implements IEppoClient {
26
- constructor (
27
- private subjectKey : string ,
28
- private configurationRequestor : ExperimentConfigurationRequestor ,
29
- private subjectAttributes = { } ,
30
- ) { }
34
+ constructor ( private configurationRequestor : ExperimentConfigurationRequestor ) { }
31
35
32
- getAssignment ( experimentKey : string ) : string {
36
+ getAssignment ( subjectKey : string , experimentKey : string , subjectAttributes = { } ) : string {
37
+ validateNotBlank ( subjectKey , 'Invalid argument: subjectKey cannot be blank' ) ;
33
38
validateNotBlank ( experimentKey , 'Invalid argument: experimentKey cannot be blank' ) ;
34
39
const experimentConfig = this . configurationRequestor . getConfiguration ( experimentKey ) ;
35
40
if (
36
41
! experimentConfig ?. enabled ||
37
- ! this . subjectAttributesSatisfyRules ( experimentConfig . rules ) ||
38
- ! this . isInExperimentSample ( experimentKey , experimentConfig )
42
+ ! this . subjectAttributesSatisfyRules ( subjectAttributes , experimentConfig . rules ) ||
43
+ ! this . isInExperimentSample ( subjectKey , experimentKey , experimentConfig )
39
44
) {
40
45
return null ;
41
46
}
42
- const override = this . getSubjectVariationOverride ( experimentConfig ) ;
47
+ const override = this . getSubjectVariationOverride ( subjectKey , experimentConfig ) ;
43
48
if ( override ) {
44
49
return override ;
45
50
}
46
51
const { variations, subjectShards } = experimentConfig ;
47
- const shard = getShard ( `assignment-${ this . subjectKey } -${ experimentKey } ` , subjectShards ) ;
52
+ const shard = getShard ( `assignment-${ subjectKey } -${ experimentKey } ` , subjectShards ) ;
48
53
return variations . find ( ( variation ) => isShardInRange ( shard , variation . shardRange ) ) . name ;
49
54
}
50
55
51
- private subjectAttributesSatisfyRules ( rules ?: Rule [ ] ) {
56
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
57
+ private subjectAttributesSatisfyRules ( subjectAttributes ?: Record < string , any > , rules ?: Rule [ ] ) {
52
58
if ( ! rules || rules . length === 0 ) {
53
59
return true ;
54
60
}
55
- return matchesAnyRule ( this . subjectAttributes || { } , rules ) ;
61
+ return matchesAnyRule ( subjectAttributes || { } , rules ) ;
56
62
}
57
63
58
- private getSubjectVariationOverride ( experimentConfig : IExperimentConfiguration ) : string {
59
- const subjectHash = createHash ( 'md5' ) . update ( this . subjectKey ) . digest ( 'hex' ) ;
64
+ private getSubjectVariationOverride (
65
+ subjectKey : string ,
66
+ experimentConfig : IExperimentConfiguration ,
67
+ ) : string {
68
+ const subjectHash = createHash ( 'md5' ) . update ( subjectKey ) . digest ( 'hex' ) ;
60
69
return experimentConfig . overrides [ subjectHash ] ;
61
70
}
62
71
@@ -66,11 +75,12 @@ export default class EppoClient implements IEppoClient {
66
75
* Given a hash function output (bucket), check whether the bucket is between 0 and exposure_percent * total_buckets.
67
76
*/
68
77
private isInExperimentSample (
78
+ subjectKey : string ,
69
79
experimentKey : string ,
70
80
experimentConfig : IExperimentConfiguration ,
71
81
) : boolean {
72
82
const { percentExposure, subjectShards } = experimentConfig ;
73
- const shard = getShard ( `exposure-${ this . subjectKey } -${ experimentKey } ` , subjectShards ) ;
83
+ const shard = getShard ( `exposure-${ subjectKey } -${ experimentKey } ` , subjectShards ) ;
74
84
return shard <= percentExposure * subjectShards ;
75
85
}
76
86
}
0 commit comments