sui_gql_client/queries/
latest_full_objects.rs

1use af_sui_types::{Address, Object};
2use futures::Stream;
3use sui_gql_schema::scalars::Base64Bcs;
4
5use super::model::fragments::{ObjectFilter, PageInfo};
6use super::stream;
7use crate::queries::Error;
8use crate::queries::model::fragments::ObjectConnection;
9use crate::{GraphQlClient, GraphQlResponseExt, schema};
10
11#[derive(cynic::QueryVariables, Clone, Debug)]
12struct Variables {
13    filter: ObjectFilter,
14    after: Option<String>,
15    first: Option<i32>,
16}
17
18#[derive(cynic::QueryFragment, Clone, Debug)]
19#[cynic(variables = "Variables")]
20struct Query {
21    #[arguments(filter: $filter, first: $first, after: $after)]
22    objects: Option<ObjectConnection>,
23}
24
25pub(super) fn query<C: GraphQlClient>(
26    client: &C,
27    owner: Option<Address>,
28    type_: Option<String>,
29    page_size: Option<u32>,
30) -> impl Stream<Item = super::Result<Object, C>> {
31    let filter = ObjectFilter {
32        owner,
33        type_,
34        owner_kind: None,
35    };
36    let vars = Variables {
37        after: None,
38        first: page_size.map(|v| v.try_into().unwrap_or(i32::MAX)),
39        filter,
40    };
41    stream::forward(client, vars, request)
42}
43
44async fn request<C: GraphQlClient>(
45    client: &C,
46    vars: Variables,
47) -> super::Result<stream::Page<impl Iterator<Item = super::Result<Object, C>> + use<C>>, C> {
48    let data = client
49        .query::<Query, _>(vars)
50        .await
51        .map_err(Error::Client)?
52        .try_into_data()?;
53
54    let stream::Page { info, data } = extract(data)?;
55    Ok(stream::Page::new(
56        info,
57        data.map(|r| r.map_err(Error::MissingData)),
58    ))
59}
60
61fn extract(
62    data: Option<Query>,
63) -> Result<stream::Page<impl Iterator<Item = Result<Object, String>>>, &'static str> {
64    graphql_extract::extract!(data => {
65        objects? {
66            nodes[] {
67                id
68                object
69            }
70            page_info
71        }
72    });
73    let nodes = nodes
74        .into_iter()
75        .map(|r: Result<_, &'static str>| -> Result<_, String> {
76            let (id, bcs) = r?;
77            bcs.ok_or_else(|| format!("BCS for object {id}"))
78                .map(Base64Bcs::into_inner)
79        });
80    Ok(stream::Page::new(page_info, nodes))
81}
82
83impl stream::UpdatePageInfo for Variables {
84    fn update_page_info(&mut self, info: &PageInfo) {
85        self.after.clone_from(&info.end_cursor)
86    }
87}
88
89#[cfg(test)]
90#[test]
91fn gql_output() -> color_eyre::Result<()> {
92    use cynic::QueryBuilder as _;
93
94    let vars = Variables {
95        filter: ObjectFilter {
96            type_: None,
97            owner: None,
98            owner_kind: None,
99        },
100        after: None,
101        first: None,
102    };
103
104    let operation = Query::build(vars);
105    insta::assert_snapshot!(operation.query, @r"
106    query Query($filter: ObjectFilter!, $after: String, $first: Int) {
107      objects(filter: $filter, first: $first, after: $after) {
108        nodes {
109          address
110          objectBcs
111        }
112        pageInfo {
113          hasNextPage
114          endCursor
115        }
116      }
117    }
118    ");
119    Ok(())
120}