-
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
285 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Pheature\Community\Symfony\DependencyInjection; | ||
|
||
use Pheature\Community\Symfony\Twig\PheatureFlagsExtension; | ||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; | ||
use Symfony\Component\DependencyInjection\ContainerBuilder; | ||
use Symfony\Component\DependencyInjection\Reference; | ||
|
||
final class TwigExtensionPass implements CompilerPassInterface | ||
{ | ||
/** @psalm-suppress ReservedWord */ | ||
public function process(ContainerBuilder $container): void | ||
{ | ||
if (!$container->hasDefinition('twig')) { | ||
return; | ||
} | ||
|
||
$container->register(PheatureFlagsExtension::class, PheatureFlagsExtension::class); | ||
|
||
$container->findDefinition('twig') | ||
->addMethodCall('addExtension', [new Reference(PheatureFlagsExtension::class)]) | ||
; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Pheature\Community\Symfony\Twig; | ||
|
||
use Twig\Extension\AbstractExtension; | ||
use Twig\TwigFunction; | ||
use Twig\TwigTest; | ||
|
||
final class PheatureFlagsExtension extends AbstractExtension | ||
{ | ||
/** | ||
* @return TwigTest[] | ||
*/ | ||
public function getTests(): array | ||
{ | ||
return [ | ||
new TwigTest('enabled', [PheatureFlagsRuntime::class, 'isEnabled']), | ||
new TwigTest('enabled_for', [PheatureFlagsRuntime::class, 'isEnabled'], ['one_mandatory_argument' => true]), | ||
]; | ||
} | ||
|
||
/** | ||
* @return TwigFunction[] | ||
*/ | ||
public function getFunctions(): array | ||
{ | ||
return [ | ||
new TwigFunction('is_feature_enabled', [PheatureFlagsRuntime::class, 'isFeatureEnabled']), | ||
]; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Pheature\Community\Symfony\Twig; | ||
|
||
use Pheature\Core\Toggle\Exception\FeatureNotFoundException; | ||
use Pheature\Core\Toggle\Read\ConsumerIdentity; | ||
use Pheature\Core\Toggle\Read\Toggle; | ||
use Pheature\Model\Toggle\Identity; | ||
use Twig\Extension\RuntimeExtensionInterface; | ||
|
||
final class PheatureFlagsRuntime implements RuntimeExtensionInterface | ||
{ | ||
private Toggle $toggle; | ||
|
||
public function __construct(Toggle $toggle) | ||
{ | ||
$this->toggle = $toggle; | ||
} | ||
|
||
/** | ||
* @throws FeatureNotFoundException | ||
*/ | ||
public function isEnabled(string $feature, ConsumerIdentity $consumerIdentity = null): bool | ||
{ | ||
return $this->toggle->isEnabled($feature, $consumerIdentity); | ||
} | ||
|
||
/** | ||
* @param array<string, mixed> $payload | ||
* | ||
* @throws FeatureNotFoundException | ||
*/ | ||
public function isFeatureEnabled(string $feature, ?string $identity = null, array $payload = []): bool | ||
{ | ||
$consumerIdentity = is_string($identity) ? new Identity($identity, $payload) : null; | ||
|
||
return $this->isEnabled($feature, $consumerIdentity); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Pheature\Test\Community\Symfony\DependencyInjection; | ||
|
||
use Pheature\Community\Symfony\Twig\PheatureFlagsExtension; | ||
use Pheature\Community\Symfony\Twig\PheatureFlagsRuntime; | ||
use Pheature\Core\Toggle\Read\ConsumerIdentity; | ||
use Pheature\Core\Toggle\Read\Feature; | ||
use Pheature\Core\Toggle\Read\FeatureFinder; | ||
use PHPUnit\Framework\TestCase; | ||
use Pheature\Core\Toggle\Read\Toggle; | ||
use Pheature\Core\Toggle\Read\ToggleStrategies; | ||
use Pheature\Core\Toggle\Read\ToggleStrategy; | ||
use Twig\Environment; | ||
use Twig\Loader\ArrayLoader; | ||
use Twig\RuntimeLoader\FactoryRuntimeLoader; | ||
|
||
final class PheatureFlagsExtensionTest extends TestCase | ||
{ | ||
public function testItShouldExposeTwoTests(): void | ||
{ | ||
$extension = new PheatureFlagsExtension(new Toggle($this->createStub(FeatureFinder::class))); | ||
self::assertCount(2, $extension->getTests()); | ||
} | ||
|
||
public function testItShouldExposeOneFunction(): void | ||
{ | ||
$extension = new PheatureFlagsExtension(new Toggle($this->createStub(FeatureFinder::class))); | ||
self::assertCount(1, $extension->getFunctions()); | ||
} | ||
|
||
/** | ||
* @dataProvider getTemplates | ||
*/ | ||
public function testRendering($template, $variables) | ||
{ | ||
$loader = new ArrayLoader(['index' => $template]); | ||
$twig = new Environment($loader, ['debug' => true, 'cache' => false]); | ||
$twig->addExtension(new PheatureFlagsExtension()); | ||
$toggle = $this->createAllFeaturesEnabledToggle(); | ||
$twig->addRuntimeLoader(new FactoryRuntimeLoader([PheatureFlagsRuntime::class => fn() => new PheatureFlagsRuntime($toggle)])); | ||
|
||
$this->assertSame('yes', $twig->load('index')->render($variables)); | ||
} | ||
|
||
public function getTemplates() | ||
{ | ||
return [ | ||
['{% if "foo" is enabled %}yes{% endif %}', []], | ||
['{% if "foo" is enabled_for(i) %}yes{% endif %}', ['i' => $this->createStub(ConsumerIdentity::class)]], | ||
['{% if is_feature_enabled("foo") %}yes{% endif %}', []], | ||
]; | ||
} | ||
|
||
private function createAllFeaturesEnabledToggle(): Toggle | ||
{ | ||
$strategyStub = $this->createStub(ToggleStrategy::class); | ||
$strategyStub | ||
->method('isSatisfiedBy') | ||
->willReturn(true) | ||
; | ||
|
||
$featureStub = $this->createStub(Feature::class); | ||
$featureStub | ||
->method('isEnabled') | ||
->willReturn(true) | ||
; | ||
$featureStub | ||
->method('strategies') | ||
->willReturn(new ToggleStrategies($strategyStub)) | ||
; | ||
|
||
$featureFinderStub = $this->createStub(FeatureFinder::class); | ||
$featureFinderStub | ||
->method('get') | ||
->willReturn($featureStub) | ||
; | ||
|
||
return new Toggle($featureFinderStub); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Pheature\Test\Community\Symfony\DependencyInjection; | ||
|
||
use Pheature\Community\Symfony\Twig\PheatureFlagsRuntime; | ||
use Pheature\Core\Toggle\Read\ConsumerIdentity; | ||
use Pheature\Core\Toggle\Read\Feature; | ||
use Pheature\Core\Toggle\Read\FeatureFinder; | ||
use PHPUnit\Framework\TestCase; | ||
use Pheature\Core\Toggle\Read\Toggle; | ||
use Pheature\Core\Toggle\Read\ToggleStrategies; | ||
use Pheature\Core\Toggle\Read\ToggleStrategy; | ||
|
||
final class PheatureFlagsRuntimeTest extends TestCase | ||
{ | ||
public function testIsEnabledMethodDelegatesToToggle(): void | ||
{ | ||
$featureMock = $this->createMock(Feature::class); | ||
$featureMock | ||
->expects($this->once()) | ||
->method('isEnabled') | ||
->willReturn(false) | ||
; | ||
|
||
$featureFinderMock = $this->createMock(FeatureFinder::class); | ||
$featureFinderMock | ||
->expects($this->once()) | ||
->method('get') | ||
->with($this->identicalTo('foobar')) | ||
->willReturn($featureMock) | ||
; | ||
|
||
$extension = new PheatureFlagsRuntime(new Toggle($featureFinderMock)); | ||
self::assertFalse($extension->isEnabled('foobar')); | ||
} | ||
|
||
public function testIsFeatureEnabledMethodDelegatesToToggle(): void | ||
{ | ||
$featureMock = $this->createMock(Feature::class); | ||
$featureMock | ||
->expects($this->once()) | ||
->method('isEnabled') | ||
->willReturn(false) | ||
; | ||
|
||
$featureFinderMock = $this->createMock(FeatureFinder::class); | ||
$featureFinderMock | ||
->expects($this->once()) | ||
->method('get') | ||
->with($this->identicalTo('foobar')) | ||
->willReturn($featureMock) | ||
; | ||
|
||
$extension = new PheatureFlagsRuntime(new Toggle($featureFinderMock)); | ||
self::assertFalse($extension->isFeatureEnabled('foobar')); | ||
} | ||
|
||
public function testIsFeatureEnabledMethodPassesAConsumerIdentityToStrategies(): void | ||
{ | ||
$strategyMock = $this->createMock(ToggleStrategy::class); | ||
$strategyMock | ||
->expects($this->once()) | ||
->method('isSatisfiedBy') | ||
->with($this->callback(fn(ConsumerIdentity $identity) => $identity->id() === 'foo' && $identity->payload() === ['foo' => 'bar', 'baz' => 'qux'])) | ||
->willReturn(true) | ||
; | ||
|
||
$featureMock = $this->createMock(Feature::class); | ||
$featureMock | ||
->expects($this->once()) | ||
->method('isEnabled') | ||
->willReturn(true) | ||
; | ||
$featureMock | ||
->expects($this->once()) | ||
->method('strategies') | ||
->willReturn(new ToggleStrategies($strategyMock)) | ||
; | ||
|
||
$featureFinderMock = $this->createMock(FeatureFinder::class); | ||
$featureFinderMock | ||
->expects($this->once()) | ||
->method('get') | ||
->with($this->identicalTo('foobar')) | ||
->willReturn($featureMock) | ||
; | ||
|
||
$extension = new PheatureFlagsRuntime(new Toggle($featureFinderMock)); | ||
self::assertTrue($extension->isFeatureEnabled('foobar', 'foo', ['foo' => 'bar', 'baz' => 'qux'])); | ||
} | ||
} |