//! Reservations, from the administration side. //! //! Reading the whole list is an admin action for now; changing a status is //! reserved to the unit the reservation belongs to (an admin manages every //! unit), which is why the handler resolves the unit before narrowing the //! controller down. use aide::{ axum::{ApiRouter, routing::get_with}, transform::TransformOperation, }; use axum::{ Json, extract::Path, http::StatusCode, }; use schemars::JsonSchema; use serde::Deserialize; use crate::{ api::helpers::{admin, admin_desc, manager, manager_desc, unexpected_error}, core::{ controller::{AppController, ControllerError, reservations::ReservationsControllerError}, models::reservation::{Reservation, ReservationId, ReservationStatus}, }, }; pub fn routes() -> ApiRouter { ApiRouter::new() .api_route("/", get_with(get_reservations, get_reservations_docs)) .api_route( "/{id}/status", aide::axum::routing::put_with(set_status, set_status_docs), ) } #[axum::debug_handler] async fn get_reservations( ac: AppController, ) -> Result>, (StatusCode, String)> { match admin(ac)?.get_reservations().await { Ok(reservations) => Ok(Json(reservations)), Err(err) => unexpected_error("get_reservations", err), } } fn get_reservations_docs(op: TransformOperation) -> TransformOperation { op.tag("Reservations") .summary("Get every reservation") .response_with::<403, (), _>(admin_desc) } #[derive(Debug, Deserialize, JsonSchema)] struct SetStatusForm { status: ReservationStatus, } #[axum::debug_handler] async fn set_status( ac: AppController, Path(id): Path, Json(SetStatusForm { status }): Json, ) -> Result<(), (StatusCode, String)> { // The unit is not in the body: it is the reservation's own let reservation = match ac.get_reservation(id).await { Ok(reservation) => reservation, Err(err) if err.is_not_found() => { return Err((StatusCode::NOT_FOUND, "No such reservation".to_owned())); } Err(err) => return unexpected_error("set_status", err), }; match manager(ac, reservation.unit.id)? .set_reservation_status(id, status) .await { Ok(()) => Ok(()), Err(ControllerError::Reservation(err @ ReservationsControllerError::InvalidTransition(..))) => { Err((StatusCode::CONFLICT, err.to_string())) } Err(err) => unexpected_error("set_status", err), } } fn set_status_docs(op: TransformOperation) -> TransformOperation { op.tag("Reservations") .summary("Move a reservation through its state machine") .description( "Refuses a transition the state machine does not allow, with a 409.", ) .response_with::<403, (), _>(manager_desc) .response::<404, ()>() .response::<409, ()>() }