diff --git a/engine/apps/api/serializers/organization.py b/engine/apps/api/serializers/organization.py index 05a17578..523f6825 100644 --- a/engine/apps/api/serializers/organization.py +++ b/engine/apps/api/serializers/organization.py @@ -2,6 +2,7 @@ from dataclasses import asdict from rest_framework import serializers +from apps.base.messaging import get_messaging_backend_from_id from apps.base.models import LiveSetting from apps.phone_notifications.phone_provider import get_phone_provider from apps.slack.models import SlackTeamIdentity @@ -107,3 +108,30 @@ class FastOrganizationSerializer(serializers.ModelSerializer): class Meta: model = Organization fields = ["pk", "name"] + + +class CurrentOrganizationConfigChecksSerializer(serializers.ModelSerializer): + is_chatops_connected = serializers.SerializerMethodField() + is_integration_chatops_connected = serializers.SerializerMethodField() + + class Meta: + model = Organization + fields = [ + "is_chatops_connected", + "is_integration_chatops_connected", + ] + + def get_is_chatops_connected(self, obj): + msteams_backend = get_messaging_backend_from_id("MSTEAMS") + return bool( + obj.slack_team_identity_id is not None # slack is connected + or obj.telegram_channel.exists() # telegram is connected + or (msteams_backend and msteams_backend.is_configured_for_organization(obj)) # msteams is connected + ) + + def get_is_integration_chatops_connected(self, obj): + return ( + obj.alert_receive_channels.filter(channel_filters__notify_in_slack=True).exists() + or obj.alert_receive_channels.filter(channel_filters__notify_in_telegram=True).exists() + or obj.alert_receive_channels.filter(channel_filters__notification_backends__MSTEAMS__enabled=True).exists() + ) diff --git a/engine/apps/api/tests/test_organization.py b/engine/apps/api/tests/test_organization.py index 3397ff41..a4eceec0 100644 --- a/engine/apps/api/tests/test_organization.py +++ b/engine/apps/api/tests/test_organization.py @@ -230,3 +230,93 @@ def test_organization_get_channel_verification_code_invalid( response = client.get(url, format="json", **make_user_auth_headers(tester, token)) assert response.status_code == status.HTTP_400_BAD_REQUEST + + +@pytest.mark.django_db +def test_get_organization_slack_config_checks( + make_organization_and_user_with_plugin_token, + make_slack_team_identity, + make_alert_receive_channel, + make_channel_filter, + make_user_auth_headers, +): + organization, user, token = make_organization_and_user_with_plugin_token() + + client = APIClient() + url = reverse("api-internal:api-organization-config-checks") + expected_result = { + "is_chatops_connected": False, + "is_integration_chatops_connected": False, + } + response = client.get(url, format="json", **make_user_auth_headers(user, token)) + assert response.status_code == status.HTTP_200_OK + assert response.json() == expected_result + + # connect Slack + slack_team_identity = make_slack_team_identity() + organization.slack_team_identity = slack_team_identity + organization.save() + + response = client.get(url, format="json", **make_user_auth_headers(user, token)) + assert response.status_code == status.HTTP_200_OK + expected_result["is_chatops_connected"] = True + assert response.json() == expected_result + + # create integration + integration = make_alert_receive_channel(organization) + + response = client.get(url, format="json", **make_user_auth_headers(user, token)) + assert response.status_code == status.HTTP_200_OK + assert response.json() == expected_result + + # connect integration to Slack + make_channel_filter(integration, notify_in_slack=True) + + response = client.get(url, format="json", **make_user_auth_headers(user, token)) + assert response.status_code == status.HTTP_200_OK + expected_result["is_integration_chatops_connected"] = True + assert response.json() == expected_result + + +@pytest.mark.django_db +def test_get_organization_telegram_config_checks( + make_organization_and_user_with_plugin_token, + make_telegram_channel, + make_alert_receive_channel, + make_channel_filter, + make_user_auth_headers, +): + organization, user, token = make_organization_and_user_with_plugin_token() + + client = APIClient() + url = reverse("api-internal:api-organization-config-checks") + expected_result = { + "is_chatops_connected": False, + "is_integration_chatops_connected": False, + } + response = client.get(url, format="json", **make_user_auth_headers(user, token)) + assert response.status_code == status.HTTP_200_OK + assert response.json() == expected_result + + # connect Telegram + make_telegram_channel(organization) + + response = client.get(url, format="json", **make_user_auth_headers(user, token)) + assert response.status_code == status.HTTP_200_OK + expected_result["is_chatops_connected"] = True + assert response.json() == expected_result + + # create integration + integration = make_alert_receive_channel(organization) + + response = client.get(url, format="json", **make_user_auth_headers(user, token)) + assert response.status_code == status.HTTP_200_OK + assert response.json() == expected_result + + # connect integration to Slack + make_channel_filter(integration, notify_in_telegram=True) + + response = client.get(url, format="json", **make_user_auth_headers(user, token)) + assert response.status_code == status.HTTP_200_OK + expected_result["is_integration_chatops_connected"] = True + assert response.json() == expected_result diff --git a/engine/apps/api/urls.py b/engine/apps/api/urls.py index 0f3b8e95..639d1094 100644 --- a/engine/apps/api/urls.py +++ b/engine/apps/api/urls.py @@ -20,6 +20,7 @@ from .views.organization import ( CurrentOrganizationView, GetChannelVerificationCode, GetTelegramVerificationCode, + OrganizationConfigChecksView, SetGeneralChannel, ) from .views.paging import DirectPagingAPIView @@ -72,6 +73,11 @@ urlpatterns = [ optional_slash_path("user", CurrentUserView.as_view(), name="api-user"), optional_slash_path("set_general_channel", SetGeneralChannel.as_view(), name="api-set-general-log-channel"), optional_slash_path("organization", CurrentOrganizationView.as_view(), name="api-organization"), + optional_slash_path( + "organization/config-checks", + OrganizationConfigChecksView.as_view(), + name="api-organization-config-checks", + ), # TODO: remove current_team routes in future release optional_slash_path("current_team", CurrentOrganizationView.as_view(), name="api-current-team"), optional_slash_path( diff --git a/engine/apps/api/views/organization.py b/engine/apps/api/views/organization.py index 2ec62976..8b6f5d70 100644 --- a/engine/apps/api/views/organization.py +++ b/engine/apps/api/views/organization.py @@ -6,7 +6,7 @@ from rest_framework.response import Response from rest_framework.views import APIView from apps.api.permissions import RBACPermission -from apps.api.serializers.organization import CurrentOrganizationSerializer +from apps.api.serializers.organization import CurrentOrganizationConfigChecksSerializer, CurrentOrganizationSerializer from apps.auth_token.auth import PluginAuthentication from apps.base.messaging import get_messaging_backend_from_id from apps.mobile_app.auth import MobileAppAuthTokenAuthentication @@ -50,6 +50,20 @@ class CurrentOrganizationView(APIView): return Response(serializer.data) +class OrganizationConfigChecksView(APIView): + authentication_classes = (PluginAuthentication,) + permission_classes = (IsAuthenticated, RBACPermission) + + rbac_permissions = { + "get": [RBACPermission.Permissions.OTHER_SETTINGS_READ], + } + + def get(self, request): + organization = request.auth.organization + serializer = CurrentOrganizationConfigChecksSerializer(organization) + return Response(serializer.data) + + class GetTelegramVerificationCode(APIView): authentication_classes = (PluginAuthentication,) permission_classes = (IsAuthenticated, RBACPermission) diff --git a/engine/apps/base/messaging.py b/engine/apps/base/messaging.py index 9b2e6b75..a31bd5f2 100644 --- a/engine/apps/base/messaging.py +++ b/engine/apps/base/messaging.py @@ -43,6 +43,9 @@ class BaseMessagingBackend: def is_enabled_for_organization(organization): return True + def is_configured_for_organization(self, organization): + return True + def serialize_user(self, user): """Return a serialized backend user representation.""" raise NotImplementedError("serialize_user method missing implementation")