diff --git a/api/audit/signals.py b/api/audit/signals.py index 5a832e4fe294..0f909a8ab96d 100644 --- a/api/audit/signals.py +++ b/api/audit/signals.py @@ -1,4 +1,5 @@ import logging +import typing from django.conf import settings from django.db.models.signals import post_save @@ -18,6 +19,33 @@ logger = logging.getLogger(__name__) +AuditLogIntegrationAttrName = typing.Literal[ + "data_dog_config", + "dynatrace_config", + "grafana_config", + "new_relic_config", + "slack_config", +] + + +def _get_integration_config( + instance: AuditLog, + integration_name: AuditLogIntegrationAttrName, +) -> IntegrationsModel | None: + for relation_name in ("project", "environment", "organisation"): + if hasattr( + related_object := getattr(instance, relation_name), + integration_name, + ): + integration_config: IntegrationsModel = getattr( + related_object, + integration_name, + ) + if not integration_config.deleted: + return integration_config + return None + + @receiver(post_save, sender=AuditLog) def call_webhooks(sender, instance, **kwargs): # type: ignore[no-untyped-def] if settings.DISABLE_WEBHOOKS: @@ -42,18 +70,6 @@ def call_webhooks(sender, instance, **kwargs): # type: ignore[no-untyped-def] ) -def _get_integration_config( - instance: AuditLog, integration_name: str -) -> IntegrationsModel | None: - if hasattr(project := instance.project, integration_name): - return getattr(project, integration_name) # type: ignore[no-any-return] - if hasattr(environment := instance.environment, integration_name): - return getattr(environment, integration_name) # type: ignore[no-any-return] - if hasattr(organisation := instance.organisation, integration_name): - return getattr(organisation, integration_name) # type: ignore[no-any-return] - return None - - def track_only_feature_related_events(signal_function): # type: ignore[no-untyped-def] def signal_wrapper(sender, instance, **kwargs): # type: ignore[no-untyped-def] # Only handle Feature related changes diff --git a/api/tests/unit/audit/test_unit_audit_signals.py b/api/tests/unit/audit/test_unit_audit_signals.py index 3d28ed77f1bd..bbd15ad42d51 100644 --- a/api/tests/unit/audit/test_unit_audit_signals.py +++ b/api/tests/unit/audit/test_unit_audit_signals.py @@ -168,6 +168,32 @@ def test_send_audit_log_event_to_grafana__organisation_grafana_config__calls_exp ) +def test_send_audit_log_event_to_grafana__organisation_grafana_config__deleted__doesnt_call( + mocker: MockerFixture, + organisation: Organisation, + project: Project, +) -> None: + # Given + audit_log_record = AuditLog.objects.create( + project=project, + related_object_type=RelatedObjectType.FEATURE.name, + ) + grafana_wrapper_mock = mocker.patch("audit.signals.GrafanaWrapper", autospec=True) + + grafana_config = GrafanaOrganisationConfiguration.objects.create( + organisation=organisation, + base_url="test.com", + api_key="test", + ) + grafana_config.delete() + + # When + send_audit_log_event_to_grafana(AuditLog, audit_log_record) + + # Then + grafana_wrapper_mock.assert_not_called() + + @responses.activate def test_send_environment_feature_version_audit_log_event_to_grafana( tagged_feature: Feature,