Merge pull request 'Kanban-Farbe verwaister Tickets nach Projekt-Entfernung zuruecksetzen' (#35) from fds2610-bot/monitor:main into main

Reviewed-on: https://codeberg.org/fds2610/monitor/pulls/35
This commit is contained in:
fds2610 2026-08-04 12:07:43 +02:00
commit 58eb022113
4 changed files with 87 additions and 1 deletions

View file

@ -170,6 +170,33 @@ final class TicketRepository
]); ]);
} }
/**
* Loescht die Projektfarbe von Tickets, deren Quell-Projekt nicht (mehr) in $slugs steht
* (Issue #34: nach Entfernen/Umbenennen eines Projekts im Projekte-Modal blieben dessen
* Tickets dauerhaft in dessen alter Farbe eingefaerbt, weil CodebergSyncService sie ab
* dann gar nicht mehr anfasst). Tickets ohne Projektbezug (repo = NULL, z.B. per API
* angelegt) bleiben unberuehrt.
*
* @param string[] $slugs - aktuell konfigurierte "owner/repo"-Werte.
*/
public function clearColorForReposNotIn(array $slugs): void
{
if ($slugs === []) {
$this->db->pdo()->exec(
"UPDATE tickets SET color = NULL WHERE repo IS NOT NULL AND color IS NOT NULL"
);
return;
}
$placeholders = implode(', ', array_fill(0, count($slugs), '?'));
$stmt = $this->db->pdo()->prepare(
"UPDATE tickets SET color = NULL
WHERE repo IS NOT NULL AND color IS NOT NULL AND repo NOT IN ($placeholders)"
);
$stmt->execute($slugs);
}
/** /**
* Billiger "Fingerabdruck" (Anzahl + juengster Aenderungszeitpunkt) fuer den SSE-Endpoint * Billiger "Fingerabdruck" (Anzahl + juengster Aenderungszeitpunkt) fuer den SSE-Endpoint
* (Realtime\ChangeFeed, F13) - aendert sich bei jedem Anlegen/Verschieben eines Tickets. * (Realtime\ChangeFeed, F13) - aendert sich bei jedem Anlegen/Verschieben eines Tickets.

View file

@ -42,9 +42,19 @@ final class CodebergSyncService
*/ */
public function sync(): void public function sync(): void
{ {
foreach ($this->repoConfigs->all() as $repoConfig) { $repoConfigs = $this->repoConfigs->all();
foreach ($repoConfigs as $repoConfig) {
$this->syncRepo($repoConfig['owner'], $repoConfig['repo'], $repoConfig['color']); $this->syncRepo($repoConfig['owner'], $repoConfig['repo'], $repoConfig['color']);
} }
// Issue #34: Tickets aus zwischenzeitlich entfernten oder umbenannten Projekten werden
// oben nicht mehr angefasst und wuerden sonst dauerhaft in der alten, nicht mehr
// zutreffenden Projektfarbe angezeigt.
$this->tickets->clearColorForReposNotIn(array_map(
static fn (array $repoConfig): string => $repoConfig['owner'] . '/' . $repoConfig['repo'],
$repoConfigs
));
} }
private function syncRepo(string $owner, string $repo, string $color): void private function syncRepo(string $owner, string $repo, string $color): void

View file

@ -164,4 +164,33 @@ final class TicketRepositoryTest extends TestCase
self::assertSame($this->repository->fingerprint(), $this->repository->fingerprint()); self::assertSame($this->repository->fingerprint(), $this->repository->fingerprint());
} }
public function testClearColorForReposNotInRemovesColorFromOrphanedRepo(): void
{
$kept = $this->repository->create(['title' => 'A', 'repo' => 'fds2610/monitor', 'color' => '#fbca04']);
$orphaned = $this->repository->create(['title' => 'B', 'repo' => 'fds2610/altes-projekt', 'color' => '#3b82f6']);
$this->repository->clearColorForReposNotIn(['fds2610/monitor']);
self::assertSame('#fbca04', $this->repository->find((int) $kept['id'])['color']);
self::assertNull($this->repository->find((int) $orphaned['id'])['color']);
}
public function testClearColorForReposNotInIgnoresTicketsWithoutRepo(): void
{
$manual = $this->repository->create(['title' => 'API-Ticket', 'color' => '#ffffff']);
$this->repository->clearColorForReposNotIn(['fds2610/monitor']);
self::assertSame('#ffffff', $this->repository->find((int) $manual['id'])['color']);
}
public function testClearColorForReposNotInClearsAllColorsWhenNoRepoConfigured(): void
{
$ticket = $this->repository->create(['title' => 'B', 'repo' => 'fds2610/altes-projekt', 'color' => '#3b82f6']);
$this->repository->clearColorForReposNotIn([]);
self::assertNull($this->repository->find((int) $ticket['id'])['color']);
}
} }

View file

@ -117,6 +117,26 @@ final class CodebergSyncServiceTest extends TestCase
self::assertSame('#28a745', $steuerkramTicket['color']); self::assertSame('#28a745', $steuerkramTicket['color']);
} }
public function testSyncClearsColorOfTicketsFromRemovedProject(): void
{
$tickets = new TicketRepository(Database::inMemory());
$client = self::clientForMonitor([
'open' => [['number' => 1, 'title' => 'Issue', 'body' => null, 'labels' => []]],
'closed' => [],
]);
(new CodebergSyncService($client, $tickets, self::repoConfigsFor([self::MONITOR_REPO])))->sync();
self::assertSame('#fbca04', $tickets->findByExternalRef('1', self::MONITOR_SLUG)['color']);
// Projekt wurde im Projekte-Modal entfernt (Issue #34): der naechste Sync-Lauf sieht
// es nicht mehr in repoConfigsFor() und darf das verwaiste Ticket nicht laenger blau
// faerben, obwohl es kein Projekt mit dieser Farbe mehr gibt.
$client = new FakeCodebergClient([]);
(new CodebergSyncService($client, $tickets, self::repoConfigsFor([])))->sync();
self::assertNull($tickets->findByExternalRef('1', self::MONITOR_SLUG)['color']);
}
public function testSyncDoesNotResetManuallyMovedTicket(): void public function testSyncDoesNotResetManuallyMovedTicket(): void
{ {
$tickets = new TicketRepository(Database::inMemory()); $tickets = new TicketRepository(Database::inMemory());