#![allow(dead_code)]
#![allow(unreachable_pub)]
use std::sync::LazyLock;
use regex::Regex;
use serde::{Deserialize, Serialize};
use vector_lib::{
event::DatadogMetricOriginMetadata, schema::meaning, sensitive_string::SensitiveString,
};
pub(crate) const DD_US_SITE: &str = "datadoghq.com";
pub(crate) const DD_EU_SITE: &str = "datadoghq.eu";
pub const DDTAGS: &str = "ddtags";
pub const DD_RESERVED_SEMANTIC_ATTRS: [(&str, &str); 6] = [
(meaning::SEVERITY, "status"), (meaning::TIMESTAMP, "timestamp"),
(meaning::HOST, "hostname"),
(meaning::SERVICE, "service"),
(meaning::SOURCE, "ddsource"),
(meaning::TAGS, DDTAGS),
];
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
pub struct DatadogSeriesMetric {
pub metric: String,
pub r#type: DatadogMetricType,
pub interval: Option<u32>,
pub points: Vec<DatadogPoint<f64>>,
pub tags: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub host: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub source_type_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub device: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub metadata: Option<DatadogSeriesMetricMetadata>,
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
pub struct DatadogSeriesMetricMetadata {
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) origin: Option<DatadogMetricOriginMetadata>,
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum DatadogMetricType {
Gauge,
Count,
Rate,
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
pub struct DatadogPoint<T>(pub i64, pub T);
pub(crate) fn get_api_base_endpoint(endpoint: Option<&str>, site: &str) -> String {
endpoint.map_or_else(|| format!("https://api.{}", site), compute_api_endpoint)
}
fn compute_api_endpoint(endpoint: &str) -> String {
static DOMAIN_REGEX: LazyLock<Regex> = LazyLock::new(|| {
Regex::new(r"(?:[a-z]{2}\d\.)?(datadoghq\.[a-z]+|ddog-gov\.com)/*$")
.expect("Could not build Datadog domain regex")
});
if let Some(caps) = DOMAIN_REGEX.captures(endpoint) {
format!("https://api.{}", &caps[1])
} else {
endpoint.into()
}
}
#[derive(Clone, Debug, Derivative)]
#[derivative(Default)]
pub struct Options {
#[derivative(Default(value = "default_api_key()"))]
pub api_key: Option<SensitiveString>,
#[derivative(Default(value = "default_site()"))]
pub site: String,
}
fn default_api_key() -> Option<SensitiveString> {
std::env::var("DD_API_KEY").ok().map(Into::into)
}
pub(crate) fn default_site() -> String {
std::env::var("DD_SITE").unwrap_or(DD_US_SITE.to_string())
}
#[cfg(test)]
mod tests {
use similar_asserts::assert_eq;
use super::*;
#[test]
fn computes_correct_api_endpoint() {
assert_eq!(
compute_api_endpoint("https://http-intake.logs.datadoghq.com"),
"https://api.datadoghq.com"
);
assert_eq!(
compute_api_endpoint("https://http-intake.logs.datadoghq.com/"),
"https://api.datadoghq.com"
);
assert_eq!(
compute_api_endpoint("http://http-intake.logs.datadoghq.com/"),
"https://api.datadoghq.com"
);
assert_eq!(
compute_api_endpoint("https://anythingelse.datadoghq.com/"),
"https://api.datadoghq.com"
);
assert_eq!(
compute_api_endpoint("https://this.datadoghq.eu/"),
"https://api.datadoghq.eu"
);
assert_eq!(
compute_api_endpoint("http://datadog.com/"),
"http://datadog.com/"
);
}
#[test]
fn gets_correct_api_base_endpoint() {
assert_eq!(
get_api_base_endpoint(None, DD_US_SITE),
"https://api.datadoghq.com"
);
assert_eq!(
get_api_base_endpoint(None, "datadog.net"),
"https://api.datadog.net"
);
assert_eq!(
get_api_base_endpoint(Some("https://logs.datadoghq.eu"), DD_US_SITE),
"https://api.datadoghq.eu"
);
}
}