diff --git a/libs/sdk-core/src/persist/transactions.rs b/libs/sdk-core/src/persist/transactions.rs index 97be387e2..ad7b56931 100644 --- a/libs/sdk-core/src/persist/transactions.rs +++ b/libs/sdk-core/src/persist/transactions.rs @@ -2,6 +2,7 @@ use super::db::SqliteStorage; use super::error::{PersistError, PersistResult}; use crate::lnurl::pay::model::SuccessActionProcessed; use crate::{ensure_sdk, models::*}; +use anyhow::anyhow; use rusqlite::types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef}; use rusqlite::Row; use rusqlite::{named_params, params, OptionalExtension}; @@ -112,7 +113,7 @@ impl SqliteStorage { ) -> PersistResult<()> { ensure_sdk!( new_metadata.len() <= METADATA_MAX_LEN, - PersistError::Generic(anyhow::anyhow!( + PersistError::Generic(anyhow!( "Max metadata size ({} characters) has been exceeded", METADATA_MAX_LEN )) @@ -120,6 +121,16 @@ impl SqliteStorage { let _ = serde_json::from_str::>(&new_metadata)?; + // Check if the payment exists + let payment_exists = self + .get_connection()? + .prepare("SELECT 1 FROM payments WHERE id = ?1;")? + .exists(params![payment_hash])?; + + if !payment_exists { + return Err(PersistError::Generic(anyhow!("Payment not found"))); + } + self.get_connection()?.execute( " INSERT OR REPLACE INTO sync.payments_metadata( diff --git a/tools/sdk-cli/src/command_handlers.rs b/tools/sdk-cli/src/command_handlers.rs index c2b7d4a01..429ab4179 100644 --- a/tools/sdk-cli/src/command_handlers.rs +++ b/tools/sdk-cli/src/command_handlers.rs @@ -1,12 +1,12 @@ use std::fs; use std::sync::Arc; -use anyhow::{anyhow, Error, Result}; +use anyhow::{anyhow, Context, Error, Result}; use breez_sdk_core::InputType::{LnUrlAuth, LnUrlPay, LnUrlWithdraw}; use breez_sdk_core::{ parse, BreezEvent, BreezServices, BuyBitcoinRequest, CheckMessageRequest, EventListener, GreenlightCredentials, ListPaymentsRequest, LnUrlPayRequest, LnUrlWithdrawRequest, - PrepareRedeemOnchainFundsRequest, PrepareRefundRequest, ReceiveOnchainRequest, + MetadataFilter, PrepareRedeemOnchainFundsRequest, PrepareRefundRequest, ReceiveOnchainRequest, ReceivePaymentRequest, RedeemOnchainFundsRequest, RefundRequest, ReportIssueRequest, ReportPaymentFailureDetails, ReverseSwapFeesRequest, SendOnchainRequest, SendPaymentRequest, SendSpontaneousPaymentRequest, SignMessageRequest, StaticBackupRequest, @@ -215,11 +215,31 @@ pub(crate) async fn handle_command( include_failures, limit, offset, + metadata_filters: metadata_filters_raw, } => { + let metadata_filters = match metadata_filters_raw { + Some(raw_filters) => { + let mut filters = vec![]; + + for filter in raw_filters.iter() { + let (json_path, json_value) = + filter.split_once(':').context("Invalid metadata filter")?; + + filters.push(MetadataFilter { + json_path: json_path.to_string(), + json_value: json_value.to_string(), + }); + } + + Some(filters) + } + None => None, + }; + let payments = sdk()? .list_payments(ListPaymentsRequest { filters: None, - metadata_filters: None, + metadata_filters, from_timestamp, to_timestamp, include_failures: Some(include_failures), @@ -229,6 +249,14 @@ pub(crate) async fn handle_command( .await?; serde_json::to_string_pretty(&payments).map_err(|e| e.into()) } + Commands::SetPaymentMetadata { + payment_hash, + metadata, + } => { + sdk()?.set_payment_metadata(payment_hash, metadata).await?; + + Ok("Payment metadata was set successfully".to_string()) + } Commands::PaymentByHash { hash } => { let payment = sdk()?.payment_by_hash(hash).await?; serde_json::to_string_pretty(&payment).map_err(|e| e.into()) diff --git a/tools/sdk-cli/src/commands.rs b/tools/sdk-cli/src/commands.rs index 10f816991..0cb119f7b 100644 --- a/tools/sdk-cli/src/commands.rs +++ b/tools/sdk-cli/src/commands.rs @@ -144,6 +144,16 @@ pub(crate) enum Commands { /// Optional offset in payments #[clap(short = 'o', long = "offset")] offset: Option, + + /// Optional metadata filter, in the form of json_path:json_value + #[clap(short = 'm', long = "metadata", num_args = 1..)] + metadata_filters: Option>, + }, + + /// Set the metadata for a given payment + SetPaymentMetadata { + payment_hash: String, + metadata: String, }, /// Retrieve a payment by its hash