opendal/services/dropbox/
builder.rs

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
18use std::fmt::Debug;
19use std::fmt::Formatter;
20use std::sync::Arc;
21
22use chrono::DateTime;
23use chrono::Utc;
24use tokio::sync::Mutex;
25
26use super::backend::DropboxBackend;
27use super::core::DropboxCore;
28use super::core::DropboxSigner;
29use crate::raw::*;
30use crate::services::DropboxConfig;
31use crate::*;
32
33impl Configurator for DropboxConfig {
34    type Builder = DropboxBuilder;
35
36    #[allow(deprecated)]
37    fn into_builder(self) -> Self::Builder {
38        DropboxBuilder {
39            config: self,
40            http_client: None,
41        }
42    }
43}
44
45/// [Dropbox](https://www.dropbox.com/) backend support.
46#[doc = include_str!("docs.md")]
47#[derive(Default)]
48pub struct DropboxBuilder {
49    config: DropboxConfig,
50
51    #[deprecated(since = "0.53.0", note = "Use `Operator::update_http_client` instead")]
52    http_client: Option<HttpClient>,
53}
54
55impl Debug for DropboxBuilder {
56    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
57        f.debug_struct("Builder")
58            .field("root", &self.config.root)
59            .finish()
60    }
61}
62
63impl DropboxBuilder {
64    /// Set the root directory for dropbox.
65    ///
66    /// Default to `/` if not set.
67    pub fn root(mut self, root: &str) -> Self {
68        self.config.root = if root.is_empty() {
69            None
70        } else {
71            Some(root.to_string())
72        };
73
74        self
75    }
76
77    /// Access token is used for temporary access to the Dropbox API.
78    ///
79    /// You can get the access token from [Dropbox App Console](https://www.dropbox.com/developers/apps)
80    ///
81    /// NOTE: this token will be expired in 4 hours.
82    /// If you are trying to use the Dropbox service in a long time, please set a refresh_token instead.
83    pub fn access_token(mut self, access_token: &str) -> Self {
84        self.config.access_token = Some(access_token.to_string());
85        self
86    }
87
88    /// Refresh token is used for long term access to the Dropbox API.
89    ///
90    /// You can get the refresh token via OAuth 2.0 Flow of Dropbox.
91    ///
92    /// OpenDAL will use this refresh token to get a new access token when the old one is expired.
93    pub fn refresh_token(mut self, refresh_token: &str) -> Self {
94        self.config.refresh_token = Some(refresh_token.to_string());
95        self
96    }
97
98    /// Set the client id for Dropbox.
99    ///
100    /// This is required for OAuth 2.0 Flow to refresh the access token.
101    pub fn client_id(mut self, client_id: &str) -> Self {
102        self.config.client_id = Some(client_id.to_string());
103        self
104    }
105
106    /// Set the client secret for Dropbox.
107    ///
108    /// This is required for OAuth 2.0 Flow with refresh the access token.
109    pub fn client_secret(mut self, client_secret: &str) -> Self {
110        self.config.client_secret = Some(client_secret.to_string());
111        self
112    }
113
114    /// Specify the http client that used by this service.
115    ///
116    /// # Notes
117    ///
118    /// This API is part of OpenDAL's Raw API. `HttpClient` could be changed
119    /// during minor updates.
120    #[deprecated(since = "0.53.0", note = "Use `Operator::update_http_client` instead")]
121    #[allow(deprecated)]
122    pub fn http_client(mut self, http_client: HttpClient) -> Self {
123        self.http_client = Some(http_client);
124        self
125    }
126}
127
128impl Builder for DropboxBuilder {
129    const SCHEME: Scheme = Scheme::Dropbox;
130    type Config = DropboxConfig;
131
132    fn build(self) -> Result<impl Access> {
133        let root = normalize_root(&self.config.root.unwrap_or_default());
134
135        let signer = match (self.config.access_token, self.config.refresh_token) {
136            (Some(access_token), None) => DropboxSigner {
137                access_token,
138                // We will never expire user specified token.
139                expires_in: DateTime::<Utc>::MAX_UTC,
140                ..Default::default()
141            },
142            (None, Some(refresh_token)) => {
143                let client_id = self.config.client_id.ok_or_else(|| {
144                    Error::new(
145                        ErrorKind::ConfigInvalid,
146                        "client_id must be set when refresh_token is set",
147                    )
148                    .with_context("service", Scheme::Dropbox)
149                })?;
150                let client_secret = self.config.client_secret.ok_or_else(|| {
151                    Error::new(
152                        ErrorKind::ConfigInvalid,
153                        "client_secret must be set when refresh_token is set",
154                    )
155                    .with_context("service", Scheme::Dropbox)
156                })?;
157
158                DropboxSigner {
159                    refresh_token,
160                    client_id,
161                    client_secret,
162                    ..Default::default()
163                }
164            }
165            (Some(_), Some(_)) => {
166                return Err(Error::new(
167                    ErrorKind::ConfigInvalid,
168                    "access_token and refresh_token can not be set at the same time",
169                )
170                .with_context("service", Scheme::Dropbox))
171            }
172            (None, None) => {
173                return Err(Error::new(
174                    ErrorKind::ConfigInvalid,
175                    "access_token or refresh_token must be set",
176                )
177                .with_context("service", Scheme::Dropbox))
178            }
179        };
180
181        Ok(DropboxBackend {
182            core: Arc::new(DropboxCore {
183                info: {
184                    let am = AccessorInfo::default();
185                    am.set_scheme(Scheme::Dropbox)
186                        .set_root(&root)
187                        .set_native_capability(Capability {
188                            stat: true,
189                            stat_has_last_modified: true,
190                            stat_has_content_length: true,
191
192                            read: true,
193
194                            write: true,
195
196                            create_dir: true,
197
198                            delete: true,
199
200                            list: true,
201                            list_with_recursive: true,
202                            list_has_last_modified: true,
203                            list_has_content_length: true,
204
205                            copy: true,
206
207                            rename: true,
208
209                            shared: true,
210
211                            ..Default::default()
212                        });
213
214                    // allow deprecated api here for compatibility
215                    #[allow(deprecated)]
216                    if let Some(client) = self.http_client {
217                        am.update_http_client(|_| client);
218                    }
219
220                    am.into()
221                },
222                root,
223                signer: Arc::new(Mutex::new(signer)),
224            }),
225        })
226    }
227}