Skip to content

Commit

Permalink
Merge pull request #184 from oprypkhantc/macroable-check
Browse files Browse the repository at this point in the history
Make Check macroable and allow chaining callables in if() and unless()
  • Loading branch information
freekmurze authored Jul 21, 2023
2 parents 2068e26 + a663803 commit 2e5ea91
Show file tree
Hide file tree
Showing 4 changed files with 141 additions and 32 deletions.
76 changes: 76 additions & 0 deletions docs/basic-usage/conditionally-running-or-modifying-checks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
---
title: Conditionally running or modifying checks
weight: 5
---

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 run.

```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)
),
]);
```
15 changes: 0 additions & 15 deletions docs/viewing-results/general.md
Original file line number Diff line number Diff line change
Expand Up @@ -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')),
]);
```
33 changes: 24 additions & 9 deletions src/Checks/Check.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,28 @@
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;

abstract class Check
{
use ManagesFrequencies;
use Macroable;
use Conditionable {
unless as doUnless;
}

protected string $expression = '* * * * *';

protected ?string $name = null;

protected ?string $label = null;

protected bool $shouldRun = true;
/**
* @var array<bool|callable(): bool>
*/
protected array $shouldRun = [];

public function __construct()
{
Expand All @@ -33,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;

Expand Down Expand Up @@ -71,25 +80,31 @@ public function getName(): string

public function shouldRun(): bool
{
if (! $this->shouldRun) {
return false;
foreach ($this->shouldRun as $shouldRun) {
$shouldRun = is_callable($shouldRun) ? $shouldRun() : $shouldRun;

if (!$shouldRun) {
return false;
}
}

$date = Date::now();

return (new CronExpression($this->expression))->isDue($date->toDateTimeString());
}

public function if(bool $condition)
public function if(bool|callable $condition)
{
$this->shouldRun = $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;
}
Expand Down
49 changes: 41 additions & 8 deletions tests/HealthTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -27,7 +28,9 @@
it('can run checks conditionally using if method', function () {
Health::checks([
UsedDiskSpaceCheck::new(),
DebugModeCheck::new()->if(false),
DebugModeCheck::new()->name('Debug 1')->if(false),
DebugModeCheck::new()->name('Debug 2')->if(true)->if(false),
EnvironmentCheck::new()->if(fn () => false),
]);

$checks = Health::registeredChecks()->filter(function (Check $check) {
Expand All @@ -43,23 +46,31 @@

Health::checks([
UsedDiskSpaceCheck::new(),
DebugModeCheck::new()->if(true),
DebugModeCheck::new()->name('Debug 1')->if(true),
DebugModeCheck::new()->name('Debug 2')->if(true)->if(true),
EnvironmentCheck::new()->if(fn () => true),
]);

$checks = Health::registeredChecks()->filter(function (Check $check) {
return $check->shouldRun();
});

expect($checks)
->toHaveCount(2)
->toHaveCount(4)
->and($checks[1])
->toBeInstanceOf(DebugModeCheck::class);
->toBeInstanceOf(DebugModeCheck::class)
->and($checks[2])
->toBeInstanceOf(DebugModeCheck::class)
->and($checks[3])
->toBeInstanceOf(EnvironmentCheck::class);
});

it('can run checks conditionally using unless method', function () {
Health::checks([
UsedDiskSpaceCheck::new(),
DebugModeCheck::new()->unless(true),
DebugModeCheck::new()->name('Debug 1')->unless(true),
DebugModeCheck::new()->name('Debug 2')->unless(false)->unless(true),
EnvironmentCheck::new()->unless(fn () => true),
]);

$checks = Health::registeredChecks()->filter(function (Check $check) {
Expand All @@ -75,17 +86,39 @@

Health::checks([
UsedDiskSpaceCheck::new(),
DebugModeCheck::new()->unless(false),
DebugModeCheck::new()->name('Debug 1')->unless(false),
DebugModeCheck::new()->name('Debug 2')->unless(false)->unless(false),
EnvironmentCheck::new()->unless(fn () => false),
]);

$checks = Health::registeredChecks()->filter(function (Check $check) {
return $check->shouldRun();
});

expect($checks)
->toHaveCount(2)
->toHaveCount(4)
->and($checks[1])
->toBeInstanceOf(DebugModeCheck::class);
->toBeInstanceOf(DebugModeCheck::class)
->and($checks[2])
->toBeInstanceOf(DebugModeCheck::class)
->and($checks[3])
->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 () {
Expand Down

0 comments on commit 2e5ea91

Please sign in to comment.