This is an automated email from the ASF dual-hosted git repository. xuanwo pushed a commit to branch xuanwo/default-provider-api-oracle in repository https://gitbox.apache.org/repos/asf/opendal-reqsign.git
commit 42031c18b2ab96f3e6e0dc8a56f23320e9be86b7 Author: Xuanwo <[email protected]> AuthorDate: Wed Mar 18 18:46:08 2026 +0800 services/oracle: migrate default credential builder api --- services/oracle/src/provide_credential/default.rs | 143 ++++++++++++++++------ 1 file changed, 106 insertions(+), 37 deletions(-) diff --git a/services/oracle/src/provide_credential/default.rs b/services/oracle/src/provide_credential/default.rs index f29ce3e..bfee5c2 100644 --- a/services/oracle/src/provide_credential/default.rs +++ b/services/oracle/src/provide_credential/default.rs @@ -75,58 +75,50 @@ impl DefaultCredentialProvider { /// Builder for `DefaultCredentialProvider`. /// -/// Use `configure_env` / `configure_config_file` to customize providers, and -/// `disable_env(bool)` / `disable_config_file(bool)` to control participation. -/// Finish with `build()` to construct the provider. -#[derive(Default)] +/// Use `env` / `config_file` to customize providers, `no_env` / +/// `no_config_file` to remove them from the chain, and `build()` to construct +/// the provider. pub struct DefaultCredentialProviderBuilder { env: Option<EnvCredentialProvider>, config_file: Option<ConfigFileCredentialProvider>, } +impl Default for DefaultCredentialProviderBuilder { + fn default() -> Self { + Self { + env: Some(EnvCredentialProvider::default()), + config_file: Some(ConfigFileCredentialProvider::default()), + } + } +} + impl DefaultCredentialProviderBuilder { /// Create a new builder with default state. pub fn new() -> Self { Self::default() } - /// Configure the environment credential provider. - pub fn configure_env<F>(mut self, f: F) -> Self - where - F: FnOnce(EnvCredentialProvider) -> EnvCredentialProvider, - { - let p = self.env.take().unwrap_or_default(); - self.env = Some(f(p)); + /// Set the environment credential provider slot. + pub fn env(mut self, provider: EnvCredentialProvider) -> Self { + self.env = Some(provider); self } - /// Disable (true) or ensure enabled (false) the environment provider. - pub fn disable_env(mut self, disable: bool) -> Self { - if disable { - self.env = None; - } else if self.env.is_none() { - self.env = Some(EnvCredentialProvider::new()); - } + /// Remove the environment credential provider slot. + pub fn no_env(mut self) -> Self { + self.env = None; self } - /// Configure the config-file credential provider. - pub fn configure_config_file<F>(mut self, f: F) -> Self - where - F: FnOnce(ConfigFileCredentialProvider) -> ConfigFileCredentialProvider, - { - let p = self.config_file.take().unwrap_or_default(); - self.config_file = Some(f(p)); + /// Set the config-file credential provider slot. + pub fn config_file(mut self, provider: ConfigFileCredentialProvider) -> Self { + self.config_file = Some(provider); self } - /// Disable (true) or ensure enabled (false) the config-file provider. - pub fn disable_config_file(mut self, disable: bool) -> Self { - if disable { - self.config_file = None; - } else if self.config_file.is_none() { - self.config_file = Some(ConfigFileCredentialProvider::new()); - } + /// Remove the config-file credential provider slot. + pub fn no_config_file(mut self) -> Self { + self.config_file = None; self } @@ -135,13 +127,9 @@ impl DefaultCredentialProviderBuilder { let mut chain = ProvideCredentialChain::new(); if let Some(p) = self.env { chain = chain.push(p); - } else { - chain = chain.push(EnvCredentialProvider::new()); } if let Some(p) = self.config_file { chain = chain.push(p); - } else { - chain = chain.push(ConfigFileCredentialProvider::new()); } DefaultCredentialProvider::with_chain(chain) } @@ -157,9 +145,15 @@ impl ProvideCredential for DefaultCredentialProvider { #[cfg(test)] mod tests { use super::*; - use crate::constants::{ORACLE_FINGERPRINT, ORACLE_KEY_FILE, ORACLE_TENANCY, ORACLE_USER}; + use crate::constants::{ + ORACLE_CONFIG_FILE, ORACLE_FINGERPRINT, ORACLE_KEY_FILE, ORACLE_TENANCY, ORACLE_USER, + }; use reqsign_core::{Context, StaticEnv}; + use reqsign_file_read_tokio::TokioFileRead; + use reqsign_http_send_reqwest::ReqwestHttpSend; use std::collections::HashMap; + use std::fs; + use std::time::{SystemTime, UNIX_EPOCH}; #[tokio::test] async fn test_default_matches_new() { @@ -194,4 +188,79 @@ mod tests { assert!(from_default.expires_in.is_some()); assert!(from_new.expires_in.is_some()); } + + #[tokio::test] + async fn test_builder_no_env_removes_env_provider() { + let ctx = Context::new() + .with_file_read(TokioFileRead) + .with_http_send(ReqwestHttpSend::default()) + .with_env(StaticEnv { + home_dir: Some("/tmp".into()), + envs: HashMap::from([ + (ORACLE_USER.to_string(), "test_user".to_string()), + (ORACLE_TENANCY.to_string(), "test_tenancy".to_string()), + (ORACLE_KEY_FILE.to_string(), "/tmp/key.pem".to_string()), + ( + ORACLE_FINGERPRINT.to_string(), + "test_fingerprint".to_string(), + ), + ]), + }); + + let credential = DefaultCredentialProvider::builder() + .no_env() + .build() + .provide_credential(&ctx) + .await + .expect("load must succeed"); + + assert!(credential.is_none()); + } + + #[tokio::test] + async fn test_builder_no_config_file_removes_config_file_provider() { + let unique = SystemTime::now() + .duration_since(UNIX_EPOCH) + .expect("system time must be after unix epoch") + .as_nanos(); + let root = std::env::temp_dir().join(format!("reqsign-oracle-default-provider-{unique}")); + let config_dir = root.join(".oci"); + let config_path = config_dir.join("config"); + + fs::create_dir_all(&config_dir).expect("create config dir must succeed"); + fs::write( + &config_path, + "[DEFAULT]\ntenancy=test_tenancy\nuser=test_user\nkey_file=/tmp/key.pem\nfingerprint=test_fingerprint\n", + ) + .expect("write config file must succeed"); + + let ctx = Context::new() + .with_file_read(TokioFileRead) + .with_http_send(ReqwestHttpSend::default()) + .with_env(StaticEnv { + home_dir: Some(root.clone()), + envs: HashMap::from([( + ORACLE_CONFIG_FILE.to_string(), + "~/.oci/config".to_string(), + )]), + }); + + let from_default = DefaultCredentialProvider::new() + .provide_credential(&ctx) + .await + .expect("load must succeed"); + assert!(from_default.is_some()); + + let without_config_file = DefaultCredentialProvider::builder() + .no_env() + .no_config_file() + .build() + .provide_credential(&ctx) + .await + .expect("load must succeed"); + + assert!(without_config_file.is_none()); + + fs::remove_dir_all(&root).expect("cleanup temp dir must succeed"); + } }
