Re-implement FCM relay after introducing firebase (#1121)

This PR changes how `FCMRelayView` handles push notifications from OSS
instances, also changing how the mobile app backend sends push
notifications.
This commit is contained in:
Vadim Stepanov 2023-01-11 11:42:01 +00:00 committed by GitHub
parent 2dcb25721e
commit 231c0f45a3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 84 additions and 22 deletions

View file

@ -1,29 +1,72 @@
# from firebase_admin.messaging import Message
# from fcm_django.models import FCMDevice
import logging
from fcm_django.models import FCMDevice
from firebase_admin.messaging import APNSConfig, APNSPayload, Aps, ApsAlert, CriticalSound, Message
from rest_framework import status
from rest_framework.response import Response
from rest_framework.views import APIView
REQUIRED_FIELDS = {"registration_ids", "notification", "data"}
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
# TODO: update thie
class FCMRelayView(APIView):
# TODO: use public API authentication (then it would be required to connect to a cloud instance to use the app)
authentication_classes = []
permission_classes = []
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)
This view accepts push notifications from OSS instances and forwards these requests to FCM.
Requests to this endpoint come from OSS instances: apps.mobile_app.tasks.send_push_notification_to_fcm_relay
"""
if not REQUIRED_FIELDS.issubset(request.data.keys()):
try:
token = request.data["token"]
data = request.data["data"]
except KeyError:
return Response(status=status.HTTP_400_BAD_REQUEST)
# registration_ids = request.data["registration_ids"]
# data = {
# **request.data["data"],
# **request.data["notification"],
# }
message = Message(token=token, data=data, apns=get_apns(request.data))
# return FCMDevice.objects.send_message(Message(), False, ["registration_ids"])
return "TODO:"
logger.debug(f"Sending message to FCM: {message}")
result = FCMDevice(registration_id=token).send_message(message)
logger.debug(f"FCM response: {result}")
return Response(status=status.HTTP_200_OK)
def get_apns(data):
"""
Create APNSConfig object from JSON payload from OSS instance.
"""
aps = data.get("apns", {}).get("payload", {}).get("aps", {})
if not aps:
return None
thread_id = aps.get("thread-id")
badge = aps.get("badge")
alert = aps.get("alert")
if isinstance(alert, dict):
alert = ApsAlert(**alert)
sound = aps.get("sound")
if isinstance(sound, dict):
sound = CriticalSound(**sound)
# remove all keys from "aps" so it can be used for custom_data
for key in ["thread-id", "badge", "alert", "sound"]:
aps.pop(key, None)
return APNSConfig(
payload=APNSPayload(
aps=Aps(
thread_id=thread_id,
badge=badge,
alert=alert,
sound=sound,
custom_data=aps,
)
)
)

View file

@ -1,3 +1,7 @@
import json
import logging
import requests
from celery.utils.log import get_task_logger
from django.conf import settings
from fcm_django.models import FCMDevice
@ -6,10 +10,12 @@ from firebase_admin.messaging import APNSConfig, APNSPayload, Aps, ApsAlert, Cri
from apps.alerts.models import AlertGroup
from apps.mobile_app.alert_rendering import get_push_notification_message
from apps.user_management.models import User
from common.api_helpers.utils import create_engine_url
from common.custom_celery_tasks import shared_dedicated_queue_retry_task
MAX_RETRIES = 1 if settings.DEBUG else 10
logger = get_task_logger(__name__)
logger.setLevel(logging.DEBUG)
@shared_dedicated_queue_retry_task(autoretry_for=(Exception,), retry_backoff=True, max_retries=MAX_RETRIES)
@ -71,8 +77,6 @@ def notify_user_async(user_pk, alert_group_pk, notification_policy_pk, critical)
alert_body = f"Status: {status_verbose}, alerts: {alerts_count_str}"
# TODO: we should update this to check if FCM_RELAY is set and conditionally make a call here..
message = Message(
token=device_to_notify.registration_id,
data={
@ -109,10 +113,25 @@ def notify_user_async(user_pk, alert_group_pk, notification_policy_pk, critical)
),
)
logger.info(f"Sending push notification with message: {message}; thread-id: {thread_id};")
logger.debug(f"Sending push notification with message: {message}; thread-id: {thread_id};")
fcm_response = device_to_notify.send_message(message)
if settings.LICENSE == settings.OPEN_SOURCE_LICENSE_NAME:
response = send_push_notification_to_fcm_relay(message)
logger.debug(f"FCM relay response: {response}")
else:
response = device_to_notify.send_message(message)
# NOTE: we may want to further handle the response from FCM, but for now lets simply log it out
# https://firebase.google.com/docs/cloud-messaging/http-server-ref#interpret-downstream
logger.debug(f"FCM response: {response}")
# NOTE: we may want to further handle the response from FCM, but for now lets simply log it out
# https://firebase.google.com/docs/cloud-messaging/http-server-ref#interpret-downstream
logger.info(f"FCM response was: {fcm_response}")
def send_push_notification_to_fcm_relay(message):
"""
Send push notification to FCM relay on cloud instance: apps.mobile_app.fcm_relay.FCMRelayView
"""
url = create_engine_url("mobile_app/v1/fcm_relay", override_base=settings.GRAFANA_CLOUD_ONCALL_API_URL)
response = requests.post(url, json=json.loads(str(message)))
response.raise_for_status()
return response