From ac157a15d60c1cd9cdc0229943bcd54edf0eadd5 Mon Sep 17 00:00:00 2001 From: rito528 <39003544+rito528@users.noreply.github.com> Date: Fri, 13 Oct 2023 22:19:41 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=E3=83=AD=E3=82=B0=E3=82=A4=E3=83=B3?= =?UTF-8?q?=E7=8A=B6=E6=85=8B=E3=81=A7API=E3=81=AB=E3=82=A2=E3=82=AF?= =?UTF-8?q?=E3=82=BB=E3=82=B9=E3=81=97=E3=81=9F=E5=A0=B4=E5=90=88=E3=81=AB?= =?UTF-8?q?=E3=80=81UUID=E3=81=A8=E5=90=8D=E5=89=8D=E3=82=92DB=E3=81=AB?= =?UTF-8?q?=E4=BF=9D=E5=AD=98=E3=81=99=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/domain/src/repository.rs | 3 ++ .../domain/src/repository/user_repository.rs | 10 +++++ server/entrypoint/src/main.rs | 5 ++- server/infra/resource/src/database.rs | 1 + .../infra/resource/src/database/components.rs | 8 ++++ .../infra/resource/src/database/connection.rs | 5 +++ server/infra/resource/src/database/user.rs | 23 ++++++++++++ server/infra/resource/src/repository.rs | 6 +++ .../src/repository/user_repository_impl.rs | 17 +++++++++ .../src/m20231008_135425_create_user_table.rs | 7 +++- server/presentation/src/auth.rs | 37 +++++++++++++------ server/usecase/src/lib.rs | 1 + server/usecase/src/user.rs | 13 +++++++ 13 files changed, 123 insertions(+), 13 deletions(-) create mode 100644 server/domain/src/repository/user_repository.rs create mode 100644 server/infra/resource/src/database/user.rs create mode 100644 server/infra/resource/src/repository/user_repository_impl.rs create mode 100644 server/usecase/src/user.rs diff --git a/server/domain/src/repository.rs b/server/domain/src/repository.rs index 403c82ba..ed9444ea 100644 --- a/server/domain/src/repository.rs +++ b/server/domain/src/repository.rs @@ -1,7 +1,10 @@ pub mod form_repository; +pub mod user_repository; pub trait Repositories: Send + Sync { type ConcreteFormRepository: form_repository::FormRepository; + type ConcreteUserRepository: user_repository::UserRepository; fn form_repository(&self) -> &Self::ConcreteFormRepository; + fn user_repository(&self) -> &Self::ConcreteUserRepository; } diff --git a/server/domain/src/repository/user_repository.rs b/server/domain/src/repository/user_repository.rs new file mode 100644 index 00000000..39212398 --- /dev/null +++ b/server/domain/src/repository/user_repository.rs @@ -0,0 +1,10 @@ +use crate::user::models::User; +use async_trait::async_trait; +use errors::Error; +use mockall::automock; + +#[automock] +#[async_trait] +pub trait UserRepository: Send + Sync + 'static { + async fn upsert_user(&self, user: &User) -> Result<(), Error>; +} diff --git a/server/entrypoint/src/main.rs b/server/entrypoint/src/main.rs index ef08f8b5..16713018 100644 --- a/server/entrypoint/src/main.rs +++ b/server/entrypoint/src/main.rs @@ -78,7 +78,10 @@ async fn main() -> anyhow::Result<()> { .with_state(shared_repository.to_owned()) .route("/health", get(health_check)) .layer(layer) - .route_layer(middleware::from_fn(auth)) + .route_layer(middleware::from_fn_with_state( + shared_repository.to_owned(), + auth, + )) .layer( CorsLayer::new() .allow_methods([Method::GET, Method::POST, Method::DELETE, Method::PATCH]) diff --git a/server/infra/resource/src/database.rs b/server/infra/resource/src/database.rs index 0c4d4434..ea9b5937 100644 --- a/server/infra/resource/src/database.rs +++ b/server/infra/resource/src/database.rs @@ -2,3 +2,4 @@ pub mod components; pub mod config; pub mod connection; pub mod form; +pub mod user; diff --git a/server/infra/resource/src/database/components.rs b/server/infra/resource/src/database/components.rs index b3b34c49..0454aae0 100644 --- a/server/infra/resource/src/database/components.rs +++ b/server/infra/resource/src/database/components.rs @@ -14,10 +14,12 @@ use crate::dto::{FormDto, PostedAnswersDto}; #[async_trait] pub trait DatabaseComponents: Send + Sync { type ConcreteFormDatabase: FormDatabase; + type ConcreteUserDatabase: UserDatabase; type TransactionAcrossComponents: Send + Sync; async fn begin_transaction(&self) -> anyhow::Result; fn form(&self) -> &Self::ConcreteFormDatabase; + fn user(&self) -> &Self::ConcreteUserDatabase; } #[automock] @@ -42,3 +44,9 @@ pub trait FormDatabase: Send + Sync { async fn create_questions(&self, questions: FormQuestionUpdateSchema) -> Result<(), InfraError>; } + +#[automock] +#[async_trait] +pub trait UserDatabase: Send + Sync { + async fn upsert_user(&self, user: &User) -> Result<(), InfraError>; +} diff --git a/server/infra/resource/src/database/connection.rs b/server/infra/resource/src/database/connection.rs index 6e47b1cc..06e38dd9 100644 --- a/server/infra/resource/src/database/connection.rs +++ b/server/infra/resource/src/database/connection.rs @@ -42,6 +42,7 @@ impl ConnectionPool { #[async_trait] impl DatabaseComponents for ConnectionPool { type ConcreteFormDatabase = Self; + type ConcreteUserDatabase = Self; type TransactionAcrossComponents = DatabaseTransaction; async fn begin_transaction(&self) -> anyhow::Result { @@ -51,4 +52,8 @@ impl DatabaseComponents for ConnectionPool { fn form(&self) -> &Self::ConcreteFormDatabase { self } + + fn user(&self) -> &Self::ConcreteUserDatabase { + self + } } diff --git a/server/infra/resource/src/database/user.rs b/server/infra/resource/src/database/user.rs new file mode 100644 index 00000000..e3143428 --- /dev/null +++ b/server/infra/resource/src/database/user.rs @@ -0,0 +1,23 @@ +use crate::database::components::UserDatabase; +use crate::database::connection::ConnectionPool; +use async_trait::async_trait; +use domain::user::models::User; +use errors::infra::InfraError; +use sea_orm::{ConnectionTrait, DatabaseBackend, Statement}; + +#[async_trait] +impl UserDatabase for ConnectionPool { + async fn upsert_user(&self, user: &User) -> Result<(), InfraError> { + self.pool + .execute(Statement::from_sql_and_values( + DatabaseBackend::MySql, + "INSERT INTO users (uuid, name) VALUES (UUID_TO_BIN(?), ?) + ON DUPLICATE KEY UPDATE + name = VALUES(name)", + [user.id.to_string().into(), user.name.to_owned().into()], + )) + .await?; + + Ok(()) + } +} diff --git a/server/infra/resource/src/repository.rs b/server/infra/resource/src/repository.rs index 060349f3..6030b26e 100644 --- a/server/infra/resource/src/repository.rs +++ b/server/infra/resource/src/repository.rs @@ -1,4 +1,5 @@ pub mod form_repository_impl; +mod user_repository_impl; use std::sync::Arc; @@ -27,8 +28,13 @@ impl Repository { impl Repositories for SharedRepository { type ConcreteFormRepository = Repository; + type ConcreteUserRepository = Repository; fn form_repository(&self) -> &Self::ConcreteFormRepository { &self.0 } + + fn user_repository(&self) -> &Self::ConcreteUserRepository { + &self.0 + } } diff --git a/server/infra/resource/src/repository/user_repository_impl.rs b/server/infra/resource/src/repository/user_repository_impl.rs new file mode 100644 index 00000000..172cb6a7 --- /dev/null +++ b/server/infra/resource/src/repository/user_repository_impl.rs @@ -0,0 +1,17 @@ +use crate::database::components::UserDatabase; +use crate::{database::components::DatabaseComponents, repository::Repository}; +use async_trait::async_trait; +use domain::repository::user_repository::UserRepository; +use domain::user::models::User; +use errors::Error; + +#[async_trait] +impl UserRepository for Repository { + async fn upsert_user(&self, user: &User) -> Result<(), Error> { + self.client + .user() + .upsert_user(user) + .await + .map_err(Into::into) + } +} diff --git a/server/migration/src/m20231008_135425_create_user_table.rs b/server/migration/src/m20231008_135425_create_user_table.rs index f72b771d..26915cf3 100644 --- a/server/migration/src/m20231008_135425_create_user_table.rs +++ b/server/migration/src/m20231008_135425_create_user_table.rs @@ -18,7 +18,12 @@ impl MigrationTrait for Migration { .auto_increment() .primary_key(), ) - .col(ColumnDef::new(UsersTable::Uuid).uuid().not_null()) + .col( + ColumnDef::new(UsersTable::Uuid) + .uuid() + .unique_key() + .not_null(), + ) .col(ColumnDef::new(UsersTable::Name).string().not_null()) .to_owned(), ) diff --git a/server/presentation/src/auth.rs b/server/presentation/src/auth.rs index 586dcd4b..5aaab83e 100644 --- a/server/presentation/src/auth.rs +++ b/server/presentation/src/auth.rs @@ -1,3 +1,4 @@ +use axum::extract::State; use axum::{ extract::TypedHeader, headers::authorization::{Authorization, Bearer}, @@ -6,25 +7,27 @@ use axum::{ response::Response, }; use common::config::ENV; +use domain::repository::user_repository::UserRepository; +use domain::repository::Repositories; use domain::user::models::User; use reqwest::header::{ACCEPT, CONTENT_TYPE}; +use resource::repository::RealInfrastructureRepository; +use usecase::user::UserUseCase; use uuid::uuid; pub async fn auth( + State(repository): State, TypedHeader(auth): TypedHeader>, mut request: Request, next: Next, ) -> Result { let token = auth.token(); - if ENV.name == "local" && token == "debug_user" { - let user = User { + let user = if ENV.name == "local" && token == "debug_user" { + User { name: "test_user".to_string(), id: uuid!("478911be-3356-46c1-936e-fb14b71bf282"), - }; - request.extensions_mut().insert(user); - let response = next.run(request).await; - Ok(response) + } } else { let client = reqwest::Client::new(); @@ -37,17 +40,29 @@ pub async fn auth( .await .map_err(|_| StatusCode::UNAUTHORIZED)?; - let user = serde_json::from_str::( + serde_json::from_str::( response .text() .await .map_err(|_| StatusCode::UNAUTHORIZED)? .as_str(), ) - .map_err(|_| StatusCode::UNAUTHORIZED)?; + .map_err(|_| StatusCode::UNAUTHORIZED)? + }; - request.extensions_mut().insert(user); - let response = next.run(request).await; - Ok(response) + let user_use_case = UserUseCase { + repository: repository.user_repository(), + }; + + match user_use_case.repository.upsert_user(&user).await { + Ok(_) => { + request.extensions_mut().insert(user); + let response = next.run(request).await; + Ok(response) + } + Err(err) => { + tracing::error!("{}", err); + Err(StatusCode::INTERNAL_SERVER_ERROR) + } } } diff --git a/server/usecase/src/lib.rs b/server/usecase/src/lib.rs index 5a8189bc..43d78c4c 100644 --- a/server/usecase/src/lib.rs +++ b/server/usecase/src/lib.rs @@ -1 +1,2 @@ pub mod form; +pub mod user; diff --git a/server/usecase/src/user.rs b/server/usecase/src/user.rs new file mode 100644 index 00000000..a1fb849a --- /dev/null +++ b/server/usecase/src/user.rs @@ -0,0 +1,13 @@ +use domain::repository::user_repository::UserRepository; +use domain::user::models::User; +use errors::Error; + +pub struct UserUseCase<'a, UserRepo: UserRepository> { + pub repository: &'a UserRepo, +} + +impl UserUseCase<'_, R> { + pub async fn upsert_user(&self, user: &User) -> Result<(), Error> { + self.repository.upsert_user(user).await + } +}