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
+98 -386
Diff #7
+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 } ··· 295 294 return GetIssuesPaginated(e, pagination.Page{}, filters...) 296 295 } 297 296 298 - func AddIssueComment(tx *sql.Tx, c models.IssueComment) (int64, error) { 299 - result, err := tx.Exec( 300 - `insert into issue_comments ( 301 - did, 302 - rkey, 303 - issue_at, 304 - body, 305 - reply_to, 306 - created, 307 - edited 308 - ) 309 - values (?, ?, ?, ?, ?, ?, null) 310 - on conflict(did, rkey) do update set 311 - issue_at = excluded.issue_at, 312 - body = excluded.body, 313 - edited = case 314 - when 315 - issue_comments.issue_at != excluded.issue_at 316 - or issue_comments.body != excluded.body 317 - or issue_comments.reply_to != excluded.reply_to 318 - then ? 319 - else issue_comments.edited 320 - end`, 321 - c.Did, 322 - c.Rkey, 323 - c.IssueAt, 324 - c.Body, 325 - c.ReplyTo, 326 - c.Created.Format(time.RFC3339), 327 - time.Now().Format(time.RFC3339), 328 - ) 329 - if err != nil { 330 - return 0, err 331 - } 332 - 333 - id, err := result.LastInsertId() 334 - if err != nil { 335 - return 0, err 336 - } 337 - 338 - if err := putReferences(tx, c.AtUri(), c.References); err != nil { 339 - return 0, fmt.Errorf("put reference_links: %w", err) 340 - } 341 - 342 - return id, nil 343 - } 344 - 345 - func DeleteIssueComments(e Execer, filters ...orm.Filter) error { 346 - var conditions []string 347 - var args []any 348 - for _, filter := range filters { 349 - conditions = append(conditions, filter.Condition()) 350 - args = append(args, filter.Arg()...) 351 - } 352 - 353 - whereClause := "" 354 - if conditions != nil { 355 - whereClause = " where " + strings.Join(conditions, " and ") 356 - } 357 - 358 - query := fmt.Sprintf(`update issue_comments set body = "", deleted = strftime('%%Y-%%m-%%dT%%H:%%M:%%SZ', 'now') %s`, whereClause) 359 - 360 - _, err := e.Exec(query, args...) 361 - return err 362 - } 363 - 364 - func GetIssueComments(e Execer, filters ...orm.Filter) ([]models.IssueComment, error) { 365 - commentMap := make(map[string]*models.IssueComment) 366 - 367 - var conditions []string 368 - var args []any 369 - for _, filter := range filters { 370 - conditions = append(conditions, filter.Condition()) 371 - args = append(args, filter.Arg()...) 372 - } 373 - 374 - whereClause := "" 375 - if conditions != nil { 376 - whereClause = " where " + strings.Join(conditions, " and ") 377 - } 378 - 379 - query := fmt.Sprintf(` 380 - select 381 - id, 382 - did, 383 - rkey, 384 - issue_at, 385 - reply_to, 386 - body, 387 - created, 388 - edited, 389 - deleted 390 - from 391 - issue_comments 392 - %s 393 - `, whereClause) 394 - 395 - rows, err := e.Query(query, args...) 396 - if err != nil { 397 - return nil, err 398 - } 399 - defer rows.Close() 400 - 401 - for rows.Next() { 402 - var comment models.IssueComment 403 - var created string 404 - var rkey, edited, deleted, replyTo sql.Null[string] 405 - err := rows.Scan( 406 - &comment.Id, 407 - &comment.Did, 408 - &rkey, 409 - &comment.IssueAt, 410 - &replyTo, 411 - &comment.Body, 412 - &created, 413 - &edited, 414 - &deleted, 415 - ) 416 - if err != nil { 417 - return nil, err 418 - } 419 - 420 - // this is a remnant from old times, newer comments always have rkey 421 - if rkey.Valid { 422 - comment.Rkey = rkey.V 423 - } 424 - 425 - if t, err := time.Parse(time.RFC3339, created); err == nil { 426 - comment.Created = t 427 - } 428 - 429 - if edited.Valid { 430 - if t, err := time.Parse(time.RFC3339, edited.V); err == nil { 431 - comment.Edited = &t 432 - } 433 - } 434 - 435 - if deleted.Valid { 436 - if t, err := time.Parse(time.RFC3339, deleted.V); err == nil { 437 - comment.Deleted = &t 438 - } 439 - } 440 - 441 - if replyTo.Valid { 442 - comment.ReplyTo = &replyTo.V 443 - } 444 - 445 - atUri := comment.AtUri().String() 446 - commentMap[atUri] = &comment 447 - } 448 - 449 - if err = rows.Err(); err != nil { 450 - return nil, err 451 - } 452 - 453 - // collect references for each comments 454 - commentAts := slices.Collect(maps.Keys(commentMap)) 455 - allReferencs, err := GetReferencesAll(e, orm.FilterIn("from_at", commentAts)) 456 - if err != nil { 457 - return nil, fmt.Errorf("failed to query reference_links: %w", err) 458 - } 459 - for commentAt, references := range allReferencs { 460 - if comment, ok := commentMap[commentAt.String()]; ok { 461 - comment.References = references 462 - } 463 - } 464 - 465 - var comments []models.IssueComment 466 - for _, c := range commentMap { 467 - comments = append(comments, *c) 468 - } 469 - 470 - sort.Slice(comments, func(i, j int) bool { 471 - return comments[i].Created.After(comments[j].Created) 472 - }) 473 - 474 - return comments, nil 475 - } 476 - 477 297 func DeleteIssues(tx *sql.Tx, did, rkey string) error { 478 298 _, err := tx.Exec( 479 299 `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`,
+15 -6
appview/ingester.go
··· 891 891 } 892 892 893 893 switch e.Commit.Operation { 894 - case jmodels.CommitOperationCreate, jmodels.CommitOperationUpdate: 894 + case jmodels.CommitOperationUpdate: 895 895 raw := json.RawMessage(e.Commit.Record) 896 896 record := tangled.RepoIssueComment{} 897 897 err = json.Unmarshal(raw, &record) ··· 899 899 return fmt.Errorf("invalid record: %w", err) 900 900 } 901 901 902 - comment, err := models.IssueCommentFromRecord(did, rkey, record) 902 + // convert 'sh.tangled.repo.issue.comment' to 'sh.tangled.comment' 903 + comment, err := models.CommentFromRecord(syntax.DID(did), syntax.RecordKey(rkey), tangled.Comment{ 904 + Body: record.Body, 905 + CreatedAt: record.CreatedAt, 906 + Mentions: record.Mentions, 907 + References: record.References, 908 + ReplyTo: record.ReplyTo, 909 + Subject: record.Issue, 910 + }) 903 911 if err != nil { 904 912 return fmt.Errorf("failed to parse comment from record: %w", err) 905 913 } 906 914 907 - if err := i.Validator.ValidateIssueComment(comment); err != nil { 915 + if err := comment.Validate(); err != nil { 908 916 return fmt.Errorf("failed to validate comment: %w", err) 909 917 } 910 918 ··· 914 922 } 915 923 defer tx.Rollback() 916 924 917 - _, err = db.AddIssueComment(tx, *comment) 925 + err = db.PutComment(tx, comment) 918 926 if err != nil { 919 - return fmt.Errorf("failed to create issue comment: %w", err) 927 + return fmt.Errorf("failed to create comment: %w", err) 920 928 } 921 929 922 930 return tx.Commit() 923 931 924 932 case jmodels.CommitOperationDelete: 925 - if err := db.DeleteIssueComments( 933 + if err := db.DeleteComments( 926 934 ddb, 927 935 orm.FilterEq("did", did), 936 + orm.FilterEq("collection", e.Commit.Collection), 928 937 orm.FilterEq("rkey", rkey), 929 938 ); err != nil { 930 939 return fmt.Errorf("failed to delete issue comment record: %w", err)
+38 -36
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(replyToRaw) 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.mentionsResolver.Resolve(r.Context(), body) 416 421 417 - comment := models.IssueComment{ 418 - Did: user.Active.Did, 422 + comment := models.Comment{ 423 + Did: syntax.DID(user.Active.Did), 424 + Collection: tangled.CommentNSID, 419 425 Rkey: tid.TID(), 420 - IssueAt: issue.AtUri().String(), 426 + Subject: issue.AtUri(), 421 427 ReplyTo: replyTo, 422 428 Body: body, 423 429 Created: time.Now(), 424 430 Mentions: mentions, 425 431 References: references, 426 432 } 427 - if err = rp.validator.ValidateIssueComment(&comment); err != nil { 433 + if err = comment.Validate(); err != nil { 428 434 l.Error("failed to validate comment", "err", err) 429 435 rp.pages.Notice(w, "issue-comment", "Failed to create comment.") 430 436 return 431 437 } 432 - record := comment.AsRecord() 433 438 434 439 client, err := rp.oauth.AuthorizedClient(r) 435 440 if err != nil { ··· 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: comment.Collection.String(), 449 + Repo: comment.Did.String(), 445 450 Rkey: comment.Rkey, 446 451 Record: &lexutil.LexiconTypeDecoder{ 447 - Val: &record, 452 + Val: comment.AsRecord(), 448 453 }, 449 454 }) 450 455 if err != nil { ··· 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 orm.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 orm.FilterEq("id", commentId), 546 548 ) ··· 556 558 } 557 559 comment := comments[0] 558 560 559 - if comment.Did != user.Active.Did { 561 + if comment.Did.String() != user.Active.Did { 560 562 l.Error("unauthorized comment edit", "expectedDid", comment.Did, "gotDid", user.Active.Did) 561 563 http.Error(w, "you are not the author of this comment", http.StatusUnauthorized) 562 564 return ··· 586 588 newComment.Edited = &now 587 589 newComment.Mentions, newComment.References = rp.mentionsResolver.Resolve(r.Context(), newBody) 588 590 589 - record := newComment.AsRecord() 590 - 591 591 tx, err := rp.db.Begin() 592 592 if err != nil { 593 593 l.Error("failed to start transaction", "err", err) ··· 596 596 } 597 597 defer tx.Rollback() 598 598 599 - _, err = db.AddIssueComment(tx, newComment) 599 + err = db.PutComment(tx, &newComment) 600 600 if err != nil { 601 601 l.Error("failed to perferom update-description query", "err", err) 602 602 rp.pages.Notice(w, "repo-notice", "Failed to update description, try again later.") ··· 606 606 607 607 // rkey is optional, it was introduced later 608 608 if newComment.Rkey != "" { 609 + // TODO: update correct comment 610 + 609 611 // update the record on pds 610 - ex, err := comatproto.RepoGetRecord(r.Context(), client, "", tangled.RepoIssueCommentNSID, user.Active.Did, comment.Rkey) 612 + ex, err := comatproto.RepoGetRecord(r.Context(), client, "", newComment.Collection.String(), newComment.Did.String(), newComment.Rkey) 611 613 if err != nil { 612 614 l.Error("failed to get record", "err", err, "did", newComment.Did, "rkey", newComment.Rkey) 613 - rp.pages.Notice(w, fmt.Sprintf("comment-%s-status", commentId), "Failed to update description, no record found on PDS.") 615 + rp.pages.Notice(w, fmt.Sprintf("comment-%s-status", commentId), "Failed to update comment, no record found on PDS.") 614 616 return 615 617 } 616 618 617 619 _, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ 618 - Collection: tangled.RepoIssueCommentNSID, 619 - Repo: user.Active.Did, 620 + Collection: newComment.Collection.String(), 621 + Repo: newComment.Did.String(), 620 622 Rkey: newComment.Rkey, 621 623 SwapRecord: ex.Cid, 622 624 Record: &lexutil.LexiconTypeDecoder{ 623 - Val: &record, 625 + Val: newComment.AsRecord(), 624 626 }, 625 627 }) 626 628 if err != nil { ··· 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 orm.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 orm.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 orm.FilterEq("id", commentId), 728 730 ) ··· 738 740 } 739 741 comment := comments[0] 740 742 741 - if comment.Did != user.Active.Did { 743 + if comment.Did.String() != user.Active.Did { 742 744 l.Error("unauthorized action", "expectedDid", comment.Did, "gotDid", user.Active.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, orm.FilterEq("id", comment.Id)) 756 + err = db.DeleteComments(rp.db, orm.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, 771 - Repo: user.Active.Did, 772 + Collection: comment.Collection.String(), 773 + Repo: comment.Did.String(), 772 774 Rkey: comment.Rkey, 773 775 }) 774 776 if err != nil {
+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
··· 1004 1004 LoggedInUser *oauth.MultiAccountUser 1005 1005 RepoInfo repoinfo.RepoInfo 1006 1006 Issue *models.Issue 1007 - Comment *models.IssueComment 1007 + Comment *models.Comment 1008 1008 } 1009 1009 1010 1010 func (p *Pages) EditIssueCommentFragment(w io.Writer, params EditIssueCommentParams) error { ··· 1015 1015 LoggedInUser *oauth.MultiAccountUser 1016 1016 RepoInfo repoinfo.RepoInfo 1017 1017 Issue *models.Issue 1018 - Comment *models.IssueComment 1018 + Comment *models.Comment 1019 1019 } 1020 1020 1021 1021 func (p *Pages) ReplyIssueCommentPlaceholderFragment(w io.Writer, params ReplyIssueCommentPlaceholderParams) error { ··· 1026 1026 LoggedInUser *oauth.MultiAccountUser 1027 1027 RepoInfo repoinfo.RepoInfo 1028 1028 Issue *models.Issue 1029 - Comment *models.IssueComment 1029 + Comment *models.Comment 1030 1030 } 1031 1031 1032 1032 func (p *Pages) ReplyIssueCommentFragment(w io.Writer, params ReplyIssueCommentParams) error { ··· 1037 1037 LoggedInUser *oauth.MultiAccountUser 1038 1038 RepoInfo repoinfo.RepoInfo 1039 1039 Issue *models.Issue 1040 - Comment *models.IssueComment 1040 + Comment *models.Comment 1041 1041 } 1042 1042 1043 1043 func (p *Pages) IssueCommentBodyFragment(w io.Writer, params IssueCommentBodyParams) error {
+2 -2
appview/pages/templates/repo/issues/fragments/commentList.html
··· 41 41 {{ define "topLevelComment" }} 42 42 <div class="rounded px-6 py-4 bg-white dark:bg-gray-800 flex gap-2 "> 43 43 <div class="flex-shrink-0"> 44 - {{ template "user/fragments/picLink" (list .Comment.Did "size-8 mr-1") }} 44 + {{ template "user/fragments/picLink" (list .Comment.Did.String "size-8 mr-1") }} 45 45 </div> 46 46 <div class="flex-1 min-w-0"> 47 47 {{ template "repo/issues/fragments/issueCommentHeader" . }} ··· 53 53 {{ define "replyComment" }} 54 54 <div class="py-4 pr-4 w-full mx-auto overflow-hidden flex gap-2 "> 55 55 <div class="flex-shrink-0"> 56 - {{ template "user/fragments/picLink" (list .Comment.Did "size-8 mr-1") }} 56 + {{ template "user/fragments/picLink" (list .Comment.Did.String "size-8 mr-1") }} 57 57 </div> 58 58 <div class="flex-1 min-w-0"> 59 59 {{ template "repo/issues/fragments/issueCommentHeader" . }}
+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 - {{ $handle := resolve .Comment.Did }} 3 + {{ $handle := resolve .Comment.Did.String }} 4 4 <a class="text-gray-500 dark:text-gray-400 hover:text-gray-500 dark:hover:text-gray-300" href="/{{ $handle }}">{{ $handle }}</a> 5 5 {{ template "hats" $ }} 6 6 <span class="before:content-['路']"></span> 7 7 {{ template "timestamp" . }} 8 - {{ $isCommentOwner := and .LoggedInUser (eq .LoggedInUser.Did .Comment.Did) }} 8 + {{ $isCommentOwner := and .LoggedInUser (eq .LoggedInUser.Did .Comment.Did.String) }} 9 9 {{ if and $isCommentOwner (not .Comment.Deleted) }} 10 10 {{ template "editIssueComment" . }} 11 11 {{ template "deleteIssueComment" . }}
-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