remote_api/
lib.rs

1//! A fully generated & opinionated API client for the Remote API.
2//!
3//! [![docs.rs](https://docs.rs/remote-api/badge.svg)](https://docs.rs/remote-api)
4//!
5//! ## API Details
6//!
7//! # Authentication
8//!
9//! <!-- ReDoc-Inject: <security-definitions> -->
10//!
11//!
12//!
13//!
14//!
15//!
16//! ## Client Details
17//!
18//!
19//!
20//! The documentation for the crate is generated
21//! along with the code to make this library easy to use.
22//!
23//!
24//! To install the library, add the following to your `Cargo.toml` file.
25//!
26//! ```toml
27//! [dependencies]
28//! remote-api = "0.1.2"
29//! ```
30//!
31//! ## Basic example
32//!
33//! Typical use will require intializing a `Client`. This requires
34//! a user agent string and set of credentials.
35//!
36//! ```rust,no_run
37//! use remote_api::Client;
38//!
39//! let client = Client::new(String::from("api-key"));
40//! ```
41//!
42//! Alternatively, the library can search for most of the variables required for
43//! the client in the environment:
44//!
45//! - `REMOTE_API_TOKEN`
46//!
47//!
48//! And then you can create a client from the environment.
49//!
50//! ```rust,no_run
51//! use remote_api::Client;
52//!
53//! let client = Client::new_from_env();
54//! ```
55#![allow(elided_named_lifetimes)]
56#![allow(missing_docs)]
57#![allow(unused_imports)]
58#![allow(clippy::needless_lifetimes)]
59#![allow(clippy::too_many_arguments)]
60#![cfg_attr(docsrs, feature(doc_cfg))]
61
62#[cfg(feature = "requests")]
63pub mod company_managers;
64#[cfg(feature = "requests")]
65pub mod countries;
66#[cfg(feature = "requests")]
67pub mod employments;
68mod methods;
69#[cfg(feature = "requests")]
70pub mod sandbox;
71#[cfg(test)]
72mod tests;
73#[cfg(feature = "requests")]
74pub mod time_off;
75pub mod types;
76pub mod utils;
77
78#[cfg(feature = "requests")]
79use std::env;
80
81#[cfg(not(target_arch = "wasm32"))]
82#[cfg(feature = "requests")]
83static APP_USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), ".rs/", env!("CARGO_PKG_VERSION"),);
84
85/// Entrypoint for interacting with the API client.
86#[derive(Clone, Debug)]
87#[cfg(feature = "requests")]
88pub struct Client {
89    token: String,
90    base_url: String,
91
92    #[cfg(feature = "retry")]
93    client: reqwest_middleware::ClientWithMiddleware,
94    #[cfg(feature = "retry")]
95    #[cfg(not(target_arch = "wasm32"))]
96    #[allow(dead_code)]
97    client_http1_only: reqwest_middleware::ClientWithMiddleware,
98
99    #[cfg(not(feature = "retry"))]
100    client: reqwest::Client,
101    #[cfg(not(feature = "retry"))]
102    #[cfg(not(target_arch = "wasm32"))]
103    #[allow(dead_code)]
104    client_http1_only: reqwest::Client,
105}
106
107/// A request builder.
108#[cfg(feature = "retry")]
109#[cfg(feature = "requests")]
110pub struct RequestBuilder(pub reqwest_middleware::RequestBuilder);
111#[cfg(not(feature = "retry"))]
112#[cfg(feature = "requests")]
113pub struct RequestBuilder(pub reqwest::RequestBuilder);
114
115#[cfg(feature = "requests")]
116impl Client {
117    /// Create a new Client struct. It takes a type that can convert into
118    /// an &str (`String` or `Vec<u8>` for example). As long as the function is
119    /// given a valid API key your requests will work.
120    /// Also takes reqwest client builders, for customizing the client's behaviour.
121    #[tracing::instrument]
122    #[cfg(not(target_arch = "wasm32"))]
123    pub fn new_from_reqwest<T>(
124        token: T,
125        builder_http: reqwest::ClientBuilder,
126        builder_websocket: reqwest::ClientBuilder,
127    ) -> Self
128    where
129        T: ToString + std::fmt::Debug,
130    {
131        #[cfg(feature = "retry")]
132        {
133            // Retry up to 3 times with increasing intervals between attempts.
134            let retry_policy =
135                reqwest_retry::policies::ExponentialBackoff::builder().build_with_max_retries(3);
136            match (builder_http.build(), builder_websocket.build()) {
137                (Ok(c), Ok(c1)) => {
138                    let client = reqwest_middleware::ClientBuilder::new(c)
139                        // Trace HTTP requests. See the tracing crate to make use of these traces.
140                        .with(reqwest_tracing::TracingMiddleware::default())
141                        // Retry failed requests.
142                        .with(reqwest_conditional_middleware::ConditionalMiddleware::new(
143                            reqwest_retry::RetryTransientMiddleware::new_with_policy(retry_policy),
144                            |req: &reqwest::Request| req.try_clone().is_some(),
145                        ))
146                        .build();
147                    let client_http1_only = reqwest_middleware::ClientBuilder::new(c1)
148                        .with(reqwest_tracing::TracingMiddleware::default())
149                        .with(reqwest_conditional_middleware::ConditionalMiddleware::new(
150                            reqwest_retry::RetryTransientMiddleware::new_with_policy(retry_policy),
151                            |req: &reqwest::Request| req.try_clone().is_some(),
152                        ))
153                        .build();
154                    Client {
155                        token: token.to_string(),
156                        base_url: "https://gateway.remote.com".to_string(),
157
158                        client,
159                        client_http1_only,
160                    }
161                }
162                (Err(e), _) | (_, Err(e)) => panic!("creating reqwest client failed: {:?}", e),
163            }
164        }
165        #[cfg(not(feature = "retry"))]
166        {
167            match (builder_http.build(), builder_websocket.build()) {
168                (Ok(c), Ok(c1)) => Client {
169                    token: token.to_string(),
170                    base_url: "https://gateway.remote.com".to_string(),
171
172                    client: c,
173                    client_http1_only: c1,
174                },
175                (Err(e), _) | (_, Err(e)) => panic!("creating reqwest client failed: {:?}", e),
176            }
177        }
178    }
179
180    /// Create a new Client struct. It takes a type that can convert into
181    /// an &str (`String` or `Vec<u8>` for example). As long as the function is
182    /// given a valid API key your requests will work.
183    /// Also takes reqwest client builders, for customizing the client's behaviour.
184    #[tracing::instrument]
185    #[cfg(target_arch = "wasm32")]
186    pub fn new_from_reqwest<T>(token: T, builder_http: reqwest::ClientBuilder) -> Self
187    where
188        T: ToString + std::fmt::Debug,
189    {
190        #[cfg(feature = "retry")]
191        {
192            // Retry up to 3 times with increasing intervals between attempts.
193            let retry_policy =
194                reqwest_retry::policies::ExponentialBackoff::builder().build_with_max_retries(3);
195            match builder_http.build() {
196                Ok(c) => {
197                    let client = reqwest_middleware::ClientBuilder::new(c)
198                        // Trace HTTP requests. See the tracing crate to make use of these traces.
199                        .with(reqwest_tracing::TracingMiddleware::default())
200                        // Retry failed requests.
201                        .with(reqwest_conditional_middleware::ConditionalMiddleware::new(
202                            reqwest_retry::RetryTransientMiddleware::new_with_policy(retry_policy),
203                            |req: &reqwest::Request| req.try_clone().is_some(),
204                        ))
205                        .build();
206                    Client {
207                        token: token.to_string(),
208                        base_url: "https://gateway.remote.com".to_string(),
209
210                        client,
211                    }
212                }
213                Err(e) => panic!("creating reqwest client failed: {:?}", e),
214            }
215        }
216        #[cfg(not(feature = "retry"))]
217        {
218            match builder_http.build() {
219                Ok(c) => Client {
220                    token: token.to_string(),
221                    base_url: "https://gateway.remote.com".to_string(),
222
223                    client: c,
224                },
225                Err(e) => panic!("creating reqwest client failed: {:?}", e),
226            }
227        }
228    }
229
230    /// Create a new Client struct. It takes a type that can convert into
231    /// an &str (`String` or `Vec<u8>` for example). As long as the function is
232    /// given a valid API key your requests will work.
233    #[tracing::instrument]
234    pub fn new<T>(token: T) -> Self
235    where
236        T: ToString + std::fmt::Debug,
237    {
238        #[cfg(not(target_arch = "wasm32"))]
239        let client = reqwest::Client::builder()
240            .user_agent(APP_USER_AGENT)
241            // For file conversions we need this to be long.
242            .timeout(std::time::Duration::from_secs(600))
243            .connect_timeout(std::time::Duration::from_secs(60));
244        #[cfg(target_arch = "wasm32")]
245        let client = reqwest::Client::builder();
246        #[cfg(not(target_arch = "wasm32"))]
247        let client_http1 = reqwest::Client::builder()
248            // For file conversions we need this to be long.
249            .user_agent(APP_USER_AGENT)
250            .timeout(std::time::Duration::from_secs(600))
251            .connect_timeout(std::time::Duration::from_secs(60))
252            .http1_only();
253        #[cfg(not(target_arch = "wasm32"))]
254        return Self::new_from_reqwest(token, client, client_http1);
255        #[cfg(target_arch = "wasm32")]
256        Self::new_from_reqwest(token, client)
257    }
258
259    /// Set the base URL for the client to something other than the default: <https://gateway.remote.com>.
260    #[tracing::instrument]
261    pub fn set_base_url<H>(&mut self, base_url: H)
262    where
263        H: Into<String> + std::fmt::Display + std::fmt::Debug,
264    {
265        self.base_url = base_url.to_string().trim_end_matches('/').to_string();
266    }
267
268    /// Create a new Client struct from the environment variable: `ENV_VARIABLE_PREFIX_API_TOKEN`.
269    #[tracing::instrument]
270    pub fn new_from_env() -> Self {
271        let token = env::var("REMOTE_API_TOKEN").expect("must set REMOTE_API_TOKEN");
272        let base_url = env::var("REMOTE_HOST").unwrap_or("https://gateway.remote.com".to_string());
273
274        let mut c = Client::new(token);
275        c.set_base_url(base_url);
276        c
277    }
278
279    /// Create a raw request to our API.
280    #[tracing::instrument]
281    pub async fn request_raw(
282        &self,
283        method: reqwest::Method,
284        uri: &str,
285        body: Option<reqwest::Body>,
286    ) -> anyhow::Result<RequestBuilder> {
287        let u = if uri.starts_with("https://") || uri.starts_with("http://") {
288            uri.to_string()
289        } else {
290            format!("{}/{}", self.base_url, uri.trim_start_matches('/'))
291        };
292
293        let mut req = self.client.request(method, &u);
294
295        // Add in our authentication.
296        req = req.bearer_auth(&self.token);
297
298        // Set the default headers.
299        req = req.header(
300            reqwest::header::ACCEPT,
301            reqwest::header::HeaderValue::from_static("application/json"),
302        );
303        req = req.header(
304            reqwest::header::CONTENT_TYPE,
305            reqwest::header::HeaderValue::from_static("application/json"),
306        );
307
308        if let Some(body) = body {
309            req = req.body(body);
310        }
311
312        Ok(RequestBuilder(req))
313    }
314
315    /// Return a reference to an interface that provides access to Company Managers operations.
316    pub fn company_managers(&self) -> company_managers::CompanyManagers {
317        company_managers::CompanyManagers::new(self.clone())
318    }
319
320    /// Return a reference to an interface that provides access to Countries operations.
321    pub fn countries(&self) -> countries::Countries {
322        countries::Countries::new(self.clone())
323    }
324
325    /// Return a reference to an interface that provides access to Employments operations.
326    pub fn employments(&self) -> employments::Employments {
327        employments::Employments::new(self.clone())
328    }
329
330    /// Return a reference to an interface that provides access to Sandbox operations.
331    pub fn sandbox(&self) -> sandbox::Sandbox {
332        sandbox::Sandbox::new(self.clone())
333    }
334
335    /// Return a reference to an interface that provides access to Time Off operations.
336    pub fn time_off(&self) -> time_off::TimeOff {
337        time_off::TimeOff::new(self.clone())
338    }
339}