diff --git a/Cargo.toml b/Cargo.toml index ab916b7..fef08e7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "up-api" -version = "0.1.0" +version = "0.1.1" edition = "2021" description = "A convenient and easy to use wrapper for the Up Bank API." license = "MIT OR Apache-2.0" diff --git a/README.md b/README.md index 59eadd7..e262d69 100644 --- a/README.md +++ b/README.md @@ -4,11 +4,20 @@ A convenient and easy to use wrapper for the [Up Bank API](https://developer.up. ## Example -The following example shows the calculation of the sum of all transactions after a given date (up to the page limit). +The following example shows the calculation of the sum of all earnings (transactions with positive value) since a given date: ``` use up_api::v1::Client; -use up_api::v1::transactions::ListTransactionsOptions; +use up_api::v1::transactions::{ListTransactionsOptions, TransactionResource}; + +fn sum_earnings(transactions : &Vec) -> f32 { + transactions + .iter() + .map(|t| &t.attributes.amount.value) + .map(|v| v.parse::().unwrap()) + .filter(|a| a > &0.0) + .sum() +} #[tokio::main] async fn main() { @@ -17,19 +26,20 @@ async fn main() { let client = Client::new(token.to_string()); let mut options = ListTransactionsOptions::default(); - options.filter_since("2020-01-01T01:02:03Z".to_string()); + options.filter_since("2022-01-01T00:00:00Z".to_string()); options.page_size(100); - let transactions = client.list_transactions(&options).await.unwrap(); + let mut transactions = client.list_transactions(&options).await.unwrap(); - let total : f32 = - transactions - .data - .into_iter() - .map(|t| t.attributes.amount.value) - .map(|v| v.parse::().unwrap()) - .filter(|a| a > &0.0) - .sum(); + let mut total = sum_earnings(&transactions.data); + + while let Some(next_page) = transactions.next(&client).await { + let next_page = next_page.unwrap(); + + total = total + sum_earnings(&next_page.data); + + transactions = next_page; + } println!("{}", total); } @@ -38,4 +48,3 @@ async fn main() { ## Planned Features - Currently this API wrapper supports all of the `v1` Up API endpoints except [webhooks](https://developer.up.com.au/#webhooks). This is planned for a (hopefully soon) future release. -- Functions `prev` and `next` on the reslts of paginated endpoints are planned to make moving between pages significantly easier. diff --git a/src/v1/accounts.rs b/src/v1/accounts.rs index c328a0e..31459d5 100644 --- a/src/v1/accounts.rs +++ b/src/v1/accounts.rs @@ -194,3 +194,7 @@ impl Client { } } } + +// ----------------- Page Navigation ----------------- + +implement_pagination_v1!(ListAccountsResponse); diff --git a/src/v1/macros.rs b/src/v1/macros.rs new file mode 100644 index 0000000..075f9b9 --- /dev/null +++ b/src/v1/macros.rs @@ -0,0 +1,59 @@ +macro_rules! implement_pagination_v1 { + ($t:ty) => { + impl $t { + async fn follow_link(client : &Client, url : &str) -> Result { + let res = reqwest::Client::new() + .get(url) + .header("Authorization", client.auth_header()) + .send() + .await + .map_err(error::Error::Request)?; + + match res.status() { + reqwest::StatusCode::OK => { + let body = res.text().await.map_err(error::Error::BodyRead)?; + let response : Self = serde_json::from_str(&body).map_err(error::Error::Json)?; + + Ok(response) + }, + _ => { + let body = res.text().await.map_err(error::Error::BodyRead)?; + let error : error::ErrorResponse = serde_json::from_str(&body).map_err(error::Error::Json)?; + + Err(error::Error::Api(error)) + } + } + } + + /// Follows the link to the next page, returns None of the next page does not exist. + pub async fn next(&self, client : &Client) -> Option> { + match + self + .links + .next + .as_ref() + .map(|url| Self::follow_link(client, &url)) { + + Some(data) => Some(data.await), + None => None, + } + } + + /// Follows the link to the previous page, returns None of the previous page does not exist. + pub async fn prev(&self, client : &Client) -> Option> { + match + self + .links + .prev + .as_ref() + .map(|url| Self::follow_link(client, &url)) { + + Some(data) => Some(data.await), + None => None, + } + } + } + } +} + + diff --git a/src/v1/mod.rs b/src/v1/mod.rs index 23dea07..7c2ce3c 100644 --- a/src/v1/mod.rs +++ b/src/v1/mod.rs @@ -1,3 +1,6 @@ +#[macro_use] +mod macros; + /// Error types and trait implementations. pub mod error; /// Types for modelling and interacting with [accounts](https://developer.up.com.au/#accounts). @@ -13,6 +16,7 @@ pub mod utilities; /// Types which are stardized (and named) across many resources. pub mod standard; + static BASE_URL : &str = "https://api.up.com.au/api/v1"; /// A client for interacting with the Up API. diff --git a/src/v1/tags.rs b/src/v1/tags.rs index ba047a6..1bd0b3a 100644 --- a/src/v1/tags.rs +++ b/src/v1/tags.rs @@ -194,3 +194,6 @@ impl Client { } } +// ----------------- Page Navigation ----------------- + +implement_pagination_v1!(ListTagsResponse); diff --git a/src/v1/transactions.rs b/src/v1/transactions.rs index 7f91cd2..4ecca11 100644 --- a/src/v1/transactions.rs +++ b/src/v1/transactions.rs @@ -378,3 +378,8 @@ impl Client { } } } + + +// ----------------- Page Navigation ----------------- + +implement_pagination_v1!(ListTransactionsResponse);