Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add BannerNotification mutations #919

Merged
merged 9 commits into from
Aug 31, 2023
6 changes: 3 additions & 3 deletions api/graphql/banner_notification/filtersets.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@

class BannerNotificationOrderingFilter(CustomOrderingFilter):
@staticmethod
def order_by_type(qs: BannerNotificationQuerySet, desc: bool) -> BannerNotificationQuerySet:
return qs.order_by_type(desc)
def order_by_level(qs: BannerNotificationQuerySet, desc: bool) -> BannerNotificationQuerySet:
return qs.order_by_level(desc)

@staticmethod
def order_by_target(qs: BannerNotificationQuerySet, desc: bool) -> BannerNotificationQuerySet:
Expand All @@ -29,7 +29,7 @@ class BannerNotificationFilterSet(django_filters.FilterSet):
("active_until", "ends"),
),
custom_fields=(
"type",
"level",
"state",
"target",
),
Expand Down
34 changes: 34 additions & 0 deletions api/graphql/banner_notification/mutations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
from api.graphql.base_mutations import CreateAuthMutation, DeleteAuthMutation, UpdateAuthMutation
from common.models import BannerNotification

from .permissions import BannerNotificationPermission
from .serializers import BannerNotificationSerializer

__all__ = [
"BannerNotificationCreateMutation",
"BannerNotificationUpdateMutation",
"BannerNotificationDeleteMutation",
]


class BannerNotificationCreateMutation(CreateAuthMutation):
permission_classes = (BannerNotificationPermission,)

class Meta:
serializer_class = BannerNotificationSerializer


class BannerNotificationUpdateMutation(UpdateAuthMutation):
permission_classes = (BannerNotificationPermission,)

class Meta:
serializer_class = BannerNotificationSerializer
lookup_field = "pk"


class BannerNotificationDeleteMutation(DeleteAuthMutation):
permission_classes = (BannerNotificationPermission,)

class Meta:
model = BannerNotification
lookup_field = "pk"
12 changes: 10 additions & 2 deletions api/graphql/banner_notification/permissions.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
from typing import Any

from graphene_permissions.permissions import BasePermission
from graphql import GraphQLResolveInfo

from common.typing import GQLInfo
from permissions.helpers import can_manage_banner_notifications


class BannerNotificationPermission(BasePermission):
@classmethod
def has_permission(cls, info: GraphQLResolveInfo) -> bool:
def has_permission(cls, info: GQLInfo) -> bool:
return True

@classmethod
def has_mutation_permission(cls, root: Any, info: GQLInfo, input: dict) -> bool:
return can_manage_banner_notifications(info.context.user)
57 changes: 57 additions & 0 deletions api/graphql/banner_notification/serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
from collections import defaultdict
from datetime import datetime
from types import NoneType
from typing import Any, Optional

from rest_framework import serializers

from common.models import BannerNotification
from common.serializers import TranslatedModelSerializer

__all__ = [
"BannerNotificationSerializer",
]


class BannerNotificationSerializer(TranslatedModelSerializer):
class Meta:
model = BannerNotification
fields = [
"pk",
"name",
"message",
"draft",
"level",
"target",
"active_from",
"active_until",
]

def validate(self, attrs: dict[str, Any]) -> dict[str, Any]:
errors: dict[str, list[str]] = defaultdict(list)

active_from: Optional[datetime] = self.get_or_default("active_from", attrs)
active_until: Optional[datetime] = self.get_or_default("active_until", attrs)
draft: bool = self.get_or_default("draft", attrs)
message: str = self.get_or_default("message", attrs)

if not isinstance(active_from, type(active_until)) or not isinstance(active_from, (datetime, NoneType)):
msg = "Both 'active_from' and 'active_until' must be either set or null."
errors["active_until"].append(msg)
errors["active_from"].append(msg)

if active_from and active_until and active_from >= active_until:
errors["active_from"].append("'active_from' must be before 'active_until'.")

if not draft:
if not active_from:
errors["active_from"].append("Non-draft notifications must set 'active_from'")
if not active_until:
errors["active_until"].append("Non-draft notifications must set 'active_until'")
if not message:
errors["message"].append("Non-draft notifications must have a message.")

if errors:
raise serializers.ValidationError(errors)

return attrs
2 changes: 1 addition & 1 deletion api/graphql/banner_notification/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class Meta:
"message_en",
"message_sv",
"draft",
"type",
"level",
"target",
"active_from",
"active_until",
Expand Down
Loading