Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 88 additions & 9 deletions src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,9 @@ pub enum Error {

#[error("Unsupported configuration version: {version}")]
UnsupportedVersion { version: Version },

#[error("Missing mandatory configuration option. Option path: {path}")]
MissingMandatoryOption { path: String },
}

impl From<figment::Error> for Error {
Expand Down Expand Up @@ -295,26 +298,28 @@ impl Configuration {
/// Loads the settings from the `Info` struct. The whole
/// configuration in toml format is included in the `info.index_toml` string.
///
/// Optionally will override the:
///
/// - Tracker api token.
/// - The auth secret key.
/// Configuration provided via env var has priority over config file path.
///
/// # Errors
///
/// Will return `Err` if the environment variable does not exist or has a bad configuration.
pub fn load_settings(info: &Info) -> Result<Settings, Error> {
// Load configuration provided by the user, prioritizing env vars
let figment = if let Some(config_toml) = &info.config_toml {
// Config in env var has priority over config file path
Figment::from(Serialized::defaults(Settings::default()))
.merge(Toml::string(config_toml))
.merge(Env::prefixed(CONFIG_OVERRIDE_PREFIX).split(CONFIG_OVERRIDE_SEPARATOR))
Figment::from(Toml::string(config_toml)).merge(Env::prefixed(CONFIG_OVERRIDE_PREFIX).split(CONFIG_OVERRIDE_SEPARATOR))
} else {
Figment::from(Serialized::defaults(Settings::default()))
.merge(Toml::file(&info.config_toml_path))
Figment::from(Toml::file(&info.config_toml_path))
.merge(Env::prefixed(CONFIG_OVERRIDE_PREFIX).split(CONFIG_OVERRIDE_SEPARATOR))
};

// Make sure user has provided the mandatory options.
Self::check_mandatory_options(&figment)?;

// Fill missing options with default values.
let figment = figment.join(Serialized::defaults(Settings::default()));

// Build final configuration.
let settings: Settings = figment.extract()?;

if settings.metadata.schema_version != Version::new(VERSION_2) {
Expand All @@ -326,6 +331,28 @@ impl Configuration {
Ok(settings)
}

/// Some configuration options are mandatory. The tracker will panic if
/// the user doesn't provide an explicit value for them from one of the
/// configuration sources: TOML or ENV VARS.
///
/// # Errors
///
/// Will return an error if a mandatory configuration option is only
/// obtained by default value (code), meaning the user hasn't overridden it.
fn check_mandatory_options(figment: &Figment) -> Result<(), Error> {
let mandatory_options = ["metadata.schema_version", "logging.threshold"];

for mandatory_option in mandatory_options {
figment
.find_value(mandatory_option)
.map_err(|_err| Error::MissingMandatoryOption {
path: mandatory_option.to_owned(),
})?;
}

Ok(())
}

pub async fn get_all(&self) -> Settings {
let settings_lock = self.settings.read().await;

Expand Down Expand Up @@ -474,6 +501,58 @@ mod tests {
});
}

#[test]
fn configuration_should_use_the_default_values_when_only_the_mandatory_options_are_provided_by_the_user_via_toml_file() {
figment::Jail::expect_with(|jail| {
jail.create_file(
"index.toml",
r#"
[metadata]
schema_version = "2.0.0"

[logging]
threshold = "info"
"#,
)?;

let info = Info {
config_toml: None,
config_toml_path: "index.toml".to_string(),
};

let settings = Configuration::load_settings(&info).expect("Could not load configuration from file");

assert_eq!(settings, Settings::default());

Ok(())
});
}

#[test]
fn configuration_should_use_the_default_values_when_only_the_mandatory_options_are_provided_by_the_user_via_toml_content() {
figment::Jail::expect_with(|_jail| {
let config_toml = r#"
[metadata]
schema_version = "2.0.0"

[logging]
threshold = "info"
"#
.to_string();

let info = Info {
config_toml: Some(config_toml),
config_toml_path: String::new(),
};

let settings = Configuration::load_settings(&info).expect("Could not load configuration from file");

assert_eq!(settings, Settings::default());

Ok(())
});
}

#[tokio::test]
async fn configuration_should_allow_to_override_the_tracker_api_token_provided_in_the_toml_file() {
figment::Jail::expect_with(|jail| {
Expand Down