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.

feat(lexicon): custom representation of url

+130 -7
+6 -1
document.go
··· 17 17 type Document struct { 18 18 // Site points to a [Publication] record `at://` or a [Publication.URL] `https://` for loose documents. 19 19 // Avoid trailing slashes. 20 - Site string `json:"site"` 20 + Site *URL `json:"site" map:"string"` 21 21 // Title of the [Document]. 22 22 // Max length: 5000. 23 23 // Max graphemes: 500. ··· 111 111 fmt.Sprintf(`<link rel="%s" href="%s">`, CollectionDocument, createAtURI(repo, CollectionDocument, rkey)), 112 112 ) 113 113 } 114 + 115 + // getPublicationVerification returns the string used during the verification of the [Publication]. 116 + func getDocumentVerification(repo syntax.AtIdentifier, rkey syntax.RecordKey) string { 117 + return createAtURI(repo, CollectionDocument, rkey) 118 + }
+3 -3
document_test.go
··· 31 31 t.Fatal(err) 32 32 } 33 33 doc := v.Record.(*site.Document) 34 - if doc.Site != `at://did:plc:jdhpqeb4cb4mng533dx56cbc/site.standard.publication/3mhm4m2tets2y` { 34 + if doc.Site.String() != `at://did:plc:jdhpqeb4cb4mng533dx56cbc/site.standard.publication/3mhm4m2tets2y` { 35 35 t.Errorf("invalid site: %s", doc.Site) 36 36 } 37 37 if doc.Title != `hello world` { ··· 84 84 85 85 func TestGetDocument(t *testing.T) { 86 86 if testing.Short() { 87 - t.Skip() 87 + t.Skip("not doing http requests in short") 88 88 } 89 89 uri, client := getClient(t, testDoc, &docURI, &docClient) 90 90 doc, err := site.GetRecord[*site.Document](context.Background(), client, uri.Authority(), uri.RecordKey()) ··· 98 98 99 99 func TestListDocuments(t *testing.T) { 100 100 if testing.Short() { 101 - t.Skip() 101 + t.Skip("not doing http requests in short") 102 102 } 103 103 uri, client := getClient(t, testDoc, &docURI, &docClient) 104 104 docs, _, err := site.ListRecords[*site.Document](context.Background(), client, uri.Authority(), "", false)
+1
go.mod
··· 28 28 gitlab.com/yawning/secp256k1-voi v0.0.0-20230925100816-f2616030848b // indirect 29 29 gitlab.com/yawning/tuplehash v0.0.0-20230713102510-df83abbf9a02 // indirect 30 30 golang.org/x/crypto v0.49.0 // indirect 31 + golang.org/x/net v0.52.0 // indirect 31 32 golang.org/x/sys v0.42.0 // indirect 32 33 golang.org/x/time v0.3.0 // indirect 33 34 golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect
+2
go.sum
··· 54 54 gitlab.com/yawning/tuplehash v0.0.0-20230713102510-df83abbf9a02/go.mod h1:JTnUj0mpYiAsuZLmKjTx/ex3AtMowcCgnE7YNyCEP0I= 55 55 golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4= 56 56 golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA= 57 + golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0= 58 + golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw= 57 59 golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= 58 60 golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= 59 61 golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
+2 -1
map.go
··· 95 95 return nil, err 96 96 } 97 97 name := fieldType.Name 98 - data := strings.Split(refType.Field(i).Tag.Get("json"), ",") 98 + data := strings.Split(fieldType.Tag.Get("json"), ",") 99 + data = append(data, strings.Split(fieldType.Tag.Get("map"), ",")...) 99 100 if len(data) > 0 { 100 101 if len(data[0]) > 0 { 101 102 name = data[0]
+2 -2
publication_test.go
··· 183 183 184 184 func TestListPublications(t *testing.T) { 185 185 if testing.Short() { 186 - t.Skip() 186 + t.Skip("not doing http requests in short") 187 187 } 188 188 uri, client := getClient(t, testPub, &pubURI, &pubClient) 189 189 pubs, _, err := site.ListRecords[*site.Publication](context.Background(), client, uri.Authority(), "", false) ··· 217 217 218 218 func TestPublication_Verify(t *testing.T) { 219 219 if testing.Short() { 220 - t.Skip() 220 + t.Skip("not doing http requests in short") 221 221 } 222 222 id, client := getClient(t, testPub, &pubURI, &pubClient) 223 223 pub, err := site.GetRecord[*site.Publication](context.Background(), client, id.Authority(), id.RecordKey())
+114
url.go
··· 1 + package site 2 + 3 + import ( 4 + "encoding/json" 5 + "errors" 6 + "net/url" 7 + "strings" 8 + 9 + "github.com/bluesky-social/indigo/atproto/syntax" 10 + ) 11 + 12 + var ( 13 + ErrIncompleteURL = errors.New("incomplete url") 14 + ) 15 + 16 + // ATURL represents a full AT [url.URL]. 17 + type ATURL struct { 18 + syntax.ATURI 19 + } 20 + 21 + func (at *ATURL) String() string { 22 + // not using [fmt.Sprintf] because it may be slower 23 + return "at://" + at.Authority().String() + 24 + "/" + at.Collection().String() + 25 + "/" + at.RecordKey().String() 26 + } 27 + 28 + // ParseATURL returns an [ATURL] from a raw string. 29 + func ParseATURL(raw string) (*ATURL, error) { 30 + u, err := syntax.ParseATURI(raw) 31 + if err != nil { 32 + return nil, err 33 + } 34 + if u.Collection() == "" { 35 + return nil, ErrIncompleteURL 36 + } 37 + if u.RecordKey() == "" { 38 + return nil, ErrIncompleteURL 39 + } 40 + return &ATURL{u}, nil 41 + } 42 + 43 + // URL represents an [url.URL] that may be an [ATURL]. 44 + type URL struct { 45 + url *url.URL 46 + at *ATURL 47 + } 48 + 49 + // IsAT returns true if the [URL] is an [ATURL]. 50 + // 51 + // See [URL.AT] and [URL.URL]. 52 + func (u *URL) IsAT() bool { 53 + return u.at != nil 54 + } 55 + 56 + // URL returns the [url.URL]. 57 + // Panics if it is an [ATURL]. 58 + // 59 + // See [URL.IsAT]. 60 + func (u *URL) URL() *url.URL { 61 + if u.IsAT() { 62 + panic("not an URL") 63 + } 64 + return u.url 65 + } 66 + 67 + // AT returns the [ATURL]. 68 + // Panics if it isn't an [ATURL]. 69 + // 70 + // See [URL.IsAT]. 71 + func (u *URL) AT() *ATURL { 72 + if !u.IsAT() { 73 + panic("not an AT URL") 74 + } 75 + return u.at 76 + } 77 + 78 + func (u *URL) String() string { 79 + if u.IsAT() { 80 + return u.at.String() 81 + } 82 + return u.url.String() 83 + } 84 + 85 + func (u *URL) UnmarshalJSON(b []byte) error { 86 + var s string 87 + err := json.Unmarshal(b, &s) 88 + if err != nil { 89 + return err 90 + } 91 + url, err := ParseURL(s) 92 + if err != nil { 93 + return err 94 + } 95 + *u = *url 96 + return nil 97 + } 98 + 99 + // ParseURL returns an [URL] from a raw string. 100 + func ParseURL(raw string) (*URL, error) { 101 + if strings.HasPrefix(raw, "at://") { 102 + u, err := ParseATURL(raw) 103 + if err != nil { 104 + return nil, err 105 + } 106 + return &URL{at: u}, nil 107 + } 108 + u, err := url.Parse(raw) 109 + if err != nil { 110 + return nil, err 111 + } 112 + u.Path = strings.TrimPrefix(u.Path, "/") 113 + return &URL{url: u}, nil 114 + }