mithril_aggregator_client/query/get/
get_cardano_database_list.rs

1use async_trait::async_trait;
2use reqwest::StatusCode;
3use std::fmt::{Display, Formatter};
4
5use mithril_common::entities::EpochSpecifier;
6use mithril_common::messages::CardanoDatabaseSnapshotListMessage;
7
8use crate::AggregatorHttpClientResult;
9use crate::query::{AggregatorQuery, QueryContext, QueryMethod, ResponseExt};
10
11/// Query to get a list of Cardano database v2 snapshots
12pub struct GetCardanoDatabaseListQuery {
13    scope: ListScope,
14}
15
16enum ListScope {
17    /// Fetch the latest list of Cardano database v2 snapshots, regardless of their epochs.
18    Latest,
19    /// Fetch the list of Cardano database v2 snapshots for a specified epoch
20    Epoch(EpochSpecifier),
21}
22
23impl Display for ListScope {
24    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
25        match &self {
26            ListScope::Latest => {
27                write!(f, "latest")
28            }
29            ListScope::Epoch(specifier) => {
30                write!(f, "epoch({specifier})")
31            }
32        }
33    }
34}
35
36impl GetCardanoDatabaseListQuery {
37    /// Instantiate a query to get the latest Cardano database v2 snapshots list
38    pub fn latest() -> Self {
39        Self {
40            scope: ListScope::Latest,
41        }
42    }
43
44    /// Instantiate a query to get the Cardano database v2 snapshots list for a specified epoch
45    pub fn for_epoch(epoch_specifier: EpochSpecifier) -> Self {
46        Self {
47            scope: ListScope::Epoch(epoch_specifier),
48        }
49    }
50}
51
52#[cfg_attr(target_family = "wasm", async_trait(?Send))]
53#[cfg_attr(not(target_family = "wasm"), async_trait)]
54impl AggregatorQuery for GetCardanoDatabaseListQuery {
55    type Response = CardanoDatabaseSnapshotListMessage;
56    type Body = ();
57
58    fn method() -> QueryMethod {
59        QueryMethod::Get
60    }
61
62    fn route(&self) -> String {
63        match &self.scope {
64            ListScope::Latest => "artifact/cardano-database".to_string(),
65            ListScope::Epoch(specifier) => {
66                format!("artifact/cardano-database/epoch/{specifier}")
67            }
68        }
69    }
70
71    async fn handle_response(
72        &self,
73        context: QueryContext,
74    ) -> AggregatorHttpClientResult<Self::Response> {
75        match context.response.status() {
76            StatusCode::OK => context.response.parse_json().await,
77            _ => Err(context.unhandled_status_code().await),
78        }
79    }
80}
81
82#[cfg(test)]
83mod tests {
84    use serde_json::json;
85
86    use mithril_common::entities::Epoch;
87    use mithril_common::messages::CardanoDatabaseSnapshotListItemMessage;
88    use mithril_common::test::double::Dummy;
89
90    use crate::AggregatorHttpClientError;
91    use crate::test::{assert_error_matches, setup_server_and_client};
92
93    use super::*;
94
95    #[tokio::test]
96    async fn test_latest_cardano_database_list_ok_200() {
97        let (server, client) = setup_server_and_client();
98        let expected_list = vec![
99            CardanoDatabaseSnapshotListItemMessage::dummy(),
100            CardanoDatabaseSnapshotListItemMessage::dummy(),
101        ];
102        let _server_mock = server.mock(|when, then| {
103            when.path("/artifact/cardano-database");
104            then.status(200).body(json!(expected_list).to_string());
105        });
106
107        let fetched_list = client.send(GetCardanoDatabaseListQuery::latest()).await.unwrap();
108
109        assert_eq!(expected_list, fetched_list);
110    }
111
112    #[tokio::test]
113    async fn test_specific_epoch_cardano_database_list_ok_200() {
114        let (server, client) = setup_server_and_client();
115        let expected_list = vec![
116            CardanoDatabaseSnapshotListItemMessage::dummy(),
117            CardanoDatabaseSnapshotListItemMessage::dummy(),
118        ];
119        let _server_mock = server.mock(|when, then| {
120            when.path("/artifact/cardano-database/epoch/12");
121            then.status(200).body(json!(expected_list).to_string());
122        });
123
124        let fetched_list = client
125            .send(GetCardanoDatabaseListQuery::for_epoch(
126                EpochSpecifier::Number(Epoch(12)),
127            ))
128            .await
129            .unwrap();
130
131        assert_eq!(expected_list, fetched_list);
132    }
133
134    #[tokio::test]
135    async fn test_latest_epoch_cardano_database_list_ok_200() {
136        let (server, client) = setup_server_and_client();
137        let expected_list = vec![
138            CardanoDatabaseSnapshotListItemMessage::dummy(),
139            CardanoDatabaseSnapshotListItemMessage::dummy(),
140        ];
141        let _server_mock = server.mock(|when, then| {
142            when.path("/artifact/cardano-database/epoch/latest");
143            then.status(200).body(json!(expected_list).to_string());
144        });
145
146        let fetched_list = client
147            .send(GetCardanoDatabaseListQuery::for_epoch(
148                EpochSpecifier::Latest,
149            ))
150            .await
151            .unwrap();
152
153        assert_eq!(expected_list, fetched_list);
154    }
155
156    #[tokio::test]
157    async fn test_latest_epoch_with_offset_cardano_database_list_ok_200() {
158        let (server, client) = setup_server_and_client();
159        let expected_list = vec![
160            CardanoDatabaseSnapshotListItemMessage::dummy(),
161            CardanoDatabaseSnapshotListItemMessage::dummy(),
162        ];
163        let _server_mock = server.mock(|when, then| {
164            when.path("/artifact/cardano-database/epoch/latest-3");
165            then.status(200).body(json!(expected_list).to_string());
166        });
167
168        let fetched_list = client
169            .send(GetCardanoDatabaseListQuery::for_epoch(
170                EpochSpecifier::LatestMinusOffset(3),
171            ))
172            .await
173            .unwrap();
174
175        assert_eq!(expected_list, fetched_list);
176    }
177
178    #[tokio::test]
179    async fn test_cardano_database_list_ko_500() {
180        let (server, client) = setup_server_and_client();
181        let _server_mock = server.mock(|when, then| {
182            when.any_request();
183            then.status(500).body("an error occurred");
184        });
185
186        let error = client.send(GetCardanoDatabaseListQuery::latest()).await.unwrap_err();
187
188        assert_error_matches!(error, AggregatorHttpClientError::RemoteServerTechnical(_));
189    }
190}