gsuite_api/
lib.rs

1//! A fully generated, opinionated API client library for Google Admin.
2//!
3//!
4//! [![docs.rs](https://docs.rs/gsuite-api/badge.svg)](https://docs.rs/gsuite-api)
5//!
6//! ## API Details
7//!
8//! Admin SDK lets administrators of enterprise domains to view and manage resources like user, groups etc. It also provides audit and usage reports of domain.
9//!
10//! [API Terms of Service](https://developers.google.com/terms/)
11//!
12//! ### Contact
13//!
14//!
15//! | name | url |
16//! |----|----|
17//! | Google | <https://google.com> |
18//!
19//! ### License
20//!
21//!
22//! | name | url |
23//! |----|----|
24//! | Creative Commons Attribution 3.0 | <http://creativecommons.org/licenses/by/3.0/> |
25//!
26//!
27//! ## Client Details
28//!
29//! This client is generated from the [Google Admin OpenAPI
30//! specs](https://admin.googleapis.com/iscovery/rest?version=directory_v1) based on API spec version `directory_v1`. This way it will remain
31//! up to date as features are added. The documentation for the crate is generated
32//! along with the code to make this library easy to use.
33//!
34//!
35//! To install the library, add the following to your `Cargo.toml` file.
36//!
37//! ```toml
38//! [dependencies]
39//! gsuite-api = "0.10.0"
40//! ```
41//!
42//! ## Basic example
43//!
44//! Typical use will require intializing a `Client`. This requires
45//! a user agent string and set of credentials.
46//!
47//! ```rust
48//! use gsuite_api::Client;
49//!
50//! let google admin = Client::new(
51//!     String::from("client-id"),
52//!     String::from("client-secret"),
53//!     String::from("redirect-uri"),
54//!     String::from("token"),
55//!     String::from("refresh-token")
56//! );
57//! ```
58//!
59//! Alternatively, the library can search for most of the variables required for
60//! the client in the environment:
61//!
62//! - `GOOGLE ADMIN_CLIENT_ID`
63//! - `GOOGLE ADMIN_CLIENT_SECRET`
64//! - `GOOGLE ADMIN_REDIRECT_URI`
65//!
66//! And then you can create a client from the environment.
67//!
68//! ```rust
69//! use gsuite_api::Client;
70//!
71//! let google admin = Client::new_from_env(
72//!     String::from("token"),
73//!     String::from("refresh-token")
74//! );
75//! ```
76//!
77//! It is okay to pass empty values for `token` and `refresh_token`. In
78//! the initial state of the client, you will not know these values.
79//!
80//! To start off a fresh client and get a `token` and `refresh_token`, use the following.
81//!
82//! ```rust
83//! use gsuite_api::Client;
84//!
85//! async fn do_call() {
86//!     let mut google admin = Client::new_from_env("", "");
87//!
88//!     // Get the URL to request consent from the user.
89//!     // You can optionally pass in scopes. If none are provided, then the
90//!     // resulting URL will not have any scopes.
91//!     let user_consent_url = google admin.user_consent_url(&["some-scope".to_string()]);
92//!
93//!     // In your redirect URL capture the code sent and our state.
94//!     // Send it along to the request for the token.
95//!     let code = "thing-from-redirect-url";
96//!     let state = "state-from-redirect-url";
97//!     let mut access_token = google admin.get_access_token(code, state).await.unwrap();
98//!
99//!     // You can additionally refresh the access token with the following.
100//!     // You must have a refresh token to be able to call this function.
101//!     access_token = google admin.refresh_access_token().await.unwrap();
102//! }
103//! ```
104//!
105#![allow(clippy::derive_partial_eq_without_eq)]
106#![allow(clippy::too_many_arguments)]
107#![allow(clippy::nonstandard_macro_braces)]
108#![allow(clippy::large_enum_variant)]
109#![allow(clippy::tabs_in_doc_comments)]
110#![allow(missing_docs)]
111#![cfg_attr(docsrs, feature(doc_cfg))]
112
113pub mod asps;
114pub mod channels;
115pub mod chromeosdevices;
116pub mod customer;
117pub mod customers;
118pub mod domain_aliases;
119pub mod domains;
120pub mod groups;
121pub mod members;
122pub mod mobiledevices;
123pub mod orgunits;
124pub mod privileges;
125pub mod resources;
126pub mod role_assignments;
127pub mod roles;
128pub mod schemas;
129pub mod tokens;
130pub mod two_step_verification;
131pub mod types;
132pub mod users;
133#[doc(hidden)]
134pub mod utils;
135pub mod verification_codes;
136
137pub use reqwest::{header::HeaderMap, StatusCode};
138
139#[derive(Debug)]
140pub struct Response<T> {
141    pub status: reqwest::StatusCode,
142    pub headers: reqwest::header::HeaderMap,
143    pub body: T,
144}
145
146impl<T> Response<T> {
147    pub fn new(status: reqwest::StatusCode, headers: reqwest::header::HeaderMap, body: T) -> Self {
148        Self {
149            status,
150            headers,
151            body,
152        }
153    }
154}
155
156type ClientResult<T> = Result<T, ClientError>;
157
158use thiserror::Error;
159
160/// Errors returned by the client
161#[derive(Debug, Error)]
162pub enum ClientError {
163    // Generic Token Client
164    /// Empty refresh auth token
165    #[error("Refresh AuthToken is empty")]
166    EmptyRefreshToken,
167    /// utf8 convertion error
168    #[error(transparent)]
169    FromUtf8Error(#[from] std::string::FromUtf8Error),
170    /// URL Parsing Error
171    #[error(transparent)]
172    UrlParserError(#[from] url::ParseError),
173    /// Serde JSON parsing error
174    #[error(transparent)]
175    SerdeJsonError(#[from] serde_json::Error),
176    /// Errors returned by reqwest
177    #[error(transparent)]
178    ReqwestError(#[from] reqwest::Error),
179    /// Errors returned by reqwest::header
180    #[error(transparent)]
181    InvalidHeaderValue(#[from] reqwest::header::InvalidHeaderValue),
182    #[cfg(feature = "middleware")]
183    /// Errors returned by reqwest middleware
184    #[error(transparent)]
185    ReqwestMiddleWareError(#[from] reqwest_middleware::Error),
186    /// Generic HTTP Error
187    #[error("HTTP Error. Code: {status}, message: {error}")]
188    HttpError {
189        status: http::StatusCode,
190        headers: reqwest::header::HeaderMap,
191        error: String,
192    },
193}
194
195pub const FALLBACK_HOST: &str = "https://www.googleapis.com";
196
197mod progenitor_support {
198    use percent_encoding::{utf8_percent_encode, AsciiSet, CONTROLS};
199
200    const PATH_SET: &AsciiSet = &CONTROLS
201        .add(b' ')
202        .add(b'"')
203        .add(b'#')
204        .add(b'<')
205        .add(b'>')
206        .add(b'?')
207        .add(b'`')
208        .add(b'{')
209        .add(b'}');
210
211    #[allow(dead_code)]
212    pub(crate) fn encode_path(pc: &str) -> String {
213        utf8_percent_encode(pc, PATH_SET).to_string()
214    }
215}
216
217#[derive(Debug, Default)]
218pub(crate) struct Message {
219    pub body: Option<reqwest::Body>,
220    pub content_type: Option<String>,
221}
222
223use std::convert::TryInto;
224use std::env;
225use std::ops::Add;
226use std::sync::Arc;
227use std::time::{Duration, Instant};
228use tokio::sync::RwLock;
229
230const TOKEN_ENDPOINT: &str = "https://oauth2.googleapis.com/token";
231const USER_CONSENT_ENDPOINT: &str = "https://accounts.google.com/o/oauth2/v2/auth";
232
233#[derive(Debug, Default, Clone)]
234pub struct RootDefaultServer {}
235
236impl RootDefaultServer {
237    pub fn default_url(&self) -> &str {
238        "https://www.googleapis.com"
239    }
240}
241
242/// Entrypoint for interacting with the API client.
243#[derive(Clone)]
244pub struct Client {
245    host: String,
246    host_override: Option<String>,
247    token: Arc<RwLock<InnerToken>>,
248    client_id: String,
249    client_secret: String,
250    redirect_uri: String,
251
252    auto_refresh: bool,
253    #[cfg(feature = "middleware")]
254    client: reqwest_middleware::ClientWithMiddleware,
255    #[cfg(not(feature = "middleware"))]
256    client: reqwest::Client,
257}
258
259use schemars::JsonSchema;
260use serde::{Deserialize, Serialize};
261
262#[derive(Debug, JsonSchema, Clone, Default, Serialize, Deserialize)]
263pub struct AccessToken {
264    #[serde(
265        default,
266        skip_serializing_if = "String::is_empty",
267        deserialize_with = "crate::utils::deserialize_null_string::deserialize"
268    )]
269    pub token_type: String,
270
271    #[serde(
272        default,
273        skip_serializing_if = "String::is_empty",
274        deserialize_with = "crate::utils::deserialize_null_string::deserialize"
275    )]
276    pub access_token: String,
277    #[serde(default)]
278    pub expires_in: i64,
279
280    #[serde(
281        default,
282        skip_serializing_if = "String::is_empty",
283        deserialize_with = "crate::utils::deserialize_null_string::deserialize"
284    )]
285    pub refresh_token: String,
286    #[serde(default, alias = "x_refresh_token_expires_in")]
287    pub refresh_token_expires_in: i64,
288
289    #[serde(
290        default,
291        skip_serializing_if = "String::is_empty",
292        deserialize_with = "crate::utils::deserialize_null_string::deserialize"
293    )]
294    pub scope: String,
295}
296
297/// Time in seconds before the access token expiration point that a refresh should
298/// be performed. This value is subtracted from the `expires_in` value returned by
299/// the provider prior to storing
300const REFRESH_THRESHOLD: Duration = Duration::from_secs(60);
301
302#[derive(Debug, Clone)]
303struct InnerToken {
304    access_token: String,
305    refresh_token: String,
306    expires_at: Option<Instant>,
307}
308
309impl Client {
310    /// Create a new Client struct. Requires OAuth2 configuration values as well as an access and refresh token.
311    ///
312    /// # Panics
313    ///
314    /// This function will panic if the internal http client fails to create
315    pub fn new<I, K, R, T, Q>(
316        client_id: I,
317        client_secret: K,
318        redirect_uri: R,
319        token: T,
320        refresh_token: Q,
321    ) -> Self
322    where
323        I: ToString,
324        K: ToString,
325        R: ToString,
326        T: ToString,
327        Q: ToString,
328    {
329        // Retry up to 3 times with increasing intervals between attempts.
330        let retry_policy =
331            reqwest_retry::policies::ExponentialBackoff::builder().build_with_max_retries(3);
332        let client = reqwest::Client::builder()
333            .redirect(reqwest::redirect::Policy::none())
334            .build();
335        match client {
336            Ok(c) => {
337                #[cfg(feature = "middleware")]
338                let client = {
339                    reqwest_middleware::ClientBuilder::new(c)
340                        // Trace HTTP requests. See the tracing crate to make use of these traces.
341                        .with(reqwest_tracing::TracingMiddleware::default())
342                        // Retry failed requests.
343                        .with(reqwest_conditional_middleware::ConditionalMiddleware::new(
344                            reqwest_retry::RetryTransientMiddleware::new_with_policy(retry_policy),
345                            |req: &reqwest::Request| req.try_clone().is_some(),
346                        ))
347                        .build()
348                };
349                #[cfg(not(feature = "middleware"))]
350                let client = c;
351
352                let host = RootDefaultServer::default().default_url().to_string();
353
354                Client {
355                    host,
356                    host_override: None,
357                    client_id: client_id.to_string(),
358                    client_secret: client_secret.to_string(),
359                    redirect_uri: redirect_uri.to_string(),
360                    token: Arc::new(RwLock::new(InnerToken {
361                        access_token: token.to_string(),
362                        refresh_token: refresh_token.to_string(),
363                        expires_at: None,
364                    })),
365
366                    auto_refresh: false,
367                    client,
368                }
369            }
370            Err(e) => panic!("creating reqwest client failed: {:?}", e),
371        }
372    }
373
374    /// Enables or disables the automatic refreshing of access tokens upon expiration
375    pub fn set_auto_access_token_refresh(&mut self, enabled: bool) -> &mut Self {
376        self.auto_refresh = enabled;
377        self
378    }
379
380    /// Sets a specific `Instant` at which the access token should be considered expired.
381    /// The expiration value will only be used when automatic access token refreshing is
382    /// also enabled. `None` may be passed in if the expiration is unknown. In this case
383    /// automatic refreshes will be attempted when encountering an UNAUTHENTICATED status
384    /// code on a response.
385    pub async fn set_expires_at(&self, expires_at: Option<Instant>) -> &Self {
386        self.token.write().await.expires_at = expires_at;
387        self
388    }
389
390    /// Gets the `Instant` at which the access token used by this client is set to expire
391    /// if one is known
392    pub async fn expires_at(&self) -> Option<Instant> {
393        self.token.read().await.expires_at
394    }
395
396    /// Sets the number of seconds in which the current access token should be considered
397    /// expired
398    pub async fn set_expires_in(&self, expires_in: i64) -> &Self {
399        self.token.write().await.expires_at = Self::compute_expires_at(expires_in);
400        self
401    }
402
403    /// Gets the number of seconds from now in which the current access token will be
404    /// considered expired if one is known
405    pub async fn expires_in(&self) -> Option<Duration> {
406        self.token
407            .read()
408            .await
409            .expires_at
410            .map(|i| i.duration_since(Instant::now()))
411    }
412
413    /// Determines if the access token currently stored in the client is expired. If the
414    /// expiration can not be determined, None is returned
415    pub async fn is_expired(&self) -> Option<bool> {
416        self.token
417            .read()
418            .await
419            .expires_at
420            .map(|expiration| expiration <= Instant::now())
421    }
422
423    fn compute_expires_at(expires_in: i64) -> Option<Instant> {
424        let seconds_valid = expires_in
425            .try_into()
426            .ok()
427            .map(Duration::from_secs)
428            .and_then(|dur| dur.checked_sub(REFRESH_THRESHOLD))
429            .or_else(|| Some(Duration::from_secs(0)));
430
431        seconds_valid.map(|seconds_valid| Instant::now().add(seconds_valid))
432    }
433
434    /// Override the host for all endpoins in the client.
435    pub fn with_host_override<H>(&mut self, host: H) -> &mut Self
436    where
437        H: ToString,
438    {
439        self.host_override = Some(host.to_string());
440        self
441    }
442
443    /// Disables the global host override for the client.
444    pub fn remove_host_override(&mut self) -> &mut Self {
445        self.host_override = None;
446        self
447    }
448
449    pub fn get_host_override(&self) -> Option<&str> {
450        self.host_override.as_deref()
451    }
452
453    pub(crate) fn url(&self, path: &str, host: Option<&str>) -> String {
454        format!(
455            "{}{}",
456            self.get_host_override()
457                .or(host)
458                .unwrap_or(self.host.as_str()),
459            path
460        )
461    }
462
463    /// Create a new Client struct from environment variables. Requires an existing access and refresh token
464    ///
465    /// The following environment variables are expected to be set:
466    ///   * `GOOGLE_KEY_ENCODED` - A base64 encoded version of JSON formatted application secret
467    ///
468    /// # Panics
469    ///
470    /// This function will panic if an application secret can not be parsed from the encoded key
471    ///
472    /// This function will panic if the internal http client fails to create
473    pub async fn new_from_env<T, R>(token: T, refresh_token: R) -> Self
474    where
475        T: ToString,
476        R: ToString,
477    {
478        use base64::{engine::general_purpose::STANDARD, Engine};
479
480        let google_key = env::var("GOOGLE_KEY_ENCODED").unwrap_or_default();
481        let decoded_google_key = STANDARD.decode(google_key).unwrap();
482        let secret = yup_oauth2::parse_application_secret(decoded_google_key)
483            .expect("failed to read from google credential env var");
484
485        let client = reqwest::Client::builder()
486            .redirect(reqwest::redirect::Policy::none())
487            .build();
488        let retry_policy =
489            reqwest_retry::policies::ExponentialBackoff::builder().build_with_max_retries(3);
490
491        match client {
492            Ok(c) => {
493                #[cfg(feature = "middleware")]
494                let client = {
495                    reqwest_middleware::ClientBuilder::new(c)
496                        // Trace HTTP requests. See the tracing crate to make use of these traces.
497                        .with(reqwest_tracing::TracingMiddleware::default())
498                        // Retry failed requests.
499                        .with(reqwest_conditional_middleware::ConditionalMiddleware::new(
500                            reqwest_retry::RetryTransientMiddleware::new_with_policy(retry_policy),
501                            |req: &reqwest::Request| req.try_clone().is_some(),
502                        ))
503                        .build()
504                };
505                #[cfg(not(feature = "middleware"))]
506                let client = c;
507                let host = RootDefaultServer::default().default_url().to_string();
508
509                Client {
510                    host,
511                    host_override: None,
512                    client_id: secret.client_id.to_string(),
513                    client_secret: secret.client_secret.to_string(),
514                    redirect_uri: secret.redirect_uris[0].to_string(),
515                    token: Arc::new(RwLock::new(InnerToken {
516                        access_token: token.to_string(),
517                        refresh_token: refresh_token.to_string(),
518                        expires_at: None,
519                    })),
520                    auto_refresh: false,
521                    client,
522                }
523            }
524            Err(e) => panic!("creating reqwest client failed: {:?}", e),
525        }
526    }
527
528    /// Return a user consent url with an optional set of scopes.
529    /// If no scopes are provided, they will not be passed in the url.
530    pub fn user_consent_url(&self, scopes: &[String]) -> String {
531        let state = uuid::Uuid::new_v4();
532
533        let url = format!(
534            "{}?client_id={}&access_type=offline&response_type=code&redirect_uri={}&state={}",
535            USER_CONSENT_ENDPOINT, self.client_id, self.redirect_uri, state
536        );
537
538        if scopes.is_empty() {
539            return url;
540        }
541
542        // Add the scopes.
543        format!("{}&scope={}", url, scopes.join(" "))
544    }
545
546    /// Refresh an access token from a refresh token. Client must have a refresh token
547    /// for this to work.
548    pub async fn refresh_access_token(&self) -> ClientResult<AccessToken> {
549        let response = {
550            let refresh_token = &self.token.read().await.refresh_token;
551
552            if refresh_token.is_empty() {
553                return Err(ClientError::EmptyRefreshToken);
554            }
555
556            let mut headers = reqwest::header::HeaderMap::new();
557            headers.append(
558                reqwest::header::ACCEPT,
559                reqwest::header::HeaderValue::from_static("application/json"),
560            );
561
562            let params = [
563                ("grant_type", "refresh_token"),
564                ("refresh_token", refresh_token),
565                ("client_id", &self.client_id),
566                ("client_secret", &self.client_secret),
567                ("redirect_uri", &self.redirect_uri),
568            ];
569            let client = reqwest::Client::new();
570            client
571                .post(TOKEN_ENDPOINT)
572                .headers(headers)
573                .form(&params)
574                .basic_auth(&self.client_id, Some(&self.client_secret))
575                .send()
576                .await?
577        };
578
579        // Unwrap the response.
580        let t: AccessToken = response.json().await?;
581
582        let refresh_token = self.token.read().await.refresh_token.clone();
583
584        *self.token.write().await = InnerToken {
585            access_token: t.access_token.clone(),
586            refresh_token,
587            expires_at: Self::compute_expires_at(t.expires_in),
588        };
589
590        Ok(t)
591    }
592
593    /// Get an access token from the code returned by the URL paramter sent to the
594    /// redirect URL.
595    pub async fn get_access_token(&mut self, code: &str, state: &str) -> ClientResult<AccessToken> {
596        let mut headers = reqwest::header::HeaderMap::new();
597        headers.append(
598            reqwest::header::ACCEPT,
599            reqwest::header::HeaderValue::from_static("application/json"),
600        );
601
602        let params = [
603            ("grant_type", "authorization_code"),
604            ("code", code),
605            ("client_id", &self.client_id),
606            ("client_secret", &self.client_secret),
607            ("redirect_uri", &self.redirect_uri),
608            ("state", state),
609        ];
610        let client = reqwest::Client::new();
611        let resp = client
612            .post(TOKEN_ENDPOINT)
613            .headers(headers)
614            .form(&params)
615            .basic_auth(&self.client_id, Some(&self.client_secret))
616            .send()
617            .await?;
618
619        // Unwrap the response.
620        let t: AccessToken = resp.json().await?;
621
622        *self.token.write().await = InnerToken {
623            access_token: t.access_token.clone(),
624            refresh_token: t.refresh_token.clone(),
625            expires_at: Self::compute_expires_at(t.expires_in),
626        };
627
628        Ok(t)
629    }
630
631    async fn url_and_auth(&self, uri: &str) -> ClientResult<(reqwest::Url, Option<String>)> {
632        let parsed_url = uri.parse::<reqwest::Url>()?;
633
634        let auth = format!("Bearer {}", self.token.read().await.access_token);
635        Ok((parsed_url, Some(auth)))
636    }
637
638    async fn make_request(
639        &self,
640        method: &reqwest::Method,
641        uri: &str,
642        message: Message,
643    ) -> ClientResult<reqwest::Request> {
644        let (url, auth) = self.url_and_auth(uri).await?;
645
646        let instance = <&Client>::clone(&self);
647
648        let mut req = instance.client.request(method.clone(), url);
649
650        // Set the default headers.
651        req = req.header(
652            reqwest::header::ACCEPT,
653            reqwest::header::HeaderValue::from_static("application/json"),
654        );
655
656        if let Some(content_type) = &message.content_type {
657            req = req.header(
658                reqwest::header::CONTENT_TYPE,
659                reqwest::header::HeaderValue::from_str(content_type).unwrap(),
660            );
661        } else {
662            req = req.header(
663                reqwest::header::CONTENT_TYPE,
664                reqwest::header::HeaderValue::from_static("application/json"),
665            );
666        }
667
668        if let Some(auth_str) = auth {
669            req = req.header(http::header::AUTHORIZATION, &*auth_str);
670        }
671
672        if let Some(body) = message.body {
673            req = req.body(body);
674        }
675
676        Ok(req.build()?)
677    }
678
679    async fn request_raw(
680        &self,
681        method: reqwest::Method,
682        uri: &str,
683        message: Message,
684    ) -> ClientResult<reqwest::Response> {
685        if self.auto_refresh {
686            let expired = self.is_expired().await;
687
688            match expired {
689                // We have a known expired token, we know we need to perform a refresh prior to
690                // attempting to make a request
691                Some(true) => {
692                    self.refresh_access_token().await?;
693                }
694
695                // We have a (theoretically) known good token available. We make an optimistic
696                // attempting at the request. If the token is no longer good, then something other
697                // than the expiration is triggering the failure. We defer handling of these errors
698                // to the caller
699                Some(false) => (),
700
701                // We do not know what state we are in. We could have a valid or expired token.
702                // Generally this means we are in one of two cases:
703                //   1. We have not yet performed a token refresh, nor has the user provided
704                //      expiration data, and therefore do not know the expiration of the user
705                //      provided token
706                //   2. The provider is returning unusable expiration times, at which point we
707                //      choose to ignore them
708                None => (),
709            }
710        }
711
712        let req = self.make_request(&method, uri, message).await?;
713        let resp = self.client.execute(req).await?;
714
715        Ok(resp)
716    }
717
718    async fn request<Out>(
719        &self,
720        method: reqwest::Method,
721        uri: &str,
722        message: Message,
723    ) -> ClientResult<crate::Response<Out>>
724    where
725        Out: serde::de::DeserializeOwned + 'static + Send,
726    {
727        let response = self.request_raw(method, uri, message).await?;
728
729        let status = response.status();
730        let headers = response.headers().clone();
731
732        let response_body = response.bytes().await?;
733
734        if status.is_success() {
735            log::debug!("Received successful response. Read payload.");
736            let parsed_response = if status == http::StatusCode::NO_CONTENT
737                || std::any::TypeId::of::<Out>() == std::any::TypeId::of::<()>()
738            {
739                serde_json::from_str("null")?
740            } else {
741                serde_json::from_slice::<Out>(&response_body)?
742            };
743            Ok(crate::Response::new(status, headers, parsed_response))
744        } else {
745            let error = if response_body.is_empty() {
746                ClientError::HttpError {
747                    status,
748                    headers,
749                    error: "empty response".into(),
750                }
751            } else {
752                ClientError::HttpError {
753                    status,
754                    headers,
755                    error: String::from_utf8_lossy(&response_body).into(),
756                }
757            };
758
759            Err(error)
760        }
761    }
762
763    async fn request_with_links<Out>(
764        &self,
765        method: http::Method,
766        uri: &str,
767        message: Message,
768    ) -> ClientResult<(Option<crate::utils::NextLink>, crate::Response<Out>)>
769    where
770        Out: serde::de::DeserializeOwned + 'static + Send,
771    {
772        let response = self.request_raw(method, uri, message).await?;
773
774        let status = response.status();
775        let headers = response.headers().clone();
776        let link = response
777            .headers()
778            .get(http::header::LINK)
779            .and_then(|l| l.to_str().ok())
780            .and_then(|l| parse_link_header::parse(l).ok())
781            .as_ref()
782            .and_then(crate::utils::next_link);
783
784        let response_body = response.bytes().await?;
785
786        if status.is_success() {
787            log::debug!("Received successful response. Read payload.");
788
789            let parsed_response = if status == http::StatusCode::NO_CONTENT
790                || std::any::TypeId::of::<Out>() == std::any::TypeId::of::<()>()
791            {
792                serde_json::from_str("null")?
793            } else {
794                serde_json::from_slice::<Out>(&response_body)?
795            };
796            Ok((link, crate::Response::new(status, headers, parsed_response)))
797        } else {
798            let error = if response_body.is_empty() {
799                ClientError::HttpError {
800                    status,
801                    headers,
802                    error: "empty response".into(),
803                }
804            } else {
805                ClientError::HttpError {
806                    status,
807                    headers,
808                    error: String::from_utf8_lossy(&response_body).into(),
809                }
810            };
811            Err(error)
812        }
813    }
814
815    /* TODO: make this more DRY */
816    #[allow(dead_code)]
817    async fn post_form<Out>(
818        &self,
819        uri: &str,
820        form: reqwest::multipart::Form,
821    ) -> ClientResult<crate::Response<Out>>
822    where
823        Out: serde::de::DeserializeOwned + 'static + Send,
824    {
825        let (url, auth) = self.url_and_auth(uri).await?;
826
827        let instance = <&Client>::clone(&self);
828
829        let mut req = instance.client.request(http::Method::POST, url);
830
831        // Set the default headers.
832        req = req.header(
833            reqwest::header::ACCEPT,
834            reqwest::header::HeaderValue::from_static("application/json"),
835        );
836
837        if let Some(auth_str) = auth {
838            req = req.header(http::header::AUTHORIZATION, &*auth_str);
839        }
840
841        req = req.multipart(form);
842
843        let response = req.send().await?;
844
845        let status = response.status();
846        let headers = response.headers().clone();
847
848        let response_body = response.bytes().await?;
849
850        if status.is_success() {
851            log::debug!("Received successful response. Read payload.");
852            let parsed_response = if status == http::StatusCode::NO_CONTENT
853                || std::any::TypeId::of::<Out>() == std::any::TypeId::of::<()>()
854            {
855                serde_json::from_str("null")?
856            } else if std::any::TypeId::of::<Out>() == std::any::TypeId::of::<String>() {
857                // Parse the output as a string.
858                let s = String::from_utf8(response_body.to_vec())?;
859                serde_json::from_value(serde_json::json!(&s))?
860            } else {
861                serde_json::from_slice::<Out>(&response_body)?
862            };
863            Ok(crate::Response::new(status, headers, parsed_response))
864        } else {
865            let error = if response_body.is_empty() {
866                ClientError::HttpError {
867                    status,
868                    headers,
869                    error: "empty response".into(),
870                }
871            } else {
872                ClientError::HttpError {
873                    status,
874                    headers,
875                    error: String::from_utf8_lossy(&response_body).into(),
876                }
877            };
878
879            Err(error)
880        }
881    }
882
883    /* TODO: make this more DRY */
884    #[allow(dead_code)]
885    async fn request_with_accept_mime<Out>(
886        &self,
887        method: reqwest::Method,
888        uri: &str,
889        accept_mime_type: &str,
890    ) -> ClientResult<crate::Response<Out>>
891    where
892        Out: serde::de::DeserializeOwned + 'static + Send,
893    {
894        let (url, auth) = self.url_and_auth(uri).await?;
895
896        let instance = <&Client>::clone(&self);
897
898        let mut req = instance.client.request(method, url);
899
900        // Set the default headers.
901        req = req.header(
902            reqwest::header::ACCEPT,
903            reqwest::header::HeaderValue::from_str(accept_mime_type)?,
904        );
905
906        if let Some(auth_str) = auth {
907            req = req.header(http::header::AUTHORIZATION, &*auth_str);
908        }
909
910        let response = req.send().await?;
911
912        let status = response.status();
913        let headers = response.headers().clone();
914
915        let response_body = response.bytes().await?;
916
917        if status.is_success() {
918            log::debug!("Received successful response. Read payload.");
919            let parsed_response = if status == http::StatusCode::NO_CONTENT
920                || std::any::TypeId::of::<Out>() == std::any::TypeId::of::<()>()
921            {
922                serde_json::from_str("null")?
923            } else if std::any::TypeId::of::<Out>() == std::any::TypeId::of::<String>() {
924                // Parse the output as a string.
925                let s = String::from_utf8(response_body.to_vec())?;
926                serde_json::from_value(serde_json::json!(&s))?
927            } else {
928                serde_json::from_slice::<Out>(&response_body)?
929            };
930            Ok(crate::Response::new(status, headers, parsed_response))
931        } else {
932            let error = if response_body.is_empty() {
933                ClientError::HttpError {
934                    status,
935                    headers,
936                    error: "empty response".into(),
937                }
938            } else {
939                ClientError::HttpError {
940                    status,
941                    headers,
942                    error: String::from_utf8_lossy(&response_body).into(),
943                }
944            };
945
946            Err(error)
947        }
948    }
949
950    /* TODO: make this more DRY */
951    #[allow(dead_code)]
952    async fn request_with_mime<Out>(
953        &self,
954        method: reqwest::Method,
955        uri: &str,
956        content: &[u8],
957        mime_type: &str,
958    ) -> ClientResult<crate::Response<Out>>
959    where
960        Out: serde::de::DeserializeOwned + 'static + Send,
961    {
962        let (url, auth) = self.url_and_auth(uri).await?;
963
964        let instance = <&Client>::clone(&self);
965
966        let mut req = instance.client.request(method, url);
967
968        // Set the default headers.
969        req = req.header(
970            reqwest::header::ACCEPT,
971            reqwest::header::HeaderValue::from_static("application/json"),
972        );
973        req = req.header(
974            reqwest::header::CONTENT_TYPE,
975            reqwest::header::HeaderValue::from_bytes(mime_type.as_bytes()).unwrap(),
976        );
977        // We are likely uploading a file so add the right headers.
978        req = req.header(
979            reqwest::header::HeaderName::from_static("x-upload-content-type"),
980            reqwest::header::HeaderValue::from_static("application/octet-stream"),
981        );
982        req = req.header(
983            reqwest::header::HeaderName::from_static("x-upload-content-length"),
984            reqwest::header::HeaderValue::from_bytes(format!("{}", content.len()).as_bytes())
985                .unwrap(),
986        );
987
988        if let Some(auth_str) = auth {
989            req = req.header(http::header::AUTHORIZATION, &*auth_str);
990        }
991
992        if content.len() > 1 {
993            let b = bytes::Bytes::copy_from_slice(content);
994            // We are uploading a file so add that as the body.
995            req = req.body(b);
996        }
997
998        let response = req.send().await?;
999
1000        let status = response.status();
1001        let headers = response.headers().clone();
1002
1003        let response_body = response.bytes().await?;
1004
1005        if status.is_success() {
1006            log::debug!("Received successful response. Read payload.");
1007            let parsed_response = if status == http::StatusCode::NO_CONTENT
1008                || std::any::TypeId::of::<Out>() == std::any::TypeId::of::<()>()
1009            {
1010                serde_json::from_str("null")?
1011            } else {
1012                serde_json::from_slice::<Out>(&response_body)?
1013            };
1014            Ok(crate::Response::new(status, headers, parsed_response))
1015        } else {
1016            let error = if response_body.is_empty() {
1017                ClientError::HttpError {
1018                    status,
1019                    headers,
1020                    error: "empty response".into(),
1021                }
1022            } else {
1023                ClientError::HttpError {
1024                    status,
1025                    headers,
1026                    error: String::from_utf8_lossy(&response_body).into(),
1027                }
1028            };
1029
1030            Err(error)
1031        }
1032    }
1033
1034    async fn request_entity<D>(
1035        &self,
1036        method: http::Method,
1037        uri: &str,
1038        message: Message,
1039    ) -> ClientResult<crate::Response<D>>
1040    where
1041        D: serde::de::DeserializeOwned + 'static + Send,
1042    {
1043        let r = self.request(method, uri, message).await?;
1044        Ok(r)
1045    }
1046
1047    #[allow(dead_code)]
1048    async fn get<D>(&self, uri: &str, message: Message) -> ClientResult<crate::Response<D>>
1049    where
1050        D: serde::de::DeserializeOwned + 'static + Send,
1051    {
1052        self.request_entity(http::Method::GET, uri, message).await
1053    }
1054
1055    #[allow(dead_code)]
1056    async fn get_all_pages<D>(&self, uri: &str, _message: Message) -> ClientResult<Response<Vec<D>>>
1057    where
1058        D: serde::de::DeserializeOwned + 'static + Send,
1059    {
1060        // TODO: implement this.
1061        self.unfold(uri).await
1062    }
1063
1064    /// "unfold" paginated results of a vector of items
1065    #[allow(dead_code)]
1066    async fn unfold<D>(&self, uri: &str) -> ClientResult<crate::Response<Vec<D>>>
1067    where
1068        D: serde::de::DeserializeOwned + 'static + Send,
1069    {
1070        let mut global_items = Vec::new();
1071        let (new_link, mut response) = self.get_pages(uri).await?;
1072        let mut link = new_link;
1073        while !response.body.is_empty() {
1074            global_items.append(&mut response.body);
1075            // We need to get the next link.
1076            if let Some(url) = &link {
1077                let url = reqwest::Url::parse(&url.0)?;
1078                let (new_link, new_response) = self.get_pages_url(&url).await?;
1079                link = new_link;
1080                response = new_response;
1081            }
1082        }
1083
1084        Ok(Response::new(
1085            response.status,
1086            response.headers,
1087            global_items,
1088        ))
1089    }
1090
1091    #[allow(dead_code)]
1092    async fn get_pages<D>(
1093        &self,
1094        uri: &str,
1095    ) -> ClientResult<(Option<crate::utils::NextLink>, crate::Response<Vec<D>>)>
1096    where
1097        D: serde::de::DeserializeOwned + 'static + Send,
1098    {
1099        self.request_with_links(http::Method::GET, uri, Message::default())
1100            .await
1101    }
1102
1103    #[allow(dead_code)]
1104    async fn get_pages_url<D>(
1105        &self,
1106        url: &reqwest::Url,
1107    ) -> ClientResult<(Option<crate::utils::NextLink>, crate::Response<Vec<D>>)>
1108    where
1109        D: serde::de::DeserializeOwned + 'static + Send,
1110    {
1111        self.request_with_links(http::Method::GET, url.as_str(), Message::default())
1112            .await
1113    }
1114
1115    #[allow(dead_code)]
1116    async fn post<D>(&self, uri: &str, message: Message) -> ClientResult<crate::Response<D>>
1117    where
1118        D: serde::de::DeserializeOwned + 'static + Send,
1119    {
1120        self.request_entity(http::Method::POST, uri, message).await
1121    }
1122
1123    #[allow(dead_code)]
1124    async fn patch<D>(&self, uri: &str, message: Message) -> ClientResult<crate::Response<D>>
1125    where
1126        D: serde::de::DeserializeOwned + 'static + Send,
1127    {
1128        self.request_entity(http::Method::PATCH, uri, message).await
1129    }
1130
1131    #[allow(dead_code)]
1132    async fn put<D>(&self, uri: &str, message: Message) -> ClientResult<crate::Response<D>>
1133    where
1134        D: serde::de::DeserializeOwned + 'static + Send,
1135    {
1136        self.request_entity(http::Method::PUT, uri, message).await
1137    }
1138
1139    #[allow(dead_code)]
1140    async fn delete<D>(&self, uri: &str, message: Message) -> ClientResult<crate::Response<D>>
1141    where
1142        D: serde::de::DeserializeOwned + 'static + Send,
1143    {
1144        self.request_entity(http::Method::DELETE, uri, message)
1145            .await
1146    }
1147
1148    /// Return a reference to an interface that provides access to asps operations.
1149    pub fn asps(&self) -> asps::Asps {
1150        asps::Asps::new(self.clone())
1151    }
1152
1153    /// Return a reference to an interface that provides access to channels operations.
1154    pub fn channels(&self) -> channels::Channels {
1155        channels::Channels::new(self.clone())
1156    }
1157
1158    /// Return a reference to an interface that provides access to chromeosdevices operations.
1159    pub fn chromeosdevices(&self) -> chromeosdevices::Chromeosdevices {
1160        chromeosdevices::Chromeosdevices::new(self.clone())
1161    }
1162
1163    /// Return a reference to an interface that provides access to customer operations.
1164    pub fn customer(&self) -> customer::Customer {
1165        customer::Customer::new(self.clone())
1166    }
1167
1168    /// Return a reference to an interface that provides access to customers operations.
1169    pub fn customers(&self) -> customers::Customers {
1170        customers::Customers::new(self.clone())
1171    }
1172
1173    /// Return a reference to an interface that provides access to domainAliases operations.
1174    pub fn domain_aliases(&self) -> domain_aliases::DomainAliases {
1175        domain_aliases::DomainAliases::new(self.clone())
1176    }
1177
1178    /// Return a reference to an interface that provides access to domains operations.
1179    pub fn domains(&self) -> domains::Domains {
1180        domains::Domains::new(self.clone())
1181    }
1182
1183    /// Return a reference to an interface that provides access to groups operations.
1184    pub fn groups(&self) -> groups::Groups {
1185        groups::Groups::new(self.clone())
1186    }
1187
1188    /// Return a reference to an interface that provides access to members operations.
1189    pub fn members(&self) -> members::Members {
1190        members::Members::new(self.clone())
1191    }
1192
1193    /// Return a reference to an interface that provides access to mobiledevices operations.
1194    pub fn mobiledevices(&self) -> mobiledevices::Mobiledevices {
1195        mobiledevices::Mobiledevices::new(self.clone())
1196    }
1197
1198    /// Return a reference to an interface that provides access to orgunits operations.
1199    pub fn orgunits(&self) -> orgunits::Orgunits {
1200        orgunits::Orgunits::new(self.clone())
1201    }
1202
1203    /// Return a reference to an interface that provides access to privileges operations.
1204    pub fn privileges(&self) -> privileges::Privileges {
1205        privileges::Privileges::new(self.clone())
1206    }
1207
1208    /// Return a reference to an interface that provides access to resources operations.
1209    pub fn resources(&self) -> resources::Resources {
1210        resources::Resources::new(self.clone())
1211    }
1212
1213    /// Return a reference to an interface that provides access to roleAssignments operations.
1214    pub fn role_assignments(&self) -> role_assignments::RoleAssignments {
1215        role_assignments::RoleAssignments::new(self.clone())
1216    }
1217
1218    /// Return a reference to an interface that provides access to roles operations.
1219    pub fn roles(&self) -> roles::Roles {
1220        roles::Roles::new(self.clone())
1221    }
1222
1223    /// Return a reference to an interface that provides access to schemas operations.
1224    pub fn schemas(&self) -> schemas::Schemas {
1225        schemas::Schemas::new(self.clone())
1226    }
1227
1228    /// Return a reference to an interface that provides access to tokens operations.
1229    pub fn tokens(&self) -> tokens::Tokens {
1230        tokens::Tokens::new(self.clone())
1231    }
1232
1233    /// Return a reference to an interface that provides access to twoStepVerification operations.
1234    pub fn two_step_verification(&self) -> two_step_verification::TwoStepVerification {
1235        two_step_verification::TwoStepVerification::new(self.clone())
1236    }
1237
1238    /// Return a reference to an interface that provides access to users operations.
1239    pub fn users(&self) -> users::Users {
1240        users::Users::new(self.clone())
1241    }
1242
1243    /// Return a reference to an interface that provides access to verificationCodes operations.
1244    pub fn verification_codes(&self) -> verification_codes::VerificationCodes {
1245        verification_codes::VerificationCodes::new(self.clone())
1246    }
1247}