slack_chat_api/
lib.rs

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