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

xuanwo pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-opendal.git


The following commit(s) were added to refs/heads/main by this push:
     new ec9edd1a feat(services/gcs): Allow setting default storage_class 
(#1996)
ec9edd1a is described below

commit ec9edd1aa10043c532635ee9ad66270b2aa3b0b8
Author: silver-ymz <[email protected]>
AuthorDate: Fri Apr 14 20:35:45 2023 +0800

    feat(services/gcs): Allow setting default storage_class (#1996)
    
    * feat(services/gcs): Allow setting default storage_class
    
    * fix(services/gcs): Change impl of default storage_class
    
    * fix(services/gcs): Change impl of default storage_class
---
 core/src/services/gcs/backend.rs | 22 +++++++++++++++++++
 core/src/services/gcs/core.rs    | 47 +++++++++++++++++++++++++++++++++-------
 2 files changed, 61 insertions(+), 8 deletions(-)

diff --git a/core/src/services/gcs/backend.rs b/core/src/services/gcs/backend.rs
index 619d3c34..cbd5cb5f 100644
--- a/core/src/services/gcs/backend.rs
+++ b/core/src/services/gcs/backend.rs
@@ -62,6 +62,7 @@ const DEFAULT_GCS_SCOPE: &str = 
"https://www.googleapis.com/auth/devstorage.read
 /// - `endpoint`: Customizable endpoint setting
 /// - `credentials`: Credential string for GCS OAuth2
 /// - `predefined_acl`: Predefined ACL for GCS
+/// - `default_storage_class`: Default storage class for GCS
 ///
 /// You can refer to [`GcsBuilder`]'s docs for more information
 ///
@@ -88,6 +89,8 @@ const DEFAULT_GCS_SCOPE: &str = 
"https://www.googleapis.com/auth/devstorage.read
 ///     builder.credential("authentication token");
 ///     // set the predefined ACL for GCS
 ///     builder.predefined_acl("publicRead");
+///     // set the default storage class for GCS
+///     builder.default_storage_class("STANDARD");
 ///
 ///     let op: Operator = Operator::new(builder)?.finish();
 ///     Ok(())
@@ -115,6 +118,7 @@ pub struct GcsBuilder {
     http_client: Option<HttpClient>,
     customed_token_loader: Option<Box<dyn GoogleTokenLoad>>,
     predefined_acl: Option<String>,
+    default_storage_class: Option<String>,
 }
 
 impl GcsBuilder {
@@ -218,6 +222,20 @@ impl GcsBuilder {
         };
         self
     }
+
+    /// Set the default storage class for GCS.
+    ///
+    /// Available values are:
+    /// - `STANDARD`
+    /// - `NEARLINE`
+    /// - `COLDLINE`
+    /// - `ARCHIVE`
+    pub fn default_storage_class(&mut self, class: &str) -> &mut Self {
+        if !class.is_empty() {
+            self.default_storage_class = Some(class.to_string())
+        };
+        self
+    }
 }
 
 impl Debug for GcsBuilder {
@@ -233,6 +251,7 @@ impl Debug for GcsBuilder {
         if self.predefined_acl.is_some() {
             ds.field("predefined_acl", &self.predefined_acl);
         }
+        ds.field("default_storage_class", &self.default_storage_class);
         ds.finish()
     }
 }
@@ -250,6 +269,8 @@ impl Builder for GcsBuilder {
         map.get("credential").map(|v| builder.credential(v));
         map.get("scope").map(|v| builder.scope(v));
         map.get("predefined_acl").map(|v| builder.predefined_acl(v));
+        map.get("default_storage_class")
+            .map(|v| builder.default_storage_class(v));
 
         builder
     }
@@ -324,6 +345,7 @@ impl Builder for GcsBuilder {
                 token_loader,
                 credential_loader: cred_loader,
                 predefined_acl: self.predefined_acl.clone(),
+                default_storage_class: self.default_storage_class.clone(),
             }),
         };
 
diff --git a/core/src/services/gcs/core.rs b/core/src/services/gcs/core.rs
index 780999d5..780b178d 100644
--- a/core/src/services/gcs/core.rs
+++ b/core/src/services/gcs/core.rs
@@ -21,6 +21,7 @@ use std::fmt::Write;
 
 use backon::ExponentialBuilder;
 use backon::Retryable;
+use bytes::BytesMut;
 use http::header::CONTENT_LENGTH;
 use http::header::CONTENT_TYPE;
 use http::Request;
@@ -46,6 +47,7 @@ pub struct GcsCore {
     pub credential_loader: GoogleCredentialLoader,
 
     pub predefined_acl: Option<String>,
+    pub default_storage_class: Option<String>,
 }
 
 impl Debug for GcsCore {
@@ -139,9 +141,14 @@ impl GcsCore {
         let p = build_abs_path(&self.root, path);
 
         let mut url = format!(
-            "{}/upload/storage/v1/b/{}/o?uploadType=media&name={}",
+            "{}/upload/storage/v1/b/{}/o?uploadType={}&name={}",
             self.endpoint,
             self.bucket,
+            if self.default_storage_class.is_some() {
+                "multipart"
+            } else {
+                "media"
+            },
             percent_encode_path(&p)
         );
 
@@ -155,14 +162,38 @@ impl GcsCore {
             req = req.header(CONTENT_LENGTH, size)
         }
 
-        if let Some(mime) = content_type {
-            req = req.header(CONTENT_TYPE, mime)
-        }
-
-        // Set body
-        let req = req.body(body).map_err(new_request_build_error)?;
+        if let Some(storage_class) = &self.default_storage_class {
+            req = req.header(CONTENT_TYPE, "multipart/related; 
boundary=my-boundary");
+
+            let mut req_body = BytesMut::with_capacity(100);
+            write!(
+                &mut req_body,
+                "--my-boundary\nContent-Type: application/json; 
charset=UTF-8\n\n{{\"storageClass\": \"{}\"}}\n\n--my-boundary\n",
+                storage_class
+            ).unwrap();
+
+            if let Some(mime) = content_type {
+                write!(&mut req_body, "Content-Type: {}\n\n", mime).unwrap();
+            } else {
+                write!(&mut req_body, "Content-Type: 
application/octet-stream\n\n").unwrap();
+            }
+
+            if let AsyncBody::Bytes(bytes) = body {
+                req_body.extend_from_slice(&bytes);
+            }
+            write!(&mut req_body, "\n--my-boundary").unwrap();
+
+            let req_body = AsyncBody::Bytes(req_body.freeze());
+            let req = req.body(req_body).map_err(new_request_build_error)?;
+            Ok(req)
+        } else {
+            if let Some(content_type) = content_type {
+                req = req.header(CONTENT_TYPE, content_type);
+            }
 
-        Ok(req)
+            let req = req.body(body).map_err(new_request_build_error)?;
+            Ok(req)
+        }
     }
 
     pub async fn gcs_get_object_metadata(&self, path: &str) -> 
Result<Response<IncomingAsyncBody>> {

Reply via email to