internal/httpd/mractions.go

b3b4b490f0b68b6589df1a9e18f145cc28c1923c
gitbay/internal/httpd/mractions.go history · blame · raw

157 lines · 4454 bytes

  1package httpd
  2
  3import (
  4	"fmt"
  5	"net/http"
  6	"net/url"
  7	"strconv"
  8	"strings"
  9
 10	"gitbay.org/gitbay/internal/gitutil"
 11	"gitbay.org/gitbay/internal/store"
 12)
 13
 14// Merge request actions. Each one runs the control command the CLI runs,
 15// so review rules, merge gates, and audit entries have a single
 16// implementation; the browser only chooses arguments and shows the
 17// result.
 18
 19// mrRedirect returns to the merge request, carrying a failure message the
 20// page renders as a banner.
 21func (s *Server) mrRedirect(w http.ResponseWriter, r *http.Request, msg string) {
 22	dest := fmt.Sprintf("/%s/%s/mrs/%s",
 23		r.PathValue("owner"), r.PathValue("repo"), r.PathValue("n"))
 24	if msg != "" {
 25		if len(msg) > 300 {
 26			msg = msg[:300]
 27		}
 28		dest += "?e=" + url.QueryEscape(msg)
 29	}
 30	http.Redirect(w, r, dest, http.StatusSeeOther)
 31}
 32
 33// mrArgs builds "<verb> owner/name <n>" for the mr command family.
 34func mrArgs(r *http.Request, verb string, extra ...string) []string {
 35	repo := r.PathValue("owner") + "/" + r.PathValue("repo")
 36	return append([]string{"mr", verb, repo, r.PathValue("n")}, extra...)
 37}
 38
 39func (s *Server) mrReviewSubmit(w http.ResponseWriter, r *http.Request, u store.User) {
 40	flag := map[string]string{
 41		"approve":         "--approve",
 42		"request-changes": "--request-changes",
 43		"comment":         "--comment",
 44	}[r.FormValue("verdict")]
 45	if flag == "" {
 46		s.mrRedirect(w, r, "pick approve, request changes, or comment")
 47		return
 48	}
 49	_, msg, ok := s.runControl(u, mrArgs(r, "review", flag))
 50	if ok {
 51		msg = ""
 52	}
 53	s.mrRedirect(w, r, msg)
 54}
 55
 56func (s *Server) mrMergeSubmit(w http.ResponseWriter, r *http.Request, u store.User) {
 57	args := []string{}
 58	if st := strings.TrimSpace(r.FormValue("strategy")); st != "" && st != "auto" {
 59		args = append(args, "--strategy", st)
 60	}
 61	_, msg, ok := s.runControl(u, mrArgs(r, "merge", args...))
 62	if ok {
 63		msg = ""
 64	}
 65	s.mrRedirect(w, r, msg)
 66}
 67
 68func (s *Server) mrCloseSubmit(w http.ResponseWriter, r *http.Request, u store.User) {
 69	_, msg, ok := s.runControl(u, mrArgs(r, "close"))
 70	if ok {
 71		msg = ""
 72	}
 73	s.mrRedirect(w, r, msg)
 74}
 75
 76// mrThreadSubmit resolves or reopens one review thread.
 77func (s *Server) mrThreadSubmit(w http.ResponseWriter, r *http.Request, u store.User) {
 78	verb := "resolve"
 79	if r.FormValue("action") == "unresolve" {
 80		verb = "unresolve"
 81	}
 82	id := strings.TrimSpace(r.FormValue("thread"))
 83	if _, err := strconv.ParseInt(id, 10, 64); err != nil {
 84		s.mrRedirect(w, r, "bad thread id")
 85		return
 86	}
 87	_, msg, ok := s.runControl(u, mrArgs(r, verb, id))
 88	if ok {
 89		msg = ""
 90	}
 91	s.mrRedirect(w, r, msg)
 92}
 93
 94// mrNewPage is the create form: branches to choose from, plus whatever
 95// the last attempt had in it so a refusal does not lose the draft.
 96type mrNewPage struct {
 97	repoPage
 98	Branches []gitutil.Ref
 99	Source   string
100	Target   string
101	Title    string
102	Body     string
103	Notice   string
104}
105
106func (s *Server) mrCreateForm(w http.ResponseWriter, r *http.Request, u store.User) {
107	p, ok := s.repoFor(w, r, "")
108	if !ok {
109		return
110	}
111	p.Tab = "merge requests"
112	branches, _ := gitutil.Refs(p.Dir, "heads")
113	q := r.URL.Query()
114	target := q.Get("target")
115	if target == "" {
116		target = p.Repo.DefaultBranch
117	}
118	s.render(w, "mrnew.html", mrNewPage{
119		repoPage: p, Branches: branches,
120		Source: q.Get("source"), Target: target,
121		Title: q.Get("title"), Body: q.Get("body"), Notice: q.Get("e"),
122	})
123}
124
125func (s *Server) mrCreateSubmit(w http.ResponseWriter, r *http.Request, u store.User) {
126	p, ok := s.repoFor(w, r, "")
127	if !ok {
128		return
129	}
130	source := strings.TrimSpace(r.FormValue("source"))
131	target := strings.TrimSpace(r.FormValue("target"))
132	title := strings.TrimSpace(r.FormValue("title"))
133	body := strings.TrimSpace(r.FormValue("body"))
134
135	back := func(msg string) {
136		q := url.Values{"source": {source}, "target": {target}, "title": {title}, "body": {body}, "e": {msg}}
137		http.Redirect(w, r, fmt.Sprintf("/%s/mrs/new?%s", p.Repo.Path(), q.Encode()), http.StatusSeeOther)
138	}
139	if source == "" || title == "" {
140		back("pick a source branch and give the merge request a title")
141		return
142	}
143	argv := []string{"mr", "create", p.Repo.Path(), "--source", source, "--title", title}
144	if target != "" {
145		argv = append(argv, "--target", target)
146	}
147	if body != "" {
148		argv = append(argv, "--body", body)
149	}
150	data, msg, ok := s.runControlJSON(u, argv)
151	if !ok {
152		back(msg)
153		return
154	}
155	n, _ := data["number"].(float64)
156	http.Redirect(w, r, fmt.Sprintf("/%s/mrs/%d", p.Repo.Path(), int64(n)), http.StatusSeeOther)
157}