mithril_aggregator/http_server/routes/
root_routes.rs

1use super::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    root(router_state)
9}
10
11/// GET /
12fn root(
13    router_state: &RouterState,
14) -> impl Filter<Extract = (impl warp::Reply,), Error = warp::Rejection> + Clone {
15    warp::path::end()
16        .and(middlewares::with_logger(router_state))
17        .and(middlewares::with_api_version_provider(router_state))
18        .and(middlewares::extract_config(router_state, |config| {
19            config.allowed_discriminants.clone()
20        }))
21        .and(middlewares::extract_config(router_state, |config| {
22            config.cardano_transactions_prover_max_hashes_allowed_by_request
23        }))
24        .and_then(handlers::root)
25}
26
27mod handlers {
28    use std::collections::BTreeSet;
29    use std::{convert::Infallible, sync::Arc};
30
31    use slog::Logger;
32    use warp::http::StatusCode;
33
34    use mithril_common::api_version::APIVersionProvider;
35    use mithril_common::entities::SignedEntityTypeDiscriminants;
36    use mithril_common::messages::{
37        AggregatorCapabilities, AggregatorFeaturesMessage, CardanoTransactionsProverCapabilities,
38    };
39
40    use crate::http_server::routes::reply::json;
41    use crate::unwrap_to_internal_server_error;
42
43    /// Root
44    pub async fn root(
45        logger: Logger,
46        api_version_provider: Arc<APIVersionProvider>,
47        allowed_signed_entity_type_discriminants: BTreeSet<SignedEntityTypeDiscriminants>,
48        max_hashes_allowed_by_request: usize,
49    ) -> Result<impl warp::Reply, Infallible> {
50        let open_api_version = unwrap_to_internal_server_error!(
51            api_version_provider.compute_current_version(),
52            logger => "root::error"
53        );
54
55        let mut capabilities = AggregatorCapabilities {
56            signed_entity_types: allowed_signed_entity_type_discriminants,
57            cardano_transactions_prover: None,
58        };
59
60        if capabilities
61            .signed_entity_types
62            .contains(&SignedEntityTypeDiscriminants::CardanoTransactions)
63        {
64            capabilities.cardano_transactions_prover =
65                Some(CardanoTransactionsProverCapabilities {
66                    max_hashes_allowed_by_request,
67                });
68        }
69
70        Ok(json(
71            &AggregatorFeaturesMessage {
72                open_api_version: open_api_version.to_string(),
73                documentation_url: env!("CARGO_PKG_HOMEPAGE").to_string(),
74                capabilities,
75            },
76            StatusCode::OK,
77        ))
78    }
79}
80
81#[cfg(test)]
82mod tests {
83    use crate::http_server::routes::router::RouterConfig;
84    use crate::initialize_dependencies;
85    use mithril_common::entities::SignedEntityTypeDiscriminants;
86    use mithril_common::messages::{
87        AggregatorCapabilities, AggregatorFeaturesMessage, CardanoTransactionsProverCapabilities,
88    };
89    use mithril_common::test_utils::apispec::APISpec;
90    use serde_json::Value::Null;
91    use std::collections::BTreeSet;
92    use std::sync::Arc;
93    use warp::http::Method;
94    use warp::http::StatusCode;
95    use warp::test::request;
96    use warp::Filter;
97
98    use super::*;
99
100    fn setup_router(
101        state: RouterState,
102    ) -> impl Filter<Extract = (impl warp::Reply,), Error = warp::Rejection> + Clone {
103        let cors = warp::cors()
104            .allow_any_origin()
105            .allow_headers(vec!["content-type"])
106            .allow_methods(vec![Method::GET, Method::POST, Method::OPTIONS]);
107
108        warp::any().and(routes(&state).with(cors))
109    }
110
111    #[tokio::test]
112    async fn test_root_route_ok() {
113        let method = Method::GET.as_str();
114        let path = "/";
115        let config = RouterConfig {
116            allowed_discriminants: BTreeSet::from([
117                SignedEntityTypeDiscriminants::CardanoStakeDistribution,
118                SignedEntityTypeDiscriminants::CardanoImmutableFilesFull,
119                SignedEntityTypeDiscriminants::MithrilStakeDistribution,
120            ]),
121            ..RouterConfig::dummy()
122        };
123        let dependency_manager = initialize_dependencies!().await;
124
125        let expected_open_api_version = dependency_manager
126            .api_version_provider
127            .clone()
128            .compute_current_version()
129            .unwrap()
130            .to_string();
131
132        let response = request()
133            .method(method)
134            .path(path)
135            .reply(&setup_router(RouterState::new(
136                Arc::new(dependency_manager),
137                config,
138            )))
139            .await;
140
141        let response_body: AggregatorFeaturesMessage =
142            serde_json::from_slice(response.body()).unwrap();
143
144        assert_eq!(response.status(), StatusCode::OK);
145
146        assert_eq!(
147            response_body,
148            AggregatorFeaturesMessage {
149                open_api_version: expected_open_api_version,
150                documentation_url: env!("CARGO_PKG_HOMEPAGE").to_string(),
151                capabilities: AggregatorCapabilities {
152                    signed_entity_types: BTreeSet::from_iter([
153                        SignedEntityTypeDiscriminants::CardanoStakeDistribution,
154                        SignedEntityTypeDiscriminants::CardanoImmutableFilesFull,
155                        SignedEntityTypeDiscriminants::MithrilStakeDistribution,
156                    ]),
157                    cardano_transactions_prover: None,
158                },
159            }
160        );
161
162        APISpec::verify_conformity(
163            APISpec::get_all_spec_files(),
164            method,
165            path,
166            "application/json",
167            &Null,
168            &response,
169            &StatusCode::OK,
170        )
171        .unwrap();
172    }
173
174    #[tokio::test]
175    async fn test_root_route_ok_with_cardano_transactions_enabled() {
176        let method = Method::GET.as_str();
177        let path = "/";
178        let config = RouterConfig {
179            allowed_discriminants: BTreeSet::from([
180                SignedEntityTypeDiscriminants::CardanoTransactions,
181            ]),
182            cardano_transactions_prover_max_hashes_allowed_by_request: 99,
183            ..RouterConfig::dummy()
184        };
185        let dependency_manager = initialize_dependencies!().await;
186
187        let response = request()
188            .method(method)
189            .path(path)
190            .reply(&setup_router(RouterState::new(
191                Arc::new(dependency_manager),
192                config,
193            )))
194            .await;
195
196        let response_body: AggregatorFeaturesMessage =
197            serde_json::from_slice(response.body()).unwrap();
198
199        assert_eq!(response.status(), StatusCode::OK);
200
201        assert_eq!(
202            response_body.capabilities.cardano_transactions_prover,
203            Some(CardanoTransactionsProverCapabilities {
204                max_hashes_allowed_by_request: 99
205            })
206        );
207
208        APISpec::verify_conformity(
209            APISpec::get_all_spec_files(),
210            method,
211            path,
212            "application/json",
213            &Null,
214            &response,
215            &StatusCode::OK,
216        )
217        .unwrap();
218    }
219}