diff --git a/Cargo.lock b/Cargo.lock index fb36f9c..d6c5412 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -775,6 +775,7 @@ name = "dpts" version = "0.1.0" dependencies = [ "dpts-csv", + "dpts-database", "dpts-entity", "dpts-error", "dpts-migration", @@ -800,6 +801,21 @@ dependencies = [ "serde", ] +[[package]] +name = "dpts-database" +version = "0.1.0" +dependencies = [ + "async-graphql", + "axum", + "chrono", + "dotenv", + "dpts-entity", + "dpts-migration", + "log", + "sea-orm", + "tokio", +] + [[package]] name = "dpts-entity" version = "0.1.0" @@ -808,6 +824,7 @@ dependencies = [ "axum", "chrono", "dotenv", + "dpts-database", "dpts-migration", "log", "sea-orm", diff --git a/Cargo.toml b/Cargo.toml index 86c509e..7d9b164 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ dpts-csv = { workspace = true } dpts-entity = { workspace = true } dpts-error = { workspace = true } dpts-migration = { workspace = true } +dpts-database = { workspace = true } [workspace] members = [".", "dpts-*"] @@ -17,13 +18,14 @@ members = [".", "dpts-*"] [workspace.dependencies] dpts = { path = "." } dpts-csv = { path = "dpts-csv" } +dpts-database = { path = "dpts-database" } dpts-entity = { path = "dpts-entity" } dpts-error = { path = "dpts-error" } dpts-migration = {path = "dpts-migration"} chrono = {version = "0.4", features = ["serde"]} clap = "4.5" dotenv = "0.15.0" -serde = "1.0.219" +serde = { version = "1.0", features = ["derive"] } [workspace.package] version = "0.1.0" diff --git a/dpts-database/Cargo.toml b/dpts-database/Cargo.toml new file mode 100644 index 0000000..1b06f4a --- /dev/null +++ b/dpts-database/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "dpts-database" +version.workspace = true +edition.workspace = true +license.workspace = true +repository.workspace = true + +[features] +default = [] +test = [] + +[dependencies] +dpts-entity = { workspace = true } +dpts-migration = { workspace = true } +async-graphql = {version = "7.0", features = ["chrono"]} +axum = "0.8" +chrono = {workspace = true} +dotenv = {workspace = true} +log = "0.4.27" +tokio = "1.44.2" + +[dependencies.sea-orm] +version = "1.1" +features = [ + "macros", + "debug-print", + "runtime-tokio-native-tls", + "sqlx-sqlite", + "with-chrono", +] +default-features = false \ No newline at end of file diff --git a/dpts-database/src/connection.rs b/dpts-database/src/connection.rs new file mode 100644 index 0000000..1688316 --- /dev/null +++ b/dpts-database/src/connection.rs @@ -0,0 +1,123 @@ +use std::time::Duration; +use sea_orm::{entity::*, query::*, ConnectOptions, Database, DatabaseConnection}; +use dpts_migration::{Migrator, 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_init(&self, f: F) -> &DatabaseConnection where + F: FnOnce() -> T, + T: Future + { + self.inner.get_or_init(f).await + } + + #[cfg(test)] + pub async fn init_test(&self) { + self.get_or_init( || async { + let mut opt = ConnectOptions::new("sqlite::memory:"); + opt.max_connections(100) + .min_connections(5) + .connect_timeout(Duration::from_secs(8)) + .acquire_timeout(Duration::from_secs(8)) + .idle_timeout(Duration::from_secs(8)) + .max_lifetime(Duration::from_secs(8)) + .sqlx_logging(true) + .sqlx_logging_level(log::LevelFilter::Info); + //.set_schema_search_path("my_schema"); // Setting default PostgreSQL schema + let db = Database::connect(opt).await.unwrap(); + Migrator::fresh(&db).await.unwrap(); + db + }).await; + + } + +} + +pub static DATABASE_CONNECTION: OnceDatabaseConnection = OnceDatabaseConnection::new(); + +#[cfg(test)] +mod tests { + use super::*; + + use std::time::Duration; + use chrono::{offset, FixedOffset, Local, TimeZone}; + use sea_orm::{entity::*, query::*, ConnectOptions, Database}; + use dpts_migration::{Migrator, MigratorTrait}; + use dpts_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/dpts-database/src/lib.rs b/dpts-database/src/lib.rs new file mode 100644 index 0000000..437ab8e --- /dev/null +++ b/dpts-database/src/lib.rs @@ -0,0 +1,7 @@ +mod connection; + +pub use connection::*; + +#[cfg(test)] +mod tests { +} \ No newline at end of file diff --git a/dpts-entity/Cargo.toml b/dpts-entity/Cargo.toml index 16ebcd7..b51c97c 100644 --- a/dpts-entity/Cargo.toml +++ b/dpts-entity/Cargo.toml @@ -12,7 +12,7 @@ axum = "0.8" chrono = {workspace = true} dotenv = {workspace = true} log = "0.4.27" -serde = { version = "1.0", features = ["derive"] } +serde = { workspace = true } tokio = "1.44.2" [dependencies.sea-orm] @@ -25,3 +25,6 @@ features = [ "with-chrono", ] default-features = false + +[dev-dependencies] +dpts-database = { workspace = true, features = ["test"] } \ No newline at end of file diff --git a/dpts-entity/tests/db.rs b/dpts-entity/tests/db.rs deleted file mode 100644 index 9c0a0aa..0000000 --- a/dpts-entity/tests/db.rs +++ /dev/null @@ -1,70 +0,0 @@ -use std::time::Duration; -use chrono::{offset, FixedOffset, Local, TimeZone}; -use sea_orm::{entity::*, query::*, ConnectOptions, Database}; -use dpts_migration::{Migrator, MigratorTrait}; -use dpts_entity::*; - -#[tokio::test] -async fn main() { - let mut opt = ConnectOptions::new("sqlite::memory:"); - opt.max_connections(100) - .min_connections(5) - .connect_timeout(Duration::from_secs(8)) - .acquire_timeout(Duration::from_secs(8)) - .idle_timeout(Duration::from_secs(8)) - .max_lifetime(Duration::from_secs(8)) - .sqlx_logging(true) - .sqlx_logging_level(log::LevelFilter::Info); - //.set_schema_search_path("my_schema"); // Setting default PostgreSQL schema - let db= Database::connect(opt).await.unwrap(); - Migrator::fresh(&db).await.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.close().await.unwrap(); -} \ No newline at end of file