From 2aeb4225f7e98f93a45d7c1c9c70c7fbec1b2c67 Mon Sep 17 00:00:00 2001 From: "ci.datadog-api-spec" Date: Fri, 16 Oct 2026 20:36:00 +0000 Subject: [PATCH] Regenerate client from commit 3f6ba8e of spec repo --- .generator/schemas/v2/openapi.yaml | 162 +++++++++++++++--- examples/v2_usage-metering_CreateQuotas.rs | 9 +- .../v2_usage-metering_DeletePendingQuota.rs | 21 +++ examples/v2_usage-metering_DeleteQuota.rs | 2 +- examples/v2_usage-metering_UpdateQuota.rs | 5 +- src/datadog/configuration.rs | 1 + src/datadogV2/api/api_usage_metering.rs | 127 +++++++++++++- ...odel_usage_quota_bulk_result_attributes.rs | 38 +++- .../model_usage_quota_bulk_result_data.rs | 2 +- .../model_usage_quota_create_attributes.rs | 59 +++++-- .../model/model_usage_quota_create_data.rs | 2 +- .../model_usage_quota_response_attributes.rs | 38 ++++ .../model_usage_quota_update_attributes.rs | 21 ++- .../model/model_usage_quota_update_data.rs | 2 +- tests/scenarios/features/v2/undo.json | 6 + .../features/v2/usage_metering.feature | 45 ++++- tests/scenarios/function_mappings.rs | 30 ++++ 17 files changed, 509 insertions(+), 61 deletions(-) create mode 100644 examples/v2_usage-metering_DeletePendingQuota.rs diff --git a/.generator/schemas/v2/openapi.yaml b/.generator/schemas/v2/openapi.yaml index 8e540e2cff..6b96392bdd 100644 --- a/.generator/schemas/v2/openapi.yaml +++ b/.generator/schemas/v2/openapi.yaml @@ -137258,7 +137258,7 @@ components: type: array type: object UsageQuotaBulkResultAttributes: - description: Attributes of a usage quota bulk write result. On success, all fields except `error` are present. On failure, only `error` is present and the other fields are omitted. + description: Attributes of a usage quota bulk write result. On success, quota fields are present as applicable, and pending fields are present only when a change is scheduled. On failure, only `error` is present and the other fields are omitted. properties: enforced: description: Whether usage above the limit is actively blocked instead of only tracked or alerted on. Omitted if this item failed to write. @@ -137272,6 +137272,16 @@ components: description: The public ID of the organization that owns the quota. Omitted if this item failed to write. example: abcdef12345 type: string + pending_effective_from: + description: The future UTC month when the scheduled limit takes effect, formatted as `YYYY-MM`, starting at 00:00 UTC on its first day. Present only together with `pending_usage_limit` and omitted when no change is scheduled or this item failed to write. + example: "2026-11" + pattern: '^\d{4}-\d{2}$' + type: string + pending_usage_limit: + description: The usage limit scheduled for the organization-wide quota in the usage units defined by the quota namespace. A value of `0` is valid. At the start of the effective month, this value becomes `usage_limit` and both pending fields are omitted. Omitted when no change is scheduled or this item failed to write. + example: 50000 + format: double + type: number scope: $ref: "#/components/schemas/UsageQuotaResponseScope" usage_limit: @@ -137287,7 +137297,7 @@ components: $ref: "#/components/schemas/UsageQuotaBulkResultAttributes" id: description: An opaque usage quota identifier. Clients must pass this value back verbatim in update and delete requests and must not infer any structure from it. - example: "MjAfYWlfY3JlZGl0c1911c2VyX2hhbmRsZTpfX0FMTF9f" + example: "MTIzNB9haV9jcmVkaXRzH3VzZXJfaGFuZGxlOl9fQUxMX18" type: string type: $ref: "#/components/schemas/UsageQuotaType" @@ -137302,23 +137312,30 @@ components: $ref: "#/components/schemas/UsageQuotaBulkResultData" type: array UsageQuotaCreateAttributes: - description: Attributes for creating or updating a usage quota by scope. + description: Attributes for creating or updating a usage quota by scope. Each item must provide `usage_limit`, `pending_usage_limit`, or both. Providing only `pending_usage_limit` updates an existing organization-wide quota, never creates one, requires `enforced` to be omitted, and fails if the quota does not exist. + example: + enforced: true + pending_usage_limit: 100000 + usage_limit: 600000 properties: enforced: - description: Whether to actively block usage above the limit instead of only tracking or alerting on it. + description: Whether to actively block usage above `usage_limit` instead of only tracking or alerting on it. Required when `usage_limit` is provided and must be omitted when only `pending_usage_limit` is provided. example: true type: boolean + pending_usage_limit: + description: The non-negative, whole-number limit to schedule for the organization-wide quota in the usage units defined by the quota namespace. It is not checked against current usage. Each write schedules the value for 00:00 UTC on the first day of the next calendar month and replaces any previously scheduled change; the server computes `pending_effective_from`. Omit this field to leave any scheduled change unchanged, including when raising `usage_limit`. Cancel a scheduled change only by deleting the quota's `/pending` sub-resource. + example: 50000 + format: int64 + minimum: 0 + type: integer scope: $ref: "#/components/schemas/UsageQuotaRequestScope" usage_limit: - description: The quota limit to set in the usage units defined by the quota namespace. For an organization-wide quota (scope omitted), the limit must be greater than the usage already recorded in the current period. + description: The non-negative, whole-number quota limit to set in the usage units defined by the quota namespace. For an organization-wide quota (scope omitted), the limit must be greater than usage already recorded in the current period. When this field is provided, `enforced` is required. example: 100000 format: int64 minimum: 0 type: integer - required: - - usage_limit - - enforced type: object UsageQuotaCreateData: description: A usage quota resource to create or update by scope. @@ -137366,6 +137383,19 @@ components: description: The public ID of the organization that owns the quota. example: abcdef12345 type: string + pending_effective_from: + description: The future UTC month when the scheduled limit takes effect, formatted as `YYYY-MM`, starting at 00:00 UTC on its first day. Present only together with `pending_usage_limit` and omitted when no change is scheduled. + example: "2026-11" + pattern: '^\d{4}-\d{2}$' + type: string + pending_usage_limit: + description: |- + The usage limit scheduled for the organization-wide quota in the usage units defined by the quota namespace. + A value of `0` is valid. At the start of the effective month, this value becomes `usage_limit` and both pending + fields are omitted. Omitted when no change is scheduled. + example: 50000 + format: double + type: number scope: $ref: "#/components/schemas/UsageQuotaResponseScope" usage_limit: @@ -137385,7 +137415,7 @@ components: $ref: "#/components/schemas/UsageQuotaResponseAttributes" id: description: An opaque usage quota identifier. Clients must pass this value back verbatim in update and delete requests and must not infer any structure from it. - example: "MjAfYWlfY3JlZGl0c1911c2VyX2hhbmRsZTpfX0FMTF9f" + example: "MTIzNB9haV9jcmVkaXRzH3VzZXJfaGFuZGxlOl9fQUxMX18" type: string type: $ref: "#/components/schemas/UsageQuotaType" @@ -137418,13 +137448,20 @@ components: x-enum-varnames: - QUOTAS UsageQuotaUpdateAttributes: - description: Attributes to update on a usage quota. Omitting a property leaves its current value unchanged. + description: Attributes to update on a usage quota. At least one of `usage_limit`, `enforced`, or `pending_usage_limit` must be provided. Omitting a property leaves its current value unchanged. properties: enforced: description: Whether to actively block usage above the limit. Omit this field to leave the current enforcement setting unchanged. example: false nullable: true type: boolean + pending_usage_limit: + description: The non-negative, whole-number limit to schedule for the organization-wide quota in the usage units defined by the quota namespace. It is not checked against current usage. Each write schedules the value for 00:00 UTC on the first day of the next calendar month and replaces any previously scheduled change; the server computes `pending_effective_from`. Omit this field to leave any scheduled change unchanged, including when raising `usage_limit`; use `DELETE /api/v2/usage/quotas/{quota_namespace}/{id}/pending` to cancel one. + example: 50000 + format: int64 + minimum: 0 + nullable: true + type: integer usage_limit: description: The new quota limit in the usage units defined by the quota namespace. For an organization-wide quota (empty scope), the limit must be greater than the usage already recorded in the current period. Omit this field to leave the current limit unchanged. example: 120000 @@ -137440,7 +137477,7 @@ components: $ref: "#/components/schemas/UsageQuotaUpdateAttributes" id: description: The opaque usage quota identifier, which must match the identifier in the request path. - example: "MjAfYWlfY3JlZGl0c1911c2VyX2hhbmRsZTpfX0FMTF9f" + example: "MTIzNB9haV9jcmVkaXRzH3VzZXJfaGFuZGxlOl9fQUxMX18" type: string type: $ref: "#/components/schemas/UsageQuotaType" @@ -245841,7 +245878,15 @@ paths: scope: user_handle: jane@example.com usage_limit: 100000 - id: "MjAfYWlfY3JlZGl0c1911c2VyX2hhbmRsZTpfX0FMTF9f" + id: "MTIzNB9haV9jcmVkaXRzH3VzZXJfaGFuZGxlOmphbmVAZXhhbXBsZS5jb20" + type: quotas + - attributes: + enforced: true + org_public_id: abcdef12345 + pending_effective_from: "2026-11" + pending_usage_limit: 100000 + usage_limit: 600000 + id: "MTIzNB9haV9jcmVkaXRzH3VzZXJfaGFuZGxlOl9fQUxMX18" type: quotas meta: page: @@ -245888,7 +245933,11 @@ paths: If you have any feedback, contact [Datadog support](https://docs.datadoghq.com/help/). post: description: |- - Creates or updates one or more usage quotas by scope. If a quota already exists for a supplied scope, it is updated; otherwise, a new quota is created. Requires the `billing_edit` permission. + Creates or updates one or more usage quotas by scope. If a quota already exists for a supplied scope, it is updated. + Otherwise, a quota is created only when `usage_limit` and `enforced` are provided. + For the organization-wide quota, `pending_usage_limit` schedules a limit for the next usage period and can + accompany an immediate limit or update an existing quota by itself. + Scheduled changes follow `include_descendants` like the other fields. Requires the `billing_edit` permission. operationId: CreateQuotas parameters: - description: The product-specific namespace whose usage quotas are being managed. @@ -245925,6 +245974,15 @@ paths: user_handle: "*" usage_limit: 250000 type: quotas + schedule_reduction: + summary: Set a limit and schedule a reduction + value: + data: + - attributes: + enforced: true + pending_usage_limit: 100000 + usage_limit: 600000 + type: quotas schema: $ref: "#/components/schemas/UsageQuotasCreateRequest" description: The usage quotas to create or update. @@ -245943,11 +246001,11 @@ paths: scope: user_handle: jane@example.com usage_limit: 100000 - id: "MjAfYWlfY3JlZGl0c1911c2VyX2hhbmRsZTpfX0FMTF9f" + id: "MTIzNB9haV9jcmVkaXRzH3VzZXJfaGFuZGxlOmphbmVAZXhhbXBsZS5jb20" type: quotas - attributes: error: usage_limit is out of range - id: "MjAfYWlfY3JlZGl0c1911c2VyX2hhbmRsZTp1c2VyQGV4YW1wbGUuY29t" + id: "MTIzNB9haV9jcmVkaXRzH3VzZXJfaGFuZGxlOnVzZXJAZXhhbXBsZS5jb20" type: quotas schema: $ref: "#/components/schemas/UsageQuotasBulkResponse" @@ -245957,7 +246015,7 @@ paths: application/json: schema: $ref: "#/components/schemas/JSONAPIErrorResponse" - description: Bad Request + description: Bad Request. Returned if the request is malformed, including when an item sets `pending_usage_limit` on a scoped quota, provides `enforced` without `usage_limit`, or provides neither limit. A pending-only item for a quota that does not exist is reported in that item's `error`; when only the caller's organization is targeted, this failure returns `400`. "403": content: application/json: @@ -246005,7 +246063,7 @@ paths: name: id required: true schema: - example: "MjAfYWlfY3JlZGl0c1911c2VyX2hhbmRsZTpfX0FMTF9f" + example: "MTIzNB9haV9jcmVkaXRzH3VzZXJfaGFuZGxlOl9fQUxMX18" type: string responses: "204": @@ -246041,7 +246099,7 @@ paths: If you have any feedback, contact [Datadog support](https://docs.datadoghq.com/help/). patch: description: |- - Updates the supplied fields on a usage quota and leaves omitted fields unchanged. The quota must belong to the caller's organization or one of its descendants, and its opaque identifier must belong to the requested quota namespace. Requires the `billing_edit` permission. + Updates the supplied fields on a usage quota and leaves omitted fields unchanged. For an organization-wide quota, `pending_usage_limit` schedules a limit for the next usage period. The quota must belong to the caller's organization or one of its descendants, and its opaque identifier must belong to the requested quota namespace. Requires the `billing_edit` permission. operationId: UpdateQuota parameters: - description: The product-specific namespace whose usage quotas are being managed. @@ -246056,7 +246114,7 @@ paths: name: id required: true schema: - example: "MjAfYWlfY3JlZGl0c1911c2VyX2hhbmRsZTpfX0FMTF9f" + example: "MTIzNB9haV9jcmVkaXRzH3VzZXJfaGFuZGxlOl9fQUxMX18" type: string requestBody: content: @@ -246068,7 +246126,7 @@ paths: attributes: enforced: false usage_limit: 120000 - id: "MjAfYWlfY3JlZGl0c1911c2VyX2hhbmRsZTpfX0FMTF9f" + id: "MTIzNB9haV9jcmVkaXRzH3VzZXJfaGFuZGxlOl9fQUxMX18" type: quotas schema: $ref: "#/components/schemas/UsageQuotaUpdateRequest" @@ -246085,10 +246143,8 @@ paths: attributes: enforced: false org_public_id: abcdef12345 - scope: - user_handle: jane@example.com usage_limit: 120000 - id: "MjAfYWlfY3JlZGl0c1911c2VyX2hhbmRsZTpfX0FMTF9f" + id: "MTIzNB9haV9jcmVkaXRzH3VzZXJfaGFuZGxlOl9fQUxMX18" type: quotas schema: $ref: "#/components/schemas/UsageQuotaResponse" @@ -246098,7 +246154,7 @@ paths: application/json: schema: $ref: "#/components/schemas/JSONAPIErrorResponse" - description: Bad Request. Returned if the request is malformed, or if the `id` in the request body does not match the `id` in the request path. + description: Bad Request. Returned if the request is malformed, if the `id` in the request body does not match the `id` in the request path, if none of `usage_limit`, `enforced`, or `pending_usage_limit` is provided, or if `pending_usage_limit` is set on a scoped quota. "403": content: application/json: @@ -246128,6 +246184,64 @@ paths: 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/usage/quotas/{quota_namespace}/{id}/pending: + delete: + description: |- + Cancels the limit change scheduled to take effect at the start of the next usage period, leaving the usage quota and its current limit unchanged. Returns `404` when the quota does not exist, has no scheduled change, or its scheduled change has already taken effect; in every case the quota is left unchanged. The quota must belong to the caller's organization or one of its descendants, and its opaque identifier must belong to the requested quota namespace. Requires the `billing_edit` permission. + operationId: DeletePendingQuota + parameters: + - description: The product-specific namespace whose usage quotas are being managed. + in: path + name: quota_namespace + required: true + schema: + example: ai_credits + type: string + - description: The opaque quota identifier returned by a previous list or create request. Clients must pass this value verbatim. + in: path + name: id + required: true + schema: + example: "MTIzNB9haV9jcmVkaXRzH3VzZXJfaGFuZGxlOl9fQUxMX18" + type: string + responses: + "204": + description: No Content + "400": + content: + application/json: + schema: + $ref: "#/components/schemas/JSONAPIErrorResponse" + description: Bad Request. Returned if the `id` is malformed or does not belong to the requested quota namespace. + "403": + content: + application/json: + schema: + $ref: "#/components/schemas/JSONAPIErrorResponse" + description: Forbidden + "404": + content: + application/json: + schema: + $ref: "#/components/schemas/JSONAPIErrorResponse" + description: Not Found + "429": + $ref: "#/components/responses/TooManyRequestsResponse" + security: + - apiKeyAuth: [] + appKeyAuth: [] + - AuthZ: + - billing_edit + summary: Cancel a scheduled usage quota limit + tags: + - Usage Metering + "x-permission": + operator: OR + permissions: + - billing_edit + 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/usage/summary/available_fields: get: description: |- diff --git a/examples/v2_usage-metering_CreateQuotas.rs b/examples/v2_usage-metering_CreateQuotas.rs index d6bfc94ff6..3de956d14f 100644 --- a/examples/v2_usage-metering_CreateQuotas.rs +++ b/examples/v2_usage-metering_CreateQuotas.rs @@ -8,15 +8,14 @@ use datadog_api_client::datadogV2::model::UsageQuotaCreateAttributes; use datadog_api_client::datadogV2::model::UsageQuotaCreateData; use datadog_api_client::datadogV2::model::UsageQuotaType; use datadog_api_client::datadogV2::model::UsageQuotasCreateRequest; -use std::collections::BTreeMap; #[tokio::main] async fn main() { let body = UsageQuotasCreateRequest::new(vec![UsageQuotaCreateData::new( - UsageQuotaCreateAttributes::new(true, 100000).scope(BTreeMap::from([( - "user_handle".to_string(), - "jane@example.com".to_string(), - )])), + UsageQuotaCreateAttributes::new() + .enforced(true) + .pending_usage_limit(100000) + .usage_limit(600000), UsageQuotaType::QUOTAS, )]); let mut configuration = datadog::Configuration::new(); diff --git a/examples/v2_usage-metering_DeletePendingQuota.rs b/examples/v2_usage-metering_DeletePendingQuota.rs new file mode 100644 index 0000000000..9627c12cc6 --- /dev/null +++ b/examples/v2_usage-metering_DeletePendingQuota.rs @@ -0,0 +1,21 @@ +// Cancel a scheduled usage quota limit returns "No Content" response +use datadog_api_client::datadog; +use datadog_api_client::datadogV2::api_usage_metering::UsageMeteringAPI; + +#[tokio::main] +async fn main() { + let mut configuration = datadog::Configuration::new(); + configuration.set_unstable_operation_enabled("v2.delete_pending_quota", true); + let api = UsageMeteringAPI::with_config(configuration); + let resp = api + .delete_pending_quota( + "ai_credits".to_string(), + "MTIzNB9haV9jcmVkaXRzH3VzZXJfaGFuZGxlOl9fQUxMX18".to_string(), + ) + .await; + if let Ok(value) = resp { + println!("{:#?}", value); + } else { + println!("{:#?}", resp.unwrap_err()); + } +} diff --git a/examples/v2_usage-metering_DeleteQuota.rs b/examples/v2_usage-metering_DeleteQuota.rs index 026966cb73..036c7fc497 100644 --- a/examples/v2_usage-metering_DeleteQuota.rs +++ b/examples/v2_usage-metering_DeleteQuota.rs @@ -10,7 +10,7 @@ async fn main() { let resp = api .delete_quota( "ai_credits".to_string(), - "MjAfYWlfY3JlZGl0c1911c2VyX2hhbmRsZTpfX0FMTF9f".to_string(), + "MTIzNB9haV9jcmVkaXRzH3VzZXJfaGFuZGxlOl9fQUxMX18".to_string(), ) .await; if let Ok(value) = resp { diff --git a/examples/v2_usage-metering_UpdateQuota.rs b/examples/v2_usage-metering_UpdateQuota.rs index 654414bd83..d6463f7d2a 100644 --- a/examples/v2_usage-metering_UpdateQuota.rs +++ b/examples/v2_usage-metering_UpdateQuota.rs @@ -11,8 +11,9 @@ async fn main() { let body = UsageQuotaUpdateRequest::new(UsageQuotaUpdateData::new( UsageQuotaUpdateAttributes::new() .enforced(Some(false)) + .pending_usage_limit(Some(50000)) .usage_limit(Some(120000)), - "MjAfYWlfY3JlZGl0c1911c2VyX2hhbmRsZTpfX0FMTF9f".to_string(), + "MTIzNB9haV9jcmVkaXRzH3VzZXJfaGFuZGxlOl9fQUxMX18".to_string(), UsageQuotaType::QUOTAS, )); let mut configuration = datadog::Configuration::new(); @@ -21,7 +22,7 @@ async fn main() { let resp = api .update_quota( "ai_credits".to_string(), - "MjAfYWlfY3JlZGl0c1911c2VyX2hhbmRsZTpfX0FMTF9f".to_string(), + "MTIzNB9haV9jcmVkaXRzH3VzZXJfaGFuZGxlOl9fQUxMX18".to_string(), body, ) .await; diff --git a/src/datadog/configuration.rs b/src/datadog/configuration.rs index a8278679ed..df89c3d7f8 100644 --- a/src/datadog/configuration.rs +++ b/src/datadog/configuration.rs @@ -585,6 +585,7 @@ impl Default for Configuration { ("v2.search_cost_recommendations".to_owned(), false), ("v2.update_unit_cost".to_owned(), false), ("v2.create_quotas".to_owned(), false), + ("v2.delete_pending_quota".to_owned(), false), ("v2.delete_quota".to_owned(), false), ("v2.list_quotas".to_owned(), false), ("v2.update_quota".to_owned(), false), diff --git a/src/datadogV2/api/api_usage_metering.rs b/src/datadogV2/api/api_usage_metering.rs index d18ab20b0e..63c6076214 100644 --- a/src/datadogV2/api/api_usage_metering.rs +++ b/src/datadogV2/api/api_usage_metering.rs @@ -387,6 +387,15 @@ pub enum CreateQuotasError { UnknownValue(serde_json::Value), } +/// DeletePendingQuotaError is a struct for typed errors of method [`UsageMeteringAPI::delete_pending_quota`] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum DeletePendingQuotaError { + JSONAPIErrorResponse(crate::datadogV2::model::JSONAPIErrorResponse), + APIErrorResponse(crate::datadogV2::model::APIErrorResponse), + UnknownValue(serde_json::Value), +} + /// DeleteQuotaError is a struct for typed errors of method [`UsageMeteringAPI::delete_quota`] #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(untagged)] @@ -601,7 +610,11 @@ impl UsageMeteringAPI { Self { config, client } } - /// Creates or updates one or more usage quotas by scope. If a quota already exists for a supplied scope, it is updated; otherwise, a new quota is created. Requires the `billing_edit` permission. + /// Creates or updates one or more usage quotas by scope. If a quota already exists for a supplied scope, it is updated. + /// Otherwise, a quota is created only when `usage_limit` and `enforced` are provided. + /// For the organization-wide quota, `pending_usage_limit` schedules a limit for the next usage period and can + /// accompany an immediate limit or update an existing quota by itself. + /// Scheduled changes follow `include_descendants` like the other fields. Requires the `billing_edit` permission. pub async fn create_quotas( &self, quota_namespace: String, @@ -626,7 +639,11 @@ impl UsageMeteringAPI { } } - /// Creates or updates one or more usage quotas by scope. If a quota already exists for a supplied scope, it is updated; otherwise, a new quota is created. Requires the `billing_edit` permission. + /// Creates or updates one or more usage quotas by scope. If a quota already exists for a supplied scope, it is updated. + /// Otherwise, a quota is created only when `usage_limit` and `enforced` are provided. + /// For the organization-wide quota, `pending_usage_limit` schedules a limit for the next usage period and can + /// accompany an immediate limit or update an existing quota by itself. + /// Scheduled changes follow `include_descendants` like the other fields. Requires the `billing_edit` permission. pub async fn create_quotas_with_http_info( &self, quota_namespace: String, @@ -777,6 +794,108 @@ impl UsageMeteringAPI { } } + /// Cancels the limit change scheduled to take effect at the start of the next usage period, leaving the usage quota and its current limit unchanged. Returns `404` when the quota does not exist, has no scheduled change, or its scheduled change has already taken effect; in every case the quota is left unchanged. The quota must belong to the caller's organization or one of its descendants, and its opaque identifier must belong to the requested quota namespace. Requires the `billing_edit` permission. + pub async fn delete_pending_quota( + &self, + quota_namespace: String, + id: String, + ) -> Result<(), datadog::Error> { + match self + .delete_pending_quota_with_http_info(quota_namespace, id) + .await + { + Ok(_) => Ok(()), + Err(err) => Err(err), + } + } + + /// Cancels the limit change scheduled to take effect at the start of the next usage period, leaving the usage quota and its current limit unchanged. Returns `404` when the quota does not exist, has no scheduled change, or its scheduled change has already taken effect; in every case the quota is left unchanged. The quota must belong to the caller's organization or one of its descendants, and its opaque identifier must belong to the requested quota namespace. Requires the `billing_edit` permission. + pub async fn delete_pending_quota_with_http_info( + &self, + quota_namespace: String, + id: String, + ) -> Result, datadog::Error> { + let local_configuration = &self.config; + let local_operation_id = "v2.delete_pending_quota"; + if local_configuration.is_unstable_operation_enabled(local_operation_id) { + warn!("Using unstable operation {local_operation_id}"); + } else { + let local_error = datadog::UnstableOperationDisabledError { + msg: "Operation 'v2.delete_pending_quota' is not enabled".to_string(), + }; + return Err(datadog::Error::UnstableOperationDisabledError(local_error)); + } + + let local_client = &self.client; + + let local_uri_str = format!( + "{}/api/v2/usage/quotas/{quota_namespace}/{id}/pending", + local_configuration.get_operation_host(local_operation_id), + quota_namespace = datadog::urlencode(quota_namespace), + id = datadog::urlencode(id) + ); + let mut local_req_builder = + local_client.request(reqwest::Method::DELETE, local_uri_str.as_str()); + + // build headers + let mut headers = HeaderMap::new(); + headers.insert("Accept", HeaderValue::from_static("*/*")); + + // 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() { + Ok(datadog::ResponseContent { + status: local_status, + content: local_content, + entity: None, + }) + } 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)) + } + } + /// Deletes a usage quota by its opaque identifier. The quota must belong to the caller's organization or one of its descendants, and its opaque identifier must belong to the requested quota namespace. Requires the `billing_edit` permission. pub async fn delete_quota( &self, @@ -2926,7 +3045,7 @@ impl UsageMeteringAPI { } } - /// Updates the supplied fields on a usage quota and leaves omitted fields unchanged. The quota must belong to the caller's organization or one of its descendants, and its opaque identifier must belong to the requested quota namespace. Requires the `billing_edit` permission. + /// Updates the supplied fields on a usage quota and leaves omitted fields unchanged. For an organization-wide quota, `pending_usage_limit` schedules a limit for the next usage period. The quota must belong to the caller's organization or one of its descendants, and its opaque identifier must belong to the requested quota namespace. Requires the `billing_edit` permission. pub async fn update_quota( &self, quota_namespace: String, @@ -2950,7 +3069,7 @@ impl UsageMeteringAPI { } } - /// Updates the supplied fields on a usage quota and leaves omitted fields unchanged. The quota must belong to the caller's organization or one of its descendants, and its opaque identifier must belong to the requested quota namespace. Requires the `billing_edit` permission. + /// Updates the supplied fields on a usage quota and leaves omitted fields unchanged. For an organization-wide quota, `pending_usage_limit` schedules a limit for the next usage period. The quota must belong to the caller's organization or one of its descendants, and its opaque identifier must belong to the requested quota namespace. Requires the `billing_edit` permission. pub async fn update_quota_with_http_info( &self, quota_namespace: String, diff --git a/src/datadogV2/model/model_usage_quota_bulk_result_attributes.rs b/src/datadogV2/model/model_usage_quota_bulk_result_attributes.rs index c32562fd01..4d246c869e 100644 --- a/src/datadogV2/model/model_usage_quota_bulk_result_attributes.rs +++ b/src/datadogV2/model/model_usage_quota_bulk_result_attributes.rs @@ -6,7 +6,7 @@ use serde::{Deserialize, Deserializer, Serialize}; use serde_with::skip_serializing_none; use std::fmt::{self, Formatter}; -/// Attributes of a usage quota bulk write result. On success, all fields except `error` are present. On failure, only `error` is present and the other fields are omitted. +/// Attributes of a usage quota bulk write result. On success, quota fields are present as applicable, and pending fields are present only when a change is scheduled. On failure, only `error` is present and the other fields are omitted. #[non_exhaustive] #[skip_serializing_none] #[derive(Clone, Debug, PartialEq, Serialize)] @@ -20,6 +20,12 @@ pub struct UsageQuotaBulkResultAttributes { /// The public ID of the organization that owns the quota. Omitted if this item failed to write. #[serde(rename = "org_public_id")] pub org_public_id: Option, + /// The future UTC month when the scheduled limit takes effect, formatted as `YYYY-MM`, starting at 00:00 UTC on its first day. Present only together with `pending_usage_limit` and omitted when no change is scheduled or this item failed to write. + #[serde(rename = "pending_effective_from")] + pub pending_effective_from: Option, + /// The usage limit scheduled for the organization-wide quota in the usage units defined by the quota namespace. A value of `0` is valid. At the start of the effective month, this value becomes `usage_limit` and both pending fields are omitted. Omitted when no change is scheduled or this item failed to write. + #[serde(rename = "pending_usage_limit")] + pub pending_usage_limit: Option, /// A namespace-specific key and value identifying what the quota applies to within an organization. The object contains exactly one entry. A value of `"*"` identifies the default quota applied to entities without a specific quota. This field is omitted for an organization-wide quota. #[serde(rename = "scope")] pub scope: Option>, @@ -39,6 +45,8 @@ impl UsageQuotaBulkResultAttributes { enforced: None, error: None, org_public_id: None, + pending_effective_from: None, + pending_usage_limit: None, scope: None, usage_limit: None, additional_properties: std::collections::BTreeMap::new(), @@ -61,6 +69,16 @@ impl UsageQuotaBulkResultAttributes { self } + pub fn pending_effective_from(mut self, value: String) -> Self { + self.pending_effective_from = Some(value); + self + } + + pub fn pending_usage_limit(mut self, value: f64) -> Self { + self.pending_usage_limit = Some(value); + self + } + pub fn scope(mut self, value: std::collections::BTreeMap) -> Self { self.scope = Some(value); self @@ -106,6 +124,8 @@ impl<'de> Deserialize<'de> for UsageQuotaBulkResultAttributes { let mut enforced: Option = None; let mut error: Option = None; let mut org_public_id: Option = None; + let mut pending_effective_from: Option = None; + let mut pending_usage_limit: Option = None; let mut scope: Option> = None; let mut usage_limit: Option = None; let mut additional_properties: std::collections::BTreeMap< @@ -135,6 +155,20 @@ impl<'de> Deserialize<'de> for UsageQuotaBulkResultAttributes { org_public_id = Some(serde_json::from_value(v).map_err(M::Error::custom)?); } + "pending_effective_from" => { + if v.is_null() { + continue; + } + pending_effective_from = + Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "pending_usage_limit" => { + if v.is_null() || v.as_str() == Some("") { + continue; + } + pending_usage_limit = + Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } "scope" => { if v.is_null() { continue; @@ -160,6 +194,8 @@ impl<'de> Deserialize<'de> for UsageQuotaBulkResultAttributes { enforced, error, org_public_id, + pending_effective_from, + pending_usage_limit, scope, usage_limit, additional_properties, diff --git a/src/datadogV2/model/model_usage_quota_bulk_result_data.rs b/src/datadogV2/model/model_usage_quota_bulk_result_data.rs index 082e72508c..29f1c1808f 100644 --- a/src/datadogV2/model/model_usage_quota_bulk_result_data.rs +++ b/src/datadogV2/model/model_usage_quota_bulk_result_data.rs @@ -11,7 +11,7 @@ use std::fmt::{self, Formatter}; #[skip_serializing_none] #[derive(Clone, Debug, PartialEq, Serialize)] pub struct UsageQuotaBulkResultData { - /// Attributes of a usage quota bulk write result. On success, all fields except `error` are present. On failure, only `error` is present and the other fields are omitted. + /// Attributes of a usage quota bulk write result. On success, quota fields are present as applicable, and pending fields are present only when a change is scheduled. On failure, only `error` is present and the other fields are omitted. #[serde(rename = "attributes")] pub attributes: crate::datadogV2::model::UsageQuotaBulkResultAttributes, /// An opaque usage quota identifier. Clients must pass this value back verbatim in update and delete requests and must not infer any structure from it. diff --git a/src/datadogV2/model/model_usage_quota_create_attributes.rs b/src/datadogV2/model/model_usage_quota_create_attributes.rs index 417f5a396c..16359ff04d 100644 --- a/src/datadogV2/model/model_usage_quota_create_attributes.rs +++ b/src/datadogV2/model/model_usage_quota_create_attributes.rs @@ -6,20 +6,23 @@ use serde::{Deserialize, Deserializer, Serialize}; use serde_with::skip_serializing_none; use std::fmt::{self, Formatter}; -/// Attributes for creating or updating a usage quota by scope. +/// Attributes for creating or updating a usage quota by scope. Each item must provide `usage_limit`, `pending_usage_limit`, or both. Providing only `pending_usage_limit` updates an existing organization-wide quota, never creates one, requires `enforced` to be omitted, and fails if the quota does not exist. #[non_exhaustive] #[skip_serializing_none] #[derive(Clone, Debug, PartialEq, Serialize)] pub struct UsageQuotaCreateAttributes { - /// Whether to actively block usage above the limit instead of only tracking or alerting on it. + /// Whether to actively block usage above `usage_limit` instead of only tracking or alerting on it. Required when `usage_limit` is provided and must be omitted when only `pending_usage_limit` is provided. #[serde(rename = "enforced")] - pub enforced: bool, + pub enforced: Option, + /// The non-negative, whole-number limit to schedule for the organization-wide quota in the usage units defined by the quota namespace. It is not checked against current usage. Each write schedules the value for 00:00 UTC on the first day of the next calendar month and replaces any previously scheduled change; the server computes `pending_effective_from`. Omit this field to leave any scheduled change unchanged, including when raising `usage_limit`. Cancel a scheduled change only by deleting the quota's `/pending` sub-resource. + #[serde(rename = "pending_usage_limit")] + pub pending_usage_limit: Option, /// A namespace-specific key and value identifying what the quota applies to within an organization. The object must contain exactly one entry. Use `"*"` as the value for the default quota applied to entities without a specific quota, or omit the scope for an organization-wide quota. A specific value must identify an existing user handle in the caller's organization when `include_descendants` is false. When `include_descendants` is true, the handle must exist in the caller's organization or in at least one targeted descendant organization; the quota is then applied only to the organizations where that handle exists, and the request fails only if the handle exists in none of them. #[serde(rename = "scope")] pub scope: Option>, - /// The quota limit to set in the usage units defined by the quota namespace. For an organization-wide quota (scope omitted), the limit must be greater than the usage already recorded in the current period. + /// The non-negative, whole-number quota limit to set in the usage units defined by the quota namespace. For an organization-wide quota (scope omitted), the limit must be greater than usage already recorded in the current period. When this field is provided, `enforced` is required. #[serde(rename = "usage_limit")] - pub usage_limit: i64, + pub usage_limit: Option, #[serde(flatten)] pub additional_properties: std::collections::BTreeMap, #[serde(skip)] @@ -28,21 +31,37 @@ pub struct UsageQuotaCreateAttributes { } impl UsageQuotaCreateAttributes { - pub fn new(enforced: bool, usage_limit: i64) -> UsageQuotaCreateAttributes { + pub fn new() -> UsageQuotaCreateAttributes { UsageQuotaCreateAttributes { - enforced, + enforced: None, + pending_usage_limit: None, scope: None, - usage_limit, + usage_limit: None, additional_properties: std::collections::BTreeMap::new(), _unparsed: false, } } + pub fn enforced(mut self, value: bool) -> Self { + self.enforced = Some(value); + self + } + + pub fn pending_usage_limit(mut self, value: i64) -> Self { + self.pending_usage_limit = Some(value); + self + } + pub fn scope(mut self, value: std::collections::BTreeMap) -> Self { self.scope = Some(value); self } + pub fn usage_limit(mut self, value: i64) -> Self { + self.usage_limit = Some(value); + self + } + pub fn additional_properties( mut self, value: std::collections::BTreeMap, @@ -52,6 +71,12 @@ impl UsageQuotaCreateAttributes { } } +impl Default for UsageQuotaCreateAttributes { + fn default() -> Self { + Self::new() + } +} + impl<'de> Deserialize<'de> for UsageQuotaCreateAttributes { fn deserialize(deserializer: D) -> Result where @@ -70,6 +95,7 @@ impl<'de> Deserialize<'de> for UsageQuotaCreateAttributes { M: MapAccess<'a>, { let mut enforced: Option = None; + let mut pending_usage_limit: Option = None; let mut scope: Option> = None; let mut usage_limit: Option = None; let mut additional_properties: std::collections::BTreeMap< @@ -81,8 +107,18 @@ impl<'de> Deserialize<'de> for UsageQuotaCreateAttributes { while let Some((k, v)) = map.next_entry::()? { match k.as_str() { "enforced" => { + if v.is_null() { + continue; + } enforced = Some(serde_json::from_value(v).map_err(M::Error::custom)?); } + "pending_usage_limit" => { + if v.is_null() { + continue; + } + pending_usage_limit = + Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } "scope" => { if v.is_null() { continue; @@ -90,6 +126,9 @@ impl<'de> Deserialize<'de> for UsageQuotaCreateAttributes { scope = Some(serde_json::from_value(v).map_err(M::Error::custom)?); } "usage_limit" => { + if v.is_null() { + continue; + } usage_limit = Some(serde_json::from_value(v).map_err(M::Error::custom)?); } @@ -100,12 +139,10 @@ impl<'de> Deserialize<'de> for UsageQuotaCreateAttributes { } } } - let enforced = enforced.ok_or_else(|| M::Error::missing_field("enforced"))?; - let usage_limit = - usage_limit.ok_or_else(|| M::Error::missing_field("usage_limit"))?; let content = UsageQuotaCreateAttributes { enforced, + pending_usage_limit, scope, usage_limit, additional_properties, diff --git a/src/datadogV2/model/model_usage_quota_create_data.rs b/src/datadogV2/model/model_usage_quota_create_data.rs index ceb966becc..c277e55523 100644 --- a/src/datadogV2/model/model_usage_quota_create_data.rs +++ b/src/datadogV2/model/model_usage_quota_create_data.rs @@ -11,7 +11,7 @@ use std::fmt::{self, Formatter}; #[skip_serializing_none] #[derive(Clone, Debug, PartialEq, Serialize)] pub struct UsageQuotaCreateData { - /// Attributes for creating or updating a usage quota by scope. + /// Attributes for creating or updating a usage quota by scope. Each item must provide `usage_limit`, `pending_usage_limit`, or both. Providing only `pending_usage_limit` updates an existing organization-wide quota, never creates one, requires `enforced` to be omitted, and fails if the quota does not exist. #[serde(rename = "attributes")] pub attributes: crate::datadogV2::model::UsageQuotaCreateAttributes, /// The JSON:API resource type for a usage quota. diff --git a/src/datadogV2/model/model_usage_quota_response_attributes.rs b/src/datadogV2/model/model_usage_quota_response_attributes.rs index 5af3549010..7c12108355 100644 --- a/src/datadogV2/model/model_usage_quota_response_attributes.rs +++ b/src/datadogV2/model/model_usage_quota_response_attributes.rs @@ -17,6 +17,14 @@ pub struct UsageQuotaResponseAttributes { /// The public ID of the organization that owns the quota. #[serde(rename = "org_public_id")] pub org_public_id: String, + /// The future UTC month when the scheduled limit takes effect, formatted as `YYYY-MM`, starting at 00:00 UTC on its first day. Present only together with `pending_usage_limit` and omitted when no change is scheduled. + #[serde(rename = "pending_effective_from")] + pub pending_effective_from: Option, + /// The usage limit scheduled for the organization-wide quota in the usage units defined by the quota namespace. + /// A value of `0` is valid. At the start of the effective month, this value becomes `usage_limit` and both pending + /// fields are omitted. Omitted when no change is scheduled. + #[serde(rename = "pending_usage_limit")] + pub pending_usage_limit: Option, /// A namespace-specific key and value identifying what the quota applies to within an organization. The object contains exactly one entry. A value of `"*"` identifies the default quota applied to entities without a specific quota. This field is omitted for an organization-wide quota. #[serde(rename = "scope")] pub scope: Option>, @@ -39,6 +47,8 @@ impl UsageQuotaResponseAttributes { UsageQuotaResponseAttributes { enforced, org_public_id, + pending_effective_from: None, + pending_usage_limit: None, scope: None, usage_limit, additional_properties: std::collections::BTreeMap::new(), @@ -46,6 +56,16 @@ impl UsageQuotaResponseAttributes { } } + pub fn pending_effective_from(mut self, value: String) -> Self { + self.pending_effective_from = Some(value); + self + } + + pub fn pending_usage_limit(mut self, value: f64) -> Self { + self.pending_usage_limit = Some(value); + self + } + pub fn scope(mut self, value: std::collections::BTreeMap) -> Self { self.scope = Some(value); self @@ -79,6 +99,8 @@ impl<'de> Deserialize<'de> for UsageQuotaResponseAttributes { { let mut enforced: Option = None; let mut org_public_id: Option = None; + let mut pending_effective_from: Option = None; + let mut pending_usage_limit: Option = None; let mut scope: Option> = None; let mut usage_limit: Option = None; let mut additional_properties: std::collections::BTreeMap< @@ -96,6 +118,20 @@ impl<'de> Deserialize<'de> for UsageQuotaResponseAttributes { org_public_id = Some(serde_json::from_value(v).map_err(M::Error::custom)?); } + "pending_effective_from" => { + if v.is_null() { + continue; + } + pending_effective_from = + Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "pending_usage_limit" => { + if v.is_null() || v.as_str() == Some("") { + continue; + } + pending_usage_limit = + Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } "scope" => { if v.is_null() { continue; @@ -122,6 +158,8 @@ impl<'de> Deserialize<'de> for UsageQuotaResponseAttributes { let content = UsageQuotaResponseAttributes { enforced, org_public_id, + pending_effective_from, + pending_usage_limit, scope, usage_limit, additional_properties, diff --git a/src/datadogV2/model/model_usage_quota_update_attributes.rs b/src/datadogV2/model/model_usage_quota_update_attributes.rs index 587dea2e0d..12be81e6f8 100644 --- a/src/datadogV2/model/model_usage_quota_update_attributes.rs +++ b/src/datadogV2/model/model_usage_quota_update_attributes.rs @@ -6,7 +6,7 @@ use serde::{Deserialize, Deserializer, Serialize}; use serde_with::skip_serializing_none; use std::fmt::{self, Formatter}; -/// Attributes to update on a usage quota. Omitting a property leaves its current value unchanged. +/// Attributes to update on a usage quota. At least one of `usage_limit`, `enforced`, or `pending_usage_limit` must be provided. Omitting a property leaves its current value unchanged. #[non_exhaustive] #[skip_serializing_none] #[derive(Clone, Debug, PartialEq, Serialize)] @@ -18,6 +18,13 @@ pub struct UsageQuotaUpdateAttributes { with = "::serde_with::rust::double_option" )] pub enforced: Option>, + /// The non-negative, whole-number limit to schedule for the organization-wide quota in the usage units defined by the quota namespace. It is not checked against current usage. Each write schedules the value for 00:00 UTC on the first day of the next calendar month and replaces any previously scheduled change; the server computes `pending_effective_from`. Omit this field to leave any scheduled change unchanged, including when raising `usage_limit`; use `DELETE /api/v2/usage/quotas/{quota_namespace}/{id}/pending` to cancel one. + #[serde( + rename = "pending_usage_limit", + default, + with = "::serde_with::rust::double_option" + )] + pub pending_usage_limit: Option>, /// The new quota limit in the usage units defined by the quota namespace. For an organization-wide quota (empty scope), the limit must be greater than the usage already recorded in the current period. Omit this field to leave the current limit unchanged. #[serde( rename = "usage_limit", @@ -36,6 +43,7 @@ impl UsageQuotaUpdateAttributes { pub fn new() -> UsageQuotaUpdateAttributes { UsageQuotaUpdateAttributes { enforced: None, + pending_usage_limit: None, usage_limit: None, additional_properties: std::collections::BTreeMap::new(), _unparsed: false, @@ -47,6 +55,11 @@ impl UsageQuotaUpdateAttributes { self } + pub fn pending_usage_limit(mut self, value: Option) -> Self { + self.pending_usage_limit = Some(value); + self + } + pub fn usage_limit(mut self, value: Option) -> Self { self.usage_limit = Some(value); self @@ -85,6 +98,7 @@ impl<'de> Deserialize<'de> for UsageQuotaUpdateAttributes { M: MapAccess<'a>, { let mut enforced: Option> = None; + let mut pending_usage_limit: Option> = None; let mut usage_limit: Option> = None; let mut additional_properties: std::collections::BTreeMap< String, @@ -97,6 +111,10 @@ impl<'de> Deserialize<'de> for UsageQuotaUpdateAttributes { "enforced" => { enforced = Some(serde_json::from_value(v).map_err(M::Error::custom)?); } + "pending_usage_limit" => { + pending_usage_limit = + Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } "usage_limit" => { usage_limit = Some(serde_json::from_value(v).map_err(M::Error::custom)?); @@ -111,6 +129,7 @@ impl<'de> Deserialize<'de> for UsageQuotaUpdateAttributes { let content = UsageQuotaUpdateAttributes { enforced, + pending_usage_limit, usage_limit, additional_properties, _unparsed, diff --git a/src/datadogV2/model/model_usage_quota_update_data.rs b/src/datadogV2/model/model_usage_quota_update_data.rs index 52064bb60f..62268bd346 100644 --- a/src/datadogV2/model/model_usage_quota_update_data.rs +++ b/src/datadogV2/model/model_usage_quota_update_data.rs @@ -11,7 +11,7 @@ use std::fmt::{self, Formatter}; #[skip_serializing_none] #[derive(Clone, Debug, PartialEq, Serialize)] pub struct UsageQuotaUpdateData { - /// Attributes to update on a usage quota. Omitting a property leaves its current value unchanged. + /// Attributes to update on a usage quota. At least one of `usage_limit`, `enforced`, or `pending_usage_limit` must be provided. Omitting a property leaves its current value unchanged. #[serde(rename = "attributes")] pub attributes: crate::datadogV2::model::UsageQuotaUpdateAttributes, /// The opaque usage quota identifier, which must match the identifier in the request path. diff --git a/tests/scenarios/features/v2/undo.json b/tests/scenarios/features/v2/undo.json index 8b44c94157..47f57825d5 100644 --- a/tests/scenarios/features/v2/undo.json +++ b/tests/scenarios/features/v2/undo.json @@ -11046,6 +11046,12 @@ "type": "idempotent" } }, + "DeletePendingQuota": { + "tag": "Usage Metering", + "undo": { + "type": "idempotent" + } + }, "GetUsageSummaryAvailableFields": { "tag": "Usage Metering", "undo": { diff --git a/tests/scenarios/features/v2/usage_metering.feature b/tests/scenarios/features/v2/usage_metering.feature index cdab1476ab..9fd783ecd6 100644 --- a/tests/scenarios/features/v2/usage_metering.feature +++ b/tests/scenarios/features/v2/usage_metering.feature @@ -15,20 +15,47 @@ Feature: Usage Metering And an instance of "UsageMetering" API @generated @skip @team:ddoghq/billing-hub - Scenario: Create or update usage quotas returns "Bad Request" response + Scenario: Cancel a scheduled usage quota limit returns "Bad Request. Returned if the `id` is malformed or does not belong to the requested quota namespace." response + Given operation "DeletePendingQuota" enabled + And new "DeletePendingQuota" request + And request contains "quota_namespace" parameter from "REPLACE.ME" + And request contains "id" parameter from "REPLACE.ME" + When the request is sent + Then the response status is 400 Bad Request. Returned if the `id` is malformed or does not belong to the requested quota namespace. + + @generated @skip @team:ddoghq/billing-hub + Scenario: Cancel a scheduled usage quota limit returns "No Content" response + Given operation "DeletePendingQuota" enabled + And new "DeletePendingQuota" request + And request contains "quota_namespace" parameter from "REPLACE.ME" + And request contains "id" parameter from "REPLACE.ME" + When the request is sent + Then the response status is 204 No Content + + @generated @skip @team:ddoghq/billing-hub + Scenario: Cancel a scheduled usage quota limit returns "Not Found" response + Given operation "DeletePendingQuota" enabled + And new "DeletePendingQuota" request + And request contains "quota_namespace" parameter from "REPLACE.ME" + And request contains "id" parameter from "REPLACE.ME" + When the request is sent + Then the response status is 404 Not Found + + @generated @skip @team:ddoghq/billing-hub + Scenario: Create or update usage quotas returns "Bad Request. Returned if the request is malformed, including when an item sets `pending_usage_limit` on a scoped quota, provides `enforced` without `usage_limit`, or provides neither limit. A pending-only item for a quota that does not exist is reported in that item's `error`; when only the caller's organization is targeted, this failure returns `400`." response Given operation "CreateQuotas" enabled And new "CreateQuotas" request And request contains "quota_namespace" parameter from "REPLACE.ME" - And body with value {"data": [{"attributes": {"enforced": true, "scope": {"user_handle": "jane@example.com"}, "usage_limit": 100000}, "type": "quotas"}]} + And body with value {"data": [{"attributes": {"enforced": true, "pending_usage_limit": 100000, "usage_limit": 600000}, "type": "quotas"}]} When the request is sent - Then the response status is 400 Bad Request + Then the response status is 400 Bad Request. Returned if the request is malformed, including when an item sets `pending_usage_limit` on a scoped quota, provides `enforced` without `usage_limit`, or provides neither limit. A pending-only item for a quota that does not exist is reported in that item's `error`; when only the caller's organization is targeted, this failure returns `400`. @generated @skip @team:ddoghq/billing-hub Scenario: Create or update usage quotas returns "OK. The response includes each item's result; see each item's `error` attribute for any that failed to write." response Given operation "CreateQuotas" enabled And new "CreateQuotas" request And request contains "quota_namespace" parameter from "REPLACE.ME" - And body with value {"data": [{"attributes": {"enforced": true, "scope": {"user_handle": "jane@example.com"}, "usage_limit": 100000}, "type": "quotas"}]} + And body with value {"data": [{"attributes": {"enforced": true, "pending_usage_limit": 100000, "usage_limit": 600000}, "type": "quotas"}]} When the request is sent Then the response status is 200 OK. The response includes each item's result; see each item's `error` attribute for any that failed to write. @@ -304,14 +331,14 @@ Feature: Usage Metering Then the response status is 200 OK @generated @skip @team:ddoghq/billing-hub - Scenario: Update a usage quota returns "Bad Request. Returned if the request is malformed, or if the `id` in the request body does not match the `id` in the request path." response + Scenario: Update a usage quota returns "Bad Request. Returned if the request is malformed, if the `id` in the request body does not match the `id` in the request path, if none of `usage_limit`, `enforced`, or `pending_usage_limit` is provided, or if `pending_usage_limit` is set on a scoped quota." response Given operation "UpdateQuota" enabled And new "UpdateQuota" request And request contains "quota_namespace" parameter from "REPLACE.ME" And request contains "id" parameter from "REPLACE.ME" - And body with value {"data": {"attributes": {"enforced": false, "usage_limit": 120000}, "id": "MjAfYWlfY3JlZGl0c1911c2VyX2hhbmRsZTpfX0FMTF9f", "type": "quotas"}} + And body with value {"data": {"attributes": {"enforced": false, "pending_usage_limit": 50000, "usage_limit": 120000}, "id": "MTIzNB9haV9jcmVkaXRzH3VzZXJfaGFuZGxlOl9fQUxMX18", "type": "quotas"}} When the request is sent - Then the response status is 400 Bad Request. Returned if the request is malformed, or if the `id` in the request body does not match the `id` in the request path. + Then the response status is 400 Bad Request. Returned if the request is malformed, if the `id` in the request body does not match the `id` in the request path, if none of `usage_limit`, `enforced`, or `pending_usage_limit` is provided, or if `pending_usage_limit` is set on a scoped quota. @generated @skip @team:ddoghq/billing-hub Scenario: Update a usage quota returns "Not Found" response @@ -319,7 +346,7 @@ Feature: Usage Metering And new "UpdateQuota" request And request contains "quota_namespace" parameter from "REPLACE.ME" And request contains "id" parameter from "REPLACE.ME" - And body with value {"data": {"attributes": {"enforced": false, "usage_limit": 120000}, "id": "MjAfYWlfY3JlZGl0c1911c2VyX2hhbmRsZTpfX0FMTF9f", "type": "quotas"}} + And body with value {"data": {"attributes": {"enforced": false, "pending_usage_limit": 50000, "usage_limit": 120000}, "id": "MTIzNB9haV9jcmVkaXRzH3VzZXJfaGFuZGxlOl9fQUxMX18", "type": "quotas"}} When the request is sent Then the response status is 404 Not Found @@ -329,6 +356,6 @@ Feature: Usage Metering And new "UpdateQuota" request And request contains "quota_namespace" parameter from "REPLACE.ME" And request contains "id" parameter from "REPLACE.ME" - And body with value {"data": {"attributes": {"enforced": false, "usage_limit": 120000}, "id": "MjAfYWlfY3JlZGl0c1911c2VyX2hhbmRsZTpfX0FMTF9f", "type": "quotas"}} + And body with value {"data": {"attributes": {"enforced": false, "pending_usage_limit": 50000, "usage_limit": 120000}, "id": "MTIzNB9haV9jcmVkaXRzH3VzZXJfaGFuZGxlOl9fQUxMX18", "type": "quotas"}} When the request is sent Then the response status is 200 OK diff --git a/tests/scenarios/function_mappings.rs b/tests/scenarios/function_mappings.rs index d082bed83a..3941e79a58 100644 --- a/tests/scenarios/function_mappings.rs +++ b/tests/scenarios/function_mappings.rs @@ -4857,6 +4857,9 @@ pub fn collect_function_calls(world: &mut DatadogWorld) { world .function_mappings .insert("v2.UpdateQuota".into(), test_v2_update_quota); + world + .function_mappings + .insert("v2.DeletePendingQuota".into(), test_v2_delete_pending_quota); world.function_mappings.insert( "v2.GetUsageSummaryAvailableFields".into(), test_v2_get_usage_summary_available_fields, @@ -36986,6 +36989,33 @@ fn test_v2_update_quota(world: &mut DatadogWorld, _parameters: &HashMap) { + let api = world + .api_instances + .v2_api_usage_metering + .as_ref() + .expect("api instance not found"); + let quota_namespace = + serde_json::from_value(_parameters.get("quota_namespace").unwrap().clone()).unwrap(); + let id = serde_json::from_value(_parameters.get("id").unwrap().clone()).unwrap(); + let response = match block_on(api.delete_pending_quota_with_http_info(quota_namespace, id)) { + 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_usage_summary_available_fields( world: &mut DatadogWorld, _parameters: &HashMap,