import json from unittest.mock import ANY, Mock, patch import pytest from django.urls import reverse from rest_framework import serializers, status from rest_framework.response import Response from rest_framework.test import APIClient from apps.alerts.constants import SERVICE_LABEL, SERVICE_LABEL_TEMPLATE_FOR_ALERTING_INTEGRATION from apps.alerts.grafana_alerting_sync_manager import GrafanaAlertingSyncManager from apps.alerts.models import AlertReceiveChannel, EscalationPolicy from apps.api.permissions import LegacyAccessControlRole from apps.base.messaging import load_backend from apps.labels.models import LabelKeyCache from common.exceptions import BacksyncIntegrationRequestError class AdditionalSettingsTestSerializer(serializers.Serializer): instance_url = serializers.CharField(required=True) def validate(self, data): if hasattr(self, "initial_data"): unknown_fields = set(self.initial_data.keys()) - set(self.fields.keys()) if unknown_fields: raise serializers.ValidationError("Unexpected fields: {}".format(unknown_fields)) return data def to_internal_value(self, data): return super().to_internal_value(data) def to_representation(self, instance): return super().to_representation(instance.additional_settings) @pytest.fixture() def alert_receive_channel_internal_api_setup( make_organization_and_user_with_plugin_token, make_alert_receive_channel, make_escalation_chain, ): organization, user, token = make_organization_and_user_with_plugin_token() alert_receive_channel = make_alert_receive_channel(organization) make_escalation_chain(organization) return user, token, alert_receive_channel @pytest.fixture def setup_additional_settings_for_integration(): integration_config = AlertReceiveChannel._config[2] # It points to alertmanager previous_value = getattr(integration_config, "additional_settings_serializer", None) integration_config.additional_settings_serializer = AdditionalSettingsTestSerializer try: yield integration_config finally: integration_config.additional_settings_serializer = previous_value @pytest.mark.django_db def test_get_alert_receive_channel(alert_receive_channel_internal_api_setup, make_user_auth_headers): user, token, _ = alert_receive_channel_internal_api_setup client = APIClient() url = reverse("api-internal:alert_receive_channel-list") response = client.get(url, format="json", **make_user_auth_headers(user, token)) assert response.status_code == status.HTTP_200_OK @pytest.mark.django_db def test_get_alert_receive_channel_by_integration_ne( make_organization_and_user_with_plugin_token, make_user_auth_headers, make_alert_receive_channel ): organization, user, token = make_organization_and_user_with_plugin_token() make_alert_receive_channel(organization, integration=AlertReceiveChannel.INTEGRATION_GRAFANA) make_alert_receive_channel(organization, integration=AlertReceiveChannel.INTEGRATION_GRAFANA_ALERTING) make_alert_receive_channel(organization, integration=AlertReceiveChannel.INTEGRATION_DIRECT_PAGING) client = APIClient() url = f"{reverse('api-internal:alert_receive_channel-list')}?integration_ne={AlertReceiveChannel.INTEGRATION_DIRECT_PAGING}" response = client.get(url, format="json", **make_user_auth_headers(user, token)) results = response.json()["results"] assert response.status_code == status.HTTP_200_OK assert len(results) == 2 for result in results: assert result["integration"] != AlertReceiveChannel.INTEGRATION_DIRECT_PAGING @pytest.mark.django_db def test_get_alert_receive_channel_by_id_ne( make_organization_and_user_with_plugin_token, make_organization, make_user_auth_headers, make_alert_receive_channel ): organization, user, token = make_organization_and_user_with_plugin_token() alert_receive_channel_1 = make_alert_receive_channel(organization) alert_receive_channel_2 = make_alert_receive_channel(organization) alert_receive_channel_3 = make_alert_receive_channel(organization) # integration in a different org organization = make_organization() alert_receive_channel_4 = make_alert_receive_channel(organization) client = APIClient() url = ( reverse("api-internal:alert_receive_channel-list") + f"?id_ne={alert_receive_channel_1.public_primary_key}&id_ne={alert_receive_channel_2.public_primary_key}" ) response = client.get(url, **make_user_auth_headers(user, token)) assert response.status_code == status.HTTP_200_OK assert len(response.json()["results"]) == 1 assert response.json()["results"][0]["id"] == alert_receive_channel_3.public_primary_key # integration in a different org shouldn't work url = reverse("api-internal:alert_receive_channel-list") + f"?id_ne={alert_receive_channel_4.public_primary_key}" response = client.get(url, **make_user_auth_headers(user, token)) assert response.status_code == status.HTTP_400_BAD_REQUEST @pytest.mark.django_db @pytest.mark.parametrize( "query_param,should_be_unpaginated", [ ("True", True), ("true", True), ("TRUE", True), ("", False), ("False", False), ("false", False), ("FALSE", False), ], ) def test_list_alert_receive_channel_skip_pagination_for_grafana_alerting( alert_receive_channel_internal_api_setup, make_user_auth_headers, query_param, should_be_unpaginated, ): user, token, _ = alert_receive_channel_internal_api_setup client = APIClient() url = reverse("api-internal:alert_receive_channel-list") response = client.get(f"{url}?skip_pagination={query_param}", format="json", **make_user_auth_headers(user, token)) results = response.json() assert response.status_code == status.HTTP_200_OK if should_be_unpaginated: assert type(results) is list assert len(results) > 0 else: assert type(results["results"]) is list assert len(results["results"]) > 0 @pytest.mark.django_db def test_heartbeat_data_absence_alert_receive_channel(alert_receive_channel_internal_api_setup, make_user_auth_headers): """ We get AlertReceiveChannel and there is no related heartbeat model object. """ user, token, alert_receive_channel = alert_receive_channel_internal_api_setup client = APIClient() url = reverse("api-internal:alert_receive_channel-detail", kwargs={"pk": alert_receive_channel.public_primary_key}) response = client.get(url, format="json", **make_user_auth_headers(user, token)) assert response.status_code == status.HTTP_200_OK assert response.json()["heartbeat"] is None @pytest.mark.django_db def test_heartbeat_data_presence_alert_receive_channel( alert_receive_channel_internal_api_setup, make_integration_heartbeat, make_user_auth_headers, ): """ We get AlertReceiveChannel and there IS related heartbeat model object. That is why we check for heartbeat model properties. """ user, token, alert_receive_channel = alert_receive_channel_internal_api_setup _ = make_integration_heartbeat(alert_receive_channel) client = APIClient() url = reverse("api-internal:alert_receive_channel-detail", kwargs={"pk": alert_receive_channel.public_primary_key}) response = client.get(url, format="json", **make_user_auth_headers(user, token)) expected_heartbeat_payload = { "id": alert_receive_channel.integration_heartbeat.public_primary_key, "last_heartbeat_time_verbal": None, "alert_receive_channel": alert_receive_channel.public_primary_key, "link": alert_receive_channel.integration_heartbeat.link, "timeout_seconds": 60, "status": False, "instruction": response.json()["heartbeat"]["instruction"], } assert response.json()["heartbeat"] is not None assert response.json()["heartbeat"] == expected_heartbeat_payload @pytest.mark.django_db def test_create_alert_receive_channel(alert_receive_channel_internal_api_setup, make_user_auth_headers): user, token, _ = alert_receive_channel_internal_api_setup client = APIClient() url = reverse("api-internal:alert_receive_channel-list") data = { "integration": AlertReceiveChannel.INTEGRATION_GRAFANA, "team": None, } response = client.post(url, data, format="json", **make_user_auth_headers(user, token)) assert response.status_code == status.HTTP_201_CREATED @pytest.mark.django_db def test_alert_receive_channel_name_uniqueness( alert_receive_channel_internal_api_setup, make_team, make_user_auth_headers, ): user, token, alert_receive_channel = alert_receive_channel_internal_api_setup client = APIClient() url = reverse("api-internal:alert_receive_channel-list") data = { "integration": AlertReceiveChannel.INTEGRATION_GRAFANA, "team": alert_receive_channel.team.public_primary_key if alert_receive_channel.team else None, "verbal_name": alert_receive_channel.verbal_name, } response = client.post(url, data, format="json", **make_user_auth_headers(user, token)) assert response.status_code == status.HTTP_400_BAD_REQUEST # name can be reused in a different team another_team = make_team(alert_receive_channel.organization) data = { "integration": AlertReceiveChannel.INTEGRATION_GRAFANA, "team": another_team.public_primary_key, "verbal_name": alert_receive_channel.verbal_name, } response = client.post(url, data, format="json", **make_user_auth_headers(user, token)) assert response.status_code == status.HTTP_201_CREATED # update works url = reverse("api-internal:alert_receive_channel-detail", kwargs={"pk": alert_receive_channel.public_primary_key}) response = client.put( url, data=json.dumps( { "team": alert_receive_channel.team, "verbal_name": alert_receive_channel.verbal_name, "description": "update description", } ), content_type="application/json", **make_user_auth_headers(user, token), ) assert response.status_code == status.HTTP_200_OK # but updating team will fail if name exists response = client.put( url, data=json.dumps( { "team": another_team.public_primary_key, "verbal_name": alert_receive_channel.verbal_name, } ), content_type="application/json", **make_user_auth_headers(user, token), ) assert response.status_code == status.HTTP_400_BAD_REQUEST @pytest.mark.django_db def test_alert_receive_channel_name_uniqueness_validation_optional_team_field( alert_receive_channel_internal_api_setup, make_team, make_user_auth_headers, make_alert_receive_channel ): """ Test the uniqueness validation for alert receive channel names with optional team field. Creates two alert receive channels with the same name, where the first has no team and the second has a team. It checks if updating the second channel with a request without the "team" field passes the uniqueness validation based on team+name. (The team field is optional in the API). Related issue: https://github.com/grafana/oncall/issues/4118 """ user, token, alert_receive_channel = alert_receive_channel_internal_api_setup team = make_team(alert_receive_channel.organization) alert_receive_channel_with_team = make_alert_receive_channel( alert_receive_channel.organization, team=team, verbal_name=alert_receive_channel.verbal_name ) client = APIClient() # update works when team is not present in request data url = reverse( "api-internal:alert_receive_channel-detail", kwargs={"pk": alert_receive_channel_with_team.public_primary_key} ) response = client.put( url, data=json.dumps( { "verbal_name": alert_receive_channel.verbal_name, "description": "update description", } ), content_type="application/json", **make_user_auth_headers(user, token), ) assert response.status_code == status.HTTP_200_OK # update works when team is present url = reverse( "api-internal:alert_receive_channel-detail", kwargs={"pk": alert_receive_channel_with_team.public_primary_key} ) response = client.put( url, data=json.dumps( { "verbal_name": alert_receive_channel.verbal_name, "description": "update description", "team": team.public_primary_key, } ), content_type="application/json", **make_user_auth_headers(user, token), ) assert response.status_code == status.HTTP_200_OK @pytest.mark.django_db def test_alert_receive_channel_name_duplicated( alert_receive_channel_internal_api_setup, make_alert_receive_channel, make_user_auth_headers, ): # this could happen in case a team is removed and integrations are set to have "no team" user, token, alert_receive_channel = alert_receive_channel_internal_api_setup # another integration with the same verbal name make_alert_receive_channel( alert_receive_channel.organization, verbal_name=alert_receive_channel.verbal_name, ) client = APIClient() # updating team will require changing the name or the team url = reverse("api-internal:alert_receive_channel-detail", kwargs={"pk": alert_receive_channel.public_primary_key}) response = client.put( url, data=json.dumps({"verbal_name": alert_receive_channel.verbal_name}), content_type="application/json", **make_user_auth_headers(user, token), ) assert response.status_code == status.HTTP_400_BAD_REQUEST response = client.put( url, data=json.dumps({"verbal_name": "a new name"}), content_type="application/json", **make_user_auth_headers(user, token), ) assert response.status_code == status.HTTP_200_OK @pytest.mark.django_db def test_create_invalid_alert_receive_channel(alert_receive_channel_internal_api_setup, make_user_auth_headers): user, token, _ = alert_receive_channel_internal_api_setup client = APIClient() url = reverse("api-internal:alert_receive_channel-list") data = {"integration": AlertReceiveChannel.INTEGRATION_GRAFANA, "verbal_name": ""} response = client.post(url, data, format="json", **make_user_auth_headers(user, token)) assert response.status_code == status.HTTP_400_BAD_REQUEST @pytest.mark.django_db def test_create_invalid_alert_receive_channel_type(alert_receive_channel_internal_api_setup, make_user_auth_headers): user, token, _ = alert_receive_channel_internal_api_setup client = APIClient() url = reverse("api-internal:alert_receive_channel-list") response_1 = client.post( url, data={"integration": "random123", "verbal_name": "Random 123"}, format="json", **make_user_auth_headers(user, token), ) response_2 = client.post( url, data={"verbal_name": "Random 123"}, format="json", **make_user_auth_headers(user, token) ) assert response_1.status_code == status.HTTP_400_BAD_REQUEST assert response_2.status_code == status.HTTP_400_BAD_REQUEST @pytest.mark.django_db def test_update_alert_receive_channel(alert_receive_channel_internal_api_setup, make_user_auth_headers): user, token, alert_receive_channel = alert_receive_channel_internal_api_setup client = APIClient() url = reverse("api-internal:alert_receive_channel-detail", kwargs={"pk": alert_receive_channel.public_primary_key}) response = client.patch( url, data=json.dumps({"verbal_name": "test_set_verbal_name"}), content_type="application/json", **make_user_auth_headers(user, token), ) alert_receive_channel.refresh_from_db() assert response.status_code == status.HTTP_200_OK assert alert_receive_channel.verbal_name == "test_set_verbal_name" @pytest.mark.django_db def test_integration_filter_by_maintenance(alert_receive_channel_internal_api_setup, make_user_auth_headers): user, token, alert_receive_channel = alert_receive_channel_internal_api_setup client = APIClient() mode = AlertReceiveChannel.MAINTENANCE duration = AlertReceiveChannel.DURATION_ONE_HOUR.seconds alert_receive_channel.start_maintenance(mode, duration, user) url = reverse("api-internal:alert_receive_channel-list") response = client.get( f"{url}?maintenance_mode={AlertReceiveChannel.MAINTENANCE}", content_type="application/json", **make_user_auth_headers(user, token), ) assert response.status_code == status.HTTP_200_OK assert len(response.json()["results"]) == 1 @pytest.mark.django_db def test_integration_filter_by_debug(alert_receive_channel_internal_api_setup, make_user_auth_headers): user, token, alert_receive_channel = alert_receive_channel_internal_api_setup client = APIClient() mode = AlertReceiveChannel.DEBUG_MAINTENANCE duration = AlertReceiveChannel.DURATION_ONE_HOUR.seconds with patch("apps.slack.utils.post_message_to_channel"): alert_receive_channel.start_maintenance(mode, duration, user) url = reverse("api-internal:alert_receive_channel-list") response = client.get( f"{url}?maintenance_mode={AlertReceiveChannel.DEBUG_MAINTENANCE}", content_type="application/json", **make_user_auth_headers(user, token), ) assert response.status_code == status.HTTP_200_OK assert len(response.json()["results"]) == 1 @pytest.mark.django_db def test_integration_search( make_organization_and_user_with_plugin_token, make_alert_receive_channel, make_user_auth_headers, ): organization, user, token = make_organization_and_user_with_plugin_token() make_alert_receive_channel(organization, verbal_name="grafana_prod") make_alert_receive_channel(organization, verbal_name="grafana_stage") client = APIClient() url = reverse("api-internal:alert_receive_channel-list") response = client.get( f"{url}?search=grafana", content_type="application/json", **make_user_auth_headers(user, token) ) assert response.status_code == status.HTTP_200_OK assert len(response.json()["results"]) == 2 response = client.get( f"{url}?search=zabbix", content_type="application/json", **make_user_auth_headers(user, token) ) assert response.status_code == status.HTTP_200_OK assert len(response.json()["results"]) == 0 response = client.get(f"{url}?search=prod", content_type="application/json", **make_user_auth_headers(user, token)) assert response.status_code == status.HTTP_200_OK assert len(response.json()["results"]) == 1 @pytest.mark.django_db @pytest.mark.parametrize( "role,expected_status", [ (LegacyAccessControlRole.ADMIN, status.HTTP_200_OK), (LegacyAccessControlRole.EDITOR, status.HTTP_403_FORBIDDEN), (LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN), (LegacyAccessControlRole.NONE, status.HTTP_403_FORBIDDEN), ], ) def test_alert_receive_channel_create_permissions( make_organization_and_user_with_plugin_token, make_user_auth_headers, role, expected_status, ): _, user, token = make_organization_and_user_with_plugin_token(role) client = APIClient() url = reverse("api-internal:alert_receive_channel-list") with patch( "apps.api.views.alert_receive_channel.AlertReceiveChannelView.create", return_value=Response( status=status.HTTP_200_OK, ), ): response = client.post(url, format="json", **make_user_auth_headers(user, token)) assert response.status_code == expected_status @pytest.mark.django_db @pytest.mark.parametrize( "role,expected_status", [ (LegacyAccessControlRole.ADMIN, status.HTTP_200_OK), (LegacyAccessControlRole.EDITOR, status.HTTP_403_FORBIDDEN), (LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN), (LegacyAccessControlRole.NONE, status.HTTP_403_FORBIDDEN), ], ) def test_alert_receive_channel_update_permissions( make_organization_and_user_with_plugin_token, make_user_auth_headers, make_alert_receive_channel, role, expected_status, ): organization, user, token = make_organization_and_user_with_plugin_token(role) alert_receive_channel = make_alert_receive_channel(organization) client = APIClient() url = reverse("api-internal:alert_receive_channel-detail", kwargs={"pk": alert_receive_channel.public_primary_key}) with patch( "apps.api.views.alert_receive_channel.AlertReceiveChannelView.update", return_value=Response( status=status.HTTP_200_OK, ), ): response = client.put(url, format="json", **make_user_auth_headers(user, token)) assert response.status_code == expected_status response = client.patch(url, format="json", **make_user_auth_headers(user, token)) assert response.status_code == expected_status @pytest.mark.django_db @pytest.mark.parametrize( "role,expected_status", [ (LegacyAccessControlRole.ADMIN, status.HTTP_204_NO_CONTENT), (LegacyAccessControlRole.EDITOR, status.HTTP_403_FORBIDDEN), (LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN), (LegacyAccessControlRole.NONE, status.HTTP_403_FORBIDDEN), ], ) def test_alert_receive_channel_delete_permissions( make_organization_and_user_with_plugin_token, make_user_auth_headers, make_alert_receive_channel, role, expected_status, ): organization, user, token = make_organization_and_user_with_plugin_token(role) alert_receive_channel = make_alert_receive_channel(organization) client = APIClient() url = reverse("api-internal:alert_receive_channel-detail", kwargs={"pk": alert_receive_channel.public_primary_key}) with patch( "apps.api.views.alert_receive_channel.AlertReceiveChannelView.destroy", return_value=Response( status=status.HTTP_204_NO_CONTENT, ), ): response = client.delete(url, format="json", **make_user_auth_headers(user, token)) assert response.status_code == expected_status @pytest.mark.django_db @pytest.mark.parametrize( "role,expected_status", [ (LegacyAccessControlRole.ADMIN, status.HTTP_200_OK), (LegacyAccessControlRole.EDITOR, status.HTTP_200_OK), (LegacyAccessControlRole.VIEWER, status.HTTP_200_OK), (LegacyAccessControlRole.NONE, status.HTTP_403_FORBIDDEN), ], ) def test_alert_receive_channel_list_permissions( make_organization_and_user_with_plugin_token, make_user_auth_headers, role, expected_status, ): _, user, token = make_organization_and_user_with_plugin_token(role) client = APIClient() url = reverse("api-internal:alert_receive_channel-list") with patch( "apps.api.views.alert_receive_channel.AlertReceiveChannelView.list", return_value=Response( status=status.HTTP_200_OK, ), ): response = client.get(url, format="json", **make_user_auth_headers(user, token)) assert response.status_code == expected_status @pytest.mark.django_db @pytest.mark.parametrize( "role,expected_status", [ (LegacyAccessControlRole.ADMIN, status.HTTP_200_OK), (LegacyAccessControlRole.EDITOR, status.HTTP_200_OK), (LegacyAccessControlRole.VIEWER, status.HTTP_200_OK), (LegacyAccessControlRole.NONE, status.HTTP_403_FORBIDDEN), ], ) def test_alert_receive_channel_detail_permissions( make_organization_and_user_with_plugin_token, make_user_auth_headers, make_alert_receive_channel, role, expected_status, ): organization, user, token = make_organization_and_user_with_plugin_token(role) alert_receive_channel = make_alert_receive_channel(organization) client = APIClient() url = reverse("api-internal:alert_receive_channel-detail", kwargs={"pk": alert_receive_channel.public_primary_key}) with patch( "apps.api.views.alert_receive_channel.AlertReceiveChannelView.retrieve", return_value=Response( status=status.HTTP_200_OK, ), ): response = client.get(url, format="json", **make_user_auth_headers(user, token)) assert response.status_code == expected_status @pytest.mark.django_db @pytest.mark.parametrize( "role,expected_status", [ (LegacyAccessControlRole.ADMIN, status.HTTP_200_OK), (LegacyAccessControlRole.EDITOR, status.HTTP_200_OK), (LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN), (LegacyAccessControlRole.NONE, status.HTTP_403_FORBIDDEN), ], ) def test_alert_receive_channel_send_demo_alert_permissions( make_organization_and_user_with_plugin_token, make_user_auth_headers, make_alert_receive_channel, role, expected_status, ): organization, user, token = make_organization_and_user_with_plugin_token(role) alert_receive_channel = make_alert_receive_channel(organization) client = APIClient() url = reverse( "api-internal:alert_receive_channel-send-demo-alert", kwargs={"pk": alert_receive_channel.public_primary_key} ) with patch( "apps.api.views.alert_receive_channel.AlertReceiveChannelView.send_demo_alert", return_value=Response( status=status.HTTP_200_OK, ), ): response = client.post(url, format="json", **make_user_auth_headers(user, token)) assert response.status_code == expected_status @pytest.mark.django_db @pytest.mark.parametrize( "role,expected_status", [ (LegacyAccessControlRole.ADMIN, status.HTTP_200_OK), (LegacyAccessControlRole.EDITOR, status.HTTP_200_OK), (LegacyAccessControlRole.VIEWER, status.HTTP_200_OK), (LegacyAccessControlRole.NONE, status.HTTP_403_FORBIDDEN), ], ) def test_alert_receive_channel_integration_options_permissions( make_organization_and_user_with_plugin_token, make_user_auth_headers, role, expected_status, ): _, user, token = make_organization_and_user_with_plugin_token(role) client = APIClient() url = reverse("api-internal:alert_receive_channel-integration-options") with patch( "apps.api.views.alert_receive_channel.AlertReceiveChannelView.integration_options", return_value=Response( status=status.HTTP_200_OK, ), ): response = client.get(url, format="json", **make_user_auth_headers(user, token)) assert response.status_code == expected_status @pytest.mark.django_db @pytest.mark.parametrize( "role,expected_status", [ (LegacyAccessControlRole.ADMIN, status.HTTP_200_OK), (LegacyAccessControlRole.EDITOR, status.HTTP_200_OK), (LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN), (LegacyAccessControlRole.NONE, status.HTTP_403_FORBIDDEN), ], ) def test_alert_receive_channel_preview_template_permissions( make_organization_and_user_with_plugin_token, make_user_auth_headers, make_alert_receive_channel, role, expected_status, ): organization, user, token = make_organization_and_user_with_plugin_token(role) alert_receive_channel = make_alert_receive_channel(organization) client = APIClient() url = reverse( "api-internal:alert_receive_channel-preview-template", kwargs={"pk": alert_receive_channel.public_primary_key} ) with patch( "apps.api.views.alert_receive_channel.AlertReceiveChannelView.preview_template", return_value=Response( status=status.HTTP_200_OK, ), ): response = client.post(url, format="json", **make_user_auth_headers(user, token)) assert response.status_code == expected_status @pytest.mark.django_db @pytest.mark.parametrize("template_name", ["title", "message", "image_url"]) @pytest.mark.parametrize("notification_channel", ["slack", "web", "telegram"]) def test_alert_receive_channel_preview_template_require_notification_channel( make_organization_and_user_with_plugin_token, make_user_auth_headers, make_alert_receive_channel, template_name, notification_channel, make_alert_group, make_alert, ): organization, user, token = make_organization_and_user_with_plugin_token() alert_receive_channel = make_alert_receive_channel(organization) alert_group = make_alert_group(alert_receive_channel) make_alert(alert_group=alert_group, raw_request_data=alert_receive_channel.config.example_payload) client = APIClient() url = reverse( "api-internal:alert_receive_channel-preview-template", kwargs={"pk": alert_receive_channel.public_primary_key} ) data = { "template_body": "Template" if template_name != "image_url" else "http://example.com/image.jpg", "template_name": template_name, } response = client.post(url, data=data, format="json", **make_user_auth_headers(user, token)) assert response.status_code == status.HTTP_400_BAD_REQUEST data = { "template_body": "Template" if template_name != "image_url" else "http://example.com/image.jpg", "template_name": f"{notification_channel}_{template_name}", } response = client.post(url, data=data, format="json", **make_user_auth_headers(user, token)) assert response.status_code == status.HTTP_200_OK @pytest.mark.django_db @pytest.mark.parametrize("template_name", ["title", "message", "image_url"]) @pytest.mark.parametrize("notification_channel", ["slack", "web", "telegram"]) def test_alert_receive_channel_preview_template_dynamic_payload( make_organization_and_user_with_plugin_token, make_user_auth_headers, make_alert_receive_channel, template_name, notification_channel, make_alert_group, make_alert, ): organization, user, token = make_organization_and_user_with_plugin_token() alert_receive_channel = make_alert_receive_channel(organization) alert_group = make_alert_group(alert_receive_channel) make_alert(alert_group=alert_group, raw_request_data=alert_receive_channel.config.example_payload) client = APIClient() url = reverse( "api-internal:alert_receive_channel-preview-template", kwargs={"pk": alert_receive_channel.public_primary_key} ) data = { "template_body": "{{ payload.foo }}", "template_name": f"{notification_channel}_{template_name}", "payload": {"foo": "bar" if template_name != "image_url" else "http://example.com/image.jpg"}, } response = client.post(url, data=data, format="json", **make_user_auth_headers(user, token)) assert response.status_code == status.HTTP_200_OK if notification_channel == "web" and template_name == "message": assert response.data["preview"] == "
bar
" else: assert response.data["preview"] == data["payload"]["foo"] @pytest.mark.django_db @pytest.mark.parametrize("template_name", ["title", "message"]) @pytest.mark.parametrize("backend_path", ["apps.mobile_app.backend.MobileAppBackend"]) def test_alert_receive_channel_preview_template_dynamic_payload_custom_backends( make_organization_and_user_with_plugin_token, make_user_auth_headers, make_alert_receive_channel, template_name, backend_path, make_alert_group, make_alert, ): organization, user, token = make_organization_and_user_with_plugin_token() alert_receive_channel = make_alert_receive_channel(organization) alert_group = make_alert_group(alert_receive_channel) make_alert(alert_group=alert_group, raw_request_data=alert_receive_channel.config.example_payload) client = APIClient() url = reverse( "api-internal:alert_receive_channel-preview-template", kwargs={"pk": alert_receive_channel.public_primary_key} ) # load backend backend = load_backend(backend_path, notification_channel_id=111) notification_channel = backend.backend_id.lower() data = { "template_body": "{{ payload.foo }}", "template_name": f"{notification_channel}_{template_name}", "payload": {"foo": "bar" if template_name != "image_url" else "http://example.com/image.jpg"}, } with patch( "apps.alerts.incident_appearance.templaters.alert_templater.get_messaging_backend_from_id" ) as mock_get_backend: mock_get_backend.return_value = backend from common.api_helpers import mixins with patch.object(mixins, "NOTIFICATION_CHANNEL_OPTIONS", new=(notification_channel,)): with patch.dict( mixins.NOTIFICATION_CHANNEL_TO_TEMPLATER_MAP, {notification_channel: backend.get_templater_class()} ): response = client.post(url, data=data, format="json", **make_user_auth_headers(user, token)) assert response.status_code == status.HTTP_200_OK assert response.data["preview"] == data["payload"]["foo"] @pytest.mark.django_db @pytest.mark.parametrize( "role,expected_status", [ (LegacyAccessControlRole.ADMIN, status.HTTP_200_OK), (LegacyAccessControlRole.EDITOR, status.HTTP_403_FORBIDDEN), (LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN), (LegacyAccessControlRole.NONE, status.HTTP_403_FORBIDDEN), ], ) def test_alert_receive_channel_change_team_permissions( make_organization_and_user_with_plugin_token, make_user_auth_headers, make_alert_receive_channel, role, expected_status, ): organization, user, token = make_organization_and_user_with_plugin_token(role) alert_receive_channel = make_alert_receive_channel(organization) client = APIClient() url = reverse( "api-internal:alert_receive_channel-change-team", kwargs={"pk": alert_receive_channel.public_primary_key} ) with patch( "apps.api.views.alert_receive_channel.AlertReceiveChannelView.change_team", return_value=Response( status=status.HTTP_200_OK, ), ): response = client.put(url, format="json", **make_user_auth_headers(user, token)) assert response.status_code == expected_status @pytest.mark.django_db def test_alert_receive_channel_change_team( make_organization_and_user_with_plugin_token, make_team, make_user_for_organization, make_user_auth_headers, make_alert_receive_channel, make_channel_filter, make_integration_escalation_chain_route_escalation_policy, ): organization, user, token = make_organization_and_user_with_plugin_token() team = make_team(organization) integration, escalation_chain, _, escalation_policy = make_integration_escalation_chain_route_escalation_policy( organization, EscalationPolicy.STEP_NOTIFY_MULTIPLE_USERS ) client = APIClient() url = reverse("api-internal:alert_receive_channel-change-team", kwargs={"pk": integration.public_primary_key}) assert integration.team != team # return 200 on change team for integration as user is Admin response = client.put( f"{url}?team_id={team.public_primary_key}", format="json", **make_user_auth_headers(user, token) ) assert response.status_code == status.HTTP_200_OK integration.refresh_from_db() assert integration.team == team @pytest.mark.django_db @pytest.mark.parametrize( "role,expected_status", [ (LegacyAccessControlRole.ADMIN, status.HTTP_200_OK), (LegacyAccessControlRole.EDITOR, status.HTTP_200_OK), (LegacyAccessControlRole.VIEWER, status.HTTP_200_OK), (LegacyAccessControlRole.NONE, status.HTTP_403_FORBIDDEN), ], ) def test_alert_receive_channel_counters_permissions( make_organization_and_user_with_plugin_token, make_user_auth_headers, role, expected_status, ): _, user, token = make_organization_and_user_with_plugin_token(role) client = APIClient() url = reverse( "api-internal:alert_receive_channel-counters", ) with patch( "apps.api.views.alert_receive_channel.AlertReceiveChannelView.counters", return_value=Response( status=status.HTTP_200_OK, ), ): response = client.get(url, format="json", **make_user_auth_headers(user, token)) assert response.status_code == expected_status @pytest.mark.django_db @pytest.mark.parametrize( "role,expected_status", [ (LegacyAccessControlRole.ADMIN, status.HTTP_200_OK), (LegacyAccessControlRole.EDITOR, status.HTTP_200_OK), (LegacyAccessControlRole.VIEWER, status.HTTP_200_OK), (LegacyAccessControlRole.NONE, status.HTTP_403_FORBIDDEN), ], ) def test_alert_receive_channel_counters_per_integration_permissions( make_organization_and_user_with_plugin_token, make_user_auth_headers, make_alert_receive_channel, role, expected_status, ): organization, user, token = make_organization_and_user_with_plugin_token(role) client = APIClient() alert_receive_channel = make_alert_receive_channel(organization) url = reverse( "api-internal:alert_receive_channel-counters-per-integration", kwargs={"pk": alert_receive_channel.public_primary_key}, ) with patch( "apps.api.views.alert_receive_channel.AlertReceiveChannelView.counters_per_integration", return_value=Response( status=status.HTTP_200_OK, ), ): response = client.get(url, format="json", **make_user_auth_headers(user, token)) assert response.status_code == expected_status @pytest.mark.django_db def test_get_alert_receive_channels_direct_paging_present_for_filters( make_organization_and_user_with_plugin_token, make_alert_receive_channel, make_user_auth_headers ): organization, user, token = make_organization_and_user_with_plugin_token() alert_receive_channel = make_alert_receive_channel( user.organization, integration=AlertReceiveChannel.INTEGRATION_DIRECT_PAGING ) client = APIClient() url = reverse("api-internal:alert_receive_channel-list") response = client.get(url + "?filters=true", format="json", **make_user_auth_headers(user, token)) # Check direct paging integration is in the response assert response.status_code == status.HTTP_200_OK assert response.json()["results"][0]["value"] == alert_receive_channel.public_primary_key @pytest.mark.django_db def test_cant_create_alert_receive_channels_direct_paging( make_organization_and_user_with_plugin_token, make_team, make_alert_receive_channel, make_user_auth_headers ): organization, user, token = make_organization_and_user_with_plugin_token() client = APIClient() url = reverse("api-internal:alert_receive_channel-list") response = client.post( url, data={"integration": "direct_paging"}, format="json", **make_user_auth_headers(user, token) ) assert response.status_code == status.HTTP_400_BAD_REQUEST @pytest.mark.django_db def test_update_alert_receive_channels_direct_paging( make_organization_and_user_with_plugin_token, make_team, make_alert_receive_channel, make_user_auth_headers ): organization, user, token = make_organization_and_user_with_plugin_token() team = make_team(organization) integration = make_alert_receive_channel( organization, integration=AlertReceiveChannel.INTEGRATION_DIRECT_PAGING, team=None ) make_alert_receive_channel(organization, integration=AlertReceiveChannel.INTEGRATION_DIRECT_PAGING, team=team) client = APIClient() url = reverse("api-internal:alert_receive_channel-detail", kwargs={"pk": integration.public_primary_key}) # Move direct paging integration from "No team" to team response = client.put( url, data={"integration": "direct_paging", "team": team.public_primary_key}, format="json", **make_user_auth_headers(user, token), ) assert response.status_code == status.HTTP_400_BAD_REQUEST assert response.json()["detail"] == AlertReceiveChannel.DuplicateDirectPagingError.DETAIL @pytest.mark.django_db def test_cant_delete_direct_paging_integration( make_organization_and_user_with_plugin_token, make_alert_receive_channel, make_user_auth_headers ): organization, user, token = make_organization_and_user_with_plugin_token() integration = make_alert_receive_channel(organization, integration=AlertReceiveChannel.INTEGRATION_DIRECT_PAGING) # check allow_delete is False (so the frontend can hide the delete button) client = APIClient() url = reverse("api-internal:alert_receive_channel-detail", kwargs={"pk": integration.public_primary_key}) response = client.get(url, **make_user_auth_headers(user, token)) assert response.status_code == status.HTTP_200_OK assert response.json()["allow_delete"] is False # check delete is not allowed client = APIClient() url = reverse("api-internal:alert_receive_channel-detail", kwargs={"pk": integration.public_primary_key}) response = client.delete(url, **make_user_auth_headers(user, token)) assert response.status_code == status.HTTP_400_BAD_REQUEST @pytest.mark.django_db def test_start_maintenance_integration( make_user_auth_headers, make_organization_and_user_with_plugin_token, make_escalation_chain, make_alert_receive_channel, ): organization, user, token = make_organization_and_user_with_plugin_token() make_escalation_chain(organization) alert_receive_channel = make_alert_receive_channel(organization) client = APIClient() url = reverse( "api-internal:alert_receive_channel-start-maintenance", kwargs={"pk": alert_receive_channel.public_primary_key} ) data = { "mode": AlertReceiveChannel.MAINTENANCE, "duration": AlertReceiveChannel.DURATION_ONE_HOUR.total_seconds(), "type": "alert_receive_channel", } response = client.post(url, data=data, format="json", **make_user_auth_headers(user, token)) alert_receive_channel.refresh_from_db() assert response.status_code == status.HTTP_200_OK assert alert_receive_channel.maintenance_mode == AlertReceiveChannel.MAINTENANCE assert alert_receive_channel.maintenance_duration == AlertReceiveChannel.DURATION_ONE_HOUR assert alert_receive_channel.maintenance_uuid is not None assert alert_receive_channel.maintenance_started_at is not None assert alert_receive_channel.maintenance_author is not None @pytest.mark.django_db def test_stop_maintenance_integration( make_user_auth_headers, make_organization_and_user_with_plugin_token, make_escalation_chain, make_alert_receive_channel, ): organization, user, token = make_organization_and_user_with_plugin_token() make_escalation_chain(organization) alert_receive_channel = make_alert_receive_channel(organization) client = APIClient() mode = AlertReceiveChannel.MAINTENANCE duration = AlertReceiveChannel.DURATION_ONE_HOUR.seconds alert_receive_channel.start_maintenance(mode, duration, user) url = reverse( "api-internal:alert_receive_channel-stop-maintenance", kwargs={"pk": alert_receive_channel.public_primary_key} ) data = { "type": "alert_receive_channel", } response = client.post(url, data=data, format="json", **make_user_auth_headers(user, token)) alert_receive_channel.refresh_from_db() assert response.status_code == status.HTTP_200_OK assert alert_receive_channel.maintenance_mode is None assert alert_receive_channel.maintenance_duration is None assert alert_receive_channel.maintenance_uuid is None assert alert_receive_channel.maintenance_started_at is None assert alert_receive_channel.maintenance_author is None @pytest.mark.django_db def test_alert_receive_channel_send_demo_alert( make_organization_and_user_with_plugin_token, make_user_auth_headers, make_alert_receive_channel, ): organization, user, token = make_organization_and_user_with_plugin_token() alert_receive_channel = make_alert_receive_channel( organization, integration=AlertReceiveChannel.INTEGRATION_GRAFANA ) client = APIClient() url = reverse( "api-internal:alert_receive_channel-send-demo-alert", kwargs={"pk": alert_receive_channel.public_primary_key}, ) response = client.post(url, format="json", **make_user_auth_headers(user, token)) assert response.status_code == status.HTTP_200_OK @pytest.mark.django_db def test_alert_receive_channel_send_demo_alert_not_enabled( make_organization_and_user_with_plugin_token, make_user_auth_headers, make_alert_receive_channel, ): organization, user, token = make_organization_and_user_with_plugin_token() alert_receive_channel = make_alert_receive_channel(organization, integration=AlertReceiveChannel.INTEGRATION_MANUAL) client = APIClient() url = reverse( "api-internal:alert_receive_channel-send-demo-alert", kwargs={"pk": alert_receive_channel.public_primary_key}, ) response = client.post(url, format="json", **make_user_auth_headers(user, token)) assert response.status_code == status.HTTP_400_BAD_REQUEST @pytest.mark.django_db @pytest.mark.parametrize( "role,expected_status", [ (LegacyAccessControlRole.ADMIN, status.HTTP_200_OK), (LegacyAccessControlRole.EDITOR, status.HTTP_200_OK), (LegacyAccessControlRole.VIEWER, status.HTTP_200_OK), (LegacyAccessControlRole.NONE, status.HTTP_403_FORBIDDEN), ], ) def test_alert_receive_channel_get_connected_contact_points_permissions( make_organization_and_user_with_plugin_token, make_user_auth_headers, make_alert_receive_channel, role, expected_status, ): organization, user, token = make_organization_and_user_with_plugin_token(role) client = APIClient() alert_receive_channel = make_alert_receive_channel( organization, integration=AlertReceiveChannel.INTEGRATION_GRAFANA_ALERTING ) url = reverse( "api-internal:alert_receive_channel-connected-contact-points", kwargs={"pk": alert_receive_channel.public_primary_key}, ) with patch( "apps.api.views.alert_receive_channel.AlertReceiveChannelView.connected_contact_points", return_value=Response( status=status.HTTP_200_OK, ), ): response = client.get(url, format="json", **make_user_auth_headers(user, token)) assert response.status_code == expected_status @pytest.mark.django_db @pytest.mark.parametrize( "role,expected_status", [ (LegacyAccessControlRole.ADMIN, status.HTTP_200_OK), (LegacyAccessControlRole.EDITOR, status.HTTP_200_OK), (LegacyAccessControlRole.VIEWER, status.HTTP_200_OK), (LegacyAccessControlRole.NONE, status.HTTP_403_FORBIDDEN), ], ) def test_alert_receive_channel_get_contact_points_permissions( make_organization_and_user_with_plugin_token, make_user_auth_headers, make_alert_receive_channel, role, expected_status, ): organization, user, token = make_organization_and_user_with_plugin_token(role) client = APIClient() url = reverse( "api-internal:alert_receive_channel-contact-points", ) with patch( "apps.api.views.alert_receive_channel.AlertReceiveChannelView.contact_points", return_value=Response( status=status.HTTP_200_OK, ), ): response = client.get(url, format="json", **make_user_auth_headers(user, token)) assert response.status_code == expected_status @pytest.mark.django_db @pytest.mark.parametrize( "role,expected_status", [ (LegacyAccessControlRole.ADMIN, status.HTTP_200_OK), (LegacyAccessControlRole.EDITOR, status.HTTP_403_FORBIDDEN), (LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN), (LegacyAccessControlRole.NONE, status.HTTP_403_FORBIDDEN), ], ) def test_alert_receive_channel_connect_contact_point_permissions( make_organization_and_user_with_plugin_token, make_user_auth_headers, make_alert_receive_channel, role, expected_status, ): organization, user, token = make_organization_and_user_with_plugin_token(role) client = APIClient() alert_receive_channel = make_alert_receive_channel( organization, integration=AlertReceiveChannel.INTEGRATION_GRAFANA_ALERTING ) url = reverse( "api-internal:alert_receive_channel-connect-contact-point", kwargs={"pk": alert_receive_channel.public_primary_key}, ) with patch( "apps.api.views.alert_receive_channel.AlertReceiveChannelView.connect_contact_point", return_value=Response( status=status.HTTP_200_OK, ), ): response = client.post(url, format="json", data={}, **make_user_auth_headers(user, token)) assert response.status_code == expected_status @pytest.mark.django_db @pytest.mark.parametrize( "role,expected_status", [ (LegacyAccessControlRole.ADMIN, status.HTTP_201_CREATED), (LegacyAccessControlRole.EDITOR, status.HTTP_403_FORBIDDEN), (LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN), (LegacyAccessControlRole.NONE, status.HTTP_403_FORBIDDEN), ], ) def test_alert_receive_channel_create_contact_point_permissions( make_organization_and_user_with_plugin_token, make_user_auth_headers, make_alert_receive_channel, role, expected_status, ): organization, user, token = make_organization_and_user_with_plugin_token(role) client = APIClient() alert_receive_channel = make_alert_receive_channel( organization, integration=AlertReceiveChannel.INTEGRATION_GRAFANA_ALERTING ) url = reverse( "api-internal:alert_receive_channel-create-contact-point", kwargs={"pk": alert_receive_channel.public_primary_key}, ) with patch( "apps.api.views.alert_receive_channel.AlertReceiveChannelView.create_contact_point", return_value=Response( status=status.HTTP_201_CREATED, ), ): response = client.post(url, format="json", data={}, **make_user_auth_headers(user, token)) assert response.status_code == expected_status @pytest.mark.django_db @pytest.mark.parametrize( "role,expected_status", [ (LegacyAccessControlRole.ADMIN, status.HTTP_200_OK), (LegacyAccessControlRole.EDITOR, status.HTTP_403_FORBIDDEN), (LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN), (LegacyAccessControlRole.NONE, status.HTTP_403_FORBIDDEN), ], ) def test_alert_receive_channel_disconnect_contact_point_permissions( make_organization_and_user_with_plugin_token, make_user_auth_headers, make_alert_receive_channel, role, expected_status, ): organization, user, token = make_organization_and_user_with_plugin_token(role) client = APIClient() alert_receive_channel = make_alert_receive_channel( organization, integration=AlertReceiveChannel.INTEGRATION_GRAFANA_ALERTING ) url = reverse( "api-internal:alert_receive_channel-disconnect-contact-point", kwargs={"pk": alert_receive_channel.public_primary_key}, ) with patch( "apps.api.views.alert_receive_channel.AlertReceiveChannelView.disconnect_contact_point", return_value=Response( status=status.HTTP_200_OK, ), ): response = client.post(url, format="json", data={}, **make_user_auth_headers(user, token)) assert response.status_code == expected_status @patch( "apps.alerts.grafana_alerting_sync_manager.GrafanaAlertingSyncManager.get_connected_contact_points", return_value=True, ) @patch( "apps.alerts.grafana_alerting_sync_manager.GrafanaAlertingSyncManager.connect_contact_point", return_value=(True, ""), ) @patch( "apps.alerts.grafana_alerting_sync_manager.GrafanaAlertingSyncManager.disconnect_contact_point", return_value=(True, ""), ) @pytest.mark.django_db @pytest.mark.parametrize( "endpoint,expected_status", [ ("connected-contact-points", status.HTTP_200_OK), ("connect-contact-point", status.HTTP_200_OK), ("create-contact-point", status.HTTP_201_CREATED), ("disconnect-contact-point", status.HTTP_200_OK), ], ) def test_alert_receive_channel_contact_points_endpoints( mocked_get_connected_contact_points, mocked_get_connect_contact_point, mocked_get_disconnect_contact_point, make_organization_and_user_with_plugin_token, make_user_auth_headers, make_alert_receive_channel, endpoint, expected_status, ): organization, user, token = make_organization_and_user_with_plugin_token() client = APIClient() alert_receive_channel = make_alert_receive_channel( organization, integration=AlertReceiveChannel.INTEGRATION_GRAFANA_ALERTING ) url = reverse( f"api-internal:alert_receive_channel-{endpoint}", kwargs={"pk": alert_receive_channel.public_primary_key}, ) if endpoint == "connected-contact-points": response = client.get(url, format="json", **make_user_auth_headers(user, token)) else: data = { "datasource_uid": "test_datasource", "contact_point_name": "test contact point", } response = client.post(url, format="json", data=data, **make_user_auth_headers(user, token)) assert response.status_code == expected_status @patch("apps.alerts.grafana_alerting_sync_manager.GrafanaAlertingSyncManager.get_contact_points", return_value=[]) @pytest.mark.django_db def test_alert_receive_channel_get_contact_points( mocked_get_contact_points, make_organization_and_user_with_plugin_token, make_user_auth_headers, make_alert_receive_channel, ): organization, user, token = make_organization_and_user_with_plugin_token() client = APIClient() url = reverse("api-internal:alert_receive_channel-contact-points") response = client.get(url, format="json", **make_user_auth_headers(user, token)) assert response.status_code == status.HTTP_200_OK @pytest.mark.django_db @pytest.mark.parametrize( "endpoint", ["connected-contact-points", "connect-contact-point", "create-contact-point", "disconnect-contact-point"], ) def test_alert_receive_channel_contact_points_wrong_integration( make_organization_and_user_with_plugin_token, make_user_auth_headers, make_alert_receive_channel, endpoint, ): organization, user, token = make_organization_and_user_with_plugin_token() client = APIClient() alert_receive_channel = make_alert_receive_channel( organization, integration=AlertReceiveChannel.INTEGRATION_GRAFANA ) url = reverse( f"api-internal:alert_receive_channel-{endpoint}", kwargs={"pk": alert_receive_channel.public_primary_key}, ) if endpoint == "connected-contact-points": response = client.get(url, format="json", **make_user_auth_headers(user, token)) else: response = client.post(url, format="json", data={}, **make_user_auth_headers(user, token)) assert response.status_code == status.HTTP_400_BAD_REQUEST @pytest.mark.django_db def test_integration_filter_by_labels( make_organization_and_user_with_plugin_token, make_alert_receive_channel, make_static_label_config, make_user_auth_headers, ): organization, user, token = make_organization_and_user_with_plugin_token() alert_receive_channel_1 = make_alert_receive_channel(organization) alert_receive_channel_2 = make_alert_receive_channel(organization) associated_label_1 = make_static_label_config(organization, alert_receive_channel_1) associated_label_2 = make_static_label_config(organization, alert_receive_channel_1) alert_receive_channel_2.labels.create( key=associated_label_1.key, value=associated_label_1.value, organization=organization ) client = APIClient() url = reverse("api-internal:alert_receive_channel-list") response = client.get( f"{url}?label={associated_label_1.key_id}:{associated_label_1.value_id}", content_type="application/json", **make_user_auth_headers(user, token), ) assert response.status_code == status.HTTP_200_OK assert len(response.json()["results"]) == 2 response = client.get( f"{url}?label={associated_label_1.key_id}:{associated_label_1.value_id}" f"&label={associated_label_2.key_id}:{associated_label_2.value_id}", content_type="application/json", **make_user_auth_headers(user, token), ) assert response.status_code == status.HTTP_200_OK assert len(response.json()["results"]) == 1 assert response.json()["results"][0]["id"] == alert_receive_channel_1.public_primary_key @pytest.mark.django_db def test_update_alert_receive_channel_labels( make_organization_and_user_with_plugin_token, make_alert_receive_channel, make_user_auth_headers, ): organization, user, token = make_organization_and_user_with_plugin_token() alert_receive_channel = make_alert_receive_channel(organization) client = APIClient() url = reverse("api-internal:alert_receive_channel-detail", kwargs={"pk": alert_receive_channel.public_primary_key}) key_id = "testkey" value_id = "testvalue" data = { "labels": [ { "key": {"id": key_id, "name": "test", "prescribed": False}, "value": {"id": value_id, "name": "testv", "prescribed": False}, } ] } response = client.patch( url, data=json.dumps(data), content_type="application/json", **make_user_auth_headers(user, token), ) alert_receive_channel.refresh_from_db() assert response.status_code == status.HTTP_200_OK assert alert_receive_channel.labels.count() == 1 label = alert_receive_channel.labels.first() assert label.key_id == key_id assert label.value_id == value_id response = client.patch( url, data=json.dumps({"labels": []}), content_type="application/json", **make_user_auth_headers(user, token), ) alert_receive_channel.refresh_from_db() assert response.status_code == status.HTTP_200_OK assert alert_receive_channel.labels.count() == 0 @pytest.mark.django_db def test_update_alert_receive_channel_presribed_labels( make_organization_and_user_with_plugin_token, make_alert_receive_channel, make_user_auth_headers, ): organization, user, token = make_organization_and_user_with_plugin_token() alert_receive_channel = make_alert_receive_channel(organization) client = APIClient() url = reverse("api-internal:alert_receive_channel-detail", kwargs={"pk": alert_receive_channel.public_primary_key}) key_id = "testkey" value_id = "testvalue" data = { "labels": [ { "key": {"id": key_id, "name": "test", "prescribed": True}, "value": {"id": value_id, "name": "testv", "prescribed": True}, } ] } response = client.patch( url, data=json.dumps(data), content_type="application/json", **make_user_auth_headers(user, token), ) alert_receive_channel.refresh_from_db() assert response.status_code == status.HTTP_200_OK assert alert_receive_channel.labels.count() == 1 label = alert_receive_channel.labels.first() assert label.key_id == key_id assert label.value_id == value_id # Check if cached labels are prescribed assert label.key.prescribed is True assert label.value.prescribed is True response = client.patch( url, data=json.dumps({"labels": []}), content_type="application/json", **make_user_auth_headers(user, token), ) alert_receive_channel.refresh_from_db() assert response.status_code == status.HTTP_200_OK assert alert_receive_channel.labels.count() == 0 @pytest.mark.django_db def test_update_alert_receive_channel_labels_duplicate_key( make_organization_and_user_with_plugin_token, make_alert_receive_channel, make_user_auth_headers, ): organization, user, token = make_organization_and_user_with_plugin_token() alert_receive_channel = make_alert_receive_channel(organization) client = APIClient() url = reverse("api-internal:alert_receive_channel-detail", kwargs={"pk": alert_receive_channel.public_primary_key}) key_id = "testkey" data = { "labels": [ {"key": {"id": key_id, "name": "test"}, "value": {"id": "testvalue1", "name": "testv1"}}, {"key": {"id": key_id, "name": "test"}, "value": {"id": "testvalue2", "name": "testv2"}}, ] } response = client.patch( url, data=json.dumps(data), content_type="application/json", **make_user_auth_headers(user, token), ) alert_receive_channel.refresh_from_db() assert response.status_code == status.HTTP_400_BAD_REQUEST assert alert_receive_channel.labels.count() == 0 @pytest.mark.django_db def test_alert_group_labels_get( make_organization_and_user_with_plugin_token, make_alert_receive_channel, make_label_key_and_value, make_static_label_config, make_user_auth_headers, ): organization, user, token = make_organization_and_user_with_plugin_token() alert_receive_channel = make_alert_receive_channel(organization) label_key, label_value = make_label_key_and_value(organization) label_key_1, _ = make_label_key_and_value(organization) client = APIClient() url = reverse("api-internal:alert_receive_channel-detail", kwargs={"pk": alert_receive_channel.public_primary_key}) response = client.get(url, **make_user_auth_headers(user, token)) assert response.status_code == status.HTTP_200_OK assert response.json()["alert_group_labels"] == {"inheritable": {}, "custom": [], "template": None} label = make_static_label_config(organization, alert_receive_channel) template = "{{ payload.labels | tojson }}" alert_receive_channel.alert_group_labels_template = template alert_receive_channel.alert_group_labels_custom = [ (label_key.id, label_value.id, None), (label_key_1.id, None, "{{ payload.foo }}"), ] alert_receive_channel.save(update_fields=["alert_group_labels_custom", "alert_group_labels_template"]) response = client.get(url, **make_user_auth_headers(user, token)) assert response.status_code == status.HTTP_200_OK assert response.json()["alert_group_labels"] == { "inheritable": {label.key_id: True}, "custom": [ { "key": {"id": label_key.id, "name": label_key.name, "prescribed": False}, "value": {"id": label_value.id, "name": label_value.name, "prescribed": False}, }, { "key": {"id": label_key_1.id, "name": label_key_1.name, "prescribed": False}, "value": {"id": None, "name": "{{ payload.foo }}", "prescribed": False}, }, ], "template": template, } @pytest.mark.django_db def test_alert_group_labels_put( make_organization_and_user_with_plugin_token, make_alert_receive_channel, make_static_label_config, make_user_auth_headers, ): organization, user, token = make_organization_and_user_with_plugin_token() alert_receive_channel = make_alert_receive_channel(organization) label_1 = make_static_label_config(organization, alert_receive_channel) label_2 = make_static_label_config(organization, alert_receive_channel) label_3 = make_static_label_config(organization, alert_receive_channel) custom = [ # static label (deprecated, will be skipped) { "key": {"id": label_2.key.id, "name": label_2.key.name, "prescribed": False}, "value": {"id": label_2.value.id, "name": label_2.value.name, "prescribed": False}, }, # dynamic label { "key": {"id": label_3.key.id, "name": label_3.key.name, "prescribed": False}, "value": {"id": None, "name": "{{ payload.foo }}", "prescribed": False}, }, # dynamic label not present in DB cache { "key": {"id": "hello", "name": "world", "prescribed": False}, "value": {"id": None, "name": "{{ payload.bar }}", "prescribed": False}, }, ] template = "{{ payload.labels | tojson }}" # advanced template client = APIClient() url = reverse("api-internal:alert_receive_channel-detail", kwargs={"pk": alert_receive_channel.public_primary_key}) data = { "alert_group_labels": { "inheritable": {label_1.key_id: False, label_2.key_id: True, label_3.key_id: False}, "custom": custom, "template": template, } } response = client.put(url, data, format="json", **make_user_auth_headers(user, token)) assert response.status_code == status.HTTP_200_OK assert response.json()["alert_group_labels"] == { "inheritable": {label_1.key_id: True, label_2.key_id: True, label_3.key_id: True}, "custom": [ { "key": {"id": label_3.key.id, "name": label_3.key.name, "prescribed": False}, "value": {"id": None, "name": "{{ payload.foo }}", "prescribed": False}, }, { "key": {"id": "hello", "name": "world", "prescribed": False}, "value": {"id": None, "name": "{{ payload.bar }}", "prescribed": False}, }, ], "template": template, } alert_receive_channel.refresh_from_db() # check deprecated static label is not in the custom labels list assert alert_receive_channel.alert_group_labels_custom == [ [label_3.key_id, None, "{{ payload.foo }}"], ["hello", None, "{{ payload.bar }}"], ] assert alert_receive_channel.alert_group_labels_template == template # check label key is created assert LabelKeyCache.objects.filter(id="hello", name="world", organization=organization).exists() @pytest.mark.django_db def test_alert_group_labels_put_none( make_organization_and_user_with_plugin_token, make_alert_receive_channel, make_user_auth_headers, ): organization, user, token = make_organization_and_user_with_plugin_token() alert_receive_channel = make_alert_receive_channel(organization) client = APIClient() url = reverse("api-internal:alert_receive_channel-detail", kwargs={"pk": alert_receive_channel.public_primary_key}) response = client.put(url, {"verbal_name": "123"}, format="json", **make_user_auth_headers(user, token)) assert response.status_code == status.HTTP_200_OK assert response.json()["verbal_name"] == "123" assert response.json()["alert_group_labels"] == {"inheritable": {}, "custom": [], "template": None} @pytest.mark.django_db def test_alert_group_labels_post(alert_receive_channel_internal_api_setup, make_user_auth_headers): user, token, _ = alert_receive_channel_internal_api_setup labels = [ { "key": {"id": "test", "name": "test", "prescribed": False}, "value": {"id": "123", "name": "123", "prescribed": False}, } ] alert_group_labels = { "inheritable": {"test": False}, "custom": [ { "key": {"id": "test", "name": "test", "prescribed": False}, "value": {"id": "123", "name": "123", "prescribed": False}, }, { "key": {"id": "test2", "name": "test2", "prescribed": False}, "value": {"id": None, "name": "{{ payload.foo }}", "prescribed": False}, }, ], "template": "{{ payload.labels | tojson }}", } expected_alert_group_labels = { "inheritable": {"test": True}, "custom": [ { "key": {"id": "test2", "name": "test2", "prescribed": False}, "value": {"id": None, "name": "{{ payload.foo }}", "prescribed": False}, } ], "template": "{{ payload.labels | tojson }}", } data = { "integration": AlertReceiveChannel.INTEGRATION_GRAFANA, "team": None, "labels": labels, "alert_group_labels": alert_group_labels, } client = APIClient() url = reverse("api-internal:alert_receive_channel-list") response = client.post(url, data, format="json", **make_user_auth_headers(user, token)) assert response.status_code == status.HTTP_201_CREATED assert response.json()["labels"] == labels assert response.json()["alert_group_labels"] == expected_alert_group_labels alert_receive_channel = AlertReceiveChannel.objects.get(public_primary_key=response.json()["id"]) assert alert_receive_channel.alert_group_labels_custom == [["test2", None, "{{ payload.foo }}"]] assert alert_receive_channel.alert_group_labels_template == "{{ payload.labels | tojson }}" @patch.object(GrafanaAlertingSyncManager, "check_for_connection_errors", return_value=None) @pytest.mark.django_db def test_create_service_name_label_for_new_alerting_integration( _, make_organization_and_user_with_plugin_token, make_label_key, make_user_auth_headers, ): """Test adding default `service_name` dynamic label for new alerting integration.""" organization, user, token = make_organization_and_user_with_plugin_token() service_name_label_key = make_label_key( organization=organization, key_id="test", key_name=SERVICE_LABEL, prescribed=True ) client = APIClient() url = reverse("api-internal:alert_receive_channel-list") data = { "integration": AlertReceiveChannel.INTEGRATION_GRAFANA_ALERTING, "team": None, "labels": [], "alert_group_labels": { "inheritable": {}, "custom": [ { "key": {"id": "testid", "name": "testname", "prescribed": False}, "value": {"id": None, "name": "{{ payload.foo }}", "prescribed": False}, } ], "template": None, }, } expected_alert_group_labels_response = { "inheritable": {}, "custom": [ { "key": {"id": service_name_label_key.id, "name": SERVICE_LABEL, "prescribed": True}, "value": {"id": None, "name": SERVICE_LABEL_TEMPLATE_FOR_ALERTING_INTEGRATION, "prescribed": False}, }, { "key": {"id": "testid", "name": "testname", "prescribed": False}, "value": {"id": None, "name": "{{ payload.foo }}", "prescribed": False}, }, ], "template": None, } expected_alert_group_labels = [ [service_name_label_key.id, None, SERVICE_LABEL_TEMPLATE_FOR_ALERTING_INTEGRATION], ["testid", None, "{{ payload.foo }}"], ] response = client.post(url, data, format="json", **make_user_auth_headers(user, token)) assert response.status_code == status.HTTP_201_CREATED assert response.json()["alert_group_labels"] == expected_alert_group_labels_response alert_receive_channel = organization.alert_receive_channels.filter(public_primary_key=response.json()["id"]).first() assert alert_receive_channel is not None assert alert_receive_channel.alert_group_labels_custom == expected_alert_group_labels @patch.object(GrafanaAlertingSyncManager, "check_for_connection_errors", return_value=None) @pytest.mark.django_db def test_skip_creating_service_name_label_for_new_alerting_integration( _, make_organization_and_user_with_plugin_token, make_label_key, make_user_auth_headers, ): """ Test skipping adding default `service_name` dynamic label for new alerting integration, when this label was already added by user """ organization, user, token = make_organization_and_user_with_plugin_token() service_name_label_key = make_label_key( organization=organization, key_id="test", key_name=SERVICE_LABEL, prescribed=True ) client = APIClient() url = reverse("api-internal:alert_receive_channel-list") data = { "integration": AlertReceiveChannel.INTEGRATION_GRAFANA_ALERTING, "team": None, "labels": [], "alert_group_labels": { "inheritable": {}, "custom": [ { "key": {"id": service_name_label_key.id, "name": SERVICE_LABEL, "prescribed": True}, "value": {"id": None, "name": "{{ payload.foo }}", "prescribed": False}, } ], "template": None, }, } expected_alert_group_labels_response = { "inheritable": {}, "custom": [ { "key": {"id": service_name_label_key.id, "name": SERVICE_LABEL, "prescribed": True}, "value": {"id": None, "name": "{{ payload.foo }}", "prescribed": False}, } ], "template": None, } expected_alert_group_labels = [ [service_name_label_key.id, None, "{{ payload.foo }}"], ] response = client.post(url, data, format="json", **make_user_auth_headers(user, token)) assert response.status_code == status.HTTP_201_CREATED assert response.json()["alert_group_labels"] == expected_alert_group_labels_response alert_receive_channel = organization.alert_receive_channels.filter(public_primary_key=response.json()["id"]).first() assert alert_receive_channel is not None assert alert_receive_channel.alert_group_labels_custom == expected_alert_group_labels @pytest.mark.django_db def test_team_not_updated_if_not_in_data( make_organization_and_user_with_plugin_token, make_alert_receive_channel, make_team, make_user_auth_headers, ): organization, user, token = make_organization_and_user_with_plugin_token() team = make_team(organization) alert_receive_channel = make_alert_receive_channel(organization, team=team) assert alert_receive_channel.team == team client = APIClient() url = reverse("api-internal:alert_receive_channel-detail", kwargs={"pk": alert_receive_channel.public_primary_key}) data = {"verbal_name": "test integration"} response = client.put(url, data, format="json", **make_user_auth_headers(user, token)) assert response.status_code == status.HTTP_200_OK assert response.json()["team"] == alert_receive_channel.team.public_primary_key alert_receive_channel.refresh_from_db() assert alert_receive_channel.team == team @pytest.mark.django_db def test_create_additional_settings_integration( setup_additional_settings_for_integration, make_organization_and_user_with_plugin_token, make_user_auth_headers, ): _, user, token = make_organization_and_user_with_plugin_token() client = APIClient() # set up additional settings for an integration integration_config = setup_additional_settings_for_integration url = reverse("api-internal:alert_receive_channel-list") # create without additional_settings data = { "integration": integration_config.slug, "team": None, } response = client.post(url, data, format="json", **make_user_auth_headers(user, token)) assert response.status_code == status.HTTP_400_BAD_REQUEST # create with empty additional_settings data = {"integration": integration_config.slug, "team": None, "additional_settings": {}} response = client.post(url, data, format="json", **make_user_auth_headers(user, token)) assert response.status_code == status.HTTP_400_BAD_REQUEST assert "additional_settings" in response.json() # create with wrong additional_settings data = { "integration": integration_config.slug, "team": None, "additional_settings": {"test": "test"}, } response = client.post(url, data, format="json", **make_user_auth_headers(user, token)) assert response.status_code == status.HTTP_400_BAD_REQUEST # create with correct additional_settings data = { "integration": integration_config.slug, "team": None, "additional_settings": {"instance_url": "test"}, } response = client.post(url, data, format="json", **make_user_auth_headers(user, token)) assert response.status_code == status.HTTP_201_CREATED @pytest.mark.django_db def test_update_additional_settings_integration( setup_additional_settings_for_integration, make_organization_and_user_with_plugin_token, make_alert_receive_channel, make_user_auth_headers, ): organization, user, token = make_organization_and_user_with_plugin_token() client = APIClient() settings = {"instance_url": "test"} # set up additional settings for an integration integration_config = setup_additional_settings_for_integration integration_config.update_default_webhooks = Mock() alert_receive_channel = make_alert_receive_channel( organization, integration=integration_config.slug, additional_settings=settings ) url = reverse("api-internal:alert_receive_channel-detail", kwargs={"pk": alert_receive_channel.public_primary_key}) # wrong additional_settings data = {"additional_settings": {"test": "test", "username": "test", "password": "test"}} response = client.put(url, data, format="json", **make_user_auth_headers(user, token)) assert response.status_code == status.HTTP_400_BAD_REQUEST assert "additional_settings" in response.json() data = {"additional_settings": {}} response = client.put(url, data, format="json", **make_user_auth_headers(user, token)) assert response.status_code == status.HTTP_400_BAD_REQUEST assert "additional_settings" in response.json() data = {"additional_settings": None} response = client.put(url, data, format="json", **make_user_auth_headers(user, token)) assert response.status_code == status.HTTP_400_BAD_REQUEST assert "additional_settings" in response.json() data = { "additional_settings": { "test": "test", "instance_url": "test2", } } response = client.put(url, data, format="json", **make_user_auth_headers(user, token)) assert response.status_code == status.HTTP_400_BAD_REQUEST assert "additional_settings" in response.json() # update with correct settings data = { "additional_settings": { "instance_url": "test2", } } response = client.put(url, data, format="json", **make_user_auth_headers(user, token)) assert response.status_code == status.HTTP_200_OK alert_receive_channel.refresh_from_db() assert alert_receive_channel.additional_settings == data["additional_settings"] integration_config.update_default_webhooks.assert_called_once_with(alert_receive_channel) @pytest.mark.django_db def test_update_other_integration_additional_settings( make_organization_and_user_with_plugin_token, make_alert_receive_channel, make_user_auth_headers, ): organization, user, token = make_organization_and_user_with_plugin_token() client = APIClient() alert_receive_channel = make_alert_receive_channel(organization) url = reverse("api-internal:alert_receive_channel-detail", kwargs={"pk": alert_receive_channel.public_primary_key}) # integration doesn't have additional_settings data = { "additional_settings": { "instance_url": "test", "username": "test", "password": "test", "is_configured": True, "state_mapping": { "firing": [1, "New"], "acknowledged": None, "resolved": None, "silenced": None, }, } } response = client.put(url, data, format="json", **make_user_auth_headers(user, token)) assert response.status_code == status.HTTP_400_BAD_REQUEST @pytest.mark.django_db def test_create_integration_default_webhooks( setup_additional_settings_for_integration, make_organization_and_user_with_plugin_token, make_user_auth_headers, ): organization, user, token = make_organization_and_user_with_plugin_token() integration_config = setup_additional_settings_for_integration integration_config.additional_settings_serializer = AdditionalSettingsTestSerializer integration_config.create_default_webhooks = Mock() client = APIClient() response = client.post( path=reverse("api-internal:alert_receive_channel-list"), data={ "integration": integration_config.slug, "team": None, "additional_settings": {"instance_url": "test"}, }, format="json", **make_user_auth_headers(user, token), ) assert response.status_code == status.HTTP_201_CREATED alert_receive_channel = AlertReceiveChannel.objects.get(public_primary_key=response.json()["id"]) integration_config.create_default_webhooks.assert_called_once_with(alert_receive_channel) integration_config.create_default_webhooks.reset_mock() # Create without default webhooks response = client.post( path=reverse("api-internal:alert_receive_channel-list"), data={ "integration": integration_config.slug, "team": None, "additional_settings": {"instance_url": "test"}, "create_default_webhooks": False, }, format="json", **make_user_auth_headers(user, token), ) assert response.status_code == status.HTTP_201_CREATED integration_config.create_default_webhooks.assert_not_called() @pytest.mark.django_db def test_alert_receive_channel_test_connection( make_organization_and_user_with_plugin_token, make_user_auth_headers, ): _, user, token = make_organization_and_user_with_plugin_token() client = APIClient() url = reverse("api-internal:alert_receive_channel-test-connection-create") integration_config = AlertReceiveChannel._config[0] data = { "integration": integration_config.slug, "team": None, "create_default_webhooks": True, "alert_group_labels": { "inheritable": {}, "custom": [], "template": None, }, } # no test connection setup response = client.post(url, data, format="json", **make_user_auth_headers(user, token)) assert response.status_code == status.HTTP_200_OK # test ok def testing_ok(instance): return True with patch.object(integration_config, "test_connection", side_effect=testing_ok, create=True): response = client.post(url, data, format="json", **make_user_auth_headers(user, token)) assert response.status_code == status.HTTP_200_OK # test error def testing_error(instance): raise BacksyncIntegrationRequestError(error_msg="Error!") with patch.object(integration_config, "test_connection", side_effect=testing_error, create=True): response = client.post(url, data, format="json", **make_user_auth_headers(user, token)) assert response.status_code == status.HTTP_400_BAD_REQUEST assert response.json() == {"detail": "Error!"} # test with invalid data data["team"] = "does-not-exist" response = client.post(url, data, format="json", **make_user_auth_headers(user, token)) assert response.status_code == status.HTTP_400_BAD_REQUEST assert response.json() == {"team": ["Object does not exist"]} @pytest.mark.django_db def test_alert_receive_channel_test_connection_existing_integration( make_organization_and_user_with_plugin_token, make_user_auth_headers, make_alert_receive_channel, ): organization, user, token = make_organization_and_user_with_plugin_token() client = APIClient() integration_config = AlertReceiveChannel._config[0] integration = make_alert_receive_channel(organization, integration=integration_config.slug, description_short="ok") def testing_connection(instance): if instance.description_short != "ok": raise BacksyncIntegrationRequestError(error_msg="Error!") url = reverse("api-internal:alert_receive_channel-test-connection", kwargs={"pk": integration.public_primary_key}) with patch.object(integration_config, "test_connection", side_effect=testing_connection, create=True): data = {} # no updates, use original data response = client.post(url, data, format="json", **make_user_auth_headers(user, token)) assert response.status_code == status.HTTP_200_OK data = {"description_short": "notok"} response = client.post(url, data, format="json", **make_user_auth_headers(user, token)) assert response.status_code == status.HTTP_400_BAD_REQUEST assert response.json() == {"detail": "Error!"} @pytest.mark.django_db def test_alert_receive_channel_status_options( make_organization_and_user_with_plugin_token, make_alert_receive_channel, make_user_auth_headers, ): organization, user, token = make_organization_and_user_with_plugin_token() integration_config = AlertReceiveChannel._config[0] alert_receive_channel = make_alert_receive_channel(organization, integration=integration_config.slug) client = APIClient() url = reverse( "api-internal:alert_receive_channel-status-options", kwargs={"pk": alert_receive_channel.public_primary_key}, ) # no status options setup response = client.get(url, format="json", **make_user_auth_headers(user, token)) assert response.status_code == status.HTTP_200_OK assert response.json() == [] # test ok def testing_ok(instance): return [("The option", "value"), ("Another", "another")] with patch.object(integration_config, "status_options", side_effect=testing_ok, create=True): response = client.get(url, format="json", **make_user_auth_headers(user, token)) assert response.status_code == status.HTTP_200_OK assert response.json() == [["The option", "value"], ["Another", "another"]] # test error def testing_error(instance): raise BacksyncIntegrationRequestError(error_msg="Error!") with patch.object(integration_config, "status_options", side_effect=testing_error, create=True): response = client.get(url, format="json", **make_user_auth_headers(user, token)) assert response.status_code == status.HTTP_400_BAD_REQUEST assert response.json() == {"detail": "Error!"} def _webhook_data(webhook_id=ANY, webhook_name=ANY, webhook_url=ANY, alert_receive_channel_id=ANY): return { "authorization_header": None, "data": None, "forward_all": True, "headers": None, "http_method": "POST", "id": webhook_id, "integration_filter": [alert_receive_channel_id], "is_legacy": False, "is_webhook_enabled": True, "labels": [], "last_response_log": { "content": "", "event_data": "", "request_data": "", "request_headers": "", "request_trigger": "", "status_code": None, "timestamp": None, "url": "", }, "name": webhook_name, "password": None, "preset": None, "team": None, "trigger_template": None, "trigger_type": "0", "trigger_type_name": "Manual or escalation step", "url": webhook_url, "username": None, } @pytest.mark.django_db def test_alert_receive_channel_webhooks_get( make_organization_and_user_with_plugin_token, make_alert_receive_channel, make_custom_webhook, make_user_auth_headers, ): organization, user, token = make_organization_and_user_with_plugin_token() alert_receive_channel = make_alert_receive_channel(organization) webhook = make_custom_webhook(organization, is_from_connected_integration=True) webhook.filtered_integrations.set([alert_receive_channel]) # create 2 webhooks that are not connected to the integration make_custom_webhook(organization) webhook2 = make_custom_webhook(organization, is_from_connected_integration=False) webhook2.filtered_integrations.set([alert_receive_channel]) client = APIClient() url = reverse( "api-internal:alert_receive_channel-webhooks-get", kwargs={"pk": alert_receive_channel.public_primary_key} ) response = client.get(url, **make_user_auth_headers(user, token)) assert response.status_code == status.HTTP_200_OK assert response.json() == [ _webhook_data( webhook_id=webhook.public_primary_key, alert_receive_channel_id=alert_receive_channel.public_primary_key, ) ] @pytest.mark.django_db def test_alert_receive_channel_webhooks_post( make_organization_and_user_with_plugin_token, make_alert_receive_channel, make_user_auth_headers, ): organization, user, token = make_organization_and_user_with_plugin_token() alert_receive_channel = make_alert_receive_channel(organization) client = APIClient() url = reverse( "api-internal:alert_receive_channel-webhooks-get", kwargs={"pk": alert_receive_channel.public_primary_key} ) data = { "name": None, "enabled": True, "url": "http://example.com/", "http_method": "POST", "trigger_type": "0", "trigger_template": None, } response = client.post(url, data, format="json", **make_user_auth_headers(user, token)) assert response.status_code == status.HTTP_201_CREATED assert response.json() == _webhook_data( webhook_url="http://example.com/", alert_receive_channel_id=alert_receive_channel.public_primary_key, ) assert alert_receive_channel.webhooks.get().is_from_connected_integration is True @pytest.mark.django_db def test_alert_receive_channel_webhooks_put( make_organization_and_user_with_plugin_token, make_alert_receive_channel, make_custom_webhook, make_user_auth_headers, ): organization, user, token = make_organization_and_user_with_plugin_token() alert_receive_channel = make_alert_receive_channel(organization) webhook = make_custom_webhook(organization, is_from_connected_integration=True) webhook.filtered_integrations.set([alert_receive_channel]) client = APIClient() url = reverse( "api-internal:alert_receive_channel-webhooks-put", kwargs={"pk": alert_receive_channel.public_primary_key, "webhook_id": webhook.public_primary_key}, ) data = _webhook_data( webhook_id=webhook.public_primary_key, webhook_name="Test", webhook_url="http://example.com/", alert_receive_channel_id=alert_receive_channel.public_primary_key, ) response = client.put(url, data, format="json", **make_user_auth_headers(user, token)) assert response.status_code == status.HTTP_200_OK webhook.refresh_from_db() assert webhook.url == "http://example.com/" @pytest.mark.django_db def test_alert_receive_channel_webhooks_delete( make_organization_and_user_with_plugin_token, make_alert_receive_channel, make_custom_webhook, make_user_auth_headers, ): organization, user, token = make_organization_and_user_with_plugin_token() alert_receive_channel = make_alert_receive_channel(organization) webhook = make_custom_webhook(organization, is_from_connected_integration=True) webhook.filtered_integrations.set([alert_receive_channel]) client = APIClient() url = reverse( "api-internal:alert_receive_channel-webhooks-put", kwargs={"pk": alert_receive_channel.public_primary_key, "webhook_id": webhook.public_primary_key}, ) response = client.delete(url, **make_user_auth_headers(user, token)) assert response.status_code == status.HTTP_204_NO_CONTENT webhook.refresh_from_db() assert webhook.deleted_at is not None assert alert_receive_channel.webhooks.count() == 0 @pytest.mark.django_db def test_connected_alert_receive_channels_get( make_organization_and_user_with_plugin_token, make_alert_receive_channel, make_alert_receive_channel_connection, make_user_auth_headers, ): organization, user, token = make_organization_and_user_with_plugin_token() source_alert_receive_channel = make_alert_receive_channel(organization) connected_alert_receive_channel = make_alert_receive_channel(organization) make_alert_receive_channel_connection(source_alert_receive_channel, connected_alert_receive_channel) # get integrations connected to source integration client = APIClient() url = reverse( "api-internal:alert_receive_channel-connected-alert-receive-channels-get", kwargs={"pk": source_alert_receive_channel.public_primary_key}, ) response = client.get(url, **make_user_auth_headers(user, token)) assert response.status_code == status.HTTP_200_OK assert response.json() == { "source_alert_receive_channels": [], "connected_alert_receive_channels": [ { "alert_receive_channel": { "id": connected_alert_receive_channel.public_primary_key, "integration": connected_alert_receive_channel.integration, "verbal_name": connected_alert_receive_channel.verbal_name, "deleted": False, }, "backsync": False, }, ], } # get source integrations for particular integration url = reverse( "api-internal:alert_receive_channel-connected-alert-receive-channels-get", kwargs={"pk": connected_alert_receive_channel.public_primary_key}, ) response = client.get(url, format="json", **make_user_auth_headers(user, token)) assert response.status_code == status.HTTP_200_OK assert response.json() == { "source_alert_receive_channels": [ { "alert_receive_channel": { "id": source_alert_receive_channel.public_primary_key, "integration": source_alert_receive_channel.integration, "verbal_name": source_alert_receive_channel.verbal_name, "deleted": False, }, "backsync": False, }, ], "connected_alert_receive_channels": [], } @pytest.mark.django_db def test_connected_alert_receive_channels_post( make_organization_and_user_with_plugin_token, make_alert_receive_channel, make_custom_webhook, make_user_auth_headers, ): organization, user, token = make_organization_and_user_with_plugin_token() source_alert_receive_channel = make_alert_receive_channel(organization) webhook = make_custom_webhook(organization, is_from_connected_integration=True) webhook.filtered_integrations.set([source_alert_receive_channel]) alert_receive_channel_to_connect_1 = make_alert_receive_channel(organization) alert_receive_channel_to_connect_2 = make_alert_receive_channel(organization) client = APIClient() url = reverse( "api-internal:alert_receive_channel-connected-alert-receive-channels-get", kwargs={"pk": source_alert_receive_channel.public_primary_key}, ) response = client.post( url, data=[ {"id": alert_receive_channel_to_connect_1.public_primary_key, "backsync": False}, {"id": alert_receive_channel_to_connect_2.public_primary_key, "backsync": True}, ], format="json", **make_user_auth_headers(user, token), ) assert response.status_code == status.HTTP_201_CREATED assert response.json() == { "source_alert_receive_channels": [], "connected_alert_receive_channels": [ { "alert_receive_channel": { "id": alert_receive_channel_to_connect_1.public_primary_key, "integration": alert_receive_channel_to_connect_1.integration, "verbal_name": alert_receive_channel_to_connect_1.verbal_name, "deleted": False, }, "backsync": False, }, { "alert_receive_channel": { "id": alert_receive_channel_to_connect_2.public_primary_key, "integration": alert_receive_channel_to_connect_2.integration, "verbal_name": alert_receive_channel_to_connect_2.verbal_name, "deleted": False, }, "backsync": True, }, ], } assert source_alert_receive_channel.connected_alert_receive_channels.count() == 2 assert set(webhook.filtered_integrations.values_list("id", flat=True)) == { source_alert_receive_channel.id, alert_receive_channel_to_connect_1.id, alert_receive_channel_to_connect_2.id, } @pytest.mark.django_db def test_connected_alert_receive_channels_put( make_organization_and_user_with_plugin_token, make_alert_receive_channel, make_alert_receive_channel_connection, make_user_auth_headers, ): organization, user, token = make_organization_and_user_with_plugin_token() source_alert_receive_channel = make_alert_receive_channel(organization) connected_alert_receive_channel = make_alert_receive_channel(organization) connection = make_alert_receive_channel_connection(source_alert_receive_channel, connected_alert_receive_channel) # update backsync for connected integration client = APIClient() url = reverse( "api-internal:alert_receive_channel-connected-alert-receive-channels-put", kwargs={ "pk": source_alert_receive_channel.public_primary_key, "connected_alert_receive_channel_id": connected_alert_receive_channel.public_primary_key, }, ) response = client.put(url, data={"backsync": True}, format="json", **make_user_auth_headers(user, token)) assert response.status_code == status.HTTP_200_OK assert response.json() == { "alert_receive_channel": { "id": connected_alert_receive_channel.public_primary_key, "integration": connected_alert_receive_channel.integration, "verbal_name": connected_alert_receive_channel.verbal_name, "deleted": False, }, "backsync": True, } connection.refresh_from_db() assert connection.backsync is True @pytest.mark.django_db def test_connected_alert_receive_channels_delete( make_organization_and_user_with_plugin_token, make_alert_receive_channel, make_alert_receive_channel_connection, make_custom_webhook, make_user_auth_headers, ): organization, user, token = make_organization_and_user_with_plugin_token() source_alert_receive_channel = make_alert_receive_channel(organization) connected_alert_receive_channel_1 = make_alert_receive_channel(organization) connected_alert_receive_channel_2 = make_alert_receive_channel(organization) make_alert_receive_channel_connection(source_alert_receive_channel, connected_alert_receive_channel_1) make_alert_receive_channel_connection(source_alert_receive_channel, connected_alert_receive_channel_2) webhook = make_custom_webhook(organization, is_from_connected_integration=True) webhook.filtered_integrations.set( [source_alert_receive_channel, connected_alert_receive_channel_1, connected_alert_receive_channel_2] ) client = APIClient() url = reverse( "api-internal:alert_receive_channel-connected-alert-receive-channels-put", kwargs={ "pk": source_alert_receive_channel.public_primary_key, "connected_alert_receive_channel_id": connected_alert_receive_channel_1.public_primary_key, }, ) response = client.delete(url, **make_user_auth_headers(user, token)) assert response.status_code == status.HTTP_204_NO_CONTENT assert source_alert_receive_channel.connected_alert_receive_channels.count() == 1 assert ( source_alert_receive_channel.connected_alert_receive_channels.first().connected_alert_receive_channel == connected_alert_receive_channel_2 ) assert set(webhook.filtered_integrations.values_list("id", flat=True)) == { source_alert_receive_channel.id, connected_alert_receive_channel_2.id, } @pytest.mark.django_db def test_delete_connection_on_channel_delete( make_organization_and_user_with_plugin_token, make_alert_receive_channel, make_alert_receive_channel_connection, make_user_auth_headers, ): organization, user, token = make_organization_and_user_with_plugin_token() source_alert_receive_channel = make_alert_receive_channel(organization) connected_alert_receive_channel_1 = make_alert_receive_channel(organization) connected_alert_receive_channel_2 = make_alert_receive_channel(organization) make_alert_receive_channel_connection(source_alert_receive_channel, connected_alert_receive_channel_1) make_alert_receive_channel_connection(source_alert_receive_channel, connected_alert_receive_channel_2) client = APIClient() source_integration_url = reverse( "api-internal:alert_receive_channel-connected-alert-receive-channels-get", kwargs={ "pk": source_alert_receive_channel.public_primary_key, }, ) connected_integration_url = reverse( "api-internal:alert_receive_channel-connected-alert-receive-channels-get", kwargs={ "pk": connected_alert_receive_channel_1.public_primary_key, }, ) response = client.get(source_integration_url, **make_user_auth_headers(user, token)) assert len(response.json()["connected_alert_receive_channels"]) == 2 # delete connected integration connected_alert_receive_channel_2.delete() response = client.get(source_integration_url, **make_user_auth_headers(user, token)) assert len(response.json()["connected_alert_receive_channels"]) == 1 assert ( response.json()["connected_alert_receive_channels"][0]["alert_receive_channel"]["id"] == connected_alert_receive_channel_1.public_primary_key ) # delete source integration response = client.get(connected_integration_url, **make_user_auth_headers(user, token)) assert len(response.json()["source_alert_receive_channels"]) == 1 assert ( response.json()["source_alert_receive_channels"][0]["alert_receive_channel"]["id"] == source_alert_receive_channel.public_primary_key ) source_alert_receive_channel.delete() response = client.get(connected_integration_url, **make_user_auth_headers(user, token)) assert len(response.json()["source_alert_receive_channels"]) == 0 @pytest.mark.django_db @pytest.mark.parametrize( "role,expected_status", [ (LegacyAccessControlRole.ADMIN, status.HTTP_200_OK), (LegacyAccessControlRole.EDITOR, status.HTTP_200_OK), (LegacyAccessControlRole.VIEWER, status.HTTP_200_OK), (LegacyAccessControlRole.NONE, status.HTTP_403_FORBIDDEN), ], ) def test_alert_receive_channel_api_token_get( make_organization_and_user_with_plugin_token, make_alert_receive_channel, make_user_auth_headers, role, expected_status, ): organization, user, token = make_organization_and_user_with_plugin_token(role) alert_receive_channel = make_alert_receive_channel(organization) url = reverse( "api-internal:alert_receive_channel-backsync-token-get", kwargs={"pk": alert_receive_channel.public_primary_key}, ) client = APIClient() with patch( "apps.api.views.alert_receive_channel.AlertReceiveChannelView.backsync_token_get", return_value=Response( status=status.HTTP_200_OK, ), ): response = client.get(url, format="json", **make_user_auth_headers(user, token)) assert response.status_code == expected_status @pytest.mark.django_db @pytest.mark.parametrize( "role,expected_status", [ (LegacyAccessControlRole.ADMIN, status.HTTP_201_CREATED), (LegacyAccessControlRole.EDITOR, status.HTTP_403_FORBIDDEN), (LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN), (LegacyAccessControlRole.NONE, status.HTTP_403_FORBIDDEN), ], ) def test_alert_receive_channel_api_token_post( make_organization_and_user_with_plugin_token, make_alert_receive_channel, make_user_auth_headers, role, expected_status, ): organization, user, token = make_organization_and_user_with_plugin_token(role) alert_receive_channel = make_alert_receive_channel(organization) url = reverse( "api-internal:alert_receive_channel-backsync-token-post", kwargs={"pk": alert_receive_channel.public_primary_key}, ) client = APIClient() with patch( "apps.api.views.alert_receive_channel.AlertReceiveChannelView.backsync_token_post", return_value=Response( status=status.HTTP_201_CREATED, ), ): response = client.post(url, format="json", **make_user_auth_headers(user, token)) assert response.status_code == expected_status @pytest.mark.django_db def test_integration_api_token( make_organization_and_user_with_plugin_token, make_alert_receive_channel, make_user_auth_headers, ): organization, user, token = make_organization_and_user_with_plugin_token() integration_config = AlertReceiveChannel._config[0] alert_receive_channel = make_alert_receive_channel(organization, integration=integration_config.slug) client = APIClient() get_token_url = reverse( "api-internal:alert_receive_channel-backsync-token-get", kwargs={ "pk": alert_receive_channel.public_primary_key, }, ) post_token_url = reverse( "api-internal:alert_receive_channel-backsync-token-post", kwargs={ "pk": alert_receive_channel.public_primary_key, }, ) # token does not exist response = client.get(get_token_url, **make_user_auth_headers(user, token)) assert response.status_code == status.HTTP_404_NOT_FOUND # create token def mock_token_usage(instance, token): return f"token:{token}" with patch.object(integration_config, "get_token_usage", side_effect=mock_token_usage, create=True): response = client.post(post_token_url, **make_user_auth_headers(user, token)) assert response.status_code == status.HTTP_201_CREATED response_data = response.json() integration_token_string = response_data.get("token") assert response_data.get("usage") == f"token:{integration_token_string}" integration_token_1 = alert_receive_channel.auth_tokens.first() # token was found response = client.get(get_token_url, **make_user_auth_headers(user, token)) assert response.status_code == status.HTTP_200_OK # recreate token, the first token will be revoked response = client.post(post_token_url, **make_user_auth_headers(user, token)) assert response.status_code == status.HTTP_201_CREATED integration_token_1.refresh_from_db() integration_token_2 = alert_receive_channel.auth_tokens.first() assert integration_token_2 != integration_token_1 assert integration_token_1.revoked_at is not None assert integration_token_string != response.json().get("token") @pytest.mark.django_db def test_alert_receive_channel_integration_options_search( make_organization_and_user_with_plugin_token, make_user_auth_headers, ): _, user, token = make_organization_and_user_with_plugin_token() client = APIClient() url = reverse("api-internal:alert_receive_channel-integration-options") search_url = f"{url}?search=graf" response = client.get(search_url, format="json", **make_user_auth_headers(user, token)) assert response.status_code == status.HTTP_200_OK returned_choices = [i["display_name"] for i in response.json()] assert returned_choices == ["Grafana Alerting", "Grafana Legacy Alerting", "(Deprecated) Grafana Alerting"] search_url = f"{url}?search=notfound" response = client.get(search_url, format="json", **make_user_auth_headers(user, token)) assert response.status_code == status.HTTP_200_OK returned_choices = [i["display_name"] for i in response.json()] assert returned_choices == [] search_url = f"{url}?search=webhook" response = client.get(search_url, format="json", **make_user_auth_headers(user, token)) assert response.status_code == status.HTTP_200_OK returned_choices = [i["display_name"] for i in response.json()] assert returned_choices == ["Webhook", "Formatted webhook"]