| // 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 std::fmt::Debug; |
| use std::sync::Arc; |
| |
| use log::debug; |
| use mea::mutex::Mutex; |
| |
| use super::GDRIVE_SCHEME; |
| use super::backend::GdriveBackend; |
| use super::config::GdriveConfig; |
| use super::core::GdriveCore; |
| use super::core::GdrivePathQuery; |
| use super::core::GdriveSigner; |
| use opendal_core::raw::*; |
| use opendal_core::*; |
| |
| /// [GoogleDrive](https://drive.google.com/) backend support. |
| #[derive(Default)] |
| #[doc = include_str!("docs.md")] |
| pub struct GdriveBuilder { |
| pub(super) config: GdriveConfig, |
| } |
| |
| impl Debug for GdriveBuilder { |
| fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| f.debug_struct("GdriveBuilder") |
| .field("config", &self.config) |
| .finish_non_exhaustive() |
| } |
| } |
| |
| impl GdriveBuilder { |
| /// Set root path of GoogleDrive folder. |
| pub fn root(mut self, root: &str) -> Self { |
| self.config.root = if root.is_empty() { |
| None |
| } else { |
| Some(root.to_string()) |
| }; |
| |
| self |
| } |
| |
| /// Access token is used for temporary access to the GoogleDrive API. |
| /// |
| /// You can get the access token from [GoogleDrive App Console](https://console.cloud.google.com/apis/credentials) |
| /// or [GoogleDrive OAuth2 Playground](https://developers.google.com/oauthplayground/) |
| /// |
| /// # Note |
| /// |
| /// - An access token is valid for 1 hour. |
| /// - If you want to use the access token for a long time, |
| /// you can use the refresh token to get a new access token. |
| pub fn access_token(mut self, access_token: &str) -> Self { |
| self.config.access_token = Some(access_token.to_string()); |
| self |
| } |
| |
| /// Refresh token is used for long term access to the GoogleDrive API. |
| /// |
| /// You can get the refresh token via OAuth 2.0 Flow of GoogleDrive API. |
| /// |
| /// OpenDAL will use this refresh token to get a new access token when the old one is expired. |
| pub fn refresh_token(mut self, refresh_token: &str) -> Self { |
| self.config.refresh_token = Some(refresh_token.to_string()); |
| self |
| } |
| |
| /// Set the client id for GoogleDrive. |
| /// |
| /// This is required for OAuth 2.0 Flow to refresh the access token. |
| pub fn client_id(mut self, client_id: &str) -> Self { |
| self.config.client_id = Some(client_id.to_string()); |
| self |
| } |
| |
| /// Set the client secret for GoogleDrive. |
| /// |
| /// This is required for OAuth 2.0 Flow with refresh the access token. |
| pub fn client_secret(mut self, client_secret: &str) -> Self { |
| self.config.client_secret = Some(client_secret.to_string()); |
| self |
| } |
| } |
| |
| impl Builder for GdriveBuilder { |
| type Config = GdriveConfig; |
| |
| fn build(self) -> Result<impl Access> { |
| let root = normalize_root(&self.config.root.unwrap_or_default()); |
| debug!("backend use root {root}"); |
| |
| let info = AccessorInfo::default(); |
| info.set_scheme(GDRIVE_SCHEME) |
| .set_root(&root) |
| .set_native_capability(Capability { |
| stat: true, |
| |
| read: true, |
| |
| list: true, |
| list_with_recursive: true, |
| |
| write: true, |
| |
| create_dir: true, |
| delete: true, |
| rename: true, |
| copy: true, |
| |
| shared: true, |
| |
| ..Default::default() |
| }); |
| |
| let accessor_info = Arc::new(info); |
| let mut signer = GdriveSigner::new(accessor_info.clone()); |
| match (self.config.access_token, self.config.refresh_token) { |
| (Some(access_token), None) => { |
| signer.access_token = access_token; |
| // We will never expire user specified access token. |
| signer.expires_in = Timestamp::MAX; |
| } |
| (None, Some(refresh_token)) => { |
| let client_id = self.config.client_id.ok_or_else(|| { |
| Error::new( |
| ErrorKind::ConfigInvalid, |
| "client_id must be set when refresh_token is set", |
| ) |
| .with_context("service", GDRIVE_SCHEME) |
| })?; |
| let client_secret = self.config.client_secret.ok_or_else(|| { |
| Error::new( |
| ErrorKind::ConfigInvalid, |
| "client_secret must be set when refresh_token is set", |
| ) |
| .with_context("service", GDRIVE_SCHEME) |
| })?; |
| |
| signer.refresh_token = refresh_token; |
| signer.client_id = client_id; |
| signer.client_secret = client_secret; |
| } |
| (Some(_), Some(_)) => { |
| return Err(Error::new( |
| ErrorKind::ConfigInvalid, |
| "access_token and refresh_token cannot be set at the same time", |
| ) |
| .with_context("service", GDRIVE_SCHEME)); |
| } |
| (None, None) => { |
| return Err(Error::new( |
| ErrorKind::ConfigInvalid, |
| "access_token or refresh_token must be set", |
| ) |
| .with_context("service", GDRIVE_SCHEME)); |
| } |
| }; |
| |
| let signer = Arc::new(Mutex::new(signer)); |
| |
| Ok(GdriveBackend { |
| core: Arc::new(GdriveCore { |
| info: accessor_info.clone(), |
| root, |
| signer: signer.clone(), |
| path_cache: PathCacher::new(GdrivePathQuery::new(accessor_info, signer)) |
| .with_lock(), |
| }), |
| }) |
| } |
| } |