From 005a5217f6ddf857236d22d3b9f7374057e32a5a Mon Sep 17 00:00:00 2001 From: "ci.datadog-api-spec" Date: Tue, 2 Jun 2026 22:40:51 +0000 Subject: [PATCH] Regenerate client from commit d03a328 of spec repo --- .generator/schemas/v2/openapi.yaml | 143 ++++++++++ .../v2_stegadography_GetWidgetsFromImage.rs | 19 ++ src/datadog/configuration.rs | 1 + src/datadogV2/api/api_stegadography.rs | 255 ++++++++++++++++++ src/datadogV2/api/mod.rs | 1 + src/datadogV2/mod.rs | 1 + src/datadogV2/model/mod.rs | 10 + ...del_stegadography_get_widgets_form_data.rs | 105 ++++++++ .../model_stegadography_widget_attributes.rs | 127 +++++++++ .../model/model_stegadography_widget_data.rs | 125 +++++++++ .../model/model_stegadography_widget_type.rs | 48 ++++ .../model_stegadography_widgets_response.rs | 94 +++++++ .../features/v2/stegadography.feature | 25 ++ tests/scenarios/features/v2/undo.json | 6 + tests/scenarios/function_mappings.rs | 47 ++++ 15 files changed, 1007 insertions(+) create mode 100644 examples/v2_stegadography_GetWidgetsFromImage.rs create mode 100644 src/datadogV2/api/api_stegadography.rs create mode 100644 src/datadogV2/model/model_stegadography_get_widgets_form_data.rs create mode 100644 src/datadogV2/model/model_stegadography_widget_attributes.rs create mode 100644 src/datadogV2/model/model_stegadography_widget_data.rs create mode 100644 src/datadogV2/model/model_stegadography_widget_type.rs create mode 100644 src/datadogV2/model/model_stegadography_widgets_response.rs create mode 100644 tests/scenarios/features/v2/stegadography.feature diff --git a/.generator/schemas/v2/openapi.yaml b/.generator/schemas/v2/openapi.yaml index 52c5ef104a..2b63bd06dd 100644 --- a/.generator/schemas/v2/openapi.yaml +++ b/.generator/schemas/v2/openapi.yaml @@ -86739,6 +86739,77 @@ components: required: - data type: object + StegadographyGetWidgetsFormData: + description: The form data submitted to look up widgets from a watermarked image. + properties: + image: + description: A PNG image (for example, a dashboard screenshot) containing embedded widget watermarks. + format: binary + type: string + x-mimetype: image/png + type: object + StegadographyWidgetAttributes: + description: Attributes of a widget recovered from an image watermark. + properties: + locationx: + description: Horizontal pixel coordinate of the watermark within the image. + example: 120 + format: int64 + type: integer + locationy: + description: Vertical pixel coordinate of the watermark within the image. + example: 240 + format: int64 + type: integer + rawData: + description: Stored snapshot of the widget state, returned exactly as it was cached. + example: '{"definition":{"type":"timeseries"}}' + type: string + watermark: + description: The watermark value extracted from the image that this widget was matched against. + example: "abc123" + type: string + required: + - rawData + - watermark + - locationx + - locationy + type: object + StegadographyWidgetData: + description: A single widget recovered from an image watermark. + properties: + attributes: + $ref: "#/components/schemas/StegadographyWidgetAttributes" + id: + description: Identifier of the cached widget, scoped to the organization. + example: "1a2b:abc123" + type: string + type: + $ref: "#/components/schemas/StegadographyWidgetType" + required: + - id + - type + - attributes + type: object + StegadographyWidgetType: + description: Widget resource type. + enum: + - widget + example: widget + type: string + x-enum-varnames: + - WIDGET + StegadographyWidgetsResponse: + description: Response containing the widgets recovered from the uploaded image. + properties: + data: + description: List of widgets matched to watermarks found in the image. + items: + $ref: "#/components/schemas/StegadographyWidgetData" + type: array + required: + - data + type: object Step: description: A Step is a sub-component of a workflow. Each Step performs an action. properties: @@ -167338,6 +167409,76 @@ paths: - status_pages_settings_write - status_pages_public_page_publish - status_pages_internal_page_publish + /api/v2/stegadography/get-widgets: + post: + description: |- + Extracts embedded watermarks from an uploaded PNG image (for example, a dashboard screenshot) + and returns the cached widget state matching each watermark found. + operationId: GetWidgetsFromImage + requestBody: + content: + multipart/form-data: + examples: + default: + value: {} + schema: + $ref: "#/components/schemas/StegadographyGetWidgetsFormData" + description: The image to extract widget watermarks from. + required: true + responses: + "200": + content: + application/json: + examples: + default: + value: + data: + - attributes: + locationx: 120 + locationy: 240 + rawData: '{"definition":{"type":"timeseries"}}' + watermark: "abc123" + id: "1a2b:abc123" + type: widget + schema: + $ref: "#/components/schemas/StegadographyWidgetsResponse" + description: OK + "400": + content: + application/json: + schema: + $ref: "#/components/schemas/JSONAPIErrorResponse" + description: Bad Request + "403": + content: + application/json: + schema: + $ref: "#/components/schemas/JSONAPIErrorResponse" + description: Forbidden + "415": + content: + application/json: + schema: + $ref: "#/components/schemas/JSONAPIErrorResponse" + description: Unsupported Media Type + "429": + $ref: "#/components/responses/TooManyRequestsResponse" + "500": + content: + application/json: + schema: + $ref: "#/components/schemas/JSONAPIErrorResponse" + description: Internal Server Error + security: + - apiKeyAuth: [] + appKeyAuth: [] + summary: Get widgets from an image + tags: + - Stegadography + x-codegen-request-body-name: body + x-unstable: |- + **Note**: This endpoint is in preview and is subject to change. + If you have any feedback, contact [Datadog support](https://docs.datadoghq.com/help/). /api/v2/synthetics/api-multistep/subtests/{public_id}: get: description: |- @@ -175651,6 +175792,8 @@ tags: externalDocs: url: https://docs.datadoghq.com/api/latest/statuspage-integration name: Statuspage Integration + - description: Recover dashboard widget data from watermarks embedded in images. + name: Stegadography - description: |- Enable Storage Management for S3 buckets, GCS buckets, and Azure containers. Each configuration registers the destination that holds inventory reports for the storage being monitored. name: Storage Management diff --git a/examples/v2_stegadography_GetWidgetsFromImage.rs b/examples/v2_stegadography_GetWidgetsFromImage.rs new file mode 100644 index 0000000000..105e7cc311 --- /dev/null +++ b/examples/v2_stegadography_GetWidgetsFromImage.rs @@ -0,0 +1,19 @@ +// Get widgets from an image returns "OK" response +use datadog_api_client::datadog; +use datadog_api_client::datadogV2::api_stegadography::GetWidgetsFromImageOptionalParams; +use datadog_api_client::datadogV2::api_stegadography::StegadographyAPI; + +#[tokio::main] +async fn main() { + let mut configuration = datadog::Configuration::new(); + configuration.set_unstable_operation_enabled("v2.GetWidgetsFromImage", true); + let api = StegadographyAPI::with_config(configuration); + let resp = api + .get_widgets_from_image(GetWidgetsFromImageOptionalParams::default()) + .await; + if let Ok(value) = resp { + println!("{:#?}", value); + } else { + println!("{:#?}", resp.unwrap_err()); + } +} diff --git a/src/datadog/configuration.rs b/src/datadog/configuration.rs index 90cb71c4a4..f80f6ff493 100644 --- a/src/datadog/configuration.rs +++ b/src/datadog/configuration.rs @@ -636,6 +636,7 @@ impl Default for Configuration { ("v2.revert_custom_rule_revision".to_owned(), false), ("v2.update_ai_custom_ruleset".to_owned(), false), ("v2.update_custom_ruleset".to_owned(), false), + ("v2.get_widgets_from_image".to_owned(), false), ("v2.add_member_team".to_owned(), false), ("v2.list_member_teams".to_owned(), false), ("v2.remove_member_team".to_owned(), false), diff --git a/src/datadogV2/api/api_stegadography.rs b/src/datadogV2/api/api_stegadography.rs new file mode 100644 index 0000000000..612789b7a9 --- /dev/null +++ b/src/datadogV2/api/api_stegadography.rs @@ -0,0 +1,255 @@ +// 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 crate::datadog; +use log::warn; +use reqwest::header::{HeaderMap, HeaderValue}; +use serde::{Deserialize, Serialize}; + +/// GetWidgetsFromImageOptionalParams is a struct for passing parameters to the method [`StegadographyAPI::get_widgets_from_image`] +#[non_exhaustive] +#[derive(Clone, Default, Debug)] +pub struct GetWidgetsFromImageOptionalParams { + /// A PNG image (for example, a dashboard screenshot) containing embedded widget watermarks. + pub image: Option>, +} + +impl GetWidgetsFromImageOptionalParams { + /// A PNG image (for example, a dashboard screenshot) containing embedded widget watermarks. + pub fn image(mut self, value: Vec) -> Self { + self.image = Some(value); + self + } +} + +/// GetWidgetsFromImageError is a struct for typed errors of method [`StegadographyAPI::get_widgets_from_image`] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum GetWidgetsFromImageError { + JSONAPIErrorResponse(crate::datadogV2::model::JSONAPIErrorResponse), + APIErrorResponse(crate::datadogV2::model::APIErrorResponse), + UnknownValue(serde_json::Value), +} + +/// Recover dashboard widget data from watermarks embedded in images. +#[derive(Debug, Clone)] +pub struct StegadographyAPI { + config: datadog::Configuration, + client: reqwest_middleware::ClientWithMiddleware, +} + +impl Default for StegadographyAPI { + fn default() -> Self { + Self::with_config(datadog::Configuration::default()) + } +} + +impl StegadographyAPI { + pub fn new() -> Self { + Self::default() + } + pub fn with_config(config: datadog::Configuration) -> Self { + let reqwest_client_builder = { + let builder = reqwest::Client::builder(); + #[cfg(not(target_arch = "wasm32"))] + let builder = if let Some(proxy_url) = &config.proxy_url { + builder.proxy(reqwest::Proxy::all(proxy_url).expect("Failed to parse proxy URL")) + } else { + builder + }; + builder + }; + + let middleware_client_builder = { + let builder = + reqwest_middleware::ClientBuilder::new(reqwest_client_builder.build().unwrap()); + #[cfg(feature = "retry")] + let builder = if config.enable_retry { + struct RetryableStatus; + impl reqwest_retry::RetryableStrategy for RetryableStatus { + fn handle( + &self, + res: &Result, + ) -> Option { + match res { + Ok(success) => reqwest_retry::default_on_request_success(success), + Err(_) => None, + } + } + } + let backoff_policy = reqwest_retry::policies::ExponentialBackoff::builder() + .build_with_max_retries(config.max_retries); + + let retry_middleware = + reqwest_retry::RetryTransientMiddleware::new_with_policy_and_strategy( + backoff_policy, + RetryableStatus, + ); + + builder.with(retry_middleware) + } else { + builder + }; + builder + }; + + let client = middleware_client_builder.build(); + + Self { config, client } + } + + pub fn with_client_and_config( + config: datadog::Configuration, + client: reqwest_middleware::ClientWithMiddleware, + ) -> Self { + Self { config, client } + } + + /// Extracts embedded watermarks from an uploaded PNG image (for example, a dashboard screenshot) + /// and returns the cached widget state matching each watermark found. + pub async fn get_widgets_from_image( + &self, + params: GetWidgetsFromImageOptionalParams, + ) -> Result< + crate::datadogV2::model::StegadographyWidgetsResponse, + datadog::Error, + > { + match self.get_widgets_from_image_with_http_info(params).await { + Ok(response_content) => { + if let Some(e) = response_content.entity { + Ok(e) + } else { + Err(datadog::Error::Serde(serde::de::Error::custom( + "response content was None", + ))) + } + } + Err(err) => Err(err), + } + } + + /// Extracts embedded watermarks from an uploaded PNG image (for example, a dashboard screenshot) + /// and returns the cached widget state matching each watermark found. + pub async fn get_widgets_from_image_with_http_info( + &self, + params: GetWidgetsFromImageOptionalParams, + ) -> Result< + datadog::ResponseContent, + datadog::Error, + > { + let local_configuration = &self.config; + let operation_id = "v2.get_widgets_from_image"; + if local_configuration.is_unstable_operation_enabled(operation_id) { + warn!("Using unstable operation {operation_id}"); + } else { + let local_error = datadog::UnstableOperationDisabledError { + msg: "Operation 'v2.get_widgets_from_image' is not enabled".to_string(), + }; + return Err(datadog::Error::UnstableOperationDisabledError(local_error)); + } + + // unbox and build optional parameters + let image = params.image; + + let local_client = &self.client; + + let local_uri_str = format!( + "{}/api/v2/stegadography/get-widgets", + local_configuration.get_operation_host(operation_id) + ); + let mut local_req_builder = + local_client.request(reqwest::Method::POST, local_uri_str.as_str()); + + // build headers + let mut headers = HeaderMap::new(); + headers.insert( + "Content-Type", + HeaderValue::from_static("multipart/form-data"), + ); + headers.insert("Accept", HeaderValue::from_static("application/json")); + + // build user agent + match HeaderValue::from_str(local_configuration.user_agent.as_str()) { + Ok(user_agent) => headers.insert(reqwest::header::USER_AGENT, user_agent), + Err(e) => { + log::warn!("Failed to parse user agent header: {e}, falling back to default"); + headers.insert( + reqwest::header::USER_AGENT, + HeaderValue::from_static(datadog::DEFAULT_USER_AGENT.as_str()), + ) + } + }; + + // build auth + if let Some(local_key) = local_configuration.auth_keys.get("apiKeyAuth") { + headers.insert( + "DD-API-KEY", + HeaderValue::from_str(local_key.key.as_str()) + .expect("failed to parse DD-API-KEY header"), + ); + }; + if let Some(local_key) = local_configuration.auth_keys.get("appKeyAuth") { + headers.insert( + "DD-APPLICATION-KEY", + HeaderValue::from_str(local_key.key.as_str()) + .expect("failed to parse DD-APPLICATION-KEY header"), + ); + }; + + // build form parameters + if let Some(image) = image { + let mut local_form = form_data_builder::FormData::new(Vec::new()); + let cursor = std::io::Cursor::new(image); + if let Err(e) = local_form.write_file( + "image", + cursor, + Some("image".as_ref()), + "application/octet-stream", + ) { + return Err(crate::datadog::Error::Io(e)); + }; + headers.insert( + "Content-Type", + local_form.content_type_header().parse().unwrap(), + ); + let form_result = local_form.finish(); + match form_result { + Ok(form) => local_req_builder = local_req_builder.body(form), + Err(e) => return Err(crate::datadog::Error::Io(e)), + }; + }; + + local_req_builder = local_req_builder.headers(headers); + let local_req = local_req_builder.build()?; + log::debug!("request content: {:?}", local_req.body()); + let local_resp = local_client.execute(local_req).await?; + + let local_status = local_resp.status(); + let local_content = local_resp.text().await?; + log::debug!("response content: {}", local_content); + + if !local_status.is_client_error() && !local_status.is_server_error() { + match serde_json::from_str::( + &local_content, + ) { + Ok(e) => { + return Ok(datadog::ResponseContent { + status: local_status, + content: local_content, + entity: Some(e), + }) + } + Err(e) => return Err(datadog::Error::Serde(e)), + }; + } else { + let local_entity: Option = + serde_json::from_str(&local_content).ok(); + let local_error = datadog::ResponseContent { + status: local_status, + content: local_content, + entity: local_entity, + }; + Err(datadog::Error::ResponseError(local_error)) + } + } +} diff --git a/src/datadogV2/api/mod.rs b/src/datadogV2/api/mod.rs index 6df9808946..3c5f2fef63 100644 --- a/src/datadogV2/api/mod.rs +++ b/src/datadogV2/api/mod.rs @@ -112,6 +112,7 @@ pub mod api_spans_metrics; pub mod api_static_analysis; pub mod api_status_pages; pub mod api_statuspage_integration; +pub mod api_stegadography; pub mod api_storage_management; pub mod api_synthetics; pub mod api_teams; diff --git a/src/datadogV2/mod.rs b/src/datadogV2/mod.rs index b62d4ea0ab..95117706fe 100644 --- a/src/datadogV2/mod.rs +++ b/src/datadogV2/mod.rs @@ -113,6 +113,7 @@ pub use self::api::api_spans_metrics; pub use self::api::api_static_analysis; pub use self::api::api_status_pages; pub use self::api::api_statuspage_integration; +pub use self::api::api_stegadography; pub use self::api::api_storage_management; pub use self::api::api_synthetics; pub use self::api::api_teams; diff --git a/src/datadogV2/model/mod.rs b/src/datadogV2/model/mod.rs index 98cd61dd7f..8fbfb1d88c 100644 --- a/src/datadogV2/model/mod.rs +++ b/src/datadogV2/model/mod.rs @@ -11030,6 +11030,16 @@ pub mod model_patch_maintenance_request_data_attributes; pub use self::model_patch_maintenance_request_data_attributes::PatchMaintenanceRequestDataAttributes; pub mod model_patch_maintenance_request_data_attributes_components_affected_items; pub use self::model_patch_maintenance_request_data_attributes_components_affected_items::PatchMaintenanceRequestDataAttributesComponentsAffectedItems; +pub mod model_stegadography_get_widgets_form_data; +pub use self::model_stegadography_get_widgets_form_data::StegadographyGetWidgetsFormData; +pub mod model_stegadography_widgets_response; +pub use self::model_stegadography_widgets_response::StegadographyWidgetsResponse; +pub mod model_stegadography_widget_data; +pub use self::model_stegadography_widget_data::StegadographyWidgetData; +pub mod model_stegadography_widget_attributes; +pub use self::model_stegadography_widget_attributes::StegadographyWidgetAttributes; +pub mod model_stegadography_widget_type; +pub use self::model_stegadography_widget_type::StegadographyWidgetType; pub mod model_synthetics_api_multistep_subtests_response; pub use self::model_synthetics_api_multistep_subtests_response::SyntheticsApiMultistepSubtestsResponse; pub mod model_synthetics_api_multistep_subtest_data; diff --git a/src/datadogV2/model/model_stegadography_get_widgets_form_data.rs b/src/datadogV2/model/model_stegadography_get_widgets_form_data.rs new file mode 100644 index 0000000000..5b730b1d03 --- /dev/null +++ b/src/datadogV2/model/model_stegadography_get_widgets_form_data.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}; + +/// The form data submitted to look up widgets from a watermarked image. +#[non_exhaustive] +#[skip_serializing_none] +#[derive(Clone, Debug, PartialEq, Serialize)] +pub struct StegadographyGetWidgetsFormData { + /// A PNG image (for example, a dashboard screenshot) containing embedded widget watermarks. + #[serde(rename = "image")] + pub image: Option>, + #[serde(flatten)] + pub additional_properties: std::collections::BTreeMap, + #[serde(skip)] + #[serde(default)] + pub(crate) _unparsed: bool, +} + +impl StegadographyGetWidgetsFormData { + pub fn new() -> StegadographyGetWidgetsFormData { + StegadographyGetWidgetsFormData { + image: None, + additional_properties: std::collections::BTreeMap::new(), + _unparsed: false, + } + } + + pub fn image(mut self, value: Vec) -> Self { + self.image = Some(value); + self + } + + pub fn additional_properties( + mut self, + value: std::collections::BTreeMap, + ) -> Self { + self.additional_properties = value; + self + } +} + +impl Default for StegadographyGetWidgetsFormData { + fn default() -> Self { + Self::new() + } +} + +impl<'de> Deserialize<'de> for StegadographyGetWidgetsFormData { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct StegadographyGetWidgetsFormDataVisitor; + impl<'a> Visitor<'a> for StegadographyGetWidgetsFormDataVisitor { + type Value = StegadographyGetWidgetsFormData; + + 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 image: 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() { + "image" => { + if v.is_null() { + continue; + } + image = 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 = StegadographyGetWidgetsFormData { + image, + additional_properties, + _unparsed, + }; + + Ok(content) + } + } + + deserializer.deserialize_any(StegadographyGetWidgetsFormDataVisitor) + } +} diff --git a/src/datadogV2/model/model_stegadography_widget_attributes.rs b/src/datadogV2/model/model_stegadography_widget_attributes.rs new file mode 100644 index 0000000000..cc7b70cd8a --- /dev/null +++ b/src/datadogV2/model/model_stegadography_widget_attributes.rs @@ -0,0 +1,127 @@ +// 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}; + +/// Attributes of a widget recovered from an image watermark. +#[non_exhaustive] +#[skip_serializing_none] +#[derive(Clone, Debug, PartialEq, Serialize)] +pub struct StegadographyWidgetAttributes { + /// Horizontal pixel coordinate of the watermark within the image. + #[serde(rename = "locationx")] + pub locationx: i64, + /// Vertical pixel coordinate of the watermark within the image. + #[serde(rename = "locationy")] + pub locationy: i64, + /// Stored snapshot of the widget state, returned exactly as it was cached. + #[serde(rename = "rawData")] + pub raw_data: String, + /// The watermark value extracted from the image that this widget was matched against. + #[serde(rename = "watermark")] + pub watermark: String, + #[serde(flatten)] + pub additional_properties: std::collections::BTreeMap, + #[serde(skip)] + #[serde(default)] + pub(crate) _unparsed: bool, +} + +impl StegadographyWidgetAttributes { + pub fn new( + locationx: i64, + locationy: i64, + raw_data: String, + watermark: String, + ) -> StegadographyWidgetAttributes { + StegadographyWidgetAttributes { + locationx, + locationy, + raw_data, + watermark, + additional_properties: std::collections::BTreeMap::new(), + _unparsed: false, + } + } + + pub fn additional_properties( + mut self, + value: std::collections::BTreeMap, + ) -> Self { + self.additional_properties = value; + self + } +} + +impl<'de> Deserialize<'de> for StegadographyWidgetAttributes { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct StegadographyWidgetAttributesVisitor; + impl<'a> Visitor<'a> for StegadographyWidgetAttributesVisitor { + type Value = StegadographyWidgetAttributes; + + 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 locationx: Option = None; + let mut locationy: Option = None; + let mut raw_data: Option = None; + let mut watermark: 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() { + "locationx" => { + locationx = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "locationy" => { + locationy = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "rawData" => { + raw_data = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "watermark" => { + watermark = 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 locationx = locationx.ok_or_else(|| M::Error::missing_field("locationx"))?; + let locationy = locationy.ok_or_else(|| M::Error::missing_field("locationy"))?; + let raw_data = raw_data.ok_or_else(|| M::Error::missing_field("raw_data"))?; + let watermark = watermark.ok_or_else(|| M::Error::missing_field("watermark"))?; + + let content = StegadographyWidgetAttributes { + locationx, + locationy, + raw_data, + watermark, + additional_properties, + _unparsed, + }; + + Ok(content) + } + } + + deserializer.deserialize_any(StegadographyWidgetAttributesVisitor) + } +} diff --git a/src/datadogV2/model/model_stegadography_widget_data.rs b/src/datadogV2/model/model_stegadography_widget_data.rs new file mode 100644 index 0000000000..68ab11faa1 --- /dev/null +++ b/src/datadogV2/model/model_stegadography_widget_data.rs @@ -0,0 +1,125 @@ +// 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}; + +/// A single widget recovered from an image watermark. +#[non_exhaustive] +#[skip_serializing_none] +#[derive(Clone, Debug, PartialEq, Serialize)] +pub struct StegadographyWidgetData { + /// Attributes of a widget recovered from an image watermark. + #[serde(rename = "attributes")] + pub attributes: crate::datadogV2::model::StegadographyWidgetAttributes, + /// Identifier of the cached widget, scoped to the organization. + #[serde(rename = "id")] + pub id: String, + /// Widget resource type. + #[serde(rename = "type")] + pub type_: crate::datadogV2::model::StegadographyWidgetType, + #[serde(flatten)] + pub additional_properties: std::collections::BTreeMap, + #[serde(skip)] + #[serde(default)] + pub(crate) _unparsed: bool, +} + +impl StegadographyWidgetData { + pub fn new( + attributes: crate::datadogV2::model::StegadographyWidgetAttributes, + id: String, + type_: crate::datadogV2::model::StegadographyWidgetType, + ) -> StegadographyWidgetData { + StegadographyWidgetData { + attributes, + id, + type_, + additional_properties: std::collections::BTreeMap::new(), + _unparsed: false, + } + } + + pub fn additional_properties( + mut self, + value: std::collections::BTreeMap, + ) -> Self { + self.additional_properties = value; + self + } +} + +impl<'de> Deserialize<'de> for StegadographyWidgetData { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct StegadographyWidgetDataVisitor; + impl<'a> Visitor<'a> for StegadographyWidgetDataVisitor { + type Value = StegadographyWidgetData; + + 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 attributes: Option = + None; + let mut id: 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() { + "attributes" => { + attributes = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "id" => { + id = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "type" => { + type_ = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + if let Some(ref _type_) = type_ { + match _type_ { + crate::datadogV2::model::StegadographyWidgetType::UnparsedObject(_type_) => { + _unparsed = true; + }, + _ => {} + } + } + } + &_ => { + if let Ok(value) = serde_json::from_value(v.clone()) { + additional_properties.insert(k, value); + } + } + } + } + let attributes = attributes.ok_or_else(|| M::Error::missing_field("attributes"))?; + let id = id.ok_or_else(|| M::Error::missing_field("id"))?; + let type_ = type_.ok_or_else(|| M::Error::missing_field("type_"))?; + + let content = StegadographyWidgetData { + attributes, + id, + type_, + additional_properties, + _unparsed, + }; + + Ok(content) + } + } + + deserializer.deserialize_any(StegadographyWidgetDataVisitor) + } +} diff --git a/src/datadogV2/model/model_stegadography_widget_type.rs b/src/datadogV2/model/model_stegadography_widget_type.rs new file mode 100644 index 0000000000..d25e53ce46 --- /dev/null +++ b/src/datadogV2/model/model_stegadography_widget_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 StegadographyWidgetType { + WIDGET, + UnparsedObject(crate::datadog::UnparsedObject), +} + +impl ToString for StegadographyWidgetType { + fn to_string(&self) -> String { + match self { + Self::WIDGET => String::from("widget"), + Self::UnparsedObject(v) => v.value.to_string(), + } + } +} + +impl Serialize for StegadographyWidgetType { + 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 StegadographyWidgetType { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let s: String = String::deserialize(deserializer)?; + Ok(match s.as_str() { + "widget" => Self::WIDGET, + _ => Self::UnparsedObject(crate::datadog::UnparsedObject { + value: serde_json::Value::String(s.into()), + }), + }) + } +} diff --git a/src/datadogV2/model/model_stegadography_widgets_response.rs b/src/datadogV2/model/model_stegadography_widgets_response.rs new file mode 100644 index 0000000000..31d550d828 --- /dev/null +++ b/src/datadogV2/model/model_stegadography_widgets_response.rs @@ -0,0 +1,94 @@ +// 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 containing the widgets recovered from the uploaded image. +#[non_exhaustive] +#[skip_serializing_none] +#[derive(Clone, Debug, PartialEq, Serialize)] +pub struct StegadographyWidgetsResponse { + /// List of widgets matched to watermarks found in the image. + #[serde(rename = "data")] + pub data: Vec, + #[serde(flatten)] + pub additional_properties: std::collections::BTreeMap, + #[serde(skip)] + #[serde(default)] + pub(crate) _unparsed: bool, +} + +impl StegadographyWidgetsResponse { + pub fn new( + data: Vec, + ) -> StegadographyWidgetsResponse { + StegadographyWidgetsResponse { + data, + additional_properties: std::collections::BTreeMap::new(), + _unparsed: false, + } + } + + pub fn additional_properties( + mut self, + value: std::collections::BTreeMap, + ) -> Self { + self.additional_properties = value; + self + } +} + +impl<'de> Deserialize<'de> for StegadographyWidgetsResponse { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct StegadographyWidgetsResponseVisitor; + impl<'a> Visitor<'a> for StegadographyWidgetsResponseVisitor { + type Value = StegadographyWidgetsResponse; + + 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 data: 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() { + "data" => { + data = 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 data = data.ok_or_else(|| M::Error::missing_field("data"))?; + + let content = StegadographyWidgetsResponse { + data, + additional_properties, + _unparsed, + }; + + Ok(content) + } + } + + deserializer.deserialize_any(StegadographyWidgetsResponseVisitor) + } +} diff --git a/tests/scenarios/features/v2/stegadography.feature b/tests/scenarios/features/v2/stegadography.feature new file mode 100644 index 0000000000..b33a502f26 --- /dev/null +++ b/tests/scenarios/features/v2/stegadography.feature @@ -0,0 +1,25 @@ +@endpoint(stegadography) @endpoint(stegadography-v2) +Feature: Stegadography + Recover dashboard widget data from watermarks embedded in images. + + Background: + Given a valid "apiKeyAuth" key in the system + And a valid "appKeyAuth" key in the system + And an instance of "Stegadography" API + And operation "GetWidgetsFromImage" enabled + And new "GetWidgetsFromImage" request + + @generated @skip @team:DataDog/dataviz-backend-maintainers + Scenario: Get widgets from an image returns "Bad Request" response + When the request is sent + Then the response status is 400 Bad Request + + @generated @skip @team:DataDog/dataviz-backend-maintainers + Scenario: Get widgets from an image returns "OK" response + When the request is sent + Then the response status is 200 OK + + @generated @skip @team:DataDog/dataviz-backend-maintainers + Scenario: Get widgets from an image returns "Unsupported Media Type" response + When the request is sent + Then the response status is 415 Unsupported Media Type diff --git a/tests/scenarios/features/v2/undo.json b/tests/scenarios/features/v2/undo.json index f60cf1a2f7..ba316f6d90 100644 --- a/tests/scenarios/features/v2/undo.json +++ b/tests/scenarios/features/v2/undo.json @@ -7918,6 +7918,12 @@ "type": "idempotent" } }, + "GetWidgetsFromImage": { + "tag": "Stegadography", + "undo": { + "type": "safe" + } + }, "GetApiMultistepSubtests": { "tag": "Synthetics", "undo": { diff --git a/tests/scenarios/function_mappings.rs b/tests/scenarios/function_mappings.rs index 3e402918f0..8b8ddd2793 100644 --- a/tests/scenarios/function_mappings.rs +++ b/tests/scenarios/function_mappings.rs @@ -201,6 +201,7 @@ pub struct ApiInstances { pub v2_api_spans: Option, pub v2_api_static_analysis: Option, pub v2_api_status_pages: Option, + pub v2_api_stegadography: Option, pub v2_api_synthetics: Option, pub v2_api_teams: Option, pub v2_api_web_integrations: Option, @@ -1278,6 +1279,14 @@ pub fn initialize_api_instance(world: &mut DatadogWorld, api: String) { ), ); } + "Stegadography" => { + world.api_instances.v2_api_stegadography = Some( + datadogV2::api_stegadography::StegadographyAPI::with_client_and_config( + world.config.clone(), + world.http_client.as_ref().unwrap().clone(), + ), + ); + } "Teams" => { world.api_instances.v2_api_teams = Some(datadogV2::api_teams::TeamsAPI::with_client_and_config( @@ -6608,6 +6617,10 @@ pub fn collect_function_calls(world: &mut DatadogWorld) { "v2.UnpublishStatusPage".into(), test_v2_unpublish_status_page, ); + world.function_mappings.insert( + "v2.GetWidgetsFromImage".into(), + test_v2_get_widgets_from_image, + ); world.function_mappings.insert( "v2.GetApiMultistepSubtests".into(), test_v2_get_api_multistep_subtests, @@ -52517,6 +52530,40 @@ fn test_v2_unpublish_status_page(world: &mut DatadogWorld, _parameters: &HashMap world.response.code = response.status.as_u16(); } +fn test_v2_get_widgets_from_image(world: &mut DatadogWorld, _parameters: &HashMap) { + let api = world + .api_instances + .v2_api_stegadography + .as_ref() + .expect("api instance not found"); + let image = _parameters.get("image").and_then(|param| { + std::fs::read(format!( + "tests/scenarios/features/v{}/{}", + world.api_version, + param.as_str().unwrap() + )) + .ok() + }); + let mut params = datadogV2::api_stegadography::GetWidgetsFromImageOptionalParams::default(); + params.image = image; + let response = match block_on(api.get_widgets_from_image_with_http_info(params)) { + Ok(response) => response, + Err(error) => { + return match error { + Error::ResponseError(e) => { + world.response.code = e.status.as_u16(); + 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(response.entity).unwrap(); + world.response.code = response.status.as_u16(); +} + fn test_v2_get_api_multistep_subtests( world: &mut DatadogWorld, _parameters: &HashMap,