forked from tangled.org/core
Monorepo for Tangled — https://tangled.org
at enable-html 8.7 kB view raw
1package db 2 3import ( 4 "database/sql" 5 "fmt" 6 "time" 7 8 "github.com/bluesky-social/indigo/atproto/syntax" 9 "tangled.sh/tangled.sh/core/api/tangled" 10) 11 12type Repo struct { 13 Did string 14 Name string 15 Knot string 16 Rkey string 17 Created time.Time 18 AtUri string 19 Description string 20 21 // optionally, populate this when querying for reverse mappings 22 RepoStats *RepoStats 23 24 // optional 25 Source string 26} 27 28func (r Repo) RepoAt() syntax.ATURI { 29 return syntax.ATURI(fmt.Sprintf("at://%s/%s/%s", r.Did, tangled.RepoNSID, r.Rkey)) 30} 31 32func GetAllRepos(e Execer, limit int) ([]Repo, error) { 33 var repos []Repo 34 35 rows, err := e.Query( 36 `select did, name, knot, rkey, description, created, source 37 from repos 38 order by created desc 39 limit ? 40 `, 41 limit, 42 ) 43 if err != nil { 44 return nil, err 45 } 46 defer rows.Close() 47 48 for rows.Next() { 49 var repo Repo 50 err := scanRepo( 51 rows, &repo.Did, &repo.Name, &repo.Knot, &repo.Rkey, &repo.Description, &repo.Created, &repo.Source, 52 ) 53 if err != nil { 54 return nil, err 55 } 56 repos = append(repos, repo) 57 } 58 59 if err := rows.Err(); err != nil { 60 return nil, err 61 } 62 63 return repos, nil 64} 65 66func GetAllReposByDid(e Execer, did string) ([]Repo, error) { 67 var repos []Repo 68 69 rows, err := e.Query( 70 `select 71 r.did, 72 r.name, 73 r.knot, 74 r.rkey, 75 r.description, 76 r.created, 77 count(s.id) as star_count, 78 r.source 79 from 80 repos r 81 left join 82 stars s on r.at_uri = s.repo_at 83 where 84 r.did = ? 85 group by 86 r.at_uri 87 order by r.created desc`, 88 did) 89 if err != nil { 90 return nil, err 91 } 92 defer rows.Close() 93 94 for rows.Next() { 95 var repo Repo 96 var repoStats RepoStats 97 var createdAt string 98 var nullableDescription sql.NullString 99 var nullableSource sql.NullString 100 101 err := rows.Scan(&repo.Did, &repo.Name, &repo.Knot, &repo.Rkey, &nullableDescription, &createdAt, &repoStats.StarCount, &nullableSource) 102 if err != nil { 103 return nil, err 104 } 105 106 if nullableDescription.Valid { 107 repo.Description = nullableDescription.String 108 } 109 110 if nullableSource.Valid { 111 repo.Source = nullableSource.String 112 } 113 114 createdAtTime, err := time.Parse(time.RFC3339, createdAt) 115 if err != nil { 116 repo.Created = time.Now() 117 } else { 118 repo.Created = createdAtTime 119 } 120 121 repo.RepoStats = &repoStats 122 123 repos = append(repos, repo) 124 } 125 126 if err := rows.Err(); err != nil { 127 return nil, err 128 } 129 130 return repos, nil 131} 132 133func GetRepo(e Execer, did, name string) (*Repo, error) { 134 var repo Repo 135 var nullableDescription sql.NullString 136 137 row := e.QueryRow(`select did, name, knot, created, at_uri, description from repos where did = ? and name = ?`, did, name) 138 139 var createdAt string 140 if err := row.Scan(&repo.Did, &repo.Name, &repo.Knot, &createdAt, &repo.AtUri, &nullableDescription); err != nil { 141 return nil, err 142 } 143 createdAtTime, _ := time.Parse(time.RFC3339, createdAt) 144 repo.Created = createdAtTime 145 146 if nullableDescription.Valid { 147 repo.Description = nullableDescription.String 148 } else { 149 repo.Description = "" 150 } 151 152 return &repo, nil 153} 154 155func GetRepoByAtUri(e Execer, atUri string) (*Repo, error) { 156 var repo Repo 157 var nullableDescription sql.NullString 158 159 row := e.QueryRow(`select did, name, knot, created, at_uri, description from repos where at_uri = ?`, atUri) 160 161 var createdAt string 162 if err := row.Scan(&repo.Did, &repo.Name, &repo.Knot, &createdAt, &repo.AtUri, &nullableDescription); err != nil { 163 return nil, err 164 } 165 createdAtTime, _ := time.Parse(time.RFC3339, createdAt) 166 repo.Created = createdAtTime 167 168 if nullableDescription.Valid { 169 repo.Description = nullableDescription.String 170 } else { 171 repo.Description = "" 172 } 173 174 return &repo, nil 175} 176 177func AddRepo(e Execer, repo *Repo) error { 178 _, err := e.Exec( 179 `insert into repos 180 (did, name, knot, rkey, at_uri, description, source) 181 values (?, ?, ?, ?, ?, ?, ?)`, 182 repo.Did, repo.Name, repo.Knot, repo.Rkey, repo.AtUri, repo.Description, repo.Source, 183 ) 184 return err 185} 186 187func RemoveRepo(e Execer, did, name string) error { 188 _, err := e.Exec(`delete from repos where did = ? and name = ?`, did, name) 189 return err 190} 191 192func GetRepoSource(e Execer, repoAt syntax.ATURI) (string, error) { 193 var nullableSource sql.NullString 194 err := e.QueryRow(`select source from repos where at_uri = ?`, repoAt).Scan(&nullableSource) 195 if err != nil { 196 return "", err 197 } 198 return nullableSource.String, nil 199} 200 201func GetForksByDid(e Execer, did string) ([]Repo, error) { 202 var repos []Repo 203 204 rows, err := e.Query( 205 `select did, name, knot, rkey, description, created, at_uri, source 206 from repos 207 where did = ? and source is not null and source != '' 208 order by created desc`, 209 did, 210 ) 211 if err != nil { 212 return nil, err 213 } 214 defer rows.Close() 215 216 for rows.Next() { 217 var repo Repo 218 var createdAt string 219 var nullableDescription sql.NullString 220 var nullableSource sql.NullString 221 222 err := rows.Scan(&repo.Did, &repo.Name, &repo.Knot, &repo.Rkey, &nullableDescription, &createdAt, &repo.AtUri, &nullableSource) 223 if err != nil { 224 return nil, err 225 } 226 227 if nullableDescription.Valid { 228 repo.Description = nullableDescription.String 229 } 230 231 if nullableSource.Valid { 232 repo.Source = nullableSource.String 233 } 234 235 createdAtTime, err := time.Parse(time.RFC3339, createdAt) 236 if err != nil { 237 repo.Created = time.Now() 238 } else { 239 repo.Created = createdAtTime 240 } 241 242 repos = append(repos, repo) 243 } 244 245 if err := rows.Err(); err != nil { 246 return nil, err 247 } 248 249 return repos, nil 250} 251 252func GetForkByDid(e Execer, did string, name string) (*Repo, error) { 253 var repo Repo 254 var createdAt string 255 var nullableDescription sql.NullString 256 var nullableSource sql.NullString 257 258 row := e.QueryRow( 259 `select did, name, knot, rkey, description, created, at_uri, source 260 from repos 261 where did = ? and name = ? and source is not null and source != ''`, 262 did, name, 263 ) 264 265 err := row.Scan(&repo.Did, &repo.Name, &repo.Knot, &repo.Rkey, &nullableDescription, &createdAt, &repo.AtUri, &nullableSource) 266 if err != nil { 267 return nil, err 268 } 269 270 if nullableDescription.Valid { 271 repo.Description = nullableDescription.String 272 } 273 274 if nullableSource.Valid { 275 repo.Source = nullableSource.String 276 } 277 278 createdAtTime, err := time.Parse(time.RFC3339, createdAt) 279 if err != nil { 280 repo.Created = time.Now() 281 } else { 282 repo.Created = createdAtTime 283 } 284 285 return &repo, nil 286} 287 288func AddCollaborator(e Execer, collaborator, repoOwnerDid, repoName, repoKnot string) error { 289 _, err := e.Exec( 290 `insert into collaborators (did, repo) 291 values (?, (select id from repos where did = ? and name = ? and knot = ?));`, 292 collaborator, repoOwnerDid, repoName, repoKnot) 293 return err 294} 295 296func UpdateDescription(e Execer, repoAt, newDescription string) error { 297 _, err := e.Exec( 298 `update repos set description = ? where at_uri = ?`, newDescription, repoAt) 299 return err 300} 301 302func CollaboratingIn(e Execer, collaborator string) ([]Repo, error) { 303 var repos []Repo 304 305 rows, err := e.Query( 306 `select 307 r.did, r.name, r.knot, r.rkey, r.description, r.created, count(s.id) as star_count 308 from 309 repos r 310 join 311 collaborators c on r.id = c.repo 312 left join 313 stars s on r.at_uri = s.repo_at 314 where 315 c.did = ? 316 group by 317 r.id;`, collaborator) 318 if err != nil { 319 return nil, err 320 } 321 defer rows.Close() 322 323 for rows.Next() { 324 var repo Repo 325 var repoStats RepoStats 326 var createdAt string 327 var nullableDescription sql.NullString 328 329 err := rows.Scan(&repo.Did, &repo.Name, &repo.Knot, &repo.Rkey, &nullableDescription, &createdAt, &repoStats.StarCount) 330 if err != nil { 331 return nil, err 332 } 333 334 if nullableDescription.Valid { 335 repo.Description = nullableDescription.String 336 } else { 337 repo.Description = "" 338 } 339 340 createdAtTime, err := time.Parse(time.RFC3339, createdAt) 341 if err != nil { 342 repo.Created = time.Now() 343 } else { 344 repo.Created = createdAtTime 345 } 346 347 repo.RepoStats = &repoStats 348 349 repos = append(repos, repo) 350 } 351 352 if err := rows.Err(); err != nil { 353 return nil, err 354 } 355 356 return repos, nil 357} 358 359type RepoStats struct { 360 StarCount int 361 IssueCount IssueCount 362 PullCount PullCount 363} 364 365func scanRepo(rows *sql.Rows, did, name, knot, rkey, description *string, created *time.Time, source *string) error { 366 var createdAt string 367 var nullableDescription sql.NullString 368 var nullableSource sql.NullString 369 if err := rows.Scan(did, name, knot, rkey, &nullableDescription, &createdAt, &nullableSource); err != nil { 370 return err 371 } 372 373 if nullableDescription.Valid { 374 *description = nullableDescription.String 375 } else { 376 *description = "" 377 } 378 379 createdAtTime, err := time.Parse(time.RFC3339, createdAt) 380 if err != nil { 381 *created = time.Now() 382 } else { 383 *created = createdAtTime 384 } 385 386 if nullableSource.Valid { 387 *source = nullableSource.String 388 } else { 389 *source = "" 390 } 391 392 return nil 393}