All files / server/services/examples github-revision-resolver.ts

94.73% Statements 18/19
100% Branches 7/7
100% Functions 2/2
94.44% Lines 17/18

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                  5x       6x 6x               6x 6x       6x   6x 6x   2x 2x 1x   1x     3x   3x 3x 1x   2x      
import { z } from "zod";
import type { ExamplesRef, FullCommitSha, RepositorySlug } from "@shared/examples";
import { fullCommitShaSchema } from "@shared/examples";
import { config } from "../../config";
import type { RequestContext, RevisionResolver } from "./source-provider";
import { ExamplesError } from "./examples-error";
import type { TextFetcher } from "./http-provider";
import { githubApiBudget, type GitHubApiBudget } from "./github-api-budget";
 
const githubCommitResponseSchema = z.object({ sha: fullCommitShaSchema }).passthrough();
 
export class GitHubRevisionResolver implements RevisionResolver {
  constructor(
    private readonly fetcher: TextFetcher,
    private readonly budget: Pick<GitHubApiBudget, "assertMayCall" | "blockedForMs"> = githubApiBudget,
  ) {}
 
  async resolve(
    repository: RepositorySlug,
    ref: ExamplesRef,
    context: RequestContext,
  ): Promise<FullCommitSha> {
    const [owner, name] = repository.split("/");
    const url = new URL(
      `/repos/${encodeURIComponent(owner)}/${encodeURIComponent(name)}/commits/${encodeURIComponent(ref)}`,
      "https://api.github.com",
    );
    this.budget.assertMayCall(context.sourcePriority ?? "default");
    let text: string;
    try {
      text = await this.fetcher.fetchText(url, config.examples.maxManifestBytes, context.signal);
    } catch {
      const blockedForMs = this.budget.blockedForMs();
      if (blockedForMs > 0) {
        throw new ExamplesError("RATE_LIMITED", "GitHub API rate limit reached for external examples", Math.ceil(blockedForMs / 1000));
      }
      throw new ExamplesError("SOURCE_UNAVAILABLE", "External examples ref is unavailable");
    }
    let decoded: unknown;
    try { decoded = JSON.parse(text) as unknown; }
    catch { throw new ExamplesError("SOURCE_UNAVAILABLE", "External examples ref response is invalid"); }
    const parsed = githubCommitResponseSchema.safeParse(decoded);
    if (!parsed.success) {
      throw new ExamplesError("SOURCE_UNAVAILABLE", "External examples ref response is invalid");
    }
    return parsed.data.sha;
  }
}