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}