diff --git a/src/gcp/builder.rs b/src/gcp/builder.rs index b637c53e..48a52464 100644 --- a/src/gcp/builder.rs +++ b/src/gcp/builder.rs @@ -98,6 +98,8 @@ pub struct GoogleCloudStorageBuilder { bucket_name: Option, /// Url url: Option, + /// Base URL + base_url: Option, /// Path to the service account file service_account_path: Option, /// The serialized service account key @@ -159,6 +161,15 @@ pub enum GoogleConfigKey { /// - `bucket_name` Bucket, + /// Base URL + /// + /// See [`GoogleCloudStorageBuilder::with_base_url`] for details. + /// + /// Supported keys: + /// - `google_base_url` + /// - `base_url` + BaseUrl, + /// Application credentials path /// /// See [`GoogleCloudStorageBuilder::with_application_credentials`]. @@ -185,6 +196,7 @@ impl AsRef for GoogleConfigKey { Self::ServiceAccount => "google_service_account", Self::ServiceAccountKey => "google_service_account_key", Self::Bucket => "google_bucket", + Self::BaseUrl => "google_base_url", Self::ApplicationCredentials => "google_application_credentials", Self::SkipSignature => "google_skip_signature", Self::Client(key) => key.as_ref(), @@ -203,6 +215,7 @@ impl FromStr for GoogleConfigKey { | "service_account_path" => Ok(Self::ServiceAccount), "google_service_account_key" | "service_account_key" => Ok(Self::ServiceAccountKey), "google_bucket" | "google_bucket_name" | "bucket" | "bucket_name" => Ok(Self::Bucket), + "google_base_url" | "base_url" => Ok(Self::BaseUrl), "google_application_credentials" | "application_credentials" => { Ok(Self::ApplicationCredentials) } @@ -225,6 +238,7 @@ impl Default for GoogleCloudStorageBuilder { retry_config: Default::default(), client_options: ClientOptions::new().with_allow_http(true), url: None, + base_url: None, credentials: None, skip_signature: Default::default(), signing_credentials: None, @@ -304,6 +318,7 @@ impl GoogleCloudStorageBuilder { GoogleConfigKey::ServiceAccount => self.service_account_path = Some(value.into()), GoogleConfigKey::ServiceAccountKey => self.service_account_key = Some(value.into()), GoogleConfigKey::Bucket => self.bucket_name = Some(value.into()), + GoogleConfigKey::BaseUrl => self.base_url = Some(value.into()), GoogleConfigKey::ApplicationCredentials => { self.application_credentials_path = Some(value.into()) } @@ -331,6 +346,7 @@ impl GoogleCloudStorageBuilder { GoogleConfigKey::ServiceAccount => self.service_account_path.clone(), GoogleConfigKey::ServiceAccountKey => self.service_account_key.clone(), GoogleConfigKey::Bucket => self.bucket_name.clone(), + GoogleConfigKey::BaseUrl => self.base_url.clone(), GoogleConfigKey::ApplicationCredentials => self.application_credentials_path.clone(), GoogleConfigKey::SkipSignature => Some(self.skip_signature.to_string()), GoogleConfigKey::Client(key) => self.client_options.get_config_value(key), @@ -367,6 +383,25 @@ impl GoogleCloudStorageBuilder { self } + /// Sets the base URL for communicating with GCS. + /// + /// If not explicitly set, it will be: + /// 1. Derived from the service account credentials, if provided + /// 2. Otherwise, uses the default GCS endpoint + /// + /// # Example + /// ``` + /// use object_store::gcp::GoogleCloudStorageBuilder; + /// + /// let gcs = GoogleCloudStorageBuilder::from_env() + /// .with_base_url("https://localhost:4443") + /// .build(); + /// ``` + pub fn with_base_url(mut self, base_url: &str) -> Self { + self.base_url = Some(base_url.into()); + self + } + /// Set the path to the service account file. /// /// This or [`GoogleCloudStorageBuilder::with_service_account_key`] must be @@ -506,9 +541,13 @@ impl GoogleCloudStorageBuilder { .map(|c| c.disable_oauth) .unwrap_or(false); - let gcs_base_url: String = service_account_credentials - .as_ref() - .and_then(|c| c.gcs_base_url.clone()) + let gcs_base_url: String = self + .base_url + .or_else(|| { + service_account_credentials + .as_ref() + .and_then(|c| c.gcs_base_url.clone()) + }) .unwrap_or_else(|| DEFAULT_GCS_BASE_URL.to_string()); let credentials = if let Some(credentials) = self.credentials { @@ -607,6 +646,7 @@ mod tests { use tempfile::NamedTempFile; const FAKE_KEY: &str = r#"{"private_key": "private_key", "private_key_id": "private_key_id", "client_email":"client_email", "disable_oauth":true}"#; + const FAKE_KEY_WITH_BASE_URL: &str = r#"{"private_key": "private_key", "private_key_id": "private_key_id", "client_email":"client_email", "disable_oauth":true, "gcs_base_url": "https://base-url-from-credentials:4443"}"#; #[test] fn gcs_test_service_account_key_and_path() { @@ -721,6 +761,46 @@ mod tests { .unwrap(); } + #[test] + fn gcs_test_with_base_url() { + let no_base_url = GoogleCloudStorageBuilder::new() + .with_bucket_name("foo") + .build() + .unwrap(); + assert_eq!(no_base_url.client.config().base_url, DEFAULT_GCS_BASE_URL); + + let explicit_override = GoogleCloudStorageBuilder::new() + .with_bucket_name("foo") + .with_base_url("https://explicitly-overriden:4443") + .build() + .unwrap(); + assert_eq!( + explicit_override.client.config().base_url, + "https://explicitly-overriden:4443" + ); + + let url_in_credentials = GoogleCloudStorageBuilder::new() + .with_bucket_name("foo") + .with_service_account_key(FAKE_KEY_WITH_BASE_URL) + .build() + .unwrap(); + assert_eq!( + url_in_credentials.client.config().base_url, + "https://base-url-from-credentials:4443" + ); + + let explicit_override_and_credentials = GoogleCloudStorageBuilder::new() + .with_bucket_name("foo") + .with_base_url("https://explicitly-overriden:4443") // this should take precedence + .with_service_account_key(FAKE_KEY_WITH_BASE_URL) + .build() + .unwrap(); + assert_eq!( + explicit_override_and_credentials.client.config().base_url, + "https://explicitly-overriden:4443" + ); + } + #[test] fn gcs_test_config_get_value() { let google_service_account = "object_store:fake_service_account".to_string();