whisper-money/app/Console/Commands/ReviewIntegrationRequestsCo...

178 lines
5.2 KiB
PHP

<?php
namespace App\Console\Commands;
use App\Enums\IntegrationRequestStatus;
use App\Models\IntegrationRequest;
use Illuminate\Console\Command;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
class ReviewIntegrationRequestsCommand extends Command
{
/**
* @var string
*/
protected $signature = 'integration-requests:review {--all : Pick any request from the full list and change its status}';
/**
* @var string
*/
protected $description = 'Review integration requests and change their status';
public function handle(): int
{
return $this->option('all') ? $this->reviewAny() : $this->reviewPending();
}
private function reviewPending(): int
{
$pending = $this->load(IntegrationRequest::query()->where('status', IntegrationRequestStatus::Pending));
if ($pending->isEmpty()) {
$this->info('No pending integration requests.');
return self::SUCCESS;
}
$this->printTable($pending);
$changed = 0;
foreach ($pending as $request) {
$decision = $this->choice(
"Review \"{$request->name}\" ({$request->url})",
['approve', 'in progress', 'reject', 'not doable', 'done', 'skip'],
'skip',
);
if ($decision !== 'skip' && $this->apply($request, $decision)) {
$changed++;
}
}
$this->info("Done. Updated {$changed} of {$pending->count()} requests.");
return self::SUCCESS;
}
private function reviewAny(): int
{
$requests = $this->load(IntegrationRequest::query());
if ($requests->isEmpty()) {
$this->info('No integration requests.');
return self::SUCCESS;
}
$this->printTable($requests, withIndex: true);
$request = $requests->get((int) $this->ask('Which request do you want to update? (number)') - 1);
if ($request === null) {
$this->error('That number is not on the list.');
return self::FAILURE;
}
$this->apply($request, $this->choice(
"New status for \"{$request->name}\"",
['approve', 'in progress', 'reject', 'not doable', 'done'],
));
$this->info("\"{$request->name}\" is now {$request->status->label()}.");
return self::SUCCESS;
}
private function apply(IntegrationRequest $request, string $decision): bool
{
$status = match ($decision) {
'approve' => IntegrationRequestStatus::Approved,
'in progress' => IntegrationRequestStatus::InProgress,
'reject' => IntegrationRequestStatus::Rejected,
'not doable' => IntegrationRequestStatus::NotDoable,
'done' => IntegrationRequestStatus::Done,
default => null,
};
if ($status === null) {
return false;
}
$request->update([
'status' => $status,
'comment' => $this->commentFor($status),
]);
return true;
}
private function commentFor(IntegrationRequestStatus $status): ?string
{
if ($status === IntegrationRequestStatus::NotDoable) {
$comment = $this->ask('Why is this integration not doable? (shown to users)');
while (blank($comment)) {
$comment = $this->ask('A comment is required to mark an integration as not doable');
}
return $comment;
}
if ($status === IntegrationRequestStatus::InProgress) {
return $this->ask('Add a comment for this request (optional, shown to users)') ?: null;
}
// Approving, rejecting, re-queuing or marking done drops any stale public comment.
return null;
}
/**
* @param Builder<IntegrationRequest> $query
* @return Collection<int, IntegrationRequest>
*/
private function load(Builder $query): Collection
{
return $query
->withCount('votes')
// Authors who deleted their account are soft-deleted, but their requests
// linger; load them trashed so the table still shows who submitted each one.
->with(['user' => fn ($user) => $user->withTrashed()->select('id', 'email')])
->orderBy('created_at')
->get();
}
/**
* @param Collection<int, IntegrationRequest> $requests
*/
private function printTable(Collection $requests, bool $withIndex = false): void
{
$headers = ['Name', 'URL', 'Status', 'Submitted by', 'Votes', 'Created'];
if ($withIndex) {
array_unshift($headers, '#');
}
$rows = $requests->values()->map(function (IntegrationRequest $request, int $index) use ($withIndex): array {
$row = [
$request->name,
$request->url,
$request->status->label(),
$request->user->email,
$request->votes_count,
$request->created_at?->format('Y-m-d') ?? '—',
];
if ($withIndex) {
array_unshift($row, $index + 1);
}
return $row;
})->all();
$this->table($headers, $rows);
}
}