Skip to content

Commit 8e019a4

Browse files
rashidspaliabbasrizvi
authored andcommitted
feat(DecisionListener): Adds feature decision listener. (#164)
1 parent d6ed90b commit 8e019a4

File tree

7 files changed

+631
-28
lines changed

7 files changed

+631
-28
lines changed

src/Optimizely/DecisionService/DecisionService.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ public function getVariationForFeature(FeatureFlag $featureFlag, $userId, $userA
197197
"User '{$userId}' is not bucketed into rollout for feature flag '{$featureFlag->getKey()}'."
198198
);
199199

200-
return null;
200+
return new FeatureDecision(null, null, FeatureDecision::DECISION_SOURCE_ROLLOUT);
201201
}
202202

203203
/**

src/Optimizely/DecisionService/FeatureDecision.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php
22
/**
3-
* Copyright 2017, Optimizely Inc and Contributors
3+
* Copyright 2017, 2019 Optimizely Inc and Contributors
44
*
55
* Licensed under the Apache License, Version 2.0 (the "License");
66
* you may not use this file except in compliance with the License.
@@ -18,8 +18,8 @@
1818

1919
class FeatureDecision
2020
{
21-
const DECISION_SOURCE_EXPERIMENT = 'experiment';
22-
const DECISION_SOURCE_ROLLOUT = 'rollout';
21+
const DECISION_SOURCE_EXPERIMENT = 'EXPERIMENT';
22+
const DECISION_SOURCE_ROLLOUT = 'ROLLOUT';
2323

2424
/**
2525
* The experiment in this decision.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
/**
3+
* Copyright 2019, Optimizely
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
namespace Optimizely\Enums;
19+
20+
class DecisionInfoTypes
21+
{
22+
const EXPERIMENT = "experiment";
23+
const FEATURE = "feature";
24+
const FEATURE_VARIABLE = "feature_variable";
25+
}

src/Optimizely/Notification/NotificationType.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php
22
/**
3-
* Copyright 2017, Optimizely Inc and Contributors
3+
* Copyright 2017, 2019, Optimizely Inc and Contributors
44
*
55
* Licensed under the Apache License, Version 2.0 (the "License");
66
* you may not use this file except in compliance with the License.
@@ -20,6 +20,7 @@ class NotificationType
2020
{
2121
// format is EVENT: list of parameters to callback.
2222
const ACTIVATE = "ACTIVATE:experiment, user_id, attributes, variation, event";
23+
const DECISION = "DECISION:type, user_id, attributes, decision_info";
2324
const TRACK = "TRACK:event_key, user_id, attributes, event_tags, event";
2425

2526
public static function isNotificationTypeValid($notification_type)

src/Optimizely/Optimizely.php

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
use Optimizely\DecisionService\FeatureDecision;
2828
use Optimizely\Entity\Experiment;
2929
use Optimizely\Entity\FeatureVariable;
30+
use Optimizely\Enums\DecisionInfoTypes;
3031
use Optimizely\ErrorHandler\ErrorHandlerInterface;
3132
use Optimizely\ErrorHandler\NoOpErrorHandler;
3233
use Optimizely\Event\Builder\EventBuilder;
@@ -500,24 +501,41 @@ public function isFeatureEnabled($featureFlagKey, $userId, $attributes = null)
500501
return false;
501502
}
502503

504+
$featureEnabled = false;
503505
$decision = $this->_decisionService->getVariationForFeature($featureFlag, $userId, $attributes);
504-
if (!$decision) {
505-
$this->_logger->log(Logger::INFO, "Feature Flag '{$featureFlagKey}' is not enabled for user '{$userId}'.");
506-
return false;
507-
}
508-
509-
$experiment = $decision->getExperiment();
510506
$variation = $decision->getVariation();
511-
512-
if ($decision->getSource() == FeatureDecision::DECISION_SOURCE_EXPERIMENT) {
513-
$this->sendImpressionEvent($experiment->getKey(), $variation->getKey(), $userId, $attributes);
514-
} else {
515-
$this->_logger->log(Logger::INFO, "The user '{$userId}' is not being experimented on Feature Flag '{$featureFlagKey}'.");
507+
if ($variation) {
508+
$experiment = $decision->getExperiment();
509+
$featureEnabled = $variation->getFeatureEnabled();
510+
if ($decision->getSource() == FeatureDecision::DECISION_SOURCE_EXPERIMENT) {
511+
$experimentKey = $experiment->getKey();
512+
$variationKey = $variation->getKey();
513+
$this->sendImpressionEvent($experimentKey, $variationKey, $userId, $attributes);
514+
} else {
515+
$this->_logger->log(Logger::INFO, "The user '{$userId}' is not being experimented on Feature Flag '{$featureFlagKey}'.");
516+
}
516517
}
517518

518-
if ($variation->getFeatureEnabled()) {
519+
$attributes = is_null($attributes) ? [] : $attributes;
520+
$this->notificationCenter->sendNotifications(
521+
NotificationType::DECISION,
522+
array(
523+
DecisionInfoTypes::FEATURE,
524+
$userId,
525+
$attributes,
526+
(object) array(
527+
'featureKey'=>$featureFlagKey,
528+
'featureEnabled'=> $featureEnabled,
529+
'source'=> $decision->getSource(),
530+
'sourceExperimentKey'=> isset($experimentKey) ? $experimentKey : null,
531+
'sourceVariationKey'=> isset($variationKey) ? $variationKey : null
532+
)
533+
)
534+
);
535+
536+
if ($featureEnabled == true) {
519537
$this->_logger->log(Logger::INFO, "Feature Flag '{$featureFlagKey}' is enabled for user '{$userId}'.");
520-
return true;
538+
return $featureEnabled;
521539
}
522540

523541
$this->_logger->log(Logger::INFO, "Feature Flag '{$featureFlagKey}' is not enabled for user '{$userId}'.");
@@ -611,8 +629,9 @@ public function getFeatureVariableValueForType(
611629

612630
$decision = $this->_decisionService->getVariationForFeature($featureFlag, $userId, $attributes);
613631
$variableValue = $variable->getDefaultValue();
632+
$variation = $decision->getVariation();
614633

615-
if (!$decision) {
634+
if ($variation === null) {
616635
$this->_logger->log(
617636
Logger::INFO,
618637
"User '{$userId}'is not in any variation, ".

tests/DecisionServiceTests/DecisionServiceTest.php

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -909,7 +909,16 @@ public function testGetVariationForFeatureWhenTheUserIsNeitherBucketedIntoFeatur
909909
"User 'user_1' is not bucketed into rollout for feature flag 'string_single_variable_feature'."
910910
);
911911

912-
$this->assertNull($decisionServiceMock->getVariationForFeature($featureFlag, 'user_1', []));
912+
// mock getVariationForFeature to return rolloutDecision
913+
$expectedDecision = new FeatureDecision(
914+
null,
915+
null,
916+
FeatureDecision::DECISION_SOURCE_ROLLOUT
917+
);
918+
$this->assertEquals(
919+
$decisionServiceMock->getVariationForFeature($featureFlag, 'user_1', []),
920+
$expectedDecision
921+
);
913922
}
914923

915924
// should return null

0 commit comments

Comments
 (0)