Skip to content

Commit

Permalink
Merge pull request #152 from open-zaak/feature/ztc-fields-immutable
Browse files Browse the repository at this point in the history
Feature/ztc fields immutable
  • Loading branch information
joeribekker committed Nov 5, 2019
2 parents 786d7b6 + 7bfa89c commit 3dd96a6
Show file tree
Hide file tree
Showing 7 changed files with 131 additions and 8 deletions.
6 changes: 4 additions & 2 deletions src/openzaak/components/besluiten/api/serializers.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
"""
Serializers of the Besluit Registratie Component REST API
"""
from django_loose_fk.drf import FKOrURLField
from rest_framework import serializers
from rest_framework.validators import UniqueTogetherValidator
from vng_api_common.serializers import add_choice_values_help_text
from vng_api_common.utils import get_help_text
from vng_api_common.validators import IsImmutableValidator, validate_rsin

from openzaak.components.documenten.api.serializers import (
EnkelvoudigInformatieObjectHyperlinkedRelatedField,
)
from openzaak.components.documenten.models import EnkelvoudigInformatieObject
from openzaak.utils.validators import PublishValidator
from openzaak.utils.validators import LooseFkIsImmutableValidator, PublishValidator

from ..constants import VervalRedenen
from ..models import Besluit, BesluitInformatieObject
Expand Down Expand Up @@ -51,7 +53,7 @@ class Meta:
"besluittype": {
"lookup_field": "uuid",
"max_length": 200,
"validators": [PublishValidator()],
"validators": [LooseFkIsImmutableValidator(), PublishValidator()],
},
# per BRC API spec!
"zaak": {"lookup_field": "uuid", "max_length": 200},
Expand Down
40 changes: 40 additions & 0 deletions src/openzaak/components/besluiten/tests/test_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,46 @@ def test_no_zaaktype_besluittype_relation(self):
error = get_validation_errors(response, "nonFieldErrors")
self.assertEqual(error["code"], "zaaktype-mismatch")

def test_update(self):
besluit = BesluitFactory.create(besluittype__concept=False)
besluit_url = reverse(besluit)

besluittype_url = reverse(besluit.besluittype)
response = self.client.put(
besluit_url,
{
"identificatie": besluit.identificatie,
"verantwoordelijkeOrganisatie": besluit.verantwoordelijke_organisatie,
"datum": "2019-01-01",
"ingangsdatum": "2018-01-01",
"besluittype": f"http://testserver{besluittype_url}",
"toelichting": "aangepast",
},
)

self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data["toelichting"], "aangepast")

besluit.refresh_from_db()
self.assertEqual(besluit.toelichting, "aangepast")

def test_update_besluittype_fails(self):
besluit = BesluitFactory.create()
besluit_url = reverse(besluit)

besluittype = BesluitTypeFactory.create()
besluittype_url = reverse(besluittype)
response = self.client.patch(
besluit_url, {"besluittype": f"http://testserver{besluittype_url}"}
)

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

error = get_validation_errors(response, "besluittype")
self.assertEqual(error["code"], IsImmutableValidator.code)


class BesluitInformatieObjectTests(JWTAuthMixin, APITestCase):

Expand Down
4 changes: 2 additions & 2 deletions src/openzaak/components/documenten/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +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 openzaak.utils.validators import IsImmutableValidator, PublishValidator

from ..constants import ChecksumAlgoritmes, OndertekeningSoorten, Statussen
from ..models import (
Expand Down Expand Up @@ -156,7 +156,7 @@ class EnkelvoudigInformatieObjectSerializer(serializers.HyperlinkedModelSerializ
help_text=get_help_text(
"documenten.EnkelvoudigInformatieObject", "informatieobjecttype"
),
validators=[PublishValidator()],
validators=[IsImmutableValidator(), PublishValidator()],
)
inhoud = AnyBase64File(
view_name="enkelvoudiginformatieobject-download",
Expand Down
19 changes: 19 additions & 0 deletions src/openzaak/components/documenten/tests/test_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from rest_framework import status
from rest_framework.test import APITestCase
from vng_api_common.tests import get_validation_errors, reverse, reverse_lazy
from vng_api_common.validators import IsImmutableValidator

from openzaak.components.catalogi.tests.factories import InformatieObjectTypeFactory
from openzaak.utils.tests import JWTAuthMixin
Expand Down Expand Up @@ -108,6 +109,24 @@ def test_ondertekening_bad_values(self):

self.assertGegevensGroepValidation(url, "ondertekening", base_body, cases)

def test_update_informatieobjecttype_fails(self):
eio = EnkelvoudigInformatieObjectFactory.create()
eio_url = reverse(eio)

iotype = InformatieObjectTypeFactory.create()
iotype_url = reverse(iotype)

response = self.client.patch(
eio_url, {"informatieobjecttype": f"http://testserver{iotype_url}"}
)

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

error = get_validation_errors(response, "informatieobjecttype")
self.assertEqual(error["code"], IsImmutableValidator.code)


@override_settings(LINK_FETCHER="vng_api_common.mocks.link_fetcher_200")
class InformatieObjectStatusTests(JWTAuthMixin, APITestCase):
Expand Down
6 changes: 3 additions & 3 deletions src/openzaak/components/zaken/api/serializers/zaken.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
from openzaak.utils.auth import get_auth
from openzaak.utils.exceptions import DetermineProcessEndDateException
from openzaak.utils.serializer_fields import LengthHyperlinkedRelatedField
from openzaak.utils.validators import PublishValidator
from openzaak.utils.validators import LooseFkIsImmutableValidator, PublishValidator

from ...brondatum import BrondatumCalculator
from ...constants import AardZaakRelatie, BetalingsIndicatie, IndicatieMachtiging
Expand Down Expand Up @@ -280,7 +280,7 @@ class Meta:
"Resultaat",
settings.REFERENTIELIJSTEN_API_SPEC,
get_auth=get_auth,
)
),
]
},
"hoofdzaak": {
Expand Down Expand Up @@ -580,7 +580,7 @@ class ZaakInformatieObjectSerializer(serializers.HyperlinkedModelSerializer):
choices=[(force_text(value), key) for key, value in RelatieAarden.choices],
)
informatieobject = EnkelvoudigInformatieObjectField(
validators=[IsImmutableValidator()],
validators=[LooseFkIsImmutableValidator(instance_path="canonical")],
max_length=1000,
min_length=1,
help_text=get_help_text("zaken.ZaakInformatieObject", "informatieobject"),
Expand Down
19 changes: 19 additions & 0 deletions src/openzaak/components/zaken/tests/test_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,25 @@ def test_informatieobject_create(self):

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

def test_update_informatieobject_fails(self):
io = EnkelvoudigInformatieObjectFactory.create(
informatieobjecttype__concept=False
)
io_url = reverse(io)

zio = ZaakInformatieObjectFactory.create(
informatieobject__latest_version__informatieobjecttype=io.informatieobjecttype
)
zio_url = reverse(zio)

response = self.client.patch(
zio_url, {"informatieobject": f"http://testserver{io_url}"}
)

self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
validation_error = get_validation_errors(response, "informatieobject")
self.assertEqual(validation_error["code"], IsImmutableValidator.code)


class FilterValidationTests(JWTAuthMixin, APITestCase):
"""
Expand Down
45 changes: 44 additions & 1 deletion src/openzaak/utils/validators.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
from urllib.parse import urlparse

from django.utils.translation import ugettext_lazy as _

from django_loose_fk.drf import FKOrURLField, FKOrURLValidator
from django_loose_fk.drf import FKOrURLField, FKOrURLValidator, Resolver
from rest_framework import serializers
from vng_api_common.validators import IsImmutableValidator


class PublishValidator(FKOrURLValidator):
Expand All @@ -23,3 +26,43 @@ def __call__(self, value):
raise serializers.ValidationError(
self.publish_message, code=self.publish_code
)


class LooseFkIsImmutableValidator(FKOrURLValidator):
"""
Valideer dat de waarde van het FkOrUrlField niet wijzigt bij een update actie.
"""

def __init__(self, *args, **kwargs):
self.instance_path = kwargs.pop("instance_path", None)
super().__init__(*args, **kwargs)

def set_context(self, serializer_field):
# loose-fk field
if isinstance(serializer_field, FKOrURLField):
super().set_context(serializer_field)

# Determine the existing instance, if this is an update operation.
self.serializer_field = serializer_field
self.instance = getattr(serializer_field.parent, "instance", None)

def __call__(self, new_value):
# no instance -> it's not an update
if not self.instance:
return

current_value = getattr(self.instance, self.serializer_field.field_name)

# loose-fk field
if new_value and isinstance(new_value, str):
super().__call__(new_value)
new_value = self.resolver.resolve(self.host, new_value)

if self.instance_path:
for bit in self.instance_path.split("."):
new_value = getattr(new_value, bit)

if new_value != current_value:
raise serializers.ValidationError(
IsImmutableValidator.message, code=IsImmutableValidator.code
)

0 comments on commit 3dd96a6

Please sign in to comment.