1#![allow(clippy::derive_partial_eq_without_eq)]
95#![allow(clippy::too_many_arguments)]
96#![allow(clippy::nonstandard_macro_braces)]
97#![allow(clippy::large_enum_variant)]
98#![allow(clippy::tabs_in_doc_comments)]
99#![allow(missing_docs)]
100#![cfg_attr(docsrs, feature(doc_cfg))]
101
102pub mod auths;
103pub mod businesses;
104pub mod card_programs;
105pub mod cards;
106pub mod custom_ids;
107pub mod departments;
108pub mod locations;
109pub mod receipts;
110pub mod reimbursements;
111pub mod transactions;
112pub mod types;
113pub mod users;
114#[doc(hidden)]
115pub mod utils;
116
117pub use reqwest::{header::HeaderMap, StatusCode};
118
119#[derive(Debug)]
120pub struct Response<T> {
121 pub status: reqwest::StatusCode,
122 pub headers: reqwest::header::HeaderMap,
123 pub body: T,
124}
125
126impl<T> Response<T> {
127 pub fn new(status: reqwest::StatusCode, headers: reqwest::header::HeaderMap, body: T) -> Self {
128 Self {
129 status,
130 headers,
131 body,
132 }
133 }
134}
135
136type ClientResult<T> = Result<T, ClientError>;
137
138use thiserror::Error;
139
140#[derive(Debug, Error)]
142pub enum ClientError {
143 #[error("Refresh AuthToken is empty")]
146 EmptyRefreshToken,
147 #[error(transparent)]
149 FromUtf8Error(#[from] std::string::FromUtf8Error),
150 #[error(transparent)]
152 UrlParserError(#[from] url::ParseError),
153 #[error(transparent)]
155 SerdeJsonError(#[from] serde_json::Error),
156 #[error(transparent)]
158 ReqwestError(#[from] reqwest::Error),
159 #[error(transparent)]
161 InvalidHeaderValue(#[from] reqwest::header::InvalidHeaderValue),
162 #[error(transparent)]
164 ReqwestMiddleWareError(#[from] reqwest_middleware::Error),
165 #[error("HTTP Error. Code: {status}, message: {error}")]
167 HttpError {
168 status: http::StatusCode,
169 headers: reqwest::header::HeaderMap,
170 error: String,
171 },
172}
173
174pub const FALLBACK_HOST: &str = "https://api.ramp.com/developer/v1";
175
176mod progenitor_support {
177 use percent_encoding::{utf8_percent_encode, AsciiSet, CONTROLS};
178
179 const PATH_SET: &AsciiSet = &CONTROLS
180 .add(b' ')
181 .add(b'"')
182 .add(b'#')
183 .add(b'<')
184 .add(b'>')
185 .add(b'?')
186 .add(b'`')
187 .add(b'{')
188 .add(b'}');
189
190 #[allow(dead_code)]
191 pub(crate) fn encode_path(pc: &str) -> String {
192 utf8_percent_encode(pc, PATH_SET).to_string()
193 }
194}
195
196#[derive(Debug, Default)]
197pub(crate) struct Message {
198 pub body: Option<reqwest::Body>,
199 pub content_type: Option<String>,
200}
201
202use std::convert::TryInto;
203use std::env;
204use std::ops::Add;
205use std::sync::Arc;
206use std::time::{Duration, Instant};
207use tokio::sync::RwLock;
208
209const TOKEN_ENDPOINT: &str = "https://api.ramp.com/v1/public/customer/token";
210const USER_CONSENT_ENDPOINT: &str = "https://app.ramp.com/v1/authorize";
211
212#[derive(Debug, Default, Clone)]
213pub struct RootDefaultServer {}
214
215impl RootDefaultServer {
216 pub fn default_url(&self) -> &str {
217 "https://api.ramp.com/developer/v1"
218 }
219}
220
221#[derive(Clone)]
223pub struct Client {
224 host: String,
225 host_override: Option<String>,
226 token: Arc<RwLock<InnerToken>>,
227 client_id: String,
228 client_secret: String,
229 redirect_uri: String,
230
231 auto_refresh: bool,
232 client: reqwest_middleware::ClientWithMiddleware,
233}
234
235use schemars::JsonSchema;
236use serde::{Deserialize, Serialize};
237
238#[derive(Debug, JsonSchema, Clone, Default, Serialize, Deserialize)]
239pub struct AccessToken {
240 #[serde(
241 default,
242 skip_serializing_if = "String::is_empty",
243 deserialize_with = "crate::utils::deserialize_null_string::deserialize"
244 )]
245 pub token_type: String,
246
247 #[serde(
248 default,
249 skip_serializing_if = "String::is_empty",
250 deserialize_with = "crate::utils::deserialize_null_string::deserialize"
251 )]
252 pub access_token: String,
253 #[serde(default)]
254 pub expires_in: i64,
255
256 #[serde(
257 default,
258 skip_serializing_if = "String::is_empty",
259 deserialize_with = "crate::utils::deserialize_null_string::deserialize"
260 )]
261 pub refresh_token: String,
262 #[serde(default, alias = "x_refresh_token_expires_in")]
263 pub refresh_token_expires_in: i64,
264
265 #[serde(
266 default,
267 skip_serializing_if = "String::is_empty",
268 deserialize_with = "crate::utils::deserialize_null_string::deserialize"
269 )]
270 pub scope: String,
271}
272
273const REFRESH_THRESHOLD: Duration = Duration::from_secs(60);
277
278#[derive(Debug, Clone)]
279struct InnerToken {
280 access_token: String,
281 refresh_token: String,
282 expires_at: Option<Instant>,
283}
284
285impl Client {
286 pub fn new<I, K, R, T, Q>(
292 client_id: I,
293 client_secret: K,
294 redirect_uri: R,
295 token: T,
296 refresh_token: Q,
297 ) -> Self
298 where
299 I: ToString,
300 K: ToString,
301 R: ToString,
302 T: ToString,
303 Q: ToString,
304 {
305 let retry_policy =
307 reqwest_retry::policies::ExponentialBackoff::builder().build_with_max_retries(3);
308 let client = reqwest::Client::builder()
309 .redirect(reqwest::redirect::Policy::none())
310 .build();
311 match client {
312 Ok(c) => {
313 let client = reqwest_middleware::ClientBuilder::new(c)
314 .with(reqwest_tracing::TracingMiddleware::default())
316 .with(reqwest_conditional_middleware::ConditionalMiddleware::new(
318 reqwest_retry::RetryTransientMiddleware::new_with_policy(retry_policy),
319 |req: &reqwest::Request| req.try_clone().is_some(),
320 ))
321 .build();
322
323 let host = RootDefaultServer::default().default_url().to_string();
324
325 Client {
326 host,
327 host_override: None,
328 client_id: client_id.to_string(),
329 client_secret: client_secret.to_string(),
330 redirect_uri: redirect_uri.to_string(),
331 token: Arc::new(RwLock::new(InnerToken {
332 access_token: token.to_string(),
333 refresh_token: refresh_token.to_string(),
334 expires_at: None,
335 })),
336
337 auto_refresh: false,
338 client,
339 }
340 }
341 Err(e) => panic!("creating reqwest client failed: {:?}", e),
342 }
343 }
344
345 pub fn set_auto_access_token_refresh(&mut self, enabled: bool) -> &mut Self {
347 self.auto_refresh = enabled;
348 self
349 }
350
351 pub async fn set_expires_at(&self, expires_at: Option<Instant>) -> &Self {
357 self.token.write().await.expires_at = expires_at;
358 self
359 }
360
361 pub async fn expires_at(&self) -> Option<Instant> {
364 self.token.read().await.expires_at
365 }
366
367 pub async fn set_expires_in(&self, expires_in: i64) -> &Self {
370 self.token.write().await.expires_at = Self::compute_expires_at(expires_in);
371 self
372 }
373
374 pub async fn expires_in(&self) -> Option<Duration> {
377 self.token
378 .read()
379 .await
380 .expires_at
381 .map(|i| i.duration_since(Instant::now()))
382 }
383
384 pub async fn is_expired(&self) -> Option<bool> {
387 self.token
388 .read()
389 .await
390 .expires_at
391 .map(|expiration| expiration <= Instant::now())
392 }
393
394 fn compute_expires_at(expires_in: i64) -> Option<Instant> {
395 let seconds_valid = expires_in
396 .try_into()
397 .ok()
398 .map(Duration::from_secs)
399 .and_then(|dur| dur.checked_sub(REFRESH_THRESHOLD))
400 .or_else(|| Some(Duration::from_secs(0)));
401
402 seconds_valid.map(|seconds_valid| Instant::now().add(seconds_valid))
403 }
404
405 pub fn with_host_override<H>(&mut self, host: H) -> &mut Self
407 where
408 H: ToString,
409 {
410 self.host_override = Some(host.to_string());
411 self
412 }
413
414 pub fn remove_host_override(&mut self) -> &mut Self {
416 self.host_override = None;
417 self
418 }
419
420 pub fn get_host_override(&self) -> Option<&str> {
421 self.host_override.as_deref()
422 }
423
424 pub(crate) fn url(&self, path: &str, host: Option<&str>) -> String {
425 format!(
426 "{}{}",
427 self.get_host_override()
428 .or(host)
429 .unwrap_or(self.host.as_str()),
430 path
431 )
432 }
433
434 pub fn new_from_env<T, R>(token: T, refresh_token: R) -> Self
445 where
446 T: ToString,
447 R: ToString,
448 {
449 let client_id = env::var("RAMP_CLIENT_ID").expect("must set RAMP_CLIENT_ID");
450 let client_secret = env::var("RAMP_CLIENT_SECRET").expect("must set RAMP_CLIENT_SECRET");
451 let redirect_uri = env::var("RAMP_REDIRECT_URI").expect("must set RAMP_REDIRECT_URI");
452
453 Client::new(client_id, client_secret, redirect_uri, token, refresh_token)
454 }
455
456 pub fn user_consent_url(&self, scopes: &[String]) -> String {
459 let state = uuid::Uuid::new_v4();
460
461 let url = format!(
462 "{}?client_id={}&response_type=code&redirect_uri={}&state={}",
463 USER_CONSENT_ENDPOINT, self.client_id, self.redirect_uri, state
464 );
465
466 if scopes.is_empty() {
467 return url;
468 }
469
470 format!("{}&scope={}", url, scopes.join(" "))
472 }
473
474 pub async fn refresh_access_token(&self) -> ClientResult<AccessToken> {
477 let response = {
478 let refresh_token = &self.token.read().await.refresh_token;
479
480 if refresh_token.is_empty() {
481 return Err(ClientError::EmptyRefreshToken);
482 }
483
484 let mut headers = reqwest::header::HeaderMap::new();
485 headers.append(
486 reqwest::header::ACCEPT,
487 reqwest::header::HeaderValue::from_static("application/json"),
488 );
489
490 let params = [
491 ("grant_type", "refresh_token"),
492 ("refresh_token", refresh_token),
493 ("client_id", &self.client_id),
494 ("client_secret", &self.client_secret),
495 ("redirect_uri", &self.redirect_uri),
496 ];
497 let client = reqwest::Client::new();
498 client
499 .post(TOKEN_ENDPOINT)
500 .headers(headers)
501 .form(¶ms)
502 .basic_auth(&self.client_id, Some(&self.client_secret))
503 .send()
504 .await?
505 };
506
507 let t: AccessToken = response.json().await?;
509
510 let refresh_token = self.token.read().await.refresh_token.clone();
511
512 *self.token.write().await = InnerToken {
513 access_token: t.access_token.clone(),
514 refresh_token,
515 expires_at: Self::compute_expires_at(t.expires_in),
516 };
517
518 Ok(t)
519 }
520
521 pub async fn get_access_token(&mut self, code: &str, state: &str) -> ClientResult<AccessToken> {
524 let mut headers = reqwest::header::HeaderMap::new();
525 headers.append(
526 reqwest::header::ACCEPT,
527 reqwest::header::HeaderValue::from_static("application/json"),
528 );
529
530 let params = [
531 ("grant_type", "authorization_code"),
532 ("code", code),
533 ("client_id", &self.client_id),
534 ("client_secret", &self.client_secret),
535 ("redirect_uri", &self.redirect_uri),
536 ("state", state),
537 ];
538 let client = reqwest::Client::new();
539 let resp = client
540 .post(TOKEN_ENDPOINT)
541 .headers(headers)
542 .form(¶ms)
543 .basic_auth(&self.client_id, Some(&self.client_secret))
544 .send()
545 .await?;
546
547 let t: AccessToken = resp.json().await?;
549
550 *self.token.write().await = InnerToken {
551 access_token: t.access_token.clone(),
552 refresh_token: t.refresh_token.clone(),
553 expires_at: Self::compute_expires_at(t.expires_in),
554 };
555
556 Ok(t)
557 }
558
559 async fn url_and_auth(&self, uri: &str) -> ClientResult<(reqwest::Url, Option<String>)> {
560 let parsed_url = uri.parse::<reqwest::Url>()?;
561
562 let auth = format!("Bearer {}", self.token.read().await.access_token);
563 Ok((parsed_url, Some(auth)))
564 }
565
566 async fn make_request(
567 &self,
568 method: &reqwest::Method,
569 uri: &str,
570 message: Message,
571 ) -> ClientResult<reqwest::Request> {
572 let (url, auth) = self.url_and_auth(uri).await?;
573
574 let instance = <&Client>::clone(&self);
575
576 let mut req = instance.client.request(method.clone(), url);
577
578 req = req.header(
580 reqwest::header::ACCEPT,
581 reqwest::header::HeaderValue::from_static("application/json"),
582 );
583
584 if let Some(content_type) = &message.content_type {
585 req = req.header(
586 reqwest::header::CONTENT_TYPE,
587 reqwest::header::HeaderValue::from_str(content_type).unwrap(),
588 );
589 } else {
590 req = req.header(
591 reqwest::header::CONTENT_TYPE,
592 reqwest::header::HeaderValue::from_static("application/json"),
593 );
594 }
595
596 if let Some(auth_str) = auth {
597 req = req.header(http::header::AUTHORIZATION, &*auth_str);
598 }
599
600 if let Some(body) = message.body {
601 req = req.body(body);
602 }
603
604 Ok(req.build()?)
605 }
606
607 async fn request_raw(
608 &self,
609 method: reqwest::Method,
610 uri: &str,
611 message: Message,
612 ) -> ClientResult<reqwest::Response> {
613 if self.auto_refresh {
614 let expired = self.is_expired().await;
615
616 match expired {
617 Some(true) => {
620 self.refresh_access_token().await?;
621 }
622
623 Some(false) => (),
628
629 None => (),
637 }
638 }
639
640 let req = self.make_request(&method, uri, message).await?;
641 let resp = self.client.execute(req).await?;
642
643 Ok(resp)
644 }
645
646 async fn request<Out>(
647 &self,
648 method: reqwest::Method,
649 uri: &str,
650 message: Message,
651 ) -> ClientResult<crate::Response<Out>>
652 where
653 Out: serde::de::DeserializeOwned + 'static + Send,
654 {
655 let response = self.request_raw(method, uri, message).await?;
656
657 let status = response.status();
658 let headers = response.headers().clone();
659
660 let response_body = response.bytes().await?;
661
662 if status.is_success() {
663 log::debug!("Received successful response. Read payload.");
664 let parsed_response = if status == http::StatusCode::NO_CONTENT
665 || std::any::TypeId::of::<Out>() == std::any::TypeId::of::<()>()
666 {
667 serde_json::from_str("null")?
668 } else {
669 serde_json::from_slice::<Out>(&response_body)?
670 };
671 Ok(crate::Response::new(status, headers, parsed_response))
672 } else {
673 let error = if response_body.is_empty() {
674 ClientError::HttpError {
675 status,
676 headers,
677 error: "empty response".into(),
678 }
679 } else {
680 ClientError::HttpError {
681 status,
682 headers,
683 error: String::from_utf8_lossy(&response_body).into(),
684 }
685 };
686
687 Err(error)
688 }
689 }
690
691 async fn request_with_links<Out>(
692 &self,
693 method: http::Method,
694 uri: &str,
695 message: Message,
696 ) -> ClientResult<(Option<crate::utils::NextLink>, crate::Response<Out>)>
697 where
698 Out: serde::de::DeserializeOwned + 'static + Send,
699 {
700 let response = self.request_raw(method, uri, message).await?;
701
702 let status = response.status();
703 let headers = response.headers().clone();
704 let link = response
705 .headers()
706 .get(http::header::LINK)
707 .and_then(|l| l.to_str().ok())
708 .and_then(|l| parse_link_header::parse(l).ok())
709 .as_ref()
710 .and_then(crate::utils::next_link);
711
712 let response_body = response.bytes().await?;
713
714 if status.is_success() {
715 log::debug!("Received successful response. Read payload.");
716
717 let parsed_response = if status == http::StatusCode::NO_CONTENT
718 || std::any::TypeId::of::<Out>() == std::any::TypeId::of::<()>()
719 {
720 serde_json::from_str("null")?
721 } else {
722 serde_json::from_slice::<Out>(&response_body)?
723 };
724 Ok((link, crate::Response::new(status, headers, parsed_response)))
725 } else {
726 let error = if response_body.is_empty() {
727 ClientError::HttpError {
728 status,
729 headers,
730 error: "empty response".into(),
731 }
732 } else {
733 ClientError::HttpError {
734 status,
735 headers,
736 error: String::from_utf8_lossy(&response_body).into(),
737 }
738 };
739 Err(error)
740 }
741 }
742
743 #[allow(dead_code)]
745 async fn post_form<Out>(
746 &self,
747 uri: &str,
748 form: reqwest::multipart::Form,
749 ) -> ClientResult<crate::Response<Out>>
750 where
751 Out: serde::de::DeserializeOwned + 'static + Send,
752 {
753 let (url, auth) = self.url_and_auth(uri).await?;
754
755 let instance = <&Client>::clone(&self);
756
757 let mut req = instance.client.request(http::Method::POST, url);
758
759 req = req.header(
761 reqwest::header::ACCEPT,
762 reqwest::header::HeaderValue::from_static("application/json"),
763 );
764
765 if let Some(auth_str) = auth {
766 req = req.header(http::header::AUTHORIZATION, &*auth_str);
767 }
768
769 req = req.multipart(form);
770
771 let response = req.send().await?;
772
773 let status = response.status();
774 let headers = response.headers().clone();
775
776 let response_body = response.bytes().await?;
777
778 if status.is_success() {
779 log::debug!("Received successful response. Read payload.");
780 let parsed_response = if status == http::StatusCode::NO_CONTENT
781 || std::any::TypeId::of::<Out>() == std::any::TypeId::of::<()>()
782 {
783 serde_json::from_str("null")?
784 } else if std::any::TypeId::of::<Out>() == std::any::TypeId::of::<String>() {
785 let s = String::from_utf8(response_body.to_vec())?;
787 serde_json::from_value(serde_json::json!(&s))?
788 } else {
789 serde_json::from_slice::<Out>(&response_body)?
790 };
791 Ok(crate::Response::new(status, headers, parsed_response))
792 } else {
793 let error = if response_body.is_empty() {
794 ClientError::HttpError {
795 status,
796 headers,
797 error: "empty response".into(),
798 }
799 } else {
800 ClientError::HttpError {
801 status,
802 headers,
803 error: String::from_utf8_lossy(&response_body).into(),
804 }
805 };
806
807 Err(error)
808 }
809 }
810
811 #[allow(dead_code)]
813 async fn request_with_accept_mime<Out>(
814 &self,
815 method: reqwest::Method,
816 uri: &str,
817 accept_mime_type: &str,
818 ) -> ClientResult<crate::Response<Out>>
819 where
820 Out: serde::de::DeserializeOwned + 'static + Send,
821 {
822 let (url, auth) = self.url_and_auth(uri).await?;
823
824 let instance = <&Client>::clone(&self);
825
826 let mut req = instance.client.request(method, url);
827
828 req = req.header(
830 reqwest::header::ACCEPT,
831 reqwest::header::HeaderValue::from_str(accept_mime_type)?,
832 );
833
834 if let Some(auth_str) = auth {
835 req = req.header(http::header::AUTHORIZATION, &*auth_str);
836 }
837
838 let response = req.send().await?;
839
840 let status = response.status();
841 let headers = response.headers().clone();
842
843 let response_body = response.bytes().await?;
844
845 if status.is_success() {
846 log::debug!("Received successful response. Read payload.");
847 let parsed_response = if status == http::StatusCode::NO_CONTENT
848 || std::any::TypeId::of::<Out>() == std::any::TypeId::of::<()>()
849 {
850 serde_json::from_str("null")?
851 } else if std::any::TypeId::of::<Out>() == std::any::TypeId::of::<String>() {
852 let s = String::from_utf8(response_body.to_vec())?;
854 serde_json::from_value(serde_json::json!(&s))?
855 } else {
856 serde_json::from_slice::<Out>(&response_body)?
857 };
858 Ok(crate::Response::new(status, headers, parsed_response))
859 } else {
860 let error = if response_body.is_empty() {
861 ClientError::HttpError {
862 status,
863 headers,
864 error: "empty response".into(),
865 }
866 } else {
867 ClientError::HttpError {
868 status,
869 headers,
870 error: String::from_utf8_lossy(&response_body).into(),
871 }
872 };
873
874 Err(error)
875 }
876 }
877
878 #[allow(dead_code)]
880 async fn request_with_mime<Out>(
881 &self,
882 method: reqwest::Method,
883 uri: &str,
884 content: &[u8],
885 mime_type: &str,
886 ) -> ClientResult<crate::Response<Out>>
887 where
888 Out: serde::de::DeserializeOwned + 'static + Send,
889 {
890 let (url, auth) = self.url_and_auth(uri).await?;
891
892 let instance = <&Client>::clone(&self);
893
894 let mut req = instance.client.request(method, url);
895
896 req = req.header(
898 reqwest::header::ACCEPT,
899 reqwest::header::HeaderValue::from_static("application/json"),
900 );
901 req = req.header(
902 reqwest::header::CONTENT_TYPE,
903 reqwest::header::HeaderValue::from_bytes(mime_type.as_bytes()).unwrap(),
904 );
905 req = req.header(
907 reqwest::header::HeaderName::from_static("x-upload-content-type"),
908 reqwest::header::HeaderValue::from_static("application/octet-stream"),
909 );
910 req = req.header(
911 reqwest::header::HeaderName::from_static("x-upload-content-length"),
912 reqwest::header::HeaderValue::from_bytes(format!("{}", content.len()).as_bytes())
913 .unwrap(),
914 );
915
916 if let Some(auth_str) = auth {
917 req = req.header(http::header::AUTHORIZATION, &*auth_str);
918 }
919
920 if content.len() > 1 {
921 let b = bytes::Bytes::copy_from_slice(content);
922 req = req.body(b);
924 }
925
926 let response = req.send().await?;
927
928 let status = response.status();
929 let headers = response.headers().clone();
930
931 let response_body = response.bytes().await?;
932
933 if status.is_success() {
934 log::debug!("Received successful response. Read payload.");
935 let parsed_response = if status == http::StatusCode::NO_CONTENT
936 || std::any::TypeId::of::<Out>() == std::any::TypeId::of::<()>()
937 {
938 serde_json::from_str("null")?
939 } else {
940 serde_json::from_slice::<Out>(&response_body)?
941 };
942 Ok(crate::Response::new(status, headers, parsed_response))
943 } else {
944 let error = if response_body.is_empty() {
945 ClientError::HttpError {
946 status,
947 headers,
948 error: "empty response".into(),
949 }
950 } else {
951 ClientError::HttpError {
952 status,
953 headers,
954 error: String::from_utf8_lossy(&response_body).into(),
955 }
956 };
957
958 Err(error)
959 }
960 }
961
962 async fn request_entity<D>(
963 &self,
964 method: http::Method,
965 uri: &str,
966 message: Message,
967 ) -> ClientResult<crate::Response<D>>
968 where
969 D: serde::de::DeserializeOwned + 'static + Send,
970 {
971 let r = self.request(method, uri, message).await?;
972 Ok(r)
973 }
974
975 #[allow(dead_code)]
976 async fn get<D>(&self, uri: &str, message: Message) -> ClientResult<crate::Response<D>>
977 where
978 D: serde::de::DeserializeOwned + 'static + Send,
979 {
980 self.request_entity(http::Method::GET, uri, message).await
981 }
982
983 #[allow(dead_code)]
984 async fn get_all_pages<D>(&self, uri: &str, _message: Message) -> ClientResult<Response<Vec<D>>>
985 where
986 D: serde::de::DeserializeOwned + 'static + Send,
987 {
988 self.unfold(uri).await
990 }
991
992 #[allow(dead_code)]
994 async fn unfold<D>(&self, uri: &str) -> ClientResult<crate::Response<Vec<D>>>
995 where
996 D: serde::de::DeserializeOwned + 'static + Send,
997 {
998 let mut global_items = Vec::new();
999 let (new_link, mut response) = self.get_pages(uri).await?;
1000 let mut link = new_link;
1001 while !response.body.is_empty() {
1002 global_items.append(&mut response.body);
1003 if let Some(url) = &link {
1005 let url = reqwest::Url::parse(&url.0)?;
1006 let (new_link, new_response) = self.get_pages_url(&url).await?;
1007 link = new_link;
1008 response = new_response;
1009 }
1010 }
1011
1012 Ok(Response::new(
1013 response.status,
1014 response.headers,
1015 global_items,
1016 ))
1017 }
1018
1019 #[allow(dead_code)]
1020 async fn get_pages<D>(
1021 &self,
1022 uri: &str,
1023 ) -> ClientResult<(Option<crate::utils::NextLink>, crate::Response<Vec<D>>)>
1024 where
1025 D: serde::de::DeserializeOwned + 'static + Send,
1026 {
1027 self.request_with_links(http::Method::GET, uri, Message::default())
1028 .await
1029 }
1030
1031 #[allow(dead_code)]
1032 async fn get_pages_url<D>(
1033 &self,
1034 url: &reqwest::Url,
1035 ) -> ClientResult<(Option<crate::utils::NextLink>, crate::Response<Vec<D>>)>
1036 where
1037 D: serde::de::DeserializeOwned + 'static + Send,
1038 {
1039 self.request_with_links(http::Method::GET, url.as_str(), Message::default())
1040 .await
1041 }
1042
1043 #[allow(dead_code)]
1044 async fn post<D>(&self, uri: &str, message: Message) -> ClientResult<crate::Response<D>>
1045 where
1046 D: serde::de::DeserializeOwned + 'static + Send,
1047 {
1048 self.request_entity(http::Method::POST, uri, message).await
1049 }
1050
1051 #[allow(dead_code)]
1052 async fn patch<D>(&self, uri: &str, message: Message) -> ClientResult<crate::Response<D>>
1053 where
1054 D: serde::de::DeserializeOwned + 'static + Send,
1055 {
1056 self.request_entity(http::Method::PATCH, uri, message).await
1057 }
1058
1059 #[allow(dead_code)]
1060 async fn put<D>(&self, uri: &str, message: Message) -> ClientResult<crate::Response<D>>
1061 where
1062 D: serde::de::DeserializeOwned + 'static + Send,
1063 {
1064 self.request_entity(http::Method::PUT, uri, message).await
1065 }
1066
1067 #[allow(dead_code)]
1068 async fn delete<D>(&self, uri: &str, message: Message) -> ClientResult<crate::Response<D>>
1069 where
1070 D: serde::de::DeserializeOwned + 'static + Send,
1071 {
1072 self.request_entity(http::Method::DELETE, uri, message)
1073 .await
1074 }
1075
1076 pub fn auths(&self) -> auths::Auths {
1077 auths::Auths::new(self.clone())
1078 }
1079
1080 pub fn businesses(&self) -> businesses::Businesses {
1081 businesses::Businesses::new(self.clone())
1082 }
1083
1084 pub fn card_programs(&self) -> card_programs::CardPrograms {
1085 card_programs::CardPrograms::new(self.clone())
1086 }
1087
1088 pub fn cards(&self) -> cards::Cards {
1089 cards::Cards::new(self.clone())
1090 }
1091
1092 pub fn custom_ids(&self) -> custom_ids::CustomIds {
1093 custom_ids::CustomIds::new(self.clone())
1094 }
1095
1096 pub fn departments(&self) -> departments::Departments {
1097 departments::Departments::new(self.clone())
1098 }
1099
1100 pub fn locations(&self) -> locations::Locations {
1101 locations::Locations::new(self.clone())
1102 }
1103
1104 pub fn receipts(&self) -> receipts::Receipts {
1105 receipts::Receipts::new(self.clone())
1106 }
1107
1108 pub fn reimbursements(&self) -> reimbursements::Reimbursements {
1109 reimbursements::Reimbursements::new(self.clone())
1110 }
1111
1112 pub fn transactions(&self) -> transactions::Transactions {
1113 transactions::Transactions::new(self.clone())
1114 }
1115
1116 pub fn users(&self) -> users::Users {
1117 users::Users::new(self.clone())
1118 }
1119}