Skip to content

Commit e5859c6

Browse files
committed
updated as per entry points
1 parent 1aa3fd1 commit e5859c6

File tree

12 files changed

+249
-244
lines changed

12 files changed

+249
-244
lines changed

packages/optimizely-sdk/lib/core/audience_evaluator/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Copyright 2016, 2018-2020, Optimizely
2+
* Copyright 2016, 2018-2020 Optimizely
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -33,7 +33,7 @@ var MODULE_NAME = 'AUDIENCE_EVALUATOR';
3333
* Optimizely evaluators cannot be overridden.
3434
* @constructor
3535
*/
36-
function AudienceEvaluator(UNSTABLE_conditionEvaluators) {
36+
export function AudienceEvaluator(UNSTABLE_conditionEvaluators) {
3737
this.typeToEvaluatorMap = fns.assign({}, UNSTABLE_conditionEvaluators, {
3838
custom_attribute: customAttributeConditionEvaluator,
3939
});

packages/optimizely-sdk/lib/core/audience_evaluator/index.tests.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Copyright 2016, 2018-2020, Optimizely
2+
* Copyright 2016, 2018-2020 Optimizely
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -19,7 +19,6 @@ import conditionTreeEvaluator from '../condition_tree_evaluator';
1919
import customAttributeConditionEvaluator from '../custom_attribute_condition_evaluator';
2020
import sinon from 'sinon';
2121
import { getLogger } from '@optimizely/js-sdk-logging';
22-
import enums from '../../utils/enums';
2322

2423
var assert = chai.assert;
2524
var mockLogger = getLogger();
Lines changed: 152 additions & 147 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Copyright 2016, 2019 Optimizely
2+
* Copyright 2016, 2019-2020 Optimizely
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -17,9 +17,9 @@
1717
/**
1818
* Bucketer API for determining the variation id from the specified parameters
1919
*/
20-
var enums = require('../../utils/enums');
21-
var murmurhash = require('murmurhash');
22-
var sprintf = require('@optimizely/js-sdk-utils').sprintf;
20+
import enums from '../../utils/enums';
21+
import murmurhash from 'murmurhash';
22+
import { sprintf } from '@optimizely/js-sdk-utils';
2323

2424
var ERROR_MESSAGES = enums.ERROR_MESSAGES;
2525
var HASH_SEED = 1;
@@ -30,166 +30,171 @@ var MAX_TRAFFIC_VALUE = 10000;
3030
var MODULE_NAME = 'BUCKETER';
3131
var RANDOM_POLICY = 'random';
3232

33-
module.exports = {
34-
/**
35-
* Determines ID of variation to be shown for the given input params
36-
* @param {Object} bucketerParams
37-
* @param {string} bucketerParams.experimentId
38-
* @param {string} bucketerParams.experimentKey
39-
* @param {string} bucketerParams.userId
40-
* @param {Object[]} bucketerParams.trafficAllocationConfig
41-
* @param {Array} bucketerParams.experimentKeyMap
42-
* @param {Object} bucketerParams.groupIdMap
43-
* @param {Object} bucketerParams.variationIdMap
44-
* @param {string} bucketerParams.varationIdMap[].key
45-
* @param {Object} bucketerParams.logger
46-
* @param {string} bucketerParams.bucketingId
47-
* @return Variation ID that user has been bucketed into, null if user is not bucketed into any experiment
48-
*/
49-
bucket: function(bucketerParams) {
50-
// Check if user is in a random group; if so, check if user is bucketed into a specific experiment
51-
var experiment = bucketerParams.experimentKeyMap[bucketerParams.experimentKey];
52-
var groupId = experiment['groupId'];
53-
if (groupId) {
54-
var group = bucketerParams.groupIdMap[groupId];
55-
if (!group) {
56-
throw new Error(sprintf(ERROR_MESSAGES.INVALID_GROUP_ID, MODULE_NAME, groupId));
57-
}
58-
if (group.policy === RANDOM_POLICY) {
59-
var bucketedExperimentId = module.exports.bucketUserIntoExperiment(
60-
group,
61-
bucketerParams.bucketingId,
33+
/**
34+
* Determines ID of variation to be shown for the given input params
35+
* @param {Object} bucketerParams
36+
* @param {string} bucketerParams.experimentId
37+
* @param {string} bucketerParams.experimentKey
38+
* @param {string} bucketerParams.userId
39+
* @param {Object[]} bucketerParams.trafficAllocationConfig
40+
* @param {Array} bucketerParams.experimentKeyMap
41+
* @param {Object} bucketerParams.groupIdMap
42+
* @param {Object} bucketerParams.variationIdMap
43+
* @param {string} bucketerParams.varationIdMap[].key
44+
* @param {Object} bucketerParams.logger
45+
* @param {string} bucketerParams.bucketingId
46+
* @return Variation ID that user has been bucketed into, null if user is not bucketed into any experiment
47+
*/
48+
export var bucket = function(bucketerParams) {
49+
// Check if user is in a random group; if so, check if user is bucketed into a specific experiment
50+
var experiment = bucketerParams.experimentKeyMap[bucketerParams.experimentKey];
51+
var groupId = experiment['groupId'];
52+
if (groupId) {
53+
var group = bucketerParams.groupIdMap[groupId];
54+
if (!group) {
55+
throw new Error(sprintf(ERROR_MESSAGES.INVALID_GROUP_ID, MODULE_NAME, groupId));
56+
}
57+
if (group.policy === RANDOM_POLICY) {
58+
var bucketedExperimentId = this.bucketUserIntoExperiment(
59+
group,
60+
bucketerParams.bucketingId,
61+
bucketerParams.userId,
62+
bucketerParams.logger
63+
);
64+
65+
// Return if user is not bucketed into any experiment
66+
if (bucketedExperimentId === null) {
67+
var notbucketedInAnyExperimentLogMessage = sprintf(
68+
LOG_MESSAGES.USER_NOT_IN_ANY_EXPERIMENT,
69+
MODULE_NAME,
6270
bucketerParams.userId,
63-
bucketerParams.logger
71+
groupId
6472
);
73+
bucketerParams.logger.log(LOG_LEVEL.INFO, notbucketedInAnyExperimentLogMessage);
74+
return null;
75+
}
6576

66-
// Return if user is not bucketed into any experiment
67-
if (bucketedExperimentId === null) {
68-
var notbucketedInAnyExperimentLogMessage = sprintf(
69-
LOG_MESSAGES.USER_NOT_IN_ANY_EXPERIMENT,
70-
MODULE_NAME,
71-
bucketerParams.userId,
72-
groupId
73-
);
74-
bucketerParams.logger.log(LOG_LEVEL.INFO, notbucketedInAnyExperimentLogMessage);
75-
return null;
76-
}
77-
78-
// Return if user is bucketed into a different experiment than the one specified
79-
if (bucketedExperimentId !== bucketerParams.experimentId) {
80-
var notBucketedIntoExperimentOfGroupLogMessage = sprintf(
81-
LOG_MESSAGES.USER_NOT_BUCKETED_INTO_EXPERIMENT_IN_GROUP,
82-
MODULE_NAME,
83-
bucketerParams.userId,
84-
bucketerParams.experimentKey,
85-
groupId
86-
);
87-
bucketerParams.logger.log(LOG_LEVEL.INFO, notBucketedIntoExperimentOfGroupLogMessage);
88-
return null;
89-
}
90-
91-
// Continue bucketing if user is bucketed into specified experiment
92-
var bucketedIntoExperimentOfGroupLogMessage = sprintf(
93-
LOG_MESSAGES.USER_BUCKETED_INTO_EXPERIMENT_IN_GROUP,
77+
// Return if user is bucketed into a different experiment than the one specified
78+
if (bucketedExperimentId !== bucketerParams.experimentId) {
79+
var notBucketedIntoExperimentOfGroupLogMessage = sprintf(
80+
LOG_MESSAGES.USER_NOT_BUCKETED_INTO_EXPERIMENT_IN_GROUP,
9481
MODULE_NAME,
9582
bucketerParams.userId,
9683
bucketerParams.experimentKey,
9784
groupId
9885
);
99-
bucketerParams.logger.log(LOG_LEVEL.INFO, bucketedIntoExperimentOfGroupLogMessage);
86+
bucketerParams.logger.log(LOG_LEVEL.INFO, notBucketedIntoExperimentOfGroupLogMessage);
87+
return null;
10088
}
101-
}
102-
var bucketingId = sprintf('%s%s', bucketerParams.bucketingId, bucketerParams.experimentId);
103-
var bucketValue = module.exports._generateBucketValue(bucketingId);
10489

105-
var bucketedUserLogMessage = sprintf(
106-
LOG_MESSAGES.USER_ASSIGNED_TO_VARIATION_BUCKET,
107-
MODULE_NAME,
108-
bucketValue,
109-
bucketerParams.userId
110-
);
111-
bucketerParams.logger.log(LOG_LEVEL.DEBUG, bucketedUserLogMessage);
112-
113-
var entityId = module.exports._findBucket(bucketValue, bucketerParams.trafficAllocationConfig);
114-
if (!entityId) {
115-
var userHasNoVariationLogMessage = sprintf(
116-
LOG_MESSAGES.USER_HAS_NO_VARIATION,
90+
// Continue bucketing if user is bucketed into specified experiment
91+
var bucketedIntoExperimentOfGroupLogMessage = sprintf(
92+
LOG_MESSAGES.USER_BUCKETED_INTO_EXPERIMENT_IN_GROUP,
11793
MODULE_NAME,
11894
bucketerParams.userId,
119-
bucketerParams.experimentKey
95+
bucketerParams.experimentKey,
96+
groupId
12097
);
121-
bucketerParams.logger.log(LOG_LEVEL.DEBUG, userHasNoVariationLogMessage);
122-
} else if (!bucketerParams.variationIdMap.hasOwnProperty(entityId)) {
123-
var invalidVariationIdLogMessage = sprintf(LOG_MESSAGES.INVALID_VARIATION_ID, MODULE_NAME);
124-
bucketerParams.logger.log(LOG_LEVEL.WARNING, invalidVariationIdLogMessage);
125-
return null;
126-
} else {
127-
var variationKey = bucketerParams.variationIdMap[entityId].key;
128-
var userInVariationLogMessage = sprintf(
129-
LOG_MESSAGES.USER_HAS_VARIATION,
130-
MODULE_NAME,
131-
bucketerParams.userId,
132-
variationKey,
133-
bucketerParams.experimentKey
134-
);
135-
bucketerParams.logger.log(LOG_LEVEL.INFO, userInVariationLogMessage);
98+
bucketerParams.logger.log(LOG_LEVEL.INFO, bucketedIntoExperimentOfGroupLogMessage);
13699
}
100+
}
101+
var bucketingId = sprintf('%s%s', bucketerParams.bucketingId, bucketerParams.experimentId);
102+
var bucketValue = this._generateBucketValue(bucketingId);
137103

138-
return entityId;
139-
},
104+
var bucketedUserLogMessage = sprintf(
105+
LOG_MESSAGES.USER_ASSIGNED_TO_VARIATION_BUCKET,
106+
MODULE_NAME,
107+
bucketValue,
108+
bucketerParams.userId
109+
);
110+
bucketerParams.logger.log(LOG_LEVEL.DEBUG, bucketedUserLogMessage);
140111

141-
/**
142-
* Returns bucketed experiment ID to compare against experiment user is being called into
143-
* @param {Object} group Group that experiment is in
144-
* @param {string} bucketingId Bucketing ID
145-
* @param {string} userId ID of user to be bucketed into experiment
146-
* @param {Object} logger Logger implementation
147-
* @return {string} ID of experiment if user is bucketed into experiment within the group, null otherwise
148-
*/
149-
bucketUserIntoExperiment: function(group, bucketingId, userId, logger) {
150-
var bucketingKey = sprintf('%s%s', bucketingId, group.id);
151-
var bucketValue = module.exports._generateBucketValue(bucketingKey);
152-
logger.log(
153-
LOG_LEVEL.DEBUG,
154-
sprintf(LOG_MESSAGES.USER_ASSIGNED_TO_EXPERIMENT_BUCKET, MODULE_NAME, bucketValue, userId)
112+
var entityId = this._findBucket(bucketValue, bucketerParams.trafficAllocationConfig);
113+
if (!entityId) {
114+
var userHasNoVariationLogMessage = sprintf(
115+
LOG_MESSAGES.USER_HAS_NO_VARIATION,
116+
MODULE_NAME,
117+
bucketerParams.userId,
118+
bucketerParams.experimentKey
155119
);
156-
var trafficAllocationConfig = group.trafficAllocation;
157-
var bucketedExperimentId = module.exports._findBucket(bucketValue, trafficAllocationConfig);
158-
return bucketedExperimentId;
159-
},
160-
161-
/**
162-
* Returns entity ID associated with bucket value
163-
* @param {string} bucketValue
164-
* @param {Object[]} trafficAllocationConfig
165-
* @param {number} trafficAllocationConfig[].endOfRange
166-
* @param {number} trafficAllocationConfig[].entityId
167-
* @return {string} Entity ID for bucketing if bucket value is within traffic allocation boundaries, null otherwise
168-
*/
169-
_findBucket: function(bucketValue, trafficAllocationConfig) {
170-
for (var i = 0; i < trafficAllocationConfig.length; i++) {
171-
if (bucketValue < trafficAllocationConfig[i].endOfRange) {
172-
return trafficAllocationConfig[i].entityId;
173-
}
174-
}
120+
bucketerParams.logger.log(LOG_LEVEL.DEBUG, userHasNoVariationLogMessage);
121+
} else if (!bucketerParams.variationIdMap.hasOwnProperty(entityId)) {
122+
var invalidVariationIdLogMessage = sprintf(LOG_MESSAGES.INVALID_VARIATION_ID, MODULE_NAME);
123+
bucketerParams.logger.log(LOG_LEVEL.WARNING, invalidVariationIdLogMessage);
175124
return null;
176-
},
125+
} else {
126+
var variationKey = bucketerParams.variationIdMap[entityId].key;
127+
var userInVariationLogMessage = sprintf(
128+
LOG_MESSAGES.USER_HAS_VARIATION,
129+
MODULE_NAME,
130+
bucketerParams.userId,
131+
variationKey,
132+
bucketerParams.experimentKey
133+
);
134+
bucketerParams.logger.log(LOG_LEVEL.INFO, userInVariationLogMessage);
135+
}
177136

178-
/**
179-
* Helper function to generate bucket value in half-closed interval [0, MAX_TRAFFIC_VALUE)
180-
* @param {string} bucketingKey String value for bucketing
181-
* @return {string} the generated bucket value
182-
* @throws If bucketing value is not a valid string
183-
*/
184-
_generateBucketValue: function(bucketingKey) {
185-
try {
186-
// NOTE: the mmh library already does cast the hash value as an unsigned 32bit int
187-
// https://github.com/perezd/node-murmurhash/blob/master/murmurhash.js#L115
188-
var hashValue = murmurhash.v3(bucketingKey, HASH_SEED);
189-
var ratio = hashValue / MAX_HASH_VALUE;
190-
return parseInt(ratio * MAX_TRAFFIC_VALUE, 10);
191-
} catch (ex) {
192-
throw new Error(sprintf(ERROR_MESSAGES.INVALID_BUCKETING_ID, MODULE_NAME, bucketingKey, ex.message));
137+
return entityId;
138+
}
139+
140+
/**
141+
* Returns bucketed experiment ID to compare against experiment user is being called into
142+
* @param {Object} group Group that experiment is in
143+
* @param {string} bucketingId Bucketing ID
144+
* @param {string} userId ID of user to be bucketed into experiment
145+
* @param {Object} logger Logger implementation
146+
* @return {string} ID of experiment if user is bucketed into experiment within the group, null otherwise
147+
*/
148+
export var bucketUserIntoExperiment = function(group, bucketingId, userId, logger) {
149+
var bucketingKey = sprintf('%s%s', bucketingId, group.id);
150+
var bucketValue = this._generateBucketValue(bucketingKey);
151+
logger.log(
152+
LOG_LEVEL.DEBUG,
153+
sprintf(LOG_MESSAGES.USER_ASSIGNED_TO_EXPERIMENT_BUCKET, MODULE_NAME, bucketValue, userId)
154+
);
155+
var trafficAllocationConfig = group.trafficAllocation;
156+
var bucketedExperimentId = this._findBucket(bucketValue, trafficAllocationConfig);
157+
return bucketedExperimentId;
158+
}
159+
160+
/**
161+
* Returns entity ID associated with bucket value
162+
* @param {string} bucketValue
163+
* @param {Object[]} trafficAllocationConfig
164+
* @param {number} trafficAllocationConfig[].endOfRange
165+
* @param {number} trafficAllocationConfig[].entityId
166+
* @return {string} Entity ID for bucketing if bucket value is within traffic allocation boundaries, null otherwise
167+
*/
168+
export var _findBucket = function(bucketValue, trafficAllocationConfig) {
169+
for (var i = 0; i < trafficAllocationConfig.length; i++) {
170+
if (bucketValue < trafficAllocationConfig[i].endOfRange) {
171+
return trafficAllocationConfig[i].entityId;
193172
}
194-
},
195-
};
173+
}
174+
return null;
175+
}
176+
177+
/**
178+
* Helper function to generate bucket value in half-closed interval [0, MAX_TRAFFIC_VALUE)
179+
* @param {string} bucketingKey String value for bucketing
180+
* @return {string} the generated bucket value
181+
* @throws If bucketing value is not a valid string
182+
*/
183+
export var _generateBucketValue = function(bucketingKey) {
184+
try {
185+
// NOTE: the mmh library already does cast the hash value as an unsigned 32bit int
186+
// https://github.com/perezd/node-murmurhash/blob/master/murmurhash.js#L115
187+
var hashValue = murmurhash.v3(bucketingKey, HASH_SEED);
188+
var ratio = hashValue / MAX_HASH_VALUE;
189+
return parseInt(ratio * MAX_TRAFFIC_VALUE, 10);
190+
} catch (ex) {
191+
throw new Error(sprintf(ERROR_MESSAGES.INVALID_BUCKETING_ID, MODULE_NAME, bucketingKey, ex.message));
192+
}
193+
}
194+
195+
export default {
196+
bucket,
197+
bucketUserIntoExperiment,
198+
_findBucket,
199+
_generateBucketValue,
200+
}

0 commit comments

Comments
 (0)