From 46c9611c958e858eccfe7b12d5daac81541348b4 Mon Sep 17 00:00:00 2001 From: Spencer Ferris <3319370+spencewenski@users.noreply.github.com> Date: Sat, 22 Jun 2024 23:32:23 -0700 Subject: [PATCH] feat: Add `From` impl to convert db config to ConnectOptions (#240) Add `From` impl for `ConnectOptions` to encapsulate the conversion, which would make it easier if a consumer wants to override the default `App#db_connection_options` method but keep some of our defaults. --- src/app/mod.rs | 25 +++------- src/config/database/mod.rs | 49 ++++++++++++++++++- ...e_tests__db_config_to_connect_options.snap | 32 ++++++++++++ 3 files changed, 85 insertions(+), 21 deletions(-) create mode 100644 src/config/database/snapshots/roadster__config__database__deserialize_tests__db_config_to_connect_options.snap diff --git a/src/app/mod.rs b/src/app/mod.rs index 914ed1fa..b75a1d34 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -72,6 +72,11 @@ where let mut service_registry = ServiceRegistry::new(&context); A::services(&mut service_registry, &context).await?; + if service_registry.services.is_empty() { + warn!("No enabled services were registered, exiting."); + return Ok(()); + } + #[cfg(feature = "cli")] if crate::service::runner::handle_cli(&roadster_cli, &app_cli, &service_registry, &context) .await? @@ -79,11 +84,6 @@ where return Ok(()); } - if service_registry.services.is_empty() { - warn!("No enabled services were registered, exiting."); - return Ok(()); - } - #[cfg(feature = "db-sql")] if context.config().database.auto_migrate { A::M::up(context.db(), None).await?; @@ -118,20 +118,7 @@ pub trait App: Send + Sync { #[cfg(feature = "db-sql")] fn db_connection_options(config: &AppConfig) -> RoadsterResult { - let mut options = ConnectOptions::new(config.database.uri.to_string()); - options - .connect_timeout(config.database.connect_timeout) - .acquire_timeout(config.database.acquire_timeout) - .min_connections(config.database.min_connections) - .max_connections(config.database.max_connections) - .sqlx_logging(false); - if let Some(idle_timeout) = config.database.idle_timeout { - options.idle_timeout(idle_timeout); - } - if let Some(max_lifetime) = config.database.max_lifetime { - options.max_lifetime(max_lifetime); - } - Ok(options) + Ok(ConnectOptions::from(&config.database)) } /// Convert the [AppContext] to the custom [Self::State] that will be used throughout the app. diff --git a/src/config/database/mod.rs b/src/config/database/mod.rs index 9646dea1..73138698 100644 --- a/src/config/database/mod.rs +++ b/src/config/database/mod.rs @@ -1,3 +1,4 @@ +use sea_orm::ConnectOptions; use serde_derive::{Deserialize, Serialize}; use serde_with::serde_as; use std::time::Duration; @@ -9,7 +10,7 @@ use validator::Validate; #[serde(rename_all = "kebab-case")] #[non_exhaustive] pub struct Database { - /// This can be overridden with an environment variable, e.g. `ROADSTER.DATABASE.URI=postgres://example:example@example:1234/example_app` + /// This can be overridden with an environment variable, e.g. `ROADSTER__DATABASE__URI=postgres://example:example@example:1234/example_app` pub uri: Url, /// Whether to automatically apply migrations during the app's start up. Migrations can also /// be manually performed via the `roadster migration [COMMAND]` CLI command. @@ -39,11 +40,36 @@ impl Database { } } +impl From for ConnectOptions { + fn from(database: Database) -> Self { + ConnectOptions::from(&database) + } +} + +impl From<&Database> for ConnectOptions { + fn from(database: &Database) -> Self { + let mut options = ConnectOptions::new(database.uri.to_string()); + options + .connect_timeout(database.connect_timeout) + .acquire_timeout(database.acquire_timeout) + .min_connections(database.min_connections) + .max_connections(database.max_connections) + .sqlx_logging(false); + if let Some(idle_timeout) = database.idle_timeout { + options.idle_timeout(idle_timeout); + } + if let Some(max_lifetime) = database.max_lifetime { + options.max_lifetime(max_lifetime); + } + options + } +} + #[cfg(test)] mod deserialize_tests { use super::*; use crate::util::test_util::TestCase; - use insta::assert_toml_snapshot; + use insta::{assert_debug_snapshot, assert_toml_snapshot}; use rstest::{fixture, rstest}; #[fixture] @@ -77,4 +103,23 @@ mod deserialize_tests { assert_toml_snapshot!(database); } + + #[test] + #[cfg_attr(coverage_nightly, coverage(off))] + fn db_config_to_connect_options() { + let db = Database { + uri: Url::parse("postgres://example:example@example:1234/example_app").unwrap(), + auto_migrate: true, + connect_timeout: Duration::from_secs(1), + acquire_timeout: Duration::from_secs(2), + idle_timeout: Some(Duration::from_secs(3)), + max_lifetime: Some(Duration::from_secs(4)), + min_connections: 10, + max_connections: 20, + }; + + let connect_options = ConnectOptions::from(&db); + + assert_debug_snapshot!(connect_options); + } } diff --git a/src/config/database/snapshots/roadster__config__database__deserialize_tests__db_config_to_connect_options.snap b/src/config/database/snapshots/roadster__config__database__deserialize_tests__db_config_to_connect_options.snap new file mode 100644 index 00000000..21d7223b --- /dev/null +++ b/src/config/database/snapshots/roadster__config__database__deserialize_tests__db_config_to_connect_options.snap @@ -0,0 +1,32 @@ +--- +source: src/config/database/mod.rs +expression: connect_options +--- +ConnectOptions { + url: "postgres://example:example@example:1234/example_app", + max_connections: Some( + 20, + ), + min_connections: Some( + 10, + ), + connect_timeout: Some( + 1s, + ), + idle_timeout: Some( + 3s, + ), + acquire_timeout: Some( + 2s, + ), + max_lifetime: Some( + 4s, + ), + sqlx_logging: false, + sqlx_logging_level: Info, + sqlx_slow_statements_logging_level: Off, + sqlx_slow_statements_logging_threshold: 1s, + sqlcipher_key: None, + schema_search_path: None, + test_before_acquire: true, +}