Skip to content

Commit

Permalink
feat: Support for Sending Flag Decisions (#215)
Browse files Browse the repository at this point in the history
  • Loading branch information
zashraf1985 authored Oct 16, 2020
1 parent 9aef7ba commit 12a0ae3
Show file tree
Hide file tree
Showing 10 changed files with 221 additions and 32 deletions.
35 changes: 34 additions & 1 deletion src/Optimizely/Config/DatafileProjectConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,13 @@ class DatafileProjectConfig implements ProjectConfigInterface
*/
private $_experimentFeatureMap;

/**
* Boolean indicating if flag decisions should be sent to server or not
*
* @return boolean
*/
private $_sendFlagDecisions;

/**
* DatafileProjectConfig constructor to load and set project configuration data.
*
Expand All @@ -216,6 +223,7 @@ public function __construct($datafile, $logger, $errorHandler)
$this->_anonymizeIP = isset($config['anonymizeIP'])? $config['anonymizeIP'] : false;
$this->_botFiltering = isset($config['botFiltering'])? $config['botFiltering'] : null;
$this->_revision = $config['revision'];
$this->_sendFlagDecisions = isset($config['sendFlagDecisions']) ? $config['sendFlagDecisions'] : false;

$groups = $config['groups'] ?: [];
$experiments = $config['experiments'] ?: [];
Expand Down Expand Up @@ -256,6 +264,12 @@ public function __construct($datafile, $logger, $errorHandler)
$this->_experimentKeyMap = $this->_experimentKeyMap + $experimentsInGroup;
}

foreach ($this->_rollouts as $rollout) {
foreach ($rollout->getExperiments() as $experiment) {
$this->_experimentKeyMap[$experiment->getKey()] = $experiment;
}
}

$this->_variationKeyMap = [];
$this->_variationIdMap = [];
$this->_experimentIdMap = [];
Expand Down Expand Up @@ -425,7 +439,16 @@ public function getFeatureFlags()
*/
public function getAllExperiments()
{
return array_values($this->_experimentKeyMap);
// Exclude rollout experiments
$rolloutExperimentIds = [];
foreach ($this->_rollouts as $rollout) {
foreach ($rollout->getExperiments() as $experiment) {
$rolloutExperimentIds[] = $experiment->getId();
}
}
return array_filter(array_values($this->_experimentKeyMap), function ($experiment) use ($rolloutExperimentIds) {
return !in_array($experiment->getId(), $rolloutExperimentIds);
});
}

/**
Expand Down Expand Up @@ -678,4 +701,14 @@ public function isFeatureExperiment($experimentId)
{
return array_key_exists($experimentId, $this->_experimentFeatureMap);
}

/**
* Returns if flag decisions should be sent to server or not
*
* @return boolean
*/
public function getSendFlagDecisions()
{
return $this->_sendFlagDecisions;
}
}
7 changes: 7 additions & 0 deletions src/Optimizely/Config/ProjectConfigInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -166,4 +166,11 @@ public function isFeatureExperiment($experimentId);
* @return string A string value that contains datafile contents.
*/
public function toDatafile();

/**
* Returns if flag decisions should be sent to server or not
*
* @return boolean
*/
public function getSendFlagDecisions();
}
1 change: 1 addition & 0 deletions src/Optimizely/DecisionService/FeatureDecision.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class FeatureDecision
{
const DECISION_SOURCE_FEATURE_TEST = 'feature-test';
const DECISION_SOURCE_ROLLOUT = 'rollout';
const DECITION_SOURCE_EXPERIMENT = 'experiment';

/**
* The experiment in this decision.
Expand Down
15 changes: 11 additions & 4 deletions src/Optimizely/Event/Builder/EventBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -143,14 +143,21 @@ private function getCommonParams($config, $userId, $attributes)
*
* @return array Hash representing parameters particular to impression event.
*/
private function getImpressionParams(Experiment $experiment, $variationId)
private function getImpressionParams(Experiment $experiment, $variation, $flagKey, $ruleKey, $ruleType)
{
$variationKey = $variation->getKey() ? $variation->getKey() : '';
$impressionParams = [
DECISIONS => [
[
CAMPAIGN_ID => $experiment->getLayerId(),
EXPERIMENT_ID => $experiment->getId(),
VARIATION_ID => $variationId
VARIATION_ID => $variation->getId(),
METADATA => [
FLAG_KEY => $flagKey,
RULE_KEY => $ruleKey,
RULE_TYPE => $ruleType,
VARIATION_KEY => $variationKey
],
]
],

Expand Down Expand Up @@ -221,13 +228,13 @@ private function getConversionParams($eventEntity, $eventTags)
*
* @return LogEvent Event object to be sent to dispatcher.
*/
public function createImpressionEvent($config, $experimentKey, $variationKey, $userId, $attributes)
public function createImpressionEvent($config, $experimentKey, $variationKey, $flagKey, $ruleKey, $ruleType, $userId, $attributes)
{
$eventParams = $this->getCommonParams($config, $userId, $attributes);

$experiment = $config->getExperimentFromKey($experimentKey);
$variation = $config->getVariationFromKey($experimentKey, $variationKey);
$impressionParams = $this->getImpressionParams($experiment, $variation->getId());
$impressionParams = $this->getImpressionParams($experiment, $variation, $flagKey, $ruleKey, $ruleType);

$eventParams[VISITORS][0][SNAPSHOTS][] = $impressionParams;

Expand Down
5 changes: 5 additions & 0 deletions src/Optimizely/Event/Builder/Params.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,8 @@
define('VARIATION_ID', 'variation_id');
define('VISITOR_ID', 'visitor_id');
define('VISITORS', 'visitors');
define('METADATA', 'metadata');
define('FLAG_KEY', 'flag_key');
define('RULE_KEY', 'rule_key');
define('RULE_TYPE', 'rule_type');
define('VARIATION_KEY', 'variation_key');
20 changes: 12 additions & 8 deletions src/Optimizely/Optimizely.php
Original file line number Diff line number Diff line change
Expand Up @@ -195,10 +195,10 @@ private function validateUserInputs($attributes, $eventTags = null)
* @param array Associative array of user attributes
* @param DatafileProjectConfig DatafileProjectConfig instance
*/
protected function sendImpressionEvent($config, $experimentKey, $variationKey, $userId, $attributes)
protected function sendImpressionEvent($config, $experimentKey, $variationKey, $flagKey, $ruleKey, $ruleType, $userId, $attributes)
{
$impressionEvent = $this->_eventBuilder
->createImpressionEvent($config, $experimentKey, $variationKey, $userId, $attributes);
->createImpressionEvent($config, $experimentKey, $variationKey, $flagKey, $ruleKey, $ruleType, $userId, $attributes);
$this->_logger->log(Logger::INFO, sprintf('Activating user "%s" in experiment "%s".', $userId, $experimentKey));
$this->_logger->log(
Logger::DEBUG,
Expand Down Expand Up @@ -274,7 +274,7 @@ public function activate($experimentKey, $userId, $attributes = null)
return null;
}

$this->sendImpressionEvent($config, $experimentKey, $variationKey, $userId, $attributes);
$this->sendImpressionEvent($config, $experimentKey, $variationKey, '', $experimentKey, FeatureDecision::DECITION_SOURCE_EXPERIMENT, $userId, $attributes);

return $variationKey;
}
Expand Down Expand Up @@ -554,18 +554,22 @@ public function isFeatureEnabled($featureFlagKey, $userId, $attributes = null)
$featureEnabled = false;
$decision = $this->_decisionService->getVariationForFeature($config, $featureFlag, $userId, $attributes);
$variation = $decision->getVariation();

if ($config->getSendFlagDecisions() && ($decision->getSource() == FeatureDecision::DECISION_SOURCE_ROLLOUT || !$variation)) {
$ruleKey = $decision->getExperiment() ? $decision->getExperiment()->getKey() : '';
$this->sendImpressionEvent($config, $ruleKey, $variation ? $variation->getKey() : '', $featureFlagKey, $ruleKey, $decision->getSource(), $userId, $attributes);
}

if ($variation) {
$experiment = $decision->getExperiment();
$experimentKey = $decision->getExperiment()->getKey();
$featureEnabled = $variation->getFeatureEnabled();
if ($decision->getSource() == FeatureDecision::DECISION_SOURCE_FEATURE_TEST) {
$experimentKey = $experiment->getKey();
$variationKey = $variation->getKey();
$sourceInfo = (object) array(
'experimentKey'=> $experimentKey,
'variationKey'=> $variationKey
'variationKey'=> $variation->getKey()
);

$this->sendImpressionEvent($config, $experimentKey, $variationKey, $userId, $attributes);
$this->sendImpressionEvent($config, $experimentKey, $variation->getKey(), $featureFlagKey, $experimentKey, $decision->getSource(), $userId, $attributes);
} else {
$this->_logger->log(Logger::INFO, "The user '{$userId}' is not being experimented on Feature Flag '{$featureFlagKey}'.");
}
Expand Down
14 changes: 12 additions & 2 deletions tests/ConfigTests/DatafileProjectConfigTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,12 @@ public function testInit()
'test_experiment_double_feature' => $this->config->getExperimentFromKey('test_experiment_double_feature'),
'test_experiment_integer_feature' => $this->config->getExperimentFromKey('test_experiment_integer_feature'),
'test_experiment_2' => $this->config->getExperimentFromKey('test_experiment_2'),
'test_experiment_json_feature' => $this->config->getExperimentFromKey('test_experiment_json_feature')
'test_experiment_json_feature' => $this->config->getExperimentFromKey('test_experiment_json_feature'),
'rollout_1_exp_1' => $this->config->getExperimentFromKey('rollout_1_exp_1'),
'rollout_1_exp_2' => $this->config->getExperimentFromKey('rollout_1_exp_2'),
'rollout_1_exp_3' => $this->config->getExperimentFromKey('rollout_1_exp_3'),
'rollout_2_exp_1' => $this->config->getExperimentFromKey('rollout_2_exp_1'),
'rollout_2_exp_2' => $this->config->getExperimentFromKey('rollout_2_exp_2'),
],
$experimentKeyMap->getValue($this->config)
);
Expand All @@ -135,7 +140,12 @@ public function testInit()
'122238' => $this->config->getExperimentFromId('122238'),
'122241' => $this->config->getExperimentFromId('122241'),
'111133' => $this->config->getExperimentFromId('111133'),
'122245' => $this->config->getExperimentFromId('122245')
'122245' => $this->config->getExperimentFromId('122245'),
'177770' => $this->config->getExperimentFromId('177770'),
'177772' => $this->config->getExperimentFromId('177772'),
'177776' => $this->config->getExperimentFromId('177776'),
'177774' => $this->config->getExperimentFromId('177774'),
'177779' => $this->config->getExperimentFromId('177779'),
],
$experimentIdMap->getValue($this->config)
);
Expand Down
38 changes: 37 additions & 1 deletion tests/EventTests/EventBuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,13 @@ public function setUp()
$decisions = array('decisions' => [[
'campaign_id'=> '7719770039',
'experiment_id'=> '7716830082',
'variation_id'=> '7721010009'
'variation_id'=> '7721010009',
'metadata'=> [
'flag_key' => 'test_experiment',
'rule_key' => 'test_experiment',
'rule_type' => 'experiment',
'variation_key'=> 'variation'
]
]]
);
$this->expectedImpressionEventParams = $this->expectedEventParams;
Expand Down Expand Up @@ -137,6 +143,9 @@ public function testCreateImpressionEventNoAttributesNoValue()
$this->config,
'test_experiment',
'variation',
'test_experiment',
'test_experiment',
'experiment',
$this->testUserId,
null
);
Expand Down Expand Up @@ -193,6 +202,9 @@ public function testCreateImpressionEventWithAttributesNoValue()
$this->config,
'test_experiment',
'variation',
'test_experiment',
'test_experiment',
'experiment',
$this->testUserId,
$userAttributes
);
Expand Down Expand Up @@ -230,6 +242,9 @@ public function testCreateImpressionEventWithFalseAttributesNoValue()
$this->config,
'test_experiment',
'variation',
'test_experiment',
'test_experiment',
'experiment',
$this->testUserId,
$userAttributes
);
Expand Down Expand Up @@ -268,6 +283,9 @@ public function testCreateImpressionEventWithZeroAttributesNoValue()
$this->config,
'test_experiment',
'variation',
'test_experiment',
'test_experiment',
'experiment',
$this->testUserId,
$userAttributes
);
Expand Down Expand Up @@ -296,6 +314,9 @@ public function testCreateImpressionEventWithInvalidAttributesNoValue()
$this->config,
'test_experiment',
'variation',
'test_experiment',
'test_experiment',
'experiment',
$this->testUserId,
$userAttributes
);
Expand Down Expand Up @@ -333,6 +354,9 @@ public function testCreateImpressionEventWithUserAgentWhenBotFilteringIsEnabled(
$this->config,
'test_experiment',
'variation',
'test_experiment',
'test_experiment',
'experiment',
$this->testUserId,
$userAttributes
);
Expand Down Expand Up @@ -377,6 +401,9 @@ public function testCreateImpressionEventWithInvalidAttributeTypes()
$this->config,
'test_experiment',
'variation',
'test_experiment',
'test_experiment',
'experiment',
$this->testUserId,
$userAttributes
);
Expand Down Expand Up @@ -424,6 +451,9 @@ public function testCreateImpressionEventWithUserAgentWhenBotFilteringIsDisabled
$configMock,
'test_experiment',
'variation',
'test_experiment',
'test_experiment',
'experiment',
$this->testUserId,
$userAttributes
);
Expand Down Expand Up @@ -467,6 +497,9 @@ public function testCreateImpressionEventWithUserAgentWhenBotFilteringIsNull()
$configMock,
'test_experiment',
'variation',
'test_experiment',
'test_experiment',
'experiment',
$this->testUserId,
$userAttributes
);
Expand Down Expand Up @@ -793,6 +826,9 @@ public function testCreateImpressionEventWithBucketingIDAttribute()
$this->config,
'test_experiment',
'variation',
'test_experiment',
'test_experiment',
'experiment',
$this->testUserId,
$userAttributes
);
Expand Down
Loading

0 comments on commit 12a0ae3

Please sign in to comment.