mithril_aggregator/http_server/routes/
certificate_routes.rs1use warp::Filter;
2
3use crate::http_server::routes::middlewares;
4use crate::http_server::routes::router::RouterState;
5
6pub fn routes(
7 router_state: &RouterState,
8) -> impl Filter<Extract = (impl warp::Reply,), Error = warp::Rejection> + Clone {
9 certificate_certificates(router_state).or(certificate_certificate_hash(router_state))
10}
11
12fn certificate_certificates(
14 router_state: &RouterState,
15) -> impl Filter<Extract = (impl warp::Reply,), Error = warp::Rejection> + Clone {
16 warp::path!("certificates")
17 .and(warp::get())
18 .and(middlewares::with_logger(router_state))
19 .and(middlewares::with_http_message_service(router_state))
20 .and_then(handlers::certificate_certificates)
21}
22
23fn certificate_certificate_hash(
25 router_state: &RouterState,
26) -> impl Filter<Extract = (impl warp::Reply,), Error = warp::Rejection> + Clone {
27 warp::path!("certificate" / 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::certificate_certificate_hash)
33}
34
35mod handlers {
36 use crate::MetricsService;
37 use crate::{http_server::routes::reply, services::MessageService};
38
39 use slog::{warn, Logger};
40 use std::convert::Infallible;
41 use std::sync::Arc;
42 use warp::http::StatusCode;
43
44 pub const LIST_MAX_ITEMS: usize = 20;
45
46 pub async fn certificate_certificates(
48 logger: Logger,
49 http_message_service: Arc<dyn MessageService>,
50 ) -> Result<impl warp::Reply, Infallible> {
51 match http_message_service
52 .get_certificate_list_message(LIST_MAX_ITEMS)
53 .await
54 {
55 Ok(certificates) => Ok(reply::json(&certificates, StatusCode::OK)),
56 Err(err) => {
57 warn!(logger,"certificate_certificates::error"; "error" => ?err);
58 Ok(reply::server_error(err))
59 }
60 }
61 }
62
63 pub async fn certificate_certificate_hash(
65 certificate_hash: String,
66 logger: Logger,
67 http_message_service: Arc<dyn MessageService>,
68 metrics_service: Arc<MetricsService>,
69 ) -> Result<impl warp::Reply, Infallible> {
70 metrics_service
71 .get_certificate_detail_total_served_since_startup()
72 .increment();
73
74 match http_message_service
75 .get_certificate_message(&certificate_hash)
76 .await
77 {
78 Ok(Some(certificate)) => Ok(reply::json(&certificate, StatusCode::OK)),
79 Ok(None) => Ok(reply::empty(StatusCode::NOT_FOUND)),
80 Err(err) => {
81 warn!(logger,"certificate_certificate_hash::error"; "error" => ?err);
82 Ok(reply::server_error(err))
83 }
84 }
85 }
86}
87
88#[cfg(test)]
89mod tests {
90 use anyhow::anyhow;
91 use mithril_common::test_utils::{apispec::APISpec, fake_data};
92 use serde_json::Value::Null;
93 use std::sync::Arc;
94 use warp::{
95 http::{Method, StatusCode},
96 test::request,
97 };
98
99 use crate::{initialize_dependencies, services::MockMessageService};
100
101 use super::*;
102
103 fn setup_router(
104 state: RouterState,
105 ) -> impl Filter<Extract = (impl warp::Reply,), Error = warp::Rejection> + Clone {
106 let cors = warp::cors()
107 .allow_any_origin()
108 .allow_headers(vec!["content-type"])
109 .allow_methods(vec![Method::GET, Method::POST, Method::OPTIONS]);
110
111 warp::any().and(routes(&state).with(cors))
112 }
113
114 #[tokio::test]
115 async fn test_certificate_certificates_get_ok() {
116 let dependency_manager = initialize_dependencies!().await;
117 dependency_manager
118 .certificate_repository
119 .create_certificate(fake_data::genesis_certificate("{certificate_hash}"))
120 .await
121 .expect("certificate store save should have succeeded");
122
123 let method = Method::GET.as_str();
124 let path = "/certificates";
125
126 let response = request()
127 .method(method)
128 .path(path)
129 .reply(&setup_router(RouterState::new_with_dummy_config(Arc::new(
130 dependency_manager,
131 ))))
132 .await;
133
134 APISpec::verify_conformity(
135 APISpec::get_all_spec_files(),
136 method,
137 path,
138 "application/json",
139 &Null,
140 &response,
141 &StatusCode::OK,
142 )
143 .unwrap();
144 }
145
146 #[tokio::test]
147 async fn test_certificate_when_error_retrieving_certificates_returns_ko_500() {
148 let mut dependency_manager = initialize_dependencies!().await;
149 let mut message_service = MockMessageService::new();
150 message_service
151 .expect_get_certificate_list_message()
152 .returning(|_| Err(anyhow!("an error")));
153 dependency_manager.message_service = Arc::new(message_service);
154
155 let method = Method::GET.as_str();
156 let path = "/certificates";
157
158 let response = request()
159 .method(method)
160 .path(path)
161 .reply(&setup_router(RouterState::new_with_dummy_config(Arc::new(
162 dependency_manager,
163 ))))
164 .await;
165
166 APISpec::verify_conformity(
167 APISpec::get_all_spec_files(),
168 method,
169 path,
170 "application/json",
171 &Null,
172 &response,
173 &StatusCode::INTERNAL_SERVER_ERROR,
174 )
175 .unwrap();
176 }
177
178 #[tokio::test]
179 async fn test_certificate_certificate_hash_increments_certificate_detail_total_served_since_startup_metric(
180 ) {
181 let method = Method::GET.as_str();
182 let path = "/certificate/{certificate_hash}";
183 let dependency_manager = Arc::new(initialize_dependencies!().await);
184 let initial_counter_value = dependency_manager
185 .metrics_service
186 .get_certificate_detail_total_served_since_startup()
187 .get();
188
189 request()
190 .method(method)
191 .path(path)
192 .reply(&setup_router(RouterState::new_with_dummy_config(
193 dependency_manager.clone(),
194 )))
195 .await;
196
197 assert_eq!(
198 initial_counter_value + 1,
199 dependency_manager
200 .metrics_service
201 .get_certificate_detail_total_served_since_startup()
202 .get()
203 );
204 }
205
206 #[tokio::test]
207 async fn test_certificate_certificate_hash_get_ok() {
208 let dependency_manager = initialize_dependencies!().await;
209 dependency_manager
210 .certificate_repository
211 .create_certificate(fake_data::genesis_certificate("{certificate_hash}"))
212 .await
213 .expect("certificate store save should have succeeded");
214
215 let method = Method::GET.as_str();
216 let path = "/certificate/{certificate_hash}";
217
218 let response = request()
219 .method(method)
220 .path(path)
221 .reply(&setup_router(RouterState::new_with_dummy_config(Arc::new(
222 dependency_manager,
223 ))))
224 .await;
225
226 APISpec::verify_conformity(
227 APISpec::get_all_spec_files(),
228 method,
229 path,
230 "application/json",
231 &Null,
232 &response,
233 &StatusCode::OK,
234 )
235 .unwrap();
236 }
237
238 #[tokio::test]
239 async fn test_certificate_certificate_hash_get_ok_404() {
240 let dependency_manager = initialize_dependencies!().await;
241
242 let method = Method::GET.as_str();
243 let path = "/certificate/{certificate_hash}";
244
245 let response = request()
246 .method(method)
247 .path(path)
248 .reply(&setup_router(RouterState::new_with_dummy_config(Arc::new(
249 dependency_manager,
250 ))))
251 .await;
252
253 APISpec::verify_conformity(
254 APISpec::get_all_spec_files(),
255 method,
256 path,
257 "application/json",
258 &Null,
259 &response,
260 &StatusCode::NOT_FOUND,
261 )
262 .unwrap();
263 }
264
265 #[tokio::test]
266 async fn test_certificate_when_error_on_retrieving_certificate_hash_returns_ko_500() {
267 let mut dependency_manager = initialize_dependencies!().await;
268 let mut message_service = MockMessageService::new();
269 message_service
270 .expect_get_certificate_message()
271 .returning(|_| Err(anyhow!("an error")));
272 dependency_manager.message_service = Arc::new(message_service);
273
274 let method = Method::GET.as_str();
275 let path = "/certificate/{certificate_hash}";
276
277 let response = request()
278 .method(method)
279 .path(&path.replace("{certificate_hash}", "whatever"))
280 .reply(&setup_router(RouterState::new_with_dummy_config(Arc::new(
281 dependency_manager,
282 ))))
283 .await;
284
285 APISpec::verify_conformity(
286 APISpec::get_all_spec_files(),
287 method,
288 path,
289 "application/json",
290 &Null,
291 &response,
292 &StatusCode::INTERNAL_SERVER_ERROR,
293 )
294 .unwrap();
295 }
296}