forked from
tangled.org/core
Monorepo for Tangled — https://tangled.org
1package db
2
3import (
4 "database/sql"
5 "time"
6
7 "github.com/bluesky-social/indigo/atproto/syntax"
8)
9
10type Repo struct {
11 Did string
12 Name string
13 Knot string
14 Rkey string
15 Created time.Time
16 AtUri string
17 Description string
18
19 // optionally, populate this when querying for reverse mappings
20 RepoStats *RepoStats
21
22 // optional
23 Source string
24}
25
26func GetAllRepos(e Execer, limit int) ([]Repo, error) {
27 var repos []Repo
28
29 rows, err := e.Query(
30 `select did, name, knot, rkey, description, created, source
31 from repos
32 order by created desc
33 limit ?
34 `,
35 limit,
36 )
37 if err != nil {
38 return nil, err
39 }
40 defer rows.Close()
41
42 for rows.Next() {
43 var repo Repo
44 err := scanRepo(
45 rows, &repo.Did, &repo.Name, &repo.Knot, &repo.Rkey, &repo.Description, &repo.Created, &repo.Source,
46 )
47 if err != nil {
48 return nil, err
49 }
50 repos = append(repos, repo)
51 }
52
53 if err := rows.Err(); err != nil {
54 return nil, err
55 }
56
57 return repos, nil
58}
59
60func GetAllReposByDid(e Execer, did string) ([]Repo, error) {
61 var repos []Repo
62
63 rows, err := e.Query(
64 `select
65 r.did,
66 r.name,
67 r.knot,
68 r.rkey,
69 r.description,
70 r.created,
71 count(s.id) as star_count,
72 r.source
73 from
74 repos r
75 left join
76 stars s on r.at_uri = s.repo_at
77 where
78 r.did = ?
79 group by
80 r.at_uri`, did)
81 if err != nil {
82 return nil, err
83 }
84 defer rows.Close()
85
86 for rows.Next() {
87 var repo Repo
88 var repoStats RepoStats
89 var createdAt string
90 var nullableDescription sql.NullString
91 var nullableSource sql.NullString
92
93 err := rows.Scan(&repo.Did, &repo.Name, &repo.Knot, &repo.Rkey, &nullableDescription, &createdAt, &repoStats.StarCount, &nullableSource)
94 if err != nil {
95 return nil, err
96 }
97
98 if nullableDescription.Valid {
99 repo.Description = nullableDescription.String
100 }
101
102 if nullableSource.Valid {
103 repo.Source = nullableSource.String
104 }
105
106 createdAtTime, err := time.Parse(time.RFC3339, createdAt)
107 if err != nil {
108 repo.Created = time.Now()
109 } else {
110 repo.Created = createdAtTime
111 }
112
113 repo.RepoStats = &repoStats
114
115 repos = append(repos, repo)
116 }
117
118 if err := rows.Err(); err != nil {
119 return nil, err
120 }
121
122 return repos, nil
123}
124
125func GetRepo(e Execer, did, name string) (*Repo, error) {
126 var repo Repo
127 var nullableDescription sql.NullString
128
129 row := e.QueryRow(`select did, name, knot, created, at_uri, description from repos where did = ? and name = ?`, did, name)
130
131 var createdAt string
132 if err := row.Scan(&repo.Did, &repo.Name, &repo.Knot, &createdAt, &repo.AtUri, &nullableDescription); err != nil {
133 return nil, err
134 }
135 createdAtTime, _ := time.Parse(time.RFC3339, createdAt)
136 repo.Created = createdAtTime
137
138 if nullableDescription.Valid {
139 repo.Description = nullableDescription.String
140 } else {
141 repo.Description = ""
142 }
143
144 return &repo, nil
145}
146
147func GetRepoByAtUri(e Execer, atUri string) (*Repo, error) {
148 var repo Repo
149 var nullableDescription sql.NullString
150
151 row := e.QueryRow(`select did, name, knot, created, at_uri, description from repos where at_uri = ?`, atUri)
152
153 var createdAt string
154 if err := row.Scan(&repo.Did, &repo.Name, &repo.Knot, &createdAt, &repo.AtUri, &nullableDescription); err != nil {
155 return nil, err
156 }
157 createdAtTime, _ := time.Parse(time.RFC3339, createdAt)
158 repo.Created = createdAtTime
159
160 if nullableDescription.Valid {
161 repo.Description = nullableDescription.String
162 } else {
163 repo.Description = ""
164 }
165
166 return &repo, nil
167}
168
169func AddRepo(e Execer, repo *Repo) error {
170 _, err := e.Exec(
171 `insert into repos
172 (did, name, knot, rkey, at_uri, description, source)
173 values (?, ?, ?, ?, ?, ?, ?)`,
174 repo.Did, repo.Name, repo.Knot, repo.Rkey, repo.AtUri, repo.Description, repo.Source,
175 )
176 return err
177}
178
179func RemoveRepo(e Execer, did, name, rkey string) error {
180 _, err := e.Exec(`delete from repos where did = ? and name = ? and rkey = ?`, did, name, rkey)
181 return err
182}
183
184func GetRepoSource(e Execer, repoAt syntax.ATURI) (string, error) {
185 var nullableSource sql.NullString
186 err := e.QueryRow(`select source from repos where at_uri = ?`, repoAt).Scan(&nullableSource)
187 if err != nil {
188 return "", err
189 }
190 return nullableSource.String, nil
191}
192
193func AddCollaborator(e Execer, collaborator, repoOwnerDid, repoName, repoKnot string) error {
194 _, err := e.Exec(
195 `insert into collaborators (did, repo)
196 values (?, (select id from repos where did = ? and name = ? and knot = ?));`,
197 collaborator, repoOwnerDid, repoName, repoKnot)
198 return err
199}
200
201func UpdateDescription(e Execer, repoAt, newDescription string) error {
202 _, err := e.Exec(
203 `update repos set description = ? where at_uri = ?`, newDescription, repoAt)
204 return err
205}
206
207func CollaboratingIn(e Execer, collaborator string) ([]Repo, error) {
208 var repos []Repo
209
210 rows, err := e.Query(
211 `select
212 r.did, r.name, r.knot, r.rkey, r.description, r.created, count(s.id) as star_count
213 from
214 repos r
215 join
216 collaborators c on r.id = c.repo
217 left join
218 stars s on r.at_uri = s.repo_at
219 where
220 c.did = ?
221 group by
222 r.id;`, collaborator)
223 if err != nil {
224 return nil, err
225 }
226 defer rows.Close()
227
228 for rows.Next() {
229 var repo Repo
230 var repoStats RepoStats
231 var createdAt string
232 var nullableDescription sql.NullString
233
234 err := rows.Scan(&repo.Did, &repo.Name, &repo.Knot, &repo.Rkey, &nullableDescription, &createdAt, &repoStats.StarCount)
235 if err != nil {
236 return nil, err
237 }
238
239 if nullableDescription.Valid {
240 repo.Description = nullableDescription.String
241 } else {
242 repo.Description = ""
243 }
244
245 createdAtTime, err := time.Parse(time.RFC3339, createdAt)
246 if err != nil {
247 repo.Created = time.Now()
248 } else {
249 repo.Created = createdAtTime
250 }
251
252 repo.RepoStats = &repoStats
253
254 repos = append(repos, repo)
255 }
256
257 if err := rows.Err(); err != nil {
258 return nil, err
259 }
260
261 return repos, nil
262}
263
264type RepoStats struct {
265 StarCount int
266 IssueCount IssueCount
267 PullCount PullCount
268}
269
270func scanRepo(rows *sql.Rows, did, name, knot, rkey, description *string, created *time.Time, source *string) error {
271 var createdAt string
272 var nullableDescription sql.NullString
273 var nullableSource sql.NullString
274 if err := rows.Scan(did, name, knot, rkey, &nullableDescription, &createdAt, &nullableSource); err != nil {
275 return err
276 }
277
278 if nullableDescription.Valid {
279 *description = nullableDescription.String
280 } else {
281 *description = ""
282 }
283
284 createdAtTime, err := time.Parse(time.RFC3339, createdAt)
285 if err != nil {
286 *created = time.Now()
287 } else {
288 *created = createdAtTime
289 }
290
291 if nullableSource.Valid {
292 *source = nullableSource.String
293 } else {
294 *source = ""
295 }
296
297 return nil
298}