use af_sui_types::{Address, Object};
use futures::Stream;
use sui_gql_schema::scalars::Base64Bcs;
use super::model::fragments::{ObjectFilter, PageInfo};
use super::stream;
use crate::queries::Error;
use crate::queries::model::fragments::ObjectConnection;
use crate::{GraphQlClient, GraphQlResponseExt, schema};
#[derive(cynic::QueryVariables, Clone, Debug)]
struct Variables {
filter: ObjectFilter,
after: Option<String>,
first: Option<i32>,
}
#[derive(cynic::QueryFragment, Clone, Debug)]
#[cynic(variables = "Variables")]
struct Query {
#[arguments(filter: $filter, first: $first, after: $after)]
objects: Option<ObjectConnection>,
}
pub(super) fn query<C: GraphQlClient>(
client: &C,
owner: Option<Address>,
type_: Option<String>,
page_size: Option<u32>,
) -> impl Stream<Item = super::Result<Object, C>> {
let filter = ObjectFilter {
owner,
type_,
owner_kind: None,
};
let vars = Variables {
after: None,
first: page_size.map(|v| v.try_into().unwrap_or(i32::MAX)),
filter,
};
stream::forward(client, vars, request)
}
async fn request<C: GraphQlClient>(
client: &C,
vars: Variables,
) -> super::Result<stream::Page<impl Iterator<Item = super::Result<Object, C>> + use<C>>, C> {
let data = client
.query::<Query, _>(vars)
.await
.map_err(Error::Client)?
.try_into_data()?;
let stream::Page { info, data } = extract(data)?;
Ok(stream::Page::new(
info,
data.map(|r| r.map_err(Error::MissingData)),
))
}
fn extract(
data: Option<Query>,
) -> Result<stream::Page<impl Iterator<Item = Result<Object, String>>>, &'static str> {
graphql_extract::extract!(data => {
objects? {
nodes[] {
id
object
}
page_info
}
});
let nodes = nodes
.into_iter()
.map(|r: Result<_, &'static str>| -> Result<_, String> {
let (id, bcs) = r?;
bcs.ok_or_else(|| format!("BCS for object {id}"))
.map(Base64Bcs::into_inner)
});
Ok(stream::Page::new(page_info, nodes))
}
impl stream::UpdatePageInfo for Variables {
fn update_page_info(&mut self, info: &PageInfo) {
self.after.clone_from(&info.end_cursor)
}
}
#[cfg(test)]
#[test]
fn gql_output() -> color_eyre::Result<()> {
use cynic::QueryBuilder as _;
let vars = Variables {
filter: ObjectFilter {
type_: None,
owner: None,
owner_kind: None,
},
after: None,
first: None,
};
let operation = Query::build(vars);
insta::assert_snapshot!(operation.query, @r"
query Query($filter: ObjectFilter!, $after: String, $first: Int) {
objects(filter: $filter, first: $first, after: $after) {
nodes {
address
objectBcs
}
pageInfo {
hasNextPage
endCursor
}
}
}
");
Ok(())
}