diff --git a/.gitmodules b/.gitmodules index 4a9a831..f8ca411 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "tests/Engine/EngineTests/EngineTestData"] path = tests/Engine/EngineTests/EngineTestData url = git@github.com:Flagsmith/engine-test-data.git - tag = v2.5.0 + tag = v3.2.1 diff --git a/composer.phar b/composer.phar new file mode 100755 index 0000000..e6ba7bb Binary files /dev/null and b/composer.phar differ diff --git a/src/Engine/Engine.php b/src/Engine/Engine.php index 66b3a06..f1e97e4 100644 --- a/src/Engine/Engine.php +++ b/src/Engine/Engine.php @@ -44,25 +44,26 @@ public static function getEvaluationResult($context): EvaluationResult $evaluatedFeatures = []; /** @var array */ - $matchedSegmentsByFeatureKey = []; + $matchedSegmentsByFeatureName = []; /** @var array */ $evaluatedFlags = []; + $context = self::getEnrichedContext($context); + foreach ($context->segments as $segment) { if (!self::isContextInSegment($context, $segment)) { continue; } $segmentResult = new SegmentResult(); - $segmentResult->key = $segment->key; $segmentResult->name = $segment->name; $segmentResult->metadata = $segment->metadata; $evaluatedSegments[] = $segmentResult; foreach ($segment->overrides as $overrideFeature) { - $featureKey = $overrideFeature->feature_key; - $evaluatedFeature = $evaluatedFeatures[$featureKey] ?? null; + $featureName = $overrideFeature->name; + $evaluatedFeature = $evaluatedFeatures[$featureName] ?? null; if ($evaluatedFeature) { $overrideWinsPriority = ($overrideFeature->priority ?? self::WEAKEST_PRIORITY) < @@ -72,19 +73,18 @@ public static function getEvaluationResult($context): EvaluationResult } } - $evaluatedFeatures[$featureKey] = $overrideFeature; - $matchedSegmentsByFeatureKey[$featureKey] = $segment; + $evaluatedFeatures[$featureName] = $overrideFeature; + $matchedSegmentsByFeatureName[$featureName] = $segment; } } foreach ($context->features as $feature) { - $featureKey = $feature->feature_key; $featureName = $feature->name; - $evaluatedFeature = $evaluatedFeatures[$featureKey] ?? null; + $evaluatedFeature = $evaluatedFeatures[$featureName] ?? null; if ($evaluatedFeature) { $evaluatedFlags[$featureName] = self::getFlagResultFromSegmentContext( $evaluatedFeature, - $matchedSegmentsByFeatureKey[$featureKey], + $matchedSegmentsByFeatureName[$featureName], ); continue; } @@ -101,6 +101,24 @@ public static function getEvaluationResult($context): EvaluationResult return $result; } + /** + * Get an enriched evaluation context with derived values: + * - `$.identity.key` if missing + * Returns a cloned context if any enrichment is applied. + * Returns the original context if no enrichment is needed. + * @param EvaluationContext $context + * @return EvaluationContext + */ + private static function getEnrichedContext(EvaluationContext $context): EvaluationContext + { + if ($context->identity !== null && $context->identity->key === null) { + $context = clone $context; + $context->identity = clone $context->identity; + $context->identity->key = "{$context->environment->key}_{$context->identity->identifier}"; + } + return $context; + } + /** * @param EvaluationContext $context * @param SegmentContext $segment @@ -147,7 +165,6 @@ private static function getFlagResultFromFeatureContext($feature, $splitKey) $percentageValue < $limit ) { $flag = new FlagResult(); - $flag->feature_key = $feature->feature_key; $flag->name = $feature->name; $flag->enabled = $feature->enabled; $flag->value = $variant->value; @@ -160,7 +177,6 @@ private static function getFlagResultFromFeatureContext($feature, $splitKey) } $flag = new FlagResult(); - $flag->feature_key = $feature->feature_key; $flag->name = $feature->name; $flag->enabled = $feature->enabled; $flag->value = $feature->value; @@ -177,7 +193,6 @@ private static function getFlagResultFromFeatureContext($feature, $splitKey) private static function getFlagResultFromSegmentContext($feature, $segment) { $flag = new FlagResult(); - $flag->feature_key = $feature->feature_key; $flag->name = $feature->name; $flag->enabled = $feature->enabled; $flag->value = $feature->value; @@ -259,7 +274,7 @@ private static function _contextMatchesCondition( switch ($condition->operator) { case SegmentConditionOperator::IN: - if ($contextValue === null) { + if ($contextValue === null || gettype($contextValue) === 'boolean') { return false; } if (is_array($condition->value)) { @@ -278,7 +293,8 @@ private static function _contextMatchesCondition( $inValues = explode(',', $condition->value); } } - $inValues = array_map($cast, $inValues); + $contextValue = strval($contextValue); + $inValues = array_map('strval', $inValues); return in_array($contextValue, $inValues, strict: true); case SegmentConditionOperator::PERCENTAGE_SPLIT: diff --git a/src/Engine/Utils/Types/Context/EvaluationContext.php b/src/Engine/Utils/Types/Context/EvaluationContext.php index bc10665..7630116 100644 --- a/src/Engine/Utils/Types/Context/EvaluationContext.php +++ b/src/Engine/Utils/Types/Context/EvaluationContext.php @@ -104,7 +104,6 @@ private static function _convertFeatures($jsonFeatures): array foreach ($jsonFeatures as $jsonFeature) { $feature = new FeatureContext(); $feature->key = $jsonFeature->key; - $feature->feature_key = (string) $jsonFeature->feature_key; $feature->name = $jsonFeature->name; $feature->enabled = $jsonFeature->enabled; $feature->value = $jsonFeature->value; diff --git a/src/Engine/Utils/Types/Context/FeatureContext.php b/src/Engine/Utils/Types/Context/FeatureContext.php index 6588414..9771190 100644 --- a/src/Engine/Utils/Types/Context/FeatureContext.php +++ b/src/Engine/Utils/Types/Context/FeatureContext.php @@ -7,9 +7,6 @@ class FeatureContext /** @var string */ public $key; - /** @var string */ - public $feature_key; - /** @var string */ public $name; diff --git a/src/Engine/Utils/Types/Result/FlagResult.php b/src/Engine/Utils/Types/Result/FlagResult.php index b992764..f68742b 100644 --- a/src/Engine/Utils/Types/Result/FlagResult.php +++ b/src/Engine/Utils/Types/Result/FlagResult.php @@ -4,9 +4,6 @@ class FlagResult implements \JsonSerializable { - /** @var string */ - public $feature_key; - /** @var string */ public $name; diff --git a/src/Engine/Utils/Types/Result/SegmentResult.php b/src/Engine/Utils/Types/Result/SegmentResult.php index 7d3b20e..4f36f09 100644 --- a/src/Engine/Utils/Types/Result/SegmentResult.php +++ b/src/Engine/Utils/Types/Result/SegmentResult.php @@ -4,9 +4,6 @@ class SegmentResult implements \JsonSerializable { - /** @var string */ - public $key; - /** @var string */ public $name; diff --git a/src/Utils/Mappers.php b/src/Utils/Mappers.php index 273ba74..8e2e62a 100644 --- a/src/Utils/Mappers.php +++ b/src/Utils/Mappers.php @@ -64,7 +64,6 @@ public static function mapContextAndIdentityToContext($context, $identifier, $tr { $identity = new IdentityContext(); $identity->identifier = $identifier; - $identity->key = "{$context->environment->key}_{$identifier}"; $identity->traits = (array) $traits; $context = $context->deepClone(); @@ -112,7 +111,6 @@ private static function _mapEnvironmentDocumentFeatureStatesToFeatureContexts($f foreach ($featureStates as $featureState) { $feature = new FeatureContext(); $feature->key = (string) ($featureState->django_id ?? $featureState->featurestate_uuid); - $feature->feature_key = (string) $featureState->feature->id; $feature->name = $featureState->feature->name; $feature->enabled = $featureState->enabled; $feature->value = $featureState->feature_state_value; @@ -190,7 +188,6 @@ private static function _mapIdentityOverridesToSegments($identityOverrides) [$featureId, $featureName, $enabled, $value] = $overrideKey; $feature = new FeatureContext(); $feature->key = ''; // Not used in identity overrides - $feature->feature_key = (string) $featureId; $feature->name = $featureName; $feature->enabled = $enabled; $feature->value = $value; diff --git a/tests/Engine/EngineTests/EngineTestData b/tests/Engine/EngineTests/EngineTestData index 41c2021..218757f 160000 --- a/tests/Engine/EngineTests/EngineTestData +++ b/tests/Engine/EngineTests/EngineTestData @@ -1 +1 @@ -Subproject commit 41c202145e375c712600e318c439456de5b221d7 +Subproject commit 218757fd23f932760be681a09c686c9d6ef55fad diff --git a/tests/Utils/MappersTest.php b/tests/Utils/MappersTest.php index 672298d..60d4304 100644 --- a/tests/Utils/MappersTest.php +++ b/tests/Utils/MappersTest.php @@ -63,7 +63,6 @@ public function testMapEnvironmentDocumentToContextProducesEvaluationContext(): $this->assertEquals(['overridden-id'], $context->segments[$overrideKey]->rules[0]->conditions[0]->value); $this->assertEquals('', $context->segments[$overrideKey]->overrides[0]->key); - $this->assertEquals(1, $context->segments[$overrideKey]->overrides[0]->feature_key); $this->assertEquals('some_feature', $context->segments[$overrideKey]->overrides[0]->name); $this->assertFalse($context->segments[$overrideKey]->overrides[0]->enabled); $this->assertEquals('some-overridden-value', $context->segments[$overrideKey]->overrides[0]->value); @@ -74,7 +73,6 @@ public function testMapEnvironmentDocumentToContextProducesEvaluationContext(): $this->assertCount(3, $context->features); $this->assertArrayHasKey('some_feature', $context->features); $this->assertEquals('00000000-0000-0000-0000-000000000000', $context->features['some_feature']->key); - $this->assertEquals('1', $context->features['some_feature']->feature_key); $this->assertEquals('some_feature', $context->features['some_feature']->name); $this->assertTrue($context->features['some_feature']->enabled); $this->assertEquals('some-value', $context->features['some_feature']->value); @@ -86,7 +84,6 @@ public function testMapEnvironmentDocumentToContextProducesEvaluationContext(): $this->assertArrayHasKey('mv_feature_with_ids', $context->features); $mvFeatureWithIds = $context->features['mv_feature_with_ids']; $this->assertEquals('2', $mvFeatureWithIds->key); - $this->assertEquals('2', $mvFeatureWithIds->feature_key); $this->assertEquals('mv_feature_with_ids', $mvFeatureWithIds->name); $this->assertTrue($mvFeatureWithIds->enabled); $this->assertEquals('default_value', $mvFeatureWithIds->value); @@ -108,7 +105,6 @@ public function testMapEnvironmentDocumentToContextProducesEvaluationContext(): $this->assertArrayHasKey('mv_feature_without_ids', $context->features); $mvFeatureWithoutIds = $context->features['mv_feature_without_ids']; $this->assertEquals('3', $mvFeatureWithoutIds->key); - $this->assertEquals('3', $mvFeatureWithoutIds->feature_key); $this->assertEquals('mv_feature_without_ids', $mvFeatureWithoutIds->name); $this->assertFalse($mvFeatureWithoutIds->enabled); $this->assertEquals('fallback_value', $mvFeatureWithoutIds->value); @@ -127,32 +123,4 @@ public function testMapEnvironmentDocumentToContextProducesEvaluationContext(): $this->assertEquals(25.0, $mvFeatureWithoutIds->variants[2]->weight); $this->assertEquals(2, $mvFeatureWithoutIds->variants[2]->priority); // Third } - - public function testMapContextAndIdentityToContextProducesEvaluationContextWithIdentity(): void - { - // Given - $originalContext = new EvaluationContext(); - $originalContext->environment = new EnvironmentContext(); - $originalContext->environment->key = 'public-env-key'; - - // When - $context = Mappers::mapContextAndIdentityToContext( - context: $originalContext, - identifier: 'neo', - traits: (object) [ - 'chosen-pill' => 'red', - 'has-met-the-oracle' => true, - ], - ); - - // Then - $this->assertInstanceOf(EvaluationContext::class, $context); - $this->assertNotSame($originalContext, $context); - $this->assertEquals('public-env-key', $context->environment->key); - $this->assertEquals('neo', $context->identity->identifier); - $this->assertEquals('public-env-key_neo', $context->identity->key); - $this->assertCount(2, $context->identity->traits); - $this->assertEquals('red', $context->identity->traits['chosen-pill']); - $this->assertTrue($context->identity->traits['has-met-the-oracle']); - } }