Skip to content

Commit dea0c92

Browse files
authored
Account for traffic allocation on all flags (#87)
1 parent 9c9da6f commit dea0c92

File tree

8 files changed

+102
-30
lines changed

8 files changed

+102
-30
lines changed

.circleci/config.yml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,13 +75,12 @@ jobs:
7575
- run:
7676
name: php-cs-fixer check
7777
command: composer cs-check
78-
- run: mkdir -p ~/phpunit
7978
- run:
8079
name: run tests with highest compatible dependency versions
8180
command: php -d xdebug.mode=coverage vendor/bin/phpunit
8281
enviroment:
8382
XDEBUG_MODE: coverage
8483
- store_test_results:
85-
path: ~/phpunit
84+
path: build/phpunit
8685
- store_artifacts:
87-
path: ~/phpunit
86+
path: build/phpunit

build/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
*
2+
!.gitignore

phpunit.xml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,16 @@
22
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd" bootstrap="vendor/autoload.php" colors="true" beStrictAboutChangesToGlobalState="true" beStrictAboutOutputDuringTests="true" beStrictAboutResourceUsageDuringSmallTests="true" beStrictAboutTestsThatDoNotTestAnything="true" beStrictAboutTodoAnnotatedTests="true" verbose="true" defaultTimeLimit="10">
33

44
<logging>
5-
<junit outputFile="~/phpunit/junit.xml"/>
5+
<junit outputFile="build/phpunit/junit.xml"/>
66
</logging>
77

88
<coverage>
99
<include>
1010
<directory suffix=".php">src</directory>
1111
</include>
1212
<report>
13-
<html outputDirectory="~/phpunit/html-coverage"/>
14-
<xml outputDirectory="~/phpunit/xml-coverage"/>
13+
<html outputDirectory="build/phpunit/html-coverage"/>
14+
<xml outputDirectory="build/phpunit/xml-coverage"/>
1515
</report>
1616
</coverage>
1717

src/LaunchDarkly/FeatureFlagsState.php

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,20 +47,38 @@ public function addFlag(
4747
bool $withReason = false,
4848
bool $detailsOnlyIfTracked = false
4949
): void {
50+
$requireExperimentData = $flag->isExperiment($detail->getReason());
51+
5052
$this->_flagValues[$flag->getKey()] = $detail->getValue();
5153
$meta = [];
52-
if (!$detailsOnlyIfTracked || $flag->isTrackEvents() || $flag->getDebugEventsUntilDate()) {
53-
$meta['version'] = $flag->getVersion();
54-
if ($withReason) {
55-
$meta['reason'] = $detail->getReason();
54+
55+
$trackEvents = $flag->isTrackEvents() || $requireExperimentData;
56+
$trackReason = $requireExperimentData;
57+
58+
$omitDetails = false;
59+
if ($detailsOnlyIfTracked) {
60+
if (!$trackEvents && !$trackReason && !$flag->getDebugEventsUntilDate()) {
61+
$omitDetails = true;
5662
}
5763
}
64+
65+
$reason = (!$withReason && !$trackReason) ? null : $detail->getReason();
66+
67+
if ($reason && !$omitDetails) {
68+
$meta['reason'] = $reason;
69+
}
70+
if (!$omitDetails) {
71+
$meta['version'] = $flag->getVersion();
72+
}
5873
if (!is_null($detail->getVariationIndex())) {
5974
$meta['variation'] = $detail->getVariationIndex();
6075
}
61-
if ($flag->isTrackEvents()) {
76+
if ($trackEvents) {
6277
$meta['trackEvents'] = true;
6378
}
79+
if ($trackReason) {
80+
$meta['trackReason'] = true;
81+
}
6482
if ($flag->getDebugEventsUntilDate()) {
6583
$meta['debugEventsUntilDate'] = $flag->getDebugEventsUntilDate();
6684
}

src/LaunchDarkly/Impl/Events/EventFactory.php

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
namespace LaunchDarkly\Impl\Events;
44

55
use LaunchDarkly\EvaluationDetail;
6-
use LaunchDarkly\EvaluationReason;
76
use LaunchDarkly\Impl\Model\FeatureFlag;
87
use LaunchDarkly\Impl\Util;
98
use LaunchDarkly\LDUser;
@@ -37,7 +36,7 @@ public function newEvalEvent(
3736
$default,
3837
$prereqOfFlag = null
3938
): array {
40-
$addExperimentData = static::isExperiment($flag, $detail->getReason());
39+
$addExperimentData = $flag->isExperiment($detail->getReason());
4140
$e = [
4241
'kind' => 'feature',
4342
'creationDate' => Util::currentTimeUnixMillis(),
@@ -186,21 +185,4 @@ private static function contextKind(LDUser $user): string
186185
return 'user';
187186
}
188187
}
189-
190-
private static function isExperiment(FeatureFlag $flag, EvaluationReason $reason): bool
191-
{
192-
if ($reason->isInExperiment()) {
193-
return true;
194-
}
195-
switch ($reason->getKind()) {
196-
case 'RULE_MATCH':
197-
$i = $reason->getRuleIndex();
198-
$rules = $flag->getRules();
199-
return isset($i) && $i >= 0 && $i < count($rules) && $rules[$i]->isTrackEvents();
200-
case 'FALLTHROUGH':
201-
return $flag->isTrackEventsFallthrough();
202-
default:
203-
return false;
204-
}
205-
}
206188
}

src/LaunchDarkly/Impl/Model/FeatureFlag.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,24 @@ public function evaluate(LDUser $user, FeatureRequester $featureRequester, Event
137137
return new EvalResult($detail, $prereqEvents);
138138
}
139139

140+
public function isExperiment(EvaluationReason $reason): bool
141+
{
142+
if ($reason->isInExperiment()) {
143+
return true;
144+
}
145+
146+
switch ($reason->getKind()) {
147+
case 'RULE_MATCH':
148+
$i = $reason->getRuleIndex();
149+
$rules = $this->getRules();
150+
return isset($i) && $i >= 0 && $i < count($rules) && $rules[$i]->isTrackEvents();
151+
case 'FALLTHROUGH':
152+
return $this->isTrackEventsFallthrough();
153+
default:
154+
return false;
155+
}
156+
}
157+
140158
private function evaluateInternal(
141159
LDUser $user,
142160
FeatureRequester $featureRequester,

src/LaunchDarkly/LDClient.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,11 @@ public function identify(LDUser $user): void
341341
* - `clientSideOnly`: Set this to true to specify that only flags marked for client-side use
342342
* should be included; by default, all flags are included
343343
* - `withReasons`: Set this to true to include evaluation reasons (see {@see \LaunchDarkly\LDClient::variationDetail()})
344+
* - `detailsOnlyForTrackedFlags`: Set to true to omit any metadata that is
345+
* normally only used for event generation, such as flag versions and
346+
* evaluation reasons, unless the flag has event tracking or debugging
347+
* turned on
348+
*
344349
* @return FeatureFlagsState a FeatureFlagsState object (will never be null)
345350
*/
346351
public function allFlagsState(LDUser $user, array $options = []): FeatureFlagsState

tests/LDClientTest.php

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,54 @@ public function testAllFlagsStateReturnsState()
377377
$this->assertEquals($expectedState, $state->jsonSerialize());
378378
}
379379

380+
public function testAllFlagsStateHandlesExperimentationReasons()
381+
{
382+
$flagJson = [
383+
'key' => 'feature',
384+
'version' => 100,
385+
'deleted' => false,
386+
'on' => true,
387+
'targets' => [],
388+
'prerequisites' => [],
389+
'rules' => [],
390+
'offVariation' => 1,
391+
'fallthrough' => ['variation' => 0],
392+
'variations' => ['fall', 'off', 'on'],
393+
'salt' => '',
394+
'trackEvents' => false,
395+
'trackEventsFallthrough' => true,
396+
'debugEventsUntilDate' => 1000
397+
];
398+
$flag = FeatureFlag::decode($flagJson);
399+
400+
MockFeatureRequester::$flags = ['feature' => $flag];
401+
$client = $this->makeClient();
402+
403+
$builder = new LDUserBuilder(3);
404+
$user = $builder->build();
405+
$state = $client->allFlagsState($user);
406+
407+
$this->assertTrue($state->isValid());
408+
$this->assertEquals(['feature' => 'fall'], $state->toValuesMap());
409+
$expectedState = [
410+
'feature' => 'fall',
411+
'$flagsState' => [
412+
'feature' => [
413+
'variation' => 0,
414+
'version' => 100,
415+
'trackEvents' => true,
416+
'trackReason' => true,
417+
'debugEventsUntilDate' => 1000,
418+
'reason' => [
419+
'kind' => 'FALLTHROUGH',
420+
],
421+
]
422+
],
423+
'$valid' => true
424+
];
425+
$this->assertEquals($expectedState, $state->jsonSerialize());
426+
}
427+
380428
public function testAllFlagsStateReturnsStateWithReasons()
381429
{
382430
$flagJson = [

0 commit comments

Comments
 (0)