1use std::collections::HashMap;
5
6use crossbeam_channel::Receiver;
7use log::info;
8use serde_json::Value;
9
10use crate::errors::Result;
11use crate::fields::FieldHolder;
12use crate::response::Response;
13use crate::sender::Sender;
14use crate::Event;
15use crate::{Builder, DynamicFieldFunc};
16
17const DEFAULT_API_HOST: &str = "https://api.honeycomb.io";
18const DEFAULT_API_KEY: &str = "";
19const DEFAULT_DATASET: &str = "librust-dataset";
20const DEFAULT_SAMPLE_RATE: usize = 1;
21
22#[derive(Debug, Clone)]
25pub struct Options {
26 pub api_key: String,
31
32 pub api_host: String,
35
36 pub dataset: String,
41
42 pub sample_rate: usize,
46}
47
48impl Default for Options {
49 fn default() -> Self {
50 Self {
51 api_key: DEFAULT_API_KEY.to_string(),
52 dataset: DEFAULT_DATASET.to_string(),
53 api_host: DEFAULT_API_HOST.to_string(),
54 sample_rate: DEFAULT_SAMPLE_RATE,
55 }
56 }
57}
58
59#[derive(Debug, Clone)]
62pub struct Client<T: Sender> {
63 pub(crate) options: Options,
64 pub transmission: T,
66
67 builder: Builder,
68}
69
70impl<T> Client<T>
71where
72 T: Sender,
73{
74 pub fn new(options: Options, transmission: T) -> Self {
80 info!("Creating honey client");
81
82 let mut c = Self {
83 transmission,
84 options: options.clone(),
85 builder: Builder::new(options),
86 };
87 c.start();
88 c
89 }
90
91 fn start(&mut self) {
92 self.transmission.start();
93 }
94
95 pub fn add(&mut self, data: HashMap<String, Value>) {
99 self.builder.add(data);
100 }
101
102 pub fn add_field(&mut self, name: &str, value: Value) {
105 self.builder.add_field(name, value);
106 }
107
108 pub fn add_dynamic_field(&mut self, name: &str, func: DynamicFieldFunc) {
112 self.builder.add_dynamic_field(name, func);
113 }
114
115 pub fn close(mut self) -> Result<()> {
118 info!("closing libhoney client");
119 self.transmission.stop()
120 }
121
122 pub fn flush(&mut self) -> Result<()> {
129 info!("flushing libhoney client");
130 self.transmission.stop()?;
131 self.transmission.start();
132 Ok(())
133 }
134
135 pub fn new_builder(&self) -> Builder {
138 self.builder.clone()
139 }
140
141 pub fn new_event(&self) -> Event {
144 self.builder.new_event()
145 }
146
147 pub fn responses(&self) -> Receiver<Response> {
149 self.transmission.responses()
150 }
151}
152
153#[cfg(test)]
154mod tests {
155 use super::{Client, FieldHolder, Options, Value};
156 use crate::transmission::{self, Transmission};
157
158 #[test]
159 fn test_init() {
160 let client = Client::new(
161 Options::default(),
162 Transmission::new(transmission::Options::default()).unwrap(),
163 );
164 client.close().unwrap();
165 }
166
167 #[test]
168 fn test_flush() {
169 use reqwest::StatusCode;
170 use serde_json::json;
171
172 let api_host = &mockito::server_url();
173 let _m = mockito::mock(
174 "POST",
175 mockito::Matcher::Regex(r"/1/batch/(.*)$".to_string()),
176 )
177 .with_status(200)
178 .with_header("content-type", "application/json")
179 .with_body("[{ \"status\": 202 }]")
180 .create();
181
182 let mut client = Client::new(
183 Options {
184 api_key: "some api key".to_string(),
185 api_host: api_host.to_string(),
186 ..Options::default()
187 },
188 Transmission::new(transmission::Options::default()).unwrap(),
189 );
190
191 let mut event = client.new_event();
192 event.add_field("some_field", Value::String("some_value".to_string()));
193 event.metadata = Some(json!("some metadata in a string"));
194 event.send(&mut client).unwrap();
195
196 let response = client.responses().iter().next().unwrap();
197 assert_eq!(response.status_code, Some(StatusCode::ACCEPTED));
198 assert_eq!(response.metadata, Some(json!("some metadata in a string")));
199
200 client.flush().unwrap();
201
202 event = client.new_event();
203 event.add_field("some_field", Value::String("some_value".to_string()));
204 event.metadata = Some(json!("some metadata in a string"));
205 event.send(&mut client).unwrap();
206
207 let response = client.responses().iter().next().unwrap();
208 assert_eq!(response.status_code, Some(StatusCode::ACCEPTED));
209 assert_eq!(response.metadata, Some(json!("some metadata in a string")));
210
211 client.close().unwrap();
212 }
213
214 #[test]
215 fn test_send_without_api_key() {
216 use serde_json::json;
217
218 use crate::errors::ErrorKind;
219
220 let api_host = &mockito::server_url();
221 let _m = mockito::mock(
222 "POST",
223 mockito::Matcher::Regex(r"/1/batch/(.*)$".to_string()),
224 )
225 .with_status(200)
226 .with_header("content-type", "application/json")
227 .with_body("[{ \"status\": 202 }]")
228 .create();
229
230 let mut client = Client::new(
231 Options {
232 api_host: api_host.to_string(),
233 ..Options::default()
234 },
235 Transmission::new(transmission::Options::default()).unwrap(),
236 );
237
238 let mut event = client.new_event();
239 event.add_field("some_field", Value::String("some_value".to_string()));
240 event.metadata = Some(json!("some metadata in a string"));
241 let err = event.send(&mut client).err().unwrap();
242
243 assert_eq!(err.kind, ErrorKind::MissingOption);
244 assert_eq!(
245 err.message,
246 "missing option 'api_key', can't send to Honeycomb"
247 );
248 client.close().unwrap();
249 }
250}