Signed-off-by: Seongmin Lee git@boltless.me
+118
-42
Diff
round #1
-5
appview/db/comments.go
-5
appview/db/comments.go
···
11
11
12
12
"github.com/bluesky-social/indigo/api/atproto"
13
13
"github.com/bluesky-social/indigo/atproto/syntax"
14
-
"tangled.org/core/api/tangled"
15
14
"tangled.org/core/appview/models"
16
15
"tangled.org/core/orm"
17
16
)
18
17
19
18
func PutComment(tx *sql.Tx, c *models.Comment, references []syntax.ATURI) error {
20
-
if c.Collection == "" {
21
-
c.Collection = tangled.FeedCommentNSID
22
-
}
23
-
24
19
var bodyBlobs, replyToUri, replyToCid *string
25
20
if len(c.Body.Blobs) > 0 {
26
21
encoded, err := json.Marshal(c.Body.Blobs)
+58
-17
appview/migration/migrate_use_feed_comment.go
+58
-17
appview/migration/migrate_use_feed_comment.go
···
1
1
package migration
2
2
3
3
import (
4
+
"bytes"
4
5
"context"
5
6
"fmt"
6
7
···
10
11
"github.com/bluesky-social/indigo/atproto/syntax"
11
12
"github.com/bluesky-social/indigo/lex/util"
12
13
"github.com/bluesky-social/indigo/xrpc"
14
+
"github.com/ipfs/go-cid"
15
+
"github.com/multiformats/go-multihash"
13
16
"tangled.org/core/api/tangled"
14
17
"tangled.org/core/appview/db"
18
+
"tangled.org/core/appview/models"
15
19
"tangled.org/core/orm"
16
20
)
17
21
···
26
30
return fmt.Errorf("unexpected collection: '%s'", record.Collection())
27
31
}
28
32
29
-
comment, err := db.GetComment(s.db, orm.FilterEq("at_uri", record))
33
+
comments, err := db.GetComments(s.db, orm.FilterEq("at_uri", record))
30
34
if err != nil {
31
35
return fmt.Errorf("db: %w", err)
32
36
}
37
+
if len(comments) < 1 {
38
+
l.Info("can't found legacy record from db. skipping migration")
39
+
return nil
40
+
}
41
+
comment := comments[0]
33
42
34
43
comment.Collection = tangled.FeedCommentNSID
35
44
···
58
67
// fail if it isn't ready
59
68
uri = syntax.ATURI(fmt.Sprintf("at://%s/%s/%s", uri.Authority(), tangled.FeedCommentNSID, uri.RecordKey()))
60
69
61
-
cid, err := s.getRecordCid(ctx, uri)
70
+
cid, err := s.guessParentCommentCid(uri, &comment)
62
71
if err != nil {
63
-
return fmt.Errorf("pds: getRecordCid for replyTo.uri: %w", err)
72
+
return fmt.Errorf("cbor: guessParentCommentCid for replyTo.uri: %w", err)
64
73
}
65
74
comment.ReplyTo.Uri = uri.String()
66
75
comment.ReplyTo.Cid = cid.String()
···
69
78
// use same rkey for new record
70
79
rkey := record.RecordKey().String()
71
80
72
-
if _, err := comatproto.RepoApplyWrites(ctx, client, &comatproto.RepoApplyWrites_Input{
73
-
Repo: did.String(),
74
-
Writes: []*comatproto.RepoApplyWrites_Input_Writes_Elem{
75
-
{RepoApplyWrites_Delete: &comatproto.RepoApplyWrites_Delete{
76
-
Collection: record.Collection().String(),
77
-
Rkey: rkey,
78
-
}},
79
-
{RepoApplyWrites_Create: &comatproto.RepoApplyWrites_Create{
80
-
Collection: tangled.FeedCommentNSID,
81
-
Rkey: &rkey,
82
-
Value: &util.LexiconTypeDecoder{Val: comment.AsRecord()},
83
-
}},
84
-
},
81
+
if _, err := comatproto.RepoDeleteRecord(ctx, client, &comatproto.RepoDeleteRecord_Input{
82
+
Repo: did.String(),
83
+
Collection: record.Collection().String(),
84
+
Rkey: rkey,
85
85
}); err != nil {
86
-
return fmt.Errorf("pds: applyWrites: %w", err)
86
+
l.Info("Failed to cleanup old record. Proceeding migration...", "err", err)
87
+
}
88
+
89
+
// ensure new record is missing in PDS
90
+
if _, err := agnostic.RepoGetRecord(ctx, client, "", tangled.FeedCommentNSID, did.String(), rkey); err == nil {
91
+
l.Info("New comment record already exists")
92
+
} else {
93
+
// insert new record
94
+
if _, err := comatproto.RepoCreateRecord(ctx, client, &comatproto.RepoCreateRecord_Input{
95
+
Repo: did.String(),
96
+
Collection: tangled.FeedCommentNSID,
97
+
Rkey: &rkey,
98
+
Record: &util.LexiconTypeDecoder{Val: comment.AsRecord()},
99
+
}); err != nil {
100
+
return fmt.Errorf("pds: putRecord: %w", err)
101
+
}
87
102
}
88
103
89
104
return nil
···
111
126
112
127
return cid, nil
113
128
}
129
+
130
+
func (s *Migration) guessParentCommentCid(uri syntax.ATURI, comment *models.Comment) (syntax.CID, error) {
131
+
parent, err := db.GetComment(s.db, orm.FilterEq("did", uri.Authority()), orm.FilterEq("rkey", uri.RecordKey()))
132
+
if err != nil {
133
+
return "", fmt.Errorf("db: failed to queyr subject comment: %w", err)
134
+
}
135
+
if parent.Deleted != nil {
136
+
// leave cid empty. reply comment won't pass the schema validation.
137
+
return "", nil
138
+
}
139
+
140
+
// since parent comment is also migrating, parent comments subject.cid might be empty
141
+
if parent.Subject.Cid == "" {
142
+
parent.Subject.Cid = comment.Subject.Cid
143
+
}
144
+
145
+
buf := new(bytes.Buffer)
146
+
if err := parent.AsRecord().MarshalCBOR(buf); err != nil {
147
+
return "", fmt.Errorf("MarshalCBOR: %w", err)
148
+
}
149
+
c, err := cid.NewPrefixV1(cid.DagCBOR, multihash.SHA2_256).Sum(buf.Bytes())
150
+
if err != nil {
151
+
return "", fmt.Errorf("cid: sum: %w", err)
152
+
}
153
+
return syntax.CID(c.String()), nil
154
+
}
+12
-5
appview/models/comment.go
+12
-5
appview/models/comment.go
···
44
44
}
45
45
46
46
func (c Comment) AsRecord() typegen.CBORMarshaler {
47
-
// can't convert to record for legacy types
48
-
if c.Collection != tangled.FeedCommentNSID {
49
-
return nil
50
-
}
51
47
var pullRoundIdx *int64
52
48
if c.PullRoundIdx != nil {
53
49
pullRoundIdx = new(int64)
···
204
200
if r.ReplyTo == nil {
205
201
continue
206
202
}
207
-
if parent, exists := toplevel[syntax.ATURI(r.ReplyTo.Uri)]; exists {
203
+
uri := syntax.ATURI(r.ReplyTo.Uri)
204
+
if parent, exists := toplevel[uri]; exists {
205
+
parent.Replies = append(parent.Replies, r)
206
+
continue
207
+
}
208
+
// HACK: fallback to legacy comment collections
209
+
if parent, exists := toplevel[syntax.ATURI(fmt.Sprintf("at://%s/%s/%s", uri.Authority(), tangled.RepoIssueCommentNSID, uri.RecordKey()))]; exists {
208
210
parent.Replies = append(parent.Replies, r)
211
+
continue
212
+
}
213
+
if parent, exists := toplevel[syntax.ATURI(fmt.Sprintf("at://%s/%s/%s", uri.Authority(), tangled.RepoPullCommentNSID, uri.RecordKey()))]; exists {
214
+
parent.Replies = append(parent.Replies, r)
215
+
continue
209
216
}
210
217
}
211
218
+1
-7
appview/pages/templates/fragments/comment/commentList.html
+1
-7
appview/pages/templates/fragments/comment/commentList.html
···
38
38
<input name="subject-cid" type="hidden" value="{{ $item.Self.Subject.Cid }}">
39
39
<input name="reply-to-uri" type="hidden" value="{{ $item.Self.AtUri }}">
40
40
<input name="reply-to-cid" type="hidden" value="{{ $item.Self.Cid }}">
41
-
{{ if $item.Self.IsLegacy }}
42
-
<div class="py-2 px-6 border-t flex gap-2 items-center border-gray-300 dark:border-gray-700">
43
-
<span class="text-orange-500">Can't reply to legacy comment.</span>
44
-
</div>
45
-
{{ else }}
46
-
{{ template "fragments/comment/replyPlaceholder" (dict "LoggedInUser" $root.LoggedInUser) }}
47
-
{{ end }}
41
+
{{ template "fragments/comment/replyPlaceholder" (dict "LoggedInUser" $root.LoggedInUser) }}
48
42
</div>
49
43
</div>
50
44
{{ end }}
+47
-8
appview/state/comment.go
+47
-8
appview/state/comment.go
···
1
1
package state
2
2
3
3
import (
4
+
"bytes"
4
5
"fmt"
5
6
"net/http"
6
7
"strconv"
···
10
11
"github.com/bluesky-social/indigo/atproto/syntax"
11
12
lexutil "github.com/bluesky-social/indigo/lex/util"
12
13
indigoxrpc "github.com/bluesky-social/indigo/xrpc"
14
+
"github.com/ipfs/go-cid"
15
+
"github.com/multiformats/go-multihash"
13
16
14
17
"tangled.org/core/api/tangled"
15
18
"tangled.org/core/appview/db"
···
176
179
var replyTo *comatproto.RepoStrongRef
177
180
replyToUriRaw := r.FormValue("reply-to-uri")
178
181
replyToCidRaw := r.FormValue("reply-to-cid")
179
-
if replyToUriRaw != "" && replyToCidRaw != "" {
180
-
uri, err := syntax.ParseATURI(replyToUriRaw)
182
+
if replyToUriRaw != "" {
183
+
replyToUri, err := syntax.ParseATURI(replyToUriRaw)
181
184
if err != nil {
182
185
s.pages.Notice(w, noticeId, "reply-to-uri should be valid AT-URI")
183
186
return
184
187
}
185
-
cid, err := syntax.ParseCID(replyToCidRaw)
186
-
if err != nil {
187
-
s.pages.Notice(w, noticeId, "reply-to-cid should be valid CID")
188
-
return
188
+
// force replyTo.uri to `sh.tangled.feed.comment` collection, even when they aren't.
189
+
// we are expecting parent comment will be migrated later.
190
+
replyToUri = syntax.ATURI(fmt.Sprintf("at://%s/%s/%s", replyToUri.Authority(), tangled.FeedCommentNSID, replyToUri.RecordKey()))
191
+
192
+
var replyToCid syntax.CID
193
+
if replyToCidRaw != "" {
194
+
replyToCid, err = syntax.ParseCID(replyToCidRaw)
195
+
if err != nil {
196
+
s.pages.Notice(w, noticeId, "reply-to-cid should be valid CID")
197
+
return
198
+
}
199
+
} else {
200
+
// guess parent comment cid
201
+
subjectComment, err := db.GetComment(s.db, orm.FilterEq("did", replyToUri.Authority()), orm.FilterEq("rkey", replyToUri.RecordKey()))
202
+
if err != nil {
203
+
l.Warn("db: failed to query subject comment", "err", err)
204
+
s.pages.Notice(w, noticeId, "Subject record is unknown.")
205
+
return
206
+
}
207
+
if subjectComment.Deleted != nil {
208
+
// leave cid empty. reply comment won't pass the schema validation.
209
+
} else {
210
+
// guess cid from content
211
+
c, err := func() (cid.Cid, error) {
212
+
buf := new(bytes.Buffer)
213
+
if subjectComment.Subject.Cid == "" {
214
+
subjectComment.Subject.Cid = subject.Cid
215
+
}
216
+
if err := subjectComment.AsRecord().MarshalCBOR(buf); err != nil {
217
+
return cid.Undef, fmt.Errorf("MarshalCBOR: %w", err)
218
+
}
219
+
return cid.NewPrefixV1(cid.DagCBOR, multihash.SHA2_256).Sum(buf.Bytes())
220
+
}()
221
+
if err != nil {
222
+
l.Warn("cbor: failed to guess parent comment cid", "err", err)
223
+
s.pages.Notice(w, noticeId, "Parent comment is invalid.")
224
+
return
225
+
}
226
+
replyToCid = syntax.CID(c.String())
227
+
}
189
228
}
190
229
replyTo = &comatproto.RepoStrongRef{
191
-
Uri: uri.String(),
192
-
Cid: cid.String(),
230
+
Uri: replyToUri.String(),
231
+
Cid: replyToCid.String(),
193
232
}
194
233
}
195
234
History
2 rounds
0 comments
boltless.me
submitted
#1
1 commit
expand
collapse
appview: be more compatible with legacy comment records
Signed-off-by: Seongmin Lee <git@boltless.me>
merge conflicts detected
expand
collapse
expand
collapse
- appview/migration/migration.go:71
expand 0 comments
boltless.me
submitted
#0
1 commit
expand
collapse
appview: be more compatible with legacy comment records
Signed-off-by: Seongmin Lee <git@boltless.me>