"""Durable, account-scoped completed Filed review receipts.""" import re import sqlite3 from datetime import datetime from pathlib import Path from src.private_state import connect_private_sqlite _REPOSITORY = re.compile(r"^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$") class CompletedFiledReviewStore: def __init__(self, path: str | Path, *, limit: int = 200, timeout: float = 1.0): self.path = Path(path) self.limit = limit self.timeout = timeout self._initialize() def _initialize(self) -> None: with self._connect() as connection: connection.execute("PRAGMA journal_mode=WAL") connection.execute( """ CREATE TABLE IF NOT EXISTS completed_filed_reviews ( login TEXT NOT NULL, repository TEXT NOT NULL, issue_number INTEGER NOT NULL, updated_at TEXT NOT NULL, touched_at INTEGER NOT NULL, PRIMARY KEY (login, repository, issue_number) ) """ ) def _connect(self) -> sqlite3.Connection: return connect_private_sqlite(self.path, timeout=self.timeout) @staticmethod def _login(login: str) -> str: normalized = login.strip().lower() if not normalized: raise ValueError("login is required") return normalized @staticmethod def _receipt(raw: dict) -> tuple[str, int, str]: if not isinstance(raw, dict): raise ValueError("receipt must be an object") repository = raw.get("repository") number = raw.get("number") updated_at = raw.get("updated_at") if not isinstance(repository, str) or not _REPOSITORY.fullmatch(repository): raise ValueError("repository is invalid") if not isinstance(number, int) or isinstance(number, bool) or number < 1: raise ValueError("number is invalid") if not isinstance(updated_at, str): raise ValueError("updated_at is invalid") try: parsed = datetime.fromisoformat(updated_at.replace("Z", "+00:00")) except ValueError as error: raise ValueError("updated_at is invalid") from error if parsed.tzinfo is None: raise ValueError("updated_at is invalid") return repository, number, updated_at @staticmethod def _snapshot(rows) -> dict: return {"receipts": [ {"repository": row[0], "number": int(row[1]), "updated_at": row[2]} for row in rows ]} def get(self, login: str) -> dict: with self._connect() as connection: rows = connection.execute( "SELECT repository, issue_number, updated_at FROM completed_filed_reviews " "WHERE login = ? ORDER BY touched_at", (self._login(login),), ).fetchall() return self._snapshot(rows) def merge(self, login: str, receipts: list[dict]) -> dict: login = self._login(login) if not isinstance(receipts, list) or len(receipts) > self.limit: raise ValueError(f"receipts are limited to {self.limit}") normalized = [self._receipt(receipt) for receipt in receipts] with self._connect() as connection: connection.execute("BEGIN IMMEDIATE") touched = int(connection.execute( "SELECT COALESCE(MAX(touched_at), 0) FROM completed_filed_reviews WHERE login = ?", (login,), ).fetchone()[0]) for repository, number, updated_at in normalized: current = connection.execute( "SELECT updated_at FROM completed_filed_reviews " "WHERE login = ? AND repository = ? AND issue_number = ?", (login, repository, number), ).fetchone() if current is not None and current[0] >= updated_at: continue touched += 1 connection.execute( "INSERT INTO completed_filed_reviews " "(login, repository, issue_number, updated_at, touched_at) VALUES (?, ?, ?, ?, ?) " "ON CONFLICT(login, repository, issue_number) DO UPDATE SET " "updated_at=excluded.updated_at, touched_at=excluded.touched_at", (login, repository, number, updated_at, touched), ) connection.execute( "DELETE FROM completed_filed_reviews WHERE login = ? AND rowid NOT IN (" "SELECT rowid FROM completed_filed_reviews WHERE login = ? " "ORDER BY touched_at DESC LIMIT ?)", (login, login, self.limit), ) rows = connection.execute( "SELECT repository, issue_number, updated_at FROM completed_filed_reviews " "WHERE login = ? ORDER BY touched_at", (login,), ).fetchall() return self._snapshot(rows)