oncall-engine/engine/apps/public_api/serializers/personal_notification_rules.py
Michael Derynck 6b40f95033 World, meet OnCall!
Co-authored-by: Eve832 <eve.meelan@grafana.com>
    Co-authored-by: Francisco Montes de Oca <nevermind89x@gmail.com>
    Co-authored-by: Ildar Iskhakov <ildar.iskhakov@grafana.com>
    Co-authored-by: Innokentii Konstantinov <innokenty.konstantinov@grafana.com>
    Co-authored-by: Julia <ferril.darkdiver@gmail.com>
    Co-authored-by: maskin25 <kengurek@gmail.com>
    Co-authored-by: Matias Bordese <mbordese@gmail.com>
    Co-authored-by: Matvey Kukuy <motakuk@gmail.com>
    Co-authored-by: Michael Derynck <michael.derynck@grafana.com>
    Co-authored-by: Richard Hartmann <richih@richih.org>
    Co-authored-by: Robby Milo <robbymilo@fastmail.com>
    Co-authored-by: Timur Olzhabayev <timur.olzhabayev@grafana.com>
    Co-authored-by: Vadim Stepanov <vadimkerr@gmail.com>
    Co-authored-by: Yulia Shanyrova <yulia.shanyrova@grafana.com>
2022-06-03 08:09:47 -06:00

155 lines
6.4 KiB
Python

import time
from datetime import timedelta
from rest_framework import exceptions, serializers
from apps.base.models import UserNotificationPolicy
from apps.base.models.user_notification_policy import NotificationChannelPublicAPIOptions
from common.api_helpers.custom_fields import UserIdField
from common.api_helpers.exceptions import BadRequest
from common.api_helpers.mixins import EagerLoadingMixin
class PersonalNotificationRuleSerializer(EagerLoadingMixin, serializers.ModelSerializer):
id = serializers.CharField(read_only=True, source="public_primary_key")
user_id = UserIdField(required=True, source="user")
position = serializers.IntegerField(required=False, source="order")
type = serializers.CharField(
required=False,
)
duration = serializers.ChoiceField(
required=False, source="wait_delay", choices=UserNotificationPolicy.DURATION_CHOICES
)
manual_order = serializers.BooleanField(default=False, write_only=True)
SELECT_RELATED = ["user"]
# Public API has fields "step" and "notify_by" combined into one step "type"
# Step.NOTIFY is handled using NotificationChannelPublicAPIOptions class, but Step.WAIT is handled differently.
TYPE_WAIT = "wait"
class Meta:
model = UserNotificationPolicy
fields = ["id", "user_id", "position", "type", "duration", "manual_order", "important"]
def create(self, validated_data):
if "type" not in validated_data:
raise exceptions.ValidationError({"type": "Type is required"})
validated_data = self.correct_validated_data(validated_data)
# type is alias for combined step + notify_by field in serializer
# correct_validated_data parse type to step + notify_by
# that is why step key is used instead of type below
if "wait_delay" in validated_data and validated_data["step"] != UserNotificationPolicy.Step.WAIT:
raise exceptions.ValidationError({"duration": "Duration can't be set"})
user = validated_data.pop("user")
manual_order = validated_data.pop("manual_order")
if not manual_order:
order = validated_data.pop("order", None)
instance = UserNotificationPolicy.objects.create(**validated_data, user=user)
self._change_position(order, instance)
else:
instance = UserNotificationPolicy.objects.create(**validated_data, user=user)
return instance
def to_internal_value(self, data):
if "duration" in data:
try:
time.strptime(data["duration"], "%H:%M:%S")
except (ValueError, TypeError):
try:
data["duration"] = str(timedelta(seconds=data["duration"]))
except (ValueError, TypeError):
raise BadRequest(detail="Invalid duration format")
return super().to_internal_value(data)
def to_representation(self, instance):
step = instance.step
result = super().to_representation(instance)
if instance.step == UserNotificationPolicy.Step.WAIT:
result["type"] = self.TYPE_WAIT
else:
result["type"] = NotificationChannelPublicAPIOptions.LABELS[instance.notify_by]
result = self.clear_fields(step, result)
if "duration" in result and result["duration"] is not None:
result["duration"] = result["duration"].seconds
return result
# remove duration from response if step is not wait
def clear_fields(self, step, result):
possible_fields = ["duration"]
if step == UserNotificationPolicy.Step.WAIT:
possible_fields.remove("duration")
for field in possible_fields:
result.pop(field, None)
return result
def correct_validated_data(self, validated_data):
rule_type = validated_data.get("type")
step, notification_channel = self._type_to_step_and_notification_channel(rule_type)
validated_data["step"] = step
if step == UserNotificationPolicy.Step.NOTIFY:
validated_data["notify_by"] = notification_channel
if step == UserNotificationPolicy.Step.WAIT and "wait_delay" not in validated_data:
validated_data["wait_delay"] = UserNotificationPolicy.FIVE_MINUTES
validated_data.pop("type")
return validated_data
@classmethod
def _type_to_step_and_notification_channel(cls, rule_type):
if rule_type == cls.TYPE_WAIT:
return UserNotificationPolicy.Step.WAIT, None
for notification_channel in NotificationChannelPublicAPIOptions.AVAILABLE_FOR_USE:
label = NotificationChannelPublicAPIOptions.LABELS[notification_channel]
if rule_type == label:
return UserNotificationPolicy.Step.NOTIFY, notification_channel
raise exceptions.ValidationError({"type": "Invalid type"})
def _change_position(self, order, instance):
if order is not None:
if order >= 0:
instance.to(order)
elif order == -1:
instance.bottom()
else:
raise BadRequest(detail="Invalid value for position field")
class PersonalNotificationRuleUpdateSerializer(PersonalNotificationRuleSerializer):
user_id = UserIdField(read_only=True, source="user")
important = serializers.BooleanField(read_only=True)
def update(self, instance, validated_data):
if validated_data.get("type", None):
validated_data = self.correct_validated_data(validated_data)
# type is alias for combined step + notify_by field in serializer
# correct_validated_data parse type to step + notify_by
# that is why step key is used instead of type below
if "wait_delay" in validated_data and validated_data["step"] != UserNotificationPolicy.Step.WAIT:
raise exceptions.ValidationError({"duration": "Duration can't be set"})
if validated_data["step"] != UserNotificationPolicy.Step.WAIT:
validated_data["wait_delay"] = None
else:
if "wait_delay" in validated_data and instance.step != UserNotificationPolicy.Step.WAIT:
raise exceptions.ValidationError({"duration": "Duration can't be set"})
manual_order = validated_data.pop("manual_order")
if not manual_order:
order = validated_data.pop("order", None)
self._change_position(order, instance)
return super().update(instance, validated_data)