Skip to content

Commit

Permalink
[#467] Setup objecttypes through django-setup-configuration (#492)
Browse files Browse the repository at this point in the history
  • Loading branch information
SonnyBA authored Dec 13, 2024
1 parent 2604513 commit a2678c7
Show file tree
Hide file tree
Showing 14 changed files with 394 additions and 6 deletions.
16 changes: 16 additions & 0 deletions docker/setup_configuration/data.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
zgw_consumers_config_enable: true
zgw_consumers:
services:
- identifier: objecttypes-api
label: Objecttypes API
api_root: http://objecttypes.local/api/v1/
api_connection_check_path: objecttypes
api_type: orc
auth_type: api_key
header_key: Authorization
header_value: Token b9f100590925b529664ed9d370f5f8da124b2c20

- identifier: notifications-api
label: Notificaties API
api_root: http://notificaties.local/api/v1/
Expand All @@ -16,3 +25,10 @@ notifications_config:
notification_delivery_max_retries: 1
notification_delivery_retry_backoff: 2
notification_delivery_retry_backoff_max: 3

objecttypes_config_enable: true
objecttypes:
items:
- uuid: b427ef84-189d-43aa-9efd-7bb2c459e281
name: Object Type 1
service_identifier: objecttypes-api
75 changes: 71 additions & 4 deletions docs/installation/config_cli.rst
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,81 @@ format, use by each step.
Objects API
===========

Objecttypes configuration
-------------------------

To configure objecttypes the following configuration could be used:

.. code-block:: yaml
...
zgw_consumers_config_enable: true
zgw_consumers:
services:
- identifier: objecttypen-foo
label: Objecttypen API Foo
api_root: http://objecttypen.foo/api/v1/
api_type: orc
auth_type: api_key
header_key: Authorization
header_value: Token ba9d233e95e04c4a8a661a27daffe7c9bd019067
- identifier: objecttypen-bar
label: Objecttypen API Bar
api_root: http://objecttypen.bar/api/v1/
api_type: orc
auth_type: api_key
header_key: Authorization
header_value: Token b9f100590925b529664ed9d370f5f8da124b2c20
objecttypes_config_enable: true
objecttypes:
items:
- uuid: b427ef84-189d-43aa-9efd-7bb2c459e281
name: Object Type 1
service_identifier: objecttypen-foo
- uuid: b0e8553f-8b1a-4d55-ab90-6d02f1bcf2c2
name: Object Type 2
service_identifier: objecttypen-bar
...
.. note:: The ``uuid`` field will be used to lookup existing ``ObjectType``'s.

Objecttypes require a corresponding ``Service`` to work correctly. Creating
these ``Service``'s can be done by defining these in the same yaml file. ``Service``
instances will be created before the ``ObjectType``'s are created.

Objecttypes connection configuration
------------------------------------

Tokens configuration
-------------------
In order to be able to retrieve objecttypes, a corresponding ``Service`` should be
created. An example of a configuration could be seen below:

Notifications configuration
-------------------------
.. code-block:: yaml
...
zgw_consumers_config_enable: true
zgw_consumers:
services:
- identifier: objecttypes-api-1
label: Objecttypes API 1
api_root: http://objecttypes-1.local/api/v1/
api_connection_check_path: objecttypes
api_type: orc
auth_type: api_key
header_key: Authorization
header_value: Token ba9d233e95e04c4a8a661a27daffe7c9bd019067
- identifier: objecttypes-api-2
label: Objecttypes API 2
api_root: http://objecttypes-2.local/api/v1/
api_connection_check_path: objecttypes
api_type: orc
auth_type: api_key
header_key: Authorization
header_value: Token b9f100590925b529664ed9d370f5f8da124b2c20
....
Tokens configuration
--------------------

Mozilla-django-oidc-db
----------------------
Expand Down
2 changes: 2 additions & 0 deletions src/objects/conf/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"rest_framework_gis",
# Project applications.
"objects.accounts",
"objects.setup_configuration",
"objects.api",
"objects.core",
"objects.token",
Expand Down Expand Up @@ -85,4 +86,5 @@
SETUP_CONFIGURATION_STEPS = (
"zgw_consumers.contrib.setup_configuration.steps.ServiceConfigurationStep",
"notifications_api_common.contrib.setup_configuration.steps.NotificationConfigurationStep",
"objects.setup_configuration.steps.objecttypes.ObjectTypesConfigurationStep",
)
11 changes: 9 additions & 2 deletions src/objects/core/models.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import datetime
import uuid
from typing import Iterable

from django.contrib.gis.db.models import GeometryField
from django.core.exceptions import ValidationError
Expand Down Expand Up @@ -41,8 +42,14 @@ def url(self):
# zds_client.get_operation_url() can be used here but it increases HTTP overhead
return f"{self.service.api_root}objecttypes/{self.uuid}"

def clean(self):
def clean_fields(self, exclude: Iterable[str] | None = None) -> None:
super().clean_fields(exclude=exclude)

if exclude and "service" in exclude:
return

client = build_client(self.service)

try:
response = client.get(url=self.url)
except (requests.RequestException, ConnectionError, ValueError) as exc:
Expand All @@ -51,7 +58,7 @@ def clean(self):
try:
object_type_data = response.json()
except requests.exceptions.JSONDecodeError:
ValidationError("Object type version didn't have any data")
raise ValidationError("Object type version didn't have any data")

if not self._name:
self._name = object_type_data["name"]
Expand Down
10 changes: 10 additions & 0 deletions src/objects/core/tests/files/objecttypes_empty_database.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
objecttypes_config_enable: true
objecttypes:
items:
- uuid: b427ef84-189d-43aa-9efd-7bb2c459e281
name: Object Type 1
service_identifier: service-1

- uuid: b0e8553f-8b1a-4d55-ab90-6d02f1bcf2c2
name: Object Type 2
service_identifier: service-2
10 changes: 10 additions & 0 deletions src/objects/core/tests/files/objecttypes_existing_objecttype.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
objecttypes_config_enable: true
objecttypes:
items:
- uuid: b427ef84-189d-43aa-9efd-7bb2c459e281
name: Object Type 1
service_identifier: service-1

- uuid: 7229549b-7b41-47d1-8106-414b2a69751b
name: Object Type 3
service_identifier: service-2
10 changes: 10 additions & 0 deletions src/objects/core/tests/files/objecttypes_idempotent.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
objecttypes_config_enable: true
objecttypes:
items:
- uuid: b427ef84-189d-43aa-9efd-7bb2c459e281
name: Object Type 1
service_identifier: service-1

- uuid: b0e8553f-8b1a-4d55-ab90-6d02f1bcf2c2
name: Object Type 2
service_identifier: service-2
10 changes: 10 additions & 0 deletions src/objects/core/tests/files/objecttypes_invalid_uuid.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
objecttypes_config_enable: true
objecttypes:
items:
- uuid: b427ef84-189d-43aa-9efd-7bb2c459e281
name: Object Type 1
service_identifier: service-1

- uuid: foobar
name: Object Type 2
service_identifier: service-1
10 changes: 10 additions & 0 deletions src/objects/core/tests/files/objecttypes_unknown_service.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
objecttypes_config_enable: true
objecttypes:
items:
- uuid: b427ef84-189d-43aa-9efd-7bb2c459e281
name: Object Type 1
service_identifier: unknown

- uuid: b0e8553f-8b1a-4d55-ab90-6d02f1bcf2c2
name: Object Type 2
service_identifier: service-1
172 changes: 172 additions & 0 deletions src/objects/core/tests/test_objecttype_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
from pathlib import Path

from django.db.models import QuerySet
from django.test import TestCase

from django_setup_configuration.exceptions import ConfigurationRunFailed
from django_setup_configuration.test_utils import execute_single_step
from zgw_consumers.models import Service
from zgw_consumers.test.factories import ServiceFactory

from objects.core.models import ObjectType
from objects.core.tests.factories import ObjectTypeFactory
from objects.setup_configuration.steps.objecttypes import ObjectTypesConfigurationStep

TEST_FILES = (Path(__file__).parent / "files").resolve()


class ObjectTypesConfigurationStepTests(TestCase):
def test_empty_database(self):
service_1 = ServiceFactory(slug="service-1")
service_2 = ServiceFactory(slug="service-2")

test_file_path = str(TEST_FILES / "objecttypes_empty_database.yaml")

execute_single_step(ObjectTypesConfigurationStep, yaml_source=test_file_path)

objecttypes: QuerySet[ObjectType] = ObjectType.objects.order_by("_name")

self.assertEqual(objecttypes.count(), 2)

objecttype_1: ObjectType = objecttypes.first()

self.assertEqual(str(objecttype_1.uuid), "b427ef84-189d-43aa-9efd-7bb2c459e281")
self.assertEqual(objecttype_1._name, "Object Type 1")
self.assertEqual(objecttype_1.service, service_1)

objecttype_2: ObjectType = objecttypes.last()

self.assertEqual(str(objecttype_2.uuid), "b0e8553f-8b1a-4d55-ab90-6d02f1bcf2c2")
self.assertEqual(objecttype_2._name, "Object Type 2")
self.assertEqual(objecttype_2.service, service_2)

def test_existing_objecttype(self):
test_file_path = str(TEST_FILES / "objecttypes_existing_objecttype.yaml")

service_1: Service = ServiceFactory(slug="service-1")
service_2: Service = ServiceFactory(slug="service-2")

objecttype_1: ObjectType = ObjectTypeFactory(
service=service_1,
uuid="b427ef84-189d-43aa-9efd-7bb2c459e281",
_name="Object Type 001",
)
objecttype_2: ObjectType = ObjectTypeFactory(
service=service_2,
uuid="b0e8553f-8b1a-4d55-ab90-6d02f1bcf2c2",
_name="Object Type 002",
)

execute_single_step(ObjectTypesConfigurationStep, yaml_source=test_file_path)

self.assertEqual(ObjectType.objects.count(), 3)

objecttype_1.refresh_from_db()

self.assertEqual(str(objecttype_1.uuid), "b427ef84-189d-43aa-9efd-7bb2c459e281")
self.assertEqual(objecttype_1._name, "Object Type 1")
self.assertEqual(objecttype_1.service, service_1)

objecttype_2.refresh_from_db()

self.assertEqual(str(objecttype_2.uuid), "b0e8553f-8b1a-4d55-ab90-6d02f1bcf2c2")
self.assertEqual(objecttype_2._name, "Object Type 002")
self.assertEqual(objecttype_2.service, service_2)

objecttype_3: ObjectType = ObjectType.objects.get(
uuid="7229549b-7b41-47d1-8106-414b2a69751b"
)

self.assertEqual(str(objecttype_3.uuid), "7229549b-7b41-47d1-8106-414b2a69751b")
self.assertEqual(objecttype_3._name, "Object Type 3")
self.assertEqual(objecttype_3.service, service_2)

def test_unknown_service(self):
service = ServiceFactory(slug="service-1")

objecttype: ObjectType = ObjectTypeFactory(
uuid="b427ef84-189d-43aa-9efd-7bb2c459e281",
_name="Object Type 001",
service=service,
)

test_file_path = str(TEST_FILES / "objecttypes_unknown_service.yaml")

with self.assertRaises(ConfigurationRunFailed):
execute_single_step(
ObjectTypesConfigurationStep, yaml_source=test_file_path
)

self.assertEqual(ObjectType.objects.count(), 1)

objecttype.refresh_from_db()

self.assertEqual(str(objecttype.uuid), "b427ef84-189d-43aa-9efd-7bb2c459e281")
self.assertEqual(objecttype._name, "Object Type 001")
self.assertEqual(objecttype.service, service)

def test_invalid_uuid(self):
test_file_path = str(TEST_FILES / "objecttypes_invalid_uuid.yaml")

service: Service = ServiceFactory(slug="service-1")

objecttype: ObjectType = ObjectTypeFactory(
service=service,
uuid="b427ef84-189d-43aa-9efd-7bb2c459e281",
_name="Object Type 001",
)

with self.assertRaises(ConfigurationRunFailed):
execute_single_step(
ObjectTypesConfigurationStep, yaml_source=test_file_path
)

self.assertEqual(ObjectType.objects.count(), 1)

objecttype.refresh_from_db()

self.assertEqual(str(objecttype.uuid), "b427ef84-189d-43aa-9efd-7bb2c459e281")
self.assertEqual(objecttype._name, "Object Type 1")
self.assertEqual(objecttype.service, service)

def test_idempotent_step(self):
service_1 = ServiceFactory(slug="service-1")
service_2 = ServiceFactory(slug="service-2")

test_file_path = str(TEST_FILES / "objecttypes_idempotent.yaml")

execute_single_step(ObjectTypesConfigurationStep, yaml_source=test_file_path)

objecttypes: QuerySet[ObjectType] = ObjectType.objects.order_by("_name")

self.assertEqual(objecttypes.count(), 2)

objecttype_1: ObjectType = objecttypes.first()

self.assertEqual(str(objecttype_1.uuid), "b427ef84-189d-43aa-9efd-7bb2c459e281")
self.assertEqual(objecttype_1._name, "Object Type 1")
self.assertEqual(objecttype_1.service, service_1)

objecttype_2: ObjectType = objecttypes.last()

self.assertEqual(str(objecttype_2.uuid), "b0e8553f-8b1a-4d55-ab90-6d02f1bcf2c2")
self.assertEqual(objecttype_2._name, "Object Type 2")
self.assertEqual(objecttype_2.service, service_2)

# Rerun
execute_single_step(ObjectTypesConfigurationStep, yaml_source=test_file_path)

objecttype_1.refresh_from_db()
objecttype_2.refresh_from_db()

self.assertEqual(ObjectType.objects.count(), 2)

# objecttype 1
self.assertEqual(str(objecttype_1.uuid), "b427ef84-189d-43aa-9efd-7bb2c459e281")
self.assertEqual(objecttype_1._name, "Object Type 1")
self.assertEqual(objecttype_1.service, service_1)

# objecttype 2
self.assertEqual(str(objecttype_2.uuid), "b0e8553f-8b1a-4d55-ab90-6d02f1bcf2c2")
self.assertEqual(objecttype_2._name, "Object Type 2")
self.assertEqual(objecttype_2.service, service_2)
Empty file.
Loading

0 comments on commit a2678c7

Please sign in to comment.