nautilus_rs/resources/relay/mod.rs
1pub mod types;
2
3use std::sync::Arc;
4
5use crate::{error::Error, http::HttpClient, types::Paginated};
6use types::{ListMessagesParams, Message, SendMessageParams};
7
8/// Relay service client — Webhooks-as-a-Service.
9///
10/// Delivers JSON events to HTTP endpoints that have subscribed through the
11/// Verne dashboard or the Relay API.
12///
13/// Obtain a `Relay` instance either as part of the unified [`Verne`] client or
14/// standalone:
15///
16/// ```no_run
17/// // Standalone
18/// use nautilus_rs::Relay;
19/// let relay = Relay::new("vrn_relay_live_sk_…");
20///
21/// // Via unified client
22/// use nautilus_rs::Verne;
23/// # fn run() -> Result<(), nautilus_rs::Error> {
24/// let verne = Verne::builder().relay("vrn_relay_live_sk_…").build()?;
25/// let relay = verne.relay()?;
26/// # Ok(())
27/// # }
28/// ```
29///
30/// [`Verne`]: crate::Verne
31pub struct Relay {
32 http: Arc<HttpClient>,
33}
34
35impl std::fmt::Debug for Relay {
36 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
37 f.debug_struct("Relay").finish_non_exhaustive()
38 }
39}
40
41impl Relay {
42 /// Create a `Relay` client with default settings.
43 ///
44 /// Panics if the API key is empty or the HTTP client cannot be
45 /// initialised. Use [`Relay::builder`] for fallible construction.
46 pub fn new(api_key: impl Into<String>) -> Self {
47 Self::builder()
48 .api_key(api_key)
49 .build()
50 .expect("failed to build Relay client")
51 }
52
53 /// Return a [`RelayBuilder`] for fine-grained configuration.
54 pub fn builder() -> RelayBuilder {
55 RelayBuilder::default()
56 }
57
58 pub(crate) fn from_http(http: Arc<HttpClient>) -> Self {
59 Self { http }
60 }
61
62 /// Return a [`MessagesClient`] for sending and listing webhook messages.
63 pub fn messages(&self) -> MessagesClient {
64 MessagesClient {
65 http: Arc::clone(&self.http),
66 }
67 }
68}
69
70/// Builder for a standalone [`Relay`] client.
71///
72/// # Example
73///
74/// ```no_run
75/// use nautilus_rs::Relay;
76///
77/// let relay = Relay::builder()
78/// .api_key("vrn_relay_live_sk_…")
79/// .timeout_secs(15)
80/// .build()
81/// .expect("invalid configuration");
82/// ```
83#[derive(Default)]
84pub struct RelayBuilder {
85 api_key: Option<String>,
86 base_url: Option<String>,
87 timeout_secs: Option<u64>,
88}
89
90impl RelayBuilder {
91 /// Set the Relay API key (**required**).
92 pub fn api_key(mut self, key: impl Into<String>) -> Self {
93 self.api_key = Some(key.into());
94 self
95 }
96
97 /// Override the API base URL (default: `https://api.vernesoft.com`).
98 pub fn base_url(mut self, url: impl Into<String>) -> Self {
99 self.base_url = Some(url.into());
100 self
101 }
102
103 /// Set the HTTP request timeout in seconds (default: `30`).
104 pub fn timeout_secs(mut self, secs: u64) -> Self {
105 self.timeout_secs = Some(secs);
106 self
107 }
108
109 /// Consume the builder and return a configured [`Relay`].
110 ///
111 /// # Errors
112 ///
113 /// Returns [`Error::Config`] if the API key was not set.
114 pub fn build(self) -> Result<Relay, Error> {
115 let key = self
116 .api_key
117 .ok_or_else(|| Error::Config("relay API key is required".into()))?;
118 let http = HttpClient::new(key, self.base_url, self.timeout_secs)?;
119 Ok(Relay {
120 http: Arc::new(http),
121 })
122 }
123}
124
125/// Access to the `/v1/relay/messages` endpoints.
126///
127/// Obtain via [`Relay::messages`].
128pub struct MessagesClient {
129 http: Arc<HttpClient>,
130}
131
132impl MessagesClient {
133 /// Send an event to all subscribed endpoints.
134 ///
135 /// Maps to `POST /v1/relay/messages`.
136 ///
137 /// # Example
138 ///
139 /// ```no_run
140 /// use nautilus_rs::{Relay, SendMessageParams};
141 /// use serde_json::json;
142 ///
143 /// # async fn run() -> Result<(), nautilus_rs::Error> {
144 /// let relay = Relay::new("vrn_relay_live_sk_…");
145 /// let msg = relay.messages().send(SendMessageParams {
146 /// event_type: "user.signed_up".into(),
147 /// payload: json!({ "user_id": "usr_abc" }),
148 /// ..Default::default()
149 /// }).await?;
150 /// println!("message id: {}", msg.id);
151 /// # Ok(())
152 /// # }
153 /// ```
154 pub async fn send(&self, params: SendMessageParams) -> Result<Message, Error> {
155 self.http.post("/v1/relay/messages", ¶ms, false).await
156 }
157
158 /// Retrieve a paginated list of past messages.
159 ///
160 /// Maps to `GET /v1/relay/messages`.
161 ///
162 /// # Example
163 ///
164 /// ```no_run
165 /// use nautilus_rs::{Relay, ListMessagesParams};
166 ///
167 /// # async fn run() -> Result<(), nautilus_rs::Error> {
168 /// let relay = Relay::new("vrn_relay_live_sk_…");
169 /// let page = relay.messages().list(ListMessagesParams {
170 /// limit: Some(20),
171 /// event_type: Some("order.placed".into()),
172 /// ..Default::default()
173 /// }).await?;
174 ///
175 /// for msg in &page.data {
176 /// println!("{} — {}", msg.timestamp, msg.event_type);
177 /// }
178 /// # Ok(())
179 /// # }
180 /// ```
181 pub async fn list(&self, params: ListMessagesParams) -> Result<Paginated<Message>, Error> {
182 let mut query = vec![];
183 if let Some(limit) = params.limit {
184 query.push(format!("limit={limit}"));
185 }
186 if let Some(cursor) = ¶ms.cursor {
187 query.push(format!("cursor={cursor}"));
188 }
189 if let Some(event_type) = ¶ms.event_type {
190 query.push(format!("event_type={event_type}"));
191 }
192
193 let path = if query.is_empty() {
194 "/v1/relay/messages".to_string()
195 } else {
196 format!("/v1/relay/messages?{}", query.join("&"))
197 };
198
199 self.http.get(&path).await
200 }
201}