From 00961b3fe85960f2e3af8a11f3f6f9d38b62d9ec Mon Sep 17 00:00:00 2001 From: Leopold Jacquot Date: Thu, 9 Jan 2025 11:17:15 +0100 Subject: [PATCH] feat: add ResolveAll endpoint for bulk evaluation --- providers/Flagd/src/FlagdProvider.php | 5 ++ .../FlagdResponseResolutionAllAdapter.php | 22 +++++++ providers/Flagd/src/http/GrpcWebEndpoint.php | 1 + providers/Flagd/src/http/HttpService.php | 14 ++++- .../Flagd/src/service/ServiceInterface.php | 5 ++ .../Flagd/tests/unit/FlagdProviderTest.php | 58 +++++++++++++++---- 6 files changed, 93 insertions(+), 12 deletions(-) create mode 100644 providers/Flagd/src/http/FlagdResponseResolutionAllAdapter.php diff --git a/providers/Flagd/src/FlagdProvider.php b/providers/Flagd/src/FlagdProvider.php index cb72bc04..17b93591 100644 --- a/providers/Flagd/src/FlagdProvider.php +++ b/providers/Flagd/src/FlagdProvider.php @@ -53,4 +53,9 @@ public function resolveObjectValue(string $flagKey, mixed $defaultValue, ?Evalua { return $this->service->resolveValue($flagKey, FlagValueType::OBJECT, $defaultValue, $context); } + + public function resolveAllValues(?EvaluationContext $context = null): array + { + return $this->service->resolveValues($context); + } } diff --git a/providers/Flagd/src/http/FlagdResponseResolutionAllAdapter.php b/providers/Flagd/src/http/FlagdResponseResolutionAllAdapter.php new file mode 100644 index 00000000..8468d8c1 --- /dev/null +++ b/providers/Flagd/src/http/FlagdResponseResolutionAllAdapter.php @@ -0,0 +1,22 @@ + FlagdResponseResolutionDetailsAdapter::forSuccess($flagDetails), $response['flags'] + ); + } +} diff --git a/providers/Flagd/src/http/GrpcWebEndpoint.php b/providers/Flagd/src/http/GrpcWebEndpoint.php index 06110871..c72bcdd8 100644 --- a/providers/Flagd/src/http/GrpcWebEndpoint.php +++ b/providers/Flagd/src/http/GrpcWebEndpoint.php @@ -14,4 +14,5 @@ class GrpcWebEndpoint public const FLOAT = 'schema.v1.Service/ResolveFloat'; public const INTEGER = 'schema.v1.Service/ResolveInt'; public const OBJECT = 'schema.v1.Service/ResolveObject'; + public const ALL = 'schema.v1.Service/ResolveAll'; } diff --git a/providers/Flagd/src/http/HttpService.php b/providers/Flagd/src/http/HttpService.php index 1eec8994..48387b4e 100644 --- a/providers/Flagd/src/http/HttpService.php +++ b/providers/Flagd/src/http/HttpService.php @@ -96,12 +96,24 @@ public function resolveValue(string $flagKey, string $flagType, $defaultValue, ? return FlagdResponseResolutionDetailsAdapter::forSuccess($validDetails); } + public function resolveValues(?EvaluationContext $context): array + { + $response = $this->sendRequest(GrpcWebEndpoint::ALL, null, $context); + /** @var string[] $details */ + $details = json_decode((string) $response->getBody(), true); + + /** @var array{flags: array{array{value: mixed[]|bool|DateTime|float|int|string|null, variant: ?string, reason: ?string}}} $validDetails */ + $validDetails = $details; + + return FlagdResponseResolutionAllAdapter::forSuccess($validDetails); + } + private function buildRoute(string $path): string { return $this->target . '/' . $path; } - private function sendRequest(string $path, string $flagKey, ?EvaluationContext $context): ResponseInterface + private function sendRequest(string $path, ?string $flagKey, ?EvaluationContext $context): ResponseInterface { /** * This method is equivalent to: diff --git a/providers/Flagd/src/service/ServiceInterface.php b/providers/Flagd/src/service/ServiceInterface.php index c8950c83..5689071a 100644 --- a/providers/Flagd/src/service/ServiceInterface.php +++ b/providers/Flagd/src/service/ServiceInterface.php @@ -14,4 +14,9 @@ interface ServiceInterface * @param mixed[]|bool|DateTime|float|int|string|null $defaultValue */ public function resolveValue(string $flagKey, string $flagType, mixed $defaultValue, ?EvaluationContext $context): ResolutionDetails; + + /** + * @return ResolutionDetails[] + */ + public function resolveValues(?EvaluationContext $context): array; } diff --git a/providers/Flagd/tests/unit/FlagdProviderTest.php b/providers/Flagd/tests/unit/FlagdProviderTest.php index 2eca3114..fb845494 100644 --- a/providers/Flagd/tests/unit/FlagdProviderTest.php +++ b/providers/Flagd/tests/unit/FlagdProviderTest.php @@ -58,11 +58,22 @@ public function testCanInstantiateHttpWithConfigObject(): void $mockStreamFactory->shouldReceive('createStream')->andReturn($mockStream); $mockResponse = $this->mockery(ResponseInterface::class); - $mockResponse->shouldReceive('getBody->__toString')->andReturn("{ - \"value\":\"{$expectedValue}\", - \"variant\":\"{$expectedVariant}\", - \"reason\":\"{$expectedReason}\" - }"); + $mockResponse->shouldReceive('getBody->__toString')->andReturn( + "{ + \"value\":\"{$expectedValue}\", + \"variant\":\"{$expectedVariant}\", + \"reason\":\"{$expectedReason}\" + }", + "{ + \"flags\":{ + \"any-key\": + {\"reason\":\"{$expectedReason}\", + \"variant\":\"{$expectedVariant}\", + \"doubleValue\":\"{$expectedValue}\" + } + } + }" + ); $mockClient = $this->mockery(ClientInterface::class); $mockClient->shouldReceive('sendRequest')->with($mockRequest)->andReturn($mockResponse); @@ -90,6 +101,13 @@ public function testCanInstantiateHttpWithConfigObject(): void $this->assertEquals($expectedValue, $actualDetails->getValue()); $this->assertEquals($expectedVariant, $actualDetails->getVariant()); $this->assertEquals($expectedReason, $actualDetails->getReason()); + + $actualFlagsDetails = $provider->resolveAllValues(null); + + // Then + $this->assertEquals($expectedValue, $actualFlagsDetails['any-key']->getValue()); + $this->assertEquals($expectedVariant, $actualFlagsDetails['any-key']->getVariant()); + $this->assertEquals($expectedReason, $actualFlagsDetails['any-key']->getReason()); } public function testCanInstantiateHttpWithConfigArray(): void @@ -112,12 +130,23 @@ public function testCanInstantiateHttpWithConfigArray(): void $mockStreamFactory->shouldReceive('createStream')->andReturn($mockStream); $mockResponse = $this->mockery(ResponseInterface::class); - $mockResponse->shouldReceive('getBody->__toString')->andReturn("{ - \"value\":\"{$expectedValue}\", - \"variant\":\"{$expectedVariant}\", - \"reason\":\"{$expectedReason}\" - }"); - + $mockResponse->shouldReceive('getBody->__toString')->andReturn( + "{ + \"value\":\"{$expectedValue}\", + \"variant\":\"{$expectedVariant}\", + \"reason\":\"{$expectedReason}\" + }", + "{ + \"flags\":{ + \"any-key\": + {\"reason\":\"{$expectedReason}\", + \"variant\":\"{$expectedVariant}\", + \"doubleValue\":\"{$expectedValue}\" + } + } + }" + ); + $mockClient = $this->mockery(ClientInterface::class); $mockClient->shouldReceive('sendRequest')->with($mockRequest)->andReturn($mockResponse); @@ -148,5 +177,12 @@ public function testCanInstantiateHttpWithConfigArray(): void $this->assertEquals($expectedValue, $actualDetails->getValue()); $this->assertEquals($expectedVariant, $actualDetails->getVariant()); $this->assertEquals($expectedReason, $actualDetails->getReason()); + + $actualFlagsDetails = $provider->resolveAllValues(null); + + // Then + $this->assertEquals($expectedValue, $actualFlagsDetails['any-key']->getValue()); + $this->assertEquals($expectedVariant, $actualFlagsDetails['any-key']->getVariant()); + $this->assertEquals($expectedReason, $actualFlagsDetails['any-key']->getReason()); } }