Skip to content

Commit

Permalink
editoast: work-schedules: add endpoints for easier edition and viewing
Browse files Browse the repository at this point in the history
Signed-off-by: Eloi Charpentier <eloi.charpentier.42@gmail.com>
  • Loading branch information
eckter committed Nov 19, 2024
1 parent 535d81d commit 7525cf6
Show file tree
Hide file tree
Showing 2 changed files with 195 additions and 21 deletions.
2 changes: 1 addition & 1 deletion editoast/src/models/work_schedules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ pub enum WorkScheduleType {
Track,
}

#[derive(Debug, Default, Clone, Model)]
#[derive(Debug, Default, Clone, Model, Serialize, Deserialize, ToSchema)]
#[model(table = editoast_models::tables::work_schedule)]
#[model(gen(batch_ops = c, list))]
pub struct WorkSchedule {
Expand Down
214 changes: 194 additions & 20 deletions editoast/src/views/work_schedules.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use axum::extract::Json;
use axum::extract::State;
use axum::extract::{Json, Path};
use axum::Extension;
use chrono::DateTime;
use chrono::Utc;
Expand All @@ -12,7 +12,7 @@ use serde::Deserialize;
use serde::Serialize;
use std::result::Result as StdResult;
use thiserror::Error;
use utoipa::ToSchema;
use utoipa::{IntoParams, ToSchema};

use crate::core::pathfinding::TrackRange as CoreTrackRange;
use crate::error::InternalError;
Expand All @@ -32,6 +32,14 @@ crate::routes! {
"/work_schedules" => {
create,
"/project_path" => project_path,
"/group" => {
create_group,
list_groups,
"/{id}" => {
get_group,
put_in_group,
},
},
},
}

Expand All @@ -41,12 +49,21 @@ editoast_common::schemas! {
WorkScheduleItemForm,
}

#[derive(IntoParams, Deserialize)]
struct WorkScheduleGroupIdParam {
/// A work schedule group ID
id: i64,
}

#[derive(Debug, Error, EditoastError)]
#[editoast_error(base_id = "work_schedule")]
enum WorkScheduleError {
#[error("Name '{name}' already used")]
#[editoast_error(status = 400)]
NameAlreadyUsed { name: String },
#[error("Work schedule group '{id}' not found")]
#[editoast_error(status = 404)]
WorkScheduleGroupNotFound { id: i64 },
}

pub fn map_diesel_error(e: InternalError, name: impl AsRef<str>) -> InternalError {
Expand Down Expand Up @@ -149,36 +166,31 @@ async fn create(
work_schedules,
}): Json<WorkScheduleCreateForm>,
) -> Result<Json<WorkScheduleCreateResponse>> {
let authorized = auth
.check_roles([BuiltinRole::WorkScheduleWrite].into())
.await
.map_err(AuthorizationError::AuthError)?;
if !authorized {
return Err(AuthorizationError::Unauthorized.into());
}
// Create the group (using the method for the create group endpoint)
let work_schedule_group = create_group(
State(app_state.clone()),
Extension(auth),
Json(WorkScheduleGroupCreateForm {
work_schedule_group_name,
}),
)
.await?;

let db_pool = app_state.db_pool_v2.clone();
let conn = &mut db_pool.get().await?;

// Create the work_schedule_group
let work_schedule_group = WorkScheduleGroup::changeset()
.name(work_schedule_group_name.clone())
.creation_date(Utc::now())
.create(conn)
.await;
let work_schedule_group =
work_schedule_group.map_err(|e| map_diesel_error(e, work_schedule_group_name))?;

// Create work schedules
let work_schedules_changesets = work_schedules
.into_iter()
.map(|work_schedule| work_schedule.into_work_schedule_changeset(work_schedule_group.id))
.map(|work_schedule| {
work_schedule.into_work_schedule_changeset(work_schedule_group.work_schedule_group_id)
})
.collect::<Vec<_>>();
let _work_schedules: Vec<_> =
WorkSchedule::create_batch(conn, work_schedules_changesets).await?;

Ok(Json(WorkScheduleCreateResponse {
work_schedule_group_id: work_schedule_group.id,
work_schedule_group_id: work_schedule_group.work_schedule_group_id,
}))
}

Expand Down Expand Up @@ -275,6 +287,168 @@ async fn project_path(
Ok(Json(projections))
}

#[derive(Serialize, Deserialize, ToSchema)]
struct WorkScheduleGroupCreateForm {
work_schedule_group_name: String,
}

#[derive(Serialize, Deserialize, ToSchema)]
struct WorkScheduleGroupCreateResponse {
work_schedule_group_id: i64,
}

#[utoipa::path(
post, path = "",
tag = "work_schedules",
request_body = WorkScheduleGroupCreateForm,
responses(
(status = 201, body = WorkScheduleGroupCreateResponse, description = "The id of the created work schedule group"),
)
)]
async fn create_group(
State(app_state): State<AppState>,
Extension(auth): AuthenticationExt,
Json(WorkScheduleGroupCreateForm {
work_schedule_group_name,
}): Json<WorkScheduleGroupCreateForm>,
) -> Result<Json<WorkScheduleGroupCreateResponse>> {
let authorized = auth
.check_roles([BuiltinRole::WorkScheduleWrite].into())
.await
.map_err(AuthorizationError::AuthError)?;
if !authorized {
return Err(AuthorizationError::Unauthorized.into());
}

let db_pool = app_state.db_pool_v2.clone();
let conn = &mut db_pool.get().await?;

// Create the work_schedule_group
let work_schedule_group = WorkScheduleGroup::changeset()
.name(work_schedule_group_name.clone())
.creation_date(Utc::now())
.create(conn)
.await;
let work_schedule_group =
work_schedule_group.map_err(|e| map_diesel_error(e, work_schedule_group_name))?;
Ok(Json(WorkScheduleGroupCreateResponse {
work_schedule_group_id: work_schedule_group.id,
}))
}

#[utoipa::path(
get, path = "",
tag = "work_schedules",
responses(
(status = 201, body = Vec<i64>, description = "The existing work schedule group ids"),
)
)]
async fn list_groups(
State(app_state): State<AppState>,
Extension(auth): AuthenticationExt,
) -> Result<Json<Vec<i64>>> {
let authorized = auth
.check_roles([BuiltinRole::WorkScheduleRead].into())
.await
.map_err(AuthorizationError::AuthError)?;
if !authorized {
return Err(AuthorizationError::Unauthorized.into());
}

let db_pool = app_state.db_pool_v2.clone();
let conn = &mut db_pool.get().await?;

let selection_setting = SelectionSettings::new();
let work_schedule_group_ids = WorkScheduleGroup::list(conn, selection_setting)
.await?
.iter()
.map(|group| group.id)
.collect::<Vec<i64>>();

Ok(Json(work_schedule_group_ids))
}

#[derive(Serialize, Deserialize, ToSchema)]
struct WorkScheduleInGroupCreateForm {
work_schedules: Vec<WorkScheduleItemForm>,
}

#[utoipa::path(
put, path = "",
tag = "work_schedules",
request_body = WorkScheduleInGroupCreateForm,
params(WorkScheduleGroupIdParam),
responses(
(status = 201, description = "The work schedules have been created", body = Vec<WorkSchedule>),
(status = 404, description = "Work schedule group not found"),
)
)]
async fn put_in_group(
State(app_state): State<AppState>,
Extension(auth): AuthenticationExt,
raw_group_id: Path<WorkScheduleGroupIdParam>,
Json(WorkScheduleInGroupCreateForm { work_schedules }): Json<WorkScheduleInGroupCreateForm>,
) -> Result<Json<Vec<WorkSchedule>>> {
let authorized = auth
.check_roles([BuiltinRole::WorkScheduleWrite].into())
.await
.map_err(AuthorizationError::AuthError)?;
if !authorized {
return Err(AuthorizationError::Unauthorized.into());
}
let group_id = raw_group_id.id;

let db_pool = app_state.db_pool_v2.clone();
let conn = &mut db_pool.get().await?;

// Check that the group exists
WorkScheduleGroup::retrieve_or_fail(conn, group_id, || {
WorkScheduleError::WorkScheduleGroupNotFound { id: group_id }
})
.await?;

// Create work schedules
let work_schedules_changesets = work_schedules
.into_iter()
.map(|work_schedule| work_schedule.into_work_schedule_changeset(group_id))
.collect::<Vec<_>>();
let work_schedules = WorkSchedule::create_batch(conn, work_schedules_changesets).await?;

Ok(Json(work_schedules))
}

#[utoipa::path(
get, path = "",
tag = "work_schedules",
params(WorkScheduleGroupIdParam),
responses(
(status = 200, description = "The work schedules in the group", body = Vec<WorkScheduleResponse>),
)
)]
async fn get_group(
State(app_state): State<AppState>,
Extension(auth): AuthenticationExt,
raw_group_id: Path<WorkScheduleGroupIdParam>,
) -> Result<Json<Vec<WorkSchedule>>> {
let authorized = auth
.check_roles([BuiltinRole::WorkScheduleRead].into())
.await
.map_err(AuthorizationError::AuthError)?;
if !authorized {
return Err(AuthorizationError::Unauthorized.into());
}
let group_id = raw_group_id.id;

let db_pool = app_state.db_pool_v2.clone();
let conn = &mut db_pool.get().await?;

let selection_setting =
SelectionSettings::new().filter(move || WorkSchedule::WORK_SCHEDULE_GROUP_ID.eq(group_id));
let work_schedules = WorkSchedule::list(conn, selection_setting).await?;

Ok(Json(work_schedules))
}

#[cfg(test)]
pub mod tests {
use axum::http::StatusCode;
Expand Down

0 comments on commit 7525cf6

Please sign in to comment.