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