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 80f15f5bb feat(service): Support stat for Dropbox (#2588)
80f15f5bb is described below
commit 80f15f5bb6cc4f48e671e85a9f89ca728b365b58
Author: Nadeshiko Manju <[email protected]>
AuthorDate: Wed Jul 5 15:13:52 2023 +0800
feat(service): Support stat for Dropbox (#2588)
* feat(service): Support stat for Dropbox
* feat(service): Support stat for Dropbox
* feat(service): Support stat for Dropbox
* feat(service): Support stat for Dropbox
* Update code
* Update code
---
core/src/services/dropbox/backend.rs | 34 ++++++++++++++
core/src/services/dropbox/core.rs | 36 +++++++++++++++
core/src/services/dropbox/error.rs | 18 +-------
core/src/services/dropbox/mod.rs | 1 +
core/src/services/dropbox/response.rs | 86 +++++++++++++++++++++++++++++++++++
5 files changed, 159 insertions(+), 16 deletions(-)
diff --git a/core/src/services/dropbox/backend.rs
b/core/src/services/dropbox/backend.rs
index da8e3e690..e9a2fecf1 100644
--- a/core/src/services/dropbox/backend.rs
+++ b/core/src/services/dropbox/backend.rs
@@ -23,6 +23,7 @@ use http::StatusCode;
use super::core::DropboxCore;
use super::error::parse_error;
+use super::response::DropboxMetadataResponse;
use super::writer::DropboxWriter;
use crate::raw::*;
use crate::*;
@@ -90,4 +91,37 @@ impl Accessor for DropboxBackend {
_ => Err(parse_error(resp).await?),
}
}
+
+ async fn stat(&self, path: &str, _: OpStat) -> Result<RpStat> {
+ if path == "/" {
+ return Ok(RpStat::new(Metadata::new(EntryMode::DIR)));
+ }
+ let resp = self.core.dropbox_get_metadata(path).await?;
+ let status = resp.status();
+ match status {
+ StatusCode::OK => {
+ let bytes = resp.into_body().bytes().await?;
+ let decoded_response =
serde_json::from_slice::<DropboxMetadataResponse>(&bytes)
+ .map_err(new_json_deserialize_error)?;
+ let entry_mode: EntryMode = match
decoded_response.tag.as_str() {
+ "file" => EntryMode::FILE,
+ "folder" => EntryMode::DIR,
+ _ => EntryMode::Unknown,
+ };
+ let mut metadata = Metadata::new(entry_mode);
+ let last_modified = decoded_response.client_modified;
+ let date_utc_last_modified =
parse_datetime_from_rfc3339(&last_modified)?;
+ metadata.set_last_modified(date_utc_last_modified);
+ if decoded_response.size.is_some() {
+ let size = decoded_response.size.unwrap();
+ metadata.set_content_length(size);
+ }
+ Ok(RpStat::new(metadata))
+ }
+ StatusCode::NOT_FOUND if path.ends_with('/') => {
+ Ok(RpStat::new(Metadata::new(EntryMode::DIR)))
+ }
+ _ => Err(parse_error(resp).await?),
+ }
+ }
}
diff --git a/core/src/services/dropbox/core.rs
b/core/src/services/dropbox/core.rs
index 31b597d4c..ad4655a32 100644
--- a/core/src/services/dropbox/core.rs
+++ b/core/src/services/dropbox/core.rs
@@ -111,6 +111,23 @@ impl DropboxCore {
self.client.send(request).await
}
+ pub async fn dropbox_get_metadata(&self, path: &str) ->
Result<Response<IncomingAsyncBody>> {
+ let url =
"https://api.dropboxapi.com/2/files/get_metadata".to_string();
+ let args = DropboxMetadataArgs {
+ path: build_rooted_abs_path(&self.root, path),
+ ..Default::default()
+ };
+
+ let bs =
Bytes::from(serde_json::to_string(&args).map_err(new_json_serialize_error)?);
+
+ let request = self
+ .build_auth_header(Request::post(&url))
+ .header(header::CONTENT_TYPE, "application/json")
+ .body(AsyncBody::Bytes(bs))
+ .map_err(new_request_build_error)?;
+ self.client.send(request).await
+ }
+
fn build_auth_header(&self, mut req: Builder) -> Builder {
let auth_header_content = format!("Bearer {}", self.token);
req = req.header(header::AUTHORIZATION, auth_header_content);
@@ -137,6 +154,14 @@ struct DropboxDeleteArgs {
path: String,
}
+#[derive(Clone, Debug, Deserialize, Serialize)]
+struct DropboxMetadataArgs {
+ include_deleted: bool,
+ include_has_explicit_shared_members: bool,
+ include_media_info: bool,
+ path: String,
+}
+
impl Default for DropboxUploadArgs {
fn default() -> Self {
DropboxUploadArgs {
@@ -148,3 +173,14 @@ impl Default for DropboxUploadArgs {
}
}
}
+
+impl Default for DropboxMetadataArgs {
+ fn default() -> Self {
+ DropboxMetadataArgs {
+ include_deleted: false,
+ include_has_explicit_shared_members: false,
+ include_media_info: false,
+ path: "".to_string(),
+ }
+ }
+}
diff --git a/core/src/services/dropbox/error.rs
b/core/src/services/dropbox/error.rs
index aa7981ba0..db878f6a0 100644
--- a/core/src/services/dropbox/error.rs
+++ b/core/src/services/dropbox/error.rs
@@ -17,27 +17,13 @@
use http::Response;
use http::StatusCode;
-use serde::Deserialize;
+use super::response::DropboxErrorResponse;
use crate::raw::*;
use crate::Error;
use crate::ErrorKind;
use crate::Result;
-#[derive(Default, Debug, Deserialize)]
-#[serde(default)]
-struct DropboxError {
- error_summary: String,
- error: DropboxErrorDetail,
-}
-
-#[derive(Default, Debug, Deserialize)]
-#[serde(default)]
-struct DropboxErrorDetail {
- #[serde(rename(deserialize = ".tag"))]
- tag: String,
-}
-
/// Parse error response into Error.
pub async fn parse_error(resp: Response<IncomingAsyncBody>) -> Result<Error> {
let (parts, body) = resp.into_parts();
@@ -53,7 +39,7 @@ pub async fn parse_error(resp: Response<IncomingAsyncBody>)
-> Result<Error> {
_ => (ErrorKind::Unexpected, false),
};
let dropbox_error =
-
serde_json::from_slice::<DropboxError>(&bs).map_err(new_json_deserialize_error);
+
serde_json::from_slice::<DropboxErrorResponse>(&bs).map_err(new_json_deserialize_error);
match dropbox_error {
Ok(dropbox_error) => {
let mut err = Error::new(kind,
dropbox_error.error_summary.as_ref())
diff --git a/core/src/services/dropbox/mod.rs b/core/src/services/dropbox/mod.rs
index 239974a75..048b7d74d 100644
--- a/core/src/services/dropbox/mod.rs
+++ b/core/src/services/dropbox/mod.rs
@@ -19,6 +19,7 @@ mod backend;
mod builder;
mod core;
mod error;
+mod response;
mod writer;
pub use builder::DropboxBuilder as Dropbox;
diff --git a/core/src/services/dropbox/response.rs
b/core/src/services/dropbox/response.rs
new file mode 100644
index 000000000..2e5c15a81
--- /dev/null
+++ b/core/src/services/dropbox/response.rs
@@ -0,0 +1,86 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+use serde::Deserialize;
+
+#[derive(Default, Debug, Deserialize)]
+#[serde(default)]
+pub struct DropboxErrorResponse {
+ pub error_summary: String,
+ pub error: DropboxErrorDetail,
+}
+
+#[derive(Default, Debug, Deserialize)]
+#[serde(default)]
+pub struct DropboxErrorDetail {
+ #[serde(rename(deserialize = ".tag"))]
+ pub tag: String,
+}
+
+#[derive(Default, Debug, Deserialize)]
+#[serde(default)]
+pub struct DropboxMetadataResponse {
+ #[serde(rename(deserialize = ".tag"))]
+ pub tag: String,
+ pub client_modified: String,
+ pub content_hash: Option<String>,
+ pub file_lock_info: Option<DropboxMetadataFileLockInfo>,
+ pub has_explicit_shared_members: Option<bool>,
+ pub id: String,
+ pub is_downloadable: Option<bool>,
+ pub name: String,
+ pub path_display: String,
+ pub path_lower: String,
+ pub property_groups: Option<Vec<DropboxMetadataPropertyGroup>>,
+ pub rev: Option<String>,
+ pub server_modified: Option<String>,
+ pub sharing_info: Option<DropboxMetadataSharingInfo>,
+ pub size: Option<u64>,
+}
+
+#[derive(Default, Debug, Deserialize)]
+#[serde(default)]
+pub struct DropboxMetadataFileLockInfo {
+ pub created: Option<String>,
+ pub is_lockholder: bool,
+ pub lockholder_name: Option<String>,
+}
+
+#[derive(Default, Debug, Deserialize)]
+#[serde(default)]
+pub struct DropboxMetadataPropertyGroup {
+ pub fields: Vec<DropboxMetadataPropertyGroupField>,
+ pub template_id: String,
+}
+
+#[derive(Default, Debug, Deserialize)]
+#[serde(default)]
+pub struct DropboxMetadataPropertyGroupField {
+ pub name: String,
+ pub value: String,
+}
+
+#[derive(Default, Debug, Deserialize)]
+#[serde(default)]
+pub struct DropboxMetadataSharingInfo {
+ pub modified_by: Option<String>,
+ pub parent_shared_folder_id: Option<String>,
+ pub read_only: Option<bool>,
+ pub shared_folder_id: Option<String>,
+ pub traverse_only: Option<bool>,
+ pub no_access: Option<bool>,
+}