forked from tangled.org/core
Monorepo for Tangled — https://tangled.org
at master 5.6 kB view raw
1package repo 2 3import ( 4 "encoding/json" 5 "fmt" 6 "net/http" 7 "net/url" 8 "strings" 9 10 "tangled.org/core/api/tangled" 11 "tangled.org/core/appview/pages" 12 xrpcclient "tangled.org/core/appview/xrpcclient" 13 "tangled.org/core/patchutil" 14 "tangled.org/core/types" 15 16 indigoxrpc "github.com/bluesky-social/indigo/xrpc" 17 "github.com/go-chi/chi/v5" 18) 19 20func (rp *Repo) CompareNew(w http.ResponseWriter, r *http.Request) { 21 l := rp.logger.With("handler", "RepoCompareNew") 22 23 user := rp.oauth.GetUser(r) 24 f, err := rp.repoResolver.Resolve(r) 25 if err != nil { 26 l.Error("failed to get repo and knot", "err", err) 27 return 28 } 29 30 scheme := "http" 31 if !rp.config.Core.Dev { 32 scheme = "https" 33 } 34 host := fmt.Sprintf("%s://%s", scheme, f.Knot) 35 xrpcc := &indigoxrpc.Client{ 36 Host: host, 37 } 38 39 repo := fmt.Sprintf("%s/%s", f.Did, f.Name) 40 branchBytes, err := tangled.RepoBranches(r.Context(), xrpcc, "", 0, repo) 41 if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil { 42 l.Error("failed to call XRPC repo.branches", "err", xrpcerr) 43 rp.pages.Error503(w) 44 return 45 } 46 47 var branchResult types.RepoBranchesResponse 48 if err := json.Unmarshal(branchBytes, &branchResult); err != nil { 49 l.Error("failed to decode XRPC branches response", "err", err) 50 rp.pages.Notice(w, "compare-error", "Failed to produce comparison. Try again later.") 51 return 52 } 53 branches := branchResult.Branches 54 55 sortBranches(branches) 56 57 var defaultBranch string 58 for _, b := range branches { 59 if b.IsDefault { 60 defaultBranch = b.Name 61 } 62 } 63 64 base := defaultBranch 65 head := defaultBranch 66 67 params := r.URL.Query() 68 queryBase := params.Get("base") 69 queryHead := params.Get("head") 70 if queryBase != "" { 71 base = queryBase 72 } 73 if queryHead != "" { 74 head = queryHead 75 } 76 77 tagBytes, err := tangled.RepoTags(r.Context(), xrpcc, "", 0, repo) 78 if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil { 79 l.Error("failed to call XRPC repo.tags", "err", xrpcerr) 80 rp.pages.Error503(w) 81 return 82 } 83 84 var tags types.RepoTagsResponse 85 if err := json.Unmarshal(tagBytes, &tags); err != nil { 86 l.Error("failed to decode XRPC tags response", "err", err) 87 rp.pages.Notice(w, "compare-error", "Failed to produce comparison. Try again later.") 88 return 89 } 90 91 rp.pages.RepoCompareNew(w, pages.RepoCompareNewParams{ 92 LoggedInUser: user, 93 RepoInfo: rp.repoResolver.GetRepoInfo(r, user), 94 Branches: branches, 95 Tags: tags.Tags, 96 Base: base, 97 Head: head, 98 }) 99} 100 101func (rp *Repo) Compare(w http.ResponseWriter, r *http.Request) { 102 l := rp.logger.With("handler", "RepoCompare") 103 104 user := rp.oauth.GetUser(r) 105 f, err := rp.repoResolver.Resolve(r) 106 if err != nil { 107 l.Error("failed to get repo and knot", "err", err) 108 return 109 } 110 111 var diffOpts types.DiffOpts 112 if d := r.URL.Query().Get("diff"); d == "split" { 113 diffOpts.Split = true 114 } 115 116 // if user is navigating to one of 117 // /compare/{base}...{head} 118 // /compare/{base}/{head} 119 var base, head string 120 rest := chi.URLParam(r, "*") 121 122 var parts []string 123 if strings.Contains(rest, "...") { 124 parts = strings.SplitN(rest, "...", 2) 125 } else if strings.Contains(rest, "/") { 126 parts = strings.SplitN(rest, "/", 2) 127 } 128 129 if len(parts) == 2 { 130 base = parts[0] 131 head = parts[1] 132 } 133 134 base, _ = url.PathUnescape(base) 135 head, _ = url.PathUnescape(head) 136 137 if base == "" || head == "" { 138 l.Error("invalid comparison") 139 rp.pages.Error404(w) 140 return 141 } 142 143 scheme := "http" 144 if !rp.config.Core.Dev { 145 scheme = "https" 146 } 147 host := fmt.Sprintf("%s://%s", scheme, f.Knot) 148 xrpcc := &indigoxrpc.Client{ 149 Host: host, 150 } 151 152 repo := fmt.Sprintf("%s/%s", f.Did, f.Name) 153 154 branchBytes, err := tangled.RepoBranches(r.Context(), xrpcc, "", 0, repo) 155 if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil { 156 l.Error("failed to call XRPC repo.branches", "err", xrpcerr) 157 rp.pages.Error503(w) 158 return 159 } 160 161 var branches types.RepoBranchesResponse 162 if err := json.Unmarshal(branchBytes, &branches); err != nil { 163 l.Error("failed to decode XRPC branches response", "err", err) 164 rp.pages.Notice(w, "compare-error", "Failed to produce comparison. Try again later.") 165 return 166 } 167 168 tagBytes, err := tangled.RepoTags(r.Context(), xrpcc, "", 0, repo) 169 if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil { 170 l.Error("failed to call XRPC repo.tags", "err", xrpcerr) 171 rp.pages.Error503(w) 172 return 173 } 174 175 var tags types.RepoTagsResponse 176 if err := json.Unmarshal(tagBytes, &tags); err != nil { 177 l.Error("failed to decode XRPC tags response", "err", err) 178 rp.pages.Notice(w, "compare-error", "Failed to produce comparison. Try again later.") 179 return 180 } 181 182 compareBytes, err := tangled.RepoCompare(r.Context(), xrpcc, repo, base, head) 183 if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil { 184 l.Error("failed to call XRPC repo.compare", "err", xrpcerr) 185 rp.pages.Error503(w) 186 return 187 } 188 189 var formatPatch types.RepoFormatPatchResponse 190 if err := json.Unmarshal(compareBytes, &formatPatch); err != nil { 191 l.Error("failed to decode XRPC compare response", "err", err) 192 rp.pages.Notice(w, "compare-error", "Failed to produce comparison. Try again later.") 193 return 194 } 195 196 var diff types.NiceDiff 197 if formatPatch.CombinedPatchRaw != "" { 198 diff = patchutil.AsNiceDiff(formatPatch.CombinedPatchRaw, base) 199 } else { 200 diff = patchutil.AsNiceDiff(formatPatch.FormatPatchRaw, base) 201 } 202 203 rp.pages.RepoCompare(w, pages.RepoCompareParams{ 204 LoggedInUser: user, 205 RepoInfo: rp.repoResolver.GetRepoInfo(r, user), 206 Branches: branches.Branches, 207 Tags: tags.Tags, 208 Base: base, 209 Head: head, 210 Diff: &diff, 211 DiffOpts: diffOpts, 212 }) 213 214}