This repository has no description
1package migration
2
3import (
4 "context"
5 "encoding/json"
6 "fmt"
7 "strings"
8
9 comatproto "github.com/bluesky-social/indigo/api/atproto"
10 "github.com/bluesky-social/indigo/atproto/atclient"
11 "github.com/bluesky-social/indigo/atproto/syntax"
12 lexutil "github.com/bluesky-social/indigo/lex/util"
13 "tangled.org/core/api/tangled"
14 "tangled.org/core/appview/db"
15)
16
17func (s *Migration) migrateAddRepoDid(ctx context.Context, client *atclient.APIClient, did syntax.DID, record syntax.ATURI) error {
18 if record.Collection().String() == tangled.FeedStarNSID {
19 return s.migrateAddRepoDidStar(ctx, client, did, record)
20 }
21
22 ex, err := comatproto.RepoGetRecord(ctx, client, "", record.Collection().String(), did.String(), record.RecordKey().String())
23 if err != nil {
24 return fmt.Errorf("pds: %w", err)
25 }
26
27 val := ex.Value.Val
28
29 switch record.Collection().String() {
30 case tangled.RepoNSID:
31 rec, ok := val.(*tangled.Repo)
32 if !ok {
33 return fmt.Errorf("unexpected type for repo record")
34 }
35 repo, err := db.GetRepoByAtUri(s.db, record.String())
36 if err != nil {
37 return fmt.Errorf("db: failed to query repo: %w", err)
38 }
39 rec.RepoDid = &repo.RepoDid
40
41 case tangled.RepoIssueNSID:
42 rec, ok := val.(*tangled.RepoIssue)
43 if !ok {
44 return fmt.Errorf("unexpected type for issue record")
45 }
46 if strings.HasPrefix(rec.Repo, "did:") {
47 return nil
48 }
49 repo, err := db.GetRepoByAtUri(s.db, rec.Repo)
50 if err != nil {
51 return fmt.Errorf("db: failed to query repo by at_uri %q: %w", rec.Repo, err)
52 }
53 rec.Repo = repo.RepoDid
54
55 case tangled.RepoPullNSID:
56 rec, ok := val.(*tangled.RepoPull)
57 if !ok {
58 return fmt.Errorf("unexpected type for pull record")
59 }
60 if rec.Target == nil {
61 return fmt.Errorf("pull record has nil target")
62 }
63 if !strings.HasPrefix(rec.Target.Repo, "did:") {
64 repo, err := db.GetRepoByAtUri(s.db, rec.Target.Repo)
65 if err != nil {
66 return fmt.Errorf("db: failed to query target repo by at_uri %q: %w", rec.Target.Repo, err)
67 }
68 rec.Target.Repo = repo.RepoDid
69 }
70 if rec.Source != nil && rec.Source.Repo != nil && !strings.HasPrefix(*rec.Source.Repo, "did:") {
71 sourceRepo, srcErr := db.GetRepoByAtUri(s.db, *rec.Source.Repo)
72 if srcErr == nil && sourceRepo.RepoDid != "" {
73 rec.Source.Repo = &sourceRepo.RepoDid
74 }
75 }
76
77 case tangled.RepoCollaboratorNSID:
78 rec, ok := val.(*tangled.RepoCollaborator)
79 if !ok {
80 return fmt.Errorf("unexpected type for collaborator record")
81 }
82 if strings.HasPrefix(rec.Repo, "did:") {
83 return nil
84 }
85 repo, err := db.GetRepoByAtUri(s.db, rec.Repo)
86 if err != nil {
87 return fmt.Errorf("db: failed to query repo by at_uri %q: %w", rec.Repo, err)
88 }
89 rec.Repo = repo.RepoDid
90
91 case tangled.RepoArtifactNSID:
92 rec, ok := val.(*tangled.RepoArtifact)
93 if !ok {
94 return fmt.Errorf("unexpected type for artifact record")
95 }
96 if rec.Repo != nil {
97 repo, err := db.GetRepoByAtUri(s.db, *rec.Repo)
98 if err != nil {
99 return fmt.Errorf("db: failed to query repo by at_uri %q: %w", *rec.Repo, err)
100 }
101 rec.RepoDid = &repo.RepoDid
102 }
103
104 case tangled.ActorProfileNSID:
105 rec, ok := val.(*tangled.ActorProfile)
106 if !ok {
107 return fmt.Errorf("unexpected type for profile record")
108 }
109 rewritten := make([]string, 0, len(rec.PinnedRepositories))
110 for _, pin := range rec.PinnedRepositories {
111 if strings.HasPrefix(pin, "did:") {
112 rewritten = append(rewritten, pin)
113 continue
114 }
115 repo, repoErr := db.GetRepoByAtUri(s.db, pin)
116 if repoErr != nil || repo.RepoDid == "" {
117 rewritten = append(rewritten, pin)
118 continue
119 }
120 rewritten = append(rewritten, repo.RepoDid)
121 }
122 rec.PinnedRepositories = rewritten
123
124 default:
125 return fmt.Errorf("unexpected collection: '%s'", record.Collection())
126 }
127
128 _, err = comatproto.RepoPutRecord(ctx, client, &comatproto.RepoPutRecord_Input{
129 Repo: did.String(),
130 Collection: record.Collection().String(),
131 Rkey: record.RecordKey().String(),
132 SwapRecord: ex.Cid,
133 Record: &lexutil.LexiconTypeDecoder{Val: val},
134 })
135 if err != nil {
136 return fmt.Errorf("put record: %w", err)
137 }
138
139 return nil
140}
141
142func (s *Migration) migrateAddRepoDidStar(ctx context.Context, client *atclient.APIClient, did syntax.DID, record syntax.ATURI) error {
143 var raw struct {
144 Cid *string `json:"cid,omitempty"`
145 Uri string `json:"uri"`
146 Value json.RawMessage `json:"value"`
147 }
148 params := map[string]any{
149 "collection": record.Collection().String(),
150 "repo": did.String(),
151 "rkey": record.RecordKey().String(),
152 }
153 if err := client.LexDo(ctx, lexutil.Query, "", "com.atproto.repo.getRecord", params, nil, &raw); err != nil {
154 return fmt.Errorf("get record: %w", err)
155 }
156
157 var legacy struct {
158 CreatedAt string `json:"createdAt"`
159 Subject *string `json:"subject,omitempty"`
160 }
161 if err := json.Unmarshal(raw.Value, &legacy); err != nil {
162 return fmt.Errorf("decode old star fields: %w", err)
163 }
164 if legacy.Subject == nil {
165 return fmt.Errorf("star record has no subject field")
166 }
167
168 repo, err := db.GetRepoByAtUri(s.db, *legacy.Subject)
169 if err != nil {
170 return fmt.Errorf("db: failed to query repo by at_uri %q: %w", *legacy.Subject, err)
171 }
172 if repo.RepoDid == "" {
173 return fmt.Errorf("repo has no repoDid: %s", *legacy.Subject)
174 }
175
176 newRecord := &tangled.FeedStar{
177 CreatedAt: legacy.CreatedAt,
178 Subject: &tangled.FeedStar_Subject{
179 FeedStar_Repo: &tangled.FeedStar_Repo{Did: repo.RepoDid},
180 },
181 }
182
183 _, err = comatproto.RepoPutRecord(ctx, client, &comatproto.RepoPutRecord_Input{
184 Repo: did.String(),
185 Collection: record.Collection().String(),
186 Rkey: record.RecordKey().String(),
187 SwapRecord: raw.Cid,
188 Record: &lexutil.LexiconTypeDecoder{Val: newRecord},
189 })
190 if err != nil {
191 return fmt.Errorf("put record: %w", err)
192 }
193 return nil
194}