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 30d672631 feat(services/gdrive): implement rename (#3007)
30d672631 is described below

commit 30d672631dbdf3259ba3ea557db9ccaf08378436
Author: Suyan <[email protected]>
AuthorDate: Mon Sep 4 22:42:47 2023 +0800

    feat(services/gdrive): implement rename (#3007)
    
    * feat(services/gdrive): implement rename
    
    Signed-off-by: suyanhanx <[email protected]>
    
    * trigger ci
    
    Signed-off-by: suyanhanx <[email protected]>
    
    ---------
    
    Signed-off-by: suyanhanx <[email protected]>
---
 core/src/services/gdrive/backend.rs | 33 ++++++++++++++++--
 core/src/services/gdrive/core.rs    | 69 ++++++++++++++++++++++++++-----------
 2 files changed, 78 insertions(+), 24 deletions(-)

diff --git a/core/src/services/gdrive/backend.rs 
b/core/src/services/gdrive/backend.rs
index da6f950b3..df8ee7451 100644
--- a/core/src/services/gdrive/backend.rs
+++ b/core/src/services/gdrive/backend.rs
@@ -58,6 +58,8 @@ impl Accessor for GdriveBackend {
 
                 create_dir: true,
 
+                rename: true,
+
                 delete: true,
 
                 ..Default::default()
@@ -103,7 +105,10 @@ impl Accessor for GdriveBackend {
                 if !meta.files.is_empty() {
                     let mut cache = self.core.path_cache.lock().await;
 
-                    cache.insert(path.to_string(), meta.files[0].id.clone());
+                    cache.insert(
+                        build_abs_path(&self.core.root, path),
+                        meta.files[0].id.clone(),
+                    );
 
                     return Ok(RpCreateDir::default());
                 }
@@ -123,7 +128,7 @@ impl Accessor for GdriveBackend {
 
                 let mut cache = self.core.path_cache.lock().await;
 
-                cache.insert(path.to_string(), meta.id.clone());
+                cache.insert(build_abs_path(&self.core.root, path), 
meta.id.clone());
 
                 Ok(RpCreateDir::default())
             }
@@ -193,6 +198,28 @@ impl Accessor for GdriveBackend {
         ))
     }
 
+    async fn rename(&self, from: &str, to: &str, _args: OpRename) -> 
Result<RpRename> {
+        let resp = self.core.gdrive_patch_metadata_request(from, to).await?;
+
+        let status = resp.status();
+
+        match status {
+            StatusCode::OK => {
+                let body = resp.into_body().bytes().await?;
+                let meta = serde_json::from_slice::<GdriveFile>(&body)
+                    .map_err(new_json_deserialize_error)?;
+
+                let mut cache = self.core.path_cache.lock().await;
+
+                cache.remove(&build_abs_path(&self.core.root, from));
+                cache.insert(build_abs_path(&self.core.root, to), 
meta.id.clone());
+
+                Ok(RpRename::default())
+            }
+            _ => Err(parse_error(resp).await?),
+        }
+    }
+
     async fn delete(&self, path: &str, _: OpDelete) -> Result<RpDelete> {
         let resp = self.core.gdrive_delete(path).await;
         if let Ok(resp) = resp {
@@ -202,7 +229,7 @@ impl Accessor for GdriveBackend {
                 StatusCode::NO_CONTENT => {
                     let mut cache = self.core.path_cache.lock().await;
 
-                    cache.remove(path);
+                    cache.remove(&build_abs_path(&self.core.root, path));
 
                     return Ok(RpDelete::default());
                 }
diff --git a/core/src/services/gdrive/core.rs b/core/src/services/gdrive/core.rs
index 24ff1cf89..6e08f4d31 100644
--- a/core/src/services/gdrive/core.rs
+++ b/core/src/services/gdrive/core.rs
@@ -21,6 +21,7 @@ use std::fmt::Formatter;
 use std::sync::Arc;
 
 use bytes;
+use bytes::Bytes;
 use chrono::DateTime;
 use chrono::Utc;
 use http::header;
@@ -48,6 +49,11 @@ pub struct GdriveCore {
     ///
     /// Google Drive uses file id to identify a file.
     /// As the path is immutable, we can cache the mapping from path to file 
id.
+    ///
+    /// # Notes
+    ///
+    /// - The path is rooted at the root of the Google Drive.
+    /// - The path is absolute path, like `foo/bar`.
     pub path_cache: Arc<Mutex<HashMap<String, String>>>,
 }
 
@@ -71,7 +77,7 @@ impl GdriveCore {
     /// - A file only knows its parent id, but not its name.
     /// - To find the file id of a file, we need to traverse the path from the 
root to the file.
     pub(crate) async fn get_file_id_by_path(&self, file_path: &str) -> 
Result<String> {
-        let path = build_rooted_abs_path(&self.root, file_path);
+        let path = build_abs_path(&self.root, file_path);
 
         let mut cache = self.path_cache.lock().await;
 
@@ -148,7 +154,7 @@ impl GdriveCore {
     /// - The path is rooted at the root of the Google Drive.
     /// - Will create the parent path recursively.
     pub(crate) async fn ensure_parent_path(&self, path: &str) -> 
Result<String> {
-        let path = build_rooted_abs_path(&self.root, path);
+        let path = build_abs_path(&self.root, path);
 
         let mut parent: String = "root".to_owned();
         let mut file_path_items: Vec<&str> = path.split('/').filter(|&x| 
!x.is_empty()).collect();
@@ -319,29 +325,44 @@ impl GdriveCore {
         self.client.send(req).await
     }
 
-    pub async fn gdrive_update(
+    // Update with content and metadata
+    pub async fn gdrive_patch_metadata_request(
         &self,
-        path: &str,
-        size: Option<usize>,
-        content_type: Option<&str>,
-        body: AsyncBody,
+        source: &str,
+        target: &str,
     ) -> Result<Response<IncomingAsyncBody>> {
-        let url = format!(
-            
"https://www.googleapis.com/upload/drive/v3/files/{}?uploadType=media";,
-            self.get_file_id_by_path(path).await?
-        );
+        let file_id = self.get_file_id_by_path(source).await?;
 
-        let mut req = Request::put(&url);
+        let parent = self.ensure_parent_path(target).await?;
 
-        if let Some(size) = size {
-            req = req.header(header::CONTENT_LENGTH, size)
-        }
+        let url = format!("https://www.googleapis.com/drive/v3/files/{}";, 
file_id);
 
-        if let Some(mime) = content_type {
-            req = req.header(header::CONTENT_TYPE, mime)
-        }
+        let source_abs_path = build_abs_path(&self.root, source);
+        let mut source_parent: Vec<&str> = source_abs_path
+            .split('/')
+            .filter(|&x| !x.is_empty())
+            .collect();
+        source_parent.pop();
+
+        let cache = self.path_cache.lock().await;
+
+        let file_name = build_abs_path(&self.root, target)
+            .split('/')
+            .filter(|&x| !x.is_empty())
+            .last()
+            .unwrap()
+            .to_string();
+
+        let metadata = &json!({
+            "name": file_name,
+            "removeParents": 
[cache.get(&source_parent.join("/")).unwrap().to_string()],
+            "addParents": [parent],
+        });
+
+        let mut req = Request::patch(url)
+            .body(AsyncBody::Bytes(Bytes::from(metadata.to_string())))
+            .map_err(new_request_build_error)?;
 
-        let mut req = req.body(body).map_err(new_request_build_error)?;
         self.sign(&mut req).await?;
 
         self.client.send(req).await
@@ -362,11 +383,12 @@ impl GdriveCore {
         self.client.send(req).await
     }
 
+    /// Create a file with the content.
     pub async fn gdrive_upload_simple_request(
         &self,
         path: &str,
         size: u64,
-        body: bytes::Bytes,
+        body: Bytes,
     ) -> Result<Response<IncomingAsyncBody>> {
         let parent = self.ensure_parent_path(path).await?;
 
@@ -406,11 +428,16 @@ impl GdriveCore {
         self.client.send(req).await
     }
 
+    /// Overwrite the file with the content.
+    ///
+    /// # Notes
+    ///
+    /// - The file id is required. Do not use this method to create a file.
     pub async fn gdrive_upload_overwrite_simple_request(
         &self,
         file_id: &str,
         size: u64,
-        body: bytes::Bytes,
+        body: Bytes,
     ) -> Result<Response<IncomingAsyncBody>> {
         let url = format!(
             
"https://www.googleapis.com/upload/drive/v3/files/{}?uploadType=media";,

Reply via email to