sui_gql_client/queries/
object_dfs.rs1use af_sui_types::Address as SuiAddress;
2use futures_core::Stream;
3
4use super::model::fragments;
5use super::model::outputs::{DynamicField as OutputDf, RawMoveValue};
6use super::{Error, stream};
7use crate::queries::model::fragments::{
8 DynamicField,
9 DynamicFieldConnection,
10 DynamicFieldValue,
11 MoveObject,
12 ObjectKey,
13};
14use crate::{GraphQlClient, GraphQlResponseExt as _, missing_data, schema};
15
16#[derive(cynic::QueryVariables, Debug, Clone)]
17struct Variables {
18 address: SuiAddress,
19 at_checkpoint: Option<u64>,
20 after: Option<String>,
21 first: Option<i32>,
22}
23
24#[derive(cynic::QueryFragment, Debug)]
25#[cynic(variables = "Variables")]
26struct Query {
27 #[arguments(address: $address, atCheckpoint: $at_checkpoint)]
28 pub object: Option<ObjectDfs>,
29}
30
31#[derive(cynic::QueryFragment, Debug)]
32#[cynic(graphql_type = "Object", variables = "Variables")]
33struct ObjectDfs {
34 #[arguments(first: $first, after: $after)]
35 pub dynamic_fields: Option<DynamicFieldConnection>,
36}
37
38pub async fn query<C: GraphQlClient>(
39 client: &C,
40 address: SuiAddress,
41 at_checkpoint: Option<u64>,
42 page_size: Option<i32>,
43) -> impl Stream<Item = super::Result<(RawMoveValue, OutputDf), C>> + '_ {
44 let vars = Variables {
45 address,
46 at_checkpoint,
47 first: page_size,
48 after: None,
49 };
50
51 stream::forward(client, vars, request)
52}
53
54async fn request<C: GraphQlClient>(
55 client: &C,
56 vars: Variables,
57) -> super::Result<
58 stream::Page<
59 impl Iterator<Item = super::Result<(RawMoveValue, OutputDf), C>> + 'static + use<C>,
60 >,
61 C,
62> {
63 let at_checkpoint = vars.at_checkpoint;
64 let data = client
65 .query::<Query, _>(vars)
66 .await
67 .map_err(Error::Client)?
68 .try_into_data()?
69 .ok_or(missing_data!("Response empty"))?;
70
71 let DynamicFieldConnection { nodes, page_info } = data
72 .object
73 .ok_or(missing_data!("No parent object found"))?
74 .dynamic_fields
75 .ok_or(missing_data!("No dynamic fields found"))?;
76
77 let data = nodes.into_iter().map(move |DynamicField { name, value }| {
78 let name = name
79 .ok_or(missing_data!("Dynamic field found but with no name"))?
80 .try_into()
81 .map_err(|e| missing_data!("Dynamic field name content empty. Error: {e}"))?;
82 let instance = value.ok_or(missing_data!("Dynamic field found but with no value"))?;
83 let out = match instance {
84 DynamicFieldValue::MoveObject(MoveObject {
85 address,
86 version,
87 contents,
88 }) => {
89 let struct_ = contents
90 .ok_or(missing_data!("No contents for DF"))?
91 .try_into()
92 .expect("Only Move structs can be top-level objects");
93 OutputDf::Object(
94 ObjectKey {
95 version,
96 address,
97 root_version: None,
98 at_checkpoint,
99 },
100 struct_,
101 )
102 }
103 DynamicFieldValue::MoveValue(value) => OutputDf::Field(
104 value
105 .try_into()
106 .map_err(|e| missing_data!("Dynamic field name content empty. Error: {e}"))?,
107 ),
108 DynamicFieldValue::Unknown => return Err(missing_data!("Unknown dynamic field type")),
109 };
110 Ok((name, out))
111 });
112
113 Ok(stream::Page {
114 info: page_info,
115 data,
116 })
117}
118
119#[cfg(test)]
120#[allow(clippy::unwrap_used)]
121#[test]
122fn gql_output() {
123 use cynic::QueryBuilder as _;
124 let vars = Variables {
125 address: SuiAddress::new(rand::random()),
126 at_checkpoint: None,
127 first: None,
128 after: None,
129 };
130 let operation = Query::build(vars);
131 insta::assert_snapshot!(operation.query, @r"
132 query Query($address: SuiAddress!, $atCheckpoint: UInt53, $after: String, $first: Int) {
133 object(address: $address, atCheckpoint: $atCheckpoint) {
134 dynamicFields(first: $first, after: $after) {
135 nodes {
136 name {
137 type {
138 repr
139 }
140 bcs
141 }
142 value {
143 __typename
144 ... on MoveObject {
145 address
146 version
147 contents {
148 type {
149 repr
150 }
151 bcs
152 }
153 }
154 ... on MoveValue {
155 type {
156 repr
157 }
158 bcs
159 }
160 }
161 }
162 pageInfo {
163 hasNextPage
164 endCursor
165 hasPreviousPage
166 startCursor
167 }
168 }
169 }
170 }
171 ");
172}
173
174impl stream::UpdatePageInfo for Variables {
175 fn update_page_info(&mut self, info: &fragments::PageInfo) {
176 self.after.clone_from(&info.end_cursor)
177 }
178}