diff --git a/.generator/schemas/v2/openapi.yaml b/.generator/schemas/v2/openapi.yaml index 8e540e2cff55..6b96392bdd5c 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.ts b/examples/v2/usage-metering/CreateQuotas.ts index e50f39d9fa2f..41b8ddef74ea 100644 --- a/examples/v2/usage-metering/CreateQuotas.ts +++ b/examples/v2/usage-metering/CreateQuotas.ts @@ -21,10 +21,8 @@ const params: v2.UsageMeteringApiCreateQuotasRequest = { { attributes: { enforced: true, - scope: { - user_handle: "jane@example.com", - }, - usageLimit: 100000, + pendingUsageLimit: 100000, + usageLimit: 600000, }, type: "quotas", }, diff --git a/examples/v2/usage-metering/DeletePendingQuota.ts b/examples/v2/usage-metering/DeletePendingQuota.ts new file mode 100644 index 000000000000..83a3e905b5cc --- /dev/null +++ b/examples/v2/usage-metering/DeletePendingQuota.ts @@ -0,0 +1,29 @@ +/** + * Cancel a scheduled usage quota limit returns "No Content" response + */ + +import { client, v2 } from "@datadog/datadog-api-client"; + +const configuration = client.createConfiguration({ + authMethods: { + AuthZ: { + accessToken: process.env.DD_BEARER_TOKEN as string, + }, + }, +}); +configuration.unstableOperations["v2.deletePendingQuota"] = true; +const apiInstance = new v2.UsageMeteringApi(configuration); + +const params: v2.UsageMeteringApiDeletePendingQuotaRequest = { + quotaNamespace: "ai_credits", + id: "MTIzNB9haV9jcmVkaXRzH3VzZXJfaGFuZGxlOl9fQUxMX18", +}; + +apiInstance + .deletePendingQuota(params) + .then((data: any) => { + console.log( + "API called successfully. Returned data: " + JSON.stringify(data) + ); + }) + .catch((error: any) => console.error(error)); diff --git a/examples/v2/usage-metering/DeleteQuota.ts b/examples/v2/usage-metering/DeleteQuota.ts index 6630a0ef5bf5..807c7f63eda3 100644 --- a/examples/v2/usage-metering/DeleteQuota.ts +++ b/examples/v2/usage-metering/DeleteQuota.ts @@ -16,7 +16,7 @@ const apiInstance = new v2.UsageMeteringApi(configuration); const params: v2.UsageMeteringApiDeleteQuotaRequest = { quotaNamespace: "ai_credits", - id: "MjAfYWlfY3JlZGl0c1911c2VyX2hhbmRsZTpfX0FMTF9f", + id: "MTIzNB9haV9jcmVkaXRzH3VzZXJfaGFuZGxlOl9fQUxMX18", }; apiInstance diff --git a/examples/v2/usage-metering/UpdateQuota.ts b/examples/v2/usage-metering/UpdateQuota.ts index d9aeda53ec9d..1ef9702218d2 100644 --- a/examples/v2/usage-metering/UpdateQuota.ts +++ b/examples/v2/usage-metering/UpdateQuota.ts @@ -19,14 +19,15 @@ const params: v2.UsageMeteringApiUpdateQuotaRequest = { data: { attributes: { enforced: false, + pendingUsageLimit: 50000, usageLimit: 120000, }, - id: "MjAfYWlfY3JlZGl0c1911c2VyX2hhbmRsZTpfX0FMTF9f", + id: "MTIzNB9haV9jcmVkaXRzH3VzZXJfaGFuZGxlOl9fQUxMX18", type: "quotas", }, }, quotaNamespace: "ai_credits", - id: "MjAfYWlfY3JlZGl0c1911c2VyX2hhbmRsZTpfX0FMTF9f", + id: "MTIzNB9haV9jcmVkaXRzH3VzZXJfaGFuZGxlOl9fQUxMX18", }; apiInstance diff --git a/features/support/scenarios_model_mapping.ts b/features/support/scenarios_model_mapping.ts index bdb8640d989e..2efc82ec2ad7 100644 --- a/features/support/scenarios_model_mapping.ts +++ b/features/support/scenarios_model_mapping.ts @@ -9125,6 +9125,17 @@ export const ScenariosModelMappings: {[key: string]: {[key: string]: any}} = { }, "operationResponseType": "UsageQuotaResponse", }, + "v2.DeletePendingQuota": { + "quotaNamespace": { + "type": "string", + "format": "", + }, + "id": { + "type": "string", + "format": "", + }, + "operationResponseType": "{}", + }, "v2.GetUsageSummaryAvailableFields": { "operationResponseType": "UsageSummaryAvailableFieldsResponse", }, diff --git a/features/v2/undo.json b/features/v2/undo.json index 8b44c9415758..47f57825d51c 100644 --- a/features/v2/undo.json +++ b/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/features/v2/usage_metering.feature b/features/v2/usage_metering.feature index cdab1476abf5..9fd783ecd662 100644 --- a/features/v2/usage_metering.feature +++ b/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/packages/datadog-api-client-common/configuration.ts b/packages/datadog-api-client-common/configuration.ts index 4106cba489f9..729138178c5b 100644 --- a/packages/datadog-api-client-common/configuration.ts +++ b/packages/datadog-api-client-common/configuration.ts @@ -468,6 +468,7 @@ export function createConfiguration( "v2.searchCostRecommendations": false, "v2.updateUnitCost": false, "v2.createQuotas": false, + "v2.deletePendingQuota": false, "v2.deleteQuota": false, "v2.listQuotas": false, "v2.updateQuota": false, diff --git a/packages/datadog-api-client-v2/apis/UsageMeteringApi.ts b/packages/datadog-api-client-v2/apis/UsageMeteringApi.ts index 75cc1ee02ed0..3b3aa8721ec7 100644 --- a/packages/datadog-api-client-v2/apis/UsageMeteringApi.ts +++ b/packages/datadog-api-client-v2/apis/UsageMeteringApi.ts @@ -110,6 +110,55 @@ export class UsageMeteringApiRequestFactory extends BaseAPIRequestFactory { return requestContext; } + public async deletePendingQuota( + quotaNamespace: string, + id: string, + _options?: Configuration + ): Promise { + const _config = _options || this.configuration; + + logger.warn("Using unstable operation 'deletePendingQuota'"); + if (!_config.unstableOperations["v2.deletePendingQuota"]) { + throw new Error("Unstable operation 'deletePendingQuota' is disabled"); + } + + // verify required parameter 'quotaNamespace' is not null or undefined + if (quotaNamespace === null || quotaNamespace === undefined) { + throw new RequiredError("quotaNamespace", "deletePendingQuota"); + } + + // verify required parameter 'id' is not null or undefined + if (id === null || id === undefined) { + throw new RequiredError("id", "deletePendingQuota"); + } + + // Path Params + const localVarPath = "/api/v2/usage/quotas/{quota_namespace}/{id}/pending" + .replace("{quota_namespace}", encodeURIComponent(String(quotaNamespace))) + .replace("{id}", encodeURIComponent(String(id))); + + // Make Request Context + const requestContext = _config + .getServer("v2.UsageMeteringApi.deletePendingQuota") + .makeRequestContext(localVarPath, HttpMethod.DELETE); + requestContext.setHeaderParam("Accept", "*/*"); + requestContext.setHttpConfig(_config.httpConfig); + + // Set IaC header + if (_config.isIaC) { + requestContext.setHeaderParam("X-Datadog-Managed-By", "iac"); + } + + // Apply auth methods + applySecurityAuthentication(_config, requestContext, [ + "apiKeyAuth", + "appKeyAuth", + "AuthZ", + ]); + + return requestContext; + } + public async deleteQuota( quotaNamespace: string, id: string, @@ -1203,6 +1252,80 @@ export class UsageMeteringApiResponseProcessor { ); } + /** + * Unwraps the actual response sent by the server from the response context and deserializes the response content + * to the expected objects + * + * @params response Response returned by the server for a request to deletePendingQuota + * @throws ApiException if the response code was not in [200, 299] + */ + public async deletePendingQuota(response: ResponseContext): Promise { + const contentType = ObjectSerializer.normalizeMediaType( + response.headers["content-type"] + ); + if (response.httpStatusCode === 204) { + return; + } + if ( + response.httpStatusCode === 400 || + response.httpStatusCode === 403 || + response.httpStatusCode === 404 + ) { + const bodyText = ObjectSerializer.parse( + await response.body.text(), + contentType + ); + let body: JSONAPIErrorResponse; + try { + body = ObjectSerializer.deserialize( + bodyText, + "JSONAPIErrorResponse" + ) as JSONAPIErrorResponse; + } catch (error) { + logger.debug(`Got error deserializing error: ${error}`); + throw new ApiException( + response.httpStatusCode, + bodyText + ); + } + throw new ApiException( + response.httpStatusCode, + body + ); + } + if (response.httpStatusCode === 429) { + const bodyText = ObjectSerializer.parse( + await response.body.text(), + contentType + ); + let body: APIErrorResponse; + try { + body = ObjectSerializer.deserialize( + bodyText, + "APIErrorResponse" + ) as APIErrorResponse; + } catch (error) { + logger.debug(`Got error deserializing error: ${error}`); + throw new ApiException( + response.httpStatusCode, + bodyText + ); + } + throw new ApiException(response.httpStatusCode, body); + } + + // Work around for missing responses in specification, e.g. for petstore.yaml + if (response.httpStatusCode >= 200 && response.httpStatusCode <= 299) { + return; + } + + const body = (await response.body.text()) || ""; + throw new ApiException( + response.httpStatusCode, + 'Unknown API Status Code!\nBody: "' + body + '"' + ); + } + /** * Unwraps the actual response sent by the server from the response context and deserializes the response content * to the expected objects @@ -2268,6 +2391,19 @@ export interface UsageMeteringApiCreateQuotasRequest { includeDescendants?: boolean; } +export interface UsageMeteringApiDeletePendingQuotaRequest { + /** + * The product-specific namespace whose usage quotas are being managed. + * @type string + */ + quotaNamespace: string; + /** + * The opaque quota identifier returned by a previous list or create request. Clients must pass this value verbatim. + * @type string + */ + id: string; +} + export interface UsageMeteringApiDeleteQuotaRequest { /** * The product-specific namespace whose usage quotas are being managed. @@ -2590,7 +2726,11 @@ export class 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. * @param param The request object */ public createQuotas( @@ -2612,6 +2752,28 @@ export class 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. + * @param param The request object + */ + public deletePendingQuota( + param: UsageMeteringApiDeletePendingQuotaRequest, + options?: Configuration + ): Promise { + const requestContextPromise = this.requestFactory.deletePendingQuota( + param.quotaNamespace, + param.id, + options + ); + return requestContextPromise.then((requestContext) => { + return this.configuration.httpApi + .send(requestContext) + .then((responseContext) => { + return this.responseProcessor.deletePendingQuota(responseContext); + }); + }); + } + /** * 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. * @param param The request object @@ -3103,7 +3265,7 @@ export class 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. * @param param The request object */ public updateQuota( diff --git a/packages/datadog-api-client-v2/index.ts b/packages/datadog-api-client-v2/index.ts index ef565f298abb..eb069580d951 100644 --- a/packages/datadog-api-client-v2/index.ts +++ b/packages/datadog-api-client-v2/index.ts @@ -2061,6 +2061,7 @@ export { export { UsageMeteringApiCreateQuotasRequest, + UsageMeteringApiDeletePendingQuotaRequest, UsageMeteringApiDeleteQuotaRequest, UsageMeteringApiGetBillingDimensionMappingRequest, UsageMeteringApiGetCostByOrgRequest, diff --git a/packages/datadog-api-client-v2/models/UsageQuotaBulkResultAttributes.ts b/packages/datadog-api-client-v2/models/UsageQuotaBulkResultAttributes.ts index c6b0fc37429a..7c4fc4421fba 100644 --- a/packages/datadog-api-client-v2/models/UsageQuotaBulkResultAttributes.ts +++ b/packages/datadog-api-client-v2/models/UsageQuotaBulkResultAttributes.ts @@ -7,7 +7,7 @@ import { AttributeTypeMap } from "../../datadog-api-client-common/util"; /** - * 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. */ export class UsageQuotaBulkResultAttributes { /** @@ -22,6 +22,14 @@ export class UsageQuotaBulkResultAttributes { * The public ID of the organization that owns the quota. Omitted if this item failed to write. */ "orgPublicId"?: 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 or this item failed to write. + */ + "pendingEffectiveFrom"?: string; + /** + * 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. + */ + "pendingUsageLimit"?: number; /** * 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. */ @@ -59,6 +67,15 @@ export class UsageQuotaBulkResultAttributes { baseName: "org_public_id", type: "string", }, + pendingEffectiveFrom: { + baseName: "pending_effective_from", + type: "string", + }, + pendingUsageLimit: { + baseName: "pending_usage_limit", + type: "number", + format: "double", + }, scope: { baseName: "scope", type: "{ [key: string]: string; }", diff --git a/packages/datadog-api-client-v2/models/UsageQuotaBulkResultData.ts b/packages/datadog-api-client-v2/models/UsageQuotaBulkResultData.ts index 3744863a5b6e..fdc006ad12dd 100644 --- a/packages/datadog-api-client-v2/models/UsageQuotaBulkResultData.ts +++ b/packages/datadog-api-client-v2/models/UsageQuotaBulkResultData.ts @@ -13,7 +13,7 @@ import { AttributeTypeMap } from "../../datadog-api-client-common/util"; */ export class 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. */ "attributes": UsageQuotaBulkResultAttributes; /** diff --git a/packages/datadog-api-client-v2/models/UsageQuotaCreateAttributes.ts b/packages/datadog-api-client-v2/models/UsageQuotaCreateAttributes.ts index 056eca57cdb6..3132f82edc7c 100644 --- a/packages/datadog-api-client-v2/models/UsageQuotaCreateAttributes.ts +++ b/packages/datadog-api-client-v2/models/UsageQuotaCreateAttributes.ts @@ -7,21 +7,25 @@ import { AttributeTypeMap } from "../../datadog-api-client-common/util"; /** - * 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. */ export class 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. */ - "enforced": boolean; + "enforced"?: boolean; + /** + * 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. + */ + "pendingUsageLimit"?: number; /** * 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. */ "scope"?: { [key: string]: string }; /** - * 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. */ - "usageLimit": number; + "usageLimit"?: number; /** * A container for additional, undeclared properties. @@ -42,7 +46,11 @@ export class UsageQuotaCreateAttributes { enforced: { baseName: "enforced", type: "boolean", - required: true, + }, + pendingUsageLimit: { + baseName: "pending_usage_limit", + type: "number", + format: "int64", }, scope: { baseName: "scope", @@ -51,7 +59,6 @@ export class UsageQuotaCreateAttributes { usageLimit: { baseName: "usage_limit", type: "number", - required: true, format: "int64", }, additionalProperties: { diff --git a/packages/datadog-api-client-v2/models/UsageQuotaCreateData.ts b/packages/datadog-api-client-v2/models/UsageQuotaCreateData.ts index b036c689418c..f1e531d4e115 100644 --- a/packages/datadog-api-client-v2/models/UsageQuotaCreateData.ts +++ b/packages/datadog-api-client-v2/models/UsageQuotaCreateData.ts @@ -13,7 +13,7 @@ import { AttributeTypeMap } from "../../datadog-api-client-common/util"; */ export class 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. */ "attributes": UsageQuotaCreateAttributes; /** diff --git a/packages/datadog-api-client-v2/models/UsageQuotaResponseAttributes.ts b/packages/datadog-api-client-v2/models/UsageQuotaResponseAttributes.ts index d5a03f424183..0041131ba1ab 100644 --- a/packages/datadog-api-client-v2/models/UsageQuotaResponseAttributes.ts +++ b/packages/datadog-api-client-v2/models/UsageQuotaResponseAttributes.ts @@ -18,6 +18,16 @@ export class UsageQuotaResponseAttributes { * The public ID of the organization that owns the quota. */ "orgPublicId": 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. + */ + "pendingEffectiveFrom"?: string; + /** + * 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. + */ + "pendingUsageLimit"?: number; /** * 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. */ @@ -53,6 +63,15 @@ export class UsageQuotaResponseAttributes { type: "string", required: true, }, + pendingEffectiveFrom: { + baseName: "pending_effective_from", + type: "string", + }, + pendingUsageLimit: { + baseName: "pending_usage_limit", + type: "number", + format: "double", + }, scope: { baseName: "scope", type: "{ [key: string]: string; }", diff --git a/packages/datadog-api-client-v2/models/UsageQuotaUpdateAttributes.ts b/packages/datadog-api-client-v2/models/UsageQuotaUpdateAttributes.ts index f346b180cdb0..1079e5b1f025 100644 --- a/packages/datadog-api-client-v2/models/UsageQuotaUpdateAttributes.ts +++ b/packages/datadog-api-client-v2/models/UsageQuotaUpdateAttributes.ts @@ -7,13 +7,17 @@ import { AttributeTypeMap } from "../../datadog-api-client-common/util"; /** - * 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. */ export class UsageQuotaUpdateAttributes { /** * Whether to actively block usage above the limit. Omit this field to leave the current enforcement setting unchanged. */ "enforced"?: boolean; + /** + * 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. + */ + "pendingUsageLimit"?: number; /** * 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. */ @@ -39,6 +43,11 @@ export class UsageQuotaUpdateAttributes { baseName: "enforced", type: "boolean", }, + pendingUsageLimit: { + baseName: "pending_usage_limit", + type: "number", + format: "int64", + }, usageLimit: { baseName: "usage_limit", type: "number", diff --git a/packages/datadog-api-client-v2/models/UsageQuotaUpdateData.ts b/packages/datadog-api-client-v2/models/UsageQuotaUpdateData.ts index 59423ca329dc..95a7c50f41dd 100644 --- a/packages/datadog-api-client-v2/models/UsageQuotaUpdateData.ts +++ b/packages/datadog-api-client-v2/models/UsageQuotaUpdateData.ts @@ -13,7 +13,7 @@ import { AttributeTypeMap } from "../../datadog-api-client-common/util"; */ export class 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. */ "attributes": UsageQuotaUpdateAttributes; /**