Monorepo for Tangled tangled.org

appview: replace PullComment to Comment #865

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/3m7iohv2yb422
+557 -199
Diff #7
+202
appview/db/comments.go
··· 1 + package db 2 + 3 + import ( 4 + "database/sql" 5 + "fmt" 6 + "maps" 7 + "slices" 8 + "sort" 9 + "strings" 10 + "time" 11 + 12 + "github.com/bluesky-social/indigo/atproto/syntax" 13 + "tangled.org/core/api/tangled" 14 + "tangled.org/core/appview/models" 15 + "tangled.org/core/orm" 16 + ) 17 + 18 + func PutComment(tx *sql.Tx, c *models.Comment) error { 19 + if c.Collection == "" { 20 + c.Collection = tangled.CommentNSID 21 + } 22 + result, err := tx.Exec( 23 + `insert into comments ( 24 + did, 25 + collection, 26 + rkey, 27 + subject_at, 28 + reply_to, 29 + body, 30 + pull_submission_id, 31 + created 32 + ) 33 + values (?, ?, ?, ?, ?, ?, ?, ?) 34 + on conflict(did, collection, rkey) do update set 35 + subject_at = excluded.subject_at, 36 + reply_to = excluded.reply_to, 37 + body = excluded.body, 38 + edited = case 39 + when 40 + comments.subject_at != excluded.subject_at 41 + or comments.body != excluded.body 42 + or comments.reply_to != excluded.reply_to 43 + then ? 44 + else comments.edited 45 + end`, 46 + c.Did, 47 + c.Collection, 48 + c.Rkey, 49 + c.Subject, 50 + c.ReplyTo, 51 + c.Body, 52 + c.PullSubmissionId, 53 + c.Created.Format(time.RFC3339), 54 + time.Now().Format(time.RFC3339), 55 + ) 56 + if err != nil { 57 + return err 58 + } 59 + 60 + c.Id, err = result.LastInsertId() 61 + if err != nil { 62 + return err 63 + } 64 + 65 + if err := putReferences(tx, c.AtUri(), c.References); err != nil { 66 + return fmt.Errorf("put reference_links: %w", err) 67 + } 68 + 69 + return nil 70 + } 71 + 72 + func DeleteComments(e Execer, filters ...orm.Filter) error { 73 + var conditions []string 74 + var args []any 75 + for _, filter := range filters { 76 + conditions = append(conditions, filter.Condition()) 77 + args = append(args, filter.Arg()...) 78 + } 79 + 80 + whereClause := "" 81 + if conditions != nil { 82 + whereClause = " where " + strings.Join(conditions, " and ") 83 + } 84 + 85 + query := fmt.Sprintf(`update comments set body = "", deleted = strftime('%%Y-%%m-%%dT%%H:%%M:%%SZ', 'now') %s`, whereClause) 86 + 87 + _, err := e.Exec(query, args...) 88 + return err 89 + } 90 + 91 + func GetComments(e Execer, filters ...orm.Filter) ([]models.Comment, error) { 92 + commentMap := make(map[string]*models.Comment) 93 + 94 + var conditions []string 95 + var args []any 96 + for _, filter := range filters { 97 + conditions = append(conditions, filter.Condition()) 98 + args = append(args, filter.Arg()...) 99 + } 100 + 101 + whereClause := "" 102 + if conditions != nil { 103 + whereClause = " where " + strings.Join(conditions, " and ") 104 + } 105 + 106 + query := fmt.Sprintf(` 107 + select 108 + id, 109 + did, 110 + collection, 111 + rkey, 112 + subject_at, 113 + reply_to, 114 + body, 115 + pull_submission_id, 116 + created, 117 + edited, 118 + deleted 119 + from 120 + comments 121 + %s 122 + `, whereClause) 123 + 124 + rows, err := e.Query(query, args...) 125 + if err != nil { 126 + return nil, err 127 + } 128 + 129 + for rows.Next() { 130 + var comment models.Comment 131 + var created string 132 + var edited, deleted, replyTo sql.Null[string] 133 + err := rows.Scan( 134 + &comment.Id, 135 + &comment.Did, 136 + &comment.Collection, 137 + &comment.Rkey, 138 + &comment.Subject, 139 + &replyTo, 140 + &comment.Body, 141 + &comment.PullSubmissionId, 142 + &created, 143 + &edited, 144 + &deleted, 145 + ) 146 + if err != nil { 147 + return nil, err 148 + } 149 + 150 + if t, err := time.Parse(time.RFC3339, created); err == nil { 151 + comment.Created = t 152 + } 153 + 154 + if edited.Valid { 155 + if t, err := time.Parse(time.RFC3339, edited.V); err == nil { 156 + comment.Edited = &t 157 + } 158 + } 159 + 160 + if deleted.Valid { 161 + if t, err := time.Parse(time.RFC3339, deleted.V); err == nil { 162 + comment.Deleted = &t 163 + } 164 + } 165 + 166 + if replyTo.Valid { 167 + rt := syntax.ATURI(replyTo.V) 168 + comment.ReplyTo = &rt 169 + } 170 + 171 + atUri := comment.AtUri().String() 172 + commentMap[atUri] = &comment 173 + } 174 + 175 + if err := rows.Err(); err != nil { 176 + return nil, err 177 + } 178 + defer rows.Close() 179 + 180 + // collect references from each comments 181 + commentAts := slices.Collect(maps.Keys(commentMap)) 182 + allReferencs, err := GetReferencesAll(e, orm.FilterIn("from_at", commentAts)) 183 + if err != nil { 184 + return nil, fmt.Errorf("failed to query reference_links: %w", err) 185 + } 186 + for commentAt, references := range allReferencs { 187 + if comment, ok := commentMap[commentAt.String()]; ok { 188 + comment.References = references 189 + } 190 + } 191 + 192 + var comments []models.Comment 193 + for _, c := range commentMap { 194 + comments = append(comments, *c) 195 + } 196 + 197 + sort.Slice(comments, func(i, j int) bool { 198 + return comments[i].Created.Before(comments[j].Created) 199 + }) 200 + 201 + return comments, nil 202 + }
+81
appview/db/db.go
··· 1181 1181 return err 1182 1182 }) 1183 1183 1184 + orm.RunMigration(conn, logger, "add-comments-table", func(tx *sql.Tx) error { 1185 + _, err := tx.Exec(` 1186 + drop table if exists comments; 1187 + 1188 + create table comments ( 1189 + -- identifiers 1190 + id integer primary key autoincrement, 1191 + did text not null, 1192 + collection text not null default 'sh.tangled.comment', 1193 + rkey text not null, 1194 + at_uri text generated always as ('at://' || did || '/' || collection || '/' || rkey) stored, 1195 + 1196 + -- at identifiers 1197 + subject_at text not null, 1198 + reply_to text, -- at_uri of parent comment 1199 + 1200 + pull_submission_id integer, -- dirty fix until we atprotate the pull-rounds 1201 + 1202 + -- content 1203 + body text not null, 1204 + created text not null default (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')), 1205 + edited text, 1206 + deleted text, 1207 + 1208 + -- constraints 1209 + unique(did, collection, rkey) 1210 + ); 1211 + 1212 + insert into comments ( 1213 + did, 1214 + collection, 1215 + rkey, 1216 + subject_at, 1217 + reply_to, 1218 + body, 1219 + created, 1220 + edited, 1221 + deleted 1222 + ) 1223 + select 1224 + did, 1225 + 'sh.tangled.repo.issue.comment', 1226 + rkey, 1227 + issue_at, 1228 + reply_to, 1229 + body, 1230 + created, 1231 + edited, 1232 + deleted 1233 + from issue_comments 1234 + where rkey is not null; 1235 + 1236 + insert into comments ( 1237 + did, 1238 + collection, 1239 + rkey, 1240 + subject_at, 1241 + pull_submission_id, 1242 + body, 1243 + created 1244 + ) 1245 + select 1246 + c.owner_did, 1247 + 'sh.tangled.repo.pull.comment', 1248 + substr( 1249 + substr(c.comment_at, 6 + instr(substr(c.comment_at, 6), '/')), -- nsid/rkey 1250 + instr( 1251 + substr(c.comment_at, 6 + instr(substr(c.comment_at, 6), '/')), -- nsid/rkey 1252 + '/' 1253 + ) + 1 1254 + ), -- rkey 1255 + p.at_uri, 1256 + c.submission_id, 1257 + c.body, 1258 + c.created 1259 + from pull_comments c 1260 + join pulls p on c.repo_at = p.repo_at and c.pull_id = p.pull_id; 1261 + `) 1262 + return err 1263 + }) 1264 + 1184 1265 return &DB{ 1185 1266 db, 1186 1267 logger,
+6 -121
appview/db/pulls.go
··· 391 391 return nil, err 392 392 } 393 393 394 - // Get comments for all submissions using GetPullComments 394 + // Get comments for all submissions using GetComments 395 395 submissionIds := slices.Collect(maps.Keys(submissionMap)) 396 - comments, err := GetPullComments(e, orm.FilterIn("submission_id", submissionIds)) 396 + comments, err := GetComments(e, orm.FilterIn("pull_submission_id", submissionIds)) 397 397 if err != nil { 398 398 return nil, fmt.Errorf("failed to get pull comments: %w", err) 399 399 } 400 400 for _, comment := range comments { 401 - if submission, ok := submissionMap[comment.SubmissionId]; ok { 402 - submission.Comments = append(submission.Comments, comment) 401 + if comment.PullSubmissionId != nil { 402 + if submission, ok := submissionMap[*comment.PullSubmissionId]; ok { 403 + submission.Comments = append(submission.Comments, comment) 404 + } 403 405 } 404 406 } 405 407 ··· 419 421 return m, nil 420 422 } 421 423 422 - func GetPullComments(e Execer, filters ...orm.Filter) ([]models.PullComment, error) { 423 - var conditions []string 424 - var args []any 425 - for _, filter := range filters { 426 - conditions = append(conditions, filter.Condition()) 427 - args = append(args, filter.Arg()...) 428 - } 429 - 430 - whereClause := "" 431 - if conditions != nil { 432 - whereClause = " where " + strings.Join(conditions, " and ") 433 - } 434 - 435 - query := fmt.Sprintf(` 436 - select 437 - id, 438 - pull_id, 439 - submission_id, 440 - repo_at, 441 - owner_did, 442 - comment_at, 443 - body, 444 - created 445 - from 446 - pull_comments 447 - %s 448 - order by 449 - created asc 450 - `, whereClause) 451 - 452 - rows, err := e.Query(query, args...) 453 - if err != nil { 454 - return nil, err 455 - } 456 - defer rows.Close() 457 - 458 - commentMap := make(map[string]*models.PullComment) 459 - for rows.Next() { 460 - var comment models.PullComment 461 - var createdAt string 462 - err := rows.Scan( 463 - &comment.ID, 464 - &comment.PullId, 465 - &comment.SubmissionId, 466 - &comment.RepoAt, 467 - &comment.OwnerDid, 468 - &comment.CommentAt, 469 - &comment.Body, 470 - &createdAt, 471 - ) 472 - if err != nil { 473 - return nil, err 474 - } 475 - 476 - if t, err := time.Parse(time.RFC3339, createdAt); err == nil { 477 - comment.Created = t 478 - } 479 - 480 - atUri := comment.AtUri().String() 481 - commentMap[atUri] = &comment 482 - } 483 - 484 - if err := rows.Err(); err != nil { 485 - return nil, err 486 - } 487 - 488 - // collect references for each comments 489 - commentAts := slices.Collect(maps.Keys(commentMap)) 490 - allReferencs, err := GetReferencesAll(e, orm.FilterIn("from_at", commentAts)) 491 - if err != nil { 492 - return nil, fmt.Errorf("failed to query reference_links: %w", err) 493 - } 494 - for commentAt, references := range allReferencs { 495 - if comment, ok := commentMap[commentAt.String()]; ok { 496 - comment.References = references 497 - } 498 - } 499 - 500 - var comments []models.PullComment 501 - for _, c := range commentMap { 502 - comments = append(comments, *c) 503 - } 504 - 505 - sort.Slice(comments, func(i, j int) bool { 506 - return comments[i].Created.Before(comments[j].Created) 507 - }) 508 - 509 - return comments, nil 510 - } 511 - 512 424 // timeframe here is directly passed into the sql query filter, and any 513 425 // timeframe in the past should be negative; e.g.: "-3 months" 514 426 func GetPullsByOwnerDid(e Execer, did, timeframe string) ([]models.Pull, error) { ··· 585 497 return pulls, nil 586 498 } 587 499 588 - func NewPullComment(tx *sql.Tx, comment *models.PullComment) (int64, error) { 589 - query := `insert into pull_comments (owner_did, repo_at, submission_id, comment_at, pull_id, body) values (?, ?, ?, ?, ?, ?)` 590 - res, err := tx.Exec( 591 - query, 592 - comment.OwnerDid, 593 - comment.RepoAt, 594 - comment.SubmissionId, 595 - comment.CommentAt, 596 - comment.PullId, 597 - comment.Body, 598 - ) 599 - if err != nil { 600 - return 0, err 601 - } 602 - 603 - i, err := res.LastInsertId() 604 - if err != nil { 605 - return 0, err 606 - } 607 - 608 - if err := putReferences(tx, comment.AtUri(), comment.References); err != nil { 609 - return 0, fmt.Errorf("put reference_links: %w", err) 610 - } 611 - 612 - return i, nil 613 - } 614 - 615 500 func SetPullState(e Execer, repoAt syntax.ATURI, pullId int, pullState models.PullState) error { 616 501 _, err := e.Exec( 617 502 `update pulls set state = ? where repo_at = ? and pull_id = ? and (state <> ? or state <> ?)`,
+7 -8
appview/db/reference.go
··· 124 124 values %s 125 125 ) 126 126 select 127 - p.owner_did, p.rkey, 128 - c.comment_at 127 + p.owner_did, p.rkey, c.at_uri 129 128 from input inp 130 129 join repos r 131 130 on r.did = inp.owner_did ··· 133 132 join pulls p 134 133 on p.repo_at = r.at_uri 135 134 and p.pull_id = inp.pull_id 136 - left join pull_comments c 135 + left join comments c 137 136 on inp.comment_id is not null 138 - and c.repo_at = r.at_uri and c.pull_id = p.pull_id 137 + and c.subject_at = ('at://' || p.owner_did || '/' || 'sh.tangled.repo.pull' || '/' || p.rkey) 139 138 and c.id = inp.comment_id 140 139 `, 141 140 strings.Join(vals, ","), ··· 293 292 return nil, fmt.Errorf("get pull backlinks: %w", err) 294 293 } 295 294 backlinks = append(backlinks, ls...) 296 - ls, err = getPullCommentBacklinks(e, backlinksMap[tangled.RepoPullCommentNSID]) 295 + ls, err = getPullCommentBacklinks(e, backlinksMap[tangled.CommentNSID]) 297 296 if err != nil { 298 297 return nil, fmt.Errorf("get pull_comment backlinks: %w", err) 299 298 } ··· 428 427 if len(aturis) == 0 { 429 428 return nil, nil 430 429 } 431 - filter := orm.FilterIn("c.comment_at", aturis) 430 + filter := orm.FilterIn("c.at_uri", aturis) 432 431 rows, err := e.Query( 433 432 fmt.Sprintf( 434 433 `select r.did, r.name, p.pull_id, c.id, p.title, p.state 435 434 from repos r 436 435 join pulls p 437 436 on r.at_uri = p.repo_at 438 - join pull_comments c 439 - on r.at_uri = c.repo_at and p.pull_id = c.pull_id 437 + join comments c 438 + on ('at://' || p.owner_did || '/' || 'sh.tangled.repo.pull' || '/' || p.rkey) = c.subject_at 440 439 where %s`, 441 440 filter.Condition(), 442 441 ),
+71
appview/ingester.go
··· 79 79 err = i.ingestString(e) 80 80 case tangled.RepoIssueNSID: 81 81 err = i.ingestIssue(ctx, e) 82 + case tangled.CommentNSID: 83 + err = i.ingestComment(e) 82 84 case tangled.RepoIssueCommentNSID: 83 85 err = i.ingestIssueComment(e) 84 86 case tangled.LabelDefinitionNSID: ··· 934 936 return nil 935 937 } 936 938 939 + func (i *Ingester) ingestComment(e *jmodels.Event) error { 940 + did := e.Did 941 + rkey := e.Commit.RKey 942 + 943 + var err error 944 + 945 + l := i.Logger.With("handler", "ingestComment", "nsid", e.Commit.Collection, "did", did, "rkey", rkey) 946 + l.Info("ingesting record") 947 + 948 + ddb, ok := i.Db.Execer.(*db.DB) 949 + if !ok { 950 + return fmt.Errorf("failed to index issue comment record, invalid db cast") 951 + } 952 + 953 + switch e.Commit.Operation { 954 + case jmodels.CommitOperationCreate, jmodels.CommitOperationUpdate: 955 + raw := json.RawMessage(e.Commit.Record) 956 + record := tangled.Comment{} 957 + err = json.Unmarshal(raw, &record) 958 + if err != nil { 959 + return fmt.Errorf("invalid record: %w", err) 960 + } 961 + 962 + comment, err := models.CommentFromRecord(syntax.DID(did), syntax.RecordKey(rkey), record) 963 + if err != nil { 964 + return fmt.Errorf("failed to parse comment from record: %w", err) 965 + } 966 + 967 + // TODO: ingest pull comments 968 + // we aren't ingesting pull comments yet because pull itself isn't fully atprotated. 969 + // so we cannot know which round this comment is pointing to 970 + if comment.Subject.Collection().String() == tangled.RepoPullNSID { 971 + l.Info("skip ingesting pull comments") 972 + return nil 973 + } 974 + 975 + if err := comment.Validate(); err != nil { 976 + return fmt.Errorf("failed to validate comment: %w", err) 977 + } 978 + 979 + tx, err := ddb.Begin() 980 + if err != nil { 981 + return fmt.Errorf("failed to start transaction: %w", err) 982 + } 983 + defer tx.Rollback() 984 + 985 + err = db.PutComment(tx, comment) 986 + if err != nil { 987 + return fmt.Errorf("failed to create comment: %w", err) 988 + } 989 + 990 + return tx.Commit() 991 + 992 + case jmodels.CommitOperationDelete: 993 + if err := db.DeleteComments( 994 + ddb, 995 + orm.FilterEq("did", did), 996 + orm.FilterEq("collection", e.Commit.Collection), 997 + orm.FilterEq("rkey", rkey), 998 + ); err != nil { 999 + return fmt.Errorf("failed to delete comment record: %w", err) 1000 + } 1001 + 1002 + return nil 1003 + } 1004 + 1005 + return nil 1006 + } 1007 + 937 1008 func (i *Ingester) ingestLabelDefinition(e *jmodels.Event) error { 938 1009 did := e.Did 939 1010 rkey := e.Commit.RKey
+138
appview/models/comment.go
··· 1 + package models 2 + 3 + import ( 4 + "fmt" 5 + "strings" 6 + "time" 7 + 8 + "github.com/bluesky-social/indigo/atproto/syntax" 9 + "github.com/whyrusleeping/cbor-gen" 10 + "tangled.org/core/api/tangled" 11 + ) 12 + 13 + type Comment struct { 14 + Id int64 15 + Did syntax.DID 16 + Collection syntax.NSID 17 + Rkey string 18 + Subject syntax.ATURI 19 + ReplyTo *syntax.ATURI 20 + Body string 21 + Created time.Time 22 + Edited *time.Time 23 + Deleted *time.Time 24 + Mentions []syntax.DID 25 + References []syntax.ATURI 26 + PullSubmissionId *int 27 + } 28 + 29 + func (c *Comment) AtUri() syntax.ATURI { 30 + return syntax.ATURI(fmt.Sprintf("at://%s/%s/%s", c.Did, c.Collection, c.Rkey)) 31 + } 32 + 33 + func (c *Comment) AsRecord() typegen.CBORMarshaler { 34 + mentions := make([]string, len(c.Mentions)) 35 + for i, did := range c.Mentions { 36 + mentions[i] = string(did) 37 + } 38 + references := make([]string, len(c.References)) 39 + for i, uri := range c.References { 40 + references[i] = string(uri) 41 + } 42 + var replyTo *string 43 + if c.ReplyTo != nil { 44 + replyToStr := c.ReplyTo.String() 45 + replyTo = &replyToStr 46 + } 47 + switch c.Collection { 48 + case tangled.RepoIssueCommentNSID: 49 + return &tangled.RepoIssueComment{ 50 + Issue: c.Subject.String(), 51 + Body: c.Body, 52 + CreatedAt: c.Created.Format(time.RFC3339), 53 + ReplyTo: replyTo, 54 + Mentions: mentions, 55 + References: references, 56 + } 57 + case tangled.RepoPullCommentNSID: 58 + return &tangled.RepoPullComment{ 59 + Pull: c.Subject.String(), 60 + Body: c.Body, 61 + CreatedAt: c.Created.Format(time.RFC3339), 62 + Mentions: mentions, 63 + References: references, 64 + } 65 + default: // default to CommentNSID 66 + return &tangled.Comment{ 67 + Subject: c.Subject.String(), 68 + Body: c.Body, 69 + CreatedAt: c.Created.Format(time.RFC3339), 70 + ReplyTo: replyTo, 71 + Mentions: mentions, 72 + References: references, 73 + } 74 + } 75 + } 76 + 77 + func (c *Comment) IsTopLevel() bool { 78 + return c.ReplyTo == nil 79 + } 80 + 81 + func (c *Comment) IsReply() bool { 82 + return c.ReplyTo != nil 83 + } 84 + 85 + func (c *Comment) Validate() error { 86 + // TODO: sanitize the body and then trim space 87 + if sb := strings.TrimSpace(c.Body); sb == "" { 88 + return fmt.Errorf("body is empty after HTML sanitization") 89 + } 90 + 91 + // if it's for PR, PullSubmissionId should not be nil 92 + if c.Subject.Collection().String() == tangled.RepoPullNSID { 93 + if c.PullSubmissionId == nil { 94 + return fmt.Errorf("PullSubmissionId should not be nil") 95 + } 96 + } 97 + return nil 98 + } 99 + 100 + func CommentFromRecord(did syntax.DID, rkey syntax.RecordKey, record tangled.Comment) (*Comment, error) { 101 + created, err := time.Parse(time.RFC3339, record.CreatedAt) 102 + if err != nil { 103 + created = time.Now() 104 + } 105 + 106 + if _, err = syntax.ParseATURI(record.Subject); err != nil { 107 + return nil, err 108 + } 109 + 110 + i := record 111 + mentions := make([]syntax.DID, len(record.Mentions)) 112 + for i, did := range record.Mentions { 113 + mentions[i] = syntax.DID(did) 114 + } 115 + references := make([]syntax.ATURI, len(record.References)) 116 + for i, uri := range i.References { 117 + references[i] = syntax.ATURI(uri) 118 + } 119 + var replyTo *syntax.ATURI 120 + if record.ReplyTo != nil { 121 + replyToAtUri := syntax.ATURI(*record.ReplyTo) 122 + replyTo = &replyToAtUri 123 + } 124 + 125 + comment := Comment{ 126 + Did: did, 127 + Collection: tangled.CommentNSID, 128 + Rkey: rkey.String(), 129 + Body: record.Body, 130 + Subject: syntax.ATURI(record.Subject), 131 + ReplyTo: replyTo, 132 + Created: created, 133 + Mentions: mentions, 134 + References: references, 135 + } 136 + 137 + return &comment, nil 138 + }
+2 -28
appview/models/pull.go
··· 138 138 RoundNumber int 139 139 Patch string 140 140 Combined string 141 - Comments []PullComment 141 + Comments []Comment 142 142 SourceRev string // include the rev that was used to create this submission: only for branch/fork PRs 143 143 144 144 // meta 145 145 Created time.Time 146 146 } 147 147 148 - type PullComment struct { 149 - // ids 150 - ID int 151 - PullId int 152 - SubmissionId int 153 - 154 - // at ids 155 - RepoAt string 156 - OwnerDid string 157 - CommentAt string 158 - 159 - // content 160 - Body string 161 - 162 - // meta 163 - Mentions []syntax.DID 164 - References []syntax.ATURI 165 - 166 - // meta 167 - Created time.Time 168 - } 169 - 170 - func (p *PullComment) AtUri() syntax.ATURI { 171 - return syntax.ATURI(p.CommentAt) 172 - } 173 - 174 148 func (p *Pull) TotalComments() int { 175 149 total := 0 176 150 for _, s := range p.Submissions { ··· 279 253 addParticipant(s.PullAt.Authority().String()) 280 254 281 255 for _, c := range s.Comments { 282 - addParticipant(c.OwnerDid) 256 + addParticipant(c.Did.String()) 283 257 } 284 258 285 259 return participants
+11 -6
appview/notify/db/db.go
··· 260 260 ) 261 261 } 262 262 263 - func (n *databaseNotifier) NewPullComment(ctx context.Context, comment *models.PullComment, mentions []syntax.DID) { 264 - pull, err := db.GetPull(n.db, 265 - syntax.ATURI(comment.RepoAt), 266 - comment.PullId, 263 + func (n *databaseNotifier) NewPullComment(ctx context.Context, comment *models.Comment, mentions []syntax.DID) { 264 + pulls, err := db.GetPulls(n.db, 265 + orm.FilterEq("owner_did", comment.Subject.Authority()), 266 + orm.FilterEq("rkey", comment.Subject.RecordKey()), 267 267 ) 268 268 if err != nil { 269 269 log.Printf("NewPullComment: failed to get pulls: %v", err) 270 270 return 271 271 } 272 + if len(pulls) == 0 { 273 + log.Printf("NewPullComment: no pull found for %s", comment.Subject) 274 + return 275 + } 276 + pull := pulls[0] 272 277 273 - repo, err := db.GetRepo(n.db, orm.FilterEq("at_uri", comment.RepoAt)) 278 + repo, err := db.GetRepo(n.db, orm.FilterEq("at_uri", pull.RepoAt)) 274 279 if err != nil { 275 280 log.Printf("NewPullComment: failed to get repos: %v", err) 276 281 return ··· 288 293 recipients.Remove(m) 289 294 } 290 295 291 - actorDid := syntax.DID(comment.OwnerDid) 296 + actorDid := comment.Did 292 297 eventType := models.NotificationTypePullCommented 293 298 entityType := "pull" 294 299 entityId := pull.AtUri().String()
+1 -1
appview/notify/merged_notifier.go
··· 81 81 m.fanout("NewPull", ctx, pull) 82 82 } 83 83 84 - func (m *mergedNotifier) NewPullComment(ctx context.Context, comment *models.PullComment, mentions []syntax.DID) { 84 + func (m *mergedNotifier) NewPullComment(ctx context.Context, comment *models.Comment, mentions []syntax.DID) { 85 85 m.fanout("NewPullComment", ctx, comment, mentions) 86 86 } 87 87
+2 -2
appview/notify/notifier.go
··· 22 22 DeleteFollow(ctx context.Context, follow *models.Follow) 23 23 24 24 NewPull(ctx context.Context, pull *models.Pull) 25 - NewPullComment(ctx context.Context, comment *models.PullComment, mentions []syntax.DID) 25 + NewPullComment(ctx context.Context, comment *models.Comment, mentions []syntax.DID) 26 26 NewPullState(ctx context.Context, actor syntax.DID, pull *models.Pull) 27 27 28 28 UpdateProfile(ctx context.Context, profile *models.Profile) ··· 52 52 func (m *BaseNotifier) DeleteFollow(ctx context.Context, follow *models.Follow) {} 53 53 54 54 func (m *BaseNotifier) NewPull(ctx context.Context, pull *models.Pull) {} 55 - func (m *BaseNotifier) NewPullComment(ctx context.Context, models *models.PullComment, mentions []syntax.DID) { 55 + func (m *BaseNotifier) NewPullComment(ctx context.Context, models *models.Comment, mentions []syntax.DID) { 56 56 } 57 57 func (m *BaseNotifier) NewPullState(ctx context.Context, actor syntax.DID, pull *models.Pull) {} 58 58
+3 -4
appview/notify/posthog/notifier.go
··· 86 86 } 87 87 } 88 88 89 - func (n *posthogNotifier) NewPullComment(ctx context.Context, comment *models.PullComment, mentions []syntax.DID) { 89 + func (n *posthogNotifier) NewPullComment(ctx context.Context, comment *models.Comment, mentions []syntax.DID) { 90 90 err := n.client.Enqueue(posthog.Capture{ 91 - DistinctId: comment.OwnerDid, 91 + DistinctId: comment.Did.String(), 92 92 Event: "new_pull_comment", 93 93 Properties: posthog.Properties{ 94 - "repo_at": comment.RepoAt, 95 - "pull_id": comment.PullId, 94 + "pull_at": comment.Subject, 96 95 "mentions": mentions, 97 96 }, 98 97 })
+5 -4
appview/pages/templates/repo/pulls/pull.html
··· 561 561 {{ end }} 562 562 563 563 {{ define "submissionComment" }} 564 - <div id="comment-{{.ID}}" class="flex gap-2 -ml-4 py-4 w-full mx-auto"> 564 + <div id="comment-{{.Id}}" class="flex gap-2 -ml-4 py-4 w-full mx-auto"> 565 565 <!-- left column: profile picture --> 566 566 <div class="flex-shrink-0 h-fit relative"> 567 - {{ template "user/fragments/picLink" (list .OwnerDid "size-8") }} 567 + {{ template "user/fragments/picLink" (list .Did.String "size-8") }} 568 568 </div> 569 569 <!-- right column: name and body in two rows --> 570 570 <div class="flex-1 min-w-0"> 571 571 <!-- Row 1: Author and timestamp --> 572 572 <div class="text-sm text-gray-500 dark:text-gray-400 flex items-center gap-1"> 573 - {{ $handle := resolve .OwnerDid }} 573 + {{ $handle := resolve .Did.String }} 574 574 <a class="text-gray-500 dark:text-gray-400 hover:text-gray-500 dark:hover:text-gray-300" href="/{{ $handle }}">{{ $handle }}</a> 575 575 <span class="before:content-['路']"></span> 576 - <a class="text-gray-500 dark:text-gray-400 hover:text-gray-500 dark:hover:text-gray-300" href="#comment-{{.ID}}"> 576 + <a class="text-gray-500 dark:text-gray-400 hover:text-gray-500 dark:hover:text-gray-300" href="#comment-{{.Id}}"> 577 + {{ template "repo/fragments/time" .Created }} 577 578 {{ template "repo/fragments/shortTime" .Created }} 578 579 </a> 579 580 </div>
+1 -1
appview/pulls/opengraph.go
··· 277 277 } 278 278 279 279 // Get comment count from database 280 - comments, err := db.GetPullComments(s.db, orm.FilterEq("pull_id", pull.ID)) 280 + comments, err := db.GetComments(s.db, orm.FilterEq("subject_at", pull.AtUri())) 281 281 if err != nil { 282 282 log.Printf("failed to get pull comments: %v", err) 283 283 }
+26 -24
appview/pulls/pulls.go
··· 727 727 } 728 728 defer tx.Rollback() 729 729 730 - createdAt := time.Now().Format(time.RFC3339) 730 + comment := models.Comment{ 731 + Did: syntax.DID(user.Active.Did), 732 + Collection: tangled.CommentNSID, 733 + Rkey: tid.TID(), 734 + Subject: pull.AtUri(), 735 + ReplyTo: nil, 736 + Body: body, 737 + Created: time.Now(), 738 + Mentions: mentions, 739 + References: references, 740 + PullSubmissionId: &pull.Submissions[roundNumber].ID, 741 + } 742 + if err = comment.Validate(); err != nil { 743 + log.Println("failed to validate comment", err) 744 + s.pages.Notice(w, "pull-comment", "Failed to create comment.") 745 + return 746 + } 731 747 732 748 client, err := s.oauth.AuthorizedClient(r) 733 749 if err != nil { ··· 735 751 s.pages.Notice(w, "pull-comment", "Failed to create comment.") 736 752 return 737 753 } 738 - atResp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ 739 - Collection: tangled.RepoPullCommentNSID, 740 - Repo: user.Active.Did, 741 - Rkey: tid.TID(), 754 + 755 + _, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ 756 + Collection: comment.Collection.String(), 757 + Repo: comment.Did.String(), 758 + Rkey: comment.Rkey, 742 759 Record: &lexutil.LexiconTypeDecoder{ 743 - Val: &tangled.RepoPullComment{ 744 - Pull: pull.AtUri().String(), 745 - Body: body, 746 - CreatedAt: createdAt, 747 - }, 760 + Val: comment.AsRecord(), 748 761 }, 749 762 }) 750 763 if err != nil { ··· 753 766 return 754 767 } 755 768 756 - comment := &models.PullComment{ 757 - OwnerDid: user.Active.Did, 758 - RepoAt: f.RepoAt().String(), 759 - PullId: pull.PullId, 760 - Body: body, 761 - CommentAt: atResp.Uri, 762 - SubmissionId: pull.Submissions[roundNumber].ID, 763 - Mentions: mentions, 764 - References: references, 765 - } 766 - 767 769 // Create the pull comment in the database with the commentAt field 768 - commentId, err := db.NewPullComment(tx, comment) 770 + err = db.PutComment(tx, &comment) 769 771 if err != nil { 770 772 log.Println("failed to create pull comment", err) 771 773 s.pages.Notice(w, "pull-comment", "Failed to create comment.") ··· 779 781 return 780 782 } 781 783 782 - s.notifier.NewPullComment(r.Context(), comment, mentions) 784 + s.notifier.NewPullComment(r.Context(), &comment, mentions) 783 785 784 786 ownerSlashRepo := reporesolver.GetBaseRepoPath(r, f) 785 - s.pages.HxLocation(w, fmt.Sprintf("/%s/pulls/%d#comment-%d", ownerSlashRepo, pull.PullId, commentId)) 787 + s.pages.HxLocation(w, fmt.Sprintf("/%s/pulls/%d#comment-%d", ownerSlashRepo, pull.PullId, comment.Id)) 786 788 return 787 789 } 788 790 }
+1
appview/state/state.go
··· 118 118 tangled.StringNSID, 119 119 tangled.RepoIssueNSID, 120 120 tangled.RepoIssueCommentNSID, 121 + tangled.CommentNSID, 121 122 tangled.LabelDefinitionNSID, 122 123 tangled.LabelOpNSID, 123 124 },

History

8 rounds 4 comments
sign up or login to add to the discussion
1 commit
expand
appview: replace PullComment to Comment
2/3 timeout, 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 PullComment to Comment
2/3 failed, 1/3 success
expand
expand 0 comments
1 commit
expand
appview: replace PullComment to Comment
3/3 success
expand
expand 0 comments
1 commit
expand
appview: replace PullComment to Comment
3/3 success
expand
expand 1 comment

here, the sort order is reversed, it should be .Before.

1 commit
expand
appview: replace PullComment to Comment
1/3 failed, 2/3 success
expand
expand 0 comments
1 commit
expand
appview: replace PullComment to Comment
1/3 failed, 2/3 success
expand
expand 0 comments
1 commit
expand
appview: replace PullComment to Comment
3/3 success
expand
expand 3 comments

here will this delete all comments made on tangled thus far? don't think this is the right approach if so.

I should've been more precise about my expression. Existing comments won't be dropped as they are stored in issue_comments and pull_comments table (I'm just dropping legacy table with same name there), but yeah this PR itself doesn't include the backward compatibility layer yet so old comments won't be rendered.

Honestly this PR hasn't been updated since you two suggested to just ingest old records. I'll push more commits on top of this stack.

As a side note: I'd suggest to not publish the new sh.tangled.comment lexicon yet.

This unified comment record can be used for PR review comments and it might make us to modify the schema again. I think it would be safest to publish this when PR redesign discussion is finished.

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