Skip to content

Commit 500bdcb

Browse files
committed
editoast: front: adapt timetable endpoint and delete post endpoint
Signed-off-by: Youness CHRIFI ALAOUI <youness.chrifi@gmail.com>
1 parent 42f1939 commit 500bdcb

File tree

15 files changed

+143
-233
lines changed

15 files changed

+143
-233
lines changed

editoast/openapi.yaml

+31-50
Original file line numberDiff line numberDiff line change
@@ -2719,27 +2719,6 @@ paths:
27192719
'404':
27202720
description: Timetable not found
27212721
/timetable/{id}:
2722-
get:
2723-
tags:
2724-
- timetable
2725-
summary: Return a specific timetable with its associated schedules
2726-
parameters:
2727-
- name: id
2728-
in: path
2729-
description: A timetable ID
2730-
required: true
2731-
schema:
2732-
type: integer
2733-
format: int64
2734-
responses:
2735-
'200':
2736-
description: Timetable with train schedules ids
2737-
content:
2738-
application/json:
2739-
schema:
2740-
$ref: '#/components/schemas/TimetableDetailedResult'
2741-
'404':
2742-
description: Timetable not found
27432722
delete:
27442723
tags:
27452724
- timetable
@@ -2985,7 +2964,37 @@ paths:
29852964
type: string
29862965
enum:
29872966
- preprocessing_simulation_error
2988-
/timetable/{id}/train_schedule:
2967+
/timetable/{id}/train_schedules:
2968+
get:
2969+
tags:
2970+
- timetable
2971+
summary: Return a specific timetable with its associated schedules
2972+
parameters:
2973+
- name: id
2974+
in: path
2975+
description: A timetable ID
2976+
required: true
2977+
schema:
2978+
type: integer
2979+
format: int64
2980+
responses:
2981+
'200':
2982+
description: Timetable with train schedules ids
2983+
content:
2984+
application/json:
2985+
schema:
2986+
allOf:
2987+
- $ref: '#/components/schemas/PaginationStats'
2988+
- type: object
2989+
required:
2990+
- results
2991+
properties:
2992+
results:
2993+
type: array
2994+
items:
2995+
$ref: '#/components/schemas/TrainScheduleResult'
2996+
'404':
2997+
description: Timetable not found
29892998
post:
29902999
tags:
29913000
- timetable
@@ -3133,34 +3142,6 @@ paths:
31333142
'204':
31343143
description: No content when successful
31353144
/train_schedule:
3136-
post:
3137-
tags:
3138-
- train_schedule
3139-
summary: Return a specific train schedule
3140-
requestBody:
3141-
content:
3142-
application/json:
3143-
schema:
3144-
type: object
3145-
required:
3146-
- ids
3147-
properties:
3148-
ids:
3149-
type: array
3150-
items:
3151-
type: integer
3152-
format: int64
3153-
uniqueItems: true
3154-
required: true
3155-
responses:
3156-
'200':
3157-
description: Retrieve a list of train schedule
3158-
content:
3159-
application/json:
3160-
schema:
3161-
type: array
3162-
items:
3163-
$ref: '#/components/schemas/TrainScheduleResult'
31643145
delete:
31653146
tags:
31663147
- timetable

editoast/src/views/timetable.rs

+41-21
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ use axum::Extension;
1212
use derivative::Derivative;
1313
use editoast_authz::BuiltinRole;
1414
use editoast_derive::EditoastError;
15-
use editoast_schemas::train_schedule::TrainScheduleBase;
1615
use itertools::Itertools;
1716
use serde::Deserialize;
1817
use serde::Serialize;
@@ -30,25 +29,30 @@ use crate::models::prelude::*;
3029
use crate::models::timetable::Timetable;
3130
use crate::models::timetable::TimetableWithTrains;
3231
use crate::models::train_schedule::TrainSchedule;
33-
use crate::models::train_schedule::TrainScheduleChangeset;
3432
use crate::models::Infra;
3533
use crate::views::train_schedule::train_simulation_batch;
36-
use crate::views::train_schedule::TrainScheduleForm;
3734
use crate::views::train_schedule::TrainScheduleResult;
3835
use crate::views::AuthenticationExt;
3936
use crate::views::AuthorizationError;
4037
use crate::AppState;
4138
use crate::RetrieveBatch;
4239
use editoast_models::DbConnectionPoolV2;
40+
use editoast_schemas::train_schedule::TrainScheduleBase;
41+
use crate::models::train_schedule::TrainScheduleChangeset;
42+
use crate::views::train_schedule::TrainScheduleForm;
43+
44+
use super::pagination::PaginatedList as _;
45+
use super::pagination::PaginationQueryParams;
46+
use super::pagination::PaginationStats;
4347

4448
crate::routes! {
4549
"/timetable" => {
4650
post,
4751
"/{id}" => {
4852
delete,
49-
get,
53+
"/train_schedules" => get,
54+
"/train_schedules" => train_schedule,
5055
"/conflicts" => conflicts,
51-
"/train_schedule" => train_schedule,
5256
&stdcm,
5357
},
5458
},
@@ -112,37 +116,57 @@ struct TimetableIdParam {
112116
id: i64,
113117
}
114118

119+
#[derive(Serialize, ToSchema, Debug)]
120+
#[cfg_attr(test, derive(Deserialize))]
121+
struct ListTrainSchedulesResponse {
122+
#[schema(value_type = Vec<TrainScheduleResult>)]
123+
results: Vec<TrainScheduleResult>,
124+
#[serde(flatten)]
125+
stats: PaginationStats,
126+
}
127+
115128
/// Return a specific timetable with its associated schedules
116129
#[utoipa::path(
117130
get, path = "",
118131
tag = "timetable",
119132
params(TimetableIdParam),
120133
responses(
121-
(status = 200, description = "Timetable with train schedules ids", body = TimetableDetailedResult),
134+
(status = 200, description = "Timetable with train schedules ids", body = inline(ListTrainSchedulesResponse)),
122135
(status = 404, description = "Timetable not found"),
123136
),
124137
)]
125138
async fn get(
126139
State(db_pool): State<DbConnectionPoolV2>,
127140
Extension(auth): AuthenticationExt,
128141
Path(TimetableIdParam { id: timetable_id }): Path<TimetableIdParam>,
129-
) -> Result<Json<TimetableDetailedResult>> {
142+
Query(pagination_params): Query<PaginationQueryParams>,
143+
) -> Result<Json<ListTrainSchedulesResponse>> {
130144
let authorized = auth
131-
.check_roles([BuiltinRole::TimetableRead].into())
132-
.await
133-
.map_err(AuthorizationError::AuthError)?;
145+
.check_roles([BuiltinRole::TimetableRead].into())
146+
.await
147+
.map_err(AuthorizationError::AuthError)?;
134148
if !authorized {
135149
return Err(AuthorizationError::Forbidden.into());
136150
}
137151

138152
let conn = &mut db_pool.get().await?;
139-
let timetable = TimetableWithTrains::retrieve_or_fail(conn, timetable_id, || {
153+
Timetable::retrieve_or_fail(conn, timetable_id, || {
140154
TimetableError::NotFound { timetable_id }
141155
})
142156
.await?;
143-
Ok(Json(timetable.into()))
157+
158+
let settings = pagination_params
159+
.validate(25)?
160+
.into_selection_settings()
161+
.filter(move || TrainSchedule::TIMETABLE_ID.eq(timetable_id));
162+
163+
let (train_schedules, stats) = TrainSchedule::list_paginated(conn, settings).await?;
164+
let results = train_schedules.into_iter().map_into().collect();
165+
166+
Ok(Json(ListTrainSchedulesResponse {stats, results}))
144167
}
145168

169+
146170
/// Create a timetable
147171
#[utoipa::path(
148172
post, path = "",
@@ -362,24 +386,20 @@ mod tests {
362386

363387
let timetable = create_timetable(&mut pool.get_ok()).await;
364388

365-
let request = app.get(&format!("/timetable/{}", timetable.id));
389+
let request = app.get(&format!("/timetable/{}/train_schedules", timetable.id));
366390

367-
let timetable_from_response: TimetableDetailedResult =
391+
let timetable_from_response: ListTrainSchedulesResponse =
368392
app.fetch(request).assert_status(StatusCode::OK).json_into();
369-
370393
assert_eq!(
371-
timetable_from_response,
372-
TimetableDetailedResult {
373-
timetable_id: timetable.id,
374-
train_ids: vec![],
375-
}
394+
timetable_from_response.results.len(),
395+
0
376396
);
377397
}
378398

379399
#[rstest]
380400
async fn get_unexisting_timetable() {
381401
let app = TestAppBuilder::default_app();
382-
let request = app.get(&format!("/timetable/{}", 0));
402+
let request = app.get(&format!("/timetable/{}/train_schedules", 0));
383403
app.fetch(request).assert_status(StatusCode::NOT_FOUND);
384404
}
385405

editoast/src/views/train_schedule.rs

-54
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ use editoast_derive::EditoastError;
1919
use editoast_models::DbConnection;
2020
use editoast_models::DbConnectionPoolV2;
2121
use editoast_schemas::train_schedule::TrainScheduleBase;
22-
use itertools::Itertools;
2322
use serde::Deserialize;
2423
use serde::Serialize;
2524
use thiserror::Error;
@@ -67,7 +66,6 @@ crate::routes! {
6766
"/train_schedule" => {
6867
delete,
6968
"/simulation_summary" => simulation_summary,
70-
get_batch,
7169
&projection,
7270
"/{id}" => {
7371
get,
@@ -202,39 +200,6 @@ struct BatchRequest {
202200
ids: HashSet<i64>,
203201
}
204202

205-
/// Return a specific train schedule
206-
#[utoipa::path(
207-
post, path = "",
208-
tag = "train_schedule",
209-
request_body = inline(BatchRequest),
210-
responses(
211-
(status = 200, description = "Retrieve a list of train schedule", body = Vec<TrainScheduleResult>)
212-
)
213-
)]
214-
async fn get_batch(
215-
State(db_pool): State<DbConnectionPoolV2>,
216-
Extension(auth): AuthenticationExt,
217-
Json(BatchRequest { ids: train_ids }): Json<BatchRequest>,
218-
) -> Result<Json<Vec<TrainScheduleResult>>> {
219-
let authorized = auth
220-
.check_roles([BuiltinRole::InfraRead, BuiltinRole::TimetableRead].into())
221-
.await
222-
.map_err(AuthorizationError::AuthError)?;
223-
if !authorized {
224-
return Err(AuthorizationError::Forbidden.into());
225-
}
226-
227-
let conn = &mut db_pool.get().await?;
228-
let train_schedules: Vec<TrainSchedule> =
229-
TrainSchedule::retrieve_batch_or_fail(conn, train_ids, |missing| {
230-
TrainScheduleError::BatchTrainScheduleNotFound {
231-
number: missing.len(),
232-
}
233-
})
234-
.await?;
235-
Ok(Json(train_schedules.into_iter().map_into().collect()))
236-
}
237-
238203
/// Delete a train schedule and its result
239204
#[utoipa::path(
240205
delete, path = "",
@@ -899,25 +864,6 @@ mod tests {
899864
);
900865
}
901866

902-
#[rstest]
903-
async fn train_schedule_get_batch() {
904-
let app = TestAppBuilder::default_app();
905-
let pool = app.db_pool();
906-
907-
let timetable = create_timetable(&mut pool.get_ok()).await;
908-
let ts1 = create_simple_train_schedule(&mut pool.get_ok(), timetable.id).await;
909-
let ts2 = create_simple_train_schedule(&mut pool.get_ok(), timetable.id).await;
910-
let ts3 = create_simple_train_schedule(&mut pool.get_ok(), timetable.id).await;
911-
912-
// Should succeed
913-
let request = app.post("/train_schedule").json(&json!({
914-
"ids": vec![ts1.id, ts2.id, ts3.id]
915-
}));
916-
let response: Vec<TrainScheduleResult> =
917-
app.fetch(request).assert_status(StatusCode::OK).json_into();
918-
assert_eq!(response.len(), 3);
919-
}
920-
921867
#[rstest]
922868
async fn train_schedule_post() {
923869
let app = TestAppBuilder::default_app();

front/src/applications/operationalStudies/components/MacroEditor/ngeToOsrd.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,7 @@ const handleTrainrunOperation = async ({
337337
);
338338
const startDate = new Date();
339339
const newTrainSchedules = await dispatch(
340-
osrdEditoastApi.endpoints.postTimetableByIdTrainSchedule.initiate({
340+
osrdEditoastApi.endpoints.postTimetableByIdTrainSchedules.initiate({
341341
id: timeTableId,
342342
body: [
343343
{

front/src/applications/operationalStudies/components/Scenario/ScenarioContent.tsx

+7-13
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@ import ManageTrainSchedule from 'applications/operationalStudies/views/ManageTra
2020
import SimulationResults from 'applications/operationalStudies/views/SimulationResults';
2121
import { osrdEditoastApi } from 'common/api/osrdEditoastApi';
2222
import type {
23+
GetTimetableByIdTrainSchedulesApiResponse,
2324
InfraWithState,
2425
ScenarioResponse,
25-
TimetableDetailedResult,
2626
TrainScheduleResult,
2727
} from 'common/api/osrdEditoastApi';
2828
import ScenarioLoaderMessage from 'modules/scenario/components/ScenarioLoaderMessage';
@@ -35,7 +35,7 @@ import MacroEditorState from '../MacroEditor/MacroEditorState';
3535

3636
type ScenarioDescriptionProps = {
3737
scenario: ScenarioResponse;
38-
timetable: TimetableDetailedResult;
38+
timetable: GetTimetableByIdTrainSchedulesApiResponse;
3939
infra: InfraWithState;
4040
infraMetadata: { isInfraLoaded: boolean; reloadCount: number };
4141
};
@@ -68,28 +68,22 @@ const ScenarioContent = ({
6868
const [ngeDto, setNgeDto] = useState<NetzgrafikDto>();
6969

7070
const dtoImport = useCallback(async () => {
71-
const timetablePromise = dispatch(
72-
osrdEditoastApi.endpoints.getTimetableById.initiate(
73-
{ id: timetable.timetable_id },
74-
{ forceRefetch: true, subscribe: false }
75-
)
76-
);
77-
const { train_ids } = await timetablePromise.unwrap();
7871
const trainSchedulesPromise = dispatch(
79-
osrdEditoastApi.endpoints.postTrainSchedule.initiate(
80-
{ body: { ids: train_ids } },
72+
osrdEditoastApi.endpoints.getTimetableByIdTrainSchedules.initiate(
73+
{ id: timetable.results[0].timetable_id }, // TODO
8174
{ forceRefetch: true, subscribe: false }
8275
)
8376
);
84-
const schedules = (await trainSchedulesPromise.unwrap()).filter(
77+
78+
const schedules = (await trainSchedulesPromise.unwrap()).results.filter(
8579
(trainSchedule) => trainSchedule.path.length >= 2
8680
);
8781
const state = new MacroEditorState(scenario, schedules || []);
8882
await loadAndIndexNge(state, dispatch);
8983
const dto = getNgeDto(state);
9084
macroEditorState.current = state;
9185
setNgeDto(dto);
92-
}, [dispatch, scenario, timetable.timetable_id]);
86+
}, [dispatch, scenario, timetable.results[0].timetable_id]);
9387

9488
const toggleMicroMacroButton = useCallback(
9589
(isMacroMode: boolean) => {

front/src/applications/operationalStudies/hooks/useScenario.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ const useScenario = () => {
4949
}
5050
);
5151

52-
const { data: timetable } = osrdEditoastApi.endpoints.getTimetableById.useQuery(
52+
const { data: timetable } = osrdEditoastApi.endpoints.getTimetableByIdTrainSchedules.useQuery(
5353
{ id: scenario?.timetable_id! },
5454
{
5555
skip: !scenario,

0 commit comments

Comments
 (0)