this repo has no description

appview/issues: use middleware to extract issue

Signed-off-by: oppiliappan <me@oppi.li>

Changed files
+47 -87
appview
issues
+47 -87
appview/issues/issues.go
··· 1 package issues 2 3 import ( 4 "fmt" 5 "log" 6 - mathrand "math/rand/v2" 7 "net/http" 8 "slices" 9 - "strconv" 10 "strings" 11 "time" 12 13 comatproto "github.com/bluesky-social/indigo/api/atproto" 14 - "github.com/bluesky-social/indigo/atproto/data" 15 lexutil "github.com/bluesky-social/indigo/lex/util" 16 "github.com/go-chi/chi/v5" 17 ··· 24 "tangled.sh/tangled.sh/core/appview/pages/markup" 25 "tangled.sh/tangled.sh/core/appview/pagination" 26 "tangled.sh/tangled.sh/core/appview/reporesolver" 27 "tangled.sh/tangled.sh/core/idresolver" 28 "tangled.sh/tangled.sh/core/tid" 29 ) 30 ··· 36 db *db.DB 37 config *config.Config 38 notifier notify.Notifier 39 } 40 41 func New( ··· 55 db: db, 56 config: config, 57 notifier: notifier, 58 } 59 } 60 61 func (rp *Issues) RepoSingleIssue(w http.ResponseWriter, r *http.Request) { 62 user := rp.oauth.GetUser(r) 63 f, err := rp.repoResolver.Resolve(r) 64 if err != nil { ··· 66 return 67 } 68 69 - issueId := chi.URLParam(r, "issue") 70 - issueIdInt, err := strconv.Atoi(issueId) 71 - if err != nil { 72 - http.Error(w, "bad issue id", http.StatusBadRequest) 73 - log.Println("failed to parse issue id", err) 74 - return 75 - } 76 - 77 - issue, comments, err := db.GetIssueWithComments(rp.db, f.RepoAt(), issueIdInt) 78 - if err != nil { 79 - log.Println("failed to get issue and comments", err) 80 - rp.pages.Notice(w, "issues", "Failed to load issue. Try again later.") 81 return 82 } 83 84 reactionCountMap, err := db.GetReactionCountMap(rp.db, issue.AtUri()) 85 if err != nil { 86 - log.Println("failed to get issue reactions") 87 - rp.pages.Notice(w, "issues", "Failed to load issue. Try again later.") 88 } 89 90 userReactions := map[db.ReactionKind]bool{} ··· 92 userReactions = db.GetReactionStatusMap(rp.db, user.Did, issue.AtUri()) 93 } 94 95 - issueOwnerIdent, err := rp.idResolver.ResolveIdent(r.Context(), issue.OwnerDid) 96 - if err != nil { 97 - log.Println("failed to resolve issue owner", err) 98 - } 99 - 100 rp.pages.RepoSingleIssue(w, pages.RepoSingleIssueParams{ 101 - LoggedInUser: user, 102 - RepoInfo: f.RepoInfo(user), 103 - Issue: issue, 104 - Comments: comments, 105 - 106 - IssueOwnerHandle: issueOwnerIdent.Handle.String(), 107 - 108 OrderedReactionKinds: db.OrderedReactionKinds, 109 Reactions: reactionCountMap, 110 UserReacted: userReactions, ··· 113 } 114 115 func (rp *Issues) CloseIssue(w http.ResponseWriter, r *http.Request) { 116 user := rp.oauth.GetUser(r) 117 f, err := rp.repoResolver.Resolve(r) 118 if err != nil { 119 - log.Println("failed to get repo and knot", err) 120 - return 121 - } 122 - 123 - issueId := chi.URLParam(r, "issue") 124 - issueIdInt, err := strconv.Atoi(issueId) 125 - if err != nil { 126 - http.Error(w, "bad issue id", http.StatusBadRequest) 127 - log.Println("failed to parse issue id", err) 128 return 129 } 130 131 - issue, err := db.GetIssue(rp.db, f.RepoAt(), issueIdInt) 132 - if err != nil { 133 - log.Println("failed to get issue", err) 134 - rp.pages.Notice(w, "issue-action", "Failed to close issue. Try again later.") 135 return 136 } 137 ··· 142 isCollaborator := slices.ContainsFunc(collaborators, func(collab pages.Collaborator) bool { 143 return user.Did == collab.Did 144 }) 145 - isIssueOwner := user.Did == issue.OwnerDid 146 147 // TODO: make this more granular 148 if isIssueOwner || isCollaborator { 149 - 150 - closed := tangled.RepoIssueStateClosed 151 - 152 - client, err := rp.oauth.AuthorizedClient(r) 153 - if err != nil { 154 - log.Println("failed to get authorized client", err) 155 - return 156 - } 157 - _, err = client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{ 158 - Collection: tangled.RepoIssueStateNSID, 159 - Repo: user.Did, 160 - Rkey: tid.TID(), 161 - Record: &lexutil.LexiconTypeDecoder{ 162 - Val: &tangled.RepoIssueState{ 163 - Issue: issue.AtUri().String(), 164 - State: closed, 165 - }, 166 - }, 167 - }) 168 - 169 - if err != nil { 170 - log.Println("failed to update issue state", err) 171 - rp.pages.Notice(w, "issue-action", "Failed to close issue. Try again later.") 172 - return 173 - } 174 - 175 - err = db.CloseIssue(rp.db, f.RepoAt(), issueIdInt) 176 if err != nil { 177 log.Println("failed to close issue", err) 178 rp.pages.Notice(w, "issue-action", "Failed to close issue. Try again later.") 179 return 180 } 181 182 - rp.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d", f.OwnerSlashRepo(), issueIdInt)) 183 return 184 } else { 185 log.Println("user is not permitted to close issue") ··· 189 } 190 191 func (rp *Issues) ReopenIssue(w http.ResponseWriter, r *http.Request) { 192 user := rp.oauth.GetUser(r) 193 f, err := rp.repoResolver.Resolve(r) 194 if err != nil { ··· 196 return 197 } 198 199 - issueId := chi.URLParam(r, "issue") 200 - issueIdInt, err := strconv.Atoi(issueId) 201 - if err != nil { 202 - http.Error(w, "bad issue id", http.StatusBadRequest) 203 - log.Println("failed to parse issue id", err) 204 - return 205 - } 206 - 207 - issue, err := db.GetIssue(rp.db, f.RepoAt(), issueIdInt) 208 - if err != nil { 209 - log.Println("failed to get issue", err) 210 - rp.pages.Notice(w, "issue-action", "Failed to close issue. Try again later.") 211 return 212 } 213 ··· 218 isCollaborator := slices.ContainsFunc(collaborators, func(collab pages.Collaborator) bool { 219 return user.Did == collab.Did 220 }) 221 - isIssueOwner := user.Did == issue.OwnerDid 222 223 if isCollaborator || isIssueOwner { 224 - err := db.ReopenIssue(rp.db, f.RepoAt(), issueIdInt) 225 if err != nil { 226 log.Println("failed to reopen issue", err) 227 rp.pages.Notice(w, "issue-action", "Failed to reopen issue. Try again later.") 228 return 229 } 230 - rp.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d", f.OwnerSlashRepo(), issueIdInt)) 231 return 232 } else { 233 log.Println("user is not the owner of the repo") ··· 237 } 238 239 func (rp *Issues) NewIssueComment(w http.ResponseWriter, r *http.Request) { 240 user := rp.oauth.GetUser(r) 241 f, err := rp.repoResolver.Resolve(r) 242 if err != nil { ··· 411 LoggedInUser: user, 412 RepoInfo: f.RepoInfo(user), 413 Issue: issue, 414 - Comment: comment, 415 }) 416 case http.MethodPost: 417 // extract form value
··· 1 package issues 2 3 import ( 4 + "context" 5 + "database/sql" 6 + "errors" 7 "fmt" 8 "log" 9 + "log/slog" 10 "net/http" 11 "slices" 12 "strings" 13 "time" 14 15 comatproto "github.com/bluesky-social/indigo/api/atproto" 16 + "github.com/bluesky-social/indigo/atproto/syntax" 17 lexutil "github.com/bluesky-social/indigo/lex/util" 18 "github.com/go-chi/chi/v5" 19 ··· 26 "tangled.sh/tangled.sh/core/appview/pages/markup" 27 "tangled.sh/tangled.sh/core/appview/pagination" 28 "tangled.sh/tangled.sh/core/appview/reporesolver" 29 + "tangled.sh/tangled.sh/core/appview/validator" 30 + "tangled.sh/tangled.sh/core/appview/xrpcclient" 31 "tangled.sh/tangled.sh/core/idresolver" 32 + tlog "tangled.sh/tangled.sh/core/log" 33 "tangled.sh/tangled.sh/core/tid" 34 ) 35 ··· 41 db *db.DB 42 config *config.Config 43 notifier notify.Notifier 44 + logger *slog.Logger 45 + validator *validator.Validator 46 } 47 48 func New( ··· 62 db: db, 63 config: config, 64 notifier: notifier, 65 + logger: tlog.New("issues"), 66 + validator: validator, 67 } 68 } 69 70 func (rp *Issues) RepoSingleIssue(w http.ResponseWriter, r *http.Request) { 71 + l := rp.logger.With("handler", "RepoSingleIssue") 72 user := rp.oauth.GetUser(r) 73 f, err := rp.repoResolver.Resolve(r) 74 if err != nil { ··· 76 return 77 } 78 79 + issue, ok := r.Context().Value("issue").(*db.Issue) 80 + if !ok { 81 + l.Error("failed to get issue") 82 + rp.pages.Error404(w) 83 return 84 } 85 86 reactionCountMap, err := db.GetReactionCountMap(rp.db, issue.AtUri()) 87 if err != nil { 88 + l.Error("failed to get issue reactions", "err", err) 89 } 90 91 userReactions := map[db.ReactionKind]bool{} ··· 93 userReactions = db.GetReactionStatusMap(rp.db, user.Did, issue.AtUri()) 94 } 95 96 rp.pages.RepoSingleIssue(w, pages.RepoSingleIssueParams{ 97 + LoggedInUser: user, 98 + RepoInfo: f.RepoInfo(user), 99 + Issue: issue, 100 + CommentList: issue.CommentList(), 101 OrderedReactionKinds: db.OrderedReactionKinds, 102 Reactions: reactionCountMap, 103 UserReacted: userReactions, ··· 106 } 107 108 func (rp *Issues) CloseIssue(w http.ResponseWriter, r *http.Request) { 109 + l := rp.logger.With("handler", "CloseIssue") 110 user := rp.oauth.GetUser(r) 111 f, err := rp.repoResolver.Resolve(r) 112 if err != nil { 113 + l.Error("failed to get repo and knot", "err", err) 114 return 115 } 116 117 + issue, ok := r.Context().Value("issue").(*db.Issue) 118 + if !ok { 119 + l.Error("failed to get issue") 120 + rp.pages.Error404(w) 121 return 122 } 123 ··· 128 isCollaborator := slices.ContainsFunc(collaborators, func(collab pages.Collaborator) bool { 129 return user.Did == collab.Did 130 }) 131 + isIssueOwner := user.Did == issue.Did 132 133 // TODO: make this more granular 134 if isIssueOwner || isCollaborator { 135 + err = db.CloseIssues( 136 + rp.db, 137 + db.FilterEq("id", issue.Id), 138 + ) 139 if err != nil { 140 log.Println("failed to close issue", err) 141 rp.pages.Notice(w, "issue-action", "Failed to close issue. Try again later.") 142 return 143 } 144 145 + rp.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d", f.OwnerSlashRepo(), issue.IssueId)) 146 return 147 } else { 148 log.Println("user is not permitted to close issue") ··· 152 } 153 154 func (rp *Issues) ReopenIssue(w http.ResponseWriter, r *http.Request) { 155 + l := rp.logger.With("handler", "ReopenIssue") 156 user := rp.oauth.GetUser(r) 157 f, err := rp.repoResolver.Resolve(r) 158 if err != nil { ··· 160 return 161 } 162 163 + issue, ok := r.Context().Value("issue").(*db.Issue) 164 + if !ok { 165 + l.Error("failed to get issue") 166 + rp.pages.Error404(w) 167 return 168 } 169 ··· 174 isCollaborator := slices.ContainsFunc(collaborators, func(collab pages.Collaborator) bool { 175 return user.Did == collab.Did 176 }) 177 + isIssueOwner := user.Did == issue.Did 178 179 if isCollaborator || isIssueOwner { 180 + err := db.ReopenIssues( 181 + rp.db, 182 + db.FilterEq("id", issue.Id), 183 + ) 184 if err != nil { 185 log.Println("failed to reopen issue", err) 186 rp.pages.Notice(w, "issue-action", "Failed to reopen issue. Try again later.") 187 return 188 } 189 + rp.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d", f.OwnerSlashRepo(), issue.IssueId)) 190 return 191 } else { 192 log.Println("user is not the owner of the repo") ··· 196 } 197 198 func (rp *Issues) NewIssueComment(w http.ResponseWriter, r *http.Request) { 199 + l := rp.logger.With("handler", "NewIssueComment") 200 user := rp.oauth.GetUser(r) 201 f, err := rp.repoResolver.Resolve(r) 202 if err != nil { ··· 371 LoggedInUser: user, 372 RepoInfo: f.RepoInfo(user), 373 Issue: issue, 374 + Comment: &comment, 375 }) 376 case http.MethodPost: 377 // extract form value