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 endpoints to viewsets #163

Merged
merged 17 commits into from
Apr 30, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions src/openforms/forms/api/permissions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from rest_framework.permissions import SAFE_METHODS, BasePermission


class IsStaffOrReadOnly(BasePermission):
"""
The request is a staff user, or is a read-only request.
"""

def has_permission(self, request, view):
return request.method in SAFE_METHODS or (
request.user and request.user.is_staff
)
25 changes: 20 additions & 5 deletions src/openforms/forms/api/serializers.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import json
from django.shortcuts import get_object_or_404

from rest_framework import serializers
from rest_framework_nested.relations import NestedHyperlinkedRelatedField
Expand Down Expand Up @@ -83,14 +83,29 @@ class Meta:
}


class FormStepSerializer(serializers.ModelSerializer):
index = serializers.IntegerField(source="order")
configuration = serializers.JSONField(source="form_definition.configuration")
class FormStepSerializer(serializers.HyperlinkedModelSerializer):
index = serializers.IntegerField(source="order", read_only=True)
configuration = serializers.JSONField(
source="form_definition.configuration", read_only=True
)

parent_lookup_kwargs = {
"form_uuid": "form__uuid",
}

class Meta:
model = FormStep
fields = ("index", "configuration")
fields = ("index", "configuration", "form_definition")

extra_kwargs = {
"form_definition": {
"view_name": "api:formdefinition-detail",
"lookup_field": "uuid",
},
}

def create(self, validated_data):
validated_data["form"] = get_object_or_404(
Form, uuid=self.context["view"].kwargs["form_uuid"]
)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the Form doesn't exist when trying to create a FormStep then this code would be reached since there didn't seem to be anywhere that validates a Form exists for a given uuid. This seemed the best place to raise a 404 in that case though I'm happy to hear other options. :)

Copy link
Member

@sergei-maertens sergei-maertens Apr 30, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, it seems to be a limitation of the nested viewset library used in this project. Perhaps we should replace it with the one used in Open Zaak, that one seems to be more feature complete. So we can keep this for now.

It's just very nasty in this particular case (not your fault) that the serializer has to be aware of the view's URL keywords, that's violating two abstraction levels.

return super().create(validated_data)
36 changes: 25 additions & 11 deletions src/openforms/forms/api/viewsets.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@

from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import OpenApiParameter, extend_schema, extend_schema_view
from rest_framework import permissions, status, viewsets
from rest_framework import mixins, permissions, status, viewsets
from rest_framework.decorators import action
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework_nested.viewsets import NestedViewSetMixin

from openforms.api.pagination import PageNumberPagination

from ..api.permissions import IsStaffOrReadOnly
from ..api.serializers import (
FormDefinitionSerializer,
FormSerializer,
Expand All @@ -18,13 +19,6 @@
from ..models import Form, FormDefinition, FormStep


class BaseFormsViewSet(viewsets.ReadOnlyModelViewSet):
lookup_field = "uuid"
# anonymous clients must be able to get the form definitions in the browser
# The DRF settings apply some default throttling to mitigate abuse
permission_classes = [permissions.AllowAny]


@extend_schema(
parameters=[
OpenApiParameter("form_uuid", OpenApiTypes.UUID, location=OpenApiParameter.PATH)
Expand All @@ -33,10 +27,19 @@ class BaseFormsViewSet(viewsets.ReadOnlyModelViewSet):
@extend_schema_view(
list=extend_schema(summary=_("List form steps")),
retrieve=extend_schema(summary=_("Retrieve form step details")),
create=extend_schema(summary=_("Create a form step")),
update=extend_schema(summary=_("Update all details of a form step")),
partial_update=extend_schema(summary=_("Update some details of a form step")),
destroy=extend_schema(summary=_("Delete a form step")),
)
class FormStepViewSet(NestedViewSetMixin, BaseFormsViewSet):
class FormStepViewSet(
NestedViewSetMixin,
viewsets.ModelViewSet,
):
serializer_class = FormStepSerializer
queryset = FormStep.objects.all()
permission_classes = [IsStaffOrReadOnly]
lookup_field = "uuid"


@extend_schema_view(
Expand All @@ -49,10 +52,14 @@ class FormStepViewSet(NestedViewSetMixin, BaseFormsViewSet):
tags=["forms"],
),
)
class FormDefinitionViewSet(BaseFormsViewSet):
class FormDefinitionViewSet(viewsets.ReadOnlyModelViewSet):
queryset = FormDefinition.objects.order_by("slug")
serializer_class = FormDefinitionSerializer
pagination_class = PageNumberPagination
lookup_field = "uuid"
# anonymous clients must be able to get the form definitions in the browser
# The DRF settings apply some default throttling to mitigate abuse
permission_classes = [permissions.AllowAny]

def get_serializer_context(self) -> dict:
context = super().get_serializer_context()
Expand All @@ -79,7 +86,14 @@ def configuration(self, request: Request, *args, **kwargs):
@extend_schema_view(
list=extend_schema(summary=_("List forms")),
retrieve=extend_schema(summary=_("Retrieve form details")),
create=extend_schema(summary=_("Create form")),
update=extend_schema(summary=_("Update all details of a form")),
partial_update=extend_schema(summary=_("Update given details of a form")),
)
class FormViewSet(BaseFormsViewSet):
class FormViewSet(
mixins.CreateModelMixin, mixins.UpdateModelMixin, viewsets.ReadOnlyModelViewSet
):
queryset = Form.objects.filter(active=True)
lookup_field = "uuid"
serializer_class = FormSerializer
permission_classes = [IsStaffOrReadOnly]
Loading