mithril_aggregator/http_server/routes/artifact_routes/
mithril_stake_distribution.rs1use crate::http_server::routes::middlewares;
2use crate::http_server::routes::router::RouterState;
3use warp::Filter;
4
5pub fn routes(
6 router_state: &RouterState,
7) -> impl Filter<Extract = (impl warp::Reply,), Error = warp::Rejection> + Clone {
8 artifact_mithril_stake_distributions(router_state)
9 .or(artifact_mithril_stake_distribution_by_id(router_state))
10}
11
12fn artifact_mithril_stake_distributions(
14 router_state: &RouterState,
15) -> impl Filter<Extract = (impl warp::Reply,), Error = warp::Rejection> + Clone {
16 warp::path!("artifact" / "mithril-stake-distributions")
17 .and(warp::get())
18 .and(middlewares::with_logger(router_state))
19 .and(middlewares::with_http_message_service(router_state))
20 .and_then(handlers::list_artifacts)
21}
22
23fn artifact_mithril_stake_distribution_by_id(
25 router_state: &RouterState,
26) -> impl Filter<Extract = (impl warp::Reply,), Error = warp::Rejection> + Clone {
27 warp::path!("artifact" / "mithril-stake-distribution" / String)
28 .and(warp::get())
29 .and(middlewares::with_logger(router_state))
30 .and(middlewares::with_http_message_service(router_state))
31 .and(middlewares::with_metrics_service(router_state))
32 .and_then(handlers::get_artifact_by_signed_entity_id)
33}
34
35pub mod handlers {
36 use crate::http_server::routes::reply;
37 use crate::services::MessageService;
38 use crate::MetricsService;
39
40 use slog::{warn, Logger};
41 use std::convert::Infallible;
42 use std::sync::Arc;
43 use warp::http::StatusCode;
44
45 pub const LIST_MAX_ITEMS: usize = 20;
46
47 pub async fn list_artifacts(
49 logger: Logger,
50 http_message_service: Arc<dyn MessageService>,
51 ) -> Result<impl warp::Reply, Infallible> {
52 match http_message_service
53 .get_mithril_stake_distribution_list_message(LIST_MAX_ITEMS)
54 .await
55 {
56 Ok(message) => Ok(reply::json(&message, StatusCode::OK)),
57 Err(err) => {
58 warn!(logger,"list_artifacts_mithril_stake_distribution"; "error" => ?err);
59 Ok(reply::server_error(err))
60 }
61 }
62 }
63
64 pub async fn get_artifact_by_signed_entity_id(
66 signed_entity_id: String,
67 logger: Logger,
68 http_message_service: Arc<dyn MessageService>,
69 metrics_service: Arc<MetricsService>,
70 ) -> Result<impl warp::Reply, Infallible> {
71 metrics_service
72 .get_artifact_detail_mithril_stake_distribution_total_served_since_startup()
73 .increment();
74
75 match http_message_service
76 .get_mithril_stake_distribution_message(&signed_entity_id)
77 .await
78 {
79 Ok(Some(message)) => Ok(reply::json(&message, StatusCode::OK)),
80 Ok(None) => {
81 warn!(logger, "get_mithril_stake_distribution_details::not_found");
82 Ok(reply::empty(StatusCode::NOT_FOUND))
83 }
84 Err(err) => {
85 warn!(logger,"get_mithril_stake_distribution_details::error"; "error" => ?err);
86 Ok(reply::server_error(err))
87 }
88 }
89 }
90}
91
92#[cfg(test)]
93pub mod tests {
94 use serde_json::Value::Null;
95 use std::sync::Arc;
96 use warp::{
97 http::{Method, StatusCode},
98 test::request,
99 };
100
101 use mithril_common::messages::{
102 MithrilStakeDistributionListItemMessage, MithrilStakeDistributionMessage,
103 };
104 use mithril_common::test_utils::apispec::APISpec;
105 use mithril_persistence::sqlite::HydrationError;
106
107 use crate::{initialize_dependencies, services::MockMessageService};
108
109 use super::*;
110
111 fn setup_router(
112 state: RouterState,
113 ) -> impl Filter<Extract = (impl warp::Reply,), Error = warp::Rejection> + Clone {
114 let cors = warp::cors()
115 .allow_any_origin()
116 .allow_headers(vec!["content-type"])
117 .allow_methods(vec![Method::GET, Method::POST, Method::OPTIONS]);
118
119 warp::any().and(routes(&state).with(cors))
120 }
121
122 #[tokio::test]
123 async fn test_mithril_stake_distributions_get_ok() {
124 let mut mock_http_message_service = MockMessageService::new();
125 mock_http_message_service
126 .expect_get_mithril_stake_distribution_list_message()
127 .return_once(|_| Ok(vec![MithrilStakeDistributionListItemMessage::dummy()]))
128 .once();
129 let mut dependency_manager = initialize_dependencies!().await;
130 dependency_manager.message_service = Arc::new(mock_http_message_service);
131
132 let method = Method::GET.as_str();
133 let path = "/artifact/mithril-stake-distributions";
134
135 let response = request()
136 .method(method)
137 .path(path)
138 .reply(&setup_router(RouterState::new_with_dummy_config(Arc::new(
139 dependency_manager,
140 ))))
141 .await;
142
143 APISpec::verify_conformity(
144 APISpec::get_all_spec_files(),
145 method,
146 path,
147 "application/json",
148 &Null,
149 &response,
150 &StatusCode::OK,
151 )
152 .unwrap();
153 }
154
155 #[tokio::test]
156 async fn test_mithril_stake_distributions_get_ko() {
157 let mut mock_http_message_service = MockMessageService::new();
158 mock_http_message_service
159 .expect_get_mithril_stake_distribution_list_message()
160 .return_once(|_| Err(HydrationError::InvalidData("invalid data".to_string()).into()))
161 .once();
162 let mut dependency_manager = initialize_dependencies!().await;
163 dependency_manager.message_service = Arc::new(mock_http_message_service);
164
165 let method = Method::GET.as_str();
166 let path = "/artifact/mithril-stake-distributions";
167
168 let response = request()
169 .method(method)
170 .path(path)
171 .reply(&setup_router(RouterState::new_with_dummy_config(Arc::new(
172 dependency_manager,
173 ))))
174 .await;
175
176 APISpec::verify_conformity(
177 APISpec::get_all_spec_files(),
178 method,
179 path,
180 "application/json",
181 &Null,
182 &response,
183 &StatusCode::INTERNAL_SERVER_ERROR,
184 )
185 .unwrap();
186 }
187
188 #[tokio::test]
189 async fn test_mithril_stake_distribution_increments_artifact_detail_total_served_since_startup_metric(
190 ) {
191 let method = Method::GET.as_str();
192 let path = "/artifact/mithril-stake-distribution/{hash}";
193 let dependency_manager = Arc::new(initialize_dependencies!().await);
194 let initial_counter_value = dependency_manager
195 .metrics_service
196 .get_artifact_detail_mithril_stake_distribution_total_served_since_startup()
197 .get();
198
199 request()
200 .method(method)
201 .path(path)
202 .reply(&setup_router(RouterState::new_with_dummy_config(
203 dependency_manager.clone(),
204 )))
205 .await;
206
207 assert_eq!(
208 initial_counter_value + 1,
209 dependency_manager
210 .metrics_service
211 .get_artifact_detail_mithril_stake_distribution_total_served_since_startup()
212 .get()
213 );
214 }
215
216 #[tokio::test]
217 async fn test_mithril_stake_distribution_get_ok() {
218 let mut mock_http_message_service = MockMessageService::new();
219 mock_http_message_service
220 .expect_get_mithril_stake_distribution_message()
221 .return_once(|_| Ok(Some(MithrilStakeDistributionMessage::dummy())))
222 .once();
223 let mut dependency_manager = initialize_dependencies!().await;
224 dependency_manager.message_service = Arc::new(mock_http_message_service);
225
226 let method = Method::GET.as_str();
227 let path = "/artifact/mithril-stake-distribution/{hash}";
228
229 let response = request()
230 .method(method)
231 .path(path)
232 .reply(&setup_router(RouterState::new_with_dummy_config(Arc::new(
233 dependency_manager,
234 ))))
235 .await;
236
237 APISpec::verify_conformity(
238 APISpec::get_all_spec_files(),
239 method,
240 path,
241 "application/json",
242 &Null,
243 &response,
244 &StatusCode::OK,
245 )
246 .unwrap();
247 }
248
249 #[tokio::test]
250 async fn test_mithril_stake_distribution_returns_404_no_found_when_no_record() {
251 let mut mock_http_message_service = MockMessageService::new();
252 mock_http_message_service
253 .expect_get_mithril_stake_distribution_message()
254 .return_once(|_| Ok(None))
255 .once();
256 let mut dependency_manager = initialize_dependencies!().await;
257 dependency_manager.message_service = Arc::new(mock_http_message_service);
258
259 let method = Method::GET.as_str();
260 let path = "/artifact/mithril-stake-distribution/{hash}";
261
262 let response = request()
263 .method(method)
264 .path(path)
265 .reply(&setup_router(RouterState::new_with_dummy_config(Arc::new(
266 dependency_manager,
267 ))))
268 .await;
269
270 APISpec::verify_conformity(
271 APISpec::get_all_spec_files(),
272 method,
273 path,
274 "application/json",
275 &Null,
276 &response,
277 &StatusCode::NOT_FOUND,
278 )
279 .unwrap();
280 }
281
282 #[tokio::test]
283 async fn test_mithril_stake_distribution_get_ko() {
284 let mut mock_http_message_service = MockMessageService::new();
285 mock_http_message_service
286 .expect_get_mithril_stake_distribution_message()
287 .return_once(|_| Err(HydrationError::InvalidData("invalid data".to_string()).into()))
288 .once();
289 let mut dependency_manager = initialize_dependencies!().await;
290 dependency_manager.message_service = Arc::new(mock_http_message_service);
291
292 let method = Method::GET.as_str();
293 let path = "/artifact/mithril-stake-distribution/{hash}";
294
295 let response = request()
296 .method(method)
297 .path(path)
298 .reply(&setup_router(RouterState::new_with_dummy_config(Arc::new(
299 dependency_manager,
300 ))))
301 .await;
302
303 APISpec::verify_conformity(
304 APISpec::get_all_spec_files(),
305 method,
306 path,
307 "application/json",
308 &Null,
309 &response,
310 &StatusCode::INTERNAL_SERVER_ERROR,
311 )
312 .unwrap();
313 }
314}