diff --git a/src/sentry/integrations/api/serializers/rest_framework/data_forwarder.py b/src/sentry/integrations/api/serializers/rest_framework/data_forwarder.py index 0105b9cd9011fb..137ea9165900ba 100644 --- a/src/sentry/integrations/api/serializers/rest_framework/data_forwarder.py +++ b/src/sentry/integrations/api/serializers/rest_framework/data_forwarder.py @@ -50,12 +50,14 @@ class DataForwarderSerializer(Serializer): (DataForwarderProviderSlug.SPLUNK, "Splunk"), ] ) - config = serializers.DictField(child=serializers.CharField(allow_blank=False), default=dict) + config = serializers.DictField(child=serializers.CharField(allow_blank=True), default=dict) project_ids = serializers.ListField( - child=serializers.IntegerField(), allow_empty=True, required=True + child=serializers.IntegerField(), allow_empty=True, required=False, default=list ) def validate_config(self, config) -> SQSConfig | SegmentConfig | SplunkConfig: + # Filter out empty string values (cleared optional fields) + config = {k: v for k, v in config.items() if v != ""} provider = self.initial_data.get("provider") if provider == DataForwarderProviderSlug.SQS: @@ -210,12 +212,16 @@ def create(self, validated_data: Mapping[str, Any]) -> DataForwarder: # Enroll specified projects if project_ids: - for project_id in project_ids: - DataForwarderProject.objects.create( - data_forwarder=data_forwarder, - project_id=project_id, - is_enabled=False, - ) + DataForwarderProject.objects.bulk_create( + [ + DataForwarderProject( + data_forwarder=data_forwarder, + project_id=project_id, + is_enabled=True, + ) + for project_id in project_ids + ] + ) return data_forwarder def update(self, instance: DataForwarder, validated_data: Mapping[str, Any]) -> DataForwarder: diff --git a/tests/sentry/integrations/api/endpoints/test_data_forwarding.py b/tests/sentry/integrations/api/endpoints/test_data_forwarding.py index 3c07f9964f195c..0616420ad2a5c0 100644 --- a/tests/sentry/integrations/api/endpoints/test_data_forwarding.py +++ b/tests/sentry/integrations/api/endpoints/test_data_forwarding.py @@ -346,14 +346,17 @@ def test_create_missing_config(self) -> None: response = self.get_error_response(self.organization.slug, status_code=400, **payload) assert "config" in str(response.data).lower() - def test_create_missing_project_ids(self) -> None: + def test_create_without_project_ids(self) -> None: payload = { "provider": DataForwarderProviderSlug.SEGMENT, "config": {"write_key": "test_key"}, } - response = self.get_error_response(self.organization.slug, status_code=400, **payload) - assert "project_ids" in str(response.data).lower() + response = self.get_success_response(self.organization.slug, status_code=201, **payload) + assert response.data["provider"] == DataForwarderProviderSlug.SEGMENT + + data_forwarder = DataForwarder.objects.get(id=response.data["id"]) + assert data_forwarder.projects.count() == 0 def test_create_sqs_fifo_queue_validation(self) -> None: payload = { diff --git a/tests/sentry/integrations/api/serializers/rest_framework/test_data_forwarder.py b/tests/sentry/integrations/api/serializers/rest_framework/test_data_forwarder.py index 5c23e479ea554f..64a5e037c4688a 100644 --- a/tests/sentry/integrations/api/serializers/rest_framework/test_data_forwarder.py +++ b/tests/sentry/integrations/api/serializers/rest_framework/test_data_forwarder.py @@ -69,17 +69,6 @@ def test_required_fields(self) -> None: assert not serializer.is_valid() assert "provider" in serializer.errors - # Missing project_ids - serializer = DataForwarderSerializer( - data={ - "organization_id": self.organization.id, - "provider": DataForwarderProviderSlug.SEGMENT, - "config": {"write_key": "test_key"}, - } - ) - assert not serializer.is_valid() - assert "project_ids" in serializer.errors - def test_provider_choice_validation(self) -> None: # Valid providers provider_configs = { @@ -230,8 +219,8 @@ def test_sqs_config_validation_empty_credentials(self) -> None: ) assert not serializer.is_valid() assert "config" in serializer.errors - config_errors = serializer.errors["config"] - assert "access_key" in config_errors or "secret_key" in config_errors + config_errors_str = str(serializer.errors["config"]) + assert "access_key" in config_errors_str and "secret_key" in config_errors_str def test_sqs_config_validation_fifo_queue_without_message_group_id(self) -> None: config: dict[str, str] = { @@ -419,8 +408,8 @@ def test_splunk_config_validation_empty_strings(self) -> None: ) assert not serializer.is_valid() assert "config" in serializer.errors - config_errors = serializer.errors["config"] - assert "index" in config_errors or "source" in config_errors + config_errors_str = str(serializer.errors["config"]) + assert "index" in config_errors_str and "source" in config_errors_str def test_splunk_config_validation_invalid_token_format(self) -> None: config: dict[str, str] = {