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 super::DEFAULT_SCHEME;
30use crate::raw::*;
31use crate::services::DropboxConfig;
32use crate::*;
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    type Config = DropboxConfig;
130
131    fn build(self) -> Result<impl Access> {
132        let root = normalize_root(&self.config.root.unwrap_or_default());
133
134        let signer = match (self.config.access_token, self.config.refresh_token) {
135            (Some(access_token), None) => DropboxSigner {
136                access_token,
137                // We will never expire user specified token.
138                expires_in: DateTime::<Utc>::MAX_UTC,
139                ..Default::default()
140            },
141            (None, Some(refresh_token)) => {
142                let client_id = self.config.client_id.ok_or_else(|| {
143                    Error::new(
144                        ErrorKind::ConfigInvalid,
145                        "client_id must be set when refresh_token is set",
146                    )
147                    .with_context("service", Scheme::Dropbox)
148                })?;
149                let client_secret = self.config.client_secret.ok_or_else(|| {
150                    Error::new(
151                        ErrorKind::ConfigInvalid,
152                        "client_secret must be set when refresh_token is set",
153                    )
154                    .with_context("service", Scheme::Dropbox)
155                })?;
156
157                DropboxSigner {
158                    refresh_token,
159                    client_id,
160                    client_secret,
161                    ..Default::default()
162                }
163            }
164            (Some(_), Some(_)) => {
165                return Err(Error::new(
166                    ErrorKind::ConfigInvalid,
167                    "access_token and refresh_token can not be set at the same time",
168                )
169                .with_context("service", Scheme::Dropbox))
170            }
171            (None, None) => {
172                return Err(Error::new(
173                    ErrorKind::ConfigInvalid,
174                    "access_token or refresh_token must be set",
175                )
176                .with_context("service", Scheme::Dropbox))
177            }
178        };
179
180        Ok(DropboxBackend {
181            core: Arc::new(DropboxCore {
182                info: {
183                    let am = AccessorInfo::default();
184                    am.set_scheme(DEFAULT_SCHEME)
185                        .set_root(&root)
186                        .set_native_capability(Capability {
187                            stat: true,
188
189                            read: true,
190
191                            write: true,
192
193                            create_dir: true,
194
195                            delete: true,
196
197                            list: true,
198                            list_with_recursive: true,
199
200                            copy: true,
201
202                            rename: true,
203
204                            shared: true,
205
206                            ..Default::default()
207                        });
208
209                    // allow deprecated api here for compatibility
210                    #[allow(deprecated)]
211                    if let Some(client) = self.http_client {
212                        am.update_http_client(|_| client);
213                    }
214
215                    am.into()
216                },
217                root,
218                signer: Arc::new(Mutex::new(signer)),
219            }),
220        })
221    }
222}