Skip to content

Commit

Permalink
Merge pull request #97 from helius-labs/feat/create-from-config
Browse files Browse the repository at this point in the history
feat(client): Allow Config-based Client Creation
  • Loading branch information
0xIchigo authored Nov 29, 2024
2 parents 6a2999a + 80a5c9d commit 6cfdc02
Show file tree
Hide file tree
Showing 3 changed files with 162 additions and 0 deletions.
23 changes: 23 additions & 0 deletions examples/async_config_based_creation.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
use helius::config::Config;
use helius::error::Result;
use helius::types::Cluster;
use helius::Helius;

/// Demonstrates creating a Helius client with async Solana capabilities using the config-based approach
#[tokio::main]
async fn main() -> Result<()> {
let api_key: &str = "your_api_key";
let cluster: Cluster = Cluster::MainnetBeta;

let config: Config = Config::new(api_key, cluster)?;
let async_client: Helius = config.create_client_with_async()?;

if let Ok(async_conn) = async_client.async_connection() {
println!(
"Async client - Get Block Height: {:?}",
async_conn.get_block_height().await
);
}

Ok(())
}
116 changes: 116 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
use crate::error::{HeliusError, Result};
use crate::rpc_client::RpcClient;
use crate::types::{Cluster, HeliusEndpoints, MintApiAuthority};
use crate::websocket::{EnhancedWebsocket, ENHANCED_WEBSOCKET_URL};
use crate::Helius;
use reqwest::Client;
use solana_client::nonblocking::rpc_client::RpcClient as AsyncSolanaRpcClient;
use std::sync::Arc;
use url::{ParseError, Url};

/// Configuration settings for the Helius client
///
Expand Down Expand Up @@ -41,6 +48,115 @@ impl Config {
})
}

pub fn rpc_client_with_reqwest_client(&self, client: Client) -> Result<RpcClient> {
RpcClient::new(Arc::new(client), Arc::new(self.clone()))
}

/// Creates a basic Helius client from this configuration
///
/// # Returns
/// A `Result` containing a Helius client with basic RPC capabilities
pub fn create_client(self) -> Result<Helius> {
let client: Client = Client::builder().build().map_err(HeliusError::ReqwestError)?;
let rpc_client: Arc<RpcClient> = Arc::new(self.rpc_client_with_reqwest_client(client.clone())?);

Ok(Helius {
config: Arc::new(self),
client,
rpc_client,
async_rpc_client: None,
ws_client: None,
})
}

/// Creates a Helius client with async Solana capabilities
///
/// # Returns
/// A `Result` containing a Helius client with both RPC and async Solana capabilities
pub fn create_client_with_async(self) -> Result<Helius> {
let client: Client = Client::builder().build().map_err(HeliusError::ReqwestError)?;
let mut rpc_url: Url = Url::parse(&self.endpoints.rpc)
.map_err(|e: ParseError| HeliusError::InvalidInput(format!("Invalid RPC URL: {}", e)))?;

rpc_url.query_pairs_mut().append_pair("api-key", &self.api_key);

let async_solana_client: Arc<AsyncSolanaRpcClient> = Arc::new(AsyncSolanaRpcClient::new(rpc_url.to_string()));
let rpc_client: Arc<RpcClient> = Arc::new(self.rpc_client_with_reqwest_client(client.clone())?);

Ok(Helius {
config: Arc::new(self),
client,
rpc_client,
async_rpc_client: Some(async_solana_client),
ws_client: None,
})
}

/// Creates a Helius client with websocket support
///
/// # Arguments
/// * `ping_interval_secs` - Optional duration in seconds between ping messages
/// * `pong_timeout_secs` - Optional duration in seconds to wait for pong response
///
/// # Returns
/// A `Result` containing a Helius client with websocket support
pub async fn create_client_with_ws(
self,
ping_interval_secs: Option<u64>,
pong_timeout_secs: Option<u64>,
) -> Result<Helius> {
let client: Client = Client::builder().build().map_err(HeliusError::ReqwestError)?;
let rpc_client: Arc<RpcClient> = Arc::new(self.rpc_client_with_reqwest_client(client.clone())?);

let wss: String = format!("{}{}", ENHANCED_WEBSOCKET_URL, self.api_key);
let ws_client: Arc<EnhancedWebsocket> =
Arc::new(EnhancedWebsocket::new(&wss, ping_interval_secs, pong_timeout_secs).await?);

Ok(Helius {
config: Arc::new(self),
client,
rpc_client,
async_rpc_client: None,
ws_client: Some(ws_client),
})
}

/// Creates a full-featured Helius client with both async and websocket support
///
/// # Arguments
/// * `ping_interval_secs` - Optional duration in seconds between ping messages
/// * `pong_timeout_secs` - Optional duration in seconds to wait for pong response
///
/// # Returns
/// A `Result` containing a fully-featured Helius client
pub async fn create_full_client(
self,
ping_interval_secs: Option<u64>,
pong_timeout_secs: Option<u64>,
) -> Result<Helius> {
let client: Client = Client::builder().build().map_err(HeliusError::ReqwestError)?;
let rpc_client: Arc<RpcClient> = Arc::new(self.rpc_client_with_reqwest_client(client.clone())?);

// Setup async client
let mut rpc_url: Url = Url::parse(&self.endpoints.rpc)
.map_err(|e: ParseError| HeliusError::InvalidInput(format!("Invalid RPC URL: {}", e)))?;
rpc_url.query_pairs_mut().append_pair("api-key", &self.api_key);
let async_solana_client = Arc::new(AsyncSolanaRpcClient::new(rpc_url.to_string()));

// Setup websocket
let wss: String = format!("{}{}", ENHANCED_WEBSOCKET_URL, self.api_key);
let ws_client: Arc<EnhancedWebsocket> =
Arc::new(EnhancedWebsocket::new(&wss, ping_interval_secs, pong_timeout_secs).await?);

Ok(Helius {
config: Arc::new(self),
client,
rpc_client,
async_rpc_client: Some(async_solana_client),
ws_client: Some(ws_client),
})
}

pub fn mint_api_authority(&self) -> MintApiAuthority {
MintApiAuthority::from_cluster(&self.cluster)
}
Expand Down
23 changes: 23 additions & 0 deletions tests/test_config.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use helius::config::Config;
use helius::error::{HeliusError, Result};
use helius::types::Cluster;
use helius::Helius;

#[test]
fn test_config_new_with_empty_api_key() {
Expand All @@ -18,3 +19,25 @@ fn test_config_new_with_valid_api_key() {
assert_eq!(config.endpoints.api, "https://api-devnet.helius-rpc.com/");
assert_eq!(config.endpoints.rpc, "https://devnet.helius-rpc.com/");
}

#[test]
fn test_create_basic_client() {
let config: Config = Config::new("valid-api-key", Cluster::Devnet).unwrap();
let result: Result<Helius> = config.create_client();
assert!(result.is_ok());

let client: Helius = result.unwrap();
assert!(client.async_rpc_client.is_none());
assert!(client.ws_client.is_none());
}

#[test]
fn test_create_async_client() {
let config: Config = Config::new("valid-api-key", Cluster::Devnet).unwrap();
let result: Result<Helius> = config.create_client_with_async();
assert!(result.is_ok());

let client: Helius = result.unwrap();
assert!(client.async_rpc_client.is_some());
assert!(client.ws_client.is_none());
}

0 comments on commit 6cfdc02

Please sign in to comment.