91 - Record activity from fetch, validate and publish paths

This commit is contained in:
myrmidex 2026-08-10 20:44:57 +02:00
parent c7de5bc3df
commit f8084d144c
4 changed files with 84 additions and 0 deletions

View file

@ -2,11 +2,13 @@
namespace App\Actions; namespace App\Actions;
use App\Enums\ActivityTypeEnum;
use App\Enums\LogLevelEnum; use App\Enums\LogLevelEnum;
use App\Enums\NotificationSeverityEnum; use App\Enums\NotificationSeverityEnum;
use App\Enums\NotificationTypeEnum; use App\Enums\NotificationTypeEnum;
use App\Enums\PublishStatusEnum; use App\Enums\PublishStatusEnum;
use App\Events\ActionPerformed; use App\Events\ActionPerformed;
use App\Events\ActivityLogged;
use App\Exceptions\PublishException; use App\Exceptions\PublishException;
use App\Models\Article; use App\Models\Article;
use App\Models\RouteArticle; use App\Models\RouteArticle;
@ -48,6 +50,13 @@ public function execute(RouteArticle $routeArticle): PublishOutcome
'error' => $e->getMessage(), 'error' => $e->getMessage(),
]); ]);
ActivityLogged::dispatch(
ActivityTypeEnum::ERROR,
"Failed to publish \"{$article->title}\"",
['error' => $e->getMessage()],
$article,
);
$this->notificationService->send( $this->notificationService->send(
NotificationTypeEnum::PUBLISH_FAILED, NotificationTypeEnum::PUBLISH_FAILED,
NotificationSeverityEnum::ERROR, NotificationSeverityEnum::ERROR,
@ -101,6 +110,13 @@ private function recordPublished(RouteArticle $routeArticle): void
'article_id' => $routeArticle->article->id, 'article_id' => $routeArticle->article->id,
'title' => $routeArticle->article->title, 'title' => $routeArticle->article->title,
]); ]);
ActivityLogged::dispatch(
ActivityTypeEnum::PUBLISH,
"Published \"{$routeArticle->article->title}\"",
['route_article_id' => $routeArticle->id],
$routeArticle->article,
);
} }
private function recordSkipped(RouteArticle $routeArticle, PublishOutcome $outcome): void private function recordSkipped(RouteArticle $routeArticle, PublishOutcome $outcome): void
@ -112,6 +128,13 @@ private function recordSkipped(RouteArticle $routeArticle, PublishOutcome $outco
'title' => $routeArticle->article->title, 'title' => $routeArticle->article->title,
'reason' => $outcome->reason, 'reason' => $outcome->reason,
]); ]);
ActivityLogged::dispatch(
ActivityTypeEnum::PUBLISH,
"Skipped \"{$routeArticle->article->title}\"",
['skipped' => true, 'reason' => $outcome->reason],
$routeArticle->article,
);
} }
private function recordFailed(RouteArticle $routeArticle, PublishOutcome $outcome): void private function recordFailed(RouteArticle $routeArticle, PublishOutcome $outcome): void
@ -128,6 +151,13 @@ private function recordFailed(RouteArticle $routeArticle, PublishOutcome $outcom
'attempt' => $routeArticle->publish_attempts, 'attempt' => $routeArticle->publish_attempts,
]); ]);
ActivityLogged::dispatch(
ActivityTypeEnum::ERROR,
"Failed to publish \"{$article->title}\"",
['reason' => $outcome->reason, 'attempt' => $routeArticle->publish_attempts],
$article,
);
$this->notificationService->send( $this->notificationService->send(
NotificationTypeEnum::PUBLISH_FAILED, NotificationTypeEnum::PUBLISH_FAILED,
NotificationSeverityEnum::WARNING, NotificationSeverityEnum::WARNING,

View file

@ -2,8 +2,10 @@
namespace App\Jobs; namespace App\Jobs;
use App\Enums\ActivityTypeEnum;
use App\Enums\NotificationSeverityEnum; use App\Enums\NotificationSeverityEnum;
use App\Enums\NotificationTypeEnum; use App\Enums\NotificationTypeEnum;
use App\Events\ActivityLogged;
use App\Models\Feed; use App\Models\Feed;
use App\Models\Notification; use App\Models\Notification;
use App\Services\Article\ArticleFetcher; use App\Services\Article\ArticleFetcher;
@ -43,8 +45,24 @@ public function handle(LogSaver $logSaver, ArticleFetcher $articleFetcher, Notif
$this->feed->update(['last_fetched_at' => now()]); $this->feed->update(['last_fetched_at' => now()]);
if ($articles->isEmpty()) { if ($articles->isEmpty()) {
ActivityLogged::dispatch(
ActivityTypeEnum::ERROR,
"{$this->feed->name} returned no articles",
['articles_count' => 0],
$this->feed,
);
$this->warnFeedReturnedNothing($notificationService); $this->warnFeedReturnedNothing($notificationService);
return;
} }
ActivityLogged::dispatch(
ActivityTypeEnum::FETCH,
"Fetched {$articles->count()} articles from {$this->feed->name}",
['articles_count' => $articles->count()],
$this->feed,
);
} }
private function warnFeedReturnedNothing(NotificationService $notificationService): void private function warnFeedReturnedNothing(NotificationService $notificationService): void

View file

@ -2,8 +2,10 @@
namespace App\Listeners; namespace App\Listeners;
use App\Enums\ActivityTypeEnum;
use App\Enums\LogLevelEnum; use App\Enums\LogLevelEnum;
use App\Events\ActionPerformed; use App\Events\ActionPerformed;
use App\Events\ActivityLogged;
use App\Events\NewArticleFetched; use App\Events\NewArticleFetched;
use App\Services\Article\ValidationService; use App\Services\Article\ValidationService;
use Exception; use Exception;
@ -32,11 +34,25 @@ public function handle(NewArticleFetched $event): void
try { try {
$this->validationService->validate($article); $this->validationService->validate($article);
ActivityLogged::dispatch(
ActivityTypeEnum::VALIDATE,
"Validated \"{$article->title}\"",
[],
$article,
);
} catch (Exception $e) { } catch (Exception $e) {
ActionPerformed::dispatch('Article validation failed', LogLevelEnum::ERROR, [ ActionPerformed::dispatch('Article validation failed', LogLevelEnum::ERROR, [
'article_id' => $article->id, 'article_id' => $article->id,
'error' => $e->getMessage(), 'error' => $e->getMessage(),
]); ]);
ActivityLogged::dispatch(
ActivityTypeEnum::ERROR,
"Validation failed for \"{$article->title}\"",
['error' => $e->getMessage()],
$article,
);
} }
} }
} }

View file

@ -2,8 +2,10 @@
namespace App\Models; namespace App\Models;
use App\Enums\ActivityTypeEnum;
use App\Enums\ApprovalStatusEnum; use App\Enums\ApprovalStatusEnum;
use App\Enums\PublishStatusEnum; use App\Enums\PublishStatusEnum;
use App\Events\ActivityLogged;
use App\Events\RouteArticleApproved; use App\Events\RouteArticleApproved;
use Database\Factories\RouteArticleFactory; use Database\Factories\RouteArticleFactory;
use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Builder;
@ -105,12 +107,30 @@ public function approve(): void
$this->update(['approval_status' => ApprovalStatusEnum::APPROVED]); $this->update(['approval_status' => ApprovalStatusEnum::APPROVED]);
ActivityLogged::dispatch(
ActivityTypeEnum::APPROVE,
"Approved \"{$this->article->title}\"",
['route_article_id' => $this->id],
$this->article,
);
event(new RouteArticleApproved($this)); event(new RouteArticleApproved($this));
} }
public function reject(): void public function reject(): void
{ {
if ($this->isRejected()) {
return;
}
$this->update(['approval_status' => ApprovalStatusEnum::REJECTED]); $this->update(['approval_status' => ApprovalStatusEnum::REJECTED]);
ActivityLogged::dispatch(
ActivityTypeEnum::REJECT,
"Rejected \"{$this->article->title}\"",
['route_article_id' => $this->id],
$this->article,
);
} }
private const RETRY_BACKOFF_MINUTES = [5, 30, 120, 360]; private const RETRY_BACKOFF_MINUTES = [5, 30, 120, 360];