This is an automated email from the ASF dual-hosted git repository.

tustvold pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow-rs-object-store.git


The following commit(s) were added to refs/heads/main by this push:
     new de4a30e  Allow explicitly specifying GCS base URL (#567)
de4a30e is described below

commit de4a30e24b40ade260f88b7894dbc819124597ee
Author: Renan GEHAN <[email protected]>
AuthorDate: Sat Dec 13 14:21:33 2025 +0100

    Allow explicitly specifying GCS base URL (#567)
    
    * Allow explicitly specify GCS base URL
    
    * Add configuration key
    
    * Fix doc (don't refer to private var)
    
    * Reformat
---
 src/gcp/builder.rs | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 83 insertions(+), 3 deletions(-)

diff --git a/src/gcp/builder.rs b/src/gcp/builder.rs
index b637c53..48a5246 100644
--- a/src/gcp/builder.rs
+++ b/src/gcp/builder.rs
@@ -98,6 +98,8 @@ pub struct GoogleCloudStorageBuilder {
     bucket_name: Option<String>,
     /// Url
     url: Option<String>,
+    /// Base URL
+    base_url: Option<String>,
     /// Path to the service account file
     service_account_path: Option<String>,
     /// 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<str> 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();

Reply via email to