Mobile app FCM support (#923)

* Add ability to configure FCM_API_KEY and FCM_POST_URL

* Delete APNSDevice and GCMDevice instances when unlinking the mobile app backend

* Add a simple FCM relay endpoint

* GCM -> FCM

* comment
This commit is contained in:
Vadim Stepanov 2022-12-01 15:17:01 +00:00 committed by GitHub
parent c08eeb72a3
commit 1878b7e596
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 59 additions and 8 deletions

View file

@ -1,6 +1,7 @@
import json
from django.conf import settings
from push_notifications.models import APNSDevice, GCMDevice
from apps.base.messaging import BaseMessagingBackend
from apps.mobile_app.tasks import notify_user_async
@ -33,6 +34,10 @@ class MobileAppBackend(BaseMessagingBackend):
token = MobileAppAuthToken.objects.get(user=user)
token.delete()
# delete push notification related info for user
APNSDevice.objects.filter(user=user).delete()
GCMDevice.objects.filter(user=user).delete()
def serialize_user(self, user):
from apps.mobile_app.models import MobileAppAuthToken

View file

@ -0,0 +1,26 @@
from push_notifications.gcm import send_message
from rest_framework import status
from rest_framework.response import Response
from rest_framework.views import APIView
REQUIRED_FIELDS = {"registration_ids", "notification", "data"}
class FCMRelayView(APIView):
def post(self, request):
"""
This view accepts requests from OSS instances of Grafana OnCall and forwards these requests to FCM.
Requests will be sent with the FCM_API_KEY configured in server settings
(see PUSH_NOTIFICATIONS_SETTINGS in settings/base.py)
"""
if not REQUIRED_FIELDS.issubset(request.data.keys()):
return Response(status=status.HTTP_400_BAD_REQUEST)
registration_ids = request.data["registration_ids"]
data = {
**request.data["data"],
**request.data["notification"],
}
return send_message(registration_ids=registration_ids, data=data, cloud_type="FCM")

View file

@ -1,13 +1,21 @@
from apps.mobile_app.views import APNSDeviceAuthorizedViewSet, GCMDeviceAuthorizedViewSet, MobileAppAuthTokenAPIView
from django.conf import settings
from apps.mobile_app.fcm_relay import FCMRelayView
from apps.mobile_app.views import APNSDeviceAuthorizedViewSet, FCMDeviceAuthorizedViewSet, MobileAppAuthTokenAPIView
from common.api_helpers.optional_slash_router import OptionalSlashRouter, optional_slash_path
app_name = "mobile_app"
router = OptionalSlashRouter()
router.register("apns", APNSDeviceAuthorizedViewSet, basename="apns")
router.register("gcm", GCMDeviceAuthorizedViewSet, basename="gcm")
router.register("fcm", FCMDeviceAuthorizedViewSet, basename="fcm")
urlpatterns = [
*router.urls,
optional_slash_path("auth_token", MobileAppAuthTokenAPIView.as_view(), name="auth_token"),
]
if settings.FCM_RELAY_ENABLED:
urlpatterns += [
optional_slash_path("fcm_relay", FCMRelayView.as_view(), name="fcm_relay"),
]

View file

@ -1,8 +1,9 @@
from push_notifications.api.rest_framework import APNSDeviceAuthorizedViewSet as BaseAPNSDeviceAuthorizedViewSet
from push_notifications.api.rest_framework import GCMDeviceAuthorizedViewSet as BaseGCMDeviceAuthorizedViewSet
from push_notifications.api.rest_framework import GCMDeviceAuthorizedViewSet, GCMDeviceSerializer
from rest_framework import status
from rest_framework.exceptions import NotFound
from rest_framework.response import Response
from rest_framework.serializers import HiddenField
from rest_framework.views import APIView
from apps.mobile_app.auth import MobileAppAuthTokenAuthentication, MobileAppVerificationTokenAuthentication
@ -13,8 +14,17 @@ class APNSDeviceAuthorizedViewSet(BaseAPNSDeviceAuthorizedViewSet):
authentication_classes = (MobileAppAuthTokenAuthentication,)
class GCMDeviceAuthorizedViewSet(BaseGCMDeviceAuthorizedViewSet):
class FCMDeviceAuthorizedViewSet(GCMDeviceAuthorizedViewSet):
class FCMDeviceSerializer(GCMDeviceSerializer):
"""
GCMDevice has cloud_message_type equal to "GCM" by default, in this serializer cloud_message_type is always set
to "FCM" no matter what was provided in the request.
"""
cloud_message_type = HiddenField(default="FCM")
authentication_classes = (MobileAppAuthTokenAuthentication,)
serializer_class = FCMDeviceSerializer
class MobileAppAuthTokenAPIView(APIView):

View file

@ -552,16 +552,18 @@ if FEATURE_MOBILE_APP_INTEGRATION_ENABLED:
]
PUSH_NOTIFICATIONS_SETTINGS = {
"FCM_API_KEY": os.environ.get("FCM_API_KEY", None),
"GCM_API_KEY": os.environ.get("GCM_API_KEY", None),
"FCM_API_KEY": os.getenv("FCM_API_KEY"),
"FCM_POST_URL": os.getenv("FCM_POST_URL", default="https://fcm.googleapis.com/fcm/send"),
"USER_MODEL": "user_management.User",
"UPDATE_ON_DUPLICATE_REG_ID": True,
# TODO: remove APNS related endpoints after the hackathon app is deprecated
"APNS_AUTH_KEY_PATH": os.environ.get("APNS_AUTH_KEY_PATH", None),
"APNS_TOPIC": os.environ.get("APNS_TOPIC", None),
"APNS_AUTH_KEY_ID": os.environ.get("APNS_AUTH_KEY_ID", None),
"APNS_TEAM_ID": os.environ.get("APNS_TEAM_ID", None),
"APNS_USE_SANDBOX": getenv_boolean("APNS_USE_SANDBOX", True),
"USER_MODEL": "user_management.User",
"UPDATE_ON_DUPLICATE_REG_ID": True,
}
FCM_RELAY_ENABLED = getenv_boolean("FCM_RELAY_ENABLED", default=False)
SELF_HOSTED_SETTINGS = {
"STACK_ID": 5,