diff --git a/.generator/schemas/v2/openapi.yaml b/.generator/schemas/v2/openapi.yaml index 9bceb7154..526df34c7 100644 --- a/.generator/schemas/v2/openapi.yaml +++ b/.generator/schemas/v2/openapi.yaml @@ -23526,6 +23526,30 @@ components: required: - data type: object + GetSuppressionVersionHistoryData: + description: Data for the suppression version history. + properties: + attributes: + $ref: '#/components/schemas/SuppressionVersionHistory' + id: + description: ID of the suppression. + type: string + type: + $ref: '#/components/schemas/GetSuppressionVersionHistoryDataType' + type: object + GetSuppressionVersionHistoryDataType: + description: Type of data. + enum: + - suppression_version_history + type: string + x-enum-varnames: + - SUPPRESSIONVERSIONHISTORY + GetSuppressionVersionHistoryResponse: + description: Response for getting the suppression version history. + properties: + data: + $ref: '#/components/schemas/GetSuppressionVersionHistoryData' + type: object GetTeamMembershipsSort: description: Specifies the order of returned team memberships enum: @@ -42745,38 +42769,13 @@ components: description: The `RuleVersionHistory` `data`. type: object type: object - RuleVersionUpdate: - description: A change in a rule version. - properties: - change: - description: The new value of the field. - example: cloud_provider:aws - type: string - field: - description: The field that was changed. - example: Tags - type: string - type: - $ref: '#/components/schemas/RuleVersionUpdateType' - type: object - RuleVersionUpdateType: - description: The type of change. - enum: - - create - - update - - delete - type: string - x-enum-varnames: - - CREATE - - UPDATE - - DELETE RuleVersions: description: A rule version with a list of updates. properties: changes: description: A list of changes. items: - $ref: '#/components/schemas/RuleVersionUpdate' + $ref: '#/components/schemas/VersionHistoryUpdate' type: array rule: $ref: '#/components/schemas/SecurityMonitoringRuleResponse' @@ -51322,6 +51321,32 @@ components: format: double type: number type: object + SuppressionVersionHistory: + description: Response object containing the version history of a suppression. + properties: + count: + description: The number of suppression versions. + format: int32 + maximum: 2147483647 + type: integer + data: + additionalProperties: + $ref: '#/components/schemas/SuppressionVersions' + description: A suppression version with a list of updates. + description: The version history of a suppression. + type: object + type: object + SuppressionVersions: + description: A suppression version with a list of updates. + properties: + changes: + description: A list of changes. + items: + $ref: '#/components/schemas/VersionHistoryUpdate' + type: array + suppression: + $ref: '#/components/schemas/SecurityMonitoringSuppressionAttributes' + type: object TableResultV2: description: A reference table resource containing its full configuration and state. @@ -55372,6 +55397,31 @@ components: example: 1 format: int64 type: integer + VersionHistoryUpdate: + description: A change in a rule version. + properties: + change: + description: The new value of the field. + example: cloud_provider:aws + type: string + field: + description: The field that was changed. + example: Tags + type: string + type: + $ref: '#/components/schemas/VersionHistoryUpdateType' + type: object + VersionHistoryUpdateType: + description: The type of change. + enum: + - create + - update + - delete + type: string + x-enum-varnames: + - CREATE + - UPDATE + - DELETE VirusTotalAPIKey: description: The definition of the `VirusTotalAPIKey` object. properties: @@ -79065,6 +79115,35 @@ paths: summary: Update a suppression rule tags: - Security Monitoring + /api/v2/security_monitoring/configuration/suppressions/{suppression_id}/version_history: + get: + description: Get a suppression's version history. + operationId: GetSuppressionVersionHistory + parameters: + - $ref: '#/components/parameters/SecurityMonitoringSuppressionID' + - $ref: '#/components/parameters/PageSize' + - $ref: '#/components/parameters/PageNumber' + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/GetSuppressionVersionHistoryResponse' + description: OK + '403': + $ref: '#/components/responses/NotAuthorizedResponse' + '404': + $ref: '#/components/responses/NotFoundResponse' + '429': + $ref: '#/components/responses/TooManyRequestsResponse' + security: + - apiKeyAuth: [] + appKeyAuth: [] + - AuthZ: + - security_monitoring_suppressions_read + summary: Get a suppression's version history + tags: + - Security Monitoring /api/v2/security_monitoring/rules: get: description: List rules. diff --git a/examples/v2_security-monitoring_GetSuppressionVersionHistory.rs b/examples/v2_security-monitoring_GetSuppressionVersionHistory.rs new file mode 100644 index 000000000..cdfbb91e5 --- /dev/null +++ b/examples/v2_security-monitoring_GetSuppressionVersionHistory.rs @@ -0,0 +1,23 @@ +// Get a suppression's version history returns "OK" response +use datadog_api_client::datadog; +use datadog_api_client::datadogV2::api_security_monitoring::GetSuppressionVersionHistoryOptionalParams; +use datadog_api_client::datadogV2::api_security_monitoring::SecurityMonitoringAPI; + +#[tokio::main] +async fn main() { + // there is a valid "suppression" in the system + let suppression_data_id = std::env::var("SUPPRESSION_DATA_ID").unwrap(); + let configuration = datadog::Configuration::new(); + let api = SecurityMonitoringAPI::with_config(configuration); + let resp = api + .get_suppression_version_history( + suppression_data_id.clone(), + GetSuppressionVersionHistoryOptionalParams::default(), + ) + .await; + if let Ok(value) = resp { + println!("{:#?}", value); + } else { + println!("{:#?}", resp.unwrap_err()); + } +} diff --git a/src/datadogV2/api/api_security_monitoring.rs b/src/datadogV2/api/api_security_monitoring.rs index 3a44bb32f..0b25fa3e0 100644 --- a/src/datadogV2/api/api_security_monitoring.rs +++ b/src/datadogV2/api/api_security_monitoring.rs @@ -156,6 +156,29 @@ impl GetSecurityMonitoringHistsignalsByJobIdOptionalParams { } } +/// GetSuppressionVersionHistoryOptionalParams is a struct for passing parameters to the method [`SecurityMonitoringAPI::get_suppression_version_history`] +#[non_exhaustive] +#[derive(Clone, Default, Debug)] +pub struct GetSuppressionVersionHistoryOptionalParams { + /// Size for a given page. The maximum allowed value is 100. + pub page_size: Option, + /// Specific page number to return. + pub page_number: Option, +} + +impl GetSuppressionVersionHistoryOptionalParams { + /// Size for a given page. The maximum allowed value is 100. + pub fn page_size(mut self, value: i64) -> Self { + self.page_size = Some(value); + self + } + /// Specific page number to return. + pub fn page_number(mut self, value: i64) -> Self { + self.page_number = Some(value); + self + } +} + /// ListAssetsSBOMsOptionalParams is a struct for passing parameters to the method [`SecurityMonitoringAPI::list_assets_sbo_ms`] #[non_exhaustive] #[derive(Clone, Default, Debug)] @@ -1320,6 +1343,14 @@ pub enum GetSignalNotificationRulesError { UnknownValue(serde_json::Value), } +/// GetSuppressionVersionHistoryError is a struct for typed errors of method [`SecurityMonitoringAPI::get_suppression_version_history`] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum GetSuppressionVersionHistoryError { + APIErrorResponse(crate::datadogV2::model::APIErrorResponse), + UnknownValue(serde_json::Value), +} + /// GetSuppressionsAffectingFutureRuleError is a struct for typed errors of method [`SecurityMonitoringAPI::get_suppressions_affecting_future_rule`] #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(untagged)] @@ -5982,6 +6013,134 @@ impl SecurityMonitoringAPI { } } + /// Get a suppression's version history. + pub async fn get_suppression_version_history( + &self, + suppression_id: String, + params: GetSuppressionVersionHistoryOptionalParams, + ) -> Result< + crate::datadogV2::model::GetSuppressionVersionHistoryResponse, + datadog::Error, + > { + match self + .get_suppression_version_history_with_http_info(suppression_id, 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), + } + } + + /// Get a suppression's version history. + pub async fn get_suppression_version_history_with_http_info( + &self, + suppression_id: String, + params: GetSuppressionVersionHistoryOptionalParams, + ) -> Result< + datadog::ResponseContent, + datadog::Error, + > { + let local_configuration = &self.config; + let operation_id = "v2.get_suppression_version_history"; + + // unbox and build optional parameters + let page_size = params.page_size; + let page_number = params.page_number; + + let local_client = &self.client; + + let local_uri_str = format!( + "{}/api/v2/security_monitoring/configuration/suppressions/{suppression_id}/version_history", + local_configuration.get_operation_host(operation_id), suppression_id= + datadog::urlencode(suppression_id) + ); + let mut local_req_builder = + local_client.request(reqwest::Method::GET, local_uri_str.as_str()); + + 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_number { + local_req_builder = + local_req_builder.query(&[("page[number]", &local_query_param.to_string())]); + }; + + // build headers + let mut headers = HeaderMap::new(); + 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"), + ); + }; + + 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::< + crate::datadogV2::model::GetSuppressionVersionHistoryResponse, + >(&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)) + } + } + /// Get the list of suppressions that would affect a rule. pub async fn get_suppressions_affecting_future_rule( &self, diff --git a/src/datadogV2/model/mod.rs b/src/datadogV2/model/mod.rs index 19b16a61f..9af7b5584 100644 --- a/src/datadogV2/model/mod.rs +++ b/src/datadogV2/model/mod.rs @@ -5868,6 +5868,20 @@ pub mod model_security_monitoring_suppression_update_data; pub use self::model_security_monitoring_suppression_update_data::SecurityMonitoringSuppressionUpdateData; pub mod model_security_monitoring_suppression_update_attributes; pub use self::model_security_monitoring_suppression_update_attributes::SecurityMonitoringSuppressionUpdateAttributes; +pub mod model_get_suppression_version_history_response; +pub use self::model_get_suppression_version_history_response::GetSuppressionVersionHistoryResponse; +pub mod model_get_suppression_version_history_data; +pub use self::model_get_suppression_version_history_data::GetSuppressionVersionHistoryData; +pub mod model_suppression_version_history; +pub use self::model_suppression_version_history::SuppressionVersionHistory; +pub mod model_suppression_versions; +pub use self::model_suppression_versions::SuppressionVersions; +pub mod model_version_history_update; +pub use self::model_version_history_update::VersionHistoryUpdate; +pub mod model_version_history_update_type; +pub use self::model_version_history_update_type::VersionHistoryUpdateType; +pub mod model_get_suppression_version_history_data_type; +pub use self::model_get_suppression_version_history_data_type::GetSuppressionVersionHistoryDataType; pub mod model_security_monitoring_list_rules_response; pub use self::model_security_monitoring_list_rules_response::SecurityMonitoringListRulesResponse; pub mod model_security_monitoring_standard_rule_response; @@ -5922,10 +5936,6 @@ pub mod model_rule_version_history; pub use self::model_rule_version_history::RuleVersionHistory; pub mod model_rule_versions; pub use self::model_rule_versions::RuleVersions; -pub mod model_rule_version_update; -pub use self::model_rule_version_update::RuleVersionUpdate; -pub mod model_rule_version_update_type; -pub use self::model_rule_version_update_type::RuleVersionUpdateType; pub mod model_get_rule_version_history_data_type; pub use self::model_get_rule_version_history_data_type::GetRuleVersionHistoryDataType; pub mod model_security_monitoring_signals_sort; diff --git a/src/datadogV2/model/model_get_suppression_version_history_data.rs b/src/datadogV2/model/model_get_suppression_version_history_data.rs new file mode 100644 index 000000000..e6b49fbb9 --- /dev/null +++ b/src/datadogV2/model/model_get_suppression_version_history_data.rs @@ -0,0 +1,153 @@ +// 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}; + +/// Data for the suppression version history. +#[non_exhaustive] +#[skip_serializing_none] +#[derive(Clone, Debug, PartialEq, Serialize)] +pub struct GetSuppressionVersionHistoryData { + /// Response object containing the version history of a suppression. + #[serde(rename = "attributes")] + pub attributes: Option, + /// ID of the suppression. + #[serde(rename = "id")] + pub id: Option, + /// Type of data. + #[serde(rename = "type")] + pub type_: Option, + #[serde(flatten)] + pub additional_properties: std::collections::BTreeMap, + #[serde(skip)] + #[serde(default)] + pub(crate) _unparsed: bool, +} + +impl GetSuppressionVersionHistoryData { + pub fn new() -> GetSuppressionVersionHistoryData { + GetSuppressionVersionHistoryData { + attributes: None, + id: None, + type_: None, + additional_properties: std::collections::BTreeMap::new(), + _unparsed: false, + } + } + + pub fn attributes(mut self, value: crate::datadogV2::model::SuppressionVersionHistory) -> Self { + self.attributes = Some(value); + self + } + + pub fn id(mut self, value: String) -> Self { + self.id = Some(value); + self + } + + pub fn type_( + mut self, + value: crate::datadogV2::model::GetSuppressionVersionHistoryDataType, + ) -> 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 GetSuppressionVersionHistoryData { + fn default() -> Self { + Self::new() + } +} + +impl<'de> Deserialize<'de> for GetSuppressionVersionHistoryData { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct GetSuppressionVersionHistoryDataVisitor; + impl<'a> Visitor<'a> for GetSuppressionVersionHistoryDataVisitor { + type Value = GetSuppressionVersionHistoryData; + + 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< + crate::datadogV2::model::GetSuppressionVersionHistoryDataType, + > = 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" => { + if v.is_null() { + continue; + } + attributes = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "id" => { + if v.is_null() { + continue; + } + id = 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::GetSuppressionVersionHistoryDataType::UnparsedObject(_type_) => { + _unparsed = true; + }, + _ => {} + } + } + } + &_ => { + if let Ok(value) = serde_json::from_value(v.clone()) { + additional_properties.insert(k, value); + } + } + } + } + + let content = GetSuppressionVersionHistoryData { + attributes, + id, + type_, + additional_properties, + _unparsed, + }; + + Ok(content) + } + } + + deserializer.deserialize_any(GetSuppressionVersionHistoryDataVisitor) + } +} diff --git a/src/datadogV2/model/model_get_suppression_version_history_data_type.rs b/src/datadogV2/model/model_get_suppression_version_history_data_type.rs new file mode 100644 index 000000000..face836c6 --- /dev/null +++ b/src/datadogV2/model/model_get_suppression_version_history_data_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 GetSuppressionVersionHistoryDataType { + SUPPRESSIONVERSIONHISTORY, + UnparsedObject(crate::datadog::UnparsedObject), +} + +impl ToString for GetSuppressionVersionHistoryDataType { + fn to_string(&self) -> String { + match self { + Self::SUPPRESSIONVERSIONHISTORY => String::from("suppression_version_history"), + Self::UnparsedObject(v) => v.value.to_string(), + } + } +} + +impl Serialize for GetSuppressionVersionHistoryDataType { + 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 GetSuppressionVersionHistoryDataType { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let s: String = String::deserialize(deserializer)?; + Ok(match s.as_str() { + "suppression_version_history" => Self::SUPPRESSIONVERSIONHISTORY, + _ => Self::UnparsedObject(crate::datadog::UnparsedObject { + value: serde_json::Value::String(s.into()), + }), + }) + } +} diff --git a/src/datadogV2/model/model_get_suppression_version_history_response.rs b/src/datadogV2/model/model_get_suppression_version_history_response.rs new file mode 100644 index 000000000..c4321a208 --- /dev/null +++ b/src/datadogV2/model/model_get_suppression_version_history_response.rs @@ -0,0 +1,109 @@ +// 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 for getting the suppression version history. +#[non_exhaustive] +#[skip_serializing_none] +#[derive(Clone, Debug, PartialEq, Serialize)] +pub struct GetSuppressionVersionHistoryResponse { + /// Data for the suppression version history. + #[serde(rename = "data")] + pub data: Option, + #[serde(flatten)] + pub additional_properties: std::collections::BTreeMap, + #[serde(skip)] + #[serde(default)] + pub(crate) _unparsed: bool, +} + +impl GetSuppressionVersionHistoryResponse { + pub fn new() -> GetSuppressionVersionHistoryResponse { + GetSuppressionVersionHistoryResponse { + data: None, + additional_properties: std::collections::BTreeMap::new(), + _unparsed: false, + } + } + + pub fn data( + mut self, + value: crate::datadogV2::model::GetSuppressionVersionHistoryData, + ) -> Self { + self.data = Some(value); + self + } + + pub fn additional_properties( + mut self, + value: std::collections::BTreeMap, + ) -> Self { + self.additional_properties = value; + self + } +} + +impl Default for GetSuppressionVersionHistoryResponse { + fn default() -> Self { + Self::new() + } +} + +impl<'de> Deserialize<'de> for GetSuppressionVersionHistoryResponse { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct GetSuppressionVersionHistoryResponseVisitor; + impl<'a> Visitor<'a> for GetSuppressionVersionHistoryResponseVisitor { + type Value = GetSuppressionVersionHistoryResponse; + + 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" => { + if v.is_null() { + continue; + } + 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 content = GetSuppressionVersionHistoryResponse { + data, + additional_properties, + _unparsed, + }; + + Ok(content) + } + } + + deserializer.deserialize_any(GetSuppressionVersionHistoryResponseVisitor) + } +} diff --git a/src/datadogV2/model/model_rule_versions.rs b/src/datadogV2/model/model_rule_versions.rs index f19a706da..cdc4c10ac 100644 --- a/src/datadogV2/model/model_rule_versions.rs +++ b/src/datadogV2/model/model_rule_versions.rs @@ -13,7 +13,7 @@ use std::fmt::{self, Formatter}; pub struct RuleVersions { /// A list of changes. #[serde(rename = "changes")] - pub changes: Option>, + pub changes: Option>, /// Create a new rule. #[serde(rename = "rule")] pub rule: Option, @@ -34,7 +34,7 @@ impl RuleVersions { } } - pub fn changes(mut self, value: Vec) -> Self { + pub fn changes(mut self, value: Vec) -> Self { self.changes = Some(value); self } @@ -76,7 +76,7 @@ impl<'de> Deserialize<'de> for RuleVersions { where M: MapAccess<'a>, { - let mut changes: Option> = None; + let mut changes: Option> = None; let mut rule: Option = None; let mut additional_properties: std::collections::BTreeMap< diff --git a/src/datadogV2/model/model_suppression_version_history.rs b/src/datadogV2/model/model_suppression_version_history.rs new file mode 100644 index 000000000..e39e42b5f --- /dev/null +++ b/src/datadogV2/model/model_suppression_version_history.rs @@ -0,0 +1,131 @@ +// 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 object containing the version history of a suppression. +#[non_exhaustive] +#[skip_serializing_none] +#[derive(Clone, Debug, PartialEq, Serialize)] +pub struct SuppressionVersionHistory { + /// The number of suppression versions. + #[serde(rename = "count")] + pub count: Option, + /// The version history of a suppression. + #[serde(rename = "data")] + pub data: + Option>, + #[serde(flatten)] + pub additional_properties: std::collections::BTreeMap, + #[serde(skip)] + #[serde(default)] + pub(crate) _unparsed: bool, +} + +impl SuppressionVersionHistory { + pub fn new() -> SuppressionVersionHistory { + SuppressionVersionHistory { + count: None, + data: None, + additional_properties: std::collections::BTreeMap::new(), + _unparsed: false, + } + } + + pub fn count(mut self, value: i32) -> Self { + self.count = Some(value); + self + } + + pub fn data( + mut self, + value: std::collections::BTreeMap, + ) -> Self { + self.data = Some(value); + self + } + + pub fn additional_properties( + mut self, + value: std::collections::BTreeMap, + ) -> Self { + self.additional_properties = value; + self + } +} + +impl Default for SuppressionVersionHistory { + fn default() -> Self { + Self::new() + } +} + +impl<'de> Deserialize<'de> for SuppressionVersionHistory { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct SuppressionVersionHistoryVisitor; + impl<'a> Visitor<'a> for SuppressionVersionHistoryVisitor { + type Value = SuppressionVersionHistory; + + 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 count: Option = None; + let mut data: Option< + std::collections::BTreeMap< + String, + crate::datadogV2::model::SuppressionVersions, + >, + > = 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() { + "count" => { + if v.is_null() { + continue; + } + count = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "data" => { + if v.is_null() { + continue; + } + 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 content = SuppressionVersionHistory { + count, + data, + additional_properties, + _unparsed, + }; + + Ok(content) + } + } + + deserializer.deserialize_any(SuppressionVersionHistoryVisitor) + } +} diff --git a/src/datadogV2/model/model_suppression_versions.rs b/src/datadogV2/model/model_suppression_versions.rs new file mode 100644 index 000000000..70a7def7b --- /dev/null +++ b/src/datadogV2/model/model_suppression_versions.rs @@ -0,0 +1,128 @@ +// 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 suppression version with a list of updates. +#[non_exhaustive] +#[skip_serializing_none] +#[derive(Clone, Debug, PartialEq, Serialize)] +pub struct SuppressionVersions { + /// A list of changes. + #[serde(rename = "changes")] + pub changes: Option>, + /// The attributes of the suppression rule. + #[serde(rename = "suppression")] + pub suppression: Option, + #[serde(flatten)] + pub additional_properties: std::collections::BTreeMap, + #[serde(skip)] + #[serde(default)] + pub(crate) _unparsed: bool, +} + +impl SuppressionVersions { + pub fn new() -> SuppressionVersions { + SuppressionVersions { + changes: None, + suppression: None, + additional_properties: std::collections::BTreeMap::new(), + _unparsed: false, + } + } + + pub fn changes(mut self, value: Vec) -> Self { + self.changes = Some(value); + self + } + + pub fn suppression( + mut self, + value: crate::datadogV2::model::SecurityMonitoringSuppressionAttributes, + ) -> Self { + self.suppression = Some(value); + self + } + + pub fn additional_properties( + mut self, + value: std::collections::BTreeMap, + ) -> Self { + self.additional_properties = value; + self + } +} + +impl Default for SuppressionVersions { + fn default() -> Self { + Self::new() + } +} + +impl<'de> Deserialize<'de> for SuppressionVersions { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct SuppressionVersionsVisitor; + impl<'a> Visitor<'a> for SuppressionVersionsVisitor { + type Value = SuppressionVersions; + + 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 changes: Option> = None; + let mut suppression: Option< + crate::datadogV2::model::SecurityMonitoringSuppressionAttributes, + > = 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() { + "changes" => { + if v.is_null() { + continue; + } + changes = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "suppression" => { + if v.is_null() { + continue; + } + suppression = + 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 = SuppressionVersions { + changes, + suppression, + additional_properties, + _unparsed, + }; + + Ok(content) + } + } + + deserializer.deserialize_any(SuppressionVersionsVisitor) + } +} diff --git a/src/datadogV2/model/model_rule_version_update.rs b/src/datadogV2/model/model_version_history_update.rs similarity index 82% rename from src/datadogV2/model/model_rule_version_update.rs rename to src/datadogV2/model/model_version_history_update.rs index fe9e88e07..637f0e981 100644 --- a/src/datadogV2/model/model_rule_version_update.rs +++ b/src/datadogV2/model/model_version_history_update.rs @@ -10,7 +10,7 @@ use std::fmt::{self, Formatter}; #[non_exhaustive] #[skip_serializing_none] #[derive(Clone, Debug, PartialEq, Serialize)] -pub struct RuleVersionUpdate { +pub struct VersionHistoryUpdate { /// The new value of the field. #[serde(rename = "change")] pub change: Option, @@ -19,7 +19,7 @@ pub struct RuleVersionUpdate { pub field: Option, /// The type of change. #[serde(rename = "type")] - pub type_: Option, + pub type_: Option, #[serde(flatten)] pub additional_properties: std::collections::BTreeMap, #[serde(skip)] @@ -27,9 +27,9 @@ pub struct RuleVersionUpdate { pub(crate) _unparsed: bool, } -impl RuleVersionUpdate { - pub fn new() -> RuleVersionUpdate { - RuleVersionUpdate { +impl VersionHistoryUpdate { + pub fn new() -> VersionHistoryUpdate { + VersionHistoryUpdate { change: None, field: None, type_: None, @@ -48,7 +48,7 @@ impl RuleVersionUpdate { self } - pub fn type_(mut self, value: crate::datadogV2::model::RuleVersionUpdateType) -> Self { + pub fn type_(mut self, value: crate::datadogV2::model::VersionHistoryUpdateType) -> Self { self.type_ = Some(value); self } @@ -62,20 +62,20 @@ impl RuleVersionUpdate { } } -impl Default for RuleVersionUpdate { +impl Default for VersionHistoryUpdate { fn default() -> Self { Self::new() } } -impl<'de> Deserialize<'de> for RuleVersionUpdate { +impl<'de> Deserialize<'de> for VersionHistoryUpdate { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { - struct RuleVersionUpdateVisitor; - impl<'a> Visitor<'a> for RuleVersionUpdateVisitor { - type Value = RuleVersionUpdate; + struct VersionHistoryUpdateVisitor; + impl<'a> Visitor<'a> for VersionHistoryUpdateVisitor { + type Value = VersionHistoryUpdate; fn expecting(&self, f: &mut Formatter<'_>) -> fmt::Result { f.write_str("a mapping") @@ -87,7 +87,7 @@ impl<'de> Deserialize<'de> for RuleVersionUpdate { { let mut change: Option = None; let mut field: Option = None; - let mut type_: Option = None; + let mut type_: Option = None; let mut additional_properties: std::collections::BTreeMap< String, serde_json::Value, @@ -115,7 +115,7 @@ impl<'de> Deserialize<'de> for RuleVersionUpdate { type_ = Some(serde_json::from_value(v).map_err(M::Error::custom)?); if let Some(ref _type_) = type_ { match _type_ { - crate::datadogV2::model::RuleVersionUpdateType::UnparsedObject(_type_) => { + crate::datadogV2::model::VersionHistoryUpdateType::UnparsedObject(_type_) => { _unparsed = true; }, _ => {} @@ -130,7 +130,7 @@ impl<'de> Deserialize<'de> for RuleVersionUpdate { } } - let content = RuleVersionUpdate { + let content = VersionHistoryUpdate { change, field, type_, @@ -142,6 +142,6 @@ impl<'de> Deserialize<'de> for RuleVersionUpdate { } } - deserializer.deserialize_any(RuleVersionUpdateVisitor) + deserializer.deserialize_any(VersionHistoryUpdateVisitor) } } diff --git a/src/datadogV2/model/model_rule_version_update_type.rs b/src/datadogV2/model/model_version_history_update_type.rs similarity index 89% rename from src/datadogV2/model/model_rule_version_update_type.rs rename to src/datadogV2/model/model_version_history_update_type.rs index 5ef6817db..ce2c6c03f 100644 --- a/src/datadogV2/model/model_rule_version_update_type.rs +++ b/src/datadogV2/model/model_version_history_update_type.rs @@ -6,14 +6,14 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer}; #[non_exhaustive] #[derive(Clone, Debug, Eq, PartialEq)] -pub enum RuleVersionUpdateType { +pub enum VersionHistoryUpdateType { CREATE, UPDATE, DELETE, UnparsedObject(crate::datadog::UnparsedObject), } -impl ToString for RuleVersionUpdateType { +impl ToString for VersionHistoryUpdateType { fn to_string(&self) -> String { match self { Self::CREATE => String::from("create"), @@ -24,7 +24,7 @@ impl ToString for RuleVersionUpdateType { } } -impl Serialize for RuleVersionUpdateType { +impl Serialize for VersionHistoryUpdateType { fn serialize(&self, serializer: S) -> Result where S: Serializer, @@ -36,7 +36,7 @@ impl Serialize for RuleVersionUpdateType { } } -impl<'de> Deserialize<'de> for RuleVersionUpdateType { +impl<'de> Deserialize<'de> for VersionHistoryUpdateType { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, diff --git a/tests/scenarios/cassettes/v2/security_monitoring/Get-a-suppression-s-version-history-returns-Not-Found-response.frozen b/tests/scenarios/cassettes/v2/security_monitoring/Get-a-suppression-s-version-history-returns-Not-Found-response.frozen new file mode 100644 index 000000000..24bfeae8d --- /dev/null +++ b/tests/scenarios/cassettes/v2/security_monitoring/Get-a-suppression-s-version-history-returns-Not-Found-response.frozen @@ -0,0 +1 @@ +2025-11-26T13:33:06.081Z \ No newline at end of file diff --git a/tests/scenarios/cassettes/v2/security_monitoring/Get-a-suppression-s-version-history-returns-Not-Found-response.json b/tests/scenarios/cassettes/v2/security_monitoring/Get-a-suppression-s-version-history-returns-Not-Found-response.json new file mode 100644 index 000000000..a1d09fc95 --- /dev/null +++ b/tests/scenarios/cassettes/v2/security_monitoring/Get-a-suppression-s-version-history-returns-Not-Found-response.json @@ -0,0 +1,33 @@ +{ + "http_interactions": [ + { + "request": { + "body": "", + "headers": { + "Accept": [ + "application/json" + ] + }, + "method": "get", + "uri": "https://api.datadoghq.com/api/v2/security_monitoring/configuration/suppressions/this-does-not-exist/version_history" + }, + "response": { + "body": { + "string": "{\"errors\":[\"not_found(Suppression with ID this-does-not-exist not found)\"]}", + "encoding": null + }, + "headers": { + "Content-Type": [ + "application/json" + ] + }, + "status": { + "code": 404, + "message": "Not Found" + } + }, + "recorded_at": "Wed, 26 Nov 2025 13:33:06 GMT" + } + ], + "recorded_with": "VCR 6.0.0" +} \ No newline at end of file diff --git a/tests/scenarios/cassettes/v2/security_monitoring/Get-a-suppression-s-version-history-returns-OK-response.frozen b/tests/scenarios/cassettes/v2/security_monitoring/Get-a-suppression-s-version-history-returns-OK-response.frozen new file mode 100644 index 000000000..d0d5ccec3 --- /dev/null +++ b/tests/scenarios/cassettes/v2/security_monitoring/Get-a-suppression-s-version-history-returns-OK-response.frozen @@ -0,0 +1 @@ +2025-11-26T13:33:06.482Z \ No newline at end of file diff --git a/tests/scenarios/cassettes/v2/security_monitoring/Get-a-suppression-s-version-history-returns-OK-response.json b/tests/scenarios/cassettes/v2/security_monitoring/Get-a-suppression-s-version-history-returns-OK-response.json new file mode 100644 index 000000000..11848288d --- /dev/null +++ b/tests/scenarios/cassettes/v2/security_monitoring/Get-a-suppression-s-version-history-returns-OK-response.json @@ -0,0 +1,91 @@ +{ + "http_interactions": [ + { + "request": { + "body": { + "string": "{\"data\":{\"attributes\":{\"description\":\"Test-Get_a_suppression_s_version_history_returns_OK_response-1764163986\",\"enabled\":true,\"name\":\"Test-Get_a_suppression_s_version_history_returns_OK_response-1764163986\",\"rule_query\":\"source:cloudtrail\",\"suppression_query\":\"env:test\",\"tags\":[\"technique:T1110-brute-force\",\"source:cloudtrail\"]},\"type\":\"suppressions\"}}", + "encoding": null + }, + "headers": { + "Accept": [ + "application/json" + ], + "Content-Type": [ + "application/json" + ] + }, + "method": "post", + "uri": "https://api.datadoghq.com/api/v2/security_monitoring/configuration/suppressions" + }, + "response": { + "body": { + "string": "{\"data\":{\"id\":\"456-piv-74h\",\"type\":\"suppressions\",\"attributes\":{\"creation_date\":1764163986851,\"creator\":{\"handle\":\"frog@datadoghq.com\",\"name\":\"frog\"},\"data_exclusion_query\":\"\",\"description\":\"Test-Get_a_suppression_s_version_history_returns_OK_response-1764163986\",\"editable\":true,\"enabled\":true,\"name\":\"Test-Get_a_suppression_s_version_history_returns_OK_response-1764163986\",\"rule_query\":\"source:cloudtrail\",\"suppression_query\":\"env:test\",\"tags\":[\"source:cloudtrail\",\"technique:T1110-brute-force\"],\"update_date\":1764163986851,\"updater\":{\"handle\":\"frog@datadoghq.com\",\"name\":\"frog\"},\"version\":1}}}", + "encoding": null + }, + "headers": { + "Content-Type": [ + "application/vnd.api+json" + ] + }, + "status": { + "code": 200, + "message": "OK" + } + }, + "recorded_at": "Wed, 26 Nov 2025 13:33:06 GMT" + }, + { + "request": { + "body": "", + "headers": { + "Accept": [ + "application/json" + ] + }, + "method": "get", + "uri": "https://api.datadoghq.com/api/v2/security_monitoring/configuration/suppressions/456-piv-74h/version_history" + }, + "response": { + "body": { + "string": "{\"data\":{\"id\":\"456-piv-74h\",\"type\":\"suppression_version_history\",\"attributes\":{\"count\":1,\"data\":{\"1\":{\"suppression\":{\"id\":\"456-piv-74h\",\"name\":\"Test-Get_a_suppression_s_version_history_returns_OK_response-1764163986\",\"enabled\":true,\"description\":\"Test-Get_a_suppression_s_version_history_returns_OK_response-1764163986\",\"rule_query\":\"source:cloudtrail\",\"suppression_query\":\"env:test\",\"data_exclusion_query\":\"\",\"version\":1,\"creator\":{\"handle\":\"frog@datadoghq.com\",\"name\":\"frog\"},\"updater\":{\"handle\":\"frog@datadoghq.com\",\"name\":\"frog\"},\"creation_date\":1764163986851,\"update_date\":1764163986851,\"editable\":true,\"tags\":[\"source:cloudtrail\",\"technique:T1110-brute-force\"]},\"changes\":[]}}}}}", + "encoding": null + }, + "headers": { + "Content-Type": [ + "application/vnd.api+json" + ] + }, + "status": { + "code": 200, + "message": "OK" + } + }, + "recorded_at": "Wed, 26 Nov 2025 13:33:06 GMT" + }, + { + "request": { + "body": "", + "headers": { + "Accept": [ + "*/*" + ] + }, + "method": "delete", + "uri": "https://api.datadoghq.com/api/v2/security_monitoring/configuration/suppressions/456-piv-74h" + }, + "response": { + "body": { + "string": "", + "encoding": null + }, + "headers": {}, + "status": { + "code": 204, + "message": "No Content" + } + }, + "recorded_at": "Wed, 26 Nov 2025 13:33:06 GMT" + } + ], + "recorded_with": "VCR 6.0.0" +} \ No newline at end of file diff --git a/tests/scenarios/features/v2/security_monitoring.feature b/tests/scenarios/features/v2/security_monitoring.feature index 531c84c19..578e8d28e 100644 --- a/tests/scenarios/features/v2/security_monitoring.feature +++ b/tests/scenarios/features/v2/security_monitoring.feature @@ -833,6 +833,21 @@ Feature: Security Monitoring And the response "data.attributes.rule_query" has the same value as "suppression.data.attributes.rule_query" And the response "data.attributes.suppression_query" is equal to "env:test" + @team:DataDog/k9-cloud-security-platform + Scenario: Get a suppression's version history returns "Not Found" response + Given new "GetSuppressionVersionHistory" request + And request contains "suppression_id" parameter with value "this-does-not-exist" + When the request is sent + Then the response status is 404 Not Found + + @team:DataDog/k9-cloud-security-platform + Scenario: Get a suppression's version history returns "OK" response + Given new "GetSuppressionVersionHistory" request + And there is a valid "suppression" in the system + And request contains "suppression_id" parameter from "suppression.data.id" + When the request is sent + Then the response status is 200 OK + @team:DataDog/k9-cloud-security-platform Scenario: Get all security filters returns "OK" response Given new "ListSecurityFilters" request diff --git a/tests/scenarios/features/v2/undo.json b/tests/scenarios/features/v2/undo.json index 3799e64e6..67b48870c 100644 --- a/tests/scenarios/features/v2/undo.json +++ b/tests/scenarios/features/v2/undo.json @@ -3830,6 +3830,12 @@ "type": "idempotent" } }, + "GetSuppressionVersionHistory": { + "tag": "Security Monitoring", + "undo": { + "type": "safe" + } + }, "ListSecurityMonitoringRules": { "tag": "Security Monitoring", "undo": { diff --git a/tests/scenarios/function_mappings.rs b/tests/scenarios/function_mappings.rs index 4f9cae93a..f333e4378 100644 --- a/tests/scenarios/function_mappings.rs +++ b/tests/scenarios/function_mappings.rs @@ -2491,6 +2491,10 @@ pub fn collect_function_calls(world: &mut DatadogWorld) { "v2.UpdateSecurityMonitoringSuppression".into(), test_v2_update_security_monitoring_suppression, ); + world.function_mappings.insert( + "v2.GetSuppressionVersionHistory".into(), + test_v2_get_suppression_version_history, + ); world.function_mappings.insert( "v2.ListSecurityMonitoringRules".into(), test_v2_list_security_monitoring_rules, @@ -17761,6 +17765,47 @@ fn test_v2_update_security_monitoring_suppression( world.response.code = response.status.as_u16(); } +fn test_v2_get_suppression_version_history( + world: &mut DatadogWorld, + _parameters: &HashMap, +) { + let api = world + .api_instances + .v2_api_security_monitoring + .as_ref() + .expect("api instance not found"); + let suppression_id = + serde_json::from_value(_parameters.get("suppression_id").unwrap().clone()).unwrap(); + let page_size = _parameters + .get("page[size]") + .and_then(|param| Some(serde_json::from_value(param.clone()).unwrap())); + let page_number = _parameters + .get("page[number]") + .and_then(|param| Some(serde_json::from_value(param.clone()).unwrap())); + let mut params = + datadogV2::api_security_monitoring::GetSuppressionVersionHistoryOptionalParams::default(); + params.page_size = page_size; + params.page_number = page_number; + let response = match block_on( + api.get_suppression_version_history_with_http_info(suppression_id, 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_list_security_monitoring_rules( world: &mut DatadogWorld, _parameters: &HashMap,