Add rest service for booking

This commit is contained in:
Simon Goller 2024-05-09 07:16:38 +02:00
parent d8dcc7099d
commit 8ea16624ad
4 changed files with 162 additions and 4 deletions

View file

@ -18,17 +18,26 @@ type SalesPersonService = service_impl::sales_person::SalesPersonServiceImpl<
ClockService, ClockService,
UuidService, UuidService,
>; >;
type BookingService = service_impl::booking::BookingServiceImpl<
dao_impl::booking::BookingDaoImpl,
PermissionService,
ClockService,
UuidService,
>;
#[derive(Clone)] #[derive(Clone)]
pub struct RestStateImpl { pub struct RestStateImpl {
permission_service: Arc<PermissionService>, permission_service: Arc<PermissionService>,
slot_service: Arc<SlotService>, slot_service: Arc<SlotService>,
sales_person_service: Arc<SalesPersonService>, sales_person_service: Arc<SalesPersonService>,
booking_service: Arc<BookingService>,
} }
impl rest::RestStateDef for RestStateImpl { impl rest::RestStateDef for RestStateImpl {
type PermissionService = PermissionService; type PermissionService = PermissionService;
type SlotService = SlotService; type SlotService = SlotService;
type SalesPersonService = SalesPersonService; type SalesPersonService = SalesPersonService;
type BookingService = BookingService;
fn permission_service(&self) -> Arc<Self::PermissionService> { fn permission_service(&self) -> Arc<Self::PermissionService> {
self.permission_service.clone() self.permission_service.clone()
@ -39,12 +48,16 @@ impl rest::RestStateDef for RestStateImpl {
fn sales_person_service(&self) -> Arc<Self::SalesPersonService> { fn sales_person_service(&self) -> Arc<Self::SalesPersonService> {
self.sales_person_service.clone() self.sales_person_service.clone()
} }
fn booking_service(&self) -> Arc<Self::BookingService> {
self.booking_service.clone()
}
} }
impl RestStateImpl { impl RestStateImpl {
pub fn new(pool: Arc<sqlx::Pool<sqlx::Sqlite>>) -> Self { pub fn new(pool: Arc<sqlx::Pool<sqlx::Sqlite>>) -> Self {
let permission_dao = dao_impl::PermissionDaoImpl::new(pool.clone()); let permission_dao = dao_impl::PermissionDaoImpl::new(pool.clone());
let slot_dao = dao_impl::slot::SlotDaoImpl::new(pool.clone()); let slot_dao = dao_impl::slot::SlotDaoImpl::new(pool.clone());
let sales_person_dao = dao_impl::sales_person::SalesPersonDaoImpl::new(pool); let sales_person_dao = dao_impl::sales_person::SalesPersonDaoImpl::new(pool.clone());
let booking_dao = dao_impl::booking::BookingDaoImpl::new(pool);
// Always authenticate with DEVUSER during development. // Always authenticate with DEVUSER during development.
// This is used to test the permission service locally without a login service. // This is used to test the permission service locally without a login service.
@ -69,13 +82,20 @@ impl RestStateImpl {
Arc::new(service_impl::sales_person::SalesPersonServiceImpl::new( Arc::new(service_impl::sales_person::SalesPersonServiceImpl::new(
sales_person_dao.into(), sales_person_dao.into(),
permission_service.clone(), permission_service.clone(),
clock_service, clock_service.clone(),
uuid_service, uuid_service.clone(),
)); ));
let booking_service = Arc::new(service_impl::booking::BookingServiceImpl::new(
booking_dao.into(),
permission_service.clone(),
clock_service,
uuid_service,
));
Self { Self {
permission_service, permission_service,
slot_service, slot_service,
sales_person_service, sales_person_service,
booking_service,
} }
} }
} }

135
rest/src/booking.rs Normal file
View file

@ -0,0 +1,135 @@
use std::sync::Arc;
use axum::body::Body;
use axum::extract::Path;
use axum::routing::{delete, get, post};
use axum::{extract::State, response::Response};
use axum::{Json, Router};
use serde::{Deserialize, Serialize};
use time::PrimitiveDateTime;
use uuid::Uuid;
use crate::{error_handler, RestStateDef};
use service::booking::{BookingService, Booking};
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct BookingTO {
#[serde(default)]
pub id: Uuid,
pub sales_person_id: Uuid,
pub slot_id: Uuid,
pub calendar_week: i32,
pub year: u32,
#[serde(default)]
pub created: Option<PrimitiveDateTime>,
#[serde(default)]
pub deleted: Option<PrimitiveDateTime>,
#[serde(rename = "$version")]
#[serde(default)]
pub version: Uuid,
}
impl From<&Booking> for BookingTO {
fn from(booking: &Booking) -> Self {
Self {
id: booking.id,
sales_person_id: booking.sales_person_id,
slot_id: booking.slot_id,
calendar_week: booking.calendar_week,
year: booking.year,
created: booking.created,
deleted: booking.deleted,
version: booking.version,
}
}
}
impl From<&BookingTO> for Booking {
fn from(booking: &BookingTO) -> Self {
Self {
id: booking.id,
sales_person_id: booking.sales_person_id,
slot_id: booking.slot_id,
calendar_week: booking.calendar_week,
year: booking.year,
created: booking.created,
deleted: booking.deleted,
version: booking.version,
}
}
}
pub fn generate_route<RestState: RestStateDef>() -> Router<RestState> {
Router::new()
.route("/", get(get_all_bookings::<RestState>))
.route("/:id", get(get_booking::<RestState>))
.route("/", post(create_booking::<RestState>))
.route("/:id", delete(delete_booking::<RestState>))
}
pub async fn get_all_bookings<RestState: RestStateDef>(
rest_state: State<RestState>,
) -> Response {
error_handler(
(async {
let bookings: Arc<[BookingTO]> = rest_state
.booking_service()
.get_all(())
.await?
.iter()
.map(BookingTO::from)
.collect();
Ok(Response::builder()
.status(200)
.body(Body::new(serde_json::to_string(&bookings).unwrap()))
.unwrap())
})
.await,
)
}
pub async fn get_booking<RestState: RestStateDef>(
rest_state: State<RestState>,
Path(booking_id): Path<Uuid>,
) -> Response {
error_handler(
(async {
let booking = rest_state.booking_service().get(booking_id, ()).await?;
Ok(Response::builder()
.status(200)
.body(Body::new(serde_json::to_string(&BookingTO::from(&booking)).unwrap()))
.unwrap())
})
.await,
)
}
pub async fn create_booking<RestState: RestStateDef>(
rest_state: State<RestState>,
Json(booking): Json<BookingTO>,
) -> Response {
error_handler(
(async {
let booking = rest_state.booking_service().create(&Booking::from(&booking), ()).await?;
Ok(Response::builder()
.status(200)
.body(Body::new(serde_json::to_string(&BookingTO::from(&booking)).unwrap()))
.unwrap())
})
.await,
)
}
pub async fn delete_booking<RestState: RestStateDef>(
rest_state: State<RestState>,
Path(booking_id): Path<Uuid>,
) -> Response {
error_handler(
(async {
rest_state.booking_service().delete(booking_id, ()).await?;
Ok(Response::builder()
.status(200)
.body(Body::empty())
.unwrap())
})
.await,
)
}

View file

@ -1,5 +1,6 @@
use std::{convert::Infallible, sync::Arc}; use std::{convert::Infallible, sync::Arc};
mod booking;
mod permission; mod permission;
mod sales_person; mod sales_person;
mod slot; mod slot;
@ -139,10 +140,12 @@ pub trait RestStateDef: Clone + Send + Sync + 'static {
+ Send + Send
+ Sync + Sync
+ 'static; + 'static;
type BookingService: service::booking::BookingService<Context = Context> + Send + Sync + 'static;
fn permission_service(&self) -> Arc<Self::PermissionService>; fn permission_service(&self) -> Arc<Self::PermissionService>;
fn slot_service(&self) -> Arc<Self::SlotService>; fn slot_service(&self) -> Arc<Self::SlotService>;
fn sales_person_service(&self) -> Arc<Self::SalesPersonService>; fn sales_person_service(&self) -> Arc<Self::SalesPersonService>;
fn booking_service(&self) -> Arc<Self::BookingService>;
} }
pub async fn start_server<RestState: RestStateDef>(rest_state: RestState) { pub async fn start_server<RestState: RestStateDef>(rest_state: RestState) {
@ -150,6 +153,7 @@ pub async fn start_server<RestState: RestStateDef>(rest_state: RestState) {
.nest("/permission", permission::generate_route()) .nest("/permission", permission::generate_route())
.nest("/slot", slot::generate_route()) .nest("/slot", slot::generate_route())
.nest("/sales-person", sales_person::generate_route()) .nest("/sales-person", sales_person::generate_route())
.nest("/booking", booking::generate_route())
.with_state(rest_state); .with_state(rest_state);
let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") let listener = tokio::net::TcpListener::bind("127.0.0.1:3000")
.await .await

View file

@ -1,5 +1,4 @@
use async_trait::async_trait; use async_trait::async_trait;
use dao::booking;
use service::{ use service::{
booking::{Booking, BookingService}, booking::{Booking, BookingService},
ServiceError, ValidationFailureItem, ServiceError, ValidationFailureItem,