Skip to content

Commit 80f15f5

Browse files
authored
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
1 parent 975c2dd commit 80f15f5

File tree

5 files changed

+159
-16
lines changed

5 files changed

+159
-16
lines changed

core/src/services/dropbox/backend.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ use http::StatusCode;
2323

2424
use super::core::DropboxCore;
2525
use super::error::parse_error;
26+
use super::response::DropboxMetadataResponse;
2627
use super::writer::DropboxWriter;
2728
use crate::raw::*;
2829
use crate::*;
@@ -90,4 +91,37 @@ impl Accessor for DropboxBackend {
9091
_ => Err(parse_error(resp).await?),
9192
}
9293
}
94+
95+
async fn stat(&self, path: &str, _: OpStat) -> Result<RpStat> {
96+
if path == "/" {
97+
return Ok(RpStat::new(Metadata::new(EntryMode::DIR)));
98+
}
99+
let resp = self.core.dropbox_get_metadata(path).await?;
100+
let status = resp.status();
101+
match status {
102+
StatusCode::OK => {
103+
let bytes = resp.into_body().bytes().await?;
104+
let decoded_response = serde_json::from_slice::<DropboxMetadataResponse>(&bytes)
105+
.map_err(new_json_deserialize_error)?;
106+
let entry_mode: EntryMode = match decoded_response.tag.as_str() {
107+
"file" => EntryMode::FILE,
108+
"folder" => EntryMode::DIR,
109+
_ => EntryMode::Unknown,
110+
};
111+
let mut metadata = Metadata::new(entry_mode);
112+
let last_modified = decoded_response.client_modified;
113+
let date_utc_last_modified = parse_datetime_from_rfc3339(&last_modified)?;
114+
metadata.set_last_modified(date_utc_last_modified);
115+
if decoded_response.size.is_some() {
116+
let size = decoded_response.size.unwrap();
117+
metadata.set_content_length(size);
118+
}
119+
Ok(RpStat::new(metadata))
120+
}
121+
StatusCode::NOT_FOUND if path.ends_with('/') => {
122+
Ok(RpStat::new(Metadata::new(EntryMode::DIR)))
123+
}
124+
_ => Err(parse_error(resp).await?),
125+
}
126+
}
93127
}

core/src/services/dropbox/core.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,23 @@ impl DropboxCore {
111111
self.client.send(request).await
112112
}
113113

114+
pub async fn dropbox_get_metadata(&self, path: &str) -> Result<Response<IncomingAsyncBody>> {
115+
let url = "https://api.dropboxapi.com/2/files/get_metadata".to_string();
116+
let args = DropboxMetadataArgs {
117+
path: build_rooted_abs_path(&self.root, path),
118+
..Default::default()
119+
};
120+
121+
let bs = Bytes::from(serde_json::to_string(&args).map_err(new_json_serialize_error)?);
122+
123+
let request = self
124+
.build_auth_header(Request::post(&url))
125+
.header(header::CONTENT_TYPE, "application/json")
126+
.body(AsyncBody::Bytes(bs))
127+
.map_err(new_request_build_error)?;
128+
self.client.send(request).await
129+
}
130+
114131
fn build_auth_header(&self, mut req: Builder) -> Builder {
115132
let auth_header_content = format!("Bearer {}", self.token);
116133
req = req.header(header::AUTHORIZATION, auth_header_content);
@@ -137,6 +154,14 @@ struct DropboxDeleteArgs {
137154
path: String,
138155
}
139156

157+
#[derive(Clone, Debug, Deserialize, Serialize)]
158+
struct DropboxMetadataArgs {
159+
include_deleted: bool,
160+
include_has_explicit_shared_members: bool,
161+
include_media_info: bool,
162+
path: String,
163+
}
164+
140165
impl Default for DropboxUploadArgs {
141166
fn default() -> Self {
142167
DropboxUploadArgs {
@@ -148,3 +173,14 @@ impl Default for DropboxUploadArgs {
148173
}
149174
}
150175
}
176+
177+
impl Default for DropboxMetadataArgs {
178+
fn default() -> Self {
179+
DropboxMetadataArgs {
180+
include_deleted: false,
181+
include_has_explicit_shared_members: false,
182+
include_media_info: false,
183+
path: "".to_string(),
184+
}
185+
}
186+
}

core/src/services/dropbox/error.rs

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,27 +17,13 @@
1717

1818
use http::Response;
1919
use http::StatusCode;
20-
use serde::Deserialize;
2120

21+
use super::response::DropboxErrorResponse;
2222
use crate::raw::*;
2323
use crate::Error;
2424
use crate::ErrorKind;
2525
use crate::Result;
2626

27-
#[derive(Default, Debug, Deserialize)]
28-
#[serde(default)]
29-
struct DropboxError {
30-
error_summary: String,
31-
error: DropboxErrorDetail,
32-
}
33-
34-
#[derive(Default, Debug, Deserialize)]
35-
#[serde(default)]
36-
struct DropboxErrorDetail {
37-
#[serde(rename(deserialize = ".tag"))]
38-
tag: String,
39-
}
40-
4127
/// Parse error response into Error.
4228
pub async fn parse_error(resp: Response<IncomingAsyncBody>) -> Result<Error> {
4329
let (parts, body) = resp.into_parts();
@@ -53,7 +39,7 @@ pub async fn parse_error(resp: Response<IncomingAsyncBody>) -> Result<Error> {
5339
_ => (ErrorKind::Unexpected, false),
5440
};
5541
let dropbox_error =
56-
serde_json::from_slice::<DropboxError>(&bs).map_err(new_json_deserialize_error);
42+
serde_json::from_slice::<DropboxErrorResponse>(&bs).map_err(new_json_deserialize_error);
5743
match dropbox_error {
5844
Ok(dropbox_error) => {
5945
let mut err = Error::new(kind, dropbox_error.error_summary.as_ref())

core/src/services/dropbox/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ mod backend;
1919
mod builder;
2020
mod core;
2121
mod error;
22+
mod response;
2223
mod writer;
2324

2425
pub use builder::DropboxBuilder as Dropbox;

core/src/services/dropbox/response.rs

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
use serde::Deserialize;
19+
20+
#[derive(Default, Debug, Deserialize)]
21+
#[serde(default)]
22+
pub struct DropboxErrorResponse {
23+
pub error_summary: String,
24+
pub error: DropboxErrorDetail,
25+
}
26+
27+
#[derive(Default, Debug, Deserialize)]
28+
#[serde(default)]
29+
pub struct DropboxErrorDetail {
30+
#[serde(rename(deserialize = ".tag"))]
31+
pub tag: String,
32+
}
33+
34+
#[derive(Default, Debug, Deserialize)]
35+
#[serde(default)]
36+
pub struct DropboxMetadataResponse {
37+
#[serde(rename(deserialize = ".tag"))]
38+
pub tag: String,
39+
pub client_modified: String,
40+
pub content_hash: Option<String>,
41+
pub file_lock_info: Option<DropboxMetadataFileLockInfo>,
42+
pub has_explicit_shared_members: Option<bool>,
43+
pub id: String,
44+
pub is_downloadable: Option<bool>,
45+
pub name: String,
46+
pub path_display: String,
47+
pub path_lower: String,
48+
pub property_groups: Option<Vec<DropboxMetadataPropertyGroup>>,
49+
pub rev: Option<String>,
50+
pub server_modified: Option<String>,
51+
pub sharing_info: Option<DropboxMetadataSharingInfo>,
52+
pub size: Option<u64>,
53+
}
54+
55+
#[derive(Default, Debug, Deserialize)]
56+
#[serde(default)]
57+
pub struct DropboxMetadataFileLockInfo {
58+
pub created: Option<String>,
59+
pub is_lockholder: bool,
60+
pub lockholder_name: Option<String>,
61+
}
62+
63+
#[derive(Default, Debug, Deserialize)]
64+
#[serde(default)]
65+
pub struct DropboxMetadataPropertyGroup {
66+
pub fields: Vec<DropboxMetadataPropertyGroupField>,
67+
pub template_id: String,
68+
}
69+
70+
#[derive(Default, Debug, Deserialize)]
71+
#[serde(default)]
72+
pub struct DropboxMetadataPropertyGroupField {
73+
pub name: String,
74+
pub value: String,
75+
}
76+
77+
#[derive(Default, Debug, Deserialize)]
78+
#[serde(default)]
79+
pub struct DropboxMetadataSharingInfo {
80+
pub modified_by: Option<String>,
81+
pub parent_shared_folder_id: Option<String>,
82+
pub read_only: Option<bool>,
83+
pub shared_folder_id: Option<String>,
84+
pub traverse_only: Option<bool>,
85+
pub no_access: Option<bool>,
86+
}

0 commit comments

Comments
 (0)