1#![allow(clippy::derive_partial_eq_without_eq)]
101#![allow(clippy::too_many_arguments)]
102#![allow(clippy::nonstandard_macro_braces)]
103#![allow(clippy::large_enum_variant)]
104#![allow(clippy::tabs_in_doc_comments)]
105#![allow(missing_docs)]
106#![cfg_attr(docsrs, feature(doc_cfg))]
107
108pub mod admins_beta;
109pub mod benefits;
110pub mod companies;
111pub mod company_bank_accounts_beta;
112pub mod compensations;
113pub mod contractor_payments;
114pub mod contractors;
115pub mod current_user;
116pub mod custom_fields;
117pub mod earning_type;
118pub mod employees;
119pub mod garnishments;
120pub mod job_applicants_beta;
121pub mod jobs;
122pub mod locations;
123pub mod pay_schedules;
124pub mod payroll;
125pub mod terminations;
126pub mod time_off_requests;
127pub mod types;
128#[doc(hidden)]
129pub mod utils;
130
131pub use reqwest::{header::HeaderMap, StatusCode};
132
133#[derive(Debug)]
134pub struct Response<T> {
135 pub status: reqwest::StatusCode,
136 pub headers: reqwest::header::HeaderMap,
137 pub body: T,
138}
139
140impl<T> Response<T> {
141 pub fn new(status: reqwest::StatusCode, headers: reqwest::header::HeaderMap, body: T) -> Self {
142 Self {
143 status,
144 headers,
145 body,
146 }
147 }
148}
149
150type ClientResult<T> = Result<T, ClientError>;
151
152use thiserror::Error;
153
154#[derive(Debug, Error)]
156pub enum ClientError {
157 #[error("Refresh AuthToken is empty")]
160 EmptyRefreshToken,
161 #[error(transparent)]
163 FromUtf8Error(#[from] std::string::FromUtf8Error),
164 #[error(transparent)]
166 UrlParserError(#[from] url::ParseError),
167 #[error(transparent)]
169 SerdeJsonError(#[from] serde_json::Error),
170 #[error(transparent)]
172 ReqwestError(#[from] reqwest::Error),
173 #[error(transparent)]
175 InvalidHeaderValue(#[from] reqwest::header::InvalidHeaderValue),
176 #[error(transparent)]
178 ReqwestMiddleWareError(#[from] reqwest_middleware::Error),
179 #[error("HTTP Error. Code: {status}, message: {error}")]
181 HttpError {
182 status: http::StatusCode,
183 headers: reqwest::header::HeaderMap,
184 error: String,
185 },
186}
187
188pub const FALLBACK_HOST: &str = "https://api.gusto.com";
189
190mod progenitor_support {
191 use percent_encoding::{utf8_percent_encode, AsciiSet, CONTROLS};
192
193 const PATH_SET: &AsciiSet = &CONTROLS
194 .add(b' ')
195 .add(b'"')
196 .add(b'#')
197 .add(b'<')
198 .add(b'>')
199 .add(b'?')
200 .add(b'`')
201 .add(b'{')
202 .add(b'}');
203
204 #[allow(dead_code)]
205 pub(crate) fn encode_path(pc: &str) -> String {
206 utf8_percent_encode(pc, PATH_SET).to_string()
207 }
208}
209
210#[derive(Debug, Default)]
211pub(crate) struct Message {
212 pub body: Option<reqwest::Body>,
213 pub content_type: Option<String>,
214}
215
216use std::convert::TryInto;
217use std::env;
218use std::ops::Add;
219use std::sync::Arc;
220use std::time::{Duration, Instant};
221use tokio::sync::RwLock;
222
223const TOKEN_ENDPOINT: &str = "https://api.gusto.com/oauth/token";
224const USER_CONSENT_ENDPOINT: &str = "https://api.gusto.com/oauth/authorize";
225
226pub enum RootDefaultServers {
227 RootDemoServer(RootDemoServer),
228 RootProductionServer(RootProductionServer),
229}
230
231impl RootDefaultServers {
232 pub fn default_url(&self) -> &str {
233 match self {
234 Self::RootDemoServer(inner) => inner.default_url(),
235 Self::RootProductionServer(inner) => inner.default_url(),
236 }
237 }
238}
239
240#[derive(Debug, Default, Clone)]
241pub struct RootDemoServer {}
242
243impl RootDemoServer {
244 pub fn default_url(&self) -> &str {
245 "https://api.gusto-demo.com"
246 }
247}
248
249#[derive(Debug, Default, Clone)]
250pub struct RootProductionServer {}
251
252impl RootProductionServer {
253 pub fn default_url(&self) -> &str {
254 "https://api.gusto.com"
255 }
256}
257
258impl From<RootDemoServer> for RootDefaultServers {
259 fn from(server: RootDemoServer) -> Self {
260 Self::RootDemoServer(server)
261 }
262}
263impl From<RootProductionServer> for RootDefaultServers {
264 fn from(server: RootProductionServer) -> Self {
265 Self::RootProductionServer(server)
266 }
267}
268
269#[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
321const 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 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
346 server: impl Into<RootDefaultServers>,
347 ) -> Self
348 where
349 I: ToString,
350 K: ToString,
351 R: ToString,
352 T: ToString,
353 Q: ToString,
354 {
355 let retry_policy =
357 reqwest_retry::policies::ExponentialBackoff::builder().build_with_max_retries(3);
358 let client = reqwest::Client::builder()
359 .redirect(reqwest::redirect::Policy::none())
360 .build();
361 match client {
362 Ok(c) => {
363 let client = reqwest_middleware::ClientBuilder::new(c)
364 .with(reqwest_tracing::TracingMiddleware::default())
366 .with(reqwest_conditional_middleware::ConditionalMiddleware::new(
368 reqwest_retry::RetryTransientMiddleware::new_with_policy(retry_policy),
369 |req: &reqwest::Request| req.try_clone().is_some(),
370 ))
371 .build();
372
373 let host = server.into().default_url().to_string();
374
375 Client {
376 host,
377 host_override: None,
378 client_id: client_id.to_string(),
379 client_secret: client_secret.to_string(),
380 redirect_uri: redirect_uri.to_string(),
381 token: Arc::new(RwLock::new(InnerToken {
382 access_token: token.to_string(),
383 refresh_token: refresh_token.to_string(),
384 expires_at: None,
385 })),
386
387 auto_refresh: false,
388 client,
389 }
390 }
391 Err(e) => panic!("creating reqwest client failed: {:?}", e),
392 }
393 }
394
395 pub fn set_auto_access_token_refresh(&mut self, enabled: bool) -> &mut Self {
397 self.auto_refresh = enabled;
398 self
399 }
400
401 pub async fn set_expires_at(&self, expires_at: Option<Instant>) -> &Self {
407 self.token.write().await.expires_at = expires_at;
408 self
409 }
410
411 pub async fn expires_at(&self) -> Option<Instant> {
414 self.token.read().await.expires_at
415 }
416
417 pub async fn set_expires_in(&self, expires_in: i64) -> &Self {
420 self.token.write().await.expires_at = Self::compute_expires_at(expires_in);
421 self
422 }
423
424 pub async fn expires_in(&self) -> Option<Duration> {
427 self.token
428 .read()
429 .await
430 .expires_at
431 .map(|i| i.duration_since(Instant::now()))
432 }
433
434 pub async fn is_expired(&self) -> Option<bool> {
437 self.token
438 .read()
439 .await
440 .expires_at
441 .map(|expiration| expiration <= Instant::now())
442 }
443
444 fn compute_expires_at(expires_in: i64) -> Option<Instant> {
445 let seconds_valid = expires_in
446 .try_into()
447 .ok()
448 .map(Duration::from_secs)
449 .and_then(|dur| dur.checked_sub(REFRESH_THRESHOLD))
450 .or_else(|| Some(Duration::from_secs(0)));
451
452 seconds_valid.map(|seconds_valid| Instant::now().add(seconds_valid))
453 }
454
455 pub fn with_host_override<H>(&mut self, host: H) -> &mut Self
457 where
458 H: ToString,
459 {
460 self.host_override = Some(host.to_string());
461 self
462 }
463
464 pub fn remove_host_override(&mut self) -> &mut Self {
466 self.host_override = None;
467 self
468 }
469
470 pub fn get_host_override(&self) -> Option<&str> {
471 self.host_override.as_deref()
472 }
473
474 pub(crate) fn url(&self, path: &str, host: Option<&str>) -> String {
475 format!(
476 "{}{}",
477 self.get_host_override()
478 .or(host)
479 .unwrap_or(self.host.as_str()),
480 path
481 )
482 }
483
484 pub fn new_from_env<T, R>(
495 token: T,
496 refresh_token: R,
497 server: impl Into<RootDefaultServers>,
498 ) -> Self
499 where
500 T: ToString,
501 R: ToString,
502 {
503 let client_id = env::var("GUSTO_CLIENT_ID").expect("must set GUSTO_CLIENT_ID");
504 let client_secret = env::var("GUSTO_CLIENT_SECRET").expect("must set GUSTO_CLIENT_SECRET");
505 let redirect_uri = env::var("GUSTO_REDIRECT_URI").expect("must set GUSTO_REDIRECT_URI");
506
507 Client::new(
508 client_id,
509 client_secret,
510 redirect_uri,
511 token,
512 refresh_token,
513 server,
514 )
515 }
516
517 pub fn user_consent_url(&self, scopes: &[String]) -> String {
520 let state = uuid::Uuid::new_v4();
521
522 let url = format!(
523 "{}?client_id={}&response_type=code&redirect_uri={}&state={}",
524 USER_CONSENT_ENDPOINT, self.client_id, self.redirect_uri, state
525 );
526
527 if scopes.is_empty() {
528 return url;
529 }
530
531 format!("{}&scope={}", url, scopes.join(" "))
533 }
534
535 pub async fn refresh_access_token(&self) -> ClientResult<AccessToken> {
538 let response = {
539 let refresh_token = &self.token.read().await.refresh_token;
540
541 if refresh_token.is_empty() {
542 return Err(ClientError::EmptyRefreshToken);
543 }
544
545 let mut headers = reqwest::header::HeaderMap::new();
546 headers.append(
547 reqwest::header::ACCEPT,
548 reqwest::header::HeaderValue::from_static("application/json"),
549 );
550
551 let params = [
552 ("grant_type", "refresh_token"),
553 ("refresh_token", refresh_token),
554 ("client_id", &self.client_id),
555 ("client_secret", &self.client_secret),
556 ("redirect_uri", &self.redirect_uri),
557 ];
558 let client = reqwest::Client::new();
559 client
560 .post(TOKEN_ENDPOINT)
561 .headers(headers)
562 .form(¶ms)
563 .basic_auth(&self.client_id, Some(&self.client_secret))
564 .send()
565 .await?
566 };
567
568 let t: AccessToken = response.json().await?;
570
571 let refresh_token = self.token.read().await.refresh_token.clone();
572
573 *self.token.write().await = InnerToken {
574 access_token: t.access_token.clone(),
575 refresh_token,
576 expires_at: Self::compute_expires_at(t.expires_in),
577 };
578
579 Ok(t)
580 }
581
582 pub async fn get_access_token(&mut self, code: &str, state: &str) -> ClientResult<AccessToken> {
585 let mut headers = reqwest::header::HeaderMap::new();
586 headers.append(
587 reqwest::header::ACCEPT,
588 reqwest::header::HeaderValue::from_static("application/json"),
589 );
590
591 let params = [
592 ("grant_type", "authorization_code"),
593 ("code", code),
594 ("client_id", &self.client_id),
595 ("client_secret", &self.client_secret),
596 ("redirect_uri", &self.redirect_uri),
597 ("state", state),
598 ];
599 let client = reqwest::Client::new();
600 let resp = client
601 .post(TOKEN_ENDPOINT)
602 .headers(headers)
603 .form(¶ms)
604 .basic_auth(&self.client_id, Some(&self.client_secret))
605 .send()
606 .await?;
607
608 let t: AccessToken = resp.json().await?;
610
611 *self.token.write().await = InnerToken {
612 access_token: t.access_token.clone(),
613 refresh_token: t.refresh_token.clone(),
614 expires_at: Self::compute_expires_at(t.expires_in),
615 };
616
617 Ok(t)
618 }
619
620 async fn url_and_auth(&self, uri: &str) -> ClientResult<(reqwest::Url, Option<String>)> {
621 let parsed_url = uri.parse::<reqwest::Url>()?;
622
623 let auth = format!("Bearer {}", self.token.read().await.access_token);
624 Ok((parsed_url, Some(auth)))
625 }
626
627 async fn make_request(
628 &self,
629 method: &reqwest::Method,
630 uri: &str,
631 message: Message,
632 ) -> ClientResult<reqwest::Request> {
633 let (url, auth) = self.url_and_auth(uri).await?;
634
635 let instance = <&Client>::clone(&self);
636
637 let mut req = instance.client.request(method.clone(), url);
638
639 req = req.header(
641 reqwest::header::ACCEPT,
642 reqwest::header::HeaderValue::from_static("application/json"),
643 );
644
645 if let Some(content_type) = &message.content_type {
646 req = req.header(
647 reqwest::header::CONTENT_TYPE,
648 reqwest::header::HeaderValue::from_str(content_type).unwrap(),
649 );
650 } else {
651 req = req.header(
652 reqwest::header::CONTENT_TYPE,
653 reqwest::header::HeaderValue::from_static("application/json"),
654 );
655 }
656
657 if let Some(auth_str) = auth {
658 req = req.header(http::header::AUTHORIZATION, &*auth_str);
659 }
660
661 if let Some(body) = message.body {
662 req = req.body(body);
663 }
664
665 Ok(req.build()?)
666 }
667
668 async fn request_raw(
669 &self,
670 method: reqwest::Method,
671 uri: &str,
672 message: Message,
673 ) -> ClientResult<reqwest::Response> {
674 if self.auto_refresh {
675 let expired = self.is_expired().await;
676
677 match expired {
678 Some(true) => {
681 self.refresh_access_token().await?;
682 }
683
684 Some(false) => (),
689
690 None => (),
698 }
699 }
700
701 let req = self.make_request(&method, uri, message).await?;
702 let resp = self.client.execute(req).await?;
703
704 Ok(resp)
705 }
706
707 async fn request<Out>(
708 &self,
709 method: reqwest::Method,
710 uri: &str,
711 message: Message,
712 ) -> ClientResult<crate::Response<Out>>
713 where
714 Out: serde::de::DeserializeOwned + 'static + Send,
715 {
716 let response = self.request_raw(method, uri, message).await?;
717
718 let status = response.status();
719 let headers = response.headers().clone();
720
721 let response_body = response.bytes().await?;
722
723 if status.is_success() {
724 log::debug!("Received successful response. Read payload.");
725 let parsed_response = if status == http::StatusCode::NO_CONTENT
726 || std::any::TypeId::of::<Out>() == std::any::TypeId::of::<()>()
727 {
728 serde_json::from_str("null")?
729 } else {
730 serde_json::from_slice::<Out>(&response_body)?
731 };
732 Ok(crate::Response::new(status, headers, parsed_response))
733 } else {
734 let error = if response_body.is_empty() {
735 ClientError::HttpError {
736 status,
737 headers,
738 error: "empty response".into(),
739 }
740 } else {
741 ClientError::HttpError {
742 status,
743 headers,
744 error: String::from_utf8_lossy(&response_body).into(),
745 }
746 };
747
748 Err(error)
749 }
750 }
751
752 async fn request_with_links<Out>(
753 &self,
754 method: http::Method,
755 uri: &str,
756 message: Message,
757 ) -> ClientResult<(Option<crate::utils::NextLink>, crate::Response<Out>)>
758 where
759 Out: serde::de::DeserializeOwned + 'static + Send,
760 {
761 let response = self.request_raw(method, uri, message).await?;
762
763 let status = response.status();
764 let headers = response.headers().clone();
765 let link = response
766 .headers()
767 .get(http::header::LINK)
768 .and_then(|l| l.to_str().ok())
769 .and_then(|l| parse_link_header::parse(l).ok())
770 .as_ref()
771 .and_then(crate::utils::next_link);
772
773 let response_body = response.bytes().await?;
774
775 if status.is_success() {
776 log::debug!("Received successful response. Read payload.");
777
778 let parsed_response = if status == http::StatusCode::NO_CONTENT
779 || std::any::TypeId::of::<Out>() == std::any::TypeId::of::<()>()
780 {
781 serde_json::from_str("null")?
782 } else {
783 serde_json::from_slice::<Out>(&response_body)?
784 };
785 Ok((link, crate::Response::new(status, headers, parsed_response)))
786 } else {
787 let error = if response_body.is_empty() {
788 ClientError::HttpError {
789 status,
790 headers,
791 error: "empty response".into(),
792 }
793 } else {
794 ClientError::HttpError {
795 status,
796 headers,
797 error: String::from_utf8_lossy(&response_body).into(),
798 }
799 };
800 Err(error)
801 }
802 }
803
804 #[allow(dead_code)]
806 async fn post_form<Out>(
807 &self,
808 uri: &str,
809 form: reqwest::multipart::Form,
810 ) -> ClientResult<crate::Response<Out>>
811 where
812 Out: serde::de::DeserializeOwned + 'static + Send,
813 {
814 let (url, auth) = self.url_and_auth(uri).await?;
815
816 let instance = <&Client>::clone(&self);
817
818 let mut req = instance.client.request(http::Method::POST, url);
819
820 req = req.header(
822 reqwest::header::ACCEPT,
823 reqwest::header::HeaderValue::from_static("application/json"),
824 );
825
826 if let Some(auth_str) = auth {
827 req = req.header(http::header::AUTHORIZATION, &*auth_str);
828 }
829
830 req = req.multipart(form);
831
832 let response = req.send().await?;
833
834 let status = response.status();
835 let headers = response.headers().clone();
836
837 let response_body = response.bytes().await?;
838
839 if status.is_success() {
840 log::debug!("Received successful response. Read payload.");
841 let parsed_response = if status == http::StatusCode::NO_CONTENT
842 || std::any::TypeId::of::<Out>() == std::any::TypeId::of::<()>()
843 {
844 serde_json::from_str("null")?
845 } else if std::any::TypeId::of::<Out>() == std::any::TypeId::of::<String>() {
846 let s = String::from_utf8(response_body.to_vec())?;
848 serde_json::from_value(serde_json::json!(&s))?
849 } else {
850 serde_json::from_slice::<Out>(&response_body)?
851 };
852 Ok(crate::Response::new(status, headers, parsed_response))
853 } else {
854 let error = if response_body.is_empty() {
855 ClientError::HttpError {
856 status,
857 headers,
858 error: "empty response".into(),
859 }
860 } else {
861 ClientError::HttpError {
862 status,
863 headers,
864 error: String::from_utf8_lossy(&response_body).into(),
865 }
866 };
867
868 Err(error)
869 }
870 }
871
872 #[allow(dead_code)]
874 async fn request_with_accept_mime<Out>(
875 &self,
876 method: reqwest::Method,
877 uri: &str,
878 accept_mime_type: &str,
879 ) -> ClientResult<crate::Response<Out>>
880 where
881 Out: serde::de::DeserializeOwned + 'static + Send,
882 {
883 let (url, auth) = self.url_and_auth(uri).await?;
884
885 let instance = <&Client>::clone(&self);
886
887 let mut req = instance.client.request(method, url);
888
889 req = req.header(
891 reqwest::header::ACCEPT,
892 reqwest::header::HeaderValue::from_str(accept_mime_type)?,
893 );
894
895 if let Some(auth_str) = auth {
896 req = req.header(http::header::AUTHORIZATION, &*auth_str);
897 }
898
899 let response = req.send().await?;
900
901 let status = response.status();
902 let headers = response.headers().clone();
903
904 let response_body = response.bytes().await?;
905
906 if status.is_success() {
907 log::debug!("Received successful response. Read payload.");
908 let parsed_response = if status == http::StatusCode::NO_CONTENT
909 || std::any::TypeId::of::<Out>() == std::any::TypeId::of::<()>()
910 {
911 serde_json::from_str("null")?
912 } else if std::any::TypeId::of::<Out>() == std::any::TypeId::of::<String>() {
913 let s = String::from_utf8(response_body.to_vec())?;
915 serde_json::from_value(serde_json::json!(&s))?
916 } else {
917 serde_json::from_slice::<Out>(&response_body)?
918 };
919 Ok(crate::Response::new(status, headers, parsed_response))
920 } else {
921 let error = if response_body.is_empty() {
922 ClientError::HttpError {
923 status,
924 headers,
925 error: "empty response".into(),
926 }
927 } else {
928 ClientError::HttpError {
929 status,
930 headers,
931 error: String::from_utf8_lossy(&response_body).into(),
932 }
933 };
934
935 Err(error)
936 }
937 }
938
939 #[allow(dead_code)]
941 async fn request_with_mime<Out>(
942 &self,
943 method: reqwest::Method,
944 uri: &str,
945 content: &[u8],
946 mime_type: &str,
947 ) -> ClientResult<crate::Response<Out>>
948 where
949 Out: serde::de::DeserializeOwned + 'static + Send,
950 {
951 let (url, auth) = self.url_and_auth(uri).await?;
952
953 let instance = <&Client>::clone(&self);
954
955 let mut req = instance.client.request(method, url);
956
957 req = req.header(
959 reqwest::header::ACCEPT,
960 reqwest::header::HeaderValue::from_static("application/json"),
961 );
962 req = req.header(
963 reqwest::header::CONTENT_TYPE,
964 reqwest::header::HeaderValue::from_bytes(mime_type.as_bytes()).unwrap(),
965 );
966 req = req.header(
968 reqwest::header::HeaderName::from_static("x-upload-content-type"),
969 reqwest::header::HeaderValue::from_static("application/octet-stream"),
970 );
971 req = req.header(
972 reqwest::header::HeaderName::from_static("x-upload-content-length"),
973 reqwest::header::HeaderValue::from_bytes(format!("{}", content.len()).as_bytes())
974 .unwrap(),
975 );
976
977 if let Some(auth_str) = auth {
978 req = req.header(http::header::AUTHORIZATION, &*auth_str);
979 }
980
981 if content.len() > 1 {
982 let b = bytes::Bytes::copy_from_slice(content);
983 req = req.body(b);
985 }
986
987 let response = req.send().await?;
988
989 let status = response.status();
990 let headers = response.headers().clone();
991
992 let response_body = response.bytes().await?;
993
994 if status.is_success() {
995 log::debug!("Received successful response. Read payload.");
996 let parsed_response = if status == http::StatusCode::NO_CONTENT
997 || std::any::TypeId::of::<Out>() == std::any::TypeId::of::<()>()
998 {
999 serde_json::from_str("null")?
1000 } else {
1001 serde_json::from_slice::<Out>(&response_body)?
1002 };
1003 Ok(crate::Response::new(status, headers, parsed_response))
1004 } else {
1005 let error = if response_body.is_empty() {
1006 ClientError::HttpError {
1007 status,
1008 headers,
1009 error: "empty response".into(),
1010 }
1011 } else {
1012 ClientError::HttpError {
1013 status,
1014 headers,
1015 error: String::from_utf8_lossy(&response_body).into(),
1016 }
1017 };
1018
1019 Err(error)
1020 }
1021 }
1022
1023 async fn request_entity<D>(
1024 &self,
1025 method: http::Method,
1026 uri: &str,
1027 message: Message,
1028 ) -> ClientResult<crate::Response<D>>
1029 where
1030 D: serde::de::DeserializeOwned + 'static + Send,
1031 {
1032 let r = self.request(method, uri, message).await?;
1033 Ok(r)
1034 }
1035
1036 #[allow(dead_code)]
1037 async fn get<D>(&self, uri: &str, message: Message) -> ClientResult<crate::Response<D>>
1038 where
1039 D: serde::de::DeserializeOwned + 'static + Send,
1040 {
1041 self.request_entity(http::Method::GET, uri, message).await
1042 }
1043
1044 #[allow(dead_code)]
1045 async fn get_all_pages<D>(&self, uri: &str, _message: Message) -> ClientResult<Response<Vec<D>>>
1046 where
1047 D: serde::de::DeserializeOwned + 'static + Send,
1048 {
1049 self.unfold(uri).await
1051 }
1052
1053 #[allow(dead_code)]
1055 async fn unfold<D>(&self, uri: &str) -> ClientResult<crate::Response<Vec<D>>>
1056 where
1057 D: serde::de::DeserializeOwned + 'static + Send,
1058 {
1059 let mut global_items = Vec::new();
1060 let (new_link, mut response) = self.get_pages(uri).await?;
1061 let mut link = new_link;
1062 while !response.body.is_empty() {
1063 global_items.append(&mut response.body);
1064 if let Some(url) = &link {
1066 let url = reqwest::Url::parse(&url.0)?;
1067 let (new_link, new_response) = self.get_pages_url(&url).await?;
1068 link = new_link;
1069 response = new_response;
1070 }
1071 }
1072
1073 Ok(Response::new(
1074 response.status,
1075 response.headers,
1076 global_items,
1077 ))
1078 }
1079
1080 #[allow(dead_code)]
1081 async fn get_pages<D>(
1082 &self,
1083 uri: &str,
1084 ) -> ClientResult<(Option<crate::utils::NextLink>, crate::Response<Vec<D>>)>
1085 where
1086 D: serde::de::DeserializeOwned + 'static + Send,
1087 {
1088 self.request_with_links(http::Method::GET, uri, Message::default())
1089 .await
1090 }
1091
1092 #[allow(dead_code)]
1093 async fn get_pages_url<D>(
1094 &self,
1095 url: &reqwest::Url,
1096 ) -> ClientResult<(Option<crate::utils::NextLink>, crate::Response<Vec<D>>)>
1097 where
1098 D: serde::de::DeserializeOwned + 'static + Send,
1099 {
1100 self.request_with_links(http::Method::GET, url.as_str(), Message::default())
1101 .await
1102 }
1103
1104 #[allow(dead_code)]
1105 async fn post<D>(&self, uri: &str, message: Message) -> ClientResult<crate::Response<D>>
1106 where
1107 D: serde::de::DeserializeOwned + 'static + Send,
1108 {
1109 self.request_entity(http::Method::POST, uri, message).await
1110 }
1111
1112 #[allow(dead_code)]
1113 async fn patch<D>(&self, uri: &str, message: Message) -> ClientResult<crate::Response<D>>
1114 where
1115 D: serde::de::DeserializeOwned + 'static + Send,
1116 {
1117 self.request_entity(http::Method::PATCH, uri, message).await
1118 }
1119
1120 #[allow(dead_code)]
1121 async fn put<D>(&self, uri: &str, message: Message) -> ClientResult<crate::Response<D>>
1122 where
1123 D: serde::de::DeserializeOwned + 'static + Send,
1124 {
1125 self.request_entity(http::Method::PUT, uri, message).await
1126 }
1127
1128 #[allow(dead_code)]
1129 async fn delete<D>(&self, uri: &str, message: Message) -> ClientResult<crate::Response<D>>
1130 where
1131 D: serde::de::DeserializeOwned + 'static + Send,
1132 {
1133 self.request_entity(http::Method::DELETE, uri, message)
1134 .await
1135 }
1136
1137 pub fn current_user(&self) -> current_user::CurrentUser {
1139 current_user::CurrentUser::new(self.clone())
1140 }
1141
1142 pub fn companies(&self) -> companies::Companies {
1144 companies::Companies::new(self.clone())
1145 }
1146
1147 pub fn employees(&self) -> employees::Employees {
1149 employees::Employees::new(self.clone())
1150 }
1151
1152 pub fn contractors(&self) -> contractors::Contractors {
1154 contractors::Contractors::new(self.clone())
1155 }
1156
1157 pub fn payroll(&self) -> payroll::Payroll {
1159 payroll::Payroll::new(self.clone())
1160 }
1161
1162 pub fn contractor_payments(&self) -> contractor_payments::ContractorPayments {
1164 contractor_payments::ContractorPayments::new(self.clone())
1165 }
1166
1167 pub fn company_bank_accounts_beta(
1169 &self,
1170 ) -> company_bank_accounts_beta::CompanyBankAccountsBeta {
1171 company_bank_accounts_beta::CompanyBankAccountsBeta::new(self.clone())
1172 }
1173
1174 pub fn benefits(&self) -> benefits::Benefits {
1176 benefits::Benefits::new(self.clone())
1177 }
1178
1179 pub fn locations(&self) -> locations::Locations {
1181 locations::Locations::new(self.clone())
1182 }
1183
1184 pub fn jobs(&self) -> jobs::Jobs {
1186 jobs::Jobs::new(self.clone())
1187 }
1188
1189 pub fn job_applicants_beta(&self) -> job_applicants_beta::JobApplicantsBeta {
1191 job_applicants_beta::JobApplicantsBeta::new(self.clone())
1192 }
1193
1194 pub fn compensations(&self) -> compensations::Compensations {
1196 compensations::Compensations::new(self.clone())
1197 }
1198
1199 pub fn pay_schedules(&self) -> pay_schedules::PaySchedules {
1201 pay_schedules::PaySchedules::new(self.clone())
1202 }
1203
1204 pub fn garnishments(&self) -> garnishments::Garnishments {
1206 garnishments::Garnishments::new(self.clone())
1207 }
1208
1209 pub fn time_off_requests(&self) -> time_off_requests::TimeOffRequests {
1211 time_off_requests::TimeOffRequests::new(self.clone())
1212 }
1213
1214 pub fn earning_type(&self) -> earning_type::EarningType {
1216 earning_type::EarningType::new(self.clone())
1217 }
1218
1219 pub fn terminations(&self) -> terminations::Terminations {
1221 terminations::Terminations::new(self.clone())
1222 }
1223
1224 pub fn custom_fields(&self) -> custom_fields::CustomFields {
1226 custom_fields::CustomFields::new(self.clone())
1227 }
1228
1229 pub fn admins_beta(&self) -> admins_beta::AdminsBeta {
1231 admins_beta::AdminsBeta::new(self.clone())
1232 }
1233}