opendal/services/onedrive/
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::sync::Arc;
20
21use log::debug;
22use services::onedrive::core::OneDriveCore;
23use services::onedrive::core::OneDriveSigner;
24use tokio::sync::Mutex;
25
26use super::ONEDRIVE_SCHEME;
27use super::backend::OnedriveBackend;
28use super::config::OnedriveConfig;
29use crate::raw::*;
30use crate::*;
31
32/// Microsoft [OneDrive](https://onedrive.com) backend support.
33#[doc = include_str!("docs.md")]
34#[derive(Default)]
35pub struct OnedriveBuilder {
36    pub(super) config: OnedriveConfig,
37
38    #[deprecated(since = "0.53.0", note = "Use `Operator::update_http_client` instead")]
39    pub(super) http_client: Option<HttpClient>,
40}
41
42impl Debug for OnedriveBuilder {
43    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
44        f.debug_struct("OnedriveBuilder")
45            .field("config", &self.config)
46            .finish_non_exhaustive()
47    }
48}
49
50impl OnedriveBuilder {
51    /// Set root path of OneDrive folder.
52    pub fn root(mut self, root: &str) -> Self {
53        self.config.root = if root.is_empty() {
54            None
55        } else {
56            Some(root.to_string())
57        };
58
59        self
60    }
61
62    /// Specify the http client that used by this service.
63    ///
64    /// # Notes
65    ///
66    /// This API is part of OpenDAL's Raw API. `HttpClient` could be changed
67    /// during minor updates.
68    #[deprecated(since = "0.53.0", note = "Use `Operator::update_http_client` instead")]
69    #[allow(deprecated)]
70    pub fn http_client(mut self, http_client: HttpClient) -> Self {
71        self.http_client = Some(http_client);
72        self
73    }
74
75    /// Set the access token for a time limited access to Microsoft Graph API (also OneDrive).
76    ///
77    /// Microsoft Graph API uses a typical OAuth 2.0 flow for authentication and authorization.
78    /// You can get a access token from [Microsoft Graph Explore](https://developer.microsoft.com/en-us/graph/graph-explorer).
79    ///
80    /// # Note
81    ///
82    /// - An access token is short-lived.
83    /// - Use a refresh_token if you want to use OneDrive API for an extended period of time.
84    pub fn access_token(mut self, access_token: &str) -> Self {
85        self.config.access_token = Some(access_token.to_string());
86        self
87    }
88
89    /// Set the refresh token for long term access to Microsoft Graph API.
90    ///
91    /// OpenDAL will use a refresh token to maintain a fresh access token automatically.
92    ///
93    /// # Note
94    ///
95    /// - A refresh token is available through a OAuth 2.0 flow, with an additional scope `offline_access`.
96    pub fn refresh_token(mut self, refresh_token: &str) -> Self {
97        self.config.refresh_token = Some(refresh_token.to_string());
98        self
99    }
100
101    /// Set the client_id for a Microsoft Graph API application (available though Azure's registration portal)
102    ///
103    /// Required when using the refresh token.
104    pub fn client_id(mut self, client_id: &str) -> Self {
105        self.config.client_id = Some(client_id.to_string());
106        self
107    }
108
109    /// Set the client_secret for a Microsoft Graph API application
110    ///
111    /// Required for Web app when using the refresh token.
112    /// Don't use a client secret when use in a native app since the native app can't store the secret reliably.
113    pub fn client_secret(mut self, client_secret: &str) -> Self {
114        self.config.client_secret = Some(client_secret.to_string());
115        self
116    }
117
118    /// Enable versioning support for OneDrive
119    pub fn enable_versioning(mut self, enabled: bool) -> Self {
120        self.config.enable_versioning = enabled;
121        self
122    }
123}
124
125impl Builder for OnedriveBuilder {
126    type Config = OnedriveConfig;
127
128    fn build(self) -> Result<impl Access> {
129        let root = normalize_root(&self.config.root.unwrap_or_default());
130        debug!("backend use root {root}");
131
132        let info = AccessorInfo::default();
133        info.set_scheme(ONEDRIVE_SCHEME)
134            .set_root(&root)
135            .set_native_capability(Capability {
136                read: true,
137                read_with_if_none_match: true,
138
139                write: true,
140                write_with_if_match: true,
141                // OneDrive supports the file size up to 250GB
142                // Read more at https://support.microsoft.com/en-us/office/restrictions-and-limitations-in-onedrive-and-sharepoint-64883a5d-228e-48f5-b3d2-eb39e07630fa#individualfilesize
143                // However, we can't enable this, otherwise OpenDAL behavior tests will try to test creating huge
144                // file up to this size.
145                // write_total_max_size: Some(250 * 1024 * 1024 * 1024),
146                copy: true,
147                rename: true,
148
149                stat: true,
150                stat_with_if_none_match: true,
151                stat_with_version: self.config.enable_versioning,
152
153                delete: true,
154                create_dir: true,
155
156                list: true,
157                list_with_limit: true,
158                list_with_versions: self.config.enable_versioning,
159
160                shared: true,
161
162                ..Default::default()
163            });
164
165        // allow deprecated api here for compatibility
166        #[allow(deprecated)]
167        if let Some(client) = self.http_client {
168            info.update_http_client(|_| client);
169        }
170
171        let accessor_info = Arc::new(info);
172        let mut signer = OneDriveSigner::new(accessor_info.clone());
173
174        // Requires OAuth 2.0 tokens:
175        // - `access_token` (the short-lived token)
176        // - `refresh_token` flow (the long term token)
177        // to be mutually exclusive for setting up for implementation simplicity
178        match (self.config.access_token, self.config.refresh_token) {
179            (Some(access_token), None) => {
180                signer.access_token = access_token;
181                signer.expires_in = Timestamp::MAX;
182            }
183            (None, Some(refresh_token)) => {
184                let client_id = self.config.client_id.ok_or_else(|| {
185                    Error::new(
186                        ErrorKind::ConfigInvalid,
187                        "client_id must be set when refresh_token is set",
188                    )
189                    .with_context("service", ONEDRIVE_SCHEME)
190                })?;
191
192                signer.refresh_token = refresh_token;
193                signer.client_id = client_id;
194                if let Some(client_secret) = self.config.client_secret {
195                    signer.client_secret = client_secret;
196                }
197            }
198            (Some(_), Some(_)) => {
199                return Err(Error::new(
200                    ErrorKind::ConfigInvalid,
201                    "access_token and refresh_token cannot be set at the same time",
202                )
203                .with_context("service", ONEDRIVE_SCHEME));
204            }
205            (None, None) => {
206                return Err(Error::new(
207                    ErrorKind::ConfigInvalid,
208                    "access_token or refresh_token must be set",
209                )
210                .with_context("service", ONEDRIVE_SCHEME));
211            }
212        };
213
214        let core = Arc::new(OneDriveCore {
215            info: accessor_info,
216            root,
217            signer: Arc::new(Mutex::new(signer)),
218        });
219
220        Ok(OnedriveBackend { core })
221    }
222}