Current section
Files
Jump to
Current section
Files
src/atproto/constellation.gleam
//// Client for [Constellation](https://constellation.microcosm.blue), the
//// microcosm atproto-wide backlink index: which records link to a given
//// record, identity, or URL.
import atproto/xrpc.{type Client, type XrpcError}
import gleam/dynamic/decode
import gleam/int
import gleam/list
import gleam/option.{type Option, None, Some}
import gleam/result
import gleam/uri
pub const default_host = "https://constellation.microcosm.blue"
/// One linking record; `record_uri` turns it back into an at-uri.
pub type Backlink {
Backlink(did: String, collection: String, rkey: String)
}
pub type BacklinksPage {
BacklinksPage(total: Int, records: List(Backlink), cursor: Option(String))
}
/// Records linking to `subject` (an at-uri, DID, or plain URL). `source` names
/// where the link must appear: `<collection>:<json.path>`, e.g.
/// `app.bsky.feed.like:subject.uri`.
pub fn get_backlinks(
client: Client,
host: String,
subject subject: String,
source source: String,
limit limit: Int,
cursor cursor: Option(String),
) -> Result(BacklinksPage, XrpcError) {
let params = [
#("subject", subject),
#("source", source),
#("limit", int.to_string(limit)),
]
let params = case cursor {
Some(c) -> list.append(params, [#("cursor", c)])
None -> params
}
let url =
host
<> "/xrpc/blue.microcosm.links.getBacklinks?"
<> uri.query_to_string(params)
use resp <- result.try(xrpc.get(client, url, None))
xrpc.parse(resp.body, page_decoder())
}
pub fn record_uri(backlink: Backlink) -> String {
"at://" <> backlink.did <> "/" <> backlink.collection <> "/" <> backlink.rkey
}
fn page_decoder() -> decode.Decoder(BacklinksPage) {
use total <- decode.field("total", decode.int)
use records <- decode.field("records", decode.list(backlink_decoder()))
use cursor <- decode.optional_field(
"cursor",
None,
decode.optional(decode.string),
)
decode.success(BacklinksPage(total:, records:, cursor:))
}
fn backlink_decoder() -> decode.Decoder(Backlink) {
use did <- decode.field("did", decode.string)
use collection <- decode.field("collection", decode.string)
use rkey <- decode.field("rkey", decode.string)
decode.success(Backlink(did:, collection:, rkey:))
}