From c828cadb5f6c8235ce586f1a0fc17548e1ea7153 Mon Sep 17 00:00:00 2001 From: Seongmin Lee Date: Tue, 9 Dec 2025 00:06:33 +0900 Subject: [PATCH] appview: replace `IssueComment` to `Comment` Change-Id: ztnzmtpyuznxmoyzpwxwkqqkyzmrszmx Signed-off-by: Seongmin Lee --- appview/db/issues.go | 191 +----------------- appview/db/reference.go | 37 ++-- appview/ingester.go | 30 ++- appview/issues/issues.go | 58 +++--- appview/models/issue.go | 97 +-------- appview/notify/db/db.go | 8 +- appview/notify/merged_notifier.go | 2 +- appview/notify/notifier.go | 4 +- appview/notify/posthog/notifier.go | 6 +- appview/pages/pages.go | 8 +- .../issues/fragments/issueCommentHeader.html | 4 +- appview/state/state.go | 2 +- appview/validator/issue.go | 26 --- 13 files changed, 93 insertions(+), 380 deletions(-) diff --git a/appview/db/issues.go b/appview/db/issues.go index e96f6ed3..ea82c7fb 100644 --- a/appview/db/issues.go +++ b/appview/db/issues.go @@ -99,7 +99,7 @@ func updateIssue(tx *sql.Tx, issue *models.Issue) error { } func GetIssuesPaginated(e Execer, page pagination.Page, filters ...filter) ([]models.Issue, error) { - issueMap := make(map[string]*models.Issue) // at-uri -> issue + issueMap := make(map[syntax.ATURI]*models.Issue) // at-uri -> issue var conditions []string var args []any @@ -195,8 +195,7 @@ func GetIssuesPaginated(e Execer, page pagination.Page, filters ...filter) ([]mo } } - atUri := issue.AtUri().String() - issueMap[atUri] = &issue + issueMap[issue.AtUri()] = &issue } // collect reverse repos @@ -228,12 +227,12 @@ func GetIssuesPaginated(e Execer, page pagination.Page, filters ...filter) ([]mo // collect comments issueAts := slices.Collect(maps.Keys(issueMap)) - comments, err := GetIssueComments(e, FilterIn("issue_at", issueAts)) + comments, err := GetComments(e, FilterIn("subject_at", issueAts)) if err != nil { return nil, fmt.Errorf("failed to query comments: %w", err) } for i := range comments { - issueAt := comments[i].IssueAt + issueAt := comments[i].Subject if issue, ok := issueMap[issueAt]; ok { issue.Comments = append(issue.Comments, comments[i]) } @@ -245,7 +244,7 @@ func GetIssuesPaginated(e Execer, page pagination.Page, filters ...filter) ([]mo return nil, fmt.Errorf("failed to query labels: %w", err) } for issueAt, labels := range allLabels { - if issue, ok := issueMap[issueAt.String()]; ok { + if issue, ok := issueMap[issueAt]; ok { issue.Labels = labels } } @@ -256,7 +255,7 @@ func GetIssuesPaginated(e Execer, page pagination.Page, filters ...filter) ([]mo return nil, fmt.Errorf("failed to query reference_links: %w", err) } for issueAt, references := range allReferencs { - if issue, ok := issueMap[issueAt.String()]; ok { + if issue, ok := issueMap[issueAt]; ok { issue.References = references } } @@ -350,184 +349,6 @@ func GetIssueIDs(e Execer, opts models.IssueSearchOptions) ([]int64, error) { return ids, nil } -func AddIssueComment(tx *sql.Tx, c models.IssueComment) (int64, error) { - result, err := tx.Exec( - `insert into issue_comments ( - did, - rkey, - issue_at, - body, - reply_to, - created, - edited - ) - values (?, ?, ?, ?, ?, ?, null) - on conflict(did, rkey) do update set - issue_at = excluded.issue_at, - body = excluded.body, - edited = case - when - issue_comments.issue_at != excluded.issue_at - or issue_comments.body != excluded.body - or issue_comments.reply_to != excluded.reply_to - then ? - else issue_comments.edited - end`, - c.Did, - c.Rkey, - c.IssueAt, - c.Body, - c.ReplyTo, - c.Created.Format(time.RFC3339), - time.Now().Format(time.RFC3339), - ) - if err != nil { - return 0, err - } - - id, err := result.LastInsertId() - if err != nil { - return 0, err - } - - if err := putReferences(tx, c.AtUri(), c.References); err != nil { - return 0, fmt.Errorf("put reference_links: %w", err) - } - - return id, nil -} - -func DeleteIssueComments(e Execer, filters ...filter) error { - var conditions []string - var args []any - for _, filter := range filters { - conditions = append(conditions, filter.Condition()) - args = append(args, filter.Arg()...) - } - - whereClause := "" - if conditions != nil { - whereClause = " where " + strings.Join(conditions, " and ") - } - - query := fmt.Sprintf(`update issue_comments set body = "", deleted = strftime('%%Y-%%m-%%dT%%H:%%M:%%SZ', 'now') %s`, whereClause) - - _, err := e.Exec(query, args...) - return err -} - -func GetIssueComments(e Execer, filters ...filter) ([]models.IssueComment, error) { - commentMap := make(map[string]*models.IssueComment) - - var conditions []string - var args []any - for _, filter := range filters { - conditions = append(conditions, filter.Condition()) - args = append(args, filter.Arg()...) - } - - whereClause := "" - if conditions != nil { - whereClause = " where " + strings.Join(conditions, " and ") - } - - query := fmt.Sprintf(` - select - id, - did, - rkey, - issue_at, - reply_to, - body, - created, - edited, - deleted - from - issue_comments - %s - `, whereClause) - - rows, err := e.Query(query, args...) - if err != nil { - return nil, err - } - - for rows.Next() { - var comment models.IssueComment - var created string - var rkey, edited, deleted, replyTo sql.Null[string] - err := rows.Scan( - &comment.Id, - &comment.Did, - &rkey, - &comment.IssueAt, - &replyTo, - &comment.Body, - &created, - &edited, - &deleted, - ) - if err != nil { - return nil, err - } - - // this is a remnant from old times, newer comments always have rkey - if rkey.Valid { - comment.Rkey = rkey.V - } - - if t, err := time.Parse(time.RFC3339, created); err == nil { - comment.Created = t - } - - if edited.Valid { - if t, err := time.Parse(time.RFC3339, edited.V); err == nil { - comment.Edited = &t - } - } - - if deleted.Valid { - if t, err := time.Parse(time.RFC3339, deleted.V); err == nil { - comment.Deleted = &t - } - } - - if replyTo.Valid { - comment.ReplyTo = &replyTo.V - } - - atUri := comment.AtUri().String() - commentMap[atUri] = &comment - } - - if err = rows.Err(); err != nil { - return nil, err - } - - // collect references for each comments - commentAts := slices.Collect(maps.Keys(commentMap)) - allReferencs, err := GetReferencesAll(e, FilterIn("from_at", commentAts)) - if err != nil { - return nil, fmt.Errorf("failed to query reference_links: %w", err) - } - for commentAt, references := range allReferencs { - if comment, ok := commentMap[commentAt.String()]; ok { - comment.References = references - } - } - - var comments []models.IssueComment - for _, c := range commentMap { - comments = append(comments, *c) - } - - sort.Slice(comments, func(i, j int) bool { - return comments[i].Created.After(comments[j].Created) - }) - - return comments, nil -} - func DeleteIssues(tx *sql.Tx, did, rkey string) error { _, err := tx.Exec( `delete from issues diff --git a/appview/db/reference.go b/appview/db/reference.go index eabe6811..b6884fbb 100644 --- a/appview/db/reference.go +++ b/appview/db/reference.go @@ -10,7 +10,7 @@ import ( "tangled.org/core/appview/models" ) -// ValidateReferenceLinks resolves refLinks to Issue/PR/IssueComment/PullComment ATURIs. +// ValidateReferenceLinks resolves refLinks to Issue/PR/Comment ATURIs. // It will ignore missing refLinks. func ValidateReferenceLinks(e Execer, refLinks []models.ReferenceLink) ([]syntax.ATURI, error) { var ( @@ -52,8 +52,7 @@ func findIssueReferences(e Execer, refLinks []models.ReferenceLink) ([]syntax.AT values %s ) select - i.did, i.rkey, - c.did, c.rkey + i.at_uri, c.at_uri from input inp join repos r on r.did = inp.owner_did @@ -61,9 +60,9 @@ func findIssueReferences(e Execer, refLinks []models.ReferenceLink) ([]syntax.AT join issues i on i.repo_at = r.at_uri and i.issue_id = inp.issue_id - left join issue_comments c + left join comments c on inp.comment_id is not null - and c.issue_at = i.at_uri + and c.subject_at = i.at_uri and c.id = inp.comment_id `, strings.Join(vals, ","), @@ -78,26 +77,16 @@ func findIssueReferences(e Execer, refLinks []models.ReferenceLink) ([]syntax.AT for rows.Next() { // Scan rows - var issueOwner, issueRkey string - var commentOwner, commentRkey sql.NullString + var issueUri string + var commentUri sql.NullString var uri syntax.ATURI - if err := rows.Scan(&issueOwner, &issueRkey, &commentOwner, &commentRkey); err != nil { + if err := rows.Scan(&issueUri, &commentUri); err != nil { return nil, err } - if commentOwner.Valid && commentRkey.Valid { - uri = syntax.ATURI(fmt.Sprintf( - "at://%s/%s/%s", - commentOwner.String, - tangled.RepoIssueCommentNSID, - commentRkey.String, - )) + if commentUri.Valid { + uri = syntax.ATURI(commentUri.String) } else { - uri = syntax.ATURI(fmt.Sprintf( - "at://%s/%s/%s", - issueOwner, - tangled.RepoIssueNSID, - issueRkey, - )) + uri = syntax.ATURI(issueUri) } uris = append(uris, uri) } @@ -281,7 +270,7 @@ func GetBacklinks(e Execer, target syntax.ATURI) ([]models.RichReferenceLink, er return nil, fmt.Errorf("get issue backlinks: %w", err) } backlinks = append(backlinks, ls...) - ls, err = getIssueCommentBacklinks(e, backlinksMap[tangled.RepoIssueCommentNSID]) + ls, err = getIssueCommentBacklinks(e, backlinksMap[tangled.CommentNSID]) if err != nil { return nil, fmt.Errorf("get issue_comment backlinks: %w", err) } @@ -350,9 +339,9 @@ func getIssueCommentBacklinks(e Execer, aturis []syntax.ATURI) ([]models.RichRef rows, err := e.Query( fmt.Sprintf( `select r.did, r.name, i.issue_id, c.id, i.title, i.open - from issue_comments c + from comments c join issues i - on i.at_uri = c.issue_at + on i.at_uri = c.subject_at join repos r on r.at_uri = i.repo_at where %s`, diff --git a/appview/ingester.go b/appview/ingester.go index ee48d9d1..919af1f0 100644 --- a/appview/ingester.go +++ b/appview/ingester.go @@ -78,8 +78,8 @@ func (i *Ingester) Ingest() processFunc { err = i.ingestString(e) case tangled.RepoIssueNSID: err = i.ingestIssue(ctx, e) - case tangled.RepoIssueCommentNSID: - err = i.ingestIssueComment(e) + case tangled.CommentNSID: + err = i.ingestComment(e) case tangled.LabelDefinitionNSID: err = i.ingestLabelDefinition(e) case tangled.LabelOpNSID: @@ -867,13 +867,13 @@ func (i *Ingester) ingestIssue(ctx context.Context, e *jmodels.Event) error { return nil } -func (i *Ingester) ingestIssueComment(e *jmodels.Event) error { +func (i *Ingester) ingestComment(e *jmodels.Event) error { did := e.Did rkey := e.Commit.RKey var err error - l := i.Logger.With("handler", "ingestIssueComment", "nsid", e.Commit.Collection, "did", did, "rkey", rkey) + l := i.Logger.With("handler", "ingestComment", "nsid", e.Commit.Collection, "did", did, "rkey", rkey) l.Info("ingesting record") ddb, ok := i.Db.Execer.(*db.DB) @@ -884,18 +884,26 @@ func (i *Ingester) ingestIssueComment(e *jmodels.Event) error { switch e.Commit.Operation { case jmodels.CommitOperationCreate, jmodels.CommitOperationUpdate: raw := json.RawMessage(e.Commit.Record) - record := tangled.RepoIssueComment{} + record := tangled.Comment{} err = json.Unmarshal(raw, &record) if err != nil { return fmt.Errorf("invalid record: %w", err) } - comment, err := models.IssueCommentFromRecord(did, rkey, record) + comment, err := models.CommentFromRecord(did, rkey, record) if err != nil { return fmt.Errorf("failed to parse comment from record: %w", err) } - if err := i.Validator.ValidateIssueComment(comment); err != nil { + // TODO: ingest pull comments + // we aren't ingesting pull comments yet because pull itself isn't fully atprotated. + // so we cannot know which round this comment is pointing to + if comment.Subject.Collection().String() == tangled.RepoPullNSID { + l.Info("skip ingesting pull comments") + return nil + } + + if err := comment.Validate(); err != nil { return fmt.Errorf("failed to validate comment: %w", err) } @@ -905,20 +913,20 @@ func (i *Ingester) ingestIssueComment(e *jmodels.Event) error { } defer tx.Rollback() - _, err = db.AddIssueComment(tx, *comment) + err = db.PutComment(tx, comment) if err != nil { - return fmt.Errorf("failed to create issue comment: %w", err) + return fmt.Errorf("failed to create comment: %w", err) } return tx.Commit() case jmodels.CommitOperationDelete: - if err := db.DeleteIssueComments( + if err := db.DeleteComments( ddb, db.FilterEq("did", did), db.FilterEq("rkey", rkey), ); err != nil { - return fmt.Errorf("failed to delete issue comment record: %w", err) + return fmt.Errorf("failed to delete comment record: %w", err) } return nil diff --git a/appview/issues/issues.go b/appview/issues/issues.go index 3f5dc796..a230ff6a 100644 --- a/appview/issues/issues.go +++ b/appview/issues/issues.go @@ -402,29 +402,34 @@ func (rp *Issues) NewIssueComment(w http.ResponseWriter, r *http.Request) { body := r.FormValue("body") if body == "" { - rp.pages.Notice(w, "issue", "Body is required") + rp.pages.Notice(w, "issue-comment", "Body is required") return } - replyToUri := r.FormValue("reply-to") - var replyTo *string - if replyToUri != "" { - replyTo = &replyToUri + var replyTo *syntax.ATURI + replyToRaw := r.FormValue("reply-to") + if replyToRaw != "" { + aturi, err := syntax.ParseATURI(r.FormValue("reply-to")) + if err != nil { + rp.pages.Notice(w, "issue-comment", "reply-to should be valid AT-URI") + return + } + replyTo = &aturi } mentions, references := rp.refResolver.Resolve(r.Context(), body) - comment := models.IssueComment{ - Did: user.Did, + comment := models.Comment{ + Did: syntax.DID(user.Did), Rkey: tid.TID(), - IssueAt: issue.AtUri().String(), + Subject: issue.AtUri(), ReplyTo: replyTo, Body: body, Created: time.Now(), Mentions: mentions, References: references, } - if err = rp.validator.ValidateIssueComment(&comment); err != nil { + if err = comment.Validate(); err != nil { l.Error("failed to validate comment", "err", err) rp.pages.Notice(w, "issue-comment", "Failed to create comment.") return @@ -440,8 +445,8 @@ func (rp *Issues) NewIssueComment(w http.ResponseWriter, r *http.Request) { // create a record first resp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ - Collection: tangled.RepoIssueCommentNSID, - Repo: comment.Did, + Collection: tangled.CommentNSID, + Repo: user.Did, Rkey: comment.Rkey, Record: &lexutil.LexiconTypeDecoder{ Val: &record, @@ -467,7 +472,7 @@ func (rp *Issues) NewIssueComment(w http.ResponseWriter, r *http.Request) { } defer tx.Rollback() - commentId, err := db.AddIssueComment(tx, comment) + err = db.PutComment(tx, &comment) if err != nil { l.Error("failed to create comment", "err", err) rp.pages.Notice(w, "issue-comment", "Failed to create comment.") @@ -483,13 +488,10 @@ func (rp *Issues) NewIssueComment(w http.ResponseWriter, r *http.Request) { // reset atUri to make rollback a no-op atUri = "" - // notify about the new comment - comment.Id = commentId - rp.notifier.NewIssueComment(r.Context(), &comment, mentions) ownerSlashRepo := reporesolver.GetBaseRepoPath(r, f) - rp.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d#comment-%d", ownerSlashRepo, issue.IssueId, commentId)) + rp.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d#comment-%d", ownerSlashRepo, issue.IssueId, comment.Id)) } func (rp *Issues) IssueComment(w http.ResponseWriter, r *http.Request) { @@ -504,7 +506,7 @@ func (rp *Issues) IssueComment(w http.ResponseWriter, r *http.Request) { } commentId := chi.URLParam(r, "commentId") - comments, err := db.GetIssueComments( + comments, err := db.GetComments( rp.db, db.FilterEq("id", commentId), ) @@ -540,7 +542,7 @@ func (rp *Issues) EditIssueComment(w http.ResponseWriter, r *http.Request) { } commentId := chi.URLParam(r, "commentId") - comments, err := db.GetIssueComments( + comments, err := db.GetComments( rp.db, db.FilterEq("id", commentId), ) @@ -556,7 +558,7 @@ func (rp *Issues) EditIssueComment(w http.ResponseWriter, r *http.Request) { } comment := comments[0] - if comment.Did != user.Did { + if comment.Did.String() != user.Did { l.Error("unauthorized comment edit", "expectedDid", comment.Did, "gotDid", user.Did) http.Error(w, "you are not the author of this comment", http.StatusUnauthorized) return @@ -596,7 +598,7 @@ func (rp *Issues) EditIssueComment(w http.ResponseWriter, r *http.Request) { } defer tx.Rollback() - _, err = db.AddIssueComment(tx, newComment) + err = db.PutComment(tx, &newComment) if err != nil { l.Error("failed to perferom update-description query", "err", err) rp.pages.Notice(w, "repo-notice", "Failed to update description, try again later.") @@ -607,7 +609,7 @@ func (rp *Issues) EditIssueComment(w http.ResponseWriter, r *http.Request) { // rkey is optional, it was introduced later if newComment.Rkey != "" { // update the record on pds - ex, err := comatproto.RepoGetRecord(r.Context(), client, "", tangled.RepoIssueCommentNSID, user.Did, comment.Rkey) + ex, err := comatproto.RepoGetRecord(r.Context(), client, "", tangled.CommentNSID, user.Did, comment.Rkey) if err != nil { l.Error("failed to get record", "err", err, "did", newComment.Did, "rkey", newComment.Rkey) rp.pages.Notice(w, fmt.Sprintf("comment-%s-status", commentId), "Failed to update description, no record found on PDS.") @@ -615,7 +617,7 @@ func (rp *Issues) EditIssueComment(w http.ResponseWriter, r *http.Request) { } _, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ - Collection: tangled.RepoIssueCommentNSID, + Collection: tangled.CommentNSID, Repo: user.Did, Rkey: newComment.Rkey, SwapRecord: ex.Cid, @@ -650,7 +652,7 @@ func (rp *Issues) ReplyIssueCommentPlaceholder(w http.ResponseWriter, r *http.Re } commentId := chi.URLParam(r, "commentId") - comments, err := db.GetIssueComments( + comments, err := db.GetComments( rp.db, db.FilterEq("id", commentId), ) @@ -686,7 +688,7 @@ func (rp *Issues) ReplyIssueComment(w http.ResponseWriter, r *http.Request) { } commentId := chi.URLParam(r, "commentId") - comments, err := db.GetIssueComments( + comments, err := db.GetComments( rp.db, db.FilterEq("id", commentId), ) @@ -722,7 +724,7 @@ func (rp *Issues) DeleteIssueComment(w http.ResponseWriter, r *http.Request) { } commentId := chi.URLParam(r, "commentId") - comments, err := db.GetIssueComments( + comments, err := db.GetComments( rp.db, db.FilterEq("id", commentId), ) @@ -738,7 +740,7 @@ func (rp *Issues) DeleteIssueComment(w http.ResponseWriter, r *http.Request) { } comment := comments[0] - if comment.Did != user.Did { + if comment.Did.String() != user.Did { l.Error("unauthorized action", "expectedDid", comment.Did, "gotDid", user.Did) http.Error(w, "you are not the author of this comment", http.StatusUnauthorized) return @@ -751,7 +753,7 @@ func (rp *Issues) DeleteIssueComment(w http.ResponseWriter, r *http.Request) { // optimistic deletion deleted := time.Now() - err = db.DeleteIssueComments(rp.db, db.FilterEq("id", comment.Id)) + err = db.DeleteComments(rp.db, db.FilterEq("id", comment.Id)) if err != nil { l.Error("failed to delete comment", "err", err) rp.pages.Notice(w, fmt.Sprintf("comment-%s-status", commentId), "failed to delete comment") @@ -767,7 +769,7 @@ func (rp *Issues) DeleteIssueComment(w http.ResponseWriter, r *http.Request) { return } _, err = comatproto.RepoDeleteRecord(r.Context(), client, &comatproto.RepoDeleteRecord_Input{ - Collection: tangled.RepoIssueCommentNSID, + Collection: tangled.CommentNSID, Repo: user.Did, Rkey: comment.Rkey, }) diff --git a/appview/models/issue.go b/appview/models/issue.go index 1576d43b..1f9a183d 100644 --- a/appview/models/issue.go +++ b/appview/models/issue.go @@ -26,7 +26,7 @@ type Issue struct { // optionally, populate this when querying for reverse mappings // like comment counts, parent repo etc. - Comments []IssueComment + Comments []Comment Labels LabelState Repo *Repo } @@ -62,8 +62,8 @@ func (i *Issue) State() string { } type CommentListItem struct { - Self *IssueComment - Replies []*IssueComment + Self *Comment + Replies []*Comment } func (it *CommentListItem) Participants() []syntax.DID { @@ -88,13 +88,13 @@ func (it *CommentListItem) Participants() []syntax.DID { func (i *Issue) CommentList() []CommentListItem { // Create a map to quickly find comments by their aturi - toplevel := make(map[string]*CommentListItem) - var replies []*IssueComment + toplevel := make(map[syntax.ATURI]*CommentListItem) + var replies []*Comment // collect top level comments into the map for _, comment := range i.Comments { if comment.IsTopLevel() { - toplevel[comment.AtUri().String()] = &CommentListItem{ + toplevel[comment.AtUri()] = &CommentListItem{ Self: &comment, } } else { @@ -115,7 +115,7 @@ func (i *Issue) CommentList() []CommentListItem { } // sort everything - sortFunc := func(a, b *IssueComment) bool { + sortFunc := func(a, b *Comment) bool { return a.Created.Before(b.Created) } sort.Slice(listing, func(i, j int) bool { @@ -144,7 +144,7 @@ func (i *Issue) Participants() []string { addParticipant(i.Did) for _, c := range i.Comments { - addParticipant(c.Did) + addParticipant(c.Did.String()) } return participants @@ -171,84 +171,3 @@ func IssueFromRecord(did, rkey string, record tangled.RepoIssue) Issue { Open: true, // new issues are open by default } } - -type IssueComment struct { - Id int64 - Did string - Rkey string - IssueAt string - ReplyTo *string - Body string - Created time.Time - Edited *time.Time - Deleted *time.Time - Mentions []syntax.DID - References []syntax.ATURI -} - -func (i *IssueComment) AtUri() syntax.ATURI { - return syntax.ATURI(fmt.Sprintf("at://%s/%s/%s", i.Did, tangled.RepoIssueCommentNSID, i.Rkey)) -} - -func (i *IssueComment) AsRecord() tangled.RepoIssueComment { - mentions := make([]string, len(i.Mentions)) - for i, did := range i.Mentions { - mentions[i] = string(did) - } - references := make([]string, len(i.References)) - for i, uri := range i.References { - references[i] = string(uri) - } - return tangled.RepoIssueComment{ - Body: i.Body, - Issue: i.IssueAt, - CreatedAt: i.Created.Format(time.RFC3339), - ReplyTo: i.ReplyTo, - Mentions: mentions, - References: references, - } -} - -func (i *IssueComment) IsTopLevel() bool { - return i.ReplyTo == nil -} - -func (i *IssueComment) IsReply() bool { - return i.ReplyTo != nil -} - -func IssueCommentFromRecord(did, rkey string, record tangled.RepoIssueComment) (*IssueComment, error) { - created, err := time.Parse(time.RFC3339, record.CreatedAt) - if err != nil { - created = time.Now() - } - - ownerDid := did - - if _, err = syntax.ParseATURI(record.Issue); err != nil { - return nil, err - } - - i := record - mentions := make([]syntax.DID, len(record.Mentions)) - for i, did := range record.Mentions { - mentions[i] = syntax.DID(did) - } - references := make([]syntax.ATURI, len(record.References)) - for i, uri := range i.References { - references[i] = syntax.ATURI(uri) - } - - comment := IssueComment{ - Did: ownerDid, - Rkey: rkey, - Body: record.Body, - IssueAt: record.Issue, - ReplyTo: record.ReplyTo, - Created: created, - Mentions: mentions, - References: references, - } - - return &comment, nil -} diff --git a/appview/notify/db/db.go b/appview/notify/db/db.go index f1cb50bc..6d7df6a7 100644 --- a/appview/notify/db/db.go +++ b/appview/notify/db/db.go @@ -118,14 +118,14 @@ func (n *databaseNotifier) NewIssue(ctx context.Context, issue *models.Issue, me ) } -func (n *databaseNotifier) NewIssueComment(ctx context.Context, comment *models.IssueComment, mentions []syntax.DID) { - issues, err := db.GetIssues(n.db, db.FilterEq("at_uri", comment.IssueAt)) +func (n *databaseNotifier) NewIssueComment(ctx context.Context, comment *models.Comment, mentions []syntax.DID) { + issues, err := db.GetIssues(n.db, db.FilterEq("at_uri", comment.Subject)) if err != nil { log.Printf("NewIssueComment: failed to get issues: %v", err) return } if len(issues) == 0 { - log.Printf("NewIssueComment: no issue found for %s", comment.IssueAt) + log.Printf("NewIssueComment: no issue found for %s", comment.Subject) return } issue := issues[0] @@ -140,7 +140,7 @@ func (n *databaseNotifier) NewIssueComment(ctx context.Context, comment *models. // find the parent thread, and add all DIDs from here to the recipient list for _, t := range allThreads { - if t.Self.AtUri().String() == parentAtUri { + if t.Self.AtUri() == parentAtUri { recipients = append(recipients, t.Participants()...) } } diff --git a/appview/notify/merged_notifier.go b/appview/notify/merged_notifier.go index d744494b..5b29d49b 100644 --- a/appview/notify/merged_notifier.go +++ b/appview/notify/merged_notifier.go @@ -58,7 +58,7 @@ func (m *mergedNotifier) NewIssue(ctx context.Context, issue *models.Issue, ment m.fanout("NewIssue", ctx, issue, mentions) } -func (m *mergedNotifier) NewIssueComment(ctx context.Context, comment *models.IssueComment, mentions []syntax.DID) { +func (m *mergedNotifier) NewIssueComment(ctx context.Context, comment *models.Comment, mentions []syntax.DID) { m.fanout("NewIssueComment", ctx, comment, mentions) } diff --git a/appview/notify/notifier.go b/appview/notify/notifier.go index 45e7e11c..685b71c7 100644 --- a/appview/notify/notifier.go +++ b/appview/notify/notifier.go @@ -14,7 +14,7 @@ type Notifier interface { DeleteStar(ctx context.Context, star *models.Star) NewIssue(ctx context.Context, issue *models.Issue, mentions []syntax.DID) - NewIssueComment(ctx context.Context, comment *models.IssueComment, mentions []syntax.DID) + NewIssueComment(ctx context.Context, comment *models.Comment, mentions []syntax.DID) NewIssueState(ctx context.Context, actor syntax.DID, issue *models.Issue) DeleteIssue(ctx context.Context, issue *models.Issue) @@ -43,7 +43,7 @@ func (m *BaseNotifier) NewStar(ctx context.Context, star *models.Star) {} func (m *BaseNotifier) DeleteStar(ctx context.Context, star *models.Star) {} func (m *BaseNotifier) NewIssue(ctx context.Context, issue *models.Issue, mentions []syntax.DID) {} -func (m *BaseNotifier) NewIssueComment(ctx context.Context, comment *models.IssueComment, mentions []syntax.DID) { +func (m *BaseNotifier) NewIssueComment(ctx context.Context, comment *models.Comment, mentions []syntax.DID) { } func (m *BaseNotifier) NewIssueState(ctx context.Context, actor syntax.DID, issue *models.Issue) {} func (m *BaseNotifier) DeleteIssue(ctx context.Context, issue *models.Issue) {} diff --git a/appview/notify/posthog/notifier.go b/appview/notify/posthog/notifier.go index 8b2accf3..aa11523b 100644 --- a/appview/notify/posthog/notifier.go +++ b/appview/notify/posthog/notifier.go @@ -179,12 +179,12 @@ func (n *posthogNotifier) NewString(ctx context.Context, string *models.String) } } -func (n *posthogNotifier) NewIssueComment(ctx context.Context, comment *models.IssueComment, mentions []syntax.DID) { +func (n *posthogNotifier) NewIssueComment(ctx context.Context, comment *models.Comment, mentions []syntax.DID) { err := n.client.Enqueue(posthog.Capture{ - DistinctId: comment.Did, + DistinctId: comment.Did.String(), Event: "new_issue_comment", Properties: posthog.Properties{ - "issue_at": comment.IssueAt, + "issue_at": comment.Subject, "mentions": mentions, }, }) diff --git a/appview/pages/pages.go b/appview/pages/pages.go index 16c4f87f..29a08e8a 100644 --- a/appview/pages/pages.go +++ b/appview/pages/pages.go @@ -989,7 +989,7 @@ type EditIssueCommentParams struct { LoggedInUser *oauth.User RepoInfo repoinfo.RepoInfo Issue *models.Issue - Comment *models.IssueComment + Comment *models.Comment } func (p *Pages) EditIssueCommentFragment(w io.Writer, params EditIssueCommentParams) error { @@ -1000,7 +1000,7 @@ type ReplyIssueCommentPlaceholderParams struct { LoggedInUser *oauth.User RepoInfo repoinfo.RepoInfo Issue *models.Issue - Comment *models.IssueComment + Comment *models.Comment } func (p *Pages) ReplyIssueCommentPlaceholderFragment(w io.Writer, params ReplyIssueCommentPlaceholderParams) error { @@ -1011,7 +1011,7 @@ type ReplyIssueCommentParams struct { LoggedInUser *oauth.User RepoInfo repoinfo.RepoInfo Issue *models.Issue - Comment *models.IssueComment + Comment *models.Comment } func (p *Pages) ReplyIssueCommentFragment(w io.Writer, params ReplyIssueCommentParams) error { @@ -1022,7 +1022,7 @@ type IssueCommentBodyParams struct { LoggedInUser *oauth.User RepoInfo repoinfo.RepoInfo Issue *models.Issue - Comment *models.IssueComment + Comment *models.Comment } func (p *Pages) IssueCommentBodyFragment(w io.Writer, params IssueCommentBodyParams) error { diff --git a/appview/pages/templates/repo/issues/fragments/issueCommentHeader.html b/appview/pages/templates/repo/issues/fragments/issueCommentHeader.html index 1d4fdacb..d53d81a1 100644 --- a/appview/pages/templates/repo/issues/fragments/issueCommentHeader.html +++ b/appview/pages/templates/repo/issues/fragments/issueCommentHeader.html @@ -1,9 +1,9 @@ {{ define "repo/issues/fragments/issueCommentHeader" }}
- {{ template "user/fragments/picHandleLink" .Comment.Did }} + {{ template "user/fragments/picHandleLink" .Comment.Did.String }} {{ template "hats" $ }} {{ template "timestamp" . }} - {{ $isCommentOwner := and .LoggedInUser (eq .LoggedInUser.Did .Comment.Did) }} + {{ $isCommentOwner := and .LoggedInUser (eq .LoggedInUser.Did .Comment.Did.String) }} {{ if and $isCommentOwner (not .Comment.Deleted) }} {{ template "editIssueComment" . }} {{ template "deleteIssueComment" . }} diff --git a/appview/state/state.go b/appview/state/state.go index aeef9f78..feae4530 100644 --- a/appview/state/state.go +++ b/appview/state/state.go @@ -116,7 +116,7 @@ func Make(ctx context.Context, config *config.Config) (*State, error) { tangled.SpindleNSID, tangled.StringNSID, tangled.RepoIssueNSID, - tangled.RepoIssueCommentNSID, + tangled.CommentNSID, tangled.LabelDefinitionNSID, tangled.LabelOpNSID, }, diff --git a/appview/validator/issue.go b/appview/validator/issue.go index 17aa6a73..9d0edcbd 100644 --- a/appview/validator/issue.go +++ b/appview/validator/issue.go @@ -4,35 +4,9 @@ import ( "fmt" "strings" - "tangled.org/core/appview/db" "tangled.org/core/appview/models" ) -func (v *Validator) ValidateIssueComment(comment *models.IssueComment) error { - // if comments have parents, only ingest ones that are 1 level deep - if comment.ReplyTo != nil { - parents, err := db.GetIssueComments(v.db, db.FilterEq("at_uri", *comment.ReplyTo)) - if err != nil { - return fmt.Errorf("failed to fetch parent comment: %w", err) - } - if len(parents) != 1 { - return fmt.Errorf("incorrect number of parent comments returned: %d", len(parents)) - } - - // depth check - parent := parents[0] - if parent.ReplyTo != nil { - return fmt.Errorf("incorrect depth, this comment is replying at depth >1") - } - } - - if sb := strings.TrimSpace(v.sanitizer.SanitizeDefault(comment.Body)); sb == "" { - return fmt.Errorf("body is empty after HTML sanitization") - } - - return nil -} - func (v *Validator) ValidateIssue(issue *models.Issue) error { if issue.Title == "" { return fmt.Errorf("issue title is empty") -- 2.43.0