@optimizely/optimizely-sdk
Advanced tools
Comparing version
@@ -0,1 +1,6 @@ | ||
## 2.1.0 | ||
May 24, 2018 | ||
* Introduces support for bot filtering. | ||
## 2.0.3 | ||
@@ -2,0 +7,0 @@ May 24, 2018 |
/**************************************************************************** | ||
* Copyright 2017, Optimizely, Inc. and contributors * | ||
* Copyright 2017-2018, Optimizely, Inc. and contributors * | ||
* * | ||
@@ -29,3 +29,2 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * | ||
var LOG_MESSAGES = enums.LOG_MESSAGES; | ||
var RESERVED_ATTRIBUTE_KEY_BUCKETING_ID = '$opt_bucketing_id'; | ||
var DECISION_SOURCES = enums.DECISION_SOURCES; | ||
@@ -71,4 +70,4 @@ | ||
if (!fns.isEmpty(attributes)) { | ||
if (attributes.hasOwnProperty(RESERVED_ATTRIBUTE_KEY_BUCKETING_ID)) { | ||
bucketingId = attributes[RESERVED_ATTRIBUTE_KEY_BUCKETING_ID]; | ||
if (attributes.hasOwnProperty(enums.CONTROL_ATTRIBUTES.BUCKETING_ID)) { | ||
bucketingId = attributes[enums.CONTROL_ATTRIBUTES.BUCKETING_ID]; | ||
this.logger.log(LOG_LEVEL.DEBUG, sprintf('Setting the bucketing ID to %s.', bucketingId)) | ||
@@ -75,0 +74,0 @@ } |
/** | ||
* Copyright 2016-2017, Optimizely | ||
* Copyright 2016-2018, Optimizely | ||
* | ||
@@ -33,2 +33,3 @@ * Licensed under the Apache License, Version 2.0 (the "License"); | ||
* @param {string} options.userId ID for user | ||
* @param {Object} options.Logger logger | ||
* @return {Object} Common params with properties that are used in both conversion and impression events | ||
@@ -40,3 +41,3 @@ */ | ||
var anonymize_ip = configObj.anonymizeIP; | ||
var botFiltering = configObj.botFiltering; | ||
if (anonymize_ip === null || anonymize_ip === undefined) { | ||
@@ -63,5 +64,5 @@ anonymize_ip = false; | ||
fns.forOwn(attributes, function(attributeValue, attributeKey){ | ||
var attributeId = projectConfig.getAttributeId(options.configObj, attributeKey); | ||
var attributeId = projectConfig.getAttributeId(options.configObj, attributeKey, options.logger); | ||
if (attributeId) { | ||
var feature = { | ||
commonParams.visitors[0].attributes.push({ | ||
entity_id: attributeId, | ||
@@ -71,6 +72,14 @@ key: attributeKey, | ||
value: attributes[attributeKey], | ||
}; | ||
commonParams.visitors[0].attributes.push(feature); | ||
}); | ||
} | ||
}); | ||
if (typeof botFiltering === 'boolean') { | ||
commonParams.visitors[0].attributes.push({ | ||
entity_id: enums.CONTROL_ATTRIBUTES.BOT_FILTERING, | ||
key: enums.CONTROL_ATTRIBUTES.BOT_FILTERING, | ||
type: CUSTOM_ATTRIBUTE_FEATURE_TYPE, | ||
value: botFiltering, | ||
}); | ||
}; | ||
return commonParams; | ||
@@ -77,0 +86,0 @@ } |
/** | ||
* Copyright 2016-2017, Optimizely | ||
* Copyright 2016-2018, Optimizely | ||
* | ||
@@ -30,2 +30,3 @@ * Licensed under the Apache License, Version 2.0 (the "License"); | ||
var mockLogger; | ||
var configObj; | ||
@@ -38,2 +39,5 @@ var clock; | ||
sinon.stub(uuid, 'v4').returns('a68cf1ad-0393-4e18-af87-efe8f01a7c9c'); | ||
mockLogger = { | ||
log: sinon.stub(), | ||
}; | ||
}); | ||
@@ -284,2 +288,3 @@ | ||
userId: 'testUser', | ||
logger: mockLogger, | ||
}; | ||
@@ -291,13 +296,120 @@ | ||
}); | ||
}); | ||
it('should fill in userFeatures for user agent and bot filtering (bot filtering enabled)', function() { | ||
var v4ConfigObj = projectConfig.createProjectConfig(testData.getTestProjectConfigWithFeatures()); | ||
var expectedParams = { | ||
url: 'https://logx.optimizely.com/v1/events', | ||
httpVerb: 'POST', | ||
params: { | ||
'account_id': '572018', | ||
'project_id': '594001', | ||
'visitors': [{ | ||
'attributes': [{ | ||
'entity_id': '$opt_user_agent', | ||
'key': '$opt_user_agent', | ||
'type': 'custom', | ||
'value': 'Chrome' | ||
}, { | ||
'entity_id': '$opt_bot_filtering', | ||
'key': '$opt_bot_filtering', | ||
'type': 'custom', | ||
'value': true | ||
}], | ||
'visitor_id': 'testUser', | ||
'snapshots': [{ | ||
'decisions': [{ | ||
'variation_id': '595008', | ||
'experiment_id': '595010', | ||
'campaign_id': '595005' | ||
}], | ||
'events': [{ | ||
'timestamp': Math.round(new Date().getTime()), | ||
'entity_id': '595005', | ||
'uuid': 'a68cf1ad-0393-4e18-af87-efe8f01a7c9c', | ||
'key': 'campaign_activated' | ||
}] | ||
}] | ||
}], | ||
'revision': '35', | ||
'client_name': 'node-sdk', | ||
'client_version': packageJSON.version, | ||
'anonymize_ip': true, | ||
} | ||
}; | ||
describe('getConversionEvent', function() { | ||
var mockLogger; | ||
var eventOptions = { | ||
attributes: {'$opt_user_agent': 'Chrome'}, | ||
clientEngine: 'node-sdk', | ||
clientVersion: packageJSON.version, | ||
configObj: v4ConfigObj, | ||
experimentId: '595010', | ||
variationId: '595008', | ||
userId: 'testUser', | ||
}; | ||
beforeEach(function() { | ||
mockLogger = { | ||
log: sinon.stub(), | ||
var actualParams = eventBuilder.getImpressionEvent(eventOptions); | ||
assert.deepEqual(actualParams, expectedParams); | ||
}); | ||
it('should fill in userFeatures for user agent and bot filtering (bot filtering disabled)', function() { | ||
var v4ConfigObj = projectConfig.createProjectConfig(testData.getTestProjectConfigWithFeatures()); | ||
v4ConfigObj.botFiltering = false; | ||
var expectedParams = { | ||
url: 'https://logx.optimizely.com/v1/events', | ||
httpVerb: 'POST', | ||
params: { | ||
'account_id': '572018', | ||
'project_id': '594001', | ||
'visitors': [{ | ||
'attributes': [{ | ||
'entity_id': '$opt_user_agent', | ||
'key': '$opt_user_agent', | ||
'type': 'custom', | ||
'value': 'Chrome' | ||
}, { | ||
'entity_id': '$opt_bot_filtering', | ||
'key': '$opt_bot_filtering', | ||
'type': 'custom', | ||
'value': false | ||
}], | ||
'visitor_id': 'testUser', | ||
'snapshots': [{ | ||
'decisions': [{ | ||
'variation_id': '595008', | ||
'experiment_id': '595010', | ||
'campaign_id': '595005' | ||
}], | ||
'events': [{ | ||
'timestamp': Math.round(new Date().getTime()), | ||
'entity_id': '595005', | ||
'uuid': 'a68cf1ad-0393-4e18-af87-efe8f01a7c9c', | ||
'key': 'campaign_activated' | ||
}] | ||
}] | ||
}], | ||
'revision': '35', | ||
'client_name': 'node-sdk', | ||
'client_version': packageJSON.version, | ||
'anonymize_ip': true, | ||
} | ||
}; | ||
var eventOptions = { | ||
attributes: {'$opt_user_agent': 'Chrome'}, | ||
clientEngine: 'node-sdk', | ||
clientVersion: packageJSON.version, | ||
configObj: v4ConfigObj, | ||
experimentId: '595010', | ||
variationId: '595008', | ||
userId: 'testUser', | ||
}; | ||
var actualParams = eventBuilder.getImpressionEvent(eventOptions); | ||
assert.deepEqual(actualParams, expectedParams); | ||
}); | ||
}); | ||
describe('getConversionEvent', function() { | ||
it('should create proper params for getConversionEvent without attributes or event value', function() { | ||
@@ -556,6 +668,123 @@ var expectedParams = { | ||
var actualParams = eventBuilder.getConversionEvent(eventOptions); | ||
sinon.assert.calledOnce(mockLogger.log); | ||
assert.deepEqual(actualParams, expectedParams); | ||
}); | ||
it('should fill in userFeatures for user agent and bot filtering (bot filtering enabled)', function() { | ||
var v4ConfigObj = projectConfig.createProjectConfig(testData.getTestProjectConfigWithFeatures()); | ||
var expectedParams = { | ||
url: 'https://logx.optimizely.com/v1/events', | ||
httpVerb: 'POST', | ||
params: { | ||
'account_id': '572018', | ||
'project_id': '594001', | ||
'visitors': [{ | ||
'attributes': [{ | ||
'entity_id': '$opt_user_agent', | ||
'key': '$opt_user_agent', | ||
'type': 'custom', | ||
'value': 'Chrome' | ||
}, { | ||
'entity_id': '$opt_bot_filtering', | ||
'key': '$opt_bot_filtering', | ||
'type': 'custom', | ||
'value': true | ||
}], | ||
'visitor_id': 'testUser', | ||
'snapshots': [{ | ||
'decisions': [{ | ||
'variation_id': '595008', | ||
'experiment_id': '595010', | ||
'campaign_id': '595005' | ||
}], | ||
'events': [{ | ||
'timestamp': Math.round(new Date().getTime()), | ||
'entity_id': '594089', | ||
'uuid': 'a68cf1ad-0393-4e18-af87-efe8f01a7c9c', | ||
'key': 'item_bought' | ||
}] | ||
}] | ||
}], | ||
'revision': '35', | ||
'client_name': 'node-sdk', | ||
'client_version': packageJSON.version, | ||
'anonymize_ip': true, | ||
} | ||
}; | ||
var eventOptions = { | ||
attributes: {'$opt_user_agent': 'Chrome'}, | ||
clientEngine: 'node-sdk', | ||
clientVersion: packageJSON.version, | ||
configObj: v4ConfigObj, | ||
eventKey: 'item_bought', | ||
logger: mockLogger, | ||
experimentsToVariationMap: { '595010': '595008' }, | ||
userId: 'testUser', | ||
}; | ||
var actualParams = eventBuilder.getConversionEvent(eventOptions); | ||
assert.deepEqual(actualParams, expectedParams); | ||
}); | ||
it('should fill in userFeatures for user agent and bot filtering (bot filtering disabled)', function() { | ||
var v4ConfigObj = projectConfig.createProjectConfig(testData.getTestProjectConfigWithFeatures()); | ||
v4ConfigObj.botFiltering = false; | ||
var expectedParams = { | ||
url: 'https://logx.optimizely.com/v1/events', | ||
httpVerb: 'POST', | ||
params: { | ||
'account_id': '572018', | ||
'project_id': '594001', | ||
'visitors': [{ | ||
'attributes': [{ | ||
'entity_id': '$opt_user_agent', | ||
'key': '$opt_user_agent', | ||
'type': 'custom', | ||
'value': 'Chrome' | ||
}, { | ||
'entity_id': '$opt_bot_filtering', | ||
'key': '$opt_bot_filtering', | ||
'type': 'custom', | ||
'value': false | ||
}], | ||
'visitor_id': 'testUser', | ||
'snapshots': [{ | ||
'decisions': [{ | ||
'variation_id': '595008', | ||
'experiment_id': '595010', | ||
'campaign_id': '595005' | ||
}], | ||
'events': [{ | ||
'timestamp': Math.round(new Date().getTime()), | ||
'entity_id': '594089', | ||
'uuid': 'a68cf1ad-0393-4e18-af87-efe8f01a7c9c', | ||
'key': 'item_bought' | ||
}] | ||
}] | ||
}], | ||
'revision': '35', | ||
'client_name': 'node-sdk', | ||
'client_version': packageJSON.version, | ||
'anonymize_ip': true, | ||
} | ||
}; | ||
var eventOptions = { | ||
attributes: {'$opt_user_agent': 'Chrome'}, | ||
clientEngine: 'node-sdk', | ||
clientVersion: packageJSON.version, | ||
configObj: v4ConfigObj, | ||
eventKey: 'item_bought', | ||
logger: mockLogger, | ||
experimentsToVariationMap: { '595010': '595008' }, | ||
userId: 'testUser', | ||
}; | ||
var actualParams = eventBuilder.getConversionEvent(eventOptions); | ||
assert.deepEqual(actualParams, expectedParams); | ||
}); | ||
describe('and event tags are passed it', function() { | ||
@@ -842,10 +1071,2 @@ it('should create proper params for getConversionEvent with event tags', function() { | ||
describe('createEventWithBucketingId', function () { | ||
var mockLogger; | ||
beforeEach(function () { | ||
mockLogger = { | ||
log: sinon.stub(), | ||
}; | ||
}); | ||
it('should send proper bucketingID with user attributes', function () { | ||
@@ -860,3 +1081,8 @@ var expectedParams = { | ||
'visitor_id': 'testUser', | ||
'attributes': [], | ||
'attributes': [{ | ||
'entity_id': '$opt_bucketing_id', | ||
'key': '$opt_bucketing_id', | ||
'type': 'custom', | ||
'value': 'variation', | ||
}], | ||
'snapshots': [{ | ||
@@ -891,3 +1117,3 @@ 'decisions': [{ | ||
userId: 'testUser', | ||
attributes: {'Optimizely Bucketing ID': 'variation'}, | ||
attributes: {'$opt_bucketing_id': 'variation'}, | ||
}; | ||
@@ -894,0 +1120,0 @@ |
@@ -22,2 +22,3 @@ /** | ||
var EXPERIMENT_RUNNING_STATUS = 'Running'; | ||
var RESERVED_ATTRIBUTE_PREFIX = '$opt_'; | ||
var MODULE_NAME = 'PROJECT_CONFIG'; | ||
@@ -136,9 +137,19 @@ | ||
* @param {string} attributeKey Attribute key for which ID is to be determined | ||
* @return {string|null} Attribute ID corresponding to the provided attribute key | ||
* @param {Object} logger | ||
* @return {string|null} Attribute ID corresponding to the provided attribute key. Attribute key if it is a reserved attribute. | ||
*/ | ||
getAttributeId: function(projectConfig, attributeKey) { | ||
getAttributeId: function(projectConfig, attributeKey, logger) { | ||
var attribute = projectConfig.attributeKeyMap[attributeKey]; | ||
var hasReservedPrefix = attributeKey.indexOf(RESERVED_ATTRIBUTE_PREFIX) === 0; | ||
if (attribute) { | ||
if (hasReservedPrefix) { | ||
logger.log(LOG_LEVEL.WARN, | ||
sprintf('Attribute %s unexpectedly has reserved prefix %s; using attribute ID instead of reserved attribute name.', attributeKey, RESERVED_ATTRIBUTE_PREFIX)); | ||
} | ||
return attribute.id; | ||
} else if (hasReservedPrefix) { | ||
return attributeKey; | ||
} | ||
logger.log(LOG_LEVEL.DEBUG, sprintf(ERROR_MESSAGES.UNRECOGNIZED_ATTRIBUTE, MODULE_NAME, attributeKey)); | ||
return null; | ||
@@ -145,0 +156,0 @@ }, |
@@ -219,7 +219,13 @@ /** | ||
var configObj; | ||
var createdLogger = logger.createLogger({logLevel: LOG_LEVEL.INFO}); | ||
beforeEach(function() { | ||
configObj = projectConfig.createProjectConfig(testData); | ||
sinon.stub(createdLogger, 'log'); | ||
}); | ||
afterEach(function() { | ||
createdLogger.log.restore(); | ||
}); | ||
it('should retrieve experiment ID for valid experiment key in getExperimentId', function() { | ||
@@ -250,6 +256,25 @@ assert.strictEqual(projectConfig.getExperimentId(configObj, testData.experiments[0].key), | ||
it('should retrieve attribute ID for reserved attribute key in getAttributeId', function() { | ||
assert.strictEqual(projectConfig.getAttributeId(configObj, '$opt_user_agent'), '$opt_user_agent'); | ||
}); | ||
it('should return null for invalid attribute key in getAttributeId', function() { | ||
assert.isNull(projectConfig.getAttributeId(configObj, 'invalidAttributeKey')); | ||
assert.isNull(projectConfig.getAttributeId(configObj, 'invalidAttributeKey', createdLogger)); | ||
sinon.assert.calledWithExactly(createdLogger.log, | ||
LOG_LEVEL.DEBUG, | ||
'PROJECT_CONFIG: Unrecognized attribute invalidAttributeKey provided. Pruning before sending event to Optimizely.') | ||
}); | ||
it('should return null for invalid attribute key in getAttributeId', function() { | ||
// Adding attribute in key map with reserved prefix | ||
configObj.attributeKeyMap['$opt_some_reserved_attribute'] = { | ||
id: '42', | ||
key: '$opt_some_reserved_attribute' | ||
}; | ||
assert.strictEqual(projectConfig.getAttributeId(configObj, '$opt_some_reserved_attribute', createdLogger), '42'); | ||
sinon.assert.calledWithExactly(createdLogger.log, | ||
LOG_LEVEL.WARN, | ||
'Attribute $opt_some_reserved_attribute unexpectedly has reserved prefix $opt_; using attribute ID instead of reserved attribute name.') | ||
}); | ||
it('should retrieve event ID for valid event key in getEventId', function() { | ||
@@ -256,0 +281,0 @@ assert.strictEqual(projectConfig.getEventId(configObj, 'testEvent'), '111095'); |
@@ -184,2 +184,3 @@ /**************************************************************************** | ||
variationId: variationId, | ||
logger: this.logger, | ||
}; | ||
@@ -186,0 +187,0 @@ var impressionEvent = eventBuilder.getImpressionEvent(impressionEventOptions); |
/** | ||
* Copyright 2016-2017, Optimizely | ||
* Copyright 2016-2018, Optimizely | ||
* | ||
@@ -534,2 +534,3 @@ * Licensed under the Apache License, Version 2.0 (the "License"); | ||
'anonymizeIP': true, | ||
'botFiltering': true, | ||
'audiences': [ | ||
@@ -536,0 +537,0 @@ { |
@@ -53,2 +53,3 @@ /**************************************************************************** | ||
UNDEFINED_ATTRIBUTE: '%s: Provided attribute: %s has an undefined value.', | ||
UNRECOGNIZED_ATTRIBUTE: '%s: Unrecognized attribute %s provided. Pruning before sending event to Optimizely.', | ||
UNABLE_TO_CAST_VALUE: '%s: Unable to cast value %s to type %s, returning null.', | ||
@@ -132,5 +133,11 @@ USER_NOT_IN_FORCED_VARIATION: '%s: User %s is not in the forced variation map. Cannot remove their forced variation.', | ||
exports.CONTROL_ATTRIBUTES = { | ||
BOT_FILTERING: '$opt_bot_filtering', | ||
BUCKETING_ID: '$opt_bucketing_id', | ||
USER_AGENT: '$opt_user_agent', | ||
}; | ||
exports.JAVASCRIPT_CLIENT_ENGINE = 'javascript-sdk'; | ||
exports.NODE_CLIENT_ENGINE = 'node-sdk'; | ||
exports.NODE_CLIENT_VERSION = '2.0.3'; | ||
exports.NODE_CLIENT_VERSION = '2.1.0'; | ||
@@ -137,0 +144,0 @@ /* |
{ | ||
"name": "@optimizely/optimizely-sdk", | ||
"version": "2.0.3", | ||
"version": "2.1.0", | ||
"description": "JavaScript SDK package for Optimizely X Full Stack", | ||
@@ -5,0 +5,0 @@ "main": "dist/optimizely.node.js", |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
1641284
1%38627
0.9%