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 syntax.ATURI 31 Issue int 32 CommentId int 33 Body string 34 Created *time.Time 35 Deleted *time.Time 36 Edited *time.Time 37} 38 39func NewIssue(tx *sql.Tx, issue *Issue) error { 40 defer tx.Rollback() 41 42 _, err := tx.Exec(` 43 insert or ignore into repo_issue_seqs (repo_at, next_issue_id) 44 values (?, 1) 45 `, issue.RepoAt) 46 if err != nil { 47 return err 48 } 49 50 var nextId int 51 err = tx.QueryRow(` 52 update repo_issue_seqs 53 set next_issue_id = next_issue_id + 1 54 where repo_at = ? 55 returning next_issue_id - 1 56 `, issue.RepoAt).Scan(&nextId) 57 if err != nil { 58 return err 59 } 60 61 issue.IssueId = nextId 62 63 _, err = tx.Exec(` 64 insert into issues (repo_at, owner_did, issue_id, title, body) 65 values (?, ?, ?, ?, ?) 66 `, issue.RepoAt, issue.OwnerDid, issue.IssueId, issue.Title, issue.Body) 67 if err != nil { 68 return err 69 } 70 71 if err := tx.Commit(); err != nil { 72 return err 73 } 74 75 return nil 76} 77 78func SetIssueAt(e Execer, repoAt syntax.ATURI, issueId int, issueAt string) error { 79 _, err := e.Exec(`update issues set issue_at = ? where repo_at = ? and issue_id = ?`, issueAt, repoAt, issueId) 80 return err 81} 82 83func GetIssueAt(e Execer, repoAt syntax.ATURI, issueId int) (string, error) { 84 var issueAt string 85 err := e.QueryRow(`select issue_at from issues where repo_at = ? and issue_id = ?`, repoAt, issueId).Scan(&issueAt) 86 return issueAt, err 87} 88 89func GetIssueId(e Execer, repoAt syntax.ATURI) (int, error) { 90 var issueId int 91 err := e.QueryRow(`select next_issue_id from repo_issue_seqs where repo_at = ?`, repoAt).Scan(&issueId) 92 return issueId - 1, err 93} 94 95func GetIssueOwnerDid(e Execer, repoAt syntax.ATURI, issueId int) (string, error) { 96 var ownerDid string 97 err := e.QueryRow(`select owner_did from issues where repo_at = ? and issue_id = ?`, repoAt, issueId).Scan(&ownerDid) 98 return ownerDid, err 99} 100 101func GetIssues(e Execer, repoAt syntax.ATURI, isOpen bool) ([]Issue, error) { 102 var issues []Issue 103 openValue := 0 104 if isOpen { 105 openValue = 1 106 } 107 108 rows, err := e.Query( 109 `select 110 i.owner_did, 111 i.issue_id, 112 i.created, 113 i.title, 114 i.body, 115 i.open, 116 count(c.id) 117 from 118 issues i 119 left join 120 comments c on i.repo_at = c.repo_at and i.issue_id = c.issue_id 121 where 122 i.repo_at = ? and i.open = ? 123 group by 124 i.id, i.owner_did, i.issue_id, i.created, i.title, i.body, i.open 125 order by 126 i.created desc`, 127 repoAt, openValue) 128 if err != nil { 129 return nil, err 130 } 131 defer rows.Close() 132 133 for rows.Next() { 134 var issue Issue 135 var createdAt string 136 var metadata IssueMetadata 137 err := rows.Scan(&issue.OwnerDid, &issue.IssueId, &createdAt, &issue.Title, &issue.Body, &issue.Open, &metadata.CommentCount) 138 if err != nil { 139 return nil, err 140 } 141 142 createdTime, err := time.Parse(time.RFC3339, createdAt) 143 if err != nil { 144 return nil, err 145 } 146 issue.Created = &createdTime 147 issue.Metadata = &metadata 148 149 issues = append(issues, issue) 150 } 151 152 if err := rows.Err(); err != nil { 153 return nil, err 154 } 155 156 return issues, nil 157} 158 159func GetIssue(e Execer, repoAt syntax.ATURI, issueId int) (*Issue, error) { 160 query := `select owner_did, created, title, body, open from issues where repo_at = ? and issue_id = ?` 161 row := e.QueryRow(query, repoAt, issueId) 162 163 var issue Issue 164 var createdAt string 165 err := row.Scan(&issue.OwnerDid, &createdAt, &issue.Title, &issue.Body, &issue.Open) 166 if err != nil { 167 return nil, err 168 } 169 170 createdTime, err := time.Parse(time.RFC3339, createdAt) 171 if err != nil { 172 return nil, err 173 } 174 issue.Created = &createdTime 175 176 return &issue, nil 177} 178 179func GetIssueWithComments(e Execer, repoAt syntax.ATURI, issueId int) (*Issue, []Comment, error) { 180 query := `select owner_did, issue_id, created, title, body, open from issues where repo_at = ? and issue_id = ?` 181 row := e.QueryRow(query, repoAt, issueId) 182 183 var issue Issue 184 var createdAt string 185 err := row.Scan(&issue.OwnerDid, &issue.IssueId, &createdAt, &issue.Title, &issue.Body, &issue.Open) 186 if err != nil { 187 return nil, nil, err 188 } 189 190 createdTime, err := time.Parse(time.RFC3339, createdAt) 191 if err != nil { 192 return nil, nil, err 193 } 194 issue.Created = &createdTime 195 196 comments, err := GetComments(e, repoAt, issueId) 197 if err != nil { 198 return nil, nil, err 199 } 200 201 return &issue, comments, nil 202} 203 204func NewComment(e Execer, comment *Comment) error { 205 query := `insert into comments (owner_did, repo_at, comment_at, issue_id, comment_id, body) values (?, ?, ?, ?, ?, ?)` 206 _, err := e.Exec( 207 query, 208 comment.OwnerDid, 209 comment.RepoAt, 210 comment.CommentAt, 211 comment.Issue, 212 comment.CommentId, 213 comment.Body, 214 ) 215 return err 216} 217 218func GetComments(e Execer, repoAt syntax.ATURI, issueId int) ([]Comment, error) { 219 var comments []Comment 220 221 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) 222 if err == sql.ErrNoRows { 223 return []Comment{}, nil 224 } 225 if err != nil { 226 return nil, err 227 } 228 defer rows.Close() 229 230 for rows.Next() { 231 var comment Comment 232 var createdAt string 233 err := rows.Scan(&comment.OwnerDid, &comment.Issue, &comment.CommentId, &comment.CommentAt, &comment.Body, &createdAt) 234 if err != nil { 235 return nil, err 236 } 237 238 createdAtTime, err := time.Parse(time.RFC3339, createdAt) 239 if err != nil { 240 return nil, err 241 } 242 comment.Created = &createdAtTime 243 244 comments = append(comments, comment) 245 } 246 247 if err := rows.Err(); err != nil { 248 return nil, err 249 } 250 251 return comments, nil 252} 253 254func GetComment(e Execer, repoAt syntax.ATURI, issueId, commentId int) (*Comment, error) { 255 query := ` 256 select 257 owner_did, body, comment_at, created, deleted, edited 258 from 259 comments where repo_at = ? and issue_id = ? and comment_id = ? 260 ` 261 row := e.QueryRow(query, repoAt, issueId, commentId) 262 263 var comment Comment 264 var createdAt string 265 var deletedAt, editedAt sql.NullString 266 err := row.Scan(&comment.OwnerDid, &comment.Body, &comment.CommentAt, &createdAt, &deletedAt, &editedAt) 267 if err != nil { 268 return nil, err 269 } 270 271 createdTime, err := time.Parse(time.RFC3339, createdAt) 272 if err != nil { 273 return nil, err 274 } 275 comment.Created = &createdTime 276 277 if deletedAt.Valid { 278 deletedTime, err := time.Parse(time.RFC3339, deletedAt.String) 279 if err != nil { 280 return nil, err 281 } 282 comment.Deleted = &deletedTime 283 } 284 285 if editedAt.Valid { 286 editedTime, err := time.Parse(time.RFC3339, editedAt.String) 287 if err != nil { 288 return nil, err 289 } 290 comment.Edited = &editedTime 291 } 292 293 comment.RepoAt = repoAt 294 comment.Issue = issueId 295 comment.CommentId = commentId 296 297 return &comment, nil 298} 299 300func EditComment(e Execer, repoAt syntax.ATURI, issueId, commentId int, newBody string) error { 301 _, err := e.Exec( 302 ` 303 update comments 304 set body = ?, 305 edited = strftime('%Y-%m-%dT%H:%M:%SZ', 'now') 306 where repo_at = ? and issue_id = ? and comment_id = ? 307 `, newBody, repoAt, issueId, commentId) 308 return err 309} 310 311func DeleteComment(e Execer, repoAt syntax.ATURI, issueId, commentId int) error { 312 _, err := e.Exec( 313 ` 314 update comments 315 set body = "", 316 deleted = strftime('%Y-%m-%dT%H:%M:%SZ', 'now') 317 where repo_at = ? and issue_id = ? and comment_id = ? 318 `, repoAt, issueId, commentId) 319 return err 320} 321 322func CloseIssue(e Execer, repoAt syntax.ATURI, issueId int) error { 323 _, err := e.Exec(`update issues set open = 0 where repo_at = ? and issue_id = ?`, repoAt, issueId) 324 return err 325} 326 327func ReopenIssue(e Execer, repoAt syntax.ATURI, issueId int) error { 328 _, err := e.Exec(`update issues set open = 1 where repo_at = ? and issue_id = ?`, repoAt, issueId) 329 return err 330} 331 332type IssueCount struct { 333 Open int 334 Closed int 335} 336 337func GetIssueCount(e Execer, repoAt syntax.ATURI) (IssueCount, error) { 338 row := e.QueryRow(` 339 select 340 count(case when open = 1 then 1 end) as open_count, 341 count(case when open = 0 then 1 end) as closed_count 342 from issues 343 where repo_at = ?`, 344 repoAt, 345 ) 346 347 var count IssueCount 348 if err := row.Scan(&count.Open, &count.Closed); err != nil { 349 return IssueCount{0, 0}, err 350 } 351 352 return count, nil 353}