Skip to content

Commit

Permalink
[feature] Add support for symfony/notifier. (#72)
Browse files Browse the repository at this point in the history
* Add symfony/notifier integration

* Limit notifier to 5.2+

* Update PHPStan baseline

* Exclude exception from notifier content

The exception trace can get very long, which will not be useful in some
of the notifier channels (E.G receiving an SMS with a big wall of text is
not very helpful). Also some channels might limit the amount of text that
can be sent which will result in api errors.

* Rename slack to chat/slack
  • Loading branch information
pierredup authored Nov 20, 2022
1 parent ce80440 commit 2063ad1
Show file tree
Hide file tree
Showing 16 changed files with 1,168 additions and 24 deletions.
74 changes: 72 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@ Task Scheduling feature](https://laravel.com/docs/master/scheduling).
2. [Callbacks](doc/define-schedule.md#callbacks)
3. [Ping Webhook](doc/define-schedule.md#ping-webhook)
4. [Email On Failure](doc/define-schedule.md#email-on-failure)
5. [Run on Single Server](doc/define-schedule.md#run-on-single-server)
6. [Limit to specific environment(s)](doc/define-schedule.md#limit-to-specific-environments)
5. [Notify On Failure](doc/define-schedule.md#notify-on-failure)
6. [Run on Single Server](doc/define-schedule.md#run-on-single-server)
7. [Limit to specific environment(s)](doc/define-schedule.md#limit-to-specific-environments)
4. [Defining Tasks](doc/define-tasks.md)
1. [Task Types](doc/define-tasks.md#task-types)
1. [CommandTask](doc/define-tasks.md#commandtask)
Expand All @@ -51,6 +52,7 @@ Task Scheduling feature](https://laravel.com/docs/master/scheduling).
2. [Callbacks](doc/define-tasks.md#callbacks)
3. [Ping Webhook](doc/define-tasks.md#ping-webhook)
4. [Email Output](doc/define-tasks.md#email-output)
4. [Notify Output](doc/define-tasks.md#notify-output)
5. [Prevent Overlap](doc/define-tasks.md#prevent-overlap)
6. [Run on Single Server](doc/define-tasks.md#run-on-single-server)
7. [Between](doc/define-tasks.md#between)
Expand Down Expand Up @@ -164,6 +166,24 @@ zenstruck_schedule:
# The prefix to use for email subjects (use to distinguish between different application schedules)
subject_prefix: null # Example: "[Acme Inc Website]"

notifier:
enabled: false

# The notifier service to use
service: notifier

# The default channel (can use a string, or array of channels)
default_channel: null

# The default email address for email notifications
default_email: null

# The default phone number for SMS notifications (can be overridden by extension)
default_phone: null

# The prefix to use for notification subjects (use to distinguish between different application schedules)
subject_prefix: null # Example: "[Acme Inc Website]"

schedule_extensions:

# Set the environment(s) you only want the schedule to run in.
Expand All @@ -185,6 +205,23 @@ zenstruck_schedule:

# Email subject (leave blank to use extension default)
subject: null
3600

# Send notification if schedule fails (alternatively enable by passing a channel)
notify_on_failure:
enabled: false

# Channel to send notification to (leave blank to use "zenstruck_schedule.notifier.default_channel")
channel: null

# Notification subject (leave blank to use extension default)
subject: null

# Email address for email notifications (leave blank to use extension default)
email: null

# Phone number for SMS notifications (leave blank to use extension default)
phone: null

# Ping a url before schedule runs (alternatively enable by passing a url)
ping_before:
Expand Down Expand Up @@ -248,6 +285,7 @@ zenstruck_schedule:
only_between: 9-17
ping_on_success: https://example.com/hourly-report-health-check
email_on_failure: sales@example.com
notify_on_failure: chat/slack

# Prototype
-
Expand Down Expand Up @@ -354,4 +392,36 @@ zenstruck_schedule:

# Email subject (leave blank to use extension default)
subject: null

# Send notification after task runs (alternatively enable by passing a channel)
notify_after:
enabled: false

# Channel to send notification to (leave blank to use "zenstruck_schedule.notifier.default_channel")
channel: null

# Notification subject (leave blank to use extension default)
subject: null

# Email to send email notifications to (leave blank to use extension default)
email: null

# Phone number for SMS notifications (leave blank to use extension default)
phone: null

# Send email if task fails (alternatively enable by passing a "to" email)
notify_on_failure:
enabled: false

# Channel to send notification to (leave blank to use "zenstruck_schedule.notifier.default_channel")
channel: null

# Notification subject (leave blank to use extension default)
subject: null

# Email to send email notifications to (leave blank to use extension default)
email: null

# Phone number for SMS notifications (leave blank to use extension default)
phone: null
```
2 changes: 2 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"symfony/lock": "^4.4|^5.0|^6.0",
"symfony/mailer": "^4.4|^5.0|^6.0",
"symfony/messenger": "^4.4|^5.0|^6.0",
"symfony/notifier": "^5.2|^6.0",
"symfony/phpunit-bridge": "^6.0",
"symfony/process": "^4.2|^5.0|^6.0"
},
Expand All @@ -38,6 +39,7 @@
"symfony/http-client": "Allows usage of ping* extensions",
"symfony/lock": "Allows usage of withoutOverlapping and onSingleServer extensions (4.4+)",
"symfony/mailer": "Allows usage of email* extensions (4.4+)",
"symfony/notifier": "Allows usage of notify* extensions (5.2+)",
"symfony/process": "Allows usage of ProcessTask (4.2+)"
},
"config": {
Expand Down
93 changes: 93 additions & 0 deletions doc/define-schedule.md
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,99 @@ zenstruck_schedule:
Failed task's exception stack trace (if any)
```

### Notify On Failure

This extension can be used to notify site administrators via any notification
when tasks fail.

**Define in [PHP](#schedulebuilder-service):**

```php
/* @var $schedule \Zenstruck\ScheduleBundle\Schedule */
$schedule->notifyOnFailure(['chat/slack', 'sms', 'email'], 'admin@example.com', '123456789');
// default channel can be configured (see below)
$schedule->notifyOnFailure();
// customise the notification
$schedule->notifyOnFailure('chat/slack', null, null, null, function (\Symfony\Component\Notifier\Notification\Notification $notification) {
$notification->emoji('user');
});
```

**Define in [Configuration](#bundle-configuration):**

```yaml
# config/packages/zenstruck_schedule.yaml
zenstruck_schedule:
schedule_extensions:
notify_on_failure:
channel: chat/slack; # optional if configured
subject: my subject # optional, leave empty to use default
```

**Notes:**

1. This extension **requires** `symfony/notifier`:

```console
$ composer require symfony/notifier
```

2. This extension **requires** configuration:

```yaml
# config/packages/zenstruck_schedule.yaml
zenstruck_schedule:
notifier:
service: notifier # required
default_channel: chat/slack # optional (exclude if defined in code/config)
default_email: webmaster@example.com # optional
default_phone: 1234567890 # optional
subject_prefix: "[Acme Inc]" # optional
```

3. The notification has the subject `[Schedule Failure] 2 tasks failed`
(assuming 2 tasks failed, the subject can be configured). The content
has the following structure:

```
2 tasks failed

# (Failure 1/2) CommandTask: failed task 1 description

Result: "failure description (ie exception message)"

Task ID: <task ID>

## Task Output

Failed task's output (if any)

## Exception

Failed task's exception stack trace (if any)

---

# (Failure 2/2) CommandTask: failed task 2 description

Result: "failure description (ie exception message)"

Task ID: <task ID>

## Task Output

Failed task's output (if any)

## Exception

Failed task's exception stack trace (if any)
```

### Run on Single Server

This extension *locks* the schedule so it only runs on one server. The server
Expand Down
105 changes: 105 additions & 0 deletions doc/define-tasks.md
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ $schedule->addCompound()
->at('1:30')
->timezone('UTC')
->emailOnFailure('admin@example.com')
->notifyOnFailure('chat/slack')
;
```

Expand All @@ -234,6 +235,7 @@ zenstruck_schedule:
frequency: '0 * * * *'
timezone: UTC
email_on_failure: ~
notify_on_failure: ~
- task: # optionally key by the desired task description
"run my command": my:command arg --option
Expand Down Expand Up @@ -731,6 +733,109 @@ zenstruck_schedule:
Task's output (if any)
```

### Notify Output

This extension can be used to notify site administrators via notification channels
that the task ran. Either just if it failed (`notify_on_failure`) or
regardless of the result (`notify_after`).

**Define in [PHP](define-schedule.md#schedulebuilder-service):**

```php
/* @var $task \Zenstruck\ScheduleBundle\Schedule\Task */
$task->notifyAfter('chat/slack');
$task->thenNotify('chat/slack'); // alias for ->notifyAfter()
$task->notifyOnFailure('chat/slack');
// default channel can be configured (see below)
$task->notifyAfter();
$task->notifyOnFailure();
// customise notification
$task->notifyAfter('chat/slack', null, null, 'my subject', function (Symfony\Component\Notifier\Notification\Notification $notification) {
$notification->emoji('check');
});
$task->notifyOnFailure('chat/slack', null, null, 'my subject', function (Symfony\Component\Notifier\Notification\Notification $email) {
$notification->emoji('alert');
});
```

**Define in [Configuration](define-schedule.md#bundle-configuration):**

```yaml
# config/packages/zenstruck_schedule.yaml
zenstruck_schedule:
tasks:
- task: my:command
frequency: '0 * * * *'
notify_after: chat/slack
notify_on_failure: ~ # default channel can be configured (see below)
- task: my:command
frequency: '0 * * * *'
notify_after:
channel: chat/slack
subject: my custom subject
```

**Notes:**

1. This extension **requires** `symfony/notifier`:

```console
$ composer require symfony/notifier
```

2. This extension **requires** configuration:

```yaml
# config/packages/zenstruck_schedule.yaml
zenstruck_schedule:
notifier:
service: notifier # required
default_channel: chat/slack # optional (exclude if defined in code)
default_email: admin@example.com # optional (exclude if defined in code)
default_phone: webmaster@example.com # optional (exclude if defined in code)
subject_prefix: "[Acme Inc]" # optional
```

3. Failed task notifications have the subject `[Scheduled Task Failed] CommandTask: failed
task description` (the subject can be configured). The content has the following
structure:

```
Result: "failure description (ie exception message)"

Task ID: <task ID>

## Task Output

Failed task's output (if any)

## Exception

Failed task's exception stack trace (if any)
```

4. Successful task notifications (if using `notify_after`) have the subject
`[Scheduled Task Succeeded] CommandTask: task description`. The content
has the following structure:

```
Result: "Successful"

Task ID: <task ID>

## Task Output:

Task's output (if any)
```

### Prevent Overlap

This extension *locks* the task so it cannot run if it is still running from
Expand Down
17 changes: 16 additions & 1 deletion phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ parameters:

-
message: "#^Call to an undefined method Symfony\\\\Component\\\\Config\\\\Definition\\\\Builder\\\\NodeDefinition\\:\\:canBeEnabled\\(\\)\\.$#"
count: 2
count: 3
path: src/DependencyInjection/Configuration.php

-
Expand All @@ -30,6 +30,11 @@ parameters:
count: 1
path: src/DependencyInjection/Configuration.php

-
message: "#^Method Zenstruck\\\\ScheduleBundle\\\\DependencyInjection\\\\Configuration\\:\\:createNotifyExtension\\(\\) should return Symfony\\\\Component\\\\Config\\\\Definition\\\\Builder\\\\ArrayNodeDefinition but returns Symfony\\\\Component\\\\Config\\\\Definition\\\\Builder\\\\NodeDefinition\\.$#"
count: 1
path: src/DependencyInjection/Configuration.php

-
message: "#^Method Zenstruck\\\\ScheduleBundle\\\\DependencyInjection\\\\Configuration\\:\\:createPingExtension\\(\\) should return Symfony\\\\Component\\\\Config\\\\Definition\\\\Builder\\\\ArrayNodeDefinition but returns Symfony\\\\Component\\\\Config\\\\Definition\\\\Builder\\\\NodeDefinition\\.$#"
count: 1
Expand Down Expand Up @@ -115,6 +120,16 @@ parameters:
count: 1
path: src/Schedule.php

-
message: "#^Parameter \\#1 \\$start of method Zenstruck\\\\ScheduleBundle\\\\Schedule\\\\CronExpression\\:\\:hashField\\(\\) expects int, int\\|string given\\.$#"
count: 1
path: src/Schedule/CronExpression.php

-
message: "#^Parameter \\#2 \\$end of method Zenstruck\\\\ScheduleBundle\\\\Schedule\\\\CronExpression\\:\\:hashField\\(\\) expects int, int\\|string given\\.$#"
count: 1
path: src/Schedule/CronExpression.php

-
message: "#^Parameter \\#3 \\.\\.\\.\\$values of function sprintf expects bool\\|float\\|int\\|string\\|null, object given\\.$#"
count: 1
Expand Down
Loading

0 comments on commit 2063ad1

Please sign in to comment.