GoAT Site is library that implements Standard.site in Go.
atprotocol standard-site atproto library
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

refactor(xrpc): generalize using generics

+46 -107
+6 -7
README.md
··· 20 20 21 21 These types implement `Record`, an interface describing records. 22 22 23 - You can get, list, create, update or delete them with functions. 24 - Each function starts with the action followed by the lexicon's name, e.g., 25 - - `GetPublication` to get a publication; 26 - - `ListDocuments` to list documents; 27 - - `CreateDocument` to create a new document. 28 - 29 - Currently, functions related to `Subscription` are not implemented. 23 + You can get, list, create, update or delete them with functions: 24 + - `GetRecord[*site.Publication]` to get a publication; 25 + - `ListRecords[*site.Document]` to list documents; 26 + - `CreateRecord[*site.Document]` to create a new document; 27 + - `UpdateRecord[*site.Subscription]` to update a subscription; 28 + - `DeleteRecord[*site.Publication]` to delete a publication. 30 29 31 30 You can [verify](https://standard.site/docs/verification/) a publication with `Publication.Verify`. 32 31
-34
document.go
··· 1 1 package site 2 2 3 3 import ( 4 - "context" 5 4 "encoding/json" 6 5 "fmt" 7 6 "html/template" ··· 9 8 "time" 10 9 11 10 "github.com/bluesky-social/indigo/atproto/syntax" 12 - lexutil "github.com/bluesky-social/indigo/lex/util" 13 11 ) 14 12 15 13 const CollectionDocument = CollectionBase + ".document" ··· 103 101 } 104 102 *d = Document(v.t) 105 103 return nil 106 - } 107 - 108 - // GetDocument returns the [Document] in the repo associated with the rkey. 109 - // Automatically uses the latest CID. 110 - func GetDocument(ctx context.Context, client lexutil.LexClient, repo syntax.AtIdentifier, rkey syntax.RecordKey) (*Document, error) { 111 - return get[*Document](ctx, client, CollectionDocument, repo, rkey) 112 - } 113 - 114 - // ListDocuments returns all the [Document]s stored in the repo and the cursor. 115 - // 116 - // See [MaxItemsPerList]. 117 - func ListDocuments(ctx context.Context, client lexutil.LexClient, repo syntax.AtIdentifier, cursor string, reverse bool) ([]*Document, *string, error) { 118 - return listRecord[*Document](ctx, client, CollectionDocument, repo, cursor, reverse) 119 - } 120 - 121 - // CreateDocument in a repo with the given rkey. 122 - // Always tries to validate the [Document] against the lexicon saved. 123 - // 124 - // Rkey can be nil. 125 - func CreateDocument(ctx context.Context, client lexutil.LexClient, repo syntax.AtIdentifier, rkey *syntax.RecordKey, doc *Document) (*Result, error) { 126 - return createRecord(ctx, client, CollectionDocument, repo, rkey, doc) 127 - } 128 - 129 - // UpdateDocument in a repo with the given rkey. 130 - // Always tries to validate the [Document] against the lexicon saved. 131 - func UpdateDocument(ctx context.Context, client lexutil.LexClient, repo syntax.AtIdentifier, rkey syntax.RecordKey, doc *Document) (*Result, error) { 132 - return updateRecord(ctx, client, CollectionDocument, repo, rkey, doc) 133 - } 134 - 135 - // DeleteDocument in a repo with the given rkey. 136 - func DeleteDocument(ctx context.Context, client lexutil.LexClient, repo syntax.AtIdentifier, rkey syntax.RecordKey) error { 137 - return deleteRecord(ctx, client, CollectionDocument, repo, rkey) 138 104 } 139 105 140 106 // GetDocumentVerificationTag returns the HTML link tag checked during the verification of the [Document].
+2 -2
document_test.go
··· 87 87 t.Skip() 88 88 } 89 89 uri, client := getClient(t, testDoc, &docURI, &docClient) 90 - doc, err := site.GetDocument(context.Background(), client, uri.Authority(), uri.RecordKey()) 90 + doc, err := site.GetRecord[*site.Document](context.Background(), client, uri.Authority(), uri.RecordKey()) 91 91 if err != nil { 92 92 t.Fatal(err) 93 93 } ··· 101 101 t.Skip() 102 102 } 103 103 uri, client := getClient(t, testDoc, &docURI, &docClient) 104 - docs, _, err := site.ListDocuments(context.Background(), client, uri.Authority(), "", false) 104 + docs, _, err := site.ListRecords[*site.Document](context.Background(), client, uri.Authority(), "", false) 105 105 if err != nil { 106 106 t.Fatal(err) 107 107 }
+35 -28
lexicons.go
··· 77 77 return &RecordJSON{Record: r} 78 78 } 79 79 80 + // GetType returns the type associated with the [RecordJSON]. 81 + func (r *RecordJSON) GetType() string { 82 + if r.Record != nil { 83 + return r.Record.Type() 84 + } 85 + return r.Type 86 + } 87 + 80 88 // As unmarshals the [RecordJSON] as the provided [Record]. 81 89 // 82 90 // [ErrRecordAlreadyParsed] if the [Record] was already parsed (stored in [RecordJSON.Record]). ··· 193 201 Commit *agnostic.RepoDefs_CommitMeta 194 202 } 195 203 196 - // get returns the T in the repo associated with the rkey. 204 + // GetRecord returns the [Record] in the repo associated with the rkey. 197 205 // Automatically uses the latest CID. 198 - func get[T Record](ctx context.Context, client lexutil.LexClient, collection string, repo syntax.AtIdentifier, rkey syntax.RecordKey) (t T, err error) { 206 + // 207 + // Returns [ErrInvalidType] if the [Record] got doesn't have a valid type. 208 + func GetRecord[T Record](ctx context.Context, client lexutil.LexClient, repo syntax.AtIdentifier, rkey syntax.RecordKey) (t T, err error) { 199 209 var rec *agnostic.RepoGetRecord_Output 200 - rec, err = agnostic.RepoGetRecord(ctx, client, "", collection, repo.String(), rkey.String()) 210 + rec, err = agnostic.RepoGetRecord(ctx, client, "", t.Type(), repo.String(), rkey.String()) 201 211 if err != nil { 202 212 return 203 213 } ··· 206 216 if err != nil { 207 217 return 208 218 } 209 - if v.Record == nil { 210 - err = ErrInvalidType{collection, v.Type} 211 - return 212 - } 213 - if v.Record.Type() != collection { 214 - err = ErrInvalidType{collection, v.Record.Type()} 219 + if v.GetType() != t.Type() { 220 + err = ErrInvalidType{t.Type(), v.Type} 215 221 return 216 222 } 217 223 return v.Record.(T), nil 218 224 } 219 225 220 - // listRecord returns all the Ts stored in the repo and the cursor. 226 + // ListRecords returns all the [Record]s stored in the repo and the cursor. 227 + // 228 + // Returns [ErrInvalidType] if a [Record] got doesn't have a valid type. 221 229 // 222 230 // See [MaxItemsPerList]. 223 - func listRecord[T Record](ctx context.Context, client lexutil.LexClient, collection string, repo syntax.AtIdentifier, cursor string, reverse bool) ([]T, *string, error) { 224 - rec, err := agnostic.RepoListRecords(ctx, client, collection, cursor, MaxItemsPerList, repo.String(), reverse) 231 + func ListRecords[T Record](ctx context.Context, client lexutil.LexClient, repo syntax.AtIdentifier, cursor string, reverse bool) ([]T, *string, error) { 232 + var t T 233 + rec, err := agnostic.RepoListRecords(ctx, client, t.Type(), cursor, MaxItemsPerList, repo.String(), reverse) 225 234 if err != nil { 226 235 return nil, nil, err 227 236 } ··· 234 243 if err != nil { 235 244 return nil, nil, err 236 245 } 237 - if v.Record == nil { 238 - return nil, nil, ErrInvalidType{collection, v.Type} 239 - } 240 - if v.Record.Type() != collection { 241 - return nil, nil, ErrInvalidType{collection, v.Record.Type()} 246 + if v.GetType() != t.Type() { 247 + return nil, nil, ErrInvalidType{t.Type(), v.Type} 242 248 } 243 249 docs[i] = v.Record.(T) 244 250 } 245 251 return docs[:i], rec.Cursor, nil 246 252 } 247 253 248 - // createRecord a T in a repo with the given rkey. 249 - // Always tries to validate the [Document] against the lexicon saved. 254 + // CreateRecord in a repo with the given rkey. 255 + // Always tries to validate the [Record] against the lexicon saved. 250 256 // 251 257 // Rkey can be nil. 252 - func createRecord[T Record](ctx context.Context, client lexutil.LexClient, collection string, repo syntax.AtIdentifier, rkey *syntax.RecordKey, v T) (*Result, error) { 258 + func CreateRecord[T Record](ctx context.Context, client lexutil.LexClient, repo syntax.AtIdentifier, rkey *syntax.RecordKey, v T) (*Result, error) { 253 259 mp, err := MarshalToMap(AsJSON(v)) 254 260 if err != nil { 255 261 return nil, err ··· 261 267 } 262 268 t := true 263 269 out, err := agnostic.RepoCreateRecord(ctx, client, &agnostic.RepoCreateRecord_Input{ 264 - Collection: collection, 270 + Collection: v.Type(), 265 271 Record: mp, 266 272 Repo: repo.String(), 267 273 Rkey: cv, ··· 273 279 return &Result{out.Uri, out.Cid, out.ValidationStatus, out.Commit}, nil 274 280 } 275 281 276 - // updateRecord T in a repo with the given rkey. 277 - // Always tries to validate the [Document] against the lexicon saved. 278 - func updateRecord[T Record](ctx context.Context, client lexutil.LexClient, collection string, repo syntax.AtIdentifier, rkey syntax.RecordKey, v T) (*Result, error) { 282 + // UpdateRecord in a repo with the given rkey. 283 + // Always tries to validate the [Record] against the lexicon saved. 284 + func UpdateRecord[T Record](ctx context.Context, client lexutil.LexClient, repo syntax.AtIdentifier, rkey syntax.RecordKey, v T) (*Result, error) { 279 285 mp, err := MarshalToMap(AsJSON(v)) 280 286 if err != nil { 281 287 return nil, err 282 288 } 283 289 t := true 284 290 out, err := agnostic.RepoPutRecord(ctx, client, &agnostic.RepoPutRecord_Input{ 285 - Collection: collection, 291 + Collection: v.Type(), 286 292 Record: mp, 287 293 Repo: repo.String(), 288 294 Rkey: rkey.String(), ··· 295 301 return &Result{out.Uri, out.Cid, out.ValidationStatus, out.Commit}, nil 296 302 } 297 303 298 - // delete in a repo with the given rkey. 299 - func deleteRecord(ctx context.Context, client lexutil.LexClient, collection string, repo syntax.AtIdentifier, rkey syntax.RecordKey) error { 304 + // DeleteRecord in a repo with the given rkey. 305 + func DeleteRecord[T Record](ctx context.Context, client lexutil.LexClient, repo syntax.AtIdentifier, rkey syntax.RecordKey) error { 306 + var t T 300 307 _, err := atproto.RepoDeleteRecord(ctx, client, &atproto.RepoDeleteRecord_Input{ 301 - Collection: collection, 308 + Collection: t.Type(), 302 309 Repo: repo.String(), 303 310 Rkey: rkey.String(), 304 311 })
-33
publication.go
··· 10 10 "strings" 11 11 12 12 "github.com/bluesky-social/indigo/atproto/syntax" 13 - lexutil "github.com/bluesky-social/indigo/lex/util" 14 13 ) 15 14 16 15 const CollectionPublication = CollectionBase + ".publication" ··· 96 95 type Preferences struct { 97 96 // ShowInDiscover decides whether the [Publication] should appear in discovery feeds. 98 97 ShowInDiscover bool `json:"showInDiscover"` 99 - } 100 - 101 - // GetPublication returns the [Publication] in the repo associated with the rkey. 102 - // Automatically uses the latest CID. 103 - func GetPublication(ctx context.Context, client lexutil.LexClient, repo syntax.AtIdentifier, rkey syntax.RecordKey) (*Publication, error) { 104 - return get[*Publication](ctx, client, CollectionPublication, repo, rkey) 105 - } 106 - 107 - // ListPublications returns all the [Publication]s stored in the repo and the cursor. 108 - // 109 - // See [MaxItemsPerList]. 110 - func ListPublications(ctx context.Context, client lexutil.LexClient, repo syntax.AtIdentifier, cursor string, reverse bool) ([]*Publication, *string, error) { 111 - return listRecord[*Publication](ctx, client, CollectionPublication, repo, cursor, reverse) 112 - } 113 - 114 - // CreatePublication in a repo with the given rkey. 115 - // Always tries to validate the [Publication] against the lexicon saved. 116 - // 117 - // Rkey can be nil. 118 - func CreatePublication(ctx context.Context, client lexutil.LexClient, repo syntax.AtIdentifier, rkey *syntax.RecordKey, pub *Publication) (*Result, error) { 119 - return createRecord(ctx, client, CollectionPublication, repo, rkey, pub) 120 - } 121 - 122 - // UpdatePublication in a repo with the given rkey. 123 - // Always tries to validate the [Publication] against the lexicon saved. 124 - func UpdatePublication(ctx context.Context, client lexutil.LexClient, repo syntax.AtIdentifier, rkey syntax.RecordKey, pub *Publication) (*Result, error) { 125 - return updateRecord(ctx, client, CollectionPublication, repo, rkey, pub) 126 - } 127 - 128 - // DeletePublication in a repo with the given rkey. 129 - func DeletePublication(ctx context.Context, client lexutil.LexClient, repo syntax.AtIdentifier, rkey syntax.RecordKey) error { 130 - return deleteRecord(ctx, client, CollectionPublication, repo, rkey) 131 98 } 132 99 133 100 // getPublicationVerification returns the string used during the verification of the [Publication].
+3 -3
publication_test.go
··· 172 172 t.Skip() 173 173 } 174 174 uri, client := getClient(t, testPub, &pubURI, &pubClient) 175 - pub, err := site.GetPublication(context.Background(), client, uri.Authority(), uri.RecordKey()) 175 + pub, err := site.GetRecord[*site.Publication](context.Background(), client, uri.Authority(), uri.RecordKey()) 176 176 if err != nil { 177 177 t.Fatal(err) 178 178 } ··· 186 186 t.Skip() 187 187 } 188 188 uri, client := getClient(t, testPub, &pubURI, &pubClient) 189 - pubs, _, err := site.ListPublications(context.Background(), client, uri.Authority(), "", false) 189 + pubs, _, err := site.ListRecords[*site.Publication](context.Background(), client, uri.Authority(), "", false) 190 190 if err != nil { 191 191 t.Fatal(err) 192 192 } ··· 220 220 t.Skip() 221 221 } 222 222 id, client := getClient(t, testPub, &pubURI, &pubClient) 223 - pub, err := site.GetPublication(context.Background(), client, id.Authority(), id.RecordKey()) 223 + pub, err := site.GetRecord[*site.Publication](context.Background(), client, id.Authority(), id.RecordKey()) 224 224 if err != nil { 225 225 t.Fatal(err) 226 226 }