ikurotime / gitgud

public
main / internal/interface/web/pull_handler.go
4.8 KB · Go Raw
  1package web
  2
  3import (
  4	"errors"
  5	"net/http"
  6	"strconv"
  7	"strings"
  8
  9	"github.com/go-chi/chi"
 10
 11	"gitgud/internal/app"
 12	"gitgud/internal/domain"
 13	"gitgud/internal/interface/web/templates"
 14)
 15
 16func (h *Handlers) pullsList(w http.ResponseWriter, r *http.Request) {
 17	repo := h.viewRepo(w, r)
 18	if repo == nil {
 19		return
 20	}
 21
 22	state := r.URL.Query().Get("state")
 23	var filter domain.PRState
 24	switch state {
 25	case "merged":
 26		filter = domain.PRMerged
 27	case "closed":
 28		filter = domain.PRClosed
 29	case "all":
 30		filter = ""
 31	default:
 32		state = "open"
 33		filter = domain.PROpen
 34	}
 35
 36	prs, err := h.pulls.List(r.Context(), repo.ID, filter)
 37	if err != nil {
 38		h.writeError(w, r, err)
 39		return
 40	}
 41	open, merged, closed, err := h.pulls.Counts(r.Context(), repo.ID)
 42	if err != nil {
 43		h.writeError(w, r, err)
 44		return
 45	}
 46	render(w, r, http.StatusOK, templates.PullsList(currentUser(r.Context()), repo, state, prs, open, merged, closed))
 47}
 48
 49func (h *Handlers) comparePull(w http.ResponseWriter, r *http.Request) {
 50	repo := h.viewRepo(w, r)
 51	if repo == nil {
 52		return
 53	}
 54
 55	branches, err := h.browse.Branches(r.Context(), repo)
 56	if err != nil {
 57		h.writeError(w, r, err)
 58		return
 59	}
 60
 61	base := r.URL.Query().Get("base")
 62	if base == "" {
 63		base = repo.DefaultBranch
 64	}
 65	head := r.URL.Query().Get("head")
 66
 67	var cmp *domain.Comparison
 68	errMsg := ""
 69	if head != "" && head != base {
 70		cmp, err = h.pulls.Compare(r.Context(), repo, base, head)
 71		if err != nil {
 72			errMsg = "could not compare those branches"
 73		}
 74	}
 75	render(w, r, http.StatusOK, templates.Compare(currentUser(r.Context()), repo, base, head, branches, cmp, errMsg))
 76}
 77
 78func (h *Handlers) createPull(w http.ResponseWriter, r *http.Request) {
 79	repo := h.viewRepo(w, r)
 80	if repo == nil {
 81		return
 82	}
 83	title := r.FormValue("title")
 84	body := r.FormValue("body")
 85	base := r.FormValue("base")
 86	head := r.FormValue("head")
 87
 88	pr, err := h.pulls.Open(r.Context(), repo, currentUser(r.Context()), title, body, base, head)
 89	if err != nil {
 90		if errors.Is(err, domain.ErrValidation) {
 91			branches, _ := h.browse.Branches(r.Context(), repo)
 92			cmp, _ := h.pulls.Compare(r.Context(), repo, base, head)
 93			msg := strings.TrimSuffix(err.Error(), ": "+domain.ErrValidation.Error())
 94			render(w, r, http.StatusBadRequest, templates.Compare(currentUser(r.Context()), repo, base, head, branches, cmp, msg))
 95			return
 96		}
 97		h.writeError(w, r, err)
 98		return
 99	}
100	http.Redirect(w, r, repoURL(repo, "/pulls/"+strconv.Itoa(pr.Number)), http.StatusSeeOther)
101}
102
103func (h *Handlers) pullDetail(w http.ResponseWriter, r *http.Request) {
104	repo, pr := h.loadPull(w, r)
105	if pr == nil {
106		return
107	}
108	h.renderPull(w, r, repo, pr, "", http.StatusOK)
109}
110
111func (h *Handlers) addPullComment(w http.ResponseWriter, r *http.Request) {
112	repo, pr := h.loadPull(w, r)
113	if pr == nil {
114		return
115	}
116	if err := h.pulls.Comment(r.Context(), pr, currentUser(r.Context()), r.FormValue("body")); err != nil {
117		h.writeError(w, r, err)
118		return
119	}
120	http.Redirect(w, r, repoURL(repo, "/pulls/"+strconv.Itoa(pr.Number)), http.StatusSeeOther)
121}
122
123func (h *Handlers) mergePull(w http.ResponseWriter, r *http.Request) {
124	repo, pr := h.loadPull(w, r)
125	if pr == nil {
126		return
127	}
128	err := h.pulls.Merge(r.Context(), repo, pr, currentUser(r.Context()))
129	if err == nil {
130		h.flash(r, "Pull request merged.")
131		http.Redirect(w, r, repoURL(repo, "/pulls/"+strconv.Itoa(pr.Number)), http.StatusSeeOther)
132		return
133	}
134	if errors.Is(err, domain.ErrConflict) {
135		h.renderPull(w, r, repo, pr, "Cannot merge automatically — the branches conflict.", http.StatusConflict)
136		return
137	}
138	h.writeError(w, r, err)
139}
140
141func (h *Handlers) closePull(w http.ResponseWriter, r *http.Request) {
142	repo, pr := h.loadPull(w, r)
143	if pr == nil {
144		return
145	}
146	if err := h.pulls.Close(r.Context(), repo, pr, currentUser(r.Context())); err != nil {
147		h.writeError(w, r, err)
148		return
149	}
150	http.Redirect(w, r, repoURL(repo, "/pulls/"+strconv.Itoa(pr.Number)), http.StatusSeeOther)
151}
152
153func (h *Handlers) renderPull(w http.ResponseWriter, r *http.Request, repo *domain.Repository, pr *domain.PullRequest, errMsg string, status int) {
154	cmp, _ := h.pulls.Compare(r.Context(), repo, pr.BaseBranch, pr.HeadBranch)
155	comments, err := h.pulls.Comments(r.Context(), pr.ID)
156	if err != nil {
157		h.writeError(w, r, err)
158		return
159	}
160	canMerge := pr.State == domain.PROpen && app.CanMergePR(repo, currentUser(r.Context())) && cmp != nil && cmp.Mergeable
161	render(w, r, status, templates.PullDetail(currentUser(r.Context()), repo, pr, cmp, comments, canMerge, errMsg))
162}
163
164func (h *Handlers) loadPull(w http.ResponseWriter, r *http.Request) (*domain.Repository, *domain.PullRequest) {
165	repo := h.viewRepo(w, r)
166	if repo == nil {
167		return nil, nil
168	}
169	number, err := strconv.Atoi(chi.URLParam(r, "number"))
170	if err != nil {
171		h.notFound(w, r)
172		return nil, nil
173	}
174	pr, err := h.pulls.Get(r.Context(), repo.ID, number)
175	if err != nil {
176		h.writeError(w, r, err)
177		return nil, nil
178	}
179	return repo, pr
180}