Skip to content

Commit

Permalink
editoast: update paced train endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
Wadjetz committed Feb 26, 2025
1 parent cd7dcb3 commit 83774fa
Show file tree
Hide file tree
Showing 6 changed files with 256 additions and 66 deletions.
2 changes: 1 addition & 1 deletion editoast/editoast_schemas/src/primitives/duration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ pub enum PositiveDurationError {
/// let err_s = r#"{"duration":"P1M"}"#; // 1 month
/// assert!(serde_json::from_str::<MyStruct>(err_s).is_err());
/// ```
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq)]
pub struct PositiveDuration(ChronoDuration);

impl TryFrom<ChronoDuration> for PositiveDuration {
Expand Down
59 changes: 59 additions & 0 deletions editoast/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1539,6 +1539,40 @@ paths:
responses:
'204':
description: All paced_trains have been deleted
/paced_train/{paced_train_id}:
put:
tags:
- timetable
- paced_train
summary: Update a train schedule and its result
parameters:
- name: paced_train_id
in: path
required: true
schema:
type: integer
format: int64
requestBody:
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/PacedTrainBase'
- type: object
properties:
timetable_id:
type: integer
format: int64
description: Timetable attached to the train schedule
nullable: true
required: true
responses:
'204':
description: Paced train have been updated
content:
application/json:
schema:
$ref: '#/components/schemas/PacedTrainResult'
/projects:
get:
tags:
Expand Down Expand Up @@ -4910,6 +4944,7 @@ components:
- $ref: '#/components/schemas/EditoastOperationErrorObjectNotFound'
- $ref: '#/components/schemas/EditoastPacedTrainErrorBatchPacedTrainNotFound'
- $ref: '#/components/schemas/EditoastPacedTrainErrorDatabase'
- $ref: '#/components/schemas/EditoastPacedTrainErrorPacedTrainNotFound'
- $ref: '#/components/schemas/EditoastPaginationErrorInvalidPage'
- $ref: '#/components/schemas/EditoastPaginationErrorInvalidPageSize'
- $ref: '#/components/schemas/EditoastPathfindingErrorInfraNotFound'
Expand Down Expand Up @@ -5561,6 +5596,30 @@ components:
type: string
enum:
- editoast:paced_train:Database
EditoastPacedTrainErrorPacedTrainNotFound:
type: object
required:
- type
- status
- message
properties:
context:
type: object
required:
- paced_train_id
properties:
paced_train_id:
type: integer
message:
type: string
status:
type: integer
enum:
- 400
type:
type: string
enum:
- editoast:paced_train:PacedTrainNotFound
EditoastPaginationErrorInvalidPage:
type: object
required:
Expand Down
117 changes: 113 additions & 4 deletions editoast/src/views/paced_train.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
use crate::error::Result;
use crate::models::prelude::*;
use crate::views::ListId;
use axum::extract::Json;
use axum::extract::Path;
use axum::extract::State;
use axum::{response::IntoResponse, Extension};
use editoast_authz::BuiltinRole;
Expand All @@ -11,9 +9,13 @@ use editoast_schemas::paced_train::PacedTrainBase;
use editoast_schemas::train_schedule::TrainScheduleBase;
use serde::{Deserialize, Serialize};
use thiserror::Error;
use utoipa::IntoParams;
use utoipa::ToSchema;

use super::AuthenticationExt;
use crate::error::Result;
use crate::models::prelude::*;
use crate::views::ListId;
use crate::{
models::paced_train::{PacedTrain, PacedTrainChangeset},
views::AuthorizationError,
Expand All @@ -22,6 +24,9 @@ use crate::{
crate::routes! {
"/paced_train" => {
delete,
"/{paced_train_id}" => {
update_paced_train,
},
},
}

Expand All @@ -36,12 +41,14 @@ enum PacedTrainError {
#[error("{count} paced train(s) could not be found")]
#[editoast_error(status = 404)]
BatchPacedTrainNotFound { count: usize },
#[error("Paced train {paced_train_id} does not exist")]
PacedTrainNotFound { paced_train_id: i64 },
#[error(transparent)]
#[editoast_error(status = 500)]
Database(#[from] editoast_models::model::Error),
}

#[derive(Debug, Clone, Serialize, ToSchema)]
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub(in crate::views) struct PacedTrainForm {
/// Timetable attached to the train schedule
pub(in crate::views) timetable_id: Option<i64>,
Expand Down Expand Up @@ -128,3 +135,105 @@ async fn delete(

Ok(axum::http::StatusCode::NO_CONTENT)
}

#[derive(Debug, IntoParams, Deserialize)]
struct PacedTrainIdParam {
paced_train_id: i64,
}

/// Update a train schedule and its result
#[utoipa::path(
put, path = "",
tag = "timetable,paced_train",
params(PacedTrainIdParam),
request_body = inline(PacedTrainForm),
responses(
(status = 204, body = PacedTrainResult, description = "Paced train have been updated")
)
)]
async fn update_paced_train(
State(db_pool): State<DbConnectionPoolV2>,
Extension(auth): AuthenticationExt,
Path(PacedTrainIdParam { paced_train_id }): Path<PacedTrainIdParam>,
Json(paced_train_form): Json<PacedTrainForm>,
) -> Result<impl IntoResponse> {
let authorized = auth
.check_roles([BuiltinRole::InfraRead, BuiltinRole::TimetableWrite].into())
.await
.map_err(AuthorizationError::AuthError)?;
if !authorized {
return Err(AuthorizationError::Forbidden.into());
}

let conn = &mut db_pool.get().await?;
let paced_train_changeset: PacedTrainChangeset = paced_train_form.into();
let paced_train = paced_train_changeset
.update_or_fail(conn, paced_train_id, || {
PacedTrainError::PacedTrainNotFound { paced_train_id }
})
.await?;

let paced_train_result: PacedTrainResult = paced_train.into();

Ok(Json(paced_train_result))
}

#[cfg(test)]
mod tests {
use axum::http::StatusCode;
use chrono::Duration;
use editoast_schemas::paced_train::PacedTrainBase;
use pretty_assertions::assert_eq;
use rstest::rstest;
use serde_json::json;

use crate::models::fixtures::create_timetable;
use crate::models::fixtures::simple_train_schedule_base;
use crate::models::paced_train::PacedTrainChangeset;
use crate::models::prelude::*;
use crate::views::paced_train::PacedTrainForm;
use crate::views::paced_train::PacedTrainResult;
use crate::views::test_app::TestAppBuilder;

fn create_paced_train_form(timetable_id: i64) -> PacedTrainForm {
let train_schedule = simple_train_schedule_base();
PacedTrainForm {
timetable_id: Some(timetable_id),
paced_train: PacedTrainBase {
train_schedule_base: train_schedule,
duration: Duration::minutes(120).try_into().unwrap(),
step: Duration::seconds(30).try_into().unwrap(),
},
}
}

#[rstest]
async fn update_paced_train() {
let app = TestAppBuilder::default_app();
let pool = app.db_pool();

let timetable = create_timetable(&mut pool.get_ok()).await;
let mut paced_train_form = create_paced_train_form(timetable.id);
let paced_train_changeset: PacedTrainChangeset = paced_train_form.clone().into();
let paced_train = paced_train_changeset
.create(&mut pool.get_ok())
.await
.expect("Failed to create paced train");

paced_train_form.paced_train.duration = Duration::minutes(90).try_into().unwrap();
paced_train_form.paced_train.step = Duration::minutes(15).try_into().unwrap();

let request = app
.put(format!("/paced_train/{}", paced_train.id).as_str())
.json(&json!(&paced_train_form));

let response: PacedTrainResult =
app.fetch(request).assert_status(StatusCode::OK).json_into();

assert_eq!(
response.paced_train.duration,
paced_train_form.paced_train.duration
);
assert_eq!(response.paced_train.step, paced_train_form.paced_train.step);
}
}
3 changes: 2 additions & 1 deletion front/public/locales/en/errors.json
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,8 @@
},
"paced_train": {
"Database": "Internal error (database)",
"BatchPacedTrainNotFound": "Some Paced trains could not be found"
"BatchPacedTrainNotFound": "Some Paced trains could not be found",
"PacedTrainNotFound": "Paced train '{{paced_train_id}}' not found"
},
"url": {
"InvalidUrl": "Invalid url '{{url}}'"
Expand Down
3 changes: 2 additions & 1 deletion front/public/locales/fr/errors.json
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,8 @@
},
"paced_train": {
"Database": "Erreur interne (base de données)",
"BatchPacedTrainNotFound": "Certaines missions sont introuvables"
"BatchPacedTrainNotFound": "Certaines missions sont introuvables",
"PacedTrainNotFound": "Mission '{{paced_train_id}}' non trouvée"
},
"url": {
"InvalidUrl": "Url invalide '{{url}}'"
Expand Down
Loading

0 comments on commit 83774fa

Please sign in to comment.