Skip to content

Commit

Permalink
Merge 0f52749 into 5ed543c
Browse files Browse the repository at this point in the history
  • Loading branch information
mfahadahmed committed Jul 2, 2019
2 parents 5ed543c + 0f52749 commit 0616d7c
Show file tree
Hide file tree
Showing 8 changed files with 335 additions and 124 deletions.
22 changes: 22 additions & 0 deletions src/Optimizely/Entity/FeatureVariable.php
Original file line number Diff line number Diff line change
Expand Up @@ -132,4 +132,26 @@ public function setDefaultValue($value)
{
$this->_defaultValue = $value;
}

/**
* Returns feature variable method name based on
* feature variable type.
*
* @param String $type Feature variable type.
*/
public static function getFeatureVariableMethodName($type)
{
switch ($type) {
case FeatureVariable::BOOLEAN_TYPE:
return "getFeatureVariableBoolean";
case FeatureVariable::INTEGER_TYPE:
return "getFeatureVariableInteger";
case FeatureVariable::DOUBLE_TYPE:
return "getFeatureVariableDouble";
case FeatureVariable::STRING_TYPE:
return "getFeatureVariableString";
default:
return null;
}
}
}
144 changes: 72 additions & 72 deletions src/Optimizely/Optimizely.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,7 @@

use Exception;
use Optimizely\Exceptions\InvalidAttributeException;
use Optimizely\Exceptions\InvalidDatafileVersionException;
use Optimizely\Exceptions\InvalidEventTagException;
use Optimizely\Exceptions\InvalidInputException;
use Throwable;
use Monolog\Logger;
use Optimizely\DecisionService\DecisionService;
Expand All @@ -33,9 +31,9 @@
use Optimizely\Event\Builder\EventBuilder;
use Optimizely\Event\Dispatcher\DefaultEventDispatcher;
use Optimizely\Event\Dispatcher\EventDispatcherInterface;
use Optimizely\Logger\DefaultLogger;
use Optimizely\Logger\LoggerInterface;
use Optimizely\Logger\NoOpLogger;
use Optimizely\ProjectConfigManager\StaticProjectConfigManager;
use Optimizely\Notification\NotificationCenter;
use Optimizely\Notification\NotificationType;
use Optimizely\UserProfile\UserProfileServiceInterface;
Expand All @@ -57,11 +55,6 @@ class Optimizely
const VARIABLE_KEY = 'Variable Key';
const VARIATION_KEY = 'Variation Key';

/**
* @var ProjectConfig
*/
private $_config;

/**
* @var DecisionService
*/
Expand Down Expand Up @@ -92,6 +85,11 @@ class Optimizely
*/
private $_logger;

/**
* @var ProjectConfigManager
*/
private $_projectConfigManager;

/**
* @var NotificationCenter
*/
Expand Down Expand Up @@ -119,47 +117,20 @@ public function __construct(
$this->_eventDispatcher = $eventDispatcher ?: new DefaultEventDispatcher();
$this->_logger = $logger ?: new NoOpLogger();
$this->_errorHandler = $errorHandler ?: new NoOpErrorHandler();

if (!$this->validateDatafile($datafile, $skipJsonValidation)) {
$this->_isValid = false;
$defaultLogger = new DefaultLogger();

$defaultLogger->log(Logger::ERROR, 'Provided "datafile" has invalid schema.');
$this->_logger->log(Logger::ERROR, 'Provided "datafile" has invalid schema.');
return;
}

try {
$this->_config = new ProjectConfig($datafile, $this->_logger, $this->_errorHandler);
} catch (Exception $exception) {
$this->_isValid = false;
$defaultLogger = new DefaultLogger();
$errorMsg = $exception->getCode() == InvalidDatafileVersionException::class ? $exception->getMessage() : sprintf(Errors::INVALID_FORMAT, 'datafile');
$errorToHandle = $exception->getCode() == InvalidDatafileVersionException::class ? new InvalidDatafileVersionException($errorMsg) : new InvalidInputException($errorMsg);
$defaultLogger->log(Logger::ERROR, $errorMsg);
$this->_logger->log(Logger::ERROR, $errorMsg);
$this->_errorHandler->handleError($errorToHandle);
return;
}

$this->_eventBuilder = new EventBuilder($this->_logger);
$this->_projectConfigManager = new StaticProjectConfigManager($datafile, $skipJsonValidation, $this->_logger, $this->_errorHandler);
$this->_decisionService = new DecisionService($this->_logger, $userProfileService);
$this->notificationCenter = new NotificationCenter($this->_logger, $this->_errorHandler);
}

/**
* @param $datafile string JSON string representing the project.
* @param $skipJsonValidation boolean representing whether JSON schema validation needs to be performed.
*
* @return boolean Representing whether the provided datafile is valid or not.
* Returns ProjectConfig instance.
* @return ProjectConfig ProjectConfig instance or null
*/
private function validateDatafile($datafile, $skipJsonValidation)
protected function getConfig()
{
if (!$skipJsonValidation && !Validator::validateJsonSchema($datafile)) {
return false;
}

return true;
$config = $this->_projectConfigManager->getConfig();
return $config instanceof ProjectConfig ? $config : null;
}

/**
Expand Down Expand Up @@ -201,8 +172,12 @@ private function validateUserInputs($attributes, $eventTags = null)
*/
protected function sendImpressionEvent($experimentKey, $variationKey, $userId, $attributes)
{
// TODO: Config should be passed as param when this is called from activate but
// since PHP is single-threaded we can leave this for now.
$config = $this->getConfig();

$impressionEvent = $this->_eventBuilder
->createImpressionEvent($this->_config, $experimentKey, $variationKey, $userId, $attributes);
->createImpressionEvent($config, $experimentKey, $variationKey, $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 @@ -236,10 +211,10 @@ protected function sendImpressionEvent($experimentKey, $variationKey, $userId, $
$this->notificationCenter->sendNotifications(
NotificationType::ACTIVATE,
array(
$this->_config->getExperimentFromKey($experimentKey),
$config->getExperimentFromKey($experimentKey),
$userId,
$attributes,
$this->_config->getVariationFromKey($experimentKey, $variationKey),
$config->getVariationFromKey($experimentKey, $variationKey),
$impressionEvent
)
);
Expand All @@ -256,8 +231,9 @@ protected function sendImpressionEvent($experimentKey, $variationKey, $userId, $
*/
public function activate($experimentKey, $userId, $attributes = null)
{
if (!$this->_isValid) {
$this->_logger->log(Logger::ERROR, 'Datafile has invalid format. Failing "activate".');
$config = $this->getConfig();
if ($config === null) {
$this->_logger->log(Logger::ERROR, sprintf(Errors::INVALID_DATAFILE, __FUNCTION__));
return null;
}

Expand Down Expand Up @@ -292,8 +268,9 @@ public function activate($experimentKey, $userId, $attributes = null)
*/
public function track($eventKey, $userId, $attributes = null, $eventTags = null)
{
if (!$this->_isValid) {
$this->_logger->log(Logger::ERROR, 'Datafile has invalid format. Failing "track".');
$config = $this->getConfig();
if ($config === null) {
$this->_logger->log(Logger::ERROR, sprintf(Errors::INVALID_DATAFILE, __FUNCTION__));
return;
}

Expand All @@ -311,7 +288,7 @@ public function track($eventKey, $userId, $attributes = null, $eventTags = null)
return;
}

$event = $this->_config->getEvent($eventKey);
$event = $config->getEvent($eventKey);

if (is_null($event->getKey())) {
$this->_logger->log(Logger::INFO, sprintf('Not tracking user "%s" for event "%s".', $userId, $eventKey));
Expand All @@ -320,7 +297,7 @@ public function track($eventKey, $userId, $attributes = null, $eventTags = null)

$conversionEvent = $this->_eventBuilder
->createConversionEvent(
$this->_config,
$config,
$eventKey,
$userId,
$attributes,
Expand Down Expand Up @@ -380,8 +357,11 @@ public function track($eventKey, $userId, $attributes = null, $eventTags = null)
*/
public function getVariation($experimentKey, $userId, $attributes = null)
{
if (!$this->_isValid) {
$this->_logger->log(Logger::ERROR, 'Datafile has invalid format. Failing "getVariation".');
// TODO: Config should be passed as param when this is called from activate but
// since PHP is single-threaded we can leave this for now.
$config = $this->getConfig();
if ($config === null) {
$this->_logger->log(Logger::ERROR, sprintf(Errors::INVALID_DATAFILE, __FUNCTION__));
return null;
}

Expand All @@ -395,7 +375,7 @@ public function getVariation($experimentKey, $userId, $attributes = null)
return null;
}

$experiment = $this->_config->getExperimentFromKey($experimentKey);
$experiment = $config->getExperimentFromKey($experimentKey);

if (is_null($experiment->getKey())) {
return null;
Expand All @@ -405,10 +385,10 @@ public function getVariation($experimentKey, $userId, $attributes = null)
return null;
}

$variation = $this->_decisionService->getVariation($this->_config, $experiment, $userId, $attributes);
$variation = $this->_decisionService->getVariation($config, $experiment, $userId, $attributes);
$variationKey = ($variation === null) ? null : $variation->getKey();

if ($this->_config->isFeatureExperiment($experiment->getId())) {
if ($config->isFeatureExperiment($experiment->getId())) {
$decisionNotificationType = DecisionNotificationTypes::FEATURE_TEST;
} else {
$decisionNotificationType = DecisionNotificationTypes::AB_TEST;
Expand Down Expand Up @@ -443,6 +423,12 @@ public function getVariation($experimentKey, $userId, $attributes = null)
*/
public function setForcedVariation($experimentKey, $userId, $variationKey)
{
$config = $this->getConfig();
if ($config === null) {
$this->_logger->log(Logger::ERROR, sprintf(Errors::INVALID_DATAFILE, __FUNCTION__));
return false;
}

if (!$this->validateInputs(
[
self::EXPERIMENT_KEY =>$experimentKey,
Expand All @@ -451,7 +437,7 @@ public function setForcedVariation($experimentKey, $userId, $variationKey)
)) {
return false;
}
return $this->_decisionService->setForcedVariation($this->_config, $experimentKey, $userId, $variationKey);
return $this->_decisionService->setForcedVariation($config, $experimentKey, $userId, $variationKey);
}

/**
Expand All @@ -464,6 +450,12 @@ public function setForcedVariation($experimentKey, $userId, $variationKey)
*/
public function getForcedVariation($experimentKey, $userId)
{
$config = $this->getConfig();
if ($config === null) {
$this->_logger->log(Logger::ERROR, sprintf(Errors::INVALID_DATAFILE, __FUNCTION__));
return null;
}

if (!$this->validateInputs(
[
self::EXPERIMENT_KEY =>$experimentKey,
Expand All @@ -473,7 +465,7 @@ public function getForcedVariation($experimentKey, $userId)
return null;
}

$forcedVariation = $this->_decisionService->getForcedVariation($this->_config, $experimentKey, $userId);
$forcedVariation = $this->_decisionService->getForcedVariation($config, $experimentKey, $userId);
if (isset($forcedVariation)) {
return $forcedVariation->getKey();
} else {
Expand All @@ -493,8 +485,9 @@ public function getForcedVariation($experimentKey, $userId)
*/
public function isFeatureEnabled($featureFlagKey, $userId, $attributes = null)
{
if (!$this->_isValid) {
$this->_logger->log(Logger::ERROR, "Datafile has invalid format. Failing '".__FUNCTION__."'.");
$config = $this->getConfig();
if ($config === null) {
$this->_logger->log(Logger::ERROR, sprintf(Errors::INVALID_DATAFILE, __FUNCTION__));
return false;
}

Expand All @@ -508,19 +501,19 @@ public function isFeatureEnabled($featureFlagKey, $userId, $attributes = null)
return false;
}

$featureFlag = $this->_config->getFeatureFlagFromKey($featureFlagKey);
$featureFlag = $config->getFeatureFlagFromKey($featureFlagKey);
if ($featureFlag && (!$featureFlag->getId())) {
// Error logged in ProjectConfig - getFeatureFlagFromKey
return false;
}

//validate feature flag
if (!Validator::isFeatureFlagValid($this->_config, $featureFlag)) {
if (!Validator::isFeatureFlagValid($config, $featureFlag)) {
return false;
}

$featureEnabled = false;
$decision = $this->_decisionService->getVariationForFeature($this->_config, $featureFlag, $userId, $attributes);
$decision = $this->_decisionService->getVariationForFeature($config, $featureFlag, $userId, $attributes);
$variation = $decision->getVariation();
if ($variation) {
$experiment = $decision->getExperiment();
Expand Down Expand Up @@ -575,6 +568,12 @@ public function getEnabledFeatures($userId, $attributes = null)
{
$enabledFeatureKeys = [];

$config = $this->getConfig();
if ($config === null) {
$this->_logger->log(Logger::ERROR, sprintf(Errors::INVALID_DATAFILE, __FUNCTION__));
return $enabledFeatureKeys;
}

if (!$this->validateInputs(
[
self::USER_ID => $userId
Expand All @@ -584,12 +583,7 @@ public function getEnabledFeatures($userId, $attributes = null)
return $enabledFeatureKeys;
}

if (!$this->_isValid) {
$this->_logger->log(Logger::ERROR, "Datafile has invalid format. Failing '".__FUNCTION__."'.");
return $enabledFeatureKeys;
}

$featureFlags = $this->_config->getFeatureFlags();
$featureFlags = $config->getFeatureFlags();
foreach ($featureFlags as $feature) {
$featureKey = $feature->getKey();
if ($this->isFeatureEnabled($featureKey, $userId, $attributes) === true) {
Expand Down Expand Up @@ -618,6 +612,12 @@ public function getFeatureVariableValueForType(
$attributes = null,
$variableType = null
) {
$config = $this->getConfig();
if ($config === null) {
$this->_logger->log(Logger::ERROR, sprintf(Errors::INVALID_DATAFILE, FeatureVariable::getFeatureVariableMethodName($variableType)));
return null;
}

if (!$this->validateInputs(
[
self::FEATURE_FLAG_KEY => $featureFlagKey,
Expand All @@ -629,13 +629,13 @@ public function getFeatureVariableValueForType(
return null;
}

$featureFlag = $this->_config->getFeatureFlagFromKey($featureFlagKey);
$featureFlag = $config->getFeatureFlagFromKey($featureFlagKey);
if ($featureFlag && (!$featureFlag->getId())) {
// Error logged in ProjectConfig - getFeatureFlagFromKey
return null;
}

$variable = $this->_config->getFeatureVariableFromKey($featureFlagKey, $variableKey);
$variable = $config->getFeatureVariableFromKey($featureFlagKey, $variableKey);
if (!$variable) {
// Error message logged in ProjectConfig- getFeatureVariableFromKey
return null;
Expand All @@ -650,7 +650,7 @@ public function getFeatureVariableValueForType(
}

$featureEnabled = false;
$decision = $this->_decisionService->getVariationForFeature($this->_config, $featureFlag, $userId, $attributes);
$decision = $this->_decisionService->getVariationForFeature($config, $featureFlag, $userId, $attributes);
$variableValue = $variable->getDefaultValue();

if ($decision->getVariation() === null) {
Expand Down Expand Up @@ -816,7 +816,7 @@ public function getFeatureVariableString($featureFlagKey, $variableKey, $userId,
*/
public function isValid()
{
return $this->_isValid;
return $this->getConfig() !== null;
}

/**
Expand Down

0 comments on commit 0616d7c

Please sign in to comment.