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}