diff --git a/engine/apps/api/serializers/alert_group.py b/engine/apps/api/serializers/alert_group.py index ea3892ca..1c2a9e9f 100644 --- a/engine/apps/api/serializers/alert_group.py +++ b/engine/apps/api/serializers/alert_group.py @@ -22,6 +22,13 @@ logger = logging.getLogger(__name__) logger.setLevel(logging.DEBUG) +class ExternalURL(typing.TypedDict): + integration: str + integration_type: str + external_id: str + url: str + + class RenderForWeb(typing.TypedDict): title: str message: str @@ -213,6 +220,7 @@ class AlertGroupSerializer(AlertGroupListSerializer): alerts = serializers.SerializerMethodField("get_limited_alerts") last_alert_at = serializers.SerializerMethodField() paged_users = serializers.SerializerMethodField() + external_urls = serializers.SerializerMethodField() class Meta(AlertGroupListSerializer.Meta): fields = AlertGroupListSerializer.Meta.fields + [ @@ -221,6 +229,7 @@ class AlertGroupSerializer(AlertGroupListSerializer): "slack_permalink", # TODO: make plugin frontend use "permalinks" field to get Slack link "last_alert_at", "paged_users", + "external_urls", ] def get_last_alert_at(self, obj: "AlertGroup") -> datetime.datetime: @@ -242,3 +251,21 @@ class AlertGroupSerializer(AlertGroupListSerializer): def get_paged_users(self, obj: "AlertGroup") -> typing.List[PagedUser]: return obj.get_paged_users() + + def get_external_urls(self, obj: "AlertGroup") -> typing.List[ExternalURL]: + external_urls = [] + external_ids = obj.external_ids.all() + for external_id in external_ids: + source_integration = external_id.source_alert_receive_channel + get_url = getattr(source_integration.config, "get_url", None) + if get_url: + url = source_integration.config.get_url(source_integration, external_id.value) + external_urls.append( + { + "integration": source_integration.public_primary_key, + "integration_type": source_integration.integration, + "external_id": external_id.value, + "url": url, + } + ) + return external_urls diff --git a/engine/apps/api/tests/test_alert_group.py b/engine/apps/api/tests/test_alert_group.py index a25f897b..136d8e7a 100644 --- a/engine/apps/api/tests/test_alert_group.py +++ b/engine/apps/api/tests/test_alert_group.py @@ -1,5 +1,5 @@ import datetime -from unittest.mock import patch +from unittest.mock import Mock, patch import pytest from django.core.cache import cache @@ -10,7 +10,13 @@ from rest_framework.response import Response from rest_framework.test import APIClient from apps.alerts.constants import ActionSource -from apps.alerts.models import AlertGroup, AlertGroupLogRecord, ResolutionNote +from apps.alerts.models import ( + AlertGroup, + AlertGroupExternalID, + AlertGroupLogRecord, + AlertReceiveChannel, + ResolutionNote, +) from apps.alerts.paging import direct_paging from apps.alerts.tasks import wipe from apps.api.errors import AlertGroupAPIError @@ -2243,3 +2249,42 @@ def test_alert_group_list_labels( assert response.json()["results"][-1]["labels"] == [ {"key": {"id": "a", "name": "a"}, "value": {"id": "b", "name": "b"}} ] + + +@pytest.mark.django_db +def test_alert_group_external_urls( + make_organization_and_user_with_plugin_token, + make_user_auth_headers, + make_alert_receive_channel, + make_alert_receive_channel_connection, + make_alert_group, +): + client = APIClient() + + integration_config = AlertReceiveChannel._config[0] + integration_config.get_url = Mock(return_value="https://some-url") + + organization, user, token = make_organization_and_user_with_plugin_token() + source_alert_receive_channel = make_alert_receive_channel(organization, integration=integration_config.slug) + alert_receive_channel = make_alert_receive_channel(organization) + make_alert_receive_channel_connection(source_alert_receive_channel, alert_receive_channel) + + alert_group = make_alert_group(alert_receive_channel) + + url = reverse("api-internal:alertgroup-detail", kwargs={"pk": alert_group.public_primary_key}) + response = client.get(url, format="json", **make_user_auth_headers(user, token)) + assert response.json()["external_urls"] == [] + + # create external IDs + external_id = AlertGroupExternalID.objects.create( + source_alert_receive_channel=source_alert_receive_channel, alert_group=alert_group, value="test123" + ) + url = reverse("api-internal:alertgroup-detail", kwargs={"pk": alert_group.public_primary_key}) + response = client.get(url, format="json", **make_user_auth_headers(user, token)) + expected = { + "integration": source_alert_receive_channel.public_primary_key, + "integration_type": source_alert_receive_channel.integration, + "external_id": external_id.value, + "url": "https://some-url", + } + assert response.json()["external_urls"] == [expected] diff --git a/grafana-plugin/src/network/oncall-api/autogenerated-api.types.d.ts b/grafana-plugin/src/network/oncall-api/autogenerated-api.types.d.ts index 5f929dff..3c7ee24c 100644 --- a/grafana-plugin/src/network/oncall-api/autogenerated-api.types.d.ts +++ b/grafana-plugin/src/network/oncall-api/autogenerated-api.types.d.ts @@ -1411,6 +1411,12 @@ export interface components { avatar_full: string; important: boolean; }[]; + readonly external_urls: { + integration: string; + integration_type: string; + external_id: string; + url: string; + }[]; }; AlertGroupAttach: { root_alert_group_pk: string;