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

boltless.me submitted #0
1 commit
expand
appview: replace IssueComment to Comment
3/3 success
expand
expand 0 comments