Skip to content

Commit

Permalink
Merge pull request #106 from open-zaak/feature/concept-validation
Browse files Browse the repository at this point in the history
Feature/concept validation
  • Loading branch information
joeribekker committed Oct 30, 2019
2 parents ce0547e + 111f4a3 commit 6063568
Show file tree
Hide file tree
Showing 31 changed files with 194 additions and 85 deletions.
7 changes: 6 additions & 1 deletion src/openzaak/components/besluiten/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
EnkelvoudigInformatieObjectHyperlinkedRelatedField,
)
from openzaak.components.documenten.models import EnkelvoudigInformatieObject
from openzaak.utils.validators import PublishValidator

from ..constants import VervalRedenen
from ..models import Besluit, BesluitInformatieObject
Expand Down Expand Up @@ -47,7 +48,11 @@ class Meta:
extra_kwargs = {
"url": {"lookup_field": "uuid"},
# per BRC API spec!
"besluittype": {"lookup_field": "uuid", "max_length": 200},
"besluittype": {
"lookup_field": "uuid",
"max_length": 200,
"validators": [PublishValidator()],
},
# per BRC API spec!
"zaak": {"lookup_field": "uuid", "max_length": 200},
"identificatie": {"validators": [IsImmutableValidator()]},
Expand Down
6 changes: 2 additions & 4 deletions src/openzaak/components/besluiten/api/validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,7 @@ def __call__(self, attrs):
if not zaak:
return

if not besluittype.zaaktypes.filter(
id=zaak.zaaktype_id, concept=False
).exists():
if not besluittype.zaaktypes.filter(id=zaak.zaaktype_id).exists():
raise serializers.ValidationError(self.message, code=self.code)


Expand All @@ -45,6 +43,6 @@ def __call__(self, attrs):

io = informatieobject.enkelvoudiginformatieobject_set.first()
if not besluit.besluittype.informatieobjecttypen.filter(
id=io.informatieobjecttype_id, concept=False
id=io.informatieobjecttype_id
).exists():
raise serializers.ValidationError(self.message, code=self.code)
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class AuditTrailTests(JWTAuthMixin, APITestCase):

def _create_besluit(self, **HEADERS):
url = reverse(Besluit)
besluittype = BesluitTypeFactory.create()
besluittype = BesluitTypeFactory.create(concept=False)
besluittype_url = reverse(besluittype)

besluit_data = {
Expand Down
10 changes: 7 additions & 3 deletions src/openzaak/components/besluiten/tests/test_besluit_create.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class BesluitCreateTests(TypeCheckMixin, JWTAuthMixin, APITestCase):
def test_us162_voeg_besluit_toe_aan_zaak(self):
zaak = ZaakFactory.create(zaaktype__concept=False)
zaak_url = reverse(zaak)
besluittype = BesluitTypeFactory.create()
besluittype = BesluitTypeFactory.create(concept=False)
besluittype_url = reverse(besluittype)
besluittype.zaaktypes.add(zaak.zaaktype)
io = EnkelvoudigInformatieObjectFactory.create(
Expand Down Expand Up @@ -158,8 +158,12 @@ def test_besluit_create_fail_besluittype_max_length(self):
response.status_code, status.HTTP_400_BAD_REQUEST, response.data
)

error = get_validation_errors(response, "besluittype")
self.assertEqual(error["code"], "max_length")
max_length_errors = [
e
for e in response.data["invalid_params"]
if e["name"] == "besluittype" and e["code"] == "max_length"
]
self.assertEqual(len(max_length_errors), 1)


@tag("external-urls")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def test_send_notif_create_besluit(self, mock_client, *mocks):
Check if notifications will be send when Besluit is created
"""
client = mock_client.return_value
besluittype = BesluitTypeFactory.create()
besluittype = BesluitTypeFactory.create(concept=False)
besluittype_url = reverse(besluittype)
url = get_operation_url("besluit_create")
data = {
Expand Down
44 changes: 20 additions & 24 deletions src/openzaak/components/besluiten/tests/test_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@ def test_future_datum(self):
self.assertEqual(error["code"], UntilTodayValidator.code)

def test_duplicate_rsin_identificatie(self):
besluit = BesluitFactory.create(identificatie="123456")
besluit = BesluitFactory.create(
identificatie="123456", besluittype__concept=False
)
besluittype_url = reverse(besluit.besluittype)

response = self.client.post(
Expand Down Expand Up @@ -90,7 +92,7 @@ def test_change_immutable_fields(self):
)

def test_validate_besluittype_valid(self):
besluittype = BesluitTypeFactory.create()
besluittype = BesluitTypeFactory.create(concept=False)
besluittype_url = reverse(besluittype)
url = reverse("besluit-list")

Expand Down Expand Up @@ -129,33 +131,33 @@ def test_besluittype_invalid(self):
error = get_validation_errors(response, "besluittype")
self.assertEqual(error["code"], "bad-url")

def test_zaaktype_besluittype_relation(self):
def test_besluittype_unpublished(self):
besluittype = BesluitTypeFactory.create()
besluittype_url = reverse(besluittype)
zaak = ZaakFactory.create(zaaktype__concept=False)
zaak_url = reverse(zaak)
besluittype.zaaktypes.add(zaak.zaaktype)
list_url = reverse("besluit-list")
url = reverse("besluit-list")

response = self.client.post(
list_url,
url,
{
"verantwoordelijkeOrganisatie": "000000000",
"identificatie": "123456",
"besluittype": f"http://testserver{besluittype_url}",
"zaak": f"http://testserver{zaak_url}",
"datum": "2018-09-06",
"ingangsdatum": "2018-10-01",
},
)

self.assertEqual(response.status_code, status.HTTP_201_CREATED, response.data)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

def test_no_zaaktype_besluittype_relation(self):
besluittype = BesluitTypeFactory.create()
error = get_validation_errors(response, "besluittype")
self.assertEqual(error["code"], "not-published")

def test_zaaktype_besluittype_relation(self):
besluittype = BesluitTypeFactory.create(concept=False)
besluittype_url = reverse(besluittype)
zaak = ZaakFactory.create()
zaak = ZaakFactory.create(zaaktype__concept=False)
zaak_url = reverse(zaak)
besluittype.zaaktypes.add(zaak.zaaktype)
list_url = reverse("besluit-list")

response = self.client.post(
Expand All @@ -170,19 +172,13 @@ def test_no_zaaktype_besluittype_relation(self):
},
)

self.assertEqual(
response.status_code, status.HTTP_400_BAD_REQUEST, response.data
)

error = get_validation_errors(response, "nonFieldErrors")
self.assertEqual(error["code"], "zaaktype-mismatch")
self.assertEqual(response.status_code, status.HTTP_201_CREATED, response.data)

def test_relation_with_non_published_zaaktype(self):
zaak = ZaakFactory.create(zaaktype__concept=True)
zaak_url = reverse(zaak)
besluittype = BesluitTypeFactory.create()
def test_no_zaaktype_besluittype_relation(self):
besluittype = BesluitTypeFactory.create(concept=False)
besluittype_url = reverse(besluittype)
besluittype.zaaktypes.add(zaak.zaaktype)
zaak = ZaakFactory.create()
zaak_url = reverse(zaak)
list_url = reverse("besluit-list")

response = self.client.post(
Expand Down
18 changes: 0 additions & 18 deletions src/openzaak/components/catalogi/api/viewsets/mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

from drf_yasg.utils import no_body, swagger_auto_schema
from rest_framework.decorators import action
from rest_framework.exceptions import PermissionDenied
from rest_framework.response import Response
from rest_framework.serializers import ValidationError

Expand Down Expand Up @@ -82,23 +81,6 @@ class ZaakTypeConceptMixin(ZaakTypeConceptDestroyMixin, ZaakTypeConceptFilterMix
pass


class M2MConceptCreateMixin:

concept_related_fields = []

def perform_create(self, serializer):
for field_name in self.concept_related_fields:
field = serializer.validated_data.get(field_name, [])
for related_object in field:
if not related_object.concept:
msg = _(
f"Relations to a non-concept {field_name} object can't be created"
)
raise PermissionDenied(detail=msg)

super().perform_create(serializer)


class M2MConceptDestroyMixin:
def perform_destroy(self, instance):
for field_name in self.concept_related_fields:
Expand Down
36 changes: 34 additions & 2 deletions src/openzaak/components/catalogi/api/viewsets/zaaktype.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
from django.utils.translation import ugettext_lazy as _

from drf_yasg.utils import no_body, swagger_auto_schema
from rest_framework import viewsets
from rest_framework.decorators import action
from rest_framework.exceptions import ValidationError
from rest_framework.pagination import PageNumberPagination
from rest_framework.response import Response
from rest_framework.settings import api_settings
from vng_api_common.viewsets import CheckQueryParamsMixin

from openzaak.utils.permissions import AuthRequired
Expand All @@ -9,11 +15,15 @@
from ..filters import ZaakTypeFilter
from ..scopes import SCOPE_ZAAKTYPES_READ, SCOPE_ZAAKTYPES_WRITE
from ..serializers import ZaakTypeSerializer
from .mixins import ConceptMixin, M2MConceptDestroyMixin
from .mixins import ConceptDestroyMixin, ConceptFilterMixin, M2MConceptDestroyMixin


class ZaakTypeViewSet(
CheckQueryParamsMixin, ConceptMixin, M2MConceptDestroyMixin, viewsets.ModelViewSet
CheckQueryParamsMixin,
ConceptDestroyMixin,
ConceptFilterMixin,
M2MConceptDestroyMixin,
viewsets.ModelViewSet,
):
"""
Opvragen en bewerken van ZAAKTYPEn nodig voor ZAKEN in de Zaken API.
Expand Down Expand Up @@ -84,3 +94,25 @@ class ZaakTypeViewSet(
"publish": SCOPE_ZAAKTYPES_WRITE,
}
concept_related_fields = ["besluittypen", "informatieobjecttypen"]

@swagger_auto_schema(request_body=no_body)
@action(detail=True, methods=["post"])
def publish(self, request, *args, **kwargs):
instance = self.get_object()

# check related objects
if (
instance.besluittypen.filter(concept=True).exists()
or instance.informatieobjecttypen.filter(concept=True).exists()
):
msg = _("All related resources should be published")
raise ValidationError(
{api_settings.NON_FIELD_ERRORS_KEY: msg}, code="concept-relation"
)

instance.concept = False
instance.save()

serializer = self.get_serializer(instance)

return Response(serializer.data)
27 changes: 27 additions & 0 deletions src/openzaak/components/catalogi/tests/test_zaaktype.py
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,33 @@ def test_publish_zaaktype(self):

self.assertEqual(zaaktype.concept, False)

def test_publish_zaaktype_fail_not_concept_besluittype(self):
zaaktype = ZaakTypeFactory.create()
besluittype = BesluitTypeFactory.create()
zaaktype.besluittypen.add(besluittype)

zaaktype_url = get_operation_url("zaaktype_publish", uuid=zaaktype.uuid)

response = self.client.post(zaaktype_url)

self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

error = get_validation_errors(response, "nonFieldErrors")
self.assertEqual(error["code"], "concept-relation")

def test_publish_zaaktype_fail_not_concept_iotype(self):
zaaktype = ZaakTypeFactory.create()
ZaakInformatieobjectTypeFactory.create(zaaktype=zaaktype)

zaaktype_url = get_operation_url("zaaktype_publish", uuid=zaaktype.uuid)

response = self.client.post(zaaktype_url)

self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

error = get_validation_errors(response, "nonFieldErrors")
self.assertEqual(error["code"], "concept-relation")

def test_delete_zaaktype(self):
zaaktype = ZaakTypeFactory.create()
zaaktype_url = get_operation_url("zaaktype_read", uuid=zaaktype.uuid)
Expand Down
2 changes: 2 additions & 0 deletions src/openzaak/components/documenten/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
from openzaak.components.catalogi.models import InformatieObjectType
from openzaak.components.zaken.models import Zaak
from openzaak.utils.serializer_fields import LengthHyperlinkedRelatedField
from openzaak.utils.validators import PublishValidator

from ..constants import ChecksumAlgoritmes, OndertekeningSoorten, Statussen
from ..models import (
Expand Down Expand Up @@ -155,6 +156,7 @@ class EnkelvoudigInformatieObjectSerializer(serializers.HyperlinkedModelSerializ
help_text=get_help_text(
"documenten.EnkelvoudigInformatieObject", "informatieobjecttype"
),
validators=[PublishValidator()],
)
inhoud = AnyBase64File(
view_name="enkelvoudiginformatieobject-download",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class AuditTrailTests(JWTAuthMixin, APITestCase):
heeft_alle_autorisaties = True

def _create_enkelvoudiginformatieobject(self, **HEADERS):
informatieobjecttype = InformatieObjectTypeFactory.create()
informatieobjecttype = InformatieObjectTypeFactory.create(concept=False)
informatieobjecttype_url = reverse(informatieobjecttype)
content = {
"identificatie": uuid.uuid4().hex,
Expand Down
2 changes: 1 addition & 1 deletion src/openzaak/components/documenten/tests/test_eio_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def test_create_enkelvoudiginformatieobject(self):
"""
Registreer een ENKELVOUDIGINFORMATIEOBJECT
"""
informatieobjecttype = InformatieObjectTypeFactory.create()
informatieobjecttype = InformatieObjectTypeFactory.create(concept=False)
informatieobjecttype_url = reverse(informatieobjecttype)
url = get_operation_url("enkelvoudiginformatieobject_create")
data = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ def test_vertrouwelijkheidaanduiding_derived(self):
from informatieobjecttype
"""
informatieobjecttype = InformatieObjectTypeFactory.create(
vertrouwelijkheidaanduiding=VertrouwelijkheidsAanduiding.zaakvertrouwelijk
vertrouwelijkheidaanduiding=VertrouwelijkheidsAanduiding.zaakvertrouwelijk,
concept=False,
)
informatieobjecttype_url = reverse(informatieobjecttype)
url = reverse("enkelvoudiginformatieobject-list")
Expand Down Expand Up @@ -58,7 +59,8 @@ def test_vertrouwelijkheidaanduiding_explicit(self):
Assert the explicit set of vertrouwelijkheidaanduiding
"""
informatieobjecttype = InformatieObjectTypeFactory.create(
vertrouwelijkheidaanduiding=VertrouwelijkheidsAanduiding.zaakvertrouwelijk
vertrouwelijkheidaanduiding=VertrouwelijkheidsAanduiding.zaakvertrouwelijk,
concept=False,
)
informatieobjecttype_url = reverse(informatieobjecttype)
url = reverse("enkelvoudiginformatieobject-list")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class EnkelvoudigInformatieObjectAPITests(JWTAuthMixin, APITestCase):
heeft_alle_autorisaties = True

def test_create(self):
informatieobjecttype = InformatieObjectTypeFactory.create()
informatieobjecttype = InformatieObjectTypeFactory.create(concept=False)
informatieobjecttype_url = reverse(informatieobjecttype)
content = {
"identificatie": uuid.uuid4().hex,
Expand Down Expand Up @@ -208,7 +208,7 @@ def test_integrity_empty(self):
"""
Assert that integrity is optional.
"""
informatieobjecttype = InformatieObjectTypeFactory.create()
informatieobjecttype = InformatieObjectTypeFactory.create(concept=False)
informatieobjecttype_url = reverse(informatieobjecttype)
content = {
"identificatie": uuid.uuid4().hex,
Expand Down Expand Up @@ -238,7 +238,7 @@ def test_integrity_provided(self):
"""
Assert that integrity is saved.
"""
informatieobjecttype = InformatieObjectTypeFactory.create()
informatieobjecttype = InformatieObjectTypeFactory.create(concept=False)
informatieobjecttype_url = reverse(informatieobjecttype)
content = {
"identificatie": uuid.uuid4().hex,
Expand Down Expand Up @@ -328,7 +328,9 @@ class EnkelvoudigInformatieObjectVersionHistoryAPITests(JWTAuthMixin, APITestCas
heeft_alle_autorisaties = True

def test_eio_update(self):
eio = EnkelvoudigInformatieObjectFactory.create(beschrijving="beschrijving1")
eio = EnkelvoudigInformatieObjectFactory.create(
beschrijving="beschrijving1", informatieobjecttype__concept=False
)

eio_url = reverse(
"enkelvoudiginformatieobject-detail", kwargs={"uuid": eio.uuid}
Expand Down
2 changes: 1 addition & 1 deletion src/openzaak/components/documenten/tests/test_lock_eio.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ def test_update_fail_wrong_id(self):
self.assertEqual(error["code"], "incorrect-lock-id")

def test_create_ignores_lock(self):
informatieobjecttype = InformatieObjectTypeFactory.create()
informatieobjecttype = InformatieObjectTypeFactory.create(concept=False)
informatieobjecttype_url = reverse(informatieobjecttype)
url = get_operation_url("enkelvoudiginformatieobject_create")
data = {
Expand Down

0 comments on commit 6063568

Please sign in to comment.