diff --git a/editoast/openapi.yaml b/editoast/openapi.yaml index 06c1e5e265b..9695d353ef5 100644 --- a/editoast/openapi.yaml +++ b/editoast/openapi.yaml @@ -4758,6 +4758,8 @@ paths: example: track_sections type: string promoteId: + additionalProperties: + type: string type: object scheme: example: xyz diff --git a/editoast/src/models/rolling_stock/light_rolling_stock.rs b/editoast/src/models/rolling_stock/light_rolling_stock.rs index 85f0efa65f3..79cae15b113 100644 --- a/editoast/src/models/rolling_stock/light_rolling_stock.rs +++ b/editoast/src/models/rolling_stock/light_rolling_stock.rs @@ -14,7 +14,7 @@ use diesel_async::RunQueryDsl; use diesel_json::Json as DieselJson; use editoast_derive::Model; use serde::Serialize; -use serde_json::Value as JsonValue; +use std::collections::HashMap; use utoipa::ToSchema; #[derive(Debug, Model, Queryable, QueryableByName, Serialize, ToSchema)] @@ -43,8 +43,8 @@ pub struct LightRollingStockModel { rolling_resistance: DieselJson, #[schema(value_type = LoadingGaugeType)] loading_gauge: String, - #[schema(value_type = HashMap)] - power_restrictions: Option, + #[schema(value_type = Option>)] + power_restrictions: Option>>, #[schema(value_type = Vec)] energy_sources: DieselJson>, locked: bool, diff --git a/editoast/src/models/rolling_stock/mod.rs b/editoast/src/models/rolling_stock/mod.rs index b630a5eaa44..7bdacb2ffcb 100644 --- a/editoast/src/models/rolling_stock/mod.rs +++ b/editoast/src/models/rolling_stock/mod.rs @@ -27,7 +27,7 @@ use diesel_async::{AsyncPgConnection as PgConnection, RunQueryDsl}; use diesel_json::Json as DieselJson; use editoast_derive::Model; use serde::{Deserialize, Serialize}; -use serde_json::Value as JsonValue; +use std::collections::HashMap; use validator::{Validate, ValidationError}; crate::schemas! { @@ -109,9 +109,9 @@ pub struct RollingStockModel { #[diesel(deserialize_as = String)] #[schema(value_type = LoadingGaugeType)] pub loading_gauge: Option, - #[diesel(deserialize_as = Option)] + #[diesel(deserialize_as = Option>>)] #[schema(value_type = Option>)] - pub power_restrictions: Option>, + pub power_restrictions: Option>>>, #[diesel(deserialize_as = DieselJson>)] #[schema(value_type = EnergySource)] pub energy_sources: Option>>, diff --git a/editoast/src/schema/geo_json.rs b/editoast/src/schema/geo_json.rs index 0168b25e4e6..2f562dcaebd 100644 --- a/editoast/src/schema/geo_json.rs +++ b/editoast/src/schema/geo_json.rs @@ -3,7 +3,7 @@ use mvt::GeomType; use serde::{Deserialize, Serialize}; /// GeoJson representation -#[derive(Clone, Serialize, Deserialize, Debug)] +#[derive(Clone, Serialize, Deserialize, Debug, PartialEq)] #[serde(tag = "type")] pub enum GeoJson { Point { coordinates: (f64, f64) }, diff --git a/editoast/src/schema/rolling_stock/light_rolling_stock.rs b/editoast/src/schema/rolling_stock/light_rolling_stock.rs index d84cdd11572..37e5f66f9c1 100644 --- a/editoast/src/schema/rolling_stock/light_rolling_stock.rs +++ b/editoast/src/schema/rolling_stock/light_rolling_stock.rs @@ -1,7 +1,6 @@ use diesel::sql_types::{Array, BigInt, Bool, Double, Jsonb, Nullable, Text}; use diesel_json::Json as DieselJson; use serde::{Deserialize, Serialize}; -use serde_json::Value as JsonValue; use std::collections::HashMap; use utoipa::ToSchema; @@ -61,7 +60,7 @@ pub struct LightRollingStock { pub metadata: DieselJson, #[diesel(sql_type = Nullable)] #[schema(value_type = HashMap)] - pub power_restrictions: Option, + pub power_restrictions: Option>>, #[diesel(sql_type = Jsonb)] #[schema(value_type = Vec)] pub energy_sources: DieselJson>, diff --git a/editoast/src/schema/rolling_stock/mod.rs b/editoast/src/schema/rolling_stock/mod.rs index 7fa6a89906d..39ef19082d2 100644 --- a/editoast/src/schema/rolling_stock/mod.rs +++ b/editoast/src/schema/rolling_stock/mod.rs @@ -1,8 +1,8 @@ pub mod light_rolling_stock; pub mod rolling_stock_livery; +use diesel_json::Json as DieselJson; use serde::{Deserialize, Deserializer, Serialize}; -use serde_json::Value as JsonValue; use std::collections::HashMap; use strum_macros::{Display, EnumString}; use utoipa::ToSchema; @@ -53,7 +53,7 @@ pub struct RollingStockCommon { pub loading_gauge: String, /// Mapping of power restriction code to power class #[schema(value_type = HashMap)] - pub power_restrictions: Option, + pub power_restrictions: Option>>, #[serde(default)] pub energy_sources: Vec, /// The time the train takes before actually using electrical power (in seconds). Is null if the train is not electric. diff --git a/editoast/src/tests/example_rolling_stock_1.json b/editoast/src/tests/example_rolling_stock_1.json index 01f052639fa..b7621a9b312 100644 --- a/editoast/src/tests/example_rolling_stock_1.json +++ b/editoast/src/tests/example_rolling_stock_1.json @@ -981,7 +981,7 @@ "unit": "" }, "energy_sources": [], - "power_restrictions": {"C2":"2"}, + "power_restrictions": {"C2":"2", "C5":"5"}, "electrical_power_startup_time": 5.0, "raise_pantograph_time": 15.0 } diff --git a/editoast/src/views/infra/auto_fixes.rs b/editoast/src/views/infra/auto_fixes.rs index a4e27634ff0..43ee955c56a 100644 --- a/editoast/src/views/infra/auto_fixes.rs +++ b/editoast/src/views/infra/auto_fixes.rs @@ -284,11 +284,11 @@ mod test { // Check the only initial issues are "overlapping_speed_sections" warnings let infra_errors_before_all: PaginatedResponse = read_body_json(call_service(&app, errors_request(small_infra_id)).await).await; - assert!(infra_errors_before_all.results.iter().all(|e| e - .information - .get("error_type") - .unwrap() - == "overlapping_speed_sections")); + + assert!(infra_errors_before_all.results.iter().all(|e| matches!( + e.information.get_sub_type(), + InfraErrorType::OverlappingSpeedSections { .. } + ))); // Remove a track let deletion = Operation::Delete(DeleteOperation { diff --git a/editoast/src/views/infra/errors.rs b/editoast/src/views/infra/errors.rs index 182c529022f..56d655eea7c 100644 --- a/editoast/src/views/infra/errors.rs +++ b/editoast/src/views/infra/errors.rs @@ -1,4 +1,5 @@ use crate::error::Result; +use crate::schema::InfraError as SchemaInfraError; use crate::schema::InfraErrorType; use crate::views::pagination::{Paginate, PaginatedResponse, PaginationQueryParam}; use crate::DbPool; @@ -6,10 +7,10 @@ use actix_web::dev::HttpServiceFactory; use actix_web::get; use actix_web::web::{Data, Json as WebJson, Path, Query}; use diesel::sql_query; -use diesel::sql_types::{BigInt, Json, Text}; +use diesel::sql_types::{BigInt, Jsonb, Text}; +use diesel_json::Json as DieselJson; use editoast_derive::EditoastError; use serde::{Deserialize, Serialize}; -use serde_json::Value as JsonValue; use strum::VariantNames; use thiserror::Error; @@ -65,10 +66,9 @@ enum ListErrorsErrors { } #[derive(QueryableByName, Debug, Clone, Serialize, Deserialize, PartialEq)] -#[serde(deny_unknown_fields)] pub struct InfraError { - #[diesel(sql_type = Json)] - pub information: JsonValue, + #[diesel(sql_type = Jsonb)] + pub information: DieselJson, } #[derive(Default, Debug, Clone, PartialEq, Eq, Deserialize)] @@ -87,8 +87,7 @@ async fn get_paginated_infra_errors( per_page: i64, params: &QueryParams, ) -> Result> { - let mut query = - String::from("SELECT information::text FROM infra_layer_error WHERE infra_id = $1"); + let mut query = String::from("SELECT information FROM infra_layer_error WHERE infra_id = $1"); if params.level == Level::Warnings { query += " AND information->>'is_warning' = 'true'" } else if params.level == Level::Errors { diff --git a/editoast/src/views/infra/mod.rs b/editoast/src/views/infra/mod.rs index 475943ca85d..70315106548 100644 --- a/editoast/src/views/infra/mod.rs +++ b/editoast/src/views/infra/mod.rs @@ -35,7 +35,6 @@ use diesel::{sql_query, QueryableByName}; use diesel_async::RunQueryDsl; use editoast_derive::EditoastError; use serde::{Deserialize, Serialize}; -use serde_json::{json, Value as JsonValue}; use std::collections::HashMap; use thiserror::Error; use utoipa::IntoParams; @@ -128,6 +127,11 @@ struct RefreshQueryParams { infras: List, } +#[derive(Debug, Serialize)] +struct RefreshResponse { + infra_refreshed: Vec, +} + /// Refresh infra generated data #[post("/refresh")] async fn refresh( @@ -136,7 +140,7 @@ async fn refresh( query_params: Query, infra_caches: Data>, map_layers: Data, -) -> Result> { +) -> Result> { let mut conn = db_pool.get().await?; // Use a transaction to give scope to infra list lock let mut infras_list = vec![]; @@ -158,7 +162,7 @@ async fn refresh( } // Refresh each infras - let mut refreshed_infra = vec![]; + let mut infra_refreshed = vec![]; for infra in infras_list { let infra_cache = InfraCache::get_or_load(&mut conn, &infra_caches, &infra).await?; @@ -166,12 +170,12 @@ async fn refresh( .refresh(db_pool.clone(), query_params.force, &infra_cache) .await? { - refreshed_infra.push(infra.id.unwrap()); + infra_refreshed.push(infra.id.unwrap()); } } let mut conn = redis_client.get_connection().await?; - for infra_id in refreshed_infra.iter() { + for infra_id in infra_refreshed.iter() { map::invalidate_all( &mut conn, &map_layers.layers.keys().cloned().collect(), @@ -180,7 +184,7 @@ async fn refresh( .await?; } - Ok(Json(json!({ "infra_refreshed": refreshed_infra }))) + Ok(Json(RefreshResponse { infra_refreshed })) } /// Return a list of infras @@ -531,6 +535,7 @@ pub mod tests { use actix_web::test::{call_and_read_body_json, call_service, read_body_json, TestRequest}; use diesel_async::{AsyncConnection, AsyncPgConnection as PgConnection}; use rstest::*; + use serde_json::json; use strum::IntoEnumIterator; fn delete_infra_request(infra_id: i64) -> Request { diff --git a/editoast/src/views/infra/objects.rs b/editoast/src/views/infra/objects.rs index cb4c9040724..7b486da98ce 100644 --- a/editoast/src/views/infra/objects.rs +++ b/editoast/src/views/infra/objects.rs @@ -6,12 +6,13 @@ use actix_web::web::{Data, Json, Path}; use diesel::sql_types::{Array, BigInt, Jsonb, Nullable, Text}; use diesel::{sql_query, QueryableByName}; use diesel_async::RunQueryDsl; +use diesel_json::Json as DieselJson; use serde::{Deserialize, Serialize}; use serde_json::Value as JsonValue; use thiserror::Error; use crate::error::Result; -use crate::schema::ObjectType; +use crate::schema::{GeoJson, ObjectType}; use crate::DbPool; use editoast_derive::EditoastError; @@ -37,17 +38,16 @@ fn has_unique_ids(obj_ids: &Vec) -> bool { obj_ids_2.len() == obj_ids.len() } -#[derive(QueryableByName, Debug, Clone, Serialize, Deserialize)] +#[derive(QueryableByName, Debug, Clone, Serialize, Deserialize, PartialEq)] struct ObjectQueryable { #[diesel(sql_type = Text)] - #[serde(skip_serializing)] obj_id: String, #[diesel(sql_type = Jsonb)] railjson: JsonValue, #[diesel(sql_type = Nullable)] - geographic: Option, + geographic: Option>, #[diesel(sql_type = Nullable)] - schematic: Option, + schematic: Option>, } /// Return the railjson list of a specific OSRD object @@ -121,11 +121,14 @@ async fn get_objects( #[cfg(test)] mod tests { use actix_web::http::StatusCode; - use actix_web::test::{call_service, TestRequest}; + use actix_web::test::{call_service, read_body_json, TestRequest}; + use serde_json::{json, Value as JsonValue}; - use crate::fixtures::tests::{empty_infra, TestFixture}; + use crate::fixtures::tests::{db_pool, empty_infra, TestFixture}; use crate::models::Infra; - use crate::schema::SwitchType; + use crate::schema::operation::Operation; + use crate::schema::{Switch, SwitchType}; + use crate::views::infra::objects::ObjectQueryable; use crate::views::infra::tests::create_object_request; use crate::views::tests::create_test_service; use rstest::*; @@ -155,6 +158,56 @@ mod tests { assert_eq!(call_service(&app, req).await.status(), StatusCode::OK); } + #[rstest] + async fn get_objects_should_return_switch() { + // GIVEN + let app = create_test_service().await; + let empty_infra = empty_infra(db_pool()).await; + let empty_infra_id = empty_infra.id(); + + let switch = Switch { + id: "switch_001".into(), + switch_type: "switch_type_001".into(), + ..Default::default() + } + .into(); + + let create_operation = Operation::Create(Box::new(switch)); + let request = TestRequest::post() + .uri(format!("/infra/{empty_infra_id}/").as_str()) + .set_json(json!([create_operation])) + .to_request(); + let response = call_service(&app, request).await; + assert_eq!(response.status(), StatusCode::OK); + + let expected_switch_object = vec![ObjectQueryable { + obj_id: "switch_001".into(), + railjson: json!({ + "extensions": { + "sncf": JsonValue::Null + }, + "group_change_delay": 0.0, + "id": "switch_001", + "ports": {}, + "switch_type": "switch_type_001" + }), + geographic: None, + schematic: None, + }]; + + // WHEN + let req = TestRequest::post() + .uri(format!("/infra/{empty_infra_id}/objects/Switch").as_str()) + .set_json(vec!["switch_001"]) + .to_request(); + let response = call_service(&app, req).await; + + // THEN + assert_eq!(response.status(), StatusCode::OK); + let switch_object: Vec = read_body_json(response).await; + assert_eq!(switch_object, expected_switch_object); + } + #[rstest] async fn get_objects_duplicate_ids(#[future] empty_infra: TestFixture) { let empty_infra = empty_infra.await; diff --git a/editoast/src/views/infra/routes.rs b/editoast/src/views/infra/routes.rs index 858e41206de..d3d4f0092ca 100644 --- a/editoast/src/views/infra/routes.rs +++ b/editoast/src/views/infra/routes.rs @@ -12,7 +12,6 @@ use chashmap::CHashMap; use diesel::sql_query; use diesel::sql_types::{BigInt, Bool, Text}; use diesel_async::RunQueryDsl; -use serde_json::{json, Value as JsonValue}; use serde::{Deserialize, Serialize}; use strum_macros::Display; @@ -36,18 +35,24 @@ enum WaypointType { BufferStop, } -/// Return the railjson list of a specific OSRD object -#[get("/{waypoint_type}/{waypoint}")] +#[derive(Debug, Serialize, Deserialize, PartialEq)] +struct RoutesResponse { + starting: Vec, + ending: Vec, +} + +/// Return the routes list of a specific waypoint +#[get("/{waypoint_type}/{waypoint_id}")] async fn get_routes_from_waypoint( path: Path<(i64, WaypointType, String)>, db_pool: Data, -) -> Result> { - let (infra, waypoint_type, waypoint) = path.into_inner(); +) -> Result> { + let (infra, waypoint_type, waypoint_id) = path.into_inner(); let mut conn = db_pool.get().await?; let routes: Vec = sql_query(include_str!("sql/get_routes_from_waypoint.sql")) .bind::(infra) - .bind::(waypoint) + .bind::(waypoint_id) .bind::(waypoint_type.to_string()) .load(&mut conn) .await?; @@ -63,9 +68,10 @@ async fn get_routes_from_waypoint( } }); - Ok(Json( - json!({"starting": starting_routes, "ending": ending_routes}), - )) + Ok(Json(RoutesResponse { + starting: starting_routes, + ending: ending_routes, + })) } #[derive(Debug, Clone, Serialize, PartialEq)] @@ -119,3 +125,134 @@ async fn get_routes_track_ranges<'a>( Ok(Json(result)) } + +#[cfg(test)] +mod tests { + use actix_http::StatusCode; + use actix_web::test::{call_service, read_body_json, TestRequest}; + use rstest::rstest; + use serde_json::json; + + use crate::{ + fixtures::tests::{db_pool, empty_infra}, + schema::{operation::Operation, BufferStop, Detector, Route, TrackSection, Waypoint}, + views::{ + infra::routes::{RoutesResponse, WaypointType}, + tests::create_test_service, + }, + }; + + #[rstest] + async fn get_waypoint_should_return_routes_from_buffer_stop_and_detector() { + let app = create_test_service().await; + let empty_infra = empty_infra(db_pool()).await; + let empty_infra_id = empty_infra.id(); + + let track = TrackSection { + id: "track_001".into(), + length: 1_000.0, + ..Default::default() + } + .into(); + let detector = Detector { + id: "detector_001".into(), + track: "track_001".into(), + position: 100.0, + ..Default::default() + } + .into(); + let bs_start = BufferStop { + id: "bs_start".into(), + track: "track_001".into(), + position: 0.0, + ..Default::default() + } + .into(); + let bs_stop = BufferStop { + id: "bs_stop".into(), + track: "track_001".into(), + position: 1_000.0, + ..Default::default() + } + .into(); + let route_1 = Route { + id: "D001->BS_STOP".into(), + entry_point: Waypoint::new_detector("detector_001"), + exit_point: Waypoint::new_buffer_stop("bs_stop"), + ..Default::default() + } + .into(); + let route_2 = Route { + id: "BS_START->D001".into(), + entry_point: Waypoint::new_buffer_stop("bs_start"), + exit_point: Waypoint::new_detector("detector_001"), + ..Default::default() + } + .into(); + for obj in [track, detector, bs_start, bs_stop, route_1, route_2] { + let create_operation = Operation::Create(Box::new(obj)); + let request = TestRequest::post() + .uri(format!("/infra/{empty_infra_id}/").as_str()) + .set_json(json!([create_operation])) + .to_request(); + let response = call_service(&app, request).await; + assert_eq!(response.status(), StatusCode::OK); + } + + // BufferStop Routes + let waypoint_type = WaypointType::BufferStop; + let request = TestRequest::get() + .uri(format!("/infra/{empty_infra_id}/routes/{waypoint_type}/bs_stop").as_str()) + .to_request(); + let response = call_service(&app, request).await; + assert_eq!(response.status(), StatusCode::OK); + let routes: RoutesResponse = read_body_json(response).await; + assert_eq!( + routes, + RoutesResponse { + starting: vec![], + ending: vec!["D001->BS_STOP".to_string()] + } + ); + + // Detector Routes + let waypoint_type = WaypointType::Detector; + let request = TestRequest::get() + .uri(format!("/infra/{empty_infra_id}/routes/{waypoint_type}/detector_001").as_str()) + .to_request(); + let response = call_service(&app, request).await; + assert_eq!(response.status(), StatusCode::OK); + let routes: RoutesResponse = read_body_json(response).await; + assert_eq!( + routes, + RoutesResponse { + starting: vec!["D001->BS_STOP".to_string()], + ending: vec!["BS_START->D001".to_string()] + } + ); + } + + #[rstest] + async fn get_waypoint_should_return_empty_response() { + let app = create_test_service().await; + let empty_infra = empty_infra(db_pool()).await; + let empty_infra_id = empty_infra.id(); + let waypoint_type = WaypointType::Detector; + let request = TestRequest::get() + .uri( + format!("/infra/{empty_infra_id}/routes/{waypoint_type}/NOT_EXISTING_WAYPOINT_ID") + .as_str(), + ) + .to_request(); + let response = call_service(&app, request).await; + assert_eq!(response.status(), StatusCode::OK); + let routes: RoutesResponse = read_body_json(response).await; + assert_eq!( + routes, + RoutesResponse { + starting: vec![], + ending: vec![] + } + ); + } +} diff --git a/editoast/src/views/layers/mod.rs b/editoast/src/views/layers/mod.rs index c120afac132..f147b1d0f7b 100644 --- a/editoast/src/views/layers/mod.rs +++ b/editoast/src/views/layers/mod.rs @@ -1,5 +1,7 @@ mod mvt_utils; +use std::collections::HashMap; + use crate::client::{get_root_url, MapLayersConfig}; use crate::error::Result; use crate::map::redis_utils::RedisClient; @@ -13,7 +15,6 @@ use diesel_async::RunQueryDsl; use editoast_derive::EditoastError; use mvt_utils::{create_and_fill_mvt_tile, get_geo_json_sql_query, GeoJsonAndData}; use serde::{Deserialize, Serialize}; -use serde_json::{json, Value as JsonValue}; use thiserror::Error; use utoipa::{IntoParams, ToSchema}; @@ -75,15 +76,15 @@ struct LayerViewParams { view_slug: String, } -#[derive(Serialize, ToSchema)] +#[derive(Debug, Serialize, Deserialize, PartialEq, ToSchema)] struct ViewMetadata { #[serde(rename = "type")] data_type: String, #[schema(example = "track_sections")] name: String, #[serde(rename = "promoteId")] - #[schema(example = json!({track_sections: "id"}), value_type = Object)] - promote_id: JsonValue, + #[schema(value_type = HashMap)] + promote_id: HashMap, #[schema(example = "xyz")] scheme: String, #[schema(example = json!(["http://localhost:7070/tile/track_sections/geo/{z}/{x}/{y}/?infra=1"]))] @@ -131,7 +132,7 @@ async fn layer_view( Ok(Json(ViewMetadata { data_type: "vector".to_owned(), name: layer_slug.to_owned(), - promote_id: json!({layer_slug: layer.id_field}), + promote_id: HashMap::from([(layer_slug, layer.id_field.clone().unwrap_or_default())]), scheme: "xyz".to_owned(), tiles: vec![tiles_url_pattern], attribution: layer.attribution.clone().unwrap_or_default(), @@ -218,25 +219,32 @@ async fn cache_and_get_mvt_tile( #[cfg(test)] mod tests { - use crate::error::InternalError; + use std::collections::HashMap; + use crate::map::MapLayers; use crate::views::tests::create_test_service; + use crate::{error::InternalError, views::layers::ViewMetadata}; use actix_web::{ http::StatusCode, test::{call_service, read_body_json, TestRequest}, }; use rstest::rstest; - use serde_json::{json, to_value, Value as JsonValue}; + use serde::de::DeserializeOwned; + use serde_json::to_value; use super::LayersError; /// Run a simple get query on `uri` and check the status code and json body - async fn test_get_query(uri: &str, expected_status: StatusCode, expected_body: JsonValue) { + async fn test_get_query( + uri: &str, + expected_status: StatusCode, + expected_body: T, + ) { let app = create_test_service().await; let req = TestRequest::get().uri(uri).to_request(); let response = call_service(&app, req).await; assert_eq!(response.status(), expected_status); - let body: JsonValue = read_body_json(response).await; + let body: T = read_body_json(response).await; assert_eq!(expected_body, body) } @@ -245,18 +253,16 @@ mod tests { test_get_query( "/layers/layer/track_sections/mvt/geo?infra=2", StatusCode::OK, - json!({ - "type": "vector", - "name": "track_sections", - "promoteId": { - "track_sections": "id" - }, - "scheme": "xyz", - "tiles": [tiles], - "attribution": "", - "minzoom": 5, - "maxzoom": 18 - }), + ViewMetadata { + data_type: "vector".to_string(), + name: "track_sections".to_string(), + promote_id: HashMap::from([("track_sections".to_string(), "id".to_string())]), + scheme: "xyz".to_string(), + tiles: vec![tiles], + attribution: "".to_string(), + minzoom: 5, + maxzoom: 18, + }, ) .await; } diff --git a/editoast/src/views/rolling_stocks/mod.rs b/editoast/src/views/rolling_stocks/mod.rs index b3cb4d29855..07471a97f64 100644 --- a/editoast/src/views/rolling_stocks/mod.rs +++ b/editoast/src/views/rolling_stocks/mod.rs @@ -1008,12 +1008,7 @@ pub mod tests { let rolling_stock = named_fast_rolling_stock("fast_rolling_stock_get_power_restrictions_list", db_pool) .await; - let power_restrictions = rolling_stock - .model - .power_restrictions - .clone() - .unwrap() - .unwrap(); + let power_restrictions = rolling_stock.model.power_restrictions.clone().unwrap(); // WHEN let response = call_service( @@ -1025,8 +1020,12 @@ pub mod tests { .await; // THEN - assert!(power_restrictions.to_string().contains(&"C2".to_string())); + let power_restrictions = serde_json::to_string(&power_restrictions) + .expect("Failed to convert power_restrictions to string"); + assert!(power_restrictions.contains(&"C2".to_string())); + assert!(power_restrictions.contains(&"C5".to_string())); let response_body: Vec = read_body_json(response).await; assert!(response_body.contains(&"C2".to_string())); + assert!(response_body.contains(&"C5".to_string())); } } diff --git a/front/src/common/api/osrdEditoastApi.ts b/front/src/common/api/osrdEditoastApi.ts index a4e916dc973..2d417fffb9a 100644 --- a/front/src/common/api/osrdEditoastApi.ts +++ b/front/src/common/api/osrdEditoastApi.ts @@ -983,7 +983,9 @@ export type GetLayersLayerByLayerSlugMvtAndViewSlugApiResponse = maxzoom: number; minzoom: number; name: string; - promoteId: object; + promoteId: { + [key: string]: string; + }; scheme: string; tiles: string[]; type: string;