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",