Monorepo for Tangled tangled.org

appview: replace IssueComment to Comment #866

open opened by boltless.me targeting master from sl/wnrvrwyvrlzo
Labels

None yet.

assignee

None yet.

Participants 2
AT URI
at://did:plc:xasnlahkri4ewmbuzly2rlc5/sh.tangled.repo.pull/3m7iohv2yb622
+94 -382
Diff #2
+1
appview/db/comments.go
··· 172 172 if err := rows.Err(); err != nil { 173 173 return nil, err 174 174 } 175 + defer rows.Close() 175 176 176 177 // collect references from each comments 177 178 commentAts := slices.Collect(maps.Keys(commentMap))
+6 -186
appview/db/issues.go
··· 100 100 } 101 101 102 102 func GetIssuesPaginated(e Execer, page pagination.Page, filters ...orm.Filter) ([]models.Issue, error) { 103 - issueMap := make(map[string]*models.Issue) // at-uri -> issue 103 + issueMap := make(map[syntax.ATURI]*models.Issue) // at-uri -> issue 104 104 105 105 var conditions []string 106 106 var args []any ··· 196 196 } 197 197 } 198 198 199 - atUri := issue.AtUri().String() 200 - issueMap[atUri] = &issue 199 + issueMap[issue.AtUri()] = &issue 201 200 } 202 201 203 202 // collect reverse repos ··· 229 228 // collect comments 230 229 issueAts := slices.Collect(maps.Keys(issueMap)) 231 230 232 - comments, err := GetIssueComments(e, orm.FilterIn("issue_at", issueAts)) 231 + comments, err := GetComments(e, orm.FilterIn("subject_at", issueAts)) 233 232 if err != nil { 234 233 return nil, fmt.Errorf("failed to query comments: %w", err) 235 234 } 236 235 for i := range comments { 237 - issueAt := comments[i].IssueAt 236 + issueAt := comments[i].Subject 238 237 if issue, ok := issueMap[issueAt]; ok { 239 238 issue.Comments = append(issue.Comments, comments[i]) 240 239 } ··· 246 245 return nil, fmt.Errorf("failed to query labels: %w", err) 247 246 } 248 247 for issueAt, labels := range allLabels { 249 - if issue, ok := issueMap[issueAt.String()]; ok { 248 + if issue, ok := issueMap[issueAt]; ok { 250 249 issue.Labels = labels 251 250 } 252 251 } ··· 257 256 return nil, fmt.Errorf("failed to query reference_links: %w", err) 258 257 } 259 258 for issueAt, references := range allReferencs { 260 - if issue, ok := issueMap[issueAt.String()]; ok { 259 + if issue, ok := issueMap[issueAt]; ok { 261 260 issue.References = references 262 261 } 263 262 } ··· 351 350 return ids, nil 352 351 } 353 352 354 - func AddIssueComment(tx *sql.Tx, c models.IssueComment) (int64, error) { 355 - result, err := tx.Exec( 356 - `insert into issue_comments ( 357 - did, 358 - rkey, 359 - issue_at, 360 - body, 361 - reply_to, 362 - created, 363 - edited 364 - ) 365 - values (?, ?, ?, ?, ?, ?, null) 366 - on conflict(did, rkey) do update set 367 - issue_at = excluded.issue_at, 368 - body = excluded.body, 369 - edited = case 370 - when 371 - issue_comments.issue_at != excluded.issue_at 372 - or issue_comments.body != excluded.body 373 - or issue_comments.reply_to != excluded.reply_to 374 - then ? 375 - else issue_comments.edited 376 - end`, 377 - c.Did, 378 - c.Rkey, 379 - c.IssueAt, 380 - c.Body, 381 - c.ReplyTo, 382 - c.Created.Format(time.RFC3339), 383 - time.Now().Format(time.RFC3339), 384 - ) 385 - if err != nil { 386 - return 0, err 387 - } 388 - 389 - id, err := result.LastInsertId() 390 - if err != nil { 391 - return 0, err 392 - } 393 - 394 - if err := putReferences(tx, c.AtUri(), c.References); err != nil { 395 - return 0, fmt.Errorf("put reference_links: %w", err) 396 - } 397 - 398 - return id, nil 399 - } 400 - 401 - func DeleteIssueComments(e Execer, filters ...orm.Filter) error { 402 - var conditions []string 403 - var args []any 404 - for _, filter := range filters { 405 - conditions = append(conditions, filter.Condition()) 406 - args = append(args, filter.Arg()...) 407 - } 408 - 409 - whereClause := "" 410 - if conditions != nil { 411 - whereClause = " where " + strings.Join(conditions, " and ") 412 - } 413 - 414 - query := fmt.Sprintf(`update issue_comments set body = "", deleted = strftime('%%Y-%%m-%%dT%%H:%%M:%%SZ', 'now') %s`, whereClause) 415 - 416 - _, err := e.Exec(query, args...) 417 - return err 418 - } 419 - 420 - func GetIssueComments(e Execer, filters ...orm.Filter) ([]models.IssueComment, error) { 421 - commentMap := make(map[string]*models.IssueComment) 422 - 423 - var conditions []string 424 - var args []any 425 - for _, filter := range filters { 426 - conditions = append(conditions, filter.Condition()) 427 - args = append(args, filter.Arg()...) 428 - } 429 - 430 - whereClause := "" 431 - if conditions != nil { 432 - whereClause = " where " + strings.Join(conditions, " and ") 433 - } 434 - 435 - query := fmt.Sprintf(` 436 - select 437 - id, 438 - did, 439 - rkey, 440 - issue_at, 441 - reply_to, 442 - body, 443 - created, 444 - edited, 445 - deleted 446 - from 447 - issue_comments 448 - %s 449 - `, whereClause) 450 - 451 - rows, err := e.Query(query, args...) 452 - if err != nil { 453 - return nil, err 454 - } 455 - defer rows.Close() 456 - 457 - for rows.Next() { 458 - var comment models.IssueComment 459 - var created string 460 - var rkey, edited, deleted, replyTo sql.Null[string] 461 - err := rows.Scan( 462 - &comment.Id, 463 - &comment.Did, 464 - &rkey, 465 - &comment.IssueAt, 466 - &replyTo, 467 - &comment.Body, 468 - &created, 469 - &edited, 470 - &deleted, 471 - ) 472 - if err != nil { 473 - return nil, err 474 - } 475 - 476 - // this is a remnant from old times, newer comments always have rkey 477 - if rkey.Valid { 478 - comment.Rkey = rkey.V 479 - } 480 - 481 - if t, err := time.Parse(time.RFC3339, created); err == nil { 482 - comment.Created = t 483 - } 484 - 485 - if edited.Valid { 486 - if t, err := time.Parse(time.RFC3339, edited.V); err == nil { 487 - comment.Edited = &t 488 - } 489 - } 490 - 491 - if deleted.Valid { 492 - if t, err := time.Parse(time.RFC3339, deleted.V); err == nil { 493 - comment.Deleted = &t 494 - } 495 - } 496 - 497 - if replyTo.Valid { 498 - comment.ReplyTo = &replyTo.V 499 - } 500 - 501 - atUri := comment.AtUri().String() 502 - commentMap[atUri] = &comment 503 - } 504 - 505 - if err = rows.Err(); err != nil { 506 - return nil, err 507 - } 508 - 509 - // collect references for each comments 510 - commentAts := slices.Collect(maps.Keys(commentMap)) 511 - allReferencs, err := GetReferencesAll(e, orm.FilterIn("from_at", commentAts)) 512 - if err != nil { 513 - return nil, fmt.Errorf("failed to query reference_links: %w", err) 514 - } 515 - for commentAt, references := range allReferencs { 516 - if comment, ok := commentMap[commentAt.String()]; ok { 517 - comment.References = references 518 - } 519 - } 520 - 521 - var comments []models.IssueComment 522 - for _, c := range commentMap { 523 - comments = append(comments, *c) 524 - } 525 - 526 - sort.Slice(comments, func(i, j int) bool { 527 - return comments[i].Created.After(comments[j].Created) 528 - }) 529 - 530 - return comments, nil 531 - } 532 - 533 353 func DeleteIssues(tx *sql.Tx, did, rkey string) error { 534 354 _, err := tx.Exec( 535 355 `delete from issues
+13 -24
appview/db/reference.go
··· 11 11 "tangled.org/core/orm" 12 12 ) 13 13 14 - // ValidateReferenceLinks resolves refLinks to Issue/PR/IssueComment/PullComment ATURIs. 14 + // ValidateReferenceLinks resolves refLinks to Issue/PR/Comment ATURIs. 15 15 // It will ignore missing refLinks. 16 16 func ValidateReferenceLinks(e Execer, refLinks []models.ReferenceLink) ([]syntax.ATURI, error) { 17 17 var ( ··· 53 53 values %s 54 54 ) 55 55 select 56 - i.did, i.rkey, 57 - c.did, c.rkey 56 + i.at_uri, c.at_uri 58 57 from input inp 59 58 join repos r 60 59 on r.did = inp.owner_did ··· 62 61 join issues i 63 62 on i.repo_at = r.at_uri 64 63 and i.issue_id = inp.issue_id 65 - left join issue_comments c 64 + left join comments c 66 65 on inp.comment_id is not null 67 - and c.issue_at = i.at_uri 66 + and c.subject_at = i.at_uri 68 67 and c.id = inp.comment_id 69 68 `, 70 69 strings.Join(vals, ","), ··· 79 78 80 79 for rows.Next() { 81 80 // Scan rows 82 - var issueOwner, issueRkey string 83 - var commentOwner, commentRkey sql.NullString 81 + var issueUri string 82 + var commentUri sql.NullString 84 83 var uri syntax.ATURI 85 - if err := rows.Scan(&issueOwner, &issueRkey, &commentOwner, &commentRkey); err != nil { 84 + if err := rows.Scan(&issueUri, &commentUri); err != nil { 86 85 return nil, err 87 86 } 88 - if commentOwner.Valid && commentRkey.Valid { 89 - uri = syntax.ATURI(fmt.Sprintf( 90 - "at://%s/%s/%s", 91 - commentOwner.String, 92 - tangled.RepoIssueCommentNSID, 93 - commentRkey.String, 94 - )) 87 + if commentUri.Valid { 88 + uri = syntax.ATURI(commentUri.String) 95 89 } else { 96 - uri = syntax.ATURI(fmt.Sprintf( 97 - "at://%s/%s/%s", 98 - issueOwner, 99 - tangled.RepoIssueNSID, 100 - issueRkey, 101 - )) 90 + uri = syntax.ATURI(issueUri) 102 91 } 103 92 uris = append(uris, uri) 104 93 } ··· 282 271 return nil, fmt.Errorf("get issue backlinks: %w", err) 283 272 } 284 273 backlinks = append(backlinks, ls...) 285 - ls, err = getIssueCommentBacklinks(e, backlinksMap[tangled.RepoIssueCommentNSID]) 274 + ls, err = getIssueCommentBacklinks(e, backlinksMap[tangled.CommentNSID]) 286 275 if err != nil { 287 276 return nil, fmt.Errorf("get issue_comment backlinks: %w", err) 288 277 } ··· 351 340 rows, err := e.Query( 352 341 fmt.Sprintf( 353 342 `select r.did, r.name, i.issue_id, c.id, i.title, i.open 354 - from issue_comments c 343 + from comments c 355 344 join issues i 356 - on i.at_uri = c.issue_at 345 + on i.at_uri = c.subject_at 357 346 join repos r 358 347 on r.at_uri = i.repo_at 359 348 where %s`,
+19 -11
appview/ingester.go
··· 79 79 err = i.ingestString(e) 80 80 case tangled.RepoIssueNSID: 81 81 err = i.ingestIssue(ctx, e) 82 - case tangled.RepoIssueCommentNSID: 83 - err = i.ingestIssueComment(e) 82 + case tangled.CommentNSID: 83 + err = i.ingestComment(e) 84 84 case tangled.LabelDefinitionNSID: 85 85 err = i.ingestLabelDefinition(e) 86 86 case tangled.LabelOpNSID: ··· 868 868 return nil 869 869 } 870 870 871 - func (i *Ingester) ingestIssueComment(e *jmodels.Event) error { 871 + func (i *Ingester) ingestComment(e *jmodels.Event) error { 872 872 did := e.Did 873 873 rkey := e.Commit.RKey 874 874 875 875 var err error 876 876 877 - l := i.Logger.With("handler", "ingestIssueComment", "nsid", e.Commit.Collection, "did", did, "rkey", rkey) 877 + l := i.Logger.With("handler", "ingestComment", "nsid", e.Commit.Collection, "did", did, "rkey", rkey) 878 878 l.Info("ingesting record") 879 879 880 880 ddb, ok := i.Db.Execer.(*db.DB) ··· 885 885 switch e.Commit.Operation { 886 886 case jmodels.CommitOperationCreate, jmodels.CommitOperationUpdate: 887 887 raw := json.RawMessage(e.Commit.Record) 888 - record := tangled.RepoIssueComment{} 888 + record := tangled.Comment{} 889 889 err = json.Unmarshal(raw, &record) 890 890 if err != nil { 891 891 return fmt.Errorf("invalid record: %w", err) 892 892 } 893 893 894 - comment, err := models.IssueCommentFromRecord(did, rkey, record) 894 + comment, err := models.CommentFromRecord(did, rkey, record) 895 895 if err != nil { 896 896 return fmt.Errorf("failed to parse comment from record: %w", err) 897 897 } 898 898 899 - if err := i.Validator.ValidateIssueComment(comment); err != nil { 899 + // TODO: ingest pull comments 900 + // we aren't ingesting pull comments yet because pull itself isn't fully atprotated. 901 + // so we cannot know which round this comment is pointing to 902 + if comment.Subject.Collection().String() == tangled.RepoPullNSID { 903 + l.Info("skip ingesting pull comments") 904 + return nil 905 + } 906 + 907 + if err := comment.Validate(); err != nil { 900 908 return fmt.Errorf("failed to validate comment: %w", err) 901 909 } 902 910 ··· 906 914 } 907 915 defer tx.Rollback() 908 916 909 - _, err = db.AddIssueComment(tx, *comment) 917 + err = db.PutComment(tx, comment) 910 918 if err != nil { 911 - return fmt.Errorf("failed to create issue comment: %w", err) 919 + return fmt.Errorf("failed to create comment: %w", err) 912 920 } 913 921 914 922 return tx.Commit() 915 923 916 924 case jmodels.CommitOperationDelete: 917 - if err := db.DeleteIssueComments( 925 + if err := db.DeleteComments( 918 926 ddb, 919 927 orm.FilterEq("did", did), 920 928 orm.FilterEq("rkey", rkey), 921 929 ); err != nil { 922 - return fmt.Errorf("failed to delete issue comment record: %w", err) 930 + return fmt.Errorf("failed to delete comment record: %w", err) 923 931 } 924 932 925 933 return nil
+30 -28
appview/issues/issues.go
··· 403 403 404 404 body := r.FormValue("body") 405 405 if body == "" { 406 - rp.pages.Notice(w, "issue", "Body is required") 406 + rp.pages.Notice(w, "issue-comment", "Body is required") 407 407 return 408 408 } 409 409 410 - replyToUri := r.FormValue("reply-to") 411 - var replyTo *string 412 - if replyToUri != "" { 413 - replyTo = &replyToUri 410 + var replyTo *syntax.ATURI 411 + replyToRaw := r.FormValue("reply-to") 412 + if replyToRaw != "" { 413 + aturi, err := syntax.ParseATURI(r.FormValue("reply-to")) 414 + if err != nil { 415 + rp.pages.Notice(w, "issue-comment", "reply-to should be valid AT-URI") 416 + return 417 + } 418 + replyTo = &aturi 414 419 } 415 420 416 421 mentions, references := rp.mentionsResolver.Resolve(r.Context(), body) 417 422 418 - comment := models.IssueComment{ 419 - Did: user.Did, 423 + comment := models.Comment{ 424 + Did: syntax.DID(user.Did), 420 425 Rkey: tid.TID(), 421 - IssueAt: issue.AtUri().String(), 426 + Subject: issue.AtUri(), 422 427 ReplyTo: replyTo, 423 428 Body: body, 424 429 Created: time.Now(), 425 430 Mentions: mentions, 426 431 References: references, 427 432 } 428 - if err = rp.validator.ValidateIssueComment(&comment); err != nil { 433 + if err = comment.Validate(); err != nil { 429 434 l.Error("failed to validate comment", "err", err) 430 435 rp.pages.Notice(w, "issue-comment", "Failed to create comment.") 431 436 return ··· 441 446 442 447 // create a record first 443 448 resp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ 444 - Collection: tangled.RepoIssueCommentNSID, 445 - Repo: comment.Did, 449 + Collection: tangled.CommentNSID, 450 + Repo: user.Did, 446 451 Rkey: comment.Rkey, 447 452 Record: &lexutil.LexiconTypeDecoder{ 448 453 Val: &record, ··· 468 473 } 469 474 defer tx.Rollback() 470 475 471 - commentId, err := db.AddIssueComment(tx, comment) 476 + err = db.PutComment(tx, &comment) 472 477 if err != nil { 473 478 l.Error("failed to create comment", "err", err) 474 479 rp.pages.Notice(w, "issue-comment", "Failed to create comment.") ··· 484 489 // reset atUri to make rollback a no-op 485 490 atUri = "" 486 491 487 - // notify about the new comment 488 - comment.Id = commentId 489 - 490 492 rp.notifier.NewIssueComment(r.Context(), &comment, mentions) 491 493 492 494 ownerSlashRepo := reporesolver.GetBaseRepoPath(r, f) 493 - rp.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d#comment-%d", ownerSlashRepo, issue.IssueId, commentId)) 495 + rp.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d#comment-%d", ownerSlashRepo, issue.IssueId, comment.Id)) 494 496 } 495 497 496 498 func (rp *Issues) IssueComment(w http.ResponseWriter, r *http.Request) { ··· 505 507 } 506 508 507 509 commentId := chi.URLParam(r, "commentId") 508 - comments, err := db.GetIssueComments( 510 + comments, err := db.GetComments( 509 511 rp.db, 510 512 orm.FilterEq("id", commentId), 511 513 ) ··· 541 543 } 542 544 543 545 commentId := chi.URLParam(r, "commentId") 544 - comments, err := db.GetIssueComments( 546 + comments, err := db.GetComments( 545 547 rp.db, 546 548 orm.FilterEq("id", commentId), 547 549 ) ··· 557 559 } 558 560 comment := comments[0] 559 561 560 - if comment.Did != user.Did { 562 + if comment.Did.String() != user.Did { 561 563 l.Error("unauthorized comment edit", "expectedDid", comment.Did, "gotDid", user.Did) 562 564 http.Error(w, "you are not the author of this comment", http.StatusUnauthorized) 563 565 return ··· 597 599 } 598 600 defer tx.Rollback() 599 601 600 - _, err = db.AddIssueComment(tx, newComment) 602 + err = db.PutComment(tx, &newComment) 601 603 if err != nil { 602 604 l.Error("failed to perferom update-description query", "err", err) 603 605 rp.pages.Notice(w, "repo-notice", "Failed to update description, try again later.") ··· 608 610 // rkey is optional, it was introduced later 609 611 if newComment.Rkey != "" { 610 612 // update the record on pds 611 - ex, err := comatproto.RepoGetRecord(r.Context(), client, "", tangled.RepoIssueCommentNSID, user.Did, comment.Rkey) 613 + ex, err := comatproto.RepoGetRecord(r.Context(), client, "", tangled.CommentNSID, user.Did, comment.Rkey) 612 614 if err != nil { 613 615 l.Error("failed to get record", "err", err, "did", newComment.Did, "rkey", newComment.Rkey) 614 616 rp.pages.Notice(w, fmt.Sprintf("comment-%s-status", commentId), "Failed to update description, no record found on PDS.") ··· 616 618 } 617 619 618 620 _, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ 619 - Collection: tangled.RepoIssueCommentNSID, 621 + Collection: tangled.CommentNSID, 620 622 Repo: user.Did, 621 623 Rkey: newComment.Rkey, 622 624 SwapRecord: ex.Cid, ··· 651 653 } 652 654 653 655 commentId := chi.URLParam(r, "commentId") 654 - comments, err := db.GetIssueComments( 656 + comments, err := db.GetComments( 655 657 rp.db, 656 658 orm.FilterEq("id", commentId), 657 659 ) ··· 687 689 } 688 690 689 691 commentId := chi.URLParam(r, "commentId") 690 - comments, err := db.GetIssueComments( 692 + comments, err := db.GetComments( 691 693 rp.db, 692 694 orm.FilterEq("id", commentId), 693 695 ) ··· 723 725 } 724 726 725 727 commentId := chi.URLParam(r, "commentId") 726 - comments, err := db.GetIssueComments( 728 + comments, err := db.GetComments( 727 729 rp.db, 728 730 orm.FilterEq("id", commentId), 729 731 ) ··· 739 741 } 740 742 comment := comments[0] 741 743 742 - if comment.Did != user.Did { 744 + if comment.Did.String() != user.Did { 743 745 l.Error("unauthorized action", "expectedDid", comment.Did, "gotDid", user.Did) 744 746 http.Error(w, "you are not the author of this comment", http.StatusUnauthorized) 745 747 return ··· 752 754 753 755 // optimistic deletion 754 756 deleted := time.Now() 755 - err = db.DeleteIssueComments(rp.db, orm.FilterEq("id", comment.Id)) 757 + err = db.DeleteComments(rp.db, orm.FilterEq("id", comment.Id)) 756 758 if err != nil { 757 759 l.Error("failed to delete comment", "err", err) 758 760 rp.pages.Notice(w, fmt.Sprintf("comment-%s-status", commentId), "failed to delete comment") ··· 768 770 return 769 771 } 770 772 _, err = comatproto.RepoDeleteRecord(r.Context(), client, &comatproto.RepoDeleteRecord_Input{ 771 - Collection: tangled.RepoIssueCommentNSID, 773 + Collection: tangled.CommentNSID, 772 774 Repo: user.Did, 773 775 Rkey: comment.Rkey, 774 776 })
+8 -89
appview/models/issue.go
··· 26 26 27 27 // optionally, populate this when querying for reverse mappings 28 28 // like comment counts, parent repo etc. 29 - Comments []IssueComment 29 + Comments []Comment 30 30 Labels LabelState 31 31 Repo *Repo 32 32 } ··· 62 62 } 63 63 64 64 type CommentListItem struct { 65 - Self *IssueComment 66 - Replies []*IssueComment 65 + Self *Comment 66 + Replies []*Comment 67 67 } 68 68 69 69 func (it *CommentListItem) Participants() []syntax.DID { ··· 88 88 89 89 func (i *Issue) CommentList() []CommentListItem { 90 90 // Create a map to quickly find comments by their aturi 91 - toplevel := make(map[string]*CommentListItem) 92 - var replies []*IssueComment 91 + toplevel := make(map[syntax.ATURI]*CommentListItem) 92 + var replies []*Comment 93 93 94 94 // collect top level comments into the map 95 95 for _, comment := range i.Comments { 96 96 if comment.IsTopLevel() { 97 - toplevel[comment.AtUri().String()] = &CommentListItem{ 97 + toplevel[comment.AtUri()] = &CommentListItem{ 98 98 Self: &comment, 99 99 } 100 100 } else { ··· 115 115 } 116 116 117 117 // sort everything 118 - sortFunc := func(a, b *IssueComment) bool { 118 + sortFunc := func(a, b *Comment) bool { 119 119 return a.Created.Before(b.Created) 120 120 } 121 121 sort.Slice(listing, func(i, j int) bool { ··· 144 144 addParticipant(i.Did) 145 145 146 146 for _, c := range i.Comments { 147 - addParticipant(c.Did) 147 + addParticipant(c.Did.String()) 148 148 } 149 149 150 150 return participants ··· 171 171 Open: true, // new issues are open by default 172 172 } 173 173 } 174 - 175 - 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 185 - Mentions []syntax.DID 186 - References []syntax.ATURI 187 - } 188 - 189 - func (i *IssueComment) AtUri() syntax.ATURI { 190 - return syntax.ATURI(fmt.Sprintf("at://%s/%s/%s", i.Did, tangled.RepoIssueCommentNSID, i.Rkey)) 191 - } 192 - 193 - func (i *IssueComment) AsRecord() tangled.RepoIssueComment { 194 - mentions := make([]string, len(i.Mentions)) 195 - for i, did := range i.Mentions { 196 - mentions[i] = string(did) 197 - } 198 - references := make([]string, len(i.References)) 199 - for i, uri := range i.References { 200 - references[i] = string(uri) 201 - } 202 - return tangled.RepoIssueComment{ 203 - Body: i.Body, 204 - Issue: i.IssueAt, 205 - CreatedAt: i.Created.Format(time.RFC3339), 206 - ReplyTo: i.ReplyTo, 207 - Mentions: mentions, 208 - References: references, 209 - } 210 - } 211 - 212 - func (i *IssueComment) IsTopLevel() bool { 213 - return i.ReplyTo == nil 214 - } 215 - 216 - func (i *IssueComment) IsReply() bool { 217 - return i.ReplyTo != nil 218 - } 219 - 220 - func IssueCommentFromRecord(did, rkey string, record tangled.RepoIssueComment) (*IssueComment, error) { 221 - created, err := time.Parse(time.RFC3339, record.CreatedAt) 222 - if err != nil { 223 - created = time.Now() 224 - } 225 - 226 - ownerDid := did 227 - 228 - if _, err = syntax.ParseATURI(record.Issue); err != nil { 229 - return nil, err 230 - } 231 - 232 - i := record 233 - mentions := make([]syntax.DID, len(record.Mentions)) 234 - for i, did := range record.Mentions { 235 - mentions[i] = syntax.DID(did) 236 - } 237 - references := make([]syntax.ATURI, len(record.References)) 238 - for i, uri := range i.References { 239 - references[i] = syntax.ATURI(uri) 240 - } 241 - 242 - comment := IssueComment{ 243 - Did: ownerDid, 244 - Rkey: rkey, 245 - Body: record.Body, 246 - IssueAt: record.Issue, 247 - ReplyTo: record.ReplyTo, 248 - Created: created, 249 - Mentions: mentions, 250 - References: references, 251 - } 252 - 253 - return &comment, nil 254 - }
+4 -4
appview/notify/db/db.go
··· 122 122 ) 123 123 } 124 124 125 - func (n *databaseNotifier) NewIssueComment(ctx context.Context, comment *models.IssueComment, mentions []syntax.DID) { 126 - issues, err := db.GetIssues(n.db, orm.FilterEq("at_uri", comment.IssueAt)) 125 + func (n *databaseNotifier) NewIssueComment(ctx context.Context, comment *models.Comment, mentions []syntax.DID) { 126 + issues, err := db.GetIssues(n.db, orm.FilterEq("at_uri", comment.Subject)) 127 127 if err != nil { 128 128 log.Printf("NewIssueComment: failed to get issues: %v", err) 129 129 return 130 130 } 131 131 if len(issues) == 0 { 132 - log.Printf("NewIssueComment: no issue found for %s", comment.IssueAt) 132 + log.Printf("NewIssueComment: no issue found for %s", comment.Subject) 133 133 return 134 134 } 135 135 issue := issues[0] ··· 147 147 148 148 // find the parent thread, and add all DIDs from here to the recipient list 149 149 for _, t := range issue.CommentList() { 150 - if t.Self.AtUri().String() == parentAtUri { 150 + if t.Self.AtUri() == parentAtUri { 151 151 for _, p := range t.Participants() { 152 152 recipients.Insert(p) 153 153 }
+1 -1
appview/notify/merged_notifier.go
··· 57 57 m.fanout("NewIssue", ctx, issue, mentions) 58 58 } 59 59 60 - func (m *mergedNotifier) NewIssueComment(ctx context.Context, comment *models.IssueComment, mentions []syntax.DID) { 60 + func (m *mergedNotifier) NewIssueComment(ctx context.Context, comment *models.Comment, mentions []syntax.DID) { 61 61 m.fanout("NewIssueComment", ctx, comment, mentions) 62 62 } 63 63
+2 -2
appview/notify/notifier.go
··· 14 14 DeleteStar(ctx context.Context, star *models.Star) 15 15 16 16 NewIssue(ctx context.Context, issue *models.Issue, mentions []syntax.DID) 17 - NewIssueComment(ctx context.Context, comment *models.IssueComment, mentions []syntax.DID) 17 + NewIssueComment(ctx context.Context, comment *models.Comment, mentions []syntax.DID) 18 18 NewIssueState(ctx context.Context, actor syntax.DID, issue *models.Issue) 19 19 DeleteIssue(ctx context.Context, issue *models.Issue) 20 20 ··· 43 43 func (m *BaseNotifier) DeleteStar(ctx context.Context, star *models.Star) {} 44 44 45 45 func (m *BaseNotifier) NewIssue(ctx context.Context, issue *models.Issue, mentions []syntax.DID) {} 46 - func (m *BaseNotifier) NewIssueComment(ctx context.Context, comment *models.IssueComment, mentions []syntax.DID) { 46 + func (m *BaseNotifier) NewIssueComment(ctx context.Context, comment *models.Comment, mentions []syntax.DID) { 47 47 } 48 48 func (m *BaseNotifier) NewIssueState(ctx context.Context, actor syntax.DID, issue *models.Issue) {} 49 49 func (m *BaseNotifier) DeleteIssue(ctx context.Context, issue *models.Issue) {}
+3 -3
appview/notify/posthog/notifier.go
··· 179 179 } 180 180 } 181 181 182 - func (n *posthogNotifier) NewIssueComment(ctx context.Context, comment *models.IssueComment, mentions []syntax.DID) { 182 + func (n *posthogNotifier) NewIssueComment(ctx context.Context, comment *models.Comment, mentions []syntax.DID) { 183 183 err := n.client.Enqueue(posthog.Capture{ 184 - DistinctId: comment.Did, 184 + DistinctId: comment.Did.String(), 185 185 Event: "new_issue_comment", 186 186 Properties: posthog.Properties{ 187 - "issue_at": comment.IssueAt, 187 + "issue_at": comment.Subject, 188 188 "mentions": mentions, 189 189 }, 190 190 })
+4 -4
appview/pages/pages.go
··· 988 988 LoggedInUser *oauth.User 989 989 RepoInfo repoinfo.RepoInfo 990 990 Issue *models.Issue 991 - Comment *models.IssueComment 991 + Comment *models.Comment 992 992 } 993 993 994 994 func (p *Pages) EditIssueCommentFragment(w io.Writer, params EditIssueCommentParams) error { ··· 999 999 LoggedInUser *oauth.User 1000 1000 RepoInfo repoinfo.RepoInfo 1001 1001 Issue *models.Issue 1002 - Comment *models.IssueComment 1002 + Comment *models.Comment 1003 1003 } 1004 1004 1005 1005 func (p *Pages) ReplyIssueCommentPlaceholderFragment(w io.Writer, params ReplyIssueCommentPlaceholderParams) error { ··· 1010 1010 LoggedInUser *oauth.User 1011 1011 RepoInfo repoinfo.RepoInfo 1012 1012 Issue *models.Issue 1013 - Comment *models.IssueComment 1013 + Comment *models.Comment 1014 1014 } 1015 1015 1016 1016 func (p *Pages) ReplyIssueCommentFragment(w io.Writer, params ReplyIssueCommentParams) error { ··· 1021 1021 LoggedInUser *oauth.User 1022 1022 RepoInfo repoinfo.RepoInfo 1023 1023 Issue *models.Issue 1024 - Comment *models.IssueComment 1024 + Comment *models.Comment 1025 1025 } 1026 1026 1027 1027 func (p *Pages) IssueCommentBodyFragment(w io.Writer, params IssueCommentBodyParams) error {
+2 -2
appview/pages/templates/repo/issues/fragments/issueCommentHeader.html
··· 1 1 {{ define "repo/issues/fragments/issueCommentHeader" }} 2 2 <div class="flex flex-wrap items-center gap-2 text-sm text-gray-500 dark:text-gray-400 "> 3 - {{ template "user/fragments/picHandleLink" .Comment.Did }} 3 + {{ template "user/fragments/picHandleLink" .Comment.Did.String }} 4 4 {{ template "hats" $ }} 5 5 {{ template "timestamp" . }} 6 - {{ $isCommentOwner := and .LoggedInUser (eq .LoggedInUser.Did .Comment.Did) }} 6 + {{ $isCommentOwner := and .LoggedInUser (eq .LoggedInUser.Did .Comment.Did.String) }} 7 7 {{ if and $isCommentOwner (not .Comment.Deleted) }} 8 8 {{ template "editIssueComment" . }} 9 9 {{ template "deleteIssueComment" . }}
+1 -1
appview/state/state.go
··· 117 117 tangled.SpindleNSID, 118 118 tangled.StringNSID, 119 119 tangled.RepoIssueNSID, 120 - tangled.RepoIssueCommentNSID, 120 + tangled.CommentNSID, 121 121 tangled.LabelDefinitionNSID, 122 122 tangled.LabelOpNSID, 123 123 },
-27
appview/validator/issue.go
··· 4 4 "fmt" 5 5 "strings" 6 6 7 - "tangled.org/core/appview/db" 8 7 "tangled.org/core/appview/models" 9 - "tangled.org/core/orm" 10 8 ) 11 9 12 - func (v *Validator) ValidateIssueComment(comment *models.IssueComment) error { 13 - // if comments have parents, only ingest ones that are 1 level deep 14 - if comment.ReplyTo != nil { 15 - parents, err := db.GetIssueComments(v.db, orm.FilterEq("at_uri", *comment.ReplyTo)) 16 - if err != nil { 17 - return fmt.Errorf("failed to fetch parent comment: %w", err) 18 - } 19 - if len(parents) != 1 { 20 - return fmt.Errorf("incorrect number of parent comments returned: %d", len(parents)) 21 - } 22 - 23 - // depth check 24 - parent := parents[0] 25 - if parent.ReplyTo != nil { 26 - return fmt.Errorf("incorrect depth, this comment is replying at depth >1") 27 - } 28 - } 29 - 30 - if sb := strings.TrimSpace(v.sanitizer.SanitizeDefault(comment.Body)); sb == "" { 31 - return fmt.Errorf("body is empty after HTML sanitization") 32 - } 33 - 34 - return nil 35 - } 36 - 37 10 func (v *Validator) ValidateIssue(issue *models.Issue) error { 38 11 if issue.Title == "" { 39 12 return fmt.Errorf("issue title is empty")

History

8 rounds 1 comment
sign up or login to add to the discussion
1 commit
expand
appview: replace IssueComment to Comment
2/3 failed, 1/3 success
expand
merge conflicts detected
expand
  • appview/notify/db/db.go:260
  • appview/notify/merged_notifier.go:81
  • appview/notify/notifier.go:22
  • appview/pulls/opengraph.go:277
expand 0 comments
1 commit
expand
appview: replace IssueComment to Comment
2/3 failed, 1/3 success
expand
expand 0 comments
1 commit
expand
appview: replace IssueComment to Comment
3/3 success
expand
expand 0 comments
1 commit
expand
appview: replace IssueComment to Comment
3/3 success
expand
expand 0 comments
1 commit
expand
appview: replace IssueComment to Comment
1/3 failed, 2/3 success
expand
expand 0 comments
1 commit
expand
appview: replace IssueComment to Comment
1/3 failed, 2/3 success
expand
expand 0 comments
1 commit
expand
appview: replace IssueComment to Comment
3/3 success
expand
expand 1 comment

imo: we should still continue to ingest sh.tangled.issue.comment (and pull comments), but just try to convert to the regular lexicon. this would only be used for backfill purposes, newer appviews should ideally not create the old NSID anymore.

the way i see it, the new lexicon is just an alias for the old ones, so the ingesters should be backwards compatible.

i will do a deeper dive into the code itself shortly.

1 commit
expand
appview: replace IssueComment to Comment
3/3 success
expand
expand 0 comments