datadog_api_client/datadogV2/api/
api_network_device_monitoring.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 async_stream::try_stream;
6use flate2::{
7    write::{GzEncoder, ZlibEncoder},
8    Compression,
9};
10use futures_core::stream::Stream;
11use reqwest::header::{HeaderMap, HeaderValue};
12use serde::{Deserialize, Serialize};
13use std::io::Write;
14
15/// GetInterfacesOptionalParams is a struct for passing parameters to the method [`NetworkDeviceMonitoringAPI::get_interfaces`]
16#[non_exhaustive]
17#[derive(Clone, Default, Debug)]
18pub struct GetInterfacesOptionalParams {
19    /// Whether to get the IP addresses of the interfaces.
20    pub get_ip_addresses: Option<bool>,
21}
22
23impl GetInterfacesOptionalParams {
24    /// Whether to get the IP addresses of the interfaces.
25    pub fn get_ip_addresses(mut self, value: bool) -> Self {
26        self.get_ip_addresses = Some(value);
27        self
28    }
29}
30
31/// ListDevicesOptionalParams is a struct for passing parameters to the method [`NetworkDeviceMonitoringAPI::list_devices`]
32#[non_exhaustive]
33#[derive(Clone, Default, Debug)]
34pub struct ListDevicesOptionalParams {
35    /// Size for a given page. The maximum allowed value is 100.
36    pub page_size: Option<i64>,
37    /// Specific page number to return.
38    pub page_number: Option<i64>,
39    /// The field to sort the devices by.
40    pub sort: Option<String>,
41    /// Filter devices by tag.
42    pub filter_tag: Option<String>,
43}
44
45impl ListDevicesOptionalParams {
46    /// Size for a given page. The maximum allowed value is 100.
47    pub fn page_size(mut self, value: i64) -> Self {
48        self.page_size = Some(value);
49        self
50    }
51    /// Specific page number to return.
52    pub fn page_number(mut self, value: i64) -> Self {
53        self.page_number = Some(value);
54        self
55    }
56    /// The field to sort the devices by.
57    pub fn sort(mut self, value: String) -> Self {
58        self.sort = Some(value);
59        self
60    }
61    /// Filter devices by tag.
62    pub fn filter_tag(mut self, value: String) -> Self {
63        self.filter_tag = Some(value);
64        self
65    }
66}
67
68/// GetDeviceError is a struct for typed errors of method [`NetworkDeviceMonitoringAPI::get_device`]
69#[derive(Debug, Clone, Serialize, Deserialize)]
70#[serde(untagged)]
71pub enum GetDeviceError {
72    APIErrorResponse(crate::datadogV2::model::APIErrorResponse),
73    UnknownValue(serde_json::Value),
74}
75
76/// GetInterfacesError is a struct for typed errors of method [`NetworkDeviceMonitoringAPI::get_interfaces`]
77#[derive(Debug, Clone, Serialize, Deserialize)]
78#[serde(untagged)]
79pub enum GetInterfacesError {
80    APIErrorResponse(crate::datadogV2::model::APIErrorResponse),
81    UnknownValue(serde_json::Value),
82}
83
84/// ListDeviceUserTagsError is a struct for typed errors of method [`NetworkDeviceMonitoringAPI::list_device_user_tags`]
85#[derive(Debug, Clone, Serialize, Deserialize)]
86#[serde(untagged)]
87pub enum ListDeviceUserTagsError {
88    APIErrorResponse(crate::datadogV2::model::APIErrorResponse),
89    UnknownValue(serde_json::Value),
90}
91
92/// ListDevicesError is a struct for typed errors of method [`NetworkDeviceMonitoringAPI::list_devices`]
93#[derive(Debug, Clone, Serialize, Deserialize)]
94#[serde(untagged)]
95pub enum ListDevicesError {
96    APIErrorResponse(crate::datadogV2::model::APIErrorResponse),
97    UnknownValue(serde_json::Value),
98}
99
100/// UpdateDeviceUserTagsError is a struct for typed errors of method [`NetworkDeviceMonitoringAPI::update_device_user_tags`]
101#[derive(Debug, Clone, Serialize, Deserialize)]
102#[serde(untagged)]
103pub enum UpdateDeviceUserTagsError {
104    APIErrorResponse(crate::datadogV2::model::APIErrorResponse),
105    UnknownValue(serde_json::Value),
106}
107
108/// The Network Device Monitoring API allows you to fetch devices and interfaces and their attributes. See the [Network Device Monitoring page](<https://docs.datadoghq.com/network_monitoring/>) for more information.
109#[derive(Debug, Clone)]
110pub struct NetworkDeviceMonitoringAPI {
111    config: datadog::Configuration,
112    client: reqwest_middleware::ClientWithMiddleware,
113}
114
115impl Default for NetworkDeviceMonitoringAPI {
116    fn default() -> Self {
117        Self::with_config(datadog::Configuration::default())
118    }
119}
120
121impl NetworkDeviceMonitoringAPI {
122    pub fn new() -> Self {
123        Self::default()
124    }
125    pub fn with_config(config: datadog::Configuration) -> Self {
126        let mut reqwest_client_builder = reqwest::Client::builder();
127
128        if let Some(proxy_url) = &config.proxy_url {
129            let proxy = reqwest::Proxy::all(proxy_url).expect("Failed to parse proxy URL");
130            reqwest_client_builder = reqwest_client_builder.proxy(proxy);
131        }
132
133        let mut middleware_client_builder =
134            reqwest_middleware::ClientBuilder::new(reqwest_client_builder.build().unwrap());
135
136        if config.enable_retry {
137            struct RetryableStatus;
138            impl reqwest_retry::RetryableStrategy for RetryableStatus {
139                fn handle(
140                    &self,
141                    res: &Result<reqwest::Response, reqwest_middleware::Error>,
142                ) -> Option<reqwest_retry::Retryable> {
143                    match res {
144                        Ok(success) => reqwest_retry::default_on_request_success(success),
145                        Err(_) => None,
146                    }
147                }
148            }
149            let backoff_policy = reqwest_retry::policies::ExponentialBackoff::builder()
150                .build_with_max_retries(config.max_retries);
151
152            let retry_middleware =
153                reqwest_retry::RetryTransientMiddleware::new_with_policy_and_strategy(
154                    backoff_policy,
155                    RetryableStatus,
156                );
157
158            middleware_client_builder = middleware_client_builder.with(retry_middleware);
159        }
160
161        let client = middleware_client_builder.build();
162
163        Self { config, client }
164    }
165
166    pub fn with_client_and_config(
167        config: datadog::Configuration,
168        client: reqwest_middleware::ClientWithMiddleware,
169    ) -> Self {
170        Self { config, client }
171    }
172
173    /// Get the device details.
174    pub async fn get_device(
175        &self,
176        device_id: String,
177    ) -> Result<crate::datadogV2::model::GetDeviceResponse, datadog::Error<GetDeviceError>> {
178        match self.get_device_with_http_info(device_id).await {
179            Ok(response_content) => {
180                if let Some(e) = response_content.entity {
181                    Ok(e)
182                } else {
183                    Err(datadog::Error::Serde(serde::de::Error::custom(
184                        "response content was None",
185                    )))
186                }
187            }
188            Err(err) => Err(err),
189        }
190    }
191
192    /// Get the device details.
193    pub async fn get_device_with_http_info(
194        &self,
195        device_id: String,
196    ) -> Result<
197        datadog::ResponseContent<crate::datadogV2::model::GetDeviceResponse>,
198        datadog::Error<GetDeviceError>,
199    > {
200        let local_configuration = &self.config;
201        let operation_id = "v2.get_device";
202
203        let local_client = &self.client;
204
205        let local_uri_str = format!(
206            "{}/api/v2/ndm/devices/{device_id}",
207            local_configuration.get_operation_host(operation_id),
208            device_id = datadog::urlencode(device_id)
209        );
210        let mut local_req_builder =
211            local_client.request(reqwest::Method::GET, local_uri_str.as_str());
212
213        // build headers
214        let mut headers = HeaderMap::new();
215        headers.insert("Accept", HeaderValue::from_static("application/json"));
216
217        // build user agent
218        match HeaderValue::from_str(local_configuration.user_agent.as_str()) {
219            Ok(user_agent) => headers.insert(reqwest::header::USER_AGENT, user_agent),
220            Err(e) => {
221                log::warn!("Failed to parse user agent header: {e}, falling back to default");
222                headers.insert(
223                    reqwest::header::USER_AGENT,
224                    HeaderValue::from_static(datadog::DEFAULT_USER_AGENT.as_str()),
225                )
226            }
227        };
228
229        // build auth
230        if let Some(local_key) = local_configuration.auth_keys.get("apiKeyAuth") {
231            headers.insert(
232                "DD-API-KEY",
233                HeaderValue::from_str(local_key.key.as_str())
234                    .expect("failed to parse DD-API-KEY header"),
235            );
236        };
237        if let Some(local_key) = local_configuration.auth_keys.get("appKeyAuth") {
238            headers.insert(
239                "DD-APPLICATION-KEY",
240                HeaderValue::from_str(local_key.key.as_str())
241                    .expect("failed to parse DD-APPLICATION-KEY header"),
242            );
243        };
244
245        local_req_builder = local_req_builder.headers(headers);
246        let local_req = local_req_builder.build()?;
247        log::debug!("request content: {:?}", local_req.body());
248        let local_resp = local_client.execute(local_req).await?;
249
250        let local_status = local_resp.status();
251        let local_content = local_resp.text().await?;
252        log::debug!("response content: {}", local_content);
253
254        if !local_status.is_client_error() && !local_status.is_server_error() {
255            match serde_json::from_str::<crate::datadogV2::model::GetDeviceResponse>(&local_content)
256            {
257                Ok(e) => {
258                    return Ok(datadog::ResponseContent {
259                        status: local_status,
260                        content: local_content,
261                        entity: Some(e),
262                    })
263                }
264                Err(e) => return Err(datadog::Error::Serde(e)),
265            };
266        } else {
267            let local_entity: Option<GetDeviceError> = serde_json::from_str(&local_content).ok();
268            let local_error = datadog::ResponseContent {
269                status: local_status,
270                content: local_content,
271                entity: local_entity,
272            };
273            Err(datadog::Error::ResponseError(local_error))
274        }
275    }
276
277    /// Get the list of interfaces of the device.
278    pub async fn get_interfaces(
279        &self,
280        device_id: String,
281        params: GetInterfacesOptionalParams,
282    ) -> Result<crate::datadogV2::model::GetInterfacesResponse, datadog::Error<GetInterfacesError>>
283    {
284        match self.get_interfaces_with_http_info(device_id, params).await {
285            Ok(response_content) => {
286                if let Some(e) = response_content.entity {
287                    Ok(e)
288                } else {
289                    Err(datadog::Error::Serde(serde::de::Error::custom(
290                        "response content was None",
291                    )))
292                }
293            }
294            Err(err) => Err(err),
295        }
296    }
297
298    /// Get the list of interfaces of the device.
299    pub async fn get_interfaces_with_http_info(
300        &self,
301        device_id: String,
302        params: GetInterfacesOptionalParams,
303    ) -> Result<
304        datadog::ResponseContent<crate::datadogV2::model::GetInterfacesResponse>,
305        datadog::Error<GetInterfacesError>,
306    > {
307        let local_configuration = &self.config;
308        let operation_id = "v2.get_interfaces";
309
310        // unbox and build optional parameters
311        let get_ip_addresses = params.get_ip_addresses;
312
313        let local_client = &self.client;
314
315        let local_uri_str = format!(
316            "{}/api/v2/ndm/interfaces",
317            local_configuration.get_operation_host(operation_id)
318        );
319        let mut local_req_builder =
320            local_client.request(reqwest::Method::GET, local_uri_str.as_str());
321
322        local_req_builder = local_req_builder.query(&[("device_id", &device_id.to_string())]);
323        if let Some(ref local_query_param) = get_ip_addresses {
324            local_req_builder =
325                local_req_builder.query(&[("get_ip_addresses", &local_query_param.to_string())]);
326        };
327
328        // build headers
329        let mut headers = HeaderMap::new();
330        headers.insert("Accept", HeaderValue::from_static("application/json"));
331
332        // build user agent
333        match HeaderValue::from_str(local_configuration.user_agent.as_str()) {
334            Ok(user_agent) => headers.insert(reqwest::header::USER_AGENT, user_agent),
335            Err(e) => {
336                log::warn!("Failed to parse user agent header: {e}, falling back to default");
337                headers.insert(
338                    reqwest::header::USER_AGENT,
339                    HeaderValue::from_static(datadog::DEFAULT_USER_AGENT.as_str()),
340                )
341            }
342        };
343
344        // build auth
345        if let Some(local_key) = local_configuration.auth_keys.get("apiKeyAuth") {
346            headers.insert(
347                "DD-API-KEY",
348                HeaderValue::from_str(local_key.key.as_str())
349                    .expect("failed to parse DD-API-KEY header"),
350            );
351        };
352        if let Some(local_key) = local_configuration.auth_keys.get("appKeyAuth") {
353            headers.insert(
354                "DD-APPLICATION-KEY",
355                HeaderValue::from_str(local_key.key.as_str())
356                    .expect("failed to parse DD-APPLICATION-KEY header"),
357            );
358        };
359
360        local_req_builder = local_req_builder.headers(headers);
361        let local_req = local_req_builder.build()?;
362        log::debug!("request content: {:?}", local_req.body());
363        let local_resp = local_client.execute(local_req).await?;
364
365        let local_status = local_resp.status();
366        let local_content = local_resp.text().await?;
367        log::debug!("response content: {}", local_content);
368
369        if !local_status.is_client_error() && !local_status.is_server_error() {
370            match serde_json::from_str::<crate::datadogV2::model::GetInterfacesResponse>(
371                &local_content,
372            ) {
373                Ok(e) => {
374                    return Ok(datadog::ResponseContent {
375                        status: local_status,
376                        content: local_content,
377                        entity: Some(e),
378                    })
379                }
380                Err(e) => return Err(datadog::Error::Serde(e)),
381            };
382        } else {
383            let local_entity: Option<GetInterfacesError> =
384                serde_json::from_str(&local_content).ok();
385            let local_error = datadog::ResponseContent {
386                status: local_status,
387                content: local_content,
388                entity: local_entity,
389            };
390            Err(datadog::Error::ResponseError(local_error))
391        }
392    }
393
394    /// Get the list of tags for a device.
395    pub async fn list_device_user_tags(
396        &self,
397        device_id: String,
398    ) -> Result<crate::datadogV2::model::ListTagsResponse, datadog::Error<ListDeviceUserTagsError>>
399    {
400        match self.list_device_user_tags_with_http_info(device_id).await {
401            Ok(response_content) => {
402                if let Some(e) = response_content.entity {
403                    Ok(e)
404                } else {
405                    Err(datadog::Error::Serde(serde::de::Error::custom(
406                        "response content was None",
407                    )))
408                }
409            }
410            Err(err) => Err(err),
411        }
412    }
413
414    /// Get the list of tags for a device.
415    pub async fn list_device_user_tags_with_http_info(
416        &self,
417        device_id: String,
418    ) -> Result<
419        datadog::ResponseContent<crate::datadogV2::model::ListTagsResponse>,
420        datadog::Error<ListDeviceUserTagsError>,
421    > {
422        let local_configuration = &self.config;
423        let operation_id = "v2.list_device_user_tags";
424
425        let local_client = &self.client;
426
427        let local_uri_str = format!(
428            "{}/api/v2/ndm/tags/devices/{device_id}",
429            local_configuration.get_operation_host(operation_id),
430            device_id = datadog::urlencode(device_id)
431        );
432        let mut local_req_builder =
433            local_client.request(reqwest::Method::GET, local_uri_str.as_str());
434
435        // build headers
436        let mut headers = HeaderMap::new();
437        headers.insert("Accept", HeaderValue::from_static("application/json"));
438
439        // build user agent
440        match HeaderValue::from_str(local_configuration.user_agent.as_str()) {
441            Ok(user_agent) => headers.insert(reqwest::header::USER_AGENT, user_agent),
442            Err(e) => {
443                log::warn!("Failed to parse user agent header: {e}, falling back to default");
444                headers.insert(
445                    reqwest::header::USER_AGENT,
446                    HeaderValue::from_static(datadog::DEFAULT_USER_AGENT.as_str()),
447                )
448            }
449        };
450
451        // build auth
452        if let Some(local_key) = local_configuration.auth_keys.get("apiKeyAuth") {
453            headers.insert(
454                "DD-API-KEY",
455                HeaderValue::from_str(local_key.key.as_str())
456                    .expect("failed to parse DD-API-KEY header"),
457            );
458        };
459        if let Some(local_key) = local_configuration.auth_keys.get("appKeyAuth") {
460            headers.insert(
461                "DD-APPLICATION-KEY",
462                HeaderValue::from_str(local_key.key.as_str())
463                    .expect("failed to parse DD-APPLICATION-KEY header"),
464            );
465        };
466
467        local_req_builder = local_req_builder.headers(headers);
468        let local_req = local_req_builder.build()?;
469        log::debug!("request content: {:?}", local_req.body());
470        let local_resp = local_client.execute(local_req).await?;
471
472        let local_status = local_resp.status();
473        let local_content = local_resp.text().await?;
474        log::debug!("response content: {}", local_content);
475
476        if !local_status.is_client_error() && !local_status.is_server_error() {
477            match serde_json::from_str::<crate::datadogV2::model::ListTagsResponse>(&local_content)
478            {
479                Ok(e) => {
480                    return Ok(datadog::ResponseContent {
481                        status: local_status,
482                        content: local_content,
483                        entity: Some(e),
484                    })
485                }
486                Err(e) => return Err(datadog::Error::Serde(e)),
487            };
488        } else {
489            let local_entity: Option<ListDeviceUserTagsError> =
490                serde_json::from_str(&local_content).ok();
491            let local_error = datadog::ResponseContent {
492                status: local_status,
493                content: local_content,
494                entity: local_entity,
495            };
496            Err(datadog::Error::ResponseError(local_error))
497        }
498    }
499
500    /// Get the list of devices.
501    pub async fn list_devices(
502        &self,
503        params: ListDevicesOptionalParams,
504    ) -> Result<crate::datadogV2::model::ListDevicesResponse, datadog::Error<ListDevicesError>>
505    {
506        match self.list_devices_with_http_info(params).await {
507            Ok(response_content) => {
508                if let Some(e) = response_content.entity {
509                    Ok(e)
510                } else {
511                    Err(datadog::Error::Serde(serde::de::Error::custom(
512                        "response content was None",
513                    )))
514                }
515            }
516            Err(err) => Err(err),
517        }
518    }
519
520    pub fn list_devices_with_pagination(
521        &self,
522        mut params: ListDevicesOptionalParams,
523    ) -> impl Stream<
524        Item = Result<crate::datadogV2::model::DevicesListData, datadog::Error<ListDevicesError>>,
525    > + '_ {
526        try_stream! {
527            let mut page_size: i64 = 10;
528            if params.page_size.is_none() {
529                params.page_size = Some(page_size);
530            } else {
531                page_size = params.page_size.unwrap().clone();
532            }
533            if params.page_number.is_none() {
534                params.page_number = Some(0);
535            }
536            loop {
537                let resp = self.list_devices(params.clone()).await?;
538                let Some(data) = resp.data else { break };
539
540                let r = data;
541                let count = r.len();
542                for team in r {
543                    yield team;
544                }
545
546                if count < page_size as usize {
547                    break;
548                }
549                params.page_number = Some(params.page_number.unwrap() + 1);
550            }
551        }
552    }
553
554    /// Get the list of devices.
555    pub async fn list_devices_with_http_info(
556        &self,
557        params: ListDevicesOptionalParams,
558    ) -> Result<
559        datadog::ResponseContent<crate::datadogV2::model::ListDevicesResponse>,
560        datadog::Error<ListDevicesError>,
561    > {
562        let local_configuration = &self.config;
563        let operation_id = "v2.list_devices";
564
565        // unbox and build optional parameters
566        let page_size = params.page_size;
567        let page_number = params.page_number;
568        let sort = params.sort;
569        let filter_tag = params.filter_tag;
570
571        let local_client = &self.client;
572
573        let local_uri_str = format!(
574            "{}/api/v2/ndm/devices",
575            local_configuration.get_operation_host(operation_id)
576        );
577        let mut local_req_builder =
578            local_client.request(reqwest::Method::GET, local_uri_str.as_str());
579
580        if let Some(ref local_query_param) = page_size {
581            local_req_builder =
582                local_req_builder.query(&[("page[size]", &local_query_param.to_string())]);
583        };
584        if let Some(ref local_query_param) = page_number {
585            local_req_builder =
586                local_req_builder.query(&[("page[number]", &local_query_param.to_string())]);
587        };
588        if let Some(ref local_query_param) = sort {
589            local_req_builder =
590                local_req_builder.query(&[("sort", &local_query_param.to_string())]);
591        };
592        if let Some(ref local_query_param) = filter_tag {
593            local_req_builder =
594                local_req_builder.query(&[("filter[tag]", &local_query_param.to_string())]);
595        };
596
597        // build headers
598        let mut headers = HeaderMap::new();
599        headers.insert("Accept", HeaderValue::from_static("application/json"));
600
601        // build user agent
602        match HeaderValue::from_str(local_configuration.user_agent.as_str()) {
603            Ok(user_agent) => headers.insert(reqwest::header::USER_AGENT, user_agent),
604            Err(e) => {
605                log::warn!("Failed to parse user agent header: {e}, falling back to default");
606                headers.insert(
607                    reqwest::header::USER_AGENT,
608                    HeaderValue::from_static(datadog::DEFAULT_USER_AGENT.as_str()),
609                )
610            }
611        };
612
613        // build auth
614        if let Some(local_key) = local_configuration.auth_keys.get("apiKeyAuth") {
615            headers.insert(
616                "DD-API-KEY",
617                HeaderValue::from_str(local_key.key.as_str())
618                    .expect("failed to parse DD-API-KEY header"),
619            );
620        };
621        if let Some(local_key) = local_configuration.auth_keys.get("appKeyAuth") {
622            headers.insert(
623                "DD-APPLICATION-KEY",
624                HeaderValue::from_str(local_key.key.as_str())
625                    .expect("failed to parse DD-APPLICATION-KEY header"),
626            );
627        };
628
629        local_req_builder = local_req_builder.headers(headers);
630        let local_req = local_req_builder.build()?;
631        log::debug!("request content: {:?}", local_req.body());
632        let local_resp = local_client.execute(local_req).await?;
633
634        let local_status = local_resp.status();
635        let local_content = local_resp.text().await?;
636        log::debug!("response content: {}", local_content);
637
638        if !local_status.is_client_error() && !local_status.is_server_error() {
639            match serde_json::from_str::<crate::datadogV2::model::ListDevicesResponse>(
640                &local_content,
641            ) {
642                Ok(e) => {
643                    return Ok(datadog::ResponseContent {
644                        status: local_status,
645                        content: local_content,
646                        entity: Some(e),
647                    })
648                }
649                Err(e) => return Err(datadog::Error::Serde(e)),
650            };
651        } else {
652            let local_entity: Option<ListDevicesError> = serde_json::from_str(&local_content).ok();
653            let local_error = datadog::ResponseContent {
654                status: local_status,
655                content: local_content,
656                entity: local_entity,
657            };
658            Err(datadog::Error::ResponseError(local_error))
659        }
660    }
661
662    /// Update the tags for a device.
663    pub async fn update_device_user_tags(
664        &self,
665        device_id: String,
666        body: crate::datadogV2::model::ListTagsResponse,
667    ) -> Result<crate::datadogV2::model::ListTagsResponse, datadog::Error<UpdateDeviceUserTagsError>>
668    {
669        match self
670            .update_device_user_tags_with_http_info(device_id, body)
671            .await
672        {
673            Ok(response_content) => {
674                if let Some(e) = response_content.entity {
675                    Ok(e)
676                } else {
677                    Err(datadog::Error::Serde(serde::de::Error::custom(
678                        "response content was None",
679                    )))
680                }
681            }
682            Err(err) => Err(err),
683        }
684    }
685
686    /// Update the tags for a device.
687    pub async fn update_device_user_tags_with_http_info(
688        &self,
689        device_id: String,
690        body: crate::datadogV2::model::ListTagsResponse,
691    ) -> Result<
692        datadog::ResponseContent<crate::datadogV2::model::ListTagsResponse>,
693        datadog::Error<UpdateDeviceUserTagsError>,
694    > {
695        let local_configuration = &self.config;
696        let operation_id = "v2.update_device_user_tags";
697
698        let local_client = &self.client;
699
700        let local_uri_str = format!(
701            "{}/api/v2/ndm/tags/devices/{device_id}",
702            local_configuration.get_operation_host(operation_id),
703            device_id = datadog::urlencode(device_id)
704        );
705        let mut local_req_builder =
706            local_client.request(reqwest::Method::PATCH, local_uri_str.as_str());
707
708        // build headers
709        let mut headers = HeaderMap::new();
710        headers.insert("Content-Type", HeaderValue::from_static("application/json"));
711        headers.insert("Accept", HeaderValue::from_static("application/json"));
712
713        // build user agent
714        match HeaderValue::from_str(local_configuration.user_agent.as_str()) {
715            Ok(user_agent) => headers.insert(reqwest::header::USER_AGENT, user_agent),
716            Err(e) => {
717                log::warn!("Failed to parse user agent header: {e}, falling back to default");
718                headers.insert(
719                    reqwest::header::USER_AGENT,
720                    HeaderValue::from_static(datadog::DEFAULT_USER_AGENT.as_str()),
721                )
722            }
723        };
724
725        // build auth
726        if let Some(local_key) = local_configuration.auth_keys.get("apiKeyAuth") {
727            headers.insert(
728                "DD-API-KEY",
729                HeaderValue::from_str(local_key.key.as_str())
730                    .expect("failed to parse DD-API-KEY header"),
731            );
732        };
733        if let Some(local_key) = local_configuration.auth_keys.get("appKeyAuth") {
734            headers.insert(
735                "DD-APPLICATION-KEY",
736                HeaderValue::from_str(local_key.key.as_str())
737                    .expect("failed to parse DD-APPLICATION-KEY header"),
738            );
739        };
740
741        // build body parameters
742        let output = Vec::new();
743        let mut ser = serde_json::Serializer::with_formatter(output, datadog::DDFormatter);
744        if body.serialize(&mut ser).is_ok() {
745            if let Some(content_encoding) = headers.get("Content-Encoding") {
746                match content_encoding.to_str().unwrap_or_default() {
747                    "gzip" => {
748                        let mut enc = GzEncoder::new(Vec::new(), Compression::default());
749                        let _ = enc.write_all(ser.into_inner().as_slice());
750                        match enc.finish() {
751                            Ok(buf) => {
752                                local_req_builder = local_req_builder.body(buf);
753                            }
754                            Err(e) => return Err(datadog::Error::Io(e)),
755                        }
756                    }
757                    "deflate" => {
758                        let mut enc = ZlibEncoder::new(Vec::new(), Compression::default());
759                        let _ = enc.write_all(ser.into_inner().as_slice());
760                        match enc.finish() {
761                            Ok(buf) => {
762                                local_req_builder = local_req_builder.body(buf);
763                            }
764                            Err(e) => return Err(datadog::Error::Io(e)),
765                        }
766                    }
767                    "zstd1" => {
768                        let mut enc = zstd::stream::Encoder::new(Vec::new(), 0).unwrap();
769                        let _ = enc.write_all(ser.into_inner().as_slice());
770                        match enc.finish() {
771                            Ok(buf) => {
772                                local_req_builder = local_req_builder.body(buf);
773                            }
774                            Err(e) => return Err(datadog::Error::Io(e)),
775                        }
776                    }
777                    _ => {
778                        local_req_builder = local_req_builder.body(ser.into_inner());
779                    }
780                }
781            } else {
782                local_req_builder = local_req_builder.body(ser.into_inner());
783            }
784        }
785
786        local_req_builder = local_req_builder.headers(headers);
787        let local_req = local_req_builder.build()?;
788        log::debug!("request content: {:?}", local_req.body());
789        let local_resp = local_client.execute(local_req).await?;
790
791        let local_status = local_resp.status();
792        let local_content = local_resp.text().await?;
793        log::debug!("response content: {}", local_content);
794
795        if !local_status.is_client_error() && !local_status.is_server_error() {
796            match serde_json::from_str::<crate::datadogV2::model::ListTagsResponse>(&local_content)
797            {
798                Ok(e) => {
799                    return Ok(datadog::ResponseContent {
800                        status: local_status,
801                        content: local_content,
802                        entity: Some(e),
803                    })
804                }
805                Err(e) => return Err(datadog::Error::Serde(e)),
806            };
807        } else {
808            let local_entity: Option<UpdateDeviceUserTagsError> =
809                serde_json::from_str(&local_content).ok();
810            let local_error = datadog::ResponseContent {
811                status: local_status,
812                content: local_content,
813                entity: local_entity,
814            };
815            Err(datadog::Error::ResponseError(local_error))
816        }
817    }
818}