Documentation
¶
Overview ¶
Package pulls holds the Githome web front's pull-request handlers: the index with its state tabs, the PR shell the four tabs hang off, the Conversation timeline, the Commits tab, the read-only Files-changed diff, and the merge box with its poll fragment and merge mutation. Each handler resolves and authorizes through the same domain.PRService the REST and GraphQL surfaces use, so the page and the API never disagree, maps the result into a fe/view model with every URL precomputed through fe/route, and renders through fe/render. A repository the viewer cannot see was turned into a hard 404 by the Resolve middleware before any handler ran (the 404-not-403 rule), and every mutation has a plain HTML form path that works with no JavaScript. The inline review threads and the review state machine over the Files tab arrive in F5; F4 ships the Files tab read-only. See implementation/09.
Index ¶
- type Deps
- type Handlers
- func (h *Handlers) Checks(c *mizu.Ctx) error
- func (h *Handlers) CommitRedirect(c *mizu.Ctx) error
- func (h *Handlers) Commits(c *mizu.Ctx) error
- func (h *Handlers) Conversation(c *mizu.Ctx) error
- func (h *Handlers) Create(c *mizu.Ctx) error
- func (h *Handlers) CreateComment(c *mizu.Ctx) error
- func (h *Handlers) CreateReviewComment(c *mizu.Ctx) error
- func (h *Handlers) ExpandDiff(c *mizu.Ctx) error
- func (h *Handlers) Files(c *mizu.Ctx) error
- func (h *Handlers) Index(c *mizu.Ctx) error
- func (h *Handlers) Merge(c *mizu.Ctx) error
- func (h *Handlers) MergeBox(c *mizu.Ctx) error
- func (h *Handlers) ReplyReviewComment(c *mizu.Ctx) error
- func (h *Handlers) Resolve(next mizu.Handler) mizu.Handler
- func (h *Handlers) SubmitReview(c *mizu.Ctx) error
- func (h *Handlers) ToggleReviewThread(c *mizu.Ctx) error
- func (h *Handlers) ToggleState(c *mizu.Ctx) error
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Deps ¶
type Deps struct {
Pulls *domain.PRService
Issues *domain.IssueService
Reviews *domain.ReviewService
Checks *domain.ChecksService
Repos *domain.RepoService
URLs *presenter.URLBuilder
Render *render.Set
View *view.Builder
Markup *markup.Renderer
Logger *slog.Logger
}
Deps are the pulls handlers' dependencies: the domain PR service for the shell, the diff, the commits, and the merge; the issue service for the Conversation timeline, since a PR shares its number and its comments with an issue; the repo service to resolve and read-gate the repository; the presenter for avatar URLs; the render set; the view builder for the shell chrome; the shared markup renderer for comment bodies; and a logger for the notices the views emit. A nil markup renderer falls back to escaped comment source.
type Handlers ¶
type Handlers struct {
// contains filtered or unexported fields
}
Handlers is the pulls handler set. One is built at boot and shared; it holds no per-request state.
func (*Handlers) Checks ¶ added in v0.1.3
Checks renders the Checks tab. A missing PR is the soft 404; a head sha with nothing reported renders the blankslate inside the shell. The route mounts only when the checks service is wired, the same gate the standalone checks page sits behind, so h.checks is non-nil here.
func (*Handlers) CommitRedirect ¶ added in v0.1.3
CommitRedirect handles /{owner}/{repo}/pull/{number}/commits/{sha}, the per-commit view inside a pull request. github.com renders the single commit's diff framed in the PR; Githome serves the same diff from the repository commit page, so this resolves the pull request (soft 404, with the issue-number crossover loadPR already does) and then 302s to /{owner}/{repo}/commit/{sha}. The commit page itself is the authority on whether the sha exists, so a bad sha lands on its 404 rather than being probed here.
func (*Handlers) Commits ¶
Commits renders the PR Commits tab: the shell plus the pull request's own commits grouped by authored calendar date, newest day first. The commits are the head's commits over the base, the same range the diff and the API count, so the tab badge and the list never disagree. A missing PR is the soft 404. See implementation/09 section 4.
func (*Handlers) Conversation ¶
Conversation renders the PR Conversation tab: the shell, the comment timeline (the opening body first, then the comments oldest-first), the new-comment composer, and the merge box. A PR shares its number and its comments with an issue, so the timeline is the same CommentVM stream the issue detail renders, read through the issue service on the PR's number. A missing PR, or one in a repo the viewer cannot see, renders the soft 404, never a 403. See implementation/09 section 3.
func (*Handlers) Create ¶ added in v0.1.3
Create handles POST /{owner}/{repo}/pulls, the create-PR form submission from the compare range page. It reads the title, body, base, head, and draft flag from the form, delegates to the domain create, and 303-redirects to the new PR's Conversation tab. Validation errors (empty title, same base/head, unresolvable branches, duplicate PR) redirect back to the compare page with the form pre-filled; infrastructure errors let the recover layer render a 500. See implementation/09 section 8.
func (*Handlers) CreateComment ¶
CreateComment posts a comment to the PR's shared issue thread and redirects to the new comment's permalink. An empty body re-renders the Conversation with the message inline, the no-JS validation path.
func (*Handlers) CreateReviewComment ¶
CreateReviewComment opens a new inline thread: a single comment anchored to a diff line by (path, side, line) and pinned to the head commit the composer carried. The domain validates the anchor lies inside that commit's diff and rejects an off-diff anchor as a validation error, which re-renders the Files tab. On success the browser lands on the new comment's permalink on the Files tab.
func (*Handlers) ExpandDiff ¶ added in v0.1.3
ExpandDiff serves the hunk-unfold fragment: the context lines a collapsed gap on the Files tab hides, read from the head blob at the SHA the diff was built from. The query names the slice — the file path, the head SHA, the per-side start lines, the count, and the diff mode. The rows it returns are read-only context with no Position, so an unfolded line never becomes a comment anchor and the patch-offset space the review API resolves against is untouched. A missing PR, file, or blob is the soft 404; a count past the end of the file simply returns the lines that exist.
func (*Handlers) Files ¶
Files renders the PR Files-changed tab read-only: the shell plus the unified diff of every changed file, parsed from the per-file patch text the PR service yields. The diff is the three-dot range from the base tip to the head, the same range the .diff media type serves, so the page and the API show the identical change set. The inline review threads and the review state machine arrive in F5 over this same model; F4 ships the diff for reading. A missing PR is the soft 404. See implementation/09 section 4.
func (*Handlers) Index ¶
Index renders the pull-request list with its open and closed tabs. The PR index is the issue index frame pre-filtered to pull requests: the ?state= selector is the only filter F4 reads, the open and closed tabs flip it, and the list pages with prev and next links, all server-side so it works with no JavaScript. The richer is:label and is:author query grammar the issues index parses arrives for pulls in a later pass; F4 ships the state tabs and pagination the merge flow needs. See implementation/09 section 2.
func (*Handlers) Merge ¶
Merge performs the merge and redirects back to the Conversation tab. The form carries the strategy and the expected head sha; the service re-authorizes the write, re-checks mergeability, and rejects a stale head, so this handler maps the outcome rather than deciding it. A not-mergeable or head-moved result re-renders the Conversation with the reason inline (the no-JS error path), since those are expected races, not server faults. See implementation/09 section 5.3.
func (*Handlers) MergeBox ¶
MergeBox renders the merge box on its own, the fragment the Computing state re-fetches until the worker resolves the mergeability. It is the same model the Conversation tab embeds, rendered through the standalone partial so the poll swaps just the box. A missing PR is the soft 404.
func (*Handlers) ReplyReviewComment ¶
ReplyReviewComment appends a reply to an existing thread, identified by its root comment. The reply rides its own submitted comment review, the same way the API reply does. On success the browser lands on the reply's permalink.
func (*Handlers) Resolve ¶
Resolve loads the repository named by the {owner} and {repo} path parameters, read-gated for the viewer, and stores it on the context. A missing repository, or a private one the viewer cannot see, renders the same 404, so a private repo never leaks through the status code. This mirrors the issues and code-browsing Resolve so the three surfaces gate identically. It is the one place the repo is loaded; every handler reads it back with repoFromContext.
func (*Handlers) SubmitReview ¶
SubmitReview submits a PR-level verdict: approve, request changes, or comment. The service forbids self-approval and requires a body for a change request, returning a validation error the Conversation tab echoes inline. On success the browser lands on the submitted review in the Conversation timeline.
func (*Handlers) ToggleReviewThread ¶
ToggleReviewThread resolves or unresolves a thread. The handler reads the thread's current state and flips it, so the one endpoint does both. On success the browser lands back on the thread on the Files tab.
func (*Handlers) ToggleState ¶
ToggleState closes or reopens the pull request, optionally posting a comment in the same submit (the "Close with comment" path). The target state is derived from the PR's current state, so the one button does the right thing either way. Closing a PR is closing its issue; it does not merge. A merged PR is already closed, so the composer hides this control once merged.