diff --git a/README.md b/README.md index 3319b6a..e41e24e 100644 --- a/README.md +++ b/README.md @@ -55,8 +55,8 @@ class MyService ## Controller attribute -You can also check for feature flag using an `#[IsEnabled]` attribute on a controller. You can use it on the whole -controller class as well as on a concrete method. +You can also check for feature flag using `#[IsEnabled]` and `#[IsNotEnabled]` attributes on a controller. You can use +it on the whole controller class as well as on a concrete method. ```php featureName; + } + + #[ExpectedValues([ + Response::HTTP_NOT_FOUND, + Response::HTTP_FORBIDDEN, + Response::HTTP_BAD_REQUEST, + Response::HTTP_UNAUTHORIZED, + Response::HTTP_SERVICE_UNAVAILABLE, + ])] + public function getErrorCode(): int + { + return $this->errorCode; + } } diff --git a/src/Attribute/IsNotEnabled.php b/src/Attribute/IsNotEnabled.php new file mode 100644 index 0000000..9de0f94 --- /dev/null +++ b/src/Attribute/IsNotEnabled.php @@ -0,0 +1,41 @@ +featureName; + } + + #[ExpectedValues([ + Response::HTTP_NOT_FOUND, + Response::HTTP_FORBIDDEN, + Response::HTTP_BAD_REQUEST, + Response::HTTP_UNAUTHORIZED, + Response::HTTP_SERVICE_UNAVAILABLE, + ])] + public function getErrorCode(): int + { + return $this->errorCode; + } +} diff --git a/src/Listener/ControllerAttributeResolver.php b/src/Listener/ControllerAttributeResolver.php index 77bda62..40f358f 100644 --- a/src/Listener/ControllerAttributeResolver.php +++ b/src/Listener/ControllerAttributeResolver.php @@ -12,11 +12,14 @@ use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; use Symfony\Component\HttpKernel\Exception\HttpException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +use Symfony\Component\HttpKernel\Exception\ServiceUnavailableHttpException; use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException; use Symfony\Component\HttpKernel\KernelEvents; use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; use Throwable; +use Unleash\Client\Bundle\Attribute\ControllerAttribute; use Unleash\Client\Bundle\Attribute\IsEnabled; +use Unleash\Client\Bundle\Attribute\IsNotEnabled; use Unleash\Client\Bundle\Event\BeforeExceptionThrownForAttributeEvent; use Unleash\Client\Bundle\Event\UnleashEvents; use Unleash\Client\Exception\InvalidValueException; @@ -61,36 +64,44 @@ public function onControllerResolved(ControllerEvent $event): void $reflectionClass = new ReflectionClass($class); $reflectionMethod = $reflectionClass->getMethod($method); - /** @var array> $attributes */ + /** @var array> $attributes */ $attributes = [ - ...$reflectionClass->getAttributes(IsEnabled::class), - ...$reflectionMethod->getAttributes(IsEnabled::class), + ...$reflectionClass->getAttributes(ControllerAttribute::class, ReflectionAttribute::IS_INSTANCEOF), + ...$reflectionMethod->getAttributes(ControllerAttribute::class, ReflectionAttribute::IS_INSTANCEOF), ]; foreach ($attributes as $attribute) { $attribute = $attribute->newInstance(); - assert($attribute instanceof IsEnabled); - if (!$this->unleash->isEnabled($attribute->featureName)) { + assert($attribute instanceof ControllerAttribute); + + $isFeatureEnabled = $this->unleash->isEnabled($attribute->getFeatureName()); + $throwException = match ($attribute::class) { + IsEnabled::class => !$isFeatureEnabled, + IsNotEnabled::class => $isFeatureEnabled, + default => false, + }; + if ($throwException) { throw $this->getException($attribute); } } } - private function getException(IsEnabled $attribute): HttpException|Throwable + private function getException(ControllerAttribute $attribute): HttpException|Throwable { - $event = new BeforeExceptionThrownForAttributeEvent($attribute->errorCode); + $event = new BeforeExceptionThrownForAttributeEvent($attribute->getErrorCode()); $this->eventDispatcher->dispatch($event, UnleashEvents::BEFORE_EXCEPTION_THROWN_FOR_ATTRIBUTE); $exception = $event->getException(); if ($exception !== null) { return $exception; } - return match ($attribute->errorCode) { + return match ($attribute->getErrorCode()) { Response::HTTP_BAD_REQUEST => new BadRequestHttpException(), Response::HTTP_UNAUTHORIZED => new UnauthorizedHttpException('Unauthorized'), Response::HTTP_FORBIDDEN => new AccessDeniedHttpException(), Response::HTTP_NOT_FOUND => new NotFoundHttpException(), - default => throw new InvalidValueException("Unsupported status code: {$attribute->errorCode}"), + Response::HTTP_SERVICE_UNAVAILABLE => new ServiceUnavailableHttpException(), + default => throw new InvalidValueException("Unsupported status code: {$attribute->getErrorCode()}"), }; } }