3
0
mirror of https://github.com/snipe/snipe-it.git synced 2026-02-05 11:15:33 +00:00

Merge pull request #17391 from Godmartinz/add-components-notifications

FIXED: #13844 Adds Webhook and Mail Notifications for Components
This commit is contained in:
snipe
2025-07-21 11:51:30 +01:00
committed by GitHub
13 changed files with 715 additions and 30 deletions

View File

@ -4,10 +4,12 @@ namespace App\Listeners;
use App\Events\CheckoutableCheckedOut;
use App\Mail\CheckinAccessoryMail;
use App\Mail\CheckinComponentMail;
use App\Mail\CheckinLicenseMail;
use App\Mail\CheckoutAccessoryMail;
use App\Mail\CheckoutAssetMail;
use App\Mail\CheckinAssetMail;
use App\Mail\CheckoutComponentMail;
use App\Mail\CheckoutConsumableMail;
use App\Mail\CheckoutLicenseMail;
use App\Models\Accessory;
@ -22,9 +24,11 @@ use App\Models\Setting;
use App\Models\User;
use App\Notifications\CheckinAccessoryNotification;
use App\Notifications\CheckinAssetNotification;
use App\Notifications\CheckinComponentNotification;
use App\Notifications\CheckinLicenseSeatNotification;
use App\Notifications\CheckoutAccessoryNotification;
use App\Notifications\CheckoutAssetNotification;
use App\Notifications\CheckoutComponentNotification;
use App\Notifications\CheckoutConsumableNotification;
use App\Notifications\CheckoutLicenseSeatNotification;
use GuzzleHttp\Exception\ClientException;
@ -39,7 +43,7 @@ use Osama\LaravelTeamsNotification\TeamsNotification;
class CheckoutableListener
{
private array $skipNotificationsFor = [
Component::class,
// Component::class,
];
/**
@ -145,7 +149,6 @@ class CheckoutableListener
$shouldSendEmailToUser = $this->checkoutableCategoryShouldSendEmail($event->checkoutable);
$shouldSendEmailToAlertAddress = $this->shouldSendEmailToAlertAddress();
$shouldSendWebhookNotification = $this->shouldSendWebhookNotification();
if (!$shouldSendEmailToUser && !$shouldSendEmailToAlertAddress && !$shouldSendWebhookNotification) {
return;
}
@ -269,6 +272,9 @@ class CheckoutableListener
case LicenseSeat::class:
$notificationClass = CheckinLicenseSeatNotification::class;
break;
case Component::class:
$notificationClass = CheckinComponentNotification::class;
break;
}
Log::debug('Notification class: '.$notificationClass);
@ -299,6 +305,9 @@ class CheckoutableListener
case LicenseSeat::class:
$notificationClass = CheckoutLicenseSeatNotification::class;
break;
case Component::class:
$notificationClass = CheckoutComponentNotification::class;
break;
}
@ -310,6 +319,7 @@ class CheckoutableListener
Asset::class => CheckoutAssetMail::class,
LicenseSeat::class => CheckoutLicenseMail::class,
Consumable::class => CheckoutConsumableMail::class,
Component::class => CheckoutComponentMail::class,
];
$mailable= $lookup[get_class($event->checkoutable)];
@ -322,8 +332,8 @@ class CheckoutableListener
Accessory::class => CheckinAccessoryMail::class,
Asset::class => CheckinAssetMail::class,
LicenseSeat::class => CheckinLicenseMail::class,
Component::class => CheckinComponentMail::class,
];
$mailable= $lookup[get_class($event->checkoutable)];
return new $mailable($event->checkoutable, $event->checkedOutTo, $event->checkedInBy, $event->note);
@ -469,7 +479,8 @@ class CheckoutableListener
return match (true) {
$checkoutable instanceof Asset => $checkoutable->model->category,
$checkoutable instanceof Accessory,
$checkoutable instanceof Consumable => $checkoutable->category,
$checkoutable instanceof Consumable,
$checkoutable instanceof Component => $checkoutable->category,
$checkoutable instanceof LicenseSeat => $checkoutable->license->category,
};
}

View File

@ -0,0 +1,71 @@
<?php
namespace App\Mail;
use App\Models\Accessory;
use App\Models\Component;
use App\Models\Setting;
use App\Models\User;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Address;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;
class CheckinComponentMail extends Mailable
{
use Queueable, SerializesModels;
/**
* Create a new message instance.
*/
public function __construct(Component $component, $checkedOutTo, User $checkedInby, $note)
{
$this->item = $component;
$this->target = $checkedOutTo;
$this->admin = $checkedInby;
$this->note = $note;
$this->settings = Setting::getSettings();
}
/**
* Get the message envelope.
*/
public function envelope(): Envelope
{
$from = new Address(config('mail.from.address'), config('mail.from.name'));
return new Envelope(
from: $from,
subject: trans('mail.Confirm_component_checkin'),
);
}
/**
* Get the message content definition.
*/
public function content(): Content
{
return new Content(
markdown: 'mail.markdown.checkin-component',
with: [
'item' => $this->item,
'admin' => $this->admin,
'note' => $this->note,
'target' => $this->target,
]
);
}
/**
* Get the attachments for the message.
*
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
*/
public function attachments(): array
{
return [];
}
}

View File

@ -0,0 +1,82 @@
<?php
namespace App\Mail;
use App\Models\Component;
use App\Models\Setting;
use App\Models\User;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Address;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;
class CheckoutComponentMail extends Mailable
{
use Queueable, SerializesModels;
/**
* Create a new message instance.
*/
public function __construct(Component $component, $checkedOutTo, User $checkedOutBy, $acceptance, $note)
{
$this->item = $component;
$this->admin = $checkedOutBy;
$this->note = $note;
$this->target = $checkedOutTo;
$this->acceptance = $acceptance;
$this->qty = $component->assets->first()?->pivot?->assigned_qty;
$this->settings = Setting::getSettings();
}
/**
* Get the message envelope.
*/
public function envelope(): Envelope
{
$from = new Address(config('mail.from.address'), config('mail.from.name'));
return new Envelope(
from: $from,
subject: trans('mail.Confirm_component_delivery'),
);
}
/**
* Get the message content definition.
*/
public function content(): Content
{
$eula = $this->item->getEula();
$req_accept = $this->item->requireAcceptance();
$accept_url = is_null($this->acceptance) ? null : route('account.accept.item', $this->acceptance);
return new Content(
markdown: 'mail.markdown.checkout-component',
with: [
'item' => $this->item,
'admin' => $this->admin,
'note' => $this->note,
'target' => $this->target,
'eula' => $eula,
'req_accept' => $req_accept,
'accept_url' => $accept_url,
'qty' => $this->qty,
]
);
}
/**
* Get the attachments for the message.
*
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
*/
public function attachments(): array
{
return [];
}
}

View File

@ -2,12 +2,14 @@
namespace App\Models;
use App\Helpers\Helper;
use App\Models\Traits\HasUploads;
use App\Models\Traits\Searchable;
use App\Presenters\Presentable;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Storage;
use Watson\Validating\ValidatingTrait;
/**
@ -203,6 +205,36 @@ class Component extends SnipeModel
{
return $this->belongsTo(\App\Models\Manufacturer::class, 'manufacturer_id');
}
/**
* Determine whether this asset requires acceptance by the assigned user
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @return bool
*/
public function requireAcceptance()
{
return $this->category->require_acceptance;
}
/**
* Checks for a category-specific EULA, and if that doesn't exist,
* checks for a settings level EULA
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @return string | false
*/
public function getEula()
{
if ($this->category->eula_text) {
return Helper::parseEscapedMarkedown($this->category->eula_text);
} elseif ((Setting::getSettings()->default_eula_text) && ($this->category->use_default_eula == '1')) {
return Helper::parseEscapedMarkedown(Setting::getSettings()->default_eula_text);
} else {
return null;
}
}
/**
* Establishes the component -> action logs relationship
@ -248,6 +280,19 @@ class Component extends SnipeModel
}
/**
* Determine whether to send a checkin/checkout email based on
* asset model category
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @return bool
*/
public function checkin_email()
{
return $this->category?->checkin_email;
}
/**
* Check how many items within a component are remaining

View File

@ -0,0 +1,165 @@
<?php
namespace App\Notifications;
use App\Models\Component;
use App\Models\Setting;
use App\Models\User;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Channels\SlackWebhookChannel;
use Illuminate\Notifications\Messages\SlackMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Support\Str;
use NotificationChannels\GoogleChat\Card;
use NotificationChannels\GoogleChat\GoogleChatChannel;
use NotificationChannels\GoogleChat\GoogleChatMessage;
use NotificationChannels\GoogleChat\Section;
use NotificationChannels\GoogleChat\Widgets\KeyValue;
use NotificationChannels\MicrosoftTeams\MicrosoftTeamsChannel;
use NotificationChannels\MicrosoftTeams\MicrosoftTeamsMessage;
class CheckinComponentNotification extends Notification
{
use Queueable;
/**
* @var
*/
private $params;
/**
* Create a new notification instance.
*
* @param $params
*/
public function __construct(Component $component, $checkedOutTo, User $checkedInBy, $note)
{
$this->target = $checkedOutTo;
$this->item = $component;
$this->admin = $checkedInBy;
$this->note = $note;
$this->settings = Setting::getSettings();
}
/**
* Get the notification's delivery channels.
*
* @return array
*/
public function via()
{
$notifyBy = [];
if (Setting::getSettings()->webhook_selected == 'google' && Setting::getSettings()->webhook_endpoint) {
$notifyBy[] = GoogleChatChannel::class;
}
if (Setting::getSettings()->webhook_selected == 'microsoft' && Setting::getSettings()->webhook_endpoint) {
$notifyBy[] = MicrosoftTeamsChannel::class;
}
if (Setting::getSettings()->webhook_selected == 'slack' || Setting::getSettings()->webhook_selected == 'general' ) {
$notifyBy[] = SlackWebhookChannel::class;
}
return $notifyBy;
}
public function toSlack()
{
$target = $this->target;
$admin = $this->admin;
$item = $this->item;
$note = $this->note;
$botname = ($this->settings->webhook_botname) ? $this->settings->webhook_botname : 'Snipe-Bot';
$channel = ($this->settings->webhook_channel) ? $this->settings->webhook_channel : '';
if ($admin) {
$fields = [
trans('general.from') => '<'.$target->present()->viewUrl().'|'.$target->present()->fullName().'>',
trans('general.by') => '<'.$admin->present()->viewUrl().'|'.$admin->present()->fullName().'>',
];
if ($item->location) {
$fields[trans('general.location')] = $item->location->name;
}
if ($item->company) {
$fields[trans('general.company')] = $item->company->name;
}
} else {
$fields = [
'To' => '<'.$target->present()->viewUrl().'|'.$target->present()->fullName().'>',
'By' => 'CLI tool',
];
}
return (new SlackMessage)
->content(':arrow_down: :package: '.trans('mail.Component_checkin_notification'))
->from($botname)
->to($channel)
->attachment(function ($attachment) use ($item, $note, $admin, $fields) {
$attachment->title(htmlspecialchars_decode($item->present()->name), $item->present()->viewUrl())
->fields($fields)
->content($note);
});
}
public function toMicrosoftTeams()
{
$target = $this->target;
$admin = $this->admin;
$item = $this->item;
$note = $this->note;
if(!Str::contains(Setting::getSettings()->webhook_endpoint, 'workflows')) {
return MicrosoftTeamsMessage::create()
->to($this->settings->webhook_endpoint)
->type('success')
->addStartGroupToSection('activityTitle')
->title(trans('mail.Component_checkin_notification'))
->addStartGroupToSection('activityText')
->fact(htmlspecialchars_decode($item->present()->name), '', 'header')
->fact(trans('mail.Component_checkin_notification')." by ", $admin->present()->fullName() ?: 'CLI tool')
->fact(trans('mail.checkedin_from'), $target->present()->fullName())
->fact(trans('admin/consumables/general.remaining'), $item->numRemaining())
->fact(trans('mail.notes'), $note ?: '');
}
$message = trans('mail.Component_checkin_notification');
$details = [
trans('mail.checkedin_from')=> $target->present()->fullName(),
trans('mail.Component_checkin_notification')." by " => $admin->present()->fullName() ?: 'CLI tool',
trans('admin/consumables/general.remaining') => $item->numRemaining(),
trans('mail.notes') => $note ?: '',
];
return array($message, $details);
}
public function toGoogleChat()
{
$target = $this->target;
$item = $this->item;
$note = $this->note;
return GoogleChatMessage::create()
->to($this->settings->webhook_endpoint)
->card(
Card::create()
->header(
'<strong>'.trans('mail.Component_checkin_notification').'</strong>' ?: '',
htmlspecialchars_decode($item->present()->name) ?: '',
)
->section(
Section::create(
KeyValue::create(
trans('mail.checkedin_from') ?: '',
$target->present()->fullName() ?: '',
trans('admin/consumables/general.remaining').': '.$item->numRemaining(),
)
->onClick(route('components.show', $item->id))
)
)
);
}
}

View File

@ -0,0 +1,164 @@
<?php
namespace App\Notifications;
use App\Models\Component;
use App\Models\Setting;
use App\Models\User;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Channels\SlackWebhookChannel;
use Illuminate\Notifications\Messages\SlackMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Support\Str;
use NotificationChannels\GoogleChat\Card;
use NotificationChannels\GoogleChat\GoogleChatChannel;
use NotificationChannels\GoogleChat\GoogleChatMessage;
use NotificationChannels\GoogleChat\Section;
use NotificationChannels\GoogleChat\Widgets\KeyValue;
use NotificationChannels\MicrosoftTeams\MicrosoftTeamsChannel;
use NotificationChannels\MicrosoftTeams\MicrosoftTeamsMessage;
class CheckoutComponentNotification extends Notification
{
use Queueable;
/**
* @var
*/
private $params;
/**
* Create a new notification instance.
*
* @param $params
*/
public function __construct(Component $component, $checkedOutTo, User $checkedOutBy, $acceptance, $note)
{
$this->item = $component;
$this->admin = $checkedOutBy;
$this->note = $note;
$this->target = $checkedOutTo;
$this->acceptance = $acceptance;
$this->qty = $component->checkout_qty;
$this->settings = Setting::getSettings();
}
/**`
* Get the notification's delivery channels.
*
* @return array
*/
public function via()
{
$notifyBy = [];
if (Setting::getSettings()->webhook_selected == 'google' && Setting::getSettings()->webhook_endpoint) {
$notifyBy[] = GoogleChatChannel::class;
}
if (Setting::getSettings()->webhook_selected == 'microsoft' && Setting::getSettings()->webhook_endpoint) {
$notifyBy[] = MicrosoftTeamsChannel::class;
}
if (Setting::getSettings()->webhook_selected == 'slack' || Setting::getSettings()->webhook_selected == 'general' ) {
$notifyBy[] = SlackWebhookChannel::class;
}
return $notifyBy;
}
public function toSlack()
{
$target = $this->target;
$admin = $this->admin;
$item = $this->item;
$note = $this->note;
$botname = ($this->settings->webhook_botname) ? $this->settings->webhook_botname : 'Snipe-Bot';
$channel = ($this->settings->webhook_channel) ? $this->settings->webhook_channel : '';
$fields = [
trans('general.to') => '<'.$target->present()->viewUrl().'|'.$target->present()->fullName().'>',
trans('general.by') => '<'.$admin->present()->viewUrl().'|'.$admin->present()->fullName().'>',
];
if ($item->location) {
$fields[trans('general.location')] = $item->location->name;
}
if ($item->company) {
$fields[trans('general.company')] = $item->company->name;
}
return (new SlackMessage)
->content(':arrow_up: :package: '.trans('mail.Component_checkout_notification'))
->from($botname)
->to($channel)
->attachment(function ($attachment) use ($item, $note, $admin, $fields) {
$attachment->title(htmlspecialchars_decode($item->present()->name), $item->present()->viewUrl())
->fields($fields)
->content($note);
});
}
public function toMicrosoftTeams()
{
$target = $this->target;
$admin = $this->admin;
$item = $this->item;
$note = $this->note;
if(!Str::contains(Setting::getSettings()->webhook_endpoint, 'workflows')) {
return MicrosoftTeamsMessage::create()
->to($this->settings->webhook_endpoint)
->type('success')
->addStartGroupToSection('activityTitle')
->title(trans('mail.Component_checkout_notification'))
->addStartGroupToSection('activityText')
->fact(htmlspecialchars_decode($item->present()->name), '', 'activityTitle')
->fact(trans('mail.Component_checkout_notification')." by ", $admin->present()->fullName())
->fact(trans('mail.assigned_to'), $target->present()->fullName())
->fact(trans('admin/consumables/general.remaining'), $item->numRemaining())
->fact(trans('mail.notes'), $note ?: '');
}
$message = trans('mail.Component_checkout_notification');
$details = [
trans('mail.assigned_to') => $target->present()->fullName(),
trans('mail.item') => htmlspecialchars_decode($item->present()->name),
trans('mail.Component_checkout_notification').' by' => $admin->present()->fullName(),
trans('admin/consumables/general.remaining') => $item->numRemaining(),
trans('mail.notes') => $note ?: '',
];
return array($message, $details);
}
public function toGoogleChat()
{
$target = $this->target;
$item = $this->item;
$note = $this->note;
return GoogleChatMessage::create()
->to($this->settings->webhook_endpoint)
->card(
Card::create()
->header(
'<strong>'.trans('mail.Component_checkout_notification').'</strong>' ?: '',
htmlspecialchars_decode($item->present()->name) ?: '',
)
->section(
Section::create(
KeyValue::create(
trans('mail.assigned_to') ?: '',
$target->present()->fullName() ?: '',
trans('admin/consumables/general.remaining').': '.$item->numRemaining(),
)
->onClick(route('api.assets.show', $target->id))
)
)
);
}
}

View File

@ -8,11 +8,15 @@ return [
'Asset_Checkout_Notification' => 'Asset checked out',
'Confirm_Accessory_Checkin' => 'Accessory checkin confirmation',
'Confirm_Asset_Checkin' => 'Asset checkin confirmation',
'Confirm_component_checkin' => 'Component checkin confirmation',
'Confirm_accessory_delivery' => 'Accessory delivery confirmation',
'Confirm_asset_delivery' => 'Asset delivery confirmation',
'Confirm_consumable_delivery' => 'Consumable delivery confirmation',
'Confirm_component_delivery' => 'Component delivery confirmation',
'Confirm_license_delivery' => 'License delivery confirmation',
'Consumable_checkout_notification' => 'Consumable checked out',
'Component_checkout_notification' => 'Component checked out',
'Component_checkin_notification' => 'Component checked in',
'Days' => 'Days',
'Expected_Checkin_Date' => 'An asset checked out to you is due to be checked back in on :date',
'Expected_Checkin_Notification' => 'Reminder: :name checkin deadline approaching',

View File

@ -41,7 +41,31 @@
</div>
@endif
</div>
@if ($component->requireAcceptance() || $component->getEula() || ($snipeSettings->webhook_endpoint!=''))
<div class="form-group notification-callout">
<div class="col-md-8 col-md-offset-3">
<div class="callout callout-info">
@if ($component->category->require_acceptance=='1')
<i class="far fa-envelope"></i>
{{ trans('admin/categories/general.required_acceptance') }}
<br>
@endif
@if ($component->getEula())
<i class="far fa-envelope"></i>
{{ trans('admin/categories/general.required_eula') }}
<br>
@endif
@if ($snipeSettings->webhook_endpoint!='')
<i class="fab fa-slack"></i>
{{ trans('general.webhook_msg_note') }}
@endif
</div>
</div>
</div>
@endif
<!-- Note -->
<div class="form-group{{ $errors->has('note') ? ' error' : '' }}">

View File

@ -0,0 +1,25 @@
@component('mail::message')
# {{ trans('mail.hello') }} {{ $target->assignedto->present()->fullName() }},
{{ trans('mail.the_following_item') }}
@component('mail::table')
| | |
| ------------- | ------------- |
| **{{ trans('general.component') }}** | {{ $item->name }} |
@if (isset($item->manufacturer))
| **{{ trans('general.manufacturer') }}** | {{ $item->manufacturer->name }} |
@endif
@if ($admin)
| **{{ trans('general.administrator') }}** | {{ $admin->present()->fullName() }} |
@endif
@if ($note)
| **{{ trans('mail.additional_notes') }}** | {{ $note }} |
@endif
@endcomponent
{{ trans('mail.best_regards') }}
{{ $snipeSettings->site_name }}
@endcomponent

View File

@ -0,0 +1,50 @@
@component('mail::message')
# {{ trans('mail.hello') }} {{ $target->assignedto->present()->fullName() }},
{{ trans('mail.new_item_checked') }}
@component('mail::table')
| | |
| ------------- | ------------- |
@if (isset($checkout_date))
| **{{ trans('mail.checkout_date') }}** | {{ $checkout_date }} |
@endif
| **{{ trans('general.component') }}** | {{ $item->name }} |
@if (isset($qty))
| **{{ trans('general.qty') }}** | {{ $qty }} |
@endif
@if (isset($item->manufacturer))
| **{{ trans('general.manufacturer') }}** | {{ $item->manufacturer->name }} |
@endif
@if ($note)
| **{{ trans('mail.additional_notes') }}** | {{ $note }} |
@endif
@if ($admin)
| **{{ trans('general.administrator') }}** | {{ $admin->present()->fullName() }} |
@endif
@endcomponent
@if (($req_accept == 1) && ($eula!=''))
{{ trans('mail.read_the_terms_and_click') }}
@elseif (($req_accept == 1) && ($eula==''))
{{ trans('mail.click_on_the_link_asset') }}
@elseif (($req_accept == 0) && ($eula!=''))
{{ trans('mail.read_the_terms') }}
@endif
@if ($eula)
@component('mail::panel')
{!! $eula !!}
@endcomponent
@endif
@if ($req_accept == 1)
**[ {{ trans('mail.i_have_read') }}]({{ $accept_url }})**
@endif
{{ trans('mail.best_regards') }}
{{ $snipeSettings->site_name }}
@endcomponent

View File

@ -52,7 +52,12 @@ Route::group(['middleware' => 'auth'], function () {
[LabelsController::class, 'show']
)->where('labelName', '.*')->name('labels.show');
Route::get('/test-email', function () {
$mailable = new \App\Mail\CheckoutComponentMail(
);
return $mailable->render(); // dumps HTML
});
/*
* Manufacturers
*/

View File

@ -4,6 +4,7 @@ namespace Tests\Feature\Notifications\Webhooks;
use App\Models\AssetModel;
use App\Models\Category;
use App\Notifications\CheckinComponentNotification;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\Group;
use App\Events\CheckoutableCheckedIn;
@ -96,6 +97,31 @@ class SlackNotificationsUponCheckinTest extends TestCase
$this->assertNoSlackNotificationSent(CheckinAssetNotification::class);
}
#[DataProvider('assetCheckInTargets')]
public function testComponentCheckinSendsSlackNotificationWhenSettingEnabled($checkoutTarget)
{
$this->settings->enableSlackWebhook();
$this->fireCheckInEvent(
Component::factory()->create(),
$checkoutTarget(),
);
$this->assertSlackNotificationSent(CheckinComponentNotification::class);
}
#[DataProvider('assetCheckInTargets')]
public function testComponentCheckinDoesNotSendSlackNotificationWhenSettingDisabled($checkoutTarget)
{
$this->settings->disableSlackWebhook();
$this->fireCheckInEvent(
Component::factory()->create(),
$checkoutTarget(),
);
$this->assertNoSlackNotificationSent(CheckinComponentNotification::class);
}
public function testSlackNotificationIsStillSentWhenCategoryEmailIsNotSetToSendEmails()
{
@ -118,18 +144,6 @@ class SlackNotificationsUponCheckinTest extends TestCase
$this->assertSlackNotificationSent(CheckinAssetNotification::class);
}
public function testComponentCheckinDoesNotSendSlackNotification()
{
$this->settings->enableSlackWebhook();
$this->fireCheckInEvent(
Component::factory()->create(),
Asset::factory()->laptopMbp()->create(),
);
Notification::assertNothingSent();
}
#[DataProvider('licenseCheckInTargets')]
public function testLicenseCheckinSendsSlackNotificationWhenSettingEnabled($checkoutTarget)
{

View File

@ -4,6 +4,8 @@ namespace Tests\Feature\Notifications\Webhooks;
use App\Models\AssetModel;
use App\Models\Category;
use App\Notifications\CheckoutComponentNotification;
use Illuminate\Support\Facades\Mail;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\Group;
use App\Events\CheckoutableCheckedOut;
@ -30,12 +32,13 @@ class SlackNotificationsUponCheckoutTest extends TestCase
parent::setUp();
Notification::fake();
Mail::fake();
}
public static function assetCheckoutTargets(): array
{
return [
'Asset checked out to user' => [fn() => User::factory()->create()],
'Asset checked out to user' => [fn() => User::factory()->create(['email' => null])],
'Asset checked out to asset' => [fn() => Asset::factory()->laptopMbp()->create()],
'Asset checked out to location' => [fn() => Location::factory()->create()],
];
@ -44,7 +47,7 @@ class SlackNotificationsUponCheckoutTest extends TestCase
public static function licenseCheckoutTargets(): array
{
return [
'License checked out to user' => [fn() => User::factory()->create()],
'License checked out to user' => [fn() => User::factory()->create(['email' => null])],
'License checked out to asset' => [fn() => Asset::factory()->laptopMbp()->create()],
];
}
@ -98,7 +101,41 @@ class SlackNotificationsUponCheckoutTest extends TestCase
$this->assertNoSlackNotificationSent(CheckoutAssetNotification::class);
}
#[DataProvider('assetCheckoutTargets')]
public function testComponentCheckoutSendsSlackNotificationWhenSettingEnabled($checkoutTarget)
{
$this->settings->enableSlackWebhook();
$component = Component::factory()->create([
'category_id' => Category::factory()->create([
'require_acceptance' => false,
'eula_text' => null,
]),
]);
$this->fireCheckOutEvent(
$component,
$checkoutTarget(),
);
$this->assertSlackNotificationSent(CheckoutComponentNotification::class);
}
#[DataProvider('assetCheckoutTargets')]
public function testComponentCheckoutDoesNotSendSlackNotificationWhenSettingDisabled($checkoutTarget)
{
$this->settings->disableSlackWebhook();
$component = Component::factory()->create([
'category_id' => Category::factory()->create([
'require_acceptance' => false,
'eula_text' => null,
]),
]);
$this->fireCheckOutEvent(
$component,
$checkoutTarget(),
);
$this->assertNoSlackNotificationSent(CheckoutComponentNotification::class);
}
public function testSlackNotificationIsStillSentWhenCategoryEmailIsNotSetToSendEmails()
{
$this->settings->enableSlackWebhook();
@ -120,18 +157,6 @@ class SlackNotificationsUponCheckoutTest extends TestCase
$this->assertSlackNotificationSent(CheckoutAssetNotification::class);
}
public function testComponentCheckoutDoesNotSendSlackNotification()
{
$this->settings->enableSlackWebhook();
$this->fireCheckOutEvent(
Component::factory()->create(),
Asset::factory()->laptopMbp()->create(),
);
Notification::assertNothingSent();
}
public function testConsumableCheckoutSendsSlackNotificationWhenSettingEnabled()
{
$this->settings->enableSlackWebhook();