use std::convert::From;
use super::LabelerDefs;
use crate::client::{AgentError, AgentSessionExt, CollectionErr, CollectionOutput};
use crate::moderation::labeled::LabeledRecord;
#[cfg(feature = "api_bluesky")]
use jacquard_api::app_bsky::labeler::{
get_services::{GetServices, GetServicesOutput},
service::Service,
};
use jacquard_api::com_atproto::label::{Label, query_labels::QueryLabels};
use jacquard_common::BosStr;
use jacquard_common::bos::DefaultStr;
use jacquard_common::error::ClientError;
use jacquard_common::types::collection::Collection;
use jacquard_common::types::string::Did;
use jacquard_common::types::uri::RecordUri;
use jacquard_common::xrpc::{XrpcClient, XrpcError, XrpcResp};
use smol_str::SmolStr;
#[cfg(feature = "api_bluesky")]
pub async fn fetch_labeler_defs(
client: &(impl XrpcClient + Sync),
dids: Vec<Did>,
) -> Result<LabelerDefs, ClientError> {
#[cfg(feature = "tracing")]
let _span = tracing::debug_span!("fetch_labeler_defs", count = dids.len()).entered();
let request = GetServices::new().dids(dids).detailed(true).build();
let response = client.send(request).await?;
let output: GetServicesOutput = response.into_output().map_err(|e| match e {
XrpcError::Auth(auth) => ClientError::auth(auth),
XrpcError::Generic(g) => ClientError::decode(g.to_string()),
XrpcError::Decode(e) => ClientError::decode(format!("{:?}", e)),
XrpcError::Xrpc(typed) => ClientError::decode(format!("{:?}", typed)),
_ => ClientError::decode("unknown XRPC error"),
})?;
let mut defs = LabelerDefs::new();
use jacquard_api::app_bsky::labeler::get_services::GetServicesOutputViewsItem;
for view in output.views {
match view {
GetServicesOutputViewsItem::LabelerViewDetailed(detailed) => {
if let Some(label_value_definitions) = &detailed.policies.label_value_definitions {
defs.insert(
detailed.creator.did.clone(),
label_value_definitions.clone(),
);
}
}
_ => {
continue;
}
}
}
Ok(defs)
}
#[cfg(feature = "api_bluesky")]
pub async fn fetch_labeler_defs_direct(
client: &(impl AgentSessionExt + Sync),
dids: Vec<Did>,
) -> Result<LabelerDefs, AgentError> {
#[cfg(feature = "tracing")]
let _span = tracing::debug_span!("fetch_labeler_defs_direct", count = dids.len()).entered();
let mut defs = LabelerDefs::new();
for did in dids {
let uri = format!("at://{}/app.bsky.labeler.service/self", did.as_str());
let record_uri = Service::uri(uri).map_err(|e| {
AgentError::from(ClientError::invalid_request(format!("Invalid URI: {}", e)))
})?;
let output = client.fetch_record(&record_uri).await?;
let service: Service = output.value;
if let Some(label_value_definitions) = service.policies.label_value_definitions {
defs.insert(did, label_value_definitions);
}
}
Ok(defs)
}
pub async fn fetch_labels(
client: &impl AgentSessionExt,
uri_patterns: Vec<SmolStr>,
sources: Vec<Did>,
cursor: Option<SmolStr>,
) -> Result<(Vec<Label>, Option<SmolStr>), AgentError> {
#[cfg(feature = "tracing")]
let _span = tracing::debug_span!("fetch_labels", count = sources.len()).entered();
let request = QueryLabels::new()
.maybe_cursor(cursor)
.limit(250)
.uri_patterns(uri_patterns)
.sources(sources)
.build();
let labels = client
.send(request)
.await?
.into_output()
.map_err(|e| match e {
XrpcError::Auth(auth) => AgentError::from(auth),
XrpcError::Xrpc(typed) => AgentError::xrpc(XrpcError::Xrpc(typed)),
e => AgentError::xrpc(e),
})?;
Ok((labels.labels, labels.cursor))
}
pub async fn fetch_labeled_record<R, S>(
client: &impl AgentSessionExt,
record_uri: &RecordUri<S, R>,
sources: Vec<Did>,
) -> Result<LabeledRecord<DefaultStr, R>, AgentError>
where
R: Collection + From<<<R as Collection>::Record as XrpcResp>::Output<smol_str::SmolStr>>,
S: BosStr + Sync,
CollectionOutput<R>: serde::de::DeserializeOwned,
CollectionErr<R>: Send + Sync + 'static,
{
let record: R = client.fetch_record(record_uri).await?.into();
let uri_pattern = SmolStr::new(record_uri.as_uri().as_str());
let (labels, _) = fetch_labels(client, vec![uri_pattern], sources, None).await?;
Ok(LabeledRecord { record, labels })
}