From 5d15d77dfbde40e6ddf1ccc25e40d3ca19dbdad0 Mon Sep 17 00:00:00 2001 From: "api-clients-generation-pipeline[bot]" <54105614+api-clients-generation-pipeline[bot]@users.noreply.github.com> Date: Thu, 12 Dec 2024 13:54:39 +0000 Subject: [PATCH] add docs for pagination in /api/v2/metrics endpoint (#416) Co-authored-by: Sherzod Karimov Co-authored-by: api-clients-generation-pipeline[bot] <54105614+api-clients-generation-pipeline[bot]@users.noreply.github.com> Co-authored-by: ci.datadog-api-spec --- .apigentools-info | 8 +- .generator/schemas/v2/openapi.yaml | 96 +++++++++- .generator/src/generator/templates/api.j2 | 3 +- ...etrics_ListTagConfigurations_3969783727.rs | 23 +++ src/datadogV2/api/api_metrics.rs | 74 ++++++++ src/datadogV2/model/mod.rs | 8 + src/datadogV2/model/model_metric_meta_page.rs | 165 ++++++++++++++++++ .../model/model_metric_meta_page_type.rs | 48 +++++ .../model/model_metric_pagination_meta.rs | 105 +++++++++++ ..._and_metric_tag_configurations_response.rs | 34 ++++ .../model_metrics_list_response_links.rs | 164 +++++++++++++++++ ...ns-Success-response-with-pagination.frozen | 1 + ...urns-Success-response-with-pagination.json | 61 +++++++ tests/scenarios/features/v2/metrics.feature | 9 + tests/scenarios/function_mappings.rs | 85 +++++++++ 15 files changed, 878 insertions(+), 6 deletions(-) create mode 100644 examples/v2_metrics_ListTagConfigurations_3969783727.rs create mode 100644 src/datadogV2/model/model_metric_meta_page.rs create mode 100644 src/datadogV2/model/model_metric_meta_page_type.rs create mode 100644 src/datadogV2/model/model_metric_pagination_meta.rs create mode 100644 src/datadogV2/model/model_metrics_list_response_links.rs create mode 100644 tests/scenarios/cassettes/v2/metrics/Get-a-list-of-metrics-returns-Success-response-with-pagination.frozen create mode 100644 tests/scenarios/cassettes/v2/metrics/Get-a-list-of-metrics-returns-Success-response-with-pagination.json diff --git a/.apigentools-info b/.apigentools-info index 88c6f1b30..6f391c666 100644 --- a/.apigentools-info +++ b/.apigentools-info @@ -4,13 +4,13 @@ "spec_versions": { "v1": { "apigentools_version": "1.6.6", - "regenerated": "2024-12-11 20:35:39.203447", - "spec_repo_commit": "7b62d196" + "regenerated": "2024-12-12 13:29:27.994176", + "spec_repo_commit": "f0c3c0f4" }, "v2": { "apigentools_version": "1.6.6", - "regenerated": "2024-12-11 20:35:39.222879", - "spec_repo_commit": "7b62d196" + "regenerated": "2024-12-12 13:29:28.013354", + "spec_repo_commit": "f0c3c0f4" } } } \ No newline at end of file diff --git a/.generator/schemas/v2/openapi.yaml b/.generator/schemas/v2/openapi.yaml index c2b7a7ae0..643901c6d 100644 --- a/.generator/schemas/v2/openapi.yaml +++ b/.generator/schemas/v2/openapi.yaml @@ -16012,6 +16012,36 @@ components: - COUNT - RATE - GAUGE + MetricMetaPage: + description: Paging attributes. Only present if pagination query parameters + were provided. + properties: + cursor: + description: The cursor used to get the current results, if any. + nullable: true + type: string + limit: + description: Number of results returned + format: int32 + maximum: 20000 + minimum: 0 + type: integer + next_cursor: + description: The cursor used to get the next results, if any. + nullable: true + type: string + type: + $ref: '#/components/schemas/MetricMetaPageType' + type: object + MetricMetaPageType: + default: cursor_limit + description: Type of metric pagination. + enum: + - cursor_limit + example: cursor_limit + type: string + x-enum-varnames: + - CURSOR_LIMIT MetricMetadata: description: Metadata for the metric. properties: @@ -16094,6 +16124,12 @@ components: maximum: 1000 type: integer type: object + MetricPaginationMeta: + description: Response metadata object. + properties: + pagination: + $ref: '#/components/schemas/MetricMetaPage' + type: object MetricPayload: description: The metrics' payload. properties: @@ -16570,6 +16606,10 @@ components: items: $ref: '#/components/schemas/MetricsAndMetricTagConfigurations' type: array + links: + $ref: '#/components/schemas/MetricsListResponseLinks' + meta: + $ref: '#/components/schemas/MetricPaginationMeta' readOnly: true type: object MetricsDataSource: @@ -16583,6 +16623,29 @@ components: x-enum-varnames: - METRICS - CLOUD_COST + MetricsListResponseLinks: + description: Pagination links. Only present if pagination query parameters were + provided. + properties: + first: + description: Link to the first page. + type: string + last: + description: Link to the last page. + nullable: true + type: string + next: + description: Link to the next page. + nullable: true + type: string + prev: + description: Link to previous page. + nullable: true + type: string + self: + description: Link to current page. + type: string + type: object MetricsScalarQuery: description: An individual scalar metrics query. properties: @@ -35773,7 +35836,12 @@ paths: get: description: "Returns all metrics that can be configured in the Metrics Summary page or with Metrics without Limits\u2122 (matching additional filters if - specified)." + specified).\nOptionally, paginate by using the `page[cursor]` and/or `page[size]` + query parameters.\nTo fetch the first page, pass in a query parameter with + either a valid `page[size]` or an empty cursor like `page[cursor]=`. To fetch + the next page, pass in the `next_cursor` value from the response as the new + `page[cursor]` value.\nOnce the `meta.pagination.next_cursor` value is null, + all pages have been retrieved." operationId: ListTagConfigurations parameters: - description: Filter custom metrics that have configured tags. @@ -35838,6 +35906,27 @@ paths: schema: format: int64 type: integer + - description: Maximum number of results returned. + in: query + name: page[size] + required: false + schema: + default: 10000 + format: int32 + maximum: 10000 + minimum: 1 + type: integer + - description: 'String to query the next page of results. + + This key is provided with each valid response from the API in `meta.pagination.next_cursor`. + + Once the `meta.pagination.next_cursor` key is null, all pages have been + retrieved.' + in: query + name: page[cursor] + required: false + schema: + type: string responses: '200': content: @@ -35871,6 +35960,11 @@ paths: summary: Get a list of metrics tags: - Metrics + x-pagination: + cursorParam: page[cursor] + cursorPath: meta.pagination.next_cursor + limitParam: page[size] + resultsPath: data x-permission: operator: OR permissions: diff --git a/.generator/src/generator/templates/api.j2 b/.generator/src/generator/templates/api.j2 index af1009e75..eb2784c3c 100644 --- a/.generator/src/generator/templates/api.j2 +++ b/.generator/src/generator/templates/api.j2 @@ -256,12 +256,13 @@ impl {{ structName }} { {%- set cursorPathParts = pagination.cursorPath.split(".") %} {%- for i in range(cursorPathParts|length) %} {%- set ns.required = cursorPathParts[i] in ns.parentSchema.get("required", []) %} + {%- set ns.nullable = ns.parentSchema.properties[cursorPathParts[i]].get("nullable", False) %} {%- if ns.required %} {%- set ns.cursorGetter = ns.cursorGetter ~ "." ~ cursorPathParts[i] %} {%- else %} {%- set attrName = cursorPathParts[i]|variable_name %} - let Some({{ attrName }}) = {{ ns.cursorGetter }}.{{ attrName }} else { break }; + let Some({{ attrName }}) = {{ ns.cursorGetter }}.{{ attrName }}{%if ns.nullable %}.unwrap(){% endif %} else { break }; {%- set ns.cursorGetter = attrName %} {%- endif %} diff --git a/examples/v2_metrics_ListTagConfigurations_3969783727.rs b/examples/v2_metrics_ListTagConfigurations_3969783727.rs new file mode 100644 index 000000000..edda94b1c --- /dev/null +++ b/examples/v2_metrics_ListTagConfigurations_3969783727.rs @@ -0,0 +1,23 @@ +// Get a list of metrics returns "Success" response with pagination +use datadog_api_client::datadog; +use datadog_api_client::datadogV2::api_metrics::ListTagConfigurationsOptionalParams; +use datadog_api_client::datadogV2::api_metrics::MetricsAPI; +use futures_util::pin_mut; +use futures_util::stream::StreamExt; + +#[tokio::main] +async fn main() { + let configuration = datadog::Configuration::new(); + let api = MetricsAPI::with_config(configuration); + let response = api.list_tag_configurations_with_pagination( + ListTagConfigurationsOptionalParams::default().page_size(2), + ); + pin_mut!(response); + while let Some(resp) = response.next().await { + if let Ok(value) = resp { + println!("{:#?}", value); + } else { + println!("{:#?}", resp.unwrap_err()); + } + } +} diff --git a/src/datadogV2/api/api_metrics.rs b/src/datadogV2/api/api_metrics.rs index 090719e7d..bcb9a100a 100644 --- a/src/datadogV2/api/api_metrics.rs +++ b/src/datadogV2/api/api_metrics.rs @@ -2,10 +2,12 @@ // This product includes software developed at Datadog (https://www.datadoghq.com/). // Copyright 2019-Present Datadog, Inc. use crate::datadog; +use async_stream::try_stream; use flate2::{ write::{GzEncoder, ZlibEncoder}, Compression, }; +use futures_core::stream::Stream; use reqwest::header::{HeaderMap, HeaderValue}; use serde::{Deserialize, Serialize}; use std::io::Write; @@ -95,6 +97,12 @@ pub struct ListTagConfigurationsOptionalParams { /// The number of seconds of look back (from now) to apply to a filter[tag] or filter[queried] query. /// Default value is 3600 (1 hour), maximum value is 2,592,000 (30 days). pub window_seconds: Option, + /// Maximum number of results returned. + pub page_size: Option, + /// String to query the next page of results. + /// This key is provided with each valid response from the API in `meta.pagination.next_cursor`. + /// Once the `meta.pagination.next_cursor` key is null, all pages have been retrieved. + pub page_cursor: Option, } impl ListTagConfigurationsOptionalParams { @@ -140,6 +148,18 @@ impl ListTagConfigurationsOptionalParams { self.window_seconds = Some(value); self } + /// Maximum number of results returned. + pub fn page_size(mut self, value: i32) -> Self { + self.page_size = Some(value); + self + } + /// String to query the next page of results. + /// This key is provided with each valid response from the API in `meta.pagination.next_cursor`. + /// Once the `meta.pagination.next_cursor` key is null, all pages have been retrieved. + pub fn page_cursor(mut self, value: String) -> Self { + self.page_cursor = Some(value); + self + } } /// SubmitMetricsOptionalParams is a struct for passing parameters to the method [`MetricsAPI::submit_metrics`] @@ -1440,6 +1460,9 @@ impl MetricsAPI { } /// Returns all metrics that can be configured in the Metrics Summary page or with Metrics without Limits™ (matching additional filters if specified). + /// Optionally, paginate by using the `page[cursor]` and/or `page[size]` query parameters. + /// To fetch the first page, pass in a query parameter with either a valid `page[size]` or an empty cursor like `page[cursor]=`. To fetch the next page, pass in the `next_cursor` value from the response as the new `page[cursor]` value. + /// Once the `meta.pagination.next_cursor` value is null, all pages have been retrieved. pub async fn list_tag_configurations( &self, params: ListTagConfigurationsOptionalParams, @@ -1461,7 +1484,48 @@ impl MetricsAPI { } } + pub fn list_tag_configurations_with_pagination( + &self, + mut params: ListTagConfigurationsOptionalParams, + ) -> impl Stream< + Item = Result< + crate::datadogV2::model::MetricsAndMetricTagConfigurations, + datadog::Error, + >, + > + '_ { + try_stream! { + let mut page_size: i32 = 10000; + if params.page_size.is_none() { + params.page_size = Some(page_size); + } else { + page_size = params.page_size.unwrap().clone(); + } + loop { + let resp = self.list_tag_configurations(params.clone()).await?; + let Some(data) = resp.data else { break }; + + let r = data; + let count = r.len(); + for team in r { + yield team; + } + + if count < page_size as usize { + break; + } + let Some(meta) = resp.meta else { break }; + let Some(pagination) = meta.pagination else { break }; + let Some(next_cursor) = pagination.next_cursor.unwrap() else { break }; + + params.page_cursor = Some(next_cursor); + } + } + } + /// Returns all metrics that can be configured in the Metrics Summary page or with Metrics without Limits™ (matching additional filters if specified). + /// Optionally, paginate by using the `page[cursor]` and/or `page[size]` query parameters. + /// To fetch the first page, pass in a query parameter with either a valid `page[size]` or an empty cursor like `page[cursor]=`. To fetch the next page, pass in the `next_cursor` value from the response as the new `page[cursor]` value. + /// Once the `meta.pagination.next_cursor` value is null, all pages have been retrieved. pub async fn list_tag_configurations_with_http_info( &self, params: ListTagConfigurationsOptionalParams, @@ -1482,6 +1546,8 @@ impl MetricsAPI { let filter_queried = params.filter_queried; let filter_tags = params.filter_tags; let window_seconds = params.window_seconds; + let page_size = params.page_size; + let page_cursor = params.page_cursor; let local_client = &self.client; @@ -1522,6 +1588,14 @@ impl MetricsAPI { local_req_builder = local_req_builder.query(&[("window[seconds]", &local_query_param.to_string())]); }; + if let Some(ref local_query_param) = page_size { + local_req_builder = + local_req_builder.query(&[("page[size]", &local_query_param.to_string())]); + }; + if let Some(ref local_query_param) = page_cursor { + local_req_builder = + local_req_builder.query(&[("page[cursor]", &local_query_param.to_string())]); + }; // build headers let mut headers = HeaderMap::new(); diff --git a/src/datadogV2/model/mod.rs b/src/datadogV2/model/mod.rs index 714bbfb27..238d684b9 100644 --- a/src/datadogV2/model/mod.rs +++ b/src/datadogV2/model/mod.rs @@ -1908,6 +1908,14 @@ pub mod model_metric_tag_configuration_type; pub use self::model_metric_tag_configuration_type::MetricTagConfigurationType; pub mod model_metrics_and_metric_tag_configurations; pub use self::model_metrics_and_metric_tag_configurations::MetricsAndMetricTagConfigurations; +pub mod model_metrics_list_response_links; +pub use self::model_metrics_list_response_links::MetricsListResponseLinks; +pub mod model_metric_pagination_meta; +pub use self::model_metric_pagination_meta::MetricPaginationMeta; +pub mod model_metric_meta_page; +pub use self::model_metric_meta_page::MetricMetaPage; +pub mod model_metric_meta_page_type; +pub use self::model_metric_meta_page_type::MetricMetaPageType; pub mod model_metric_bulk_tag_config_delete_request; pub use self::model_metric_bulk_tag_config_delete_request::MetricBulkTagConfigDeleteRequest; pub mod model_metric_bulk_tag_config_delete; diff --git a/src/datadogV2/model/model_metric_meta_page.rs b/src/datadogV2/model/model_metric_meta_page.rs new file mode 100644 index 000000000..97fe6e9d4 --- /dev/null +++ b/src/datadogV2/model/model_metric_meta_page.rs @@ -0,0 +1,165 @@ +// Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2019-Present Datadog, Inc. +use serde::de::{Error, MapAccess, Visitor}; +use serde::{Deserialize, Deserializer, Serialize}; +use serde_with::skip_serializing_none; +use std::fmt::{self, Formatter}; + +/// Paging attributes. Only present if pagination query parameters were provided. +#[non_exhaustive] +#[skip_serializing_none] +#[derive(Clone, Debug, PartialEq, Serialize)] +pub struct MetricMetaPage { + /// The cursor used to get the current results, if any. + #[serde(rename = "cursor", default, with = "::serde_with::rust::double_option")] + pub cursor: Option>, + /// Number of results returned + #[serde(rename = "limit")] + pub limit: Option, + /// The cursor used to get the next results, if any. + #[serde( + rename = "next_cursor", + default, + with = "::serde_with::rust::double_option" + )] + pub next_cursor: Option>, + /// Type of metric pagination. + #[serde(rename = "type")] + pub type_: Option, + #[serde(flatten)] + pub additional_properties: std::collections::BTreeMap, + #[serde(skip)] + #[serde(default)] + pub(crate) _unparsed: bool, +} + +impl MetricMetaPage { + pub fn new() -> MetricMetaPage { + MetricMetaPage { + cursor: None, + limit: None, + next_cursor: None, + type_: None, + additional_properties: std::collections::BTreeMap::new(), + _unparsed: false, + } + } + + pub fn cursor(mut self, value: Option) -> Self { + self.cursor = Some(value); + self + } + + pub fn limit(mut self, value: i32) -> Self { + self.limit = Some(value); + self + } + + pub fn next_cursor(mut self, value: Option) -> Self { + self.next_cursor = Some(value); + self + } + + pub fn type_(mut self, value: crate::datadogV2::model::MetricMetaPageType) -> Self { + self.type_ = Some(value); + self + } + + pub fn additional_properties( + mut self, + value: std::collections::BTreeMap, + ) -> Self { + self.additional_properties = value; + self + } +} + +impl Default for MetricMetaPage { + fn default() -> Self { + Self::new() + } +} + +impl<'de> Deserialize<'de> for MetricMetaPage { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct MetricMetaPageVisitor; + impl<'a> Visitor<'a> for MetricMetaPageVisitor { + type Value = MetricMetaPage; + + fn expecting(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.write_str("a mapping") + } + + fn visit_map(self, mut map: M) -> Result + where + M: MapAccess<'a>, + { + let mut cursor: Option> = None; + let mut limit: Option = None; + let mut next_cursor: Option> = None; + let mut type_: Option = None; + let mut additional_properties: std::collections::BTreeMap< + String, + serde_json::Value, + > = std::collections::BTreeMap::new(); + let mut _unparsed = false; + + while let Some((k, v)) = map.next_entry::()? { + match k.as_str() { + "cursor" => { + cursor = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "limit" => { + if v.is_null() { + continue; + } + limit = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "next_cursor" => { + next_cursor = + Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "type" => { + if v.is_null() { + continue; + } + type_ = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + if let Some(ref _type_) = type_ { + match _type_ { + crate::datadogV2::model::MetricMetaPageType::UnparsedObject( + _type_, + ) => { + _unparsed = true; + } + _ => {} + } + } + } + &_ => { + if let Ok(value) = serde_json::from_value(v.clone()) { + additional_properties.insert(k, value); + } + } + } + } + + let content = MetricMetaPage { + cursor, + limit, + next_cursor, + type_, + additional_properties, + _unparsed, + }; + + Ok(content) + } + } + + deserializer.deserialize_any(MetricMetaPageVisitor) + } +} diff --git a/src/datadogV2/model/model_metric_meta_page_type.rs b/src/datadogV2/model/model_metric_meta_page_type.rs new file mode 100644 index 000000000..d8fe73757 --- /dev/null +++ b/src/datadogV2/model/model_metric_meta_page_type.rs @@ -0,0 +1,48 @@ +// Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2019-Present Datadog, Inc. + +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + +#[non_exhaustive] +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum MetricMetaPageType { + CURSOR_LIMIT, + UnparsedObject(crate::datadog::UnparsedObject), +} + +impl ToString for MetricMetaPageType { + fn to_string(&self) -> String { + match self { + Self::CURSOR_LIMIT => String::from("cursor_limit"), + Self::UnparsedObject(v) => v.value.to_string(), + } + } +} + +impl Serialize for MetricMetaPageType { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match self { + Self::UnparsedObject(v) => v.serialize(serializer), + _ => serializer.serialize_str(self.to_string().as_str()), + } + } +} + +impl<'de> Deserialize<'de> for MetricMetaPageType { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let s: String = String::deserialize(deserializer)?; + Ok(match s.as_str() { + "cursor_limit" => Self::CURSOR_LIMIT, + _ => Self::UnparsedObject(crate::datadog::UnparsedObject { + value: serde_json::Value::String(s.into()), + }), + }) + } +} diff --git a/src/datadogV2/model/model_metric_pagination_meta.rs b/src/datadogV2/model/model_metric_pagination_meta.rs new file mode 100644 index 000000000..d5b12b73a --- /dev/null +++ b/src/datadogV2/model/model_metric_pagination_meta.rs @@ -0,0 +1,105 @@ +// Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2019-Present Datadog, Inc. +use serde::de::{Error, MapAccess, Visitor}; +use serde::{Deserialize, Deserializer, Serialize}; +use serde_with::skip_serializing_none; +use std::fmt::{self, Formatter}; + +/// Response metadata object. +#[non_exhaustive] +#[skip_serializing_none] +#[derive(Clone, Debug, PartialEq, Serialize)] +pub struct MetricPaginationMeta { + /// Paging attributes. Only present if pagination query parameters were provided. + #[serde(rename = "pagination")] + pub pagination: Option, + #[serde(flatten)] + pub additional_properties: std::collections::BTreeMap, + #[serde(skip)] + #[serde(default)] + pub(crate) _unparsed: bool, +} + +impl MetricPaginationMeta { + pub fn new() -> MetricPaginationMeta { + MetricPaginationMeta { + pagination: None, + additional_properties: std::collections::BTreeMap::new(), + _unparsed: false, + } + } + + pub fn pagination(mut self, value: crate::datadogV2::model::MetricMetaPage) -> Self { + self.pagination = Some(value); + self + } + + pub fn additional_properties( + mut self, + value: std::collections::BTreeMap, + ) -> Self { + self.additional_properties = value; + self + } +} + +impl Default for MetricPaginationMeta { + fn default() -> Self { + Self::new() + } +} + +impl<'de> Deserialize<'de> for MetricPaginationMeta { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct MetricPaginationMetaVisitor; + impl<'a> Visitor<'a> for MetricPaginationMetaVisitor { + type Value = MetricPaginationMeta; + + fn expecting(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.write_str("a mapping") + } + + fn visit_map(self, mut map: M) -> Result + where + M: MapAccess<'a>, + { + let mut pagination: Option = None; + let mut additional_properties: std::collections::BTreeMap< + String, + serde_json::Value, + > = std::collections::BTreeMap::new(); + let mut _unparsed = false; + + while let Some((k, v)) = map.next_entry::()? { + match k.as_str() { + "pagination" => { + if v.is_null() { + continue; + } + pagination = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + &_ => { + if let Ok(value) = serde_json::from_value(v.clone()) { + additional_properties.insert(k, value); + } + } + } + } + + let content = MetricPaginationMeta { + pagination, + additional_properties, + _unparsed, + }; + + Ok(content) + } + } + + deserializer.deserialize_any(MetricPaginationMetaVisitor) + } +} diff --git a/src/datadogV2/model/model_metrics_and_metric_tag_configurations_response.rs b/src/datadogV2/model/model_metrics_and_metric_tag_configurations_response.rs index a576d9252..ca4ae0cf4 100644 --- a/src/datadogV2/model/model_metrics_and_metric_tag_configurations_response.rs +++ b/src/datadogV2/model/model_metrics_and_metric_tag_configurations_response.rs @@ -14,6 +14,12 @@ pub struct MetricsAndMetricTagConfigurationsResponse { /// Array of metrics and metric tag configurations. #[serde(rename = "data")] pub data: Option>, + /// Pagination links. Only present if pagination query parameters were provided. + #[serde(rename = "links")] + pub links: Option, + /// Response metadata object. + #[serde(rename = "meta")] + pub meta: Option, #[serde(flatten)] pub additional_properties: std::collections::BTreeMap, #[serde(skip)] @@ -25,6 +31,8 @@ impl MetricsAndMetricTagConfigurationsResponse { pub fn new() -> MetricsAndMetricTagConfigurationsResponse { MetricsAndMetricTagConfigurationsResponse { data: None, + links: None, + meta: None, additional_properties: std::collections::BTreeMap::new(), _unparsed: false, } @@ -38,6 +46,16 @@ impl MetricsAndMetricTagConfigurationsResponse { self } + pub fn links(mut self, value: crate::datadogV2::model::MetricsListResponseLinks) -> Self { + self.links = Some(value); + self + } + + pub fn meta(mut self, value: crate::datadogV2::model::MetricPaginationMeta) -> Self { + self.meta = Some(value); + self + } + pub fn additional_properties( mut self, value: std::collections::BTreeMap, @@ -73,6 +91,8 @@ impl<'de> Deserialize<'de> for MetricsAndMetricTagConfigurationsResponse { let mut data: Option< Vec, > = None; + let mut links: Option = None; + let mut meta: Option = None; let mut additional_properties: std::collections::BTreeMap< String, serde_json::Value, @@ -87,6 +107,18 @@ impl<'de> Deserialize<'de> for MetricsAndMetricTagConfigurationsResponse { } data = Some(serde_json::from_value(v).map_err(M::Error::custom)?); } + "links" => { + if v.is_null() { + continue; + } + links = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "meta" => { + if v.is_null() { + continue; + } + meta = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } &_ => { if let Ok(value) = serde_json::from_value(v.clone()) { additional_properties.insert(k, value); @@ -97,6 +129,8 @@ impl<'de> Deserialize<'de> for MetricsAndMetricTagConfigurationsResponse { let content = MetricsAndMetricTagConfigurationsResponse { data, + links, + meta, additional_properties, _unparsed, }; diff --git a/src/datadogV2/model/model_metrics_list_response_links.rs b/src/datadogV2/model/model_metrics_list_response_links.rs new file mode 100644 index 000000000..0e625e765 --- /dev/null +++ b/src/datadogV2/model/model_metrics_list_response_links.rs @@ -0,0 +1,164 @@ +// Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2019-Present Datadog, Inc. +use serde::de::{Error, MapAccess, Visitor}; +use serde::{Deserialize, Deserializer, Serialize}; +use serde_with::skip_serializing_none; +use std::fmt::{self, Formatter}; + +/// Pagination links. Only present if pagination query parameters were provided. +#[non_exhaustive] +#[skip_serializing_none] +#[derive(Clone, Debug, PartialEq, Serialize)] +pub struct MetricsListResponseLinks { + /// Link to the first page. + #[serde(rename = "first")] + pub first: Option, + /// Link to the last page. + #[serde(rename = "last", default, with = "::serde_with::rust::double_option")] + pub last: Option>, + /// Link to the next page. + #[serde(rename = "next", default, with = "::serde_with::rust::double_option")] + pub next: Option>, + /// Link to previous page. + #[serde(rename = "prev", default, with = "::serde_with::rust::double_option")] + pub prev: Option>, + /// Link to current page. + #[serde(rename = "self")] + pub self_: Option, + #[serde(flatten)] + pub additional_properties: std::collections::BTreeMap, + #[serde(skip)] + #[serde(default)] + pub(crate) _unparsed: bool, +} + +impl MetricsListResponseLinks { + pub fn new() -> MetricsListResponseLinks { + MetricsListResponseLinks { + first: None, + last: None, + next: None, + prev: None, + self_: None, + additional_properties: std::collections::BTreeMap::new(), + _unparsed: false, + } + } + + pub fn first(mut self, value: String) -> Self { + self.first = Some(value); + self + } + + pub fn last(mut self, value: Option) -> Self { + self.last = Some(value); + self + } + + pub fn next(mut self, value: Option) -> Self { + self.next = Some(value); + self + } + + pub fn prev(mut self, value: Option) -> Self { + self.prev = Some(value); + self + } + + pub fn self_(mut self, value: String) -> Self { + self.self_ = Some(value); + self + } + + pub fn additional_properties( + mut self, + value: std::collections::BTreeMap, + ) -> Self { + self.additional_properties = value; + self + } +} + +impl Default for MetricsListResponseLinks { + fn default() -> Self { + Self::new() + } +} + +impl<'de> Deserialize<'de> for MetricsListResponseLinks { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct MetricsListResponseLinksVisitor; + impl<'a> Visitor<'a> for MetricsListResponseLinksVisitor { + type Value = MetricsListResponseLinks; + + fn expecting(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.write_str("a mapping") + } + + fn visit_map(self, mut map: M) -> Result + where + M: MapAccess<'a>, + { + let mut first: Option = None; + let mut last: Option> = None; + let mut next: Option> = None; + let mut prev: Option> = None; + let mut self_: Option = None; + let mut additional_properties: std::collections::BTreeMap< + String, + serde_json::Value, + > = std::collections::BTreeMap::new(); + let mut _unparsed = false; + + while let Some((k, v)) = map.next_entry::()? { + match k.as_str() { + "first" => { + if v.is_null() { + continue; + } + first = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "last" => { + last = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "next" => { + next = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "prev" => { + prev = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "self" => { + if v.is_null() { + continue; + } + self_ = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + &_ => { + if let Ok(value) = serde_json::from_value(v.clone()) { + additional_properties.insert(k, value); + } + } + } + } + + let content = MetricsListResponseLinks { + first, + last, + next, + prev, + self_, + additional_properties, + _unparsed, + }; + + Ok(content) + } + } + + deserializer.deserialize_any(MetricsListResponseLinksVisitor) + } +} diff --git a/tests/scenarios/cassettes/v2/metrics/Get-a-list-of-metrics-returns-Success-response-with-pagination.frozen b/tests/scenarios/cassettes/v2/metrics/Get-a-list-of-metrics-returns-Success-response-with-pagination.frozen new file mode 100644 index 000000000..f6dce24ea --- /dev/null +++ b/tests/scenarios/cassettes/v2/metrics/Get-a-list-of-metrics-returns-Success-response-with-pagination.frozen @@ -0,0 +1 @@ +2024-12-06T19:12:25.667Z \ No newline at end of file diff --git a/tests/scenarios/cassettes/v2/metrics/Get-a-list-of-metrics-returns-Success-response-with-pagination.json b/tests/scenarios/cassettes/v2/metrics/Get-a-list-of-metrics-returns-Success-response-with-pagination.json new file mode 100644 index 000000000..963ed3306 --- /dev/null +++ b/tests/scenarios/cassettes/v2/metrics/Get-a-list-of-metrics-returns-Success-response-with-pagination.json @@ -0,0 +1,61 @@ +{ + "http_interactions": [ + { + "request": { + "body": "", + "headers": { + "Accept": [ + "application/json" + ] + }, + "method": "get", + "uri": "https://api.datadoghq.com/api/v2/metrics?page%5Bsize%5D=2" + }, + "response": { + "body": { + "string": "{\"data\":[{\"type\":\"metrics\",\"id\":\"datadog.event.tracking.indexation.audit.events\"},{\"type\":\"metrics\",\"id\":\"datadog.estimated_usage.events.ingested_events\"}],\"meta\":{\"pagination\":{\"cursor\":null,\"next_cursor\":\"6354566d64454633525642695631597759323173616d4d784f5768614d6d52355748704265455a7561453154526b704554315a4f616c5577566b685862456f305455646e6557517a526e6c5752555642526d787355314e756233686c617a56575656526b52465273516c6456526c5a74557a427756574a73525546425155464251554642526c705464316455566b354a5658706e4e574d7863465669565852465657316b576b354962464a566257733156564642516b5a7561453154526b704554315a4f616c5577566b685862456f305455646e6557517a526e6c57525556425155453950546f365a474630595752765a79356c63335270625746305a57526664584e685a3255755a585a6c626e527a4c6d6c755a32567a6447566b583256325a573530637977324c6a67794e7a677a4e3255724d44593d\",\"limit\":1,\"type\":\"cursor_limit\"}},\"links\":{\"self\":\"https://api.datadoghq.com/api/v2/metrics?page[size]=1\",\"last\":null,\"next\":\"https://api.datadoghq.com/api/v2/metrics?page[cursor]=6354566d64454633525642695631597759323173616d4d784f5768614d6d52355748704265455a7561453154526b704554315a4f616c5577566b685862456f305455646e6557517a526e6c5752555642526d787355314e756233686c617a56575656526b52465273516c6456526c5a74557a427756574a73525546425155464251554642526c705464316455566b354a5658706e4e574d7863465669565852465657316b576b354962464a566257733156564642516b5a7561453154526b704554315a4f616c5577566b685862456f305455646e6557517a526e6c57525556425155453950546f365a474630595752765a79356c63335270625746305a57526664584e685a3255755a585a6c626e527a4c6d6c755a32567a6447566b583256325a573530637977324c6a67794e7a677a4e3255724d44593d\",\"prev\":null,\"first\":\"https://api.datadoghq.com/api/v2/metrics?page[size]=1\"}}\n", + "encoding": null + }, + "headers": { + "Content-Type": [ + "application/json" + ] + }, + "status": { + "code": 200, + "message": "OK" + } + }, + "recorded_at": "Fri, 06 Dec 2024 19:12:25 GMT" + }, + { + "request": { + "body": "", + "headers": { + "Accept": [ + "application/json" + ] + }, + "method": "get", + "uri": "https://api.datadoghq.com/api/v2/metrics?page%5Bsize%5D=2&page%5Bcursor%5D=6354566d64454633525642695631597759323173616d4d784f5768614d6d52355748704265455a7561453154526b704554315a4f616c5577566b685862456f305455646e6557517a526e6c5752555642526d787355314e756233686c617a56575656526b52465273516c6456526c5a74557a427756574a73525546425155464251554642526c705464316455566b354a5658706e4e574d7863465669565852465657316b576b354962464a566257733156564642516b5a7561453154526b704554315a4f616c5577566b685862456f305455646e6557517a526e6c57525556425155453950546f365a474630595752765a79356c63335270625746305a57526664584e685a3255755a585a6c626e527a4c6d6c755a32567a6447566b583256325a573530637977324c6a67794e7a677a4e3255724d44593d" + }, + "response": { + "body": { + "string": "{\"data\":[{\"type\":\"metrics\",\"id\":\"datadog.estimated_usage.synthetics.api_test_runs\"}],\"meta\":{\"pagination\":{\"cursor\":\"6354566d64454633525642695631597759323173616d4d784f5768614d6d52355748704265455a7561453154526b704554315a4f616c5577566b685862456f305455646e6557517a526e6c5752555642526d787355314e756233686c617a56575656526b52465273516c6456526c5a74557a427756574a73525546425155464251554642526c705464316455566b354a5658706e4e574d7863465669565852465657316b576b354962464a566257733156564642516b5a7561453154526b704554315a4f616c5577566b685862456f305455646e6557517a526e6c57525556425155453950546f365a474630595752765a79356c63335270625746305a57526664584e685a3255755a585a6c626e527a4c6d6c755a32567a6447566b583256325a573530637977324c6a67794e7a677a4e3255724d44593d\",\"next_cursor\":\"6354566d64454633525642695631597759323173616d4d784f5768614d6d52355748704265455a7561453154526b704554315a4f616c5577566b685862456f305455646e6557517a526e6c5752555642526d787355314e756233686c617a56575656526b52465273516c6456526c5a74557a427756574a73525546425155464251554642526c705464316455566b354a5658706e4e574d7863465669565852465657316b576b354962464a566257733156564642516b5a7561453154526b704554315a4f616c5577566b685862456f305455646e6557517a526e6c57525556425155453950546f365a474630595752765a79356c63335270625746305a57526664584e685a32557563336c756447686c64476c6a6379356863476c666447567a644639796457357a4c4445754d44417a4e6a4d795a5373774e673d3d\",\"limit\":1,\"type\":\"cursor_limit\"}},\"links\":{\"self\":\"https://api.datadoghq.com/api/v2/metrics?page[cursor]=6354566d64454633525642695631597759323173616d4d784f5768614d6d52355748704265455a7561453154526b704554315a4f616c5577566b685862456f305455646e6557517a526e6c5752555642526d787355314e756233686c617a56575656526b52465273516c6456526c5a74557a427756574a73525546425155464251554642526c705464316455566b354a5658706e4e574d7863465669565852465657316b576b354962464a566257733156564642516b5a7561453154526b704554315a4f616c5577566b685862456f305455646e6557517a526e6c57525556425155453950546f365a474630595752765a79356c63335270625746305a57526664584e685a3255755a585a6c626e527a4c6d6c755a32567a6447566b583256325a573530637977324c6a67794e7a677a4e3255724d44593d&page[size]=1\",\"last\":null,\"next\":\"https://api.datadoghq.com/api/v2/metrics?page[cursor]=6354566d64454633525642695631597759323173616d4d784f5768614d6d52355748704265455a7561453154526b704554315a4f616c5577566b685862456f305455646e6557517a526e6c5752555642526d787355314e756233686c617a56575656526b52465273516c6456526c5a74557a427756574a73525546425155464251554642526c705464316455566b354a5658706e4e574d7863465669565852465657316b576b354962464a566257733156564642516b5a7561453154526b704554315a4f616c5577566b685862456f305455646e6557517a526e6c57525556425155453950546f365a474630595752765a79356c63335270625746305a57526664584e685a32557563336c756447686c64476c6a6379356863476c666447567a644639796457357a4c4445754d44417a4e6a4d795a5373774e673d3d\",\"prev\":null,\"first\":\"https://api.datadoghq.com/api/v2/metrics?page[size]=1\"}}\n", + "encoding": null + }, + "headers": { + "Content-Type": [ + "application/json" + ] + }, + "status": { + "code": 200, + "message": "OK" + } + }, + "recorded_at": "Fri, 06 Dec 2024 19:12:25 GMT" + } + ], + "recorded_with": "VCR 6.0.0" +} \ No newline at end of file diff --git a/tests/scenarios/features/v2/metrics.feature b/tests/scenarios/features/v2/metrics.feature index d98275fb2..b69e8dc10 100644 --- a/tests/scenarios/features/v2/metrics.feature +++ b/tests/scenarios/features/v2/metrics.feature @@ -125,6 +125,15 @@ Feature: Metrics When the request is sent Then the response status is 200 Success + @replay-only @skip-validation @team:DataDog/metrics-experience @with-pagination + Scenario: Get a list of metrics returns "Success" response with pagination + Given a valid "appKeyAuth" key in the system + And new "ListTagConfigurations" request + And request contains "page[size]" parameter with value 2 + When the request with pagination is sent + Then the response status is 200 Success + And the response has 3 items + @team:DataDog/metrics-experience Scenario: Get a list of metrics with a tag filter returns "Success" response Given a valid "appKeyAuth" key in the system diff --git a/tests/scenarios/function_mappings.rs b/tests/scenarios/function_mappings.rs index 95d78ce12..be3ebc194 100644 --- a/tests/scenarios/function_mappings.rs +++ b/tests/scenarios/function_mappings.rs @@ -2418,6 +2418,10 @@ pub fn collect_function_calls(world: &mut DatadogWorld) { "v2.ListTagConfigurations".into(), test_v2_list_tag_configurations, ); + world.function_mappings.insert( + "v2.ListTagConfigurationsWithPagination".into(), + test_v2_list_tag_configurations_with_pagination, + ); world.function_mappings.insert( "v2.DeleteBulkTagsMetricsConfiguration".into(), test_v2_delete_bulk_tags_metrics_configuration, @@ -17631,6 +17635,12 @@ fn test_v2_list_tag_configurations(world: &mut DatadogWorld, _parameters: &HashM let window_seconds = _parameters .get("window[seconds]") .and_then(|param| Some(serde_json::from_value(param.clone()).unwrap())); + let page_size = _parameters + .get("page[size]") + .and_then(|param| Some(serde_json::from_value(param.clone()).unwrap())); + let page_cursor = _parameters + .get("page[cursor]") + .and_then(|param| Some(serde_json::from_value(param.clone()).unwrap())); let mut params = datadogV2::api_metrics::ListTagConfigurationsOptionalParams::default(); params.filter_configured = filter_configured; params.filter_tags_configured = filter_tags_configured; @@ -17639,6 +17649,8 @@ fn test_v2_list_tag_configurations(world: &mut DatadogWorld, _parameters: &HashM params.filter_queried = filter_queried; params.filter_tags = filter_tags; params.window_seconds = window_seconds; + params.page_size = page_size; + params.page_cursor = page_cursor; let response = match block_on(api.list_tag_configurations_with_http_info(params)) { Ok(response) => response, Err(error) => { @@ -17656,6 +17668,79 @@ fn test_v2_list_tag_configurations(world: &mut DatadogWorld, _parameters: &HashM world.response.object = serde_json::to_value(response.entity).unwrap(); world.response.code = response.status.as_u16(); } +fn test_v2_list_tag_configurations_with_pagination( + world: &mut DatadogWorld, + _parameters: &HashMap, +) { + let api = world + .api_instances + .v2_api_metrics + .as_ref() + .expect("api instance not found"); + let filter_configured = _parameters + .get("filter[configured]") + .and_then(|param| Some(serde_json::from_value(param.clone()).unwrap())); + let filter_tags_configured = _parameters + .get("filter[tags_configured]") + .and_then(|param| Some(serde_json::from_value(param.clone()).unwrap())); + let filter_metric_type = _parameters + .get("filter[metric_type]") + .and_then(|param| Some(serde_json::from_value(param.clone()).unwrap())); + let filter_include_percentiles = _parameters + .get("filter[include_percentiles]") + .and_then(|param| Some(serde_json::from_value(param.clone()).unwrap())); + let filter_queried = _parameters + .get("filter[queried]") + .and_then(|param| Some(serde_json::from_value(param.clone()).unwrap())); + let filter_tags = _parameters + .get("filter[tags]") + .and_then(|param| Some(serde_json::from_value(param.clone()).unwrap())); + let window_seconds = _parameters + .get("window[seconds]") + .and_then(|param| Some(serde_json::from_value(param.clone()).unwrap())); + let page_size = _parameters + .get("page[size]") + .and_then(|param| Some(serde_json::from_value(param.clone()).unwrap())); + let page_cursor = _parameters + .get("page[cursor]") + .and_then(|param| Some(serde_json::from_value(param.clone()).unwrap())); + let mut params = datadogV2::api_metrics::ListTagConfigurationsOptionalParams::default(); + params.filter_configured = filter_configured; + params.filter_tags_configured = filter_tags_configured; + params.filter_metric_type = filter_metric_type; + params.filter_include_percentiles = filter_include_percentiles; + params.filter_queried = filter_queried; + params.filter_tags = filter_tags; + params.window_seconds = window_seconds; + params.page_size = page_size; + params.page_cursor = page_cursor; + let response = api.list_tag_configurations_with_pagination(params); + let mut result = Vec::new(); + + block_on(async { + pin_mut!(response); + + while let Some(resp) = response.next().await { + match resp { + Ok(response) => { + result.push(response); + } + Err(error) => { + return match error { + Error::ResponseError(e) => { + if let Some(entity) = e.entity { + world.response.object = serde_json::to_value(entity).unwrap(); + } + } + _ => panic!("error parsing response: {}", error), + }; + } + } + } + }); + world.response.object = serde_json::to_value(result).unwrap(); + world.response.code = 200; +} fn test_v2_delete_bulk_tags_metrics_configuration( world: &mut DatadogWorld,