240 lines · 10.5 KB
import { createServerFn } from "@tanstack/react-start";
import { z } from "zod";
import { requireViewer, viewerMiddleware } from "./middleware";
import { all, first, run } from "~/server/db";
import { limitWrite } from "~/server/limits";
import { loadRepoById, requireRepoAccess, hasPermission, repoPermission, visibleReposClause } from "~/server/repos";
import { loadIssue } from "~/server/issues";
import { addIssueEvent } from "~/server/pulls";
import { assertCount, PLANNING_CAPS, resolveIssueNumber, subIssueProgress, transferIssue } from "~/server/planning";
import { badRequest, forbidden, notFoundError } from "~/server/errors";
import { repoRef } from "~/lib/validation";

const issueRef = repoRef.extend({ number: z.number().int().positive() });
const MAX_TRANSFER_TARGETS = 50;

export const getIssuePlanning = createServerFn({ method: "GET" })
  .middleware([viewerMiddleware])
  .validator(issueRef)
  .handler(async ({ data, context }) => {
    const { repo, permission } = await requireRepoAccess(data.owner, data.repo, context.viewer, "read");
    const issue = await loadIssue(repo.id, data.number);
    if (!issue) throw notFoundError("Issue not found");
    const [milestone, type, parent, children, pinned, milestones, types] = await Promise.all([
      issue.milestone_id
        ? first<{ id: string; title: string }>("SELECT id, title FROM milestones WHERE id = ?", issue.milestone_id)
        : null,
      issue.type_id
        ? first<{ id: string; name: string; color: string }>(
            "SELECT id, name, color FROM issue_types WHERE id = ?",
            issue.type_id,
          )
        : null,
      issue.parent_id
        ? first<{ number: number; title: string; state: string }>(
            "SELECT number, title, state FROM issues WHERE id = ?",
            issue.parent_id,
          )
        : null,
      all<{ id: string; number: number; title: string; state: "open" | "closed" }>(
        "SELECT id, number, title, state FROM issues WHERE parent_id = ? ORDER BY number LIMIT ?",