Skip to content

Commit 2314b1d

Browse files
authored
chore: Merge 2.2.0 commits into release branch (#133)
1 parent a9156d0 commit 2314b1d

File tree

10 files changed

+307
-38
lines changed

10 files changed

+307
-38
lines changed

composer.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,15 @@
1111
],
1212
"require": {
1313
"php": ">=5.5",
14-
"justinrainbow/json-schema": "^1.6 || ^2.0 || ^4.0",
14+
"justinrainbow/json-schema": "^1.6 || ^2.0 || ^4.0 || ^5.0",
1515
"lastguest/murmurhash": "1.3.0",
1616
"guzzlehttp/guzzle": "~5.3|~6.2",
1717
"monolog/monolog": "~1.21",
1818
"icecave/parity": "^1.0"
1919
},
2020
"require-dev": {
21-
"phpunit/phpunit": "~4.8|~5.0",
22-
"satooshi/php-coveralls": "v1.0.1"
21+
"phpunit/phpunit": "^4.8|^5.0",
22+
"php-coveralls/php-coveralls": "v2.0.0"
2323
},
2424
"autoload": {
2525
"psr-4": {

src/Optimizely/Event/Builder/EventBuilder.php

Lines changed: 32 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -180,45 +180,50 @@ private function getImpressionParams(Experiment $experiment, $variationId)
180180
private function getConversionParams($config, $eventKey, $experimentVariationMap, $eventTags)
181181
{
182182
$conversionParams = [];
183+
$singleSnapshot = [];
184+
$decisions = [];
185+
183186
foreach ($experimentVariationMap as $experimentId => $variationId) {
184-
$singleSnapshot = [];
187+
188+
185189
$experiment = $config->getExperimentFromId($experimentId);
186190
$eventEntity = $config->getEvent($eventKey);
187191

188-
$singleSnapshot[DECISIONS] = [
189-
[
190-
CAMPAIGN_ID => $experiment->getLayerId(),
191-
EXPERIMENT_ID => $experiment->getId(),
192-
VARIATION_ID => $variationId
193-
]
194-
];
195-
196-
$singleSnapshot[EVENTS] = [
197-
[
198-
ENTITY_ID => $eventEntity->getId(),
199-
TIMESTAMP => time()*1000,
200-
UUID => GeneratorUtils::getRandomUuid(),
201-
KEY => $eventKey
202-
]
192+
$decision = [
193+
CAMPAIGN_ID => $experiment->getLayerId(),
194+
EXPERIMENT_ID => $experiment->getId(),
195+
VARIATION_ID => $variationId
203196
];
197+
$decisions [] = $decision;
198+
}
204199

205-
if (!is_null($eventTags)) {
206-
$revenue = EventTagUtils::getRevenueValue($eventTags, $this->_logger);
207-
if (!is_null($revenue)) {
208-
$singleSnapshot[EVENTS][0][EventTagUtils::REVENUE_EVENT_METRIC_NAME] = $revenue;
209-
}
200+
$eventDict = [
201+
ENTITY_ID => $eventEntity->getId(),
202+
TIMESTAMP => time()*1000,
203+
UUID => GeneratorUtils::getRandomUuid(),
204+
KEY => $eventKey
205+
];
210206

211-
$eventValue = EventTagUtils::getNumericValue($eventTags, $this->_logger);
212-
if (!is_null($eventValue)) {
213-
$singleSnapshot[EVENTS][0][EventTagUtils::NUMERIC_EVENT_METRIC_NAME] = $eventValue;
214-
}
207+
if (!is_null($eventTags)) {
208+
$revenue = EventTagUtils::getRevenueValue($eventTags, $this->_logger);
209+
if (!is_null($revenue)) {
210+
$eventDict[EventTagUtils::REVENUE_EVENT_METRIC_NAME] = $revenue;
211+
}
215212

216-
$singleSnapshot[EVENTS][0]['tags'] = $eventTags;
213+
$eventValue = EventTagUtils::getNumericValue($eventTags, $this->_logger);
214+
if (!is_null($eventValue)) {
215+
$eventDict[EventTagUtils::NUMERIC_EVENT_METRIC_NAME] = $eventValue;
217216
}
218217

219-
$conversionParams [] = $singleSnapshot;
218+
if(count($eventTags) > 0) {
219+
$eventDict['tags'] = $eventTags;
220+
}
220221
}
221222

223+
$singleSnapshot[DECISIONS] = $decisions;
224+
$singleSnapshot[EVENTS] [] = $eventDict;
225+
$conversionParams [] = $singleSnapshot;
226+
222227
return $conversionParams;
223228
}
224229

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
/**
3+
* Copyright 2018, 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\Exceptions;
19+
20+
class InvalidDatafileVersionException extends OptimizelyException
21+
{
22+
}

src/Optimizely/Optimizely.php

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@
1818

1919
use Exception;
2020
use Optimizely\Exceptions\InvalidAttributeException;
21+
use Optimizely\Exceptions\InvalidDatafileVersionException;
2122
use Optimizely\Exceptions\InvalidEventTagException;
23+
use Optimizely\Exceptions\InvalidInputException;
2224
use Throwable;
2325
use Monolog\Logger;
2426
use Optimizely\DecisionService\DecisionService;
@@ -36,6 +38,7 @@
3638
use Optimizely\Notification\NotificationCenter;
3739
use Optimizely\Notification\NotificationType;
3840
use Optimizely\UserProfile\UserProfileServiceInterface;
41+
use Optimizely\Utils\Errors;
3942
use Optimizely\Utils\Validator;
4043
use Optimizely\Utils\VariableTypeUtils;
4144

@@ -120,15 +123,14 @@ public function __construct(
120123

121124
try {
122125
$this->_config = new ProjectConfig($datafile, $this->_logger, $this->_errorHandler);
123-
} catch (Throwable $exception) {
124-
$this->_isValid = false;
125-
$this->_logger = new DefaultLogger();
126-
$this->_logger->log(Logger::ERROR, 'Provided "datafile" is in an invalid format.');
127-
return;
128126
} catch (Exception $exception) {
129127
$this->_isValid = false;
130-
$this->_logger = new DefaultLogger();
131-
$this->_logger->log(Logger::ERROR, 'Provided "datafile" is in an invalid format.');
128+
$defaultLogger = new DefaultLogger();
129+
$errorMsg = $exception->getCode() == InvalidDatafileVersionException::class ? $exception->getMessage() : sprintf(Errors::INVALID_FORMAT, 'datafile');
130+
$errorToHandle = $exception->getCode() == InvalidDatafileVersionException::class ? new InvalidDatafileVersionException($errorMsg) : new InvalidInputException($errorMsg);
131+
$defaultLogger->log(Logger::ERROR, $errorMsg);
132+
$this->_logger->log(Logger::ERROR, $errorMsg);
133+
$this->_errorHandler->handleError($errorToHandle);
132134
return;
133135
}
134136

@@ -728,4 +730,17 @@ public function getFeatureVariableString($featureFlagKey, $variableKey, $userId,
728730

729731
return $variableValue;
730732
}
733+
734+
/**
735+
* Determine if the instance of the Optimizely client is valid.
736+
* An instance can be deemed invalid if it was not initialized
737+
* properly due to an invalid datafile being passed in.
738+
*
739+
* @return True if the Optimizely instance is valid.
740+
* False if the Optimizely instance is not valid.
741+
*/
742+
public function isValid()
743+
{
744+
return $this->_isValid;
745+
}
731746
}

src/Optimizely/ProjectConfig.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
use Optimizely\ErrorHandler\ErrorHandlerInterface;
3232
use Optimizely\Exceptions\InvalidAttributeException;
3333
use Optimizely\Exceptions\InvalidAudienceException;
34+
use Optimizely\Exceptions\InvalidDatafileVersionException;
3435
use Optimizely\Exceptions\InvalidEventException;
3536
use Optimizely\Exceptions\InvalidExperimentException;
3637
use Optimizely\Exceptions\InvalidFeatureFlagException;
@@ -50,6 +51,9 @@
5051
class ProjectConfig
5152
{
5253
const RESERVED_ATTRIBUTE_PREFIX = '$opt_';
54+
const V2 = '2';
55+
const V3 = '3';
56+
const V4 = '4';
5357

5458
/**
5559
* @var string Version of the datafile.
@@ -185,10 +189,17 @@ class ProjectConfig
185189
*/
186190
public function __construct($datafile, $logger, $errorHandler)
187191
{
192+
$supportedVersions = array(self::V2, self::V3, self::V4);
188193
$config = json_decode($datafile, true);
189194
$this->_logger = $logger;
190195
$this->_errorHandler = $errorHandler;
191196
$this->_version = $config['version'];
197+
if(!in_array($this->_version, $supportedVersions)){
198+
throw new InvalidDatafileVersionException(
199+
"This version of the PHP SDK does not support the given datafile version: {$this->_version}."
200+
);
201+
}
202+
192203
$this->_accountId = $config['accountId'];
193204
$this->_projectId = $config['projectId'];
194205
$this->_anonymizeIP = isset($config['anonymizeIP'])? $config['anonymizeIP'] : false;

src/Optimizely/Utils/Errors.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
/**
3+
* Copyright 2018, 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+
namespace Optimizely\Utils;
18+
class Errors
19+
{
20+
const INVALID_FORMAT = 'Provided %s is in an invalid format.';
21+
}

tests/EventTests/EventBuilderTest.php

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -823,4 +823,70 @@ public function testCreateConversionEventWithUserAgentAttribute()
823823
$result = $this->areLogEventsEqual($expectedLogEvent, $logEvent);
824824
$this->assertTrue($result[0], $result[1]);
825825
}
826+
827+
public function testCreateConversionEventWhenEventUsedInMultipleExp()
828+
{
829+
$eventTags = [
830+
'revenue' => 4200,
831+
'value' => 1.234,
832+
'non-revenue' => 'abc'
833+
];
834+
$this->expectedEventParams['visitors'][0]['snapshots'][0]['decisions'][] = [
835+
'campaign_id' => '4',
836+
'experiment_id' => '122230',
837+
'variation_id' => '122234'
838+
];
839+
840+
$this->expectedEventParams['visitors'][0]['snapshots'][0]['events'][0] = [
841+
'entity_id' => '7718020065',
842+
'timestamp' => $this->timestamp,
843+
'uuid' => $this->uuid,
844+
'key' => 'multi_exp_event',
845+
'revenue' => 4200,
846+
'value' => 1.234,
847+
'tags' => $eventTags,
848+
];
849+
850+
array_unshift($this->expectedEventParams['visitors'][0]['attributes'],
851+
[
852+
'entity_id' => '7723280020',
853+
'key' => 'device_type',
854+
'type' => 'custom',
855+
'value' => 'iPhone'
856+
],[
857+
'entity_id' => '7723340004',
858+
'key' => 'location',
859+
'type' => 'custom',
860+
'value' => 'SF'
861+
]);
862+
863+
$decisions = [
864+
'7716830082' => '7721010009',
865+
'122230' => '122234'
866+
];
867+
$userAttributes = [
868+
'device_type' => 'iPhone',
869+
'location' => 'SF'
870+
];
871+
872+
$logEvent = $this->eventBuilder->createConversionEvent(
873+
$this->config,
874+
'multi_exp_event',
875+
$decisions,
876+
$this->testUserId,
877+
$userAttributes,
878+
$eventTags
879+
);
880+
$expectedLogEvent = new LogEvent(
881+
$this->expectedEventUrl,
882+
$this->expectedEventParams,
883+
$this->expectedEventHttpVerb,
884+
$this->expectedEventHeaders
885+
);
886+
887+
$logEvent = $this->fakeParamsToReconcile($logEvent);
888+
$result = $this->areLogEventsEqual($expectedLogEvent, $logEvent);
889+
$this->assertTrue($result[0], $result[1]);
890+
891+
}
826892
}

tests/OptimizelyTest.php

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@
2323
use Optimizely\ErrorHandler\NoOpErrorHandler;
2424
use Optimizely\Event\LogEvent;
2525
use Optimizely\Exceptions\InvalidAttributeException;
26+
use Optimizely\Exceptions\InvalidDatafileVersionException;
2627
use Optimizely\Exceptions\InvalidEventTagException;
28+
use Optimizely\Exceptions\InvalidInputException;
2729
use Optimizely\Logger\NoOpLogger;
2830
use Optimizely\Notification\NotificationCenter;
2931
use Optimizely\Notification\NotificationType;
@@ -74,6 +76,18 @@ public function setUp()
7476
->getMock();
7577
}
7678

79+
public function testIsValidForInvalidOptimizelyObject()
80+
{
81+
$optlyObject = new Optimizely('Random datafile');
82+
$this->assertFalse($optlyObject->isValid());
83+
}
84+
85+
public function testIsValidForValidOptimizelyObject()
86+
{
87+
$optlyObject = new Optimizely($this->datafile);
88+
$this->assertTrue($optlyObject->isValid());
89+
}
90+
7791
public function testInitValidEventDispatcher()
7892
{
7993
$validDispatcher = new ValidEventDispatcher();
@@ -134,6 +148,42 @@ public function testInitInvalidErrorHandler()
134148
$this->fail('Unexpected behavior. Invalid error handler went through.');
135149
}
136150

151+
public function testInitUnSupportedDatafileVersion()
152+
{
153+
$errorHandlerMock = $this->getMockBuilder(NoOpErrorHandler::class)
154+
->setMethods(array('handleError'))
155+
->getMock();
156+
$errorHandlerMock->expects($this->once())
157+
->method('handleError')
158+
->with(new InvalidDatafileVersionException('This version of the PHP SDK does not support the given datafile version: 5.'));
159+
$optlyObject = new Optimizely(
160+
UNSUPPORTED_DATAFILE,
161+
null,
162+
new DefaultLogger(Logger::INFO, self::OUTPUT_STREAM),
163+
$errorHandlerMock,
164+
true
165+
);
166+
$this->expectOutputRegex("/This version of the PHP SDK does not support the given datafile version: 5./");
167+
}
168+
169+
public function testInitDatafileInvalidFormat()
170+
{
171+
$errorHandlerMock = $this->getMockBuilder(NoOpErrorHandler::class)
172+
->setMethods(array('handleError'))
173+
->getMock();
174+
$errorHandlerMock->expects($this->once())
175+
->method('handleError')
176+
->with(new InvalidInputException('Provided datafile is in an invalid format.'));
177+
$optlyObject = new Optimizely(
178+
'{"version": "2"}',
179+
null,
180+
new DefaultLogger(Logger::INFO, self::OUTPUT_STREAM),
181+
$errorHandlerMock,
182+
true
183+
);
184+
$this->expectOutputRegex('/Provided datafile is in an invalid format./');
185+
}
186+
137187
public function testValidateDatafileInvalidFileJsonValidationNotSkipped()
138188
{
139189
$validateInputsMethod = new \ReflectionMethod('Optimizely\Optimizely', 'validateDatafile');

0 commit comments

Comments
 (0)