use serde::{Deserialize, Serialize};
use crate::client::Client;
use crate::errors::Result;
#[derive(Deserialize, Debug, Clone)]
pub struct Institution {
pub institution_id: String,
pub name: String,
pub products: Vec<String>,
pub country_codes: Vec<String>,
pub url: Option<String>,
pub primary_color: Option<String>,
pub logo: Option<String>,
pub routing_numbers: Option<Vec<String>>,
pub oauth: bool,
}
#[derive(Serialize)]
struct GetInstitutionsRequest<'a> {
client_id: &'a str,
secret: &'a str,
count: i32,
offset: i32,
country_codes: &'a [&'a str],
#[serde(skip_serializing_if = "Option::is_none")]
options: Option<GetInstitutionsOptions>,
}
#[derive(Serialize)]
pub struct GetInstitutionsOptions {
#[serde(skip_serializing_if = "Vec::is_empty")]
pub products: Vec<String>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub routing_numbers: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub oauth: Option<bool>,
pub include_optional_metadata: bool,
}
impl Default for GetInstitutionsOptions {
fn default() -> Self {
Self {
products: Vec::new(),
include_optional_metadata: false,
oauth: None,
routing_numbers: Vec::new(),
}
}
}
#[derive(Deserialize, Debug)]
pub struct GetInstitutionsResponse {
pub request_id: String,
pub institutions: Vec<Institution>,
pub total: i32,
}
#[derive(Serialize)]
struct GetInstitutionByIdRequest<'a> {
client_id: &'a str,
secret: &'a str,
institution_id: &'a str,
country_codes: &'a [&'a str],
#[serde(skip_serializing_if = "Option::is_none")]
options: Option<GetInstitutionByIdOptions>,
}
#[derive(Serialize)]
pub struct GetInstitutionByIdOptions {
pub include_optional_metadata: bool,
pub include_status: bool,
}
impl Default for GetInstitutionByIdOptions {
fn default() -> Self {
Self {
include_optional_metadata: false,
include_status: false,
}
}
}
#[derive(Deserialize, Debug)]
pub struct GetInstitutionByIdResponse {
request_id: String,
institution: Institution,
}
#[derive(Serialize)]
struct SearchInstitutionsRequest<'a> {
client_id: &'a str,
secret: &'a str,
query: &'a str,
country_codes: &'a [&'a str],
products: &'a [&'a str],
#[serde(skip_serializing_if = "Option::is_none")]
options: Option<SearchInstitutionsOptions>,
}
#[derive(Serialize)]
pub struct SearchInstitutionsOptions {
include_optional_metadata: bool,
#[serde(skip_serializing_if = "Option::is_none")]
oauth: Option<bool>,
}
impl Default for SearchInstitutionsOptions {
fn default() -> Self {
Self {
include_optional_metadata: false,
oauth: None,
}
}
}
#[derive(Deserialize, Debug)]
pub struct SearchInstitutionsResponse {
request_id: String,
institutions: Vec<Institution>,
}
impl Client {
pub async fn get_institution_by_id(
&self,
institution_id: &str,
country_codes: &[&str],
options: Option<GetInstitutionByIdOptions>,
) -> Result<GetInstitutionByIdResponse> {
self.send_request(
"institutions/get_by_id",
&GetInstitutionByIdRequest {
client_id: &self.client_id,
secret: &self.secret,
institution_id,
country_codes,
options,
},
)
.await
}
pub async fn get_institutions(
&self,
count: i32,
offset: i32,
country_codes: &[&str],
options: Option<GetInstitutionsOptions>,
) -> Result<GetInstitutionsResponse> {
self.send_request(
"institutions/get",
&GetInstitutionsRequest {
client_id: &self.client_id,
secret: &self.secret,
count,
offset,
country_codes,
options,
},
)
.await
}
pub async fn search_institutions(
&self,
query: &str,
products: &[&str],
country_codes: &[&str],
options: Option<SearchInstitutionsOptions>,
) -> Result<SearchInstitutionsResponse> {
self.send_request(
"institutions/search",
&SearchInstitutionsRequest {
client_id: &self.client_id,
secret: &self.secret,
query,
products,
country_codes,
options,
},
)
.await
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::client::tests::{get_test_client, SANDBOX_INSTITUTION_QUERY};
#[tokio::test]
async fn test_get_institutions() {
let client = get_test_client();
let resp = client.get_institutions(2, 1, &["US"], None).await.unwrap();
assert_eq!(resp.institutions.len(), 2);
let resp = client
.get_institutions(
2,
1,
&["US"],
Some(GetInstitutionsOptions {
include_optional_metadata: true,
..Default::default()
}),
)
.await
.unwrap();
assert_eq!(resp.institutions.len(), 2);
for institution in &resp.institutions {
assert_eq!(institution.url.is_some(), true);
assert_ne!(institution.url.as_ref().unwrap().len(), 0);
}
let resp = client
.get_institutions(
2,
1,
&["GB"],
Some(GetInstitutionsOptions {
oauth: Some(true),
..Default::default()
}),
)
.await
.unwrap();
assert_eq!(resp.institutions.len(), 2);
let resp = client.get_institutions(2, 1, &[], None).await;
assert_eq!(resp.is_err(), true);
let resp = client
.get_institutions(
1,
0,
&["US"],
Some(GetInstitutionsOptions {
routing_numbers: vec!["021200339".to_string(), "052001633".to_string()],
..Default::default()
}),
)
.await
.unwrap();
assert_eq!(resp.institutions.len(), 1);
}
#[tokio::test]
async fn test_search_institutions() {
let client = get_test_client();
let resp = client
.search_institutions(SANDBOX_INSTITUTION_QUERY, &["transactions"], &["US"], None)
.await
.unwrap();
assert!(resp.institutions.len() > 0);
let resp = client
.search_institutions(
SANDBOX_INSTITUTION_QUERY,
&["transactions"],
&["US"],
Some(SearchInstitutionsOptions {
include_optional_metadata: true,
..Default::default()
}),
)
.await
.unwrap();
assert!(resp.institutions.len() > 0);
for institution in &resp.institutions {
assert_eq!(institution.url.is_some(), true);
assert_ne!(institution.url.as_ref().unwrap().len(), 0);
}
let resp = client
.search_institutions(SANDBOX_INSTITUTION_QUERY, &["transactions"], &[""], None)
.await;
assert_eq!(resp.is_err(), true);
}
#[tokio::test]
async fn test_get_institutions_by_id() {
let client = get_test_client();
let resp = client
.get_institution_by_id("ins_12", &["US"], None)
.await
.unwrap();
assert!(resp.institution.products.len() > 0);
let resp = client
.get_institution_by_id(
"ins_12",
&["US"],
Some(GetInstitutionByIdOptions {
include_optional_metadata: true,
..Default::default()
}),
)
.await
.unwrap();
assert!(resp.institution.products.len() > 0);
assert_eq!(resp.institution.url.is_some(), true);
assert_ne!(resp.institution.url.as_ref().unwrap().len(), 0);
let resp = client.get_institution_by_id("ins_12", &[], None).await;
assert_eq!(resp.is_err(), true);
}
}