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 9b9230c68 refactor(services/gdrive): Extract folder search logic 
(#3234)
9b9230c68 is described below

commit 9b9230c689ba138424b7a66bce6cb4aa4478eaec
Author: Xuanwo <[email protected]>
AuthorDate: Mon Oct 16 16:52:06 2023 +0800

    refactor(services/gdrive): Extract folder search logic (#3234)
    
    * Fix build
    
    Signed-off-by: Xuanwo <[email protected]>
    
    * Save work
    
    Signed-off-by: Xuanwo <[email protected]>
    
    * Save
    
    Signed-off-by: Xuanwo <[email protected]>
    
    * Fix test
    
    Signed-off-by: Xuanwo <[email protected]>
    
    * Fix test
    
    Signed-off-by: Xuanwo <[email protected]>
    
    * Polish parent
    
    Signed-off-by: Xuanwo <[email protected]>
    
    * Fix build
    
    Signed-off-by: Xuanwo <[email protected]>
    
    ---------
    
    Signed-off-by: Xuanwo <[email protected]>
---
 core/src/services/gdrive/backend.rs |  53 ++------
 core/src/services/gdrive/core.rs    | 246 +++++++++++++++++-------------------
 2 files changed, 125 insertions(+), 174 deletions(-)

diff --git a/core/src/services/gdrive/backend.rs 
b/core/src/services/gdrive/backend.rs
index e7bc2a2bd..879eab7cc 100644
--- a/core/src/services/gdrive/backend.rs
+++ b/core/src/services/gdrive/backend.rs
@@ -31,7 +31,6 @@ use super::pager::GdrivePager;
 use super::writer::GdriveWriter;
 use crate::raw::*;
 use crate::services::gdrive::core::GdriveFile;
-use crate::services::gdrive::core::GdriveFileList;
 use crate::types::Result;
 use crate::*;
 
@@ -100,50 +99,22 @@ impl Accessor for GdriveBackend {
     async fn create_dir(&self, path: &str, _args: OpCreateDir) -> 
Result<RpCreateDir> {
         let parent = self.core.ensure_parent_path(path).await?;
 
-        let path = path.split('/').filter(|&x| !x.is_empty()).last().unwrap();
+        // Make sure `/` has been trimmed.
+        let path = get_basename(path).trim_end_matches('/');
 
         // As Google Drive allows files have the same name, we need to check 
if the folder exists.
-        let resp = self.core.gdrive_search_folder(path, &parent).await?;
-        let status = resp.status();
-
-        match status {
-            StatusCode::OK => {
-                let body = resp.into_body().bytes().await?;
-                let meta = serde_json::from_slice::<GdriveFileList>(&body)
-                    .map_err(new_json_deserialize_error)?;
-
-                if !meta.files.is_empty() {
-                    let mut cache = self.core.path_cache.lock().await;
-
-                    cache.insert(
-                        build_abs_path(&self.core.root, path),
-                        meta.files[0].id.clone(),
-                    );
-
-                    return Ok(RpCreateDir::default());
-                }
-            }
-            _ => return Err(parse_error(resp).await?),
-        }
-
-        let resp = self.core.gdrive_create_folder(path, Some(parent)).await?;
+        let folder_id = self.core.gdrive_search_folder(&parent, path).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;
+        let id = if let Some(id) = folder_id {
+            id
+        } else {
+            self.core.gdrive_create_folder(&parent, path).await?
+        };
 
-                cache.insert(build_abs_path(&self.core.root, path), 
meta.id.clone());
+        let mut cache = self.core.path_cache.lock().await;
+        cache.insert(build_abs_path(&self.core.root, path), id);
 
-                Ok(RpCreateDir::default())
-            }
-            _ => Err(parse_error(resp).await?),
-        }
+        Ok(RpCreateDir::default())
     }
 
     async fn read(&self, path: &str, _args: OpRead) -> Result<(RpRead, 
Self::Reader)> {
@@ -233,7 +204,7 @@ impl Accessor for GdriveBackend {
             let status = resp.status();
 
             match status {
-                StatusCode::NO_CONTENT => {
+                StatusCode::NO_CONTENT | StatusCode::NOT_FOUND => {
                     let mut cache = self.core.path_cache.lock().await;
 
                     cache.remove(&build_abs_path(&self.core.root, path));
diff --git a/core/src/services/gdrive/core.rs b/core/src/services/gdrive/core.rs
index 4e7216184..3239b281b 100644
--- a/core/src/services/gdrive/core.rs
+++ b/core/src/services/gdrive/core.rs
@@ -96,52 +96,24 @@ impl GdriveCore {
                 continue;
             }
 
-            let mut query = format!(
-                "name = \"{}\" and \"{}\" in parents and trashed = false",
-                item, parent_id
-            );
-            if i != file_path_items.len() - 1 || path.ends_with('/') {
-                query += " and mimeType = 
'application/vnd.google-apps.folder'";
-            }
-
-            let mut req = Request::get(format!(
-                "https://www.googleapis.com/drive/v3/files?q={}";,
-                percent_encode_path(&query)
-            ))
-            .body(AsyncBody::default())
-            .map_err(new_request_build_error)?;
-
-            self.sign(&mut req).await?;
-
-            let resp = self.client.send(req).await?;
-            let status = resp.status();
-
-            match status {
-                StatusCode::OK => {
-                    let resp_body = &resp.into_body().bytes().await?;
-
-                    let gdrive_file_list: GdriveFileList =
-                        
serde_json::from_slice(resp_body).map_err(new_json_deserialize_error)?;
-
-                    if gdrive_file_list.files.is_empty() {
-                        return Err(Error::new(
-                            ErrorKind::NotFound,
-                            &format!("path not found: {}", item),
-                        ));
-                    }
-
-                    if gdrive_file_list.files.len() > 1 {
-                        return Err(Error::new(ErrorKind::Unexpected, 
&format!("please ensure that the file corresponding to the path exists and is 
unique. the response body is {}", String::from_utf8_lossy(resp_body))));
-                    }
-
-                    parent_id = gdrive_file_list.files[0].id.clone();
-
-                    cache.insert(path_part, parent_id.clone());
-                }
-                _ => {
-                    return Err(parse_error(resp).await?);
-                }
-            }
+            let id = if i != file_path_items.len() - 1 || path.ends_with('/') {
+                self.gdrive_search_folder(&parent_id, item).await?
+            } else {
+                self.gdrive_search_file(&parent_id, item)
+                    .await?
+                    .map(|v| v.id)
+            };
+
+            if let Some(id) = id {
+                parent_id = id;
+                cache.insert(path_part, parent_id.clone());
+            } else {
+                // TODO: return None instead of error.
+                return Err(Error::new(
+                    ErrorKind::NotFound,
+                    &format!("path not found: {}", item),
+                ));
+            };
         }
 
         Ok(parent_id)
@@ -170,84 +142,72 @@ impl GdriveCore {
                 continue;
             }
 
-            let query = format!(
-                "name = \"{}\" and \"{}\" in parents and trashed = false and 
mimeType = 'application/vnd.google-apps.folder'",
-                item, parent
-            );
+            let folder_id = self.gdrive_search_folder(&parent, item).await?;
+            let folder_id = if let Some(id) = folder_id {
+                id
+            } else {
+                self.gdrive_create_folder(&parent, item).await?
+            };
+
+            parent = folder_id;
+            cache.insert(path_part, parent.clone());
+        }
+
+        Ok(parent.to_owned())
+    }
 
-            let mut req = Request::get(format!(
-                "https://www.googleapis.com/drive/v3/files?q={}";,
-                percent_encode_path(&query)
-            ))
+    /// Search a folder by name
+    ///
+    /// returns it's file id if exists, otherwise returns `None`.
+    pub async fn gdrive_search_file(
+        &self,
+        parent: &str,
+        basename: &str,
+    ) -> Result<Option<GdriveFile>> {
+        let query =
+            format!("name = \"{basename}\" and \"{parent}\" in parents and 
trashed = false");
+        let url = format!(
+            "https://www.googleapis.com/drive/v3/files?q={}";,
+            percent_encode_path(&query)
+        );
+
+        let mut req = Request::get(&url)
             .body(AsyncBody::Empty)
             .map_err(new_request_build_error)?;
-            self.sign(&mut req).await?;
 
-            let resp = self.client.send(req).await?;
-            let status = resp.status();
+        self.sign(&mut req).await?;
 
-            match status {
-                StatusCode::OK => {
-                    let resp_body = &resp.into_body().bytes().await?;
+        let resp = self.client.send(req).await?;
+        let status = resp.status();
+        if !status.is_success() {
+            return Err(parse_error(resp).await?);
+        }
 
-                    let gdrive_file_list: GdriveFileList =
-                        
serde_json::from_slice(resp_body).map_err(new_json_deserialize_error)?;
-
-                    if gdrive_file_list.files.len() != 1 {
-                        let parent_name = file_path_items[i];
-                        let resp_body = self
-                            .gdrive_create_folder(parent_name, 
Some(parent.to_owned()))
-                            .await?
-                            .into_body()
-                            .bytes()
-                            .await?;
-                        let parent_meta: GdriveFile = 
serde_json::from_slice(&resp_body)
-                            .map_err(new_json_deserialize_error)?;
-
-                        parent = parent_meta.id;
-                    } else {
-                        parent = gdrive_file_list.files[0].id.clone();
-                    }
-
-                    cache.insert(path_part, parent.clone());
-                }
-                StatusCode::NOT_FOUND => {
-                    let parent_name = file_path_items[i];
-                    let res = self
-                        .gdrive_create_folder(parent_name, 
Some(parent.to_owned()))
-                        .await?;
-
-                    let status = res.status();
-
-                    match status {
-                        StatusCode::OK => {
-                            let parent_id = res.into_body().bytes().await?;
-                            parent = 
String::from_utf8_lossy(&parent_id).to_string();
-
-                            cache.insert(path_part, parent.clone());
-                        }
-                        _ => {
-                            return Err(parse_error(res).await?);
-                        }
-                    }
-                }
-                _ => {
-                    return Err(parse_error(resp).await?);
-                }
-            }
+        let body = resp.into_body().bytes().await?;
+        let mut file_list: GdriveFileList =
+            serde_json::from_slice(&body).map_err(new_json_deserialize_error)?;
+
+        if file_list.files.len() > 1 {
+            return Err(Error::new(
+                ErrorKind::Unexpected,
+                "please ensure that the file corresponding to the path is 
unique.",
+            ));
         }
 
-        Ok(parent.to_owned())
+        Ok(file_list.files.pop())
     }
 
+    /// Search a folder by name
+    ///
+    /// returns it's file id if exists, otherwise returns `None`.
     pub async fn gdrive_search_folder(
         &self,
-        target: &str,
         parent: &str,
-    ) -> Result<Response<IncomingAsyncBody>> {
+        basename: &str,
+    ) -> Result<Option<String>> {
         let query = format!(
-            "name = '{}' and '{}' in parents and trashed = false and mimeType 
= 'application/vnd.google-apps.folder'",
-            target, parent
+            "name = \"{}\" and \"{}\" in parents and trashed = false and 
mimeType = 'application/vnd.google-apps.folder'",
+            basename, parent
         );
         let url = format!(
             "https://www.googleapis.com/drive/v3/files?q={}";,
@@ -260,40 +220,60 @@ impl GdriveCore {
 
         self.sign(&mut req).await?;
 
-        self.client.send(req).await
+        let resp = self.client.send(req).await?;
+        let status = resp.status();
+
+        match status {
+            StatusCode::OK => {
+                let body = resp.into_body().bytes().await?;
+                let meta: GdriveFileList =
+                    
serde_json::from_slice(&body).map_err(new_json_deserialize_error)?;
+
+                if let Some(f) = meta.files.first() {
+                    Ok(Some(f.id.clone()))
+                } else {
+                    Ok(None)
+                }
+            }
+            _ => Err(parse_error(resp).await?),
+        }
     }
 
     /// Create a folder.
-    /// Should provide the parent folder id.
-    /// Or will create the folder in the root folder.
-    pub async fn gdrive_create_folder(
-        &self,
-        name: &str,
-        parent: Option<String>,
-    ) -> Result<Response<IncomingAsyncBody>> {
+    ///
+    /// # Input
+    ///
+    /// `parent_id` is the parent folder id.
+    ///
+    /// # Output
+    ///
+    /// Returns created folder's id while success, otherwise returns an error.
+    pub async fn gdrive_create_folder(&self, parent_id: &str, name: &str) -> 
Result<String> {
         let url = "https://www.googleapis.com/drive/v3/files";;
 
+        let content = serde_json::to_vec(&json!({
+            "name": name,
+            "mimeType": "application/vnd.google-apps.folder",
+            // If the parent is not provided, the folder will be created in 
the root folder.
+            "parents": [parent_id],
+        }))
+        .map_err(new_json_serialize_error)?;
+
         let mut req = Request::post(url)
             .header(header::CONTENT_TYPE, "application/json")
-            .body(AsyncBody::Bytes(bytes::Bytes::from(
-                serde_json::to_vec(&json!({
-                    "name": name,
-                    "mimeType": "application/vnd.google-apps.folder",
-                    // If the parent is not provided, the folder will be 
created in the root folder.
-                    "parents": [parent.unwrap_or("root".to_owned())],
-                }))
-                .map_err(|e| {
-                    Error::new(
-                        ErrorKind::Unexpected,
-                        &format!("failed to serialize json(create folder 
result): {}", e),
-                    )
-                })?,
-            )))
+            .body(AsyncBody::Bytes(Bytes::from(content)))
             .map_err(new_request_build_error)?;
 
         self.sign(&mut req).await?;
 
-        self.client.send(req).await
+        let resp = self.client.send(req).await?;
+        if !resp.status().is_success() {
+            return Err(parse_error(resp).await?);
+        }
+        let body = resp.into_body().bytes().await?;
+        let file: GdriveFile = 
serde_json::from_slice(&body).map_err(new_json_deserialize_error)?;
+
+        Ok(file.id)
     }
 
     pub async fn gdrive_stat(&self, path: &str) -> 
Result<Response<IncomingAsyncBody>> {

Reply via email to