···173173 // We don't use /> to end the tag, because it is only valid for HTML5 and only required for XHTML.
174174 // See https://blog.novalistic.com/archives/2017/08/optional-end-tags-in-html/
175175 return template.HTML(
176176- fmt.Sprintf(`<link rel="%s" href="%s">`, CollectionDocument, createAtURI(repo, CollectionDocument, rkey)),
176176+ fmt.Sprintf(`<link rel="%s" href="%s">`, CollectionDocument, createAtURL(repo, CollectionDocument, rkey)),
177177 )
178178}
179179180180// getPublicationVerification returns the string used during the verification of the [Publication].
181181func getDocumentVerification(repo syntax.AtIdentifier, rkey syntax.RecordKey) string {
182182- return createAtURI(repo, CollectionDocument, rkey)
182182+ return createAtURL(repo, CollectionDocument, rkey).String()
183183}
-133
lexicons.go
···11package site
2233import (
44- "context"
54 "encoding/json"
65 "errors"
76 "fmt"
87 "time"
99-1010- "github.com/bluesky-social/indigo/api/agnostic"
1111- "github.com/bluesky-social/indigo/api/atproto"
1212- "github.com/bluesky-social/indigo/atproto/syntax"
1313- lexutil "github.com/bluesky-social/indigo/lex/util"
148)
1591610// Record represents an ATProto record.
···189183 b.CID = v.Ref.Link
190184 return nil
191185}
192192-193193-// MaxItemsPerList is the number of items per list call.
194194-const MaxItemsPerList = 25
195195-196196-// Result is returned when after creating a record.
197197-type Result struct {
198198- URI string
199199- CID string
200200- ValidationStatus *string
201201- Commit *agnostic.RepoDefs_CommitMeta
202202-}
203203-204204-// GetRecord returns the [Record] in the repo associated with the rkey.
205205-// Automatically uses the latest CID.
206206-//
207207-// Returns [ErrInvalidType] if the [Record] got doesn't have a valid type.
208208-func GetRecord[T Record](ctx context.Context, client lexutil.LexClient, repo syntax.AtIdentifier, rkey syntax.RecordKey) (t T, err error) {
209209- var rec *agnostic.RepoGetRecord_Output
210210- rec, err = agnostic.RepoGetRecord(ctx, client, "", t.Type(), repo.String(), rkey.String())
211211- if err != nil {
212212- return
213213- }
214214- var v *RecordJSON
215215- err = json.Unmarshal(*rec.Value, &v)
216216- if err != nil {
217217- return
218218- }
219219- if v.GetType() != t.Type() {
220220- err = ErrInvalidType{t.Type(), v.Type}
221221- return
222222- }
223223- return v.Record.(T), nil
224224-}
225225-226226-// ListRecords returns all the [Record]s stored in the repo and the cursor.
227227-//
228228-// Returns [ErrInvalidType] if a [Record] got doesn't have a valid type.
229229-//
230230-// See [MaxItemsPerList].
231231-func ListRecords[T Record](ctx context.Context, client lexutil.LexClient, repo syntax.AtIdentifier, cursor string, reverse bool) ([]T, *string, error) {
232232- var t T
233233- rec, err := agnostic.RepoListRecords(ctx, client, t.Type(), cursor, MaxItemsPerList, repo.String(), reverse)
234234- if err != nil {
235235- return nil, nil, err
236236- }
237237- docs := make([]T, MaxItemsPerList)
238238- i := 0
239239- for i = range len(rec.Records) {
240240- r := rec.Records[i]
241241- var v *RecordJSON
242242- err = json.Unmarshal(*r.Value, &v)
243243- if err != nil {
244244- return nil, nil, err
245245- }
246246- if v.GetType() != t.Type() {
247247- return nil, nil, ErrInvalidType{t.Type(), v.Type}
248248- }
249249- docs[i] = v.Record.(T)
250250- }
251251- return docs[:i], rec.Cursor, nil
252252-}
253253-254254-// CreateRecord in a repo with the given rkey.
255255-// Always tries to validate the [Record] against the lexicon saved.
256256-//
257257-// Rkey can be nil.
258258-func CreateRecord[T Record](ctx context.Context, client lexutil.LexClient, repo syntax.AtIdentifier, rkey *syntax.RecordKey, v T) (*Result, error) {
259259- mp, err := MarshalToMap(AsJSON(v))
260260- if err != nil {
261261- return nil, err
262262- }
263263- var cv *string
264264- if rkey != nil {
265265- t := rkey.String()
266266- cv = &t
267267- }
268268- t := true
269269- out, err := agnostic.RepoCreateRecord(ctx, client, &agnostic.RepoCreateRecord_Input{
270270- Collection: v.Type(),
271271- Record: mp,
272272- Repo: repo.String(),
273273- Rkey: cv,
274274- Validate: &t,
275275- })
276276- if err != nil {
277277- return nil, err
278278- }
279279- return &Result{out.Uri, out.Cid, out.ValidationStatus, out.Commit}, nil
280280-}
281281-282282-// UpdateRecord in a repo with the given rkey.
283283-// Always tries to validate the [Record] against the lexicon saved.
284284-func UpdateRecord[T Record](ctx context.Context, client lexutil.LexClient, repo syntax.AtIdentifier, rkey syntax.RecordKey, v T) (*Result, error) {
285285- mp, err := MarshalToMap(AsJSON(v))
286286- if err != nil {
287287- return nil, err
288288- }
289289- t := true
290290- out, err := agnostic.RepoPutRecord(ctx, client, &agnostic.RepoPutRecord_Input{
291291- Collection: v.Type(),
292292- Record: mp,
293293- Repo: repo.String(),
294294- Rkey: rkey.String(),
295295- Validate: &t,
296296- //SwapRecord: &cid,
297297- })
298298- if err != nil {
299299- return nil, err
300300- }
301301- return &Result{out.Uri, out.Cid, out.ValidationStatus, out.Commit}, nil
302302-}
303303-304304-// DeleteRecord in a repo with the given rkey.
305305-func DeleteRecord[T Record](ctx context.Context, client lexutil.LexClient, repo syntax.AtIdentifier, rkey syntax.RecordKey) error {
306306- var t T
307307- _, err := atproto.RepoDeleteRecord(ctx, client, &atproto.RepoDeleteRecord_Input{
308308- Collection: t.Type(),
309309- Repo: repo.String(),
310310- Rkey: rkey.String(),
311311- })
312312- return err
313313-}
314314-315315-// createAtURI returns a valid [syntax.ATURI].
316316-func createAtURI(repo syntax.AtIdentifier, collection string, rkey syntax.RecordKey) string {
317317- return fmt.Sprintf("at://%s/%s/%s", repo, collection, rkey)
318318-}
+1-1
publication.go
···100100101101// getPublicationVerification returns the string used during the verification of the [Publication].
102102func getPublicationVerification(repo syntax.AtIdentifier, rkey syntax.RecordKey) string {
103103- return createAtURI(repo, CollectionPublication, rkey)
103103+ return createAtURL(repo, CollectionPublication, rkey).String()
104104}
105105106106// HandlePublicationVerification returns an [http.Handler] used during the verification of the [Publication].
+138
xrpc.go
···11+package site
22+33+import (
44+ "context"
55+ "encoding/json"
66+77+ "github.com/bluesky-social/indigo/api/agnostic"
88+ "github.com/bluesky-social/indigo/api/atproto"
99+ "github.com/bluesky-social/indigo/atproto/syntax"
1010+ lexutil "github.com/bluesky-social/indigo/lex/util"
1111+)
1212+1313+// MaxItemsPerList is the number of items per list call.
1414+const MaxItemsPerList = 25
1515+1616+// Result is returned when after creating a record.
1717+type Result struct {
1818+ URI string
1919+ CID string
2020+ ValidationStatus *string
2121+ Commit *agnostic.RepoDefs_CommitMeta
2222+}
2323+2424+// GetRecord returns the [Record] in the repo associated with the rkey.
2525+// Automatically uses the latest CID.
2626+//
2727+// Returns [ErrInvalidType] if the [Record] got doesn't have a valid type.
2828+func GetRecord[T Record](ctx context.Context, client lexutil.LexClient, repo syntax.AtIdentifier, rkey syntax.RecordKey) (t T, err error) {
2929+ var rec *agnostic.RepoGetRecord_Output
3030+ rec, err = agnostic.RepoGetRecord(ctx, client, "", t.Type(), repo.String(), rkey.String())
3131+ if err != nil {
3232+ return
3333+ }
3434+ var v *RecordJSON
3535+ err = json.Unmarshal(*rec.Value, &v)
3636+ if err != nil {
3737+ return
3838+ }
3939+ if v.GetType() != t.Type() {
4040+ err = ErrInvalidType{t.Type(), v.Type}
4141+ return
4242+ }
4343+ return v.Record.(T), nil
4444+}
4545+4646+// ListRecords returns all the [Record]s stored in the repo and the cursor.
4747+//
4848+// Returns [ErrInvalidType] if a [Record] got doesn't have a valid type.
4949+//
5050+// See [MaxItemsPerList].
5151+func ListRecords[T Record](ctx context.Context, client lexutil.LexClient, repo syntax.AtIdentifier, cursor string, reverse bool) ([]T, *string, error) {
5252+ var t T
5353+ rec, err := agnostic.RepoListRecords(ctx, client, t.Type(), cursor, MaxItemsPerList, repo.String(), reverse)
5454+ if err != nil {
5555+ return nil, nil, err
5656+ }
5757+ docs := make([]T, MaxItemsPerList)
5858+ i := 0
5959+ for i = range len(rec.Records) {
6060+ r := rec.Records[i]
6161+ var v *RecordJSON
6262+ err = json.Unmarshal(*r.Value, &v)
6363+ if err != nil {
6464+ return nil, nil, err
6565+ }
6666+ if v.GetType() != t.Type() {
6767+ return nil, nil, ErrInvalidType{t.Type(), v.Type}
6868+ }
6969+ docs[i] = v.Record.(T)
7070+ }
7171+ return docs[:i], rec.Cursor, nil
7272+}
7373+7474+// CreateRecord in a repo with the given rkey.
7575+// Always tries to validate the [Record] against the lexicon saved.
7676+//
7777+// Rkey can be nil.
7878+func CreateRecord[T Record](ctx context.Context, client lexutil.LexClient, repo syntax.AtIdentifier, rkey *syntax.RecordKey, v T) (*Result, error) {
7979+ mp, err := MarshalToMap(AsJSON(v))
8080+ if err != nil {
8181+ return nil, err
8282+ }
8383+ var cv *string
8484+ if rkey != nil {
8585+ t := rkey.String()
8686+ cv = &t
8787+ }
8888+ t := true
8989+ out, err := agnostic.RepoCreateRecord(ctx, client, &agnostic.RepoCreateRecord_Input{
9090+ Collection: v.Type(),
9191+ Record: mp,
9292+ Repo: repo.String(),
9393+ Rkey: cv,
9494+ Validate: &t,
9595+ })
9696+ if err != nil {
9797+ return nil, err
9898+ }
9999+ return &Result{out.Uri, out.Cid, out.ValidationStatus, out.Commit}, nil
100100+}
101101+102102+// UpdateRecord in a repo with the given rkey.
103103+// Always tries to validate the [Record] against the lexicon saved.
104104+func UpdateRecord[T Record](ctx context.Context, client lexutil.LexClient, repo syntax.AtIdentifier, rkey syntax.RecordKey, v T) (*Result, error) {
105105+ mp, err := MarshalToMap(AsJSON(v))
106106+ if err != nil {
107107+ return nil, err
108108+ }
109109+ t := true
110110+ out, err := agnostic.RepoPutRecord(ctx, client, &agnostic.RepoPutRecord_Input{
111111+ Collection: v.Type(),
112112+ Record: mp,
113113+ Repo: repo.String(),
114114+ Rkey: rkey.String(),
115115+ Validate: &t,
116116+ //SwapRecord: &cid,
117117+ })
118118+ if err != nil {
119119+ return nil, err
120120+ }
121121+ return &Result{out.Uri, out.Cid, out.ValidationStatus, out.Commit}, nil
122122+}
123123+124124+// DeleteRecord in a repo with the given rkey.
125125+func DeleteRecord[T Record](ctx context.Context, client lexutil.LexClient, repo syntax.AtIdentifier, rkey syntax.RecordKey) error {
126126+ var t T
127127+ _, err := atproto.RepoDeleteRecord(ctx, client, &atproto.RepoDeleteRecord_Input{
128128+ Collection: t.Type(),
129129+ Repo: repo.String(),
130130+ Rkey: rkey.String(),
131131+ })
132132+ return err
133133+}
134134+135135+// createAtURL returns a valid [syntax.ATURI].
136136+func createAtURL(repo syntax.AtIdentifier, collection string, rkey syntax.RecordKey) *ATURL {
137137+ return &ATURL{syntax.ATURI("at://" + repo.String() + "/" + collection + "/" + rkey.String())}
138138+}