import { logger } from '../../../logger/index.ts'; import * as memCache from '../../../util/cache/memory/index.ts'; import { getCache } from '../../../util/cache/repository/index.ts'; import { repoCacheProvider } from '../../../util/http/cache/repository-http-cache-provider.ts'; import type { GitlabHttp, GitlabHttpOptions, } from '../../../util/http/gitlab.ts'; import { regEx } from '../../../util/regex.ts'; import { getQueryString } from '../../../util/url.ts'; import { GitLabMergeRequests } from './schema.ts'; import type { GitlabPr, GitlabPrCacheData } from './types.ts'; import { prInfo } from './utils.ts'; export class GitlabPrCache { private items: GitlabPr[] = []; private cache: GitlabPrCacheData; private repo: string; private ignorePrAuthor: boolean; private constructor( repo: string, author: string | null, ignorePrAuthor: boolean, ) { this.repo = repo; this.ignorePrAuthor = ignorePrAuthor; const repoCache = getCache(); repoCache.platform ??= {}; repoCache.platform.gitlab ??= {}; let pullRequestCache = repoCache.platform.gitlab .pullRequestsCache as GitlabPrCacheData; if (!pullRequestCache) { logger.debug('Initializing new PR cache at repository cache'); pullRequestCache = { items: {}, updated_at: null, author, }; } else if (pullRequestCache.author !== author) { logger.debug('Resetting PR cache because authors do not match'); pullRequestCache = { items: {}, updated_at: null, author, }; } else if (pullRequestCache.updated_at?.match(regEx(/\.\d\d\dZ$/))) { logger.debug('Resetting PR cache of older format'); pullRequestCache = { items: {}, updated_at: null, author, }; } repoCache.platform.gitlab.pullRequestsCache = pullRequestCache; this.cache = pullRequestCache; this.updateItems(); } private static async init( http: GitlabHttp, repo: string, author: string | null, ignorePrAuthor: boolean, ): Promise { const res = new GitlabPrCache(repo, author, ignorePrAuthor); const isSynced = memCache.get('gitlab-pr-cache-synced'); if (!isSynced) { await res.sync(http); memCache.set('gitlab-pr-cache-synced', true); } return res; } private getPrs(): GitlabPr[] { return this.items; } static async getPrs( http: GitlabHttp, repo: string, author: string | null, ignorePrAuthor: boolean, ): Promise { const prCache = await GitlabPrCache.init( http, repo, author, ignorePrAuthor, ); return prCache.getPrs(); } private setPr(pr: GitlabPr): void { logger.debug(`Adding PR #${pr.number} to the PR cache`); this.cache.items[pr.number] = pr; this.updateItems(); } static async setPr( http: GitlabHttp, repo: string, author: string | null, item: GitlabPr, ignorePrAuthor: boolean, ): Promise { const prCache = await GitlabPrCache.init( http, repo, author, ignorePrAuthor, ); prCache.setPr(item); } private async sync(http: GitlabHttp): Promise { logger.debug('Syncing PR list'); const searchParams: Record = { per_page: '100', order_by: 'updated_at', sort: 'desc', }; const opts: GitlabHttpOptions = { paginate: true }; const updated_after = this.cache.updated_at; if (updated_after) { opts.cacheProvider = repoCacheProvider; searchParams.updated_after = updated_after; } if (!this.ignorePrAuthor) { searchParams.scope = 'created_by_me'; } const query: string | null = getQueryString(searchParams); const { body: items } = await http.getJson( `/projects/${this.repo}/merge_requests?${query}`, opts, GitLabMergeRequests, ); if (items.length) { for (const item of items) { const id = item.iid; this.cache.items[id] = prInfo(item); } const [{ updated_at }] = items; this.cache.updated_at = updated_at.replace(regEx(/\.\d\d\dZ$/), 'Z'); } this.updateItems(); return this; } /** * Ensure the pr cache starts with the most recent PRs. */ private updateItems(): void { this.items = Object.values(this.cache.items).sort( (a, b) => b.number - a.number, ); } }