this repo has no description
1package db 2 3import ( 4 "database/sql" 5 "time" 6 7 "github.com/bluesky-social/indigo/atproto/syntax" 8) 9 10type Issue struct { 11 RepoAt syntax.ATURI 12 OwnerDid string 13 IssueId int 14 IssueAt string 15 Created *time.Time 16 Title string 17 Body string 18 Open bool 19 Metadata *IssueMetadata 20} 21 22type IssueMetadata struct { 23 CommentCount int 24 // labels, assignee etc. 25} 26 27type Comment struct { 28 OwnerDid string 29 RepoAt syntax.ATURI 30 CommentAt string 31 Issue int 32 CommentId int 33 Body string 34 Created *time.Time 35} 36 37func NewIssue(tx *sql.Tx, issue *Issue) error { 38 defer tx.Rollback() 39 40 _, err := tx.Exec(` 41 insert or ignore into repo_issue_seqs (repo_at, next_issue_id) 42 values (?, 1) 43 `, issue.RepoAt) 44 if err != nil { 45 return err 46 } 47 48 var nextId int 49 err = tx.QueryRow(` 50 update repo_issue_seqs 51 set next_issue_id = next_issue_id + 1 52 where repo_at = ? 53 returning next_issue_id - 1 54 `, issue.RepoAt).Scan(&nextId) 55 if err != nil { 56 return err 57 } 58 59 issue.IssueId = nextId 60 61 _, err = tx.Exec(` 62 insert into issues (repo_at, owner_did, issue_id, title, body) 63 values (?, ?, ?, ?, ?) 64 `, issue.RepoAt, issue.OwnerDid, issue.IssueId, issue.Title, issue.Body) 65 if err != nil { 66 return err 67 } 68 69 if err := tx.Commit(); err != nil { 70 return err 71 } 72 73 return nil 74} 75 76func SetIssueAt(e Execer, repoAt syntax.ATURI, issueId int, issueAt string) error { 77 _, err := e.Exec(`update issues set issue_at = ? where repo_at = ? and issue_id = ?`, issueAt, repoAt, issueId) 78 return err 79} 80 81func GetIssueAt(e Execer, repoAt syntax.ATURI, issueId int) (string, error) { 82 var issueAt string 83 err := e.QueryRow(`select issue_at from issues where repo_at = ? and issue_id = ?`, repoAt, issueId).Scan(&issueAt) 84 return issueAt, err 85} 86 87func GetIssueId(e Execer, repoAt syntax.ATURI) (int, error) { 88 var issueId int 89 err := e.QueryRow(`select next_issue_id from repo_issue_seqs where repo_at = ?`, repoAt).Scan(&issueId) 90 return issueId - 1, err 91} 92 93func GetIssueOwnerDid(e Execer, repoAt syntax.ATURI, issueId int) (string, error) { 94 var ownerDid string 95 err := e.QueryRow(`select owner_did from issues where repo_at = ? and issue_id = ?`, repoAt, issueId).Scan(&ownerDid) 96 return ownerDid, err 97} 98 99func GetIssues(e Execer, repoAt syntax.ATURI) ([]Issue, error) { 100 var issues []Issue 101 102 rows, err := e.Query( 103 `select 104 i.owner_did, 105 i.issue_id, 106 i.created, 107 i.title, 108 i.body, 109 i.open, 110 count(c.id) 111 from 112 issues i 113 left join 114 comments c on i.repo_at = c.repo_at and i.issue_id = c.issue_id 115 where 116 i.repo_at = ? 117 group by 118 i.id, i.owner_did, i.issue_id, i.created, i.title, i.body, i.open 119 order by 120 i.created desc`, 121 repoAt) 122 if err != nil { 123 return nil, err 124 } 125 defer rows.Close() 126 127 for rows.Next() { 128 var issue Issue 129 var createdAt string 130 var metadata IssueMetadata 131 err := rows.Scan(&issue.OwnerDid, &issue.IssueId, &createdAt, &issue.Title, &issue.Body, &issue.Open, &metadata.CommentCount) 132 if err != nil { 133 return nil, err 134 } 135 136 createdTime, err := time.Parse(time.RFC3339, createdAt) 137 if err != nil { 138 return nil, err 139 } 140 issue.Created = &createdTime 141 issue.Metadata = &metadata 142 143 issues = append(issues, issue) 144 } 145 146 if err := rows.Err(); err != nil { 147 return nil, err 148 } 149 150 return issues, nil 151} 152 153func GetIssue(e Execer, repoAt syntax.ATURI, issueId int) (*Issue, error) { 154 query := `select owner_did, created, title, body, open from issues where repo_at = ? and issue_id = ?` 155 row := e.QueryRow(query, repoAt, issueId) 156 157 var issue Issue 158 var createdAt string 159 err := row.Scan(&issue.OwnerDid, &createdAt, &issue.Title, &issue.Body, &issue.Open) 160 if err != nil { 161 return nil, err 162 } 163 164 createdTime, err := time.Parse(time.RFC3339, createdAt) 165 if err != nil { 166 return nil, err 167 } 168 issue.Created = &createdTime 169 170 return &issue, nil 171} 172 173func GetIssueWithComments(e Execer, repoAt syntax.ATURI, issueId int) (*Issue, []Comment, error) { 174 query := `select owner_did, issue_id, created, title, body, open from issues where repo_at = ? and issue_id = ?` 175 row := e.QueryRow(query, repoAt, issueId) 176 177 var issue Issue 178 var createdAt string 179 err := row.Scan(&issue.OwnerDid, &issue.IssueId, &createdAt, &issue.Title, &issue.Body, &issue.Open) 180 if err != nil { 181 return nil, nil, err 182 } 183 184 createdTime, err := time.Parse(time.RFC3339, createdAt) 185 if err != nil { 186 return nil, nil, err 187 } 188 issue.Created = &createdTime 189 190 comments, err := GetComments(e, repoAt, issueId) 191 if err != nil { 192 return nil, nil, err 193 } 194 195 return &issue, comments, nil 196} 197 198func NewComment(e Execer, comment *Comment) error { 199 query := `insert into comments (owner_did, repo_at, comment_at, issue_id, comment_id, body) values (?, ?, ?, ?, ?, ?)` 200 _, err := e.Exec( 201 query, 202 comment.OwnerDid, 203 comment.RepoAt, 204 comment.CommentAt, 205 comment.Issue, 206 comment.CommentId, 207 comment.Body, 208 ) 209 return err 210} 211 212func GetComments(e Execer, repoAt syntax.ATURI, issueId int) ([]Comment, error) { 213 var comments []Comment 214 215 rows, err := e.Query(`select owner_did, issue_id, comment_id, comment_at, body, created from comments where repo_at = ? and issue_id = ? order by created asc`, repoAt, issueId) 216 if err == sql.ErrNoRows { 217 return []Comment{}, nil 218 } 219 if err != nil { 220 return nil, err 221 } 222 defer rows.Close() 223 224 for rows.Next() { 225 var comment Comment 226 var createdAt string 227 err := rows.Scan(&comment.OwnerDid, &comment.Issue, &comment.CommentId, &comment.CommentAt, &comment.Body, &createdAt) 228 if err != nil { 229 return nil, err 230 } 231 232 createdAtTime, err := time.Parse(time.RFC3339, createdAt) 233 if err != nil { 234 return nil, err 235 } 236 comment.Created = &createdAtTime 237 238 comments = append(comments, comment) 239 } 240 241 if err := rows.Err(); err != nil { 242 return nil, err 243 } 244 245 return comments, nil 246} 247 248func CloseIssue(e Execer, repoAt syntax.ATURI, issueId int) error { 249 _, err := e.Exec(`update issues set open = 0 where repo_at = ? and issue_id = ?`, repoAt, issueId) 250 return err 251} 252 253func ReopenIssue(e Execer, repoAt syntax.ATURI, issueId int) error { 254 _, err := e.Exec(`update issues set open = 1 where repo_at = ? and issue_id = ?`, repoAt, issueId) 255 return err 256} 257 258type IssueCount struct { 259 Open int 260 Closed int 261} 262 263func GetIssueCount(e Execer, repoAt syntax.ATURI) (IssueCount, error) { 264 row := e.QueryRow(` 265 select 266 count(case when open = 1 then 1 end) as open_count, 267 count(case when open = 0 then 1 end) as closed_count 268 from issues 269 where repo_at = ?`, 270 repoAt, 271 ) 272 273 var count IssueCount 274 if err := row.Scan(&count.Open, &count.Closed); err != nil { 275 return IssueCount{0, 0}, err 276 } 277 278 return count, nil 279}