Skip to content

Commit 4963820

Browse files
[FSSDK-9784] Return Latest Experiment When Duplicate Keys in Config (#280)
* feat: log duplicate experiment keys includes some linting * test: updated existing dupe exp keys test
1 parent 3fd73a2 commit 4963820

File tree

2 files changed

+46
-25
lines changed

2 files changed

+46
-25
lines changed

src/Optimizely/OptimizelyConfig/OptimizelyConfigService.php

+27-10
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
<?php
22
/**
3-
* Copyright 2020-2021, Optimizely Inc and Contributors
3+
* Copyright 2020-2021, 2023 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.
77
* You may obtain a copy of the License at
88
*
9-
* http://www.apache.org/licenses/LICENSE-2.0
9+
* https://www.apache.org/licenses/LICENSE-2.0
1010
*
1111
* Unless required by applicable law or agreed to in writing, software
1212
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -16,9 +16,12 @@
1616
*/
1717
namespace Optimizely\OptimizelyConfig;
1818

19+
use Monolog\Logger;
1920
use Optimizely\Config\ProjectConfigInterface;
2021
use Optimizely\Entity\Experiment;
2122
use Optimizely\Entity\Variation;
23+
use Optimizely\Logger\DefaultLogger;
24+
use Optimizely\Logger\LoggerInterface;
2225

2326
class OptimizelyConfigService
2427
{
@@ -73,7 +76,14 @@ class OptimizelyConfigService
7376
*/
7477
private $featKeyOptlyVariableIdVariableMap;
7578

76-
public function __construct(ProjectConfigInterface $projectConfig)
79+
/**
80+
* Provided or default logger for logging.
81+
*
82+
* @var LoggerInterface $logger
83+
*/
84+
private readonly LoggerInterface $logger;
85+
86+
public function __construct(ProjectConfigInterface $projectConfig, LoggerInterface $logger = null)
7787
{
7888
$this->experiments = $projectConfig->getAllExperiments();
7989
$this->featureFlags = $projectConfig->getFeatureFlags();
@@ -82,7 +92,8 @@ public function __construct(ProjectConfigInterface $projectConfig)
8292
$this->environmentKey = $projectConfig->getEnvironmentKey();
8393
$this->sdkKey = $projectConfig->getSdkKey();
8494
$this->projectConfig = $projectConfig;
85-
95+
$this->logger = $logger ?: new DefaultLogger();
96+
8697
$this->createLookupMaps();
8798
}
8899

@@ -258,7 +269,7 @@ protected function getVariablesMap(Experiment $experiment, Variation $variation)
258269

259270
// Set default variables for variation.
260271
$variablesMap = $this->featKeyOptlyVariableKeyVariableMap[$featureKey];
261-
272+
262273
// Return default variable values if feature is not enabled.
263274
if (!$variation->getFeatureEnabled()) {
264275
return $variablesMap;
@@ -267,13 +278,13 @@ protected function getVariablesMap(Experiment $experiment, Variation $variation)
267278
// Set variation specific value if any.
268279
foreach ($variation->getVariables() as $variableUsage) {
269280
$id = $variableUsage->getId();
270-
281+
271282
$optVariable = $this->featKeyOptlyVariableIdVariableMap[$featureKey][$id];
272-
283+
273284
$key = $optVariable->getKey();
274285
$value = $variableUsage->getValue();
275286
$type = $optVariable->getType();
276-
287+
277288
$modifiedOptVariable = new OptimizelyVariable(
278289
$id,
279290
$key,
@@ -287,7 +298,7 @@ protected function getVariablesMap(Experiment $experiment, Variation $variation)
287298
return $variablesMap;
288299
}
289300

290-
301+
291302
/**
292303
* Generates Variations map for the given Experiment.
293304
*
@@ -301,7 +312,7 @@ protected function getVariationsMap(Experiment $experiment)
301312

302313
foreach ($experiment->getVariations() as $variation) {
303314
$variablesMap = $this->getVariablesMap($experiment, $variation);
304-
315+
305316
$variationKey = $variation->getKey();
306317
$optVariation = new OptimizelyVariation(
307318
$variation->getId(),
@@ -401,11 +412,17 @@ protected function getExperimentsMaps()
401412
foreach ($this->experiments as $exp) {
402413
$expId = $exp->getId();
403414
$expKey = $exp->getKey();
415+
404416
$audiences = '';
405417
if ($exp->getAudienceConditions() != null) {
406418
$audienceConditions = $exp->getAudienceConditions();
407419
$audiences = $this->getSerializedAudiences($audienceConditions);
408420
}
421+
422+
if (array_key_exists($expKey, $experimentsKeyMap)) {
423+
$this->logger->log(Logger::WARNING, sprintf('Duplicate experiment keys found in datafile: %s', $expKey));
424+
}
425+
409426
$optExp = new OptimizelyExperiment(
410427
$expId,
411428
$expKey,

tests/OptimizelyConfigTests/OptimizelyConfigServiceTest.php

+19-15
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,10 @@
1616
*/
1717
namespace Optimizely\Tests;
1818

19-
use Exception;
19+
use Monolog\Logger;
2020
use Optimizely\Config\DatafileProjectConfig;
2121
use Optimizely\ErrorHandler\NoOpErrorHandler;
22+
use Optimizely\Logger\DefaultLogger;
2223
use Optimizely\Logger\NoOpLogger;
2324
use Optimizely\OptimizelyConfig\OptimizelyAttribute;
2425
use Optimizely\OptimizelyConfig\OptimizelyAudience;
@@ -29,10 +30,12 @@
2930
use Optimizely\OptimizelyConfig\OptimizelyFeature;
3031
use Optimizely\OptimizelyConfig\OptimizelyVariable;
3132
use Optimizely\OptimizelyConfig\OptimizelyVariation;
33+
use PHPUnit\Framework\MockObject\MockObject;
3234
use PHPUnit\Framework\TestCase;
3335

3436
class OptimizelyConfigServiceTest extends TestCase
3537
{
38+
private MockObject $loggerMock;
3639

3740
protected function setUp() : void
3841
{
@@ -149,6 +152,9 @@ protected function setUp() : void
149152
$this->expectedExpIdMap['17301270474'] = $abExperiment;
150153
$this->expectedExpIdMap['17258450439'] = $groupExperiment;
151154
$this->expectedExpIdMap['17279300791'] = $featExperiment;
155+
156+
// Mock Logger
157+
$this->loggerMock = $this->getMockBuilder(DefaultLogger::class)->getMock();
152158
}
153159

154160
protected static function getMethod($name)
@@ -203,28 +209,26 @@ public function testGetVariationsMap()
203209

204210
public function testGetOptimizelyConfigWithDuplicateExperimentKeys()
205211
{
212+
$duplicatedExperimentKey = 'targeted_delivery';
213+
$secondDuplicatedExperimentId = '9300000007573';
214+
$this->loggerMock->expects($this->once())
215+
->method('log')
216+
->with(
217+
Logger::WARNING,
218+
sprintf('Duplicate experiment keys found in datafile: %s', $duplicatedExperimentKey)
219+
);
220+
206221
$this->datafile = DATAFILE_FOR_DUPLICATE_EXP_KEYS;
207222
$this->projectConfig = new DatafileProjectConfig(
208223
$this->datafile,
209224
new NoOpLogger(),
210225
new NoOpErrorHandler()
211226
);
212-
$this->optConfigService = new OptimizelyConfigService($this->projectConfig);
227+
$this->optConfigService = new OptimizelyConfigService($this->projectConfig, $this->loggerMock);
213228
$optimizelyConfig = $this->optConfigService->getConfig();
214-
$this->assertEquals(Count($optimizelyConfig->getExperimentsMap()), 1);
215-
$experimentRulesFlag1 = $optimizelyConfig->getFeaturesMap()['flag1']->getExperimentRules(); // 9300000007569
216-
$experimentRulesFlag2 = $optimizelyConfig->getFeaturesMap()['flag2']->getExperimentRules(); // 9300000007573
217-
foreach ($experimentRulesFlag1 as $experimentRule) {
218-
if ($experimentRule->getKey() == 'targeted_delivery') {
219-
$this->assertEquals($experimentRule->getId(), '9300000007569');
220-
}
221-
}
222229

223-
foreach ($experimentRulesFlag2 as $experimentRule) {
224-
if ($experimentRule->getKey() == 'targeted_delivery') {
225-
$this->assertEquals($experimentRule->getId(), '9300000007573');
226-
}
227-
}
230+
$this->assertEquals(1, Count($optimizelyConfig->getExperimentsMap()));
231+
$this->assertEquals($optimizelyConfig->getExperimentsMap()[$duplicatedExperimentKey]->getId(), $secondDuplicatedExperimentId);
228232
}
229233

230234
public function testGetOptimizelyConfigWithDuplicateRuleKeys()

0 commit comments

Comments
 (0)