Monorepo for Tangled tangled.org
5

Configure Feed

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

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