From 0f3914b64231ebd97d47eda9c00b2da4e9917803 Mon Sep 17 00:00:00 2001 From: atymic Date: Mon, 16 Dec 2019 13:16:57 +1100 Subject: [PATCH] feat: prune sent/cancelled notifications (#37) * feat: prune sent/cancelled notifications * docs: update readme + typos * fix: turn off by default --- README.md | 10 ++- config/snooze.php | 6 ++ .../Commands/PruneScheduledNotifications.php | 59 ++++++++++++++++ src/ServiceProvider.php | 5 ++ tests/PruneCommandTest.php | 69 +++++++++++++++++++ 5 files changed, 147 insertions(+), 2 deletions(-) create mode 100644 src/Console/Commands/PruneScheduledNotifications.php create mode 100644 tests/PruneCommandTest.php diff --git a/README.md b/README.md index eb8861d..e487362 100644 --- a/README.md +++ b/README.md @@ -103,11 +103,17 @@ The only thing you need to do is make sure `schedule:run` is also running. You c If your scheduler stops working, a backlog of scheduled notifications will build up. To prevent users receiving all of the old scheduled notifications at once, the command will only send mail within the configured tolerance. By default this is set to 24 hours, so only mail scheduled to be sent within that window will be sent. This can be -configured in the `snooze.php` config file. +configured (in seconds) using the `SCHEDULED_NOTIFICATION_SEND_TOLERANCE` environment variable or in the `snooze.php` config file. + +### Setting the prune age + +The package can prune sent and cancelled messages that were sent/cancelled more than x days ago. You can +configure this using the `SCHEDULED_NOTIFICATION_PRUNE_AGE` environment variable or in the `snooze.php` config file +(unit is days). This feature is turned off by default. #### Detailed Examples -- [Delayed Notifcation (1 week)][3] +- [Delayed Notification (1 week)][3] - [Simple On-boarding Email Drip][5] - [Exposing Custom Data to the Notification/Email][4] diff --git a/config/snooze.php b/config/snooze.php index 190bd79..44111ad 100644 --- a/config/snooze.php +++ b/config/snooze.php @@ -28,4 +28,10 @@ * running. By default it's set to 24 hours */ 'sendTolerance' => env('SCHEDULED_NOTIFICATION_SEND_TOLERANCE', 60 * 60 * 24), + + /* + * The age at which to prune sent/cancelled notifications, in days. + * If set to null, pruning will be turned off. By default it's turned off + */ + 'pruneAge' => env('SCHEDULED_NOTIFICATION_PRUNE_AGE', null), ]; diff --git a/src/Console/Commands/PruneScheduledNotifications.php b/src/Console/Commands/PruneScheduledNotifications.php new file mode 100644 index 0000000..d829d60 --- /dev/null +++ b/src/Console/Commands/PruneScheduledNotifications.php @@ -0,0 +1,59 @@ +error('Pruning of scheduled notifications is disabled'); + + return; + } + + $pruneBeforeDate = Carbon::now()->subDays($pruneDays); + + $notifications = ScheduledNotification::where(function ($query) { + $query->where('sent_at', '!=', null); + $query->orWhere('cancelled_at', '!=', null); + })->where(function ($query) use ($pruneBeforeDate) { + $query->where('sent_at', '<=', $pruneBeforeDate); + $query->orWhere('cancelled_at', '<=', $pruneBeforeDate); + }); + + $totalDeleted = 0; + + do { + $deleted = $notifications->take(1000)->delete(); + $totalDeleted += $deleted; + } while ($deleted !== 0); + + $this->info(sprintf('Pruned %d scheduled notifications', $totalDeleted)); + } +} diff --git a/src/ServiceProvider.php b/src/ServiceProvider.php index 3053808..8c1fb88 100644 --- a/src/ServiceProvider.php +++ b/src/ServiceProvider.php @@ -10,6 +10,7 @@ class ServiceProvider extends \Illuminate\Support\ServiceProvider protected $commands = [ Console\Commands\SendScheduledNotifications::class, + Console\Commands\PruneScheduledNotifications::class, ]; public function boot() @@ -19,6 +20,10 @@ public function boot() $frequency = config('snooze.sendFrequency', 'everyMinute'); $schedule = $this->app->make(Schedule::class); $schedule->command('snooze:send')->{$frequency}(); + + if (config('snooze.pruneAge') !== null) { + $schedule->command('snooze:prune')->daily(); + } }); $this->publishes([ diff --git a/tests/PruneCommandTest.php b/tests/PruneCommandTest.php new file mode 100644 index 0000000..baa6701 --- /dev/null +++ b/tests/PruneCommandTest.php @@ -0,0 +1,69 @@ +artisan('snooze:prune') + ->expectsOutput('Pruning of scheduled notifications is disabled'); + } + + public function testItDoesNotPruneRecentNotifications() + { + $this->app->config->set('snooze.pruneAge', 30); + + $target = User::find(1); + + $target->notifyAt(new TestNotification(User::find(2)), Carbon::now()); + $target->notifyAt(new TestNotification(User::find(2)), Carbon::now()); + $target->notifyAt(new TestNotification(User::find(2)), Carbon::now()); + + $this->artisan('snooze:prune') + ->expectsOutput('Pruned 0 scheduled notifications') + ->assertExitCode(0); + } + + public function testPrunesCorrectNotifications() + { + $this->app->config->set('snooze.pruneAge', 30); + + $target = User::find(1); + + $notification = $target->notifyAt(new TestNotification(User::find(2)), Carbon::now()); + $base = ScheduledNotificationModel::find($notification->getId()); + + $sent2MonthsAgo = $base->replicate(); + $sent2MonthsAgo->sent_at = Carbon::now()->subMonths(2); + $sent2MonthsAgo->save(); + + $cancelled2MonthsAgo = $base->replicate(); + $cancelled2MonthsAgo->cancelled_at = Carbon::now()->subMonths(2); + $cancelled2MonthsAgo->save(); + + $sent1WeekAgo = $base->replicate(); + $sent1WeekAgo->sent_at = Carbon::now()->subWeek(); + $sent1WeekAgo->save(); + + $cancelled1WeekAgo = $base->replicate(); + $cancelled1WeekAgo->cancelled_at = Carbon::now()->subWeek(); + $cancelled1WeekAgo->save(); + + $this->artisan('snooze:prune') + ->expectsOutput('Pruned 2 scheduled notifications') + ->assertExitCode(0); + + $this->assertDatabaseMissing('scheduled_notifications', ['id' => $sent2MonthsAgo->id]); + $this->assertDatabaseMissing('scheduled_notifications', ['id' => $cancelled2MonthsAgo->id]); + + $this->assertDatabaseHas('scheduled_notifications', ['id' => $base->id]); + $this->assertDatabaseHas('scheduled_notifications', ['id' => $sent1WeekAgo->id]); + $this->assertDatabaseHas('scheduled_notifications', ['id' => $cancelled1WeekAgo->id]); + } +}