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}