diff --git a/src/effects/effects_for_account_response.rs b/src/effects/effects_for_account_response.rs index e827cc6..78fa203 100644 --- a/src/effects/effects_for_account_response.rs +++ b/src/effects/effects_for_account_response.rs @@ -76,7 +76,7 @@ pub struct Record { /// The type of the effect. #[serde(rename = "type")] pub type_field: String, - /// The asset code of the effect. + /// The type_i code of the effect. #[serde(rename = "type_i")] pub type_i: i64, /// the epoch timestamp when the effect was created. diff --git a/src/effects/effects_for_liquidity_pools_request.rs b/src/effects/effects_for_liquidity_pools_request.rs new file mode 100644 index 0000000..3fdd5a6 --- /dev/null +++ b/src/effects/effects_for_liquidity_pools_request.rs @@ -0,0 +1,109 @@ +use crate::models::{Order, Request}; +use crate::BuildQueryParametersExt; + +#[derive(Default)] +pub struct EffectsForLiquidityPoolsRequest { + /// The liquidity pool id + liquidity_pool_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, +} + +impl EffectsForLiquidityPoolsRequest { + /// Creates a new `EffectsForLiquidityPoolsRequest` with default parameters. + pub fn new() -> Self { + EffectsForLiquidityPoolsRequest::default() + } + + /// Sets the liquidity pool id for the request. + /// + /// # Arguments + /// * `liquidity_pool_id` - A `String` value representing the liquidity pool id. + /// + pub fn set_liquidity_pool_id( + self, + liquidity_pool_id: String, + ) -> EffectsForLiquidityPoolsRequest { + EffectsForLiquidityPoolsRequest { + liquidity_pool_id: Some(liquidity_pool_id), + ..self + } + } + + /// 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(EffectsForLiquidityPoolsRequest { + cursor: Some(cursor), + ..self + }) + } + + /// 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(EffectsForLiquidityPoolsRequest { + 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) -> EffectsForLiquidityPoolsRequest { + EffectsForLiquidityPoolsRequest { + order: Some(order), + ..self + } + } +} + +impl Request for EffectsForLiquidityPoolsRequest { + fn get_query_parameters(&self) -> String { + vec![ + self.liquidity_pool_id + .as_ref() + .map(|l| format!("liquidity_pool_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, + self.get_query_parameters() + ) + } +} diff --git a/src/effects/effects_for_liquidity_pools_response.rs b/src/effects/effects_for_liquidity_pools_response.rs new file mode 100644 index 0000000..bce4edf --- /dev/null +++ b/src/effects/effects_for_liquidity_pools_response.rs @@ -0,0 +1,110 @@ +use derive_getters::Getters; +use serde::{Deserialize, Serialize}; + +use crate::models::Response; + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Getters)] +#[serde(rename_all = "camelCase")] +pub struct EffectsForLiquidityPoolResponse { + /// Navigational links for the current, next, and previous pages of the response. + #[serde(rename = "_links")] + pub links: Links, + /// Contains the actual list of effect records in the `records` field. + #[serde(rename = "_embedded")] + pub embedded: Embedded, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Getters)] +#[serde(rename_all = "camelCase")] +pub struct Links { + /// Navigational links for the current, next, and previous pages of the response. + #[serde(rename = "self")] + pub self_field: Link, + /// Navigational links for the next page of the response. + pub next: Link, + /// Navigational links for the previous page of the response. + pub prev: Link, +} + +/// Represents different kinds of links a Response might have. +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Getters)] +#[serde(rename_all = "camelCase")] +pub struct Link { + /// Navigational links for the current, next, and previous pages of the response. + #[serde(rename = "self")] + pub self_field: Option, + /// Navigational links for the next page of the response. + pub next: Option, + /// Navigational links for the previous page of the response. + pub prev: Option, + /// Navigational links for the operation of the response. + pub operation: Option, + /// Navigational links for the succeeds of the response. + pub succeeds: Option, + /// Navigational links for the precedes of the response. + pub precedes: Option, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Getters)] +#[serde(rename_all = "camelCase")] +pub struct Embedded { + /// A list of effect records. + pub records: Vec, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Getters)] +#[serde(rename_all = "camelCase")] +pub struct Record { + /// Navigational links for the current, next, and previous pages of the response. + #[serde(rename = "_links")] + pub links: RecordLinks, + /// A unique identifier of the effect + pub id: String, + #[serde(rename = "paging_token")] + /// A token used for paging through results. + pub paging_token: String, + /// The account that generated the effect. + pub account: String, + /// The type of effect. + #[serde(rename = "type")] + pub type_field: String, + /// The type_i of the effect. + #[serde(rename = "type_i")] + pub type_i: i64, + /// the epoch timestamp when the effect was created. + #[serde(rename = "created_at")] + pub created_at: String, + /// The starting balance of the effect. + #[serde(rename = "starting_balance")] + pub starting_balance: Option, + /// The asset type of the effect. + #[serde(rename = "asset_type")] + pub asset_type: Option, + /// the amount of the effect + pub amount: Option, + /// the wheight of the effect + pub weight: Option, + #[serde(rename = "public_key")] + /// The public key of the effect + pub public_key: Option, + /// The trustor of the effect + pub key: Option, +} + +/// Represents different kinds of links a Record might have. +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Getters)] +#[serde(rename_all = "camelCase")] +pub struct RecordLinks { + /// Navigational links for the current, next, and previous pages of the response. + pub operation: Link, + /// Navigational links for the next page of the response. + pub succeeds: Link, + /// Navigational links for the previous page of the response. + pub precedes: Link, +} + +impl Response for EffectsForLiquidityPoolResponse { + fn from_json(json: String) -> Result { + serde_json::from_str(&json).map_err(|e| e.to_string()) + } +} diff --git a/src/effects/mod.rs b/src/effects/mod.rs index ffc75b8..2781f3c 100644 --- a/src/effects/mod.rs +++ b/src/effects/mod.rs @@ -2,6 +2,8 @@ pub mod all_effects_request; pub mod all_effects_response; pub mod effects_for_account_request; pub mod effects_for_account_response; +pub mod effects_for_liquidity_pools_request; +pub mod effects_for_liquidity_pools_response; static EFFECTS_PATH: &str = "effects"; @@ -10,6 +12,8 @@ pub mod prelude { pub use super::all_effects_response::*; pub use super::effects_for_account_request::*; pub use super::effects_for_account_response::*; + pub use super::effects_for_liquidity_pools_request::*; + pub use super::effects_for_liquidity_pools_response::*; } #[cfg(test)] diff --git a/src/horizon_client.rs b/src/horizon_client.rs index c74b71f..c73bece 100644 --- a/src/horizon_client.rs +++ b/src/horizon_client.rs @@ -1,11 +1,16 @@ use crate::{ - accounts::prelude::*, assets::prelude::{AllAssetsRequest, AllAssetsResponse}, claimable_balances::prelude::{ + accounts::prelude::*, + assets::prelude::{AllAssetsRequest, AllAssetsResponse}, + claimable_balances::prelude::{ AllClaimableBalancesRequest, AllClaimableBalancesResponse, ClaimableBalanceId, SingleClaimableBalanceRequest, SingleClaimableBalanceResponse, - }, effects::prelude::*, ledgers::{ + }, + effects::prelude::*, + ledgers::{ prelude::{LedgersRequest, LedgersResponse, SingleLedgerRequest, SingleLedgerResponse}, single_ledger_request::Sequence, - }, models::{Request, Response} + }, + models::{Request, Response}, }; use reqwest; use url::Url; @@ -307,6 +312,50 @@ impl HorizonClient { self.get::(request).await } + /// Retrieves a list of effects for a specific account from the Horizon server. + /// + /// This asynchronous method fetches a list of effects for a specific account from the Horizon server. + /// It requires an [`EffectsForAccountRequest`] to specify the account ID and optional query parameters. + /// + /// # Arguments + /// * `request` - A reference to an [`EffectsForAccountRequest`] instance, containing the account ID + /// and optional query parameters for the effects request. + /// + /// # Returns + /// + /// On successful execution, returns a `Result` containing an [`EffectsForAccountResponse`], 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 [`EffectsForAccountRequest`] and set the account 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 = EffectsForAccountRequest::new() + /// .set_account_id("GDQJUTQYK2MQX2VGDR2FYWLIYAQIEGXTQVTFEMGH2BEWFG4BRUY4CKI7".to_string()); + /// + /// let response = horizon_client.get_effects_for_account(&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_account( &self, request: &EffectsForAccountRequest, @@ -314,6 +363,57 @@ impl HorizonClient { self.get::(request).await } + /// Retrieves a list of effects for a specific account from the Horizon server. + /// + /// This asynchronous method fetches a list of effects for a specific account from the Horizon server. + /// It requires an [`EffectsForLiquidityPoolsRequest`] to specify the account ID and optional query parameters. + /// + /// # Arguments + /// * `request` - A reference to an [`EffectsForLiquidityPoolsRequest`] instance, containing the account ID + /// and optional query parameters for the effects request. + /// + /// # Returns + /// + /// On successful execution, returns a `Result` containing an [`EffectsForLiquidityPoolResponse`], 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 [`EffectsForLiquidityPoolsRequest`] and set the account 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 = EffectsForAccountRequest::new() + /// .set_account_id("GDQJUTQYK2MQX2VGDR2FYWLIYAQIEGXTQVTFEMGH2BEWFG4BRUY4CKI7".to_string()); + /// + /// let response = horizon_client.get_effects_for_account(&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_liquidity_pools( + &self, + request: &EffectsForLiquidityPoolsRequest, + ) -> Result { + self.get::(request).await + } + /// Retrieves a list of all ledgers. /// /// This asynchronous method is designed to fetch list of ledgers @@ -423,6 +523,53 @@ impl HorizonClient { self.get::(request).await } + /// Retrieves a list of all effects from the Horizon server. + /// + /// This asynchronous method fetches a list of all effects from the Horizon server. + /// It requires an [`AllEffectsRequest`] to specify the optional query parameters. + /// + /// Adheres to the Retrieve a Ledger + /// endpoint. + /// + /// # Arguments + /// + /// * `request` - A reference to an [`AllEffectsRequest`] instance, containing the + /// parameters for the effects request. + /// + /// # Returns + /// + /// On successful execution, returns a `Result` containing an [`AllEffectsResponse`], which includes + /// the list of all effects obtained from the Horizon server. If the request fails, it returns an error within `Result`. + /// + /// # Usage + /// To use this method, create an instance of [`AllEffectsRequest`] and set any desired + /// filters or 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 = AllEffectsRequest::new() + /// .set_limit(2).unwrap(); + /// + /// let response = horizon_client.get_all_effects(&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_all_effects( &self, request: &AllEffectsRequest, @@ -1478,18 +1625,26 @@ mod tests { #[tokio::test] async fn test_get_all_effects() { - let horizon_client = HorizonClient::new("https://horizon-testnet.stellar.org".to_string()).unwrap(); + let horizon_client = + HorizonClient::new("https://horizon-testnet.stellar.org".to_string()).unwrap(); let num_records_to_fetch = 2; - let all_effects_request = AllEffectsRequest::new().set_limit(num_records_to_fetch).unwrap(); + let all_effects_request = AllEffectsRequest::new() + .set_limit(num_records_to_fetch) + .unwrap(); let _all_effects_response = horizon_client.get_all_effects(&all_effects_request).await; assert!(_all_effects_response.clone().is_ok()); // make sure there are actually 2 records assert_eq!( - _all_effects_response.clone().unwrap()._embedded().records().len() as u8, + _all_effects_response + .clone() + .unwrap() + ._embedded() + .records() + .len() as u8, num_records_to_fetch ); @@ -1508,13 +1663,13 @@ mod tests { #[tokio::test] async fn test_get_effects_for_account() { - let id = "0000000459561504769-0000000001"; - let paging_token = "459561504769-1"; - let account = "GAIH3ULLFQ4DGSECF2AR555KZ4KNDGEKN4AFI4SU2M7B43MGK3QJZNSR"; - let record_type = "account_created"; - let type_i = 0; - let created_at = "2024-02-06T17:42:48Z"; - let starting_balance = "10000000000.0000000".to_string(); + const ID: &str = "0000000459561504769-0000000001"; + const PAGING_TOKEN: &str = "459561504769-1"; + const ACCOUNT: &str = "GAIH3ULLFQ4DGSECF2AR555KZ4KNDGEKN4AFI4SU2M7B43MGK3QJZNSR"; + const RECORD_TYPE: &str = "account_created"; + const TYPE_I: i64 = 0; + const CREATED_AT: &str = "2024-02-06T17:42:48Z"; + const STARTING_BALANCE: &str = "10000000000.0000000"; // Initialize horizon client let horizon_client = HorizonClient::new("https://horizon-testnet.stellar.org".to_string()).unwrap(); @@ -1536,7 +1691,7 @@ mod tests { .embedded() .records()[0] .id(), - id + ID ); assert_eq!( effects_for_account_response @@ -1545,7 +1700,7 @@ mod tests { .embedded() .records()[0] .paging_token(), - paging_token + PAGING_TOKEN ); assert_eq!( effects_for_account_response @@ -1554,7 +1709,7 @@ mod tests { .embedded() .records()[0] .account(), - account + ACCOUNT ); assert_eq!( effects_for_account_response @@ -1563,7 +1718,7 @@ mod tests { .embedded() .records()[0] .type_field(), - record_type + RECORD_TYPE ); assert_eq!( effects_for_account_response @@ -1572,7 +1727,7 @@ mod tests { .embedded() .records()[0] .type_i(), - &type_i + &TYPE_I ); assert_eq!( effects_for_account_response @@ -1581,7 +1736,7 @@ mod tests { .embedded() .records()[0] .created_at(), - created_at + CREATED_AT ); assert_eq!( effects_for_account_response @@ -1592,7 +1747,102 @@ mod tests { .starting_balance() .as_ref() .unwrap(), - &starting_balance + &STARTING_BALANCE + ); + } + + #[tokio::test] + async fn get_effects_for_liquidity_pools() { + const ID: &str = "0000000459561504769-0000000001"; + const PAGING_TOKEN: &str = "459561504769-1"; + const ACCOUNT: &str = "GAIH3ULLFQ4DGSECF2AR555KZ4KNDGEKN4AFI4SU2M7B43MGK3QJZNSR"; + const RECORD_TYPE: &str = "account_created"; + const TYPE_I: i64 = 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_liquidity_pools_request = + EffectsForLiquidityPoolsRequest::new().set_limit(2).unwrap(); + + let effects_for_liquidity_pools_response = horizon_client + .get_effects_for_liquidity_pools(&effects_for_liquidity_pools_request) + .await; + + assert!(effects_for_liquidity_pools_response.clone().is_ok()); + + assert_eq!( + effects_for_liquidity_pools_response + .clone() + .unwrap() + .embedded() + .records()[0] + .id(), + ID + ); + + assert_eq!( + effects_for_liquidity_pools_response + .clone() + .unwrap() + .embedded() + .records()[0] + .paging_token(), + PAGING_TOKEN + ); + + assert_eq!( + effects_for_liquidity_pools_response + .clone() + .unwrap() + .embedded() + .records()[0] + .account(), + ACCOUNT + ); + + assert_eq!( + effects_for_liquidity_pools_response + .clone() + .unwrap() + .embedded() + .records()[0] + .type_field(), + RECORD_TYPE + ); + + assert_eq!( + effects_for_liquidity_pools_response + .clone() + .unwrap() + .embedded() + .records()[0] + .type_i(), + &TYPE_I + ); + + assert_eq!( + effects_for_liquidity_pools_response + .clone() + .unwrap() + .embedded() + .records()[0] + .created_at(), + CREATED_AT + ); + + assert_eq!( + effects_for_liquidity_pools_response + .clone() + .unwrap() + .embedded() + .records()[0] + .starting_balance() + .as_ref() + .unwrap(), + &STARTING_BALANCE ); } }