diff --git a/liana-gui/src/services/connect/client/backend/mod.rs b/liana-gui/src/services/connect/client/backend/mod.rs index a6426205..9a3d465b 100644 --- a/liana-gui/src/services/connect/client/backend/mod.rs +++ b/liana-gui/src/services/connect/client/backend/mod.rs @@ -19,6 +19,7 @@ use lianad::{ config::Config, }; use reqwest::{Error, IntoUrl, Method, RequestBuilder, Response}; +use serde::de::DeserializeOwned; use tokio::sync::RwLock; use crate::{ @@ -137,20 +138,17 @@ impl BackendClient { } pub async fn list_wallets(&self) -> Result, DaemonError> { - let response = self + let list_wallet: api::ListWallets = self .request(Method::GET, &format!("{}/v1/wallets", self.url)) .await .send() + .await? + .check_success() + .await? + .json_or_error() .await?; - if !response.status().is_success() { - return Err(DaemonError::Http( - Some(response.status().into()), - response.text().await?, - )); - } - let list: api::ListWallets = response.json().await?; - Ok(list.wallets) + Ok(list_wallet.wallets) } pub async fn create_wallet( @@ -159,8 +157,7 @@ impl BackendClient { descriptor: &LianaDescriptor, provider_keys: &Vec, ) -> Result { - let response = self - .request(Method::POST, &format!("{}/v1/wallets", self.url)) + self.request(Method::POST, &format!("{}/v1/wallets", self.url)) .await .json(&api::payload::CreateWallet { name, @@ -168,16 +165,11 @@ impl BackendClient { provider_keys, }) .send() - .await?; - if !response.status().is_success() { - return Err(DaemonError::Http( - Some(response.status().into()), - response.text().await?, - )); - } - - let wallet: api::Wallet = response.json().await?; - Ok(wallet) + .await? + .check_success() + .await? + .json_or_error() + .await } pub async fn update_wallet_metadata( @@ -220,12 +212,7 @@ impl BackendClient { .send() .await?; - if !response.status().is_success() { - return Err(DaemonError::Http( - Some(response.status().into()), - response.text().await?, - )); - } + response.check_success().await?; } } @@ -260,12 +247,7 @@ impl BackendClient { .send() .await?; - if !response.status().is_success() { - return Err(DaemonError::Http( - Some(response.status().into()), - response.text().await?, - )); - } + response.check_success().await?; } Ok(()) @@ -275,39 +257,29 @@ impl BackendClient { &self, invitation_id: &str, ) -> Result { - let response = self - .request( - Method::GET, - &format!("{}/v1/invitations/{}", self.url, invitation_id), - ) - .await - .send() - .await?; - if !response.status().is_success() { - return Err(DaemonError::Http( - Some(response.status().into()), - response.text().await?, - )); - } - - Ok(response.json().await?) + self.request( + Method::GET, + &format!("{}/v1/invitations/{}", self.url, invitation_id), + ) + .await + .send() + .await? + .check_success() + .await? + .json_or_error() + .await } pub async fn accept_wallet_invitation(&self, invitation_id: &str) -> Result<(), DaemonError> { - let response = self - .request( - Method::POST, - &format!("{}/v1/invitations/{}/accept", self.url, invitation_id), - ) - .await - .send() - .await?; - if !response.status().is_success() { - return Err(DaemonError::Http( - Some(response.status().into()), - response.text().await?, - )); - } + self.request( + Method::POST, + &format!("{}/v1/invitations/{}/accept", self.url, invitation_id), + ) + .await + .send() + .await? + .check_success() + .await?; Ok(()) } @@ -363,8 +335,7 @@ impl BackendWalletClient { .join(","), )) } - let response: Response = self - .inner + self.inner .request( Method::GET, &format!("{}/v1/wallets/{}/psbts", self.inner.url, self.wallet_uuid), @@ -372,16 +343,11 @@ impl BackendWalletClient { .await .query(&query) .send() - .await?; - - if !response.status().is_success() { - return Err(DaemonError::Http( - Some(response.status().into()), - response.text().await?, - )); - } - - response.json().await.map_err(DaemonError::from) + .await? + .check_success() + .await? + .json_or_error() + .await } async fn list_txs_by_txids( @@ -403,8 +369,7 @@ impl BackendWalletClient { transactions: Vec::new(), }); } - let response: Response = self - .inner + self.inner .request( Method::GET, &format!( @@ -415,16 +380,11 @@ impl BackendWalletClient { .await .query(&query) .send() - .await?; - - if !response.status().is_success() { - return Err(DaemonError::Http( - Some(response.status().into()), - response.text().await?, - )); - } - - response.json().await.map_err(DaemonError::from) + .await? + .check_success() + .await? + .json_or_error() + .await } async fn list_wallet_txs( @@ -439,8 +399,7 @@ impl BackendWalletClient { if let Some(limit) = limit { query.push(("limit", limit.to_string())) } - let response: Response = self - .inner + self.inner .request( Method::GET, &format!( @@ -451,16 +410,11 @@ impl BackendWalletClient { .await .query(&query) .send() - .await?; - - if !response.status().is_success() { - return Err(DaemonError::Http( - Some(response.status().into()), - response.text().await?, - )); - } - - response.json().await.map_err(DaemonError::from) + .await? + .check_success() + .await? + .json_or_error() + .await } async fn list_wallet_coins( @@ -489,8 +443,7 @@ impl BackendWalletClient { .join(","), )); } - let response: Response = self - .inner + self.inner .request( Method::GET, &format!("{}/v1/wallets/{}/coins", self.inner.url, self.wallet_uuid), @@ -498,17 +451,11 @@ impl BackendWalletClient { .await .query(&query) .send() - .await?; - - if !response.status().is_success() { - return Err(DaemonError::Http( - Some(response.status().into()), - response.text().await?, - )); - } - - let res: api::ListCoins = response.json().await?; - Ok(res) + .await? + .check_success() + .await? + .json_or_error() + .await } pub async fn auth(&self) -> AccessTokenResponse { @@ -585,7 +532,7 @@ impl Daemon for BackendWalletClient { } async fn get_new_address(&self) -> Result { - let response: Response = self + let res: api::Address = self .inner .request( Method::POST, @@ -596,16 +543,12 @@ impl Daemon for BackendWalletClient { ) .await .send() + .await? + .check_success() + .await? + .json_or_error() .await?; - if !response.status().is_success() { - return Err(DaemonError::Http( - Some(response.status().into()), - response.text().await?, - )); - } - - let res: api::Address = response.json().await?; Ok(GetAddressResult { address: res.address, derivation_index: res.derivation_index, @@ -626,7 +569,7 @@ impl Daemon for BackendWalletClient { if let Some(start) = start_index { query.push(("start_derivation_index", start.to_string())); } - let response: Response = self + let res: api::ListRevealedAddresses = self .inner .request( Method::GET, @@ -638,16 +581,12 @@ impl Daemon for BackendWalletClient { .await .query(&query) .send() + .await? + .check_success() + .await? + .json_or_error() .await?; - if !response.status().is_success() { - return Err(DaemonError::Http( - Some(response.status().into()), - response.text().await?, - )); - } - - let res: api::ListRevealedAddresses = response.json().await?; Ok(ListRevealedAddressesResult { addresses: res .addresses @@ -782,7 +721,7 @@ impl Daemon for BackendWalletClient { is_max: true, }); } - let response: Response = self + let res: api::DraftPsbtResult = self .inner .request( Method::POST, @@ -799,9 +738,12 @@ impl Daemon for BackendWalletClient { recipients, }) .send() + .await? + .check_success() + .await? + .json_or_error() .await?; - let res: api::DraftPsbtResult = response.json().await?; match res { api::DraftPsbtResult::Success(draft) => Ok(CreateSpendResult::Success { psbt: draft.raw, @@ -822,7 +764,7 @@ impl Daemon for BackendWalletClient { is_cancel: bool, feerate_vb: Option, ) -> Result { - let response: Response = self + let res: api::DraftPsbtResult = self .inner .request( Method::POST, @@ -839,9 +781,12 @@ impl Daemon for BackendWalletClient { save: false, }) .send() + .await? + .check_success() + .await? + .json_or_error() .await?; - let res: api::DraftPsbtResult = response.json().await?; match res { api::DraftPsbtResult::Success(draft) => Ok(CreateSpendResult::Success { psbt: draft.raw, @@ -857,8 +802,7 @@ impl Daemon for BackendWalletClient { } async fn update_spend_tx(&self, psbt: &Psbt) -> Result<(), DaemonError> { - let response: Response = self - .inner + self.inner .request( Method::POST, &format!("{}/v1/wallets/{}/psbts", self.inner.url, self.wallet_uuid), @@ -868,15 +812,10 @@ impl Daemon for BackendWalletClient { psbt: psbt.to_string(), }) .send() + .await? + .check_success() .await?; - if !response.status().is_success() { - return Err(DaemonError::Http( - Some(response.status().into()), - response.text().await?, - )); - } - Ok(()) } @@ -892,23 +831,17 @@ impl Daemon for BackendWalletClient { format!("psbt not found with txid: {}", txid), ))?; - let response: Response = self - .inner + self.inner .request( Method::DELETE, &format!("{}/v1/psbts/{}", self.inner.url, psbt.uuid), ) .await .send() + .await? + .check_success() .await?; - if !response.status().is_success() { - return Err(DaemonError::Http( - Some(response.status().into()), - response.text().await?, - )); - } - Ok(()) } @@ -921,23 +854,17 @@ impl Daemon for BackendWalletClient { .find(|tx| tx.txid == *txid) .ok_or(DaemonError::NoAnswer)?; - let response: Response = self - .inner + self.inner .request( Method::POST, &format!("{}/v1/psbts/{}/broadcast", self.inner.url, psbt.uuid), ) .await .send() + .await? + .check_success() .await?; - if !response.status().is_success() { - return Err(DaemonError::Http( - Some(response.status().into()), - response.text().await?, - )); - } - Ok(()) } @@ -952,7 +879,7 @@ impl Daemon for BackendWalletClient { feerate_vb: u64, sequence: Option, ) -> Result { - let response: Response = self + let res: api::DraftPsbt = self .inner .request( Method::POST, @@ -971,9 +898,12 @@ impl Daemon for BackendWalletClient { inputs: coins_outpoints, }) .send() + .await? + .check_success() + .await? + .json_or_error() .await?; - let res: api::DraftPsbt = response.json().await?; Ok(res.raw) } @@ -987,7 +917,7 @@ impl Daemon for BackendWalletClient { let items: Vec = items.iter().map(|item| item.to_string()).collect(); let mut res = HashMap::new(); for chunk in items.chunks(api::DEFAULT_LABEL_ITEMS_LIMIT) { - let response: Response = self + let wallet_labels: api::WalletLabels = self .inner .request( Method::GET, @@ -996,16 +926,12 @@ impl Daemon for BackendWalletClient { .await .query(&[("items", chunk.join(","))]) .send() + .await? + .check_success() + .await? + .json_or_error() .await?; - if !response.status().is_success() { - return Err(DaemonError::Http( - Some(response.status().into()), - response.text().await?, - )); - } - - let wallet_labels: api::WalletLabels = response.json().await?; res.extend(wallet_labels.labels); } @@ -1016,8 +942,7 @@ impl Daemon for BackendWalletClient { &self, items: &HashMap>, ) -> Result<(), DaemonError> { - let response: Response = self - .inner + self.inner .request( Method::POST, &format!("{}/v1/wallets/{}/labels", self.inner.url, self.wallet_uuid), @@ -1033,15 +958,10 @@ impl Daemon for BackendWalletClient { .collect(), }) .send() + .await? + .check_success() .await?; - if !response.status().is_success() { - return Err(DaemonError::Http( - Some(response.status().into()), - response.text().await?, - )); - } - Ok(()) } @@ -1142,8 +1062,7 @@ impl Daemon for BackendWalletClient { } async fn send_wallet_invitation(&self, email: &str) -> Result<(), DaemonError> { - let response = self - .inner + self.inner .request( Method::POST, &format!( @@ -1154,13 +1073,9 @@ impl Daemon for BackendWalletClient { .await .json(&api::payload::CreateWalletInvitation { email }) .send() + .await? + .check_success() .await?; - if !response.status().is_success() { - return Err(DaemonError::Http( - Some(response.status().into()), - response.text().await?, - )); - } Ok(()) } @@ -1290,3 +1205,27 @@ fn spend_tx_from_api( tx.load_labels(&labels); tx } + +#[async_trait] +pub trait ResponseExt { + async fn check_success(self) -> Result + where + Self: Sized; + async fn json_or_error(self) -> Result; +} + +#[async_trait] +impl ResponseExt for Response { + async fn check_success(self) -> Result { + let status = self.status(); + if !status.is_success() { + return Err(DaemonError::Http(Some(status.into()), self.text().await?)); + } + + Ok(self) + } + + async fn json_or_error(self) -> Result { + self.json::().await.map_err(DaemonError::from) + } +}