Mirror of @tangled.org/core. Running on a Raspberry Pi Zero 2 (Please be gentle).
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

appview,lexicons: atprotate the mentions & references

Storing references parsed from the markdown body in atproto record and
DB. There can be lots of reference types considering the from/to types
so storing both as AT-URIs

Using `sql.Tx` more to combine multiple DB query to single recoverable
operation.

Note: Pulls don't have mentinos/references yet

Signed-off-by: Seongmin Lee <git@boltless.me>

authored by

Seongmin Lee and committed by
Tangled
3b2409e9 d2dcc711

+439 -98
api/tangled/cbor_gen.go

This is a binary file and will not be displayed.

api/tangled/issuecomment.go

This is a binary file and will not be displayed.

api/tangled/pullcomment.go

This is a binary file and will not be displayed.

api/tangled/repoissue.go

This is a binary file and will not be displayed.

api/tangled/repopull.go

This is a binary file and will not be displayed.

+9
appview/db/db.go
··· 561 561 email_notifications integer not null default 0 562 562 ); 563 563 564 + create table if not exists reference_links ( 565 + id integer primary key autoincrement, 566 + from_at text not null, 567 + to_at text not null, 568 + unique (from_at, to_at) 569 + ); 570 + 564 571 create table if not exists migrations ( 565 572 id integer primary key autoincrement, 566 573 name text unique ··· 576 569 -- indexes for better performance 577 570 create index if not exists idx_notifications_recipient_created on notifications(recipient_did, created desc); 578 571 create index if not exists idx_notifications_recipient_read on notifications(recipient_did, read); 572 + create index if not exists idx_references_from_at on reference_links(from_at); 573 + create index if not exists idx_references_to_at on reference_links(to_at); 579 574 `) 580 575 if err != nil { 581 576 return nil, err
+73 -18
appview/db/issues.go
··· 10 10 "time" 11 11 12 12 "github.com/bluesky-social/indigo/atproto/syntax" 13 + "tangled.org/core/api/tangled" 13 14 "tangled.org/core/appview/models" 14 15 "tangled.org/core/appview/pagination" 15 16 ) ··· 70 69 returning rowid, issue_id 71 70 `, issue.RepoAt, issue.Did, issue.Rkey, newIssueId, issue.Title, issue.Body) 72 71 73 - return row.Scan(&issue.Id, &issue.IssueId) 72 + err = row.Scan(&issue.Id, &issue.IssueId) 73 + if err != nil { 74 + return fmt.Errorf("scan row: %w", err) 75 + } 76 + 77 + if err := putReferences(tx, issue.AtUri(), issue.References); err != nil { 78 + return fmt.Errorf("put reference_links: %w", err) 79 + } 80 + return nil 74 81 } 75 82 76 83 func updateIssue(tx *sql.Tx, issue *models.Issue) error { ··· 88 79 set title = ?, body = ?, edited = ? 89 80 where did = ? and rkey = ? 90 81 `, issue.Title, issue.Body, time.Now().Format(time.RFC3339), issue.Did, issue.Rkey) 91 - return err 82 + if err != nil { 83 + return err 84 + } 85 + 86 + if err := putReferences(tx, issue.AtUri(), issue.References); err != nil { 87 + return fmt.Errorf("put reference_links: %w", err) 88 + } 89 + return nil 92 90 } 93 91 94 92 func GetIssuesPaginated(e Execer, page pagination.Page, filters ...filter) ([]models.Issue, error) { ··· 250 234 } 251 235 } 252 236 237 + // collect references for each issue 238 + allReferencs, err := GetReferencesAll(e, FilterIn("from_at", issueAts)) 239 + if err != nil { 240 + return nil, fmt.Errorf("failed to query reference_links: %w", err) 241 + } 242 + for issueAt, references := range allReferencs { 243 + if issue, ok := issueMap[issueAt.String()]; ok { 244 + issue.References = references 245 + } 246 + } 247 + 253 248 var issues []models.Issue 254 249 for _, i := range issueMap { 255 250 issues = append(issues, *i) ··· 350 323 return ids, nil 351 324 } 352 325 353 - func AddIssueComment(e Execer, c models.IssueComment) (int64, error) { 354 - result, err := e.Exec( 326 + func AddIssueComment(tx *sql.Tx, c models.IssueComment) (int64, error) { 327 + result, err := tx.Exec( 355 328 `insert into issue_comments ( 356 329 did, 357 330 rkey, ··· 390 363 return 0, err 391 364 } 392 365 366 + if err := putReferences(tx, c.AtUri(), c.References); err != nil { 367 + return 0, fmt.Errorf("put reference_links: %w", err) 368 + } 369 + 393 370 return id, nil 394 371 } 395 372 ··· 417 386 } 418 387 419 388 func GetIssueComments(e Execer, filters ...filter) ([]models.IssueComment, error) { 420 - var comments []models.IssueComment 389 + commentMap := make(map[string]*models.IssueComment) 421 390 422 391 var conditions []string 423 392 var args []any ··· 496 465 comment.ReplyTo = &replyTo.V 497 466 } 498 467 499 - comments = append(comments, comment) 468 + atUri := comment.AtUri().String() 469 + commentMap[atUri] = &comment 500 470 } 501 471 502 472 if err = rows.Err(); err != nil { 503 473 return nil, err 504 474 } 505 475 476 + // collect references for each comments 477 + commentAts := slices.Collect(maps.Keys(commentMap)) 478 + allReferencs, err := GetReferencesAll(e, FilterIn("from_at", commentAts)) 479 + if err != nil { 480 + return nil, fmt.Errorf("failed to query reference_links: %w", err) 481 + } 482 + for commentAt, references := range allReferencs { 483 + if comment, ok := commentMap[commentAt.String()]; ok { 484 + comment.References = references 485 + } 486 + } 487 + 488 + var comments []models.IssueComment 489 + for _, c := range commentMap { 490 + comments = append(comments, *c) 491 + } 492 + 493 + sort.Slice(comments, func(i, j int) bool { 494 + return comments[i].Created.After(comments[j].Created) 495 + }) 496 + 506 497 return comments, nil 507 498 } 508 499 509 - func DeleteIssues(e Execer, filters ...filter) error { 510 - var conditions []string 511 - var args []any 512 - for _, filter := range filters { 513 - conditions = append(conditions, filter.Condition()) 514 - args = append(args, filter.Arg()...) 500 + func DeleteIssues(tx *sql.Tx, did, rkey string) error { 501 + _, err := tx.Exec( 502 + `delete from issues 503 + where did = ? and rkey = ?`, 504 + did, 505 + rkey, 506 + ) 507 + if err != nil { 508 + return fmt.Errorf("delete issue: %w", err) 515 509 } 516 510 517 - whereClause := "" 518 - if conditions != nil { 519 - whereClause = " where " + strings.Join(conditions, " and ") 511 + uri := syntax.ATURI(fmt.Sprintf("at://%s/%s/%s", did, tangled.RepoIssueNSID, rkey)) 512 + err = deleteReferences(tx, uri) 513 + if err != nil { 514 + return fmt.Errorf("delete reference_links: %w", err) 520 515 } 521 516 522 - query := fmt.Sprintf(`delete from issues %s`, whereClause) 523 - _, err := e.Exec(query, args...) 524 - return err 517 + return nil 525 518 } 526 519 527 520 func CloseIssues(e Execer, filters ...filter) error {
+31 -5
appview/db/pulls.go
··· 432 432 submissionIds := slices.Collect(maps.Keys(submissionMap)) 433 433 comments, err := GetPullComments(e, FilterIn("submission_id", submissionIds)) 434 434 if err != nil { 435 - return nil, err 435 + return nil, fmt.Errorf("failed to get pull comments: %w", err) 436 436 } 437 437 for _, comment := range comments { 438 438 if submission, ok := submissionMap[comment.SubmissionId]; ok { ··· 492 492 } 493 493 defer rows.Close() 494 494 495 - var comments []models.PullComment 495 + commentMap := make(map[string]*models.PullComment) 496 496 for rows.Next() { 497 497 var comment models.PullComment 498 498 var createdAt string ··· 514 514 comment.Created = t 515 515 } 516 516 517 - comments = append(comments, comment) 517 + atUri := comment.AtUri().String() 518 + commentMap[atUri] = &comment 518 519 } 519 520 520 521 if err := rows.Err(); err != nil { 521 522 return nil, err 522 523 } 524 + 525 + // collect references for each comments 526 + commentAts := slices.Collect(maps.Keys(commentMap)) 527 + allReferencs, err := GetReferencesAll(e, FilterIn("from_at", commentAts)) 528 + if err != nil { 529 + return nil, fmt.Errorf("failed to query reference_links: %w", err) 530 + } 531 + for commentAt, references := range allReferencs { 532 + if comment, ok := commentMap[commentAt.String()]; ok { 533 + comment.References = references 534 + } 535 + } 536 + 537 + var comments []models.PullComment 538 + for _, c := range commentMap { 539 + comments = append(comments, *c) 540 + } 541 + 542 + sort.Slice(comments, func(i, j int) bool { 543 + return comments[i].Created.Before(comments[j].Created) 544 + }) 523 545 524 546 return comments, nil 525 547 } ··· 622 600 return pulls, nil 623 601 } 624 602 625 - func NewPullComment(e Execer, comment *models.PullComment) (int64, error) { 603 + func NewPullComment(tx *sql.Tx, comment *models.PullComment) (int64, error) { 626 604 query := `insert into pull_comments (owner_did, repo_at, submission_id, comment_at, pull_id, body) values (?, ?, ?, ?, ?, ?)` 627 - res, err := e.Exec( 605 + res, err := tx.Exec( 628 606 query, 629 607 comment.OwnerDid, 630 608 comment.RepoAt, ··· 640 618 i, err := res.LastInsertId() 641 619 if err != nil { 642 620 return 0, err 621 + } 622 + 623 + if err := putReferences(tx, comment.AtUri(), comment.References); err != nil { 624 + return 0, fmt.Errorf("put reference_links: %w", err) 643 625 } 644 626 645 627 return i, nil
+92 -14
appview/db/reference.go
··· 10 10 "tangled.org/core/appview/models" 11 11 ) 12 12 13 - // FindReferences resolves refLinks to Issue/PR/IssueComment/PullComment ATURIs. 13 + // ValidateReferenceLinks resolves refLinks to Issue/PR/IssueComment/PullComment ATURIs. 14 14 // It will ignore missing refLinks. 15 - func FindReferences(e Execer, refLinks []models.ReferenceLink) ([]syntax.ATURI, error) { 15 + func ValidateReferenceLinks(e Execer, refLinks []models.ReferenceLink) ([]syntax.ATURI, error) { 16 16 var ( 17 17 issueRefs []models.ReferenceLink 18 18 pullRefs []models.ReferenceLink ··· 27 27 } 28 28 issueUris, err := findIssueReferences(e, issueRefs) 29 29 if err != nil { 30 - return nil, err 30 + return nil, fmt.Errorf("find issue references: %w", err) 31 31 } 32 32 pullUris, err := findPullReferences(e, pullRefs) 33 33 if err != nil { 34 - return nil, err 34 + return nil, fmt.Errorf("find pull references: %w", err) 35 35 } 36 36 37 37 return append(issueUris, pullUris...), nil ··· 101 101 } 102 102 uris = append(uris, uri) 103 103 } 104 + if err := rows.Err(); err != nil { 105 + return nil, fmt.Errorf("iterate rows: %w", err) 106 + } 107 + 104 108 return uris, nil 105 109 } 106 110 ··· 124 120 ) 125 121 select 126 122 p.owner_did, p.rkey, 127 - c.owner_did, c.rkey 123 + c.comment_at 128 124 from input inp 129 125 join repos r 130 126 on r.did = inp.owner_did ··· 150 146 for rows.Next() { 151 147 // Scan rows 152 148 var pullOwner, pullRkey string 153 - var commentOwner, commentRkey sql.NullString 149 + var commentUri sql.NullString 154 150 var uri syntax.ATURI 155 - if err := rows.Scan(&pullOwner, &pullRkey, &commentOwner, &commentRkey); err != nil { 151 + if err := rows.Scan(&pullOwner, &pullRkey, &commentUri); err != nil { 156 152 return nil, err 157 153 } 158 - if commentOwner.Valid && commentRkey.Valid { 159 - uri = syntax.ATURI(fmt.Sprintf( 160 - "at://%s/%s/%s", 161 - commentOwner.String, 162 - tangled.RepoPullCommentNSID, 163 - commentRkey.String, 164 - )) 154 + if commentUri.Valid { 155 + // no-op 156 + uri = syntax.ATURI(commentUri.String) 165 157 } else { 166 158 uri = syntax.ATURI(fmt.Sprintf( 167 159 "at://%s/%s/%s", ··· 169 169 uris = append(uris, uri) 170 170 } 171 171 return uris, nil 172 + } 173 + 174 + func putReferences(tx *sql.Tx, fromAt syntax.ATURI, references []syntax.ATURI) error { 175 + err := deleteReferences(tx, fromAt) 176 + if err != nil { 177 + return fmt.Errorf("delete old reference_links: %w", err) 178 + } 179 + if len(references) == 0 { 180 + return nil 181 + } 182 + 183 + values := make([]string, 0, len(references)) 184 + args := make([]any, 0, len(references)*2) 185 + for _, ref := range references { 186 + values = append(values, "(?, ?)") 187 + args = append(args, fromAt, ref) 188 + } 189 + _, err = tx.Exec( 190 + fmt.Sprintf( 191 + `insert into reference_links (from_at, to_at) 192 + values %s`, 193 + strings.Join(values, ","), 194 + ), 195 + args..., 196 + ) 197 + if err != nil { 198 + return fmt.Errorf("insert new reference_links: %w", err) 199 + } 200 + return nil 201 + } 202 + 203 + func deleteReferences(tx *sql.Tx, fromAt syntax.ATURI) error { 204 + _, err := tx.Exec(`delete from reference_links where from_at = ?`, fromAt) 205 + return err 206 + } 207 + 208 + func GetReferencesAll(e Execer, filters ...filter) (map[syntax.ATURI][]syntax.ATURI, error) { 209 + var ( 210 + conditions []string 211 + args []any 212 + ) 213 + for _, filter := range filters { 214 + conditions = append(conditions, filter.Condition()) 215 + args = append(args, filter.Arg()...) 216 + } 217 + 218 + whereClause := "" 219 + if conditions != nil { 220 + whereClause = " where " + strings.Join(conditions, " and ") 221 + } 222 + 223 + rows, err := e.Query( 224 + fmt.Sprintf( 225 + `select from_at, to_at from reference_links %s`, 226 + whereClause, 227 + ), 228 + args..., 229 + ) 230 + if err != nil { 231 + return nil, fmt.Errorf("query reference_links: %w", err) 232 + } 233 + defer rows.Close() 234 + 235 + result := make(map[syntax.ATURI][]syntax.ATURI) 236 + 237 + for rows.Next() { 238 + var from, to syntax.ATURI 239 + if err := rows.Scan(&from, &to); err != nil { 240 + return nil, fmt.Errorf("scan row: %w", err) 241 + } 242 + 243 + result[from] = append(result[from], to) 244 + } 245 + if err := rows.Err(); err != nil { 246 + return nil, fmt.Errorf("iterate rows: %w", err) 247 + } 248 + 249 + return result, nil 172 250 }
+22 -5
appview/ingester.go
··· 841 841 return nil 842 842 843 843 case jmodels.CommitOperationDelete: 844 + tx, err := ddb.BeginTx(ctx, nil) 845 + if err != nil { 846 + l.Error("failed to begin transaction", "err", err) 847 + return err 848 + } 849 + defer tx.Rollback() 850 + 844 851 if err := db.DeleteIssues( 845 - ddb, 846 - db.FilterEq("did", did), 847 - db.FilterEq("rkey", rkey), 852 + tx, 853 + did, 854 + rkey, 848 855 ); err != nil { 849 856 l.Error("failed to delete", "err", err) 850 857 return fmt.Errorf("failed to delete issue record: %w", err) 858 + } 859 + if err := tx.Commit(); err != nil { 860 + l.Error("failed to commit txn", "err", err) 861 + return err 851 862 } 852 863 853 864 return nil ··· 899 888 return fmt.Errorf("failed to validate comment: %w", err) 900 889 } 901 890 902 - _, err = db.AddIssueComment(ddb, *comment) 891 + tx, err := ddb.Begin() 892 + if err != nil { 893 + return fmt.Errorf("failed to start transaction: %w", err) 894 + } 895 + defer tx.Rollback() 896 + 897 + _, err = db.AddIssueComment(tx, *comment) 903 898 if err != nil { 904 899 return fmt.Errorf("failed to create issue comment: %w", err) 905 900 } 906 901 907 - return nil 902 + return tx.Commit() 908 903 909 904 case jmodels.CommitOperationDelete: 910 905 if err := db.DeleteIssueComments(
+55 -19
appview/issues/issues.go
··· 238 238 } 239 239 l = l.With("did", issue.Did, "rkey", issue.Rkey) 240 240 241 + tx, err := rp.db.Begin() 242 + if err != nil { 243 + l.Error("failed to start transaction", "err", err) 244 + rp.pages.Notice(w, "issue-comment", "Failed to create comment, try again later.") 245 + return 246 + } 247 + defer tx.Rollback() 248 + 241 249 // delete from PDS 242 250 client, err := rp.oauth.AuthorizedClient(r) 243 251 if err != nil { ··· 266 258 } 267 259 268 260 // delete from db 269 - if err := db.DeleteIssues(rp.db, db.FilterEq("id", issue.Id)); err != nil { 261 + if err := db.DeleteIssues(tx, issue.Did, issue.Rkey); err != nil { 270 262 l.Error("failed to delete issue", "err", err) 271 263 rp.pages.Notice(w, noticeId, "Failed to delete issue.") 272 264 return 273 265 } 266 + tx.Commit() 274 267 275 268 rp.notifier.DeleteIssue(r.Context(), issue) 276 269 ··· 403 394 replyTo = &replyToUri 404 395 } 405 396 406 - mentions, _ := rp.refResolver.Resolve(r.Context(), body) 397 + mentions, references := rp.refResolver.Resolve(r.Context(), body) 407 398 408 399 comment := models.IssueComment{ 409 - Did: user.Did, 410 - Rkey: tid.TID(), 411 - IssueAt: issue.AtUri().String(), 412 - ReplyTo: replyTo, 413 - Body: body, 414 - Created: time.Now(), 400 + Did: user.Did, 401 + Rkey: tid.TID(), 402 + IssueAt: issue.AtUri().String(), 403 + ReplyTo: replyTo, 404 + Body: body, 405 + Created: time.Now(), 406 + Mentions: mentions, 407 + References: references, 415 408 } 416 409 if err = rp.validator.ValidateIssueComment(&comment); err != nil { 417 410 l.Error("failed to validate comment", "err", err) ··· 450 439 } 451 440 }() 452 441 453 - commentId, err := db.AddIssueComment(rp.db, comment) 442 + tx, err := rp.db.Begin() 443 + if err != nil { 444 + l.Error("failed to start transaction", "err", err) 445 + rp.pages.Notice(w, "issue-comment", "Failed to create comment, try again later.") 446 + return 447 + } 448 + defer tx.Rollback() 449 + 450 + commentId, err := db.AddIssueComment(tx, comment) 454 451 if err != nil { 455 452 l.Error("failed to create comment", "err", err) 456 453 rp.pages.Notice(w, "issue-comment", "Failed to create comment.") 454 + return 455 + } 456 + err = tx.Commit() 457 + if err != nil { 458 + l.Error("failed to commit transaction", "err", err) 459 + rp.pages.Notice(w, "issue-comment", "Failed to create comment, try again later.") 457 460 return 458 461 } 459 462 ··· 577 552 newComment.Edited = &now 578 553 record := newComment.AsRecord() 579 554 580 - _, err = db.AddIssueComment(rp.db, newComment) 555 + tx, err := rp.db.Begin() 556 + if err != nil { 557 + l.Error("failed to start transaction", "err", err) 558 + rp.pages.Notice(w, "repo-notice", "Failed to update description, try again later.") 559 + return 560 + } 561 + defer tx.Rollback() 562 + 563 + _, err = db.AddIssueComment(tx, newComment) 581 564 if err != nil { 582 565 l.Error("failed to perferom update-description query", "err", err) 583 566 rp.pages.Notice(w, "repo-notice", "Failed to update description, try again later.") 584 567 return 585 568 } 569 + tx.Commit() 586 570 587 571 // rkey is optional, it was introduced later 588 572 if newComment.Rkey != "" { ··· 901 867 }) 902 868 case http.MethodPost: 903 869 body := r.FormValue("body") 904 - mentions, _ := rp.refResolver.Resolve(r.Context(), body) 870 + mentions, references := rp.refResolver.Resolve(r.Context(), body) 905 871 906 872 issue := &models.Issue{ 907 - RepoAt: f.RepoAt(), 908 - Rkey: tid.TID(), 909 - Title: r.FormValue("title"), 910 - Body: body, 911 - Open: true, 912 - Did: user.Did, 913 - Created: time.Now(), 914 - Repo: f, 873 + RepoAt: f.RepoAt(), 874 + Rkey: tid.TID(), 875 + Title: r.FormValue("title"), 876 + Body: body, 877 + Open: true, 878 + Did: user.Did, 879 + Created: time.Now(), 880 + Mentions: mentions, 881 + References: references, 882 + Repo: f, 915 883 } 916 884 917 885 if err := rp.validator.ValidateIssue(issue); err != nil {
+70 -34
appview/models/issue.go
··· 10 10 ) 11 11 12 12 type Issue struct { 13 - Id int64 14 - Did string 15 - Rkey string 16 - RepoAt syntax.ATURI 17 - IssueId int 18 - Created time.Time 19 - Edited *time.Time 20 - Deleted *time.Time 21 - Title string 22 - Body string 23 - Open bool 13 + Id int64 14 + Did string 15 + Rkey string 16 + RepoAt syntax.ATURI 17 + IssueId int 18 + Created time.Time 19 + Edited *time.Time 20 + Deleted *time.Time 21 + Title string 22 + Body string 23 + Open bool 24 + Mentions []syntax.DID 25 + References []syntax.ATURI 24 26 25 27 // optionally, populate this when querying for reverse mappings 26 28 // like comment counts, parent repo etc. ··· 36 34 } 37 35 38 36 func (i *Issue) AsRecord() tangled.RepoIssue { 37 + mentions := make([]string, len(i.Mentions)) 38 + for i, did := range i.Mentions { 39 + mentions[i] = string(did) 40 + } 41 + references := make([]string, len(i.References)) 42 + for i, uri := range i.References { 43 + references[i] = string(uri) 44 + } 39 45 return tangled.RepoIssue{ 40 - Repo: i.RepoAt.String(), 41 - Title: i.Title, 42 - Body: &i.Body, 43 - CreatedAt: i.Created.Format(time.RFC3339), 46 + Repo: i.RepoAt.String(), 47 + Title: i.Title, 48 + Body: &i.Body, 49 + Mentions: mentions, 50 + References: references, 51 + CreatedAt: i.Created.Format(time.RFC3339), 44 52 } 45 53 } 46 54 ··· 173 161 } 174 162 175 163 type IssueComment struct { 176 - Id int64 177 - Did string 178 - Rkey string 179 - IssueAt string 180 - ReplyTo *string 181 - Body string 182 - Created time.Time 183 - Edited *time.Time 184 - Deleted *time.Time 164 + Id int64 165 + Did string 166 + Rkey string 167 + IssueAt string 168 + ReplyTo *string 169 + Body string 170 + Created time.Time 171 + Edited *time.Time 172 + Deleted *time.Time 173 + Mentions []syntax.DID 174 + References []syntax.ATURI 185 175 } 186 176 187 177 func (i *IssueComment) AtUri() syntax.ATURI { ··· 191 177 } 192 178 193 179 func (i *IssueComment) AsRecord() tangled.RepoIssueComment { 180 + mentions := make([]string, len(i.Mentions)) 181 + for i, did := range i.Mentions { 182 + mentions[i] = string(did) 183 + } 184 + references := make([]string, len(i.References)) 185 + for i, uri := range i.References { 186 + references[i] = string(uri) 187 + } 194 188 return tangled.RepoIssueComment{ 195 - Body: i.Body, 196 - Issue: i.IssueAt, 197 - CreatedAt: i.Created.Format(time.RFC3339), 198 - ReplyTo: i.ReplyTo, 189 + Body: i.Body, 190 + Issue: i.IssueAt, 191 + CreatedAt: i.Created.Format(time.RFC3339), 192 + ReplyTo: i.ReplyTo, 193 + Mentions: mentions, 194 + References: references, 199 195 } 200 196 } 201 197 ··· 229 205 return nil, err 230 206 } 231 207 208 + i := record 209 + mentions := make([]syntax.DID, len(record.Mentions)) 210 + for i, did := range record.Mentions { 211 + mentions[i] = syntax.DID(did) 212 + } 213 + references := make([]syntax.ATURI, len(record.References)) 214 + for i, uri := range i.References { 215 + references[i] = syntax.ATURI(uri) 216 + } 217 + 232 218 comment := IssueComment{ 233 - Did: ownerDid, 234 - Rkey: rkey, 235 - Body: record.Body, 236 - IssueAt: record.Issue, 237 - ReplyTo: record.ReplyTo, 238 - Created: created, 219 + Did: ownerDid, 220 + Rkey: rkey, 221 + Body: record.Body, 222 + IssueAt: record.Issue, 223 + ReplyTo: record.ReplyTo, 224 + Created: created, 225 + Mentions: mentions, 226 + References: references, 239 227 } 240 228 241 229 return &comment, nil
+26
appview/models/pull.go
··· 148 148 Body string 149 149 150 150 // meta 151 + Mentions []syntax.DID 152 + References []syntax.ATURI 153 + 154 + // meta 151 155 Created time.Time 152 156 } 157 + 158 + func (p *PullComment) AtUri() syntax.ATURI { 159 + return syntax.ATURI(p.CommentAt) 160 + } 161 + 162 + // func (p *PullComment) AsRecord() tangled.RepoPullComment { 163 + // mentions := make([]string, len(p.Mentions)) 164 + // for i, did := range p.Mentions { 165 + // mentions[i] = string(did) 166 + // } 167 + // references := make([]string, len(p.References)) 168 + // for i, uri := range p.References { 169 + // references[i] = string(uri) 170 + // } 171 + // return tangled.RepoPullComment{ 172 + // Pull: p.PullAt, 173 + // Body: p.Body, 174 + // Mentions: mentions, 175 + // References: references, 176 + // CreatedAt: p.Created.Format(time.RFC3339), 177 + // } 178 + // } 153 179 154 180 func (p *Pull) LastRoundNumber() int { 155 181 return len(p.Submissions) - 1
+3 -1
appview/pulls/pulls.go
··· 720 720 return 721 721 } 722 722 723 - mentions, _ := s.refResolver.Resolve(r.Context(), body) 723 + mentions, references := s.refResolver.Resolve(r.Context(), body) 724 724 725 725 // Start a transaction 726 726 tx, err := s.db.BeginTx(r.Context(), nil) ··· 764 764 Body: body, 765 765 CommentAt: atResp.Uri, 766 766 SubmissionId: pull.Submissions[roundNumber].ID, 767 + Mentions: mentions, 768 + References: references, 767 769 } 768 770 769 771 // Create the pull comment in the database with the commentAt field
+2 -2
appview/refresolver/resolver.go
··· 34 34 } 35 35 36 36 func (r *Resolver) Resolve(ctx context.Context, source string) ([]syntax.DID, []syntax.ATURI) { 37 - l := r.logger.With("method", "find_references") 37 + l := r.logger.With("method", "Resolve") 38 38 rawMentions, rawRefs := markup.FindReferences(r.config.Core.AppviewHost, source) 39 39 l.Debug("found possible references", "mentions", rawMentions, "refs", rawRefs) 40 40 idents := r.idResolver.ResolveIdents(ctx, rawMentions) ··· 55 55 rawRef.Handle = string(ident.DID) 56 56 resolvedRefs = append(resolvedRefs, rawRef) 57 57 } 58 - aturiRefs, err := db.FindReferences(r.execer, resolvedRefs) 58 + aturiRefs, err := db.ValidateReferenceLinks(r.execer, resolvedRefs) 59 59 if err != nil { 60 60 l.Error("failed running query", "err", err) 61 61 }
+14
lexicons/issue/comment.json
··· 29 29 "replyTo": { 30 30 "type": "string", 31 31 "format": "at-uri" 32 + }, 33 + "mentions": { 34 + "type": "array", 35 + "items": { 36 + "type": "string", 37 + "format": "did" 38 + } 39 + }, 40 + "references": { 41 + "type": "array", 42 + "items": { 43 + "type": "string", 44 + "format": "at-uri" 45 + } 32 46 } 33 47 } 34 48 }
+14
lexicons/issue/issue.json
··· 24 24 "createdAt": { 25 25 "type": "string", 26 26 "format": "datetime" 27 + }, 28 + "mentions": { 29 + "type": "array", 30 + "items": { 31 + "type": "string", 32 + "format": "did" 33 + } 34 + }, 35 + "references": { 36 + "type": "array", 37 + "items": { 38 + "type": "string", 39 + "format": "at-uri" 40 + } 27 41 } 28 42 } 29 43 }
+14
lexicons/pulls/comment.json
··· 25 25 "createdAt": { 26 26 "type": "string", 27 27 "format": "datetime" 28 + }, 29 + "mentions": { 30 + "type": "array", 31 + "items": { 32 + "type": "string", 33 + "format": "did" 34 + } 35 + }, 36 + "references": { 37 + "type": "array", 38 + "items": { 39 + "type": "string", 40 + "format": "at-uri" 41 + } 28 42 } 29 43 } 30 44 }
+14
lexicons/pulls/pull.json
··· 36 36 "createdAt": { 37 37 "type": "string", 38 38 "format": "datetime" 39 + }, 40 + "mentions": { 41 + "type": "array", 42 + "items": { 43 + "type": "string", 44 + "format": "did" 45 + } 46 + }, 47 + "references": { 48 + "type": "array", 49 + "items": { 50 + "type": "string", 51 + "format": "at-uri" 52 + } 39 53 } 40 54 } 41 55 }