Skip to main content

chainlink_data_streams_sdk/
config.rs

1use reqwest::Response;
2use thiserror::Error;
3use zeroize::Zeroize;
4
5#[derive(Error, Debug)]
6pub enum ConfigError {
7    #[error("API key cannot be empty")]
8    EmptyApiKey,
9
10    #[error("API secret cannot be empty")]
11    EmptyApiSecret,
12}
13
14#[derive(Clone, PartialEq, Eq)]
15pub enum WebSocketHighAvailability {
16    Enabled,
17    Disabled,
18}
19
20#[derive(Clone, PartialEq, Eq)]
21pub enum InsecureSkipVerify {
22    Enabled,
23    Disabled,
24}
25
26impl InsecureSkipVerify {
27    /// Converts `InsecureSkipVerify` enum to a boolean.
28    pub fn to_bool(&self) -> bool {
29        match self {
30            InsecureSkipVerify::Enabled => true,
31            InsecureSkipVerify::Disabled => false,
32        }
33    }
34}
35
36/// Config specifies the client configuration and dependencies.
37#[derive(Clone)]
38pub struct Config {
39    /// Client API key
40    pub api_key: String,
41
42    /// Client API secret
43    pub api_secret: String,
44
45    /// REST API URL
46    pub rest_url: String,
47
48    /// WebSocket API URL
49    pub ws_url: String,
50
51    /// High Availability Mode: Use concurrent connections to multiple Streams servers
52    pub ws_ha: WebSocketHighAvailability,
53
54    /// Maximum number of reconnection attempts for underlying WebSocket connections
55    pub ws_max_reconnect: usize,
56
57    /// Skip server certificate chain and host name verification
58    pub insecure_skip_verify: InsecureSkipVerify,
59
60    /// Function to inspect HTTP responses for REST requests.
61    /// The response object must not be modified.
62    pub inspect_http_response: Option<fn(&Response)>,
63}
64
65impl Config {
66    const DEFAULT_WS_MAX_RECONNECT: usize = 5;
67    const DEFAULT_WS_HA: WebSocketHighAvailability = WebSocketHighAvailability::Disabled;
68    const DEFAULT_INSECURE_SKIP_VERIFY: InsecureSkipVerify = InsecureSkipVerify::Disabled;
69    const DEFAULT_INSPECT_HTTP_RESPONSE: Option<fn(&Response)> = None;
70
71    /// Creates a new `Config` instance with the provided parameters. (Builder pattern)
72    ///
73    /// # Arguments
74    ///
75    /// * `api_key` - Client API key for authentication.
76    /// * `api_secret` - Client API secret for signing requests.
77    /// * `rest_url` - REST API base URL.
78    /// * `ws_url` - WebSocket API base URL.
79    /// * `ws_ha` - Enable high availability for WebSocket connections.
80    /// * `ws_max_reconnect` - Maximum reconnection attempts for WebSocket (optional, defaults to 5).
81    /// * `insecure_skip_verify` - Skip TLS certificate verification (use with caution).
82    /// * `inspect_http_response` - Optional callback to inspect HTTP responses.
83    ///
84    /// # Errors
85    ///
86    /// Returns `ConfigError` if any of the provided parameters are invalid.
87    ///
88    /// # Example
89    /// ```rust
90    /// use chainlink_data_streams_sdk::config::{Config, WebSocketHighAvailability, InsecureSkipVerify};
91    ///
92    /// use std::error::Error;
93    ///
94    /// #[tokio::main]
95    /// async fn main() -> Result<(), Box<dyn Error>> {
96    ///    let api_key = "YOUR_API_KEY_GOES_HERE";
97    ///    let user_secret = "YOUR_USER_SECRET_GOES_HERE";
98    ///    let rest_url = "https://api.testnet-dataengine.chain.link";
99    ///    let ws_url = "wss://api.testnet-dataengine.chain.link/ws";
100    ///
101    ///    // Initialize the basic configuration
102    ///    let config = Config::new(
103    ///        api_key.to_string(),
104    ///        user_secret.to_string(),
105    ///        rest_url.to_string(),
106    ///        ws_url.to_string(),
107    ///    )
108    ///    .build()?;
109    ///
110    ///    // If you want to customize the configuration further, use the builder pattern
111    ///    // In HA mode, provide a single WebSocket URL — origins are discovered automatically
112    ///    // via a HEAD request to the server (X-Cll-Available-Origins header).
113    ///    let config_custom = Config::new(
114    ///        api_key.to_string(),
115    ///        user_secret.to_string(),
116    ///        rest_url.to_string(),
117    ///        ws_url.to_string(),
118    ///    )
119    ///    .with_ws_ha(WebSocketHighAvailability::Enabled) // Enable WebSocket High Availability Mode
120    ///    .with_ws_max_reconnect(10) // Set maximum reconnection attempts to 10, instead of the default 5.
121    ///    .with_insecure_skip_verify(InsecureSkipVerify::Enabled) // Skip TLS certificate verification, use with caution. This is disabled by default.
122    ///    .with_inspect_http_response(|response| {
123    ///         // Custom logic to inspect the HTTP response here
124    ///         println!("Received response with status: {}", response.status());
125    ///     })
126    ///    .build()?;
127    ///
128    ///    Ok(())
129    /// }
130    /// ```
131    pub fn new(
132        api_key: String,
133        api_secret: String,
134        rest_url: String,
135        ws_url: String,
136    ) -> ConfigBuilder {
137        ConfigBuilder {
138            api_key,
139            api_secret,
140            rest_url,
141            ws_url,
142            ws_ha: Self::DEFAULT_WS_HA,
143            ws_max_reconnect: Self::DEFAULT_WS_MAX_RECONNECT,
144            insecure_skip_verify: Self::DEFAULT_INSECURE_SKIP_VERIFY,
145            inspect_http_response: Self::DEFAULT_INSPECT_HTTP_RESPONSE,
146        }
147    }
148}
149
150impl Drop for Config {
151    fn drop(&mut self) {
152        self.api_key.zeroize();
153        self.api_secret.zeroize();
154    }
155}
156
157pub struct ConfigBuilder {
158    api_key: String,
159    api_secret: String,
160    rest_url: String,
161    ws_url: String,
162    ws_ha: WebSocketHighAvailability,
163    ws_max_reconnect: usize,
164    insecure_skip_verify: InsecureSkipVerify,
165    inspect_http_response: Option<fn(&Response)>,
166}
167
168impl ConfigBuilder {
169    /// Sets the `ws_ha` parameter.
170    pub fn with_ws_ha(mut self, ws_ha: WebSocketHighAvailability) -> Self {
171        self.ws_ha = ws_ha;
172        self
173    }
174
175    // Sets the `ws_max_reconnect` parameter.
176    pub fn with_ws_max_reconnect(mut self, ws_max_reconnect: usize) -> Self {
177        self.ws_max_reconnect = ws_max_reconnect;
178        self
179    }
180
181    /// Sets the `insecure_skip_verify` parameter.
182    pub fn with_insecure_skip_verify(mut self, insecure_skip_verify: InsecureSkipVerify) -> Self {
183        self.insecure_skip_verify = insecure_skip_verify;
184        self
185    }
186
187    /// Sets the `inspect_http_response` parameter.
188    pub fn with_inspect_http_response(mut self, inspect_http_response: fn(&Response)) -> Self {
189        self.inspect_http_response = Some(inspect_http_response);
190        self
191    }
192
193    /// Builds the `Config` instance.
194    pub fn build(self) -> Result<Config, ConfigError> {
195        if self.api_key.trim().is_empty() {
196            return Err(ConfigError::EmptyApiKey);
197        }
198
199        if self.api_secret.trim().is_empty() {
200            return Err(ConfigError::EmptyApiSecret);
201        }
202
203        Ok(Config {
204            api_key: self.api_key,
205            api_secret: self.api_secret,
206            rest_url: self.rest_url,
207            ws_url: self.ws_url,
208            ws_ha: self.ws_ha,
209            ws_max_reconnect: self.ws_max_reconnect,
210            insecure_skip_verify: self.insecure_skip_verify,
211            inspect_http_response: self.inspect_http_response,
212        })
213    }
214}