Skip to content

Commit

Permalink
feat: allow endpoints to accept either strings or numbers in JSON
Browse files Browse the repository at this point in the history
  • Loading branch information
Ben Striegel authored and bstrie committed Sep 27, 2019
1 parent c18ec5a commit a5c9ce4
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 3 deletions.
48 changes: 46 additions & 2 deletions crates/interledger-api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ use interledger_service::{Account, AddressStore, IncomingService, OutgoingServic
use interledger_service_util::{BalanceStore, ExchangeRateStore};
use interledger_settlement::{SettlementAccount, SettlementStore};
use interledger_stream::StreamNotificationsStore;
use serde::{Deserialize, Serialize};
use serde::{de, Deserialize, Serialize};
use std::{
error::Error as StdError,
fmt::{self, Display},
net::SocketAddr,
str::FromStr,
};
use warp::{self, Filter};
mod routes;
Expand All @@ -21,6 +22,42 @@ use secrecy::SecretString;

pub(crate) mod http_retry;

// This enum and the following two functions are used to allow clients to send either
// numbers or strings and have them be properly deserialized into the appropriate
// integer type.
#[derive(Deserialize)]
#[serde(untagged)]
enum NumOrStr<T> {
Num(T),
Str(String),
}

pub fn number_or_string<'de, D, T>(deserializer: D) -> Result<T, D::Error>
where
D: de::Deserializer<'de>,
T: FromStr + Deserialize<'de>,
<T as FromStr>::Err: Display,
{
match NumOrStr::deserialize(deserializer)? {
NumOrStr::Num(n) => Ok(n),
NumOrStr::Str(s) => T::from_str(&s).map_err(de::Error::custom),
}
}

pub fn optional_number_or_string<'de, D, T>(deserializer: D) -> Result<Option<T>, D::Error>
where
D: de::Deserializer<'de>,
T: FromStr + Deserialize<'de>,
<T as FromStr>::Err: Display,
{
match NumOrStr::deserialize(deserializer)? {
NumOrStr::Num(n) => Ok(Some(n)),
NumOrStr::Str(s) => T::from_str(&s)
.map_err(de::Error::custom)
.and_then(|n| Ok(Some(n))),
}
}

pub trait NodeStore: AddressStore + Clone + Send + Sync + 'static {
type Account: Account;

Expand Down Expand Up @@ -102,21 +139,28 @@ pub struct AccountDetails {
pub ilp_address: Option<Address>,
pub username: Username,
pub asset_code: String,
#[serde(deserialize_with = "number_or_string")]
pub asset_scale: u8,
#[serde(default = "u64::max_value")]
#[serde(default = "u64::max_value", deserialize_with = "number_or_string")]
pub max_packet_amount: u64,
#[serde(default, deserialize_with = "optional_number_or_string")]
pub min_balance: Option<i64>,
pub ilp_over_http_url: Option<String>,
pub ilp_over_http_incoming_token: Option<SecretString>,
pub ilp_over_http_outgoing_token: Option<SecretString>,
pub ilp_over_btp_url: Option<String>,
pub ilp_over_btp_outgoing_token: Option<SecretString>,
pub ilp_over_btp_incoming_token: Option<SecretString>,
#[serde(default, deserialize_with = "optional_number_or_string")]
pub settle_threshold: Option<i64>,
#[serde(default, deserialize_with = "optional_number_or_string")]
pub settle_to: Option<i64>,
pub routing_relation: Option<String>,
#[serde(default, deserialize_with = "optional_number_or_string")]
pub round_trip_time: Option<u32>,
#[serde(default, deserialize_with = "optional_number_or_string")]
pub amount_per_minute_limit: Option<u64>,
#[serde(default, deserialize_with = "optional_number_or_string")]
pub packets_per_minute_limit: Option<u32>,
pub settlement_engine_url: Option<String>,
}
Expand Down
5 changes: 4 additions & 1 deletion crates/interledger-api/src/routes/accounts.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use crate::{http_retry::Client, AccountDetails, AccountSettings, ApiError, NodeStore};
use crate::{
http_retry::Client, number_or_string, AccountDetails, AccountSettings, ApiError, NodeStore,
};
use bytes::Bytes;
use futures::{
future::{err, join_all, ok, Either},
Expand Down Expand Up @@ -30,6 +32,7 @@ const DEFAULT_HTTP_TIMEOUT: Duration = Duration::from_millis(5000);
#[derive(Deserialize, Debug)]
struct SpspPayRequest {
receiver: String,
#[serde(deserialize_with = "number_or_string")]
source_amount: u64,
}

Expand Down

0 comments on commit a5c9ce4

Please sign in to comment.