diff --git a/engine/apps/alerts/models/alert_group.py b/engine/apps/alerts/models/alert_group.py index cb08e8e1..06df3a2b 100644 --- a/engine/apps/alerts/models/alert_group.py +++ b/engine/apps/alerts/models/alert_group.py @@ -1,7 +1,6 @@ import logging -import typing from collections import namedtuple -from typing import Optional +from typing import Optional, TypedDict from urllib.parse import urljoin from uuid import uuid1 @@ -46,8 +45,9 @@ def generate_public_primary_key_for_alert_group(): return new_public_primary_key -class Permalinks(typing.TypedDict): - slack: str +class Permalinks(TypedDict): + slack: Optional[str] + telegram: Optional[str] class AlertGroupQuerySet(models.QuerySet): @@ -401,12 +401,12 @@ class AlertGroup(AlertGroupSlackRenderingMixin, EscalationSnapshotMixin, models. raise NotImplementedError @property - def slack_permalink(self): + def slack_permalink(self) -> Optional[str]: if self.slack_message is not None: return self.slack_message.permalink @property - def telegram_permalink(self) -> typing.Optional[str]: + def telegram_permalink(self) -> Optional[str]: """ This property will attempt to access an attribute, `prefetched_telegram_messages`, representing a list of prefetched telegram messages. If this attribute does not exist, it falls back to performing a query. diff --git a/engine/apps/api/serializers/alert_group.py b/engine/apps/api/serializers/alert_group.py index a71cfde2..5c2e9517 100644 --- a/engine/apps/api/serializers/alert_group.py +++ b/engine/apps/api/serializers/alert_group.py @@ -132,7 +132,8 @@ class AlertGroupSerializer(AlertGroupListSerializer): fields = AlertGroupListSerializer.Meta.fields + [ "alerts", "render_after_resolve_report_json", - "slack_permalink", + "slack_permalink", # TODO: make plugin frontend use "permalinks" field to get Slack link + "permalinks", "last_alert_at", ] diff --git a/engine/apps/api/serializers/user.py b/engine/apps/api/serializers/user.py index 5f428d87..e3a0d784 100644 --- a/engine/apps/api/serializers/user.py +++ b/engine/apps/api/serializers/user.py @@ -36,7 +36,7 @@ class UserSerializer(DynamicFieldsModelSerializer, EagerLoadingMixin): timezone = serializers.CharField(allow_null=True, required=False) avatar = serializers.URLField(source="avatar_url", read_only=True) - + avatar_full = serializers.URLField(source="avatar_full_url", read_only=True) permissions = serializers.SerializerMethodField() notification_chain_verbal = serializers.SerializerMethodField() cloud_connection_status = serializers.SerializerMethodField() @@ -51,8 +51,10 @@ class UserSerializer(DynamicFieldsModelSerializer, EagerLoadingMixin): "current_team", "email", "username", + "name", "role", "avatar", + "avatar_full", "timezone", "working_hours", "unverified_phone_number", @@ -68,6 +70,7 @@ class UserSerializer(DynamicFieldsModelSerializer, EagerLoadingMixin): read_only_fields = [ "email", "username", + "name", "role", "verified_phone_number", ] diff --git a/engine/apps/api/tests/test_user.py b/engine/apps/api/tests/test_user.py index 54a79ad7..ee7f9809 100644 --- a/engine/apps/api/tests/test_user.py +++ b/engine/apps/api/tests/test_user.py @@ -68,6 +68,7 @@ def test_update_user_cant_change_email_and_username( "email": admin.email, "hide_phone_number": False, "username": admin.username, + "name": admin.name, "role": admin.role, "timezone": None, "working_hours": default_working_hours(), @@ -84,6 +85,7 @@ def test_update_user_cant_change_email_and_username( "notification_chain_verbal": {"default": "", "important": ""}, "slack_user_identity": None, "avatar": admin.avatar_url, + "avatar_full": admin.avatar_full_url, } response = client.put(url, data, format="json", **make_user_auth_headers(admin, token)) assert response.status_code == status.HTTP_200_OK @@ -117,6 +119,7 @@ def test_list_users( "email": admin.email, "hide_phone_number": False, "username": admin.username, + "name": admin.name, "role": admin.role, "timezone": None, "working_hours": default_working_hours(), @@ -132,6 +135,7 @@ def test_list_users( "notification_chain_verbal": {"default": "", "important": ""}, "slack_user_identity": None, "avatar": admin.avatar_url, + "avatar_full": admin.avatar_full_url, "cloud_connection_status": 0, }, { @@ -141,6 +145,7 @@ def test_list_users( "email": editor.email, "hide_phone_number": False, "username": editor.username, + "name": editor.name, "role": editor.role, "timezone": None, "working_hours": default_working_hours(), @@ -156,6 +161,7 @@ def test_list_users( "notification_chain_verbal": {"default": "", "important": ""}, "slack_user_identity": None, "avatar": editor.avatar_url, + "avatar_full": editor.avatar_full_url, "cloud_connection_status": 0, }, ], diff --git a/engine/apps/mobile_app/backend.py b/engine/apps/mobile_app/backend.py index 82187250..f95633ee 100644 --- a/engine/apps/mobile_app/backend.py +++ b/engine/apps/mobile_app/backend.py @@ -1,5 +1,3 @@ -from push_notifications.models import APNSDevice - from apps.base.messaging import BaseMessagingBackend from apps.mobile_app.tasks import notify_user_async @@ -28,8 +26,9 @@ class MobileAppBackend(BaseMessagingBackend): token.delete() def serialize_user(self, user): - # TODO: add Android support using GCMDevice - return {"connected": APNSDevice.objects.filter(user_id=user.pk).exists()} + from apps.mobile_app.models import MobileAppAuthToken + + return {"connected": MobileAppAuthToken.objects.filter(user=user).exists()} def notify_user(self, user, alert_group, notification_policy, critical=False): notify_user_async.delay( diff --git a/engine/apps/user_management/models/user.py b/engine/apps/user_management/models/user.py index 63c3ce0e..81af4e70 100644 --- a/engine/apps/user_management/models/user.py +++ b/engine/apps/user_management/models/user.py @@ -1,4 +1,5 @@ import logging +from urllib.parse import urljoin from django.apps import apps from django.conf import settings @@ -161,6 +162,10 @@ class User(models.Model): def is_authenticated(self): return True + @property + def avatar_full_url(self): + return urljoin(self.organization.grafana_url, self.avatar_url) + @property def verified_phone_number(self): """ diff --git a/engine/apps/user_management/tests/test_sync.py b/engine/apps/user_management/tests/test_sync.py index d6a6d922..c7e03bb9 100644 --- a/engine/apps/user_management/tests/test_sync.py +++ b/engine/apps/user_management/tests/test_sync.py @@ -10,7 +10,7 @@ from apps.user_management.sync import cleanup_organization, sync_organization @pytest.mark.django_db def test_sync_users_for_organization(make_organization, make_user_for_organization): - organization = make_organization() + organization = make_organization(grafana_url="https://test.test") users = tuple(make_user_for_organization(organization, user_id=user_id) for user_id in (1, 2)) api_users = tuple( @@ -20,7 +20,7 @@ def test_sync_users_for_organization(make_organization, make_user_for_organizati "name": "Test", "login": "test", "role": "admin", - "avatarUrl": "test.test/test", + "avatarUrl": "/test/1234", } for user_id in (2, 3) ) @@ -37,12 +37,14 @@ def test_sync_users_for_organization(make_organization, make_user_for_organizati assert updated_user is not None assert updated_user.name == api_users[0]["name"] assert updated_user.email == api_users[0]["email"] + assert updated_user.avatar_full_url == "https://test.test/test/1234" # check that missing users are created created_user = organization.users.filter(user_id=api_users[1]["userId"]).first() assert created_user is not None assert created_user.user_id == api_users[1]["userId"] assert created_user.name == api_users[1]["name"] + assert created_user.avatar_full_url == "https://test.test/test/1234" @pytest.mark.django_db