import { pullInput, requirePullMutation } from "~/server/pull-access"; import { limitWrite } from "~/server/limits"; import { createServerFn } from "@tanstack/react-start"; import { z } from "zod"; import { requireViewer, viewerMiddleware } from "./middleware"; import { markdownBody } from "~/lib/validation"; import { deleteReviewComment as removeReviewComment, editReviewComment as updateReviewComment, replyReviewComment as replyToReviewComment, requestReviewers as requestPullReviewers, resolveReviewThread as setReviewThreadResolved, reviewCommentInput, submitReview as submitReviewFor, } from "~/server/reviews-write"; export const submitReview = createServerFn({ method: "POST" }) .middleware([viewerMiddleware]) .validator( pullInput.extend({ event: z.enum(["approve", "request_changes", "comment"]), body: markdownBody, comments: z.array(reviewCommentInput).max(100).default([]), expectedHead: z.string().length(40), }), ) .handler(async ({ data, context }) => { const viewer = requireViewer(context.viewer); await limitWrite(viewer); const { repo, permission, pull } = await requirePullMutation(data.owner, data.repo, data.number, viewer, "read"); await submitReviewFor({ repo, permission, pull }, viewer, data); return { ok: true }; }); export const replyReviewComment = createServerFn({ method: "POST" }) .middleware([viewerMiddleware]) .validator(pullInput.extend({ commentId: z.string().nullable(), comment: reviewCommentInput })) .handler(async ({ data, context }) => { const viewer = requireViewer(context.viewer); await limitWrite(viewer); const { repo, permission, pull } = await requirePullMutation(data.owner, data.repo, data.number, viewer, "read"); await replyToReviewComment({ repo, permission, pull }, viewer, data); return { ok: true }; }); export const resolveReviewThread = createServerFn({ method: "POST" }) .middleware([viewerMiddleware]) .validator(pullInput.extend({ commentId: z.string(), resolved: z.boolean() })) .handler(async ({ data, context }) => { const viewer = requireViewer(context.viewer); await limitWrite(viewer); const { repo, permission, pull } = await requirePullMutation(data.owner, data.repo, data.number, viewer, "read"); await setReviewThreadResolved({ repo, permission, pull }, viewer, data); return { ok: true }; }); export const editReviewComment = createServerFn({ method: "POST" }) .middleware([viewerMiddleware]) .validator(pullInput.extend({ commentId: z.string().min(1), body: markdownBody.min(1) })) .handler(async ({ data, context }) => { const viewer = requireViewer(context.viewer); await limitWrite(viewer); const { repo, permission, pull } = await requirePullMutation(data.owner, data.repo, data.number, viewer, "read"); await updateReviewComment({ repo, permission, pull }, viewer, data); return { ok: true }; }); export const deleteReviewComment = createServerFn({ method: "POST" }) .middleware([viewerMiddleware]) .validator(pullInput.extend({ commentId: z.string().min(1) })) .handler(async ({ data, context }) => { const viewer = requireViewer(context.viewer); await limitWrite(viewer); const { repo, permission, pull } = await requirePullMutation(data.owner, data.repo, data.number, viewer, "read"); await removeReviewComment({ repo, permission, pull }, viewer, data.commentId); return { ok: true }; }); export const requestReviewers = createServerFn({ method: "POST" }) .middleware([viewerMiddleware]) .validator(pullInput.extend({ usernames: z.array(z.string()).max(15) })) .handler(async ({ data, context }) => { const viewer = requireViewer(context.viewer); await limitWrite(viewer); const { repo, permission, pull } = await requirePullMutation(data.owner, data.repo, data.number, viewer, "write"); await requestPullReviewers({ repo, permission, pull }, viewer, data.usernames); return { ok: true }; });