Mirror of @tangled.org/core. Running on a Raspberry Pi Zero 2 (Please be gentle).
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

appview: implement some basic pull request handlers

authored by

Anirudh Oppiliappan and committed by
Akshay
fb6cd3b5 ed53dbc8

+502 -53
+4 -1
appview/db/db.go
··· 109 109 repo_at text not null, 110 110 pull_id integer not null, 111 111 title text not null, 112 + body text not null, 112 113 patch text, 113 - patch_at text not null, 114 + pull_at text, 115 + rkey text not null, 116 + target_branch text not null, 114 117 open integer not null default 1, 115 118 created text not null default (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')), 116 119 unique(repo_at, pull_id),
+46 -43
appview/db/pulls.go
··· 7 7 "github.com/bluesky-social/indigo/atproto/syntax" 8 8 ) 9 9 10 - type Pulls struct { 11 - ID int `json:"id"` 12 - OwnerDid string `json:"owner_did"` 13 - RepoAt string `json:"repo_at"` 14 - PullId int `json:"pull_id"` 15 - Title string `json:"title"` 16 - Patch string `json:"patch,omitempty"` 17 - PatchAt string `json:"patch_at"` 18 - Open int `json:"open"` 19 - Created time.Time `json:"created"` 10 + type Pull struct { 11 + ID int 12 + OwnerDid string 13 + RepoAt syntax.ATURI 14 + PullAt syntax.ATURI 15 + TargetBranch string 16 + Patch string 17 + PullId int 18 + Title string 19 + Body string 20 + Open int 21 + Created time.Time 22 + Rkey string 20 23 } 21 24 22 - type PullComments struct { 23 - ID int `json:"id"` 24 - OwnerDid string `json:"owner_did"` 25 - PullId int `json:"pull_id"` 26 - RepoAt string `json:"repo_at"` 27 - CommentId int `json:"comment_id"` 28 - CommentAt string `json:"comment_at"` 29 - Body string `json:"body"` 30 - Created time.Time `json:"created"` 25 + type PullComment struct { 26 + ID int 27 + OwnerDid string 28 + PullId int 29 + RepoAt string 30 + CommentId int 31 + CommentAt string 32 + Body string 33 + Created time.Time 31 34 } 32 35 33 - func NewPull(tx *sql.Tx, pull *Pulls) error { 36 + func NewPull(tx *sql.Tx, pull *Pull) error { 34 37 defer tx.Rollback() 35 38 36 39 _, err := tx.Exec(` ··· 58 55 pull.PullId = nextId 59 56 60 57 _, err = tx.Exec(` 61 - insert into pulls (repo_at, owner_did, pull_id, title, patch) 62 - values (?, ?, ?, ?, ?) 63 - `, pull.RepoAt, pull.OwnerDid, pull.PullId, pull.Title, pull.Patch) 58 + insert into pulls (repo_at, owner_did, pull_id, title, target_branch, body, patch, rkey) 59 + values (?, ?, ?, ?, ?, ?, ?, ?) 60 + `, pull.RepoAt, pull.OwnerDid, pull.PullId, pull.Title, pull.TargetBranch, pull.Body, pull.Patch, pull.Rkey) 64 61 if err != nil { 65 62 return err 66 63 } ··· 73 70 } 74 71 75 72 func SetPullAt(e Execer, repoAt syntax.ATURI, pullId int, pullAt string) error { 76 - _, err := e.Exec(`update pulls set patch_at = ? where repo_at = ? and pull_id = ?`, pullAt, repoAt, pullId) 73 + _, err := e.Exec(`update pulls set pull_at = ? where repo_at = ? and pull_id = ?`, pullAt, repoAt, pullId) 77 74 return err 78 75 } 79 76 80 77 func GetPullAt(e Execer, repoAt syntax.ATURI, pullId int) (string, error) { 81 78 var pullAt string 82 - err := e.QueryRow(`select patch_at from pulls where repo_at = ? and pull_id = ?`, repoAt, pullId).Scan(&pullAt) 79 + err := e.QueryRow(`select pull_at from pulls where repo_at = ? and pull_id = ?`, repoAt, pullId).Scan(&pullAt) 83 80 return pullAt, err 84 81 } 85 82 ··· 95 92 return ownerDid, err 96 93 } 97 94 98 - func GetPulls(e Execer, repoAt syntax.ATURI) ([]Pulls, error) { 99 - var pulls []Pulls 95 + func GetPulls(e Execer, repoAt syntax.ATURI) ([]Pull, error) { 96 + var pulls []Pull 100 97 101 - rows, err := e.Query(`select owner_did, pull_id, created, title, patch, open from pulls where repo_at = ? order by created desc`, repoAt) 98 + rows, err := e.Query(`select owner_did, pull_id, created, title, open, target_branch, pull_at, body, patch, rkey from pulls where repo_at = ? order by created desc`, repoAt) 102 99 if err != nil { 103 100 return nil, err 104 101 } 105 102 defer rows.Close() 106 103 107 104 for rows.Next() { 108 - var pull Pulls 105 + var pull Pull 109 106 var createdAt string 110 - err := rows.Scan(&pull.OwnerDid, &pull.PullId, &createdAt, &pull.Title, &pull.Patch, &pull.Open) 107 + err := rows.Scan(&pull.OwnerDid, &pull.PullId, &createdAt, &pull.Title, &pull.Open, &pull.TargetBranch, &pull.PullAt, &pull.Body, &pull.Patch, &pull.Rkey) 111 108 if err != nil { 112 109 return nil, err 113 110 } ··· 128 125 return pulls, nil 129 126 } 130 127 131 - func GetPull(e Execer, repoAt syntax.ATURI, pullId int) (*Pulls, error) { 132 - query := `select owner_did, created, title, patch, open from pulls where repo_at = ? and pull_id = ?` 128 + func GetPull(e Execer, repoAt syntax.ATURI, pullId int) (*Pull, error) { 129 + query := `select owner_did, created, title, open, target_branch, pull_at, body, patch, rkey from pulls where repo_at = ? and pull_id = ?` 133 130 row := e.QueryRow(query, repoAt, pullId) 134 131 135 - var pull Pulls 132 + var pull Pull 136 133 var createdAt string 137 - err := row.Scan(&pull.OwnerDid, &createdAt, &pull.Title, &pull.Patch, &pull.Open) 134 + err := row.Scan(&pull.OwnerDid, &createdAt, &pull.Title, &pull.Open, &pull.TargetBranch, &pull.PullAt, &pull.Body, &pull.Patch, &pull.Rkey) 138 135 if err != nil { 139 136 return nil, err 140 137 } ··· 148 145 return &pull, nil 149 146 } 150 147 151 - func GetPullWithComments(e Execer, repoAt syntax.ATURI, pullId int) (*Pulls, []PullComments, error) { 152 - query := `select owner_did, pull_id, created, title, patch, open from pulls where repo_at = ? and pull_id = ?` 148 + func GetPullWithComments(e Execer, repoAt syntax.ATURI, pullId int) (*Pull, []PullComment, error) { 149 + query := `select owner_did, pull_id, created, title, open, target_branch, pull_at, body, patch, rkey from pulls where repo_at = ? and pull_id = ?` 153 150 row := e.QueryRow(query, repoAt, pullId) 154 151 155 - var pull Pulls 152 + var pull Pull 156 153 var createdAt string 157 - err := row.Scan(&pull.OwnerDid, &pull.PullId, &createdAt, &pull.Title, &pull.Patch, &pull.Open) 154 + err := row.Scan(&pull.OwnerDid, &pull.PullId, &createdAt, &pull.Title, &pull.Open, &pull.TargetBranch, &pull.PullAt, &pull.Body, &pull.Patch, &pull.Rkey) 158 155 if err != nil { 159 156 return nil, nil, err 160 157 } ··· 173 170 return &pull, comments, nil 174 171 } 175 172 176 - func NewPullComment(e Execer, comment *PullComments) error { 173 + func NewPullComment(e Execer, comment *PullComment) error { 177 174 query := `insert into pull_comments (owner_did, repo_at, comment_at, pull_id, comment_id, body) values (?, ?, ?, ?, ?, ?)` 178 175 _, err := e.Exec( 179 176 query, ··· 187 184 return err 188 185 } 189 186 190 - func GetPullComments(e Execer, repoAt syntax.ATURI, pullId int) ([]PullComments, error) { 191 - var comments []PullComments 187 + func GetPullComments(e Execer, repoAt syntax.ATURI, pullId int) ([]PullComment, error) { 188 + var comments []PullComment 192 189 193 190 rows, err := e.Query(`select owner_did, pull_id, comment_id, comment_at, body, created from pull_comments where repo_at = ? and pull_id = ? order by created asc`, repoAt, pullId) 194 191 if err == sql.ErrNoRows { 195 - return []PullComments{}, nil 192 + return []PullComment{}, nil 196 193 } 197 194 if err != nil { 198 195 return nil, err ··· 200 197 defer rows.Close() 201 198 202 199 for rows.Next() { 203 - var comment PullComments 200 + var comment PullComment 204 201 var createdAt string 205 202 err := rows.Scan(&comment.OwnerDid, &comment.PullId, &comment.CommentId, &comment.CommentAt, &comment.Body, &createdAt) 206 203 if err != nil {
+28 -1
appview/pages/pages.go
··· 506 506 return p.executeRepo("repo/issues/new", w, params) 507 507 } 508 508 509 - type RepoPullsParams struct { 509 + type RepoNewPullParams struct { 510 510 LoggedInUser *auth.User 511 511 RepoInfo RepoInfo 512 512 Active string 513 513 } 514 514 515 + func (p *Pages) RepoNewPull(w io.Writer, params RepoNewPullParams) error { 516 + params.Active = "pulls" 517 + return p.executeRepo("repo/pulls/new", w, params) 518 + } 519 + 520 + type RepoPullsParams struct { 521 + LoggedInUser *auth.User 522 + RepoInfo RepoInfo 523 + Pulls []db.Pull 524 + Active string 525 + DidHandleMap map[string]string 526 + } 527 + 515 528 func (p *Pages) RepoPulls(w io.Writer, params RepoPullsParams) error { 516 529 params.Active = "pulls" 517 530 return p.executeRepo("repo/pulls/pulls", w, params) 531 + } 532 + 533 + type RepoSinglePullParams struct { 534 + LoggedInUser *auth.User 535 + RepoInfo RepoInfo 536 + DidHandleMap map[string]string 537 + Pull db.Pull 538 + Comments []db.PullComment 539 + Active string 540 + } 541 + 542 + func (p *Pages) RepoSinglePull(w io.Writer, params RepoSinglePullParams) error { 543 + params.Active = "pulls" 544 + return p.executeRepo("repo/pulls/pull", w, params) 518 545 } 519 546 520 547 func (p *Pages) Static() http.Handler {
+38
appview/pages/templates/repo/pulls/new.html
··· 1 + {{ define "title" }}new pull | {{ .RepoInfo.FullName }}{{ end }} 2 + 3 + {{ define "repoContent" }} 4 + <form 5 + hx-post="/{{ .RepoInfo.FullName }}/pulls/new" 6 + class="mt-6 space-y-6" 7 + hx-swap="none" 8 + > 9 + <div class="flex flex-col gap-4"> 10 + <div> 11 + <label for="title">title</label> 12 + <input type="text" name="title" id="title" class="w-full" /> 13 + <input type="text" name="targetBranch" id="targetBranch" /> 14 + </div> 15 + <div> 16 + <label for="body">body</label> 17 + <textarea 18 + name="body" 19 + id="body" 20 + rows="6" 21 + class="w-full resize-y" 22 + placeholder="Describe your change. Markdown is supported." 23 + ></textarea> 24 + <textarea 25 + name="patch" 26 + id="patch" 27 + rows="10" 28 + class="w-full resize-y font-mono" 29 + placeholder="Paste your git-format-patch output here." 30 + ></textarea> 31 + </div> 32 + <div> 33 + <button type="submit" class="btn">create</button> 34 + </div> 35 + </div> 36 + <div id="pull" class="error"></div> 37 + </form> 38 + {{ end }}
+133
appview/pages/templates/repo/pulls/pull.html
··· 1 + {{ define "title" }} 2 + {{ .Pull.Title }} &middot; 3 + {{ .RepoInfo.FullName }} 4 + {{ end }} 5 + 6 + {{ define "repoContent" }} 7 + <h1> 8 + {{ .Pull.Title }} 9 + <span class="text-gray-400">#{{ .Pull.PullId }}</span> 10 + </h1> 11 + 12 + {{ $bgColor := "bg-gray-800" }} 13 + {{ $icon := "ban" }} 14 + {{ if eq .State "open" }} 15 + {{ $bgColor = "bg-green-600" }} 16 + {{ $icon = "circle-dot" }} 17 + {{ end }} 18 + 19 + 20 + <section> 21 + <div class="flex items-center gap-2"> 22 + <div 23 + id="state" 24 + class="inline-flex items-center rounded px-3 py-1 {{ $bgColor }}" 25 + > 26 + <i 27 + data-lucide="{{ $icon }}" 28 + class="w-4 h-4 mr-1.5 text-white" 29 + ></i> 30 + <span class="text-white">{{ .State }}</span> 31 + </div> 32 + <span class="text-gray-400 text-sm"> 33 + opened by 34 + {{ $owner := didOrHandle .Pull.OwnerDid .PullOwnerHandle }} 35 + <a href="/{{ $owner }}" class="no-underline hover:underline" 36 + >{{ $owner }}</a 37 + > 38 + <span class="px-1 select-none before:content-['\00B7']"></span> 39 + <time>{{ .Pull.Created | timeFmt }}</time> 40 + </span> 41 + </div> 42 + 43 + {{ if .Pull.Body }} 44 + <article id="body" class="mt-8 prose"> 45 + {{ .Pull.Body | markdown }} 46 + </article> 47 + {{ end }} 48 + </section> 49 + {{ end }} 50 + 51 + {{ define "repoAfter" }} 52 + <section id="comments" class="mt-8 space-y-4 relative"> 53 + {{ range $index, $comment := .Comments }} 54 + <div 55 + id="comment-{{ .CommentId }}" 56 + class="rounded bg-white p-4 relative" 57 + > 58 + {{ if eq $index 0 }} 59 + <div 60 + class="absolute left-8 -top-8 w-px h-8 bg-gray-300" 61 + ></div> 62 + {{ else }} 63 + <div 64 + class="absolute left-8 -top-4 w-px h-4 bg-gray-300" 65 + ></div> 66 + {{ end }} 67 + <div class="flex items-center gap-2 mb-2 text-gray-400"> 68 + {{ $owner := index $.DidHandleMap .OwnerDid }} 69 + <span class="text-sm"> 70 + <a 71 + href="/{{ $owner }}" 72 + class="no-underline hover:underline" 73 + >{{ $owner }}</a 74 + > 75 + </span> 76 + <span 77 + class="px-1 select-none before:content-['\00B7']" 78 + ></span> 79 + <a 80 + href="#{{ .CommentId }}" 81 + class="text-gray-500 text-sm hover:text-gray-500 hover:underline no-underline" 82 + id="{{ .CommentId }}" 83 + > 84 + {{ .Created | timeFmt }} 85 + </a> 86 + </div> 87 + <div class="prose"> 88 + {{ .Body | markdown }} 89 + </div> 90 + </div> 91 + {{ end }} 92 + </section> 93 + 94 + {{ if .LoggedInUser }} 95 + <form 96 + hx-post="/{{ .RepoInfo.FullName }}/pulls/{{ .Pull.PullId }}/comment" 97 + class="mt-8" 98 + > 99 + <textarea 100 + name="body" 101 + class="w-full p-2 rounded border border-gray-200" 102 + placeholder="Add to the discussion..." 103 + ></textarea> 104 + <button type="submit" class="btn mt-2">comment</button> 105 + <div id="pull-comment"></div> 106 + </form> 107 + {{ end }} 108 + 109 + {{ if eq .LoggedInUser.Did .Pull.OwnerDid }} 110 + {{ $action := "close" }} 111 + {{ $icon := "circle-x" }} 112 + {{ $hoverColor := "red" }} 113 + {{ if eq .State "closed" }} 114 + {{ $action = "reopen" }} 115 + {{ $icon = "circle-dot" }} 116 + {{ $hoverColor = "green" }} 117 + {{ end }} 118 + <form 119 + hx-post="/{{ .RepoInfo.FullName }}/pulls/{{ .Pull.PullId }}/{{ $action }}" 120 + hx-swap="none" 121 + class="mt-8" 122 + > 123 + <button type="submit" class="btn hover:bg-{{ $hoverColor }}-300"> 124 + <i 125 + data-lucide="{{ $icon }}" 126 + class="w-4 h-4 mr-2 text-{{ $hoverColor }}-400" 127 + ></i> 128 + <span class="text-black">{{ $action }}</span> 129 + </button> 130 + <div id="pull-action" class="error"></div> 131 + </form> 132 + {{ end }} 133 + {{ end }}
+221 -6
appview/state/repo.go
··· 234 234 } 235 235 } 236 236 237 + // MergeCheck gets called async, every time the patch diff is updated in a pull. 238 + func (s *State) MergeCheck(w http.ResponseWriter, r *http.Request) { 239 + user := s.auth.GetUser(r) 240 + f, err := fullyResolvedRepo(r) 241 + if err != nil { 242 + log.Println("failed to get repo and knot", err) 243 + s.pages.Notice(w, "pull", "Failed to check mergeability. Try again later.") 244 + return 245 + } 246 + 247 + patch := r.FormValue("patch") 248 + targetBranch := r.FormValue("targetBranch") 249 + 250 + if patch == "" || targetBranch == "" { 251 + s.pages.Notice(w, "pull", "Patch and target branch are required.") 252 + return 253 + } 254 + 255 + secret, err := db.GetRegistrationKey(s.db, f.Knot) 256 + if err != nil { 257 + log.Printf("no key found for domain %s: %s\n", f.Knot, err) 258 + s.pages.Notice(w, "pull", "Failed to check mergeability. Try again later.") 259 + return 260 + } 261 + 262 + ksClient, err := NewSignedClient(f.Knot, secret, s.config.Dev) 263 + if err != nil { 264 + log.Printf("failed to create signed client for %s", f.Knot) 265 + s.pages.Notice(w, "pull", "Failed to check mergeability. Try again later.") 266 + return 267 + } 268 + 269 + resp, err := ksClient.MergeCheck([]byte(patch), user.Did, f.RepoName, targetBranch) 270 + if err != nil { 271 + log.Println("failed to check mergeability", err) 272 + s.pages.Notice(w, "pull", "Unable to check for mergeability. Try again later.") 273 + return 274 + } 275 + 276 + respBody, err := io.ReadAll(resp.Body) 277 + if err != nil { 278 + log.Println("failed to read knotserver response body") 279 + s.pages.Notice(w, "pull", "Unable to check for mergeability. Try again later.") 280 + return 281 + } 282 + 283 + var mergeCheckResponse types.MergeCheckResponse 284 + err = json.Unmarshal(respBody, &mergeCheckResponse) 285 + if err != nil { 286 + log.Println("failed to unmarshal merge check response", err) 287 + s.pages.Notice(w, "pull", "Failed to check mergeability. Try again later.") 288 + return 289 + } 290 + 291 + // TODO: this has to return a html fragment 292 + w.Header().Set("Content-Type", "application/json") 293 + json.NewEncoder(w).Encode(mergeCheckResponse) 294 + } 295 + 296 + func (s *State) NewPull(w http.ResponseWriter, r *http.Request) { 297 + user := s.auth.GetUser(r) 298 + f, err := fullyResolvedRepo(r) 299 + if err != nil { 300 + log.Println("failed to get repo and knot", err) 301 + return 302 + } 303 + 304 + switch r.Method { 305 + case http.MethodGet: 306 + s.pages.RepoNewPull(w, pages.RepoNewPullParams{ 307 + LoggedInUser: user, 308 + RepoInfo: f.RepoInfo(s, user), 309 + }) 310 + case http.MethodPost: 311 + title := r.FormValue("title") 312 + body := r.FormValue("body") 313 + targetBranch := r.FormValue("targetBranch") 314 + patch := r.FormValue("patch") 315 + 316 + if title == "" || body == "" || patch == "" { 317 + s.pages.Notice(w, "pull", "Title, body and patch diff are required.") 318 + return 319 + } 320 + 321 + tx, err := s.db.BeginTx(r.Context(), nil) 322 + if err != nil { 323 + log.Println("failed to start tx") 324 + s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.") 325 + return 326 + } 327 + 328 + defer func() { 329 + tx.Rollback() 330 + err = s.enforcer.E.LoadPolicy() 331 + if err != nil { 332 + log.Println("failed to rollback policies") 333 + } 334 + }() 335 + 336 + err = db.NewPull(tx, &db.Pull{ 337 + Title: title, 338 + Body: body, 339 + TargetBranch: targetBranch, 340 + Patch: patch, 341 + OwnerDid: user.Did, 342 + RepoAt: f.RepoAt, 343 + }) 344 + if err != nil { 345 + log.Println("failed to create pull request", err) 346 + s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.") 347 + return 348 + } 349 + client, _ := s.auth.AuthorizedClient(r) 350 + pullId, err := db.GetPullId(s.db, f.RepoAt) 351 + if err != nil { 352 + log.Println("failed to get pull id", err) 353 + s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.") 354 + return 355 + } 356 + 357 + atResp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ 358 + Collection: tangled.RepoPullNSID, 359 + Repo: user.Did, 360 + Rkey: s.TID(), 361 + Record: &lexutil.LexiconTypeDecoder{ 362 + Val: &tangled.RepoPull{ 363 + Title: title, 364 + PullId: int64(pullId), 365 + TargetRepo: string(f.RepoAt), 366 + TargetBranch: targetBranch, 367 + Patch: patch, 368 + }, 369 + }, 370 + }) 371 + 372 + err = db.SetPullAt(s.db, f.RepoAt, pullId, atResp.Uri) 373 + if err != nil { 374 + log.Println("failed to get pull id", err) 375 + s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.") 376 + return 377 + } 378 + 379 + s.pages.HxLocation(w, fmt.Sprintf("/%s/pulls/%d", f.OwnerSlashRepo(), pullId)) 380 + return 381 + } 382 + } 383 + 384 + func (s *State) RepoSinglePull(w http.ResponseWriter, r *http.Request) { 385 + user := s.auth.GetUser(r) 386 + f, err := fullyResolvedRepo(r) 387 + if err != nil { 388 + log.Println("failed to get repo and knot", err) 389 + return 390 + } 391 + 392 + prId := chi.URLParam(r, "pull") 393 + prIdInt, err := strconv.Atoi(prId) 394 + if err != nil { 395 + http.Error(w, "bad pr id", http.StatusBadRequest) 396 + log.Println("failed to parse pr id", err) 397 + return 398 + } 399 + 400 + pr, comments, err := db.GetPullWithComments(s.db, f.RepoAt, prIdInt) 401 + if err != nil { 402 + log.Println("failed to get pr and comments", err) 403 + s.pages.Notice(w, "pull", "Failed to load pull request. Try again later.") 404 + return 405 + } 406 + 407 + identsToResolve := make([]string, len(comments)) 408 + for i, comment := range comments { 409 + identsToResolve[i] = comment.OwnerDid 410 + } 411 + resolvedIds := s.resolver.ResolveIdents(r.Context(), identsToResolve) 412 + didHandleMap := make(map[string]string) 413 + for _, identity := range resolvedIds { 414 + if !identity.Handle.IsInvalidHandle() { 415 + didHandleMap[identity.DID.String()] = fmt.Sprintf("@%s", identity.Handle.String()) 416 + } else { 417 + didHandleMap[identity.DID.String()] = identity.DID.String() 418 + } 419 + } 420 + 421 + s.pages.RepoSinglePull(w, pages.RepoSinglePullParams{ 422 + LoggedInUser: user, 423 + RepoInfo: f.RepoInfo(s, user), 424 + Pull: *pr, 425 + Comments: comments, 426 + 427 + DidHandleMap: didHandleMap, 428 + }) 429 + } 430 + 237 431 func (s *State) RepoCommit(w http.ResponseWriter, r *http.Request) { 238 432 f, err := fullyResolvedRepo(r) 239 433 if err != nil { ··· 1265 1071 return 1266 1072 } 1267 1073 1268 - switch r.Method { 1269 - case http.MethodGet: 1270 - s.pages.RepoPulls(w, pages.RepoPullsParams{ 1271 - LoggedInUser: user, 1272 - RepoInfo: f.RepoInfo(s, user), 1273 - }) 1074 + pulls, err := db.GetPulls(s.db, f.RepoAt) 1075 + if err != nil { 1076 + log.Println("failed to get pulls", err) 1077 + s.pages.Notice(w, "pulls", "Failed to load pulls. Try again later.") 1078 + return 1274 1079 } 1080 + 1081 + identsToResolve := make([]string, len(pulls)) 1082 + for i, pull := range pulls { 1083 + identsToResolve[i] = pull.OwnerDid 1084 + } 1085 + resolvedIds := s.resolver.ResolveIdents(r.Context(), identsToResolve) 1086 + didHandleMap := make(map[string]string) 1087 + for _, identity := range resolvedIds { 1088 + if !identity.Handle.IsInvalidHandle() { 1089 + didHandleMap[identity.DID.String()] = fmt.Sprintf("@%s", identity.Handle.String()) 1090 + } else { 1091 + didHandleMap[identity.DID.String()] = identity.DID.String() 1092 + } 1093 + } 1094 + 1095 + s.pages.RepoPulls(w, pages.RepoPullsParams{ 1096 + LoggedInUser: s.auth.GetUser(r), 1097 + RepoInfo: f.RepoInfo(s, user), 1098 + Pulls: pulls, 1099 + DidHandleMap: didHandleMap, 1100 + }) 1101 + return 1275 1102 } 1276 1103 1277 1104 func fullyResolvedRepo(r *http.Request) (*FullyResolvedRepo, error) {
+19
appview/state/signer.go
··· 174 174 175 175 return s.client.Do(req) 176 176 } 177 + 178 + func (s *SignedClient) MergeCheck(patch []byte, ownerDid, targetRepo, branch string) (*http.Response, error) { 179 + const ( 180 + Method = "POST" 181 + ) 182 + endpoint := fmt.Sprintf("/%s/%s/merge/check", ownerDid, targetRepo) 183 + 184 + body, _ := json.Marshal(map[string]interface{}{ 185 + "patch": string(patch), 186 + "branch": branch, 187 + }) 188 + 189 + req, err := s.newRequest(Method, endpoint, body) 190 + if err != nil { 191 + return nil, err 192 + } 193 + 194 + return s.client.Do(req) 195 + }
+11
appview/state/state.go
··· 881 881 882 882 r.Route("/pulls", func(r chi.Router) { 883 883 r.Get("/", s.RepoPulls) 884 + r.Get("/{pull}", s.RepoSinglePull) 885 + 886 + r.Group(func(r chi.Router) { 887 + r.Use(AuthMiddleware(s)) 888 + r.Get("/new", s.NewPull) 889 + r.Post("/new", s.NewPull) 890 + // r.Post("/{pull}/comment", s.PullComment) 891 + // r.Post("/{pull}/close", s.ClosePull) 892 + // r.Post("/{pull}/reopen", s.ReopenPull) 893 + // r.Post("/{pull}/merge", s.MergePull) 894 + }) 884 895 }) 885 896 886 897 // These routes get proxied to the knot
+2 -2
cmd/gen.go
··· 22 22 shtangled.RepoIssueState{}, 23 23 shtangled.RepoIssue{}, 24 24 shtangled.Repo{}, 25 - shtangled.RepoPullPatch{}, 26 - shtangled.RepoPullState{}, 25 + shtangled.RepoPull{}, 26 + shtangled.RepoPullStatus{}, 27 27 shtangled.RepoPullComment{}, 28 28 ); err != nil { 29 29 panic(err)