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