From d45ccf952342aed651aac721d69fdb776b9511e2 Mon Sep 17 00:00:00 2001 From: Simon Goller Date: Sun, 28 Apr 2024 22:30:10 +0200 Subject: [PATCH] REST servcie restructuring App devices which implementation will be used. For now, the types of the implementations must be defined manually. This is requied because the State in the REST endpoints must contain a type which can provide all services. --- app/src/main.rs | 64 ++++++++++++++++++++++++++++++++---------- rest/src/hello.rs | 14 +++++++++ rest/src/lib.rs | 52 +++++++++------------------------- rest/src/permission.rs | 37 ++++++++++++++++++++++++ 4 files changed, 114 insertions(+), 53 deletions(-) create mode 100644 rest/src/hello.rs create mode 100644 rest/src/permission.rs diff --git a/app/src/main.rs b/app/src/main.rs index ab1021b..f77a62e 100644 --- a/app/src/main.rs +++ b/app/src/main.rs @@ -2,6 +2,53 @@ use std::sync::Arc; use sqlx::SqlitePool; +type PermissionService = + service_impl::PermissionServiceImpl; +type HelloService = service_impl::HelloServiceImpl; + +#[derive(Clone)] +pub struct RestStateImpl { + hello_service: Arc, + permission_service: Arc, +} +impl rest::RestStateDef for RestStateImpl { + type HelloService = HelloService; + type PermissionService = PermissionService; + + fn hello_service(&self) -> Arc { + self.hello_service.clone() + } + fn permission_service(&self) -> Arc { + self.permission_service.clone() + } +} +impl RestStateImpl { + pub fn new(pool: Arc>) -> Self { + let hello_dao = dao_impl::HelloDaoImpl::new(pool.clone()); + let permission_dao = dao_impl::PermissionDaoImpl::new(pool); + + // Always authenticate with DEVUSER during development. + // This is used to test the permission service locally without a login service. + // + // TODO: Implement a proper authentication service when used in produciton. Maybe + // use differnet implementations on debug then on release. Or control it via a + // feature. + let user_service = service_impl::UserServiceDev; + let permission_service = Arc::new(service_impl::PermissionServiceImpl::new( + permission_dao.into(), + user_service.into(), + )); + let hello_service = Arc::new(service_impl::HelloServiceImpl::new( + hello_dao.into(), + permission_service.clone(), + )); + Self { + hello_service, + permission_service, + } + } +} + #[tokio::main] async fn main() { let pool = Arc::new( @@ -9,19 +56,6 @@ async fn main() { .await .expect("Could not connect to database"), ); - let hello_dao = dao_impl::HelloDaoImpl::new(pool.clone()); - let permission_dao = dao_impl::PermissionDaoImpl::new(pool); - - // Always authenticate with DEVUSER during development. - // This is used to test the permission service locally without a login service. - // - // TODO: Implement a proper authentication service when used in produciton. Maybe - // use differnet implementations on debug then on release. Or control it via a - // feature. - let user_service = service_impl::UserServiceDev; - let permission_service = - service_impl::PermissionServiceImpl::new(permission_dao.into(), user_service.into()); - let hello_service = - service_impl::HelloServiceImpl::new(hello_dao.into(), permission_service.into()); - rest::start_server(hello_service).await + let rest_state = RestStateImpl::new(pool); + rest::start_server(rest_state).await } diff --git a/rest/src/hello.rs b/rest/src/hello.rs new file mode 100644 index 0000000..4e9ae6b --- /dev/null +++ b/rest/src/hello.rs @@ -0,0 +1,14 @@ +use axum::{extract::State, response::Response}; + +use crate::{error_handler, RestStateDef, RoString}; +use service::HelloService; + +pub async fn hello(State(rest_state): State) -> Response { + error_handler( + (async { + let string = rest_state.hello_service().hello().await?; + Ok(RoString::from(string).into()) + }) + .await, + ) +} diff --git a/rest/src/lib.rs b/rest/src/lib.rs index 7dddcc5..fc97bdc 100644 --- a/rest/src/lib.rs +++ b/rest/src/lib.rs @@ -1,14 +1,9 @@ -use serde::{Deserialize, Serialize}; use std::{convert::Infallible, sync::Arc}; -use uuid::Uuid; -use axum::{ - body::Body, - extract::State, - response::Response, - routing::{get, post}, - Json, Router, -}; +mod hello; +mod permission; + +use axum::{body::Body, response::Response, routing::get, Router}; pub struct RoString(Arc, bool); impl http_body::Body for RoString { @@ -57,38 +52,19 @@ fn error_handler(result: Result) -> Response { } } -async fn root( - State(hello_service): State>, -) -> Response { - error_handler( - (async { - let string = hello_service.hello().await?; - Ok(RoString::from(string).into()) - }) - .await, - ) +pub trait RestStateDef: Clone + Send + Sync + 'static { + type HelloService: service::HelloService + Send + Sync + 'static; + type PermissionService: service::PermissionService + Send + Sync + 'static; + + fn hello_service(&self) -> Arc; + fn permission_service(&self) -> Arc; } -#[derive(Debug, Serialize, Deserialize)] -pub struct User { - #[serde(default)] - pub id: Uuid, - pub name: String, -} - -async fn add_user(Json(user): Json) -> Response { - println!("Adding user: {:?}", user); - Response::builder().status(200).body(Body::empty()).unwrap() -} - -pub async fn start_server(hello_service: HelloService) -where - HelloService: service::HelloService + Send + Sync + 'static, -{ +pub async fn start_server(rest_state: RestState) { let app = Router::new() - .route("/", get(root)) - .route("/user", post(add_user)) - .with_state(Arc::new(hello_service)); + .route("/", get(hello::hello::)) + .nest("/permission", permission::generate_route()) + .with_state(rest_state); let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") .await .expect("Could not bind server"); diff --git a/rest/src/permission.rs b/rest/src/permission.rs new file mode 100644 index 0000000..3b26440 --- /dev/null +++ b/rest/src/permission.rs @@ -0,0 +1,37 @@ +use axum::{body::Body, extract::State, response::Response, routing::post, Json, Router}; +use serde::{Deserialize, Serialize}; +use uuid::Uuid; + +use crate::{error_handler, RestStateDef}; +use service::PermissionService; + +#[derive(Debug, Serialize, Deserialize)] +pub struct User { + #[serde(default)] + pub id: Uuid, + pub name: String, +} + +pub fn generate_route() -> Router { + Router::new().route("/user/", post(add_user::)) +} + +pub async fn add_user( + rest_state: State, + Json(user): Json, +) -> Response { + println!("Adding user: {:?}", user); + error_handler( + (async { + rest_state + .permission_service() + .create_user(user.name.as_str()) + .await?; + Ok(Response::builder() + .status(200) + .body(Body::from("")) + .unwrap()) + }) + .await, + ) +}