date filters strongly typed; formatting

This commit is contained in:
Aaron Manning
2025-02-13 15:27:44 +11:00
parent c8b91f66db
commit 9eeee510a5
11 changed files with 873 additions and 447 deletions
+74 -46
View File
@@ -7,71 +7,76 @@ use serde::Deserialize;
#[derive(Deserialize, Debug)]
pub struct ListAccountsResponse {
/// The list of accounts returned in this response.
pub data : Vec<AccountResource>,
pub links : ResponseLinks,
pub data: Vec<AccountResource>,
pub links: ResponseLinks,
}
#[derive(Deserialize, Debug)]
pub struct GetAccountResponse {
/// The account returned in this response.
pub data : AccountResource,
pub data: AccountResource,
}
#[derive(Deserialize, Debug)]
pub struct AccountResource {
/// The type of this resource: `accounts`.
pub r#type : String,
pub r#type: String,
/// The unique identifier for this account.
pub id : String,
pub attributes : Attributes,
pub relationships : Relationships,
pub links : Option<AccountResourceLinks>,
pub id: String,
pub attributes: Attributes,
pub relationships: Relationships,
pub links: Option<AccountResourceLinks>,
}
#[derive(Deserialize, Debug)]
pub struct AccountResourceLinks {
/// The canonical link to this resource within the API.
#[serde(rename = "self")]
pub this : Option<String>,
pub this: Option<String>,
}
#[derive(Deserialize, Debug)]
pub struct ResponseLinks {
/// The link to the previous page in the results. If this value is `None` there is no previous page.
pub prev : Option<String>,
/// The link to the next page in the results. If this value is `None` there is no next page.
pub next : Option<String>,
/// The link to the previous page in the results. If this value is `None`
/// there is no previous page.
pub prev: Option<String>,
/// The link to the next page in the results. If this value is `None` there
/// is no next page.
pub next: Option<String>,
}
#[derive(Deserialize, Debug)]
pub struct Relationships {
pub transactions : Transactions,
pub transactions: Transactions,
}
#[derive(Deserialize, Debug)]
pub struct Transactions {
pub links : Option<TransactionLinks>,
pub links: Option<TransactionLinks>,
}
#[derive(Deserialize, Debug)]
pub struct TransactionLinks {
/// The link to retrieve the related resource(s) in this relationship.
pub related : String,
pub related: String,
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct Attributes {
/// The name associated with the account in the Up application.
pub display_name : String,
/// The bank account type of this account. Possible values: SAVER, TRANSACTIONAL
pub account_type : AccountType,
/// The ownership structure for this account. Possible values: INDIVIDUAL, JOINT
pub ownership_type : OwnershipType,
/// The available balance of the account, taking into account any amounts that are currently on hold.
pub balance : standard::MoneyObject,
pub display_name: String,
/// The bank account type of this account. Possible values: SAVER,
/// TRANSACTIONAL
pub account_type: AccountType,
/// The ownership structure for this account. Possible values: INDIVIDUAL,
/// JOINT
pub ownership_type: OwnershipType,
/// The available balance of the account, taking into account any amounts
/// that are currently on hold.
pub balance: standard::MoneyObject,
/// The date-time at which this account was first opened.
pub created_at : String,
pub created_at: String,
}
#[derive(Deserialize, Debug)]
@@ -93,30 +98,32 @@ pub enum OwnershipType {
#[derive(Default)]
pub struct ListAccountsOptions {
/// The number of records to return in each page.
page_size : Option<u8>,
/// The type of account for which to return records. This can be used to filter Savers from spending accounts.
filter_account_type : Option<String>,
/// The account ownership structure for which to return records. This can be used to filter 2Up accounts from Up accounts.
filter_ownership_type : Option<String>,
page_size: Option<u8>,
/// The type of account for which to return records. This can be used to
/// filter Savers from spending accounts.
filter_account_type: Option<String>,
/// The account ownership structure for which to return records. This can
/// be used to filter 2Up accounts from Up accounts.
filter_ownership_type: Option<String>,
}
impl ListAccountsOptions {
/// Sets the page size.
pub fn page_size(&mut self, value : u8) {
pub fn page_size(&mut self, value: u8) {
self.page_size = Some(value);
}
/// Sets the account type filter value.
pub fn filter_account_type(&mut self, value : String) {
pub fn filter_account_type(&mut self, value: String) {
self.filter_account_type = Some(value);
}
/// Sets the ownership type filter value.
pub fn filter_ownership_type(&mut self, value : String) {
pub fn filter_ownership_type(&mut self, value: String) {
self.filter_ownership_type = Some(value);
}
fn add_params(&self, url : &mut reqwest::Url) {
fn add_params(&self, url: &mut reqwest::Url) {
let mut query = String::new();
if let Some(value) = &self.page_size {
@@ -130,14 +137,18 @@ impl ListAccountsOptions {
if !query.is_empty() {
query.push('&');
}
query.push_str(&format!("filter[accountType]={}", urlencoding::encode(value)));
query.push_str(
&format!("filter[accountType]={}", urlencoding::encode(value))
);
}
if let Some(value) = &self.filter_ownership_type {
if !query.is_empty() {
query.push('&');
}
query.push_str(&format!("filter[ownershipType]={}", urlencoding::encode(value)));
query.push_str(
&format!("filter[ownershipType]={}", urlencoding::encode(value))
);
}
if !query.is_empty() {
@@ -147,9 +158,16 @@ impl ListAccountsOptions {
}
impl Client {
/// Retrieve a paginated list of all accounts for the currently authenticated user. The returned list is paginated and can be scrolled by following the `prev` and `next` links where present.
pub async fn list_accounts(&self, options : &ListAccountsOptions) -> Result<ListAccountsResponse, error::Error> {
let mut url = reqwest::Url::parse(&format!("{}/accounts", BASE_URL)).map_err(error::Error::UrlParse)?;
/// Retrieve a paginated list of all accounts for the currently
/// authenticated user. The returned list is paginated and can be scrolled
/// by following the `prev` and `next` links where present.
pub async fn list_accounts(
&self,
options: &ListAccountsOptions,
) -> Result<ListAccountsResponse, error::Error> {
let mut url = reqwest::Url::parse(
&format!("{}/accounts", BASE_URL)
).map_err(error::Error::UrlParse)?;
options.add_params(&mut url);
let res = reqwest::Client::new()
@@ -162,13 +180,15 @@ impl Client {
match res.status() {
reqwest::StatusCode::OK => {
let body = res.text().await.map_err(error::Error::BodyRead)?;
let account_response : ListAccountsResponse = serde_json::from_str(&body).map_err(error::Error::Json)?;
let account_response: ListAccountsResponse =
serde_json::from_str(&body).map_err(error::Error::Json)?;
Ok(account_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)?;
let error: error::ErrorResponse =
serde_json::from_str(&body).map_err(error::Error::Json)?;
Err(error::Error::Api(error))
}
@@ -176,14 +196,20 @@ impl Client {
}
/// Retrieve a specific account by providing its unique identifier.
pub async fn get_account(&self, id : &str) -> Result<GetAccountResponse, error::Error> {
// This assertion is because without an ID the request is thought to be a request for
// many accounts, and therefore the error messages are very unclear.
pub async fn get_account(
&self,
id: &str,
) -> Result<GetAccountResponse, error::Error> {
// This assertion is because without an ID the request is thought to be
// a request for many accounts, and therefore the error messages are
// very unclear.
if id.is_empty() {
panic!("The provided account ID must not be empty.");
}
let url = reqwest::Url::parse(&format!("{}/accounts/{}", BASE_URL, id)).map_err(error::Error::UrlParse)?;
let url = reqwest::Url::parse(
&format!("{}/accounts/{}", BASE_URL, id)
).map_err(error::Error::UrlParse)?;
let res = reqwest::Client::new()
.get(url)
@@ -195,13 +221,15 @@ impl Client {
match res.status() {
reqwest::StatusCode::OK => {
let body = res.text().await.map_err(error::Error::BodyRead)?;
let account_response : GetAccountResponse = serde_json::from_str(&body).map_err(error::Error::Json)?;
let account_response: GetAccountResponse =
serde_json::from_str(&body).map_err(error::Error::Json)?;
Ok(account_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)?;
let error: error::ErrorResponse =
serde_json::from_str(&body).map_err(error::Error::Json)?;
Err(error::Error::Api(error))
}