oncall-engine/engine/apps/mobile_app/backend.py
Joey Orlando 425ffbb740
address mobile device push notification delivery issue when user had > 1 registered device (#2421)
# What this PR does

Address issue where if the user had multiple registered devices w/ FCM,
doing django queries like `.first()` could potentially pick the wrong
device. Do this in two ways:
1. set the `DELETE_INACTIVE_DEVICES` `fcm_django` setting to `True`.
According to the
[docs](20e275618b/README.rst (L127-L130)),
this works as follows:

> devices to which notifications cannot be sent, are deleted upon
receiving error response from FCM
2. Customizing the `FCMDevice` model provided by `fcm_django`. Add a new
method, `get_active_device_for_user`, so that we can centralize the
logic for this rather than duplicating
`FCMDevice.objects.filter(user=user).first()`

## Which issue(s) this PR fixes

https://raintank-corp.slack.com/archives/C0229FD3CE9/p1688461915752119

## Checklist

- [x] Unit, integration, and e2e (if applicable) tests updated
- [ ] Documentation added (or `pr:no public docs` PR label added if not
required) (N/A)
- [x] `CHANGELOG.md` updated (or `pr:no changelog` PR label added if not
required)
2023-07-05 15:14:46 +00:00

75 lines
2.5 KiB
Python

import json
from django.conf import settings
from apps.base.messaging import BaseMessagingBackend
from apps.mobile_app.tasks import notify_user_async
class MobileAppBackend(BaseMessagingBackend):
backend_id = "MOBILE_APP"
label = "Mobile push"
short_label = "Mobile push"
available_for_use = True
template_fields = ["title"]
def generate_user_verification_code(self, user):
from apps.mobile_app.models import MobileAppVerificationToken
# remove existing token before creating a new one
MobileAppVerificationToken.objects.filter(user=user).delete()
_, token = MobileAppVerificationToken.create_auth_token(user, user.organization)
return json.dumps(
{
"token": token,
"oncall_api_url": settings.BASE_URL,
}
)
def unlink_user(self, user):
from apps.mobile_app.models import FCMDevice, MobileAppAuthToken
token = MobileAppAuthToken.objects.get(user=user)
token.delete()
# delete push notification related info for user
user_active_device = FCMDevice.get_active_device_for_user(user)
if user_active_device is not None:
user_active_device.delete()
def serialize_user(self, user):
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(
user_pk=user.pk,
alert_group_pk=alert_group.pk,
notification_policy_pk=notification_policy.pk,
critical=critical,
)
@property
def customizable_templates(self):
"""
Disable customization if templates for mobile app
"""
return False
class MobileAppCriticalBackend(MobileAppBackend):
"""
This notification backend should not exist, criticality of the push notification should be an option passed to the
MobileAppBackend messaging backend.
TODO: add ability to pass options to messaging backends both on backend and frontend, delete this backend after that
"""
backend_id = "MOBILE_APP_CRITICAL"
label = "Mobile push important"
short_label = "Mobile push important"
template_fields = []
def notify_user(self, user, alert_group, notification_policy, critical=True):
super().notify_user(user, alert_group, notification_policy, critical)