Skip to content

Commit a9d1e23

Browse files
authored
fix: Implicit identity key not supported for % Split operator (#115)
1 parent ae35794 commit a9d1e23

File tree

10 files changed

+32
-61
lines changed

10 files changed

+32
-61
lines changed

.gitmodules

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
[submodule "tests/Engine/EngineTests/EngineTestData"]
22
path = tests/Engine/EngineTests/EngineTestData
33
url = [email protected]:Flagsmith/engine-test-data.git
4-
tag = v2.5.0
4+
tag = v3.2.1

composer.phar

2.84 MB
Binary file not shown.

src/Engine/Engine.php

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -44,25 +44,26 @@ public static function getEvaluationResult($context): EvaluationResult
4444
$evaluatedFeatures = [];
4545

4646
/** @var array<string, SegmentContext> */
47-
$matchedSegmentsByFeatureKey = [];
47+
$matchedSegmentsByFeatureName = [];
4848

4949
/** @var array<string, FlagResult> */
5050
$evaluatedFlags = [];
5151

52+
$context = self::getEnrichedContext($context);
53+
5254
foreach ($context->segments as $segment) {
5355
if (!self::isContextInSegment($context, $segment)) {
5456
continue;
5557
}
5658

5759
$segmentResult = new SegmentResult();
58-
$segmentResult->key = $segment->key;
5960
$segmentResult->name = $segment->name;
6061
$segmentResult->metadata = $segment->metadata;
6162
$evaluatedSegments[] = $segmentResult;
6263

6364
foreach ($segment->overrides as $overrideFeature) {
64-
$featureKey = $overrideFeature->feature_key;
65-
$evaluatedFeature = $evaluatedFeatures[$featureKey] ?? null;
65+
$featureName = $overrideFeature->name;
66+
$evaluatedFeature = $evaluatedFeatures[$featureName] ?? null;
6667
if ($evaluatedFeature) {
6768
$overrideWinsPriority =
6869
($overrideFeature->priority ?? self::WEAKEST_PRIORITY) <
@@ -72,19 +73,18 @@ public static function getEvaluationResult($context): EvaluationResult
7273
}
7374
}
7475

75-
$evaluatedFeatures[$featureKey] = $overrideFeature;
76-
$matchedSegmentsByFeatureKey[$featureKey] = $segment;
76+
$evaluatedFeatures[$featureName] = $overrideFeature;
77+
$matchedSegmentsByFeatureName[$featureName] = $segment;
7778
}
7879
}
7980

8081
foreach ($context->features as $feature) {
81-
$featureKey = $feature->feature_key;
8282
$featureName = $feature->name;
83-
$evaluatedFeature = $evaluatedFeatures[$featureKey] ?? null;
83+
$evaluatedFeature = $evaluatedFeatures[$featureName] ?? null;
8484
if ($evaluatedFeature) {
8585
$evaluatedFlags[$featureName] = self::getFlagResultFromSegmentContext(
8686
$evaluatedFeature,
87-
$matchedSegmentsByFeatureKey[$featureKey],
87+
$matchedSegmentsByFeatureName[$featureName],
8888
);
8989
continue;
9090
}
@@ -101,6 +101,24 @@ public static function getEvaluationResult($context): EvaluationResult
101101
return $result;
102102
}
103103

104+
/**
105+
* Get an enriched evaluation context with derived values:
106+
* - `$.identity.key` if missing
107+
* Returns a cloned context if any enrichment is applied.
108+
* Returns the original context if no enrichment is needed.
109+
* @param EvaluationContext $context
110+
* @return EvaluationContext
111+
*/
112+
private static function getEnrichedContext(EvaluationContext $context): EvaluationContext
113+
{
114+
if ($context->identity !== null && $context->identity->key === null) {
115+
$context = clone $context;
116+
$context->identity = clone $context->identity;
117+
$context->identity->key = "{$context->environment->key}_{$context->identity->identifier}";
118+
}
119+
return $context;
120+
}
121+
104122
/**
105123
* @param EvaluationContext $context
106124
* @param SegmentContext $segment
@@ -147,7 +165,6 @@ private static function getFlagResultFromFeatureContext($feature, $splitKey)
147165
$percentageValue < $limit
148166
) {
149167
$flag = new FlagResult();
150-
$flag->feature_key = $feature->feature_key;
151168
$flag->name = $feature->name;
152169
$flag->enabled = $feature->enabled;
153170
$flag->value = $variant->value;
@@ -160,7 +177,6 @@ private static function getFlagResultFromFeatureContext($feature, $splitKey)
160177
}
161178

162179
$flag = new FlagResult();
163-
$flag->feature_key = $feature->feature_key;
164180
$flag->name = $feature->name;
165181
$flag->enabled = $feature->enabled;
166182
$flag->value = $feature->value;
@@ -177,7 +193,6 @@ private static function getFlagResultFromFeatureContext($feature, $splitKey)
177193
private static function getFlagResultFromSegmentContext($feature, $segment)
178194
{
179195
$flag = new FlagResult();
180-
$flag->feature_key = $feature->feature_key;
181196
$flag->name = $feature->name;
182197
$flag->enabled = $feature->enabled;
183198
$flag->value = $feature->value;
@@ -259,7 +274,7 @@ private static function _contextMatchesCondition(
259274

260275
switch ($condition->operator) {
261276
case SegmentConditionOperator::IN:
262-
if ($contextValue === null) {
277+
if ($contextValue === null || gettype($contextValue) === 'boolean') {
263278
return false;
264279
}
265280
if (is_array($condition->value)) {
@@ -278,7 +293,8 @@ private static function _contextMatchesCondition(
278293
$inValues = explode(',', $condition->value);
279294
}
280295
}
281-
$inValues = array_map($cast, $inValues);
296+
$contextValue = strval($contextValue);
297+
$inValues = array_map('strval', $inValues);
282298
return in_array($contextValue, $inValues, strict: true);
283299

284300
case SegmentConditionOperator::PERCENTAGE_SPLIT:

src/Engine/Utils/Types/Context/EvaluationContext.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,6 @@ private static function _convertFeatures($jsonFeatures): array
104104
foreach ($jsonFeatures as $jsonFeature) {
105105
$feature = new FeatureContext();
106106
$feature->key = $jsonFeature->key;
107-
$feature->feature_key = (string) $jsonFeature->feature_key;
108107
$feature->name = $jsonFeature->name;
109108
$feature->enabled = $jsonFeature->enabled;
110109
$feature->value = $jsonFeature->value;

src/Engine/Utils/Types/Context/FeatureContext.php

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,6 @@ class FeatureContext
77
/** @var string */
88
public $key;
99

10-
/** @var string */
11-
public $feature_key;
12-
1310
/** @var string */
1411
public $name;
1512

src/Engine/Utils/Types/Result/FlagResult.php

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,6 @@
44

55
class FlagResult implements \JsonSerializable
66
{
7-
/** @var string */
8-
public $feature_key;
9-
107
/** @var string */
118
public $name;
129

src/Engine/Utils/Types/Result/SegmentResult.php

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,6 @@
44

55
class SegmentResult implements \JsonSerializable
66
{
7-
/** @var string */
8-
public $key;
9-
107
/** @var string */
118
public $name;
129

src/Utils/Mappers.php

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,6 @@ public static function mapContextAndIdentityToContext($context, $identifier, $tr
6464
{
6565
$identity = new IdentityContext();
6666
$identity->identifier = $identifier;
67-
$identity->key = "{$context->environment->key}_{$identifier}";
6867
$identity->traits = (array) $traits;
6968

7069
$context = $context->deepClone();
@@ -112,7 +111,6 @@ private static function _mapEnvironmentDocumentFeatureStatesToFeatureContexts($f
112111
foreach ($featureStates as $featureState) {
113112
$feature = new FeatureContext();
114113
$feature->key = (string) ($featureState->django_id ?? $featureState->featurestate_uuid);
115-
$feature->feature_key = (string) $featureState->feature->id;
116114
$feature->name = $featureState->feature->name;
117115
$feature->enabled = $featureState->enabled;
118116
$feature->value = $featureState->feature_state_value;
@@ -190,7 +188,6 @@ private static function _mapIdentityOverridesToSegments($identityOverrides)
190188
[$featureId, $featureName, $enabled, $value] = $overrideKey;
191189
$feature = new FeatureContext();
192190
$feature->key = ''; // Not used in identity overrides
193-
$feature->feature_key = (string) $featureId;
194191
$feature->name = $featureName;
195192
$feature->enabled = $enabled;
196193
$feature->value = $value;
Submodule EngineTestData updated 158 files

tests/Utils/MappersTest.php

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,6 @@ public function testMapEnvironmentDocumentToContextProducesEvaluationContext():
6363
$this->assertEquals(['overridden-id'], $context->segments[$overrideKey]->rules[0]->conditions[0]->value);
6464

6565
$this->assertEquals('', $context->segments[$overrideKey]->overrides[0]->key);
66-
$this->assertEquals(1, $context->segments[$overrideKey]->overrides[0]->feature_key);
6766
$this->assertEquals('some_feature', $context->segments[$overrideKey]->overrides[0]->name);
6867
$this->assertFalse($context->segments[$overrideKey]->overrides[0]->enabled);
6968
$this->assertEquals('some-overridden-value', $context->segments[$overrideKey]->overrides[0]->value);
@@ -74,7 +73,6 @@ public function testMapEnvironmentDocumentToContextProducesEvaluationContext():
7473
$this->assertCount(3, $context->features);
7574
$this->assertArrayHasKey('some_feature', $context->features);
7675
$this->assertEquals('00000000-0000-0000-0000-000000000000', $context->features['some_feature']->key);
77-
$this->assertEquals('1', $context->features['some_feature']->feature_key);
7876
$this->assertEquals('some_feature', $context->features['some_feature']->name);
7977
$this->assertTrue($context->features['some_feature']->enabled);
8078
$this->assertEquals('some-value', $context->features['some_feature']->value);
@@ -86,7 +84,6 @@ public function testMapEnvironmentDocumentToContextProducesEvaluationContext():
8684
$this->assertArrayHasKey('mv_feature_with_ids', $context->features);
8785
$mvFeatureWithIds = $context->features['mv_feature_with_ids'];
8886
$this->assertEquals('2', $mvFeatureWithIds->key);
89-
$this->assertEquals('2', $mvFeatureWithIds->feature_key);
9087
$this->assertEquals('mv_feature_with_ids', $mvFeatureWithIds->name);
9188
$this->assertTrue($mvFeatureWithIds->enabled);
9289
$this->assertEquals('default_value', $mvFeatureWithIds->value);
@@ -108,7 +105,6 @@ public function testMapEnvironmentDocumentToContextProducesEvaluationContext():
108105
$this->assertArrayHasKey('mv_feature_without_ids', $context->features);
109106
$mvFeatureWithoutIds = $context->features['mv_feature_without_ids'];
110107
$this->assertEquals('3', $mvFeatureWithoutIds->key);
111-
$this->assertEquals('3', $mvFeatureWithoutIds->feature_key);
112108
$this->assertEquals('mv_feature_without_ids', $mvFeatureWithoutIds->name);
113109
$this->assertFalse($mvFeatureWithoutIds->enabled);
114110
$this->assertEquals('fallback_value', $mvFeatureWithoutIds->value);
@@ -127,32 +123,4 @@ public function testMapEnvironmentDocumentToContextProducesEvaluationContext():
127123
$this->assertEquals(25.0, $mvFeatureWithoutIds->variants[2]->weight);
128124
$this->assertEquals(2, $mvFeatureWithoutIds->variants[2]->priority); // Third
129125
}
130-
131-
public function testMapContextAndIdentityToContextProducesEvaluationContextWithIdentity(): void
132-
{
133-
// Given
134-
$originalContext = new EvaluationContext();
135-
$originalContext->environment = new EnvironmentContext();
136-
$originalContext->environment->key = 'public-env-key';
137-
138-
// When
139-
$context = Mappers::mapContextAndIdentityToContext(
140-
context: $originalContext,
141-
identifier: 'neo',
142-
traits: (object) [
143-
'chosen-pill' => 'red',
144-
'has-met-the-oracle' => true,
145-
],
146-
);
147-
148-
// Then
149-
$this->assertInstanceOf(EvaluationContext::class, $context);
150-
$this->assertNotSame($originalContext, $context);
151-
$this->assertEquals('public-env-key', $context->environment->key);
152-
$this->assertEquals('neo', $context->identity->identifier);
153-
$this->assertEquals('public-env-key_neo', $context->identity->key);
154-
$this->assertCount(2, $context->identity->traits);
155-
$this->assertEquals('red', $context->identity->traits['chosen-pill']);
156-
$this->assertTrue($context->identity->traits['has-met-the-oracle']);
157-
}
158126
}

0 commit comments

Comments
 (0)