Monorepo for Tangled tangled.org
2

Configure Feed

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

at icy/qmlqxq 5.0 kB View raw
1package pulls 2 3import ( 4 "fmt" 5 "net/http" 6 7 "tangled.org/core/appview/db" 8 "tangled.org/core/appview/models" 9 "tangled.org/core/appview/reporesolver" 10 "tangled.org/core/orm" 11 12 "github.com/bluesky-social/indigo/atproto/syntax" 13) 14 15func (s *Pulls) ClosePull(w http.ResponseWriter, r *http.Request) { 16 l := s.logger.With("handler", "ClosePull") 17 18 user := s.oauth.GetMultiAccountUser(r) 19 if user != nil { 20 l = l.With("user", user.Did) 21 } 22 23 f, err := s.repoResolver.Resolve(r) 24 if err != nil { 25 l.Error("failed to resolve repo", "err", err) 26 return 27 } 28 29 pull, ok := r.Context().Value("pull").(*models.Pull) 30 if !ok { 31 l.Error("failed to get pull") 32 s.pages.Notice(w, "pull-error", "Failed to edit patch. Try again later.") 33 return 34 } 35 l = l.With("pull_id", pull.PullId, "pull_owner", pull.OwnerDid) 36 37 // auth filter: only owner or collaborators can close 38 roles := s.acl.RolesInRepo(r.Context(), f, user.Did) 39 isOwner := roles.IsOwner() 40 isCollaborator := roles.IsCollaborator() 41 isPullAuthor := user.Did == pull.OwnerDid 42 isCloseAllowed := isOwner || isCollaborator || isPullAuthor 43 if !isCloseAllowed { 44 l.Error("unauthorized to close pull", "is_owner", isOwner, "is_collaborator", isCollaborator, "is_pull_author", isPullAuthor) 45 s.pages.Notice(w, "pull-close", "You are unauthorized to close this pull.") 46 return 47 } 48 49 // Start a transaction 50 tx, err := s.db.BeginTx(r.Context(), nil) 51 if err != nil { 52 l.Error("failed to start transaction", "err", err) 53 s.pages.Notice(w, "pull-close", "Failed to close pull.") 54 return 55 } 56 defer tx.Rollback() 57 58 // if this PR is stacked, then we want to close all PRs above this one on the stack 59 stack := r.Context().Value("stack").(models.Stack) 60 pullsToClose := stack.Above(pull) 61 var atUris []syntax.ATURI 62 for _, p := range pullsToClose { 63 atUris = append(atUris, p.AtUri()) 64 p.State = models.PullClosed 65 } 66 err = db.ClosePulls( 67 tx, 68 orm.FilterEq("repo_did", string(f.RepoDid)), 69 orm.FilterIn("at_uri", atUris), 70 ) 71 if err != nil { 72 l.Error("failed to close pulls in database", "err", err, "pulls_to_close", len(pullsToClose)) 73 s.pages.Notice(w, "pull-close", "Failed to close pull.") 74 } 75 76 // Commit the transaction 77 if err = tx.Commit(); err != nil { 78 l.Error("failed to commit transaction", "err", err) 79 s.pages.Notice(w, "pull-close", "Failed to close pull.") 80 return 81 } 82 83 for _, p := range pullsToClose { 84 s.notifier.NewPullState(r.Context(), syntax.DID(user.Did), p) 85 } 86 87 ownerSlashRepo := reporesolver.GetBaseRepoPath(r, f) 88 s.pages.HxLocation(w, fmt.Sprintf("/%s/pulls/%d", ownerSlashRepo, pull.PullId)) 89} 90 91func (s *Pulls) ReopenPull(w http.ResponseWriter, r *http.Request) { 92 l := s.logger.With("handler", "ReopenPull") 93 94 user := s.oauth.GetMultiAccountUser(r) 95 if user != nil { 96 l = l.With("user", user.Did) 97 } 98 99 f, err := s.repoResolver.Resolve(r) 100 if err != nil { 101 l.Error("failed to resolve repo", "err", err) 102 s.pages.Notice(w, "pull-reopen", "Failed to reopen pull.") 103 return 104 } 105 106 pull, ok := r.Context().Value("pull").(*models.Pull) 107 if !ok { 108 l.Error("failed to get pull") 109 s.pages.Notice(w, "pull-error", "Failed to edit patch. Try again later.") 110 return 111 } 112 l = l.With("pull_id", pull.PullId, "pull_owner", pull.OwnerDid, "state", pull.State) 113 114 // auth filter: only owner or collaborators can close 115 roles := s.acl.RolesInRepo(r.Context(), f, user.Did) 116 isOwner := roles.IsOwner() 117 isCollaborator := roles.IsCollaborator() 118 isPullAuthor := user.Did == pull.OwnerDid 119 isCloseAllowed := isOwner || isCollaborator || isPullAuthor 120 if !isCloseAllowed { 121 l.Error("unauthorized to reopen pull", "is_owner", isOwner, "is_collaborator", isCollaborator, "is_pull_author", isPullAuthor) 122 s.pages.Notice(w, "pull-close", "You are unauthorized to close this pull.") 123 return 124 } 125 126 // Start a transaction 127 tx, err := s.db.BeginTx(r.Context(), nil) 128 if err != nil { 129 l.Error("failed to start transaction", "err", err) 130 s.pages.Notice(w, "pull-reopen", "Failed to reopen pull.") 131 return 132 } 133 defer tx.Rollback() 134 135 // if this PR is stacked, then we want to reopen all PRs above this one on the stack 136 stack := r.Context().Value("stack").(models.Stack) 137 pullsToReopen := stack.Below(pull) 138 var atUris []syntax.ATURI 139 for _, p := range pullsToReopen { 140 atUris = append(atUris, p.AtUri()) 141 p.State = models.PullOpen 142 } 143 err = db.ReopenPulls( 144 tx, 145 orm.FilterEq("repo_did", string(f.RepoDid)), 146 orm.FilterIn("at_uri", atUris), 147 ) 148 if err != nil { 149 l.Error("failed to reopen pulls in database", "err", err, "pulls_to_reopen", len(pullsToReopen)) 150 s.pages.Notice(w, "pull-close", "Failed to reopen pull.") 151 } 152 153 // Commit the transaction 154 if err = tx.Commit(); err != nil { 155 l.Error("failed to commit transaction", "err", err) 156 s.pages.Notice(w, "pull-reopen", "Failed to reopen pull.") 157 return 158 } 159 160 for _, p := range pullsToReopen { 161 s.notifier.NewPullState(r.Context(), syntax.DID(user.Did), p) 162 } 163 164 ownerSlashRepo := reporesolver.GetBaseRepoPath(r, f) 165 s.pages.HxLocation(w, fmt.Sprintf("/%s/pulls/%d", ownerSlashRepo, pull.PullId)) 166}