oncall-engine/engine/apps/mobile_app/serializers.py

83 lines
3.4 KiB
Python
Raw Permalink Normal View History

import typing
from fcm_django.api.rest_framework import FCMDeviceSerializer as BaseFCMDeviceSerializer
Mobile app settings backend (#1571) # What this PR does Adds mobile app settings support to OnCall backend. - Adds a new Django model `MobileAppUserSettings` to store push notification settings - Adds a new endpoint `/mobile_app/v1/user_settings` to fetch/update settings from the mobile app Some additional info on implementation: at first I wanted to extend the messaging backend system to allow storing / retrieving per-user data and implement mobile app settings based on those changes. After some thought I decided not to extend the messaging backend system and have this as functionality specific to the `mobile_app` Django app. Currently the messaging backend system is used by the backend and plugin UI, but mobile app settings are specific only to the mobile app and not configurable in the plugin UI. **tldr: wanted to extend messaging backend system, but decided not to do that** # Usage ## Get settings via API `GET /mobile_app/v1/user_settings` Example response: ```json { "default_notification_sound_name": "default_sound", # sound name without file extension "default_notification_volume_type": "constant", "default_notification_volume": 0.8, "default_notification_volume_override": false, "important_notification_sound_name": "default_sound_important", # sound name without file extension "important_notification_volume_type": "constant", "important_notification_volume": 0.8, "important_notification_override_dnd": true } ``` ## Update settings via API `PUT /mobile_app/v1/user_settings` - see example response above for payload shape. Note that sound names must be passed without file extension. When sending push notifications, the backend will add `.mp3` to sound names and pass it to push notification data for Android. For iOS, sound names will be suffixed with `.aiff` to be used by APNS. ## Get settings from notification data for Android All the settings from example response will be available in push notification data (along with `orgId`, `alertGroupId`, `title`, etc.). Fields `default_notification_volume`, `default_notification_volume_override` and `important_notification_volume` , `important_notification_override_dnd` will be converted to strings due to FCM limitations. Fields `default_notification_sound_name` and `important_notification_sound_name` will be suffixed with `.mp3` in push notification data. ## iOS limitations While Android push notifications are handled purely on the mobile app side, iOS notifications are sent via APNS which imposes some limitations. - Notification volume cannot be overridden for non-critical notifications (so fields `default_notification_volume_override` and `default_notification_volume` will be disregarded for iOS notifications) - It's not possible to control volume type (i.e. "constant" vs "intensifying") via APNS. A possible workaround is to have different sound files for "constant" and "intensifying" and pass that as `default_notification_sound_name` / `important_notification_sound_name`. # Which issue(s) this PR fixes Related to https://github.com/grafana/oncall-private/issues/1602 # Checklist - [x] Tests updated - [x] No changelog updates since the changes are not user-facing
2023-03-22 14:47:18 +00:00
from rest_framework import serializers
from rest_framework.serializers import ValidationError
Mobile app settings backend (#1571) # What this PR does Adds mobile app settings support to OnCall backend. - Adds a new Django model `MobileAppUserSettings` to store push notification settings - Adds a new endpoint `/mobile_app/v1/user_settings` to fetch/update settings from the mobile app Some additional info on implementation: at first I wanted to extend the messaging backend system to allow storing / retrieving per-user data and implement mobile app settings based on those changes. After some thought I decided not to extend the messaging backend system and have this as functionality specific to the `mobile_app` Django app. Currently the messaging backend system is used by the backend and plugin UI, but mobile app settings are specific only to the mobile app and not configurable in the plugin UI. **tldr: wanted to extend messaging backend system, but decided not to do that** # Usage ## Get settings via API `GET /mobile_app/v1/user_settings` Example response: ```json { "default_notification_sound_name": "default_sound", # sound name without file extension "default_notification_volume_type": "constant", "default_notification_volume": 0.8, "default_notification_volume_override": false, "important_notification_sound_name": "default_sound_important", # sound name without file extension "important_notification_volume_type": "constant", "important_notification_volume": 0.8, "important_notification_override_dnd": true } ``` ## Update settings via API `PUT /mobile_app/v1/user_settings` - see example response above for payload shape. Note that sound names must be passed without file extension. When sending push notifications, the backend will add `.mp3` to sound names and pass it to push notification data for Android. For iOS, sound names will be suffixed with `.aiff` to be used by APNS. ## Get settings from notification data for Android All the settings from example response will be available in push notification data (along with `orgId`, `alertGroupId`, `title`, etc.). Fields `default_notification_volume`, `default_notification_volume_override` and `important_notification_volume` , `important_notification_override_dnd` will be converted to strings due to FCM limitations. Fields `default_notification_sound_name` and `important_notification_sound_name` will be suffixed with `.mp3` in push notification data. ## iOS limitations While Android push notifications are handled purely on the mobile app side, iOS notifications are sent via APNS which imposes some limitations. - Notification volume cannot be overridden for non-critical notifications (so fields `default_notification_volume_override` and `default_notification_volume` will be disregarded for iOS notifications) - It's not possible to control volume type (i.e. "constant" vs "intensifying") via APNS. A possible workaround is to have different sound files for "constant" and "intensifying" and pass that as `default_notification_sound_name` / `important_notification_sound_name`. # Which issue(s) this PR fixes Related to https://github.com/grafana/oncall-private/issues/1602 # Checklist - [x] Tests updated - [x] No changelog updates since the changes are not user-facing
2023-03-22 14:47:18 +00:00
from apps.mobile_app.models import MobileAppUserSettings
from common.api_helpers.custom_fields import TimeZoneField
Mobile app settings backend (#1571) # What this PR does Adds mobile app settings support to OnCall backend. - Adds a new Django model `MobileAppUserSettings` to store push notification settings - Adds a new endpoint `/mobile_app/v1/user_settings` to fetch/update settings from the mobile app Some additional info on implementation: at first I wanted to extend the messaging backend system to allow storing / retrieving per-user data and implement mobile app settings based on those changes. After some thought I decided not to extend the messaging backend system and have this as functionality specific to the `mobile_app` Django app. Currently the messaging backend system is used by the backend and plugin UI, but mobile app settings are specific only to the mobile app and not configurable in the plugin UI. **tldr: wanted to extend messaging backend system, but decided not to do that** # Usage ## Get settings via API `GET /mobile_app/v1/user_settings` Example response: ```json { "default_notification_sound_name": "default_sound", # sound name without file extension "default_notification_volume_type": "constant", "default_notification_volume": 0.8, "default_notification_volume_override": false, "important_notification_sound_name": "default_sound_important", # sound name without file extension "important_notification_volume_type": "constant", "important_notification_volume": 0.8, "important_notification_override_dnd": true } ``` ## Update settings via API `PUT /mobile_app/v1/user_settings` - see example response above for payload shape. Note that sound names must be passed without file extension. When sending push notifications, the backend will add `.mp3` to sound names and pass it to push notification data for Android. For iOS, sound names will be suffixed with `.aiff` to be used by APNS. ## Get settings from notification data for Android All the settings from example response will be available in push notification data (along with `orgId`, `alertGroupId`, `title`, etc.). Fields `default_notification_volume`, `default_notification_volume_override` and `important_notification_volume` , `important_notification_override_dnd` will be converted to strings due to FCM limitations. Fields `default_notification_sound_name` and `important_notification_sound_name` will be suffixed with `.mp3` in push notification data. ## iOS limitations While Android push notifications are handled purely on the mobile app side, iOS notifications are sent via APNS which imposes some limitations. - Notification volume cannot be overridden for non-critical notifications (so fields `default_notification_volume_override` and `default_notification_volume` will be disregarded for iOS notifications) - It's not possible to control volume type (i.e. "constant" vs "intensifying") via APNS. A possible workaround is to have different sound files for "constant" and "intensifying" and pass that as `default_notification_sound_name` / `important_notification_sound_name`. # Which issue(s) this PR fixes Related to https://github.com/grafana/oncall-private/issues/1602 # Checklist - [x] Tests updated - [x] No changelog updates since the changes are not user-facing
2023-03-22 14:47:18 +00:00
class MobileAppUserSettingsSerializer(serializers.ModelSerializer):
time_zone = TimeZoneField(required=False, allow_null=False)
going_oncall_notification_timing = serializers.ListField(required=False, allow_null=False)
Mobile app settings backend (#1571) # What this PR does Adds mobile app settings support to OnCall backend. - Adds a new Django model `MobileAppUserSettings` to store push notification settings - Adds a new endpoint `/mobile_app/v1/user_settings` to fetch/update settings from the mobile app Some additional info on implementation: at first I wanted to extend the messaging backend system to allow storing / retrieving per-user data and implement mobile app settings based on those changes. After some thought I decided not to extend the messaging backend system and have this as functionality specific to the `mobile_app` Django app. Currently the messaging backend system is used by the backend and plugin UI, but mobile app settings are specific only to the mobile app and not configurable in the plugin UI. **tldr: wanted to extend messaging backend system, but decided not to do that** # Usage ## Get settings via API `GET /mobile_app/v1/user_settings` Example response: ```json { "default_notification_sound_name": "default_sound", # sound name without file extension "default_notification_volume_type": "constant", "default_notification_volume": 0.8, "default_notification_volume_override": false, "important_notification_sound_name": "default_sound_important", # sound name without file extension "important_notification_volume_type": "constant", "important_notification_volume": 0.8, "important_notification_override_dnd": true } ``` ## Update settings via API `PUT /mobile_app/v1/user_settings` - see example response above for payload shape. Note that sound names must be passed without file extension. When sending push notifications, the backend will add `.mp3` to sound names and pass it to push notification data for Android. For iOS, sound names will be suffixed with `.aiff` to be used by APNS. ## Get settings from notification data for Android All the settings from example response will be available in push notification data (along with `orgId`, `alertGroupId`, `title`, etc.). Fields `default_notification_volume`, `default_notification_volume_override` and `important_notification_volume` , `important_notification_override_dnd` will be converted to strings due to FCM limitations. Fields `default_notification_sound_name` and `important_notification_sound_name` will be suffixed with `.mp3` in push notification data. ## iOS limitations While Android push notifications are handled purely on the mobile app side, iOS notifications are sent via APNS which imposes some limitations. - Notification volume cannot be overridden for non-critical notifications (so fields `default_notification_volume_override` and `default_notification_volume` will be disregarded for iOS notifications) - It's not possible to control volume type (i.e. "constant" vs "intensifying") via APNS. A possible workaround is to have different sound files for "constant" and "intensifying" and pass that as `default_notification_sound_name` / `important_notification_sound_name`. # Which issue(s) this PR fixes Related to https://github.com/grafana/oncall-private/issues/1602 # Checklist - [x] Tests updated - [x] No changelog updates since the changes are not user-facing
2023-03-22 14:47:18 +00:00
class Meta:
model = MobileAppUserSettings
fields = (
"info_notification_sound_name",
"info_notification_volume_type",
"info_notification_volume",
"info_notification_volume_override",
Mobile app settings backend (#1571) # What this PR does Adds mobile app settings support to OnCall backend. - Adds a new Django model `MobileAppUserSettings` to store push notification settings - Adds a new endpoint `/mobile_app/v1/user_settings` to fetch/update settings from the mobile app Some additional info on implementation: at first I wanted to extend the messaging backend system to allow storing / retrieving per-user data and implement mobile app settings based on those changes. After some thought I decided not to extend the messaging backend system and have this as functionality specific to the `mobile_app` Django app. Currently the messaging backend system is used by the backend and plugin UI, but mobile app settings are specific only to the mobile app and not configurable in the plugin UI. **tldr: wanted to extend messaging backend system, but decided not to do that** # Usage ## Get settings via API `GET /mobile_app/v1/user_settings` Example response: ```json { "default_notification_sound_name": "default_sound", # sound name without file extension "default_notification_volume_type": "constant", "default_notification_volume": 0.8, "default_notification_volume_override": false, "important_notification_sound_name": "default_sound_important", # sound name without file extension "important_notification_volume_type": "constant", "important_notification_volume": 0.8, "important_notification_override_dnd": true } ``` ## Update settings via API `PUT /mobile_app/v1/user_settings` - see example response above for payload shape. Note that sound names must be passed without file extension. When sending push notifications, the backend will add `.mp3` to sound names and pass it to push notification data for Android. For iOS, sound names will be suffixed with `.aiff` to be used by APNS. ## Get settings from notification data for Android All the settings from example response will be available in push notification data (along with `orgId`, `alertGroupId`, `title`, etc.). Fields `default_notification_volume`, `default_notification_volume_override` and `important_notification_volume` , `important_notification_override_dnd` will be converted to strings due to FCM limitations. Fields `default_notification_sound_name` and `important_notification_sound_name` will be suffixed with `.mp3` in push notification data. ## iOS limitations While Android push notifications are handled purely on the mobile app side, iOS notifications are sent via APNS which imposes some limitations. - Notification volume cannot be overridden for non-critical notifications (so fields `default_notification_volume_override` and `default_notification_volume` will be disregarded for iOS notifications) - It's not possible to control volume type (i.e. "constant" vs "intensifying") via APNS. A possible workaround is to have different sound files for "constant" and "intensifying" and pass that as `default_notification_sound_name` / `important_notification_sound_name`. # Which issue(s) this PR fixes Related to https://github.com/grafana/oncall-private/issues/1602 # Checklist - [x] Tests updated - [x] No changelog updates since the changes are not user-facing
2023-03-22 14:47:18 +00:00
"default_notification_sound_name",
"default_notification_volume_type",
"default_notification_volume",
"default_notification_volume_override",
"important_notification_sound_name",
"important_notification_volume_type",
"important_notification_volume",
"important_notification_volume_override",
Mobile app settings backend (#1571) # What this PR does Adds mobile app settings support to OnCall backend. - Adds a new Django model `MobileAppUserSettings` to store push notification settings - Adds a new endpoint `/mobile_app/v1/user_settings` to fetch/update settings from the mobile app Some additional info on implementation: at first I wanted to extend the messaging backend system to allow storing / retrieving per-user data and implement mobile app settings based on those changes. After some thought I decided not to extend the messaging backend system and have this as functionality specific to the `mobile_app` Django app. Currently the messaging backend system is used by the backend and plugin UI, but mobile app settings are specific only to the mobile app and not configurable in the plugin UI. **tldr: wanted to extend messaging backend system, but decided not to do that** # Usage ## Get settings via API `GET /mobile_app/v1/user_settings` Example response: ```json { "default_notification_sound_name": "default_sound", # sound name without file extension "default_notification_volume_type": "constant", "default_notification_volume": 0.8, "default_notification_volume_override": false, "important_notification_sound_name": "default_sound_important", # sound name without file extension "important_notification_volume_type": "constant", "important_notification_volume": 0.8, "important_notification_override_dnd": true } ``` ## Update settings via API `PUT /mobile_app/v1/user_settings` - see example response above for payload shape. Note that sound names must be passed without file extension. When sending push notifications, the backend will add `.mp3` to sound names and pass it to push notification data for Android. For iOS, sound names will be suffixed with `.aiff` to be used by APNS. ## Get settings from notification data for Android All the settings from example response will be available in push notification data (along with `orgId`, `alertGroupId`, `title`, etc.). Fields `default_notification_volume`, `default_notification_volume_override` and `important_notification_volume` , `important_notification_override_dnd` will be converted to strings due to FCM limitations. Fields `default_notification_sound_name` and `important_notification_sound_name` will be suffixed with `.mp3` in push notification data. ## iOS limitations While Android push notifications are handled purely on the mobile app side, iOS notifications are sent via APNS which imposes some limitations. - Notification volume cannot be overridden for non-critical notifications (so fields `default_notification_volume_override` and `default_notification_volume` will be disregarded for iOS notifications) - It's not possible to control volume type (i.e. "constant" vs "intensifying") via APNS. A possible workaround is to have different sound files for "constant" and "intensifying" and pass that as `default_notification_sound_name` / `important_notification_sound_name`. # Which issue(s) this PR fixes Related to https://github.com/grafana/oncall-private/issues/1602 # Checklist - [x] Tests updated - [x] No changelog updates since the changes are not user-facing
2023-03-22 14:47:18 +00:00
"important_notification_override_dnd",
"info_notifications_enabled",
"going_oncall_notification_timing",
"locale",
"time_zone",
Mobile app settings backend (#1571) # What this PR does Adds mobile app settings support to OnCall backend. - Adds a new Django model `MobileAppUserSettings` to store push notification settings - Adds a new endpoint `/mobile_app/v1/user_settings` to fetch/update settings from the mobile app Some additional info on implementation: at first I wanted to extend the messaging backend system to allow storing / retrieving per-user data and implement mobile app settings based on those changes. After some thought I decided not to extend the messaging backend system and have this as functionality specific to the `mobile_app` Django app. Currently the messaging backend system is used by the backend and plugin UI, but mobile app settings are specific only to the mobile app and not configurable in the plugin UI. **tldr: wanted to extend messaging backend system, but decided not to do that** # Usage ## Get settings via API `GET /mobile_app/v1/user_settings` Example response: ```json { "default_notification_sound_name": "default_sound", # sound name without file extension "default_notification_volume_type": "constant", "default_notification_volume": 0.8, "default_notification_volume_override": false, "important_notification_sound_name": "default_sound_important", # sound name without file extension "important_notification_volume_type": "constant", "important_notification_volume": 0.8, "important_notification_override_dnd": true } ``` ## Update settings via API `PUT /mobile_app/v1/user_settings` - see example response above for payload shape. Note that sound names must be passed without file extension. When sending push notifications, the backend will add `.mp3` to sound names and pass it to push notification data for Android. For iOS, sound names will be suffixed with `.aiff` to be used by APNS. ## Get settings from notification data for Android All the settings from example response will be available in push notification data (along with `orgId`, `alertGroupId`, `title`, etc.). Fields `default_notification_volume`, `default_notification_volume_override` and `important_notification_volume` , `important_notification_override_dnd` will be converted to strings due to FCM limitations. Fields `default_notification_sound_name` and `important_notification_sound_name` will be suffixed with `.mp3` in push notification data. ## iOS limitations While Android push notifications are handled purely on the mobile app side, iOS notifications are sent via APNS which imposes some limitations. - Notification volume cannot be overridden for non-critical notifications (so fields `default_notification_volume_override` and `default_notification_volume` will be disregarded for iOS notifications) - It's not possible to control volume type (i.e. "constant" vs "intensifying") via APNS. A possible workaround is to have different sound files for "constant" and "intensifying" and pass that as `default_notification_sound_name` / `important_notification_sound_name`. # Which issue(s) this PR fixes Related to https://github.com/grafana/oncall-private/issues/1602 # Checklist - [x] Tests updated - [x] No changelog updates since the changes are not user-facing
2023-03-22 14:47:18 +00:00
)
def validate_going_oncall_notification_timing(
self, going_oncall_notification_timing: typing.Optional[typing.List[int]]
) -> typing.Optional[typing.List[int]]:
if going_oncall_notification_timing is not None:
if len(going_oncall_notification_timing) == 0:
raise serializers.ValidationError(detail="invalid timing options")
notification_timing_options = [opt[0] for opt in MobileAppUserSettings.NOTIFICATION_TIMING_CHOICES]
for option in going_oncall_notification_timing:
if option not in notification_timing_options:
raise serializers.ValidationError(detail="invalid timing options")
return going_oncall_notification_timing
class FCMDeviceSerializer(BaseFCMDeviceSerializer):
def validate(self, attrs):
"""
Overrides `validate` method from BaseFCMDeviceSerializer to allow different users have same device
`registration_id` (multi-stack support).
Removed deactivating devices with the same `registration_id` during validation.
"""
devices = None
request_method = None
request = self.context["request"]
if self.initial_data.get("registration_id", None):
request_method = "update" if self.instance else "create"
else:
if request.method in ["PUT", "PATCH"]:
request_method = "update"
elif request.method == "POST":
request_method = "create"
Device = self.Meta.model
# unique together with registration_id and user
user = request.user
registration_id = attrs.get("registration_id")
if request_method == "update":
if registration_id:
devices = Device.objects.filter(registration_id=registration_id, user=user).exclude(id=self.instance.id)
elif request_method == "create":
devices = Device.objects.filter(user=user, registration_id=registration_id)
if devices:
raise ValidationError({"registration_id": "This field must be unique per us."})
return attrs