Skip to main content

iota_sdk_graphql_client/api/
events.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2026 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5//! Events API implementation.
6
7use 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::{Event, EventFilter, EventsQueryArgs, EventsQueryFragment},
16    streams::stream_paginated_query,
17};
18
19define_query! {
20    /// Query for [`GraphQLClient::events`]. Await it to send the request.
21    #[derive(Clone)]
22    pub struct ListEventsQuery {
23        client: GraphQLClient,
24        filter: Option<EventFilter>,
25        pagination: PaginationFilter,
26    }
27    output: GraphQLResult<Page<Event>>;
28}
29
30impl ListEventsQuery {
31    /// Only return the events that match `filter`.
32    pub fn filter(mut self, filter: EventFilter) -> Self {
33        self.filter = Some(filter);
34        self
35    }
36
37    /// Set the page to fetch.
38    pub fn pagination(mut self, pagination: PaginationFilter) -> Self {
39        self.pagination = pagination;
40        self
41    }
42
43    /// Stream every item, page by page, starting at the pagination's cursor
44    /// and in its direction, with its limit as the page size.
45    pub fn stream(self) -> impl Stream<Item = GraphQLResult<Event>> + Unpin {
46        let pagination = self.pagination.clone();
47        stream_paginated_query(move |page| self.clone().pagination(page).send(), pagination)
48    }
49
50    fn operation<'a>(
51        filter: Option<EventFilter>,
52        pagination: &'a PaginationFilterResponse,
53    ) -> cynic::Operation<EventsQueryFragment, EventsQueryArgs<'a>> {
54        EventsQueryFragment::build(EventsQueryArgs {
55            filter,
56            after: pagination.after.as_deref(),
57            before: pagination.before.as_deref(),
58            first: pagination.first,
59            last: pagination.last,
60        })
61    }
62
63    async fn send(self) -> GraphQLResult<Page<Event>> {
64        let Self {
65            client,
66            pagination,
67            filter,
68        } = self;
69        let pagination = client.pagination_filter(pagination).await;
70        let response = client
71            .run_query(&Self::operation(filter, &pagination))
72            .await?;
73
74        let ec = response.events;
75        let page_info = ec.page_info;
76
77        let events = ec.nodes;
78
79        Ok(Page::new(page_info, events))
80    }
81}
82
83impl GraphQLClient {
84    /// Return a page of events.
85    pub fn events(&self) -> ListEventsQuery {
86        ListEventsQuery {
87            client: self.clone(),
88            filter: None,
89            pagination: PaginationFilter::default(),
90        }
91    }
92}
93
94#[cfg(all(test, not(target_arch = "wasm32")))]
95mod tests {
96    use iota_types::Address;
97
98    use crate::{
99        query_types::EventFilter,
100        test_utils::{
101            assert_backward_page, assert_forward_page, backward_page, forward_page, sent_variables,
102            test_client,
103        },
104    };
105
106    #[tokio::test]
107    async fn events_sends_the_filter_and_pagination() {
108        let vars = sent_variables("EventsQueryFragment", |client| async move {
109            let _ = client
110                .events()
111                .filter(EventFilter {
112                    sender: Some(Address::FRAMEWORK),
113                    ..Default::default()
114                })
115                .pagination(backward_page())
116                .await;
117        })
118        .await;
119        assert_eq!(vars["filter"]["sender"], Address::FRAMEWORK.to_string());
120        assert_backward_page(&vars);
121
122        let vars = sent_variables("EventsQueryFragment", |client| async move {
123            let _ = client
124                .events()
125                .filter(EventFilter {
126                    sender: Some(Address::FRAMEWORK),
127                    ..Default::default()
128                })
129                .pagination(forward_page())
130                .await;
131        })
132        .await;
133        assert_forward_page(&vars);
134    }
135
136    #[tokio::test]
137    async fn test_events_query() {
138        let client = test_client();
139        let events = client
140            .events()
141            .await
142            .map_err(|e| {
143                format!(
144                    "Events query failed for {} network: Error {e}",
145                    client.rpc_server()
146                )
147            })
148            .unwrap();
149        assert!(
150            !events.is_empty(),
151            "Events query returned no data for {} network",
152            client.rpc_server()
153        );
154    }
155}