|
| 1 | +use crate::artifact_builder::{MithrilStakeDistribution, MithrilStakeDistributionSummary}; |
| 2 | +use crate::http_server::routes::middlewares; |
| 3 | +use crate::DependencyManager; |
| 4 | +use mithril_common::entities::{Beacon, Epoch, SignedEntityType, Snapshot}; |
| 5 | +use std::sync::Arc; |
| 6 | +use warp::Filter; |
| 7 | + |
| 8 | +pub fn routes( |
| 9 | + dependency_manager: Arc<DependencyManager>, |
| 10 | +) -> impl Filter<Extract = (impl warp::Reply,), Error = warp::Rejection> + Clone { |
| 11 | + artifact_mithril_stake_distributions(dependency_manager.clone()) |
| 12 | + .or(artifact_mithril_stake_distribution_by_id( |
| 13 | + dependency_manager.clone(), |
| 14 | + )) |
| 15 | + .or(artifact_cardano_full_immutable_snapshots( |
| 16 | + dependency_manager.clone(), |
| 17 | + )) |
| 18 | + .or(artifact_cardano_full_immutable_snapshot_by_id( |
| 19 | + dependency_manager, |
| 20 | + )) |
| 21 | +} |
| 22 | + |
| 23 | +/// GET /artifact/mithril-stake-distributions |
| 24 | +fn artifact_mithril_stake_distributions( |
| 25 | + dependency_manager: Arc<DependencyManager>, |
| 26 | +) -> impl Filter<Extract = (impl warp::Reply,), Error = warp::Rejection> + Clone { |
| 27 | + warp::path!("artifact" / "mithril-stake-distributions") |
| 28 | + .and(warp::get()) |
| 29 | + .and(middlewares::with_signed_entity_storer(dependency_manager)) |
| 30 | + .and(middlewares::with_signed_entity_type( |
| 31 | + SignedEntityType::MithrilStakeDistribution(Epoch::default()), |
| 32 | + )) |
| 33 | + .and_then(handlers::artifacts_by_signed_entity_type::<MithrilStakeDistributionSummary>) |
| 34 | +} |
| 35 | + |
| 36 | +/// GET /artifact/mithril-stake-distribution/:id |
| 37 | +fn artifact_mithril_stake_distribution_by_id( |
| 38 | + dependency_manager: Arc<DependencyManager>, |
| 39 | +) -> impl Filter<Extract = (impl warp::Reply,), Error = warp::Rejection> + Clone { |
| 40 | + warp::path!("artifact" / "mithril-stake-distribution" / String) |
| 41 | + .and(warp::get()) |
| 42 | + .and(middlewares::with_signed_entity_storer(dependency_manager)) |
| 43 | + .and_then(handlers::artifact_by_signed_entity_id::<MithrilStakeDistribution>) |
| 44 | +} |
| 45 | + |
| 46 | +/// GET /artifact/snapshots |
| 47 | +fn artifact_cardano_full_immutable_snapshots( |
| 48 | + dependency_manager: Arc<DependencyManager>, |
| 49 | +) -> impl Filter<Extract = (impl warp::Reply,), Error = warp::Rejection> + Clone { |
| 50 | + warp::path!("artifact" / "snapshots") |
| 51 | + .and(warp::get()) |
| 52 | + .and(middlewares::with_signed_entity_storer(dependency_manager)) |
| 53 | + .and(middlewares::with_signed_entity_type( |
| 54 | + SignedEntityType::CardanoImmutableFilesFull(Beacon::default()), |
| 55 | + )) |
| 56 | + .and_then(handlers::artifacts_by_signed_entity_type::<Snapshot>) |
| 57 | +} |
| 58 | + |
| 59 | +/// GET /artifact/snapshot/:id |
| 60 | +fn artifact_cardano_full_immutable_snapshot_by_id( |
| 61 | + dependency_manager: Arc<DependencyManager>, |
| 62 | +) -> impl Filter<Extract = (impl warp::Reply,), Error = warp::Rejection> + Clone { |
| 63 | + warp::path!("artifact" / "snapshot" / String) |
| 64 | + .and(warp::get()) |
| 65 | + .and(middlewares::with_signed_entity_storer(dependency_manager)) |
| 66 | + .and_then(handlers::artifact_by_signed_entity_id::<Snapshot>) |
| 67 | +} |
| 68 | + |
| 69 | +mod handlers { |
| 70 | + use crate::database::provider::SignedEntityStorer; |
| 71 | + use crate::http_server::routes::reply; |
| 72 | + use mithril_common::entities::SignedEntityType; |
| 73 | + use serde::{Deserialize, Serialize}; |
| 74 | + use slog_scope::{debug, warn}; |
| 75 | + use std::convert::Infallible; |
| 76 | + use std::sync::Arc; |
| 77 | + use warp::http::StatusCode; |
| 78 | + |
| 79 | + pub const LIST_MAX_ITEMS: usize = 20; |
| 80 | + |
| 81 | + /// Artifacts by signed entity type |
| 82 | + pub async fn artifacts_by_signed_entity_type<T: Serialize + for<'a> Deserialize<'a>>( |
| 83 | + signed_entity_storer: Arc<dyn SignedEntityStorer>, |
| 84 | + signed_entity_type: SignedEntityType, |
| 85 | + ) -> Result<impl warp::Reply, Infallible> { |
| 86 | + debug!("⇄ HTTP SERVER: artifacts"); |
| 87 | + |
| 88 | + match signed_entity_storer |
| 89 | + .get_last_signed_entities_by_type(&signed_entity_type, LIST_MAX_ITEMS) |
| 90 | + .await |
| 91 | + { |
| 92 | + Ok(signed_entities) => { |
| 93 | + let mut artifacts = Vec::new(); |
| 94 | + for signed_entity in signed_entities { |
| 95 | + if let Ok(artifact) = serde_json::from_str::<T>(&signed_entity.artifact) { |
| 96 | + artifacts.push(artifact) |
| 97 | + } |
| 98 | + } |
| 99 | + Ok(reply::json(&artifacts, StatusCode::OK)) |
| 100 | + } |
| 101 | + Err(err) => { |
| 102 | + warn!("artifacts_by_signed_entity_type"; "signed_entity_type" => ?signed_entity_type, "error" => ?err); |
| 103 | + Ok(reply::internal_server_error(err.to_string())) |
| 104 | + } |
| 105 | + } |
| 106 | + } |
| 107 | + |
| 108 | + /// Artifact by signed entity id |
| 109 | + pub async fn artifact_by_signed_entity_id<T: Serialize + for<'a> Deserialize<'a>>( |
| 110 | + signed_entity_id: String, |
| 111 | + signed_entity_storer: Arc<dyn SignedEntityStorer>, |
| 112 | + ) -> Result<impl warp::Reply, Infallible> { |
| 113 | + debug!("⇄ HTTP SERVER: artifact/{signed_entity_id}"); |
| 114 | + |
| 115 | + match signed_entity_storer |
| 116 | + .get_signed_entity(signed_entity_id.clone()) |
| 117 | + .await |
| 118 | + { |
| 119 | + Ok(signed_entity) => match signed_entity { |
| 120 | + Some(signed_entity) => match serde_json::from_str::<T>(&signed_entity.artifact) { |
| 121 | + Ok(artifact) => Ok(reply::json(&artifact, StatusCode::OK)), |
| 122 | + Err(err) => Ok(reply::internal_server_error(err.to_string())), |
| 123 | + }, |
| 124 | + None => Ok(reply::empty(StatusCode::NOT_FOUND)), |
| 125 | + }, |
| 126 | + Err(err) => { |
| 127 | + warn!("artifact_by_signed_entity_id"; "signed_entity_id" => ?signed_entity_id, "error" => ?err); |
| 128 | + Ok(reply::internal_server_error(err.to_string())) |
| 129 | + } |
| 130 | + } |
| 131 | + } |
| 132 | +} |
| 133 | + |
| 134 | +/* #[cfg(test)] |
| 135 | +mod tests { |
| 136 | + use crate::http_server::SERVER_BASE_PATH; |
| 137 | + use mithril_common::test_utils::apispec::APISpec; |
| 138 | + use mithril_common::test_utils::fake_data; |
| 139 | + use serde_json::Value::Null; |
| 140 | +
|
| 141 | + use crate::initialize_dependencies; |
| 142 | + use warp::http::Method; |
| 143 | + use warp::test::request; |
| 144 | +
|
| 145 | + use super::*; |
| 146 | + use crate::snapshot_stores::{MockSnapshotStore, SnapshotStoreError}; |
| 147 | +
|
| 148 | + fn setup_router( |
| 149 | + dependency_manager: Arc<DependencyManager>, |
| 150 | + ) -> impl Filter<Extract = (impl warp::Reply,), Error = warp::Rejection> + Clone { |
| 151 | + let cors = warp::cors() |
| 152 | + .allow_any_origin() |
| 153 | + .allow_headers(vec!["content-type"]) |
| 154 | + .allow_methods(vec![Method::GET, Method::POST, Method::OPTIONS]); |
| 155 | +
|
| 156 | + warp::any() |
| 157 | + .and(warp::path(SERVER_BASE_PATH)) |
| 158 | + .and(routes(dependency_manager).with(cors)) |
| 159 | + } |
| 160 | +
|
| 161 | + #[tokio::test] |
| 162 | + async fn test_snapshots_get_ok() { |
| 163 | + let fake_snapshots = fake_data::snapshots(5); |
| 164 | + let mut mock_snapshot_store = MockSnapshotStore::new(); |
| 165 | + mock_snapshot_store |
| 166 | + .expect_list_snapshots() |
| 167 | + .return_const(Ok(fake_snapshots)) |
| 168 | + .once(); |
| 169 | + let mut dependency_manager = initialize_dependencies().await; |
| 170 | + dependency_manager.snapshot_store = Arc::new(mock_snapshot_store); |
| 171 | +
|
| 172 | + let method = Method::GET.as_str(); |
| 173 | + let path = "/snapshots"; |
| 174 | +
|
| 175 | + let response = request() |
| 176 | + .method(method) |
| 177 | + .path(&format!("/{SERVER_BASE_PATH}{path}")) |
| 178 | + .reply(&setup_router(Arc::new(dependency_manager))) |
| 179 | + .await; |
| 180 | +
|
| 181 | + APISpec::verify_conformity( |
| 182 | + APISpec::get_all_spec_files(), |
| 183 | + method, |
| 184 | + path, |
| 185 | + "application/json", |
| 186 | + &Null, |
| 187 | + &response, |
| 188 | + ); |
| 189 | + } |
| 190 | +
|
| 191 | + #[tokio::test] |
| 192 | + async fn test_snapshots_get_ko() { |
| 193 | + let mut mock_snapshot_store = MockSnapshotStore::new(); |
| 194 | + mock_snapshot_store |
| 195 | + .expect_list_snapshots() |
| 196 | + .return_const(Err(SnapshotStoreError::Manifest( |
| 197 | + "an error occurred".to_string(), |
| 198 | + ))) |
| 199 | + .once(); |
| 200 | + let mut dependency_manager = initialize_dependencies().await; |
| 201 | + dependency_manager.snapshot_store = Arc::new(mock_snapshot_store); |
| 202 | +
|
| 203 | + let method = Method::GET.as_str(); |
| 204 | + let path = "/snapshots"; |
| 205 | +
|
| 206 | + let response = request() |
| 207 | + .method(method) |
| 208 | + .path(&format!("/{SERVER_BASE_PATH}{path}")) |
| 209 | + .reply(&setup_router(Arc::new(dependency_manager))) |
| 210 | + .await; |
| 211 | +
|
| 212 | + APISpec::verify_conformity( |
| 213 | + APISpec::get_all_spec_files(), |
| 214 | + method, |
| 215 | + path, |
| 216 | + "application/json", |
| 217 | + &Null, |
| 218 | + &response, |
| 219 | + ); |
| 220 | + } |
| 221 | +
|
| 222 | + #[tokio::test] |
| 223 | + async fn test_snapshot_digest_get_ok() { |
| 224 | + let fake_snapshot = fake_data::snapshots(1).first().unwrap().to_owned(); |
| 225 | + let mut mock_snapshot_store = MockSnapshotStore::new(); |
| 226 | + mock_snapshot_store |
| 227 | + .expect_get_snapshot_details() |
| 228 | + .return_const(Ok(Some(fake_snapshot))) |
| 229 | + .once(); |
| 230 | + let mut dependency_manager = initialize_dependencies().await; |
| 231 | + dependency_manager.snapshot_store = Arc::new(mock_snapshot_store); |
| 232 | +
|
| 233 | + let method = Method::GET.as_str(); |
| 234 | + let path = "/snapshot/{digest}"; |
| 235 | +
|
| 236 | + let response = request() |
| 237 | + .method(method) |
| 238 | + .path(&format!("/{SERVER_BASE_PATH}{path}")) |
| 239 | + .reply(&setup_router(Arc::new(dependency_manager))) |
| 240 | + .await; |
| 241 | +
|
| 242 | + APISpec::verify_conformity( |
| 243 | + APISpec::get_all_spec_files(), |
| 244 | + method, |
| 245 | + path, |
| 246 | + "application/json", |
| 247 | + &Null, |
| 248 | + &response, |
| 249 | + ); |
| 250 | + } |
| 251 | +
|
| 252 | + #[tokio::test] |
| 253 | + async fn test_snapshot_digest_get_ok_nosnapshot() { |
| 254 | + let mut mock_snapshot_store = MockSnapshotStore::new(); |
| 255 | + mock_snapshot_store |
| 256 | + .expect_get_snapshot_details() |
| 257 | + .return_const(Ok(None)) |
| 258 | + .once(); |
| 259 | + let mut dependency_manager = initialize_dependencies().await; |
| 260 | + dependency_manager.snapshot_store = Arc::new(mock_snapshot_store); |
| 261 | +
|
| 262 | + let method = Method::GET.as_str(); |
| 263 | + let path = "/snapshot/{digest}"; |
| 264 | +
|
| 265 | + let response = request() |
| 266 | + .method(method) |
| 267 | + .path(&format!("/{SERVER_BASE_PATH}{path}")) |
| 268 | + .reply(&setup_router(Arc::new(dependency_manager))) |
| 269 | + .await; |
| 270 | +
|
| 271 | + APISpec::verify_conformity( |
| 272 | + APISpec::get_all_spec_files(), |
| 273 | + method, |
| 274 | + path, |
| 275 | + "application/json", |
| 276 | + &Null, |
| 277 | + &response, |
| 278 | + ); |
| 279 | + } |
| 280 | +
|
| 281 | + #[tokio::test] |
| 282 | + async fn test_snapshot_digest_get_ko() { |
| 283 | + let mut mock_snapshot_store = MockSnapshotStore::new(); |
| 284 | + mock_snapshot_store |
| 285 | + .expect_get_snapshot_details() |
| 286 | + .return_const(Err(SnapshotStoreError::Manifest( |
| 287 | + "an error occurred".to_string(), |
| 288 | + ))) |
| 289 | + .once(); |
| 290 | + let mut dependency_manager = initialize_dependencies().await; |
| 291 | + dependency_manager.snapshot_store = Arc::new(mock_snapshot_store); |
| 292 | +
|
| 293 | + let method = Method::GET.as_str(); |
| 294 | + let path = "/snapshot/{digest}"; |
| 295 | +
|
| 296 | + let response = request() |
| 297 | + .method(method) |
| 298 | + .path(&format!("/{SERVER_BASE_PATH}{path}")) |
| 299 | + .reply(&setup_router(Arc::new(dependency_manager))) |
| 300 | + .await; |
| 301 | +
|
| 302 | + APISpec::verify_conformity( |
| 303 | + APISpec::get_all_spec_files(), |
| 304 | + method, |
| 305 | + path, |
| 306 | + "application/json", |
| 307 | + &Null, |
| 308 | + &response, |
| 309 | + ); |
| 310 | + } |
| 311 | +} */ |
0 commit comments