41 lines · 1.8 KB
import { all, chunks, placeholders } from "./db";

export type WithAuthorAccount<T> = T & { authorAccount: string | null };

/**
 * Lowercased commit email to account name, through verified addresses only (primary or added), so a
 * commit is shown as someone's only once they have proven they own the address it was made with.
 */
export async function accountsForEmails(emails: string[]): Promise<Map<string, string>> {
  const unique = [...new Set(emails.map((email) => email.trim().toLowerCase()).filter(Boolean))];
  const out = new Map<string, string>();
  for (const group of chunks(unique, 45)) {
    const rows = await all<{ email: string; name: string }>(
      `SELECT LOWER(u.email) AS email, a.name FROM users u JOIN accounts a ON a.id = u.account_id
       WHERE u.email_verified_at IS NOT NULL AND LOWER(u.email) IN (${placeholders(group.length)})
       UNION SELECT LOWER(e.email) AS email, a.name FROM user_emails e JOIN accounts a ON a.id = e.user_id
       WHERE e.verified_at IS NOT NULL AND LOWER(e.email) IN (${placeholders(group.length)})`,
      ...group,
      ...group,
    );
    for (const row of rows) out.set(row.email, row.name);
  }
  return out;
}

/** Adds the account (if any) that owns each commit's author email. */
export async function withAuthorAccounts<T extends { authorEmail?: string }>(
  commits: T[],
): Promise<Array<WithAuthorAccount<T>>> {
  const accounts = await accountsForEmails(commits.map((commit) => commit.authorEmail ?? ""));
  return commits.map((commit) => ({
    ...commit,
    authorAccount: accounts.get((commit.authorEmail ?? "").trim().toLowerCase()) ?? null,
  }));
}

export async function withAuthorAccount<T extends { authorEmail?: string }>(commit: T): Promise<WithAuthorAccount<T>> {
  const [enriched] = await withAuthorAccounts([commit]);
  return enriched;
}