From 73e238c7cbe76e4b4c9ad5b4e650993a449f892c Mon Sep 17 00:00:00 2001 From: LeonardTibben Date: Mon, 26 Feb 2024 14:22:19 +0100 Subject: [PATCH 1/3] request --- src/effects/effects_for_operation_request.rs | 99 ++++++++++++++++++++ src/effects/mod.rs | 2 + src/horizon_client.rs | 8 ++ 3 files changed, 109 insertions(+) create mode 100644 src/effects/effects_for_operation_request.rs diff --git a/src/effects/effects_for_operation_request.rs b/src/effects/effects_for_operation_request.rs new file mode 100644 index 0000000..92fee78 --- /dev/null +++ b/src/effects/effects_for_operation_request.rs @@ -0,0 +1,99 @@ +use crate::{models::{Order, Request}, BuildQueryParametersExt}; + +#[derive(Default)] +pub struct EffectsForOperationRequest { + operation_id: Option, + cursor: Option, + limit: Option, + order: Option, +} + +impl EffectsForOperationRequest { + pub fn new() -> EffectsForOperationRequest { + EffectsForOperationRequest::default() + } + + pub fn set_operation_id(self, operation_id: &str) -> EffectsForOperationRequest { + EffectsForOperationRequest { + operation_id: Some(operation_id.to_string()), + ..self + } + } + + pub fn set_cursor(self, cursor: u32) -> Result { + if cursor > 0 { + Ok(EffectsForOperationRequest { + cursor: Some(cursor), + ..self + }) + } else { + Err("Cursor must be greater than 0") + } + } + + pub fn set_limit(self, limit: u64) -> Result { + if limit > 0 { + Ok(EffectsForOperationRequest { + limit: Some(limit), + ..self + }) + } else { + Err("Limit must be greater than 0") + } + } + + pub fn set_order(self, order: Order) -> EffectsForOperationRequest { + EffectsForOperationRequest { + order: Some(order), + ..self + } + } +} + +impl Request for EffectsForOperationRequest { + fn get_query_parameters(&self) -> String { + vec![ + self.operation_id + .as_ref() + .map(|l| format!("operation_id={}", l)), + self.cursor.as_ref().map(|c| format!("cursor={}", c)), + self.limit.as_ref().map(|l| format!("limit={}", l)), + self.order.as_ref().map(|o| format!("order={}", o)), + ] + .build_query_parameters() + } + + fn build_url(&self, base_url: &str) -> String { + format!( + "{}/{}{}", + base_url, + super::EFFECTS_PATH, + "/operations", + ) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_get_query_parameters() { + let request = EffectsForOperationRequest::new() + .set_operation_id("123") + .set_cursor(1).unwrap() + .set_limit(10).unwrap() + .set_order(Order::Asc); + + let query_parameters = request.get_query_parameters(); + assert_eq!(query_parameters, "?operation_id=123&cursor=1&limit=10&order=asc"); + } + + #[test] + fn test_build_url() { + let request = EffectsForOperationRequest::new(); + let base_url = "https://horizon-testnet.stellar.org"; + let url = request.build_url(base_url); + assert_eq!(url, "https://horizon-testnet.stellar.org/effects/operations"); + } +} \ No newline at end of file diff --git a/src/effects/mod.rs b/src/effects/mod.rs index 298dd07..7a331f2 100644 --- a/src/effects/mod.rs +++ b/src/effects/mod.rs @@ -3,6 +3,7 @@ pub mod effects_response; pub mod effects_for_account_request; pub mod effects_for_liquidity_pools_request; pub mod effects_for_ledger_request; +pub mod effects_for_operation_request; static EFFECTS_PATH: &str = "effects"; @@ -12,6 +13,7 @@ pub mod prelude { pub use super::effects_for_account_request::*; pub use super::effects_for_liquidity_pools_request::*; pub use super::effects_for_ledger_request::*; + pub use super::effects_for_operation_request::*; } #[cfg(test)] diff --git a/src/horizon_client.rs b/src/horizon_client.rs index c1983d1..d5ee0af 100644 --- a/src/horizon_client.rs +++ b/src/horizon_client.rs @@ -411,6 +411,14 @@ impl HorizonClient { self.get::(request).await } + + pub async fn get_effects_for_operations( + &self, + request: &EffectsForOperationRequest, + ) -> Result { + self.get::(request).await + } + /// Retrieves a list of all ledgers. /// /// This asynchronous method is designed to fetch list of ledgers From b12bda3dba8e4e5d222077c0e15000f2b18355a0 Mon Sep 17 00:00:00 2001 From: LeonardTibben Date: Mon, 26 Feb 2024 15:00:56 +0100 Subject: [PATCH 2/3] tests working --- src/effects/effects_for_operation_request.rs | 79 ++++++++++------ src/horizon_client.rs | 99 +++++++++++++++++++- 2 files changed, 146 insertions(+), 32 deletions(-) diff --git a/src/effects/effects_for_operation_request.rs b/src/effects/effects_for_operation_request.rs index 92fee78..6755112 100644 --- a/src/effects/effects_for_operation_request.rs +++ b/src/effects/effects_for_operation_request.rs @@ -1,10 +1,13 @@ -use crate::{models::{Order, Request}, BuildQueryParametersExt}; +use crate::{ + models::{Order, Request}, + BuildQueryParametersExt, +}; #[derive(Default)] pub struct EffectsForOperationRequest { operation_id: Option, cursor: Option, - limit: Option, + limit: Option, order: Option, } @@ -20,28 +23,43 @@ impl EffectsForOperationRequest { } } - pub fn set_cursor(self, cursor: u32) -> Result { - if cursor > 0 { - Ok(EffectsForOperationRequest { - cursor: Some(cursor), - ..self - }) - } else { - Err("Cursor must be greater than 0") + /// Sets the cursor for pagination. + /// + /// # Arguments + /// * `cursor` - A `u32` value pointing to a specific location in a collection of responses. + /// + pub fn set_cursor(self, cursor: u32) -> Result { + if cursor < 1 { + return Err("cursor must be greater than or equal to 1".to_string()); } + + Ok(EffectsForOperationRequest { + cursor: Some(cursor), + ..self + }) } - pub fn set_limit(self, limit: u64) -> Result { - if limit > 0 { - Ok(EffectsForOperationRequest { - limit: Some(limit), - ..self - }) - } else { - Err("Limit must be greater than 0") + /// Sets the maximum number of records to return. + /// + /// # Arguments + /// * `limit` - A `u8` value specifying the maximum number of records. Range: 1 to 200. Defaults to 10. + /// + pub fn set_limit(self, limit: u8) -> Result { + if limit < 1 || limit > 200 { + return Err("limit must be between 1 and 200".to_string()); } + + Ok(EffectsForOperationRequest { + limit: Some(limit), + ..self + }) } + /// Sets the order of the returned records. + /// + /// # Arguments + /// * `order` - An [`Order`] enum value specifying the order (ascending or descending). + /// pub fn set_order(self, order: Order) -> EffectsForOperationRequest { EffectsForOperationRequest { order: Some(order), @@ -64,12 +82,7 @@ impl Request for EffectsForOperationRequest { } fn build_url(&self, base_url: &str) -> String { - format!( - "{}/{}{}", - base_url, - super::EFFECTS_PATH, - "/operations", - ) + format!("{}/{}{}", base_url, super::EFFECTS_PATH, self.get_query_parameters(),) } } @@ -81,12 +94,17 @@ mod tests { fn test_get_query_parameters() { let request = EffectsForOperationRequest::new() .set_operation_id("123") - .set_cursor(1).unwrap() - .set_limit(10).unwrap() + .set_cursor(1) + .unwrap() + .set_limit(10) + .unwrap() .set_order(Order::Asc); let query_parameters = request.get_query_parameters(); - assert_eq!(query_parameters, "?operation_id=123&cursor=1&limit=10&order=asc"); + assert_eq!( + query_parameters, + "?operation_id=123&cursor=1&limit=10&order=asc" + ); } #[test] @@ -94,6 +112,9 @@ mod tests { let request = EffectsForOperationRequest::new(); let base_url = "https://horizon-testnet.stellar.org"; let url = request.build_url(base_url); - assert_eq!(url, "https://horizon-testnet.stellar.org/effects/operations"); + assert_eq!( + url, + "https://horizon-testnet.stellar.org/effects" + ); } -} \ No newline at end of file +} diff --git a/src/horizon_client.rs b/src/horizon_client.rs index d5ee0af..6342823 100644 --- a/src/horizon_client.rs +++ b/src/horizon_client.rs @@ -411,8 +411,7 @@ impl HorizonClient { self.get::(request).await } - - pub async fn get_effects_for_operations( + pub async fn get_effects_for_operation( &self, request: &EffectsForOperationRequest, ) -> Result { @@ -668,7 +667,7 @@ impl HorizonClient { async fn get(&self, request: &impl Request) -> Result { // Construct the URL with potential query parameters. let url = request.build_url(&self.base_url); - + // Send the request and await the response. let response = reqwest::get(&url).await.map_err(|e| e.to_string())?; @@ -1918,6 +1917,100 @@ mod tests { ); } + #[tokio::test] + async fn get_effects_for_operation() { + const ID: &str = "0000000459561504769-0000000001"; + const PAGING_TOKEN: &str = "459561504769-1"; + const ACCOUNT: &str = "GAIH3ULLFQ4DGSECF2AR555KZ4KNDGEKN4AFI4SU2M7B43MGK3QJZNSR"; + const RECORD_TYPE: &str = "account_created"; + const TYPE_I: u32 = 0; + const CREATED_AT: &str = "2024-02-06T17:42:48Z"; + const STARTING_BALANCE: &str = "10000000000.0000000"; + + let horizon_client = + HorizonClient::new("https://horizon-testnet.stellar.org".to_string()).unwrap(); + + let effects_for_operation_request = EffectsForOperationRequest::new().set_limit(2).unwrap(); + + let effects_for_operation_response = horizon_client + .get_effects_for_operation(&effects_for_operation_request) + .await; + + assert!(effects_for_operation_response.clone().is_ok()); + + assert_eq!( + effects_for_operation_response + .clone() + .unwrap() + ._embedded() + .records()[0] + .id(), + ID + ); + + assert_eq!( + effects_for_operation_response + .clone() + .unwrap() + ._embedded() + .records()[0] + .paging_token(), + PAGING_TOKEN + ); + + assert_eq!( + effects_for_operation_response + .clone() + .unwrap() + ._embedded() + .records()[0] + .account(), + ACCOUNT + ); + + assert_eq!( + effects_for_operation_response + .clone() + .unwrap() + ._embedded() + .records()[0] + .effect_type(), + RECORD_TYPE + ); + + assert_eq!( + effects_for_operation_response + .clone() + .unwrap() + ._embedded() + .records()[0] + .type_i(), + &TYPE_I + ); + + assert_eq!( + effects_for_operation_response + .clone() + .unwrap() + ._embedded() + .records()[0] + .created_at(), + CREATED_AT + ); + + assert_eq!( + effects_for_operation_response + .clone() + .unwrap() + ._embedded() + .records()[0] + .starting_balance().as_ref().unwrap(), + STARTING_BALANCE + ); + + + } + #[tokio::test] async fn test_get_effects_for_ledger() { // found by trial and error in the Stellar laboratory From 711afa5f028427c828eb07980e318e566c96fc1c Mon Sep 17 00:00:00 2001 From: LeonardTibben Date: Mon, 26 Feb 2024 15:14:21 +0100 Subject: [PATCH 3/3] documenation --- src/effects/effects_for_operation_request.rs | 54 ++++++++++++++++++-- src/horizon_client.rs | 45 +++++++++++++++- 2 files changed, 93 insertions(+), 6 deletions(-) diff --git a/src/effects/effects_for_operation_request.rs b/src/effects/effects_for_operation_request.rs index 6755112..61ce723 100644 --- a/src/effects/effects_for_operation_request.rs +++ b/src/effects/effects_for_operation_request.rs @@ -3,11 +3,53 @@ use crate::{ BuildQueryParametersExt, }; +/// Represents the request to fetch the effects for a specific operation from the Horizon API. +/// +/// `EffectsForOperationRequest` is a struct used to construct queries for retrieving information about effects +/// from the Horizon server. It includes parameters that allow for pagination control and sorting +/// of the effect records. +/// +/// # Usage +/// Instances of `EffectsForOperationRequest` are created and optionally configured using the builder pattern. +/// Once the desired parameters are set, the request can be passed to the Horizon client to fetch +/// effect data. +/// +/// # Fields +/// * `operation_id` - The operation id to filter effects. +/// * `cursor` - A pointer to a specific location in a collection of responses, derived from the +/// * `limit` - Specifies the maximum number of records to be returned in a single response. +/// * `order` - Determines the [`Order`] of the records in the response. Valid options are [`Order::Asc`] (ascending) +/// +/// # Example +/// ```rust +/// use stellar_rs::effects::effects_for_operation_request::EffectsForOperationRequest; +/// use stellar_rs::models::*; +/// +/// let request = EffectsForOperationRequest::new() +/// .set_operation_id("123") +/// .set_cursor(1).unwrap() +/// .set_limit(10).unwrap() +/// .set_order(Order::Asc); +/// +/// // The request can now be used with a Horizon client to fetch effects. +/// ``` +/// + #[derive(Default)] pub struct EffectsForOperationRequest { + /// The operation id to filter effects. operation_id: Option, + + /// A pointer to a specific location in a collection of responses, derived from the + /// `paging_token` value of a record. Used for pagination control in the API response. cursor: Option, + + /// Specifies the maximum number of records to be returned in a single response. + /// The range for this parameter is from 1 to 200. The default value is set to 10. limit: Option, + + /// Determines the [`Order`] of the records in the response. Valid options are [`Order::Asc`] (ascending) + /// and [`Order::Desc`] (descending). If not specified, it defaults to ascending. order: Option, } @@ -82,7 +124,12 @@ impl Request for EffectsForOperationRequest { } fn build_url(&self, base_url: &str) -> String { - format!("{}/{}{}", base_url, super::EFFECTS_PATH, self.get_query_parameters(),) + format!( + "{}/{}{}", + base_url, + super::EFFECTS_PATH, + self.get_query_parameters(), + ) } } @@ -112,9 +159,6 @@ mod tests { let request = EffectsForOperationRequest::new(); let base_url = "https://horizon-testnet.stellar.org"; let url = request.build_url(base_url); - assert_eq!( - url, - "https://horizon-testnet.stellar.org/effects" - ); + assert_eq!(url, "https://horizon-testnet.stellar.org/effects"); } } diff --git a/src/horizon_client.rs b/src/horizon_client.rs index 6342823..2796970 100644 --- a/src/horizon_client.rs +++ b/src/horizon_client.rs @@ -411,6 +411,49 @@ impl HorizonClient { self.get::(request).await } + /// Retrieves a list of effects for a specific operation from the Horizon server. + /// + /// This asynchronous method fetches a list of effects for a specific operation from the Horizon server. + /// It requires an [`EffectsForOperationRequest`] to specify the operation ID and optional query parameters. + /// + /// # Arguments + /// * `request` - A reference to an [`EffectsForOperationRequest`] instance, containing the operation ID + /// and optional query parameters for the effects request. + /// # Returns + /// + /// On successful execution, returns a `Result` containing an [`EffectsResponse`], which includes + /// the list of effects obtained from the Horizon server. If the request fails, it returns an error within `Result`. + /// + /// # Example + /// To use this method, create an instance of [`EffectsForOperationRequest`] and set the operation ID and any + /// desired query parameters. + /// + /// ``` + /// # use stellar_rs::effects::prelude::*; + /// # use stellar_rs::models::Request; + /// # use stellar_rs::horizon_client::HorizonClient; + /// + /// # async fn example() -> Result<(), Box> { + /// # let base_url = "https://horizon-testnet.stellar.org".to_string(); + /// # let horizon_client = HorizonClient::new(base_url) + /// # .expect("Failed to create Horizon Client"); + /// let request = EffectsForOperationRequest::new() + /// .set_operation_id("123"); + /// + /// let response = horizon_client.get_effects_for_operation(&request).await; + /// + /// // Access the effects + /// if let Ok(effects_response) = response { + /// for effect in effects_response._embedded().records() { + /// println!("Effect ID: {}", effect.id()); + /// // Further processing... + /// } + /// } + /// + /// # Ok({}) + /// # } + /// ``` + /// pub async fn get_effects_for_operation( &self, request: &EffectsForOperationRequest, @@ -667,7 +710,7 @@ impl HorizonClient { async fn get(&self, request: &impl Request) -> Result { // Construct the URL with potential query parameters. let url = request.build_url(&self.base_url); - + // Send the request and await the response. let response = reqwest::get(&url).await.map_err(|e| e.to_string())?;