diff --git a/Cargo.lock b/Cargo.lock index b202936a3b599caaa14cf66f7573e9d7dd83dba4..0ccece6f4efb194e4070b741bdd19c04d670a59c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -997,6 +997,7 @@ dependencies = [ "dubp-user-docs 0.14.0", "dup-crypto 0.7.0", "durs-common-tools 0.2.0", + "durs-message 0.3.0-dev", "durs-module 0.3.0-dev", "envy 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/lib/core/conf/Cargo.toml b/lib/core/conf/Cargo.toml index 3449896a0905ff483cd7c22b87b47654a6845709..bb34ff368f00dd16a68bf6671209c089d7a0fb2f 100644 --- a/lib/core/conf/Cargo.toml +++ b/lib/core/conf/Cargo.toml @@ -14,6 +14,7 @@ dirs = "2.0.2" dup-crypto = { path = "../../crypto" } dubp-currency-params = { path = "../../dubp/currency-params" } dubp-user-docs= { path = "../../dubp/user-docs" } +durs-message = { path = "../message" } durs-module = { path = "../module" } durs-common-tools = { path = "../../tools/common-tools" } envy = "0.4.1" diff --git a/lib/core/conf/src/lib.rs b/lib/core/conf/src/lib.rs index 0cf9a602bc1834ebeec5e2f4a6f544df22bfb75c..f1d979d9b9c923d1d0de90b71671e8c8a1dd2331 100644 --- a/lib/core/conf/src/lib.rs +++ b/lib/core/conf/src/lib.rs @@ -40,7 +40,7 @@ pub mod errors; pub mod file; mod global_conf; pub mod keypairs; -mod modules_conf; +pub mod modules_conf; mod resources; mod v1; diff --git a/lib/core/conf/src/modules_conf.rs b/lib/core/conf/src/modules_conf.rs index 38e975b0c38a189261b732d86635d365ce64e66c..ab54a2816cfae66c04899213f1e9edd7eea03c7b 100644 --- a/lib/core/conf/src/modules_conf.rs +++ b/lib/core/conf/src/modules_conf.rs @@ -15,7 +15,24 @@ //! Dunitrust modules configuration -use durs_module::ModuleName; +use crate::constants; +use crate::keypairs::DuniterKeyPairs; +use crate::DuRsConf; +use dubp_currency_params::CurrencyName; +use durs_common_tools::traits::merge::Merge; +use durs_message::DursMsg; +use durs_module::{ + DursConfTrait, DursModule, ModuleConfError, ModuleName, ModuleStaticName, RequiredKeysContent, +}; + +/// Module configurations and required keys +pub type ModuleConfsAndKeys<M> = ( + ( + <M as DursModule<DuRsConf, DursMsg>>::ModuleConf, + Option<<M as DursModule<DuRsConf, DursMsg>>::ModuleUserConf>, + ), + RequiredKeysContent, +); #[derive(Debug, Clone, Deserialize, PartialEq, Serialize)] /// Modules conf @@ -29,6 +46,44 @@ impl Default for ModulesConf { } impl ModulesConf { + // get module conf + fn get_module_conf<M: DursModule<DuRsConf, DursMsg>>( + currency_name: Option<&CurrencyName>, + global_conf: &<DuRsConf as DursConfTrait>::GlobalConf, + module_conf_json: Option<serde_json::Value>, + ) -> Result<(M::ModuleConf, Option<M::ModuleUserConf>), ModuleConfError> { + let file_module_user_conf: M::ModuleUserConf = + if let Some(module_conf_json) = module_conf_json { + let file_module_user_conf_opt: Option<M::ModuleUserConf> = + serde_json::from_str(module_conf_json.to_string().as_str())?; + file_module_user_conf_opt.unwrap_or_default() + } else { + M::ModuleUserConf::default() + }; + + let env_module_user_conf = Self::get_env_module_user_conf::<M::ModuleUserConf>(M::name())?; + + M::generate_module_conf( + currency_name, + global_conf, + Some(file_module_user_conf.merge(env_module_user_conf)), + ) + } + + // get module conf from environment variables + fn get_env_module_user_conf<ModuleUserConf: serde::de::DeserializeOwned>( + module_name: ModuleStaticName, + ) -> Result<ModuleUserConf, ModuleConfError> { + let prefix = format!( + "{}{}_", + constants::DURS_ENV_PREFIX, + module_name.0.to_ascii_uppercase() + ); + + envy::prefixed(prefix) + .from_env::<ModuleUserConf>() + .map_err(ModuleConfError::EnvyErr) + } /// Change module conf pub fn set_module_conf(&mut self, module_name: ModuleName, new_module_conf: serde_json::Value) { if self.0.is_null() { @@ -44,3 +99,97 @@ impl ModulesConf { } } } + +/// Get module conf and keys +pub fn get_module_conf_and_keys<M: DursModule<DuRsConf, DursMsg>>( + currency_name: Option<&CurrencyName>, + global_conf: &<DuRsConf as DursConfTrait>::GlobalConf, + module_conf_json: Option<serde_json::Value>, + keypairs: DuniterKeyPairs, +) -> Result<ModuleConfsAndKeys<M>, ModuleConfError> { + Ok(( + ModulesConf::get_module_conf::<M>(currency_name, global_conf, module_conf_json)?, + DuniterKeyPairs::get_required_keys_content(M::ask_required_keys(), keypairs), + )) +} + +#[cfg(test)] +mod tests { + + use super::*; + use once_cell::sync::Lazy; + use std::sync::Mutex; + + // Empty mutex used to ensure that only one test runs at a time + static MUTEX: Lazy<Mutex<()>> = Lazy::new(|| Mutex::new(())); + + #[derive(Debug, Default, Deserialize, PartialEq)] + struct TestModuleUserConf { + field1: Option<String>, + field2: Option<usize>, + } + + #[inline] + fn prefix() -> String { + format!("{}MODULE_TEST_", constants::DURS_ENV_PREFIX) + } + + fn clear_env_vars() { + if std::env::var(&format!("{}FIELD1", prefix())).is_ok() { + std::env::remove_var(&format!("{}FIELD1", prefix())); + } + if std::env::var(&format!("{}FIELD2", prefix())).is_ok() { + std::env::remove_var(&format!("{}FIELD2", prefix())); + } + } + + #[test] + fn test_env_module_conf_without_env_vars() -> Result<(), ModuleConfError> { + let _lock = MUTEX.lock().expect("MUTEX poisoned"); + clear_env_vars(); + + assert_eq!( + TestModuleUserConf::default(), + ModulesConf::get_env_module_user_conf(ModuleStaticName("module_test"))?, + ); + + Ok(()) + } + + #[test] + fn test_env_module_conf_with_some_valid_env_vars() -> Result<(), ModuleConfError> { + let _lock = MUTEX.lock().expect("MUTEX poisoned"); + clear_env_vars(); + + std::env::set_var(&format!("{}FIELD1", prefix()), "toto"); + std::env::set_var(&format!("{}FIELD2", prefix()), "4"); + + assert_eq!( + TestModuleUserConf { + field1: Some("toto".to_owned()), + field2: Some(4), + }, + ModulesConf::get_env_module_user_conf(ModuleStaticName("module_test"))?, + ); + + Ok(()) + } + + #[test] + fn test_env_module_conf_with_invalid_env_var() -> Result<(), ModuleConfError> { + let _lock = MUTEX.lock().expect("MUTEX poisoned"); + clear_env_vars(); + + // field2 must be a number + std::env::set_var(&format!("{}FIELD2", prefix()), "toto"); + + if let Err(ModuleConfError::EnvyErr(_)) = ModulesConf::get_env_module_user_conf::< + TestModuleUserConf, + >(ModuleStaticName("module_test")) + { + Ok(()) + } else { + panic!("get_env_module_user_conf() must return an error ModuleConfError::EnvyErr."); + } + } +} diff --git a/lib/core/core/src/lib.rs b/lib/core/core/src/lib.rs index cecf3fb6427c44eb4925c477bbd6306aeb5d1922..0ce8e79256bd93780cccb87853443cc45a3eac3d 100644 --- a/lib/core/core/src/lib.rs +++ b/lib/core/core/src/lib.rs @@ -44,7 +44,6 @@ use crate::errors::DursCoreError; use dubp_currency_params::CurrencyName; use durs_bc::{dbex::DbExQuery, BlockchainModule}; use durs_common_tools::fatal_error; -use durs_common_tools::traits::merge::Merge; pub use durs_conf::{ constants::KEYPAIRS_FILENAME, keypairs::cli::*, ChangeGlobalConf, DuRsConf, DuniterKeyPairs, }; @@ -124,16 +123,17 @@ impl DursCore<DuRsConf> { .get(&M::name().to_string().as_str()) .cloned(); - let ((module_conf, module_user_conf), required_keys) = get_module_conf_and_keys::<M>( - durs_core.currency_name.as_ref(), - &durs_core.soft_meta_datas.conf.get_global_conf(), - module_conf_json, - durs_core.keypairs, - ) - .map_err(|e| DursCoreError::PlugModuleError { - module_name: M::name(), - error: e.into(), - })?; + let ((module_conf, module_user_conf), required_keys) = + durs_conf::modules_conf::get_module_conf_and_keys::<M>( + durs_core.currency_name.as_ref(), + &durs_core.soft_meta_datas.conf.get_global_conf(), + module_conf_json, + durs_core.keypairs, + ) + .map_err(|e| DursCoreError::PlugModuleError { + module_name: M::name(), + error: e.into(), + })?; // Execute module subcommand let new_module_conf = M::exec_subcommand( &durs_core.soft_meta_datas, @@ -411,12 +411,13 @@ impl DursCore<DuRsConf> { .cloned(); // Load module conf and keys - let ((module_conf, _), required_keys) = get_module_conf_and_keys::<NM>( - self.currency_name.as_ref(), - &soft_meta_datas.conf.get_global_conf(), - module_conf_json, - self.keypairs.clone(), - )?; + let ((module_conf, _), required_keys) = + durs_conf::modules_conf::get_module_conf_and_keys::<NM>( + self.currency_name.as_ref(), + &soft_meta_datas.conf.get_global_conf(), + module_conf_json, + self.keypairs.clone(), + )?; let sync_params = network_sync.clone(); let thread_builder = thread::Builder::new().name(NM::name().0.into()); @@ -495,12 +496,13 @@ impl DursCore<DuRsConf> { .get(&M::name().to_string().as_str()) .cloned(); // Load module conf and keys - let ((module_conf, _), required_keys) = get_module_conf_and_keys::<M>( - self.currency_name.as_ref(), - &soft_meta_datas.conf.get_global_conf(), - module_conf_json, - self.keypairs.clone(), - )?; + let ((module_conf, _), required_keys) = + durs_conf::modules_conf::get_module_conf_and_keys::<M>( + self.currency_name.as_ref(), + &soft_meta_datas.conf.get_global_conf(), + module_conf_json, + self.keypairs.clone(), + )?; let thread_builder = thread::Builder::new().name(M::name().0.into()); self.threads.insert( @@ -553,66 +555,6 @@ impl DursCore<DuRsConf> { } } -/// Module configurations and required keys -pub type ModuleConfsAndKeys<M> = ( - ( - <M as DursModule<DuRsConf, DursMsg>>::ModuleConf, - Option<<M as DursModule<DuRsConf, DursMsg>>::ModuleUserConf>, - ), - RequiredKeysContent, -); - -/// Get module conf and keys -pub fn get_module_conf_and_keys<M: DursModule<DuRsConf, DursMsg>>( - currency_name: Option<&CurrencyName>, - global_conf: &<DuRsConf as DursConfTrait>::GlobalConf, - module_conf_json: Option<serde_json::Value>, - keypairs: DuniterKeyPairs, -) -> Result<ModuleConfsAndKeys<M>, ModuleConfError> { - Ok(( - get_module_conf::<M>(currency_name, global_conf, module_conf_json)?, - DuniterKeyPairs::get_required_keys_content(M::ask_required_keys(), keypairs), - )) -} - -fn get_env_module_user_conf<ModuleUserConf: serde::de::DeserializeOwned>( - module_name: ModuleStaticName, -) -> Result<ModuleUserConf, ModuleConfError> { - let prefix = format!( - "{}{}", - durs_conf::constants::DURS_ENV_PREFIX, - module_name.0.to_ascii_uppercase() - ); - - envy::prefixed(prefix) - .from_env::<ModuleUserConf>() - .map_err(ModuleConfError::EnvyErr) -} - -/// get module conf -pub fn get_module_conf<M: DursModule<DuRsConf, DursMsg>>( - currency_name: Option<&CurrencyName>, - global_conf: &<DuRsConf as DursConfTrait>::GlobalConf, - module_conf_json: Option<serde_json::Value>, -) -> Result<(M::ModuleConf, Option<M::ModuleUserConf>), ModuleConfError> { - let file_module_user_conf: M::ModuleUserConf = if let Some(module_conf_json) = module_conf_json - { - let file_module_user_conf_opt: Option<M::ModuleUserConf> = - serde_json::from_str(module_conf_json.to_string().as_str())?; - file_module_user_conf_opt.unwrap_or_default() - } else { - M::ModuleUserConf::default() - }; - - let env_module_user_conf = get_env_module_user_conf::<M::ModuleUserConf>(M::name())?; - - M::generate_module_conf( - currency_name, - global_conf, - Some(file_module_user_conf.merge(env_module_user_conf)), - ) -} - /// Launch databases explorer pub fn dbex(profile_path: PathBuf, csv: bool, query: &DbExQuery) { // Launch databases explorer