Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 | 153x 10x 10x 10x 3x 3x 7x 7x 1x 1x 6x 6x 6x 1x 1x 4x 4x 1x 1x 7x 7x 2x 2x | /**
* Cached Sentry repository list (per org).
*
* Powers `issue resolve --in @commit` — avoids a `GET /organizations/{org}/repos/`
* round trip on every invocation when the cache is fresh. The cache stores
* the entire repo list as a JSON blob since the typical lookup pattern is
* "match git origin → find one repo", not "get one repo by ID".
*
* TTL matches other caches (~7 days via {@link CACHE_TTL_MS}). A stale
* cache is refreshed on the next call path that hits the API anyway.
*/
import type { SentryRepository } from "../../types/index.js";
import { recordCacheHit } from "../telemetry.js";
import { getDatabase } from "./index.js";
import { runUpsert } from "./utils.js";
/**
* How long cached repo lists are considered fresh. Kept shorter than the
* project cache (30 days) because repo-to-integration links change more
* often than project listings do.
*/
export const REPO_CACHE_TTL_MS = 7 * 24 * 60 * 60 * 1000;
type RepoCacheRow = {
org_slug: string;
repos_json: string;
cached_at: number;
};
/**
* Fetch the cached repo list for an org. Returns `null` when no cache
* entry exists or the entry is older than {@link REPO_CACHE_TTL_MS}.
*
* Unparseable JSON (from a corrupted or stale schema) is treated as a
* cache miss — the caller refetches and the broken row is overwritten.
*/
export function getCachedRepos(orgSlug: string): SentryRepository[] | null {
const db = getDatabase();
const row = db
.query("SELECT * FROM repo_cache WHERE org_slug = ?")
.get(orgSlug) as RepoCacheRow | undefined;
if (!row) {
recordCacheHit("repo", false);
return null;
}
const age = Date.now() - row.cached_at;
if (age > REPO_CACHE_TTL_MS) {
recordCacheHit("repo", false);
return null;
}
try {
const repos = JSON.parse(row.repos_json) as SentryRepository[];
if (!Array.isArray(repos)) {
recordCacheHit("repo", false);
return null;
}
recordCacheHit("repo", true);
return repos;
} catch {
// Corrupted cache — treat as miss; overwritten on next setCachedRepos.
recordCacheHit("repo", false);
return null;
}
}
/**
* Upsert the cached repo list for an org. Overwrites the previous entry
* (there's only ever one row per org).
*/
export function setCachedRepos(
orgSlug: string,
repos: SentryRepository[]
): void {
const db = getDatabase();
runUpsert(
db,
"repo_cache",
{
org_slug: orgSlug,
repos_json: JSON.stringify(repos),
cached_at: Date.now(),
},
["org_slug"]
);
}
/** Clear the cached repo list for one org (for tests and manual refresh). */
export function clearCachedRepos(orgSlug: string): void {
const db = getDatabase();
db.query("DELETE FROM repo_cache WHERE org_slug = ?").run(orgSlug);
}
|