Skip to content

Commit

Permalink
add should reschedule for (#104)
Browse files Browse the repository at this point in the history
Co-authored-by: atymic <[email protected]>
  • Loading branch information
cristiancalara and atymic authored Apr 16, 2024
1 parent 7a5a7d2 commit bf0d701
Show file tree
Hide file tree
Showing 5 changed files with 182 additions and 0 deletions.
24 changes: 24 additions & 0 deletions src/Events/NotificationRescheduled.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

namespace Thomasjohnkane\Snooze\Events;

use Illuminate\Queue\SerializesModels;
use Thomasjohnkane\Snooze\Models\ScheduledNotification;

class NotificationRescheduled
{
use SerializesModels;

public $scheduledNotification;

/**
* Create a new event instance.
*
* @param \Thomasjohnkane\Snooze\Models\ScheduledNotification $scheduledNotification
* @return void
*/
public function __construct(ScheduledNotification $scheduledNotification)
{
$this->scheduledNotification = $scheduledNotification;
}
}
30 changes: 30 additions & 0 deletions src/Models/ScheduledNotification.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Model;
use Thomasjohnkane\Snooze\Events\NotificationInterrupted;
use Thomasjohnkane\Snooze\Events\NotificationRescheduled;
use Thomasjohnkane\Snooze\Events\NotificationSent;
use Thomasjohnkane\Snooze\Exception\NotificationAlreadySentException;
use Thomasjohnkane\Snooze\Exception\NotificationCancelledException;
Expand Down Expand Up @@ -66,6 +67,13 @@ public function send(): void
return;
}

if ($sendAt = $this->shouldRescheduleFor($notification, $notifiable)) {
$this->reschedule($sendAt);
event(new NotificationRescheduled($this));

return;
}

$notifiable->notify($notification);

$this->sent_at = Carbon::now();
Expand Down Expand Up @@ -96,6 +104,28 @@ public function shouldInterrupt(?object $notification = null, ?object $notifiabl
return false;
}

/**
* @param object|null $notification
* @param object|null $notifiable
* @return \DateTimeInterface|string|null
*/
public function shouldRescheduleFor(?object $notification = null, ?object $notifiable = null)
{
if (! $notification) {
$notification = $this->serializer->unserialize($this->notification);
}

if (! $notifiable) {
$notifiable = $this->serializer->unserialize($this->target);
}

if (method_exists($notification, 'shouldRescheduleFor')) {
return $notification->shouldRescheduleFor($notifiable);
}

return null;
}

/**
* @return void
*
Expand Down
8 changes: 8 additions & 0 deletions src/ScheduledNotification.php
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,14 @@ public function shouldInterrupt(): bool
return $this->scheduleNotificationModel->shouldInterrupt();
}

/**
* @return \DateTimeInterface|string|null
*/
public function shouldRescheduleFor()
{
return $this->scheduleNotificationModel->shouldRescheduleFor();
}

private static function getScheduledNotificationModelClass(): string
{
return config('snooze.model') ?? ScheduledNotificationModel::class;
Expand Down
68 changes: 68 additions & 0 deletions tests/CanRescheduleNotificationTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?php

namespace Thomasjohnkane\Snooze\Tests;

use Carbon\Carbon;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Notification;
use Thomasjohnkane\Snooze\Events\NotificationRescheduled;
use Thomasjohnkane\Snooze\Models\ScheduledNotification;
use Thomasjohnkane\Snooze\Tests\Models\User;
use Thomasjohnkane\Snooze\Tests\Notifications\TestReschedulableNotification;

class CanRescheduleNotificationTest extends TestCase
{
public function testNotificationIsRescheduled()
{
Notification::fake();
Event::fake();

// User id 1 should be interrupted
$target = User::find(1);
$notification = $target->notifyAt(new TestReschedulableNotification(User::find(2)), Carbon::now()->subSeconds(10));
$this->assertDatabaseHas('scheduled_notifications', ['id' => $notification->getId()]);

$this->artisan('snooze:send');

$notification->refresh();
$this->assertFalse($notification->isSent());
$this->assertFalse($notification->isCancelled());
$this->assertNotNull($notification->shouldRescheduleFor());

Notification::assertNothingSent();
Event::assertDispatched(NotificationRescheduled::class, 1);
}

public function testNotificationIsNotRescheduled()
{
Notification::fake();
Event::fake();

// User id 2 should NOT be rescheduled.
$target = User::find(2);

$notification = $target->notifyAt(new TestReschedulableNotification(User::find(2)), Carbon::now()->subSeconds(10));
$this->assertDatabaseHas('scheduled_notifications', ['id' => $notification->getId()]);
$this->artisan('snooze:send');

$notification->refresh();
$this->assertTrue($notification->isSent());
$this->assertFalse($notification->isCancelled());
$this->assertNull($notification->shouldRescheduleFor());
}

public function testInterruptMethodReceivesNotifiable()
{
$target = User::find(3);

$notificationMock = $this->createMock(TestReschedulableNotification::class);
$notificationMock
->expects($this->once())
->method('shouldRescheduleFor')
->with($target)
->willReturn(null);

$model = new ScheduledNotification();
$model->shouldRescheduleFor($notificationMock, $target);
}
}
52 changes: 52 additions & 0 deletions tests/Notifications/TestReschedulableNotification.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php

namespace Thomasjohnkane\Snooze\Tests\Notifications;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
use Thomasjohnkane\Snooze\Tests\Models\User;

class TestReschedulableNotification extends Notification implements ShouldQueue
{
use Queueable;

/** @var User */
public $newUser;

/**
* @param User $newUser
*/
public function __construct(User $newUser)
{
$this->newUser = $newUser;
}

/**
* Get the notification's channels.
*
* @param mixed $notifiable
* @return array|string
*/
public function via($notifiable)
{
return ['mail'];
}

public function toMail($notifiable): MailMessage
{
return (new MailMessage)
->subject('New User')
->line(sprintf('Email: %s', $this->newUser->email));
}

public function shouldRescheduleFor(object $notifiable)
{
if ($notifiable->id === 1) {
return now()->addMinutes(5);
}

return null;
}
}

0 comments on commit bf0d701

Please sign in to comment.