datadog_client/
metrics.rs1use crate::client::{Client, Error};
2use serde::ser::SerializeSeq;
3use serde::{Serialize, Serializer};
4
5#[derive(Clone, Debug, Serialize)]
6#[serde(rename_all = "snake_case")]
7pub enum Type {
8 Count,
9 Gauge,
10 Rate,
11}
12
13#[derive(Clone, Debug)]
14pub struct Point {
15 timestamp: u64,
16 value: f64,
17}
18
19impl Point {
20 pub fn new(timestamp: u64, value: f64) -> Self {
21 Self { timestamp, value }
22 }
23}
24
25impl Serialize for Point {
26 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
27 where
28 S: Serializer,
29 {
30 let mut seq = serializer.serialize_seq(Some(2))?;
31 seq.serialize_element(&self.timestamp)?;
32 seq.serialize_element(&self.value)?;
33 seq.end()
34 }
35}
36
37#[derive(Debug, Clone, Serialize)]
51pub struct Serie {
52 #[serde(skip_serializing_if = "Option::is_none")]
54 host: Option<String>,
55 #[serde(skip_serializing_if = "Option::is_none")]
57 interval: Option<i64>,
58 metric: String,
60 points: Vec<Point>,
63 tags: Vec<String>,
65 #[serde(rename = "type")]
67 dtype: Type,
68}
69
70impl Serie {
71 pub fn new(metric: String, dtype: Type) -> Self {
72 Self {
73 host: None,
74 interval: None,
75 metric,
76 points: Vec::new(),
77 tags: Vec::new(),
78 dtype,
79 }
80 }
81}
82
83impl Serie {
84 pub fn set_host(mut self, host: String) -> Self {
85 self.host = Some(host);
86 self
87 }
88
89 pub fn set_interval(mut self, interval: i64) -> Self {
90 self.interval = Some(interval);
91 self
92 }
93
94 pub fn set_points(mut self, points: Vec<Point>) -> Self {
95 self.points = points;
96 self
97 }
98
99 pub fn add_point(mut self, point: Point) -> Self {
100 self.points.push(point);
101 self
102 }
103
104 pub fn set_tags(mut self, tags: Vec<String>) -> Self {
105 self.tags = tags;
106 self
107 }
108
109 pub fn add_tag(mut self, tag: String) -> Self {
110 self.tags.push(tag);
111 self
112 }
113}
114
115impl Client {
116 pub async fn post_metrics(&self, series: &[Serie]) -> Result<(), Error> {
121 let payload = serde_json::json!({ "series": series });
122 self.post("/api/v1/series", &payload).await
123 }
124}
125
126#[cfg(test)]
127mod tests {
128 use super::*;
129 use mockito::mock;
130
131 #[test]
132 fn serialize_point() {
133 let point = Point::new(1234, 12.34);
134 assert_eq!(serde_json::to_string(&point).unwrap(), "[1234,12.34]");
135 }
136
137 #[test]
138 fn serialize_serie() {
139 let serie = Serie::new("metric".to_string(), Type::Count)
140 .add_point(Point::new(1234, 1.234))
141 .add_tag("tag".to_string())
142 .set_host("host".to_string());
143 assert_eq!(
144 serde_json::to_string(&serie).unwrap(),
145 "{\"host\":\"host\",\"metric\":\"metric\",\"points\":[[1234,1.234]],\"tags\":[\"tag\"],\"type\":\"count\"}"
146 );
147 }
148
149 #[tokio::test]
150 async fn post_metrics_success() {
151 let call = mock("POST", "/api/v1/series").with_status(202).create();
152 let client = Client::new(mockito::server_url(), "fake-api-key".to_string());
153 let series = vec![
154 Serie::new("something".to_string(), Type::Gauge).add_point(Point::new(1234, 12.34))
155 ];
156 let result = client.post_metrics(&series).await;
157 assert!(result.is_ok());
158 call.expect(1);
159 }
160
161 #[tokio::test]
162 async fn post_metrics_unauthorized() {
163 let call = mock("POST", "/api/v1/series")
164 .with_status(403)
165 .with_body("{\"errors\":[\"Authentication error\"]}")
166 .create();
167 let client = Client::new(mockito::server_url(), "fake-api-key".to_string());
168 let series = vec![
169 Serie::new("something".to_string(), Type::Gauge).add_point(Point::new(1234, 12.34))
170 ];
171 let result = client.post_metrics(&series).await;
172 assert!(result.is_err());
173 call.expect(1);
174 }
175}