From bad1e4294449faf2fb884b23e225af894f4c9ca8 Mon Sep 17 00:00:00 2001 From: Egor Berezovskiy Date: Wed, 5 Mar 2025 12:12:48 +0100 Subject: [PATCH] editoast: get paced train path endpont Signed-off-by: Egor Berezovskiy --- editoast/openapi.yaml | 25 +++++++ editoast/src/models/paced_train.rs | 26 ++++++- editoast/src/views/paced_train.rs | 105 +++++++++++++++++++++++++--- front/public/locales/en/errors.json | 3 +- front/public/locales/fr/errors.json | 3 +- 5 files changed, 147 insertions(+), 15 deletions(-) diff --git a/editoast/openapi.yaml b/editoast/openapi.yaml index e6eb16f1d91..00f75242ddc 100644 --- a/editoast/openapi.yaml +++ b/editoast/openapi.yaml @@ -5142,6 +5142,7 @@ components: - $ref: '#/components/schemas/EditoastOperationErrorObjectNotFound' - $ref: '#/components/schemas/EditoastPacedTrainErrorBatchPacedTrainNotFound' - $ref: '#/components/schemas/EditoastPacedTrainErrorDatabase' + - $ref: '#/components/schemas/EditoastPacedTrainErrorInfraNotFound' - $ref: '#/components/schemas/EditoastPacedTrainErrorPacedTrainNotFound' - $ref: '#/components/schemas/EditoastPaginationErrorInvalidPage' - $ref: '#/components/schemas/EditoastPaginationErrorInvalidPageSize' @@ -5680,6 +5681,30 @@ components: type: string enum: - editoast:paced_train:Database + EditoastPacedTrainErrorInfraNotFound: + type: object + required: + - type + - status + - message + properties: + context: + type: object + required: + - infra_id + properties: + infra_id: + type: integer + message: + type: string + status: + type: integer + enum: + - 404 + type: + type: string + enum: + - editoast:paced_train:InfraNotFound EditoastPacedTrainErrorPacedTrainNotFound: type: object required: diff --git a/editoast/src/models/paced_train.rs b/editoast/src/models/paced_train.rs index 014e766443a..57b75260eb9 100644 --- a/editoast/src/models/paced_train.rs +++ b/editoast/src/models/paced_train.rs @@ -13,9 +13,9 @@ use editoast_schemas::train_schedule::ScheduleItem; use editoast_schemas::train_schedule::TrainScheduleBase; use editoast_schemas::train_schedule::TrainScheduleOptions; -use crate::models::prelude::*; - +use super::train_schedule::TrainSchedule; use super::Tags; +use crate::models::prelude::*; #[derive(Debug, Clone, Model)] #[model(table = editoast_models::tables::paced_train)] @@ -101,3 +101,25 @@ impl From for PacedTrainBase { } } } + +impl PacedTrain { + pub fn into_first_occurrence(self) -> TrainSchedule { + TrainSchedule { + id: self.id, + train_name: self.train_name, + labels: self.labels.into(), + rolling_stock_name: self.rolling_stock_name, + timetable_id: self.timetable_id, + path: self.path, + start_time: self.start_time, + schedule: self.schedule, + margins: self.margins, + initial_speed: self.initial_speed, + comfort: self.comfort, + constraint_distribution: self.constraint_distribution, + speed_limit_tag: self.speed_limit_tag, + power_restrictions: self.power_restrictions, + options: self.options, + } + } +} diff --git a/editoast/src/views/paced_train.rs b/editoast/src/views/paced_train.rs index 9bc670b776e..14a5ab9a986 100644 --- a/editoast/src/views/paced_train.rs +++ b/editoast/src/views/paced_train.rs @@ -17,6 +17,7 @@ use thiserror::Error; use utoipa::IntoParams; use utoipa::ToSchema; +use super::path::pathfinding::pathfinding_from_train; use super::path::pathfinding::PathfindingResult; use super::projection::ProjectPathForm; use super::AppState; @@ -27,6 +28,7 @@ use crate::core::simulation::SimulationResponse; use crate::error::Result; use crate::models::paced_train::PacedTrain; use crate::models::prelude::*; +use crate::models::Infra; use crate::views::projection::ProjectPathTrainResult; use crate::views::AuthorizationError; use crate::views::ListId; @@ -60,6 +62,9 @@ enum PacedTrainError { #[editoast_error(status = 404)] #[error("Paced train {paced_train_id} does not exist")] PacedTrainNotFound { paced_train_id: i64 }, + #[error("Infra '{infra_id}', could not be found")] + #[editoast_error(status = 404)] + InfraNotFound { infra_id: i64 }, #[error(transparent)] #[editoast_error(status = 500)] Database(#[from] editoast_models::model::Error), @@ -229,20 +234,43 @@ async fn simulation_summary( )] async fn get_path( State(AppState { - db_pool: _db_pool, - valkey: _valkey_client, - core_client: _core, + db_pool, + valkey: valkey_client, + core_client, .. }): State, - Extension(_auth): AuthenticationExt, - Path(PacedTrainIdParam { - id: _paced_train_id, - }): Path, - Query(InfraIdQueryParam { - infra_id: _infra_id, - }): Query, + Extension(auth): AuthenticationExt, + Path(PacedTrainIdParam { id: paced_train_id }): Path, + Query(InfraIdQueryParam { infra_id }): Query, ) -> Result> { - todo!(); + let authorized = auth + .check_roles([BuiltinRole::OperationalStudies, BuiltinRole::Stdcm].into()) + .await + .map_err(AuthorizationError::AuthError)?; + if !authorized { + return Err(AuthorizationError::Forbidden.into()); + } + let conn = &mut db_pool.get().await?; + let mut valkey_conn = valkey_client.get_connection().await?; + + let infra = Infra::retrieve_or_fail(conn, infra_id, || PacedTrainError::InfraNotFound { + infra_id, + }) + .await?; + let paced_train = PacedTrain::retrieve_or_fail(conn, paced_train_id, || { + PacedTrainError::PacedTrainNotFound { paced_train_id } + }) + .await?; + Ok(Json( + pathfinding_from_train( + conn, + &mut valkey_conn, + core_client, + &infra, + paced_train.into_first_occurrence(), + ) + .await?, + )) } #[derive(Debug, Default, Clone, Serialize, Deserialize, IntoParams, ToSchema)] @@ -316,17 +344,23 @@ async fn project_path( #[cfg(test)] mod tests { use axum::http::StatusCode; + use editoast_models::DbConnectionPoolV2; use pretty_assertions::assert_eq; use rstest::rstest; use serde_json::json; + use crate::core::mocking::MockingClient; + use crate::core::pathfinding::PathfindingResultSuccess; use crate::error::InternalError; + use crate::models::fixtures::create_fast_rolling_stock; use crate::models::fixtures::create_simple_paced_train; + use crate::models::fixtures::create_small_infra; use crate::models::fixtures::create_timetable; use crate::models::fixtures::simple_paced_train_base; use crate::models::paced_train::PacedTrain; use crate::models::prelude::*; use crate::views::paced_train::PacedTrainResult; + use crate::views::path::pathfinding::PathfindingResult; use crate::views::test_app::TestAppBuilder; #[rstest] @@ -406,4 +440,53 @@ mod tests { ); assert_eq!(paced_train.step, response.paced_train.paced.step.into()); } + + #[rstest] + async fn get_paced_train_path() { + let db_pool = DbConnectionPoolV2::for_tests(); + let mut core = MockingClient::new(); + core.stub("/v2/pathfinding/blocks") + .method(reqwest::Method::POST) + .response(StatusCode::OK) + .json(json!({ + "blocks":[], + "routes": [], + "track_section_ranges": [], + "path_item_positions": [], + "length": 1, + "status": "success" + })) + .finish(); + let app = TestAppBuilder::new() + .db_pool(db_pool.clone()) + .core_client(core.into()) + .build(); + let pool = app.db_pool(); + + create_fast_rolling_stock(&mut db_pool.get_ok(), "R2D2").await; + let timetable = create_timetable(&mut pool.get_ok()).await; + let paced_train = create_simple_paced_train(&mut pool.get_ok(), timetable.id).await; + let small_infra = create_small_infra(&mut db_pool.get_ok()).await; + + let request = app.get(&format!( + "/paced_train/{}/path?infra_id={}", + paced_train.id, small_infra.id + )); + + let response = app + .fetch(request) + .assert_status(StatusCode::OK) + .json_into::(); + + assert_eq!( + response, + PathfindingResult::Success(PathfindingResultSuccess { + blocks: vec![], + routes: vec![], + track_section_ranges: vec![], + path_item_positions: vec![], + length: 1 + }) + ) + } } diff --git a/front/public/locales/en/errors.json b/front/public/locales/en/errors.json index c67412b5f62..c2ff1ae15bd 100644 --- a/front/public/locales/en/errors.json +++ b/front/public/locales/en/errors.json @@ -242,7 +242,8 @@ "paced_train": { "Database": "Internal error (database)", "BatchPacedTrainNotFound": "Some Paced trains could not be found", - "PacedTrainNotFound": "Paced train '{{paced_train_id}}' not found" + "PacedTrainNotFound": "Paced train '{{paced_train_id}}' not found", + "InfraNotFound": "Infrastructure '{{infra_id}}' could not be found" }, "url": { "InvalidUrl": "Invalid url '{{url}}'" diff --git a/front/public/locales/fr/errors.json b/front/public/locales/fr/errors.json index e433ed94c18..6dc34fae760 100644 --- a/front/public/locales/fr/errors.json +++ b/front/public/locales/fr/errors.json @@ -239,7 +239,8 @@ "paced_train": { "Database": "Erreur interne (base de données)", "BatchPacedTrainNotFound": "Certaines missions sont introuvables", - "PacedTrainNotFound": "Mission '{{paced_train_id}}' non trouvée" + "PacedTrainNotFound": "Mission '{{paced_train_id}}' non trouvée", + "InfraNotFound": "Infrastructure '{{infra_id}}' non trouvée" }, "url": { "InvalidUrl": "Url invalide '{{url}}'"