From 6f530b55032e04e557f3c8879929cdc43253637d Mon Sep 17 00:00:00 2001 From: Wessel Date: Thu, 15 Aug 2024 22:32:16 +0200 Subject: [PATCH 1/7] Allow more recent versions in composer.json for packages --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 1a1a579..034df05 100644 --- a/composer.json +++ b/composer.json @@ -7,7 +7,7 @@ "illuminate/support": "^6|^7|^8|^9|^10.0|^11.0", "illuminate/http": "^6|^7|^8|^9|^10.0|^11.0", "unleash/client": "^1", - "symfony/cache": "^5.3|^6.1", + "symfony/cache": "^5.3|^6.1|^7.2", "psr/cache": "^1.0|^2.0|^3.0", "psr/simple-cache": "^1.0|^2.0|^3.0" }, From 648f0cac37256a546e140557a0da0b505d7b1fd5 Mon Sep 17 00:00:00 2001 From: Wessel Date: Thu, 15 Aug 2024 22:46:15 +0200 Subject: [PATCH 2/7] Up Unleash version to v2, deprecate old versions --- composer.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/composer.json b/composer.json index 034df05..fa08b02 100644 --- a/composer.json +++ b/composer.json @@ -4,12 +4,12 @@ "type": "library", "require": { "guzzlehttp/guzzle": "^7", - "illuminate/support": "^6|^7|^8|^9|^10.0|^11.0", - "illuminate/http": "^6|^7|^8|^9|^10.0|^11.0", - "unleash/client": "^1", - "symfony/cache": "^5.3|^6.1|^7.2", - "psr/cache": "^1.0|^2.0|^3.0", - "psr/simple-cache": "^1.0|^2.0|^3.0" + "illuminate/support": "^10.0|^11.0", + "illuminate/http": "^10.0|^11.0", + "unleash/client": "^2", + "symfony/cache": "^6.1|^7.2", + "psr/cache": "^2.0|^3.0", + "psr/simple-cache": "^2.0|^3.0" }, "autoload": { "psr-4": { From 8ff0bc36354c7fd2cc8782a25c3569811b438298 Mon Sep 17 00:00:00 2001 From: Wessel Verheij Date: Sun, 25 Aug 2024 14:53:13 +0200 Subject: [PATCH 3/7] Make packags in-line with Laravel 10 minimum package versions --- composer.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/composer.json b/composer.json index fa08b02..77120aa 100644 --- a/composer.json +++ b/composer.json @@ -6,10 +6,10 @@ "guzzlehttp/guzzle": "^7", "illuminate/support": "^10.0|^11.0", "illuminate/http": "^10.0|^11.0", - "unleash/client": "^2", - "symfony/cache": "^6.1|^7.2", - "psr/cache": "^2.0|^3.0", - "psr/simple-cache": "^2.0|^3.0" + "unleash/client": "^v2", + "symfony/cache": "^6.2|^7.2", + "psr/cache": "^1.0|^2.0|^3.0", + "psr/simple-cache": "^1.0|^2.0|^3.0" }, "autoload": { "psr-4": { From e35faf377800c1aeac2071adb810d600cd9dee3e Mon Sep 17 00:00:00 2001 From: Wessel Verheij Date: Sun, 25 Aug 2024 14:53:43 +0200 Subject: [PATCH 4/7] Allow publishing of vendor config when config not set yet --- src/Providers/ServiceProvider.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Providers/ServiceProvider.php b/src/Providers/ServiceProvider.php index 3254dfd..bcbc7ec 100644 --- a/src/Providers/ServiceProvider.php +++ b/src/Providers/ServiceProvider.php @@ -61,14 +61,14 @@ public function register(): void */ public function boot(): void { - if (! config('unleash.enabled')) { - return; - } - $this->publishes([ $this->getConfigPath() => config_path('unleash.php'), ]); + if (! config('unleash.enabled')) { + return; + } + Blade::if('featureEnabled', function (string $feature) { return app(Unleash::class)->isEnabled($feature); }); From 826a93079919782699bf59ae2c4193ca74555c7e Mon Sep 17 00:00:00 2001 From: Wessel Verheij Date: Sun, 25 Aug 2024 15:05:12 +0200 Subject: [PATCH 5/7] Actually take enabled config value into account when checking for feature(s) --- config/unleash.php | 5 ++--- src/Providers/ServiceProvider.php | 4 ---- src/Unleash.php | 29 +++++++++++++++++++++++------ 3 files changed, 25 insertions(+), 13 deletions(-) diff --git a/config/unleash.php b/config/unleash.php index 709d17d..d790485 100644 --- a/config/unleash.php +++ b/config/unleash.php @@ -1,7 +1,6 @@ env('UNLEASH_URL'), + 'url' => env('UNLEASH_URL', 'https://localhost'), /* |-------------------------------------------------------------------------- @@ -119,7 +118,7 @@ | */ - 'api_key' => env('UNLEASH_API_KEY', null), + 'api_key' => env('UNLEASH_API_KEY'), /* |-------------------------------------------------------------------------- diff --git a/src/Providers/ServiceProvider.php b/src/Providers/ServiceProvider.php index bcbc7ec..88ef960 100644 --- a/src/Providers/ServiceProvider.php +++ b/src/Providers/ServiceProvider.php @@ -65,10 +65,6 @@ public function boot(): void $this->getConfigPath() => config_path('unleash.php'), ]); - if (! config('unleash.enabled')) { - return; - } - Blade::if('featureEnabled', function (string $feature) { return app(Unleash::class)->isEnabled($feature); }); diff --git a/src/Unleash.php b/src/Unleash.php index c5c23c5..34d9486 100644 --- a/src/Unleash.php +++ b/src/Unleash.php @@ -2,6 +2,10 @@ namespace JWebb\Unleash; +use Exception; +use Illuminate\Support\Facades\Log; +use ReflectionException; +use ReflectionProperty; use Unleash\Client\Configuration\Context; use Unleash\Client\DTO\Feature; use Unleash\Client\DTO\Variant; @@ -40,9 +44,15 @@ public function setClient($client): void */ public function isEnabled(string $featureName, ?Context $context = null, bool $default = false): bool { + if (! config('unleash.enabled')) { + Log::debug('Unleash is disabled, returning disabled state for feature'); + + return false; + } + try { return $this->client->isEnabled($featureName, $context, $default); - } catch (\Exception $e) { + } catch (Exception $e) { return $default; } } @@ -52,10 +62,15 @@ public function isEnabled(string $featureName, ?Context $context = null, bool $d * @param bool $onlyEnabled * @param Context|null $context * @return array - * @throws \ReflectionException */ public function getFeatures(bool $onlyEnabled = false, ?Context $context = null): array { + if (! config('unleash.enabled')) { + Log::debug('Unleash is disabled, returning empty list of features'); + + return []; + } + try { $features = $this->getRepository()->getFeatures(); @@ -72,7 +87,9 @@ public function getFeatures(bool $onlyEnabled = false, ?Context $context = null) }) : $mappedFeatures; return $toggles; - } catch (\Exception $e) { + } catch (Exception $e) { + Log::error('Error getting features from Unleash', ['error' => $e->getMessage()]); + return []; } } @@ -81,7 +98,7 @@ public function getFeatures(bool $onlyEnabled = false, ?Context $context = null) * @param string $featureName * @param Context|null $context * @param Variant|null $fallbackVariant - * @return Variant + * @return ?Variant */ public function getVariant(string $featureName, ?Context $context = null, ?Variant $fallbackVariant = null): Variant { @@ -97,11 +114,11 @@ public function register(): bool } /** - * @throws \ReflectionException + * @throws ReflectionException */ protected function getRepository(): UnleashRepository { - $reflectionProperty = new \ReflectionProperty(get_class($this->client), 'repository'); + $reflectionProperty = new ReflectionProperty(get_class($this->client), 'repository'); $reflectionProperty->setAccessible(true); return $reflectionProperty->getValue($this->client); From 2aa02aabe3501d884c4181676ebb3c0ee68a4eb2 Mon Sep 17 00:00:00 2001 From: Wessel Verheij Date: Sun, 25 Aug 2024 15:06:45 +0200 Subject: [PATCH 6/7] Cleanup unused (and deprecated) imports --- src/Providers/UnleashStrategiesProvider.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Providers/UnleashStrategiesProvider.php b/src/Providers/UnleashStrategiesProvider.php index 470da6f..0e461f2 100644 --- a/src/Providers/UnleashStrategiesProvider.php +++ b/src/Providers/UnleashStrategiesProvider.php @@ -7,13 +7,9 @@ use Unleash\Client\Strategy\AbstractStrategyHandler; use Unleash\Client\Strategy\ApplicationHostnameStrategyHandler; use Unleash\Client\Strategy\DefaultStrategyHandler; -use Unleash\Client\Strategy\GradualRolloutRandomStrategyHandler; -use Unleash\Client\Strategy\GradualRolloutSessionIdStrategyHandler; use Unleash\Client\Strategy\GradualRolloutStrategyHandler; -use Unleash\Client\Strategy\GradualRolloutUserIdStrategyHandler; use Unleash\Client\Strategy\IpAddressStrategyHandler; use Unleash\Client\Strategy\UserIdStrategyHandler; -use Unleash\Strategy\StrategyHandler; class UnleashStrategiesProvider implements UnleashStrategiesProviderInterface { From 26ef720641fb56df95359ebaee5ccbaf72969694 Mon Sep 17 00:00:00 2001 From: Wessel Verheij Date: Sun, 25 Aug 2024 16:12:30 +0200 Subject: [PATCH 7/7] General cleanup --- composer.json | 9 ++- src/Cache/CacheBridge.php | 6 +- src/Facades/Unleash.php | 10 ++-- src/Middleware/CheckFeature.php | 13 +---- src/Providers/ServiceProvider.php | 86 ---------------------------- src/ServiceProvider.php | 93 +++++++++++++++++++++++++++++++ src/Unleash.php | 9 ++- 7 files changed, 118 insertions(+), 108 deletions(-) delete mode 100644 src/Providers/ServiceProvider.php create mode 100644 src/ServiceProvider.php diff --git a/composer.json b/composer.json index 77120aa..449cf5d 100644 --- a/composer.json +++ b/composer.json @@ -23,11 +23,16 @@ "email": "jonwebb247@gmail.com" } ], + "minimum-stability": "dev", + "prefer-stable": true, "extra": { "laravel": { "providers": [ - "JWebb\\Unleash\\Providers\\ServiceProvider" - ] + "JWebb\\Unleash\\ServiceProvider" + ], + "aliases": { + "Unleash": "JWebb\\Unleash\\Facades\\Unleash" + } } } } diff --git a/src/Cache/CacheBridge.php b/src/Cache/CacheBridge.php index 39cb7f6..91acdcf 100644 --- a/src/Cache/CacheBridge.php +++ b/src/Cache/CacheBridge.php @@ -79,8 +79,12 @@ public function setMultiple($values, $ttl = null): bool public function deleteMultiple($keys): bool { foreach ($keys as $key) { - $this->delete($key); + if ($this->delete($key) === false) { + return false; + } } + + return true; } /** diff --git a/src/Facades/Unleash.php b/src/Facades/Unleash.php index 60da500..2c10dc5 100644 --- a/src/Facades/Unleash.php +++ b/src/Facades/Unleash.php @@ -3,12 +3,14 @@ namespace JWebb\Unleash\Facades; use Illuminate\Support\Facades\Facade; +use Unleash\Client\Configuration\Context; +use Unleash\Client\DTO\Variant; /** - * @method static array setClient(Unleash\Client\Unleash $client) - * @method static bool isEnabled(string $featureName, ?\Unleash\Client\Configuration\Context $context = null, bool $default = false) - * @method static array getFeatures(bool $onlyEnabled = false, ?\Unleash\Client\Configuration\Context $context = null) - * @method static \Unleash\Client\DTO\Variant getVariant(string $featureName, ?\Unleash\Client\Configuration\Context $context = null, ?\Unleash\Client\DTO\Variant $fallbackVariant = null) + * @method static array setClient(\Unleash\Client\Unleash $client) + * @method static bool isEnabled(string $featureName, ?Context $context = null, bool $default = false) + * @method static array getFeatures(bool $onlyEnabled = false, ?Context $context = null) + * @method static Variant getVariant(string $featureName, ?Context $context = null, ?Variant $fallbackVariant = null) * @method static void register() * @method static void getRepository() */ diff --git a/src/Middleware/CheckFeature.php b/src/Middleware/CheckFeature.php index c5dd5cb..3b07f38 100644 --- a/src/Middleware/CheckFeature.php +++ b/src/Middleware/CheckFeature.php @@ -3,21 +3,14 @@ namespace JWebb\Unleash\Middleware; use Closure; +use Illuminate\Http\Request; use JWebb\Unleash\Unleash; class CheckFeature { - /** - * Handle an incoming request. - * - * @param \Illuminate\Http\Request $request - * @param \Closure $next - * @param $featureName - * @return mixed - */ - public function handle($request, Closure $next, $featureName) + public function handle(Request $request, Closure $next, $featureName): mixed { - if (!app(Unleash::class)->isEnabled($featureName)) { + if (! app(Unleash::class)->isEnabled($featureName)) { abort(404); } diff --git a/src/Providers/ServiceProvider.php b/src/Providers/ServiceProvider.php deleted file mode 100644 index 88ef960..0000000 --- a/src/Providers/ServiceProvider.php +++ /dev/null @@ -1,86 +0,0 @@ -mergeConfigFrom($this->getConfigPath(), 'unleash'); - - $this->app->singleton(Unleash::class, function ($app) { - $strategyProvider = config('unleash.strategy_provider'); - $contextProvider = config('unleash.context_provider'); - - $builder = UnleashBuilder::create() - ->withInstanceId(config('unleash.instance_id')) - ->withAppUrl(config('unleash.url')) - ->withAppName(config('unleash.environment')) // Same as `withGitlabEnvironment(...)` - ->withContextProvider(new $contextProvider()) - ->withStrategies(...(new $strategyProvider())->getStrategies()) - ->withAutomaticRegistrationEnabled(!! config('unleash.automatic_registration')) - ->withMetricsEnabled(!! config('unleash.metrics')); - - if (!! config('unleash.http_client_override.enabled')) { - $builder = $builder->withHttpClient(new Client(config('unleash.http_client_override.config'))); - } - - if (!! config('unleash.cache.enabled')) { - /** @var UnleashCacheHandlerInterface $cacheHandler */ - $cacheHandler = config('unleash.cache.handler'); - - $builder = $builder->withCacheHandler( - (new $cacheHandler())->init(), - config('unleash.cache.ttl') - ); - } - if (!! config('unleash.api_key')) { - $builder = $builder->withHeader('Authorization', config('unleash.api_key')); - } - - return new Unleash($builder->build()); - }); - } - - /** - * Perform post-registration booting of services. - * - * @return void - */ - public function boot(): void - { - $this->publishes([ - $this->getConfigPath() => config_path('unleash.php'), - ]); - - Blade::if('featureEnabled', function (string $feature) { - return app(Unleash::class)->isEnabled($feature); - }); - - Blade::if('featureDisabled', function (string $feature) { - return !app(Unleash::class)->isEnabled($feature); - }); - } - - /** - * Get the path to the config. - * - * @return string - */ - private function getConfigPath(): string - { - return __DIR__ . '/../../config/unleash.php'; - } -} diff --git a/src/ServiceProvider.php b/src/ServiceProvider.php new file mode 100644 index 0000000..7479323 --- /dev/null +++ b/src/ServiceProvider.php @@ -0,0 +1,93 @@ +bootConfig(); + $this->bootBladeDirectives(); + } + + public function register(): void + { + $this->app->singleton(Unleash::class, function (Container $app) { + $strategyProvider = $app->config->get('unleash.strategy_provider'); + $contextProvider = $app->config->get('unleash.context_provider'); + + $builder = UnleashBuilder::create() + ->withInstanceId($app->config->get('unleash.instance_id')) + ->withAppUrl($app->config->get('unleash.url')) + ->withAppName($app->config->get('unleash.environment')) // Same as `withGitlabEnvironment(...)` + ->withContextProvider(new $contextProvider()) + ->withStrategies(...(new $strategyProvider())->getStrategies()) + ->withAutomaticRegistrationEnabled(!! $app->config->get('unleash.automatic_registration')) + ->withMetricsEnabled(!! $app->config->get('unleash.metrics')); + + if (!! $app->config->get('unleash.http_client_override.enabled')) { + $builder = $builder->withHttpClient(new Client($app->config->get('unleash.http_client_override.config'))); + } + + if (!! $app->config->get('unleash.cache.enabled')) { + /** @var UnleashCacheHandlerInterface $cacheHandler */ + $cacheHandler = $app->config->get('unleash.cache.handler'); + + $builder = $builder->withCacheHandler( + (new $cacheHandler())->init(), + $app->config->get('unleash.cache.ttl') + ); + } + if (!! $app->config->get('unleash.api_key')) { + $builder = $builder->withHeader('Authorization', $app->config->get('unleash.api_key')); + } + + return new Unleash($builder->build()); + }); + } + + private function bootConfig(): void + { + $source = realpath($raw = __DIR__.'/../config/unleash.php') ?: $raw; + + if ($this->app instanceof LaravelApplication && $this->app->runningInConsole()) { + $this->publishes([$source => $this->app->configPath('unleash.php')]); + } elseif ($this->app instanceof LumenApplication) { + $this->app->configure('unleash'); + } + + $this->mergeConfigFrom($source, 'unleash'); + } + + /** + * @throws BindingResolutionException + */ + private function bootBladeDirectives(): void + { + $unleash = $this->app->make(Unleash::class); + + Blade::if('feature', function ($featureName, $context = null, $default = false) use ($unleash) { + return $unleash->isEnabled($featureName, $context, $default); + }); + + Blade::if('featureEnabled', function (string $feature) use ($unleash) { + return $this->app->make(Unleash::class)->isEnabled($feature); + }); + + Blade::if('featureDisabled', function (string $feature) use ($unleash) { + return $this->app->make(Unleash::class)->isEnabled($feature) === false; + }); + } +} diff --git a/src/Unleash.php b/src/Unleash.php index 34d9486..a6cac34 100644 --- a/src/Unleash.php +++ b/src/Unleash.php @@ -95,10 +95,10 @@ public function getFeatures(bool $onlyEnabled = false, ?Context $context = null) } /** - * @param string $featureName - * @param Context|null $context - * @param Variant|null $fallbackVariant - * @return ?Variant + * @param string $featureName + * @param Context|null $context + * @param Variant|null $fallbackVariant + * @return Variant */ public function getVariant(string $featureName, ?Context $context = null, ?Variant $fallbackVariant = null): Variant { @@ -119,7 +119,6 @@ public function register(): bool protected function getRepository(): UnleashRepository { $reflectionProperty = new ReflectionProperty(get_class($this->client), 'repository'); - $reflectionProperty->setAccessible(true); return $reflectionProperty->getValue($this->client); }