From 17ae59def4bbe1f9086f62573ae1d454a4b035fc Mon Sep 17 00:00:00 2001 From: Oleksandr Prypkhan Date: Thu, 6 Jul 2023 15:36:56 +0300 Subject: [PATCH 1/8] Make Check macroable and allow callables in if() and unless() --- src/Checks/Check.php | 19 ++++++++++++++----- tests/HealthTest.php | 17 +++++++++++++---- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/src/Checks/Check.php b/src/Checks/Check.php index 290e04ac..e47ddf15 100755 --- a/src/Checks/Check.php +++ b/src/Checks/Check.php @@ -6,11 +6,13 @@ use Illuminate\Console\Scheduling\ManagesFrequencies; use Illuminate\Support\Facades\Date; use Illuminate\Support\Str; +use Illuminate\Support\Traits\Macroable; use Spatie\Health\Enums\Status; abstract class Check { use ManagesFrequencies; + use Macroable; protected string $expression = '* * * * *'; @@ -18,7 +20,10 @@ abstract class Check protected ?string $label = null; - protected bool $shouldRun = true; + /** + * @var bool|callable(): bool + */ + protected mixed $shouldRun = true; public function __construct() { @@ -71,7 +76,9 @@ public function getName(): string public function shouldRun(): bool { - if (! $this->shouldRun) { + $shouldRun = is_callable($this->shouldRun) ? ($this->shouldRun)() : $this->shouldRun; + + if (! $shouldRun) { return false; } @@ -80,16 +87,18 @@ public function shouldRun(): bool return (new CronExpression($this->expression))->isDue($date->toDateTimeString()); } - public function if(bool $condition) + public function if(bool|callable $condition) { $this->shouldRun = $condition; return $this; } - public function unless(bool $condition) + public function unless(bool|callable $condition) { - $this->shouldRun = ! $condition; + $this->shouldRun = is_callable($condition) ? + fn () => !$condition() : + $condition; return $this; } diff --git a/tests/HealthTest.php b/tests/HealthTest.php index e14f63a5..ee9bdf32 100644 --- a/tests/HealthTest.php +++ b/tests/HealthTest.php @@ -4,6 +4,7 @@ use Spatie\Health\Checks\Check; use Spatie\Health\Checks\Checks\DatabaseCheck; use Spatie\Health\Checks\Checks\DebugModeCheck; +use Spatie\Health\Checks\Checks\EnvironmentCheck; use Spatie\Health\Checks\Checks\PingCheck; use Spatie\Health\Checks\Checks\UsedDiskSpaceCheck; use Spatie\Health\Checks\Result; @@ -28,6 +29,7 @@ Health::checks([ UsedDiskSpaceCheck::new(), DebugModeCheck::new()->if(false), + EnvironmentCheck::new()->if(fn () => false), ]); $checks = Health::registeredChecks()->filter(function (Check $check) { @@ -44,6 +46,7 @@ Health::checks([ UsedDiskSpaceCheck::new(), DebugModeCheck::new()->if(true), + EnvironmentCheck::new()->if(fn () => true), ]); $checks = Health::registeredChecks()->filter(function (Check $check) { @@ -51,15 +54,18 @@ }); expect($checks) - ->toHaveCount(2) + ->toHaveCount(3) ->and($checks[1]) - ->toBeInstanceOf(DebugModeCheck::class); + ->toBeInstanceOf(DebugModeCheck::class) + ->and($checks[2]) + ->toBeInstanceOf(EnvironmentCheck::class); }); it('can run checks conditionally using unless method', function () { Health::checks([ UsedDiskSpaceCheck::new(), DebugModeCheck::new()->unless(true), + EnvironmentCheck::new()->unless(fn () => true), ]); $checks = Health::registeredChecks()->filter(function (Check $check) { @@ -76,6 +82,7 @@ Health::checks([ UsedDiskSpaceCheck::new(), DebugModeCheck::new()->unless(false), + EnvironmentCheck::new()->unless(fn () => false), ]); $checks = Health::registeredChecks()->filter(function (Check $check) { @@ -83,9 +90,11 @@ }); expect($checks) - ->toHaveCount(2) + ->toHaveCount(3) ->and($checks[1]) - ->toBeInstanceOf(DebugModeCheck::class); + ->toBeInstanceOf(DebugModeCheck::class) + ->and($checks[2]) + ->toBeInstanceOf(EnvironmentCheck::class); }); it('will throw an exception when duplicate checks are registered', function () { From 4625073adabb28421bc9d35e7a41ba53f311db01 Mon Sep 17 00:00:00 2001 From: Oleksandr Prypkhan Date: Thu, 6 Jul 2023 15:50:25 +0300 Subject: [PATCH 2/8] Allow chaining ->if() and ->unless() calls on Check --- src/Checks/Check.php | 18 ++++++++++-------- tests/HealthTest.php | 12 ++++++++++-- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/Checks/Check.php b/src/Checks/Check.php index e47ddf15..8eb5d1fc 100755 --- a/src/Checks/Check.php +++ b/src/Checks/Check.php @@ -21,9 +21,9 @@ abstract class Check protected ?string $label = null; /** - * @var bool|callable(): bool + * @var array */ - protected mixed $shouldRun = true; + protected array $shouldRun = []; public function __construct() { @@ -76,10 +76,12 @@ public function getName(): string public function shouldRun(): bool { - $shouldRun = is_callable($this->shouldRun) ? ($this->shouldRun)() : $this->shouldRun; + foreach ($this->shouldRun as $shouldRun) { + $shouldRun = is_bool($shouldRun) ?: $shouldRun(); - if (! $shouldRun) { - return false; + if (!$shouldRun) { + return false; + } } $date = Date::now(); @@ -89,16 +91,16 @@ public function shouldRun(): bool public function if(bool|callable $condition) { - $this->shouldRun = $condition; + $this->shouldRun[] = $condition; return $this; } public function unless(bool|callable $condition) { - $this->shouldRun = is_callable($condition) ? + $this->shouldRun[] = is_callable($condition) ? fn () => !$condition() : - $condition; + ! $condition; return $this; } diff --git a/tests/HealthTest.php b/tests/HealthTest.php index ee9bdf32..6c1e6759 100644 --- a/tests/HealthTest.php +++ b/tests/HealthTest.php @@ -29,6 +29,7 @@ Health::checks([ UsedDiskSpaceCheck::new(), DebugModeCheck::new()->if(false), + DebugModeCheck::new()->if(true)->if(false), EnvironmentCheck::new()->if(fn () => false), ]); @@ -46,6 +47,7 @@ Health::checks([ UsedDiskSpaceCheck::new(), DebugModeCheck::new()->if(true), + DebugModeCheck::new()->if(true)->if(true), EnvironmentCheck::new()->if(fn () => true), ]); @@ -54,10 +56,12 @@ }); expect($checks) - ->toHaveCount(3) + ->toHaveCount(4) ->and($checks[1]) ->toBeInstanceOf(DebugModeCheck::class) ->and($checks[2]) + ->toBeInstanceOf(DebugModeCheck::class) + ->and($checks[3]) ->toBeInstanceOf(EnvironmentCheck::class); }); @@ -65,6 +69,7 @@ Health::checks([ UsedDiskSpaceCheck::new(), DebugModeCheck::new()->unless(true), + DebugModeCheck::new()->unless(false)->unless(true), EnvironmentCheck::new()->unless(fn () => true), ]); @@ -82,6 +87,7 @@ Health::checks([ UsedDiskSpaceCheck::new(), DebugModeCheck::new()->unless(false), + DebugModeCheck::new()->unless(false)->unless(false), EnvironmentCheck::new()->unless(fn () => false), ]); @@ -90,10 +96,12 @@ }); expect($checks) - ->toHaveCount(3) + ->toHaveCount(4) ->and($checks[1]) ->toBeInstanceOf(DebugModeCheck::class) ->and($checks[2]) + ->toBeInstanceOf(DebugModeCheck::class) + ->and($checks[3]) ->toBeInstanceOf(EnvironmentCheck::class); }); From ef4e39033480c65bc4ccf3675da760976c89fa6d Mon Sep 17 00:00:00 2001 From: Oleksandr Prypkhan Date: Thu, 6 Jul 2023 19:00:32 +0300 Subject: [PATCH 3/8] Fix unless(false) --- src/Checks/Check.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Checks/Check.php b/src/Checks/Check.php index 8eb5d1fc..789d0b66 100755 --- a/src/Checks/Check.php +++ b/src/Checks/Check.php @@ -77,7 +77,7 @@ public function getName(): string public function shouldRun(): bool { foreach ($this->shouldRun as $shouldRun) { - $shouldRun = is_bool($shouldRun) ?: $shouldRun(); + $shouldRun = is_callable($shouldRun) ? $shouldRun() : $shouldRun; if (!$shouldRun) { return false; From e5be542951b7b92c932fa720d7a41fccb65e4a31 Mon Sep 17 00:00:00 2001 From: Oleksandr Prypkhan Date: Thu, 6 Jul 2023 19:45:57 +0300 Subject: [PATCH 4/8] Add Conditionable too --- src/Checks/Check.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Checks/Check.php b/src/Checks/Check.php index 789d0b66..55e875d8 100755 --- a/src/Checks/Check.php +++ b/src/Checks/Check.php @@ -6,6 +6,7 @@ use Illuminate\Console\Scheduling\ManagesFrequencies; use Illuminate\Support\Facades\Date; use Illuminate\Support\Str; +use Illuminate\Support\Traits\Conditionable; use Illuminate\Support\Traits\Macroable; use Spatie\Health\Enums\Status; @@ -13,6 +14,7 @@ abstract class Check { use ManagesFrequencies; use Macroable; + use Conditionable; protected string $expression = '* * * * *'; From e3e5c8af3b9dabadbebd45e646cb4fa2129a6f30 Mon Sep 17 00:00:00 2001 From: Oleksandr Prypkhan Date: Thu, 6 Jul 2023 19:48:38 +0300 Subject: [PATCH 5/8] Fix conditionable clashing with existing method --- src/Checks/Check.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Checks/Check.php b/src/Checks/Check.php index 55e875d8..d6a25f6c 100755 --- a/src/Checks/Check.php +++ b/src/Checks/Check.php @@ -14,7 +14,9 @@ abstract class Check { use ManagesFrequencies; use Macroable; - use Conditionable; + use Conditionable { + unless as whenUnless; + } protected string $expression = '* * * * *'; From 84c69e014952bf128681a91d227a5b2e86412590 Mon Sep 17 00:00:00 2001 From: Oleksandr Prypkhan Date: Mon, 10 Jul 2023 22:31:21 +0300 Subject: [PATCH 6/8] Change self to static --- src/Checks/Check.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Checks/Check.php b/src/Checks/Check.php index d6a25f6c..4c3c2719 100755 --- a/src/Checks/Check.php +++ b/src/Checks/Check.php @@ -42,14 +42,14 @@ public static function new(): static return $instance; } - public function name(string $name): self + public function name(string $name): static { $this->name = $name; return $this; } - public function label(string $label): self + public function label(string $label): static { $this->label = $label; From 853ea8fbb10562cdea7a8a7b49583ac2c5648c75 Mon Sep 17 00:00:00 2001 From: Oleksandr Prypkhan Date: Thu, 20 Jul 2023 23:49:08 +0300 Subject: [PATCH 7/8] Fix failing tests and add documentation on conditions --- ...nditionally-running-or-modifying-checks.md | 77 +++++++++++++++++++ docs/viewing-results/general.md | 15 ---- src/Checks/Check.php | 2 +- tests/HealthTest.php | 32 ++++++-- 4 files changed, 102 insertions(+), 24 deletions(-) create mode 100644 docs/basic-usage/conditionally-running-or-modifying-checks.md diff --git a/docs/basic-usage/conditionally-running-or-modifying-checks.md b/docs/basic-usage/conditionally-running-or-modifying-checks.md new file mode 100644 index 00000000..8585d064 --- /dev/null +++ b/docs/basic-usage/conditionally-running-or-modifying-checks.md @@ -0,0 +1,77 @@ +--- +title: Conditionally running or modifying checks +weight: 5 +--- + +You may need to only run specific checks on a specific environment or with a specific config +enabled. This package provides methods to run checks only when specified conditions are met. + +If you would like to conditionally run a check, you can use the `if` and `unless` methods. +For more control, you can also use callables. They are evaluated every time a health check is ran. + +```php +use Spatie\Health\Facades\Health; +use Spatie\Health\Checks\Checks\DebugModeCheck; +use Spatie\Health\Checks\Checks\RedisCheck; + +Health::checks([ + DebugModeCheck::new()->unless(app()->environment('local')), + RedisCheck::new()->if(fn () => app(SomeHeavyService::class)->shouldCheckHealth()), +]); +``` + +## Custom condition methods + +You may find yourself repeating conditions for multiple checks. To avoid that, +you can register a Laravel macro on a check with a custom condition method. + +```php +use Spatie\Health\Facades\Health; +use Spatie\Health\Checks\Check; +use Spatie\Health\Checks\Checks\DebugModeCheck; +use Spatie\Health\Checks\Checks\RedisCheck; + +Health::macro('ifEnvironment', fn (string|array $envs) => app()->environment($envs)); + +Health::checks([ + DebugModeCheck::new()->ifEnvironment('production') +]); +``` + +## Chaining conditions + +Sometimes you need more than one condition on a check, so you may chain two or more of them +simply by calling `if` or `unless` multiple times. They are evaluated in the order that +you define them. + +```php +use Spatie\Health\Facades\Health; +use Spatie\Health\Checks\Checks\DebugModeCheck; +use Spatie\Health\Checks\Checks\RedisCheck; + +Health::checks([ + DebugModeCheck::new() + ->unless(app()->environment('local')) + ->if(fn () => app(SomeHeavyService::class)->shouldCheckHealth()), +]); +``` + +## Modifying checks on a condition + +You may want to slightly change check's configuration under a specific condition. You can do +so using `when` and `doUnless` methods. In this example, a smaller memory limit is enforced +on a local environment. + +```php +use Spatie\Health\Facades\Health; +use Spatie\Health\Checks\Checks\RedisMemoryUsageCheck; + +Health::checks([ + RedisMemoryUsageCheck::new() + ->failWhenAboveMb(1000) + ->when( + app()->environment('local'), + fn (RedisMemoryUsageCheck $check) => $check->failWhenAboveMb(200) + ), +]); +``` diff --git a/docs/viewing-results/general.md b/docs/viewing-results/general.md index a8943cd9..e769c235 100644 --- a/docs/viewing-results/general.md +++ b/docs/viewing-results/general.md @@ -22,18 +22,3 @@ Health::checks([ UsedDiskSpaceCheck::new()->label('Disk space on main disk'), ]); ``` - -## Running checks conditionally - -If you would like to conditionally run a check, you can use the `if` and `unless` methods. - -```php -use Spatie\Health\Facades\Health; -use Spatie\Health\Checks\Checks\DebugModeCheck; -use Spatie\Health\Checks\Checks\RedisCheck; - -Health::checks([ - DebugModeCheck::new()->if(app()->isProduction()), - RedisCheck::new()->unless(app()->environment('development')), -]); -``` diff --git a/src/Checks/Check.php b/src/Checks/Check.php index 4c3c2719..c33bc6bb 100755 --- a/src/Checks/Check.php +++ b/src/Checks/Check.php @@ -15,7 +15,7 @@ abstract class Check use ManagesFrequencies; use Macroable; use Conditionable { - unless as whenUnless; + unless as doUnless; } protected string $expression = '* * * * *'; diff --git a/tests/HealthTest.php b/tests/HealthTest.php index 6c1e6759..64bbd679 100644 --- a/tests/HealthTest.php +++ b/tests/HealthTest.php @@ -28,8 +28,8 @@ it('can run checks conditionally using if method', function () { Health::checks([ UsedDiskSpaceCheck::new(), - DebugModeCheck::new()->if(false), - DebugModeCheck::new()->if(true)->if(false), + DebugModeCheck::new()->name('Debug 1')->if(false), + DebugModeCheck::new()->name('Debug 2')->if(true)->if(false), EnvironmentCheck::new()->if(fn () => false), ]); @@ -46,8 +46,8 @@ Health::checks([ UsedDiskSpaceCheck::new(), - DebugModeCheck::new()->if(true), - DebugModeCheck::new()->if(true)->if(true), + DebugModeCheck::new()->name('Debug 1')->if(true), + DebugModeCheck::new()->name('Debug 2')->if(true)->if(true), EnvironmentCheck::new()->if(fn () => true), ]); @@ -68,8 +68,8 @@ it('can run checks conditionally using unless method', function () { Health::checks([ UsedDiskSpaceCheck::new(), - DebugModeCheck::new()->unless(true), - DebugModeCheck::new()->unless(false)->unless(true), + DebugModeCheck::new()->name('Debug 1')->unless(true), + DebugModeCheck::new()->name('Debug 2')->unless(false)->unless(true), EnvironmentCheck::new()->unless(fn () => true), ]); @@ -86,8 +86,8 @@ Health::checks([ UsedDiskSpaceCheck::new(), - DebugModeCheck::new()->unless(false), - DebugModeCheck::new()->unless(false)->unless(false), + DebugModeCheck::new()->name('Debug 1')->unless(false), + DebugModeCheck::new()->name('Debug 2')->unless(false)->unless(false), EnvironmentCheck::new()->unless(fn () => false), ]); @@ -105,6 +105,22 @@ ->toBeInstanceOf(EnvironmentCheck::class); }); +it('can conditionally modify a check using when method', function () { + $check = DebugModeCheck::new() + ->when(true, fn (DebugModeCheck $check) => $check->name('Debug 1')) + ->when(false, fn (DebugModeCheck $check) => $check->name('Debug 2')); + + expect($check->getName())->toBe('Debug 1'); +}); + +it('can conditionally modify a check using doUnless method', function () { + $check = DebugModeCheck::new() + ->doUnless(false, fn (DebugModeCheck $check) => $check->name('Debug 1')) + ->doUnless(true, fn (DebugModeCheck $check) => $check->name('Debug 2')); + + expect($check->getName())->toBe('Debug 1'); +}); + it('will throw an exception when duplicate checks are registered', function () { Health::checks([ PingCheck::new(), From a6638037bb01fa2aeb8dfefedd68830d284561b0 Mon Sep 17 00:00:00 2001 From: Freek Van der Herten Date: Thu, 20 Jul 2023 22:30:51 -0500 Subject: [PATCH 8/8] Update conditionally-running-or-modifying-checks.md --- .../basic-usage/conditionally-running-or-modifying-checks.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/basic-usage/conditionally-running-or-modifying-checks.md b/docs/basic-usage/conditionally-running-or-modifying-checks.md index 8585d064..45f93f95 100644 --- a/docs/basic-usage/conditionally-running-or-modifying-checks.md +++ b/docs/basic-usage/conditionally-running-or-modifying-checks.md @@ -3,11 +3,10 @@ title: Conditionally running or modifying checks weight: 5 --- -You may need to only run specific checks on a specific environment or with a specific config -enabled. This package provides methods to run checks only when specified conditions are met. +This package provides methods to run certain checks only when specified conditions are met. If you would like to conditionally run a check, you can use the `if` and `unless` methods. -For more control, you can also use callables. They are evaluated every time a health check is ran. +For more control, you can also use callables. They are evaluated every time a health check is run. ```php use Spatie\Health\Facades\Health;