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.py b/examples/v2/usage-metering/CreateQuotas.py index 79f940aab3..6ab7786181 100644 --- a/examples/v2/usage-metering/CreateQuotas.py +++ b/examples/v2/usage-metering/CreateQuotas.py @@ -8,7 +8,6 @@ from datadog_api_client.v2.api.usage_metering_api import UsageMeteringApi from datadog_api_client.v2.model.usage_quota_create_attributes import UsageQuotaCreateAttributes from datadog_api_client.v2.model.usage_quota_create_data import UsageQuotaCreateData -from datadog_api_client.v2.model.usage_quota_request_scope import UsageQuotaRequestScope from datadog_api_client.v2.model.usage_quota_type import UsageQuotaType from datadog_api_client.v2.model.usage_quotas_create_request import UsageQuotasCreateRequest @@ -17,10 +16,8 @@ UsageQuotaCreateData( attributes=UsageQuotaCreateAttributes( enforced=True, - scope=UsageQuotaRequestScope( - user_handle="jane@example.com", - ), - usage_limit=100000, + pending_usage_limit=100000, + usage_limit=600000, ), type=UsageQuotaType.QUOTAS, ), diff --git a/examples/v2/usage-metering/DeletePendingQuota.py b/examples/v2/usage-metering/DeletePendingQuota.py new file mode 100644 index 0000000000..35787147da --- /dev/null +++ b/examples/v2/usage-metering/DeletePendingQuota.py @@ -0,0 +1,17 @@ +""" +Cancel a scheduled usage quota limit returns "No Content" response +""" + +from os import environ +from datadog_api_client import ApiClient, Configuration +from datadog_api_client.v2.api.usage_metering_api import UsageMeteringApi + +configuration = Configuration() +configuration.access_token = environ["DD_BEARER_TOKEN"] +configuration.unstable_operations["delete_pending_quota"] = True +with ApiClient(configuration) as api_client: + api_instance = UsageMeteringApi(api_client) + api_instance.delete_pending_quota( + quota_namespace="ai_credits", + id="MTIzNB9haV9jcmVkaXRzH3VzZXJfaGFuZGxlOl9fQUxMX18", + ) diff --git a/examples/v2/usage-metering/DeleteQuota.py b/examples/v2/usage-metering/DeleteQuota.py index 62e6930b86..6cc80c5f6e 100644 --- a/examples/v2/usage-metering/DeleteQuota.py +++ b/examples/v2/usage-metering/DeleteQuota.py @@ -13,5 +13,5 @@ api_instance = UsageMeteringApi(api_client) api_instance.delete_quota( quota_namespace="ai_credits", - id="MjAfYWlfY3JlZGl0c1911c2VyX2hhbmRsZTpfX0FMTF9f", + id="MTIzNB9haV9jcmVkaXRzH3VzZXJfaGFuZGxlOl9fQUxMX18", ) diff --git a/examples/v2/usage-metering/UpdateQuota.py b/examples/v2/usage-metering/UpdateQuota.py index 9bd3e2efc5..0537f116d9 100644 --- a/examples/v2/usage-metering/UpdateQuota.py +++ b/examples/v2/usage-metering/UpdateQuota.py @@ -14,9 +14,10 @@ data=UsageQuotaUpdateData( attributes=UsageQuotaUpdateAttributes( enforced=False, + pending_usage_limit=50000, usage_limit=120000, ), - id="MjAfYWlfY3JlZGl0c1911c2VyX2hhbmRsZTpfX0FMTF9f", + id="MTIzNB9haV9jcmVkaXRzH3VzZXJfaGFuZGxlOl9fQUxMX18", type=UsageQuotaType.QUOTAS, ), ) @@ -27,7 +28,7 @@ with ApiClient(configuration) as api_client: api_instance = UsageMeteringApi(api_client) response = api_instance.update_quota( - quota_namespace="ai_credits", id="MjAfYWlfY3JlZGl0c1911c2VyX2hhbmRsZTpfX0FMTF9f", body=body + quota_namespace="ai_credits", id="MTIzNB9haV9jcmVkaXRzH3VzZXJfaGFuZGxlOl9fQUxMX18", body=body ) print(response) diff --git a/src/datadog_api_client/configuration.py b/src/datadog_api_client/configuration.py index 821497938d..f40f18b21a 100644 --- a/src/datadog_api_client/configuration.py +++ b/src/datadog_api_client/configuration.py @@ -494,6 +494,7 @@ def __init__( "v2.search_cost_recommendations": False, "v2.update_unit_cost": False, "v2.create_quotas": False, + "v2.delete_pending_quota": False, "v2.delete_quota": False, "v2.list_quotas": False, "v2.update_quota": False, diff --git a/src/datadog_api_client/v2/api/usage_metering_api.py b/src/datadog_api_client/v2/api/usage_metering_api.py index bfae927bb5..2fc21f8f08 100644 --- a/src/datadog_api_client/v2/api/usage_metering_api.py +++ b/src/datadog_api_client/v2/api/usage_metering_api.py @@ -89,6 +89,35 @@ def __init__(self, api_client=None): api_client=api_client, ) + self._delete_pending_quota_endpoint = _Endpoint( + settings={ + "response_type": None, + "auth": ["apiKeyAuth", "appKeyAuth", "AuthZ"], + "endpoint_path": "/api/v2/usage/quotas/{quota_namespace}/{id}/pending", + "operation_id": "delete_pending_quota", + "http_method": "DELETE", + "version": "v2", + }, + params_map={ + "quota_namespace": { + "required": True, + "openapi_types": (str,), + "attribute": "quota_namespace", + "location": "path", + }, + "id": { + "required": True, + "openapi_types": (str,), + "attribute": "id", + "location": "path", + }, + }, + headers_map={ + "accept": ["*/*"], + }, + api_client=api_client, + ) + self._delete_quota_endpoint = _Endpoint( settings={ "response_type": None, @@ -632,7 +661,11 @@ def create_quotas( ) -> UsageQuotasBulkResponse: """Create or update usage quotas. - 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. :param quota_namespace: The product-specific namespace whose usage quotas are being managed. :type quota_namespace: str @@ -652,6 +685,28 @@ def create_quotas( return self._create_quotas_endpoint.call_with_http_info(**kwargs) + def delete_pending_quota( + self, + quota_namespace: str, + id: str, + ) -> None: + """Cancel a scheduled usage quota limit. + + 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. + + :param quota_namespace: The product-specific namespace whose usage quotas are being managed. + :type quota_namespace: str + :param id: The opaque quota identifier returned by a previous list or create request. Clients must pass this value verbatim. + :type id: str + :rtype: None + """ + kwargs: Dict[str, Any] = {} + kwargs["quota_namespace"] = quota_namespace + + kwargs["id"] = id + + return self._delete_pending_quota_endpoint.call_with_http_info(**kwargs) + def delete_quota( self, quota_namespace: str, @@ -1266,7 +1321,7 @@ def update_quota( ) -> UsageQuotaResponse: """Update a usage quota. - 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. :param quota_namespace: The product-specific namespace whose usage quotas are being managed. :type quota_namespace: str diff --git a/src/datadog_api_client/v2/model/usage_quota_bulk_result_attributes.py b/src/datadog_api_client/v2/model/usage_quota_bulk_result_attributes.py index 9f2d7edaf2..8808447a22 100644 --- a/src/datadog_api_client/v2/model/usage_quota_bulk_result_attributes.py +++ b/src/datadog_api_client/v2/model/usage_quota_bulk_result_attributes.py @@ -18,6 +18,10 @@ class UsageQuotaBulkResultAttributes(ModelNormal): + validations = { + "pending_effective_from": {}, + } + @cached_property def openapi_types(_): from datadog_api_client.v2.model.usage_quota_response_scope import UsageQuotaResponseScope @@ -26,6 +30,8 @@ def openapi_types(_): "enforced": (bool,), "error": (str,), "org_public_id": (str,), + "pending_effective_from": (str,), + "pending_usage_limit": (float,), "scope": (UsageQuotaResponseScope,), "usage_limit": (float,), } @@ -34,6 +40,8 @@ def openapi_types(_): "enforced": "enforced", "error": "error", "org_public_id": "org_public_id", + "pending_effective_from": "pending_effective_from", + "pending_usage_limit": "pending_usage_limit", "scope": "scope", "usage_limit": "usage_limit", } @@ -43,12 +51,14 @@ def __init__( enforced: Union[bool, UnsetType] = unset, error: Union[str, UnsetType] = unset, org_public_id: Union[str, UnsetType] = unset, + pending_effective_from: Union[str, UnsetType] = unset, + pending_usage_limit: Union[float, UnsetType] = unset, scope: Union[UsageQuotaResponseScope, UnsetType] = unset, usage_limit: Union[float, UnsetType] = unset, **kwargs, ): """ - 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. :param enforced: Whether usage above the limit is actively blocked instead of only tracked or alerted on. Omitted if this item failed to write. :type enforced: bool, optional @@ -59,6 +69,12 @@ def __init__( :param org_public_id: The public ID of the organization that owns the quota. Omitted if this item failed to write. :type org_public_id: str, optional + :param pending_effective_from: 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. + :type pending_effective_from: str, optional + + :param pending_usage_limit: 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. + :type pending_usage_limit: float, optional + :param scope: 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. :type scope: UsageQuotaResponseScope, optional @@ -71,6 +87,10 @@ def __init__( kwargs["error"] = error if org_public_id is not unset: kwargs["org_public_id"] = org_public_id + if pending_effective_from is not unset: + kwargs["pending_effective_from"] = pending_effective_from + if pending_usage_limit is not unset: + kwargs["pending_usage_limit"] = pending_usage_limit if scope is not unset: kwargs["scope"] = scope if usage_limit is not unset: diff --git a/src/datadog_api_client/v2/model/usage_quota_bulk_result_data.py b/src/datadog_api_client/v2/model/usage_quota_bulk_result_data.py index bf54ecbd2a..78ef618ae8 100644 --- a/src/datadog_api_client/v2/model/usage_quota_bulk_result_data.py +++ b/src/datadog_api_client/v2/model/usage_quota_bulk_result_data.py @@ -38,7 +38,7 @@ def __init__(self_, attributes: UsageQuotaBulkResultAttributes, id: str, type: U """ The result of writing one usage quota in a bulk create-or-update request. - :param attributes: 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. + :param attributes: 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. :type attributes: UsageQuotaBulkResultAttributes :param id: 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/datadog_api_client/v2/model/usage_quota_create_attributes.py b/src/datadog_api_client/v2/model/usage_quota_create_attributes.py index 7bfd179e0d..6c97763b3e 100644 --- a/src/datadog_api_client/v2/model/usage_quota_create_attributes.py +++ b/src/datadog_api_client/v2/model/usage_quota_create_attributes.py @@ -19,6 +19,9 @@ class UsageQuotaCreateAttributes(ModelNormal): validations = { + "pending_usage_limit": { + "inclusive_minimum": 0, + }, "usage_limit": { "inclusive_minimum": 0, }, @@ -30,34 +33,47 @@ def openapi_types(_): return { "enforced": (bool,), + "pending_usage_limit": (int,), "scope": (UsageQuotaRequestScope,), "usage_limit": (int,), } attribute_map = { "enforced": "enforced", + "pending_usage_limit": "pending_usage_limit", "scope": "scope", "usage_limit": "usage_limit", } def __init__( - self_, enforced: bool, usage_limit: int, scope: Union[UsageQuotaRequestScope, UnsetType] = unset, **kwargs + self_, + enforced: Union[bool, UnsetType] = unset, + pending_usage_limit: Union[int, UnsetType] = unset, + scope: Union[UsageQuotaRequestScope, UnsetType] = unset, + usage_limit: Union[int, UnsetType] = unset, + **kwargs, ): """ - 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. - :param enforced: Whether to actively block usage above the limit instead of only tracking or alerting on it. - :type enforced: bool + :param enforced: 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. + :type enforced: bool, optional + + :param pending_usage_limit: 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. + :type pending_usage_limit: int, optional :param scope: 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. :type scope: UsageQuotaRequestScope, optional - :param usage_limit: 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. - :type usage_limit: int + :param usage_limit: 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. + :type usage_limit: int, optional """ + if enforced is not unset: + kwargs["enforced"] = enforced + if pending_usage_limit is not unset: + kwargs["pending_usage_limit"] = pending_usage_limit if scope is not unset: kwargs["scope"] = scope + if usage_limit is not unset: + kwargs["usage_limit"] = usage_limit super().__init__(kwargs) - - self_.enforced = enforced - self_.usage_limit = usage_limit diff --git a/src/datadog_api_client/v2/model/usage_quota_create_data.py b/src/datadog_api_client/v2/model/usage_quota_create_data.py index 702e30e437..c6a2e40cba 100644 --- a/src/datadog_api_client/v2/model/usage_quota_create_data.py +++ b/src/datadog_api_client/v2/model/usage_quota_create_data.py @@ -36,7 +36,7 @@ def __init__(self_, attributes: UsageQuotaCreateAttributes, type: UsageQuotaType """ A usage quota resource to create or update by scope. - :param attributes: Attributes for creating or updating a usage quota by scope. + :param attributes: 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. :type attributes: UsageQuotaCreateAttributes :param type: The JSON:API resource type for a usage quota. diff --git a/src/datadog_api_client/v2/model/usage_quota_response_attributes.py b/src/datadog_api_client/v2/model/usage_quota_response_attributes.py index 1092391027..bc266d222f 100644 --- a/src/datadog_api_client/v2/model/usage_quota_response_attributes.py +++ b/src/datadog_api_client/v2/model/usage_quota_response_attributes.py @@ -18,6 +18,10 @@ class UsageQuotaResponseAttributes(ModelNormal): + validations = { + "pending_effective_from": {}, + } + @cached_property def openapi_types(_): from datadog_api_client.v2.model.usage_quota_response_scope import UsageQuotaResponseScope @@ -25,6 +29,8 @@ def openapi_types(_): return { "enforced": (bool,), "org_public_id": (str,), + "pending_effective_from": (str,), + "pending_usage_limit": (float,), "scope": (UsageQuotaResponseScope,), "usage_limit": (float,), } @@ -32,6 +38,8 @@ def openapi_types(_): attribute_map = { "enforced": "enforced", "org_public_id": "org_public_id", + "pending_effective_from": "pending_effective_from", + "pending_usage_limit": "pending_usage_limit", "scope": "scope", "usage_limit": "usage_limit", } @@ -41,6 +49,8 @@ def __init__( enforced: bool, org_public_id: str, usage_limit: float, + pending_effective_from: Union[str, UnsetType] = unset, + pending_usage_limit: Union[float, UnsetType] = unset, scope: Union[UsageQuotaResponseScope, UnsetType] = unset, **kwargs, ): @@ -53,12 +63,24 @@ def __init__( :param org_public_id: The public ID of the organization that owns the quota. :type org_public_id: str + :param pending_effective_from: 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. + :type pending_effective_from: str, optional + + :param pending_usage_limit: 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. + :type pending_usage_limit: float, optional + :param scope: 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. :type scope: UsageQuotaResponseScope, optional :param usage_limit: The quota limit in the usage units defined by the quota namespace. May be fractional for quotas configured before public writes required whole units. :type usage_limit: float """ + if pending_effective_from is not unset: + kwargs["pending_effective_from"] = pending_effective_from + if pending_usage_limit is not unset: + kwargs["pending_usage_limit"] = pending_usage_limit if scope is not unset: kwargs["scope"] = scope super().__init__(kwargs) diff --git a/src/datadog_api_client/v2/model/usage_quota_update_attributes.py b/src/datadog_api_client/v2/model/usage_quota_update_attributes.py index 59b2bfb073..40265cae21 100644 --- a/src/datadog_api_client/v2/model/usage_quota_update_attributes.py +++ b/src/datadog_api_client/v2/model/usage_quota_update_attributes.py @@ -16,6 +16,9 @@ class UsageQuotaUpdateAttributes(ModelNormal): validations = { + "pending_usage_limit": { + "inclusive_minimum": 0, + }, "usage_limit": { "inclusive_minimum": 0, }, @@ -25,31 +28,39 @@ class UsageQuotaUpdateAttributes(ModelNormal): def openapi_types(_): return { "enforced": (bool, none_type), + "pending_usage_limit": (int, none_type), "usage_limit": (int, none_type), } attribute_map = { "enforced": "enforced", + "pending_usage_limit": "pending_usage_limit", "usage_limit": "usage_limit", } def __init__( self_, enforced: Union[bool, none_type, UnsetType] = unset, + pending_usage_limit: Union[int, none_type, UnsetType] = unset, usage_limit: Union[int, none_type, UnsetType] = unset, **kwargs, ): """ - 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. :param enforced: Whether to actively block usage above the limit. Omit this field to leave the current enforcement setting unchanged. :type enforced: bool, none_type, optional + :param pending_usage_limit: 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. + :type pending_usage_limit: int, none_type, optional + :param usage_limit: 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. :type usage_limit: int, none_type, optional """ if enforced is not unset: kwargs["enforced"] = enforced + if pending_usage_limit is not unset: + kwargs["pending_usage_limit"] = pending_usage_limit if usage_limit is not unset: kwargs["usage_limit"] = usage_limit super().__init__(kwargs) diff --git a/src/datadog_api_client/v2/model/usage_quota_update_data.py b/src/datadog_api_client/v2/model/usage_quota_update_data.py index d3e397cd34..9689e2a69b 100644 --- a/src/datadog_api_client/v2/model/usage_quota_update_data.py +++ b/src/datadog_api_client/v2/model/usage_quota_update_data.py @@ -38,7 +38,7 @@ def __init__(self_, attributes: UsageQuotaUpdateAttributes, id: str, type: Usage """ A usage quota resource to update. - :param attributes: Attributes to update on a usage quota. Omitting a property leaves its current value unchanged. + :param attributes: 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. :type attributes: UsageQuotaUpdateAttributes :param id: The opaque usage quota identifier, which must match the identifier in the request path. diff --git a/tests/v2/features/undo.json b/tests/v2/features/undo.json index 8b44c94157..47f57825d5 100644 --- a/tests/v2/features/undo.json +++ b/tests/v2/features/undo.json @@ -11046,6 +11046,12 @@ "type": "idempotent" } }, + "DeletePendingQuota": { + "tag": "Usage Metering", + "undo": { + "type": "idempotent" + } + }, "GetUsageSummaryAvailableFields": { "tag": "Usage Metering", "undo": { diff --git a/tests/v2/features/usage_metering.feature b/tests/v2/features/usage_metering.feature index cdab1476ab..9fd783ecd6 100644 --- a/tests/v2/features/usage_metering.feature +++ b/tests/v2/features/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