iota_sdk_graphql_client/api/
network.rs1use cynic::QueryBuilder;
8use futures::Stream;
9
10use crate::{
11 GraphQLClient,
12 api::define_query,
13 error::GraphQLResult,
14 pagination::{Page, PaginationFilter, PaginationFilterResponse},
15 query_types::{
16 ActiveValidatorsArgs, ActiveValidatorsQueryFragment, ChainIdentifierQueryFragment,
17 EpochArgs, EpochSummaryQueryFragment, ProtocolConfigQueryFragment, ProtocolConfigs,
18 ProtocolVersionArgs, Validator,
19 },
20 streams::stream_paginated_query,
21};
22
23define_query! {
24 pub struct GetChainIdQuery {
26 client: GraphQLClient,
27 }
28 output: GraphQLResult<String>;
29}
30
31impl GetChainIdQuery {
32 fn operation(&self) -> cynic::Operation<ChainIdentifierQueryFragment, ()> {
33 ChainIdentifierQueryFragment::build(())
34 }
35
36 async fn send(self) -> GraphQLResult<String> {
37 let response = self.client.run_query(&self.operation()).await?;
38
39 Ok(response.chain_identifier)
40 }
41}
42
43define_query! {
44 #[derive(Clone)]
47 pub struct ListActiveValidatorsQuery {
48 client: GraphQLClient,
49 epoch_number: Option<u64>,
50 pagination: PaginationFilter,
51 }
52 output: GraphQLResult<Page<Validator>>;
53}
54
55impl ListActiveValidatorsQuery {
56 pub fn epoch_number(mut self, epoch_number: u64) -> Self {
58 self.epoch_number = Some(epoch_number);
59 self
60 }
61
62 pub fn pagination(mut self, pagination: PaginationFilter) -> Self {
64 self.pagination = pagination;
65 self
66 }
67
68 pub fn stream(self) -> impl Stream<Item = GraphQLResult<Validator>> + Unpin {
71 let pagination = self.pagination.clone();
72 stream_paginated_query(move |page| self.clone().pagination(page).send(), pagination)
73 }
74
75 fn operation<'a>(
76 epoch_number: Option<u64>,
77 pagination: &'a PaginationFilterResponse,
78 ) -> cynic::Operation<ActiveValidatorsQueryFragment, ActiveValidatorsArgs<'a>> {
79 ActiveValidatorsQueryFragment::build(ActiveValidatorsArgs {
80 id: epoch_number,
81 after: pagination.after.as_deref(),
82 before: pagination.before.as_deref(),
83 first: pagination.first,
84 last: pagination.last,
85 })
86 }
87
88 async fn send(self) -> GraphQLResult<Page<Validator>> {
89 let Self {
90 client,
91 pagination,
92 epoch_number,
93 } = self;
94 let pagination = client.pagination_filter(pagination).await;
95 let response = client
96 .run_query(&Self::operation(epoch_number, &pagination))
97 .await?;
98
99 if let Some(validators) = response.epoch.and_then(|v| v.validator_set) {
100 let page_info = validators.active_validators.page_info;
101 Ok(Page::new(page_info, validators.active_validators.nodes))
102 } else {
103 Ok(Page::new_empty())
104 }
105 }
106}
107
108define_query! {
109 pub struct GetReferenceGasPriceQuery {
112 client: GraphQLClient,
113 epoch: Option<u64>,
114 }
115 output: GraphQLResult<Option<u64>>;
116}
117
118impl GetReferenceGasPriceQuery {
119 pub fn epoch_number(mut self, epoch_number: u64) -> Self {
121 self.epoch = Some(epoch_number);
122 self
123 }
124
125 async fn send(self) -> GraphQLResult<Option<u64>> {
126 let operation = EpochSummaryQueryFragment::build(EpochArgs { id: self.epoch });
127 let response = self.client.run_query(&operation).await?;
128
129 response
130 .epoch
131 .and_then(|e| e.reference_gas_price)
132 .map(|x| x.try_into())
133 .transpose()
134 }
135}
136
137define_query! {
138 pub struct GetProtocolConfigQuery {
141 client: GraphQLClient,
142 version: Option<u64>,
143 }
144 output: GraphQLResult<ProtocolConfigs>;
145}
146
147impl GetProtocolConfigQuery {
148 pub fn version(mut self, version: u64) -> Self {
150 self.version = Some(version);
151 self
152 }
153
154 async fn send(self) -> GraphQLResult<ProtocolConfigs> {
155 let operation =
156 ProtocolConfigQueryFragment::build(ProtocolVersionArgs { id: self.version });
157 let response = self.client.run_query(&operation).await?;
158 Ok(response.protocol_config)
159 }
160}
161
162impl GraphQLClient {
163 pub fn chain_id(&self) -> GetChainIdQuery {
165 GetChainIdQuery {
166 client: self.clone(),
167 }
168 }
169
170 pub fn reference_gas_price(&self) -> GetReferenceGasPriceQuery {
175 GetReferenceGasPriceQuery {
176 client: self.clone(),
177 epoch: None,
178 }
179 }
180
181 pub fn protocol_config(&self) -> GetProtocolConfigQuery {
183 GetProtocolConfigQuery {
184 client: self.clone(),
185 version: None,
186 }
187 }
188
189 pub fn active_validators(&self) -> ListActiveValidatorsQuery {
191 ListActiveValidatorsQuery {
192 client: self.clone(),
193 epoch_number: None,
194 pagination: PaginationFilter::default(),
195 }
196 }
197}
198
199#[cfg(all(test, not(target_arch = "wasm32")))]
200mod tests {
201 use crate::{
202 GraphQLClient,
203 test_utils::{
204 assert_backward_page, assert_forward_page, backward_page, forward_page, sent_variables,
205 test_client,
206 },
207 };
208
209 #[tokio::test]
210 async fn active_validators_sends_the_epoch_and_pagination() {
211 let vars = sent_variables("ActiveValidatorsQueryFragment", |client| async move {
212 let _ = client
213 .active_validators()
214 .epoch_number(3)
215 .pagination(backward_page())
216 .await;
217 })
218 .await;
219 assert_eq!(vars["id"], 3);
220 assert_backward_page(&vars);
221
222 let vars = sent_variables("ActiveValidatorsQueryFragment", |client| async move {
223 let _ = client
224 .active_validators()
225 .epoch_number(3)
226 .pagination(forward_page())
227 .await;
228 })
229 .await;
230 assert_forward_page(&vars);
231 }
232
233 #[test]
234 fn chain_id_builds_the_chain_identifier_operation() {
235 let operation = GraphQLClient::new_localnet()
236 .unwrap()
237 .chain_id()
238 .operation();
239 assert_eq!(
240 operation.operation_name.as_deref(),
241 Some("ChainIdentifierQueryFragment")
242 );
243 assert!(operation.query.contains("chainIdentifier"));
244 }
245
246 #[tokio::test]
247 async fn test_chain_id() {
248 let client = test_client();
249 let chain_id = client.chain_id().await.unwrap();
250 assert!(!chain_id.is_empty());
251 }
252
253 #[tokio::test]
254 async fn reference_gas_price_and_protocol_config_send_their_input() {
255 let vars = sent_variables("EpochSummaryQueryFragment", |client| async move {
256 let _ = client.reference_gas_price().epoch_number(3).await;
257 })
258 .await;
259 assert_eq!(vars["id"], 3);
260
261 let vars = sent_variables("ProtocolConfigQueryFragment", |client| async move {
262 let _ = client.protocol_config().version(50).await;
263 })
264 .await;
265 assert_eq!(vars["id"], 50);
266
267 let vars = sent_variables("EpochSummaryQueryFragment", |client| async move {
268 let _ = client.reference_gas_price().await;
269 })
270 .await;
271 assert!(vars["id"].is_null());
272
273 let vars = sent_variables("ProtocolConfigQueryFragment", |client| async move {
274 let _ = client.protocol_config().await;
275 })
276 .await;
277 assert!(vars["id"].is_null());
278 }
279
280 #[tokio::test]
281 async fn test_reference_gas_price_query() {
282 let client = test_client();
283 client
284 .reference_gas_price()
285 .await
286 .map_err(|e| {
287 format!(
288 "Reference gas price query failed for {} network: Error: {e}",
289 client.rpc_server()
290 )
291 })
292 .unwrap()
293 .unwrap();
294 }
295
296 #[tokio::test]
297 async fn test_protocol_config_query() {
298 let client = test_client();
299 client
300 .protocol_config()
301 .await
302 .map_err(|e| {
303 format!(
304 "Protocol config query failed for {} network: Error: {e}",
305 client.rpc_server()
306 )
307 })
308 .unwrap();
309
310 let pc = client
312 .protocol_config()
313 .version(50)
314 .await
315 .map_err(|e| {
316 format!(
317 "Protocol config query failed for {} network: Error: {e}",
318 client.rpc_server()
319 )
320 })
321 .unwrap();
322 assert_eq!(
323 pc.protocol_version,
324 50,
325 "Protocol version query mismatch for {} network. Expected: 50, received: {}",
326 client.rpc_server(),
327 pc.protocol_version
328 );
329 }
330
331 #[tokio::test]
332 async fn test_active_validators() {
333 let client = test_client();
334 let av = client
335 .active_validators()
336 .await
337 .map_err(|e| {
338 format!(
339 "Active validators query failed for {} network: Error: {e}",
340 client.rpc_server()
341 )
342 })
343 .unwrap();
344
345 assert!(
346 !av.is_empty(),
347 "Active validators query returned no data for {} network",
348 client.rpc_server()
349 );
350 }
351}