86 lines · 2.9 KB
import { all, batch, first, run, stmt } from "./db";
import { addIssueEvent } from "./pulls";
import type { PullRow } from "./pulls";
import { ownerAccountIds, ownersToRequest, pullCodeOwners } from "./codeowners";
import { accountsById } from "./people";
import { emitNotification } from "./events/emit";
import { pullUrl } from "./pull-access";
import type { RepoRow, Viewer } from "~/lib/types";

async function notifyRequested(repo: RepoRow, pull: PullRow, actor: Viewer, userIds: string[]) {
  await emitNotification({
    repoId: repo.id,
    issueId: null,
    actorId: actor.id,
    reason: "review_requested",
    title: `${actor.name} requested your review on ${repo.owner_name}/${repo.name}#${pull.number}`,
    url: pullUrl(repo, pull.number),
    mentionText: null,
    directRecipients: userIds,
  });
}

/** Auto-requests reviews from CODEOWNERS of the changed files who have not reviewed yet. */
export async function requestCodeOwnerReviews(
  pull: PullRow,
  baseRepo: RepoRow,
  headRepo: RepoRow,
  actor: Viewer,
): Promise<void> {
  const { owned } = await pullCodeOwners(pull, baseRepo, headRepo);
  if (owned.size === 0) return;
  const candidates = ownersToRequest(owned, await ownerAccountIds(owned), pull.author_id);
  if (candidates.length === 0) return;
  const reviewed = new Set(
    (
      await all<{ author_id: string }>(
        "SELECT DISTINCT author_id FROM reviews WHERE issue_id = ? AND state != 'dismissed'",
        pull.issue_id,
      )
    ).map((row) => row.author_id),