Skip to content

Commit

Permalink
Merge pull request #29 from Stogon/develop
Browse files Browse the repository at this point in the history
v0.8.0
  • Loading branch information
jsunier authored Dec 13, 2023
2 parents 31249ac + 7e9215a commit 76bf5e3
Show file tree
Hide file tree
Showing 15 changed files with 63 additions and 151 deletions.
1 change: 1 addition & 0 deletions .github/workflows/php.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ jobs:
- '8.0'
- '8.1'
- '8.2'
- '8.3'
dependencies:
- "lowest"
- "highest"
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
[![License](https://img.shields.io/github/license/Stogon/unleash-bundle.svg?style=for-the-badge)](https://packagist.org/packages/stogon/unleash-bundle)
[![codecov](https://img.shields.io/codecov/c/github/Stogon/unleash-bundle/master?token=NI6FM0TCMS&style=for-the-badge)](https://codecov.io/gh/Stogon/unleash-bundle)

An [Unleash](https://docs.getunleash.io/) bundle for Symfony 4.4, 5.4+ and 6+ applications.
An [Unleash](https://docs.getunleash.io/) bundle for Symfony 5.4+ and 6.4+ applications.

This provide an easy way to implement **feature flags** using [Gitlab Feature Flags Feature](https://docs.gitlab.com/ee/operations/feature_flags.html).

Expand Down
19 changes: 10 additions & 9 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,21 @@
"php": "^7.4|^8.0",
"lastguest/murmurhash": "^2.1",
"symfony/cache-contracts": "^2.4|^3.0",
"symfony/config": "^4.4|^5.4|^6.0",
"symfony/dependency-injection": "^4.4|^5.4|^6.0",
"symfony/event-dispatcher-contracts": "^1.1|^2.4|^3.0",
"symfony/config": "^5.4|^6.4",
"symfony/console": "^5.4|^6.4",
"symfony/dependency-injection": "^5.4|^6.4",
"symfony/event-dispatcher-contracts": "^2.4|^3.0",
"symfony/http-client-contracts": "^2.4|^3.0",
"symfony/http-kernel": "^4.4|^5.4.20|^6.0",
"symfony/security-core": "^4.4|^5.4|^6.0",
"symfony/http-kernel": "^5.4.20|^6.4",
"symfony/security-core": "^5.4|^6.4",
"twig/twig": "^2.12|^3.0"
},
"require-dev": {
"symfony/var-dumper": "^4.4|^5.4|^6.0",
"phpunit/phpunit": "^9.5",
"phpstan/phpstan": "^1.0.0",
"symfony/var-dumper": "^5.4|^6.4",
"phpunit/phpunit": "^9.6",
"phpstan/phpstan": "^1.10.0",
"psr/log": "^1|^2|^3",
"friendsofphp/php-cs-fixer": "^3.0"
"friendsofphp/php-cs-fixer": "^3.41"
},
"scripts": {
"cs-fixer": "php-cs-fixer fix --config=.php-cs-fixer.dist.php --diff --dry-run",
Expand Down
12 changes: 6 additions & 6 deletions config/services.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
->arg('$instanceId', '%unleash.instance_id%')
->arg('$environment', '%unleash.environment%')
->autowire(true)
->call('setLogger', [\function_exists('Symfony\Component\DependencyInjection\Loader\Configurator\service') ? service('logger')->ignoreOnInvalid() : ref('logger')->ignoreOnInvalid()])
->call('setLogger', [service('logger')->ignoreOnInvalid()])
->tag('monolog.logger', ['channel' => 'unleash'])
;

Expand All @@ -41,7 +41,7 @@
$services->set(GradualRolloutRandomStrategy::class)->tag('unleash.strategy', ['activation_name' => 'gradualRolloutRandom']);

$services->set(FeatureRepository::class)
->arg('$httpClient', \function_exists('Symfony\Component\DependencyInjection\Loader\Configurator\service') ? service(UnleashHttpClient::class) : ref(UnleashHttpClient::class))
->arg('$httpClient', service(UnleashHttpClient::class))
->arg('$cache', '%unleash.cache.service%')
->arg('$ttl', '%unleash.cache.ttl%')
->autowire(true)
Expand All @@ -57,22 +57,22 @@
$services->alias(UnleashInterface::class, Unleash::class);

$services->set(UnleashExtension::class)
->arg('$unleash', \function_exists('Symfony\Component\DependencyInjection\Loader\Configurator\service') ? service(UnleashInterface::class) : ref(UnleashInterface::class))
->arg('$unleash', service(UnleashInterface::class))
->tag('twig.extension')
;

$services->set(FeatureCacheWarmer::class)
->arg('$featureRepository', \function_exists('Symfony\Component\DependencyInjection\Loader\Configurator\service') ? service(FeatureRepository::class) : ref(FeatureRepository::class))
->arg('$featureRepository', service(FeatureRepository::class))
->tag('kernel.cache_warmer', ['priority' => 0])
;

$services->set(FetchFeaturesCommand::class)
->arg('$featureRepository', \function_exists('Symfony\Component\DependencyInjection\Loader\Configurator\service') ? service(FeatureRepository::class) : ref(FeatureRepository::class))
->arg('$featureRepository', service(FeatureRepository::class))
->tag('console.command')
;

$services->set(ListFeaturesCommand::class)
->arg('$featureRepository', \function_exists('Symfony\Component\DependencyInjection\Loader\Configurator\service') ? service(FeatureRepository::class) : ref(FeatureRepository::class))
->arg('$featureRepository', service(FeatureRepository::class))
->tag('console.command')
;
};
5 changes: 1 addition & 4 deletions phpstan.neon
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
parameters:
level: 3
level: 4
paths:
- src
- tests
reportUnmatchedIgnoredErrors: false
ignoreErrors:
- '#Call to an undefined method Symfony\\Component\\Security\\Core\\User\\UserInterface::getUsername\(\)\.#'
- '#Call to an undefined method Symfony\\Component\\HttpFoundation\\RequestStack::getMasterRequest\(\)\.#'
excludePaths:
- src/Cache/FeatureCacheWarmer.php
53 changes: 14 additions & 39 deletions src/Cache/FeatureCacheWarmer.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,50 +4,25 @@

use Stogon\UnleashBundle\Repository\FeatureRepository;
use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface;
use Symfony\Component\HttpKernel\Kernel;

if (Kernel::VERSION_ID >= 50000) {
class FeatureCacheWarmer implements CacheWarmerInterface
{
private FeatureRepository $featureRepository;

public function __construct(FeatureRepository $featureRepository)
{
$this->featureRepository = $featureRepository;
}

public function warmUp(string $cacheDir): array
{
$this->featureRepository->getFeatures();
class FeatureCacheWarmer implements CacheWarmerInterface
{
private FeatureRepository $featureRepository;

return [];
}

public function isOptional(): bool
{
return true;
}
}
} else {
class FeatureCacheWarmer implements CacheWarmerInterface
public function __construct(FeatureRepository $featureRepository)
{
private FeatureRepository $featureRepository;

public function __construct(FeatureRepository $featureRepository)
{
$this->featureRepository = $featureRepository;
}
$this->featureRepository = $featureRepository;
}

public function warmUp($cacheDir)
{
$this->featureRepository->getFeatures();
public function warmUp(string $cacheDir, string $buildDir = null): array
{
$this->featureRepository->getFeatures();

return [];
}
return [];
}

public function isOptional(): bool
{
return true;
}
public function isOptional(): bool
{
return true;
}
}
11 changes: 3 additions & 8 deletions src/Command/FetchFeaturesCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
namespace Stogon\UnleashBundle\Command;

use Stogon\UnleashBundle\Repository\FeatureRepository;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;

// @phpstan-ignore-next-line
#[\Symfony\Component\Console\Attribute\AsCommand('unleash:features:fetch', 'Fetch Unleash features from remote and store them in the cache for later usage.')]
#[AsCommand('unleash:features:fetch', 'Fetch Unleash features from remote and store them in the cache for later usage.')]
class FetchFeaturesCommand extends Command
{
private FeatureRepository $featureRepository;
Expand Down Expand Up @@ -38,11 +38,6 @@ protected function execute(InputInterface $input, OutputInterface $output): int

$io->success('Features fetched from remote and stored in the cache.');

if (\defined(Command::class.'::SUCCESS')) {
// @phpstan-ignore-next-line
return Command::SUCCESS;
}

return 0;
return Command::SUCCESS;
}
}
18 changes: 4 additions & 14 deletions src/Command/ListFeaturesCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@

use Stogon\UnleashBundle\FeatureInterface;
use Stogon\UnleashBundle\Repository\FeatureRepository;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;

// @phpstan-ignore-next-line
#[\Symfony\Component\Console\Attribute\AsCommand('unleash:features:list', 'List available Unleash features from remote.')]
#[AsCommand('unleash:features:list', 'List available Unleash features from remote.')]
class ListFeaturesCommand extends Command
{
private FeatureRepository $featureRepository;
Expand Down Expand Up @@ -38,12 +38,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
if (empty($features)) {
$io->warning('There is no feature available.');

if (\defined(Command::class.'::SUCCESS')) {
// @phpstan-ignore-next-line
return Command::SUCCESS;
}

return 0;
return Command::SUCCESS;
}

$io->table([
Expand All @@ -60,11 +55,6 @@ protected function execute(InputInterface $input, OutputInterface $output): int
];
}, $features));

if (\defined(Command::class.'::SUCCESS')) {
// @phpstan-ignore-next-line
return Command::SUCCESS;
}

return 0;
return Command::SUCCESS;
}
}
6 changes: 3 additions & 3 deletions src/Repository/FeatureRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@ public function __construct(UnleashHttpClient $httpClient, CacheInterface $cache
*/
public function getFeatures(): array
{
return $this->cache->get('unleash.strategies', function (ItemInterface $item) {
return $this->cache->get('unleash.strategies', function (ItemInterface $item): array {
$features = $this->client->fetchFeatures();

$item->expiresAfter($this->ttl);

return array_map(function (array $feature) {
return array_map(function (array $feature): Feature {
return new Feature(
$feature['name'],
$feature['description'],
Expand All @@ -45,7 +45,7 @@ public function getFeature(string $name): ?Feature
{
$features = $this->getFeatures();

$filtered = array_filter($features, fn (Feature $f) => $f->getName() === $name);
$filtered = array_filter($features, fn (Feature $f): bool => $f->getName() === $name);

if (!empty($filtered)) {
return $filtered[array_key_first($filtered)];
Expand Down
7 changes: 1 addition & 6 deletions src/Strategy/FlexibleRolloutStrategy.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,9 @@ public function isEnabled(array $parameters = [], array $context = [], ...$args)
protected function getUserId(array $context): ?string
{
if (array_key_exists('user', $context) && $context['user'] !== null) {
/** @var string|\Stringable|UserInterface */
/** @var UserInterface */
$currentUser = $context['user'];

// This means user is anonymous
if (is_string($currentUser)) {
return null;
}

if (method_exists($currentUser, 'getId')) {
return $currentUser->getId();
}
Expand Down
4 changes: 0 additions & 4 deletions src/Strategy/GradualRolloutRandomStrategy.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,6 @@ public function isEnabled(array $parameters = [], array $context = [], ...$args)
$groupId = trim($parameters['groupId'] ?? '');
$randomId = sprintf('%s', mt_rand(1, 100));

if (!$randomId) {
return false;
}

$randomIdValue = ValueNormalizer::build($randomId, $groupId);

return $percentage > 0 && $randomIdValue <= $percentage;
Expand Down
7 changes: 1 addition & 6 deletions src/Strategy/GradualRolloutUserIdStrategy.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,9 @@ public function isEnabled(array $parameters = [], array $context = [], ...$args)
protected function getUserId(array $context): ?string
{
if (array_key_exists('user', $context) && $context['user'] !== null) {
/** @var string|\Stringable|UserInterface */
/** @var UserInterface */
$currentUser = $context['user'];

// This means user is anonymous
if (is_string($currentUser)) {
return null;
}

if (method_exists($currentUser, 'getId')) {
return $currentUser->getId();
}
Expand Down
18 changes: 7 additions & 11 deletions src/Strategy/UserWithIdStrategy.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,26 +14,22 @@ public function isEnabled(array $parameters = [], array $context = [], ...$args)
$ids = array_map('trim', explode(',', $userIds));

if ($context['user']) {
/** @var string|\Stringable|UserInterface */
/** @var UserInterface */
$currentUser = $context['user'];

if (is_string($currentUser)) {
return in_array($currentUser, $ids, false);
}

if (method_exists($currentUser, 'getId') && in_array($currentUser->getId(), $ids, false)) {
return true;
}

if ($currentUser instanceof UserInterface) {
if (method_exists($currentUser, 'getUserIdentifier')) {
return in_array($currentUser->getUserIdentifier(), $ids, false);
}
if (!$currentUser instanceof UserInterface) {
return false;
}

return in_array($currentUser->getUsername(), $ids, false);
if (method_exists($currentUser, 'getUserIdentifier')) {
return in_array($currentUser->getUserIdentifier(), $ids, false);
}

return in_array((string) $currentUser, $ids, false);
return in_array($currentUser->getUsername(), $ids, false);
}

/** @var Request */
Expand Down
24 changes: 5 additions & 19 deletions src/Unleash.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,15 +70,8 @@ public function isFeatureEnabled(string $name, bool $defaultValue = false): bool
$token = $this->tokenStorage->getToken();
$user = null;

if ($token === null || !method_exists($token, 'isAuthenticated')) {
$authenticated = $token !== null;
} else {
$authenticated = $token !== null && $token->isAuthenticated();
}

if ($authenticated) {
if ($token !== null) {
$user = $token->getUser();

$this->logger->debug('Using authenticated user from token', [
'name' => $name,
'feature' => $feature,
Expand All @@ -88,17 +81,10 @@ public function isFeatureEnabled(string $name, bool $defaultValue = false): bool
]);
}

if (method_exists($this->requestStack, 'getMainRequest')) {
$event = new UnleashContextEvent([
'request' => $this->requestStack->getMainRequest(),
'user' => $user,
]);
} else {
$event = new UnleashContextEvent([
'request' => $this->requestStack->getMasterRequest(),
'user' => $user,
]);
}
$event = new UnleashContextEvent([
'request' => $this->requestStack->getMainRequest(),
'user' => $user,
]);

$this->eventDispatcher->dispatch($event);

Expand Down
Loading

0 comments on commit 76bf5e3

Please sign in to comment.