diff --git a/src/sentry/workflow_engine/endpoints/validators/base/detector.py b/src/sentry/workflow_engine/endpoints/validators/base/detector.py index fb0b35697c9913..dbf76f670d107e 100644 --- a/src/sentry/workflow_engine/endpoints/validators/base/detector.py +++ b/src/sentry/workflow_engine/endpoints/validators/base/detector.py @@ -133,6 +133,14 @@ def update(self, instance: Detector, validated_data: dict[str, Any]): group_validator = BaseDataConditionGroupValidator() group_validator.update(instance.workflow_condition_group, condition_group) + # Handle config field update + if "config" in validated_data: + instance.config = validated_data.get("config", instance.config) + try: + enforce_config_schema(instance) + except JSONSchemaValidationError as error: + raise serializers.ValidationError({"config": [str(error)]}) + instance.save() create_audit_entry( diff --git a/tests/sentry/workflow_engine/endpoints/test_organization_detector_details.py b/tests/sentry/workflow_engine/endpoints/test_organization_detector_details.py index 5eb67ad81a99f7..3202652746a745 100644 --- a/tests/sentry/workflow_engine/endpoints/test_organization_detector_details.py +++ b/tests/sentry/workflow_engine/endpoints/test_organization_detector_details.py @@ -709,6 +709,59 @@ def test_update_workflows_no_changes(self) -> None: == 0 ) + def test_update_config_valid(self) -> None: + """Test updating detector config with valid schema data""" + # Initial config + initial_config = {"detection_type": "static", "comparison_delta": None} + self.detector.config = initial_config + self.detector.save() + + # Update with valid new config + updated_config = {"detection_type": "dynamic", "comparison_delta": 3600} + data = { + "config": updated_config, + } + + with self.tasks(): + response = self.get_success_response( + self.organization.slug, + self.detector.id, + **data, + status_code=200, + ) + + self.detector.refresh_from_db() + # Verify config was updated in database (snake_case) + assert self.detector.config == updated_config + # API returns camelCase + assert response.data["config"] == { + "detectionType": "dynamic", + "comparisonDelta": 3600, + } + + def test_update_config_invalid_schema(self) -> None: + """Test updating detector config with invalid schema data fails validation""" + # Config missing required field 'detection_type' + invalid_config = {"comparison_delta": 3600} + data = { + "config": invalid_config, + } + + with self.tasks(): + response = self.get_error_response( + self.organization.slug, + self.detector.id, + **data, + status_code=400, + ) + + assert "config" in response.data + assert "detection_type" in str(response.data["config"]) + + # Verify config was not updated + self.detector.refresh_from_db() + assert self.detector.config != invalid_config + @region_silo_test class OrganizationDetectorDetailsDeleteTest(OrganizationDetectorDetailsBaseTest):