mount public data from the atmosphere to a virtual filesystem (macos only) pdfs.at
0
fork

Configure Feed

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

feat(atproto): add listRecords typed endpoint

+71
+27
Packages/ATProto/Sources/ATProto/XRPC/ListRecordsOutput.swift
··· 1 + import Foundation 2 + 3 + public struct ListRecordsOutput: Decodable, Sendable { 4 + public struct Record: Decodable, Sendable { 5 + public let uri: String 6 + public let cid: String 7 + /// Pretty-printed, sorted-keys JSON bytes of the record value. Callers 8 + /// write these bytes directly to disk when rendering records as files. 9 + public let value: Data 10 + 11 + enum CodingKeys: String, CodingKey { case uri, cid, value } 12 + 13 + public init(from decoder: Decoder) throws { 14 + let c = try decoder.container(keyedBy: CodingKeys.self) 15 + uri = try c.decode(String.self, forKey: .uri) 16 + cid = try c.decode(String.self, forKey: .cid) 17 + let any = try c.decode(AnyDecodable.self, forKey: .value) 18 + value = try JSONSerialization.data( 19 + withJSONObject: any.value, 20 + options: [.prettyPrinted, .sortedKeys] 21 + ) 22 + } 23 + } 24 + 25 + public let records: [Record] 26 + public let cursor: String? 27 + }
+20
Packages/ATProto/Sources/ATProto/XRPC/XRPCEndpoints.swift
··· 1 1 import Foundation 2 2 3 + // MARK: - describeRepo 4 + 3 5 public extension XRPCClient { 4 6 func describeRepo(repo: String) async throws -> DescribeRepoOutput { 5 7 try await get(nsid: "com.atproto.repo.describeRepo", params: ["repo": repo]) 6 8 } 7 9 } 10 + 11 + // MARK: - listRecords 12 + 13 + public extension XRPCClient { 14 + func listRecords( 15 + repo: String, 16 + collection: String, 17 + limit: Int? = nil, 18 + cursor: String? = nil, 19 + reverse: Bool? = nil 20 + ) async throws -> ListRecordsOutput { 21 + var params: [String: String] = ["repo": repo, "collection": collection] 22 + if let limit { params["limit"] = String(limit) } 23 + if let cursor { params["cursor"] = cursor } 24 + if let reverse { params["reverse"] = reverse ? "true" : "false" } 25 + return try await get(nsid: "com.atproto.repo.listRecords", params: params) 26 + } 27 + }
+24
Packages/ATProto/Tests/ATProtoTests/XRPCEndpointsTests.swift
··· 28 28 #expect(result.did == "did:plc:abc") 29 29 #expect(result.collections == ["app.bsky.feed.post", "app.bsky.actor.profile"]) 30 30 } 31 + 32 + @Test("listRecords returns paged records + cursor") 33 + func listRecords() async throws { 34 + let (client, session) = makeClient { request in 35 + #expect(request.url?.path == "/xrpc/com.atproto.repo.listRecords") 36 + let q = request.url?.query ?? "" 37 + #expect(q.contains("repo=did:plc:abc")) 38 + #expect(q.contains("collection=app.bsky.feed.post")) 39 + #expect(q.contains("limit=50")) 40 + return .json(#"{"records":[{"uri":"at://did:plc:abc/app.bsky.feed.post/3l5xq2abc","cid":"bafyreibyvxuebctujncksacu3qfxsv6pzm7z67vfrftndn6pzhknf7g5me","value":{"$type":"app.bsky.feed.post","text":"hi"}}],"cursor":"next-page"}"#) 41 + } 42 + defer { URLProtocolStub.reset(session: session) } 43 + 44 + let out = try await client.listRecords( 45 + repo: "did:plc:abc", 46 + collection: "app.bsky.feed.post", 47 + limit: 50, 48 + cursor: nil, 49 + reverse: nil 50 + ) 51 + #expect(out.records.count == 1) 52 + #expect(out.records[0].uri == "at://did:plc:abc/app.bsky.feed.post/3l5xq2abc") 53 + #expect(out.cursor == "next-page") 54 + } 31 55 }