From 55d83d4f99cbc0a811f90469a3dc5af811b732b2 Mon Sep 17 00:00:00 2001 From: Cristiano Piemontese Date: Mon, 25 Nov 2024 15:08:08 +0100 Subject: [PATCH] add actuall support for new macro --- .mise.toml | 2 + Dockerfile | 2 +- src/client.rs | 13 +++++ src/lib.rs | 28 ++++++++++ src/macros/event_with_options.rs | 23 ++++++++ src/macros/mod.rs | 1 + src/tests/event_with_options.rs | 83 +++++++++++++++++++++++++++++ src/tests/mocks/dogstatsd_client.rs | 14 +++++ src/tests/mocks/mod.rs | 25 +++++++++ src/tests/mod.rs | 1 + 10 files changed, 191 insertions(+), 1 deletion(-) create mode 100644 .mise.toml create mode 100644 src/macros/event_with_options.rs create mode 100644 src/tests/event_with_options.rs diff --git a/.mise.toml b/.mise.toml new file mode 100644 index 0000000..0bafeed --- /dev/null +++ b/.mise.toml @@ -0,0 +1,2 @@ +[tools] +rust = "1.74" diff --git a/Dockerfile b/Dockerfile index dde3513..33e8c8c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM rust:1.72.0 +FROM rust:1.74.0 WORKDIR /code diff --git a/src/client.rs b/src/client.rs index 680edf6..c2a98db 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,6 +1,7 @@ use std::future::Future; use async_trait::async_trait; +use dogstatsd::EventOptions; use crate::{ServiceCheckOptions, ServiceStatus, TagsProvider}; @@ -75,6 +76,11 @@ pub trait DogstatsdClient { fn event(&self, title: &str, text: &str, tags: impl TagsProvider) where S: AsRef; + + /// Send a custom event as a title and a body + fn event_with_options(&self, title: &str, text: &str, tags: impl TagsProvider, options: Option) + where + S: AsRef; } #[async_trait] @@ -172,4 +178,11 @@ impl DogstatsdClient for dogstatsd::Client { { let _ = self.event(title, text, tags.as_ref()); } + + fn event_with_options(&self, title: &str, text: &str, tags: impl TagsProvider, options: Option) + where + S: AsRef, + { + let _ = self.event_with_options(title, text, tags.as_ref(), options); + } } diff --git a/src/lib.rs b/src/lib.rs index 31265f3..2cc0820 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -114,6 +114,7 @@ use std::future::Future; use configuration::Configuration; +use dogstatsd::EventOptions; pub use dogstatsd::{ServiceCheckOptions, ServiceStatus}; use once_cell::sync::OnceCell; @@ -295,6 +296,18 @@ impl Datadog { } } + /// Send a custom event as a title, a body and some options + pub fn event_with_options>( + metric: impl AsRef, + text: impl AsRef, + tags: impl TagsProvider, + options: Option, + ) { + if let Some(instance) = INSTANCE.get() { + instance.do_event_with_options(metric.as_ref(), text.as_ref(), tags, options); + } + } + /// Acquire a timing guard. /// When this guard is dropped, it will emit a timing metric for the duration it /// existed. The metric name is metric, and the tags are tags. @@ -455,4 +468,19 @@ impl Datadog { self.tag_tracker.track(&self.inner, metric.as_ref(), tags), ); } + + pub(crate) fn do_event_with_options>( + &self, + metric: impl AsRef, + text: impl AsRef, + tags: impl TagsProvider, + options: Option, + ) { + self.inner.event_with_options( + metric.as_ref(), + text.as_ref(), + self.tag_tracker.track(&self.inner, metric.as_ref(), tags), + options, + ); + } } diff --git a/src/macros/event_with_options.rs b/src/macros/event_with_options.rs new file mode 100644 index 0000000..985aa09 --- /dev/null +++ b/src/macros/event_with_options.rs @@ -0,0 +1,23 @@ +/// Send a custom event as a title and a body +/// NOTE: Try to minimise variation in tag values (avoid things like timestamps or ids). See note in lib docs! +#[macro_export] +macro_rules! event_with_options { + ($stat:path, $text:expr) => { + $crate::Datadog::event_with_options($stat.as_ref(), $text, $crate::EMPTY_TAGS, None); + }; + ($stat:expr, $text:expr) => { + $crate::Datadog::event_with_options($stat, $text, $crate::EMPTY_TAGS, None); + }; + ($stat:expr, $text:expr; $( $key:literal => $value:literal ), *) => { + $crate::Datadog::event_with_options($stat, $text, &[$(::core::concat!($key, ":", $value)), *], None); + }; + ($stat:path, $text:expr; $( $key:literal => $value:literal ), *) => { + $crate::Datadog::event_with_options($stat.as_ref(), $text, &[$(::core::concat!($key, ":", $value)), *], None); + }; + ($stat:expr, $text:expr; $( $key:expr => $value:expr ), *) => { + $crate::Datadog::event_with_options($stat, $text, &[$(::std::format!("{}:{}", $key, $value).as_str()), *], None); + }; + ($stat:path, $text:expr; $( $key:expr => $value:expr ), *) => { + $crate::Datadog::event_with_options($stat.as_ref(), $text, &[$(::std::format!("{}:{}", $key, $value).as_str()), *], None); + }; +} diff --git a/src/macros/mod.rs b/src/macros/mod.rs index 48d2415..9837f69 100644 --- a/src/macros/mod.rs +++ b/src/macros/mod.rs @@ -4,6 +4,7 @@ mod count; mod decr; mod distribution; mod event; +mod event_with_options; mod gauge; mod histogram; mod incr; diff --git a/src/tests/event_with_options.rs b/src/tests/event_with_options.rs new file mode 100644 index 0000000..ba7735d --- /dev/null +++ b/src/tests/event_with_options.rs @@ -0,0 +1,83 @@ +use dogstatsd::EventAlertType; +use dogstatsd::EventOptions; +use dogstatsd::EventPriority; + +use crate::event_with_options; +use crate::tests::mocks; +use crate::tests::TestEvent; +use crate::Datadog; +use crate::TagTrackerConfiguration; +use crate::EMPTY_TAGS; + +#[test] +pub fn event_with_options_with_literal() { + let mock = mocks::event_with_options_mock("test", "test_value", &[], None); + Datadog::new(mock, TagTrackerConfiguration::new()).do_event_with_options("test", "test_value", EMPTY_TAGS, None); +} + +#[test] +pub fn event_with_options_with_type() { + let mock = mocks::event_with_options_mock("test1_event", "test_value", &[], None); + Datadog::new(mock, TagTrackerConfiguration::new()).do_event_with_options( + TestEvent::Test1, + "test_value", + EMPTY_TAGS, + None, + ); +} + +#[test] +pub fn event_with_options_with_literal_and_tags() { + let mock = mocks::event_with_options_mock("test", "test_value", &["added:tag", "env:test"], None); + Datadog::new(mock, TagTrackerConfiguration::new()).do_event_with_options( + "test", + "test_value", + vec!["added:tag".to_string()], + None, + ); +} + +#[test] +pub fn event_with_options_with_type_and_tags() { + let mock = mocks::event_with_options_mock("test1_event", "test_value", &["added:tag", "env:test"], None); + Datadog::new(mock, TagTrackerConfiguration::new()).do_event_with_options( + TestEvent::Test1, + "test_value", + vec!["added:tag".to_string()], + None, + ); +} + +#[test] +pub fn event_with_options_with_options() { + let options = Some( + EventOptions::new() + .with_alert_type(EventAlertType::Info) + .with_priority(EventPriority::Low) + .with_aggregation_key("aggregation_key") + .with_source_type_name("source_type_name") + .with_hostname("hostname") + .with_timestamp(12341234), + ); + + let mock = mocks::event_with_options_mock("test1_event", "test_value", &["added:tag", "env:test"], options); + Datadog::new(mock, TagTrackerConfiguration::new()).do_event_with_options( + TestEvent::Test1, + "test_value", + vec!["added:tag".to_string()], + options, + ); +} + +#[test] +pub fn test_macro() { + let tag = String::from("tag"); + // no tags + event_with_options!("test", "test_value"); + // just literal tags + event_with_options!("test", "test_value"; "literal" => 1); + // just expression tags + event_with_options!("test", "test_value"; "expression" => tag); + // mixed tags + event_with_options!("test", "test_value"; "literal" => 1, "expression" => tag); +} diff --git a/src/tests/mocks/dogstatsd_client.rs b/src/tests/mocks/dogstatsd_client.rs index fe85789..c647239 100644 --- a/src/tests/mocks/dogstatsd_client.rs +++ b/src/tests/mocks/dogstatsd_client.rs @@ -18,6 +18,7 @@ pub(super) trait MockDogstatsdClient { fn set(&self, metric: &str, val: &str, tags: Vec); fn service_check(&self, metric: &str, val: ServiceStatus, tags: Vec, options: Option); fn event(&self, title: &str, text: &str, tags: Vec); + fn event_with_options(&self, title: &str, text: &str, tags: Vec, options: Option); } #[async_trait] @@ -143,4 +144,17 @@ impl DogstatsdClient for C { tags.as_ref().iter().map(|s| s.as_ref().to_string()).collect(), ) } + + fn event_with_options(&self, title: &str, text: &str, tags: impl TagsProvider, options: Option) + where + S: AsRef, + { + MockDogstatsdClient::event_with_options( + self, + title, + text, + tags.as_ref().iter().map(|s| s.as_ref().to_string()).collect(), + options, + ) + } } diff --git a/src/tests/mocks/mod.rs b/src/tests/mocks/mod.rs index 4e15bc1..fe479f0 100644 --- a/src/tests/mocks/mod.rs +++ b/src/tests/mocks/mod.rs @@ -48,6 +48,9 @@ mock! { /// Send a custom event as a title and a body fn event(&self, title: &str, text: &str, tags: Vec); + + /// Send a custom event as a title, a body and some options + fn event_with_options<'a>(&self, title: &str, text: &str, tags: Vec, options: Option>); } } @@ -236,6 +239,28 @@ pub fn event_mock(metric: &'static str, text: &'static str, tags: &'static [&str client_mock } +#[allow(dead_code)] +pub fn event_with_options_mock( + metric: &'static str, + text: &'static str, + tags: &'static [&str], + options: Option>, +) -> MockClient { + let mut client_mock = MockClient::new(); + client_mock + .expect_event_with_options() + .once() + .withf(move |called_metric, called_text, called_tags, called_options| { + called_metric == metric + && called_text == text + && called_tags.iter().all(|tag| tags.contains(&tag.as_str())) + && matches!((called_options, options), (Some(_), Some(_)) | (None, None)) + }) + .return_const(()); + + client_mock +} + pub fn expect_incr(mut mock: MockClient, metric: &'static str, tags: impl IntoIterator) -> MockClient { let tags = tags.into_iter().collect::>(); mock.expect_incr() diff --git a/src/tests/mod.rs b/src/tests/mod.rs index bd22192..8e74940 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -7,6 +7,7 @@ mod count; mod decr; mod distribution; mod event; +mod event_with_options; mod gauge; mod histogram; mod incr;