From f11f86ef2bdf4ee0cb42d3657acb0c5ccbf87632 Mon Sep 17 00:00:00 2001 From: "ci.datadog-api-spec" Date: Tue, 20 Oct 2026 18:07:55 +0000 Subject: [PATCH] Regenerate client from commit 34abb52 of spec repo --- .generator/schemas/v2/openapi.yaml | 365 ++++++++ .../UpdateSavedFilterAttributes.py | 31 + .../UpdateSavedFilterAttributes_1194333410.py | 48 + .../UpdateSavedFilterAttributes_1937053112.py | 46 + .../UpdateSavedFilterAttributes_2658080376.py | 49 + .../UpdateSavedFilterAttributes_281483245.py | 31 + .../UpdateSavedFilterAttributes_3212710506.py | 32 + .../UpdateSavedFilterAttributes_3817543566.py | 34 + .../UpdateSavedFilterAttributes_4162852827.py | 31 + .../UpdateSavedFilterAttributes_727520235.py | 46 + .../v2/api/feature_flags_api.py | 55 ++ .../v2/model/patch_saved_filter_attributes.py | 81 ++ .../patch_saved_filter_conflict_response.py | 45 + .../v2/model/patch_saved_filter_data.py | 48 + .../patch_saved_filter_forbidden_response.py | 47 + .../v2/model/patch_saved_filter_request.py | 40 + .../model/saved_filter_api_error_response.py | 44 + ...ved_filter_approval_conflict_attributes.py | 46 + .../saved_filter_approval_conflict_data.py | 65 ++ ...saved_filter_approval_conflict_response.py | 44 + .../saved_filter_approval_conflict_type.py | 35 + .../model/saved_filter_condition_request.py | 61 ++ .../v2/model/saved_filter_flag_reference.py | 40 + .../saved_filter_jsonapi_error_response.py | 50 ++ ...ved_filter_permission_denied_attributes.py | 46 + .../saved_filter_permission_denied_data.py | 65 ++ ...saved_filter_permission_denied_response.py | 44 + .../saved_filter_permission_denied_type.py | 35 + .../saved_filter_targeting_rule_request.py | 46 + src/datadog_api_client/v2/models/__init__.py | 36 + .../test-runner-data/manifest.json | 140 +++ ...ts-the-current-timestamp-for-metadata.json | 51 ++ ...cepts-the-current-timestamp-for-rules.json | 63 ++ ...-the-description-with-an-empty-string.json | 50 ++ ...d-filter-preserves-a-null-description.json | 51 ++ ...saved-filter-rejects-a-duplicate-name.json | 50 ++ ...ved-filter-rejects-a-null-only-update.json | 50 ++ ...ilter-rejects-a-timestamp-only-update.json | 50 ++ ...-a-saved-filter-rejects-an-empty-name.json | 50 ++ ...d-filter-rejects-an-invalid-timestamp.json | 51 ++ ...-filter-rejects-empty-targeting-rules.json | 50 ++ ...d-filter-rejects-nested-saved-filters.json | 63 ++ ...ved-filter-rejects-stale-rule-updates.json | 63 ++ ...replaces-rules-and-preserves-metadata.json | 62 ++ ...er-replaces-the-rules-of-a-saved-list.json | 62 ++ ...d-filter-returns-bad-request-response.json | 48 + ...aved-filter-returns-conflict-response.json | 51 ++ ...ved-filter-returns-not-found-response.json | 50 ++ ...ch-a-saved-filter-returns-ok-response.json | 50 ++ ...r-updates-metadata-and-rules-together.json | 64 ++ ...-only-the-description-of-a-saved-list.json | 50 ++ .../test-server-data/v2/feature-flags.json | 839 ++++++++++++++++++ tests/v2/features/feature_flags.feature | 294 ++++++ tests/v2/features/undo.json | 6 + 54 files changed, 4044 insertions(+) create mode 100644 examples/v2/feature-flags/UpdateSavedFilterAttributes.py create mode 100644 examples/v2/feature-flags/UpdateSavedFilterAttributes_1194333410.py create mode 100644 examples/v2/feature-flags/UpdateSavedFilterAttributes_1937053112.py create mode 100644 examples/v2/feature-flags/UpdateSavedFilterAttributes_2658080376.py create mode 100644 examples/v2/feature-flags/UpdateSavedFilterAttributes_281483245.py create mode 100644 examples/v2/feature-flags/UpdateSavedFilterAttributes_3212710506.py create mode 100644 examples/v2/feature-flags/UpdateSavedFilterAttributes_3817543566.py create mode 100644 examples/v2/feature-flags/UpdateSavedFilterAttributes_4162852827.py create mode 100644 examples/v2/feature-flags/UpdateSavedFilterAttributes_727520235.py create mode 100644 src/datadog_api_client/v2/model/patch_saved_filter_attributes.py create mode 100644 src/datadog_api_client/v2/model/patch_saved_filter_conflict_response.py create mode 100644 src/datadog_api_client/v2/model/patch_saved_filter_data.py create mode 100644 src/datadog_api_client/v2/model/patch_saved_filter_forbidden_response.py create mode 100644 src/datadog_api_client/v2/model/patch_saved_filter_request.py create mode 100644 src/datadog_api_client/v2/model/saved_filter_api_error_response.py create mode 100644 src/datadog_api_client/v2/model/saved_filter_approval_conflict_attributes.py create mode 100644 src/datadog_api_client/v2/model/saved_filter_approval_conflict_data.py create mode 100644 src/datadog_api_client/v2/model/saved_filter_approval_conflict_response.py create mode 100644 src/datadog_api_client/v2/model/saved_filter_approval_conflict_type.py create mode 100644 src/datadog_api_client/v2/model/saved_filter_condition_request.py create mode 100644 src/datadog_api_client/v2/model/saved_filter_flag_reference.py create mode 100644 src/datadog_api_client/v2/model/saved_filter_jsonapi_error_response.py create mode 100644 src/datadog_api_client/v2/model/saved_filter_permission_denied_attributes.py create mode 100644 src/datadog_api_client/v2/model/saved_filter_permission_denied_data.py create mode 100644 src/datadog_api_client/v2/model/saved_filter_permission_denied_response.py create mode 100644 src/datadog_api_client/v2/model/saved_filter_permission_denied_type.py create mode 100644 src/datadog_api_client/v2/model/saved_filter_targeting_rule_request.py create mode 100644 tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-accepts-the-current-timestamp-for-metadata.json create mode 100644 tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-accepts-the-current-timestamp-for-rules.json create mode 100644 tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-clears-the-description-with-an-empty-string.json create mode 100644 tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-preserves-a-null-description.json create mode 100644 tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-rejects-a-duplicate-name.json create mode 100644 tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-rejects-a-null-only-update.json create mode 100644 tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-rejects-a-timestamp-only-update.json create mode 100644 tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-rejects-an-empty-name.json create mode 100644 tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-rejects-an-invalid-timestamp.json create mode 100644 tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-rejects-empty-targeting-rules.json create mode 100644 tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-rejects-nested-saved-filters.json create mode 100644 tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-rejects-stale-rule-updates.json create mode 100644 tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-replaces-rules-and-preserves-metadata.json create mode 100644 tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-replaces-the-rules-of-a-saved-list.json create mode 100644 tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-returns-bad-request-response.json create mode 100644 tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-returns-conflict-response.json create mode 100644 tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-returns-not-found-response.json create mode 100644 tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-returns-ok-response.json create mode 100644 tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-updates-metadata-and-rules-together.json create mode 100644 tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-updates-only-the-description-of-a-saved-list.json diff --git a/.generator/schemas/v2/openapi.yaml b/.generator/schemas/v2/openapi.yaml index 0168ec45e3..64947c88c5 100644 --- a/.generator/schemas/v2/openapi.yaml +++ b/.generator/schemas/v2/openapi.yaml @@ -94889,6 +94889,64 @@ components: version: $ref: "#/components/schemas/Version" type: object + PatchSavedFilterAttributes: + description: Fields to update. At least one of `name`, `description`, or `targeting_rules` must have a non-null value. Omitted or null fields are preserved. The creation type cannot be changed. + properties: + description: + description: The new description. Send an empty string to clear the description. If omitted or null, the existing description is preserved. + example: Updated early access users. + nullable: true + type: string + last_updated_at: + description: The `updated_at` value returned by a previous read, as an RFC 3339 timestamp. If it differs from the stored update time, truncated to seconds, the request returns a 409 response. For metadata-only updates, this check does not prevent all concurrent writes. This field alone does not constitute an update. + example: "2026-01-02T12:00:00Z" + format: date-time + nullable: true + type: string + name: + description: The new name of the saved filter. Must not be empty. + example: Early access users + minLength: 1 + nullable: true + type: string + targeting_rules: + description: The complete replacement targeting rules, combined with OR. Conditions within each rule are combined with AND. Replaces all existing rules. Values are not appended. Must contain at least one rule. Saved filters cannot reference other saved filters. + items: + $ref: "#/components/schemas/SavedFilterTargetingRuleRequest" + minItems: 1 + nullable: true + type: array + type: object + PatchSavedFilterConflictResponse: + description: A stale update, duplicate name, or other conflict, or a resource listing consumer flags requiring approval. + oneOf: + - $ref: "#/components/schemas/SavedFilterJSONAPIErrorResponse" + - $ref: "#/components/schemas/SavedFilterApprovalConflictResponse" + PatchSavedFilterData: + description: The saved filter fields to update. + properties: + attributes: + $ref: "#/components/schemas/PatchSavedFilterAttributes" + type: + $ref: "#/components/schemas/SavedFilterDataType" + required: + - type + - attributes + type: object + PatchSavedFilterForbiddenResponse: + description: An authentication or permission error, or a resource listing consumer flags that the caller cannot edit. + oneOf: + - $ref: "#/components/schemas/SavedFilterAPIErrorResponse" + - $ref: "#/components/schemas/SavedFilterJSONAPIErrorResponse" + - $ref: "#/components/schemas/SavedFilterPermissionDeniedResponse" + PatchSavedFilterRequest: + description: Request to partially update a saved filter. + properties: + data: + $ref: "#/components/schemas/PatchSavedFilterData" + required: + - data + type: object PatchStatusPageRequest: description: Request object for updating a status page. example: @@ -108855,6 +108913,72 @@ components: required: - data type: object + SavedFilterAPIErrorResponse: + additionalProperties: false + description: A saved filter authorization error containing at least one error message. + properties: + errors: + description: The authorization error messages. + example: + - Forbidden + - Failed permission authorization checks + items: + type: string + minItems: 1 + type: array + required: + - errors + type: object + SavedFilterApprovalConflictAttributes: + description: One or more feature flags that reference this saved filter require approval for the rule update. + properties: + approval_required_flags: + description: Feature flags preventing this update. + items: + $ref: "#/components/schemas/SavedFilterFlagReference" + type: array + message: + description: The reason the saved filter update was rejected. + example: One or more feature flags that reference this saved filter require approval for the rule update. + type: string + required: + - message + - approval_required_flags + type: object + SavedFilterApprovalConflictData: + description: One or more feature flags that reference this saved filter require approval for the rule update. + properties: + attributes: + $ref: "#/components/schemas/SavedFilterApprovalConflictAttributes" + id: + description: The ID of the saved filter. + example: 550e8400-e29b-41d4-a716-446655440090 + format: uuid + type: string + type: + $ref: "#/components/schemas/SavedFilterApprovalConflictType" + required: + - id + - type + - attributes + type: object + SavedFilterApprovalConflictResponse: + additionalProperties: false + description: One or more feature flags that reference this saved filter require approval for the rule update. + properties: + data: + $ref: "#/components/schemas/SavedFilterApprovalConflictData" + required: + - data + type: object + SavedFilterApprovalConflictType: + description: The resource type for this saved filter update error. + enum: + - update-approval-conflict + example: update-approval-conflict + type: string + x-enum-varnames: + - UPDATE_APPROVAL_CONFLICT SavedFilterAttributes: description: Saved filter details, including the full targeting rules and conditions. properties: @@ -108970,6 +109094,30 @@ components: - SEMVER_LTE - SEMVER_GT - SEMVER_GTE + SavedFilterConditionRequest: + description: An inline condition to write in a saved filter. Nested saved-filter references are not supported. + properties: + attribute: + description: The attribute evaluated by the condition. + example: user_id + minLength: 1 + type: string + operator: + $ref: "#/components/schemas/SavedFilterConditionOperator" + value: + description: Values used by the condition operator. Values must satisfy the selected operator and the organization targeting limits. + example: + - test-user-1 + - test-user-2 + items: + type: string + minItems: 1 + type: array + required: + - operator + - attribute + - value + type: object SavedFilterCreationType: description: How the saved filter was authored. RULES indicates rule-based authoring and LIST indicates list import. Both types return targeting rules and conditions. enum: @@ -109005,6 +109153,89 @@ components: type: string x-enum-varnames: - SAVED_FILTERS + SavedFilterFlagReference: + description: A feature flag that references the saved filter. + properties: + id: + description: The ID of the feature flag. + example: 550e8400-e29b-41d4-a716-446655440000 + format: uuid + type: string + name: + description: The name of the feature flag. + example: Early access + type: string + required: + - id + - name + type: object + SavedFilterJSONAPIErrorResponse: + additionalProperties: false + description: A saved filter error containing at least one JSON:API error object. + properties: + errors: + description: The error details. + example: + - detail: A saved filter with this name already exists. + status: "409" + title: Conflict + items: + $ref: "#/components/schemas/JSONAPIErrorItem" + minItems: 1 + type: array + required: + - errors + type: object + SavedFilterPermissionDeniedAttributes: + description: The caller lacks edit permission on one or more feature flags that reference this saved filter. + properties: + denied_flags: + description: Feature flags preventing this update. + items: + $ref: "#/components/schemas/SavedFilterFlagReference" + type: array + message: + description: The reason the saved filter update was rejected. + example: The caller lacks edit permission on one or more feature flags that reference this saved filter. + type: string + required: + - message + - denied_flags + type: object + SavedFilterPermissionDeniedData: + description: The caller lacks edit permission on one or more feature flags that reference this saved filter. + properties: + attributes: + $ref: "#/components/schemas/SavedFilterPermissionDeniedAttributes" + id: + description: The ID of the saved filter. + example: 550e8400-e29b-41d4-a716-446655440090 + format: uuid + type: string + type: + $ref: "#/components/schemas/SavedFilterPermissionDeniedType" + required: + - id + - type + - attributes + type: object + SavedFilterPermissionDeniedResponse: + additionalProperties: false + description: The caller lacks edit permission on one or more feature flags that reference this saved filter. + properties: + data: + $ref: "#/components/schemas/SavedFilterPermissionDeniedData" + required: + - data + type: object + SavedFilterPermissionDeniedType: + description: The resource type for this saved filter update error. + enum: + - update-grace-denied + example: update-grace-denied + type: string + x-enum-varnames: + - UPDATE_GRACE_DENIED SavedFilterResponse: description: Response containing a saved filter and its targeting rules. properties: @@ -109030,6 +109261,18 @@ components: - id - conditions type: object + SavedFilterTargetingRuleRequest: + description: A replacement saved filter targeting rule. + properties: + conditions: + description: Inline conditions combined with AND. Must contain at least one condition. + items: + $ref: "#/components/schemas/SavedFilterConditionRequest" + minItems: 1 + type: array + required: + - conditions + type: object ScaRequest: description: The top-level request object for submitting a Software Composition Analysis (SCA) scan result. properties: @@ -173172,6 +173415,128 @@ paths: operator: AND permissions: - feature_flag_config_read + patch: + description: |- + Partially updates a saved filter. Supports both `LIST` and `RULES` filters. + Supply at least one of `name`, `description`, or `targeting_rules`. Omitted or null fields + are preserved. Supplying `targeting_rules` replaces the entire collection and propagates + changes to every feature flag that references the saved filter. + Metadata-only updates do not require edit permission or approval on those feature flags. + Rule updates require edit permission on all consumer flags and are rejected when approval + is required, unless the caller has the `feature_flag_approvals_override` permission. + The optional `last_updated_at` field detects stale updates. It does not set the update time. + operationId: UpdateSavedFilterAttributes + parameters: + - description: The ID of the saved filter. + example: 550e8400-e29b-41d4-a716-446655440090 + in: path + name: saved_filter_id + required: true + schema: + format: uuid + type: string + requestBody: + content: + application/json: + examples: + default: + summary: Rename a saved filter while preserving its description and rules + value: + data: + attributes: + name: Early access users + type: saved-filters + replace_rules: + summary: Replace the complete rules collection + value: + data: + attributes: + targeting_rules: + - conditions: + - attribute: user_id + operator: ONE_OF + value: + - test-user-1 + - test-user-2 + type: saved-filters + schema: + $ref: "#/components/schemas/PatchSavedFilterRequest" + required: true + responses: + "200": + content: + application/json: + examples: + default: + summary: A saved filter matching an email address or an organization ID + value: + data: + attributes: + created_at: "2026-01-01T12:00:00Z" + created_by: 550e8400-e29b-41d4-a716-446655440091 + creation_type: RULES + description: Users selected by email address or organization ID. + name: Early access users + targeting_rules: + - conditions: + - attribute: email + id: 550e8400-e29b-41d4-a716-446655440093 + operator: ONE_OF + value: + - user@example.com + id: 550e8400-e29b-41d4-a716-446655440092 + - conditions: + - attribute: org_id + id: 550e8400-e29b-41d4-a716-446655440095 + operator: ONE_OF + value: + - "12345" + - "67890" + id: 550e8400-e29b-41d4-a716-446655440094 + updated_at: "2026-01-02T12:00:00Z" + id: 550e8400-e29b-41d4-a716-446655440090 + type: saved-filters + schema: + $ref: "#/components/schemas/SavedFilterResponse" + description: OK + "400": + content: + application/json: + schema: + $ref: "#/components/schemas/JSONAPIErrorResponse" + description: Bad Request + "403": + content: + application/json: + schema: + $ref: "#/components/schemas/PatchSavedFilterForbiddenResponse" + description: Forbidden + "404": + content: + application/json: + schema: + $ref: "#/components/schemas/JSONAPIErrorResponse" + description: Not Found + "409": + content: + application/json: + schema: + $ref: "#/components/schemas/PatchSavedFilterConflictResponse" + description: Conflict + "429": + $ref: "#/components/responses/TooManyRequestsResponse" + security: + - apiKeyAuth: [] + appKeyAuth: [] + - AuthZ: + - feature_flag_config_write + summary: Patch a saved filter + tags: + - Feature Flags + x-permission: + operator: AND + permissions: + - feature_flag_config_write /api/v2/feature-flags/{feature_flag_id}: get: description: |- diff --git a/examples/v2/feature-flags/UpdateSavedFilterAttributes.py b/examples/v2/feature-flags/UpdateSavedFilterAttributes.py new file mode 100644 index 0000000000..f89e09ee89 --- /dev/null +++ b/examples/v2/feature-flags/UpdateSavedFilterAttributes.py @@ -0,0 +1,31 @@ +""" +Patch a saved filter returns "OK" response +""" + +from os import environ +from datadog_api_client import ApiClient, Configuration +from datadog_api_client.v2.api.feature_flags_api import FeatureFlagsApi +from datadog_api_client.v2.model.patch_saved_filter_attributes import PatchSavedFilterAttributes +from datadog_api_client.v2.model.patch_saved_filter_data import PatchSavedFilterData +from datadog_api_client.v2.model.patch_saved_filter_request import PatchSavedFilterRequest +from datadog_api_client.v2.model.saved_filter_data_type import SavedFilterDataType +from uuid import UUID + +body = PatchSavedFilterRequest( + data=PatchSavedFilterData( + type=SavedFilterDataType.SAVED_FILTERS, + attributes=PatchSavedFilterAttributes( + name="patchSavedFilterTest-renamed-157e7ff3", + ), + ), +) + +configuration = Configuration() +configuration.access_token = environ["DD_BEARER_TOKEN"] +with ApiClient(configuration) as api_client: + api_instance = FeatureFlagsApi(api_client) + response = api_instance.update_saved_filter_attributes( + saved_filter_id=UUID("2cfc7949-7eb4-4d76-b1b8-d1aa9b565535"), body=body + ) + + print(response) diff --git a/examples/v2/feature-flags/UpdateSavedFilterAttributes_1194333410.py b/examples/v2/feature-flags/UpdateSavedFilterAttributes_1194333410.py new file mode 100644 index 0000000000..afbdada55e --- /dev/null +++ b/examples/v2/feature-flags/UpdateSavedFilterAttributes_1194333410.py @@ -0,0 +1,48 @@ +""" +Patch a saved filter updates metadata and rules together +""" + +from os import environ +from datadog_api_client import ApiClient, Configuration +from datadog_api_client.v2.api.feature_flags_api import FeatureFlagsApi +from datadog_api_client.v2.model.patch_saved_filter_attributes import PatchSavedFilterAttributes +from datadog_api_client.v2.model.patch_saved_filter_data import PatchSavedFilterData +from datadog_api_client.v2.model.patch_saved_filter_request import PatchSavedFilterRequest +from datadog_api_client.v2.model.saved_filter_condition_operator import SavedFilterConditionOperator +from datadog_api_client.v2.model.saved_filter_condition_request import SavedFilterConditionRequest +from datadog_api_client.v2.model.saved_filter_data_type import SavedFilterDataType +from datadog_api_client.v2.model.saved_filter_targeting_rule_request import SavedFilterTargetingRuleRequest +from uuid import UUID + +body = PatchSavedFilterRequest( + data=PatchSavedFilterData( + type=SavedFilterDataType.SAVED_FILTERS, + attributes=PatchSavedFilterAttributes( + name="patchSavedFilterTest-combined-157e7ff3", + description="Combined synthetic test update.", + targeting_rules=[ + SavedFilterTargetingRuleRequest( + conditions=[ + SavedFilterConditionRequest( + operator=SavedFilterConditionOperator.ONE_OF, + attribute="org_id", + value=[ + "test-org-1", + ], + ), + ], + ), + ], + ), + ), +) + +configuration = Configuration() +configuration.access_token = environ["DD_BEARER_TOKEN"] +with ApiClient(configuration) as api_client: + api_instance = FeatureFlagsApi(api_client) + response = api_instance.update_saved_filter_attributes( + saved_filter_id=UUID("2cfc7949-7eb4-4d76-b1b8-d1aa9b565535"), body=body + ) + + print(response) diff --git a/examples/v2/feature-flags/UpdateSavedFilterAttributes_1937053112.py b/examples/v2/feature-flags/UpdateSavedFilterAttributes_1937053112.py new file mode 100644 index 0000000000..3348f8ca0f --- /dev/null +++ b/examples/v2/feature-flags/UpdateSavedFilterAttributes_1937053112.py @@ -0,0 +1,46 @@ +""" +Patch a saved filter replaces rules and preserves metadata +""" + +from os import environ +from datadog_api_client import ApiClient, Configuration +from datadog_api_client.v2.api.feature_flags_api import FeatureFlagsApi +from datadog_api_client.v2.model.patch_saved_filter_attributes import PatchSavedFilterAttributes +from datadog_api_client.v2.model.patch_saved_filter_data import PatchSavedFilterData +from datadog_api_client.v2.model.patch_saved_filter_request import PatchSavedFilterRequest +from datadog_api_client.v2.model.saved_filter_condition_operator import SavedFilterConditionOperator +from datadog_api_client.v2.model.saved_filter_condition_request import SavedFilterConditionRequest +from datadog_api_client.v2.model.saved_filter_data_type import SavedFilterDataType +from datadog_api_client.v2.model.saved_filter_targeting_rule_request import SavedFilterTargetingRuleRequest +from uuid import UUID + +body = PatchSavedFilterRequest( + data=PatchSavedFilterData( + type=SavedFilterDataType.SAVED_FILTERS, + attributes=PatchSavedFilterAttributes( + targeting_rules=[ + SavedFilterTargetingRuleRequest( + conditions=[ + SavedFilterConditionRequest( + operator=SavedFilterConditionOperator.ONE_OF, + attribute="email", + value=[ + "test@example.com", + ], + ), + ], + ), + ], + ), + ), +) + +configuration = Configuration() +configuration.access_token = environ["DD_BEARER_TOKEN"] +with ApiClient(configuration) as api_client: + api_instance = FeatureFlagsApi(api_client) + response = api_instance.update_saved_filter_attributes( + saved_filter_id=UUID("2cfc7949-7eb4-4d76-b1b8-d1aa9b565535"), body=body + ) + + print(response) diff --git a/examples/v2/feature-flags/UpdateSavedFilterAttributes_2658080376.py b/examples/v2/feature-flags/UpdateSavedFilterAttributes_2658080376.py new file mode 100644 index 0000000000..8ede918c15 --- /dev/null +++ b/examples/v2/feature-flags/UpdateSavedFilterAttributes_2658080376.py @@ -0,0 +1,49 @@ +""" +Patch a saved filter accepts the current timestamp for rules +""" + +from os import environ +from datadog_api_client import ApiClient, Configuration +from datadog_api_client.v2.api.feature_flags_api import FeatureFlagsApi +from datadog_api_client.v2.model.patch_saved_filter_attributes import PatchSavedFilterAttributes +from datadog_api_client.v2.model.patch_saved_filter_data import PatchSavedFilterData +from datadog_api_client.v2.model.patch_saved_filter_request import PatchSavedFilterRequest +from datadog_api_client.v2.model.saved_filter_condition_operator import SavedFilterConditionOperator +from datadog_api_client.v2.model.saved_filter_condition_request import SavedFilterConditionRequest +from datadog_api_client.v2.model.saved_filter_data_type import SavedFilterDataType +from datadog_api_client.v2.model.saved_filter_targeting_rule_request import SavedFilterTargetingRuleRequest +from datetime import datetime +from dateutil.tz import tzutc +from uuid import UUID + +body = PatchSavedFilterRequest( + data=PatchSavedFilterData( + attributes=PatchSavedFilterAttributes( + last_updated_at=datetime(2026, 10, 16, 20, 20, 30, tzinfo=tzutc()), + targeting_rules=[ + SavedFilterTargetingRuleRequest( + conditions=[ + SavedFilterConditionRequest( + attribute="user_id", + operator=SavedFilterConditionOperator.ONE_OF, + value=[ + "test-user-2", + ], + ), + ], + ), + ], + ), + type=SavedFilterDataType.SAVED_FILTERS, + ), +) + +configuration = Configuration() +configuration.access_token = environ["DD_BEARER_TOKEN"] +with ApiClient(configuration) as api_client: + api_instance = FeatureFlagsApi(api_client) + response = api_instance.update_saved_filter_attributes( + saved_filter_id=UUID("a8c393b4-3a9c-48fd-aa48-402c97e8d9c9"), body=body + ) + + print(response) diff --git a/examples/v2/feature-flags/UpdateSavedFilterAttributes_281483245.py b/examples/v2/feature-flags/UpdateSavedFilterAttributes_281483245.py new file mode 100644 index 0000000000..ce87c9a502 --- /dev/null +++ b/examples/v2/feature-flags/UpdateSavedFilterAttributes_281483245.py @@ -0,0 +1,31 @@ +""" +Patch a saved filter updates only the description of a saved list +""" + +from os import environ +from datadog_api_client import ApiClient, Configuration +from datadog_api_client.v2.api.feature_flags_api import FeatureFlagsApi +from datadog_api_client.v2.model.patch_saved_filter_attributes import PatchSavedFilterAttributes +from datadog_api_client.v2.model.patch_saved_filter_data import PatchSavedFilterData +from datadog_api_client.v2.model.patch_saved_filter_request import PatchSavedFilterRequest +from datadog_api_client.v2.model.saved_filter_data_type import SavedFilterDataType +from uuid import UUID + +body = PatchSavedFilterRequest( + data=PatchSavedFilterData( + type=SavedFilterDataType.SAVED_FILTERS, + attributes=PatchSavedFilterAttributes( + description="Updated synthetic test description.", + ), + ), +) + +configuration = Configuration() +configuration.access_token = environ["DD_BEARER_TOKEN"] +with ApiClient(configuration) as api_client: + api_instance = FeatureFlagsApi(api_client) + response = api_instance.update_saved_filter_attributes( + saved_filter_id=UUID("d8e3a81b-1505-4600-99c6-c563d01d46b1"), body=body + ) + + print(response) diff --git a/examples/v2/feature-flags/UpdateSavedFilterAttributes_3212710506.py b/examples/v2/feature-flags/UpdateSavedFilterAttributes_3212710506.py new file mode 100644 index 0000000000..7f09a357ff --- /dev/null +++ b/examples/v2/feature-flags/UpdateSavedFilterAttributes_3212710506.py @@ -0,0 +1,32 @@ +""" +Patch a saved filter preserves a null description +""" + +from os import environ +from datadog_api_client import ApiClient, Configuration +from datadog_api_client.v2.api.feature_flags_api import FeatureFlagsApi +from datadog_api_client.v2.model.patch_saved_filter_attributes import PatchSavedFilterAttributes +from datadog_api_client.v2.model.patch_saved_filter_data import PatchSavedFilterData +from datadog_api_client.v2.model.patch_saved_filter_request import PatchSavedFilterRequest +from datadog_api_client.v2.model.saved_filter_data_type import SavedFilterDataType +from uuid import UUID + +body = PatchSavedFilterRequest( + data=PatchSavedFilterData( + type=SavedFilterDataType.SAVED_FILTERS, + attributes=PatchSavedFilterAttributes( + description=None, + name="patchSavedFilterTest-null-157e7ff3", + ), + ), +) + +configuration = Configuration() +configuration.access_token = environ["DD_BEARER_TOKEN"] +with ApiClient(configuration) as api_client: + api_instance = FeatureFlagsApi(api_client) + response = api_instance.update_saved_filter_attributes( + saved_filter_id=UUID("2cfc7949-7eb4-4d76-b1b8-d1aa9b565535"), body=body + ) + + print(response) diff --git a/examples/v2/feature-flags/UpdateSavedFilterAttributes_3817543566.py b/examples/v2/feature-flags/UpdateSavedFilterAttributes_3817543566.py new file mode 100644 index 0000000000..7d4e870bc0 --- /dev/null +++ b/examples/v2/feature-flags/UpdateSavedFilterAttributes_3817543566.py @@ -0,0 +1,34 @@ +""" +Patch a saved filter accepts the current timestamp for metadata +""" + +from os import environ +from datadog_api_client import ApiClient, Configuration +from datadog_api_client.v2.api.feature_flags_api import FeatureFlagsApi +from datadog_api_client.v2.model.patch_saved_filter_attributes import PatchSavedFilterAttributes +from datadog_api_client.v2.model.patch_saved_filter_data import PatchSavedFilterData +from datadog_api_client.v2.model.patch_saved_filter_request import PatchSavedFilterRequest +from datadog_api_client.v2.model.saved_filter_data_type import SavedFilterDataType +from datetime import datetime +from dateutil.tz import tzutc +from uuid import UUID + +body = PatchSavedFilterRequest( + data=PatchSavedFilterData( + attributes=PatchSavedFilterAttributes( + description="Metadata with current timestamp.", + last_updated_at=datetime(2026, 10, 16, 20, 20, 29, tzinfo=tzutc()), + ), + type=SavedFilterDataType.SAVED_FILTERS, + ), +) + +configuration = Configuration() +configuration.access_token = environ["DD_BEARER_TOKEN"] +with ApiClient(configuration) as api_client: + api_instance = FeatureFlagsApi(api_client) + response = api_instance.update_saved_filter_attributes( + saved_filter_id=UUID("a8c393b4-3a9c-48fd-aa48-402c97e8d9c9"), body=body + ) + + print(response) diff --git a/examples/v2/feature-flags/UpdateSavedFilterAttributes_4162852827.py b/examples/v2/feature-flags/UpdateSavedFilterAttributes_4162852827.py new file mode 100644 index 0000000000..37d77dd325 --- /dev/null +++ b/examples/v2/feature-flags/UpdateSavedFilterAttributes_4162852827.py @@ -0,0 +1,31 @@ +""" +Patch a saved filter clears the description with an empty string +""" + +from os import environ +from datadog_api_client import ApiClient, Configuration +from datadog_api_client.v2.api.feature_flags_api import FeatureFlagsApi +from datadog_api_client.v2.model.patch_saved_filter_attributes import PatchSavedFilterAttributes +from datadog_api_client.v2.model.patch_saved_filter_data import PatchSavedFilterData +from datadog_api_client.v2.model.patch_saved_filter_request import PatchSavedFilterRequest +from datadog_api_client.v2.model.saved_filter_data_type import SavedFilterDataType +from uuid import UUID + +body = PatchSavedFilterRequest( + data=PatchSavedFilterData( + type=SavedFilterDataType.SAVED_FILTERS, + attributes=PatchSavedFilterAttributes( + description="", + ), + ), +) + +configuration = Configuration() +configuration.access_token = environ["DD_BEARER_TOKEN"] +with ApiClient(configuration) as api_client: + api_instance = FeatureFlagsApi(api_client) + response = api_instance.update_saved_filter_attributes( + saved_filter_id=UUID("2cfc7949-7eb4-4d76-b1b8-d1aa9b565535"), body=body + ) + + print(response) diff --git a/examples/v2/feature-flags/UpdateSavedFilterAttributes_727520235.py b/examples/v2/feature-flags/UpdateSavedFilterAttributes_727520235.py new file mode 100644 index 0000000000..a697744e00 --- /dev/null +++ b/examples/v2/feature-flags/UpdateSavedFilterAttributes_727520235.py @@ -0,0 +1,46 @@ +""" +Patch a saved filter replaces the rules of a saved list +""" + +from os import environ +from datadog_api_client import ApiClient, Configuration +from datadog_api_client.v2.api.feature_flags_api import FeatureFlagsApi +from datadog_api_client.v2.model.patch_saved_filter_attributes import PatchSavedFilterAttributes +from datadog_api_client.v2.model.patch_saved_filter_data import PatchSavedFilterData +from datadog_api_client.v2.model.patch_saved_filter_request import PatchSavedFilterRequest +from datadog_api_client.v2.model.saved_filter_condition_operator import SavedFilterConditionOperator +from datadog_api_client.v2.model.saved_filter_condition_request import SavedFilterConditionRequest +from datadog_api_client.v2.model.saved_filter_data_type import SavedFilterDataType +from datadog_api_client.v2.model.saved_filter_targeting_rule_request import SavedFilterTargetingRuleRequest +from uuid import UUID + +body = PatchSavedFilterRequest( + data=PatchSavedFilterData( + type=SavedFilterDataType.SAVED_FILTERS, + attributes=PatchSavedFilterAttributes( + targeting_rules=[ + SavedFilterTargetingRuleRequest( + conditions=[ + SavedFilterConditionRequest( + operator=SavedFilterConditionOperator.ONE_OF, + attribute="user_id", + value=[ + "test-user-3", + ], + ), + ], + ), + ], + ), + ), +) + +configuration = Configuration() +configuration.access_token = environ["DD_BEARER_TOKEN"] +with ApiClient(configuration) as api_client: + api_instance = FeatureFlagsApi(api_client) + response = api_instance.update_saved_filter_attributes( + saved_filter_id=UUID("d8e3a81b-1505-4600-99c6-c563d01d46b1"), body=body + ) + + print(response) diff --git a/src/datadog_api_client/v2/api/feature_flags_api.py b/src/datadog_api_client/v2/api/feature_flags_api.py index 9c199b319d..f3d7977af6 100644 --- a/src/datadog_api_client/v2/api/feature_flags_api.py +++ b/src/datadog_api_client/v2/api/feature_flags_api.py @@ -25,6 +25,7 @@ from datadog_api_client.v2.model.update_environment_request import UpdateEnvironmentRequest from datadog_api_client.v2.model.allocation_exposure_schedule_response import AllocationExposureScheduleResponse from datadog_api_client.v2.model.saved_filter_response import SavedFilterResponse +from datadog_api_client.v2.model.patch_saved_filter_request import PatchSavedFilterRequest from datadog_api_client.v2.model.update_feature_flag_request import UpdateFeatureFlagRequest from datadog_api_client.v2.model.allocation_response import AllocationResponse from datadog_api_client.v2.model.create_allocations_request import CreateAllocationsRequest @@ -715,6 +716,32 @@ def __init__(self, api_client=None): api_client=api_client, ) + self._update_saved_filter_attributes_endpoint = _Endpoint( + settings={ + "response_type": (SavedFilterResponse,), + "auth": ["apiKeyAuth", "appKeyAuth", "AuthZ"], + "endpoint_path": "/api/v2/feature-flags/saved-filters/{saved_filter_id}", + "operation_id": "update_saved_filter_attributes", + "http_method": "PATCH", + "version": "v2", + }, + params_map={ + "saved_filter_id": { + "required": True, + "openapi_types": (UUID,), + "attribute": "saved_filter_id", + "location": "path", + }, + "body": { + "required": True, + "openapi_types": (PatchSavedFilterRequest,), + "location": "body", + }, + }, + headers_map={"accept": ["application/json"], "content_type": ["application/json"]}, + api_client=api_client, + ) + self._update_variant_for_feature_flag_endpoint = _Endpoint( settings={ "response_type": (Variant,), @@ -1309,6 +1336,34 @@ def update_feature_flags_environment( return self._update_feature_flags_environment_endpoint.call_with_http_info(**kwargs) + def update_saved_filter_attributes( + self, + saved_filter_id: UUID, + body: PatchSavedFilterRequest, + ) -> SavedFilterResponse: + """Patch a saved filter. + + Partially updates a saved filter. Supports both ``LIST`` and ``RULES`` filters. + Supply at least one of ``name`` , ``description`` , or ``targeting_rules``. Omitted or null fields + are preserved. Supplying ``targeting_rules`` replaces the entire collection and propagates + changes to every feature flag that references the saved filter. + Metadata-only updates do not require edit permission or approval on those feature flags. + Rule updates require edit permission on all consumer flags and are rejected when approval + is required, unless the caller has the ``feature_flag_approvals_override`` permission. + The optional ``last_updated_at`` field detects stale updates. It does not set the update time. + + :param saved_filter_id: The ID of the saved filter. + :type saved_filter_id: UUID + :type body: PatchSavedFilterRequest + :rtype: SavedFilterResponse + """ + kwargs: Dict[str, Any] = {} + kwargs["saved_filter_id"] = saved_filter_id + + kwargs["body"] = body + + return self._update_saved_filter_attributes_endpoint.call_with_http_info(**kwargs) + def update_variant_for_feature_flag( self, feature_flag_id: UUID, diff --git a/src/datadog_api_client/v2/model/patch_saved_filter_attributes.py b/src/datadog_api_client/v2/model/patch_saved_filter_attributes.py new file mode 100644 index 0000000000..88396f2721 --- /dev/null +++ b/src/datadog_api_client/v2/model/patch_saved_filter_attributes.py @@ -0,0 +1,81 @@ +# 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, + datetime, + none_type, + unset, + UnsetType, +) + + +if TYPE_CHECKING: + from datadog_api_client.v2.model.saved_filter_targeting_rule_request import SavedFilterTargetingRuleRequest + + +class PatchSavedFilterAttributes(ModelNormal): + validations = { + "name": { + "min_length": 1, + }, + "targeting_rules": { + "min_items": 1, + }, + } + + @cached_property + def openapi_types(_): + from datadog_api_client.v2.model.saved_filter_targeting_rule_request import SavedFilterTargetingRuleRequest + + return { + "description": (str, none_type), + "last_updated_at": (datetime, none_type), + "name": (str, none_type), + "targeting_rules": ([SavedFilterTargetingRuleRequest], none_type), + } + + attribute_map = { + "description": "description", + "last_updated_at": "last_updated_at", + "name": "name", + "targeting_rules": "targeting_rules", + } + + def __init__( + self_, + description: Union[str, none_type, UnsetType] = unset, + last_updated_at: Union[datetime, none_type, UnsetType] = unset, + name: Union[str, none_type, UnsetType] = unset, + targeting_rules: Union[List[SavedFilterTargetingRuleRequest], none_type, UnsetType] = unset, + **kwargs, + ): + """ + Fields to update. At least one of ``name`` , ``description`` , or ``targeting_rules`` must have a non-null value. Omitted or null fields are preserved. The creation type cannot be changed. + + :param description: The new description. Send an empty string to clear the description. If omitted or null, the existing description is preserved. + :type description: str, none_type, optional + + :param last_updated_at: The ``updated_at`` value returned by a previous read, as an RFC 3339 timestamp. If it differs from the stored update time, truncated to seconds, the request returns a 409 response. For metadata-only updates, this check does not prevent all concurrent writes. This field alone does not constitute an update. + :type last_updated_at: datetime, none_type, optional + + :param name: The new name of the saved filter. Must not be empty. + :type name: str, none_type, optional + + :param targeting_rules: The complete replacement targeting rules, combined with OR. Conditions within each rule are combined with AND. Replaces all existing rules. Values are not appended. Must contain at least one rule. Saved filters cannot reference other saved filters. + :type targeting_rules: [SavedFilterTargetingRuleRequest], none_type, optional + """ + if description is not unset: + kwargs["description"] = description + if last_updated_at is not unset: + kwargs["last_updated_at"] = last_updated_at + if name is not unset: + kwargs["name"] = name + if targeting_rules is not unset: + kwargs["targeting_rules"] = targeting_rules + super().__init__(kwargs) diff --git a/src/datadog_api_client/v2/model/patch_saved_filter_conflict_response.py b/src/datadog_api_client/v2/model/patch_saved_filter_conflict_response.py new file mode 100644 index 0000000000..79fb5cf4db --- /dev/null +++ b/src/datadog_api_client/v2/model/patch_saved_filter_conflict_response.py @@ -0,0 +1,45 @@ +# 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 ( + ModelComposed, + cached_property, +) + + +class PatchSavedFilterConflictResponse(ModelComposed): + def __init__(self, **kwargs): + """ + A stale update, duplicate name, or other conflict, or a resource listing consumer flags requiring approval. + + :param errors: The error details. + :type errors: [JSONAPIErrorItem] + + :param data: One or more feature flags that reference this saved filter require approval for the rule update. + :type data: SavedFilterApprovalConflictData + """ + super().__init__(kwargs) + + @cached_property + def _composed_schemas(_): + # we need this here to make our import statements work + # we must store _composed_schemas in here so the code is only run + # when we invoke this method. If we kept this at the class + # level we would get an error because the class level + # code would be run when this module is imported, and these composed + # classes don't exist yet because their module has not finished + # loading + from datadog_api_client.v2.model.saved_filter_jsonapi_error_response import SavedFilterJSONAPIErrorResponse + from datadog_api_client.v2.model.saved_filter_approval_conflict_response import ( + SavedFilterApprovalConflictResponse, + ) + + return { + "oneOf": [ + SavedFilterJSONAPIErrorResponse, + SavedFilterApprovalConflictResponse, + ], + } diff --git a/src/datadog_api_client/v2/model/patch_saved_filter_data.py b/src/datadog_api_client/v2/model/patch_saved_filter_data.py new file mode 100644 index 0000000000..a73527dd92 --- /dev/null +++ b/src/datadog_api_client/v2/model/patch_saved_filter_data.py @@ -0,0 +1,48 @@ +# 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 TYPE_CHECKING + +from datadog_api_client.model_utils import ( + ModelNormal, + cached_property, +) + + +if TYPE_CHECKING: + from datadog_api_client.v2.model.patch_saved_filter_attributes import PatchSavedFilterAttributes + from datadog_api_client.v2.model.saved_filter_data_type import SavedFilterDataType + + +class PatchSavedFilterData(ModelNormal): + @cached_property + def openapi_types(_): + from datadog_api_client.v2.model.patch_saved_filter_attributes import PatchSavedFilterAttributes + from datadog_api_client.v2.model.saved_filter_data_type import SavedFilterDataType + + return { + "attributes": (PatchSavedFilterAttributes,), + "type": (SavedFilterDataType,), + } + + attribute_map = { + "attributes": "attributes", + "type": "type", + } + + def __init__(self_, attributes: PatchSavedFilterAttributes, type: SavedFilterDataType, **kwargs): + """ + The saved filter fields to update. + + :param attributes: Fields to update. At least one of ``name`` , ``description`` , or ``targeting_rules`` must have a non-null value. Omitted or null fields are preserved. The creation type cannot be changed. + :type attributes: PatchSavedFilterAttributes + + :param type: The saved filter resource type. + :type type: SavedFilterDataType + """ + super().__init__(kwargs) + + self_.attributes = attributes + self_.type = type diff --git a/src/datadog_api_client/v2/model/patch_saved_filter_forbidden_response.py b/src/datadog_api_client/v2/model/patch_saved_filter_forbidden_response.py new file mode 100644 index 0000000000..2af15c807e --- /dev/null +++ b/src/datadog_api_client/v2/model/patch_saved_filter_forbidden_response.py @@ -0,0 +1,47 @@ +# 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 ( + ModelComposed, + cached_property, +) + + +class PatchSavedFilterForbiddenResponse(ModelComposed): + def __init__(self, **kwargs): + """ + An authentication or permission error, or a resource listing consumer flags that the caller cannot edit. + + :param errors: The authorization error messages. + :type errors: [str] + + :param data: The caller lacks edit permission on one or more feature flags that reference this saved filter. + :type data: SavedFilterPermissionDeniedData + """ + super().__init__(kwargs) + + @cached_property + def _composed_schemas(_): + # we need this here to make our import statements work + # we must store _composed_schemas in here so the code is only run + # when we invoke this method. If we kept this at the class + # level we would get an error because the class level + # code would be run when this module is imported, and these composed + # classes don't exist yet because their module has not finished + # loading + from datadog_api_client.v2.model.saved_filter_api_error_response import SavedFilterAPIErrorResponse + from datadog_api_client.v2.model.saved_filter_jsonapi_error_response import SavedFilterJSONAPIErrorResponse + from datadog_api_client.v2.model.saved_filter_permission_denied_response import ( + SavedFilterPermissionDeniedResponse, + ) + + return { + "oneOf": [ + SavedFilterAPIErrorResponse, + SavedFilterJSONAPIErrorResponse, + SavedFilterPermissionDeniedResponse, + ], + } diff --git a/src/datadog_api_client/v2/model/patch_saved_filter_request.py b/src/datadog_api_client/v2/model/patch_saved_filter_request.py new file mode 100644 index 0000000000..c8fbbeede8 --- /dev/null +++ b/src/datadog_api_client/v2/model/patch_saved_filter_request.py @@ -0,0 +1,40 @@ +# 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 TYPE_CHECKING + +from datadog_api_client.model_utils import ( + ModelNormal, + cached_property, +) + + +if TYPE_CHECKING: + from datadog_api_client.v2.model.patch_saved_filter_data import PatchSavedFilterData + + +class PatchSavedFilterRequest(ModelNormal): + @cached_property + def openapi_types(_): + from datadog_api_client.v2.model.patch_saved_filter_data import PatchSavedFilterData + + return { + "data": (PatchSavedFilterData,), + } + + attribute_map = { + "data": "data", + } + + def __init__(self_, data: PatchSavedFilterData, **kwargs): + """ + Request to partially update a saved filter. + + :param data: The saved filter fields to update. + :type data: PatchSavedFilterData + """ + super().__init__(kwargs) + + self_.data = data diff --git a/src/datadog_api_client/v2/model/saved_filter_api_error_response.py b/src/datadog_api_client/v2/model/saved_filter_api_error_response.py new file mode 100644 index 0000000000..6eeebbf661 --- /dev/null +++ b/src/datadog_api_client/v2/model/saved_filter_api_error_response.py @@ -0,0 +1,44 @@ +# 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 + +from datadog_api_client.model_utils import ( + ModelNormal, + cached_property, +) + + +class SavedFilterAPIErrorResponse(ModelNormal): + validations = { + "errors": { + "min_items": 1, + }, + } + + @cached_property + def additional_properties_type(_): + return None + + @cached_property + def openapi_types(_): + return { + "errors": ([str],), + } + + attribute_map = { + "errors": "errors", + } + + def __init__(self_, errors: List[str], **kwargs): + """ + A saved filter authorization error containing at least one error message. + + :param errors: The authorization error messages. + :type errors: [str] + """ + super().__init__(kwargs) + + self_.errors = errors diff --git a/src/datadog_api_client/v2/model/saved_filter_approval_conflict_attributes.py b/src/datadog_api_client/v2/model/saved_filter_approval_conflict_attributes.py new file mode 100644 index 0000000000..4f09fd5cae --- /dev/null +++ b/src/datadog_api_client/v2/model/saved_filter_approval_conflict_attributes.py @@ -0,0 +1,46 @@ +# 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, TYPE_CHECKING + +from datadog_api_client.model_utils import ( + ModelNormal, + cached_property, +) + + +if TYPE_CHECKING: + from datadog_api_client.v2.model.saved_filter_flag_reference import SavedFilterFlagReference + + +class SavedFilterApprovalConflictAttributes(ModelNormal): + @cached_property + def openapi_types(_): + from datadog_api_client.v2.model.saved_filter_flag_reference import SavedFilterFlagReference + + return { + "approval_required_flags": ([SavedFilterFlagReference],), + "message": (str,), + } + + attribute_map = { + "approval_required_flags": "approval_required_flags", + "message": "message", + } + + def __init__(self_, approval_required_flags: List[SavedFilterFlagReference], message: str, **kwargs): + """ + One or more feature flags that reference this saved filter require approval for the rule update. + + :param approval_required_flags: Feature flags preventing this update. + :type approval_required_flags: [SavedFilterFlagReference] + + :param message: The reason the saved filter update was rejected. + :type message: str + """ + super().__init__(kwargs) + + self_.approval_required_flags = approval_required_flags + self_.message = message diff --git a/src/datadog_api_client/v2/model/saved_filter_approval_conflict_data.py b/src/datadog_api_client/v2/model/saved_filter_approval_conflict_data.py new file mode 100644 index 0000000000..9a3cbae783 --- /dev/null +++ b/src/datadog_api_client/v2/model/saved_filter_approval_conflict_data.py @@ -0,0 +1,65 @@ +# 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 TYPE_CHECKING + +from datadog_api_client.model_utils import ( + ModelNormal, + cached_property, + UUID, +) + + +if TYPE_CHECKING: + from datadog_api_client.v2.model.saved_filter_approval_conflict_attributes import ( + SavedFilterApprovalConflictAttributes, + ) + from datadog_api_client.v2.model.saved_filter_approval_conflict_type import SavedFilterApprovalConflictType + + +class SavedFilterApprovalConflictData(ModelNormal): + @cached_property + def openapi_types(_): + from datadog_api_client.v2.model.saved_filter_approval_conflict_attributes import ( + SavedFilterApprovalConflictAttributes, + ) + from datadog_api_client.v2.model.saved_filter_approval_conflict_type import SavedFilterApprovalConflictType + + return { + "attributes": (SavedFilterApprovalConflictAttributes,), + "id": (UUID,), + "type": (SavedFilterApprovalConflictType,), + } + + attribute_map = { + "attributes": "attributes", + "id": "id", + "type": "type", + } + + def __init__( + self_, + attributes: SavedFilterApprovalConflictAttributes, + id: UUID, + type: SavedFilterApprovalConflictType, + **kwargs, + ): + """ + One or more feature flags that reference this saved filter require approval for the rule update. + + :param attributes: One or more feature flags that reference this saved filter require approval for the rule update. + :type attributes: SavedFilterApprovalConflictAttributes + + :param id: The ID of the saved filter. + :type id: UUID + + :param type: The resource type for this saved filter update error. + :type type: SavedFilterApprovalConflictType + """ + super().__init__(kwargs) + + self_.attributes = attributes + self_.id = id + self_.type = type diff --git a/src/datadog_api_client/v2/model/saved_filter_approval_conflict_response.py b/src/datadog_api_client/v2/model/saved_filter_approval_conflict_response.py new file mode 100644 index 0000000000..cd949b433a --- /dev/null +++ b/src/datadog_api_client/v2/model/saved_filter_approval_conflict_response.py @@ -0,0 +1,44 @@ +# 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 TYPE_CHECKING + +from datadog_api_client.model_utils import ( + ModelNormal, + cached_property, +) + + +if TYPE_CHECKING: + from datadog_api_client.v2.model.saved_filter_approval_conflict_data import SavedFilterApprovalConflictData + + +class SavedFilterApprovalConflictResponse(ModelNormal): + @cached_property + def additional_properties_type(_): + return None + + @cached_property + def openapi_types(_): + from datadog_api_client.v2.model.saved_filter_approval_conflict_data import SavedFilterApprovalConflictData + + return { + "data": (SavedFilterApprovalConflictData,), + } + + attribute_map = { + "data": "data", + } + + def __init__(self_, data: SavedFilterApprovalConflictData, **kwargs): + """ + One or more feature flags that reference this saved filter require approval for the rule update. + + :param data: One or more feature flags that reference this saved filter require approval for the rule update. + :type data: SavedFilterApprovalConflictData + """ + super().__init__(kwargs) + + self_.data = data diff --git a/src/datadog_api_client/v2/model/saved_filter_approval_conflict_type.py b/src/datadog_api_client/v2/model/saved_filter_approval_conflict_type.py new file mode 100644 index 0000000000..0aa17f75cf --- /dev/null +++ b/src/datadog_api_client/v2/model/saved_filter_approval_conflict_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 SavedFilterApprovalConflictType(ModelSimple): + """ + The resource type for this saved filter update error. + + :param value: If omitted defaults to "update-approval-conflict". Must be one of ["update-approval-conflict"]. + :type value: str + """ + + allowed_values = { + "update-approval-conflict", + } + UPDATE_APPROVAL_CONFLICT: ClassVar["SavedFilterApprovalConflictType"] + + @cached_property + def openapi_types(_): + return { + "value": (str,), + } + + +SavedFilterApprovalConflictType.UPDATE_APPROVAL_CONFLICT = SavedFilterApprovalConflictType("update-approval-conflict") diff --git a/src/datadog_api_client/v2/model/saved_filter_condition_request.py b/src/datadog_api_client/v2/model/saved_filter_condition_request.py new file mode 100644 index 0000000000..c28d95c24c --- /dev/null +++ b/src/datadog_api_client/v2/model/saved_filter_condition_request.py @@ -0,0 +1,61 @@ +# 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, TYPE_CHECKING + +from datadog_api_client.model_utils import ( + ModelNormal, + cached_property, +) + + +if TYPE_CHECKING: + from datadog_api_client.v2.model.saved_filter_condition_operator import SavedFilterConditionOperator + + +class SavedFilterConditionRequest(ModelNormal): + validations = { + "attribute": { + "min_length": 1, + }, + "value": { + "min_items": 1, + }, + } + + @cached_property + def openapi_types(_): + from datadog_api_client.v2.model.saved_filter_condition_operator import SavedFilterConditionOperator + + return { + "attribute": (str,), + "operator": (SavedFilterConditionOperator,), + "value": ([str],), + } + + attribute_map = { + "attribute": "attribute", + "operator": "operator", + "value": "value", + } + + def __init__(self_, attribute: str, operator: SavedFilterConditionOperator, value: List[str], **kwargs): + """ + An inline condition to write in a saved filter. Nested saved-filter references are not supported. + + :param attribute: The attribute evaluated by the condition. + :type attribute: str + + :param operator: The comparison operator used by a saved filter condition. + :type operator: SavedFilterConditionOperator + + :param value: Values used by the condition operator. Values must satisfy the selected operator and the organization targeting limits. + :type value: [str] + """ + super().__init__(kwargs) + + self_.attribute = attribute + self_.operator = operator + self_.value = value diff --git a/src/datadog_api_client/v2/model/saved_filter_flag_reference.py b/src/datadog_api_client/v2/model/saved_filter_flag_reference.py new file mode 100644 index 0000000000..a1a3c9dad8 --- /dev/null +++ b/src/datadog_api_client/v2/model/saved_filter_flag_reference.py @@ -0,0 +1,40 @@ +# 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 ( + ModelNormal, + cached_property, + UUID, +) + + +class SavedFilterFlagReference(ModelNormal): + @cached_property + def openapi_types(_): + return { + "id": (UUID,), + "name": (str,), + } + + attribute_map = { + "id": "id", + "name": "name", + } + + def __init__(self_, id: UUID, name: str, **kwargs): + """ + A feature flag that references the saved filter. + + :param id: The ID of the feature flag. + :type id: UUID + + :param name: The name of the feature flag. + :type name: str + """ + super().__init__(kwargs) + + self_.id = id + self_.name = name diff --git a/src/datadog_api_client/v2/model/saved_filter_jsonapi_error_response.py b/src/datadog_api_client/v2/model/saved_filter_jsonapi_error_response.py new file mode 100644 index 0000000000..15d442b897 --- /dev/null +++ b/src/datadog_api_client/v2/model/saved_filter_jsonapi_error_response.py @@ -0,0 +1,50 @@ +# 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, TYPE_CHECKING + +from datadog_api_client.model_utils import ( + ModelNormal, + cached_property, +) + + +if TYPE_CHECKING: + from datadog_api_client.v2.model.jsonapi_error_item import JSONAPIErrorItem + + +class SavedFilterJSONAPIErrorResponse(ModelNormal): + validations = { + "errors": { + "min_items": 1, + }, + } + + @cached_property + def additional_properties_type(_): + return None + + @cached_property + def openapi_types(_): + from datadog_api_client.v2.model.jsonapi_error_item import JSONAPIErrorItem + + return { + "errors": ([JSONAPIErrorItem],), + } + + attribute_map = { + "errors": "errors", + } + + def __init__(self_, errors: List[JSONAPIErrorItem], **kwargs): + """ + A saved filter error containing at least one JSON:API error object. + + :param errors: The error details. + :type errors: [JSONAPIErrorItem] + """ + super().__init__(kwargs) + + self_.errors = errors diff --git a/src/datadog_api_client/v2/model/saved_filter_permission_denied_attributes.py b/src/datadog_api_client/v2/model/saved_filter_permission_denied_attributes.py new file mode 100644 index 0000000000..f9b9742d07 --- /dev/null +++ b/src/datadog_api_client/v2/model/saved_filter_permission_denied_attributes.py @@ -0,0 +1,46 @@ +# 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, TYPE_CHECKING + +from datadog_api_client.model_utils import ( + ModelNormal, + cached_property, +) + + +if TYPE_CHECKING: + from datadog_api_client.v2.model.saved_filter_flag_reference import SavedFilterFlagReference + + +class SavedFilterPermissionDeniedAttributes(ModelNormal): + @cached_property + def openapi_types(_): + from datadog_api_client.v2.model.saved_filter_flag_reference import SavedFilterFlagReference + + return { + "denied_flags": ([SavedFilterFlagReference],), + "message": (str,), + } + + attribute_map = { + "denied_flags": "denied_flags", + "message": "message", + } + + def __init__(self_, denied_flags: List[SavedFilterFlagReference], message: str, **kwargs): + """ + The caller lacks edit permission on one or more feature flags that reference this saved filter. + + :param denied_flags: Feature flags preventing this update. + :type denied_flags: [SavedFilterFlagReference] + + :param message: The reason the saved filter update was rejected. + :type message: str + """ + super().__init__(kwargs) + + self_.denied_flags = denied_flags + self_.message = message diff --git a/src/datadog_api_client/v2/model/saved_filter_permission_denied_data.py b/src/datadog_api_client/v2/model/saved_filter_permission_denied_data.py new file mode 100644 index 0000000000..4e32337342 --- /dev/null +++ b/src/datadog_api_client/v2/model/saved_filter_permission_denied_data.py @@ -0,0 +1,65 @@ +# 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 TYPE_CHECKING + +from datadog_api_client.model_utils import ( + ModelNormal, + cached_property, + UUID, +) + + +if TYPE_CHECKING: + from datadog_api_client.v2.model.saved_filter_permission_denied_attributes import ( + SavedFilterPermissionDeniedAttributes, + ) + from datadog_api_client.v2.model.saved_filter_permission_denied_type import SavedFilterPermissionDeniedType + + +class SavedFilterPermissionDeniedData(ModelNormal): + @cached_property + def openapi_types(_): + from datadog_api_client.v2.model.saved_filter_permission_denied_attributes import ( + SavedFilterPermissionDeniedAttributes, + ) + from datadog_api_client.v2.model.saved_filter_permission_denied_type import SavedFilterPermissionDeniedType + + return { + "attributes": (SavedFilterPermissionDeniedAttributes,), + "id": (UUID,), + "type": (SavedFilterPermissionDeniedType,), + } + + attribute_map = { + "attributes": "attributes", + "id": "id", + "type": "type", + } + + def __init__( + self_, + attributes: SavedFilterPermissionDeniedAttributes, + id: UUID, + type: SavedFilterPermissionDeniedType, + **kwargs, + ): + """ + The caller lacks edit permission on one or more feature flags that reference this saved filter. + + :param attributes: The caller lacks edit permission on one or more feature flags that reference this saved filter. + :type attributes: SavedFilterPermissionDeniedAttributes + + :param id: The ID of the saved filter. + :type id: UUID + + :param type: The resource type for this saved filter update error. + :type type: SavedFilterPermissionDeniedType + """ + super().__init__(kwargs) + + self_.attributes = attributes + self_.id = id + self_.type = type diff --git a/src/datadog_api_client/v2/model/saved_filter_permission_denied_response.py b/src/datadog_api_client/v2/model/saved_filter_permission_denied_response.py new file mode 100644 index 0000000000..0672c25cb1 --- /dev/null +++ b/src/datadog_api_client/v2/model/saved_filter_permission_denied_response.py @@ -0,0 +1,44 @@ +# 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 TYPE_CHECKING + +from datadog_api_client.model_utils import ( + ModelNormal, + cached_property, +) + + +if TYPE_CHECKING: + from datadog_api_client.v2.model.saved_filter_permission_denied_data import SavedFilterPermissionDeniedData + + +class SavedFilterPermissionDeniedResponse(ModelNormal): + @cached_property + def additional_properties_type(_): + return None + + @cached_property + def openapi_types(_): + from datadog_api_client.v2.model.saved_filter_permission_denied_data import SavedFilterPermissionDeniedData + + return { + "data": (SavedFilterPermissionDeniedData,), + } + + attribute_map = { + "data": "data", + } + + def __init__(self_, data: SavedFilterPermissionDeniedData, **kwargs): + """ + The caller lacks edit permission on one or more feature flags that reference this saved filter. + + :param data: The caller lacks edit permission on one or more feature flags that reference this saved filter. + :type data: SavedFilterPermissionDeniedData + """ + super().__init__(kwargs) + + self_.data = data diff --git a/src/datadog_api_client/v2/model/saved_filter_permission_denied_type.py b/src/datadog_api_client/v2/model/saved_filter_permission_denied_type.py new file mode 100644 index 0000000000..8fe5818a48 --- /dev/null +++ b/src/datadog_api_client/v2/model/saved_filter_permission_denied_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 SavedFilterPermissionDeniedType(ModelSimple): + """ + The resource type for this saved filter update error. + + :param value: If omitted defaults to "update-grace-denied". Must be one of ["update-grace-denied"]. + :type value: str + """ + + allowed_values = { + "update-grace-denied", + } + UPDATE_GRACE_DENIED: ClassVar["SavedFilterPermissionDeniedType"] + + @cached_property + def openapi_types(_): + return { + "value": (str,), + } + + +SavedFilterPermissionDeniedType.UPDATE_GRACE_DENIED = SavedFilterPermissionDeniedType("update-grace-denied") diff --git a/src/datadog_api_client/v2/model/saved_filter_targeting_rule_request.py b/src/datadog_api_client/v2/model/saved_filter_targeting_rule_request.py new file mode 100644 index 0000000000..d5734de782 --- /dev/null +++ b/src/datadog_api_client/v2/model/saved_filter_targeting_rule_request.py @@ -0,0 +1,46 @@ +# 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, TYPE_CHECKING + +from datadog_api_client.model_utils import ( + ModelNormal, + cached_property, +) + + +if TYPE_CHECKING: + from datadog_api_client.v2.model.saved_filter_condition_request import SavedFilterConditionRequest + + +class SavedFilterTargetingRuleRequest(ModelNormal): + validations = { + "conditions": { + "min_items": 1, + }, + } + + @cached_property + def openapi_types(_): + from datadog_api_client.v2.model.saved_filter_condition_request import SavedFilterConditionRequest + + return { + "conditions": ([SavedFilterConditionRequest],), + } + + attribute_map = { + "conditions": "conditions", + } + + def __init__(self_, conditions: List[SavedFilterConditionRequest], **kwargs): + """ + A replacement saved filter targeting rule. + + :param conditions: Inline conditions combined with AND. Must contain at least one condition. + :type conditions: [SavedFilterConditionRequest] + """ + super().__init__(kwargs) + + self_.conditions = conditions diff --git a/src/datadog_api_client/v2/models/__init__.py b/src/datadog_api_client/v2/models/__init__.py index f0c1d2f69e..0885f3e70b 100644 --- a/src/datadog_api_client/v2/models/__init__.py +++ b/src/datadog_api_client/v2/models/__init__.py @@ -8264,6 +8264,11 @@ from datadog_api_client.v2.model.patch_notification_rule_parameters_data_attributes import ( PatchNotificationRuleParametersDataAttributes, ) +from datadog_api_client.v2.model.patch_saved_filter_attributes import PatchSavedFilterAttributes +from datadog_api_client.v2.model.patch_saved_filter_conflict_response import PatchSavedFilterConflictResponse +from datadog_api_client.v2.model.patch_saved_filter_data import PatchSavedFilterData +from datadog_api_client.v2.model.patch_saved_filter_forbidden_response import PatchSavedFilterForbiddenResponse +from datadog_api_client.v2.model.patch_saved_filter_request import PatchSavedFilterRequest from datadog_api_client.v2.model.patch_status_page_request import PatchStatusPageRequest from datadog_api_client.v2.model.patch_status_page_request_data import PatchStatusPageRequestData from datadog_api_client.v2.model.patch_status_page_request_data_attributes import PatchStatusPageRequestDataAttributes @@ -9559,14 +9564,27 @@ from datadog_api_client.v2.model.sast_ruleset_data_attributes import SastRulesetDataAttributes from datadog_api_client.v2.model.sast_ruleset_response import SastRulesetResponse from datadog_api_client.v2.model.sast_rulesets_response import SastRulesetsResponse +from datadog_api_client.v2.model.saved_filter_api_error_response import SavedFilterAPIErrorResponse +from datadog_api_client.v2.model.saved_filter_approval_conflict_attributes import SavedFilterApprovalConflictAttributes +from datadog_api_client.v2.model.saved_filter_approval_conflict_data import SavedFilterApprovalConflictData +from datadog_api_client.v2.model.saved_filter_approval_conflict_response import SavedFilterApprovalConflictResponse +from datadog_api_client.v2.model.saved_filter_approval_conflict_type import SavedFilterApprovalConflictType from datadog_api_client.v2.model.saved_filter_attributes import SavedFilterAttributes from datadog_api_client.v2.model.saved_filter_condition import SavedFilterCondition from datadog_api_client.v2.model.saved_filter_condition_operator import SavedFilterConditionOperator +from datadog_api_client.v2.model.saved_filter_condition_request import SavedFilterConditionRequest from datadog_api_client.v2.model.saved_filter_creation_type import SavedFilterCreationType from datadog_api_client.v2.model.saved_filter_data import SavedFilterData from datadog_api_client.v2.model.saved_filter_data_type import SavedFilterDataType +from datadog_api_client.v2.model.saved_filter_flag_reference import SavedFilterFlagReference +from datadog_api_client.v2.model.saved_filter_jsonapi_error_response import SavedFilterJSONAPIErrorResponse +from datadog_api_client.v2.model.saved_filter_permission_denied_attributes import SavedFilterPermissionDeniedAttributes +from datadog_api_client.v2.model.saved_filter_permission_denied_data import SavedFilterPermissionDeniedData +from datadog_api_client.v2.model.saved_filter_permission_denied_response import SavedFilterPermissionDeniedResponse +from datadog_api_client.v2.model.saved_filter_permission_denied_type import SavedFilterPermissionDeniedType from datadog_api_client.v2.model.saved_filter_response import SavedFilterResponse from datadog_api_client.v2.model.saved_filter_targeting_rule import SavedFilterTargetingRule +from datadog_api_client.v2.model.saved_filter_targeting_rule_request import SavedFilterTargetingRuleRequest from datadog_api_client.v2.model.sca_request import ScaRequest from datadog_api_client.v2.model.sca_request_data import ScaRequestData from datadog_api_client.v2.model.sca_request_data_attributes import ScaRequestDataAttributes @@ -18381,6 +18399,11 @@ "PatchNotificationRuleParameters", "PatchNotificationRuleParametersData", "PatchNotificationRuleParametersDataAttributes", + "PatchSavedFilterAttributes", + "PatchSavedFilterConflictResponse", + "PatchSavedFilterData", + "PatchSavedFilterForbiddenResponse", + "PatchSavedFilterRequest", "PatchStatusPageRequest", "PatchStatusPageRequestData", "PatchStatusPageRequestDataAttributes", @@ -19316,14 +19339,27 @@ "SastRulesetDataAttributes", "SastRulesetResponse", "SastRulesetsResponse", + "SavedFilterAPIErrorResponse", + "SavedFilterApprovalConflictAttributes", + "SavedFilterApprovalConflictData", + "SavedFilterApprovalConflictResponse", + "SavedFilterApprovalConflictType", "SavedFilterAttributes", "SavedFilterCondition", "SavedFilterConditionOperator", + "SavedFilterConditionRequest", "SavedFilterCreationType", "SavedFilterData", "SavedFilterDataType", + "SavedFilterFlagReference", + "SavedFilterJSONAPIErrorResponse", + "SavedFilterPermissionDeniedAttributes", + "SavedFilterPermissionDeniedData", + "SavedFilterPermissionDeniedResponse", + "SavedFilterPermissionDeniedType", "SavedFilterResponse", "SavedFilterTargetingRule", + "SavedFilterTargetingRuleRequest", "ScaRequest", "ScaRequestData", "ScaRequestDataAttributes", diff --git a/tests/generated-test/test-runner-data/manifest.json b/tests/generated-test/test-runner-data/manifest.json index f22eaf0cef..dce8e01f02 100644 --- a/tests/generated-test/test-runner-data/manifest.json +++ b/tests/generated-test/test-runner-data/manifest.json @@ -6055,6 +6055,146 @@ "scenario": "List feature flags supports legacy pagination aliases", "version": "v2" }, + { + "feature": "Feature Flags", + "feature_file": "../../v2/features/feature_flags.feature", + "file": "v2/feature-flags/patch-a-saved-filter-accepts-the-current-timestamp-for-metadata.json", + "scenario": "Patch a saved filter accepts the current timestamp for metadata", + "version": "v2" + }, + { + "feature": "Feature Flags", + "feature_file": "../../v2/features/feature_flags.feature", + "file": "v2/feature-flags/patch-a-saved-filter-accepts-the-current-timestamp-for-rules.json", + "scenario": "Patch a saved filter accepts the current timestamp for rules", + "version": "v2" + }, + { + "feature": "Feature Flags", + "feature_file": "../../v2/features/feature_flags.feature", + "file": "v2/feature-flags/patch-a-saved-filter-clears-the-description-with-an-empty-string.json", + "scenario": "Patch a saved filter clears the description with an empty string", + "version": "v2" + }, + { + "feature": "Feature Flags", + "feature_file": "../../v2/features/feature_flags.feature", + "file": "v2/feature-flags/patch-a-saved-filter-preserves-a-null-description.json", + "scenario": "Patch a saved filter preserves a null description", + "version": "v2" + }, + { + "feature": "Feature Flags", + "feature_file": "../../v2/features/feature_flags.feature", + "file": "v2/feature-flags/patch-a-saved-filter-rejects-a-duplicate-name.json", + "scenario": "Patch a saved filter rejects a duplicate name", + "version": "v2" + }, + { + "feature": "Feature Flags", + "feature_file": "../../v2/features/feature_flags.feature", + "file": "v2/feature-flags/patch-a-saved-filter-rejects-a-null-only-update.json", + "scenario": "Patch a saved filter rejects a null-only update", + "version": "v2" + }, + { + "feature": "Feature Flags", + "feature_file": "../../v2/features/feature_flags.feature", + "file": "v2/feature-flags/patch-a-saved-filter-rejects-a-timestamp-only-update.json", + "scenario": "Patch a saved filter rejects a timestamp-only update", + "version": "v2" + }, + { + "feature": "Feature Flags", + "feature_file": "../../v2/features/feature_flags.feature", + "file": "v2/feature-flags/patch-a-saved-filter-rejects-an-empty-name.json", + "scenario": "Patch a saved filter rejects an empty name", + "version": "v2" + }, + { + "feature": "Feature Flags", + "feature_file": "../../v2/features/feature_flags.feature", + "file": "v2/feature-flags/patch-a-saved-filter-rejects-an-invalid-timestamp.json", + "scenario": "Patch a saved filter rejects an invalid timestamp", + "version": "v2" + }, + { + "feature": "Feature Flags", + "feature_file": "../../v2/features/feature_flags.feature", + "file": "v2/feature-flags/patch-a-saved-filter-rejects-empty-targeting-rules.json", + "scenario": "Patch a saved filter rejects empty targeting rules", + "version": "v2" + }, + { + "feature": "Feature Flags", + "feature_file": "../../v2/features/feature_flags.feature", + "file": "v2/feature-flags/patch-a-saved-filter-rejects-nested-saved-filters.json", + "scenario": "Patch a saved filter rejects nested saved filters", + "version": "v2" + }, + { + "feature": "Feature Flags", + "feature_file": "../../v2/features/feature_flags.feature", + "file": "v2/feature-flags/patch-a-saved-filter-rejects-stale-rule-updates.json", + "scenario": "Patch a saved filter rejects stale rule updates", + "version": "v2" + }, + { + "feature": "Feature Flags", + "feature_file": "../../v2/features/feature_flags.feature", + "file": "v2/feature-flags/patch-a-saved-filter-replaces-rules-and-preserves-metadata.json", + "scenario": "Patch a saved filter replaces rules and preserves metadata", + "version": "v2" + }, + { + "feature": "Feature Flags", + "feature_file": "../../v2/features/feature_flags.feature", + "file": "v2/feature-flags/patch-a-saved-filter-replaces-the-rules-of-a-saved-list.json", + "scenario": "Patch a saved filter replaces the rules of a saved list", + "version": "v2" + }, + { + "feature": "Feature Flags", + "feature_file": "../../v2/features/feature_flags.feature", + "file": "v2/feature-flags/patch-a-saved-filter-returns-bad-request-response.json", + "scenario": "Patch a saved filter returns \"Bad Request\" response", + "version": "v2" + }, + { + "feature": "Feature Flags", + "feature_file": "../../v2/features/feature_flags.feature", + "file": "v2/feature-flags/patch-a-saved-filter-returns-conflict-response.json", + "scenario": "Patch a saved filter returns \"Conflict\" response", + "version": "v2" + }, + { + "feature": "Feature Flags", + "feature_file": "../../v2/features/feature_flags.feature", + "file": "v2/feature-flags/patch-a-saved-filter-returns-not-found-response.json", + "scenario": "Patch a saved filter returns \"Not Found\" response", + "version": "v2" + }, + { + "feature": "Feature Flags", + "feature_file": "../../v2/features/feature_flags.feature", + "file": "v2/feature-flags/patch-a-saved-filter-returns-ok-response.json", + "scenario": "Patch a saved filter returns \"OK\" response", + "version": "v2" + }, + { + "feature": "Feature Flags", + "feature_file": "../../v2/features/feature_flags.feature", + "file": "v2/feature-flags/patch-a-saved-filter-updates-metadata-and-rules-together.json", + "scenario": "Patch a saved filter updates metadata and rules together", + "version": "v2" + }, + { + "feature": "Feature Flags", + "feature_file": "../../v2/features/feature_flags.feature", + "file": "v2/feature-flags/patch-a-saved-filter-updates-only-the-description-of-a-saved-list.json", + "scenario": "Patch a saved filter updates only the description of a saved list", + "version": "v2" + }, { "feature": "Feature Flags", "feature_file": "../../v2/features/feature_flags.feature", diff --git a/tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-accepts-the-current-timestamp-for-metadata.json b/tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-accepts-the-current-timestamp-for-metadata.json new file mode 100644 index 0000000000..1d4c266706 --- /dev/null +++ b/tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-accepts-the-current-timestamp-for-metadata.json @@ -0,0 +1,51 @@ +{ + "api": "FeatureFlags", + "expected_status": 200, + "feature": "Feature Flags", + "id": "v2/Feature Flags/Patch a saved filter accepts the current timestamp for metadata", + "operation_id": "UpdateSavedFilterAttributes", + "request": { + "body": { + "schema": { + "format": null, + "ref": "PatchSavedFilterRequest", + "type": "object" + }, + "source": "inline", + "value": { + "data": { + "attributes": { + "description": "Metadata with current timestamp.", + "last_updated_at": "2026-10-16T20:20:29Z" + }, + "type": "saved-filters" + } + } + }, + "content_type": "application/json", + "method": "PATCH", + "pagination": false, + "parameters": [ + { + "explode": null, + "in": "path", + "name": "saved_filter_id", + "required": true, + "schema": { + "format": "uuid", + "ref": null, + "type": "string" + }, + "source": { + "type": "literal", + "value": "a8c393b4-3a9c-48fd-aa48-402c97e8d9c9" + }, + "style": null + } + ], + "path": "/api/v2/feature-flags/saved-filters/{saved_filter_id}" + }, + "scenario": "Patch a saved filter accepts the current timestamp for metadata", + "schema_version": 1, + "version": "v2" +} diff --git a/tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-accepts-the-current-timestamp-for-rules.json b/tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-accepts-the-current-timestamp-for-rules.json new file mode 100644 index 0000000000..8060c0e5e5 --- /dev/null +++ b/tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-accepts-the-current-timestamp-for-rules.json @@ -0,0 +1,63 @@ +{ + "api": "FeatureFlags", + "expected_status": 200, + "feature": "Feature Flags", + "id": "v2/Feature Flags/Patch a saved filter accepts the current timestamp for rules", + "operation_id": "UpdateSavedFilterAttributes", + "request": { + "body": { + "schema": { + "format": null, + "ref": "PatchSavedFilterRequest", + "type": "object" + }, + "source": "inline", + "value": { + "data": { + "attributes": { + "last_updated_at": "2026-10-16T20:20:30Z", + "targeting_rules": [ + { + "conditions": [ + { + "attribute": "user_id", + "operator": "ONE_OF", + "value": [ + "test-user-2" + ] + } + ] + } + ] + }, + "type": "saved-filters" + } + } + }, + "content_type": "application/json", + "method": "PATCH", + "pagination": false, + "parameters": [ + { + "explode": null, + "in": "path", + "name": "saved_filter_id", + "required": true, + "schema": { + "format": "uuid", + "ref": null, + "type": "string" + }, + "source": { + "type": "literal", + "value": "a8c393b4-3a9c-48fd-aa48-402c97e8d9c9" + }, + "style": null + } + ], + "path": "/api/v2/feature-flags/saved-filters/{saved_filter_id}" + }, + "scenario": "Patch a saved filter accepts the current timestamp for rules", + "schema_version": 1, + "version": "v2" +} diff --git a/tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-clears-the-description-with-an-empty-string.json b/tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-clears-the-description-with-an-empty-string.json new file mode 100644 index 0000000000..c875aa0264 --- /dev/null +++ b/tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-clears-the-description-with-an-empty-string.json @@ -0,0 +1,50 @@ +{ + "api": "FeatureFlags", + "expected_status": 200, + "feature": "Feature Flags", + "id": "v2/Feature Flags/Patch a saved filter clears the description with an empty string", + "operation_id": "UpdateSavedFilterAttributes", + "request": { + "body": { + "schema": { + "format": null, + "ref": "PatchSavedFilterRequest", + "type": "object" + }, + "source": "inline", + "value": { + "data": { + "attributes": { + "description": "" + }, + "type": "saved-filters" + } + } + }, + "content_type": "application/json", + "method": "PATCH", + "pagination": false, + "parameters": [ + { + "explode": null, + "in": "path", + "name": "saved_filter_id", + "required": true, + "schema": { + "format": "uuid", + "ref": null, + "type": "string" + }, + "source": { + "type": "literal", + "value": "2cfc7949-7eb4-4d76-b1b8-d1aa9b565535" + }, + "style": null + } + ], + "path": "/api/v2/feature-flags/saved-filters/{saved_filter_id}" + }, + "scenario": "Patch a saved filter clears the description with an empty string", + "schema_version": 1, + "version": "v2" +} diff --git a/tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-preserves-a-null-description.json b/tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-preserves-a-null-description.json new file mode 100644 index 0000000000..5891aa60db --- /dev/null +++ b/tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-preserves-a-null-description.json @@ -0,0 +1,51 @@ +{ + "api": "FeatureFlags", + "expected_status": 200, + "feature": "Feature Flags", + "id": "v2/Feature Flags/Patch a saved filter preserves a null description", + "operation_id": "UpdateSavedFilterAttributes", + "request": { + "body": { + "schema": { + "format": null, + "ref": "PatchSavedFilterRequest", + "type": "object" + }, + "source": "inline", + "value": { + "data": { + "attributes": { + "description": null, + "name": "patchSavedFilterTest-null-157e7ff3" + }, + "type": "saved-filters" + } + } + }, + "content_type": "application/json", + "method": "PATCH", + "pagination": false, + "parameters": [ + { + "explode": null, + "in": "path", + "name": "saved_filter_id", + "required": true, + "schema": { + "format": "uuid", + "ref": null, + "type": "string" + }, + "source": { + "type": "literal", + "value": "2cfc7949-7eb4-4d76-b1b8-d1aa9b565535" + }, + "style": null + } + ], + "path": "/api/v2/feature-flags/saved-filters/{saved_filter_id}" + }, + "scenario": "Patch a saved filter preserves a null description", + "schema_version": 1, + "version": "v2" +} diff --git a/tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-rejects-a-duplicate-name.json b/tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-rejects-a-duplicate-name.json new file mode 100644 index 0000000000..93b05fed14 --- /dev/null +++ b/tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-rejects-a-duplicate-name.json @@ -0,0 +1,50 @@ +{ + "api": "FeatureFlags", + "expected_status": 409, + "feature": "Feature Flags", + "id": "v2/Feature Flags/Patch a saved filter rejects a duplicate name", + "operation_id": "UpdateSavedFilterAttributes", + "request": { + "body": { + "schema": { + "format": null, + "ref": "PatchSavedFilterRequest", + "type": "object" + }, + "source": "inline", + "value": { + "data": { + "attributes": { + "name": "patchSavedFilterTest-LIST-157e7ff3" + }, + "type": "saved-filters" + } + } + }, + "content_type": "application/json", + "method": "PATCH", + "pagination": false, + "parameters": [ + { + "explode": null, + "in": "path", + "name": "saved_filter_id", + "required": true, + "schema": { + "format": "uuid", + "ref": null, + "type": "string" + }, + "source": { + "type": "literal", + "value": "2cfc7949-7eb4-4d76-b1b8-d1aa9b565535" + }, + "style": null + } + ], + "path": "/api/v2/feature-flags/saved-filters/{saved_filter_id}" + }, + "scenario": "Patch a saved filter rejects a duplicate name", + "schema_version": 1, + "version": "v2" +} diff --git a/tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-rejects-a-null-only-update.json b/tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-rejects-a-null-only-update.json new file mode 100644 index 0000000000..2d587c4a2b --- /dev/null +++ b/tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-rejects-a-null-only-update.json @@ -0,0 +1,50 @@ +{ + "api": "FeatureFlags", + "expected_status": 400, + "feature": "Feature Flags", + "id": "v2/Feature Flags/Patch a saved filter rejects a null-only update", + "operation_id": "UpdateSavedFilterAttributes", + "request": { + "body": { + "schema": { + "format": null, + "ref": "PatchSavedFilterRequest", + "type": "object" + }, + "source": "inline", + "value": { + "data": { + "attributes": { + "description": null + }, + "type": "saved-filters" + } + } + }, + "content_type": "application/json", + "method": "PATCH", + "pagination": false, + "parameters": [ + { + "explode": null, + "in": "path", + "name": "saved_filter_id", + "required": true, + "schema": { + "format": "uuid", + "ref": null, + "type": "string" + }, + "source": { + "type": "literal", + "value": "2cfc7949-7eb4-4d76-b1b8-d1aa9b565535" + }, + "style": null + } + ], + "path": "/api/v2/feature-flags/saved-filters/{saved_filter_id}" + }, + "scenario": "Patch a saved filter rejects a null-only update", + "schema_version": 1, + "version": "v2" +} diff --git a/tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-rejects-a-timestamp-only-update.json b/tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-rejects-a-timestamp-only-update.json new file mode 100644 index 0000000000..62f7ee14ee --- /dev/null +++ b/tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-rejects-a-timestamp-only-update.json @@ -0,0 +1,50 @@ +{ + "api": "FeatureFlags", + "expected_status": 400, + "feature": "Feature Flags", + "id": "v2/Feature Flags/Patch a saved filter rejects a timestamp-only update", + "operation_id": "UpdateSavedFilterAttributes", + "request": { + "body": { + "schema": { + "format": null, + "ref": "PatchSavedFilterRequest", + "type": "object" + }, + "source": "inline", + "value": { + "data": { + "attributes": { + "last_updated_at": "2026-10-16T20:20:30Z" + }, + "type": "saved-filters" + } + } + }, + "content_type": "application/json", + "method": "PATCH", + "pagination": false, + "parameters": [ + { + "explode": null, + "in": "path", + "name": "saved_filter_id", + "required": true, + "schema": { + "format": "uuid", + "ref": null, + "type": "string" + }, + "source": { + "type": "literal", + "value": "a8c393b4-3a9c-48fd-aa48-402c97e8d9c9" + }, + "style": null + } + ], + "path": "/api/v2/feature-flags/saved-filters/{saved_filter_id}" + }, + "scenario": "Patch a saved filter rejects a timestamp-only update", + "schema_version": 1, + "version": "v2" +} diff --git a/tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-rejects-an-empty-name.json b/tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-rejects-an-empty-name.json new file mode 100644 index 0000000000..3e6f6fcb23 --- /dev/null +++ b/tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-rejects-an-empty-name.json @@ -0,0 +1,50 @@ +{ + "api": "FeatureFlags", + "expected_status": 400, + "feature": "Feature Flags", + "id": "v2/Feature Flags/Patch a saved filter rejects an empty name", + "operation_id": "UpdateSavedFilterAttributes", + "request": { + "body": { + "schema": { + "format": null, + "ref": "PatchSavedFilterRequest", + "type": "object" + }, + "source": "inline", + "value": { + "data": { + "attributes": { + "name": "" + }, + "type": "saved-filters" + } + } + }, + "content_type": "application/json", + "method": "PATCH", + "pagination": false, + "parameters": [ + { + "explode": null, + "in": "path", + "name": "saved_filter_id", + "required": true, + "schema": { + "format": "uuid", + "ref": null, + "type": "string" + }, + "source": { + "type": "literal", + "value": "2cfc7949-7eb4-4d76-b1b8-d1aa9b565535" + }, + "style": null + } + ], + "path": "/api/v2/feature-flags/saved-filters/{saved_filter_id}" + }, + "scenario": "Patch a saved filter rejects an empty name", + "schema_version": 1, + "version": "v2" +} diff --git a/tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-rejects-an-invalid-timestamp.json b/tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-rejects-an-invalid-timestamp.json new file mode 100644 index 0000000000..9269e3dc07 --- /dev/null +++ b/tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-rejects-an-invalid-timestamp.json @@ -0,0 +1,51 @@ +{ + "api": "FeatureFlags", + "expected_status": 400, + "feature": "Feature Flags", + "id": "v2/Feature Flags/Patch a saved filter rejects an invalid timestamp", + "operation_id": "UpdateSavedFilterAttributes", + "request": { + "body": { + "schema": { + "format": null, + "ref": "PatchSavedFilterRequest", + "type": "object" + }, + "source": "inline", + "value": { + "data": { + "attributes": { + "description": "Not applied.", + "last_updated_at": "invalid" + }, + "type": "saved-filters" + } + } + }, + "content_type": "application/json", + "method": "PATCH", + "pagination": false, + "parameters": [ + { + "explode": null, + "in": "path", + "name": "saved_filter_id", + "required": true, + "schema": { + "format": "uuid", + "ref": null, + "type": "string" + }, + "source": { + "type": "literal", + "value": "2cfc7949-7eb4-4d76-b1b8-d1aa9b565535" + }, + "style": null + } + ], + "path": "/api/v2/feature-flags/saved-filters/{saved_filter_id}" + }, + "scenario": "Patch a saved filter rejects an invalid timestamp", + "schema_version": 1, + "version": "v2" +} diff --git a/tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-rejects-empty-targeting-rules.json b/tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-rejects-empty-targeting-rules.json new file mode 100644 index 0000000000..ef8b8f482a --- /dev/null +++ b/tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-rejects-empty-targeting-rules.json @@ -0,0 +1,50 @@ +{ + "api": "FeatureFlags", + "expected_status": 400, + "feature": "Feature Flags", + "id": "v2/Feature Flags/Patch a saved filter rejects empty targeting rules", + "operation_id": "UpdateSavedFilterAttributes", + "request": { + "body": { + "schema": { + "format": null, + "ref": "PatchSavedFilterRequest", + "type": "object" + }, + "source": "inline", + "value": { + "data": { + "attributes": { + "targeting_rules": [] + }, + "type": "saved-filters" + } + } + }, + "content_type": "application/json", + "method": "PATCH", + "pagination": false, + "parameters": [ + { + "explode": null, + "in": "path", + "name": "saved_filter_id", + "required": true, + "schema": { + "format": "uuid", + "ref": null, + "type": "string" + }, + "source": { + "type": "literal", + "value": "2cfc7949-7eb4-4d76-b1b8-d1aa9b565535" + }, + "style": null + } + ], + "path": "/api/v2/feature-flags/saved-filters/{saved_filter_id}" + }, + "scenario": "Patch a saved filter rejects empty targeting rules", + "schema_version": 1, + "version": "v2" +} diff --git a/tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-rejects-nested-saved-filters.json b/tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-rejects-nested-saved-filters.json new file mode 100644 index 0000000000..7ef3e35ff9 --- /dev/null +++ b/tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-rejects-nested-saved-filters.json @@ -0,0 +1,63 @@ +{ + "api": "FeatureFlags", + "expected_status": 400, + "feature": "Feature Flags", + "id": "v2/Feature Flags/Patch a saved filter rejects nested saved filters", + "operation_id": "UpdateSavedFilterAttributes", + "request": { + "body": { + "schema": { + "format": null, + "ref": "PatchSavedFilterRequest", + "type": "object" + }, + "source": "inline", + "value": { + "data": { + "attributes": { + "targeting_rules": [ + { + "conditions": [ + { + "attribute": "user_id", + "operator": "ONE_OF", + "saved_filter_id": "d8e3a81b-1505-4600-99c6-c563d01d46b1", + "value": [ + "test-user-1" + ] + } + ] + } + ] + }, + "type": "saved-filters" + } + } + }, + "content_type": "application/json", + "method": "PATCH", + "pagination": false, + "parameters": [ + { + "explode": null, + "in": "path", + "name": "saved_filter_id", + "required": true, + "schema": { + "format": "uuid", + "ref": null, + "type": "string" + }, + "source": { + "type": "literal", + "value": "2cfc7949-7eb4-4d76-b1b8-d1aa9b565535" + }, + "style": null + } + ], + "path": "/api/v2/feature-flags/saved-filters/{saved_filter_id}" + }, + "scenario": "Patch a saved filter rejects nested saved filters", + "schema_version": 1, + "version": "v2" +} diff --git a/tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-rejects-stale-rule-updates.json b/tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-rejects-stale-rule-updates.json new file mode 100644 index 0000000000..a380c92447 --- /dev/null +++ b/tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-rejects-stale-rule-updates.json @@ -0,0 +1,63 @@ +{ + "api": "FeatureFlags", + "expected_status": 409, + "feature": "Feature Flags", + "id": "v2/Feature Flags/Patch a saved filter rejects stale rule updates", + "operation_id": "UpdateSavedFilterAttributes", + "request": { + "body": { + "schema": { + "format": null, + "ref": "PatchSavedFilterRequest", + "type": "object" + }, + "source": "inline", + "value": { + "data": { + "attributes": { + "last_updated_at": "2020-01-01T00:00:00Z", + "targeting_rules": [ + { + "conditions": [ + { + "attribute": "user_id", + "operator": "ONE_OF", + "value": [ + "not-applied" + ] + } + ] + } + ] + }, + "type": "saved-filters" + } + } + }, + "content_type": "application/json", + "method": "PATCH", + "pagination": false, + "parameters": [ + { + "explode": null, + "in": "path", + "name": "saved_filter_id", + "required": true, + "schema": { + "format": "uuid", + "ref": null, + "type": "string" + }, + "source": { + "type": "literal", + "value": "a8c393b4-3a9c-48fd-aa48-402c97e8d9c9" + }, + "style": null + } + ], + "path": "/api/v2/feature-flags/saved-filters/{saved_filter_id}" + }, + "scenario": "Patch a saved filter rejects stale rule updates", + "schema_version": 1, + "version": "v2" +} diff --git a/tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-replaces-rules-and-preserves-metadata.json b/tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-replaces-rules-and-preserves-metadata.json new file mode 100644 index 0000000000..1a273a007c --- /dev/null +++ b/tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-replaces-rules-and-preserves-metadata.json @@ -0,0 +1,62 @@ +{ + "api": "FeatureFlags", + "expected_status": 200, + "feature": "Feature Flags", + "id": "v2/Feature Flags/Patch a saved filter replaces rules and preserves metadata", + "operation_id": "UpdateSavedFilterAttributes", + "request": { + "body": { + "schema": { + "format": null, + "ref": "PatchSavedFilterRequest", + "type": "object" + }, + "source": "inline", + "value": { + "data": { + "attributes": { + "targeting_rules": [ + { + "conditions": [ + { + "attribute": "email", + "operator": "ONE_OF", + "value": [ + "test@example.com" + ] + } + ] + } + ] + }, + "type": "saved-filters" + } + } + }, + "content_type": "application/json", + "method": "PATCH", + "pagination": false, + "parameters": [ + { + "explode": null, + "in": "path", + "name": "saved_filter_id", + "required": true, + "schema": { + "format": "uuid", + "ref": null, + "type": "string" + }, + "source": { + "type": "literal", + "value": "2cfc7949-7eb4-4d76-b1b8-d1aa9b565535" + }, + "style": null + } + ], + "path": "/api/v2/feature-flags/saved-filters/{saved_filter_id}" + }, + "scenario": "Patch a saved filter replaces rules and preserves metadata", + "schema_version": 1, + "version": "v2" +} diff --git a/tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-replaces-the-rules-of-a-saved-list.json b/tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-replaces-the-rules-of-a-saved-list.json new file mode 100644 index 0000000000..b8773e586d --- /dev/null +++ b/tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-replaces-the-rules-of-a-saved-list.json @@ -0,0 +1,62 @@ +{ + "api": "FeatureFlags", + "expected_status": 200, + "feature": "Feature Flags", + "id": "v2/Feature Flags/Patch a saved filter replaces the rules of a saved list", + "operation_id": "UpdateSavedFilterAttributes", + "request": { + "body": { + "schema": { + "format": null, + "ref": "PatchSavedFilterRequest", + "type": "object" + }, + "source": "inline", + "value": { + "data": { + "attributes": { + "targeting_rules": [ + { + "conditions": [ + { + "attribute": "user_id", + "operator": "ONE_OF", + "value": [ + "test-user-3" + ] + } + ] + } + ] + }, + "type": "saved-filters" + } + } + }, + "content_type": "application/json", + "method": "PATCH", + "pagination": false, + "parameters": [ + { + "explode": null, + "in": "path", + "name": "saved_filter_id", + "required": true, + "schema": { + "format": "uuid", + "ref": null, + "type": "string" + }, + "source": { + "type": "literal", + "value": "d8e3a81b-1505-4600-99c6-c563d01d46b1" + }, + "style": null + } + ], + "path": "/api/v2/feature-flags/saved-filters/{saved_filter_id}" + }, + "scenario": "Patch a saved filter replaces the rules of a saved list", + "schema_version": 1, + "version": "v2" +} diff --git a/tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-returns-bad-request-response.json b/tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-returns-bad-request-response.json new file mode 100644 index 0000000000..65d4bf1c4e --- /dev/null +++ b/tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-returns-bad-request-response.json @@ -0,0 +1,48 @@ +{ + "api": "FeatureFlags", + "expected_status": 400, + "feature": "Feature Flags", + "id": "v2/Feature Flags/Patch a saved filter returns \"Bad Request\" response", + "operation_id": "UpdateSavedFilterAttributes", + "request": { + "body": { + "schema": { + "format": null, + "ref": "PatchSavedFilterRequest", + "type": "object" + }, + "source": "inline", + "value": { + "data": { + "attributes": {}, + "type": "saved-filters" + } + } + }, + "content_type": "application/json", + "method": "PATCH", + "pagination": false, + "parameters": [ + { + "explode": null, + "in": "path", + "name": "saved_filter_id", + "required": true, + "schema": { + "format": "uuid", + "ref": null, + "type": "string" + }, + "source": { + "type": "literal", + "value": "2cfc7949-7eb4-4d76-b1b8-d1aa9b565535" + }, + "style": null + } + ], + "path": "/api/v2/feature-flags/saved-filters/{saved_filter_id}" + }, + "scenario": "Patch a saved filter returns \"Bad Request\" response", + "schema_version": 1, + "version": "v2" +} diff --git a/tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-returns-conflict-response.json b/tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-returns-conflict-response.json new file mode 100644 index 0000000000..53bf6b445a --- /dev/null +++ b/tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-returns-conflict-response.json @@ -0,0 +1,51 @@ +{ + "api": "FeatureFlags", + "expected_status": 409, + "feature": "Feature Flags", + "id": "v2/Feature Flags/Patch a saved filter returns \"Conflict\" response", + "operation_id": "UpdateSavedFilterAttributes", + "request": { + "body": { + "schema": { + "format": null, + "ref": "PatchSavedFilterRequest", + "type": "object" + }, + "source": "inline", + "value": { + "data": { + "attributes": { + "description": "Not applied.", + "last_updated_at": "2020-01-01T00:00:00Z" + }, + "type": "saved-filters" + } + } + }, + "content_type": "application/json", + "method": "PATCH", + "pagination": false, + "parameters": [ + { + "explode": null, + "in": "path", + "name": "saved_filter_id", + "required": true, + "schema": { + "format": "uuid", + "ref": null, + "type": "string" + }, + "source": { + "type": "literal", + "value": "2cfc7949-7eb4-4d76-b1b8-d1aa9b565535" + }, + "style": null + } + ], + "path": "/api/v2/feature-flags/saved-filters/{saved_filter_id}" + }, + "scenario": "Patch a saved filter returns \"Conflict\" response", + "schema_version": 1, + "version": "v2" +} diff --git a/tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-returns-not-found-response.json b/tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-returns-not-found-response.json new file mode 100644 index 0000000000..09f2885024 --- /dev/null +++ b/tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-returns-not-found-response.json @@ -0,0 +1,50 @@ +{ + "api": "FeatureFlags", + "expected_status": 404, + "feature": "Feature Flags", + "id": "v2/Feature Flags/Patch a saved filter returns \"Not Found\" response", + "operation_id": "UpdateSavedFilterAttributes", + "request": { + "body": { + "schema": { + "format": null, + "ref": "PatchSavedFilterRequest", + "type": "object" + }, + "source": "inline", + "value": { + "data": { + "attributes": { + "description": "Not applied." + }, + "type": "saved-filters" + } + } + }, + "content_type": "application/json", + "method": "PATCH", + "pagination": false, + "parameters": [ + { + "explode": null, + "in": "path", + "name": "saved_filter_id", + "required": true, + "schema": { + "format": "uuid", + "ref": null, + "type": "string" + }, + "source": { + "type": "literal", + "value": "00000000-0000-0000-0000-000000000000" + }, + "style": null + } + ], + "path": "/api/v2/feature-flags/saved-filters/{saved_filter_id}" + }, + "scenario": "Patch a saved filter returns \"Not Found\" response", + "schema_version": 1, + "version": "v2" +} diff --git a/tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-returns-ok-response.json b/tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-returns-ok-response.json new file mode 100644 index 0000000000..18540c16ca --- /dev/null +++ b/tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-returns-ok-response.json @@ -0,0 +1,50 @@ +{ + "api": "FeatureFlags", + "expected_status": 200, + "feature": "Feature Flags", + "id": "v2/Feature Flags/Patch a saved filter returns \"OK\" response", + "operation_id": "UpdateSavedFilterAttributes", + "request": { + "body": { + "schema": { + "format": null, + "ref": "PatchSavedFilterRequest", + "type": "object" + }, + "source": "inline", + "value": { + "data": { + "attributes": { + "name": "patchSavedFilterTest-renamed-157e7ff3" + }, + "type": "saved-filters" + } + } + }, + "content_type": "application/json", + "method": "PATCH", + "pagination": false, + "parameters": [ + { + "explode": null, + "in": "path", + "name": "saved_filter_id", + "required": true, + "schema": { + "format": "uuid", + "ref": null, + "type": "string" + }, + "source": { + "type": "literal", + "value": "2cfc7949-7eb4-4d76-b1b8-d1aa9b565535" + }, + "style": null + } + ], + "path": "/api/v2/feature-flags/saved-filters/{saved_filter_id}" + }, + "scenario": "Patch a saved filter returns \"OK\" response", + "schema_version": 1, + "version": "v2" +} diff --git a/tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-updates-metadata-and-rules-together.json b/tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-updates-metadata-and-rules-together.json new file mode 100644 index 0000000000..8b52099288 --- /dev/null +++ b/tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-updates-metadata-and-rules-together.json @@ -0,0 +1,64 @@ +{ + "api": "FeatureFlags", + "expected_status": 200, + "feature": "Feature Flags", + "id": "v2/Feature Flags/Patch a saved filter updates metadata and rules together", + "operation_id": "UpdateSavedFilterAttributes", + "request": { + "body": { + "schema": { + "format": null, + "ref": "PatchSavedFilterRequest", + "type": "object" + }, + "source": "inline", + "value": { + "data": { + "attributes": { + "description": "Combined synthetic test update.", + "name": "patchSavedFilterTest-combined-157e7ff3", + "targeting_rules": [ + { + "conditions": [ + { + "attribute": "org_id", + "operator": "ONE_OF", + "value": [ + "test-org-1" + ] + } + ] + } + ] + }, + "type": "saved-filters" + } + } + }, + "content_type": "application/json", + "method": "PATCH", + "pagination": false, + "parameters": [ + { + "explode": null, + "in": "path", + "name": "saved_filter_id", + "required": true, + "schema": { + "format": "uuid", + "ref": null, + "type": "string" + }, + "source": { + "type": "literal", + "value": "2cfc7949-7eb4-4d76-b1b8-d1aa9b565535" + }, + "style": null + } + ], + "path": "/api/v2/feature-flags/saved-filters/{saved_filter_id}" + }, + "scenario": "Patch a saved filter updates metadata and rules together", + "schema_version": 1, + "version": "v2" +} diff --git a/tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-updates-only-the-description-of-a-saved-list.json b/tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-updates-only-the-description-of-a-saved-list.json new file mode 100644 index 0000000000..5930863008 --- /dev/null +++ b/tests/generated-test/test-runner-data/v2/feature-flags/patch-a-saved-filter-updates-only-the-description-of-a-saved-list.json @@ -0,0 +1,50 @@ +{ + "api": "FeatureFlags", + "expected_status": 200, + "feature": "Feature Flags", + "id": "v2/Feature Flags/Patch a saved filter updates only the description of a saved list", + "operation_id": "UpdateSavedFilterAttributes", + "request": { + "body": { + "schema": { + "format": null, + "ref": "PatchSavedFilterRequest", + "type": "object" + }, + "source": "inline", + "value": { + "data": { + "attributes": { + "description": "Updated synthetic test description." + }, + "type": "saved-filters" + } + } + }, + "content_type": "application/json", + "method": "PATCH", + "pagination": false, + "parameters": [ + { + "explode": null, + "in": "path", + "name": "saved_filter_id", + "required": true, + "schema": { + "format": "uuid", + "ref": null, + "type": "string" + }, + "source": { + "type": "literal", + "value": "d8e3a81b-1505-4600-99c6-c563d01d46b1" + }, + "style": null + } + ], + "path": "/api/v2/feature-flags/saved-filters/{saved_filter_id}" + }, + "scenario": "Patch a saved filter updates only the description of a saved list", + "schema_version": 1, + "version": "v2" +} diff --git a/tests/generated-test/test-server-data/v2/feature-flags.json b/tests/generated-test/test-server-data/v2/feature-flags.json index b843e1a2ed..9f4c6ddb22 100644 --- a/tests/generated-test/test-server-data/v2/feature-flags.json +++ b/tests/generated-test/test-server-data/v2/feature-flags.json @@ -4310,6 +4310,845 @@ "scenario": "List feature flags supports legacy pagination aliases", "version": "v2" }, + { + "feature": "Feature Flags", + "frozen_at": "2026-10-16T20:20:29.850359Z", + "interactions": [ + { + "request": { + "body": { + "type": "json", + "value": { + "data": { + "attributes": { + "description": "Metadata with current timestamp.", + "last_updated_at": "2026-10-16T20:20:29Z" + }, + "type": "saved-filters" + } + } + }, + "content_type": "application/json", + "method": "PATCH", + "path": "/api/v2/feature-flags/saved-filters/a8c393b4-3a9c-48fd-aa48-402c97e8d9c9", + "query": [] + }, + "response": { + "body": { + "encoding": "text", + "value": "{\"data\":{\"id\":\"a8c393b4-3a9c-48fd-aa48-402c97e8d9c9\",\"type\":\"saved-filters\",\"attributes\":{\"created_at\":\"2026-10-16T20:20:29Z\",\"created_by\":\"550e8400-e29b-41d4-a716-446655440091\",\"creation_type\":\"RULES\",\"description\":\"Metadata with current timestamp.\",\"name\":\"patchSavedFilterTest-concurrency-437ebfe7\",\"targeting_rules\":[{\"id\":\"6574b030-a2c7-4cd4-8adf-b10fa11c6701\",\"conditions\":[{\"id\":\"53bb120f-9a9d-4c05-a500-c89d4a51d1ad\",\"operator\":\"ONE_OF\",\"attribute\":\"user_id\",\"value\":[\"test-user-1\"]}]}],\"updated_at\":\"2026-10-16T20:20:30Z\"}}}" + }, + "headers": { + "content-type": "application/vnd.api+json" + }, + "reason": "OK", + "status": 200 + } + } + ], + "scenario": "Patch a saved filter accepts the current timestamp for metadata", + "version": "v2" + }, + { + "feature": "Feature Flags", + "frozen_at": "2026-10-16T20:20:29.969751Z", + "interactions": [ + { + "request": { + "body": { + "type": "json", + "value": { + "data": { + "attributes": { + "last_updated_at": "2026-10-16T20:20:30Z", + "targeting_rules": [ + { + "conditions": [ + { + "attribute": "user_id", + "operator": "ONE_OF", + "value": [ + "test-user-2" + ] + } + ] + } + ] + }, + "type": "saved-filters" + } + } + }, + "content_type": "application/json", + "method": "PATCH", + "path": "/api/v2/feature-flags/saved-filters/a8c393b4-3a9c-48fd-aa48-402c97e8d9c9", + "query": [] + }, + "response": { + "body": { + "encoding": "text", + "value": "{\"data\":{\"id\":\"a8c393b4-3a9c-48fd-aa48-402c97e8d9c9\",\"type\":\"saved-filters\",\"attributes\":{\"created_at\":\"2026-10-16T20:20:29Z\",\"created_by\":\"550e8400-e29b-41d4-a716-446655440091\",\"creation_type\":\"RULES\",\"description\":\"Metadata with current timestamp.\",\"name\":\"patchSavedFilterTest-concurrency-437ebfe7\",\"targeting_rules\":[{\"id\":\"efe993b9-2792-443e-838e-509ee7ee5d89\",\"conditions\":[{\"id\":\"23ba234c-a66f-4a34-9678-524d0de9742a\",\"operator\":\"ONE_OF\",\"attribute\":\"user_id\",\"value\":[\"test-user-2\"]}]}],\"updated_at\":\"2026-10-16T20:20:30Z\"}}}" + }, + "headers": { + "content-type": "application/vnd.api+json" + }, + "reason": "OK", + "status": 200 + } + } + ], + "scenario": "Patch a saved filter accepts the current timestamp for rules", + "version": "v2" + }, + { + "feature": "Feature Flags", + "frozen_at": "2026-10-16T20:15:10.461609Z", + "interactions": [ + { + "request": { + "body": { + "type": "json", + "value": { + "data": { + "attributes": { + "description": "" + }, + "type": "saved-filters" + } + } + }, + "content_type": "application/json", + "method": "PATCH", + "path": "/api/v2/feature-flags/saved-filters/2cfc7949-7eb4-4d76-b1b8-d1aa9b565535", + "query": [] + }, + "response": { + "body": { + "encoding": "text", + "value": "{\"data\":{\"id\":\"2cfc7949-7eb4-4d76-b1b8-d1aa9b565535\",\"type\":\"saved-filters\",\"attributes\":{\"created_at\":\"2026-10-16T20:15:09Z\",\"created_by\":\"550e8400-e29b-41d4-a716-446655440091\",\"creation_type\":\"RULES\",\"description\":\"\",\"name\":\"patchSavedFilterTest-null-157e7ff3\",\"targeting_rules\":[{\"id\":\"666fb698-8e0a-436b-b269-d008c3b5c860\",\"conditions\":[{\"id\":\"fb5218f6-2c90-42f0-ab6d-74d11ab3d1d8\",\"operator\":\"ONE_OF\",\"attribute\":\"org_id\",\"value\":[\"test-org-1\"]}]}],\"updated_at\":\"2026-10-16T20:15:10Z\"}}}" + }, + "headers": { + "content-type": "application/vnd.api+json" + }, + "reason": "OK", + "status": 200 + } + } + ], + "scenario": "Patch a saved filter clears the description with an empty string", + "version": "v2" + }, + { + "feature": "Feature Flags", + "frozen_at": "2026-10-16T20:15:10.283978Z", + "interactions": [ + { + "request": { + "body": { + "type": "json", + "value": { + "data": { + "attributes": { + "description": null, + "name": "patchSavedFilterTest-null-157e7ff3" + }, + "type": "saved-filters" + } + } + }, + "content_type": "application/json", + "method": "PATCH", + "path": "/api/v2/feature-flags/saved-filters/2cfc7949-7eb4-4d76-b1b8-d1aa9b565535", + "query": [] + }, + "response": { + "body": { + "encoding": "text", + "value": "{\"data\":{\"id\":\"2cfc7949-7eb4-4d76-b1b8-d1aa9b565535\",\"type\":\"saved-filters\",\"attributes\":{\"created_at\":\"2026-10-16T20:15:09Z\",\"created_by\":\"550e8400-e29b-41d4-a716-446655440091\",\"creation_type\":\"RULES\",\"description\":\"Combined synthetic test update.\",\"name\":\"patchSavedFilterTest-null-157e7ff3\",\"targeting_rules\":[{\"id\":\"666fb698-8e0a-436b-b269-d008c3b5c860\",\"conditions\":[{\"id\":\"fb5218f6-2c90-42f0-ab6d-74d11ab3d1d8\",\"operator\":\"ONE_OF\",\"attribute\":\"org_id\",\"value\":[\"test-org-1\"]}]}],\"updated_at\":\"2026-10-16T20:15:10Z\"}}}" + }, + "headers": { + "content-type": "application/vnd.api+json" + }, + "reason": "OK", + "status": 200 + } + } + ], + "scenario": "Patch a saved filter preserves a null description", + "version": "v2" + }, + { + "feature": "Feature Flags", + "frozen_at": "2026-10-16T20:15:11.650679Z", + "interactions": [ + { + "request": { + "body": { + "type": "json", + "value": { + "data": { + "attributes": { + "name": "patchSavedFilterTest-LIST-157e7ff3" + }, + "type": "saved-filters" + } + } + }, + "content_type": "application/json", + "method": "PATCH", + "path": "/api/v2/feature-flags/saved-filters/2cfc7949-7eb4-4d76-b1b8-d1aa9b565535", + "query": [] + }, + "response": { + "body": { + "encoding": "text", + "value": "{\"errors\":[{\"status\":\"409\",\"title\":\"Conflict\",\"detail\":\"a saved filter with this name already exists: conflict occurred\"}]}" + }, + "headers": { + "content-type": "application/vnd.api+json" + }, + "reason": "Conflict", + "status": 409 + } + } + ], + "scenario": "Patch a saved filter rejects a duplicate name", + "version": "v2" + }, + { + "feature": "Feature Flags", + "frozen_at": "2026-10-16T20:15:10.737063Z", + "interactions": [ + { + "request": { + "body": { + "type": "json", + "value": { + "data": { + "attributes": { + "description": null + }, + "type": "saved-filters" + } + } + }, + "content_type": "application/json", + "method": "PATCH", + "path": "/api/v2/feature-flags/saved-filters/2cfc7949-7eb4-4d76-b1b8-d1aa9b565535", + "query": [] + }, + "response": { + "body": { + "encoding": "text", + "value": "{\"errors\":[{\"status\":\"400\",\"title\":\"Bad Request\",\"detail\":\"at least one field must be provided\"}]}" + }, + "headers": { + "content-type": "application/vnd.api+json" + }, + "reason": "Bad Request", + "status": 400 + } + } + ], + "scenario": "Patch a saved filter rejects a null-only update", + "version": "v2" + }, + { + "feature": "Feature Flags", + "frozen_at": "2026-10-16T20:20:30.337845Z", + "interactions": [ + { + "request": { + "body": { + "type": "json", + "value": { + "data": { + "attributes": { + "last_updated_at": "2026-10-16T20:20:30Z" + }, + "type": "saved-filters" + } + } + }, + "content_type": "application/json", + "method": "PATCH", + "path": "/api/v2/feature-flags/saved-filters/a8c393b4-3a9c-48fd-aa48-402c97e8d9c9", + "query": [] + }, + "response": { + "body": { + "encoding": "text", + "value": "{\"errors\":[{\"status\":\"400\",\"title\":\"Bad Request\",\"detail\":\"at least one field must be provided\"}]}" + }, + "headers": { + "content-type": "application/vnd.api+json" + }, + "reason": "Bad Request", + "status": 400 + } + } + ], + "scenario": "Patch a saved filter rejects a timestamp-only update", + "version": "v2" + }, + { + "feature": "Feature Flags", + "frozen_at": "2026-10-16T20:15:10.829934Z", + "interactions": [ + { + "request": { + "body": { + "type": "json", + "value": { + "data": { + "attributes": { + "name": "" + }, + "type": "saved-filters" + } + } + }, + "content_type": "application/json", + "method": "PATCH", + "path": "/api/v2/feature-flags/saved-filters/2cfc7949-7eb4-4d76-b1b8-d1aa9b565535", + "query": [] + }, + "response": { + "body": { + "encoding": "text", + "value": "{\"errors\":[{\"status\":\"400\",\"title\":\"Bad Request\",\"detail\":\"name must not be empty\"}]}" + }, + "headers": { + "content-type": "application/vnd.api+json" + }, + "reason": "Bad Request", + "status": 400 + } + } + ], + "scenario": "Patch a saved filter rejects an empty name", + "version": "v2" + }, + { + "feature": "Feature Flags", + "frozen_at": "2026-10-16T20:15:11.185268Z", + "interactions": [ + { + "request": { + "body": { + "type": "json", + "value": { + "data": { + "attributes": { + "description": "Not applied.", + "last_updated_at": "invalid" + }, + "type": "saved-filters" + } + } + }, + "content_type": "application/json", + "method": "PATCH", + "path": "/api/v2/feature-flags/saved-filters/2cfc7949-7eb4-4d76-b1b8-d1aa9b565535", + "query": [] + }, + "response": { + "body": { + "encoding": "text", + "value": "{\"errors\":[{\"status\":\"400\",\"title\":\"Bad Request\",\"detail\":\"last_updated_at must be a valid RFC3339 timestamp\"}]}" + }, + "headers": { + "content-type": "application/vnd.api+json" + }, + "reason": "Bad Request", + "status": 400 + } + } + ], + "scenario": "Patch a saved filter rejects an invalid timestamp", + "version": "v2" + }, + { + "feature": "Feature Flags", + "frozen_at": "2026-10-16T20:15:10.983440Z", + "interactions": [ + { + "request": { + "body": { + "type": "json", + "value": { + "data": { + "attributes": { + "targeting_rules": [] + }, + "type": "saved-filters" + } + } + }, + "content_type": "application/json", + "method": "PATCH", + "path": "/api/v2/feature-flags/saved-filters/2cfc7949-7eb4-4d76-b1b8-d1aa9b565535", + "query": [] + }, + "response": { + "body": { + "encoding": "text", + "value": "{\"errors\":[{\"status\":\"400\",\"title\":\"Bad Request\",\"detail\":\"attribute \\\"targeting_rules\\\" failed min validation\"}]}" + }, + "headers": { + "content-type": "application/vnd.api+json" + }, + "reason": "Bad Request", + "status": 400 + } + } + ], + "scenario": "Patch a saved filter rejects empty targeting rules", + "version": "v2" + }, + { + "feature": "Feature Flags", + "frozen_at": "2026-10-16T20:15:11.075942Z", + "interactions": [ + { + "request": { + "body": { + "type": "json", + "value": { + "data": { + "attributes": { + "targeting_rules": [ + { + "conditions": [ + { + "attribute": "user_id", + "operator": "ONE_OF", + "saved_filter_id": "d8e3a81b-1505-4600-99c6-c563d01d46b1", + "value": [ + "test-user-1" + ] + } + ] + } + ] + }, + "type": "saved-filters" + } + } + }, + "content_type": "application/json", + "method": "PATCH", + "path": "/api/v2/feature-flags/saved-filters/2cfc7949-7eb4-4d76-b1b8-d1aa9b565535", + "query": [] + }, + "response": { + "body": { + "encoding": "text", + "value": "{\"errors\":[{\"status\":\"400\",\"title\":\"Bad Request\",\"detail\":\"targeting rule 0 condition 0: saved filters cannot reference other saved filters\"}]}" + }, + "headers": { + "content-type": "application/vnd.api+json" + }, + "reason": "Bad Request", + "status": 400 + } + } + ], + "scenario": "Patch a saved filter rejects nested saved filters", + "version": "v2" + }, + { + "feature": "Feature Flags", + "frozen_at": "2026-10-16T20:20:30.141351Z", + "interactions": [ + { + "request": { + "body": { + "type": "json", + "value": { + "data": { + "attributes": { + "last_updated_at": "2020-01-01T00:00:00Z", + "targeting_rules": [ + { + "conditions": [ + { + "attribute": "user_id", + "operator": "ONE_OF", + "value": [ + "not-applied" + ] + } + ] + } + ] + }, + "type": "saved-filters" + } + } + }, + "content_type": "application/json", + "method": "PATCH", + "path": "/api/v2/feature-flags/saved-filters/a8c393b4-3a9c-48fd-aa48-402c97e8d9c9", + "query": [] + }, + "response": { + "body": { + "encoding": "text", + "value": "{\"errors\":[{\"status\":\"409\",\"title\":\"Conflict\",\"detail\":\"saved filter has been modified since you last fetched it; please refresh and retry: conflict occurred\"}]}" + }, + "headers": { + "content-type": "application/vnd.api+json" + }, + "reason": "Conflict", + "status": 409 + } + } + ], + "scenario": "Patch a saved filter rejects stale rule updates", + "version": "v2" + }, + { + "feature": "Feature Flags", + "frozen_at": "2026-10-16T20:15:09.936504Z", + "interactions": [ + { + "request": { + "body": { + "type": "json", + "value": { + "data": { + "attributes": { + "targeting_rules": [ + { + "conditions": [ + { + "attribute": "email", + "operator": "ONE_OF", + "value": [ + "test@example.com" + ] + } + ] + } + ] + }, + "type": "saved-filters" + } + } + }, + "content_type": "application/json", + "method": "PATCH", + "path": "/api/v2/feature-flags/saved-filters/2cfc7949-7eb4-4d76-b1b8-d1aa9b565535", + "query": [] + }, + "response": { + "body": { + "encoding": "text", + "value": "{\"data\":{\"id\":\"2cfc7949-7eb4-4d76-b1b8-d1aa9b565535\",\"type\":\"saved-filters\",\"attributes\":{\"created_at\":\"2026-10-16T20:15:09Z\",\"created_by\":\"550e8400-e29b-41d4-a716-446655440091\",\"creation_type\":\"RULES\",\"description\":\"Original synthetic test description.\",\"name\":\"patchSavedFilterTest-renamed-157e7ff3\",\"targeting_rules\":[{\"id\":\"d2477e54-e8d3-4681-a61d-27475a3a15f0\",\"conditions\":[{\"id\":\"70246a9c-a20e-4ec1-b8e8-498338fa2b58\",\"operator\":\"ONE_OF\",\"attribute\":\"email\",\"value\":[\"test@example.com\"]}]}],\"updated_at\":\"2026-10-16T20:15:10Z\"}}}" + }, + "headers": { + "content-type": "application/vnd.api+json" + }, + "reason": "OK", + "status": 200 + } + } + ], + "scenario": "Patch a saved filter replaces rules and preserves metadata", + "version": "v2" + }, + { + "feature": "Feature Flags", + "frozen_at": "2026-10-16T20:15:09.710568Z", + "interactions": [ + { + "request": { + "body": { + "type": "json", + "value": { + "data": { + "attributes": { + "targeting_rules": [ + { + "conditions": [ + { + "attribute": "user_id", + "operator": "ONE_OF", + "value": [ + "test-user-3" + ] + } + ] + } + ] + }, + "type": "saved-filters" + } + } + }, + "content_type": "application/json", + "method": "PATCH", + "path": "/api/v2/feature-flags/saved-filters/d8e3a81b-1505-4600-99c6-c563d01d46b1", + "query": [] + }, + "response": { + "body": { + "encoding": "text", + "value": "{\"data\":{\"id\":\"d8e3a81b-1505-4600-99c6-c563d01d46b1\",\"type\":\"saved-filters\",\"attributes\":{\"created_at\":\"2026-10-16T20:15:09Z\",\"created_by\":\"550e8400-e29b-41d4-a716-446655440091\",\"creation_type\":\"LIST\",\"description\":\"Updated synthetic test description.\",\"name\":\"patchSavedFilterTest-LIST-157e7ff3\",\"targeting_rules\":[{\"id\":\"1257126b-c18d-49dc-827d-75c72fe023bb\",\"conditions\":[{\"id\":\"9935e8c2-4cd8-44b9-89cd-1b24f2a9442a\",\"operator\":\"ONE_OF\",\"attribute\":\"user_id\",\"value\":[\"test-user-3\"]}]}],\"updated_at\":\"2026-10-16T20:15:09Z\"}}}" + }, + "headers": { + "content-type": "application/vnd.api+json" + }, + "reason": "OK", + "status": 200 + } + } + ], + "scenario": "Patch a saved filter replaces the rules of a saved list", + "version": "v2" + }, + { + "feature": "Feature Flags", + "frozen_at": "2026-10-16T20:15:10.643890Z", + "interactions": [ + { + "request": { + "body": { + "type": "json", + "value": { + "data": { + "attributes": {}, + "type": "saved-filters" + } + } + }, + "content_type": "application/json", + "method": "PATCH", + "path": "/api/v2/feature-flags/saved-filters/2cfc7949-7eb4-4d76-b1b8-d1aa9b565535", + "query": [] + }, + "response": { + "body": { + "encoding": "text", + "value": "{\"errors\":[{\"status\":\"400\",\"title\":\"Bad Request\",\"detail\":\"at least one field must be provided\"}]}" + }, + "headers": { + "content-type": "application/vnd.api+json" + }, + "reason": "Bad Request", + "status": 400 + } + } + ], + "scenario": "Patch a saved filter returns \"Bad Request\" response", + "version": "v2" + }, + { + "feature": "Feature Flags", + "frozen_at": "2026-10-16T20:15:11.406641Z", + "interactions": [ + { + "request": { + "body": { + "type": "json", + "value": { + "data": { + "attributes": { + "description": "Not applied.", + "last_updated_at": "2020-01-01T00:00:00Z" + }, + "type": "saved-filters" + } + } + }, + "content_type": "application/json", + "method": "PATCH", + "path": "/api/v2/feature-flags/saved-filters/2cfc7949-7eb4-4d76-b1b8-d1aa9b565535", + "query": [] + }, + "response": { + "body": { + "encoding": "text", + "value": "{\"errors\":[{\"status\":\"409\",\"title\":\"Conflict\",\"detail\":\"saved filter has been modified since you last fetched it; please refresh and retry: conflict occurred\"}]}" + }, + "headers": { + "content-type": "application/vnd.api+json" + }, + "reason": "Conflict", + "status": 409 + } + } + ], + "scenario": "Patch a saved filter returns \"Conflict\" response", + "version": "v2" + }, + { + "feature": "Feature Flags", + "frozen_at": "2026-10-16T20:15:11.282379Z", + "interactions": [ + { + "request": { + "body": { + "type": "json", + "value": { + "data": { + "attributes": { + "description": "Not applied." + }, + "type": "saved-filters" + } + } + }, + "content_type": "application/json", + "method": "PATCH", + "path": "/api/v2/feature-flags/saved-filters/00000000-0000-0000-0000-000000000000", + "query": [] + }, + "response": { + "body": { + "encoding": "text", + "value": "{\"errors\":[{\"status\":\"404\",\"title\":\"Not Found\",\"detail\":\"saved filter not found or already deleted: record not found\"}]}" + }, + "headers": { + "content-type": "application/vnd.api+json" + }, + "reason": "Not Found", + "status": 404 + } + } + ], + "scenario": "Patch a saved filter returns \"Not Found\" response", + "version": "v2" + }, + { + "feature": "Feature Flags", + "frozen_at": "2026-10-16T20:15:09.418909Z", + "interactions": [ + { + "request": { + "body": { + "type": "json", + "value": { + "data": { + "attributes": { + "name": "patchSavedFilterTest-renamed-157e7ff3" + }, + "type": "saved-filters" + } + } + }, + "content_type": "application/json", + "method": "PATCH", + "path": "/api/v2/feature-flags/saved-filters/2cfc7949-7eb4-4d76-b1b8-d1aa9b565535", + "query": [] + }, + "response": { + "body": { + "encoding": "text", + "value": "{\"data\":{\"id\":\"2cfc7949-7eb4-4d76-b1b8-d1aa9b565535\",\"type\":\"saved-filters\",\"attributes\":{\"created_at\":\"2026-10-16T20:15:09Z\",\"created_by\":\"550e8400-e29b-41d4-a716-446655440091\",\"creation_type\":\"RULES\",\"description\":\"Original synthetic test description.\",\"name\":\"patchSavedFilterTest-renamed-157e7ff3\",\"targeting_rules\":[{\"id\":\"b6cb0903-79ec-42af-bd1c-139a017416d2\",\"conditions\":[{\"id\":\"ba1e08f4-ce3e-420d-9c9e-f0112edd6976\",\"operator\":\"ONE_OF\",\"attribute\":\"user_id\",\"value\":[\"test-user-1\",\"test-user-2\"]}]}],\"updated_at\":\"2026-10-16T20:15:09Z\"}}}" + }, + "headers": { + "content-type": "application/vnd.api+json" + }, + "reason": "OK", + "status": 200 + } + } + ], + "scenario": "Patch a saved filter returns \"OK\" response", + "version": "v2" + }, + { + "feature": "Feature Flags", + "frozen_at": "2026-10-16T20:15:10.111275Z", + "interactions": [ + { + "request": { + "body": { + "type": "json", + "value": { + "data": { + "attributes": { + "description": "Combined synthetic test update.", + "name": "patchSavedFilterTest-combined-157e7ff3", + "targeting_rules": [ + { + "conditions": [ + { + "attribute": "org_id", + "operator": "ONE_OF", + "value": [ + "test-org-1" + ] + } + ] + } + ] + }, + "type": "saved-filters" + } + } + }, + "content_type": "application/json", + "method": "PATCH", + "path": "/api/v2/feature-flags/saved-filters/2cfc7949-7eb4-4d76-b1b8-d1aa9b565535", + "query": [] + }, + "response": { + "body": { + "encoding": "text", + "value": "{\"data\":{\"id\":\"2cfc7949-7eb4-4d76-b1b8-d1aa9b565535\",\"type\":\"saved-filters\",\"attributes\":{\"created_at\":\"2026-10-16T20:15:09Z\",\"created_by\":\"550e8400-e29b-41d4-a716-446655440091\",\"creation_type\":\"RULES\",\"description\":\"Combined synthetic test update.\",\"name\":\"patchSavedFilterTest-combined-157e7ff3\",\"targeting_rules\":[{\"id\":\"666fb698-8e0a-436b-b269-d008c3b5c860\",\"conditions\":[{\"id\":\"fb5218f6-2c90-42f0-ab6d-74d11ab3d1d8\",\"operator\":\"ONE_OF\",\"attribute\":\"org_id\",\"value\":[\"test-org-1\"]}]}],\"updated_at\":\"2026-10-16T20:15:10Z\"}}}" + }, + "headers": { + "content-type": "application/vnd.api+json" + }, + "reason": "OK", + "status": 200 + } + } + ], + "scenario": "Patch a saved filter updates metadata and rules together", + "version": "v2" + }, + { + "feature": "Feature Flags", + "frozen_at": "2026-10-16T20:15:09.584831Z", + "interactions": [ + { + "request": { + "body": { + "type": "json", + "value": { + "data": { + "attributes": { + "description": "Updated synthetic test description." + }, + "type": "saved-filters" + } + } + }, + "content_type": "application/json", + "method": "PATCH", + "path": "/api/v2/feature-flags/saved-filters/d8e3a81b-1505-4600-99c6-c563d01d46b1", + "query": [] + }, + "response": { + "body": { + "encoding": "text", + "value": "{\"data\":{\"id\":\"d8e3a81b-1505-4600-99c6-c563d01d46b1\",\"type\":\"saved-filters\",\"attributes\":{\"created_at\":\"2026-10-16T20:15:09Z\",\"created_by\":\"550e8400-e29b-41d4-a716-446655440091\",\"creation_type\":\"LIST\",\"description\":\"Updated synthetic test description.\",\"name\":\"patchSavedFilterTest-LIST-157e7ff3\",\"targeting_rules\":[{\"id\":\"5cf3c126-8342-4187-9d2f-1b04a6cd0d97\",\"conditions\":[{\"id\":\"c4ac8587-d84d-4b1b-8e96-d347aca28f82\",\"operator\":\"ONE_OF\",\"attribute\":\"user_id\",\"value\":[\"test-user-1\",\"test-user-2\"]}]}],\"updated_at\":\"2026-10-16T20:15:09Z\"}}}" + }, + "headers": { + "content-type": "application/vnd.api+json" + }, + "reason": "OK", + "status": 200 + } + } + ], + "scenario": "Patch a saved filter updates only the description of a saved list", + "version": "v2" + }, { "feature": "Feature Flags", "frozen_at": "2026-04-22T20:15:57.687Z", diff --git a/tests/v2/features/feature_flags.feature b/tests/v2/features/feature_flags.feature index b298d717c2..c2a82ad284 100644 --- a/tests/v2/features/feature_flags.feature +++ b/tests/v2/features/feature_flags.feature @@ -632,6 +632,300 @@ Feature: Feature Flags And the response "meta.page.offset" is equal to 1 And the response "meta.page.total" is equal to 2 + @replay-only @team:ddoghq/feature-flags + Scenario: Patch a saved filter accepts the current timestamp for metadata + Given new "UpdateSavedFilterAttributes" request + And request contains "saved_filter_id" parameter with value "a8c393b4-3a9c-48fd-aa48-402c97e8d9c9" + And body with value {"data":{"attributes":{"description":"Metadata with current timestamp.","last_updated_at":"2026-10-16T20:20:29Z"},"type":"saved-filters"}} + When the request is sent + Then the response status is 200 OK + And the response "data.id" is equal to "a8c393b4-3a9c-48fd-aa48-402c97e8d9c9" + And the response "data.type" is equal to "saved-filters" + And the response "data.attributes.name" is equal to "patchSavedFilterTest-concurrency-437ebfe7" + And the response "data.attributes.description" is equal to "Metadata with current timestamp." + And the response "data.attributes.creation_type" is equal to "RULES" + And the response "data.attributes.targeting_rules" has length 1 + And the response "data.attributes.targeting_rules[0].conditions" has length 1 + And the response "data.attributes.targeting_rules[0].conditions[0].operator" is equal to "ONE_OF" + And the response "data.attributes.targeting_rules[0].conditions[0].attribute" is equal to "user_id" + And the response "data.attributes.targeting_rules[0].conditions[0].value" is equal to ["test-user-1"] + + @replay-only @team:ddoghq/feature-flags + Scenario: Patch a saved filter accepts the current timestamp for rules + Given new "UpdateSavedFilterAttributes" request + And request contains "saved_filter_id" parameter with value "a8c393b4-3a9c-48fd-aa48-402c97e8d9c9" + And body with value {"data":{"attributes":{"last_updated_at":"2026-10-16T20:20:30Z","targeting_rules":[{"conditions":[{"attribute":"user_id","operator":"ONE_OF","value":["test-user-2"]}]}]},"type":"saved-filters"}} + When the request is sent + Then the response status is 200 OK + And the response "data.id" is equal to "a8c393b4-3a9c-48fd-aa48-402c97e8d9c9" + And the response "data.type" is equal to "saved-filters" + And the response "data.attributes.name" is equal to "patchSavedFilterTest-concurrency-437ebfe7" + And the response "data.attributes.description" is equal to "Metadata with current timestamp." + And the response "data.attributes.creation_type" is equal to "RULES" + And the response "data.attributes.targeting_rules" has length 1 + And the response "data.attributes.targeting_rules[0].conditions" has length 1 + And the response "data.attributes.targeting_rules[0].conditions[0].operator" is equal to "ONE_OF" + And the response "data.attributes.targeting_rules[0].conditions[0].attribute" is equal to "user_id" + And the response "data.attributes.targeting_rules[0].conditions[0].value" is equal to ["test-user-2"] + + @replay-only @team:ddoghq/feature-flags + Scenario: Patch a saved filter clears the description with an empty string + Given new "UpdateSavedFilterAttributes" request + And request contains "saved_filter_id" parameter with value "2cfc7949-7eb4-4d76-b1b8-d1aa9b565535" + And body with value {"data":{"type":"saved-filters","attributes":{"description":""}}} + When the request is sent + Then the response status is 200 OK + And the response "data.id" is equal to "2cfc7949-7eb4-4d76-b1b8-d1aa9b565535" + And the response "data.type" is equal to "saved-filters" + And the response "data.attributes.name" is equal to "patchSavedFilterTest-null-157e7ff3" + And the response "data.attributes.description" is equal to "" + And the response "data.attributes.creation_type" is equal to "RULES" + And the response "data.attributes.targeting_rules" has length 1 + And the response "data.attributes.targeting_rules[0].conditions" has length 1 + And the response "data.attributes.targeting_rules[0].conditions[0].operator" is equal to "ONE_OF" + And the response "data.attributes.targeting_rules[0].conditions[0].attribute" is equal to "org_id" + And the response "data.attributes.targeting_rules[0].conditions[0].value" is equal to ["test-org-1"] + + @replay-only @team:ddoghq/feature-flags + Scenario: Patch a saved filter preserves a null description + Given new "UpdateSavedFilterAttributes" request + And request contains "saved_filter_id" parameter with value "2cfc7949-7eb4-4d76-b1b8-d1aa9b565535" + And body with value {"data":{"type":"saved-filters","attributes":{"description":null,"name":"patchSavedFilterTest-null-157e7ff3"}}} + When the request is sent + Then the response status is 200 OK + And the response "data.id" is equal to "2cfc7949-7eb4-4d76-b1b8-d1aa9b565535" + And the response "data.type" is equal to "saved-filters" + And the response "data.attributes.name" is equal to "patchSavedFilterTest-null-157e7ff3" + And the response "data.attributes.description" is equal to "Combined synthetic test update." + And the response "data.attributes.creation_type" is equal to "RULES" + And the response "data.attributes.targeting_rules" has length 1 + And the response "data.attributes.targeting_rules[0].conditions" has length 1 + And the response "data.attributes.targeting_rules[0].conditions[0].operator" is equal to "ONE_OF" + And the response "data.attributes.targeting_rules[0].conditions[0].attribute" is equal to "org_id" + And the response "data.attributes.targeting_rules[0].conditions[0].value" is equal to ["test-org-1"] + + @replay-only @team:ddoghq/feature-flags + Scenario: Patch a saved filter rejects a duplicate name + Given new "UpdateSavedFilterAttributes" request + And request contains "saved_filter_id" parameter with value "2cfc7949-7eb4-4d76-b1b8-d1aa9b565535" + And body with value {"data":{"type":"saved-filters","attributes":{"name":"patchSavedFilterTest-LIST-157e7ff3"}}} + When the request is sent + Then the response status is 409 Conflict + And the response "errors" has length 1 + And the response "errors[0].status" is equal to "409" + And the response "errors[0].title" is equal to "Conflict" + And the response "errors[0].detail" is equal to "a saved filter with this name already exists: conflict occurred" + + @replay-only @team:ddoghq/feature-flags + Scenario: Patch a saved filter rejects a null-only update + Given new "UpdateSavedFilterAttributes" request + And request contains "saved_filter_id" parameter with value "2cfc7949-7eb4-4d76-b1b8-d1aa9b565535" + And body with value {"data":{"type":"saved-filters","attributes":{"description":null}}} + 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].status" is equal to "400" + And the response "errors[0].title" is equal to "Bad Request" + And the response "errors[0].detail" is equal to "at least one field must be provided" + + @replay-only @team:ddoghq/feature-flags + Scenario: Patch a saved filter rejects a timestamp-only update + Given new "UpdateSavedFilterAttributes" request + And request contains "saved_filter_id" parameter with value "a8c393b4-3a9c-48fd-aa48-402c97e8d9c9" + And body with value {"data":{"attributes":{"last_updated_at":"2026-10-16T20:20:30Z"},"type":"saved-filters"}} + 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].status" is equal to "400" + And the response "errors[0].title" is equal to "Bad Request" + And the response "errors[0].detail" is equal to "at least one field must be provided" + + @replay-only @skip-python @skip-ruby @team:ddoghq/feature-flags + Scenario: Patch a saved filter rejects an empty name + Given new "UpdateSavedFilterAttributes" request + And request contains "saved_filter_id" parameter with value "2cfc7949-7eb4-4d76-b1b8-d1aa9b565535" + And body with value {"data":{"type":"saved-filters","attributes":{"name":""}}} + 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].status" is equal to "400" + And the response "errors[0].title" is equal to "Bad Request" + And the response "errors[0].detail" is equal to "name must not be empty" + + @replay-only @skip-java @skip-python @skip-ruby @skip-typescript @team:ddoghq/feature-flags + Scenario: Patch a saved filter rejects an invalid timestamp + Given new "UpdateSavedFilterAttributes" request + And request contains "saved_filter_id" parameter with value "2cfc7949-7eb4-4d76-b1b8-d1aa9b565535" + And body with value {"data":{"type":"saved-filters","attributes":{"description":"Not applied.","last_updated_at":"invalid"}}} + 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].status" is equal to "400" + And the response "errors[0].title" is equal to "Bad Request" + And the response "errors[0].detail" is equal to "last_updated_at must be a valid RFC3339 timestamp" + + @replay-only @skip-python @skip-ruby @team:ddoghq/feature-flags + Scenario: Patch a saved filter rejects empty targeting rules + Given new "UpdateSavedFilterAttributes" request + And request contains "saved_filter_id" parameter with value "2cfc7949-7eb4-4d76-b1b8-d1aa9b565535" + And body with value {"data":{"type":"saved-filters","attributes":{"targeting_rules":[]}}} + 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].status" is equal to "400" + And the response "errors[0].title" is equal to "Bad Request" + And the response "errors[0].detail" is equal to "attribute \"targeting_rules\" failed min validation" + + @replay-only @team:ddoghq/feature-flags + Scenario: Patch a saved filter rejects nested saved filters + Given new "UpdateSavedFilterAttributes" request + And request contains "saved_filter_id" parameter with value "2cfc7949-7eb4-4d76-b1b8-d1aa9b565535" + And body with value {"data":{"type":"saved-filters","attributes":{"targeting_rules":[{"conditions":[{"operator":"ONE_OF","attribute":"user_id","value":["test-user-1"],"saved_filter_id":"d8e3a81b-1505-4600-99c6-c563d01d46b1"}]}]}}} + 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].status" is equal to "400" + And the response "errors[0].title" is equal to "Bad Request" + And the response "errors[0].detail" is equal to "targeting rule 0 condition 0: saved filters cannot reference other saved filters" + + @replay-only @team:ddoghq/feature-flags + Scenario: Patch a saved filter rejects stale rule updates + Given new "UpdateSavedFilterAttributes" request + And request contains "saved_filter_id" parameter with value "a8c393b4-3a9c-48fd-aa48-402c97e8d9c9" + And body with value {"data":{"attributes":{"last_updated_at":"2020-01-01T00:00:00Z","targeting_rules":[{"conditions":[{"attribute":"user_id","operator":"ONE_OF","value":["not-applied"]}]}]},"type":"saved-filters"}} + When the request is sent + Then the response status is 409 Conflict + And the response "errors" has length 1 + And the response "errors[0].status" is equal to "409" + And the response "errors[0].title" is equal to "Conflict" + And the response "errors[0].detail" is equal to "saved filter has been modified since you last fetched it; please refresh and retry: conflict occurred" + + @replay-only @team:ddoghq/feature-flags + Scenario: Patch a saved filter replaces rules and preserves metadata + Given new "UpdateSavedFilterAttributes" request + And request contains "saved_filter_id" parameter with value "2cfc7949-7eb4-4d76-b1b8-d1aa9b565535" + And body with value {"data":{"type":"saved-filters","attributes":{"targeting_rules":[{"conditions":[{"operator":"ONE_OF","attribute":"email","value":["test@example.com"]}]}]}}} + When the request is sent + Then the response status is 200 OK + And the response "data.id" is equal to "2cfc7949-7eb4-4d76-b1b8-d1aa9b565535" + And the response "data.type" is equal to "saved-filters" + And the response "data.attributes.name" is equal to "patchSavedFilterTest-renamed-157e7ff3" + And the response "data.attributes.description" is equal to "Original synthetic test description." + And the response "data.attributes.creation_type" is equal to "RULES" + And the response "data.attributes.targeting_rules" has length 1 + And the response "data.attributes.targeting_rules[0].conditions" has length 1 + And the response "data.attributes.targeting_rules[0].conditions[0].operator" is equal to "ONE_OF" + And the response "data.attributes.targeting_rules[0].conditions[0].attribute" is equal to "email" + And the response "data.attributes.targeting_rules[0].conditions[0].value" is equal to ["test@example.com"] + + @replay-only @team:ddoghq/feature-flags + Scenario: Patch a saved filter replaces the rules of a saved list + Given new "UpdateSavedFilterAttributes" request + And request contains "saved_filter_id" parameter with value "d8e3a81b-1505-4600-99c6-c563d01d46b1" + And body with value {"data":{"type":"saved-filters","attributes":{"targeting_rules":[{"conditions":[{"operator":"ONE_OF","attribute":"user_id","value":["test-user-3"]}]}]}}} + When the request is sent + Then the response status is 200 OK + And the response "data.id" is equal to "d8e3a81b-1505-4600-99c6-c563d01d46b1" + And the response "data.type" is equal to "saved-filters" + And the response "data.attributes.name" is equal to "patchSavedFilterTest-LIST-157e7ff3" + And the response "data.attributes.description" is equal to "Updated synthetic test description." + And the response "data.attributes.creation_type" is equal to "LIST" + And the response "data.attributes.targeting_rules" has length 1 + And the response "data.attributes.targeting_rules[0].conditions" has length 1 + And the response "data.attributes.targeting_rules[0].conditions[0].operator" is equal to "ONE_OF" + And the response "data.attributes.targeting_rules[0].conditions[0].attribute" is equal to "user_id" + And the response "data.attributes.targeting_rules[0].conditions[0].value" is equal to ["test-user-3"] + + @replay-only @team:ddoghq/feature-flags + Scenario: Patch a saved filter returns "Bad Request" response + Given new "UpdateSavedFilterAttributes" request + And request contains "saved_filter_id" parameter with value "2cfc7949-7eb4-4d76-b1b8-d1aa9b565535" + And body with value {"data":{"type":"saved-filters","attributes":{}}} + 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].status" is equal to "400" + And the response "errors[0].title" is equal to "Bad Request" + And the response "errors[0].detail" is equal to "at least one field must be provided" + + @replay-only @team:ddoghq/feature-flags + Scenario: Patch a saved filter returns "Conflict" response + Given new "UpdateSavedFilterAttributes" request + And request contains "saved_filter_id" parameter with value "2cfc7949-7eb4-4d76-b1b8-d1aa9b565535" + And body with value {"data":{"type":"saved-filters","attributes":{"description":"Not applied.","last_updated_at":"2020-01-01T00:00:00Z"}}} + When the request is sent + Then the response status is 409 Conflict + And the response "errors" has length 1 + And the response "errors[0].status" is equal to "409" + And the response "errors[0].title" is equal to "Conflict" + And the response "errors[0].detail" is equal to "saved filter has been modified since you last fetched it; please refresh and retry: conflict occurred" + + @replay-only @team:ddoghq/feature-flags + Scenario: Patch a saved filter returns "Not Found" response + Given new "UpdateSavedFilterAttributes" request + And request contains "saved_filter_id" parameter with value "00000000-0000-0000-0000-000000000000" + And body with value {"data":{"type":"saved-filters","attributes":{"description":"Not applied."}}} + When the request is sent + Then the response status is 404 Not Found + And the response "errors" has length 1 + And the response "errors[0].status" is equal to "404" + And the response "errors[0].title" is equal to "Not Found" + And the response "errors[0].detail" is equal to "saved filter not found or already deleted: record not found" + + @replay-only @team:ddoghq/feature-flags + Scenario: Patch a saved filter returns "OK" response + Given new "UpdateSavedFilterAttributes" request + And request contains "saved_filter_id" parameter with value "2cfc7949-7eb4-4d76-b1b8-d1aa9b565535" + And body with value {"data":{"type":"saved-filters","attributes":{"name":"patchSavedFilterTest-renamed-157e7ff3"}}} + When the request is sent + Then the response status is 200 OK + And the response "data.id" is equal to "2cfc7949-7eb4-4d76-b1b8-d1aa9b565535" + And the response "data.type" is equal to "saved-filters" + And the response "data.attributes.name" is equal to "patchSavedFilterTest-renamed-157e7ff3" + And the response "data.attributes.description" is equal to "Original synthetic test description." + And the response "data.attributes.creation_type" is equal to "RULES" + And the response "data.attributes.targeting_rules" has length 1 + And the response "data.attributes.targeting_rules[0].conditions" has length 1 + And the response "data.attributes.targeting_rules[0].conditions[0].operator" is equal to "ONE_OF" + And the response "data.attributes.targeting_rules[0].conditions[0].attribute" is equal to "user_id" + And the response "data.attributes.targeting_rules[0].conditions[0].value" is equal to ["test-user-1","test-user-2"] + + @replay-only @team:ddoghq/feature-flags + Scenario: Patch a saved filter updates metadata and rules together + Given new "UpdateSavedFilterAttributes" request + And request contains "saved_filter_id" parameter with value "2cfc7949-7eb4-4d76-b1b8-d1aa9b565535" + And body with value {"data":{"type":"saved-filters","attributes":{"name":"patchSavedFilterTest-combined-157e7ff3","description":"Combined synthetic test update.","targeting_rules":[{"conditions":[{"operator":"ONE_OF","attribute":"org_id","value":["test-org-1"]}]}]}}} + When the request is sent + Then the response status is 200 OK + And the response "data.id" is equal to "2cfc7949-7eb4-4d76-b1b8-d1aa9b565535" + And the response "data.type" is equal to "saved-filters" + And the response "data.attributes.name" is equal to "patchSavedFilterTest-combined-157e7ff3" + And the response "data.attributes.description" is equal to "Combined synthetic test update." + And the response "data.attributes.creation_type" is equal to "RULES" + And the response "data.attributes.targeting_rules" has length 1 + And the response "data.attributes.targeting_rules[0].conditions" has length 1 + And the response "data.attributes.targeting_rules[0].conditions[0].operator" is equal to "ONE_OF" + And the response "data.attributes.targeting_rules[0].conditions[0].attribute" is equal to "org_id" + And the response "data.attributes.targeting_rules[0].conditions[0].value" is equal to ["test-org-1"] + + @replay-only @team:ddoghq/feature-flags + Scenario: Patch a saved filter updates only the description of a saved list + Given new "UpdateSavedFilterAttributes" request + And request contains "saved_filter_id" parameter with value "d8e3a81b-1505-4600-99c6-c563d01d46b1" + And body with value {"data":{"type":"saved-filters","attributes":{"description":"Updated synthetic test description."}}} + When the request is sent + Then the response status is 200 OK + And the response "data.id" is equal to "d8e3a81b-1505-4600-99c6-c563d01d46b1" + And the response "data.type" is equal to "saved-filters" + And the response "data.attributes.name" is equal to "patchSavedFilterTest-LIST-157e7ff3" + And the response "data.attributes.description" is equal to "Updated synthetic test description." + And the response "data.attributes.creation_type" is equal to "LIST" + And the response "data.attributes.targeting_rules" has length 1 + And the response "data.attributes.targeting_rules[0].conditions" has length 1 + And the response "data.attributes.targeting_rules[0].conditions[0].operator" is equal to "ONE_OF" + And the response "data.attributes.targeting_rules[0].conditions[0].attribute" is equal to "user_id" + And the response "data.attributes.targeting_rules[0].conditions[0].value" is equal to ["test-user-1","test-user-2"] + @generated @skip @team:ddoghq/feature-flags Scenario: Pause a progressive rollout returns "Bad Request" response Given new "PauseExposureSchedule" request diff --git a/tests/v2/features/undo.json b/tests/v2/features/undo.json index 5099fc97d1..3f937ed045 100644 --- a/tests/v2/features/undo.json +++ b/tests/v2/features/undo.json @@ -3263,6 +3263,12 @@ "type": "safe" } }, + "UpdateSavedFilterAttributes": { + "tag": "Feature Flags", + "undo": { + "type": "idempotent" + } + }, "GetFeatureFlag": { "tag": "Feature Flags", "undo": {