feat: more clerk endpoints

This commit is contained in:
Timo Ley 2023-06-23 15:39:33 +02:00
parent fc2e993d31
commit b8e64e2d2c
8 changed files with 267 additions and 117 deletions

View file

@ -63,6 +63,6 @@ async fn main() -> Result<(), ApplicationError> {
async fn shutdown() { async fn shutdown() {
match signal::ctrl_c().await { match signal::ctrl_c().await {
Ok(()) => {}, Ok(()) => {},
Err(err) => {}, Err(_) => {},
} }
} }

View file

@ -72,6 +72,11 @@ pub struct BookingQuery {
pub months: Option<i32>, pub months: Option<i32>,
} }
#[derive(Deserialize)]
pub struct ClerkMonthsQuery {
pub months: i32,
}
#[derive(Deserialize, Clone)] #[derive(Deserialize, Clone)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct BookingBody { pub struct BookingBody {

View file

@ -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 hyper::StatusCode;
use crate::{Service, error::ServiceError, model::{BookingQuery, BookingBody, Booking}}; use crate::{Service, error::ServiceError, model::{BookingBody, Booking, BookingQuery}};
async fn rooms(
Extension(service): Extension<Service>,
) -> Result<impl IntoResponse, ServiceError> {
let rooms = service.get_rooms().await?;
Ok(Json(rooms))
}
async fn room(
Path(id): Path<i32>,
Extension(service): Extension<Service>,
) -> Result<impl IntoResponse, ServiceError> {
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<i32>,
Extension(service): Extension<Service>,
) -> Result<impl IntoResponse, ServiceError> {
let cleaning_plans = service.get_room_cleaning_plans(id).await?;
Ok(Json(cleaning_plans))
}
async fn bookings( async fn bookings(
Query(query): Query<BookingQuery>, Query(query): Query<BookingQuery>,
@ -128,24 +104,13 @@ async fn update_booking(
Ok(StatusCode::NO_CONTENT) Ok(StatusCode::NO_CONTENT)
} }
async fn clerks(
Extension(service): Extension<Service>,
) -> Result<impl IntoResponse, ServiceError> {
let clerks = service.get_clerks().await?;
Ok(Json(clerks))
}
pub fn routes() -> Router<BoxRoute> { pub fn routes() -> Router<BoxRoute> {
Router::new() Router::new()
.route("/rooms", get(rooms)) .route("/", get(bookings).post(add_booking))
.route("/rooms/:id", get(room)) .route("/:id", get(booking).delete(delete_booking).put(update_booking))
.route("/rooms/:id/cleaningPlans", get(cleaning_plans)) .route("/:id/rooms", get(booking_rooms))
.route("/bookings", get(bookings).post(add_booking)) .route("/:id/rooms/:roomid", put(add_booking_room).delete(delete_booking_room))
.route("/bookings/:id", get(booking).delete(delete_booking).put(update_booking)) .route("/:id/guests", get(booking_guests))
.route("/bookings/:id/rooms", get(booking_rooms)) .route("/:id/guests/:personid", put(add_booking_guest).delete(delete_booking_guest))
.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))
.boxed() .boxed()
} }

37
src/routes/clerks.rs Normal file
View file

@ -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<Service>,
) -> Result<impl IntoResponse, ServiceError> {
let clerks = service.get_clerks().await?;
Ok(Json(clerks))
}
async fn clerk(
Path(id): Path<i32>,
Extension(service): Extension<Service>,
) -> Result<impl IntoResponse, ServiceError> {
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<ClerkMonthsQuery>,
Extension(service): Extension<Service>,
) -> Result<impl IntoResponse, ServiceError> {
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<BoxRoute> {
Router::new()
.route("/", get(clerks))
.route("/:id", get(clerk))
.route("/mostValuable", get(most_valuable_clerk))
.boxed()
}

13
src/routes/mod.rs Normal file
View file

@ -0,0 +1,13 @@
use axum::{Router, routing::BoxRoute};
mod rooms;
mod clerks;
mod bookings;
pub fn routes() -> Router<BoxRoute> {
Router::new()
.nest("/rooms", rooms::routes())
.nest("/bookings", bookings::routes())
.nest("/clerks", clerks::routes())
.boxed()
}

36
src/routes/rooms.rs Normal file
View file

@ -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<Service>,
) -> Result<impl IntoResponse, ServiceError> {
let rooms = service.get_rooms().await?;
Ok(Json(rooms))
}
async fn room(
Path(id): Path<i32>,
Extension(service): Extension<Service>,
) -> Result<impl IntoResponse, ServiceError> {
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<i32>,
Extension(service): Extension<Service>,
) -> Result<impl IntoResponse, ServiceError> {
let cleaning_plans = service.get_room_cleaning_plans(id).await?;
Ok(Json(cleaning_plans))
}
pub fn routes() -> Router<BoxRoute> {
Router::new()
.route("/", get(rooms))
.route("/:id", get(room))
.route("/:id/cleaningPlans", get(cleaning_plans))
.boxed()
}

View file

@ -1,6 +1,6 @@
use hyper::StatusCode; 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 { impl ServiceInner {
@ -99,6 +99,78 @@ impl ServiceInner {
}) })
} }
pub async fn add_booking(&self, booking: BookingBody) -> Result<i32, ServiceError> {
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<Vec<Room>, ServiceError> { pub async fn get_rooms_for_booking(&self, booking: i32) -> Result<Vec<Room>, ServiceError> {
let session = self.pool.get_session().await?; let session = self.pool.get_session().await?;
let stmt = session.prepare(" let stmt = session.prepare("
@ -218,76 +290,4 @@ impl ServiceInner {
Ok(()) Ok(())
} }
pub async fn add_booking(&self, booking: BookingBody) -> Result<i32, ServiceError> {
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(())
}
} }

View file

@ -50,4 +50,98 @@ impl ServiceInner {
Ok(clerks) Ok(clerks)
} }
pub async fn get_clerk(&self, id: i32) -> Result<Option<Clerk>, 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<Option<Clerk>, 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,
})
}
} }