Skip to content
This repository has been archived by the owner on Aug 22, 2023. It is now read-only.

Commit

Permalink
Merge d5cf6be into 14a2ca7
Browse files Browse the repository at this point in the history
  • Loading branch information
ajgarlag committed Dec 9, 2020
2 parents 14a2ca7 + d5cf6be commit 72c7a42
Show file tree
Hide file tree
Showing 9 changed files with 228 additions and 9 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
## [Unreleased]
### Added
- \#80 Support for multiple features in route definition @ajgarlag
- \#84 ContextResolveEvent to allow context customization when checking for a feature @ajgarlag

### Changed
- \#82 Restore support for Symfony 4.4 LTS @ajgarlag
Expand Down
50 changes: 50 additions & 0 deletions src/Event/ContextResolveEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php

namespace Flagception\Bundle\FlagceptionBundle\Event;

use Flagception\Model\Context;
use Symfony\Contracts\EventDispatcher\Event;

/**
* Class ContextResolveEvent
*
* @author Antonio J. García Lagar <aj@garcialagar.es>
* @package Flagception\Bundle\FlagceptionBundle\Listener
*/
class ContextResolveEvent extends Event
{
/**
* The feature
*
* @var string
*/
private $feature;

/**
* The context
*
* @var Context
*/
private $context;

public function __construct(string $feature, Context $context = null)
{
$this->feature = $feature;
$this->context = $context ?? new Context();
}

public function getFeature(): string
{
return $this->feature;
}

public function getContext(): Context
{
return $this->context;
}

public function setContext(Context $context): void
{
$this->context = $context;
}
}
37 changes: 32 additions & 5 deletions src/Listener/AnnotationSubscriber.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,17 @@

namespace Flagception\Bundle\FlagceptionBundle\Listener;

use Doctrine\Common\Annotations\Reader;
use Flagception\Bundle\FlagceptionBundle\Annotations\Feature;
use Flagception\Bundle\FlagceptionBundle\Event\ContextResolveEvent;
use Flagception\Manager\FeatureManagerInterface;
use Doctrine\Common\Annotations\Reader;
use ReflectionClass;
use ReflectionException;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\ControllerEvent;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;

/**
* Class AnnotationSubscriber
Expand All @@ -34,16 +36,28 @@ class AnnotationSubscriber implements EventSubscriberInterface
*/
private $manager;

/**
* The event dispatcher
*
* @var ?EventDispatcherInterface
*/
private $eventDispatcher;

/**
* FeatureListener constructor.
*
* @param Reader $reader
* @param FeatureManagerInterface $manager
* @param EventDispatcherInterface $eventDispatcher
*/
public function __construct(Reader $reader, FeatureManagerInterface $manager)
{
public function __construct(
Reader $reader,
FeatureManagerInterface $manager,
EventDispatcherInterface $eventDispatcher = null
) {
$this->reader = $reader;
$this->manager = $manager;
$this->eventDispatcher = $eventDispatcher;
}

/**
Expand Down Expand Up @@ -73,19 +87,32 @@ public function onKernelController(ControllerEvent $event)
return;
}



$object = new ReflectionClass($controller[0]);
foreach ($this->reader->getClassAnnotations($object) as $annotation) {
if ($annotation instanceof Feature) {
if (!$this->manager->isActive($annotation->name)) {
$context = null;
if (null !== $this->eventDispatcher) {
$contextEvent = $this->eventDispatcher->dispatch(new ContextResolveEvent($annotation->name));
$context = $contextEvent->getContext();
}

if (!$this->manager->isActive($annotation->name, $context)) {
throw new NotFoundHttpException('Feature for this class is not active.');
}
}
}

$method = $object->getMethod($controller[1]);
foreach ($this->reader->getMethodAnnotations($method) as $annotation) {
$context = null;
if (null !== $this->eventDispatcher) {
$contextEvent = $this->eventDispatcher->dispatch(new ContextResolveEvent($annotation->name));
$context = $contextEvent->getContext();
}
if ($annotation instanceof Feature) {
if (!$this->manager->isActive($annotation->name)) {
if (!$this->manager->isActive($annotation->name, $context)) {
throw new NotFoundHttpException('Feature for this method is not active.');
}
}
Expand Down
20 changes: 18 additions & 2 deletions src/Listener/RoutingMetadataSubscriber.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@

namespace Flagception\Bundle\FlagceptionBundle\Listener;

use Flagception\Bundle\FlagceptionBundle\Event\ContextResolveEvent;
use Flagception\Manager\FeatureManagerInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\ControllerEvent;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;

/**
* Class RoutingMetadataSubscriber
Expand All @@ -30,14 +32,23 @@ class RoutingMetadataSubscriber implements EventSubscriberInterface
*/
private $manager;

/**
* The event dispatcher
*
* @var ?EventDispatcherInterface
*/
private $eventDispatcher;

/**
* RoutingMetadataSubscriber constructor.
*
* @param FeatureManagerInterface $manager
* @param EventDispatcherInterface $eventDispatcher
*/
public function __construct(FeatureManagerInterface $manager)
public function __construct(FeatureManagerInterface $manager, EventDispatcherInterface $eventDispatcher = null)
{
$this->manager = $manager;
$this->eventDispatcher = $eventDispatcher;
}

/**
Expand All @@ -56,7 +67,12 @@ public function onKernelController(ControllerEvent $event)

$featureNames = (array) $event->getRequest()->attributes->get(static::FEATURE_KEY);
foreach ($featureNames as $featureName) {
if (!$this->manager->isActive($featureName)) {
$context = null;
if (null !== $this->eventDispatcher) {
$contextEvent = $this->eventDispatcher->dispatch(new ContextResolveEvent($featureName));
$context = $contextEvent->getContext();
}
if (!$this->manager->isActive($featureName, $context)) {
throw new NotFoundHttpException('Feature for this class is not active.');
}
}
Expand Down
3 changes: 3 additions & 0 deletions src/Resources/config/services.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ services:
class: Flagception\Bundle\FlagceptionBundle\Twig\ToggleExtension
arguments:
- '@flagception.manager.feature_manager'
- '@event_dispatcher'
tags:
- { name: twig.extension }
public: false
Expand Down Expand Up @@ -69,6 +70,7 @@ services:
arguments:
- '@annotations.reader'
- '@flagception.manager.feature_manager'
- '@event_dispatcher'
tags:
- { name: kernel.event_subscriber }
public: true
Expand All @@ -78,6 +80,7 @@ services:
class: Flagception\Bundle\FlagceptionBundle\Listener\RoutingMetadataSubscriber
arguments:
- '@flagception.manager.feature_manager'
- '@event_dispatcher'
tags:
- { name: kernel.event_subscriber }
public: true
18 changes: 17 additions & 1 deletion src/Twig/ToggleExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

namespace Flagception\Bundle\FlagceptionBundle\Twig;

use Flagception\Bundle\FlagceptionBundle\Event\ContextResolveEvent;
use Flagception\Manager\FeatureManagerInterface;
use Flagception\Model\Context;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;
use Twig\TwigTest;
Expand All @@ -23,14 +25,23 @@ class ToggleExtension extends AbstractExtension
*/
private $manager;

/**
* The event dispatcher
*
* @var ?EventDispatcherInterface
*/
private $eventDispatcher;

/**
* ToggleExtension constructor.
*
* @param FeatureManagerInterface $manager
* @param EventDispatcherInterface $eventDispatcher
*/
public function __construct(FeatureManagerInterface $manager)
public function __construct(FeatureManagerInterface $manager, EventDispatcherInterface $eventDispatcher = null)
{
$this->manager = $manager;
$this->eventDispatcher = $eventDispatcher;
}

/**
Expand All @@ -48,6 +59,11 @@ public function isActive($name, array $contextValues = [])
$context->add($contextKey, $contextValue);
}

if (null !== $this->eventDispatcher) {
$contextEvent = $this->eventDispatcher->dispatch(new ContextResolveEvent($name, $context));
$context = $contextEvent->getContext();
}

return $this->manager->isActive($name, $context);
}

Expand Down
34 changes: 34 additions & 0 deletions tests/Listener/AnnotationSubscriberTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@
namespace Flagception\Tests\FlagceptionBundle\Listener;

use Doctrine\Common\Annotations\AnnotationReader;
use Flagception\Bundle\FlagceptionBundle\Event\ContextResolveEvent;
use Flagception\Bundle\FlagceptionBundle\Listener\AnnotationSubscriber;
use Flagception\Manager\FeatureManagerInterface;
use Flagception\Model\Context;
use Flagception\Tests\FlagceptionBundle\Fixtures\Helper\AnnotationTestClass;
use PHPUnit\Framework\TestCase;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Event\ControllerEvent;
Expand Down Expand Up @@ -153,6 +156,37 @@ public function testOnMethodIsInactive()
$subscriber->onKernelController($event);
}

/**
* Test event is dispatched when event dispatcher exists
*
* @return void
*/
public function testEventIsDispatchedWhenEventDispatcherExists()
{
$context = new Context();
$manager = $this->createMock(FeatureManagerInterface::class);
$manager->method('isActive')->with('feature_abc', $context)->willReturn(true);

$listener = function (ContextResolveEvent $event) use ($context) {
$this->assertSame('feature_abc', $event->getFeature());
$this->assertNotSame($event->getContext(), $context);
$event->setContext($context);
$this->assertSame($event->getContext(), $context);
return $event;
};

$eventDispatcher = new EventDispatcher();
$eventDispatcher->addListener(ContextResolveEvent::class, $listener);

$event = $this->createControllerEvent([
new AnnotationTestClass(),
'normalMethod'
]);

$subscriber = new AnnotationSubscriber(new AnnotationReader(), $manager, $eventDispatcher);
$subscriber->onKernelController($event);
}

/**
* Create ControllerEvent
*
Expand Down
37 changes: 37 additions & 0 deletions tests/Listener/RoutingMetadataSubscriberTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@

namespace Flagception\Tests\FlagceptionBundle\Listener;

use Flagception\Bundle\FlagceptionBundle\Event\ContextResolveEvent;
use Flagception\Bundle\FlagceptionBundle\Listener\RoutingMetadataSubscriber;
use Flagception\Manager\FeatureManagerInterface;
use Flagception\Model\Context;
use PHPUnit\Framework\TestCase;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
Expand Down Expand Up @@ -184,4 +187,38 @@ function () {
$subscriber = new RoutingMetadataSubscriber($manager);
$subscriber->onKernelController($event);
}

/**
* Test event is dispatched when event dispatcher exists
*
* @return void
*/
public function testEventIsDispatchedWhenEventDispatcherExists()
{
$context = new Context();

$request = new Request([], [], ['_feature' => 'feature_abc']);
$event = $this->createControllerEvent($request);

$manager = $this->createMock(FeatureManagerInterface::class);
$manager
->expects(static::once())
->method('isActive')
->with('feature_abc', $context)
->willReturn(true);

$listener = function (ContextResolveEvent $event) use ($context) {
$this->assertSame('feature_abc', $event->getFeature());
$this->assertNotSame($event->getContext(), $context);
$event->setContext($context);
$this->assertSame($event->getContext(), $context);
return $event;
};

$eventDispatcher = new EventDispatcher();
$eventDispatcher->addListener(ContextResolveEvent::class, $listener);

$subscriber = new RoutingMetadataSubscriber($manager, $eventDispatcher);
$subscriber->onKernelController($event);
}
}
Loading

0 comments on commit 72c7a42

Please sign in to comment.