From c197fc0704a0efe13bba989310fc65d4ead68a28 Mon Sep 17 00:00:00 2001 From: Anna Shamray Date: Fri, 26 Nov 2021 16:58:31 +0100 Subject: [PATCH 1/6] :sparkles: add validation for geometry based on objecttype --- src/objects/api/serializers.py | 4 ++-- src/objects/api/validators.py | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/src/objects/api/serializers.py b/src/objects/api/serializers.py index 32a99bd9..ceafd3da 100644 --- a/src/objects/api/serializers.py +++ b/src/objects/api/serializers.py @@ -9,7 +9,7 @@ from objects.utils.serializers import DynamicFieldsMixin from .fields import ObjectSlugRelatedField, ObjectTypeField -from .validators import IsImmutableValidator, JsonSchemaValidator +from .validators import GeometryValidator, IsImmutableValidator, JsonSchemaValidator class ObjectRecordSerializer(serializers.ModelSerializer): @@ -104,7 +104,7 @@ class Meta: "url": {"lookup_field": "uuid"}, "uuid": {"validators": [IsImmutableValidator()]}, } - validators = [JsonSchemaValidator()] + validators = [JsonSchemaValidator(), GeometryValidator()] @transaction.atomic def create(self, validated_data): diff --git a/src/objects/api/validators.py b/src/objects/api/validators.py index 6b05b2d8..198e9fcb 100644 --- a/src/objects/api/validators.py +++ b/src/objects/api/validators.py @@ -2,6 +2,7 @@ from django.utils.translation import ugettext_lazy as _ from rest_framework import serializers +from zds_client.client import ClientError from objects.core.utils import check_objecttype @@ -88,3 +89,35 @@ def validate_data_attrs(value: str): "Operator `%(operator)s` supports only dates and/or numeric values" ) % {"operator": operator} raise serializers.ValidationError(message, code=code) + + +class GeometryValidator: + code = "geometry-not-allowed" + message = _("This object type doesn't support geometry") + + def set_context(self, serializer): + """ + This hook is called by the serializer instance, + prior to the validation call being made. + """ + self.instance = getattr(serializer, "instance", None) + + def __call__(self, attrs): + object_type = attrs.get("object_type") or self.instance.object_type + geometry = attrs.get("record", {}).get("geometry") + + if not geometry: + return + + client = object_type.service.build_client() + try: + response = client.retrieve("objecttype", url=object_type.url) + except ClientError as exc: + + msg = f"Object type can not be retrieved: {exc.args[0]}" + raise ValidationError(msg) + + has_geometry = response.get("hasGeometry") + + if geometry and not has_geometry: + raise serializers.ValidationError(self.message, code=self.code) From fbbf15eaf6e910e9c94a1a680281fcc5415ab9b7 Mon Sep 17 00:00:00 2001 From: Anna Shamray Date: Fri, 26 Nov 2021 16:59:13 +0100 Subject: [PATCH 2/6] :white_check_mark: test validation on geometry field --- src/objects/tests/utils.py | 15 ++-- .../tests/v1/test_notifications_send.py | 7 +- src/objects/tests/v1/test_object_api.py | 4 +- src/objects/tests/v1/test_validation.py | 70 +++++++++++++++++++ .../tests/v2/test_notifications_send.py | 6 +- src/objects/tests/v2/test_object_api.py | 4 +- src/objects/tests/v2/test_validation.py | 70 +++++++++++++++++++ 7 files changed, 165 insertions(+), 11 deletions(-) diff --git a/src/objects/tests/utils.py b/src/objects/tests/utils.py index cc14fe24..d1fac22e 100644 --- a/src/objects/tests/utils.py +++ b/src/objects/tests/utils.py @@ -34,8 +34,9 @@ def mock_service_oas_get(m: Mocker, url: str, service: str, oas_url: str = "") - m.get(oas_url, content=content) -def mock_objecttype(url: str) -> dict: - return { +def mock_objecttype(url: str, attrs=None) -> dict: + attrs = attrs or {} + response = { "url": url, "name": "Boom", "namePlural": "Bomen", @@ -52,14 +53,18 @@ def mock_objecttype(url: str) -> dict: "labels": {}, "createdAt": "2020-12-01", "modifiedAt": "2020-12-01", + "hasGeometry": True, "versions": [ f"{url}/versions/1", ], } + response.update(attrs) + return response -def mock_objecttype_version(url: str) -> dict: - return { +def mock_objecttype_version(url: str, attrs=None) -> dict: + attrs = attrs or {} + response = { "url": f"{url}/versions/1", "version": 1, "objectType": url, @@ -82,6 +87,8 @@ def mock_objecttype_version(url: str) -> dict: "modifiedAt": "2020-11-16", "publishedAt": "2020-11-16", } + response.update(attrs) + return response def notifications_client_mock(value): diff --git a/src/objects/tests/v1/test_notifications_send.py b/src/objects/tests/v1/test_notifications_send.py index e9e9ae2a..f5102d8c 100644 --- a/src/objects/tests/v1/test_notifications_send.py +++ b/src/objects/tests/v1/test_notifications_send.py @@ -8,7 +8,6 @@ from rest_framework import status from rest_framework.test import APITestCase from vng_api_common.notifications.models import NotificationsConfig -from zds_client.client import Client from zgw_consumers.constants import APITypes from zgw_consumers.models import Service @@ -19,6 +18,7 @@ from ..constants import GEO_WRITE_KWARGS from ..utils import ( + mock_objecttype, mock_objecttype_version, mock_service_oas_get, notifications_client_mock, @@ -69,6 +69,7 @@ def test_send_notif_create_object(self, mocker, mock_client): f"{self.object_type.url}/versions/1", json=mock_objecttype_version(self.object_type.url), ) + mocker.get(self.object_type.url, json=mock_objecttype(self.object_type.url)) url = reverse("object-list") data = { @@ -117,10 +118,10 @@ def test_send_notif_update_object(self, mocker, mock_client): f"{self.object_type.url}/versions/1", json=mock_objecttype_version(self.object_type.url), ) + mocker.get(self.object_type.url, json=mock_objecttype(self.object_type.url)) obj = ObjectFactory.create(object_type=self.object_type) url = reverse("object-detail", args=[obj.uuid]) - full_url = f"http://testserver{url}" data = { "type": self.object_type.url, @@ -168,10 +169,10 @@ def test_send_notif_partial_update_object(self, mocker, mock_client): f"{self.object_type.url}/versions/1", json=mock_objecttype_version(self.object_type.url), ) + mocker.get(self.object_type.url, json=mock_objecttype(self.object_type.url)) obj = ObjectFactory.create(object_type=self.object_type) url = reverse("object-detail", args=[obj.uuid]) - full_url = f"http://testserver{url}" data = { "type": self.object_type.url, diff --git a/src/objects/tests/v1/test_object_api.py b/src/objects/tests/v1/test_object_api.py index 0e5b957d..fed267a1 100644 --- a/src/objects/tests/v1/test_object_api.py +++ b/src/objects/tests/v1/test_object_api.py @@ -17,7 +17,7 @@ from objects.utils.test import TokenAuthMixin from ..constants import GEO_WRITE_KWARGS -from ..utils import mock_objecttype_version, mock_service_oas_get +from ..utils import mock_objecttype, mock_objecttype_version, mock_service_oas_get from .utils import reverse OBJECT_TYPES_API = "https://example.com/objecttypes/v1/" @@ -104,6 +104,7 @@ def test_create_object(self, m): f"{self.object_type.url}/versions/1", json=mock_objecttype_version(self.object_type.url), ) + m.get(self.object_type.url, json=mock_objecttype(self.object_type.url)) url = reverse("object-list") data = { @@ -142,6 +143,7 @@ def test_update_object(self, m): f"{self.object_type.url}/versions/1", json=mock_objecttype_version(self.object_type.url), ) + m.get(self.object_type.url, json=mock_objecttype(self.object_type.url)) # other object - to check that correction works when there is another record with the same index ObjectRecordFactory.create(object__object_type=self.object_type) diff --git a/src/objects/tests/v1/test_validation.py b/src/objects/tests/v1/test_validation.py index a5f3e78a..e05fbb3c 100644 --- a/src/objects/tests/v1/test_validation.py +++ b/src/objects/tests/v1/test_validation.py @@ -174,6 +174,39 @@ def test_create_object_correction_invalid(self, m): [f"Object with index={record.index} does not exist."], ) + def test_create_object_geometry_not_allowed(self, m): + mock_service_oas_get(m, OBJECT_TYPES_API, "objecttypes") + m.get( + f"{self.object_type.url}/versions/1", + json=mock_objecttype_version(self.object_type.url), + ) + m.get( + self.object_type.url, + json=mock_objecttype(self.object_type.url, attrs={"hasGeometry": False}), + ) + + url = reverse("object-list") + data = { + "type": self.object_type.url, + "record": { + "typeVersion": 1, + "data": {"plantDate": "2020-04-12", "diameter": 30}, + "geometry": { + "type": "Point", + "coordinates": [4.910649523925713, 52.37240093589432], + }, + "startAt": "2020-01-01", + }, + } + + response = self.client.post(url, data, **GEO_WRITE_KWARGS) + + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual( + response.json()["non_field_errors"], + ["This object type doesn't support geometry"], + ) + def test_update_object_with_correction_invalid(self, m): mock_service_oas_get(m, OBJECT_TYPES_API, "objecttypes") m.get( @@ -256,3 +289,40 @@ def test_update_uuid_invalid(self, m): data = response.json() self.assertEqual(data["uuid"], ["This field can't be changed"]) + + def test_update_geometry_not_allowed(self, m): + mock_service_oas_get(m, OBJECT_TYPES_API, "objecttypes") + m.get( + f"{self.object_type.url}/versions/1", + json=mock_objecttype_version(self.object_type.url), + ) + m.get( + self.object_type.url, + json=mock_objecttype(self.object_type.url, attrs={"hasGeometry": False}), + ) + + initial_record = ObjectRecordFactory.create( + object__object_type=self.object_type, geometry=None + ) + object = initial_record.object + + url = reverse("object-detail", args=[object.uuid]) + data = { + "record": { + "typeVersion": 1, + "data": {"plantDate": "2020-04-12", "diameter": 30}, + "geometry": { + "type": "Point", + "coordinates": [4.910649523925713, 52.37240093589432], + }, + "startAt": "2020-01-01", + } + } + + response = self.client.patch(url, data, **GEO_WRITE_KWARGS) + + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual( + response.json()["non_field_errors"], + ["This object type doesn't support geometry"], + ) diff --git a/src/objects/tests/v2/test_notifications_send.py b/src/objects/tests/v2/test_notifications_send.py index 4813b187..dac043a1 100644 --- a/src/objects/tests/v2/test_notifications_send.py +++ b/src/objects/tests/v2/test_notifications_send.py @@ -18,6 +18,7 @@ from ..constants import GEO_WRITE_KWARGS from ..utils import ( + mock_objecttype, mock_objecttype_version, mock_service_oas_get, notifications_client_mock, @@ -68,6 +69,7 @@ def test_send_notif_create_object(self, mocker, mock_client): f"{self.object_type.url}/versions/1", json=mock_objecttype_version(self.object_type.url), ) + mocker.get(self.object_type.url, json=mock_objecttype(self.object_type.url)) url = reverse("object-list") data = { @@ -116,10 +118,10 @@ def test_send_notif_update_object(self, mocker, mock_client): f"{self.object_type.url}/versions/1", json=mock_objecttype_version(self.object_type.url), ) + mocker.get(self.object_type.url, json=mock_objecttype(self.object_type.url)) obj = ObjectFactory.create(object_type=self.object_type) url = reverse("object-detail", args=[obj.uuid]) - full_url = f"http://testserver{url}" data = { "type": self.object_type.url, @@ -167,10 +169,10 @@ def test_send_notif_partial_update_object(self, mocker, mock_client): f"{self.object_type.url}/versions/1", json=mock_objecttype_version(self.object_type.url), ) + mocker.get(self.object_type.url, json=mock_objecttype(self.object_type.url)) obj = ObjectFactory.create(object_type=self.object_type) url = reverse("object-detail", args=[obj.uuid]) - full_url = f"http://testserver{url}" data = { "type": self.object_type.url, diff --git a/src/objects/tests/v2/test_object_api.py b/src/objects/tests/v2/test_object_api.py index bce23b82..9662554b 100644 --- a/src/objects/tests/v2/test_object_api.py +++ b/src/objects/tests/v2/test_object_api.py @@ -17,7 +17,7 @@ from objects.utils.test import TokenAuthMixin from ..constants import GEO_WRITE_KWARGS -from ..utils import mock_objecttype_version, mock_service_oas_get +from ..utils import mock_objecttype, mock_objecttype_version, mock_service_oas_get from .utils import reverse OBJECT_TYPES_API = "https://example.com/objecttypes/v1/" @@ -125,6 +125,7 @@ def test_create_object(self, m): f"{self.object_type.url}/versions/1", json=mock_objecttype_version(self.object_type.url), ) + m.get(self.object_type.url, json=mock_objecttype(self.object_type.url)) url = reverse("object-list") data = { @@ -163,6 +164,7 @@ def test_update_object(self, m): f"{self.object_type.url}/versions/1", json=mock_objecttype_version(self.object_type.url), ) + m.get(self.object_type.url, json=mock_objecttype(self.object_type.url)) # other object - to check that correction works when there is another record with the same index ObjectRecordFactory.create(object__object_type=self.object_type) diff --git a/src/objects/tests/v2/test_validation.py b/src/objects/tests/v2/test_validation.py index a5f3e78a..e05fbb3c 100644 --- a/src/objects/tests/v2/test_validation.py +++ b/src/objects/tests/v2/test_validation.py @@ -174,6 +174,39 @@ def test_create_object_correction_invalid(self, m): [f"Object with index={record.index} does not exist."], ) + def test_create_object_geometry_not_allowed(self, m): + mock_service_oas_get(m, OBJECT_TYPES_API, "objecttypes") + m.get( + f"{self.object_type.url}/versions/1", + json=mock_objecttype_version(self.object_type.url), + ) + m.get( + self.object_type.url, + json=mock_objecttype(self.object_type.url, attrs={"hasGeometry": False}), + ) + + url = reverse("object-list") + data = { + "type": self.object_type.url, + "record": { + "typeVersion": 1, + "data": {"plantDate": "2020-04-12", "diameter": 30}, + "geometry": { + "type": "Point", + "coordinates": [4.910649523925713, 52.37240093589432], + }, + "startAt": "2020-01-01", + }, + } + + response = self.client.post(url, data, **GEO_WRITE_KWARGS) + + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual( + response.json()["non_field_errors"], + ["This object type doesn't support geometry"], + ) + def test_update_object_with_correction_invalid(self, m): mock_service_oas_get(m, OBJECT_TYPES_API, "objecttypes") m.get( @@ -256,3 +289,40 @@ def test_update_uuid_invalid(self, m): data = response.json() self.assertEqual(data["uuid"], ["This field can't be changed"]) + + def test_update_geometry_not_allowed(self, m): + mock_service_oas_get(m, OBJECT_TYPES_API, "objecttypes") + m.get( + f"{self.object_type.url}/versions/1", + json=mock_objecttype_version(self.object_type.url), + ) + m.get( + self.object_type.url, + json=mock_objecttype(self.object_type.url, attrs={"hasGeometry": False}), + ) + + initial_record = ObjectRecordFactory.create( + object__object_type=self.object_type, geometry=None + ) + object = initial_record.object + + url = reverse("object-detail", args=[object.uuid]) + data = { + "record": { + "typeVersion": 1, + "data": {"plantDate": "2020-04-12", "diameter": 30}, + "geometry": { + "type": "Point", + "coordinates": [4.910649523925713, 52.37240093589432], + }, + "startAt": "2020-01-01", + } + } + + response = self.client.patch(url, data, **GEO_WRITE_KWARGS) + + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual( + response.json()["non_field_errors"], + ["This object type doesn't support geometry"], + ) From 03387af7787ef7888f3e5ee651eb5a422d21c7a6 Mon Sep 17 00:00:00 2001 From: Anna Shamray Date: Fri, 26 Nov 2021 17:25:45 +0100 Subject: [PATCH 3/6] :memo: document has_geometry check --- src/objects/api/v1/openapi.yaml | 6 ++- src/objects/api/v2/openapi.yaml | 6 ++- .../migrations/0027_auto_20211126_1725.py | 37 +++++++++++++++++++ src/objects/core/models.py | 4 +- 4 files changed, 48 insertions(+), 5 deletions(-) create mode 100644 src/objects/core/migrations/0027_auto_20211126_1725.py diff --git a/src/objects/api/v1/openapi.yaml b/src/objects/api/v1/openapi.yaml index dd9f3896..35915e86 100644 --- a/src/objects/api/v1/openapi.yaml +++ b/src/objects/api/v1/openapi.yaml @@ -648,7 +648,8 @@ components: - $ref: '#/components/schemas/GeoJSONGeometry' nullable: true description: Point, linestring or polygon object which represents the coordinates - of the object + of the object. Geometry can be added only if the related OBJECTTYPE allows + it (`OBJECTTYPE.hasGeometry = true`) startAt: type: string format: date @@ -793,7 +794,8 @@ components: - $ref: '#/components/schemas/GeoJSONGeometry' nullable: true description: Point, linestring or polygon object which represents the coordinates - of the object + of the object. Geometry can be added only if the related OBJECTTYPE allows + it (`OBJECTTYPE.hasGeometry = true`) startAt: type: string format: date diff --git a/src/objects/api/v2/openapi.yaml b/src/objects/api/v2/openapi.yaml index 453a1ccc..832bce96 100644 --- a/src/objects/api/v2/openapi.yaml +++ b/src/objects/api/v2/openapi.yaml @@ -715,7 +715,8 @@ components: - $ref: '#/components/schemas/GeoJSONGeometry' nullable: true description: Point, linestring or polygon object which represents the coordinates - of the object + of the object. Geometry can be added only if the related OBJECTTYPE allows + it (`OBJECTTYPE.hasGeometry = true`) startAt: type: string format: date @@ -865,7 +866,8 @@ components: - $ref: '#/components/schemas/GeoJSONGeometry' nullable: true description: Point, linestring or polygon object which represents the coordinates - of the object + of the object. Geometry can be added only if the related OBJECTTYPE allows + it (`OBJECTTYPE.hasGeometry = true`) startAt: type: string format: date diff --git a/src/objects/core/migrations/0027_auto_20211126_1725.py b/src/objects/core/migrations/0027_auto_20211126_1725.py new file mode 100644 index 00000000..6cd53951 --- /dev/null +++ b/src/objects/core/migrations/0027_auto_20211126_1725.py @@ -0,0 +1,37 @@ +# Generated by Django 2.2.24 on 2021-11-26 16:25 + +import django.contrib.gis.db.models.fields +import django.contrib.postgres.fields.jsonb +import django.core.serializers.json +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ("core", "0026_auto_20201222_1833"), + ] + + operations = [ + migrations.AlterField( + model_name="objectrecord", + name="data", + field=django.contrib.postgres.fields.jsonb.JSONField( + default=dict, + encoder=django.core.serializers.json.DjangoJSONEncoder, + help_text="Object data, based on OBJECTTYPE", + verbose_name="data", + ), + ), + migrations.AlterField( + model_name="objectrecord", + name="geometry", + field=django.contrib.gis.db.models.fields.GeometryField( + blank=True, + help_text="Point, linestring or polygon object which represents the coordinates of the object. Geometry can be added only if the related OBJECTTYPE allows this (`OBJECTTYPE.hasGeometry = true`)", + null=True, + srid=4326, + verbose_name="geometry", + ), + ), + ] diff --git a/src/objects/core/models.py b/src/objects/core/models.py index be894719..6f9edc6c 100644 --- a/src/objects/core/models.py +++ b/src/objects/core/models.py @@ -122,7 +122,9 @@ class ObjectRecord(models.Model): blank=True, null=True, help_text=_( - "Point, linestring or polygon object which represents the coordinates of the object" + "Point, linestring or polygon object which represents the coordinates of the " + "object. Geometry can be added only if the related OBJECTTYPE allows this " + "(`OBJECTTYPE.hasGeometry = true`)" ), ) From c7eb0eef37d58c6199c808f948de0964942f79b0 Mon Sep 17 00:00:00 2001 From: Anna Shamray Date: Fri, 3 Dec 2021 11:41:42 +0100 Subject: [PATCH 4/6] :ok_hand: process PR feedback --- src/objects/api/validators.py | 2 +- src/objects/tests/v1/test_validation.py | 29 +++++++++++++++++++++++++ src/objects/tests/v2/test_validation.py | 29 +++++++++++++++++++++++++ 3 files changed, 59 insertions(+), 1 deletion(-) diff --git a/src/objects/api/validators.py b/src/objects/api/validators.py index 198e9fcb..4ad670d4 100644 --- a/src/objects/api/validators.py +++ b/src/objects/api/validators.py @@ -117,7 +117,7 @@ def __call__(self, attrs): msg = f"Object type can not be retrieved: {exc.args[0]}" raise ValidationError(msg) - has_geometry = response.get("hasGeometry") + has_geometry = response.get("hasGeometry", True) if geometry and not has_geometry: raise serializers.ValidationError(self.message, code=self.code) diff --git a/src/objects/tests/v1/test_validation.py b/src/objects/tests/v1/test_validation.py index e05fbb3c..139bb990 100644 --- a/src/objects/tests/v1/test_validation.py +++ b/src/objects/tests/v1/test_validation.py @@ -207,6 +207,35 @@ def test_create_object_geometry_not_allowed(self, m): ["This object type doesn't support geometry"], ) + def test_create_object_with_geometry_without_hasgeometry(self, m): + """test the support of Objecttypes api without hasGeometry property""" + mock_service_oas_get(m, OBJECT_TYPES_API, "objecttypes") + object_type_response = mock_objecttype(self.object_type.url) + del object_type_response["hasGeometry"] + m.get(self.object_type.url, json=object_type_response) + m.get( + f"{self.object_type.url}/versions/1", + json=mock_objecttype_version(self.object_type.url), + ) + + url = reverse("object-list") + data = { + "type": self.object_type.url, + "record": { + "typeVersion": 1, + "data": {"plantDate": "2020-04-12", "diameter": 30}, + "geometry": { + "type": "Point", + "coordinates": [4.910649523925713, 52.37240093589432], + }, + "startAt": "2020-01-01", + }, + } + + response = self.client.post(url, data, **GEO_WRITE_KWARGS) + + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + def test_update_object_with_correction_invalid(self, m): mock_service_oas_get(m, OBJECT_TYPES_API, "objecttypes") m.get( diff --git a/src/objects/tests/v2/test_validation.py b/src/objects/tests/v2/test_validation.py index e05fbb3c..139bb990 100644 --- a/src/objects/tests/v2/test_validation.py +++ b/src/objects/tests/v2/test_validation.py @@ -207,6 +207,35 @@ def test_create_object_geometry_not_allowed(self, m): ["This object type doesn't support geometry"], ) + def test_create_object_with_geometry_without_hasgeometry(self, m): + """test the support of Objecttypes api without hasGeometry property""" + mock_service_oas_get(m, OBJECT_TYPES_API, "objecttypes") + object_type_response = mock_objecttype(self.object_type.url) + del object_type_response["hasGeometry"] + m.get(self.object_type.url, json=object_type_response) + m.get( + f"{self.object_type.url}/versions/1", + json=mock_objecttype_version(self.object_type.url), + ) + + url = reverse("object-list") + data = { + "type": self.object_type.url, + "record": { + "typeVersion": 1, + "data": {"plantDate": "2020-04-12", "diameter": 30}, + "geometry": { + "type": "Point", + "coordinates": [4.910649523925713, 52.37240093589432], + }, + "startAt": "2020-01-01", + }, + } + + response = self.client.post(url, data, **GEO_WRITE_KWARGS) + + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + def test_update_object_with_correction_invalid(self, m): mock_service_oas_get(m, OBJECT_TYPES_API, "objecttypes") m.get( From 51555d16506d785bfb0394ceedec696e180f50a7 Mon Sep 17 00:00:00 2001 From: Anna Shamray Date: Fri, 3 Dec 2021 12:18:52 +0100 Subject: [PATCH 5/6] :ok_hand: rename 'hasGeometry' to 'canHaveGeometry' --- src/objects/api/v1/openapi.yaml | 6 ++++-- src/objects/api/v2/openapi.yaml | 6 ++++-- src/objects/api/validators.py | 4 ++-- ...20211126_1725.py => 0027_auto_20211203_1209.py} | 4 ++-- src/objects/core/models.py | 3 ++- src/objects/tests/utils.py | 2 +- src/objects/tests/v1/test_validation.py | 14 +++++++++----- src/objects/tests/v2/test_validation.py | 14 +++++++++----- 8 files changed, 33 insertions(+), 20 deletions(-) rename src/objects/core/migrations/{0027_auto_20211126_1725.py => 0027_auto_20211203_1209.py} (86%) diff --git a/src/objects/api/v1/openapi.yaml b/src/objects/api/v1/openapi.yaml index 35915e86..6daffbd9 100644 --- a/src/objects/api/v1/openapi.yaml +++ b/src/objects/api/v1/openapi.yaml @@ -649,7 +649,8 @@ components: nullable: true description: Point, linestring or polygon object which represents the coordinates of the object. Geometry can be added only if the related OBJECTTYPE allows - it (`OBJECTTYPE.hasGeometry = true`) + this (`OBJECTTYPE.canHaveGeometry = true` or `OBJECTTYPE.canHaveGeometry` + doesn't exist) startAt: type: string format: date @@ -795,7 +796,8 @@ components: nullable: true description: Point, linestring or polygon object which represents the coordinates of the object. Geometry can be added only if the related OBJECTTYPE allows - it (`OBJECTTYPE.hasGeometry = true`) + this (`OBJECTTYPE.canHaveGeometry = true` or `OBJECTTYPE.canHaveGeometry` + doesn't exist) startAt: type: string format: date diff --git a/src/objects/api/v2/openapi.yaml b/src/objects/api/v2/openapi.yaml index 832bce96..0b6d08a5 100644 --- a/src/objects/api/v2/openapi.yaml +++ b/src/objects/api/v2/openapi.yaml @@ -716,7 +716,8 @@ components: nullable: true description: Point, linestring or polygon object which represents the coordinates of the object. Geometry can be added only if the related OBJECTTYPE allows - it (`OBJECTTYPE.hasGeometry = true`) + this (`OBJECTTYPE.canHaveGeometry = true` or `OBJECTTYPE.canHaveGeometry` + doesn't exist) startAt: type: string format: date @@ -867,7 +868,8 @@ components: nullable: true description: Point, linestring or polygon object which represents the coordinates of the object. Geometry can be added only if the related OBJECTTYPE allows - it (`OBJECTTYPE.hasGeometry = true`) + this (`OBJECTTYPE.canHaveGeometry = true` or `OBJECTTYPE.canHaveGeometry` + doesn't exist) startAt: type: string format: date diff --git a/src/objects/api/validators.py b/src/objects/api/validators.py index 4ad670d4..a37da71b 100644 --- a/src/objects/api/validators.py +++ b/src/objects/api/validators.py @@ -117,7 +117,7 @@ def __call__(self, attrs): msg = f"Object type can not be retrieved: {exc.args[0]}" raise ValidationError(msg) - has_geometry = response.get("hasGeometry", True) + can_have_geometry = response.get("canHaveGeometry", True) - if geometry and not has_geometry: + if geometry and not can_have_geometry: raise serializers.ValidationError(self.message, code=self.code) diff --git a/src/objects/core/migrations/0027_auto_20211126_1725.py b/src/objects/core/migrations/0027_auto_20211203_1209.py similarity index 86% rename from src/objects/core/migrations/0027_auto_20211126_1725.py rename to src/objects/core/migrations/0027_auto_20211203_1209.py index 6cd53951..5b3a03c6 100644 --- a/src/objects/core/migrations/0027_auto_20211126_1725.py +++ b/src/objects/core/migrations/0027_auto_20211203_1209.py @@ -1,4 +1,4 @@ -# Generated by Django 2.2.24 on 2021-11-26 16:25 +# Generated by Django 2.2.24 on 2021-12-03 11:09 import django.contrib.gis.db.models.fields import django.contrib.postgres.fields.jsonb @@ -28,7 +28,7 @@ class Migration(migrations.Migration): name="geometry", field=django.contrib.gis.db.models.fields.GeometryField( blank=True, - help_text="Point, linestring or polygon object which represents the coordinates of the object. Geometry can be added only if the related OBJECTTYPE allows this (`OBJECTTYPE.hasGeometry = true`)", + help_text="Point, linestring or polygon object which represents the coordinates of the object. Geometry can be added only if the related OBJECTTYPE allows this (`OBJECTTYPE.canHaveGeometry = true` or `OBJECTTYPE.canHaveGeometry` doesn't exist)", null=True, srid=4326, verbose_name="geometry", diff --git a/src/objects/core/models.py b/src/objects/core/models.py index 6f9edc6c..b7b4ecd5 100644 --- a/src/objects/core/models.py +++ b/src/objects/core/models.py @@ -124,7 +124,8 @@ class ObjectRecord(models.Model): help_text=_( "Point, linestring or polygon object which represents the coordinates of the " "object. Geometry can be added only if the related OBJECTTYPE allows this " - "(`OBJECTTYPE.hasGeometry = true`)" + "(`OBJECTTYPE.canHaveGeometry = true` or `OBJECTTYPE.canHaveGeometry` doesn't " + "exist)" ), ) diff --git a/src/objects/tests/utils.py b/src/objects/tests/utils.py index d1fac22e..7586a251 100644 --- a/src/objects/tests/utils.py +++ b/src/objects/tests/utils.py @@ -53,7 +53,7 @@ def mock_objecttype(url: str, attrs=None) -> dict: "labels": {}, "createdAt": "2020-12-01", "modifiedAt": "2020-12-01", - "hasGeometry": True, + "canHaveGeometry": True, "versions": [ f"{url}/versions/1", ], diff --git a/src/objects/tests/v1/test_validation.py b/src/objects/tests/v1/test_validation.py index 139bb990..a7b228c4 100644 --- a/src/objects/tests/v1/test_validation.py +++ b/src/objects/tests/v1/test_validation.py @@ -182,7 +182,9 @@ def test_create_object_geometry_not_allowed(self, m): ) m.get( self.object_type.url, - json=mock_objecttype(self.object_type.url, attrs={"hasGeometry": False}), + json=mock_objecttype( + self.object_type.url, attrs={"canHaveGeometry": False} + ), ) url = reverse("object-list") @@ -207,11 +209,11 @@ def test_create_object_geometry_not_allowed(self, m): ["This object type doesn't support geometry"], ) - def test_create_object_with_geometry_without_hasgeometry(self, m): - """test the support of Objecttypes api without hasGeometry property""" + def test_create_object_with_geometry_without_canHaveGeometry(self, m): + """test the support of Objecttypes api without canHaveGeometry property""" mock_service_oas_get(m, OBJECT_TYPES_API, "objecttypes") object_type_response = mock_objecttype(self.object_type.url) - del object_type_response["hasGeometry"] + del object_type_response["canHaveGeometry"] m.get(self.object_type.url, json=object_type_response) m.get( f"{self.object_type.url}/versions/1", @@ -327,7 +329,9 @@ def test_update_geometry_not_allowed(self, m): ) m.get( self.object_type.url, - json=mock_objecttype(self.object_type.url, attrs={"hasGeometry": False}), + json=mock_objecttype( + self.object_type.url, attrs={"canHaveGeometry": False} + ), ) initial_record = ObjectRecordFactory.create( diff --git a/src/objects/tests/v2/test_validation.py b/src/objects/tests/v2/test_validation.py index 139bb990..a7b228c4 100644 --- a/src/objects/tests/v2/test_validation.py +++ b/src/objects/tests/v2/test_validation.py @@ -182,7 +182,9 @@ def test_create_object_geometry_not_allowed(self, m): ) m.get( self.object_type.url, - json=mock_objecttype(self.object_type.url, attrs={"hasGeometry": False}), + json=mock_objecttype( + self.object_type.url, attrs={"canHaveGeometry": False} + ), ) url = reverse("object-list") @@ -207,11 +209,11 @@ def test_create_object_geometry_not_allowed(self, m): ["This object type doesn't support geometry"], ) - def test_create_object_with_geometry_without_hasgeometry(self, m): - """test the support of Objecttypes api without hasGeometry property""" + def test_create_object_with_geometry_without_canHaveGeometry(self, m): + """test the support of Objecttypes api without canHaveGeometry property""" mock_service_oas_get(m, OBJECT_TYPES_API, "objecttypes") object_type_response = mock_objecttype(self.object_type.url) - del object_type_response["hasGeometry"] + del object_type_response["canHaveGeometry"] m.get(self.object_type.url, json=object_type_response) m.get( f"{self.object_type.url}/versions/1", @@ -327,7 +329,9 @@ def test_update_geometry_not_allowed(self, m): ) m.get( self.object_type.url, - json=mock_objecttype(self.object_type.url, attrs={"hasGeometry": False}), + json=mock_objecttype( + self.object_type.url, attrs={"canHaveGeometry": False} + ), ) initial_record = ObjectRecordFactory.create( From 28789a8af1910b95372ad739e5b5b125d0cecaaa Mon Sep 17 00:00:00 2001 From: Anna Shamray Date: Fri, 3 Dec 2021 17:11:48 +0100 Subject: [PATCH 6/6] :ok_hand: rename 'canHaveGeometry' to 'allowGeometry' --- src/objects/api/v1/openapi.yaml | 4 ++-- src/objects/api/v2/openapi.yaml | 4 ++-- src/objects/api/validators.py | 4 ++-- .../core/migrations/0027_auto_20211203_1209.py | 2 +- src/objects/core/models.py | 2 +- src/objects/tests/utils.py | 2 +- src/objects/tests/v1/test_validation.py | 14 +++++--------- src/objects/tests/v2/test_validation.py | 14 +++++--------- 8 files changed, 19 insertions(+), 27 deletions(-) diff --git a/src/objects/api/v1/openapi.yaml b/src/objects/api/v1/openapi.yaml index 6daffbd9..6e014798 100644 --- a/src/objects/api/v1/openapi.yaml +++ b/src/objects/api/v1/openapi.yaml @@ -649,7 +649,7 @@ components: nullable: true description: Point, linestring or polygon object which represents the coordinates of the object. Geometry can be added only if the related OBJECTTYPE allows - this (`OBJECTTYPE.canHaveGeometry = true` or `OBJECTTYPE.canHaveGeometry` + this (`OBJECTTYPE.allowGeometry = true` or `OBJECTTYPE.allowGeometry` doesn't exist) startAt: type: string @@ -796,7 +796,7 @@ components: nullable: true description: Point, linestring or polygon object which represents the coordinates of the object. Geometry can be added only if the related OBJECTTYPE allows - this (`OBJECTTYPE.canHaveGeometry = true` or `OBJECTTYPE.canHaveGeometry` + this (`OBJECTTYPE.allowGeometry = true` or `OBJECTTYPE.allowGeometry` doesn't exist) startAt: type: string diff --git a/src/objects/api/v2/openapi.yaml b/src/objects/api/v2/openapi.yaml index 0b6d08a5..4883f817 100644 --- a/src/objects/api/v2/openapi.yaml +++ b/src/objects/api/v2/openapi.yaml @@ -716,7 +716,7 @@ components: nullable: true description: Point, linestring or polygon object which represents the coordinates of the object. Geometry can be added only if the related OBJECTTYPE allows - this (`OBJECTTYPE.canHaveGeometry = true` or `OBJECTTYPE.canHaveGeometry` + this (`OBJECTTYPE.allowGeometry = true` or `OBJECTTYPE.allowGeometry` doesn't exist) startAt: type: string @@ -868,7 +868,7 @@ components: nullable: true description: Point, linestring or polygon object which represents the coordinates of the object. Geometry can be added only if the related OBJECTTYPE allows - this (`OBJECTTYPE.canHaveGeometry = true` or `OBJECTTYPE.canHaveGeometry` + this (`OBJECTTYPE.allowGeometry = true` or `OBJECTTYPE.allowGeometry` doesn't exist) startAt: type: string diff --git a/src/objects/api/validators.py b/src/objects/api/validators.py index a37da71b..0f24d26b 100644 --- a/src/objects/api/validators.py +++ b/src/objects/api/validators.py @@ -117,7 +117,7 @@ def __call__(self, attrs): msg = f"Object type can not be retrieved: {exc.args[0]}" raise ValidationError(msg) - can_have_geometry = response.get("canHaveGeometry", True) + allow_geometry = response.get("allowGeometry", True) - if geometry and not can_have_geometry: + if geometry and not allow_geometry: raise serializers.ValidationError(self.message, code=self.code) diff --git a/src/objects/core/migrations/0027_auto_20211203_1209.py b/src/objects/core/migrations/0027_auto_20211203_1209.py index 5b3a03c6..c488d33c 100644 --- a/src/objects/core/migrations/0027_auto_20211203_1209.py +++ b/src/objects/core/migrations/0027_auto_20211203_1209.py @@ -28,7 +28,7 @@ class Migration(migrations.Migration): name="geometry", field=django.contrib.gis.db.models.fields.GeometryField( blank=True, - help_text="Point, linestring or polygon object which represents the coordinates of the object. Geometry can be added only if the related OBJECTTYPE allows this (`OBJECTTYPE.canHaveGeometry = true` or `OBJECTTYPE.canHaveGeometry` doesn't exist)", + help_text="Point, linestring or polygon object which represents the coordinates of the object. Geometry can be added only if the related OBJECTTYPE allows this (`OBJECTTYPE.allowGeometry = true` or `OBJECTTYPE.allowGeometry` doesn't exist)", null=True, srid=4326, verbose_name="geometry", diff --git a/src/objects/core/models.py b/src/objects/core/models.py index b7b4ecd5..74dec09e 100644 --- a/src/objects/core/models.py +++ b/src/objects/core/models.py @@ -124,7 +124,7 @@ class ObjectRecord(models.Model): help_text=_( "Point, linestring or polygon object which represents the coordinates of the " "object. Geometry can be added only if the related OBJECTTYPE allows this " - "(`OBJECTTYPE.canHaveGeometry = true` or `OBJECTTYPE.canHaveGeometry` doesn't " + "(`OBJECTTYPE.allowGeometry = true` or `OBJECTTYPE.allowGeometry` doesn't " "exist)" ), ) diff --git a/src/objects/tests/utils.py b/src/objects/tests/utils.py index 7586a251..94f96a57 100644 --- a/src/objects/tests/utils.py +++ b/src/objects/tests/utils.py @@ -53,7 +53,7 @@ def mock_objecttype(url: str, attrs=None) -> dict: "labels": {}, "createdAt": "2020-12-01", "modifiedAt": "2020-12-01", - "canHaveGeometry": True, + "allowGeometry": True, "versions": [ f"{url}/versions/1", ], diff --git a/src/objects/tests/v1/test_validation.py b/src/objects/tests/v1/test_validation.py index a7b228c4..c581d7ee 100644 --- a/src/objects/tests/v1/test_validation.py +++ b/src/objects/tests/v1/test_validation.py @@ -182,9 +182,7 @@ def test_create_object_geometry_not_allowed(self, m): ) m.get( self.object_type.url, - json=mock_objecttype( - self.object_type.url, attrs={"canHaveGeometry": False} - ), + json=mock_objecttype(self.object_type.url, attrs={"allowGeometry": False}), ) url = reverse("object-list") @@ -209,11 +207,11 @@ def test_create_object_geometry_not_allowed(self, m): ["This object type doesn't support geometry"], ) - def test_create_object_with_geometry_without_canHaveGeometry(self, m): - """test the support of Objecttypes api without canHaveGeometry property""" + def test_create_object_with_geometry_without_allowGeometry(self, m): + """test the support of Objecttypes api without allowGeometry property""" mock_service_oas_get(m, OBJECT_TYPES_API, "objecttypes") object_type_response = mock_objecttype(self.object_type.url) - del object_type_response["canHaveGeometry"] + del object_type_response["allowGeometry"] m.get(self.object_type.url, json=object_type_response) m.get( f"{self.object_type.url}/versions/1", @@ -329,9 +327,7 @@ def test_update_geometry_not_allowed(self, m): ) m.get( self.object_type.url, - json=mock_objecttype( - self.object_type.url, attrs={"canHaveGeometry": False} - ), + json=mock_objecttype(self.object_type.url, attrs={"allowGeometry": False}), ) initial_record = ObjectRecordFactory.create( diff --git a/src/objects/tests/v2/test_validation.py b/src/objects/tests/v2/test_validation.py index a7b228c4..c581d7ee 100644 --- a/src/objects/tests/v2/test_validation.py +++ b/src/objects/tests/v2/test_validation.py @@ -182,9 +182,7 @@ def test_create_object_geometry_not_allowed(self, m): ) m.get( self.object_type.url, - json=mock_objecttype( - self.object_type.url, attrs={"canHaveGeometry": False} - ), + json=mock_objecttype(self.object_type.url, attrs={"allowGeometry": False}), ) url = reverse("object-list") @@ -209,11 +207,11 @@ def test_create_object_geometry_not_allowed(self, m): ["This object type doesn't support geometry"], ) - def test_create_object_with_geometry_without_canHaveGeometry(self, m): - """test the support of Objecttypes api without canHaveGeometry property""" + def test_create_object_with_geometry_without_allowGeometry(self, m): + """test the support of Objecttypes api without allowGeometry property""" mock_service_oas_get(m, OBJECT_TYPES_API, "objecttypes") object_type_response = mock_objecttype(self.object_type.url) - del object_type_response["canHaveGeometry"] + del object_type_response["allowGeometry"] m.get(self.object_type.url, json=object_type_response) m.get( f"{self.object_type.url}/versions/1", @@ -329,9 +327,7 @@ def test_update_geometry_not_allowed(self, m): ) m.get( self.object_type.url, - json=mock_objecttype( - self.object_type.url, attrs={"canHaveGeometry": False} - ), + json=mock_objecttype(self.object_type.url, attrs={"allowGeometry": False}), ) initial_record = ObjectRecordFactory.create(