Merge pull request #16932 from marcusmoore/fixes/webhook-checkin-checkout-fix
Improve notifications
This commit is contained in:
@@ -27,6 +27,7 @@ use App\Notifications\CheckoutAssetNotification;
|
||||
use App\Notifications\CheckoutConsumableNotification;
|
||||
use App\Notifications\CheckoutLicenseSeatNotification;
|
||||
use GuzzleHttp\Exception\ClientException;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
use Exception;
|
||||
@@ -40,6 +41,24 @@ class CheckoutableListener
|
||||
Component::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* Register the listeners for the subscriber.
|
||||
*
|
||||
* @param Illuminate\Events\Dispatcher $events
|
||||
*/
|
||||
public function subscribe($events)
|
||||
{
|
||||
$events->listen(
|
||||
\App\Events\CheckoutableCheckedIn::class,
|
||||
'App\Listeners\CheckoutableListener@onCheckedIn'
|
||||
);
|
||||
|
||||
$events->listen(
|
||||
\App\Events\CheckoutableCheckedOut::class,
|
||||
'App\Listeners\CheckoutableListener@onCheckedOut'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify the user and post to webhook about the checked out checkoutable
|
||||
* and add a record to the checkout_requests table.
|
||||
@@ -50,94 +69,70 @@ class CheckoutableListener
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a checkout acceptance and attach it in the notification
|
||||
*/
|
||||
$settings = Setting::getSettings();
|
||||
$shouldSendEmailToUser = $this->shouldSendCheckoutEmailToUser($event->checkoutable);
|
||||
$shouldSendEmailToAlertAddress = $this->shouldSendEmailToAlertAddress();
|
||||
$shouldSendWebhookNotification = $this->shouldSendWebhookNotification();
|
||||
|
||||
if (!$shouldSendEmailToUser && !$shouldSendEmailToAlertAddress && !$shouldSendWebhookNotification) {
|
||||
return;
|
||||
}
|
||||
|
||||
$acceptance = $this->getCheckoutAcceptance($event);
|
||||
$adminCcEmailsArray = [];
|
||||
|
||||
if ($settings->admin_cc_email !== '') {
|
||||
$adminCcEmail = $settings->admin_cc_email;
|
||||
$adminCcEmailsArray = array_map('trim', explode(',', $adminCcEmail));
|
||||
}
|
||||
$ccEmails = array_filter($adminCcEmailsArray);
|
||||
$mailable = $this->getCheckoutMailType($event, $acceptance);
|
||||
$notifiable = $this->getNotifiableUsers($event);
|
||||
if ($shouldSendEmailToUser || $shouldSendEmailToAlertAddress) {
|
||||
$mailable = $this->getCheckoutMailType($event, $acceptance);
|
||||
$notifiable = $this->getNotifiableUser($event);
|
||||
|
||||
$notifiableHasEmail = $notifiable instanceof User && $notifiable->email;
|
||||
|
||||
// Send email notifications
|
||||
try {
|
||||
/**
|
||||
* Send an email if any of the following conditions are met:
|
||||
* 1. The asset requires acceptance
|
||||
* 2. The item has a EULA
|
||||
* 3. The item should send an email at check-in/check-out
|
||||
* 4. If the admin CC email is set, even if the item being checked out doesn't have an email address (location, etc)
|
||||
*/
|
||||
$shouldSendEmailToUser = $shouldSendEmailToUser && $notifiableHasEmail;
|
||||
|
||||
if ($event->checkoutable->requireAcceptance() /* does category require acceptance? */ ||
|
||||
$event->checkoutable->getEula() /* is there *some* kind of EULA? */ ||
|
||||
$this->checkoutableShouldSendEmail($event) /* does the category have 'checkin_email' [sic] set? */) {
|
||||
[$to, $cc] = $this->generateEmailRecipients($shouldSendEmailToUser, $shouldSendEmailToAlertAddress, $notifiable);
|
||||
|
||||
|
||||
// Send a checkout email to the admin CC addresses, even if the target has no email
|
||||
if (!empty($ccEmails)) {
|
||||
Mail::to($ccEmails)->send($mailable);
|
||||
Log::info('Checkout Mail sent to CC addresses');
|
||||
}
|
||||
|
||||
// Send a checkout email to the target if it has an email
|
||||
if (!empty($notifiable->email)) {
|
||||
Mail::to($notifiable)->send($mailable);
|
||||
if (!empty($to)) {
|
||||
try {
|
||||
Mail::to(array_flatten($to))->cc(array_flatten($cc))->send($mailable);
|
||||
Log::info('Checkout Mail sent to checkout target');
|
||||
} catch (ClientException $e) {
|
||||
Log::debug("Exception caught during checkout email: " . $e->getMessage());
|
||||
} catch (Exception $e) {
|
||||
Log::debug("Exception caught during checkout email: " . $e->getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
} catch (ClientException $e) {
|
||||
Log::debug("Exception caught during checkout email: " . $e->getMessage());
|
||||
} catch (Exception $e) {
|
||||
Log::debug("Exception caught during checkout email: " . $e->getMessage());
|
||||
}
|
||||
|
||||
// Send notification
|
||||
try {
|
||||
if ($this->shouldSendWebhookNotification()) {
|
||||
if ($shouldSendWebhookNotification) {
|
||||
try {
|
||||
if ($this->newMicrosoftTeamsWebhookEnabled()) {
|
||||
$message = $this->getCheckoutNotification($event)->toMicrosoftTeams();
|
||||
$notification = new TeamsNotification(Setting::getSettings()->webhook_endpoint);
|
||||
$notification->success()->sendMessage($message[0], $message[1]); // Send the message to Microsoft Teams
|
||||
} else {
|
||||
|
||||
Notification::route($this->webhookSelected(), Setting::getSettings()->webhook_endpoint)
|
||||
->notify($this->getCheckoutNotification($event, $acceptance));
|
||||
}
|
||||
} catch (ClientException $e) {
|
||||
if (strpos($e->getMessage(), 'channel_not_found') !== false) {
|
||||
Log::warning(Setting::getSettings()->webhook_selected . " notification failed: " . $e->getMessage());
|
||||
return redirect()->back()->with('warning', ucfirst(Setting::getSettings()->webhook_selected) . trans('admin/settings/message.webhook.webhook_channel_not_found'));
|
||||
} else {
|
||||
Log::error("ClientException caught during checkin notification: " . $e->getMessage());
|
||||
}
|
||||
return redirect()->back()->with('warning', ucfirst(Setting::getSettings()->webhook_selected) . trans('admin/settings/message.webhook.webhook_fail'));
|
||||
} catch (Exception $e) {
|
||||
Log::warning(ucfirst(Setting::getSettings()->webhook_selected) . ' webhook notification failed:', [
|
||||
'error' => $e->getMessage(),
|
||||
'webhook_endpoint' => Setting::getSettings()->webhook_endpoint,
|
||||
'event' => $event,
|
||||
]);
|
||||
return redirect()->back()->with('warning', ucfirst(Setting::getSettings()->webhook_selected) . trans('admin/settings/message.webhook.webhook_fail'));
|
||||
}
|
||||
} catch (ClientException $e) {
|
||||
if (strpos($e->getMessage(), 'channel_not_found') !== false) {
|
||||
Log::warning(Setting::getSettings()->webhook_selected." notification failed: " . $e->getMessage());
|
||||
return redirect()->back()->with('warning', ucfirst(Setting::getSettings()->webhook_selected) .trans('admin/settings/message.webhook.webhook_channel_not_found') );
|
||||
}
|
||||
else {
|
||||
Log::error("ClientException caught during checkin notification: " . $e->getMessage());
|
||||
}
|
||||
return redirect()->back()->with('warning', ucfirst(Setting::getSettings()->webhook_selected) .trans('admin/settings/message.webhook.webhook_fail') );
|
||||
} catch (Exception $e) {
|
||||
Log::warning(ucfirst(Setting::getSettings()->webhook_selected) . ' webhook notification failed:', [
|
||||
'error' => $e->getMessage(),
|
||||
'webhook_endpoint' => Setting::getSettings()->webhook_endpoint,
|
||||
'event' => $event,
|
||||
]);
|
||||
return redirect()->back()->with('warning', ucfirst(Setting::getSettings()->webhook_selected) . trans('admin/settings/message.webhook.webhook_fail'));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Notify the user and post to webhook about the checked in checkoutable
|
||||
*/
|
||||
*/
|
||||
public function onCheckedIn($event)
|
||||
{
|
||||
Log::debug('onCheckedIn in the Checkoutable listener fired');
|
||||
@@ -146,49 +141,44 @@ class CheckoutableListener
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the appropriate notification
|
||||
*/
|
||||
if ($event->checkedOutTo && $event->checkoutable){
|
||||
$acceptances = CheckoutAcceptance::where('checkoutable_id', $event->checkoutable->id)
|
||||
->where('assigned_to_id', $event->checkedOutTo->id)
|
||||
->get();
|
||||
$shouldSendEmailToUser = $this->checkoutableCategoryShouldSendEmail($event->checkoutable);
|
||||
$shouldSendEmailToAlertAddress = $this->shouldSendEmailToAlertAddress();
|
||||
$shouldSendWebhookNotification = $this->shouldSendWebhookNotification();
|
||||
|
||||
foreach($acceptances as $acceptance){
|
||||
if($acceptance->isPending()){
|
||||
$acceptance->delete();
|
||||
if (!$shouldSendEmailToUser && !$shouldSendEmailToAlertAddress && !$shouldSendWebhookNotification) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($shouldSendEmailToUser || $shouldSendEmailToAlertAddress) {
|
||||
/**
|
||||
* Send the appropriate notification
|
||||
*/
|
||||
if ($event->checkedOutTo && $event->checkoutable) {
|
||||
$acceptances = CheckoutAcceptance::where('checkoutable_id', $event->checkoutable->id)
|
||||
->where('assigned_to_id', $event->checkedOutTo->id)
|
||||
->get();
|
||||
|
||||
foreach ($acceptances as $acceptance) {
|
||||
if ($acceptance->isPending()) {
|
||||
$acceptance->delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$settings = Setting::getSettings();
|
||||
$adminCcEmailsArray = [];
|
||||
|
||||
if($settings->admin_cc_email !== '') {
|
||||
$adminCcEmail = $settings->admin_cc_email;
|
||||
$adminCcEmailsArray = array_map('trim', explode(',', $adminCcEmail));
|
||||
}
|
||||
$ccEmails = array_filter($adminCcEmailsArray);
|
||||
$mailable = $this->getCheckinMailType($event);
|
||||
$notifiable = $this->getNotifiableUsers($event);
|
||||
$mailable = $this->getCheckinMailType($event);
|
||||
$notifiable = $this->getNotifiableUser($event);
|
||||
|
||||
$notifiableHasEmail = $notifiable instanceof User && $notifiable->email;
|
||||
|
||||
$shouldSendEmailToUser = $shouldSendEmailToUser && $notifiableHasEmail;
|
||||
|
||||
[$to, $cc] = $this->generateEmailRecipients($shouldSendEmailToUser, $shouldSendEmailToAlertAddress, $notifiable);
|
||||
|
||||
// Send email notifications
|
||||
if ($this->checkoutableShouldSendEmail($event)) {
|
||||
try {
|
||||
/**
|
||||
* Send a check-in n email *only* if the item should send an email at check-in/check-out
|
||||
*/
|
||||
|
||||
// Send a checkout email to the admin's CC addresses, even if the target has no email
|
||||
if (!empty($ccEmails)) {
|
||||
Mail::to($ccEmails)->send($mailable);
|
||||
if (!empty($to)) {
|
||||
Mail::to(array_flatten($to))->cc(array_flatten($cc))->send($mailable);
|
||||
Log::info('Checkin Mail sent to CC addresses');
|
||||
}
|
||||
|
||||
// Send a checkout email to the target if it has an email
|
||||
if (!empty($notifiable->email)) {
|
||||
Mail::to($notifiable)->send($mailable);
|
||||
Log::info('Checkin Mail sent to checkout target');
|
||||
}
|
||||
} catch (ClientException $e) {
|
||||
Log::debug("Exception caught during checkin email: " . $e->getMessage());
|
||||
} catch (Exception $e) {
|
||||
@@ -196,9 +186,9 @@ class CheckoutableListener
|
||||
}
|
||||
}
|
||||
|
||||
// Send Webhook notification
|
||||
try {
|
||||
if ($this->shouldSendWebhookNotification()) {
|
||||
if ($shouldSendWebhookNotification) {
|
||||
// Send Webhook notification
|
||||
try {
|
||||
if ($this->newMicrosoftTeamsWebhookEnabled()) {
|
||||
$message = $this->getCheckinNotification($event)->toMicrosoftTeams();
|
||||
$notification = new TeamsNotification(Setting::getSettings()->webhook_endpoint);
|
||||
@@ -207,25 +197,24 @@ class CheckoutableListener
|
||||
Notification::route($this->webhookSelected(), Setting::getSettings()->webhook_endpoint)
|
||||
->notify($this->getCheckinNotification($event));
|
||||
}
|
||||
}
|
||||
} catch (ClientException $e) {
|
||||
if (strpos($e->getMessage(), 'channel_not_found') !== false) {
|
||||
Log::warning(Setting::getSettings()->webhook_selected." notification failed: " . $e->getMessage());
|
||||
return redirect()->back()->with('warning', ucfirst(Setting::getSettings()->webhook_selected) .trans('admin/settings/message.webhook.webhook_channel_not_found') );
|
||||
}
|
||||
else {
|
||||
Log::error("ClientException caught during checkin notification: " . $e->getMessage());
|
||||
} catch (ClientException $e) {
|
||||
if (strpos($e->getMessage(), 'channel_not_found') !== false) {
|
||||
Log::warning(Setting::getSettings()->webhook_selected . " notification failed: " . $e->getMessage());
|
||||
return redirect()->back()->with('warning', ucfirst(Setting::getSettings()->webhook_selected) . trans('admin/settings/message.webhook.webhook_channel_not_found'));
|
||||
} else {
|
||||
Log::error("ClientException caught during checkin notification: " . $e->getMessage());
|
||||
return redirect()->back()->with('warning', ucfirst(Setting::getSettings()->webhook_selected) . trans('admin/settings/message.webhook.webhook_fail'));
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
Log::warning(ucfirst(Setting::getSettings()->webhook_selected) . ' webhook notification failed:', [
|
||||
'error' => $e->getMessage(),
|
||||
'webhook_endpoint' => Setting::getSettings()->webhook_endpoint,
|
||||
'event' => $event,
|
||||
]);
|
||||
return redirect()->back()->with('warning', ucfirst(Setting::getSettings()->webhook_selected) . trans('admin/settings/message.webhook.webhook_fail'));
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
Log::warning(ucfirst(Setting::getSettings()->webhook_selected) . ' webhook notification failed:', [
|
||||
'error' => $e->getMessage(),
|
||||
'webhook_endpoint' => Setting::getSettings()->webhook_endpoint,
|
||||
'event' => $event,
|
||||
]);
|
||||
return redirect()->back()->with('warning', ucfirst(Setting::getSettings()->webhook_selected) .trans('admin/settings/message.webhook.webhook_fail'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a checkout acceptance
|
||||
@@ -247,13 +236,13 @@ class CheckoutableListener
|
||||
$acceptance->assignedTo()->associate($event->checkedOutTo);
|
||||
$acceptance->save();
|
||||
|
||||
return $acceptance;
|
||||
return $acceptance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the appropriate notification for the event
|
||||
*
|
||||
* @param CheckoutableCheckedIn $event
|
||||
*
|
||||
* @param CheckoutableCheckedIn $event
|
||||
* @return Notification
|
||||
*/
|
||||
private function getCheckinNotification($event)
|
||||
@@ -267,7 +256,7 @@ class CheckoutableListener
|
||||
break;
|
||||
case Asset::class:
|
||||
$notificationClass = CheckinAssetNotification::class;
|
||||
break;
|
||||
break;
|
||||
case LicenseSeat::class:
|
||||
$notificationClass = CheckinLicenseSeatNotification::class;
|
||||
break;
|
||||
@@ -275,9 +264,8 @@ class CheckoutableListener
|
||||
|
||||
Log::debug('Notification class: '.$notificationClass);
|
||||
|
||||
return new $notificationClass($event->checkoutable, $event->checkedOutTo, $event->checkedInBy, $event->note);
|
||||
return new $notificationClass($event->checkoutable, $event->checkedOutTo, $event->checkedInBy, $event->note);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the appropriate notification for the event
|
||||
*
|
||||
@@ -319,6 +307,7 @@ class CheckoutableListener
|
||||
return new $mailable($event->checkoutable, $event->checkedOutTo, $event->checkedOutBy, $acceptance, $event->note);
|
||||
|
||||
}
|
||||
|
||||
private function getCheckinMailType($event){
|
||||
$lookup = [
|
||||
Accessory::class => CheckinAccessoryMail::class,
|
||||
@@ -340,7 +329,8 @@ class CheckoutableListener
|
||||
* @param $event
|
||||
* @return mixed
|
||||
*/
|
||||
private function getNotifiableUsers($event){
|
||||
private function getNotifiableUser($event)
|
||||
{
|
||||
|
||||
// If it's assigned to an asset, get that asset's assignedTo object
|
||||
if ($event->checkedOutTo instanceof Asset){
|
||||
@@ -356,6 +346,7 @@ class CheckoutableListener
|
||||
return $event->checkedOutTo;
|
||||
}
|
||||
}
|
||||
|
||||
private function webhookSelected(){
|
||||
if(Setting::getSettings()->webhook_selected === 'slack' || Setting::getSettings()->webhook_selected === 'general'){
|
||||
return 'slack';
|
||||
@@ -364,32 +355,13 @@ class CheckoutableListener
|
||||
return Setting::getSettings()->webhook_selected;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the listeners for the subscriber.
|
||||
*
|
||||
* @param Illuminate\Events\Dispatcher $events
|
||||
*/
|
||||
public function subscribe($events)
|
||||
{
|
||||
$events->listen(
|
||||
\App\Events\CheckoutableCheckedIn::class,
|
||||
'App\Listeners\CheckoutableListener@onCheckedIn'
|
||||
);
|
||||
|
||||
$events->listen(
|
||||
\App\Events\CheckoutableCheckedOut::class,
|
||||
'App\Listeners\CheckoutableListener@onCheckedOut'
|
||||
);
|
||||
}
|
||||
|
||||
private function shouldNotSendAnyNotifications($checkoutable): bool
|
||||
{
|
||||
if(in_array(get_class($checkoutable), $this->skipNotificationsFor)) {
|
||||
return true;
|
||||
}
|
||||
if ($this->shouldSendWebhookNotification()) {
|
||||
return false;
|
||||
}
|
||||
return in_array(get_class($checkoutable), $this->skipNotificationsFor);
|
||||
}
|
||||
|
||||
private function shouldSendEmailNotifications(Model $checkoutable): bool
|
||||
{
|
||||
//runs a check if the category wants to send checkin/checkout emails to users
|
||||
$category = match (true) {
|
||||
$checkoutable instanceof Asset => $checkoutable->model->category,
|
||||
@@ -400,27 +372,93 @@ class CheckoutableListener
|
||||
};
|
||||
|
||||
if (!$category?->checkin_email) {
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private function shouldSendWebhookNotification(): bool
|
||||
{
|
||||
return Setting::getSettings() && Setting::getSettings()->webhook_endpoint;
|
||||
}
|
||||
|
||||
private function checkoutableShouldSendEmail($event): bool
|
||||
private function checkoutableCategoryShouldSendEmail(Model $checkoutable): bool
|
||||
{
|
||||
if($event->checkoutable instanceof LicenseSeat){
|
||||
return $event->checkoutable->license->checkin_email();
|
||||
if ($checkoutable instanceof LicenseSeat) {
|
||||
return $checkoutable->license->checkin_email();
|
||||
}
|
||||
return (method_exists($event->checkoutable, 'checkin_email') && $event->checkoutable->checkin_email());
|
||||
return (method_exists($checkoutable, 'checkin_email') && $checkoutable->checkin_email());
|
||||
}
|
||||
|
||||
private function newMicrosoftTeamsWebhookEnabled(): bool
|
||||
{
|
||||
return Setting::getSettings()->webhook_selected === 'microsoft' && Str::contains(Setting::getSettings()->webhook_endpoint, 'workflows');
|
||||
}
|
||||
|
||||
private function shouldSendCheckoutEmailToUser(Model $checkoutable): bool
|
||||
{
|
||||
/**
|
||||
* Send an email if any of the following conditions are met:
|
||||
* 1. The asset requires acceptance
|
||||
* 2. The item has a EULA
|
||||
* 3. The item should send an email at check-in/check-out
|
||||
*/
|
||||
|
||||
if ($checkoutable->requireAcceptance()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($checkoutable->getEula()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($this->checkoutableCategoryShouldSendEmail($checkoutable)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function shouldSendEmailToAlertAddress(): bool
|
||||
{
|
||||
return Setting::getSettings() && Setting::getSettings()->admin_cc_email;
|
||||
}
|
||||
|
||||
private function getFormattedAlertAddresses(): array
|
||||
{
|
||||
$alertAddresses = Setting::getSettings()->admin_cc_email;
|
||||
|
||||
if ($alertAddresses !== '') {
|
||||
return array_filter(array_map('trim', explode(',', $alertAddresses)));
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
private function generateEmailRecipients(
|
||||
bool $shouldSendEmailToUser,
|
||||
bool $shouldSendEmailToAlertAddress,
|
||||
mixed $notifiable
|
||||
): array {
|
||||
$to = [];
|
||||
$cc = [];
|
||||
|
||||
// if user && cc: to user, cc admin
|
||||
if ($shouldSendEmailToUser && $shouldSendEmailToAlertAddress) {
|
||||
$to[] = $notifiable;
|
||||
$cc[] = $this->getFormattedAlertAddresses();
|
||||
}
|
||||
|
||||
// if user && no cc: to user
|
||||
if ($shouldSendEmailToUser && !$shouldSendEmailToAlertAddress) {
|
||||
$to[] = $notifiable;
|
||||
}
|
||||
|
||||
// if no user && cc: to admin
|
||||
if (!$shouldSendEmailToUser && $shouldSendEmailToAlertAddress) {
|
||||
$to[] = $this->getFormattedAlertAddresses();
|
||||
}
|
||||
|
||||
return array($to, $cc);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ class CategoryFactory extends Factory
|
||||
'checkin_email' => true,
|
||||
'eula_text' => $this->faker->paragraph(),
|
||||
'require_acceptance' => false,
|
||||
'use_default_eula' => $this->faker->boolean(),
|
||||
'use_default_eula' => false,
|
||||
'created_by' => User::factory()->superuser(),
|
||||
'notes' => 'Created by DB seeder',
|
||||
];
|
||||
|
||||
@@ -4,6 +4,8 @@ namespace Tests\Feature\Notifications\Email;
|
||||
|
||||
use App\Mail\CheckinAssetMail;
|
||||
use App\Models\Accessory;
|
||||
use App\Models\AssetModel;
|
||||
use App\Models\Category;
|
||||
use App\Models\Consumable;
|
||||
use App\Models\LicenseSeat;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
@@ -11,8 +13,6 @@ use PHPUnit\Framework\Attributes\Group;
|
||||
use App\Events\CheckoutableCheckedIn;
|
||||
use App\Models\Asset;
|
||||
use App\Models\User;
|
||||
use App\Notifications\CheckinAssetNotification;
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
use Tests\TestCase;
|
||||
|
||||
#[Group('notifications')]
|
||||
@@ -25,10 +25,8 @@ class EmailNotificationsUponCheckinTest extends TestCase
|
||||
Mail::fake();
|
||||
}
|
||||
|
||||
public function testCheckInEmailSentToUserIfSettingEnabled()
|
||||
public function test_check_in_email_sent_to_user_if_setting_enabled()
|
||||
{
|
||||
Mail::fake();
|
||||
|
||||
$user = User::factory()->create();
|
||||
$asset = Asset::factory()->assignedToUser($user)->create();
|
||||
|
||||
@@ -39,13 +37,11 @@ class EmailNotificationsUponCheckinTest extends TestCase
|
||||
Mail::assertSent(CheckinAssetMail::class, function($mail) use ($user) {
|
||||
return $mail->hasTo($user->email);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public function testCheckInEmailNotSentToUserIfSettingDisabled()
|
||||
public function test_check_in_email_not_sent_to_user_if_setting_disabled()
|
||||
{
|
||||
$this->settings->disableAdminCC();
|
||||
Mail::fake();
|
||||
|
||||
$user = User::factory()->create();
|
||||
$checkoutables = collect([
|
||||
@@ -93,6 +89,69 @@ class EmailNotificationsUponCheckinTest extends TestCase
|
||||
});
|
||||
}
|
||||
|
||||
public function test_handles_user_not_having_email_address_set()
|
||||
{
|
||||
$user = User::factory()->create(['email' => null]);
|
||||
$asset = Asset::factory()->assignedToUser($user)->create();
|
||||
|
||||
$asset->model->category->update(['checkin_email' => true]);
|
||||
|
||||
$this->fireCheckInEvent($asset, $user);
|
||||
|
||||
Mail::assertNothingSent();
|
||||
}
|
||||
|
||||
public function test_admin_alert_email_sends()
|
||||
{
|
||||
$this->settings->enableAdminCC('cc@example.com');
|
||||
|
||||
$user = User::factory()->create();
|
||||
$asset = Asset::factory()->assignedToUser($user)->create();
|
||||
|
||||
$asset->model->category->update(['checkin_email' => true]);
|
||||
|
||||
$this->fireCheckInEvent($asset, $user);
|
||||
|
||||
Mail::assertSent(CheckinAssetMail::class, function ($mail) use ($user) {
|
||||
return $mail->hasTo($user->email) && $mail->hasCc('cc@example.com');
|
||||
});
|
||||
}
|
||||
|
||||
public function test_admin_alert_email_still_sent_when_category_email_is_not_set_to_send_email_to_user()
|
||||
{
|
||||
$this->settings->enableAdminCC('cc@example.com');
|
||||
|
||||
$category = Category::factory()->create([
|
||||
'checkin_email' => false,
|
||||
'eula_text' => null,
|
||||
'use_default_eula' => false,
|
||||
]);
|
||||
$assetModel = AssetModel::factory()->create(['category_id' => $category->id]);
|
||||
$asset = Asset::factory()->create(['model_id' => $assetModel->id]);
|
||||
|
||||
$this->fireCheckInEvent($asset, User::factory()->create());
|
||||
|
||||
Mail::assertSent(CheckinAssetMail::class, function ($mail) {
|
||||
return $mail->hasTo('cc@example.com');
|
||||
});
|
||||
}
|
||||
|
||||
public function test_admin_alert_email_still_sent_when_user_has_no_email_address()
|
||||
{
|
||||
$this->settings->enableAdminCC('cc@example.com');
|
||||
|
||||
$user = User::factory()->create(['email' => null]);
|
||||
$asset = Asset::factory()->assignedToUser($user)->create();
|
||||
|
||||
$asset->model->category->update(['checkin_email' => true]);
|
||||
|
||||
$this->fireCheckInEvent($asset, $user);
|
||||
|
||||
Mail::assertSent(CheckinAssetMail::class, function ($mail) {
|
||||
return $mail->hasTo('cc@example.com');
|
||||
});
|
||||
}
|
||||
|
||||
private function fireCheckInEvent($asset, $user): void
|
||||
{
|
||||
event(new CheckoutableCheckedIn(
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature\Notifications\Email;
|
||||
|
||||
use App\Events\CheckoutableCheckedOut;
|
||||
use App\Mail\CheckoutAssetMail;
|
||||
use App\Models\Asset;
|
||||
use App\Models\AssetModel;
|
||||
use App\Models\Category;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use PHPUnit\Framework\Attributes\Group;
|
||||
use Tests\TestCase;
|
||||
|
||||
#[Group('notifications')]
|
||||
class EmailNotificationsUponCheckoutTest extends TestCase
|
||||
{
|
||||
private Asset $asset;
|
||||
private AssetModel $assetModel;
|
||||
private Category $category;
|
||||
private User $user;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
Mail::fake();
|
||||
|
||||
$this->category = Category::factory()->create([
|
||||
'checkin_email' => false,
|
||||
'eula_text' => null,
|
||||
'require_acceptance' => false,
|
||||
'use_default_eula' => false,
|
||||
]);
|
||||
|
||||
$this->assetModel = AssetModel::factory()->for($this->category)->create();
|
||||
$this->asset = Asset::factory()->for($this->assetModel, 'model')->create();
|
||||
|
||||
$this->user = User::factory()->create();
|
||||
}
|
||||
|
||||
public function test_email_sent_to_user_when_category_requires_acceptance()
|
||||
{
|
||||
$this->category->update(['require_acceptance' => true]);
|
||||
|
||||
$this->fireCheckoutEvent();
|
||||
|
||||
$this->assertUserSentEmail();
|
||||
}
|
||||
|
||||
public function test_email_sent_to_user_when_category_using_default_eula()
|
||||
{
|
||||
$this->settings->setEula();
|
||||
|
||||
$this->category->update(['use_default_eula' => true]);
|
||||
|
||||
$this->fireCheckoutEvent();
|
||||
|
||||
$this->assertUserSentEmail();
|
||||
}
|
||||
|
||||
public function test_email_sent_to_user_when_category_using_local_eula()
|
||||
{
|
||||
$this->category->update(['eula_text' => 'Some EULA text']);
|
||||
|
||||
$this->fireCheckoutEvent();
|
||||
|
||||
$this->assertUserSentEmail();
|
||||
}
|
||||
|
||||
public function test_email_sent_to_user_when_category_set_to_explicitly_send_email()
|
||||
{
|
||||
$this->category->update(['checkin_email' => true]);
|
||||
|
||||
$this->fireCheckoutEvent();
|
||||
|
||||
$this->assertUserSentEmail();
|
||||
}
|
||||
|
||||
public function test_handles_user_not_having_email_address_set()
|
||||
{
|
||||
$this->category->update(['checkin_email' => true]);
|
||||
$this->user->update(['email' => null]);
|
||||
|
||||
$this->fireCheckoutEvent();
|
||||
|
||||
Mail::assertNothingSent();
|
||||
}
|
||||
|
||||
public function test_admin_alert_email_sends()
|
||||
{
|
||||
$this->settings->enableAdminCC('cc@example.com');
|
||||
|
||||
$this->category->update(['checkin_email' => true]);
|
||||
|
||||
$this->fireCheckoutEvent();
|
||||
|
||||
Mail::assertSent(CheckoutAssetMail::class, function (CheckoutAssetMail $mail) {
|
||||
return $mail->hasCc('cc@example.com');
|
||||
});
|
||||
}
|
||||
|
||||
public function test_admin_alert_email_still_sent_when_category_is_not_set_to_send_email_to_user()
|
||||
{
|
||||
$this->settings->enableAdminCC('cc@example.com');
|
||||
|
||||
$this->fireCheckoutEvent();
|
||||
|
||||
Mail::assertSent(CheckoutAssetMail::class, function ($mail) {
|
||||
return $mail->hasTo('cc@example.com');
|
||||
});
|
||||
}
|
||||
|
||||
public function test_admin_alert_email_still_sent_when_user_has_no_email_address()
|
||||
{
|
||||
$this->settings->enableAdminCC('cc@example.com');
|
||||
|
||||
$this->category->update(['checkin_email' => true]);
|
||||
$this->user->update(['email' => null]);
|
||||
|
||||
$this->fireCheckoutEvent();
|
||||
|
||||
Mail::assertSent(CheckoutAssetMail::class, function ($mail) {
|
||||
return $mail->hasTo('cc@example.com');
|
||||
});
|
||||
}
|
||||
|
||||
private function fireCheckoutEvent(): void
|
||||
{
|
||||
event(new CheckoutableCheckedOut(
|
||||
$this->asset,
|
||||
$this->user,
|
||||
User::factory()->superuser()->create(),
|
||||
'',
|
||||
));
|
||||
}
|
||||
|
||||
private function assertUserSentEmail(): void
|
||||
{
|
||||
Mail::assertSent(CheckoutAssetMail::class, function (CheckoutAssetMail $mail) {
|
||||
return $mail->hasTo($this->user->email);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
namespace Tests\Feature\Notifications\Webhooks;
|
||||
|
||||
use App\Models\AssetModel;
|
||||
use App\Models\Category;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\Attributes\Group;
|
||||
use App\Events\CheckoutableCheckedIn;
|
||||
@@ -95,6 +97,27 @@ class SlackNotificationsUponCheckinTest extends TestCase
|
||||
$this->assertNoSlackNotificationSent(CheckinAssetNotification::class);
|
||||
}
|
||||
|
||||
public function testSlackNotificationIsStillSentWhenCategoryEmailIsNotSetToSendEmails()
|
||||
{
|
||||
$this->settings->enableSlackWebhook();
|
||||
|
||||
$category = Category::factory()->create([
|
||||
'checkin_email' => false,
|
||||
'eula_text' => null,
|
||||
'require_acceptance' => false,
|
||||
'use_default_eula' => false,
|
||||
]);
|
||||
$assetModel = AssetModel::factory()->for($category)->create();
|
||||
$asset = Asset::factory()->for($assetModel, 'model')->assignedToUser()->create();
|
||||
|
||||
$this->fireCheckInEvent(
|
||||
$asset,
|
||||
User::factory()->create(),
|
||||
);
|
||||
|
||||
$this->assertSlackNotificationSent(CheckinAssetNotification::class);
|
||||
}
|
||||
|
||||
public function testComponentCheckinDoesNotSendSlackNotification()
|
||||
{
|
||||
$this->settings->enableSlackWebhook();
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
namespace Tests\Feature\Notifications\Webhooks;
|
||||
|
||||
use App\Models\AssetModel;
|
||||
use App\Models\Category;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\Attributes\Group;
|
||||
use App\Events\CheckoutableCheckedOut;
|
||||
@@ -97,6 +99,27 @@ class SlackNotificationsUponCheckoutTest extends TestCase
|
||||
$this->assertNoSlackNotificationSent(CheckoutAssetNotification::class);
|
||||
}
|
||||
|
||||
public function testSlackNotificationIsStillSentWhenCategoryEmailIsNotSetToSendEmails()
|
||||
{
|
||||
$this->settings->enableSlackWebhook();
|
||||
|
||||
$category = Category::factory()->create([
|
||||
'checkin_email' => false,
|
||||
'eula_text' => null,
|
||||
'require_acceptance' => false,
|
||||
'use_default_eula' => false,
|
||||
]);
|
||||
$assetModel = AssetModel::factory()->for($category)->create();
|
||||
$asset = Asset::factory()->for($assetModel, 'model')->create();
|
||||
|
||||
$this->fireCheckOutEvent(
|
||||
$asset,
|
||||
User::factory()->create(),
|
||||
);
|
||||
|
||||
$this->assertSlackNotificationSent(CheckoutAssetNotification::class);
|
||||
}
|
||||
|
||||
public function testComponentCheckoutDoesNotSendSlackNotification()
|
||||
{
|
||||
$this->settings->enableSlackWebhook();
|
||||
|
||||
@@ -46,6 +46,13 @@ class Settings
|
||||
]);
|
||||
}
|
||||
|
||||
public function enableAdminCC(string $email = 'cc@example.co'): Settings
|
||||
{
|
||||
return $this->update([
|
||||
'admin_cc_email' => $email,
|
||||
]);
|
||||
}
|
||||
|
||||
public function disableAdminCC(): Settings
|
||||
{
|
||||
return $this->update([
|
||||
|
||||
Reference in New Issue
Block a user