From b8e64e2d2cbacb7ba29316bec935f21dba436d63 Mon Sep 17 00:00:00 2001 From: Timo Ley Date: Fri, 23 Jun 2023 15:39:33 +0200 Subject: [PATCH] feat: more clerk endpoints --- src/main.rs | 2 +- src/model.rs | 5 + src/{routes.rs => routes/bookings.rs} | 51 ++------- src/routes/clerks.rs | 37 +++++++ src/routes/mod.rs | 13 +++ src/routes/rooms.rs | 36 +++++++ src/sql/bookings.rs | 146 +++++++++++++------------- src/sql/clerks.rs | 94 +++++++++++++++++ 8 files changed, 267 insertions(+), 117 deletions(-) rename src/{routes.rs => routes/bookings.rs} (64%) create mode 100644 src/routes/clerks.rs create mode 100644 src/routes/mod.rs create mode 100644 src/routes/rooms.rs diff --git a/src/main.rs b/src/main.rs index e646096..65f859d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -63,6 +63,6 @@ async fn main() -> Result<(), ApplicationError> { async fn shutdown() { match signal::ctrl_c().await { Ok(()) => {}, - Err(err) => {}, + Err(_) => {}, } } \ No newline at end of file diff --git a/src/model.rs b/src/model.rs index 3a1481d..a2aa2b8 100644 --- a/src/model.rs +++ b/src/model.rs @@ -72,6 +72,11 @@ pub struct BookingQuery { pub months: Option, } +#[derive(Deserialize)] +pub struct ClerkMonthsQuery { + pub months: i32, +} + #[derive(Deserialize, Clone)] #[serde(rename_all = "camelCase")] pub struct BookingBody { diff --git a/src/routes.rs b/src/routes/bookings.rs similarity index 64% rename from src/routes.rs rename to src/routes/bookings.rs index 0bf6ce2..f9bfdb3 100644 --- a/src/routes.rs +++ b/src/routes/bookings.rs @@ -1,31 +1,7 @@ -use axum::{Router, routing::BoxRoute, extract::{Extension, Path, Query}, response::IntoResponse, Json, handler::{get, put}}; +use axum::{Json, extract::{Path, Extension, Query}, response::IntoResponse, Router, routing::BoxRoute, handler::{get, put}}; use hyper::StatusCode; -use crate::{Service, error::ServiceError, model::{BookingQuery, BookingBody, Booking}}; - -async fn rooms( - Extension(service): Extension, -) -> Result { - let rooms = service.get_rooms().await?; - Ok(Json(rooms)) -} - -async fn room( - Path(id): Path, - Extension(service): Extension, -) -> Result { - let room = service.get_room(id).await? - .ok_or_else(|| ServiceError::ErrorResponse(StatusCode::NOT_FOUND, Some("room not found".to_string())))?; - Ok(Json(room)) -} - -async fn cleaning_plans( - Path(id): Path, - Extension(service): Extension, -) -> Result { - let cleaning_plans = service.get_room_cleaning_plans(id).await?; - Ok(Json(cleaning_plans)) -} +use crate::{Service, error::ServiceError, model::{BookingBody, Booking, BookingQuery}}; async fn bookings( Query(query): Query, @@ -128,24 +104,13 @@ async fn update_booking( Ok(StatusCode::NO_CONTENT) } -async fn clerks( - Extension(service): Extension, -) -> Result { - let clerks = service.get_clerks().await?; - Ok(Json(clerks)) -} - pub fn routes() -> Router { Router::new() - .route("/rooms", get(rooms)) - .route("/rooms/:id", get(room)) - .route("/rooms/:id/cleaningPlans", get(cleaning_plans)) - .route("/bookings", get(bookings).post(add_booking)) - .route("/bookings/:id", get(booking).delete(delete_booking).put(update_booking)) - .route("/bookings/:id/rooms", get(booking_rooms)) - .route("/bookings/:id/rooms/:roomid", put(add_booking_room).delete(delete_booking_room)) - .route("/bookings/:id/guests", get(booking_guests)) - .route("/bookings/:id/guests/:personid", put(add_booking_guest).delete(delete_booking_guest)) - .route("/clerks", get(clerks)) + .route("/", get(bookings).post(add_booking)) + .route("/:id", get(booking).delete(delete_booking).put(update_booking)) + .route("/:id/rooms", get(booking_rooms)) + .route("/:id/rooms/:roomid", put(add_booking_room).delete(delete_booking_room)) + .route("/:id/guests", get(booking_guests)) + .route("/:id/guests/:personid", put(add_booking_guest).delete(delete_booking_guest)) .boxed() } \ No newline at end of file diff --git a/src/routes/clerks.rs b/src/routes/clerks.rs new file mode 100644 index 0000000..b80a9f3 --- /dev/null +++ b/src/routes/clerks.rs @@ -0,0 +1,37 @@ +use axum::{extract::{Extension, Query, Path}, response::IntoResponse, Json, routing::BoxRoute, Router, handler::get}; +use hyper::StatusCode; + +use crate::{error::ServiceError, Service, model::ClerkMonthsQuery}; + +async fn clerks( + Extension(service): Extension, +) -> Result { + let clerks = service.get_clerks().await?; + Ok(Json(clerks)) +} + +async fn clerk( + Path(id): Path, + Extension(service): Extension, +) -> Result { + let clerk = service.get_clerk(id).await? + .ok_or_else(|| ServiceError::ErrorResponse(StatusCode::NOT_FOUND, Some("clerk not found".to_string())))?; + Ok(Json(clerk)) +} + +async fn most_valuable_clerk( + Query(query): Query, + Extension(service): Extension, +) -> Result { + let clerk = service.get_most_valuable_clerk(query.months).await? + .ok_or_else(|| ServiceError::ErrorResponse(StatusCode::NOT_FOUND, Some("clerk not found".to_string())))?; + Ok(Json(clerk)) +} + +pub fn routes() -> Router { + Router::new() + .route("/", get(clerks)) + .route("/:id", get(clerk)) + .route("/mostValuable", get(most_valuable_clerk)) + .boxed() +} \ No newline at end of file diff --git a/src/routes/mod.rs b/src/routes/mod.rs new file mode 100644 index 0000000..42eae38 --- /dev/null +++ b/src/routes/mod.rs @@ -0,0 +1,13 @@ +use axum::{Router, routing::BoxRoute}; + +mod rooms; +mod clerks; +mod bookings; + +pub fn routes() -> Router { + Router::new() + .nest("/rooms", rooms::routes()) + .nest("/bookings", bookings::routes()) + .nest("/clerks", clerks::routes()) + .boxed() +} \ No newline at end of file diff --git a/src/routes/rooms.rs b/src/routes/rooms.rs new file mode 100644 index 0000000..c4ff968 --- /dev/null +++ b/src/routes/rooms.rs @@ -0,0 +1,36 @@ +use axum::{extract::{Extension, Path}, response::IntoResponse, Json, Router, routing::BoxRoute, handler::get}; +use hyper::StatusCode; + +use crate::{error::ServiceError, Service}; + +async fn rooms( + Extension(service): Extension, +) -> Result { + let rooms = service.get_rooms().await?; + Ok(Json(rooms)) +} + +async fn room( + Path(id): Path, + Extension(service): Extension, +) -> Result { + let room = service.get_room(id).await? + .ok_or_else(|| ServiceError::ErrorResponse(StatusCode::NOT_FOUND, Some("room not found".to_string())))?; + Ok(Json(room)) +} + +async fn cleaning_plans( + Path(id): Path, + Extension(service): Extension, +) -> Result { + let cleaning_plans = service.get_room_cleaning_plans(id).await?; + Ok(Json(cleaning_plans)) +} + +pub fn routes() -> Router { + Router::new() + .route("/", get(rooms)) + .route("/:id", get(room)) + .route("/:id/cleaningPlans", get(cleaning_plans)) + .boxed() +} \ No newline at end of file diff --git a/src/sql/bookings.rs b/src/sql/bookings.rs index 9e6aaa5..299b97d 100644 --- a/src/sql/bookings.rs +++ b/src/sql/bookings.rs @@ -1,6 +1,6 @@ use hyper::StatusCode; -use crate::{ServiceInner, model::{Room, Booking, Person, Address, BookingBody}, error::ServiceError}; +use crate::{ServiceInner, model::{Booking, BookingBody, Room, Person, Address}, error::ServiceError}; impl ServiceInner { @@ -99,6 +99,78 @@ impl ServiceInner { }) } + pub async fn add_booking(&self, booking: BookingBody) -> Result { + let session = self.pool.get_session().await?; + let stmt = session.prepare(" + INSERT INTO BOOKING + ( + bookingId, + arrivalDate, + departureDate, + cost, + pensionType, + lateCheckout, + clientId + ) + VALUES + ( + BOOKING_SEQ.nextval, + TO_DATE(:ARRDATE, 'YYYY-MM-DD'), + TO_DATE(:DEPDATE, 'YYYY-MM-DD'), + :COST, + :PENSION, + 0, + :CLIENT + ) + ").await?; + stmt.execute(( + (":ARRDATE", booking.arrival_date), + (":DEPDATE", booking.depature_date), + (":COST", booking.cost), + (":PENSION", booking.pension_type), + (":CLIENT", booking.client_id) + )).await?; + let stmt = session.prepare("SELECT BOOKING_SEQ.currval FROM dual").await?; + let row = stmt.query_single("").await? + .ok_or_else(|| ServiceError::ErrorResponse(StatusCode::INTERNAL_SERVER_ERROR, None))?; + let id: i32 = row.get(0)?; + session.commit().await?; + Ok(id) + } + + pub async fn delete_booking(&self, booking: i32) -> Result<(), ServiceError> { + let session = self.pool.get_session().await?; + let stmt = session.prepare(" + DELETE FROM Booking WHERE bookingId = :BOOKING + ").await?; + stmt.execute(booking).await?; + session.commit().await?; + Ok(()) + } + + pub async fn update_booking(&self, id: i32, booking: BookingBody) -> Result<(), ServiceError> { + let session = self.pool.get_session().await?; + let stmt = session.prepare(" + UPDATE BOOKING SET + arrivalDate = TO_DATE(:ARRDATE, 'YYYY-MM-DD'), + departureDate = TO_DATE(:DEPDATE, 'YYYY-MM-DD'), + cost = :COST, + pensionType = :PENSION, + clientId = :CLIENT + WHERE bookingId = :ID + ").await?; + stmt.execute(( + (":ID", id), + (":ARRDATE", booking.arrival_date), + (":DEPDATE", booking.depature_date), + (":COST", booking.cost), + (":PENSION", booking.pension_type), + (":CLIENT", booking.client_id) + )).await?; + session.commit().await?; + Ok(()) + } + pub async fn get_rooms_for_booking(&self, booking: i32) -> Result, ServiceError> { let session = self.pool.get_session().await?; let stmt = session.prepare(" @@ -218,76 +290,4 @@ impl ServiceInner { Ok(()) } - pub async fn add_booking(&self, booking: BookingBody) -> Result { - let session = self.pool.get_session().await?; - let stmt = session.prepare(" - INSERT INTO BOOKING - ( - bookingId, - arrivalDate, - departureDate, - cost, - pensionType, - lateCheckout, - clientId - ) - VALUES - ( - BOOKING_SEQ.nextval, - TO_DATE(:ARRDATE, 'YYYY-MM-DD'), - TO_DATE(:DEPDATE, 'YYYY-MM-DD'), - :COST, - :PENSION, - 0, - :CLIENT - ) - ").await?; - stmt.execute(( - (":ARRDATE", booking.arrival_date), - (":DEPDATE", booking.depature_date), - (":COST", booking.cost), - (":PENSION", booking.pension_type), - (":CLIENT", booking.client_id) - )).await?; - let stmt = session.prepare("SELECT BOOKING_SEQ.currval FROM dual").await?; - let row = stmt.query_single("").await? - .ok_or_else(|| ServiceError::ErrorResponse(StatusCode::INTERNAL_SERVER_ERROR, None))?; - let id: i32 = row.get(0)?; - session.commit().await?; - Ok(id) - } - - pub async fn delete_booking(&self, booking: i32) -> Result<(), ServiceError> { - let session = self.pool.get_session().await?; - let stmt = session.prepare(" - DELETE FROM Booking WHERE bookingId = :BOOKING - ").await?; - stmt.execute(booking).await?; - session.commit().await?; - Ok(()) - } - - pub async fn update_booking(&self, id: i32, booking: BookingBody) -> Result<(), ServiceError> { - let session = self.pool.get_session().await?; - let stmt = session.prepare(" - UPDATE BOOKING SET - arrivalDate = TO_DATE(:ARRDATE, 'YYYY-MM-DD'), - departureDate = TO_DATE(:DEPDATE, 'YYYY-MM-DD'), - cost = :COST, - pensionType = :PENSION, - clientId = :CLIENT - WHERE bookingId = :ID - ").await?; - stmt.execute(( - (":ID", id), - (":ARRDATE", booking.arrival_date), - (":DEPDATE", booking.depature_date), - (":COST", booking.cost), - (":PENSION", booking.pension_type), - (":CLIENT", booking.client_id) - )).await?; - session.commit().await?; - Ok(()) - } - } \ No newline at end of file diff --git a/src/sql/clerks.rs b/src/sql/clerks.rs index 56a6800..810c922 100644 --- a/src/sql/clerks.rs +++ b/src/sql/clerks.rs @@ -50,4 +50,98 @@ impl ServiceInner { Ok(clerks) } + pub async fn get_clerk(&self, id: i32) -> Result, ServiceError> { + let session = self.pool.get_session().await?; + let stmt = session.prepare(" + SELECT + c.staff, + c.salary, + p.personId, + p.name, + p.lastName, + p.age, + ad.street, + ad.houseNumber, + ad.postalCode, + ad.city, + ad.country + FROM Clerk c + INNER JOIN Person p ON p.personId = c.personId + INNER JOIN Address ad ON p.addressId = ad.addressId + WHERE c.staff = :ID + ").await?; + + let row = stmt.query_single(id).await?; + + Ok(match row { + Some(row) => { + Some(Clerk { + staff_number: row.get(0)?, + salary: row.get(1)?, + person_data: Person { + id: row.get(2)?, + first_name: row.get(3)?, + last_name: row.get(4)?, + age: row.get(5)?, + address: Address { + street: row.get(6)?, + house_number: row.get(7)?, + postal_code: row.get(8)?, + city: row.get(9)?, + country: row.get(10)?, + }, + }, + }) + }, + None => None, + }) + } + + pub async fn get_most_valuable_clerk(&self, months: i32) -> Result, ServiceError> { + let session = self.pool.get_session().await?; + let stmt = session.prepare(" + SELECT + c.staff, + c.salary, + p.personId, + p.name, + p.lastName, + p.age, + ad.street, + ad.houseNumber, + ad.postalCode, + ad.city, + ad.country + FROM Clerk c + INNER JOIN Person p ON p.personId = c.personId + INNER JOIN Address ad ON p.addressId = ad.addressId + WHERE p.personId = mostValuableClerk(:MONTHS) + ").await?; + + let row = stmt.query_single(months).await?; + + Ok(match row { + Some(row) => { + Some(Clerk { + staff_number: row.get(0)?, + salary: row.get(1)?, + person_data: Person { + id: row.get(2)?, + first_name: row.get(3)?, + last_name: row.get(4)?, + age: row.get(5)?, + address: Address { + street: row.get(6)?, + house_number: row.get(7)?, + postal_code: row.get(8)?, + city: row.get(9)?, + country: row.get(10)?, + }, + }, + }) + }, + None => None, + }) + } + } \ No newline at end of file