sui_gql_client/queries/
object_args_and_content.rs1use af_sui_types::{Address, ObjectArg, Version};
2use futures::TryStreamExt as _;
3use itertools::Itertools as _;
4use sui_gql_schema::{scalars, schema};
5
6use super::fragments::{MoveObjectContent, MoveValueRaw};
7use super::object_args::{ObjectOwner, build_oarg_set_mut};
8use super::objects_flat;
9use super::objects_flat::Variables;
10use crate::queries::fragments::ObjectFilterV2;
11use crate::queries::outputs::RawMoveStruct;
12use crate::{GraphQlClient, GraphQlErrors, GraphQlResponseExt as _};
13
14type Query = objects_flat::Query<Object>;
15
16type Res<T, C> = std::result::Result<T, Error<<C as GraphQlClient>::Error>>;
19
20#[derive(thiserror::Error, Debug)]
21pub enum Error<T> {
22 #[error(transparent)]
23 Client(T),
24 #[error(transparent)]
25 Server(#[from] GraphQlErrors),
26 #[error("No data in object args query response")]
27 NoData,
28 #[error("Missing data for object: {0}")]
29 MissingObject(Address),
30}
31
32pub async fn query<C: GraphQlClient>(
40 client: &C,
41 object_ids: impl IntoIterator<Item = Address> + Send,
42 mutable: bool,
43 page_size: Option<u32>,
44) -> Result<Vec<(ObjectArg, RawMoveStruct)>, Error<C::Error>> {
45 let object_ids = object_ids.into_iter().collect_vec();
46 let filter = ObjectFilterV2 {
47 object_ids: Some(&object_ids),
48 type_: None,
49 owner: None,
50 };
51 let vars = Variables {
52 filter: Some(filter),
53 after: None,
54 first: page_size.map(|n| n as i32),
55 };
56
57 let mut stream = std::pin::pin!(super::stream::forward(client, vars, request));
58
59 let mut result = vec![];
60 while let Some(Object {
61 object_id,
62 version,
63 digest,
64 owner,
65 as_move_object,
66 }) = stream.try_next().await?
67 {
68 let oarg = build_oarg_set_mut(object_id, version, owner, digest, mutable)
69 .ok_or_else(|| Error::MissingObject(object_id))?;
70 let content = as_move_object
71 .and_then(|c| c.into_content())
72 .ok_or_else(|| Error::MissingObject(object_id))?
73 .try_into()
74 .expect("Only Move structs can be top-level objects");
75 result.push((oarg, content));
76 }
77
78 Ok(result)
79}
80
81async fn request<C: GraphQlClient>(
82 client: &C,
83 vars: Variables<'_>,
84) -> Res<super::stream::Page<impl Iterator<Item = Res<Object, C>> + 'static + use<C>>, C> {
85 let objects = client
86 .query::<Query, _>(vars)
87 .await
88 .map_err(Error::Client)?
89 .try_into_data()?
90 .ok_or(Error::NoData)?
91 .objects;
92
93 Ok(super::stream::Page {
94 info: objects.page_info.into(),
95 data: objects.nodes.into_iter().map(Ok),
96 })
97}
98
99#[cfg(test)]
100#[allow(clippy::unwrap_used)]
101#[test]
102fn gql_output() {
103 use cynic::QueryBuilder as _;
104 let vars = Variables {
105 filter: None,
106 first: None,
107 after: None,
108 };
109 let operation = Query::build(vars);
110 insta::assert_snapshot!(operation.query, @r###"
111 query Query($filter: ObjectFilter, $after: String, $first: Int) {
112 objects(filter: $filter, first: $first, after: $after) {
113 nodes {
114 address
115 version
116 digest
117 owner {
118 __typename
119 ... on Immutable {
120 _
121 }
122 ... on Shared {
123 __typename
124 initialSharedVersion
125 }
126 ... on Parent {
127 __typename
128 }
129 ... on AddressOwner {
130 __typename
131 }
132 }
133 asMoveObject {
134 contents {
135 type {
136 repr
137 }
138 bcs
139 }
140 }
141 }
142 pageInfo {
143 hasNextPage
144 endCursor
145 }
146 }
147 }
148 "###);
149}
150
151#[derive(cynic::QueryFragment, Debug)]
156struct Object {
157 #[cynic(rename = "address")]
158 object_id: Address,
159 version: Version,
160 digest: Option<scalars::Digest>,
161 owner: Option<ObjectOwner>,
162 as_move_object: Option<MoveObjectContent<MoveValueRaw>>,
163}