From 7f8000ef429ee1dfaf1993d1119c30bb7fa05685 Mon Sep 17 00:00:00 2001 From: fluo10 Date: Mon, 12 May 2025 08:49:51 +0900 Subject: [PATCH] Update global database connection --- Cargo.lock | 2 + progress-pile-client/src/entity/mod.rs | 26 ++-- progress-pile-client/src/lib.rs | 4 +- progress-pile-core/Cargo.toml | 3 + progress-pile-core/src/database_connection.rs | 46 ------- progress-pile-core/src/global/config.rs | 0 progress-pile-core/src/global/database.rs | 63 ++++++++++ progress-pile-core/src/global/mod.rs | 4 + progress-pile-core/src/lib.rs | 2 +- progress-pile-server/Cargo.toml | 1 + .../src/entity/access_token.rs | 55 +++++++++ progress-pile-server/src/entity/mod.rs | 112 ++++++++++++++++++ .../src/entity/progress_category.rs | 43 +++++++ .../src/entity/progress_entry.rs | 46 +++++++ progress-pile-server/src/entity/user.rs | 65 ++++++++++ progress-pile-server/src/lib.rs | 1 + 16 files changed, 415 insertions(+), 58 deletions(-) delete mode 100644 progress-pile-core/src/database_connection.rs create mode 100644 progress-pile-core/src/global/config.rs create mode 100644 progress-pile-core/src/global/database.rs create mode 100644 progress-pile-core/src/global/mod.rs create mode 100644 progress-pile-server/src/entity/access_token.rs create mode 100644 progress-pile-server/src/entity/mod.rs create mode 100644 progress-pile-server/src/entity/progress_category.rs create mode 100644 progress-pile-server/src/entity/progress_entry.rs create mode 100644 progress-pile-server/src/entity/user.rs diff --git a/Cargo.lock b/Cargo.lock index 216d999..4cfae41 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2287,6 +2287,7 @@ dependencies = [ "iana-time-zone", "log", "sea-orm", + "sea-orm-migration", "serde", "thiserror 2.0.12", "tokio", @@ -2313,6 +2314,7 @@ dependencies = [ "clap", "progress-pile-core", "rand 0.9.1", + "sea-orm", "serde", "thiserror 2.0.12", "tokio", diff --git a/progress-pile-client/src/entity/mod.rs b/progress-pile-client/src/entity/mod.rs index d8041f6..54b5b46 100644 --- a/progress-pile-client/src/entity/mod.rs +++ b/progress-pile-client/src/entity/mod.rs @@ -20,19 +20,23 @@ mod tests { use super::*; use chrono::Local; - use sea_orm::entity::*; + use progress_pile_core::global::GlobalDatabase; + use sea_orm::{entity::*, DatabaseConnection}; use progress_pile_migration::{ClientMigrator, MigratorTrait}; use uuid::Uuid; - use crate::database_connection::DATABASE_CONNECTION; + use crate::error::Error; + + async fn get_or_try_init_test_database() -> Result<&'static DatabaseConnection, Error> { + Ok(GlobalDatabase::get_or_try_init("sqlite::memory:", ClientMigrator).await?) + + } + #[tokio::test] async fn check_insert_entity() { - let db = DATABASE_CONNECTION.get_or_init("sqlite::memory:").await; - ClientMigrator::up(db, None).await.unwrap(); - let local_date_time = Local::now(); - let offset_date_time = local_date_time.with_timezone(local_date_time.offset()); - + let db = get_or_try_init_test_database().await.unwrap(); + let category = ProgressCategoryActiveModel{ name: Set("test_category".to_owned()), ..ProgressCategoryActiveModel::new() @@ -42,8 +46,10 @@ mod tests { progress_category_id: Set(category.id), ..ProgressEntryActiveModel::new() }.insert(db).await.unwrap(); - - ClientMigrator::reset(db).await.unwrap(); - //db.clone().close().await.unwrap(); + } + #[tokio::test] + async fn connect_database () { + let db = get_or_try_init_test_database().await.unwrap(); + assert!(db.ping().await.is_ok()); } } \ No newline at end of file diff --git a/progress-pile-client/src/lib.rs b/progress-pile-client/src/lib.rs index 5759059..7d50681 100644 --- a/progress-pile-client/src/lib.rs +++ b/progress-pile-client/src/lib.rs @@ -2,7 +2,9 @@ pub mod auth; pub mod config; pub mod entity; -pub use progress_pile_core::*; +pub use progress_pile_core::{ + error, +}; diff --git a/progress-pile-core/Cargo.toml b/progress-pile-core/Cargo.toml index 5e40ba5..f7e4c16 100644 --- a/progress-pile-core/Cargo.toml +++ b/progress-pile-core/Cargo.toml @@ -8,6 +8,8 @@ repository.workspace = true [features] default = ["clap",] clap = ["dep:clap"] +postgres = ["sea-orm/sqlx-postgres", "sea-orm/sqlx-postgres"] +server = ["clap", "postgres"] [dependencies] async-graphql.workspace = true @@ -21,6 +23,7 @@ dotenv = {workspace = true} iana-time-zone = "0.1.63" log = "0.4.27" sea-orm.workspace = true +sea-orm-migration.workspace = true serde.workspace = true thiserror.workspace = true tokio.workspace = true diff --git a/progress-pile-core/src/database_connection.rs b/progress-pile-core/src/database_connection.rs deleted file mode 100644 index 3c5ccef..0000000 --- a/progress-pile-core/src/database_connection.rs +++ /dev/null @@ -1,46 +0,0 @@ -use crate::config::{ - DATABASE_CONFIG, - DatabaseConfig -}; -use std::time::Duration; -use sea_orm::{entity::*, query::*, ConnectOptions, Database, DatabaseConnection}; - -use tokio::sync::OnceCell; - -pub struct OnceDatabaseConnection { - inner: OnceCell, -} - -impl OnceDatabaseConnection { - const fn new() -> Self { - Self { - inner: OnceCell::const_new(), - } - } - pub fn get(&self) -> Option<&DatabaseConnection> { - self.inner.get() - } - - pub async fn get_or_init(&self, c: T) -> &DatabaseConnection where - T: Into - { - self.inner.get_or_init(|| async { - Database::connect(c).await.unwrap() - }).await - } -} - - -pub static DATABASE_CONNECTION: OnceDatabaseConnection = OnceDatabaseConnection::new(); - -#[cfg(test)] -mod tests { - use super::*; - - //#[tokio::test] - async fn check_database_connection() { - DATABASE_CONNECTION.get_or_init("sqlite::memory:").await; - let db = DATABASE_CONNECTION.get().unwrap(); - assert!(db.ping().await.is_ok()); - } -} \ No newline at end of file diff --git a/progress-pile-core/src/global/config.rs b/progress-pile-core/src/global/config.rs new file mode 100644 index 0000000..e69de29 diff --git a/progress-pile-core/src/global/database.rs b/progress-pile-core/src/global/database.rs new file mode 100644 index 0000000..c87645a --- /dev/null +++ b/progress-pile-core/src/global/database.rs @@ -0,0 +1,63 @@ +use crate::error::Error; +use sea_orm::{ConnectOptions, Database, DatabaseConnection}; + +use sea_orm_migration::MigratorTrait; +use tokio::sync::OnceCell; + +/* +pub struct OnceDatabaseConnection { + inner: OnceCell, +} + +impl OnceDatabaseConnection { + const fn new() -> Self { + Self { + inner: OnceCell::const_new(), + } + } + pub fn get(&self) -> Option<&DatabaseConnection> { + self.inner.get() + } + + pub async fn get_or_try_init(&self, options: T, _: U) -> Result<&DatabaseConnection, Error> where + T: Into, + U: MigratorTrait + { + self.inner.get_or_try_init(|| async { + let db = Database::connect(options).await?; + U::up(&db, None).await?; + Ok(db) + }).await +} + +} + + +pub static DATABASE_CONNECTION: OnceDatabaseConnection = OnceDatabaseConnection::new(); +*/ + +pub static DATABASE_CONNECTION: OnceCell = OnceCell::const_new(); + +pub struct GlobalDatabase; + +impl GlobalDatabase { + pub fn get() -> Option<&'static DatabaseConnection> { + DATABASE_CONNECTION.get() + } + + pub async fn get_or_try_init(options: T, _: U) -> Result<&'static DatabaseConnection, Error> where + T: Into, + U: MigratorTrait + { + DATABASE_CONNECTION.get_or_try_init(|| async { + let db = Database::connect(options).await?; + U::up(&db, None).await?; + Ok(db) + }).await + } +} + +#[cfg(test)] +mod tests { + use super::*; +} \ No newline at end of file diff --git a/progress-pile-core/src/global/mod.rs b/progress-pile-core/src/global/mod.rs new file mode 100644 index 0000000..6e52323 --- /dev/null +++ b/progress-pile-core/src/global/mod.rs @@ -0,0 +1,4 @@ +pub mod config; +mod database; + +pub use database::GlobalDatabase; \ No newline at end of file diff --git a/progress-pile-core/src/lib.rs b/progress-pile-core/src/lib.rs index 1159fa4..ca6d5e7 100644 --- a/progress-pile-core/src/lib.rs +++ b/progress-pile-core/src/lib.rs @@ -1,8 +1,8 @@ pub mod config; pub mod csv; -pub mod database_connection; pub mod entity; pub mod error; +pub mod global; pub mod graphql; #[cfg(test)] diff --git a/progress-pile-server/Cargo.toml b/progress-pile-server/Cargo.toml index ba6c468..ee61c65 100644 --- a/progress-pile-server/Cargo.toml +++ b/progress-pile-server/Cargo.toml @@ -12,6 +12,7 @@ axum = "0.8.4" clap = {workspace = true, features = ["derive"]} progress-pile-core = {workspace = true} chrono = {workspace = true} +sea-orm = { workspace = true, features = ["sqlx-postgres"] } serde.workspace = true thiserror.workspace = true tokio.workspace = true diff --git a/progress-pile-server/src/entity/access_token.rs b/progress-pile-server/src/entity/access_token.rs new file mode 100644 index 0000000..5c3d53e --- /dev/null +++ b/progress-pile-server/src/entity/access_token.rs @@ -0,0 +1,55 @@ +use core::time; + +use async_graphql::*; +use chrono::Local; +use sea_orm::entity::{ + Set, + prelude::* +}; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Serialize, Deserialize, SimpleObject)] +#[sea_orm(table_name = "access_token")] +pub struct Model { + #[sea_orm(primary_key)] + pub id: i32, + #[sea_orm(indexed)] + pub user_id: i32, + #[sea_orm(indexed)] + pub token_value: String, + pub note: String, + #[sea_orm(indexed)] + pub created_at: DateTimeWithTimeZone, + #[sea_orm(indexed)] + pub updated_at: DateTimeWithTimeZone, + #[sea_orm(indexed)] + pub expired_at: Option, +} + +#[derive(Copy, Clone, Debug, DeriveRelation, EnumIter)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::user::Entity", + from = "Column::UserId", + to = "super::user::Column::Id" + )] + User, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::User.def() + } +} +impl ActiveModel { + pub fn new() -> Self { + let timestamp = Local::now().fixed_offset(); + Self{ + note: Set("".to_string()), + created_at: Set(timestamp), + updated_at: Set(timestamp), + ..Default::default() + } + } +} +impl ActiveModelBehavior for ActiveModel {} \ No newline at end of file diff --git a/progress-pile-server/src/entity/mod.rs b/progress-pile-server/src/entity/mod.rs new file mode 100644 index 0000000..6582504 --- /dev/null +++ b/progress-pile-server/src/entity/mod.rs @@ -0,0 +1,112 @@ +mod access_token; +mod progress_category; +mod progress_entry; +mod user; + + +pub use access_token::{ + ActiveModel as AccessTokenActiveModel, + Column as AccessTokenColumn, + Entity as AccessTokenEntity, + Model as AccessTokenModel, +}; + +pub use progress_category::{ + ActiveModel as ProgressCategoryActiveModel, + Column as ProgressCategoryColumn, + Entity as ProgressCategoryEntity, + Model as ProgressCategoryModel, +}; + +pub use progress_entry::{ + ActiveModel as ProgressEntryActiveModel, + Column as ProgressEntryColumn, + Entity as ProgressEntryEntity, + Model as ProgressEntryModel, +}; + + +pub use user::{ + ActiveModel as UserActiveModel, + Column as UserColumn, + Entity as UserEntity, + Model as UserModel, +}; + + +pub use progress_pile_core::entity::*; + + + +#[cfg(test)] +mod tests { + use super::*; + + use std::time::Duration; + use chrono::{offset, FixedOffset, Local, TimeZone}; + use sea_orm::{entity::*, query::*, ConnectOptions, Database}; + use progress_pile_migration_server::{Migrator, MigratorTrait}; + use crate::entity::*; + + #[tokio::test] + async fn check_database_connection() { + DATABASE_CONNECTION.init_test().await; + let db = DATABASE_CONNECTION.get().unwrap(); + assert!(db.ping().await.is_ok()); + } + #[tokio::test] + async fn check_insert_entity() { + DATABASE_CONNECTION.init_test().await; + let db = DATABASE_CONNECTION.get().unwrap(); + + + let local_date_time = Local::now(); + let offset_date_time = local_date_time.with_timezone(local_date_time.offset()); + + let user = UserActiveModel{ + login_name: Set("admin".to_owned()), + password_hash: Set("admin".to_owned()), + created_at: Set(offset_date_time), + updated_at: Set(offset_date_time), + ..Default::default() + }.insert(db) + .await.unwrap(); + + + let record_tag = RecordTagActiveModel{ + user_id: Set(user.id), + name: Set("test".to_owned()), + ..Default::default() + }.insert(db) + .await.unwrap(); + + let record_header = RecordHeaderActiveModel{ + user_id: Set(user.id), + created_at: Set(offset_date_time), + updated_at: Set(offset_date_time), + recorded_at: Set(offset_date_time), + comment: Set("".to_owned()), + ..Default::default() + }.insert(db) + .await.unwrap(); + + RecordDetailActiveModel { + record_header_id: Set(record_header.id), + record_tag_id: Set(record_tag.id), + count: Set(1), + ..Default::default() + }.insert(db) + .await.unwrap(); + RecordDetailActiveModel { + record_header_id: Set(record_header.id), + record_tag_id: Set(record_tag.id), + count: Set(2), + ..Default::default() + }.insert(db) + .await.unwrap(); + + + Migrator::reset(db).await.unwrap(); + db.clone().close().await.unwrap(); + } +} \ No newline at end of file diff --git a/progress-pile-server/src/entity/progress_category.rs b/progress-pile-server/src/entity/progress_category.rs new file mode 100644 index 0000000..8c5708d --- /dev/null +++ b/progress-pile-server/src/entity/progress_category.rs @@ -0,0 +1,43 @@ +use async_graphql::*; +use sea_orm::entity::prelude::*; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Serialize, Deserialize, SimpleObject)] +#[sea_orm(table_name = "record_tag")] +#[graphql(concrete(name = "RecordTag", params()))] +pub struct Model { + #[sea_orm(primary_key)] + #[serde(skip_deserializing)] + pub id: i32, + #[sea_orm(indexed)] + #[serde(skip_deserializing)] + pub user_id: i32, + #[sea_orm(indexed)] + pub name: String, +} + +#[derive(Copy, Clone, Debug, DeriveRelation, EnumIter)] +pub enum Relation { + #[sea_orm(has_many = "super::record_detail::Entity")] + RecordDetail, + #[sea_orm( + belongs_to = "super::user::Entity", + from = "Column::UserId", + to = "super::user::Column::Id" + )] + User, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::RecordDetail.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::User.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} \ No newline at end of file diff --git a/progress-pile-server/src/entity/progress_entry.rs b/progress-pile-server/src/entity/progress_entry.rs new file mode 100644 index 0000000..18c20f5 --- /dev/null +++ b/progress-pile-server/src/entity/progress_entry.rs @@ -0,0 +1,46 @@ +use async_graphql::*; +use sea_orm::entity::prelude::*; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Serialize, Deserialize, SimpleObject)] +#[sea_orm(table_name = "record_detail")] +#[graphql(concrete(name = "RecordDetail", params()))] +pub struct Model { + #[sea_orm(primary_key)] + #[serde(skip_deserializing)] + pub record_header_id: i32, + #[sea_orm(primary_key)] + #[serde(skip_deserializing)] + pub record_tag_id: i32, + pub count: i32, +} + +#[derive(Copy, Clone, Debug, DeriveRelation, EnumIter)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::record_header::Entity", + from = "Column::RecordHeaderId", + to = "super::record_header::Column::Id" + )] + RecordHeader, + #[sea_orm( + belongs_to = "super::record_tag::Entity", + from = "Column::RecordTagId", + to = "super::record_tag::Column::Id" + )] + RecordTag, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::RecordHeader.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::RecordTag.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} \ No newline at end of file diff --git a/progress-pile-server/src/entity/user.rs b/progress-pile-server/src/entity/user.rs new file mode 100644 index 0000000..5a44888 --- /dev/null +++ b/progress-pile-server/src/entity/user.rs @@ -0,0 +1,65 @@ +use async_graphql::SimpleObject; +use chrono::{DateTime, FixedOffset, Local,}; +use sea_orm::{entity::prelude::*, ActiveValue::Set}; +use serde::{Deserialize, Serialize}; +use crate::error::Error; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Serialize, SimpleObject, Deserialize)] +#[sea_orm(table_name = "user")] +#[graphql(concrete(name = "User", params()))] +pub struct Model { + #[sea_orm(primary_key)] + #[serde(skip_deserializing)] + pub id: i32, + #[sea_orm(unique, indexed)] + pub login_name: String, + pub password_hash: String, + #[sea_orm(indexed)] + pub created_at: DateTimeWithTimeZone, + #[sea_orm(indexed)] + pub updated_at: DateTimeWithTimeZone, + #[sea_orm(indexed)] + pub deleted_at: Option, +} + +#[derive(Copy, Clone, Debug, DeriveRelation, EnumIter)] +pub enum Relation { + #[sea_orm(has_many = "super::access_token::Entity")] + AccessToken, + #[sea_orm(has_many = "super::progress_category::Entity")] + ProgressCategory, + #[sea_orm(has_many = "super::progress_entry::Entity")] + ProgressEntry, +} + +impl Related for Model { + fn to() -> RelationDef { + Relation::AccessToken.def() + } +} + +impl Related for Model { + fn to() -> RelationDef { + Relation::ProgressCategory.def() + } +} + +impl Related for Model { + fn to() -> RelationDef { + Relation::ProgressEntry.def() + } +} + +impl ActiveModel { + pub fn new() -> Self { + let timestamp = Local::now().fixed_offset(); + Self { + created_at: Set(timestamp), + updated_at: Set(timestamp), + ..Default::default() + } + } +} + +impl ActiveModelBehavior for ActiveModel {} + diff --git a/progress-pile-server/src/lib.rs b/progress-pile-server/src/lib.rs index f030772..dcfcd13 100644 --- a/progress-pile-server/src/lib.rs +++ b/progress-pile-server/src/lib.rs @@ -1,6 +1,7 @@ mod args; mod auth; mod config; +pub mod entity; pub mod error; pub mod graphql;