diff --git a/.generator/schemas/v2/openapi.yaml b/.generator/schemas/v2/openapi.yaml index 74b9de4010..6e1ef6826d 100644 --- a/.generator/schemas/v2/openapi.yaml +++ b/.generator/schemas/v2/openapi.yaml @@ -12570,6 +12570,20 @@ components: BudgetAttributes: description: The attributes of a budget. properties: + costs: + $ref: "#/components/schemas/BudgetAttributesCosts" + description: Aggregated cost data for the budget. Present only when `actual=true` or `forecast=true` is requested. + costs_period_end: + description: The end of the period used to compute cost data, in milliseconds since epoch. + format: int64 + type: integer + costs_period_start: + description: The start of the period used to compute cost data, in milliseconds since epoch. + format: int64 + type: integer + costs_unit: + $ref: "#/components/schemas/BudgetAttributesCostsUnit" + description: The unit used for all cost values in the response. created_at: description: The timestamp when the budget was created. example: 1738258683590 @@ -12622,6 +12636,53 @@ components: example: 00000000-0a0a-0a0a-aaa0-00000000000a type: string type: object + BudgetAttributesCosts: + description: Aggregated cost data for the budget over the requested period. + properties: + actual: + description: The total actual cost. Present only when `actual=true` is requested. + format: double + nullable: true + type: number + amount: + description: The total budgeted amount over the requested period. + format: double + nullable: true + type: number + forecast: + description: The total forecast cost, with any custom forecast overrides applied. Present only when `forecast=true` is requested. + format: double + nullable: true + type: number + ootb_forecast: + description: The out-of-the-box ML forecast before custom overrides. Present only when `forecast=true` is requested. + format: double + nullable: true + type: number + type: object + BudgetAttributesCostsUnit: + description: The unit used for all cost values in the response. + properties: + family: + description: The unit family (for example, `currency`). + type: string + id: + description: The unique identifier for the unit. + type: string + name: + description: The full name of the unit. + type: string + plural: + description: The plural form of the unit name. + type: string + scale_factor: + description: The scale factor applied to raw cost values. + format: double + type: number + short_name: + description: The abbreviated unit name. + type: string + type: object BudgetValidationRequest: description: The request object for validating a budget configuration before creating or updating it. example: @@ -12787,6 +12848,9 @@ components: description: The budgeted amount for this entry. format: double type: number + costs: + $ref: "#/components/schemas/BudgetWithEntriesDataAttributesEntriesItemsCosts" + description: Cost data for this entry. Present only when `actual=true` or `forecast=true` is requested. month: description: The month this budget entry applies to, in YYYYMM format. format: int64 @@ -12797,6 +12861,36 @@ components: $ref: "#/components/schemas/BudgetWithEntriesDataAttributesEntriesItemsTagFiltersItems" type: array type: object + BudgetWithEntriesDataAttributesEntriesItemsCosts: + description: Cost data for a single budget entry. + properties: + actual: + description: The actual cost for this entry. Present only when `actual=true` is requested. + format: double + nullable: true + type: number + amount: + description: The budgeted amount for this entry. + format: double + nullable: true + type: number + custom_forecast: + description: |- + The custom forecast override for this entry. `null` when `forecast=true` is requested but no custom forecast has been set for this entry's month. A numeric value, including `0`, indicates an explicit custom forecast override. Omitted when `forecast=false` or the feature is not available for the organization. + format: double + nullable: true + type: number + forecast: + description: The final forecast for this entry, with any custom forecast override applied. Present only when `forecast=true` is requested. + format: double + nullable: true + type: number + ootb_forecast: + description: The out-of-the-box ML forecast for this entry, before custom overrides. Present only when `forecast=true` is requested. + format: double + nullable: true + type: number + type: object BudgetWithEntriesDataAttributesEntriesItemsTagFiltersItems: description: A tag filter used to scope a budget entry to specific resource tags. properties: @@ -116220,10 +116314,36 @@ paths: tags: - Cloud Cost Management get: - description: Get a budget + description: Get a budget by ID. Pass `actual=true` or `forecast=true` to include cost data in the response. Use `start` and `end` (millisecond epochs, both required) to set the cost window. When `forecast=true`, each entry also includes `ootb_forecast` (the ML forecast before overrides) and `custom_forecast` (`null` if no override is set, a number if one is). operationId: GetBudget parameters: - $ref: "#/components/parameters/BudgetID" + - description: When `true`, includes actual cost data in the response. + in: query + name: actual + required: false + schema: + type: boolean + - description: When `true`, includes forecast cost data in the response, including `ootb_forecast` and `custom_forecast` per entry. + in: query + name: forecast + required: false + schema: + type: boolean + - description: Start of the cost window in milliseconds since epoch. Must be used together with `end`. + in: query + name: start + required: false + schema: + format: int64 + type: integer + - description: End of the cost window in milliseconds since epoch. Must be used together with `start`. + in: query + name: end + required: false + schema: + format: int64 + type: integer responses: "200": content: @@ -116233,11 +116353,31 @@ paths: value: data: attributes: + costs: + actual: 850.25 + amount: 1000.0 + forecast: 1100.5 + ootb_forecast: 1100.5 + costs_period_end: 1740873600000 + costs_period_start: 1738281600000 + costs_unit: + family: currency + id: "1" + name: dollar + plural: dollars + scale_factor: 1.0 + short_name: $ created_at: 1738258683590 created_by: 00000000-0a0a-0a0a-aaa0-00000000000a end_month: 202502 entries: - amount: 500 + costs: + actual: 425.5 + amount: 500.0 + custom_forecast: + forecast: 550.25 + ootb_forecast: 550.25 month: 202501 tag_filters: - tag_key: service @@ -116254,6 +116394,10 @@ paths: schema: $ref: "#/components/schemas/BudgetWithEntries" description: OK + "400": + $ref: "#/components/responses/BadRequestResponse" + "404": + $ref: "#/components/responses/NotFoundResponse" "429": $ref: "#/components/responses/TooManyRequestsResponse" security: diff --git a/examples/v2_cloud-cost-management_GetBudget.rs b/examples/v2_cloud-cost-management_GetBudget.rs index df3565621c..717cd1acff 100644 --- a/examples/v2_cloud-cost-management_GetBudget.rs +++ b/examples/v2_cloud-cost-management_GetBudget.rs @@ -1,12 +1,15 @@ // Get budget returns "OK" response use datadog_api_client::datadog; use datadog_api_client::datadogV2::api_cloud_cost_management::CloudCostManagementAPI; +use datadog_api_client::datadogV2::api_cloud_cost_management::GetBudgetOptionalParams; #[tokio::main] async fn main() { let configuration = datadog::Configuration::new(); let api = CloudCostManagementAPI::with_config(configuration); - let resp = api.get_budget("budget_id".to_string()).await; + let resp = api + .get_budget("budget_id".to_string(), GetBudgetOptionalParams::default()) + .await; if let Ok(value) = resp { println!("{:#?}", value); } else { diff --git a/examples/v2_cloud-cost-management_UpsertBudget.rs b/examples/v2_cloud-cost-management_UpsertBudget.rs index ba84bd3931..e354b5453f 100644 --- a/examples/v2_cloud-cost-management_UpsertBudget.rs +++ b/examples/v2_cloud-cost-management_UpsertBudget.rs @@ -2,9 +2,12 @@ use datadog_api_client::datadog; use datadog_api_client::datadogV2::api_cloud_cost_management::CloudCostManagementAPI; use datadog_api_client::datadogV2::model::BudgetAttributes; +use datadog_api_client::datadogV2::model::BudgetAttributesCosts; +use datadog_api_client::datadogV2::model::BudgetAttributesCostsUnit; use datadog_api_client::datadogV2::model::BudgetWithEntries; use datadog_api_client::datadogV2::model::BudgetWithEntriesData; use datadog_api_client::datadogV2::model::BudgetWithEntriesDataAttributesEntriesItems; +use datadog_api_client::datadogV2::model::BudgetWithEntriesDataAttributesEntriesItemsCosts; use datadog_api_client::datadogV2::model::BudgetWithEntriesDataAttributesEntriesItemsTagFiltersItems; #[tokio::main] @@ -13,10 +16,26 @@ async fn main() { BudgetWithEntriesData::new() .attributes( BudgetAttributes::new() + .costs( + BudgetAttributesCosts::new() + .actual(None) + .amount(None) + .forecast(None) + .ootb_forecast(None), + ) + .costs_unit(BudgetAttributesCostsUnit::new()) .created_at(1738258683590) .created_by("00000000-0a0a-0a0a-aaa0-00000000000a".to_string()) .end_month(202502) .entries(vec![BudgetWithEntriesDataAttributesEntriesItems::new() + .costs( + BudgetWithEntriesDataAttributesEntriesItemsCosts::new() + .actual(None) + .amount(None) + .custom_forecast(None) + .forecast(None) + .ootb_forecast(None), + ) .tag_filters(vec![ BudgetWithEntriesDataAttributesEntriesItemsTagFiltersItems::new(), ])]) diff --git a/src/datadogV2/api/api_cloud_cost_management.rs b/src/datadogV2/api/api_cloud_cost_management.rs index 19077731bc..3f65d647dd 100644 --- a/src/datadogV2/api/api_cloud_cost_management.rs +++ b/src/datadogV2/api/api_cloud_cost_management.rs @@ -27,6 +27,43 @@ impl DeleteCostTagDescriptionByKeyOptionalParams { } } +/// GetBudgetOptionalParams is a struct for passing parameters to the method [`CloudCostManagementAPI::get_budget`] +#[non_exhaustive] +#[derive(Clone, Default, Debug)] +pub struct GetBudgetOptionalParams { + /// When `true`, includes actual cost data in the response. + pub actual: Option, + /// When `true`, includes forecast cost data in the response, including `ootb_forecast` and `custom_forecast` per entry. + pub forecast: Option, + /// Start of the cost window in milliseconds since epoch. Must be used together with `end`. + pub start: Option, + /// End of the cost window in milliseconds since epoch. Must be used together with `start`. + pub end: Option, +} + +impl GetBudgetOptionalParams { + /// When `true`, includes actual cost data in the response. + pub fn actual(mut self, value: bool) -> Self { + self.actual = Some(value); + self + } + /// When `true`, includes forecast cost data in the response, including `ootb_forecast` and `custom_forecast` per entry. + pub fn forecast(mut self, value: bool) -> Self { + self.forecast = Some(value); + self + } + /// Start of the cost window in milliseconds since epoch. Must be used together with `end`. + pub fn start(mut self, value: i64) -> Self { + self.start = Some(value); + self + } + /// End of the cost window in milliseconds since epoch. Must be used together with `start`. + pub fn end(mut self, value: i64) -> Self { + self.end = Some(value); + self + } +} + /// GetCommitmentsCommitmentListOptionalParams is a struct for passing parameters to the method [`CloudCostManagementAPI::get_commitments_commitment_list`] #[non_exhaustive] #[derive(Clone, Default, Debug)] @@ -2827,12 +2864,13 @@ impl CloudCostManagementAPI { } } - /// Get a budget + /// Get a budget by ID. Pass `actual=true` or `forecast=true` to include cost data in the response. Use `start` and `end` (millisecond epochs, both required) to set the cost window. When `forecast=true`, each entry also includes `ootb_forecast` (the ML forecast before overrides) and `custom_forecast` (`null` if no override is set, a number if one is). pub async fn get_budget( &self, budget_id: String, + params: GetBudgetOptionalParams, ) -> Result> { - match self.get_budget_with_http_info(budget_id).await { + match self.get_budget_with_http_info(budget_id, params).await { Ok(response_content) => { if let Some(e) = response_content.entity { Ok(e) @@ -2846,10 +2884,11 @@ impl CloudCostManagementAPI { } } - /// Get a budget + /// Get a budget by ID. Pass `actual=true` or `forecast=true` to include cost data in the response. Use `start` and `end` (millisecond epochs, both required) to set the cost window. When `forecast=true`, each entry also includes `ootb_forecast` (the ML forecast before overrides) and `custom_forecast` (`null` if no override is set, a number if one is). pub async fn get_budget_with_http_info( &self, budget_id: String, + params: GetBudgetOptionalParams, ) -> Result< datadog::ResponseContent, datadog::Error, @@ -2857,6 +2896,12 @@ impl CloudCostManagementAPI { let local_configuration = &self.config; let operation_id = "v2.get_budget"; + // unbox and build optional parameters + let actual = params.actual; + let forecast = params.forecast; + let start = params.start; + let end = params.end; + let local_client = &self.client; let local_uri_str = format!( @@ -2867,6 +2912,22 @@ impl CloudCostManagementAPI { let mut local_req_builder = local_client.request(reqwest::Method::GET, local_uri_str.as_str()); + if let Some(ref local_query_param) = actual { + local_req_builder = + local_req_builder.query(&[("actual", &local_query_param.to_string())]); + }; + if let Some(ref local_query_param) = forecast { + local_req_builder = + local_req_builder.query(&[("forecast", &local_query_param.to_string())]); + }; + if let Some(ref local_query_param) = start { + local_req_builder = + local_req_builder.query(&[("start", &local_query_param.to_string())]); + }; + if let Some(ref local_query_param) = end { + local_req_builder = local_req_builder.query(&[("end", &local_query_param.to_string())]); + }; + // build headers let mut headers = HeaderMap::new(); headers.insert("Accept", HeaderValue::from_static("application/json")); diff --git a/src/datadogV2/model/mod.rs b/src/datadogV2/model/mod.rs index 29b0b9dcf2..da885640d1 100644 --- a/src/datadogV2/model/mod.rs +++ b/src/datadogV2/model/mod.rs @@ -2812,8 +2812,14 @@ pub mod model_budget_with_entries_data; pub use self::model_budget_with_entries_data::BudgetWithEntriesData; pub mod model_budget_attributes; pub use self::model_budget_attributes::BudgetAttributes; +pub mod model_budget_attributes_costs; +pub use self::model_budget_attributes_costs::BudgetAttributesCosts; +pub mod model_budget_attributes_costs_unit; +pub use self::model_budget_attributes_costs_unit::BudgetAttributesCostsUnit; pub mod model_budget_with_entries_data_attributes_entries_items; pub use self::model_budget_with_entries_data_attributes_entries_items::BudgetWithEntriesDataAttributesEntriesItems; +pub mod model_budget_with_entries_data_attributes_entries_items_costs; +pub use self::model_budget_with_entries_data_attributes_entries_items_costs::BudgetWithEntriesDataAttributesEntriesItemsCosts; pub mod model_budget_with_entries_data_attributes_entries_items_tag_filters_items; pub use self::model_budget_with_entries_data_attributes_entries_items_tag_filters_items::BudgetWithEntriesDataAttributesEntriesItemsTagFiltersItems; pub mod model_validation_response; diff --git a/src/datadogV2/model/model_budget_attributes.rs b/src/datadogV2/model/model_budget_attributes.rs index 11d806a408..7c5825d2b9 100644 --- a/src/datadogV2/model/model_budget_attributes.rs +++ b/src/datadogV2/model/model_budget_attributes.rs @@ -11,6 +11,18 @@ use std::fmt::{self, Formatter}; #[skip_serializing_none] #[derive(Clone, Debug, PartialEq, Serialize)] pub struct BudgetAttributes { + /// Aggregated cost data for the budget over the requested period. + #[serde(rename = "costs")] + pub costs: Option, + /// The end of the period used to compute cost data, in milliseconds since epoch. + #[serde(rename = "costs_period_end")] + pub costs_period_end: Option, + /// The start of the period used to compute cost data, in milliseconds since epoch. + #[serde(rename = "costs_period_start")] + pub costs_period_start: Option, + /// The unit used for all cost values in the response. + #[serde(rename = "costs_unit")] + pub costs_unit: Option, /// The timestamp when the budget was created. #[serde(rename = "created_at")] pub created_at: Option, @@ -54,6 +66,10 @@ pub struct BudgetAttributes { impl BudgetAttributes { pub fn new() -> BudgetAttributes { BudgetAttributes { + costs: None, + costs_period_end: None, + costs_period_start: None, + costs_unit: None, created_at: None, created_by: None, end_month: None, @@ -70,6 +86,26 @@ impl BudgetAttributes { } } + pub fn costs(mut self, value: crate::datadogV2::model::BudgetAttributesCosts) -> Self { + self.costs = Some(value); + self + } + + pub fn costs_period_end(mut self, value: i64) -> Self { + self.costs_period_end = Some(value); + self + } + + pub fn costs_period_start(mut self, value: i64) -> Self { + self.costs_period_start = Some(value); + self + } + + pub fn costs_unit(mut self, value: crate::datadogV2::model::BudgetAttributesCostsUnit) -> Self { + self.costs_unit = Some(value); + self + } + pub fn created_at(mut self, value: i64) -> Self { self.created_at = Some(value); self @@ -160,6 +196,11 @@ impl<'de> Deserialize<'de> for BudgetAttributes { where M: MapAccess<'a>, { + let mut costs: Option = None; + let mut costs_period_end: Option = None; + let mut costs_period_start: Option = None; + let mut costs_unit: Option = + None; let mut created_at: Option = None; let mut created_by: Option = None; let mut end_month: Option = None; @@ -181,6 +222,32 @@ impl<'de> Deserialize<'de> for BudgetAttributes { while let Some((k, v)) = map.next_entry::()? { match k.as_str() { + "costs" => { + if v.is_null() { + continue; + } + costs = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "costs_period_end" => { + if v.is_null() { + continue; + } + costs_period_end = + Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "costs_period_start" => { + if v.is_null() { + continue; + } + costs_period_start = + Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "costs_unit" => { + if v.is_null() { + continue; + } + costs_unit = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } "created_at" => { if v.is_null() { continue; @@ -259,6 +326,10 @@ impl<'de> Deserialize<'de> for BudgetAttributes { } let content = BudgetAttributes { + costs, + costs_period_end, + costs_period_start, + costs_unit, created_at, created_by, end_month, diff --git a/src/datadogV2/model/model_budget_attributes_costs.rs b/src/datadogV2/model/model_budget_attributes_costs.rs new file mode 100644 index 0000000000..469746445f --- /dev/null +++ b/src/datadogV2/model/model_budget_attributes_costs.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}; + +/// Aggregated cost data for the budget over the requested period. +#[non_exhaustive] +#[skip_serializing_none] +#[derive(Clone, Debug, PartialEq, Serialize)] +pub struct BudgetAttributesCosts { + /// The total actual cost. Present only when `actual=true` is requested. + #[serde(rename = "actual", default, with = "::serde_with::rust::double_option")] + pub actual: Option>, + /// The total budgeted amount over the requested period. + #[serde(rename = "amount", default, with = "::serde_with::rust::double_option")] + pub amount: Option>, + /// The total forecast cost, with any custom forecast overrides applied. Present only when `forecast=true` is requested. + #[serde( + rename = "forecast", + default, + with = "::serde_with::rust::double_option" + )] + pub forecast: Option>, + /// The out-of-the-box ML forecast before custom overrides. Present only when `forecast=true` is requested. + #[serde( + rename = "ootb_forecast", + default, + with = "::serde_with::rust::double_option" + )] + pub ootb_forecast: Option>, + #[serde(flatten)] + pub additional_properties: std::collections::BTreeMap, + #[serde(skip)] + #[serde(default)] + pub(crate) _unparsed: bool, +} + +impl BudgetAttributesCosts { + pub fn new() -> BudgetAttributesCosts { + BudgetAttributesCosts { + actual: None, + amount: None, + forecast: None, + ootb_forecast: None, + additional_properties: std::collections::BTreeMap::new(), + _unparsed: false, + } + } + + pub fn actual(mut self, value: Option) -> Self { + self.actual = Some(value); + self + } + + pub fn amount(mut self, value: Option) -> Self { + self.amount = Some(value); + self + } + + pub fn forecast(mut self, value: Option) -> Self { + self.forecast = Some(value); + self + } + + pub fn ootb_forecast(mut self, value: Option) -> Self { + self.ootb_forecast = Some(value); + self + } + + pub fn additional_properties( + mut self, + value: std::collections::BTreeMap, + ) -> Self { + self.additional_properties = value; + self + } +} + +impl Default for BudgetAttributesCosts { + fn default() -> Self { + Self::new() + } +} + +impl<'de> Deserialize<'de> for BudgetAttributesCosts { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct BudgetAttributesCostsVisitor; + impl<'a> Visitor<'a> for BudgetAttributesCostsVisitor { + type Value = BudgetAttributesCosts; + + 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 actual: Option> = None; + let mut amount: Option> = None; + let mut forecast: Option> = None; + let mut ootb_forecast: 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() { + "actual" => { + if v.as_str() == Some("") { + continue; + } + actual = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "amount" => { + if v.as_str() == Some("") { + continue; + } + amount = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "forecast" => { + if v.as_str() == Some("") { + continue; + } + forecast = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "ootb_forecast" => { + if v.as_str() == Some("") { + continue; + } + ootb_forecast = + 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 = BudgetAttributesCosts { + actual, + amount, + forecast, + ootb_forecast, + additional_properties, + _unparsed, + }; + + Ok(content) + } + } + + deserializer.deserialize_any(BudgetAttributesCostsVisitor) + } +} diff --git a/src/datadogV2/model/model_budget_attributes_costs_unit.rs b/src/datadogV2/model/model_budget_attributes_costs_unit.rs new file mode 100644 index 0000000000..8de19fdbda --- /dev/null +++ b/src/datadogV2/model/model_budget_attributes_costs_unit.rs @@ -0,0 +1,191 @@ +// 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 unit used for all cost values in the response. +#[non_exhaustive] +#[skip_serializing_none] +#[derive(Clone, Debug, PartialEq, Serialize)] +pub struct BudgetAttributesCostsUnit { + /// The unit family (for example, `currency`). + #[serde(rename = "family")] + pub family: Option, + /// The unique identifier for the unit. + #[serde(rename = "id")] + pub id: Option, + /// The full name of the unit. + #[serde(rename = "name")] + pub name: Option, + /// The plural form of the unit name. + #[serde(rename = "plural")] + pub plural: Option, + /// The scale factor applied to raw cost values. + #[serde(rename = "scale_factor")] + pub scale_factor: Option, + /// The abbreviated unit name. + #[serde(rename = "short_name")] + pub short_name: Option, + #[serde(flatten)] + pub additional_properties: std::collections::BTreeMap, + #[serde(skip)] + #[serde(default)] + pub(crate) _unparsed: bool, +} + +impl BudgetAttributesCostsUnit { + pub fn new() -> BudgetAttributesCostsUnit { + BudgetAttributesCostsUnit { + family: None, + id: None, + name: None, + plural: None, + scale_factor: None, + short_name: None, + additional_properties: std::collections::BTreeMap::new(), + _unparsed: false, + } + } + + pub fn family(mut self, value: String) -> Self { + self.family = Some(value); + self + } + + pub fn id(mut self, value: String) -> Self { + self.id = Some(value); + self + } + + pub fn name(mut self, value: String) -> Self { + self.name = Some(value); + self + } + + pub fn plural(mut self, value: String) -> Self { + self.plural = Some(value); + self + } + + pub fn scale_factor(mut self, value: f64) -> Self { + self.scale_factor = Some(value); + self + } + + pub fn short_name(mut self, value: String) -> Self { + self.short_name = Some(value); + self + } + + pub fn additional_properties( + mut self, + value: std::collections::BTreeMap, + ) -> Self { + self.additional_properties = value; + self + } +} + +impl Default for BudgetAttributesCostsUnit { + fn default() -> Self { + Self::new() + } +} + +impl<'de> Deserialize<'de> for BudgetAttributesCostsUnit { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct BudgetAttributesCostsUnitVisitor; + impl<'a> Visitor<'a> for BudgetAttributesCostsUnitVisitor { + type Value = BudgetAttributesCostsUnit; + + 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 family: Option = None; + let mut id: Option = None; + let mut name: Option = None; + let mut plural: Option = None; + let mut scale_factor: Option = None; + let mut short_name: 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() { + "family" => { + if v.is_null() { + continue; + } + family = 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)?); + } + "name" => { + if v.is_null() { + continue; + } + name = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "plural" => { + if v.is_null() { + continue; + } + plural = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "scale_factor" => { + if v.is_null() || v.as_str() == Some("") { + continue; + } + scale_factor = + Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "short_name" => { + if v.is_null() { + continue; + } + short_name = 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 = BudgetAttributesCostsUnit { + family, + id, + name, + plural, + scale_factor, + short_name, + additional_properties, + _unparsed, + }; + + Ok(content) + } + } + + deserializer.deserialize_any(BudgetAttributesCostsUnitVisitor) + } +} diff --git a/src/datadogV2/model/model_budget_with_entries_data_attributes_entries_items.rs b/src/datadogV2/model/model_budget_with_entries_data_attributes_entries_items.rs index 0230270cbf..2424d80409 100644 --- a/src/datadogV2/model/model_budget_with_entries_data_attributes_entries_items.rs +++ b/src/datadogV2/model/model_budget_with_entries_data_attributes_entries_items.rs @@ -14,6 +14,9 @@ pub struct BudgetWithEntriesDataAttributesEntriesItems { /// The budgeted amount for this entry. #[serde(rename = "amount")] pub amount: Option, + /// Cost data for a single budget entry. + #[serde(rename = "costs")] + pub costs: Option, /// The month this budget entry applies to, in YYYYMM format. #[serde(rename = "month")] pub month: Option, @@ -33,6 +36,7 @@ impl BudgetWithEntriesDataAttributesEntriesItems { pub fn new() -> BudgetWithEntriesDataAttributesEntriesItems { BudgetWithEntriesDataAttributesEntriesItems { amount: None, + costs: None, month: None, tag_filters: None, additional_properties: std::collections::BTreeMap::new(), @@ -45,6 +49,14 @@ impl BudgetWithEntriesDataAttributesEntriesItems { self } + pub fn costs( + mut self, + value: crate::datadogV2::model::BudgetWithEntriesDataAttributesEntriesItemsCosts, + ) -> Self { + self.costs = Some(value); + self + } + pub fn month(mut self, value: i64) -> Self { self.month = Some(value); self @@ -93,6 +105,9 @@ impl<'de> Deserialize<'de> for BudgetWithEntriesDataAttributesEntriesItems { M: MapAccess<'a>, { let mut amount: Option = None; + let mut costs: Option< + crate::datadogV2::model::BudgetWithEntriesDataAttributesEntriesItemsCosts, + > = None; let mut month: Option = None; let mut tag_filters: Option> = None; let mut additional_properties: std::collections::BTreeMap< @@ -109,6 +124,12 @@ impl<'de> Deserialize<'de> for BudgetWithEntriesDataAttributesEntriesItems { } amount = Some(serde_json::from_value(v).map_err(M::Error::custom)?); } + "costs" => { + if v.is_null() { + continue; + } + costs = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } "month" => { if v.is_null() { continue; @@ -132,6 +153,7 @@ impl<'de> Deserialize<'de> for BudgetWithEntriesDataAttributesEntriesItems { let content = BudgetWithEntriesDataAttributesEntriesItems { amount, + costs, month, tag_filters, additional_properties, diff --git a/src/datadogV2/model/model_budget_with_entries_data_attributes_entries_items_costs.rs b/src/datadogV2/model/model_budget_with_entries_data_attributes_entries_items_costs.rs new file mode 100644 index 0000000000..ff244dcae8 --- /dev/null +++ b/src/datadogV2/model/model_budget_with_entries_data_attributes_entries_items_costs.rs @@ -0,0 +1,187 @@ +// 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}; + +/// Cost data for a single budget entry. +#[non_exhaustive] +#[skip_serializing_none] +#[derive(Clone, Debug, PartialEq, Serialize)] +pub struct BudgetWithEntriesDataAttributesEntriesItemsCosts { + /// The actual cost for this entry. Present only when `actual=true` is requested. + #[serde(rename = "actual", default, with = "::serde_with::rust::double_option")] + pub actual: Option>, + /// The budgeted amount for this entry. + #[serde(rename = "amount", default, with = "::serde_with::rust::double_option")] + pub amount: Option>, + /// The custom forecast override for this entry. `null` when `forecast=true` is requested but no custom forecast has been set for this entry's month. A numeric value, including `0`, indicates an explicit custom forecast override. Omitted when `forecast=false` or the feature is not available for the organization. + #[serde( + rename = "custom_forecast", + default, + with = "::serde_with::rust::double_option" + )] + pub custom_forecast: Option>, + /// The final forecast for this entry, with any custom forecast override applied. Present only when `forecast=true` is requested. + #[serde( + rename = "forecast", + default, + with = "::serde_with::rust::double_option" + )] + pub forecast: Option>, + /// The out-of-the-box ML forecast for this entry, before custom overrides. Present only when `forecast=true` is requested. + #[serde( + rename = "ootb_forecast", + default, + with = "::serde_with::rust::double_option" + )] + pub ootb_forecast: Option>, + #[serde(flatten)] + pub additional_properties: std::collections::BTreeMap, + #[serde(skip)] + #[serde(default)] + pub(crate) _unparsed: bool, +} + +impl BudgetWithEntriesDataAttributesEntriesItemsCosts { + pub fn new() -> BudgetWithEntriesDataAttributesEntriesItemsCosts { + BudgetWithEntriesDataAttributesEntriesItemsCosts { + actual: None, + amount: None, + custom_forecast: None, + forecast: None, + ootb_forecast: None, + additional_properties: std::collections::BTreeMap::new(), + _unparsed: false, + } + } + + pub fn actual(mut self, value: Option) -> Self { + self.actual = Some(value); + self + } + + pub fn amount(mut self, value: Option) -> Self { + self.amount = Some(value); + self + } + + pub fn custom_forecast(mut self, value: Option) -> Self { + self.custom_forecast = Some(value); + self + } + + pub fn forecast(mut self, value: Option) -> Self { + self.forecast = Some(value); + self + } + + pub fn ootb_forecast(mut self, value: Option) -> Self { + self.ootb_forecast = Some(value); + self + } + + pub fn additional_properties( + mut self, + value: std::collections::BTreeMap, + ) -> Self { + self.additional_properties = value; + self + } +} + +impl Default for BudgetWithEntriesDataAttributesEntriesItemsCosts { + fn default() -> Self { + Self::new() + } +} + +impl<'de> Deserialize<'de> for BudgetWithEntriesDataAttributesEntriesItemsCosts { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct BudgetWithEntriesDataAttributesEntriesItemsCostsVisitor; + impl<'a> Visitor<'a> for BudgetWithEntriesDataAttributesEntriesItemsCostsVisitor { + type Value = BudgetWithEntriesDataAttributesEntriesItemsCosts; + + 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 actual: Option> = None; + let mut amount: Option> = None; + let mut custom_forecast: Option> = None; + let mut forecast: Option> = None; + let mut ootb_forecast: 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() { + "actual" => { + if v.as_str() == Some("") { + continue; + } + actual = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "amount" => { + if v.as_str() == Some("") { + continue; + } + amount = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "custom_forecast" => { + if v.as_str() == Some("") { + continue; + } + custom_forecast = + Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "forecast" => { + if v.as_str() == Some("") { + continue; + } + forecast = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "ootb_forecast" => { + if v.as_str() == Some("") { + continue; + } + ootb_forecast = + 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 = BudgetWithEntriesDataAttributesEntriesItemsCosts { + actual, + amount, + custom_forecast, + forecast, + ootb_forecast, + additional_properties, + _unparsed, + }; + + Ok(content) + } + } + + deserializer.deserialize_any(BudgetWithEntriesDataAttributesEntriesItemsCostsVisitor) + } +} diff --git a/tests/scenarios/features/v2/cloud_cost_management.feature b/tests/scenarios/features/v2/cloud_cost_management.feature index 535fe88d7c..42cf91400a 100644 --- a/tests/scenarios/features/v2/cloud_cost_management.feature +++ b/tests/scenarios/features/v2/cloud_cost_management.feature @@ -70,21 +70,21 @@ Feature: Cloud Cost Management @generated @skip @team:DataDog/cloud-cost-management Scenario: Create or update a budget returns "Bad Request" response Given new "UpsertBudget" request - And body with value {"data": {"attributes": {"created_at": 1738258683590, "created_by": "00000000-0a0a-0a0a-aaa0-00000000000a", "end_month": 202502, "entries": [{"tag_filters": [{}]}], "metrics_query": "aws.cost.amortized{service:ec2} by {service}", "name": "my budget", "org_id": 123, "start_month": 202501, "total_amount": 1000, "updated_at": 1738258683590, "updated_by": "00000000-0a0a-0a0a-aaa0-00000000000a"}, "id": "00000000-0a0a-0a0a-aaa0-00000000000a", "type": ""}} + And body with value {"data": {"attributes": {"costs": {"actual": null, "amount": null, "forecast": null, "ootb_forecast": null}, "costs_unit": {}, "created_at": 1738258683590, "created_by": "00000000-0a0a-0a0a-aaa0-00000000000a", "end_month": 202502, "entries": [{"costs": {"actual": null, "amount": null, "custom_forecast": null, "forecast": null, "ootb_forecast": null}, "tag_filters": [{}]}], "metrics_query": "aws.cost.amortized{service:ec2} by {service}", "name": "my budget", "org_id": 123, "start_month": 202501, "total_amount": 1000, "updated_at": 1738258683590, "updated_by": "00000000-0a0a-0a0a-aaa0-00000000000a"}, "id": "00000000-0a0a-0a0a-aaa0-00000000000a", "type": ""}} When the request is sent Then the response status is 400 Bad Request @generated @skip @team:DataDog/cloud-cost-management Scenario: Create or update a budget returns "Not Found" response Given new "UpsertBudget" request - And body with value {"data": {"attributes": {"created_at": 1738258683590, "created_by": "00000000-0a0a-0a0a-aaa0-00000000000a", "end_month": 202502, "entries": [{"tag_filters": [{}]}], "metrics_query": "aws.cost.amortized{service:ec2} by {service}", "name": "my budget", "org_id": 123, "start_month": 202501, "total_amount": 1000, "updated_at": 1738258683590, "updated_by": "00000000-0a0a-0a0a-aaa0-00000000000a"}, "id": "00000000-0a0a-0a0a-aaa0-00000000000a", "type": ""}} + And body with value {"data": {"attributes": {"costs": {"actual": null, "amount": null, "forecast": null, "ootb_forecast": null}, "costs_unit": {}, "created_at": 1738258683590, "created_by": "00000000-0a0a-0a0a-aaa0-00000000000a", "end_month": 202502, "entries": [{"costs": {"actual": null, "amount": null, "custom_forecast": null, "forecast": null, "ootb_forecast": null}, "tag_filters": [{}]}], "metrics_query": "aws.cost.amortized{service:ec2} by {service}", "name": "my budget", "org_id": 123, "start_month": 202501, "total_amount": 1000, "updated_at": 1738258683590, "updated_by": "00000000-0a0a-0a0a-aaa0-00000000000a"}, "id": "00000000-0a0a-0a0a-aaa0-00000000000a", "type": ""}} When the request is sent Then the response status is 404 Not Found @generated @skip @team:DataDog/cloud-cost-management Scenario: Create or update a budget returns "OK" response Given new "UpsertBudget" request - And body with value {"data": {"attributes": {"created_at": 1738258683590, "created_by": "00000000-0a0a-0a0a-aaa0-00000000000a", "end_month": 202502, "entries": [{"tag_filters": [{}]}], "metrics_query": "aws.cost.amortized{service:ec2} by {service}", "name": "my budget", "org_id": 123, "start_month": 202501, "total_amount": 1000, "updated_at": 1738258683590, "updated_by": "00000000-0a0a-0a0a-aaa0-00000000000a"}, "id": "00000000-0a0a-0a0a-aaa0-00000000000a", "type": ""}} + And body with value {"data": {"attributes": {"costs": {"actual": null, "amount": null, "forecast": null, "ootb_forecast": null}, "costs_unit": {}, "created_at": 1738258683590, "created_by": "00000000-0a0a-0a0a-aaa0-00000000000a", "end_month": 202502, "entries": [{"costs": {"actual": null, "amount": null, "custom_forecast": null, "forecast": null, "ootb_forecast": null}, "tag_filters": [{}]}], "metrics_query": "aws.cost.amortized{service:ec2} by {service}", "name": "my budget", "org_id": 123, "start_month": 202501, "total_amount": 1000, "updated_at": 1738258683590, "updated_by": "00000000-0a0a-0a0a-aaa0-00000000000a"}, "id": "00000000-0a0a-0a0a-aaa0-00000000000a", "type": ""}} When the request is sent Then the response status is 200 OK @@ -336,6 +336,20 @@ Feature: Cloud Cost Management And the response "data.type" is equal to "ruleset" And the response "data.attributes.name" is equal to "EVP Cost Tags" + @generated @skip @team:DataDog/cloud-cost-management + Scenario: Get budget returns "Bad Request" response + Given new "GetBudget" request + And request contains "budget_id" parameter from "REPLACE.ME" + When the request is sent + Then the response status is 400 Bad Request + + @generated @skip @team:DataDog/cloud-cost-management + Scenario: Get budget returns "Not Found" response + Given new "GetBudget" request + And request contains "budget_id" parameter from "REPLACE.ME" + When the request is sent + Then the response status is 404 Not Found + @generated @skip @team:DataDog/cloud-cost-management Scenario: Get budget returns "OK" response Given new "GetBudget" request diff --git a/tests/scenarios/function_mappings.rs b/tests/scenarios/function_mappings.rs index b88984dae0..d31f139467 100644 --- a/tests/scenarios/function_mappings.rs +++ b/tests/scenarios/function_mappings.rs @@ -29705,7 +29705,24 @@ fn test_v2_get_budget(world: &mut DatadogWorld, _parameters: &HashMap response, Err(error) => { return match error {