diff --git a/public.go b/public.go index 427f66c..19a4ad7 100644 --- a/public.go +++ b/public.go @@ -198,30 +198,49 @@ func (c *PublicClient) GetProfile(ctx context.Context, actor string) (*PublicPro return &profile, nil } -// ListPublicRecords fetches up to limit records from a public collection. +// ListPublicRecordsOpts configures a ListPublicRecords call. +// All fields are optional; the zero value yields the lexicon defaults. +type ListPublicRecordsOpts struct { + Limit int // 0 means server default + Cursor string // empty for first page + Reverse bool // true returns newest-first +} + +// ListPublicRecords fetches records from a public collection. // Queries the user's PDS directly, so it works with any collection NSID. -func (c *PublicClient) ListPublicRecords(ctx context.Context, did, collection string, limit int) ([]Record, error) { +func (c *PublicClient) ListPublicRecords(ctx context.Context, did, collection string, opts ListPublicRecordsOpts) ([]Record, string, error) { pdsEndpoint, err := c.GetPDSEndpoint(ctx, did) if err != nil { - return nil, fmt.Errorf("resolve PDS: %w", err) + return nil, "", fmt.Errorf("resolve PDS: %w", err) } - reqURL := fmt.Sprintf("%s/xrpc/com.atproto.repo.listRecords?repo=%s&collection=%s&limit=%d", - pdsEndpoint, url.QueryEscape(did), url.QueryEscape(collection), limit) + q := url.Values{} + q.Set("repo", did) + q.Set("collection", collection) + if opts.Limit > 0 { + q.Set("limit", fmt.Sprintf("%d", opts.Limit)) + } + if opts.Cursor != "" { + q.Set("cursor", opts.Cursor) + } + if opts.Reverse { + q.Set("reverse", "true") + } + reqURL := fmt.Sprintf("%s/xrpc/com.atproto.repo.listRecords?%s", pdsEndpoint, q.Encode()) req, err := http.NewRequestWithContext(ctx, "GET", reqURL, nil) if err != nil { - return nil, fmt.Errorf("build request: %w", err) + return nil, "", fmt.Errorf("build request: %w", err) } resp, err := c.httpClient.Do(req) if err != nil { - return nil, fmt.Errorf("list records: %w", err) + return nil, "", fmt.Errorf("list records: %w", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("list records: HTTP %d", resp.StatusCode) + return nil, "", fmt.Errorf("list records: HTTP %d", resp.StatusCode) } var result struct { @@ -230,16 +249,17 @@ func (c *PublicClient) ListPublicRecords(ctx context.Context, did, collection st CID string `json:"cid"` Value map[string]any `json:"value"` } `json:"records"` + Cursor string `json:"cursor"` } if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { - return nil, fmt.Errorf("decode records: %w", err) + return nil, "", fmt.Errorf("decode records: %w", err) } records := make([]Record, len(result.Records)) for i, r := range result.Records { records[i] = Record{URI: r.URI, CID: r.CID, Value: r.Value} } - return records, nil + return records, result.Cursor, nil } // GetPublicRecord fetches a single public record from a user's PDS.