datadog_api_client/datadogV1/api/
api_service_checks.rs

1// Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License.
2// This product includes software developed at Datadog (https://www.datadoghq.com/).
3// Copyright 2019-Present Datadog, Inc.
4use crate::datadog;
5use flate2::{
6    write::{GzEncoder, ZlibEncoder},
7    Compression,
8};
9use reqwest::header::{HeaderMap, HeaderValue};
10use serde::{Deserialize, Serialize};
11use std::io::Write;
12
13/// SubmitServiceCheckError is a struct for typed errors of method [`ServiceChecksAPI::submit_service_check`]
14#[derive(Debug, Clone, Serialize, Deserialize)]
15#[serde(untagged)]
16pub enum SubmitServiceCheckError {
17    APIErrorResponse(crate::datadogV1::model::APIErrorResponse),
18    UnknownValue(serde_json::Value),
19}
20
21/// The service check endpoint allows you to post check statuses for use with monitors.
22/// Service check messages are limited to 500 characters. If a check is posted with a message
23/// containing more than 500 characters, only the first 500 characters are displayed. Messages
24/// are limited for checks with a Critical or Warning status, they are dropped for checks with
25/// an OK status.
26///
27/// - [Read more about Service Check monitors][1].
28/// - [Read more about Process Check monitors][2].
29/// - [Read more about Network monitors][3].
30/// - [Read more about Custom Check monitors][4].
31/// - [Read more about Service Checks and status codes][5].
32///
33/// [1]: <https://docs.datadoghq.com/monitors/types/service_check/>
34/// [2]: <https://docs.datadoghq.com/monitors/create/types/process_check/?tab=checkalert>
35/// [3]: <https://docs.datadoghq.com/monitors/create/types/network/?tab=checkalert>
36/// [4]: <https://docs.datadoghq.com/monitors/create/types/custom_check/?tab=checkalert>
37/// [5]: <https://docs.datadoghq.com/developers/service_checks/>
38#[derive(Debug, Clone)]
39pub struct ServiceChecksAPI {
40    config: datadog::Configuration,
41    client: reqwest_middleware::ClientWithMiddleware,
42}
43
44impl Default for ServiceChecksAPI {
45    fn default() -> Self {
46        Self::with_config(datadog::Configuration::default())
47    }
48}
49
50impl ServiceChecksAPI {
51    pub fn new() -> Self {
52        Self::default()
53    }
54    pub fn with_config(config: datadog::Configuration) -> Self {
55        let mut reqwest_client_builder = reqwest::Client::builder();
56
57        if let Some(proxy_url) = &config.proxy_url {
58            let proxy = reqwest::Proxy::all(proxy_url).expect("Failed to parse proxy URL");
59            reqwest_client_builder = reqwest_client_builder.proxy(proxy);
60        }
61
62        let mut middleware_client_builder =
63            reqwest_middleware::ClientBuilder::new(reqwest_client_builder.build().unwrap());
64
65        if config.enable_retry {
66            struct RetryableStatus;
67            impl reqwest_retry::RetryableStrategy for RetryableStatus {
68                fn handle(
69                    &self,
70                    res: &Result<reqwest::Response, reqwest_middleware::Error>,
71                ) -> Option<reqwest_retry::Retryable> {
72                    match res {
73                        Ok(success) => reqwest_retry::default_on_request_success(success),
74                        Err(_) => None,
75                    }
76                }
77            }
78            let backoff_policy = reqwest_retry::policies::ExponentialBackoff::builder()
79                .build_with_max_retries(config.max_retries);
80
81            let retry_middleware =
82                reqwest_retry::RetryTransientMiddleware::new_with_policy_and_strategy(
83                    backoff_policy,
84                    RetryableStatus,
85                );
86
87            middleware_client_builder = middleware_client_builder.with(retry_middleware);
88        }
89
90        let client = middleware_client_builder.build();
91
92        Self { config, client }
93    }
94
95    pub fn with_client_and_config(
96        config: datadog::Configuration,
97        client: reqwest_middleware::ClientWithMiddleware,
98    ) -> Self {
99        Self { config, client }
100    }
101
102    /// Submit a list of Service Checks.
103    ///
104    /// **Notes**:
105    /// - A valid API key is required.
106    /// - Service checks can be submitted up to 10 minutes in the past.
107    pub async fn submit_service_check(
108        &self,
109        body: Vec<crate::datadogV1::model::ServiceCheck>,
110    ) -> Result<
111        crate::datadogV1::model::IntakePayloadAccepted,
112        datadog::Error<SubmitServiceCheckError>,
113    > {
114        match self.submit_service_check_with_http_info(body).await {
115            Ok(response_content) => {
116                if let Some(e) = response_content.entity {
117                    Ok(e)
118                } else {
119                    Err(datadog::Error::Serde(serde::de::Error::custom(
120                        "response content was None",
121                    )))
122                }
123            }
124            Err(err) => Err(err),
125        }
126    }
127
128    /// Submit a list of Service Checks.
129    ///
130    /// **Notes**:
131    /// - A valid API key is required.
132    /// - Service checks can be submitted up to 10 minutes in the past.
133    pub async fn submit_service_check_with_http_info(
134        &self,
135        body: Vec<crate::datadogV1::model::ServiceCheck>,
136    ) -> Result<
137        datadog::ResponseContent<crate::datadogV1::model::IntakePayloadAccepted>,
138        datadog::Error<SubmitServiceCheckError>,
139    > {
140        let local_configuration = &self.config;
141        let operation_id = "v1.submit_service_check";
142
143        let local_client = &self.client;
144
145        let local_uri_str = format!(
146            "{}/api/v1/check_run",
147            local_configuration.get_operation_host(operation_id)
148        );
149        let mut local_req_builder =
150            local_client.request(reqwest::Method::POST, local_uri_str.as_str());
151
152        // build headers
153        let mut headers = HeaderMap::new();
154        headers.insert("Content-Type", HeaderValue::from_static("application/json"));
155        headers.insert("Accept", HeaderValue::from_static("application/json"));
156
157        // build user agent
158        match HeaderValue::from_str(local_configuration.user_agent.as_str()) {
159            Ok(user_agent) => headers.insert(reqwest::header::USER_AGENT, user_agent),
160            Err(e) => {
161                log::warn!("Failed to parse user agent header: {e}, falling back to default");
162                headers.insert(
163                    reqwest::header::USER_AGENT,
164                    HeaderValue::from_static(datadog::DEFAULT_USER_AGENT.as_str()),
165                )
166            }
167        };
168
169        // build auth
170        if let Some(local_key) = local_configuration.auth_keys.get("apiKeyAuth") {
171            headers.insert(
172                "DD-API-KEY",
173                HeaderValue::from_str(local_key.key.as_str())
174                    .expect("failed to parse DD-API-KEY header"),
175            );
176        };
177
178        // build body parameters
179        let output = Vec::new();
180        let mut ser = serde_json::Serializer::with_formatter(output, datadog::DDFormatter);
181        if body.serialize(&mut ser).is_ok() {
182            if let Some(content_encoding) = headers.get("Content-Encoding") {
183                match content_encoding.to_str().unwrap_or_default() {
184                    "gzip" => {
185                        let mut enc = GzEncoder::new(Vec::new(), Compression::default());
186                        let _ = enc.write_all(ser.into_inner().as_slice());
187                        match enc.finish() {
188                            Ok(buf) => {
189                                local_req_builder = local_req_builder.body(buf);
190                            }
191                            Err(e) => return Err(datadog::Error::Io(e)),
192                        }
193                    }
194                    "deflate" => {
195                        let mut enc = ZlibEncoder::new(Vec::new(), Compression::default());
196                        let _ = enc.write_all(ser.into_inner().as_slice());
197                        match enc.finish() {
198                            Ok(buf) => {
199                                local_req_builder = local_req_builder.body(buf);
200                            }
201                            Err(e) => return Err(datadog::Error::Io(e)),
202                        }
203                    }
204                    "zstd1" => {
205                        let mut enc = zstd::stream::Encoder::new(Vec::new(), 0).unwrap();
206                        let _ = enc.write_all(ser.into_inner().as_slice());
207                        match enc.finish() {
208                            Ok(buf) => {
209                                local_req_builder = local_req_builder.body(buf);
210                            }
211                            Err(e) => return Err(datadog::Error::Io(e)),
212                        }
213                    }
214                    _ => {
215                        local_req_builder = local_req_builder.body(ser.into_inner());
216                    }
217                }
218            } else {
219                local_req_builder = local_req_builder.body(ser.into_inner());
220            }
221        }
222
223        local_req_builder = local_req_builder.headers(headers);
224        let local_req = local_req_builder.build()?;
225        log::debug!("request content: {:?}", local_req.body());
226        let local_resp = local_client.execute(local_req).await?;
227
228        let local_status = local_resp.status();
229        let local_content = local_resp.text().await?;
230        log::debug!("response content: {}", local_content);
231
232        if !local_status.is_client_error() && !local_status.is_server_error() {
233            match serde_json::from_str::<crate::datadogV1::model::IntakePayloadAccepted>(
234                &local_content,
235            ) {
236                Ok(e) => {
237                    return Ok(datadog::ResponseContent {
238                        status: local_status,
239                        content: local_content,
240                        entity: Some(e),
241                    })
242                }
243                Err(e) => return Err(datadog::Error::Serde(e)),
244            };
245        } else {
246            let local_entity: Option<SubmitServiceCheckError> =
247                serde_json::from_str(&local_content).ok();
248            let local_error = datadog::ResponseContent {
249                status: local_status,
250                content: local_content,
251                entity: local_entity,
252            };
253            Err(datadog::Error::ResponseError(local_error))
254        }
255    }
256}