import { repoDb } from "./scoped-db"; import { HttpError } from "./errors"; import { randomId } from "./crypto"; import { assertBelowCap, CAPS } from "./limits"; const HTTP_UNPROCESSABLE = 422; export type LabelRow = { id: string; name: string; color: string; description: string | null; }; export type LabelInput = { name: string; color: string; description: string | null; }; export async function listRepoLabels(repoId: string): Promise> { return repoDb(repoId).all( `SELECT l.id, l.name, l.color, l.description, (SELECT COUNT(*) FROM issue_labels il JOIN issues i ON i.id = il.issue_id WHERE il.label_id = l.id AND i.state = 'open') AS open_count FROM labels l WHERE l.repo_id = ? ORDER BY l.name`, repoId, ); } export async function loadLabel(repoId: string, id: string): Promise { return repoDb(repoId).first( "SELECT id, name, color, description FROM labels WHERE id = ? AND repo_id = ?", id, repoId, ); } /** Creates (id null) or updates a label; returns its id. Callers must have checked write access. */ export async function saveLabel(repoId: string, id: string | null, input: LabelInput): Promise { const clash = await repoDb(repoId).first<{ id: string }>( "SELECT id FROM labels WHERE repo_id = ? AND name = ? COLLATE NOCASE AND id != ?", repoId, input.name, id ?? "", ); if (clash) throw new HttpError(HTTP_UNPROCESSABLE, "A label with that name already exists"); if (!id) assertBelowCap( (await repoDb(repoId).first<{ count: number }>("SELECT COUNT(*) AS count FROM labels WHERE repo_id = ?", repoId)) ?.count ?? 0, CAPS.labelsPerRepo, "labels", ); if (id) { await repoDb(repoId).run( "UPDATE labels SET name = ?, color = ?, description = ? WHERE id = ? AND repo_id = ?", input.name, input.color, input.description, id, repoId, ); return id; } const newId = randomId(); await repoDb(repoId).run( "INSERT INTO labels (id, repo_id, name, color, description) VALUES (?, ?, ?, ?, ?)", newId, repoId, input.name, input.color, input.description, ); return newId; } /** Returns whether a label was deleted. Callers must have checked write access. */ export async function deleteLabel(repoId: string, id: string): Promise { const result = await repoDb(repoId).run("DELETE FROM labels WHERE id = ? AND repo_id = ?", id, repoId); return result.meta.changes > 0; }