internal/httpd/mractions.go
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}