diff --git a/.generated-info b/.generated-info index 9dd05ea93f..96df2ceea3 100644 --- a/.generated-info +++ b/.generated-info @@ -1,4 +1,4 @@ { - "spec_repo_commit": "62a19e4", - "generated": "2025-08-27 15:01:27.781" + "spec_repo_commit": "6d9663b", + "generated": "2025-08-27 16:38:12.845" } diff --git a/.generator/schemas/v2/openapi.yaml b/.generator/schemas/v2/openapi.yaml index e09c948047..19e228a115 100644 --- a/.generator/schemas/v2/openapi.yaml +++ b/.generator/schemas/v2/openapi.yaml @@ -14485,6 +14485,10 @@ components: description: Entity definition in raw JSON or YAML representation. example: "apiVersion: v3\nkind: service\nmetadata:\n name: myservice\n" type: string + EntityReference: + description: The unique reference for an IDP entity. + example: service:my-service + type: string EntityRelationships: description: Entity relationships. properties: @@ -34444,6 +34448,8 @@ components: description: If enabled, the rule is calculated as part of the score. example: true type: boolean + level: + $ref: '#/components/schemas/RuleLevel' modified_at: description: Time of the last rule outcome modification. format: date-time @@ -34464,6 +34470,13 @@ components: description: The unique ID for a scorecard rule. example: q8MQxk8TCqrHnWkx type: string + RuleLevel: + description: The maturity level of the rule (1, 2, or 3). + example: 2 + format: int32 + maximum: 3 + minimum: 1 + type: integer RuleName: description: Name of the notification rule. example: Rule 1 @@ -43835,6 +43848,57 @@ components: id: $ref: '#/components/schemas/ApiID' type: object + UpdateOutcomesAsyncAttributes: + description: The JSON:API attributes for a batched set of scorecard outcomes. + properties: + results: + description: Set of scorecard outcomes to update asynchronously. + items: + $ref: '#/components/schemas/UpdateOutcomesAsyncRequestItem' + type: array + type: object + UpdateOutcomesAsyncRequest: + description: Scorecard outcomes batch request. + properties: + data: + $ref: '#/components/schemas/UpdateOutcomesAsyncRequestData' + type: object + UpdateOutcomesAsyncRequestData: + description: Scorecard outcomes batch request data. + properties: + attributes: + $ref: '#/components/schemas/UpdateOutcomesAsyncAttributes' + type: + $ref: '#/components/schemas/UpdateOutcomesAsyncType' + type: object + UpdateOutcomesAsyncRequestItem: + description: Scorecard outcome for a single entity and rule. + properties: + entity_reference: + $ref: '#/components/schemas/EntityReference' + remarks: + description: Any remarks regarding the scorecard rule's evaluation. Supports + HTML hyperlinks. + example: 'See: Services' + type: string + rule_id: + $ref: '#/components/schemas/RuleId' + state: + $ref: '#/components/schemas/State' + required: + - rule_id + - entity_reference + - state + type: object + UpdateOutcomesAsyncType: + default: batched-outcome + description: The JSON:API type for scorecard outcomes. + enum: + - batched-outcome + example: batched-outcome + type: string + x-enum-varnames: + - BATCHED_OUTCOME UpdateResourceEvaluationFiltersRequest: description: Request object to update a resource filter. properties: @@ -61882,6 +61946,39 @@ paths: resultsPath: data x-unstable: '**Note**: This endpoint is in public beta. + If you have any feedback, contact [Datadog support](https://docs.datadoghq.com/help/).' + post: + description: Updates multiple scorecard rule outcomes in a single batched request. + operationId: UpdateScorecardOutcomesAsync + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateOutcomesAsyncRequest' + description: Set of scorecard outcomes. + required: true + responses: + '202': + description: Accepted + '400': + $ref: '#/components/responses/BadRequestResponse' + '403': + $ref: '#/components/responses/ForbiddenResponse' + '409': + $ref: '#/components/responses/ConflictResponse' + '429': + $ref: '#/components/responses/TooManyRequestsResponse' + security: + - apiKeyAuth: [] + appKeyAuth: [] + - AuthZ: + - apm_service_catalog_write + summary: Update Scorecard outcomes asynchronously + tags: + - Service Scorecards + x-codegen-request-body-name: body + x-unstable: '**Note**: This endpoint is in public beta. + If you have any feedback, contact [Datadog support](https://docs.datadoghq.com/help/).' /api/v2/scorecard/outcomes/batch: post: diff --git a/docs/datadog_api_client.v2.model.rst b/docs/datadog_api_client.v2.model.rst index 2f8c1b79c8..5d21e8bf53 100644 --- a/docs/datadog_api_client.v2.model.rst +++ b/docs/datadog_api_client.v2.model.rst @@ -19527,6 +19527,41 @@ datadog\_api\_client.v2.model.update\_open\_api\_response\_data module :members: :show-inheritance: +datadog\_api\_client.v2.model.update\_outcomes\_async\_attributes module +------------------------------------------------------------------------ + +.. automodule:: datadog_api_client.v2.model.update_outcomes_async_attributes + :members: + :show-inheritance: + +datadog\_api\_client.v2.model.update\_outcomes\_async\_request module +--------------------------------------------------------------------- + +.. automodule:: datadog_api_client.v2.model.update_outcomes_async_request + :members: + :show-inheritance: + +datadog\_api\_client.v2.model.update\_outcomes\_async\_request\_data module +--------------------------------------------------------------------------- + +.. automodule:: datadog_api_client.v2.model.update_outcomes_async_request_data + :members: + :show-inheritance: + +datadog\_api\_client.v2.model.update\_outcomes\_async\_request\_item module +--------------------------------------------------------------------------- + +.. automodule:: datadog_api_client.v2.model.update_outcomes_async_request_item + :members: + :show-inheritance: + +datadog\_api\_client.v2.model.update\_outcomes\_async\_type module +------------------------------------------------------------------ + +.. automodule:: datadog_api_client.v2.model.update_outcomes_async_type + :members: + :show-inheritance: + datadog\_api\_client.v2.model.update\_resource\_evaluation\_filters\_request module ----------------------------------------------------------------------------------- diff --git a/examples/v2/service-scorecards/UpdateScorecardOutcomesAsync.py b/examples/v2/service-scorecards/UpdateScorecardOutcomesAsync.py new file mode 100644 index 0000000000..edee85101e --- /dev/null +++ b/examples/v2/service-scorecards/UpdateScorecardOutcomesAsync.py @@ -0,0 +1,38 @@ +""" +Update Scorecard outcomes asynchronously returns "Accepted" response +""" + +from os import environ +from datadog_api_client import ApiClient, Configuration +from datadog_api_client.v2.api.service_scorecards_api import ServiceScorecardsApi +from datadog_api_client.v2.model.state import State +from datadog_api_client.v2.model.update_outcomes_async_attributes import UpdateOutcomesAsyncAttributes +from datadog_api_client.v2.model.update_outcomes_async_request import UpdateOutcomesAsyncRequest +from datadog_api_client.v2.model.update_outcomes_async_request_data import UpdateOutcomesAsyncRequestData +from datadog_api_client.v2.model.update_outcomes_async_request_item import UpdateOutcomesAsyncRequestItem +from datadog_api_client.v2.model.update_outcomes_async_type import UpdateOutcomesAsyncType + +# there is a valid "create_scorecard_rule" in the system +CREATE_SCORECARD_RULE_DATA_ID = environ["CREATE_SCORECARD_RULE_DATA_ID"] + +body = UpdateOutcomesAsyncRequest( + data=UpdateOutcomesAsyncRequestData( + attributes=UpdateOutcomesAsyncAttributes( + results=[ + UpdateOutcomesAsyncRequestItem( + rule_id=CREATE_SCORECARD_RULE_DATA_ID, + entity_reference="service:my-service", + remarks='See: Services', + state=State.PASS, + ), + ], + ), + type=UpdateOutcomesAsyncType.BATCHED_OUTCOME, + ), +) + +configuration = Configuration() +configuration.unstable_operations["update_scorecard_outcomes_async"] = True +with ApiClient(configuration) as api_client: + api_instance = ServiceScorecardsApi(api_client) + api_instance.update_scorecard_outcomes_async(body=body) diff --git a/src/datadog_api_client/configuration.py b/src/datadog_api_client/configuration.py index db01c60e2b..06f44f0d23 100644 --- a/src/datadog_api_client/configuration.py +++ b/src/datadog_api_client/configuration.py @@ -312,6 +312,7 @@ def __init__( "v2.delete_scorecard_rule": False, "v2.list_scorecard_outcomes": False, "v2.list_scorecard_rules": False, + "v2.update_scorecard_outcomes_async": False, "v2.update_scorecard_rule": False, "v2.create_incident_service": False, "v2.delete_incident_service": False, diff --git a/src/datadog_api_client/v2/api/service_scorecards_api.py b/src/datadog_api_client/v2/api/service_scorecards_api.py index 63cd7157ec..17f1b0de83 100644 --- a/src/datadog_api_client/v2/api/service_scorecards_api.py +++ b/src/datadog_api_client/v2/api/service_scorecards_api.py @@ -16,6 +16,7 @@ ) from datadog_api_client.v2.model.outcomes_response import OutcomesResponse from datadog_api_client.v2.model.outcomes_response_data_item import OutcomesResponseDataItem +from datadog_api_client.v2.model.update_outcomes_async_request import UpdateOutcomesAsyncRequest from datadog_api_client.v2.model.outcomes_batch_response import OutcomesBatchResponse from datadog_api_client.v2.model.outcomes_batch_request import OutcomesBatchRequest from datadog_api_client.v2.model.list_rules_response import ListRulesResponse @@ -235,6 +236,26 @@ def __init__(self, api_client=None): api_client=api_client, ) + self._update_scorecard_outcomes_async_endpoint = _Endpoint( + settings={ + "response_type": None, + "auth": ["apiKeyAuth", "appKeyAuth", "AuthZ"], + "endpoint_path": "/api/v2/scorecard/outcomes", + "operation_id": "update_scorecard_outcomes_async", + "http_method": "POST", + "version": "v2", + }, + params_map={ + "body": { + "required": True, + "openapi_types": (UpdateOutcomesAsyncRequest,), + "location": "body", + }, + }, + headers_map={"accept": ["*/*"], "content_type": ["application/json"]}, + api_client=api_client, + ) + self._update_scorecard_rule_endpoint = _Endpoint( settings={ "response_type": (UpdateRuleResponse,), @@ -628,6 +649,23 @@ def list_scorecard_rules_with_pagination( } return endpoint.call_with_http_info_paginated(pagination) + def update_scorecard_outcomes_async( + self, + body: UpdateOutcomesAsyncRequest, + ) -> None: + """Update Scorecard outcomes asynchronously. + + Updates multiple scorecard rule outcomes in a single batched request. + + :param body: Set of scorecard outcomes. + :type body: UpdateOutcomesAsyncRequest + :rtype: None + """ + kwargs: Dict[str, Any] = {} + kwargs["body"] = body + + return self._update_scorecard_outcomes_async_endpoint.call_with_http_info(**kwargs) + def update_scorecard_rule( self, rule_id: str, diff --git a/src/datadog_api_client/v2/model/rule_attributes.py b/src/datadog_api_client/v2/model/rule_attributes.py index 3156d9a7bf..25ad024b5e 100644 --- a/src/datadog_api_client/v2/model/rule_attributes.py +++ b/src/datadog_api_client/v2/model/rule_attributes.py @@ -15,6 +15,13 @@ class RuleAttributes(ModelNormal): + validations = { + "level": { + "inclusive_maximum": 3, + "inclusive_minimum": 1, + }, + } + @cached_property def openapi_types(_): return { @@ -23,6 +30,7 @@ def openapi_types(_): "custom": (bool,), "description": (str,), "enabled": (bool,), + "level": (int,), "modified_at": (datetime,), "name": (str,), "owner": (str,), @@ -35,6 +43,7 @@ def openapi_types(_): "custom": "custom", "description": "description", "enabled": "enabled", + "level": "level", "modified_at": "modified_at", "name": "name", "owner": "owner", @@ -48,6 +57,7 @@ def __init__( custom: Union[bool, UnsetType] = unset, description: Union[str, UnsetType] = unset, enabled: Union[bool, UnsetType] = unset, + level: Union[int, UnsetType] = unset, modified_at: Union[datetime, UnsetType] = unset, name: Union[str, UnsetType] = unset, owner: Union[str, UnsetType] = unset, @@ -72,6 +82,9 @@ def __init__( :param enabled: If enabled, the rule is calculated as part of the score. :type enabled: bool, optional + :param level: The maturity level of the rule (1, 2, or 3). + :type level: int, optional + :param modified_at: Time of the last rule outcome modification. :type modified_at: datetime, optional @@ -94,6 +107,8 @@ def __init__( kwargs["description"] = description if enabled is not unset: kwargs["enabled"] = enabled + if level is not unset: + kwargs["level"] = level if modified_at is not unset: kwargs["modified_at"] = modified_at if name is not unset: diff --git a/src/datadog_api_client/v2/model/update_outcomes_async_attributes.py b/src/datadog_api_client/v2/model/update_outcomes_async_attributes.py new file mode 100644 index 0000000000..32ccccee28 --- /dev/null +++ b/src/datadog_api_client/v2/model/update_outcomes_async_attributes.py @@ -0,0 +1,42 @@ +# Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. +# This product includes software developed at Datadog (https://www.datadoghq.com/). +# Copyright 2019-Present Datadog, Inc. +from __future__ import annotations + +from typing import List, Union, TYPE_CHECKING + +from datadog_api_client.model_utils import ( + ModelNormal, + cached_property, + unset, + UnsetType, +) + + +if TYPE_CHECKING: + from datadog_api_client.v2.model.update_outcomes_async_request_item import UpdateOutcomesAsyncRequestItem + + +class UpdateOutcomesAsyncAttributes(ModelNormal): + @cached_property + def openapi_types(_): + from datadog_api_client.v2.model.update_outcomes_async_request_item import UpdateOutcomesAsyncRequestItem + + return { + "results": ([UpdateOutcomesAsyncRequestItem],), + } + + attribute_map = { + "results": "results", + } + + def __init__(self_, results: Union[List[UpdateOutcomesAsyncRequestItem], UnsetType] = unset, **kwargs): + """ + The JSON:API attributes for a batched set of scorecard outcomes. + + :param results: Set of scorecard outcomes to update asynchronously. + :type results: [UpdateOutcomesAsyncRequestItem], optional + """ + if results is not unset: + kwargs["results"] = results + super().__init__(kwargs) diff --git a/src/datadog_api_client/v2/model/update_outcomes_async_request.py b/src/datadog_api_client/v2/model/update_outcomes_async_request.py new file mode 100644 index 0000000000..72a6b65e27 --- /dev/null +++ b/src/datadog_api_client/v2/model/update_outcomes_async_request.py @@ -0,0 +1,42 @@ +# Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. +# This product includes software developed at Datadog (https://www.datadoghq.com/). +# Copyright 2019-Present Datadog, Inc. +from __future__ import annotations + +from typing import Union, TYPE_CHECKING + +from datadog_api_client.model_utils import ( + ModelNormal, + cached_property, + unset, + UnsetType, +) + + +if TYPE_CHECKING: + from datadog_api_client.v2.model.update_outcomes_async_request_data import UpdateOutcomesAsyncRequestData + + +class UpdateOutcomesAsyncRequest(ModelNormal): + @cached_property + def openapi_types(_): + from datadog_api_client.v2.model.update_outcomes_async_request_data import UpdateOutcomesAsyncRequestData + + return { + "data": (UpdateOutcomesAsyncRequestData,), + } + + attribute_map = { + "data": "data", + } + + def __init__(self_, data: Union[UpdateOutcomesAsyncRequestData, UnsetType] = unset, **kwargs): + """ + Scorecard outcomes batch request. + + :param data: Scorecard outcomes batch request data. + :type data: UpdateOutcomesAsyncRequestData, optional + """ + if data is not unset: + kwargs["data"] = data + super().__init__(kwargs) diff --git a/src/datadog_api_client/v2/model/update_outcomes_async_request_data.py b/src/datadog_api_client/v2/model/update_outcomes_async_request_data.py new file mode 100644 index 0000000000..f1e183d42f --- /dev/null +++ b/src/datadog_api_client/v2/model/update_outcomes_async_request_data.py @@ -0,0 +1,56 @@ +# Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. +# This product includes software developed at Datadog (https://www.datadoghq.com/). +# Copyright 2019-Present Datadog, Inc. +from __future__ import annotations + +from typing import Union, TYPE_CHECKING + +from datadog_api_client.model_utils import ( + ModelNormal, + cached_property, + unset, + UnsetType, +) + + +if TYPE_CHECKING: + from datadog_api_client.v2.model.update_outcomes_async_attributes import UpdateOutcomesAsyncAttributes + from datadog_api_client.v2.model.update_outcomes_async_type import UpdateOutcomesAsyncType + + +class UpdateOutcomesAsyncRequestData(ModelNormal): + @cached_property + def openapi_types(_): + from datadog_api_client.v2.model.update_outcomes_async_attributes import UpdateOutcomesAsyncAttributes + from datadog_api_client.v2.model.update_outcomes_async_type import UpdateOutcomesAsyncType + + return { + "attributes": (UpdateOutcomesAsyncAttributes,), + "type": (UpdateOutcomesAsyncType,), + } + + attribute_map = { + "attributes": "attributes", + "type": "type", + } + + def __init__( + self_, + attributes: Union[UpdateOutcomesAsyncAttributes, UnsetType] = unset, + type: Union[UpdateOutcomesAsyncType, UnsetType] = unset, + **kwargs, + ): + """ + Scorecard outcomes batch request data. + + :param attributes: The JSON:API attributes for a batched set of scorecard outcomes. + :type attributes: UpdateOutcomesAsyncAttributes, optional + + :param type: The JSON:API type for scorecard outcomes. + :type type: UpdateOutcomesAsyncType, optional + """ + if attributes is not unset: + kwargs["attributes"] = attributes + if type is not unset: + kwargs["type"] = type + super().__init__(kwargs) diff --git a/src/datadog_api_client/v2/model/update_outcomes_async_request_item.py b/src/datadog_api_client/v2/model/update_outcomes_async_request_item.py new file mode 100644 index 0000000000..f24511d62e --- /dev/null +++ b/src/datadog_api_client/v2/model/update_outcomes_async_request_item.py @@ -0,0 +1,63 @@ +# Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. +# This product includes software developed at Datadog (https://www.datadoghq.com/). +# Copyright 2019-Present Datadog, Inc. +from __future__ import annotations + +from typing import Union, TYPE_CHECKING + +from datadog_api_client.model_utils import ( + ModelNormal, + cached_property, + unset, + UnsetType, +) + + +if TYPE_CHECKING: + from datadog_api_client.v2.model.state import State + + +class UpdateOutcomesAsyncRequestItem(ModelNormal): + @cached_property + def openapi_types(_): + from datadog_api_client.v2.model.state import State + + return { + "entity_reference": (str,), + "remarks": (str,), + "rule_id": (str,), + "state": (State,), + } + + attribute_map = { + "entity_reference": "entity_reference", + "remarks": "remarks", + "rule_id": "rule_id", + "state": "state", + } + + def __init__( + self_, entity_reference: str, rule_id: str, state: State, remarks: Union[str, UnsetType] = unset, **kwargs + ): + """ + Scorecard outcome for a single entity and rule. + + :param entity_reference: The unique reference for an IDP entity. + :type entity_reference: str + + :param remarks: Any remarks regarding the scorecard rule's evaluation. Supports HTML hyperlinks. + :type remarks: str, optional + + :param rule_id: The unique ID for a scorecard rule. + :type rule_id: str + + :param state: The state of the rule evaluation. + :type state: State + """ + if remarks is not unset: + kwargs["remarks"] = remarks + super().__init__(kwargs) + + self_.entity_reference = entity_reference + self_.rule_id = rule_id + self_.state = state diff --git a/src/datadog_api_client/v2/model/update_outcomes_async_type.py b/src/datadog_api_client/v2/model/update_outcomes_async_type.py new file mode 100644 index 0000000000..ce7c4bf5cc --- /dev/null +++ b/src/datadog_api_client/v2/model/update_outcomes_async_type.py @@ -0,0 +1,35 @@ +# Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. +# This product includes software developed at Datadog (https://www.datadoghq.com/). +# Copyright 2019-Present Datadog, Inc. +from __future__ import annotations + + +from datadog_api_client.model_utils import ( + ModelSimple, + cached_property, +) + +from typing import ClassVar + + +class UpdateOutcomesAsyncType(ModelSimple): + """ + The JSON:API type for scorecard outcomes. + + :param value: If omitted defaults to "batched-outcome". Must be one of ["batched-outcome"]. + :type value: str + """ + + allowed_values = { + "batched-outcome", + } + BATCHED_OUTCOME: ClassVar["UpdateOutcomesAsyncType"] + + @cached_property + def openapi_types(_): + return { + "value": (str,), + } + + +UpdateOutcomesAsyncType.BATCHED_OUTCOME = UpdateOutcomesAsyncType("batched-outcome") diff --git a/src/datadog_api_client/v2/models/__init__.py b/src/datadog_api_client/v2/models/__init__.py index a370727c3b..17bb49b9ef 100644 --- a/src/datadog_api_client/v2/models/__init__.py +++ b/src/datadog_api_client/v2/models/__init__.py @@ -3802,6 +3802,11 @@ from datadog_api_client.v2.model.update_open_api_response import UpdateOpenAPIResponse from datadog_api_client.v2.model.update_open_api_response_attributes import UpdateOpenAPIResponseAttributes from datadog_api_client.v2.model.update_open_api_response_data import UpdateOpenAPIResponseData +from datadog_api_client.v2.model.update_outcomes_async_attributes import UpdateOutcomesAsyncAttributes +from datadog_api_client.v2.model.update_outcomes_async_request import UpdateOutcomesAsyncRequest +from datadog_api_client.v2.model.update_outcomes_async_request_data import UpdateOutcomesAsyncRequestData +from datadog_api_client.v2.model.update_outcomes_async_request_item import UpdateOutcomesAsyncRequestItem +from datadog_api_client.v2.model.update_outcomes_async_type import UpdateOutcomesAsyncType from datadog_api_client.v2.model.update_resource_evaluation_filters_request import ( UpdateResourceEvaluationFiltersRequest, ) @@ -6743,6 +6748,11 @@ "UpdateOpenAPIResponse", "UpdateOpenAPIResponseAttributes", "UpdateOpenAPIResponseData", + "UpdateOutcomesAsyncAttributes", + "UpdateOutcomesAsyncRequest", + "UpdateOutcomesAsyncRequestData", + "UpdateOutcomesAsyncRequestItem", + "UpdateOutcomesAsyncType", "UpdateResourceEvaluationFiltersRequest", "UpdateResourceEvaluationFiltersRequestData", "UpdateResourceEvaluationFiltersResponse", diff --git a/tests/v2/cassettes/test_scenarios/test_create_a_new_rule_returns_bad_request_response.frozen b/tests/v2/cassettes/test_scenarios/test_create_a_new_rule_returns_bad_request_response.frozen new file mode 100644 index 0000000000..5d0a1ab654 --- /dev/null +++ b/tests/v2/cassettes/test_scenarios/test_create_a_new_rule_returns_bad_request_response.frozen @@ -0,0 +1 @@ +2025-08-26T15:48:59.496Z \ No newline at end of file diff --git a/tests/v2/cassettes/test_scenarios/test_create_a_new_rule_returns_bad_request_response.yaml b/tests/v2/cassettes/test_scenarios/test_create_a_new_rule_returns_bad_request_response.yaml new file mode 100644 index 0000000000..52808d7836 --- /dev/null +++ b/tests/v2/cassettes/test_scenarios/test_create_a_new_rule_returns_bad_request_response.yaml @@ -0,0 +1,21 @@ +interactions: +- request: + body: '{"data":{"attributes":{"enabled":true,"level":2,"name":"Team Defined","scorecard_id":"NOT.FOUND"},"type":"rule"}}' + headers: + accept: + - application/json + content-type: + - application/json + method: POST + uri: https://api.datadoghq.com/api/v2/scorecard/rules + response: + body: + string: '{"errors":[{"status":"400","title":"Bad Request","detail":"attribute + \"scorecard_id\" failed scorecard lookup"}]}' + headers: + content-type: + - application/vnd.api+json + status: + code: 400 + message: Bad Request +version: 1 diff --git a/tests/v2/cassettes/test_scenarios/test_update_an_existing_scorecard_rule_returns_bad_request_response.frozen b/tests/v2/cassettes/test_scenarios/test_update_an_existing_scorecard_rule_returns_bad_request_response.frozen new file mode 100644 index 0000000000..5eaafa2c36 --- /dev/null +++ b/tests/v2/cassettes/test_scenarios/test_update_an_existing_scorecard_rule_returns_bad_request_response.frozen @@ -0,0 +1 @@ +2025-08-26T19:33:28.111Z \ No newline at end of file diff --git a/tests/v2/cassettes/test_scenarios/test_update_an_existing_scorecard_rule_returns_bad_request_response.yaml b/tests/v2/cassettes/test_scenarios/test_update_an_existing_scorecard_rule_returns_bad_request_response.yaml new file mode 100644 index 0000000000..78bec0e741 --- /dev/null +++ b/tests/v2/cassettes/test_scenarios/test_update_an_existing_scorecard_rule_returns_bad_request_response.yaml @@ -0,0 +1,56 @@ +interactions: +- request: + body: '{"data":{"attributes":{"enabled":true,"name":"Test-Update_an_existing_scorecard_rule_returns_Bad_Request_response-1756236808","owner":"Datadog","scorecard_name":"OpenAPI + Spec Test Best Practices"},"type":"rule"}}' + headers: + accept: + - application/json + content-type: + - application/json + method: POST + uri: https://api.datadoghq.com/api/v2/scorecard/rules + response: + body: + string: '{"data":{"id":"z661h8n2bpc5q0kx","type":"rule","attributes":{"category":"OpenAPI + Spec Test Best Practices","created_at":"2025-08-26T19:32:32.06573621Z","custom":true,"enabled":true,"level":3,"modified_at":"2025-08-26T19:32:32.06573621Z","name":"Test-Update_an_existing_scorecard_rule_returns_Bad_Request_response-1756236808","owner":"Datadog","scorecard_name":"OpenAPI + Spec Test Best Practices"},"relationships":{"scorecard":{"data":{"id":"qsxpoYRhU_yz","type":"scorecard"}}}}}' + headers: + content-type: + - application/vnd.api+json + status: + code: 201 + message: Created +- request: + body: '{"data":{"attributes":{"enabled":true,"level":2,"name":"Team Defined","scorecard_id":"NOT.FOUND"},"type":"rule"}}' + headers: + accept: + - application/json + content-type: + - application/json + method: PUT + uri: https://api.datadoghq.com/api/v2/scorecard/rules/z661h8n2bpc5q0kx + response: + body: + string: '{"errors":[{"status":"400","title":"Bad Request","detail":"attribute + \"scorecard_id\" failed scorecard lookup"}]}' + headers: + content-type: + - application/vnd.api+json + status: + code: 400 + message: Bad Request +- request: + body: null + headers: + accept: + - '*/*' + method: DELETE + uri: https://api.datadoghq.com/api/v2/scorecard/rules/z661h8n2bpc5q0kx + response: + body: + string: '' + headers: {} + status: + code: 204 + message: No Content +version: 1 diff --git a/tests/v2/cassettes/test_scenarios/test_update_an_existing_scorecard_rule_returns_not_found_response.frozen b/tests/v2/cassettes/test_scenarios/test_update_an_existing_scorecard_rule_returns_not_found_response.frozen new file mode 100644 index 0000000000..865cee5ede --- /dev/null +++ b/tests/v2/cassettes/test_scenarios/test_update_an_existing_scorecard_rule_returns_not_found_response.frozen @@ -0,0 +1 @@ +2025-08-26T19:33:28.966Z \ No newline at end of file diff --git a/tests/v2/cassettes/test_scenarios/test_update_an_existing_scorecard_rule_returns_not_found_response.yaml b/tests/v2/cassettes/test_scenarios/test_update_an_existing_scorecard_rule_returns_not_found_response.yaml new file mode 100644 index 0000000000..8e1069df4d --- /dev/null +++ b/tests/v2/cassettes/test_scenarios/test_update_an_existing_scorecard_rule_returns_not_found_response.yaml @@ -0,0 +1,22 @@ +interactions: +- request: + body: '{"data":{"attributes":{"enabled":true,"level":2,"name":"Team Defined","scorecard_name":"Deployments + automated via Deployment Trains"},"type":"rule"}}' + headers: + accept: + - application/json + content-type: + - application/json + method: PUT + uri: https://api.datadoghq.com/api/v2/scorecard/rules/REPLACE.ME + response: + body: + string: '{"errors":[{"status":"404","title":"Not Found","detail":"rule not found: + REPLACE.ME"}]}' + headers: + content-type: + - application/vnd.api+json + status: + code: 404 + message: Not Found +version: 1 diff --git a/tests/v2/cassettes/test_scenarios/test_update_scorecard_outcomes_asynchronously_returns_accepted_response.frozen b/tests/v2/cassettes/test_scenarios/test_update_scorecard_outcomes_asynchronously_returns_accepted_response.frozen new file mode 100644 index 0000000000..90ae34fee2 --- /dev/null +++ b/tests/v2/cassettes/test_scenarios/test_update_scorecard_outcomes_asynchronously_returns_accepted_response.frozen @@ -0,0 +1 @@ +2025-08-26T14:46:17.790Z \ No newline at end of file diff --git a/tests/v2/cassettes/test_scenarios/test_update_scorecard_outcomes_asynchronously_returns_accepted_response.yaml b/tests/v2/cassettes/test_scenarios/test_update_scorecard_outcomes_asynchronously_returns_accepted_response.yaml new file mode 100644 index 0000000000..bbf24ff799 --- /dev/null +++ b/tests/v2/cassettes/test_scenarios/test_update_scorecard_outcomes_asynchronously_returns_accepted_response.yaml @@ -0,0 +1,56 @@ +interactions: +- request: + body: '{"data":{"attributes":{"enabled":true,"name":"Test-Update_Scorecard_outcomes_asynchronously_returns_Accepted_response-1756219577","owner":"Datadog","scorecard_name":"OpenAPI + Spec Test Best Practices"},"type":"rule"}}' + headers: + accept: + - application/json + content-type: + - application/json + method: POST + uri: https://api.datadoghq.com/api/v2/scorecard/rules + response: + body: + string: '{"data":{"id":"5e3dexz6x_4f_4pa","type":"rule","attributes":{"category":"OpenAPI + Spec Test Best Practices","created_at":"2025-08-26T14:46:18.889883535Z","custom":true,"enabled":true,"level":3,"modified_at":"2025-08-26T14:46:18.889883535Z","name":"Test-Update_Scorecard_outcomes_asynchronously_returns_Accepted_response-1756219577","owner":"Datadog","scorecard_name":"OpenAPI + Spec Test Best Practices"},"relationships":{"scorecard":{"data":{"id":"qsxpoYRhU_yz","type":"scorecard"}}}}}' + headers: + content-type: + - application/vnd.api+json + status: + code: 201 + message: Created +- request: + body: '{"data":{"attributes":{"results":[{"entity_reference":"service:my-service","remarks":"See: + Services","rule_id":"5e3dexz6x_4f_4pa","state":"pass"}]},"type":"batched-outcome"}}' + headers: + accept: + - '*/*' + content-type: + - application/json + method: POST + uri: https://api.datadoghq.com/api/v2/scorecard/outcomes + response: + body: + string: '{"data":{"id":"4iexte5r8prsrlrtzzxpy","type":"async-request"},"meta":{"total_received":1}}' + headers: + content-type: + - application/vnd.api+json + status: + code: 202 + message: Accepted +- request: + body: null + headers: + accept: + - '*/*' + method: DELETE + uri: https://api.datadoghq.com/api/v2/scorecard/rules/5e3dexz6x_4f_4pa + response: + body: + string: '' + headers: {} + status: + code: 204 + message: No Content +version: 1 diff --git a/tests/v2/cassettes/test_scenarios/test_update_scorecard_outcomes_asynchronously_returns_bad_request_response.frozen b/tests/v2/cassettes/test_scenarios/test_update_scorecard_outcomes_asynchronously_returns_bad_request_response.frozen new file mode 100644 index 0000000000..b743ee81bc --- /dev/null +++ b/tests/v2/cassettes/test_scenarios/test_update_scorecard_outcomes_asynchronously_returns_bad_request_response.frozen @@ -0,0 +1 @@ +2025-08-26T14:46:19.541Z \ No newline at end of file diff --git a/tests/v2/cassettes/test_scenarios/test_update_scorecard_outcomes_asynchronously_returns_bad_request_response.yaml b/tests/v2/cassettes/test_scenarios/test_update_scorecard_outcomes_asynchronously_returns_bad_request_response.yaml new file mode 100644 index 0000000000..5f5c1ee07e --- /dev/null +++ b/tests/v2/cassettes/test_scenarios/test_update_scorecard_outcomes_asynchronously_returns_bad_request_response.yaml @@ -0,0 +1,56 @@ +interactions: +- request: + body: '{"data":{"attributes":{"enabled":true,"name":"Test-Update_Scorecard_outcomes_asynchronously_returns_Bad_Request_response-1756219579","owner":"Datadog","scorecard_name":"OpenAPI + Spec Test Best Practices"},"type":"rule"}}' + headers: + accept: + - application/json + content-type: + - application/json + method: POST + uri: https://api.datadoghq.com/api/v2/scorecard/rules + response: + body: + string: '{"data":{"id":"cyysw9dtqcu8zemc","type":"rule","attributes":{"category":"OpenAPI + Spec Test Best Practices","created_at":"2025-08-26T14:46:19.594805953Z","custom":true,"enabled":true,"level":3,"modified_at":"2025-08-26T14:46:19.594805953Z","name":"Test-Update_Scorecard_outcomes_asynchronously_returns_Bad_Request_response-1756219579","owner":"Datadog","scorecard_name":"OpenAPI + Spec Test Best Practices"},"relationships":{"scorecard":{"data":{"id":"qsxpoYRhU_yz","type":"scorecard"}}}}}' + headers: + content-type: + - application/vnd.api+json + status: + code: 201 + message: Created +- request: + body: '{"data":{"attributes":{"results":[{"entity_reference":"service:my-service","rule_id":"cyysw9dtqcu8zemc","state":"INVALID"}]},"type":"batched-outcome"}}' + headers: + accept: + - '*/*' + content-type: + - application/json + method: POST + uri: https://api.datadoghq.com/api/v2/scorecard/outcomes + response: + body: + string: '{"errors":[{"status":"400","title":"Bad Request","detail":"attribute + \"state\" must be one of \"pass fail skip\""}]}' + headers: + content-type: + - application/vnd.api+json + status: + code: 400 + message: Bad Request +- request: + body: null + headers: + accept: + - '*/*' + method: DELETE + uri: https://api.datadoghq.com/api/v2/scorecard/rules/cyysw9dtqcu8zemc + response: + body: + string: '' + headers: {} + status: + code: 204 + message: No Content +version: 1 diff --git a/tests/v2/cassettes/test_scenarios/test_update_scorecard_outcomes_asynchronously_returns_conflict_response.frozen b/tests/v2/cassettes/test_scenarios/test_update_scorecard_outcomes_asynchronously_returns_conflict_response.frozen new file mode 100644 index 0000000000..eb7d1dc0b1 --- /dev/null +++ b/tests/v2/cassettes/test_scenarios/test_update_scorecard_outcomes_asynchronously_returns_conflict_response.frozen @@ -0,0 +1 @@ +2025-08-26T14:46:20.159Z \ No newline at end of file diff --git a/tests/v2/cassettes/test_scenarios/test_update_scorecard_outcomes_asynchronously_returns_conflict_response.yaml b/tests/v2/cassettes/test_scenarios/test_update_scorecard_outcomes_asynchronously_returns_conflict_response.yaml new file mode 100644 index 0000000000..12a265387d --- /dev/null +++ b/tests/v2/cassettes/test_scenarios/test_update_scorecard_outcomes_asynchronously_returns_conflict_response.yaml @@ -0,0 +1,21 @@ +interactions: +- request: + body: '{"data":{"attributes":{"results":[{"entity_reference":"service:my-service","remarks":"See: + Services","rule_id":"INVALID.RULE_ID","state":"pass"}]},"type":"batched-outcome"}}' + headers: + accept: + - '*/*' + content-type: + - application/json + method: POST + uri: https://api.datadoghq.com/api/v2/scorecard/outcomes + response: + body: + string: '{"errors":[{"status":"409","title":"Invalid rule_id"}]}' + headers: + content-type: + - application/vnd.api+json + status: + code: 409 + message: Conflict +version: 1 diff --git a/tests/v2/features/service_scorecards.feature b/tests/v2/features/service_scorecards.feature index 11d2c7f432..387f04cf71 100644 --- a/tests/v2/features/service_scorecards.feature +++ b/tests/v2/features/service_scorecards.feature @@ -10,11 +10,11 @@ Feature: Service Scorecards And a valid "appKeyAuth" key in the system And an instance of "ServiceScorecards" API - @generated @skip @team:DataDog/service-catalog + @team:DataDog/service-catalog Scenario: Create a new rule returns "Bad Request" response Given operation "CreateScorecardRule" enabled And new "CreateScorecardRule" request - And body with value {"data": {"attributes": {"enabled": true, "name": "Team Defined", "scorecard_name": "Deployments automated via Deployment Trains"}, "type": "rule"}} + And body with value {"data": {"attributes": {"enabled": true, "level": 2, "name": "Team Defined", "scorecard_id": "NOT.FOUND"}, "type": "rule"}} When the request is sent Then the response status is 400 Bad Request @@ -121,12 +121,41 @@ Feature: Service Scorecards Then the response status is 200 OK And the response has 4 items + @team:DataDog/service-catalog + Scenario: Update Scorecard outcomes asynchronously returns "Accepted" response + Given operation "UpdateScorecardOutcomesAsync" enabled + And there is a valid "create_scorecard_rule" in the system + And new "UpdateScorecardOutcomesAsync" request + And body with value {"data": {"attributes": {"results": [{"rule_id": "{{create_scorecard_rule.data.id}}", "entity_reference": "service:my-service", "remarks": "See: Services", "state": "pass"}]}, "type": "batched-outcome"}} + When the request is sent + Then the response status is 202 Accepted + + @team:DataDog/service-catalog + Scenario: Update Scorecard outcomes asynchronously returns "Bad Request" response + Given operation "UpdateScorecardOutcomesAsync" enabled + And there is a valid "create_scorecard_rule" in the system + And new "UpdateScorecardOutcomesAsync" request + And body with value {"data": {"attributes": {"results": [{"rule_id": "{{create_scorecard_rule.data.id}}", "entity_reference": "service:my-service", "state": "INVALID"}]}, "type": "batched-outcome"}} + When the request is sent + Then the response status is 400 Bad Request + And the response "errors" has length 1 + And the response "errors[0]" has field "detail" + + @team:DataDog/service-catalog + Scenario: Update Scorecard outcomes asynchronously returns "Conflict" response + Given operation "UpdateScorecardOutcomesAsync" enabled + And new "UpdateScorecardOutcomesAsync" request + And body with value {"data": {"attributes": {"results": [{"rule_id": "INVALID.RULE_ID", "entity_reference": "service:my-service", "remarks": "See: Services", "state": "pass"}]}, "type": "batched-outcome"}} + When the request is sent + Then the response status is 409 Conflict + And the response "errors" has length 1 + @generated @skip @team:DataDog/service-catalog Scenario: Update an existing rule returns "Bad Request" response Given operation "UpdateScorecardRule" enabled And new "UpdateScorecardRule" request And request contains "rule_id" parameter from "REPLACE.ME" - And body with value {"data": {"attributes": {"enabled": true, "name": "Team Defined", "scorecard_name": "Deployments automated via Deployment Trains"}, "type": "rule"}} + And body with value {"data": {"attributes": {"enabled": true, "level": 2, "name": "Team Defined", "scorecard_name": "Deployments automated via Deployment Trains"}, "type": "rule"}} When the request is sent Then the response status is 400 Bad Request @@ -139,3 +168,22 @@ Feature: Service Scorecards And body with value {"data": { "attributes" : {"enabled": true, "name": "{{create_scorecard_rule.data.attributes.name}}", "scorecard_name": "{{create_scorecard_rule.data.attributes.scorecard_name}}", "description": "Updated description via test"}}} When the request is sent Then the response status is 200 Rule updated successfully + + @team:DataDog/service-catalog + Scenario: Update an existing scorecard rule returns "Bad Request" response + Given operation "UpdateScorecardRule" enabled + And there is a valid "create_scorecard_rule" in the system + And new "UpdateScorecardRule" request + And request contains "rule_id" parameter from "create_scorecard_rule.data.id" + And body with value {"data": {"attributes": {"enabled": true, "level": 2, "name": "Team Defined", "scorecard_id": "NOT.FOUND"}, "type": "rule"}} + When the request is sent + Then the response status is 400 Bad Request + + @team:DataDog/service-catalog + Scenario: Update an existing scorecard rule returns "Not Found" response + Given operation "UpdateScorecardRule" enabled + And new "UpdateScorecardRule" request + And request contains "rule_id" parameter with value "REPLACE.ME" + And body with value {"data": {"attributes": {"enabled": true, "level": 2, "name": "Team Defined", "scorecard_name": "Deployments automated via Deployment Trains"}, "type": "rule"}} + When the request is sent + Then the response status is 404 Not Found diff --git a/tests/v2/features/undo.json b/tests/v2/features/undo.json index 9878d7a467..444ec9d07d 100644 --- a/tests/v2/features/undo.json +++ b/tests/v2/features/undo.json @@ -2742,6 +2742,12 @@ "type": "safe" } }, + "UpdateScorecardOutcomesAsync": { + "tag": "Service Scorecards", + "undo": { + "type": "idempotent" + } + }, "CreateScorecardOutcomesBatch": { "tag": "Service Scorecards", "undo": {