From 4262c138019a0032e5b51ee01e61d4e9c5d09d83 Mon Sep 17 00:00:00 2001
From: Julian Leucker <leuckerj@gmail.com>
Date: Mon, 3 Apr 2023 15:44:23 +0200
Subject: [PATCH] Create CRU queries and mutations for Room

---
 aleksis/core/schema/__init__.py | 23 +++++++++++++++++-
 aleksis/core/schema/base.py     | 41 +++++++++++++++++++++++++++++++++
 aleksis/core/schema/room.py     | 16 ++++++++++++-
 pyproject.toml                  |  1 +
 4 files changed, 79 insertions(+), 2 deletions(-)

diff --git a/aleksis/core/schema/__init__.py b/aleksis/core/schema/__init__.py
index deab0e28b..901c4d8a3 100644
--- a/aleksis/core/schema/__init__.py
+++ b/aleksis/core/schema/__init__.py
@@ -9,7 +9,7 @@ from haystack.inputs import AutoQuery
 from haystack.query import SearchQuerySet
 from haystack.utils.loading import UnifiedIndex
 
-from ..models import CustomMenu, DynamicRoute, Notification, PDFFile, Person, TaskUserAssignment
+from ..models import CustomMenu, DynamicRoute, Notification, PDFFile, Person, TaskUserAssignment, Room
 from ..util.apps import AppConfig
 from ..util.core_helpers import get_allowed_object_ids, get_app_module, get_app_packages, has_person
 from .celery_progress import CeleryProgressFetchedMutation, CeleryProgressType
@@ -21,6 +21,7 @@ from .message import MessageType
 from .notification import MarkNotificationReadMutation, NotificationType
 from .pdf import PDFFileType
 from .person import PersonMutation, PersonType
+from .room import RoomType, RoomBatchPatchMutation, RoomCreateMutation
 from .school_term import SchoolTermType  # noqa
 from .search import SearchResultType
 from .system_properties import SystemPropertiesType
@@ -59,6 +60,9 @@ class Query(graphene.ObjectType):
 
     two_factor = graphene.Field(TwoFactorType)
 
+    rooms = graphene.List(RoomType)
+    room_by_id = graphene.Field(RoomType, id=graphene.ID())
+
     def resolve_ping(root, info, payload) -> str:
         return payload
 
@@ -157,6 +161,20 @@ class Query(graphene.ObjectType):
             return None
         return info.context.user
 
+    @staticmethod
+    def resolve_rooms(root, info, **kwargs):
+        return Room.objects.all()  # FIXME permissions
+
+    @staticmethod
+    def resolve_room_by_id(root, info, **kwargs):
+        pk = kwargs.get("id")
+        room_object = Room.objects.get(pk=pk)
+
+        if not info.context.user.has_perm("core.view_room_rule"):
+            raise PermissionDenied
+
+        return room_object
+
 
 class Mutation(graphene.ObjectType):
     update_person = PersonMutation.Field()
@@ -165,6 +183,9 @@ class Mutation(graphene.ObjectType):
 
     celery_progress_fetched = CeleryProgressFetchedMutation.Field()
 
+    create_room = RoomCreateMutation.Field()
+    update_rooms = RoomBatchPatchMutation.Field()
+
 
 def build_global_schema():
     """Build global GraphQL schema from all apps."""
diff --git a/aleksis/core/schema/base.py b/aleksis/core/schema/base.py
index e927dadcc..f100f562b 100644
--- a/aleksis/core/schema/base.py
+++ b/aleksis/core/schema/base.py
@@ -1,6 +1,9 @@
 import graphene
 from graphene_django import DjangoObjectType
 
+from django.db.models import Model
+from django.contrib.contenttypes.models import ContentType
+
 from ..util.core_helpers import queryset_rules_filter
 
 
@@ -24,3 +27,41 @@ class FieldFileType(graphene.ObjectType):
 
     def resolve_absolute_url(root, info, **kwargs):
         return info.context.build_absolute_uri(root.url) if root else ""
+
+
+class PermissionsTypeMixin:
+    """Mixin for adding permissions to a Graphene type.
+
+    To configure the names for the permissions or to do
+    different permission checking, override the respective
+    methods `resolve_can_edit` and `resolve_can_delete`
+    """
+
+    can_edit = graphene.Boolean()
+    can_delete = graphene.Boolean()
+
+    @staticmethod
+    def resolve_can_edit(root: Model, info, **kwargs):
+        content_type = ContentType.objects.get_for_model(root)
+        perm = f"{content_type.app_label}.edit_{content_type.model}_rule"
+        return info.context.user.has_perm(perm, root)
+
+    @staticmethod
+    def resolve_can_delete(root: Model, info, **kwargs):
+        content_type = ContentType.objects.get_for_model(root)
+        perm = f"{content_type.app_label}.delete_{content_type.model}_rule"
+        return info.context.user.has_perm(perm, root)
+
+
+class PermissionBatchPatchMixin:
+    class Meta:
+        login_required = True
+
+    @classmethod
+    def check_permissions(cls, root, info, input):
+        # TODO: Check PERMISSIONS (or rules)
+        return True
+
+
+
+
diff --git a/aleksis/core/schema/room.py b/aleksis/core/schema/room.py
index d4f767007..d4d5c1480 100644
--- a/aleksis/core/schema/room.py
+++ b/aleksis/core/schema/room.py
@@ -1,9 +1,23 @@
 from graphene_django import DjangoObjectType
+from graphene_django_cud.mutations import DjangoCreateMutation, DjangoBatchPatchMutation
 
+from .base import PermissionsTypeMixin, PermissionBatchPatchMixin
 from ..models import Room
 
 
-class RoomType(DjangoObjectType):
+class RoomType(PermissionsTypeMixin, DjangoObjectType):
     class Meta:
         model = Room
         fields = ("id", "name", "short_name")
+
+
+class RoomCreateMutation(DjangoCreateMutation):
+    class Meta:
+        model = Room
+        permissions = "core.create_room"
+
+
+class RoomBatchPatchMutation(PermissionBatchPatchMixin, DjangoBatchPatchMutation):
+    class Meta:
+        model = Room
+        permissions = "core.change_room"
diff --git a/pyproject.toml b/pyproject.toml
index 9a3318eae..3c2d76502 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -127,6 +127,7 @@ customidenticon = "^0.1.5"
 graphene-django = "^3.0.0"
 selenium = "^4.4.3"
 django-vite = "^2.0.2"
+graphene-django-cud = "^0.10.0"
 
 [tool.poetry.extras]
 ldap = ["django-auth-ldap"]
-- 
GitLab