Skip to content

Commit 76ef881

Browse files
fayyazarshadArshadfzashraf1985
authored
feat: Added getFeatureVariableJson and getAllFeatureVariables (#194)
* added featureVariableJson Co-authored-by: fayyazarshad <fayyaz.aarshad@gmail.com> Co-authored-by: zashraf1985 <35262377+zashraf1985@users.noreply.github.com> Co-authored-by: Zeeshan Ashraf <zashraf@folio3.com>
1 parent 5ab0d64 commit 76ef881

File tree

9 files changed

+718
-86
lines changed

9 files changed

+718
-86
lines changed

src/Optimizely/Config/DatafileProjectConfig.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,18 @@ public function __construct($datafile, $logger, $errorHandler)
220220
$rollouts = isset($config['rollouts']) ? $config['rollouts'] : [];
221221
$featureFlags = isset($config['featureFlags']) ? $config['featureFlags']: [];
222222

223+
// JSON type is represented in datafile as a subtype of string for the sake of backwards compatibility.
224+
// Converting it to a first-class json type while creating Project Config
225+
foreach ($featureFlags as $featureFlagKey => $featureFlag) {
226+
foreach ($featureFlag['variables'] as $variableKey => $variable) {
227+
if (isset($variable['subType']) && $variable['type'] === FeatureVariable::STRING_TYPE && $variable['subType'] === FeatureVariable::JSON_TYPE) {
228+
$variable['type'] = FeatureVariable::JSON_TYPE;
229+
unset($variable['subType']);
230+
$featureFlags[$featureFlagKey]['variables'][$variableKey] = $variable;
231+
}
232+
}
233+
}
234+
223235
$this->_groupIdMap = ConfigParser::generateMap($groups, 'id', Group::class);
224236
$this->_experimentKeyMap = ConfigParser::generateMap($experiments, 'key', Experiment::class);
225237
$this->_eventKeyMap = ConfigParser::generateMap($events, 'key', Event::class);

src/Optimizely/Entity/FeatureVariable.php

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php
22
/**
3-
* Copyright 2017, 2019, Optimizely
3+
* Copyright 2017, 2019-2020 Optimizely
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.
@@ -22,9 +22,10 @@ class FeatureVariable
2222

2323
// Feature variable primitive types
2424
const BOOLEAN_TYPE = 'boolean';
25-
const STRING_TYPE = 'string';
26-
const INTEGER_TYPE = 'integer';
2725
const DOUBLE_TYPE = 'double';
26+
const INTEGER_TYPE = 'integer';
27+
const JSON_TYPE = 'json';
28+
const STRING_TYPE = 'string';
2829

2930
/**
3031
* variable to hold the feature variable ID
@@ -46,6 +47,7 @@ class FeatureVariable
4647
* b. string
4748
* c. integer
4849
* d. double
50+
* e. json
4951
*
5052
* @var String
5153
*/
@@ -144,10 +146,12 @@ public static function getFeatureVariableMethodName($type)
144146
switch ($type) {
145147
case FeatureVariable::BOOLEAN_TYPE:
146148
return "getFeatureVariableBoolean";
147-
case FeatureVariable::INTEGER_TYPE:
148-
return "getFeatureVariableInteger";
149149
case FeatureVariable::DOUBLE_TYPE:
150150
return "getFeatureVariableDouble";
151+
case FeatureVariable::INTEGER_TYPE:
152+
return "getFeatureVariableInteger";
153+
case FeatureVariable::JSON_TYPE:
154+
return "getFeatureVariableJson";
151155
case FeatureVariable::STRING_TYPE:
152156
return "getFeatureVariableString";
153157
default:

src/Optimizely/Enums/DecisionNotificationTypes.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php
22
/**
3-
* Copyright 2019, Optimizely
3+
* Copyright 2019-2020 Optimizely
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.
@@ -23,4 +23,5 @@ class DecisionNotificationTypes
2323
const FEATURE = "feature";
2424
const FEATURE_TEST = "feature-test";
2525
const FEATURE_VARIABLE = "feature-variable";
26+
const ALL_FEATURE_VARIABLES = "all-feature-variables";
2627
}

src/Optimizely/Optimizely.php

Lines changed: 185 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
use Exception;
2020
use Optimizely\Config\DatafileProjectConfig;
21+
use Optimizely\Entity\Variation;
2122
use Optimizely\Exceptions\InvalidAttributeException;
2223
use Optimizely\Exceptions\InvalidEventTagException;
2324
use Throwable;
@@ -292,7 +293,7 @@ public function getOptimizelyConfig()
292293
}
293294

294295
$optConfigService = new OptimizelyConfigService($config);
295-
296+
296297
return $optConfigService->getConfig();
297298
}
298299

@@ -633,15 +634,18 @@ public function getEnabledFeatures($userId, $attributes = null)
633634
}
634635

635636
/**
636-
* Get the string value of the specified variable in the feature flag.
637+
* Get value of the specified variable in the feature flag.
637638
*
638639
* @param string Feature flag key
639640
* @param string Variable key
640641
* @param string User ID
641642
* @param array Associative array of user attributes
642643
* @param string Variable type
643644
*
644-
* @return string Feature variable value / null
645+
* @return string|boolean|number|array|null Value of the variable cast to the appropriate
646+
* type, or null if the feature key is invalid, the
647+
* variable key is invalid, or there is a mismatch
648+
* with the type of the variable
645649
*/
646650
public function getFeatureVariableValueForType(
647651
$featureFlagKey,
@@ -673,69 +677,20 @@ public function getFeatureVariableValueForType(
673677
return null;
674678
}
675679

676-
$variable = $config->getFeatureVariableFromKey($featureFlagKey, $variableKey);
677-
if (!$variable) {
678-
// Error message logged in ProjectConfigInterface- getFeatureVariableFromKey
679-
return null;
680-
}
681-
682-
if ($variableType != $variable->getType()) {
683-
$this->_logger->log(
684-
Logger::ERROR,
685-
"Variable is of type '{$variable->getType()}', but you requested it as type '{$variableType}'."
686-
);
680+
$decision = $this->_decisionService->getVariationForFeature($config, $featureFlag, $userId, $attributes);
681+
$variation = $decision->getVariation();
682+
$experiment = $decision->getExperiment();
683+
$featureEnabled = $variation !== null ? $variation->getFeatureEnabled() : false;
684+
$variableValue = $this->getFeatureVariableValueFromVariation($featureFlagKey, $variableKey, $variableType, $featureEnabled, $variation, $userId);
685+
// returning as variable not found or type is invalid
686+
if ($variableValue === null) {
687687
return null;
688688
}
689-
690-
$featureEnabled = false;
691-
$decision = $this->_decisionService->getVariationForFeature($config, $featureFlag, $userId, $attributes);
692-
$variableValue = $variable->getDefaultValue();
693-
694-
if ($decision->getVariation() === null) {
695-
$this->_logger->log(
696-
Logger::INFO,
697-
"User '{$userId}'is not in any variation, ".
698-
"returning default value '{$variableValue}'."
689+
if ($variation && $decision->getSource() == FeatureDecision::DECISION_SOURCE_FEATURE_TEST) {
690+
$sourceInfo = (object) array(
691+
'experimentKey'=> $experiment->getKey(),
692+
'variationKey'=> $variation->getKey()
699693
);
700-
} else {
701-
$experiment = $decision->getExperiment();
702-
$variation = $decision->getVariation();
703-
$featureEnabled = $variation->getFeatureEnabled();
704-
705-
if ($decision->getSource() == FeatureDecision::DECISION_SOURCE_FEATURE_TEST) {
706-
$sourceInfo = (object) array(
707-
'experimentKey'=> $experiment->getKey(),
708-
'variationKey'=> $variation->getKey()
709-
);
710-
}
711-
712-
if ($featureEnabled) {
713-
$variableUsage = $variation->getVariableUsageById($variable->getId());
714-
if ($variableUsage) {
715-
$variableValue = $variableUsage->getValue();
716-
$this->_logger->log(
717-
Logger::INFO,
718-
"Returning variable value '{$variableValue}' for variation '{$variation->getKey()}' ".
719-
"of feature flag '{$featureFlagKey}'"
720-
);
721-
} else {
722-
$this->_logger->log(
723-
Logger::INFO,
724-
"Variable '{$variableKey}' is not used in variation '{$variation->getKey()}', ".
725-
"returning default value '{$variableValue}'."
726-
);
727-
}
728-
} else {
729-
$this->_logger->log(
730-
Logger::INFO,
731-
"Feature '{$featureFlagKey}' for variation '{$variation->getKey()}' is not enabled, ".
732-
"returning default value '{$variableValue}'."
733-
);
734-
}
735-
}
736-
737-
if (!is_null($variableValue)) {
738-
$variableValue = VariableTypeUtils::castStringToType($variableValue, $variableType, $this->_logger);
739694
}
740695

741696
$attributes = $attributes ?: [];
@@ -844,6 +799,173 @@ public function getFeatureVariableString($featureFlagKey, $variableKey, $userId,
844799
);
845800
}
846801

802+
/**
803+
* Get the JSON value of the specified variable in the feature flag.
804+
*
805+
* @param string Feature flag key
806+
* @param string Variable key
807+
* @param string User ID
808+
* @param array Associative array of user attributes
809+
*
810+
* @return array Associative array of json variable including key and value
811+
*/
812+
public function getFeatureVariableJson($featureFlagKey, $variableKey, $userId, $attributes = null)
813+
{
814+
return $this->getFeatureVariableValueForType(
815+
$featureFlagKey,
816+
$variableKey,
817+
$userId,
818+
$attributes,
819+
FeatureVariable::JSON_TYPE
820+
);
821+
}
822+
823+
/**
824+
* Returns values for all the variables attached to the given feature
825+
*
826+
* @param string Feature flag key
827+
* @param string User ID
828+
* @param array Associative array of user attributes
829+
*
830+
* @return array|null array of all the variables, or null if the feature key is invalid
831+
*/
832+
public function getAllFeatureVariables($featureFlagKey, $userId, $attributes = null)
833+
{
834+
$config = $this->getConfig();
835+
if ($config === null) {
836+
$this->_logger->log(Logger::ERROR, sprintf(Errors::INVALID_DATAFILE, __FUNCTION__));
837+
return null;
838+
}
839+
840+
if (!$this->validateInputs(
841+
[
842+
self::FEATURE_FLAG_KEY => $featureFlagKey,
843+
self::USER_ID => $userId
844+
]
845+
)
846+
) {
847+
return null;
848+
}
849+
850+
$featureFlag = $config->getFeatureFlagFromKey($featureFlagKey);
851+
if ($featureFlag && (!$featureFlag->getId())) {
852+
return null;
853+
}
854+
855+
$decision = $this->_decisionService->getVariationForFeature($config, $featureFlag, $userId, $attributes);
856+
$variation = $decision->getVariation();
857+
$experiment = $decision->getExperiment();
858+
$featureEnabled = $variation !== null ? $variation->getFeatureEnabled() : false;
859+
860+
$allVariables = [];
861+
foreach ($featureFlag->getVariables() as $variable) {
862+
$allVariables[$variable->getKey()] = $this->getFeatureVariableValueFromVariation(
863+
$featureFlagKey,
864+
$variable->getKey(),
865+
null,
866+
$featureEnabled,
867+
$variation,
868+
$userId
869+
);
870+
}
871+
872+
$sourceInfo = (object) array();
873+
if ($variation && $decision->getSource() == FeatureDecision::DECISION_SOURCE_FEATURE_TEST) {
874+
$sourceInfo = (object) array(
875+
'experimentKey'=> $experiment->getKey(),
876+
'variationKey'=> $variation->getKey()
877+
);
878+
}
879+
880+
$attributes = $attributes ?: [];
881+
882+
$this->notificationCenter->sendNotifications(
883+
NotificationType::DECISION,
884+
array(
885+
DecisionNotificationTypes::ALL_FEATURE_VARIABLES,
886+
$userId,
887+
$attributes,
888+
(object)array(
889+
'featureKey' => $featureFlagKey,
890+
'featureEnabled' => $featureEnabled,
891+
'variableValues' => $allVariables,
892+
'source' => $decision->getSource(),
893+
'sourceInfo' => $sourceInfo
894+
)
895+
)
896+
);
897+
898+
return $allVariables;
899+
}
900+
901+
/**
902+
* Get the value of the specified variable on the basis of its status and usage
903+
*
904+
* @param string Feature flag key
905+
* @param string Variable key
906+
* @param boolean Feature Status
907+
* @param Variation for feature
908+
* @param string User Id
909+
*
910+
* @return string|boolean|number|array|null Value of the variable cast to the appropriate
911+
* type, or null if the feature key is invalid, the
912+
* variable key is invalid, or there is a mismatch
913+
* with the type of the variable
914+
*/
915+
private function getFeatureVariableValueFromVariation($featureFlagKey, $variableKey, $variableType, $featureEnabled, $variation, $userId)
916+
{
917+
$config = $this->getConfig();
918+
$variable = $config->getFeatureVariableFromKey($featureFlagKey, $variableKey);
919+
if (!$variable) {
920+
// Error message logged in ProjectConfigInterface- getFeatureVariableFromKey
921+
return null;
922+
}
923+
if ($variableType && $variableType != $variable->getType()) {
924+
$this->_logger->log(
925+
Logger::ERROR,
926+
"Variable is of type '{$variable->getType()}', but you requested it as type '{$variableType}'."
927+
);
928+
return null;
929+
}
930+
$variableValue = $variable->getDefaultValue();
931+
if ($variation === null) {
932+
$this->_logger->log(
933+
Logger::INFO,
934+
"User '{$userId}'is not in any variation, ".
935+
"returning default value '{$variableValue}'."
936+
);
937+
} else {
938+
if ($featureEnabled) {
939+
$variableUsage = $variation->getVariableUsageById($variable->getId());
940+
if ($variableUsage) {
941+
$variableValue = $variableUsage->getValue();
942+
$this->_logger->log(
943+
Logger::INFO,
944+
"Returning variable value '{$variableValue}' for variation '{$variation->getKey()}' ".
945+
"of feature flag '{$featureFlagKey}'"
946+
);
947+
} else {
948+
$this->_logger->log(
949+
Logger::INFO,
950+
"Variable '{$variableKey}' is not used in variation '{$variation->getKey()}', ".
951+
"returning default value '{$variableValue}'."
952+
);
953+
}
954+
} else {
955+
$this->_logger->log(
956+
Logger::INFO,
957+
"Feature '{$featureFlagKey}' for variation '{$variation->getKey()}' is not enabled, ".
958+
"returning default value '{$variableValue}'."
959+
);
960+
}
961+
}
962+
963+
if (!is_null($variableValue)) {
964+
$variableValue = VariableTypeUtils::castStringToType($variableValue, $variable->getType(), $this->_logger);
965+
}
966+
return $variableValue;
967+
}
968+
847969
/**
848970
* Determine if the instance of the Optimizely client is valid.
849971
* An instance can be deemed invalid if it was not initialized

src/Optimizely/Utils/VariableTypeUtils.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php
22
/**
3-
* Copyright 2017, 2019, Optimizely
3+
* Copyright 2017, 2019-2020 Optimizely
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.
@@ -48,6 +48,10 @@ public static function castStringToType($value, $variableType, LoggerInterface $
4848
$return_value = (float) $value;
4949
}
5050
break;
51+
52+
case FeatureVariable::JSON_TYPE:
53+
$return_value = json_decode($value, true);
54+
break;
5155
}
5256

5357
if (is_null($return_value) && $logger) {

0 commit comments

Comments
 (0)