From 32110d6970ae9c16d6889fc5c1fe65556fa3d469 Mon Sep 17 00:00:00 2001 From: philippeitis <33013301+philippeitis@users.noreply.github.com> Date: Sun, 16 Oct 2022 16:35:34 -0700 Subject: [PATCH 01/10] Return Option from GetToken::get_token instead of Result<...> --- google-apis-common/src/auth.rs | 96 +++++++++++++++++ google-apis-common/src/lib.rs | 108 ++------------------ src/generator/templates/api/lib/mbuild.mako | 19 +--- 3 files changed, 109 insertions(+), 114 deletions(-) create mode 100644 google-apis-common/src/auth.rs diff --git a/google-apis-common/src/auth.rs b/google-apis-common/src/auth.rs new file mode 100644 index 0000000000..bc95305b4b --- /dev/null +++ b/google-apis-common/src/auth.rs @@ -0,0 +1,96 @@ +use std::future::Future; +use std::pin::Pin; + +// TODO: Simplify this to Option +type TokenResult = Option; + +pub trait GetToken: GetTokenClone + Send + Sync { + /// Called whenever there is the need for an oauth token after + /// the official authenticator implementation didn't provide one, for some reason. + /// If this method returns None as well, the underlying operation will fail + fn get_token<'a>( + &'a self, + _scopes: &'a [&str], + ) -> Pin + Send + 'a>> { + Box::pin(async move { None }) + } +} + +pub trait GetTokenClone { + fn clone_box(&self) -> Box; +} + +impl GetTokenClone for T +where + T: 'static + GetToken + Clone, +{ + fn clone_box(&self) -> Box { + Box::new(self.clone()) + } +} + +impl Clone for Box { + fn clone(&self) -> Box { + self.clone_box() + } +} + +impl GetToken for String { + fn get_token<'a>( + &'a self, + _scopes: &'a [&str], + ) -> Pin + Send + 'a>> { + Box::pin(async move { Some(self.clone()) }) + } +} + +/// In the event that the API endpoint does not require an oauth2 token, `NoToken` should be provided to the hub to avoid specifying an +/// authenticator. +#[derive(Default, Clone)] +pub struct NoToken; + +impl GetToken for NoToken {} + +// TODO: Make this optional +// #[cfg(feature = "yup-oauth2")] +mod yup_oauth2_impl { + use core::future::Future; + use core::pin::Pin; + + use super::{GetToken, TokenResult}; + + use http::Uri; + use hyper::client::connect::Connection; + use tokio::io::{AsyncRead, AsyncWrite}; + use tower_service::Service; + use yup_oauth2::authenticator::Authenticator; + + impl GetToken for Authenticator + where + S: Service + Clone + Send + Sync + 'static, + S::Response: Connection + AsyncRead + AsyncWrite + Send + Unpin + 'static, + S::Future: Send + Unpin + 'static, + S::Error: Into>, + { + fn get_token<'a>( + &'a self, + scopes: &'a [&str], + ) -> Pin + Send + 'a>> { + Box::pin(async move { self.token(scopes).await.ok().map(|t| t.as_str().to_owned()) }) + } + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn dyn_get_token_is_send() { + fn with_send(_x: impl Send) {} + + let mut gt = String::new(); + let dgt: &mut dyn GetToken = &mut gt; + with_send(dgt); + } +} diff --git a/google-apis-common/src/lib.rs b/google-apis-common/src/lib.rs index 022b9a22c0..10aadd36b7 100644 --- a/google-apis-common/src/lib.rs +++ b/google-apis-common/src/lib.rs @@ -1,12 +1,11 @@ +pub mod auth; pub mod field_mask; pub mod serde; use std::error; use std::error::Error as StdError; use std::fmt::{self, Display}; -use std::future::Future; use std::io::{self, Cursor, Read, Seek, SeekFrom, Write}; -use std::pin::Pin; use std::str::FromStr; use std::time::Duration; @@ -26,6 +25,7 @@ use tokio::io::{AsyncRead, AsyncWrite}; use tokio::time::sleep; use tower_service; +pub use auth::{GetToken, NoToken}; pub use chrono; pub use field_mask::FieldMask; pub use serde_with; @@ -119,8 +119,7 @@ pub trait Delegate: Send { /// impending failure. /// The given Error provides information about why the token couldn't be acquired in the /// first place - fn token(&mut self, err: &oauth2::Error) -> Option { - let _ = err; + fn token(&mut self) -> Option { None } @@ -236,9 +235,8 @@ pub enum Error { /// Neither through the authenticator, nor through the Delegate. MissingAPIKey, - // TODO: Remove oauth2::Error /// We required a Token, but didn't get one from the Authenticator - MissingToken(oauth2::Error), + MissingToken, /// The delgate instructed to cancel the operation Cancelled, @@ -268,24 +266,17 @@ impl Display for Error { resource_size, max_size ), Error::MissingAPIKey => { - (writeln!( + writeln!( f, "The application's API key was not found in the configuration" - )) - .ok(); + )?; writeln!( f, "It is used as there are no Scopes defined for this method." ) } - Error::BadRequest(ref message) => { - writeln!(f, "Bad Request: {}", message)?; - Ok(()) - } - // TODO: Remove oauth2::Error - Error::MissingToken(ref err) => { - writeln!(f, "Token retrieval failed with error: {}", err) - } + Error::BadRequest(ref message) => writeln!(f, "Bad Request: {}", message), + Error::MissingToken => writeln!(f, "Token retrieval failed"), Error::Cancelled => writeln!(f, "Operation cancelled by delegate"), Error::FieldClash(field) => writeln!( f, @@ -777,86 +768,14 @@ pub async fn get_body_as_string(res_body: &mut hyper::Body) -> String { res_body_string.to_string() } -// TODO: Simplify this to Option -type TokenResult = std::result::Result, oauth2::Error>; - -pub trait GetToken: GetTokenClone + Send + Sync { - /// Called whenever there is the need for an oauth token after - /// the official authenticator implementation didn't provide one, for some reason. - /// If this method returns None as well, the underlying operation will fail - fn get_token<'a>(&'a self, _scopes: &'a [&str]) -> Pin + Send + 'a>> { - Box::pin(async move { Ok(None) }) - } -} - -pub trait GetTokenClone { - fn clone_box(&self) -> Box; -} - -impl GetTokenClone for T -where - T: 'static + GetToken + Clone, -{ - fn clone_box(&self) -> Box { - Box::new(self.clone()) - } -} - -impl Clone for Box { - fn clone(&self) -> Box { - self.clone_box() - } -} - -impl GetToken for String { - fn get_token<'a>(&'a self, _scopes: &'a [&str]) -> Pin + Send + 'a>> { - Box::pin(async move { Ok(Some(self.clone())) }) - } -} - -/// In the event that the API endpoint does not require an oauth2 token, `NoToken` should be provided to the hub to avoid specifying an -/// authenticator. -#[derive(Default, Clone)] -pub struct NoToken; - -impl GetToken for NoToken {} - -// TODO: Make this optional -// #[cfg(feature = "yup-oauth2")] -mod yup_oauth2_impl { - use core::future::Future; - use core::pin::Pin; - - use super::{GetToken, TokenResult}; - - use tower_service::Service; - use yup_oauth2::authenticator::Authenticator; - use tokio::io::{AsyncRead, AsyncWrite}; - use http::Uri; - use hyper::client::connect::Connection; - - - impl GetToken for Authenticator where - S: Service + Clone + Send + Sync + 'static, - S::Response: Connection + AsyncRead + AsyncWrite + Send + Unpin + 'static, - S::Future: Send + Unpin + 'static, - S::Error: Into> { - fn get_token<'a>(&'a self, scopes: &'a [&str]) -> Pin + Send + 'a>> { - Box::pin(async move { - self.token(scopes).await.map(|t| Some(t.as_str().to_owned())) - }) - } - } -} - #[cfg(test)] mod test_api { use super::*; use std::default::Default; use std::str::FromStr; + use ::serde::{Deserialize, Serialize}; use serde_json as json; - use ::serde::{Serialize, Deserialize}; #[test] fn serde() { @@ -915,13 +834,4 @@ mod test_api { let dlg: &mut dyn Delegate = &mut dd; with_send(dlg); } - - #[test] - fn dyn_get_token_is_send() { - fn with_send(_x: impl Send) {} - - let mut gt = String::new(); - let dgt: &mut dyn GetToken = &mut gt; - with_send(dgt); - } } diff --git a/src/generator/templates/api/lib/mbuild.mako b/src/generator/templates/api/lib/mbuild.mako index 2cb387ae82..48d35aa5c5 100644 --- a/src/generator/templates/api/lib/mbuild.mako +++ b/src/generator/templates/api/lib/mbuild.mako @@ -711,24 +711,13 @@ else { loop { % if default_scope: let token = match ${auth_call}.get_token(&self.${api.properties.scopes}.iter().map(String::as_str).collect::>()[..]).await { - // TODO: remove Ok / Err branches - Ok(Some(token)) => token.clone(), - Ok(None) => { - let err = oauth2::Error::OtherError(anyhow::Error::msg("unknown error occurred while generating oauth2 token")); - match dlg.token(&err) { + Some(token) => token.clone(), + None => { + match dlg.token() { Some(token) => token, None => { ${delegate_finish}(false); - return Err(client::Error::MissingToken(err)) - } - } - } - Err(err) => { - match dlg.token(&err) { - Some(token) => token, - None => { - ${delegate_finish}(false); - return Err(client::Error::MissingToken(err)) + return Err(client::Error::MissingToken); } } } From 50dd53a87716547ae6bf829e1baef5500b218cef Mon Sep 17 00:00:00 2001 From: philippeitis <33013301+philippeitis@users.noreply.github.com> Date: Sun, 16 Oct 2022 17:04:20 -0700 Subject: [PATCH 02/10] Document auth.rs --- google-apis-common/src/auth.rs | 70 ++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/google-apis-common/src/auth.rs b/google-apis-common/src/auth.rs index bc95305b4b..2f2cc9630a 100644 --- a/google-apis-common/src/auth.rs +++ b/google-apis-common/src/auth.rs @@ -1,3 +1,73 @@ +//! Authentication for Google API endpoints +//! +//! Allows users to provide custom authentication implementations to suite their needs. +//! +//! By default, [`GetToken`] is implemented for: +//! - [`Authenticator`] : An authenticator which supports a variety of authentication methods +//! - [`String`] : Plain oauth2 token in String format +//! - [`NoToken`] : No token, used for APIs which do not require a token +//! +//! # Usage +//! [`GetToken`] instances are designed to be used with the Hub constructor provided by the +//! generated APIs. +//! +//! If you intend to use the API libraries on client devices, +//! [`Authenticator`] supports a variety of client-side authentication methods, +//! and should be used to provide authentication. +//! +//! If you intend to use the API libraries server-side, with server-side client authentication, +//! use the [`oauth2`] crate and convert the resulting [`AccessToken`] to [`String`]. +//! +//! If you intend to use APIs which do not require authentication, use [`NoToken`]. +//! +//! If you have custom authentication requirements, you can implement [`GetToken`] manually. +//! +//! # Example +//! ```rust +//! use core::future::Future; +//! use core::pin::Pin; +//! +//! use google_apis_common::GetToken; +//! +//! use http::Uri; +//! use hyper::client::connect::Connection; +//! use tokio::io::{AsyncRead, AsyncWrite}; +//! use tower_service::Service; +//! use yup_oauth2::authenticator::Authenticator; +//! +//! #[derive(Clone)] +//! struct AuthenticatorWithRetry { +//! auth: yup_oauth2::authenticator::Authenticator, +//! retries: usize, +//! } +//! +//! impl GetToken for AuthenticatorWithRetry +//! where +//! S: Service + Clone + Send + Sync + 'static, +//! S::Response: Connection + AsyncRead + AsyncWrite + Send + Unpin + 'static, +//! S::Future: Send + Unpin + 'static, +//! S::Error: Into>, +//! { +//! fn get_token<'a>( +//! &'a self, +//! scopes: &'a [&str], +//! ) -> Pin> + Send + 'a>> { +//! Box::pin(async move { +//! let mut auth_token = None; +//! for _ in 0..self.retries { +//! if let Ok(token) = self.auth.token(scopes).await { +//! auth_token.insert(token.as_str().to_owned()); +//! break; +//! } +//! } +//! auth_token +//! }) +//! } +//! } +//! ``` +//! [`oauth2`]: https://docs.rs/oauth2/latest/oauth2/ +//! [`AccessToken`]: https://docs.rs/oauth2/latest/oauth2/struct.AccessToken.html +//! [`Authenticator`]: yup_oauth2::authenticator::Authenticator use std::future::Future; use std::pin::Pin; From 7ca7f1cafda0292e888e8faff296ab1c3c4400bb Mon Sep 17 00:00:00 2001 From: philippeitis <33013301+philippeitis@users.noreply.github.com> Date: Sun, 16 Oct 2022 17:18:33 -0700 Subject: [PATCH 03/10] Make yup-oauth2 optional --- google-apis-common/Cargo.toml | 8 +++----- google-apis-common/src/auth.rs | 4 +--- google-apis-common/src/lib.rs | 1 + src/generator/templates/Cargo.toml.mako | 8 +++++--- src/generator/templates/api/api.rs.mako | 2 +- src/generator/templates/api/lib.rs.mako | 5 ++++- 6 files changed, 15 insertions(+), 13 deletions(-) diff --git a/google-apis-common/Cargo.toml b/google-apis-common/Cargo.toml index e66cf39c19..aa601650a3 100644 --- a/google-apis-common/Cargo.toml +++ b/google-apis-common/Cargo.toml @@ -24,11 +24,9 @@ serde_json = "^ 1.0" base64 = "0.13.0" chrono = { version = "0.4.22", features = ["serde"] } -## TODO: Make yup-oauth2 optional -## yup-oauth2 = { version = "^ 7.0", optional = true } -yup-oauth2 = "^ 7.0" +yup-oauth2 = { version = "^ 7.0", optional = true } itertools = "^ 0.10" -hyper = "^ 0.14" +hyper = { version = "^ 0.14", features = ["client", "http2"] } http = "^0.2" -tokio = "^1.0" +tokio = { version = "^1.0", features = ["time"] } tower-service = "^0.3.1" diff --git a/google-apis-common/src/auth.rs b/google-apis-common/src/auth.rs index 2f2cc9630a..6b9321fb06 100644 --- a/google-apis-common/src/auth.rs +++ b/google-apis-common/src/auth.rs @@ -71,7 +71,6 @@ use std::future::Future; use std::pin::Pin; -// TODO: Simplify this to Option type TokenResult = Option; pub trait GetToken: GetTokenClone + Send + Sync { @@ -121,8 +120,7 @@ pub struct NoToken; impl GetToken for NoToken {} -// TODO: Make this optional -// #[cfg(feature = "yup-oauth2")] +#[cfg(feature = "yup-oauth2")] mod yup_oauth2_impl { use core::future::Future; use core::pin::Pin; diff --git a/google-apis-common/src/lib.rs b/google-apis-common/src/lib.rs index 10aadd36b7..758cc0e09c 100644 --- a/google-apis-common/src/lib.rs +++ b/google-apis-common/src/lib.rs @@ -29,6 +29,7 @@ pub use auth::{GetToken, NoToken}; pub use chrono; pub use field_mask::FieldMask; pub use serde_with; +#[cfg(feature = "yup-oauth2")] pub use yup_oauth2 as oauth2; const LINE_ENDING: &str = "\r\n"; diff --git a/src/generator/templates/Cargo.toml.mako b/src/generator/templates/Cargo.toml.mako index d696b97f8a..3001397c39 100644 --- a/src/generator/templates/Cargo.toml.mako +++ b/src/generator/templates/Cargo.toml.mako @@ -58,6 +58,8 @@ path = "../${api_name}" version = "${util.crate_version()}" % endif -## TODO: Make yup-oauth2 optional -# [features] -# default = ["yup-oauth2"] +% if not cargo.get("is_executable", False): +[features] +yup-oauth2 = ["google-apis-common/yup-oauth2"] +default = ["yup-oauth2"] +% endif \ No newline at end of file diff --git a/src/generator/templates/api/api.rs.mako b/src/generator/templates/api/api.rs.mako index 9f58e0cdee..7029c36241 100644 --- a/src/generator/templates/api/api.rs.mako +++ b/src/generator/templates/api/api.rs.mako @@ -31,7 +31,7 @@ use tokio::time::sleep; use tower_service; use serde::{Serialize, Deserialize}; -use crate::{client, client::GetToken, client::oauth2, client::serde_with}; +use crate::{client, client::GetToken, client::serde_with}; // ############## // UTILITIES ### diff --git a/src/generator/templates/api/lib.rs.mako b/src/generator/templates/api/lib.rs.mako index f1042232ef..3aced87c47 100644 --- a/src/generator/templates/api/lib.rs.mako +++ b/src/generator/templates/api/lib.rs.mako @@ -49,5 +49,8 @@ pub mod api; // Re-export the hub type and some basic client structs pub use api::${hub_type}; +pub use client::{Result, Error, Delegate, FieldMask}; + // Re-export the yup_oauth2 crate, that is required to call some methods of the hub and the client -pub use client::{Result, Error, Delegate, oauth2, FieldMask}; +#[cfg(feature = "yup-oauth2")] +pub use client::oauth2; \ No newline at end of file From 02dbcb9782704a1759f9cf04756d89f738be6da6 Mon Sep 17 00:00:00 2001 From: philippeitis <33013301+philippeitis@users.noreply.github.com> Date: Sun, 16 Oct 2022 17:25:46 -0700 Subject: [PATCH 04/10] Fix lib.rs docs --- src/generator/templates/api/lib/mbuild.mako | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/generator/templates/api/lib/mbuild.mako b/src/generator/templates/api/lib/mbuild.mako index 48d35aa5c5..857ea2e03f 100644 --- a/src/generator/templates/api/lib/mbuild.mako +++ b/src/generator/templates/api/lib/mbuild.mako @@ -399,7 +399,7 @@ match result { Error::HttpError(_) |Error::Io(_) |Error::MissingAPIKey - |Error::MissingToken(_) + |Error::MissingToken |Error::Cancelled |Error::UploadSizeLimitExceeded(_, _) |Error::Failure(_) From 1132b542d2590e0bccf588691d95e47148bc059d Mon Sep 17 00:00:00 2001 From: philippeitis <33013301+philippeitis@users.noreply.github.com> Date: Sun, 16 Oct 2022 17:28:29 -0700 Subject: [PATCH 05/10] More correct GetToken docs --- google-apis-common/src/auth.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/google-apis-common/src/auth.rs b/google-apis-common/src/auth.rs index 6b9321fb06..7576e56079 100644 --- a/google-apis-common/src/auth.rs +++ b/google-apis-common/src/auth.rs @@ -74,9 +74,8 @@ use std::pin::Pin; type TokenResult = Option; pub trait GetToken: GetTokenClone + Send + Sync { - /// Called whenever there is the need for an oauth token after - /// the official authenticator implementation didn't provide one, for some reason. - /// If this method returns None as well, the underlying operation will fail + /// Called whenever an API call require authentication via an oauth2 token. + /// Returns `None` if a token can not be generated for the provided scopes. fn get_token<'a>( &'a self, _scopes: &'a [&str], From a375b710b1debaef08583bfc3160c120076d0ebe Mon Sep 17 00:00:00 2001 From: philippeitis <33013301+philippeitis@users.noreply.github.com> Date: Sun, 16 Oct 2022 21:04:43 -0700 Subject: [PATCH 06/10] Use Result, _> --- google-apis-common/src/auth.rs | 43 ++++++++++++++------- google-apis-common/src/lib.rs | 37 +++++++++--------- src/generator/templates/api/lib/mbuild.mako | 28 +++++++------- 3 files changed, 62 insertions(+), 46 deletions(-) diff --git a/google-apis-common/src/auth.rs b/google-apis-common/src/auth.rs index 7576e56079..4574a27e4b 100644 --- a/google-apis-common/src/auth.rs +++ b/google-apis-common/src/auth.rs @@ -27,17 +27,17 @@ //! use core::future::Future; //! use core::pin::Pin; //! -//! use google_apis_common::GetToken; +//! use google_apis_common::{GetToken, oauth2}; //! //! use http::Uri; //! use hyper::client::connect::Connection; //! use tokio::io::{AsyncRead, AsyncWrite}; //! use tower_service::Service; -//! use yup_oauth2::authenticator::Authenticator; +//! use oauth2::authenticator::Authenticator; //! //! #[derive(Clone)] //! struct AuthenticatorWithRetry { -//! auth: yup_oauth2::authenticator::Authenticator, +//! auth: Authenticator, //! retries: usize, //! } //! @@ -51,13 +51,16 @@ //! fn get_token<'a>( //! &'a self, //! scopes: &'a [&str], -//! ) -> Pin> + Send + 'a>> { +//! ) -> Pin, Box>> + Send + 'a>> { //! Box::pin(async move { -//! let mut auth_token = None; +//! let mut auth_token = Ok(None); //! for _ in 0..self.retries { -//! if let Ok(token) = self.auth.token(scopes).await { -//! auth_token.insert(token.as_str().to_owned()); -//! break; +//! match self.auth.token(scopes).await { +//! Ok(token) => { +//! auth_token = Ok(Some(token.as_str().to_owned())); +//! break; +//! }, +//! Err(e) => auth_token = Err(e.into()), //! } //! } //! auth_token @@ -71,7 +74,7 @@ use std::future::Future; use std::pin::Pin; -type TokenResult = Option; +type TokenResult = Result, Box>; pub trait GetToken: GetTokenClone + Send + Sync { /// Called whenever an API call require authentication via an oauth2 token. @@ -79,9 +82,7 @@ pub trait GetToken: GetTokenClone + Send + Sync { fn get_token<'a>( &'a self, _scopes: &'a [&str], - ) -> Pin + Send + 'a>> { - Box::pin(async move { None }) - } + ) -> Pin + Send + 'a>>; } pub trait GetTokenClone { @@ -108,7 +109,7 @@ impl GetToken for String { &'a self, _scopes: &'a [&str], ) -> Pin + Send + 'a>> { - Box::pin(async move { Some(self.clone()) }) + Box::pin(async move { Ok(Some(self.clone())) }) } } @@ -117,7 +118,14 @@ impl GetToken for String { #[derive(Default, Clone)] pub struct NoToken; -impl GetToken for NoToken {} +impl GetToken for NoToken { + fn get_token<'a>( + &'a self, + _scopes: &'a [&str], + ) -> Pin + Send + 'a>> { + Box::pin(async move { Ok(None) }) + } +} #[cfg(feature = "yup-oauth2")] mod yup_oauth2_impl { @@ -143,7 +151,12 @@ mod yup_oauth2_impl { &'a self, scopes: &'a [&str], ) -> Pin + Send + 'a>> { - Box::pin(async move { self.token(scopes).await.ok().map(|t| t.as_str().to_owned()) }) + Box::pin(async move { + self.token(scopes) + .await + .map(|t| Some(t.as_str().to_owned())) + .map_err(|e| e.into()) + }) } } } diff --git a/google-apis-common/src/lib.rs b/google-apis-common/src/lib.rs index 758cc0e09c..e66f011465 100644 --- a/google-apis-common/src/lib.rs +++ b/google-apis-common/src/lib.rs @@ -114,14 +114,16 @@ pub trait Delegate: Send { None } - // TODO: Remove oauth2::Error /// Called whenever the Authenticator didn't yield a token. The delegate /// may attempt to provide one, or just take it as a general information about the /// impending failure. /// The given Error provides information about why the token couldn't be acquired in the /// first place - fn token(&mut self) -> Option { - None + fn token( + &mut self, + e: Box, + ) -> std::result::Result, Box> { + Err(e) } /// Called during resumable uploads to provide a URL for the impending upload. @@ -237,7 +239,7 @@ pub enum Error { MissingAPIKey, /// We required a Token, but didn't get one from the Authenticator - MissingToken, + MissingToken(Box), /// The delgate instructed to cancel the operation Cancelled, @@ -258,10 +260,10 @@ pub enum Error { impl Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - Error::Io(ref err) => err.fmt(f), - Error::HttpError(ref err) => err.fmt(f), - Error::UploadSizeLimitExceeded(ref resource_size, ref max_size) => writeln!( + match self { + Error::Io(err) => err.fmt(f), + Error::HttpError(err) => err.fmt(f), + Error::UploadSizeLimitExceeded(resource_size, max_size) => writeln!( f, "The media size {} exceeds the maximum allowed upload size of {}", resource_size, max_size @@ -276,16 +278,16 @@ impl Display for Error { "It is used as there are no Scopes defined for this method." ) } - Error::BadRequest(ref message) => writeln!(f, "Bad Request: {}", message), - Error::MissingToken => writeln!(f, "Token retrieval failed"), + Error::BadRequest(message) => writeln!(f, "Bad Request: {}", message), + Error::MissingToken(e) => writeln!(f, "Token retrieval failed: {}", e), Error::Cancelled => writeln!(f, "Operation cancelled by delegate"), Error::FieldClash(field) => writeln!( f, "The custom parameter '{}' is already provided natively by the CallBuilder.", field ), - Error::JsonDecodeError(ref json_str, ref err) => writeln!(f, "{}: {}", err, json_str), - Error::Failure(ref response) => { + Error::JsonDecodeError(json_str, err) => writeln!(f, "{}: {}", err, json_str), + Error::Failure(response) => { writeln!(f, "Http status indicates failure: {:?}", response) } } @@ -577,14 +579,12 @@ impl RangeResponseHeader { pub struct ResumableUploadHelper<'a, A: 'a, S> where S: tower_service::Service + Clone + Send + Sync + 'static, - S::Response: hyper::client::connect::Connection + AsyncRead + AsyncWrite + Send + Unpin + 'static, + S::Response: + hyper::client::connect::Connection + AsyncRead + AsyncWrite + Send + Unpin + 'static, S::Future: Send + Unpin + 'static, S::Error: Into>, { - pub client: &'a hyper::client::Client< - S, - hyper::body::Body, - >, + pub client: &'a hyper::client::Client, pub delegate: &'a mut dyn Delegate, pub start_at: Option, pub auth: &'a A, @@ -598,7 +598,8 @@ where impl<'a, A, S> ResumableUploadHelper<'a, A, S> where S: tower_service::Service + Clone + Send + Sync + 'static, - S::Response: hyper::client::connect::Connection + AsyncRead + AsyncWrite + Send + Unpin + 'static, + S::Response: + hyper::client::connect::Connection + AsyncRead + AsyncWrite + Send + Unpin + 'static, S::Future: Send + Unpin + 'static, S::Error: Into>, { diff --git a/src/generator/templates/api/lib/mbuild.mako b/src/generator/templates/api/lib/mbuild.mako index 857ea2e03f..d224dc08bc 100644 --- a/src/generator/templates/api/lib/mbuild.mako +++ b/src/generator/templates/api/lib/mbuild.mako @@ -399,7 +399,7 @@ match result { Error::HttpError(_) |Error::Io(_) |Error::MissingAPIKey - |Error::MissingToken + |Error::MissingToken(_) |Error::Cancelled |Error::UploadSizeLimitExceeded(_, _) |Error::Failure(_) @@ -711,13 +711,13 @@ else { loop { % if default_scope: let token = match ${auth_call}.get_token(&self.${api.properties.scopes}.iter().map(String::as_str).collect::>()[..]).await { - Some(token) => token.clone(), - None => { - match dlg.token() { - Some(token) => token, - None => { + Ok(token) => token, + Err(e) => { + match dlg.token(e) { + Ok(token) => token, + Err(e) => { ${delegate_finish}(false); - return Err(client::Error::MissingToken); + return Err(client::Error::MissingToken(e)); } } } @@ -756,11 +756,13 @@ else { let client = &self.hub.client; dlg.pre_request(); let mut req_builder = hyper::Request::builder().method(${method_name_to_variant(m.httpMethod)}).uri(url.clone().into_string()) - .header(USER_AGENT, self.hub._user_agent.clone())\ - % if default_scope: - .header(AUTHORIZATION, format!("Bearer {}", token.as_str()))\ - % endif -; + .header(USER_AGENT, self.hub._user_agent.clone()); + + % if default_scope: + if let Some(token) = token.as_ref() { + req_builder = req_builder.header(AUTHORIZATION, format!("Bearer {}", token)); + } + % endif % if resumable_media_param: upload_url_from_server = true; @@ -854,7 +856,7 @@ else { start_at: if upload_url_from_server { Some(0) } else { None }, auth: &${auth_call}, user_agent: &self.hub._user_agent, - auth_header: format!("Bearer {}", token.as_str()), + auth_header: format!("Bearer {}", token.expect("resumable upload requires token").as_str()), url: url_str, reader: &mut reader, media_type: reader_mime_type.clone(), From 4d30072c9152d105034eb121a89c79410933cc92 Mon Sep 17 00:00:00 2001 From: philippeitis <33013301+philippeitis@users.noreply.github.com> Date: Sun, 16 Oct 2022 21:29:10 -0700 Subject: [PATCH 07/10] docs: min 1 try + retries in example --- google-apis-common/src/auth.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/google-apis-common/src/auth.rs b/google-apis-common/src/auth.rs index 4574a27e4b..017b693e96 100644 --- a/google-apis-common/src/auth.rs +++ b/google-apis-common/src/auth.rs @@ -54,7 +54,7 @@ //! ) -> Pin, Box>> + Send + 'a>> { //! Box::pin(async move { //! let mut auth_token = Ok(None); -//! for _ in 0..self.retries { +//! for _ in 0..=self.retries { //! match self.auth.token(scopes).await { //! Ok(token) => { //! auth_token = Ok(Some(token.as_str().to_owned())); From c6439ee1659de732eaabc568403d39449b1c672d Mon Sep 17 00:00:00 2001 From: philippeitis <33013301+philippeitis@users.noreply.github.com> Date: Sun, 16 Oct 2022 21:38:06 -0700 Subject: [PATCH 08/10] Remove .expect() --- src/generator/templates/api/lib/mbuild.mako | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/generator/templates/api/lib/mbuild.mako b/src/generator/templates/api/lib/mbuild.mako index d224dc08bc..033a033481 100644 --- a/src/generator/templates/api/lib/mbuild.mako +++ b/src/generator/templates/api/lib/mbuild.mako @@ -856,7 +856,8 @@ else { start_at: if upload_url_from_server { Some(0) } else { None }, auth: &${auth_call}, user_agent: &self.hub._user_agent, - auth_header: format!("Bearer {}", token.expect("resumable upload requires token").as_str()), + // TODO: Check this assumption + auth_header: format!("Bearer {}", token.ok_or_else(|| client::Error::MissingToken("resumable upload requires token".into()))?.as_str()), url: url_str, reader: &mut reader, media_type: reader_mime_type.clone(), From 7a114a6d1c8808abff3fc2dc9817d4ca89222450 Mon Sep 17 00:00:00 2001 From: philippeitis <33013301+philippeitis@users.noreply.github.com> Date: Sun, 16 Oct 2022 21:53:58 -0700 Subject: [PATCH 09/10] Update documentation --- google-apis-common/src/auth.rs | 38 ++++++++++++++-------------------- 1 file changed, 15 insertions(+), 23 deletions(-) diff --git a/google-apis-common/src/auth.rs b/google-apis-common/src/auth.rs index 017b693e96..8bbeb33d54 100644 --- a/google-apis-common/src/auth.rs +++ b/google-apis-common/src/auth.rs @@ -74,15 +74,19 @@ use std::future::Future; use std::pin::Pin; -type TokenResult = Result, Box>; +type GetTokenOutput<'a> = Pin< + Box< + dyn Future, Box>> + + Send + + 'a, + >, +>; pub trait GetToken: GetTokenClone + Send + Sync { - /// Called whenever an API call require authentication via an oauth2 token. - /// Returns `None` if a token can not be generated for the provided scopes. - fn get_token<'a>( - &'a self, - _scopes: &'a [&str], - ) -> Pin + Send + 'a>>; + /// Called whenever an API call requires authentication via an oauth2 token. + /// Returns `Ok(None)` if a token is not necessary - otherwise, returns an error + /// indicating the reason why a token could not be produced. + fn get_token<'a>(&'a self, _scopes: &'a [&str]) -> GetTokenOutput<'a>; } pub trait GetTokenClone { @@ -105,10 +109,7 @@ impl Clone for Box { } impl GetToken for String { - fn get_token<'a>( - &'a self, - _scopes: &'a [&str], - ) -> Pin + Send + 'a>> { + fn get_token<'a>(&'a self, _scopes: &'a [&str]) -> GetTokenOutput<'a> { Box::pin(async move { Ok(Some(self.clone())) }) } } @@ -119,20 +120,14 @@ impl GetToken for String { pub struct NoToken; impl GetToken for NoToken { - fn get_token<'a>( - &'a self, - _scopes: &'a [&str], - ) -> Pin + Send + 'a>> { + fn get_token<'a>(&'a self, _scopes: &'a [&str]) -> GetTokenOutput<'a> { Box::pin(async move { Ok(None) }) } } #[cfg(feature = "yup-oauth2")] mod yup_oauth2_impl { - use core::future::Future; - use core::pin::Pin; - - use super::{GetToken, TokenResult}; + use super::{GetToken, GetTokenOutput}; use http::Uri; use hyper::client::connect::Connection; @@ -147,10 +142,7 @@ mod yup_oauth2_impl { S::Future: Send + Unpin + 'static, S::Error: Into>, { - fn get_token<'a>( - &'a self, - scopes: &'a [&str], - ) -> Pin + Send + 'a>> { + fn get_token<'a>(&'a self, scopes: &'a [&str]) -> GetTokenOutput<'a> { Box::pin(async move { self.token(scopes) .await From 0855fc3a283dc263904c36d19bf4509f42ad97cd Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Tue, 18 Oct 2022 15:45:32 +0800 Subject: [PATCH 10/10] Also validate crates build without default features Note that tests can't work without default features as they assume them to exist, and we have to chose one, going for the one most users will see. --- .github/workflows/rust.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index edd4805f46..16f6f28d45 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -19,6 +19,7 @@ jobs: source ~/.profile make test-gen make gen-all-cli cargo-api ARGS=test + make cargo-api ARGS='check --no-default-features' make cargo-api ARGS=doc make docs-all cargo test