Skip to content

Commit 0be9ae0

Browse files
authored
feat: Add init with sdk key (#189)
Summary: - This adds sdk key param to Optimizely constructor. The user can now create an Optimizely instance only using sdk key. Previously he was required to create a HTTPProjectConfigManager, and pass it as a config manager in the constructor. - The sdk key param in constructor params sequence has been set as last item as to not break. - Made configManager inside Optimizely class **public** so that the user is able to call fetch() which is a public method defined over HTTPProjectConfigManager. - Adds a factory method to create a client with sdk key and datafile. **todo** Update readme accordingly. Test plan: - Added unit tests to verify init with sdk key. Issues: - OASIS-5770
1 parent d5c038f commit 0be9ae0

File tree

4 files changed

+210
-10
lines changed

4 files changed

+210
-10
lines changed

Diff for: src/Optimizely/Optimizely.php

+15-4
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
use Optimizely\Notification\NotificationCenter;
3838
use Optimizely\Notification\NotificationType;
3939
use Optimizely\OptimizelyConfig\OptimizelyConfigService;
40+
use Optimizely\ProjectConfigManager\HTTPProjectConfigManager;
4041
use Optimizely\ProjectConfigManager\ProjectConfigManagerInterface;
4142
use Optimizely\ProjectConfigManager\StaticProjectConfigManager;
4243
use Optimizely\UserProfile\UserProfileServiceInterface;
@@ -96,7 +97,7 @@ class Optimizely
9697
/**
9798
* @var ProjectConfigManagerInterface
9899
*/
99-
private $_projectConfigManager;
100+
public $configManager;
100101

101102
/**
102103
* @var NotificationCenter
@@ -114,6 +115,7 @@ class Optimizely
114115
* @param $userProfileService UserProfileServiceInterface
115116
* @param $configManager ProjectConfigManagerInterface provides ProjectConfig through getConfig method.
116117
* @param $notificationCenter NotificationCenter
118+
* @param $sdkKey string uniquely identifying the datafile corresponding to project and environment combination. Must provide at least one of datafile or sdkKey.
117119
*/
118120
public function __construct(
119121
$datafile,
@@ -123,7 +125,8 @@ public function __construct(
123125
$skipJsonValidation = false,
124126
UserProfileServiceInterface $userProfileService = null,
125127
ProjectConfigManagerInterface $configManager = null,
126-
NotificationCenter $notificationCenter = null
128+
NotificationCenter $notificationCenter = null,
129+
$sdkKey = null
127130
) {
128131
$this->_isValid = true;
129132
$this->_eventDispatcher = $eventDispatcher ?: new DefaultEventDispatcher();
@@ -132,7 +135,15 @@ public function __construct(
132135
$this->_eventBuilder = new EventBuilder($this->_logger);
133136
$this->_decisionService = new DecisionService($this->_logger, $userProfileService);
134137
$this->notificationCenter = $notificationCenter ?: new NotificationCenter($this->_logger, $this->_errorHandler);
135-
$this->_projectConfigManager = $configManager ?: new StaticProjectConfigManager($datafile, $skipJsonValidation, $this->_logger, $this->_errorHandler);
138+
$this->configManager = $configManager;
139+
140+
if ($this->configManager === null) {
141+
if ($sdkKey) {
142+
$this->configManager = new HTTPProjectConfigManager($sdkKey, null, null, true, $datafile, $skipJsonValidation, $this->_logger, $this->_errorHandler, $this->notificationCenter);
143+
} else {
144+
$this->configManager = new StaticProjectConfigManager($datafile, $skipJsonValidation, $this->_logger, $this->_errorHandler);
145+
}
146+
}
136147
}
137148

138149
/**
@@ -141,7 +152,7 @@ public function __construct(
141152
*/
142153
protected function getConfig()
143154
{
144-
$config = $this->_projectConfigManager->getConfig();
155+
$config = $this->configManager->getConfig();
145156
return $config instanceof DatafileProjectConfig ? $config : null;
146157
}
147158

Diff for: src/Optimizely/OptimizelyFactory.php

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
/**
3+
* Copyright 2020, 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;
18+
19+
use Optimizely\Optimizely;
20+
21+
/**
22+
* Class OptimizelyFactory
23+
*
24+
* @package Optimizely
25+
*/
26+
class OptimizelyFactory
27+
{
28+
public static function createDefaultInstance($sdkKey, $fallbackDatafile = null)
29+
{
30+
return new Optimizely(
31+
$fallbackDatafile,
32+
null,
33+
null,
34+
null,
35+
null,
36+
null,
37+
null,
38+
null,
39+
$sdkKey
40+
);
41+
}
42+
}

Diff for: tests/OptimizelyFactoryTest.php

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<?php
2+
/**
3+
* Copyright 2020, 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\Tests;
18+
19+
use Exception;
20+
use GuzzleHttp\Client;
21+
use GuzzleHttp\Handler\MockHandler;
22+
use GuzzleHttp\HandlerStack;
23+
use GuzzleHttp\Psr7\Response;
24+
use Optimizely\OptimizelyFactory;
25+
use Optimizely\ProjectConfigManager\HTTPProjectConfigManager;
26+
27+
class OptimizelyFactoryTest extends \PHPUnit_Framework_TestCase
28+
{
29+
public function setUp()
30+
{
31+
$this->datafile = DATAFILE;
32+
$this->typedAudiencesDataFile = DATAFILE_WITH_TYPED_AUDIENCES;
33+
}
34+
35+
public function testDefaultInstance()
36+
{
37+
$optimizelyClient = OptimizelyFactory::createDefaultInstance("some-sdk-key", $this->datafile);
38+
39+
// client hasn't been mocked yet. Hence, config manager should return config of hardcoded
40+
// datafile.
41+
$this->assertEquals('15', $optimizelyClient->configManager->getConfig()->getRevision());
42+
43+
// Mock http client to return a valid datafile
44+
$mock = new MockHandler([
45+
new Response(200, [], $this->typedAudiencesDataFile)
46+
]);
47+
48+
$handler = HandlerStack::create($mock);
49+
50+
$client = new Client(['handler' => $handler]);
51+
$httpClient = new \ReflectionProperty(HTTPProjectConfigManager::class, 'httpClient');
52+
$httpClient->setAccessible(true);
53+
$httpClient->setValue($optimizelyClient->configManager, $client);
54+
55+
/// Fetch datafile
56+
$optimizelyClient->configManager->fetch();
57+
58+
$this->assertEquals('3', $optimizelyClient->configManager->getConfig()->getRevision());
59+
}
60+
}

Diff for: tests/OptimizelyTest.php

+93-6
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@
1717
namespace Optimizely\Tests;
1818

1919
use Exception;
20+
use GuzzleHttp\Client;
21+
use GuzzleHttp\Handler\MockHandler;
22+
use GuzzleHttp\HandlerStack;
23+
use GuzzleHttp\Middleware;
24+
use GuzzleHttp\Psr7\Response;
2025
use Monolog\Logger;
2126
use Optimizely\Config\DatafileProjectConfig;
2227
use Optimizely\DecisionService\DecisionService;
@@ -63,9 +68,7 @@ private function setOptimizelyConfigObject($optimizely, $config, $configManager)
6368
$projConfig->setAccessible(true);
6469
$projConfig->setValue($configManager, $config);
6570

66-
$projConfigManager = new \ReflectionProperty(Optimizely::class, '_projectConfigManager');
67-
$projConfigManager->setAccessible(true);
68-
$projConfigManager->setValue($optimizely, $configManager);
71+
$optimizely->configManager = $configManager;
6972
}
7073

7174
public function setUp()
@@ -221,6 +224,92 @@ public function testInitDatafileInvalidFormat()
221224
$this->expectOutputRegex('/Provided datafile is in an invalid format./');
222225
}
223226

227+
public function testInitWithSdkKey()
228+
{
229+
$sdkKey = "some-sdk-key";
230+
$optimizelyClient = new Optimizely(
231+
null,
232+
null,
233+
null,
234+
null,
235+
null,
236+
null,
237+
null,
238+
null,
239+
$sdkKey
240+
);
241+
242+
// client hasn't been mocked yet. Hence, activate should return null.
243+
$actualVariation = $optimizelyClient->activate('test_experiment_integer_feature', 'test_user');
244+
$this->assertNull($actualVariation);
245+
246+
// Mock http client to return a valid datafile
247+
$mock = new MockHandler([
248+
new Response(200, [], $this->datafile)
249+
]);
250+
251+
$container = [];
252+
$history = Middleware::history($container);
253+
$handler = HandlerStack::create($mock);
254+
$handler->push($history);
255+
256+
$client = new Client(['handler' => $handler]);
257+
$httpClient = new \ReflectionProperty(HTTPProjectConfigManager::class, 'httpClient');
258+
$httpClient->setAccessible(true);
259+
$httpClient->setValue($optimizelyClient->configManager, $client);
260+
261+
// Fetch datafile
262+
$optimizelyClient->configManager->fetch();
263+
264+
// activate should return expected variation.
265+
$actualVariation = $optimizelyClient->activate('test_experiment_integer_feature', 'test_user');
266+
$this->assertEquals('variation', $actualVariation);
267+
268+
// assert that https call is made to mock as expected.
269+
$transaction = $container[0];
270+
$this->assertEquals(
271+
'https://cdn.optimizely.com/datafiles/some-sdk-key.json',
272+
$transaction['request']->getUri()
273+
);
274+
}
275+
276+
public function testInitWithBothSdkKeyAndDatafile()
277+
{
278+
$sdkKey = "some-sdk-key";
279+
$optimizelyClient = new Optimizely(
280+
DATAFILE,
281+
null,
282+
null,
283+
null,
284+
null,
285+
null,
286+
null,
287+
null,
288+
$sdkKey
289+
);
290+
291+
// client hasn't been mocked yet. Hence, config manager should return config of hardcoded
292+
// datafile.
293+
$this->assertEquals('15', $optimizelyClient->configManager->getConfig()->getRevision());
294+
295+
296+
// Mock http client to return a valid datafile
297+
$mock = new MockHandler([
298+
new Response(200, [], $this->typedAudiencesDataFile)
299+
]);
300+
301+
$handler = HandlerStack::create($mock);
302+
$client = new Client(['handler' => $handler]);
303+
$httpClient = new \ReflectionProperty(HTTPProjectConfigManager::class, 'httpClient');
304+
$httpClient->setAccessible(true);
305+
$httpClient->setValue($optimizelyClient->configManager, $client);
306+
307+
// Fetch datafile
308+
$optimizelyClient->configManager->fetch();
309+
310+
$this->assertEquals('3', $optimizelyClient->configManager->getConfig()->getRevision());
311+
}
312+
224313
public function testActivateInvalidOptimizelyObject()
225314
{
226315
$optimizelyMock = $this->getMockBuilder(Optimizely::class)
@@ -4471,9 +4560,7 @@ public function testGetConfigReturnsDatafileProjectConfigInstance()
44714560
->setMethods(array('getConfig'))
44724561
->getMock();
44734562

4474-
$projectConfigManager = new \ReflectionProperty(Optimizely::class, '_projectConfigManager');
4475-
$projectConfigManager->setAccessible(true);
4476-
$projectConfigManager->setValue($optlyObject, $projectConfigManagerMock);
4563+
$optlyObject->configManager = $projectConfigManagerMock;
44774564

44784565
$expectedProjectConfig = new DatafileProjectConfig($this->datafile, $this->loggerMock, new NoOpErrorHandler());
44794566

0 commit comments

Comments
 (0)