gsuite_api/
lib.rs

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