fix(notifications): add missing toWebhook methods to notifications (#11528)
This commit is contained in:
commit
cfbcb7737b
9 changed files with 188 additions and 26 deletions
|
|
@ -100,4 +100,16 @@ public function toSlack(): SlackMessage
|
|||
color: SlackMessage::warningColor(),
|
||||
);
|
||||
}
|
||||
|
||||
public function toWebhook(): array
|
||||
{
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => "API token '{$this->tokenName}' expires on {$this->expiresAt}. Rotate this token before it expires to avoid API outages.",
|
||||
'event' => 'api_token_expiring',
|
||||
'token_name' => $this->tokenName,
|
||||
'expires_at' => $this->expiresAt,
|
||||
'url' => $this->manageUrl,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,4 +58,14 @@ public function toSlack(): SlackMessage
|
|||
color: SlackMessage::infoColor(),
|
||||
);
|
||||
}
|
||||
|
||||
public function toWebhook(): array
|
||||
{
|
||||
return [
|
||||
'success' => true,
|
||||
'message' => $this->message,
|
||||
'event' => 'general',
|
||||
'url' => base_url(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,22 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Illuminate\Notifications;
|
||||
|
||||
use App\Notifications\Channels\SendsEmail;
|
||||
use App\Notifications\Dto\DiscordMessage;
|
||||
use App\Notifications\Dto\PushoverMessage;
|
||||
use App\Notifications\Dto\SlackMessage;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
|
||||
interface Notification
|
||||
{
|
||||
public function toMail(SendsEmail $notifiable): MailMessage;
|
||||
|
||||
public function toPushover(): PushoverMessage;
|
||||
|
||||
public function toDiscord(): DiscordMessage;
|
||||
|
||||
public function toSlack(): SlackMessage;
|
||||
|
||||
public function toTelegram();
|
||||
}
|
||||
|
|
@ -74,4 +74,16 @@ public function toSlack(): SlackMessage
|
|||
color: SlackMessage::errorColor()
|
||||
);
|
||||
}
|
||||
|
||||
public function toWebhook(): array
|
||||
{
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => "Server ({$this->server->name}) disabled because it is not paid! All automations and integrations are stopped.",
|
||||
'event' => 'server_force_disabled',
|
||||
'server_name' => $this->server->name,
|
||||
'server_uuid' => $this->server->uuid,
|
||||
'url' => base_url().'/server/'.$this->server->uuid,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,4 +65,16 @@ public function toSlack(): SlackMessage
|
|||
color: SlackMessage::successColor()
|
||||
);
|
||||
}
|
||||
|
||||
public function toWebhook(): array
|
||||
{
|
||||
return [
|
||||
'success' => true,
|
||||
'message' => "Server ({$this->server->name}) enabled again!",
|
||||
'event' => 'server_force_enabled',
|
||||
'server_name' => $this->server->name,
|
||||
'server_uuid' => $this->server->uuid,
|
||||
'url' => base_url().'/server/'.$this->server->uuid,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,8 +17,7 @@ public function __construct(public int $hetznerServerId, public int $teamId, pub
|
|||
|
||||
public function via(object $notifiable): array
|
||||
{
|
||||
|
||||
return $notifiable->getEnabledChannels('hetzner_deletion_failed');
|
||||
return $notifiable->getEnabledChannels('hetzner_deletion_failure');
|
||||
}
|
||||
|
||||
public function toMail(): MailMessage
|
||||
|
|
@ -66,4 +65,16 @@ public function toSlack(): SlackMessage
|
|||
color: SlackMessage::errorColor()
|
||||
);
|
||||
}
|
||||
|
||||
public function toWebhook(): array
|
||||
{
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => "[ACTION REQUIRED] Failed to delete Hetzner server #{$this->hetznerServerId} from Hetzner Cloud. The server has been removed from Coolify, but may still exist in your Hetzner Cloud account.",
|
||||
'event' => 'hetzner_deletion_failed',
|
||||
'hetzner_server_id' => $this->hetznerServerId,
|
||||
'error' => $this->errorMessage,
|
||||
'url' => base_url().'/servers',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -117,4 +117,18 @@ public function toSlack(): SlackMessage
|
|||
color: SlackMessage::warningColor()
|
||||
);
|
||||
}
|
||||
|
||||
public function toWebhook(): array
|
||||
{
|
||||
$resourceNames = $this->resources->pluck('name');
|
||||
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => "SSL certificates have been renewed for: {$resourceNames->join(', ')}. These resources need to be redeployed manually for the new SSL certificates to take effect.",
|
||||
'event' => 'ssl_certificate_renewal',
|
||||
'resources' => $resourceNames->values()->all(),
|
||||
'urls' => $this->urls,
|
||||
'url' => base_url(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
->and($notification->errorMessage)->toBe('Hetzner API error: Server not found');
|
||||
});
|
||||
|
||||
it('uses hetzner_deletion_failed event for channels', function () {
|
||||
it('uses the always-send hetzner_deletion_failure event for channels', function () {
|
||||
$notification = new HetznerDeletionFailed(
|
||||
hetznerServerId: 12345,
|
||||
teamId: 1,
|
||||
|
|
@ -28,7 +28,7 @@
|
|||
|
||||
$mockNotifiable = Mockery::mock();
|
||||
$mockNotifiable->shouldReceive('getEnabledChannels')
|
||||
->with('hetzner_deletion_failed')
|
||||
->with('hetzner_deletion_failure')
|
||||
->once()
|
||||
->andReturn([]);
|
||||
|
||||
|
|
|
|||
113
tests/Unit/Notifications/Channels/WebhookChannelTest.php
Normal file
113
tests/Unit/Notifications/Channels/WebhookChannelTest.php
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
<?php
|
||||
|
||||
use App\Jobs\SendWebhookJob;
|
||||
use App\Models\InstanceSettings;
|
||||
use App\Models\PersonalAccessToken;
|
||||
use App\Models\Server;
|
||||
use App\Models\Team;
|
||||
use App\Notifications\ApiTokenExpiringNotification;
|
||||
use App\Notifications\Channels\WebhookChannel;
|
||||
use App\Notifications\Internal\GeneralNotification;
|
||||
use App\Notifications\Server\ForceDisabled;
|
||||
use App\Notifications\Server\ForceEnabled;
|
||||
use App\Notifications\SslExpirationNotification;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Notifications\Notification;
|
||||
use Illuminate\Support\Facades\Queue;
|
||||
use Tests\TestCase;
|
||||
|
||||
uses(TestCase::class, RefreshDatabase::class);
|
||||
|
||||
beforeEach(function () {
|
||||
// base_url() resolves the instance settings singleton, which lives at the id = 0 sentinel.
|
||||
InstanceSettings::unguarded(fn () => InstanceSettings::query()->updateOrCreate(['id' => 0], [
|
||||
'fqdn' => 'https://coolify.example.com',
|
||||
]));
|
||||
Queue::fake();
|
||||
|
||||
$this->team = Team::create([
|
||||
'name' => 'Webhook Channel Team',
|
||||
'personal_team' => false,
|
||||
'show_boarding' => false,
|
||||
]);
|
||||
// Assign through the model so the `encrypted` cast on webhook_url is applied.
|
||||
$settings = $this->team->webhookNotificationSettings;
|
||||
$settings->webhook_enabled = true;
|
||||
$settings->webhook_url = 'https://webhook.example.com/coolify';
|
||||
$settings->save();
|
||||
$this->team->refresh();
|
||||
});
|
||||
|
||||
/**
|
||||
* Send a notification through the webhook channel and return the dispatched payload.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
function deliverOverWebhook(Team $team, Notification $notification): array
|
||||
{
|
||||
expect($notification->via($team))->toContain(WebhookChannel::class);
|
||||
|
||||
(new WebhookChannel)->send($team, $notification);
|
||||
|
||||
$payload = null;
|
||||
Queue::assertPushed(SendWebhookJob::class, function (SendWebhookJob $job) use (&$payload) {
|
||||
$payload = $job->payload;
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
expect($payload)->toBeArray()
|
||||
->and($payload['success'])->toBeBool()
|
||||
->and($payload['message'])->toBeString()->not->toBeEmpty();
|
||||
|
||||
return $payload;
|
||||
}
|
||||
|
||||
it('delivers ssl certificate renewal notifications over the webhook channel', function () {
|
||||
$payload = deliverOverWebhook(
|
||||
$this->team,
|
||||
new SslExpirationNotification([(object) ['name' => 'my-application']])
|
||||
);
|
||||
|
||||
expect($payload['event'])->toBe('ssl_certificate_renewal')
|
||||
->and($payload['resources'])->toBe(['my-application']);
|
||||
});
|
||||
|
||||
it('delivers api token expiring notifications over the webhook channel', function () {
|
||||
$token = new PersonalAccessToken([
|
||||
'name' => 'ci-token',
|
||||
'expires_at' => now()->addDay(),
|
||||
]);
|
||||
|
||||
$payload = deliverOverWebhook($this->team, new ApiTokenExpiringNotification($token));
|
||||
|
||||
expect($payload['event'])->toBe('api_token_expiring')
|
||||
->and($payload['token_name'])->toBe('ci-token');
|
||||
});
|
||||
|
||||
it('delivers server force enabled notifications over the webhook channel', function () {
|
||||
$server = Server::factory()->create(['team_id' => $this->team->id]);
|
||||
|
||||
$payload = deliverOverWebhook($this->team, new ForceEnabled($server));
|
||||
|
||||
expect($payload['event'])->toBe('server_force_enabled')
|
||||
->and($payload['success'])->toBeTrue()
|
||||
->and($payload['server_uuid'])->toBe($server->uuid);
|
||||
});
|
||||
|
||||
it('delivers server force disabled notifications over the webhook channel', function () {
|
||||
$server = Server::factory()->create(['team_id' => $this->team->id]);
|
||||
|
||||
$payload = deliverOverWebhook($this->team, new ForceDisabled($server));
|
||||
|
||||
expect($payload['event'])->toBe('server_force_disabled')
|
||||
->and($payload['success'])->toBeFalse()
|
||||
->and($payload['server_uuid'])->toBe($server->uuid);
|
||||
});
|
||||
|
||||
it('delivers general notifications over the webhook channel', function () {
|
||||
$payload = deliverOverWebhook($this->team, new GeneralNotification('Something happened'));
|
||||
|
||||
expect($payload['event'])->toBe('general')
|
||||
->and($payload['message'])->toBe('Something happened');
|
||||
});
|
||||
Loading…
Reference in a new issue