diff --git a/editoast/src/views/infra/delimited_area.rs b/editoast/src/views/infra/delimited_area.rs index 00b3af531bb..b1413d7d6fa 100644 --- a/editoast/src/views/infra/delimited_area.rs +++ b/editoast/src/views/infra/delimited_area.rs @@ -1,4 +1,4 @@ -use crate::error::{EditoastError, InternalError, Result}; +use crate::error::Result; use crate::infra_cache::{Graph, InfraCache}; use crate::models::Infra; use crate::views::infra::{InfraApiError, InfraIdParam}; @@ -12,14 +12,13 @@ use editoast_schemas::{ infra::{Direction, DirectionalTrackRange, Endpoint, Sign, TrackEndpoint}, primitives::Identifier, }; -use itertools::Itertools; +use itertools::{Either, Itertools}; use serde::{Deserialize, Serialize}; use std::{ cmp::Ordering, collections::{HashMap, HashSet}, result::Result as StdResult, }; -use axum::http::StatusCode; use thiserror::Error; use utoipa::ToSchema; use editoast_derive::EditoastError; @@ -62,14 +61,19 @@ struct DirectedLocation { direction: Direction, } -#[derive(Debug, Error,Serialize, Deserialize, EditoastError)] +#[derive(Debug, Error, Serialize, Deserialize, EditoastError)] #[editoast_error(base_id = "delimited_area")] +enum DelimitedAreaViewError { + #[error("{0:?}")] + #[editoast_error(status = 400)] + InvalidLocations(Vec), +} + +#[derive(Debug, Error, Serialize, Deserialize)] enum DelimitedAreaError { #[error("Track '{0}' does not exist")] - #[editoast_error(status = 400)] TrackDoesNotExist(String), #[error("Invalid input position '{position}' on track '{track}' of length '{track_length}'")] - #[editoast_error(status = 400)] LocationOutOfBounds { track: String, position: f64, track_length: f64 }, } @@ -133,20 +137,23 @@ async fn delimited_area( // Validate user input let (valid_entries, invalid_entries): (Vec<_>, Vec<_>) = entries.into_iter() - .partition(|entry| validate_location(entry, &infra_cache).is_ok()); + .partition_map(|entry| { + match validate_location(&entry, &infra_cache) { + Ok(_) => Either::Left(entry), + Err(e) => Either::Right(e), + } + }); let (valid_exits, invalid_exits): (Vec<_>, Vec<_>) = exits.into_iter() - .partition(|exit| validate_location(exit, &infra_cache).is_ok()); + .partition_map(|exit| { + match validate_location(&exit, &infra_cache) { + Ok(_) => Either::Left(exit), + Err(e) => Either::Right(e), + } + }); if !(invalid_exits.is_empty() && invalid_entries.is_empty()) { - // Throw an error 4xx which contains the list of invalid input locations - let location_errors = HashMap::new(); - return Err(InternalError { - // TODO - status: StatusCode::BAD_REQUEST, - error_type: "".to_string(), - context: location_errors, - message: "Some locations were invalid".to_string() - }); + let invalid_locations = invalid_entries.into_iter().chain(invalid_exits.into_iter()).collect::>(); + return Err(DelimitedAreaViewError::InvalidLocations(invalid_locations).into()); } // Retrieve the track ranges @@ -886,4 +893,13 @@ mod tests { // N.B. This is mostly a performance issue. unimplemented!(); } + + #[rstest] + #[ignore] + async fn request_with_invalid_locations_is_rejected() { + /// Invalid locations (invalid track number, location position not on the track...) + /// get rejected with a 400 error code and the response contains context about + /// which locations were invalid and how they were invalid. + todo!() + } }