1use std::collections::HashMap;
2
3use chrono::prelude::{DateTime, Utc};
4use log::info;
5use rand::Rng;
6
7use crate::client;
8use crate::errors::{Error, Result};
9use crate::fields::FieldHolder;
10use crate::sender::Sender;
11use crate::Value;
12
13pub type Metadata = Option<Value>;
15
16#[derive(Debug, Clone)]
19pub struct Event {
20 pub(crate) options: client::Options,
21 pub(crate) timestamp: DateTime<Utc>,
22 pub(crate) fields: HashMap<String, Value>,
23 pub(crate) metadata: Metadata,
24 sent: bool,
25}
26
27impl FieldHolder for Event {
28 fn add(&mut self, data: HashMap<String, Value>) {
29 if !self.sent {
30 self.fields.extend(data);
31 }
32 }
33
34 fn add_field(&mut self, name: &str, value: Value) {
36 if !self.sent {
37 self.fields.insert(name.to_string(), value);
38 }
39 }
40
41 fn add_func<F>(&mut self, func: F)
44 where
45 F: Fn() -> Result<(String, Value)>,
46 {
47 if !self.sent {
48 while let Ok((name, value)) = func() {
49 self.add_field(&name, value);
50 }
51 }
52 }
53}
54
55impl Event {
56 pub fn new(options: &client::Options) -> Self {
58 Self {
59 options: options.clone(),
60 timestamp: Utc::now(),
61 fields: HashMap::new(),
62 metadata: None,
63 sent: false,
64 }
65 }
66
67 pub fn send<T: Sender>(&mut self, client: &mut client::Client<T>) -> Result<()> {
82 if self.should_drop() {
83 info!("dropping event due to sampling");
84 return Ok(());
85 }
86 self.send_presampled(client)
87 }
88
89 pub fn send_presampled<T: Sender>(&mut self, client: &mut client::Client<T>) -> Result<()> {
104 if self.fields.is_empty() {
105 return Err(Error::missing_event_fields());
106 }
107
108 if self.options.api_host.is_empty() {
109 return Err(Error::missing_option("api_host", "can't send to Honeycomb"));
110 }
111
112 if self.options.api_key.is_empty() {
113 return Err(Error::missing_option("api_key", "can't send to Honeycomb"));
114 }
115
116 if self.options.dataset.is_empty() {
117 return Err(Error::missing_option("dataset", "can't send to Honeycomb"));
118 }
119
120 self.sent = true;
121 client.transmission.send(self.clone());
122 Ok(())
123 }
124
125 pub fn set_sample_rate(&mut self, sample_rate: usize) {
127 self.options.sample_rate = sample_rate;
128 }
129
130 pub fn set_timestamp(&mut self, timestamp: DateTime<Utc>) {
132 self.timestamp = timestamp;
133 }
134
135 pub fn set_metadata(&mut self, metadata: Metadata) {
137 self.metadata = metadata;
138 }
139
140 pub fn metadata(&self) -> Metadata {
142 self.metadata.clone()
143 }
144
145 pub fn fields(&self) -> HashMap<String, Value> {
147 self.fields.clone()
148 }
149
150 pub fn get_fields_mut(&mut self) -> &mut HashMap<String, Value> {
152 &mut self.fields
153 }
154
155 fn should_drop(&self) -> bool {
156 if self.options.sample_rate <= 1 {
157 return false;
158 }
159 rand::thread_rng().gen_range(0..self.options.sample_rate) != 0
160 }
161
162 pub(crate) fn stop_event() -> Self {
163 let mut h: HashMap<String, Value> = HashMap::new();
164 h.insert("internal_stop_event".to_string(), Value::Null);
165
166 Self {
167 options: client::Options::default(),
168 timestamp: Utc::now(),
169 fields: h,
170 metadata: None,
171 sent: false,
172 }
173 }
174}
175
176#[cfg(test)]
177mod tests {
178 use reqwest::StatusCode;
179
180 use super::*;
181 use crate::client;
182
183 #[test]
184 fn test_add() {
185 let mut e = Event::new(&client::Options {
186 api_key: "some_api_key".to_string(),
187 ..client::Options::default()
188 });
189 let now = Value::String(Utc::now().to_rfc3339());
190 e.add_field("my_timestamp", now.clone());
191
192 assert_eq!(e.options.api_key, "some_api_key");
193 assert_eq!(e.fields["my_timestamp"], now);
194 }
195
196 #[test]
197 fn test_send() {
198 use crate::transmission;
199
200 let api_host = &mockito::server_url();
201 let _m = mockito::mock(
202 "POST",
203 mockito::Matcher::Regex(r"/1/batch/(.*)$".to_string()),
204 )
205 .with_status(200)
206 .with_header("content-type", "application/json")
207 .with_body("[{ \"status\": 200 }]")
208 .create();
209
210 let options = client::Options {
211 api_key: "some api key".to_string(),
212 api_host: api_host.to_string(),
213 ..client::Options::default()
214 };
215
216 let mut client = client::Client::new(
217 options.clone(),
218 transmission::Transmission::new(transmission::Options {
219 max_batch_size: 1,
220 ..transmission::Options::default()
221 })
222 .unwrap(),
223 );
224
225 let mut e = Event::new(&options);
226 e.add_field("field_name", Value::String("field_value".to_string()));
227 e.send(&mut client).unwrap();
228
229 if let Some(only) = client.transmission.responses().iter().next() {
230 assert_eq!(only.status_code, Some(StatusCode::OK));
231 }
232 client.close().unwrap();
233 }
234
235 #[test]
236 fn test_empty() {
237 use crate::errors::ErrorKind;
238 use crate::transmission;
239
240 let api_host = &mockito::server_url();
241 let _m = mockito::mock(
242 "POST",
243 mockito::Matcher::Regex(r"/1/batch/(.*)$".to_string()),
244 )
245 .with_status(200)
246 .with_header("content-type", "application/json")
247 .with_body("[{ \"status\": 200 }]")
248 .create();
249
250 let mut client = client::Client::new(
251 client::Options {
252 api_key: "some api key".to_string(),
253 api_host: api_host.to_string(),
254 ..client::Options::default()
255 },
256 transmission::Transmission::new(transmission::Options {
257 max_batch_size: 1,
258 ..transmission::Options::default()
259 })
260 .unwrap(),
261 );
262
263 let mut e = client.new_event();
264 assert_eq!(
265 e.send(&mut client).err().unwrap().kind,
266 ErrorKind::MissingEventFields
267 );
268 client.close().unwrap();
269 }
270}