Monorepo for Tangled

[WIP] appview/db: update query functions for repo_did across all tables

+341 -82
+14 -1
appview/db/artifact.go
··· 1 1 package db 2 2 3 3 import ( 4 + "database/sql" 4 5 "fmt" 5 6 "strings" 6 7 "time" ··· 12 13 ) 13 14 14 15 func AddArtifact(e Execer, artifact models.Artifact) error { 16 + var repoDid *string 17 + if artifact.RepoDid != "" { 18 + repoDid = &artifact.RepoDid 19 + } 15 20 _, err := e.Exec( 16 21 `insert or ignore into artifacts ( 17 22 did, 18 23 rkey, 19 24 repo_at, 25 + repo_did, 20 26 tag, 21 27 created, 22 28 blob_cid, ··· 24 30 size, 25 31 mimetype 26 32 ) 27 - values (?, ?, ?, ?, ?, ?, ?, ?, ?)`, 33 + values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, 28 34 artifact.Did, 29 35 artifact.Rkey, 30 36 artifact.RepoAt, 37 + repoDid, 31 38 artifact.Tag[:], 32 39 artifact.CreatedAt.Format(time.RFC3339), 33 40 artifact.BlobCid.String(), ··· 57 64 did, 58 65 rkey, 59 66 repo_at, 67 + repo_did, 60 68 tag, 61 69 created, 62 70 blob_cid, ··· 78 86 var createdAt string 79 87 var tag []byte 80 88 var blobCid string 89 + var repoDid sql.NullString 81 90 82 91 if err := rows.Scan( 83 92 &artifact.Did, 84 93 &artifact.Rkey, 85 94 &artifact.RepoAt, 95 + &repoDid, 86 96 &tag, 87 97 &createdAt, 88 98 &blobCid, ··· 91 101 &artifact.MimeType, 92 102 ); err != nil { 93 103 return nil, err 104 + } 105 + if repoDid.Valid { 106 + artifact.RepoDid = repoDid.String 94 107 } 95 108 96 109 artifact.CreatedAt, err = time.Parse(time.RFC3339, createdAt)
+15 -3
appview/db/collaborators.go
··· 1 1 package db 2 2 3 3 import ( 4 + "database/sql" 4 5 "fmt" 5 6 "strings" 6 7 "time" ··· 10 11 ) 11 12 12 13 func AddCollaborator(e Execer, c models.Collaborator) error { 14 + var repoDid *string 15 + if c.RepoDid != "" { 16 + repoDid = &c.RepoDid 17 + } 18 + 13 19 _, err := e.Exec( 14 - `insert into collaborators (did, rkey, subject_did, repo_at) values (?, ?, ?, ?);`, 15 - c.Did, c.Rkey, c.SubjectDid, c.RepoAt, 20 + `insert into collaborators (did, rkey, subject_did, repo_at, repo_did) values (?, ?, ?, ?, ?);`, 21 + c.Did, c.Rkey, c.SubjectDid, c.RepoAt, repoDid, 16 22 ) 17 23 return err 18 24 } ··· 80 86 rkey, 81 87 subject_did, 82 88 repo_at, 83 - created 89 + created, 90 + repo_did 84 91 from collaborators %s`, 85 92 whereClause, 86 93 ) ··· 92 99 for rows.Next() { 93 100 var collaborator models.Collaborator 94 101 var createdAt string 102 + var collabRepoDid sql.NullString 95 103 if err := rows.Scan( 96 104 &collaborator.Id, 97 105 &collaborator.Did, ··· 99 107 &collaborator.SubjectDid, 100 108 &collaborator.RepoAt, 101 109 &createdAt, 110 + &collabRepoDid, 102 111 ); err != nil { 103 112 return nil, err 104 113 } 105 114 collaborator.Created, err = time.Parse(time.RFC3339, createdAt) 106 115 if err != nil { 107 116 collaborator.Created = time.Now() 117 + } 118 + if collabRepoDid.Valid { 119 + collaborator.RepoDid = collabRepoDid.String 108 120 } 109 121 collaborators = append(collaborators, collaborator) 110 122 }
+28 -10
appview/db/issues.go
··· 17 17 ) 18 18 19 19 func PutIssue(tx *sql.Tx, issue *models.Issue) error { 20 - // ensure sequence exists 20 + var seqRepoDid *string 21 + if issue.RepoDid != "" { 22 + seqRepoDid = &issue.RepoDid 23 + } 21 24 _, err := tx.Exec(` 22 - insert or ignore into repo_issue_seqs (repo_at, next_issue_id) 23 - values (?, 1) 24 - `, issue.RepoAt) 25 + insert into repo_issue_seqs (repo_at, next_issue_id, repo_did) 26 + values (?, 1, ?) 27 + on conflict(repo_at) do update set repo_did = coalesce(excluded.repo_did, repo_did) 28 + `, issue.RepoAt, seqRepoDid) 25 29 if err != nil { 26 30 return err 27 31 } ··· 64 68 return err 65 69 } 66 70 71 + var repoDid *string 72 + if issue.RepoDid != "" { 73 + repoDid = &issue.RepoDid 74 + } 75 + 67 76 // insert new issue 68 77 row := tx.QueryRow(` 69 - insert into issues (repo_at, did, rkey, issue_id, title, body) 70 - values (?, ?, ?, ?, ?, ?) 78 + insert into issues (repo_at, repo_did, did, rkey, issue_id, title, body) 79 + values (?, ?, ?, ?, ?, ?, ?) 71 80 returning rowid, issue_id 72 - `, issue.RepoAt, issue.Did, issue.Rkey, newIssueId, issue.Title, issue.Body) 81 + `, issue.RepoAt, repoDid, issue.Did, issue.Rkey, newIssueId, issue.Title, issue.Body) 73 82 74 83 err = row.Scan(&issue.Id, &issue.IssueId) 75 84 if err != nil { ··· 83 92 } 84 93 85 94 func updateIssue(tx *sql.Tx, issue *models.Issue) error { 86 - // update existing issue 95 + var repoDid *string 96 + if issue.RepoDid != "" { 97 + repoDid = &issue.RepoDid 98 + } 87 99 _, err := tx.Exec(` 88 100 update issues 89 - set title = ?, body = ?, edited = ? 101 + set title = ?, body = ?, edited = ?, repo_did = coalesce(?, repo_did) 90 102 where did = ? and rkey = ? 91 - `, issue.Title, issue.Body, time.Now().Format(time.RFC3339), issue.Did, issue.Rkey) 103 + `, issue.Title, issue.Body, time.Now().Format(time.RFC3339), repoDid, issue.Did, issue.Rkey) 92 104 if err != nil { 93 105 return err 94 106 } ··· 133 145 did, 134 146 rkey, 135 147 repo_at, 148 + repo_did, 136 149 issue_id, 137 150 title, 138 151 body, ··· 161 174 var issue models.Issue 162 175 var createdAt string 163 176 var editedAt, deletedAt sql.Null[string] 177 + var nullableRepoDid sql.NullString 164 178 var rowNum int64 165 179 err := rows.Scan( 166 180 &issue.Id, 167 181 &issue.Did, 168 182 &issue.Rkey, 169 183 &issue.RepoAt, 184 + &nullableRepoDid, 170 185 &issue.IssueId, 171 186 &issue.Title, 172 187 &issue.Body, ··· 178 193 ) 179 194 if err != nil { 180 195 return nil, fmt.Errorf("failed to scan issue: %w", err) 196 + } 197 + if nullableRepoDid.Valid { 198 + issue.RepoDid = nullableRepoDid.String 181 199 } 182 200 183 201 if t, err := time.Parse(time.RFC3339, createdAt); err == nil {
+13 -3
appview/db/language.go
··· 24 24 } 25 25 26 26 query := fmt.Sprintf( 27 - `select id, repo_at, ref, is_default_ref, language, bytes from repo_languages %s`, 27 + `select id, repo_at, ref, is_default_ref, language, bytes, repo_did from repo_languages %s`, 28 28 whereClause, 29 29 ) 30 30 rows, err := e.Query(query, args...) ··· 37 37 for rows.Next() { 38 38 var rl models.RepoLanguage 39 39 var isDefaultRef int 40 + var langRepoDid sql.NullString 40 41 41 42 err := rows.Scan( 42 43 &rl.Id, ··· 45 46 &isDefaultRef, 46 47 &rl.Language, 47 48 &rl.Bytes, 49 + &langRepoDid, 48 50 ) 49 51 if err != nil { 50 52 return nil, fmt.Errorf("failed to scan: %w ", err) ··· 53 55 if isDefaultRef != 0 { 54 56 rl.IsDefaultRef = true 55 57 } 58 + if langRepoDid.Valid { 59 + rl.RepoDid = langRepoDid.String 60 + } 56 61 57 62 langs = append(langs, rl) 58 63 } ··· 65 70 66 71 func InsertRepoLanguages(e Execer, langs []models.RepoLanguage) error { 67 72 stmt, err := e.Prepare( 68 - "insert or replace into repo_languages (repo_at, ref, is_default_ref, language, bytes) values (?, ?, ?, ?, ?)", 73 + "insert or replace into repo_languages (repo_at, ref, is_default_ref, language, bytes, repo_did) values (?, ?, ?, ?, ?, ?)", 69 74 ) 70 75 if err != nil { 71 76 return err ··· 77 82 isDefaultRef = 1 78 83 } 79 84 80 - _, err := stmt.Exec(l.RepoAt, l.Ref, isDefaultRef, l.Language, l.Bytes) 85 + var repoDid *string 86 + if l.RepoDid != "" { 87 + repoDid = &l.RepoDid 88 + } 89 + 90 + _, err := stmt.Exec(l.RepoAt, l.Ref, isDefaultRef, l.Language, l.Bytes, repoDid) 81 91 if err != nil { 82 92 return err 83 93 }
+21 -2
appview/db/pipeline.go
··· 1 1 package db 2 2 3 3 import ( 4 + "database/sql" 4 5 "fmt" 5 6 "slices" 6 7 "strings" ··· 26 27 whereClause = " where " + strings.Join(conditions, " and ") 27 28 } 28 29 29 - query := fmt.Sprintf(`select id, rkey, knot, repo_owner, repo_name, sha, created from pipelines %s`, whereClause) 30 + query := fmt.Sprintf(`select id, rkey, knot, repo_owner, repo_name, sha, created, repo_did from pipelines %s`, whereClause) 30 31 31 32 rows, err := e.Query(query, args...) 32 33 ··· 38 39 for rows.Next() { 39 40 var pipeline models.Pipeline 40 41 var createdAt string 42 + var repoDid sql.NullString 41 43 err = rows.Scan( 42 44 &pipeline.Id, 43 45 &pipeline.Rkey, ··· 46 48 &pipeline.RepoName, 47 49 &pipeline.Sha, 48 50 &createdAt, 51 + &repoDid, 49 52 ) 50 53 if err != nil { 51 54 return nil, err ··· 54 57 if t, err := time.Parse(time.RFC3339, createdAt); err == nil { 55 58 pipeline.Created = t 56 59 } 60 + if repoDid.Valid { 61 + pipeline.RepoDid = repoDid.String 62 + } 57 63 58 64 pipelines = append(pipelines, pipeline) 59 65 } ··· 66 72 } 67 73 68 74 func AddPipeline(e Execer, pipeline models.Pipeline) error { 75 + var repoDid *string 76 + if pipeline.RepoDid != "" { 77 + repoDid = &pipeline.RepoDid 78 + } 79 + 69 80 args := []any{ 70 81 pipeline.Rkey, 71 82 pipeline.Knot, ··· 73 84 pipeline.RepoName, 74 85 pipeline.TriggerId, 75 86 pipeline.Sha, 87 + repoDid, 76 88 } 77 89 78 90 placeholders := make([]string, len(args)) ··· 87 99 repo_owner, 88 100 repo_name, 89 101 trigger_id, 90 - sha 102 + sha, 103 + repo_did 91 104 ) values (%s) 92 105 `, strings.Join(placeholders, ",")) 93 106 ··· 195 208 p.repo_name, 196 209 p.sha, 197 210 p.created, 211 + p.repo_did, 198 212 t.id, 199 213 t.kind, 200 214 t.push_ref, ··· 224 238 var p models.Pipeline 225 239 var t models.Trigger 226 240 var created string 241 + var repoDid sql.NullString 227 242 228 243 err := rows.Scan( 229 244 &p.Id, ··· 233 248 &p.RepoName, 234 249 &p.Sha, 235 250 &created, 251 + &repoDid, 236 252 &p.TriggerId, 237 253 &t.Kind, 238 254 &t.PushRef, ··· 250 266 p.Created, err = time.Parse(time.RFC3339, created) 251 267 if err != nil { 252 268 return nil, fmt.Errorf("invalid pipeline created timestamp %q: %w", created, err) 269 + } 270 + if repoDid.Valid { 271 + p.RepoDid = repoDid.String 253 272 } 254 273 255 274 t.Id = p.TriggerId
+6 -3
appview/db/profile.go
··· 75 75 // TODO: get this in the original query; requires COALESCE because nullable 76 76 var sourceRepo *models.Repo 77 77 if repo.Source != "" { 78 - sourceRepo, err = GetRepoByAtUri(e, repo.Source) 78 + if strings.HasPrefix(repo.Source, "did:") { 79 + sourceRepo, err = GetRepoByDid(e, repo.Source) 80 + } else { 81 + sourceRepo, err = GetRepoByAtUri(e, repo.Source) 82 + } 79 83 if err != nil { 80 - // the source repo was not found, skip this bit 81 84 log.Println("profile", "err", err) 82 85 } 83 86 } ··· 449 452 query = `select count(id) from repos where did = ?` 450 453 args = append(args, did) 451 454 case models.VanityStatStarCount: 452 - query = `select count(id) from stars where subject_at like 'at://' || ? || '%'` 455 + query = `select count(s.id) from stars s join repos r on (s.subject_at = r.at_uri or (s.subject_did is not null and s.subject_did = r.repo_did)) where r.did = ?` 453 456 args = append(args, did) 454 457 case models.VanityStatNone: 455 458 return 0, nil
+57 -12
appview/db/pulls.go
··· 18 18 ) 19 19 20 20 func NewPull(tx *sql.Tx, pull *models.Pull) error { 21 + var repoDid *string 22 + if pull.RepoDid != "" { 23 + repoDid = &pull.RepoDid 24 + } 25 + 21 26 _, err := tx.Exec(` 22 - insert or ignore into repo_pull_seqs (repo_at, next_pull_id) 23 - values (?, 1) 24 - `, pull.RepoAt) 27 + insert into repo_pull_seqs (repo_at, repo_did, next_pull_id) 28 + values (?, ?, 1) 29 + on conflict(repo_at) do update set repo_did = coalesce(excluded.repo_did, repo_did) 30 + `, pull.RepoAt, repoDid) 25 31 if err != nil { 26 32 return err 27 33 } ··· 40 46 pull.PullId = nextId 41 47 pull.State = models.PullOpen 42 48 43 - var sourceBranch, sourceRepoAt *string 49 + var sourceBranch, sourceRepoAt, sourceRepoDid *string 44 50 if pull.PullSource != nil { 45 51 sourceBranch = &pull.PullSource.Branch 46 52 if pull.PullSource.RepoAt != nil { 47 53 x := pull.PullSource.RepoAt.String() 48 54 sourceRepoAt = &x 49 55 } 56 + if pull.PullSource.RepoDid != "" { 57 + sourceRepoDid = &pull.PullSource.RepoDid 58 + } 50 59 } 51 60 52 61 var stackId, changeId, parentChangeId *string ··· 63 72 result, err := tx.Exec( 64 73 ` 65 74 insert into pulls ( 66 - repo_at, owner_did, pull_id, title, target_branch, body, rkey, state, source_branch, source_repo_at, stack_id, change_id, parent_change_id 75 + repo_at, owner_did, pull_id, title, target_branch, body, rkey, state, source_branch, source_repo_at, stack_id, change_id, parent_change_id, repo_did, source_repo_did 67 76 ) 68 - values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, 77 + values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, 69 78 pull.RepoAt, 70 79 pull.OwnerDid, 71 80 pull.PullId, ··· 79 88 stackId, 80 89 changeId, 81 90 parentChangeId, 91 + repoDid, 92 + sourceRepoDid, 82 93 ) 83 94 if err != nil { 84 95 return err ··· 159 170 source_repo_at, 160 171 stack_id, 161 172 change_id, 162 - parent_change_id 173 + parent_change_id, 174 + repo_did, 175 + source_repo_did 163 176 from 164 177 pulls 165 178 %s ··· 177 190 for rows.Next() { 178 191 var pull models.Pull 179 192 var createdAt string 180 - var sourceBranch, sourceRepoAt, stackId, changeId, parentChangeId sql.NullString 193 + var sourceBranch, sourceRepoAt, stackId, changeId, parentChangeId, repoDid, sourceRepoDid sql.NullString 181 194 err := rows.Scan( 182 195 &pull.ID, 183 196 &pull.OwnerDid, ··· 194 207 &stackId, 195 208 &changeId, 196 209 &parentChangeId, 210 + &repoDid, 211 + &sourceRepoDid, 197 212 ) 198 213 if err != nil { 199 214 return nil, err ··· 216 231 } 217 232 pull.PullSource.RepoAt = &sourceRepoAtParsed 218 233 } 234 + if sourceRepoDid.Valid { 235 + pull.PullSource.RepoDid = sourceRepoDid.String 236 + } 219 237 } 220 238 221 239 if stackId.Valid { ··· 227 245 if parentChangeId.Valid { 228 246 pull.ParentChangeId = parentChangeId.String 229 247 } 248 + if repoDid.Valid { 249 + pull.RepoDid = repoDid.String 250 + } 230 251 231 252 pulls[pull.AtUri()] = &pull 232 253 } ··· 441 462 owner_did, 442 463 comment_at, 443 464 body, 444 - created 465 + created, 466 + repo_did 445 467 from 446 468 pull_comments 447 469 %s ··· 459 481 for rows.Next() { 460 482 var comment models.PullComment 461 483 var createdAt string 484 + var commentRepoDid sql.NullString 462 485 err := rows.Scan( 463 486 &comment.ID, 464 487 &comment.PullId, ··· 468 491 &comment.CommentAt, 469 492 &comment.Body, 470 493 &createdAt, 494 + &commentRepoDid, 471 495 ) 472 496 if err != nil { 473 497 return nil, err ··· 475 499 476 500 if t, err := time.Parse(time.RFC3339, createdAt); err == nil { 477 501 comment.Created = t 502 + } 503 + if commentRepoDid.Valid { 504 + comment.RepoDid = commentRepoDid.String 478 505 } 479 506 480 507 atUri := comment.AtUri().String() ··· 522 549 p.created, 523 550 p.title, 524 551 p.state, 552 + p.repo_did, 525 553 r.did, 526 554 r.name, 527 555 r.knot, 528 556 r.rkey, 529 - r.created 557 + r.created, 558 + r.repo_did 530 559 from 531 560 pulls p 532 561 join ··· 544 573 var pull models.Pull 545 574 var repo models.Repo 546 575 var pullCreatedAt, repoCreatedAt string 576 + var pullRepoDid, repoRepoDid sql.NullString 547 577 err := rows.Scan( 548 578 &pull.OwnerDid, 549 579 &pull.RepoAt, ··· 551 581 &pullCreatedAt, 552 582 &pull.Title, 553 583 &pull.State, 584 + &pullRepoDid, 554 585 &repo.Did, 555 586 &repo.Name, 556 587 &repo.Knot, 557 588 &repo.Rkey, 558 589 &repoCreatedAt, 590 + &repoRepoDid, 559 591 ) 560 592 if err != nil { 561 593 return nil, err 562 594 } 563 595 596 + if pullRepoDid.Valid { 597 + pull.RepoDid = pullRepoDid.String 598 + } 599 + 564 600 pullCreatedTime, err := time.Parse(time.RFC3339, pullCreatedAt) 565 601 if err != nil { 566 602 return nil, err ··· 572 608 return nil, err 573 609 } 574 610 repo.Created = repoCreatedTime 611 + if repoRepoDid.Valid { 612 + repo.RepoDid = repoRepoDid.String 613 + } 575 614 576 615 pull.Repo = &repo 577 616 ··· 586 625 } 587 626 588 627 func NewPullComment(tx *sql.Tx, comment *models.PullComment) (int64, error) { 589 - query := `insert into pull_comments (owner_did, repo_at, submission_id, comment_at, pull_id, body) values (?, ?, ?, ?, ?, ?)` 628 + var repoDid *string 629 + if comment.RepoDid != "" { 630 + repoDid = &comment.RepoDid 631 + } 632 + 633 + query := `insert into pull_comments (owner_did, repo_at, submission_id, comment_at, pull_id, body, repo_did) values (?, ?, ?, ?, ?, ?, ?)` 590 634 res, err := tx.Exec( 591 635 query, 592 636 comment.OwnerDid, ··· 595 639 comment.CommentAt, 596 640 comment.PullId, 597 641 comment.Body, 642 + repoDid, 598 643 ) 599 644 if err != nil { 600 645 return 0, err ··· 614 659 615 660 func SetPullState(e Execer, repoAt syntax.ATURI, pullId int, pullState models.PullState) error { 616 661 _, err := e.Exec( 617 - `update pulls set state = ? where repo_at = ? and pull_id = ? and (state <> ? or state <> ?)`, 662 + `update pulls set state = ? where repo_at = ? and pull_id = ? and (state <> ? and state <> ?)`, 618 663 pullState, 619 664 repoAt, 620 665 pullId,
+36 -12
appview/db/reference.go
··· 60 60 on r.did = inp.owner_did 61 61 and r.name = inp.name 62 62 join issues i 63 - on i.repo_at = r.at_uri 63 + on coalesce( 64 + nullif(i.repo_did, '') = nullif(r.repo_did, ''), 65 + i.repo_at = r.at_uri 66 + ) 64 67 and i.issue_id = inp.issue_id 65 68 left join issue_comments c 66 69 on inp.comment_id is not null ··· 131 134 on r.did = inp.owner_did 132 135 and r.name = inp.name 133 136 join pulls p 134 - on p.repo_at = r.at_uri 137 + on coalesce( 138 + nullif(p.repo_did, '') = nullif(r.repo_did, ''), 139 + p.repo_at = r.at_uri 140 + ) 135 141 and p.pull_id = inp.pull_id 136 142 left join pull_comments c 137 143 on inp.comment_id is not null 138 - and c.repo_at = r.at_uri and c.pull_id = p.pull_id 144 + and coalesce( 145 + nullif(c.repo_did, '') = nullif(r.repo_did, ''), 146 + c.repo_at = r.at_uri 147 + ) and c.pull_id = p.pull_id 139 148 and c.id = inp.comment_id 140 149 `, 141 150 strings.Join(vals, ","), ··· 316 325 } 317 326 rows, err := e.Query( 318 327 fmt.Sprintf( 319 - `select r.did, r.name, i.issue_id, i.title, i.open 328 + `select distinct r.did, r.name, i.issue_id, i.title, i.open 320 329 from issues i 321 330 join repos r 322 - on r.at_uri = i.repo_at 331 + on coalesce( 332 + nullif(i.repo_did, '') = nullif(r.repo_did, ''), 333 + i.repo_at = r.at_uri 334 + ) 323 335 where (i.did, i.rkey) in (%s)`, 324 336 strings.Join(vals, ","), 325 337 ), ··· 351 363 filter := orm.FilterIn("c.at_uri", aturis) 352 364 rows, err := e.Query( 353 365 fmt.Sprintf( 354 - `select r.did, r.name, i.issue_id, c.id, i.title, i.open 366 + `select distinct r.did, r.name, i.issue_id, c.id, i.title, i.open 355 367 from issue_comments c 356 368 join issues i 357 369 on i.at_uri = c.issue_at 358 370 join repos r 359 - on r.at_uri = i.repo_at 371 + on coalesce( 372 + nullif(i.repo_did, '') = nullif(r.repo_did, ''), 373 + i.repo_at = r.at_uri 374 + ) 360 375 where %s`, 361 376 filter.Condition(), 362 377 ), ··· 396 411 } 397 412 rows, err := e.Query( 398 413 fmt.Sprintf( 399 - `select r.did, r.name, p.pull_id, p.title, p.state 414 + `select distinct r.did, r.name, p.pull_id, p.title, p.state 400 415 from pulls p 401 416 join repos r 402 - on r.at_uri = p.repo_at 417 + on coalesce( 418 + nullif(p.repo_did, '') = nullif(r.repo_did, ''), 419 + p.repo_at = r.at_uri 420 + ) 403 421 where (p.owner_did, p.rkey) in (%s)`, 404 422 strings.Join(vals, ","), 405 423 ), ··· 431 449 filter := orm.FilterIn("c.comment_at", aturis) 432 450 rows, err := e.Query( 433 451 fmt.Sprintf( 434 - `select r.did, r.name, p.pull_id, c.id, p.title, p.state 452 + `select distinct r.did, r.name, p.pull_id, c.id, p.title, p.state 435 453 from repos r 436 454 join pulls p 437 - on r.at_uri = p.repo_at 455 + on coalesce( 456 + nullif(p.repo_did, '') = nullif(r.repo_did, ''), 457 + p.repo_at = r.at_uri 458 + ) 438 459 join pull_comments c 439 - on r.at_uri = c.repo_at and p.pull_id = c.pull_id 460 + on coalesce( 461 + nullif(c.repo_did, '') = nullif(r.repo_did, ''), 462 + c.repo_at = r.at_uri 463 + ) and p.pull_id = c.pull_id 440 464 where %s`, 441 465 filter.Condition(), 442 466 ),
+107 -26
appview/db/repos.go
··· 46 46 website, 47 47 topics, 48 48 source, 49 - spindle 49 + spindle, 50 + repo_did 50 51 from 51 52 repos r 52 53 %s ··· 64 65 for rows.Next() { 65 66 var repo models.Repo 66 67 var createdAt string 67 - var description, website, topicStr, source, spindle sql.NullString 68 + var description, website, topicStr, source, spindle, repoDid sql.NullString 68 69 69 70 err := rows.Scan( 70 71 &repo.Id, ··· 78 79 &topicStr, 79 80 &source, 80 81 &spindle, 82 + &repoDid, 81 83 ) 82 84 if err != nil { 83 85 return nil, fmt.Errorf("failed to execute repo query: %w ", err) ··· 100 102 } 101 103 if spindle.Valid { 102 104 repo.Spindle = spindle.String 105 + } 106 + if repoDid.Valid { 107 + repo.RepoDid = repoDid.String 103 108 } 104 109 105 110 repo.RepoStats = &models.RepoStats{} ··· 184 189 return nil, fmt.Errorf("failed to execute lang query: %w ", err) 185 190 } 186 191 192 + var repoDids []any 193 + repoDidToAt := make(map[string]syntax.ATURI) 194 + for atUri, r := range repoMap { 195 + if r.RepoDid != "" { 196 + repoDids = append(repoDids, r.RepoDid) 197 + repoDidToAt[r.RepoDid] = atUri 198 + } 199 + } 200 + 201 + didInClause := "''" 202 + if len(repoDids) > 0 { 203 + didInClause = strings.TrimSuffix(strings.Repeat("?, ", len(repoDids)), ", ") 204 + } 187 205 starCountQuery := fmt.Sprintf( 188 - `select 189 - subject_at, count(1) 206 + `select coalesce(subject_did, subject_at) as key, count(1) 190 207 from stars 191 - where subject_at in (%s) 192 - group by subject_at`, 193 - inClause, 208 + where subject_at in (%s) or subject_did in (%s) 209 + group by key`, 210 + inClause, didInClause, 194 211 ) 195 - rows, err = e.Query(starCountQuery, args...) 212 + starArgs := append(append([]any{}, args...), repoDids...) 213 + rows, err = e.Query(starCountQuery, starArgs...) 196 214 if err != nil { 197 215 return nil, fmt.Errorf("failed to execute star-count query: %w ", err) 198 216 } 199 217 defer rows.Close() 200 218 201 219 for rows.Next() { 202 - var repoat string 220 + var key string 203 221 var count int 204 - if err := rows.Scan(&repoat, &count); err != nil { 222 + if err := rows.Scan(&key, &count); err != nil { 205 223 log.Println("err", "err", err) 206 224 continue 207 225 } 208 - if r, ok := repoMap[syntax.ATURI(repoat)]; ok { 226 + if r, ok := repoMap[syntax.ATURI(key)]; ok { 209 227 r.RepoStats.StarCount = count 228 + } else if atUri, ok := repoDidToAt[key]; ok { 229 + if r, ok := repoMap[atUri]; ok { 230 + r.RepoStats.StarCount = count 231 + } 210 232 } 211 233 } 212 234 if err = rows.Err(); err != nil { ··· 352 374 var nullableDescription sql.NullString 353 375 var nullableWebsite sql.NullString 354 376 var nullableTopicStr sql.NullString 377 + var nullableRepoDid sql.NullString 378 + var nullableSource sql.NullString 379 + var nullableSpindle sql.NullString 355 380 356 - row := e.QueryRow(`select id, did, name, knot, created, rkey, description, website, topics from repos where at_uri = ?`, atUri) 381 + row := e.QueryRow(`select id, did, name, knot, created, rkey, description, website, topics, source, spindle, repo_did from repos where at_uri = ?`, atUri) 357 382 358 383 var createdAt string 359 - if err := row.Scan(&repo.Id, &repo.Did, &repo.Name, &repo.Knot, &createdAt, &repo.Rkey, &nullableDescription, &nullableWebsite, &nullableTopicStr); err != nil { 384 + if err := row.Scan(&repo.Id, &repo.Did, &repo.Name, &repo.Knot, &createdAt, &repo.Rkey, &nullableDescription, &nullableWebsite, &nullableTopicStr, &nullableSource, &nullableSpindle, &nullableRepoDid); err != nil { 360 385 return nil, err 361 386 } 362 387 createdAtTime, _ := time.Parse(time.RFC3339, createdAt) ··· 371 396 if nullableTopicStr.Valid { 372 397 repo.Topics = strings.Fields(nullableTopicStr.String) 373 398 } 399 + if nullableSource.Valid { 400 + repo.Source = nullableSource.String 401 + } 402 + if nullableSpindle.Valid { 403 + repo.Spindle = nullableSpindle.String 404 + } 405 + if nullableRepoDid.Valid { 406 + repo.RepoDid = nullableRepoDid.String 407 + } 374 408 375 409 return &repo, nil 376 410 } 377 411 378 412 func PutRepo(tx *sql.Tx, repo models.Repo) error { 413 + var repoDid *string 414 + if repo.RepoDid != "" { 415 + repoDid = &repo.RepoDid 416 + } 379 417 _, err := tx.Exec( 380 418 `update repos 381 - set knot = ?, description = ?, website = ?, topics = ? 419 + set knot = ?, description = ?, website = ?, topics = ?, repo_did = coalesce(?, repo_did) 382 420 where did = ? and rkey = ? 383 421 `, 384 - repo.Knot, repo.Description, repo.Website, repo.TopicStr(), repo.Did, repo.Rkey, 422 + repo.Knot, repo.Description, repo.Website, repo.TopicStr(), repoDid, repo.Did, repo.Rkey, 385 423 ) 386 424 return err 387 425 } 388 426 389 427 func AddRepo(tx *sql.Tx, repo *models.Repo) error { 428 + var repoDid *string 429 + if repo.RepoDid != "" { 430 + repoDid = &repo.RepoDid 431 + } 390 432 _, err := tx.Exec( 391 433 `insert into repos 392 - (did, name, knot, rkey, at_uri, description, website, topics, source) 393 - values (?, ?, ?, ?, ?, ?, ?, ?, ?)`, 394 - repo.Did, repo.Name, repo.Knot, repo.Rkey, repo.RepoAt().String(), repo.Description, repo.Website, repo.TopicStr(), repo.Source, 434 + (did, name, knot, rkey, at_uri, description, website, topics, source, repo_did) 435 + values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, 436 + repo.Did, repo.Name, repo.Knot, repo.Rkey, repo.RepoAt().String(), repo.Description, repo.Website, repo.TopicStr(), repo.Source, repoDid, 395 437 ) 396 438 if err != nil { 397 439 return fmt.Errorf("failed to insert repo: %w", err) ··· 401 443 if err := SubscribeLabel(tx, &models.RepoLabel{ 402 444 RepoAt: repo.RepoAt(), 403 445 LabelAt: syntax.ATURI(dl), 446 + RepoDid: repo.RepoDid, 404 447 }); err != nil { 405 448 return fmt.Errorf("failed to subscribe to label: %w", err) 406 449 } ··· 431 474 if err != nil { 432 475 return nil, err 433 476 } 477 + if strings.HasPrefix(source, "did:") { 478 + return GetRepoByDid(e, source) 479 + } 434 480 return GetRepoByAtUri(e, source) 435 481 } 436 482 ··· 438 484 var repos []models.Repo 439 485 440 486 rows, err := e.Query( 441 - `select distinct r.id, r.did, r.name, r.knot, r.rkey, r.description, r.website, r.created, r.source 487 + `select distinct r.id, r.did, r.name, r.knot, r.rkey, r.description, r.website, r.created, r.source, r.repo_did 442 488 from repos r 443 489 left join collaborators c on r.at_uri = c.repo_at 444 490 where (r.did = ? or c.subject_did = ?) ··· 458 504 var nullableDescription sql.NullString 459 505 var nullableWebsite sql.NullString 460 506 var nullableSource sql.NullString 507 + var nullableRepoDid sql.NullString 461 508 462 - err := rows.Scan(&repo.Id, &repo.Did, &repo.Name, &repo.Knot, &repo.Rkey, &nullableDescription, &nullableWebsite, &createdAt, &nullableSource) 509 + err := rows.Scan(&repo.Id, &repo.Did, &repo.Name, &repo.Knot, &repo.Rkey, &nullableDescription, &nullableWebsite, &createdAt, &nullableSource, &nullableRepoDid) 463 510 if err != nil { 464 511 return nil, err 465 512 } ··· 467 514 if nullableDescription.Valid { 468 515 repo.Description = nullableDescription.String 469 516 } 517 + if nullableWebsite.Valid { 518 + repo.Website = nullableWebsite.String 519 + } 470 520 471 521 if nullableSource.Valid { 472 522 repo.Source = nullableSource.String 473 523 } 524 + if nullableRepoDid.Valid { 525 + repo.RepoDid = nullableRepoDid.String 526 + } 474 527 475 528 createdAtTime, err := time.Parse(time.RFC3339, createdAt) 476 529 if err != nil { ··· 496 549 var nullableWebsite sql.NullString 497 550 var nullableTopicStr sql.NullString 498 551 var nullableSource sql.NullString 552 + var nullableRepoDid sql.NullString 499 553 500 554 row := e.QueryRow( 501 - `select id, did, name, knot, rkey, description, website, topics, created, source 555 + `select id, did, name, knot, rkey, description, website, topics, created, source, repo_did 502 556 from repos 503 557 where did = ? and name = ? and source is not null and source != ''`, 504 558 did, name, 505 559 ) 506 560 507 - err := row.Scan(&repo.Id, &repo.Did, &repo.Name, &repo.Knot, &repo.Rkey, &nullableDescription, &nullableWebsite, &nullableTopicStr, &createdAt, &nullableSource) 561 + err := row.Scan(&repo.Id, &repo.Did, &repo.Name, &repo.Knot, &repo.Rkey, &nullableDescription, &nullableWebsite, &nullableTopicStr, &createdAt, &nullableSource, &nullableRepoDid) 508 562 if err != nil { 509 563 return nil, err 510 564 } ··· 524 578 if nullableSource.Valid { 525 579 repo.Source = nullableSource.String 526 580 } 581 + if nullableRepoDid.Valid { 582 + repo.RepoDid = nullableRepoDid.String 583 + } 527 584 528 585 createdAtTime, err := time.Parse(time.RFC3339, createdAt) 529 586 if err != nil { ··· 535 592 return &repo, nil 536 593 } 537 594 595 + func GetRepoByDid(e Execer, repoDid string) (*models.Repo, error) { 596 + return GetRepo(e, orm.FilterEq("repo_did", repoDid)) 597 + } 598 + 599 + func EnqueuePdsRewrite(e Execer, userDid, repoDid, recordNsid, recordRkey, oldRepoAt string) error { 600 + _, err := e.Exec( 601 + `INSERT OR IGNORE INTO pds_rewrite_status 602 + (user_did, repo_did, record_nsid, record_rkey, old_repo_at, status) 603 + VALUES (?, ?, ?, ?, ?, 'pending')`, 604 + userDid, repoDid, recordNsid, recordRkey, oldRepoAt, 605 + ) 606 + return err 607 + } 608 + 538 609 func UpdateDescription(e Execer, repoAt, newDescription string) error { 539 610 _, err := e.Exec( 540 611 `update repos set description = ? where at_uri = ?`, newDescription, repoAt) ··· 548 619 } 549 620 550 621 func SubscribeLabel(e Execer, rl *models.RepoLabel) error { 551 - query := `insert or ignore into repo_labels (repo_at, label_at) values (?, ?)` 622 + var repoDid *string 623 + if rl.RepoDid != "" { 624 + repoDid = &rl.RepoDid 625 + } 626 + query := `insert into repo_labels (repo_at, label_at, repo_did) 627 + values (?, ?, ?) 628 + on conflict(repo_at, label_at) do update set repo_did = coalesce(excluded.repo_did, repo_did)` 552 629 553 - _, err := e.Exec(query, rl.RepoAt.String(), rl.LabelAt.String()) 630 + _, err := e.Exec(query, rl.RepoAt.String(), rl.LabelAt.String(), repoDid) 554 631 return err 555 632 } 556 633 ··· 585 662 whereClause = " where " + strings.Join(conditions, " and ") 586 663 } 587 664 588 - query := fmt.Sprintf(`select id, repo_at, label_at from repo_labels %s`, whereClause) 665 + query := fmt.Sprintf(`select id, repo_at, label_at, repo_did from repo_labels %s`, whereClause) 589 666 590 667 rows, err := e.Query(query, args...) 591 668 if err != nil { ··· 596 673 var labels []models.RepoLabel 597 674 for rows.Next() { 598 675 var label models.RepoLabel 676 + var labelRepoDid sql.NullString 599 677 600 - err := rows.Scan(&label.Id, &label.RepoAt, &label.LabelAt) 678 + err := rows.Scan(&label.Id, &label.RepoAt, &label.LabelAt, &labelRepoDid) 601 679 if err != nil { 602 680 return nil, err 681 + } 682 + if labelRepoDid.Valid { 683 + label.RepoDid = labelRepoDid.String 603 684 } 604 685 605 686 labels = append(labels, label)
+29 -5
appview/db/star.go
··· 15 15 ) 16 16 17 17 func AddStar(e Execer, star *models.Star) error { 18 - query := `insert or ignore into stars (did, subject_at, rkey) values (?, ?, ?)` 18 + query := `insert or ignore into stars (did, subject_at, rkey, subject_did) values (?, ?, ?, ?)` 19 + var subjectDid *string 20 + if star.SubjectDid != "" { 21 + subjectDid = &star.SubjectDid 22 + } 19 23 _, err := e.Exec( 20 24 query, 21 25 star.Did, 22 26 star.RepoAt.String(), 23 27 star.Rkey, 28 + subjectDid, 24 29 ) 25 30 return err 26 31 } ··· 28 33 // Get a star record 29 34 func GetStar(e Execer, did string, subjectAt syntax.ATURI) (*models.Star, error) { 30 35 query := ` 31 - select did, subject_at, created, rkey 36 + select did, subject_at, created, rkey, subject_did 32 37 from stars 33 38 where did = ? and subject_at = ?` 34 39 row := e.QueryRow(query, did, subjectAt) 35 40 36 41 var star models.Star 37 42 var created string 38 - err := row.Scan(&star.Did, &star.RepoAt, &created, &star.Rkey) 43 + var nullableSubjectDid sql.NullString 44 + err := row.Scan(&star.Did, &star.RepoAt, &created, &star.Rkey, &nullableSubjectDid) 39 45 if err != nil { 40 46 return nil, err 47 + } 48 + if nullableSubjectDid.Valid { 49 + star.SubjectDid = nullableSubjectDid.String 41 50 } 42 51 43 52 createdAtTime, err := time.Parse(time.RFC3339, created) ··· 73 82 return stars, nil 74 83 } 75 84 85 + func GetStarCountByRepoDid(e Execer, repoDid string, repoAt syntax.ATURI) (int, error) { 86 + stars := 0 87 + err := e.QueryRow( 88 + `select count(did) from stars where subject_did = ? or subject_at = ?`, 89 + repoDid, repoAt.String()).Scan(&stars) 90 + if err != nil { 91 + return 0, err 92 + } 93 + return stars, nil 94 + } 95 + 76 96 // getStarStatuses returns a map of repo URIs to star status for a given user 77 97 // This is an internal helper function to avoid N+1 queries 78 98 func getStarStatuses(e Execer, userDid string, repoAts []syntax.ATURI) (map[string]bool, error) { ··· 153 173 } 154 174 155 175 repoQuery := fmt.Sprintf( 156 - `select did, subject_at, created, rkey 176 + `select did, subject_at, created, rkey, subject_did 157 177 from stars 158 178 %s 159 179 order by created desc ··· 171 191 for rows.Next() { 172 192 var star models.Star 173 193 var created string 174 - err := rows.Scan(&star.Did, &star.RepoAt, &created, &star.Rkey) 194 + var nullableSubjectDid sql.NullString 195 + err := rows.Scan(&star.Did, &star.RepoAt, &created, &star.Rkey, &nullableSubjectDid) 175 196 if err != nil { 176 197 return nil, err 198 + } 199 + if nullableSubjectDid.Valid { 200 + star.SubjectDid = nullableSubjectDid.String 177 201 } 178 202 179 203 star.Created = time.Now()
+15 -5
appview/db/webhooks.go
··· 34 34 active, 35 35 events, 36 36 created_at, 37 - updated_at 37 + updated_at, 38 + repo_did 38 39 from webhooks 39 40 %s 40 41 order by created_at desc ··· 50 51 for rows.Next() { 51 52 var wh models.Webhook 52 53 var createdAt, updatedAt, eventsStr string 53 - var secret sql.NullString 54 + var secret, whRepoDid sql.NullString 54 55 var active int 55 56 56 57 err := rows.Scan( ··· 62 63 &eventsStr, 63 64 &createdAt, 64 65 &updatedAt, 66 + &whRepoDid, 65 67 ) 66 68 if err != nil { 67 69 return nil, fmt.Errorf("failed to scan webhook: %w", err) ··· 80 82 } 81 83 if t, err := time.Parse(time.RFC3339, updatedAt); err == nil { 82 84 wh.UpdatedAt = t 85 + } 86 + if whRepoDid.Valid { 87 + wh.RepoDid = whRepoDid.String 83 88 } 84 89 85 90 webhooks = append(webhooks, wh) ··· 118 123 active = 1 119 124 } 120 125 126 + var repoDid *string 127 + if webhook.RepoDid != "" { 128 + repoDid = &webhook.RepoDid 129 + } 130 + 121 131 result, err := e.Exec(` 122 - insert into webhooks (repo_at, url, secret, active, events) 123 - values (?, ?, ?, ?, ?) 124 - `, webhook.RepoAt.String(), webhook.Url, webhook.Secret, active, eventsStr) 132 + insert into webhooks (repo_at, url, secret, active, events, repo_did) 133 + values (?, ?, ?, ?, ?, ?) 134 + `, webhook.RepoAt.String(), webhook.Url, webhook.Secret, active, eventsStr, repoDid) 125 135 126 136 if err != nil { 127 137 return fmt.Errorf("failed to insert webhook: %w", err)