From a9486c1e6e9dabf27d40535e926a73c0fcf656fe Mon Sep 17 00:00:00 2001 From: Steven Bal Date: Mon, 2 Dec 2024 13:57:43 +0100 Subject: [PATCH] :sparkles: [#4789] ConfigurationStep for ZGW API registration config this step relies on the previously added ServiceConfigurationStep and can be used to set up the necessary configuration for the ZGW API registration backend --- src/openforms/conf/base.py | 1 + .../objects_api/setup_configuration/steps.py | 13 +- .../zgw/setup_configuration/__init__.py | 0 .../contrib/zgw/setup_configuration/models.py | 48 +++ .../contrib/zgw/setup_configuration/steps.py | 42 +++ .../zgw/setup_configuration/tests/__init__.py | 0 .../tests/files/setup_config.yaml | 32 ++ .../tests/files/setup_config_all_fields.yaml | 23 ++ .../files/setup_config_required_fields.yaml | 9 + .../tests/test_zgw_api_config.py | 289 ++++++++++++++++++ src/openforms/utils/services.py | 12 + 11 files changed, 457 insertions(+), 12 deletions(-) create mode 100644 src/openforms/contrib/zgw/setup_configuration/__init__.py create mode 100644 src/openforms/contrib/zgw/setup_configuration/models.py create mode 100644 src/openforms/contrib/zgw/setup_configuration/steps.py create mode 100644 src/openforms/contrib/zgw/setup_configuration/tests/__init__.py create mode 100644 src/openforms/contrib/zgw/setup_configuration/tests/files/setup_config.yaml create mode 100644 src/openforms/contrib/zgw/setup_configuration/tests/files/setup_config_all_fields.yaml create mode 100644 src/openforms/contrib/zgw/setup_configuration/tests/files/setup_config_required_fields.yaml create mode 100644 src/openforms/contrib/zgw/setup_configuration/tests/test_zgw_api_config.py create mode 100644 src/openforms/utils/services.py diff --git a/src/openforms/conf/base.py b/src/openforms/conf/base.py index 3b35decec6..bee93636b3 100644 --- a/src/openforms/conf/base.py +++ b/src/openforms/conf/base.py @@ -1212,6 +1212,7 @@ SETUP_CONFIGURATION_STEPS = [ "zgw_consumers.contrib.setup_configuration.steps.ServiceConfigurationStep", "openforms.contrib.objects_api.setup_configuration.steps.ObjectsAPIConfigurationStep", + "openforms.contrib.zgw.setup_configuration.steps.ZGWApiConfigurationStep", ] # diff --git a/src/openforms/contrib/objects_api/setup_configuration/steps.py b/src/openforms/contrib/objects_api/setup_configuration/steps.py index 34e6baa667..bd238ffa68 100644 --- a/src/openforms/contrib/objects_api/setup_configuration/steps.py +++ b/src/openforms/contrib/objects_api/setup_configuration/steps.py @@ -1,22 +1,11 @@ from django_setup_configuration.configuration import BaseConfigurationStep -from zgw_consumers.models import Service from openforms.contrib.objects_api.models import ObjectsAPIGroupConfig +from openforms.utils.services import get_service from .models import ObjectsAPIGroupConfigModel, SingleObjectsAPIGroupConfigModel -def get_service(slug: str) -> Service: - """ - Try to find a Service and re-raise DoesNotExist with the identifier to make debugging - easier - """ - try: - return Service.objects.get(slug=slug) - except Service.DoesNotExist as e: - raise Service.DoesNotExist(f"{str(e)} (identifier = {slug})") - - class ObjectsAPIConfigurationStep(BaseConfigurationStep[ObjectsAPIGroupConfigModel]): """ Configure configuration groups for the Objects API backend diff --git a/src/openforms/contrib/zgw/setup_configuration/__init__.py b/src/openforms/contrib/zgw/setup_configuration/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/openforms/contrib/zgw/setup_configuration/models.py b/src/openforms/contrib/zgw/setup_configuration/models.py new file mode 100644 index 0000000000..83520aa272 --- /dev/null +++ b/src/openforms/contrib/zgw/setup_configuration/models.py @@ -0,0 +1,48 @@ +from django_setup_configuration.fields import DjangoModelRef +from django_setup_configuration.models import ConfigurationModel +from pydantic import Field + +from openforms.registrations.contrib.zgw_apis.models import ZGWApiGroupConfig + + +class SingleZGWApiGroupConfigModel(ConfigurationModel): + zaken_service_identifier: str = DjangoModelRef(ZGWApiGroupConfig, "zrc_service") + documenten_service_identifier: str = DjangoModelRef( + ZGWApiGroupConfig, + "drc_service", + ) + catalogi_service_identifier: str = DjangoModelRef( + ZGWApiGroupConfig, + "ztc_service", + ) + + # Slightly more descriptive name + objects_api_json_content_template: str = DjangoModelRef( + ZGWApiGroupConfig, "content_json" + ) + + # FIXME choices and blank=True doesn't seem to be picked up properly + zaak_vertrouwelijkheidaanduiding: str = DjangoModelRef( + ZGWApiGroupConfig, + "zaak_vertrouwelijkheidaanduiding", + default="", + ) + doc_vertrouwelijkheidaanduiding: str = DjangoModelRef( + ZGWApiGroupConfig, "doc_vertrouwelijkheidaanduiding", default="" + ) + + class Meta: + django_model_refs = { + ZGWApiGroupConfig: [ + "name", + "identifier", + "catalogue_domain", + "catalogue_rsin", + "organisatie_rsin", + "auteur", + ] + } + + +class ZGWApiGroupConfigModel(ConfigurationModel): + groups: list[SingleZGWApiGroupConfigModel] = Field(default_factory=list) diff --git a/src/openforms/contrib/zgw/setup_configuration/steps.py b/src/openforms/contrib/zgw/setup_configuration/steps.py new file mode 100644 index 0000000000..14a57b77fe --- /dev/null +++ b/src/openforms/contrib/zgw/setup_configuration/steps.py @@ -0,0 +1,42 @@ +from django_setup_configuration.configuration import BaseConfigurationStep + +from openforms.registrations.contrib.zgw_apis.models import ZGWApiGroupConfig +from openforms.utils.services import get_service + +from .models import SingleZGWApiGroupConfigModel, ZGWApiGroupConfigModel + + +class ZGWApiConfigurationStep(BaseConfigurationStep[ZGWApiGroupConfigModel]): + """ + Configure configuration groups for the ZGW API backend + """ + + verbose_name = "Configuration to set up ZGW API registration backend services" + config_model = ZGWApiGroupConfigModel + namespace = "zgw_api" + enable_setting = "zgw_api_config_enable" + + def execute(self, model: ZGWApiGroupConfigModel): + config: SingleZGWApiGroupConfigModel + for config in model.groups: + # setup_configuration typing doesn't work for `django_model_refs` yet, + # hence the type: ignores + # (https://github.com/maykinmedia/django-setup-configuration/issues/25) + defaults = { + "name": config.name, # type: ignore + "zrc_service": get_service(config.zaken_service_identifier), + "drc_service": get_service(config.documenten_service_identifier), + "ztc_service": get_service(config.catalogi_service_identifier), + "catalogue_domain": config.catalogue_domain, # type: ignore + "catalogue_rsin": config.catalogue_rsin, # type: ignore + "organisatie_rsin": config.organisatie_rsin, # type: ignore + "zaak_vertrouwelijkheidaanduiding": config.zaak_vertrouwelijkheidaanduiding, + "doc_vertrouwelijkheidaanduiding": config.doc_vertrouwelijkheidaanduiding, + "auteur": config.auteur, # type: ignore + "content_json": config.objects_api_json_content_template, + } + + ZGWApiGroupConfig.objects.update_or_create( + identifier=config.identifier, + defaults=defaults, + ) diff --git a/src/openforms/contrib/zgw/setup_configuration/tests/__init__.py b/src/openforms/contrib/zgw/setup_configuration/tests/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/openforms/contrib/zgw/setup_configuration/tests/files/setup_config.yaml b/src/openforms/contrib/zgw/setup_configuration/tests/files/setup_config.yaml new file mode 100644 index 0000000000..520a5d83f6 --- /dev/null +++ b/src/openforms/contrib/zgw/setup_configuration/tests/files/setup_config.yaml @@ -0,0 +1,32 @@ +zgw_api_config_enable: True +zgw_api: + groups: + - name: Config 1 + identifier: config-1 + zaken_service_identifier: zaken-test + documenten_service_identifier: documenten-test + catalogi_service_identifier: catalogi-test + catalogue_domain: TEST + catalogue_rsin: "000000000" + organisatie_rsin: "000000000" + zaak_vertrouwelijkheidaanduiding: zaakvertrouwelijk + doc_vertrouwelijkheidaanduiding: openbaar + auteur: John Doe + objects_api_json_content_template: | + { + "data": {% json_summary %}, + "type": "{{ productaanvraag_type }}", + "bsn": "{{ variables.auth_bsn }}", + "submission_id": "{{ submission.kenmerk }}", + "language_code": "{{ submission.language_code }}", + "custom_field": "foo" + } + - name: Config 2 + identifier: config-2 + zaken_service_identifier: zaken-test + documenten_service_identifier: documenten-test + catalogi_service_identifier: catalogi-test + catalogue_domain: OTHER + catalogue_rsin: "000000000" + organisatie_rsin: "000000000" + auteur: Jane Doe \ No newline at end of file diff --git a/src/openforms/contrib/zgw/setup_configuration/tests/files/setup_config_all_fields.yaml b/src/openforms/contrib/zgw/setup_configuration/tests/files/setup_config_all_fields.yaml new file mode 100644 index 0000000000..8ce6667fba --- /dev/null +++ b/src/openforms/contrib/zgw/setup_configuration/tests/files/setup_config_all_fields.yaml @@ -0,0 +1,23 @@ +zgw_api_config_enable: True +zgw_api: + groups: + - name: Config 1 + identifier: config-1 + zaken_service_identifier: zaken-test + documenten_service_identifier: documenten-test + catalogi_service_identifier: catalogi-test + catalogue_domain: TEST + catalogue_rsin: "000000000" + organisatie_rsin: "000000000" + zaak_vertrouwelijkheidaanduiding: zaakvertrouwelijk + doc_vertrouwelijkheidaanduiding: openbaar + auteur: John Doe + objects_api_json_content_template: | + { + "data": {% json_summary %}, + "type": "{{ productaanvraag_type }}", + "bsn": "{{ variables.auth_bsn }}", + "submission_id": "{{ submission.kenmerk }}", + "language_code": "{{ submission.language_code }}", + "custom_field": "foo" + } \ No newline at end of file diff --git a/src/openforms/contrib/zgw/setup_configuration/tests/files/setup_config_required_fields.yaml b/src/openforms/contrib/zgw/setup_configuration/tests/files/setup_config_required_fields.yaml new file mode 100644 index 0000000000..ba283093de --- /dev/null +++ b/src/openforms/contrib/zgw/setup_configuration/tests/files/setup_config_required_fields.yaml @@ -0,0 +1,9 @@ +zgw_api_config_enable: True +zgw_api: + groups: + - name: Config 1 + identifier: config-1 + zaken_service_identifier: zaken-test + documenten_service_identifier: documenten-test + catalogi_service_identifier: catalogi-test + auteur: Jane Doe diff --git a/src/openforms/contrib/zgw/setup_configuration/tests/test_zgw_api_config.py b/src/openforms/contrib/zgw/setup_configuration/tests/test_zgw_api_config.py new file mode 100644 index 0000000000..060fcafe55 --- /dev/null +++ b/src/openforms/contrib/zgw/setup_configuration/tests/test_zgw_api_config.py @@ -0,0 +1,289 @@ +import textwrap +from pathlib import Path + +from django.test import TestCase + +from django_setup_configuration.test_utils import execute_single_step +from zgw_consumers.api_models.constants import VertrouwelijkheidsAanduidingen +from zgw_consumers.constants import APITypes, AuthTypes +from zgw_consumers.models import Service +from zgw_consumers.test.factories import ServiceFactory + +from openforms.registrations.contrib.zgw_apis.models import ( + ZGWApiGroupConfig, + get_content_text, +) +from openforms.registrations.contrib.zgw_apis.tests.factories import ( + ZGWApiGroupConfigFactory, +) + +from ..steps import ZGWApiConfigurationStep + +TEST_FILES = (Path(__file__).parent / "files").resolve() +CONFIG_FILE_PATH = str(TEST_FILES / "setup_config.yaml") +CONFIG_FILE_PATH_REQUIRED_FIELDS = str(TEST_FILES / "setup_config_required_fields.yaml") +CONFIG_FILE_PATH_ALL_FIELDS = str(TEST_FILES / "setup_config_all_fields.yaml") + + +class ZGWApiConfigurationStepTests(TestCase): + maxDiff = None + + def setUp(self): + super().setUp() + + self.zaken_service = ServiceFactory.create( + slug="zaken-test", + label="Zaken API test", + api_root="http://localhost:8003/zaken/api/v1/", + api_type=APITypes.zrc, + auth_type=AuthTypes.zgw, + client_id="test_client_id", + secret="test_secret_key", + ) + self.documenten_service = ServiceFactory.create( + slug="documenten-test", + label="Documenten API test", + api_root="http://localhost:8003/documenten/api/v1/", + api_type=APITypes.drc, + auth_type=AuthTypes.zgw, + client_id="test_client_id", + secret="test_secret_key", + ) + self.catalogi_service = ServiceFactory.create( + slug="catalogi-test", + label="Catalogi API test", + api_root="http://localhost:8003/catalogi/api/v1/", + api_type=APITypes.ztc, + auth_type=AuthTypes.zgw, + client_id="test_client_id", + secret="test_secret_key", + ) + + def test_execute_success(self): + execute_single_step(ZGWApiConfigurationStep, yaml_source=CONFIG_FILE_PATH) + + self.assertEqual(ZGWApiGroupConfig.objects.count(), 2) + + config1, config2 = ZGWApiGroupConfig.objects.all() + + self.assertEqual(config1.name, "Config 1") + self.assertEqual(config1.identifier, "config-1") + self.assertEqual(config1.zrc_service, self.zaken_service) + self.assertEqual(config1.drc_service, self.documenten_service) + self.assertEqual(config1.ztc_service, self.catalogi_service) + self.assertEqual(config1.catalogue_domain, "TEST") + self.assertEqual(config1.catalogue_rsin, "000000000") + self.assertEqual(config1.organisatie_rsin, "000000000") + self.assertEqual( + config1.zaak_vertrouwelijkheidaanduiding, + VertrouwelijkheidsAanduidingen.zaakvertrouwelijk, + ) + self.assertEqual( + config1.doc_vertrouwelijkheidaanduiding, + VertrouwelijkheidsAanduidingen.openbaar, + ) + + expected_json = textwrap.dedent( + """\ + { + "data": {% json_summary %}, + "type": "{{ productaanvraag_type }}", + "bsn": "{{ variables.auth_bsn }}", + "submission_id": "{{ submission.kenmerk }}", + "language_code": "{{ submission.language_code }}", + "custom_field": "foo" + } + """ + ) + + self.assertEqual(config1.content_json, expected_json) + + self.assertEqual(config2.name, "Config 2") + self.assertEqual(config2.identifier, "config-2") + self.assertEqual(config2.zrc_service, self.zaken_service) + self.assertEqual(config2.drc_service, self.documenten_service) + self.assertEqual(config2.ztc_service, self.catalogi_service) + self.assertEqual(config2.catalogue_domain, "OTHER") + self.assertEqual(config2.catalogue_rsin, "000000000") + self.assertEqual(config2.organisatie_rsin, "000000000") + self.assertEqual(config2.zaak_vertrouwelijkheidaanduiding, "") + self.assertEqual(config2.doc_vertrouwelijkheidaanduiding, "") + self.assertEqual(config2.content_json, get_content_text()) + + def test_execute_update_existing_config(self): + ZGWApiGroupConfigFactory.create(name="old name", identifier="config-1") + + execute_single_step(ZGWApiConfigurationStep, yaml_source=CONFIG_FILE_PATH) + + self.assertEqual(ZGWApiGroupConfig.objects.count(), 2) + + config1, config2 = ZGWApiGroupConfig.objects.all() + + self.assertEqual(config1.name, "Config 1") + self.assertEqual(config1.identifier, "config-1") + self.assertEqual(config1.zrc_service, self.zaken_service) + self.assertEqual(config1.drc_service, self.documenten_service) + self.assertEqual(config1.ztc_service, self.catalogi_service) + self.assertEqual(config1.catalogue_domain, "TEST") + self.assertEqual(config1.catalogue_rsin, "000000000") + self.assertEqual(config1.organisatie_rsin, "000000000") + self.assertEqual( + config1.zaak_vertrouwelijkheidaanduiding, + VertrouwelijkheidsAanduidingen.zaakvertrouwelijk, + ) + self.assertEqual( + config1.doc_vertrouwelijkheidaanduiding, + VertrouwelijkheidsAanduidingen.openbaar, + ) + + expected_json = textwrap.dedent( + """\ + { + "data": {% json_summary %}, + "type": "{{ productaanvraag_type }}", + "bsn": "{{ variables.auth_bsn }}", + "submission_id": "{{ submission.kenmerk }}", + "language_code": "{{ submission.language_code }}", + "custom_field": "foo" + } + """ + ) + + self.assertEqual(config1.content_json, expected_json) + + self.assertEqual(config2.name, "Config 2") + self.assertEqual(config2.identifier, "config-2") + self.assertEqual(config2.zrc_service, self.zaken_service) + self.assertEqual(config2.drc_service, self.documenten_service) + self.assertEqual(config2.ztc_service, self.catalogi_service) + self.assertEqual(config2.catalogue_domain, "OTHER") + self.assertEqual(config2.catalogue_rsin, "000000000") + self.assertEqual(config2.organisatie_rsin, "000000000") + self.assertEqual(config2.zaak_vertrouwelijkheidaanduiding, "") + self.assertEqual(config2.doc_vertrouwelijkheidaanduiding, "") + self.assertEqual(config2.content_json, get_content_text()) + + def test_execute_with_required_fields(self): + execute_single_step( + ZGWApiConfigurationStep, yaml_source=CONFIG_FILE_PATH_REQUIRED_FIELDS + ) + + self.assertEqual(ZGWApiGroupConfig.objects.count(), 1) + + config = ZGWApiGroupConfig.objects.get() + + self.assertEqual(config.name, "Config 1") + self.assertEqual(config.identifier, "config-1") + self.assertEqual(config.zrc_service, self.zaken_service) + self.assertEqual(config.drc_service, self.documenten_service) + self.assertEqual(config.ztc_service, self.catalogi_service) + + self.assertEqual(config.catalogue_domain, "") + self.assertEqual(config.catalogue_rsin, "") + self.assertEqual(config.organisatie_rsin, "") + self.assertEqual(config.zaak_vertrouwelijkheidaanduiding, "") + self.assertEqual(config.doc_vertrouwelijkheidaanduiding, "") + self.assertEqual(config.content_json, get_content_text()) + + def test_execute_with_all_fields(self): + execute_single_step( + ZGWApiConfigurationStep, yaml_source=CONFIG_FILE_PATH_ALL_FIELDS + ) + + self.assertEqual(ZGWApiGroupConfig.objects.count(), 1) + + config = ZGWApiGroupConfig.objects.get() + + self.assertEqual(config.name, "Config 1") + self.assertEqual(config.identifier, "config-1") + self.assertEqual(config.zrc_service, self.zaken_service) + self.assertEqual(config.drc_service, self.documenten_service) + self.assertEqual(config.ztc_service, self.catalogi_service) + self.assertEqual(config.catalogue_domain, "TEST") + self.assertEqual(config.catalogue_rsin, "000000000") + self.assertEqual(config.organisatie_rsin, "000000000") + self.assertEqual( + config.zaak_vertrouwelijkheidaanduiding, + VertrouwelijkheidsAanduidingen.zaakvertrouwelijk, + ) + self.assertEqual( + config.doc_vertrouwelijkheidaanduiding, + VertrouwelijkheidsAanduidingen.openbaar, + ) + + expected_json = textwrap.dedent( + """\ + { + "data": {% json_summary %}, + "type": "{{ productaanvraag_type }}", + "bsn": "{{ variables.auth_bsn }}", + "submission_id": "{{ submission.kenmerk }}", + "language_code": "{{ submission.language_code }}", + "custom_field": "foo" + }""" + ) + + self.assertEqual(config.content_json, expected_json) + + def test_execute_is_idempotent(self): + def make_assertions(): + self.assertEqual(ZGWApiGroupConfig.objects.count(), 1) + + config = ZGWApiGroupConfig.objects.get() + + self.assertEqual(config.name, "Config 1") + self.assertEqual(config.identifier, "config-1") + self.assertEqual(config.zrc_service, self.zaken_service) + self.assertEqual(config.drc_service, self.documenten_service) + self.assertEqual(config.ztc_service, self.catalogi_service) + self.assertEqual(config.catalogue_domain, "TEST") + self.assertEqual(config.catalogue_rsin, "000000000") + self.assertEqual(config.organisatie_rsin, "000000000") + self.assertEqual( + config.zaak_vertrouwelijkheidaanduiding, + VertrouwelijkheidsAanduidingen.zaakvertrouwelijk, + ) + self.assertEqual( + config.doc_vertrouwelijkheidaanduiding, + VertrouwelijkheidsAanduidingen.openbaar, + ) + + expected_json = textwrap.dedent( + """\ + { + "data": {% json_summary %}, + "type": "{{ productaanvraag_type }}", + "bsn": "{{ variables.auth_bsn }}", + "submission_id": "{{ submission.kenmerk }}", + "language_code": "{{ submission.language_code }}", + "custom_field": "foo" + }""" + ) + + self.assertEqual(config.content_json, expected_json) + + execute_single_step( + ZGWApiConfigurationStep, yaml_source=CONFIG_FILE_PATH_ALL_FIELDS + ) + + make_assertions() + + execute_single_step( + ZGWApiConfigurationStep, yaml_source=CONFIG_FILE_PATH_ALL_FIELDS + ) + + make_assertions() + + def test_execute_service_not_found_raises_error(self): + self.zaken_service.delete() + + with self.assertRaisesMessage( + Service.DoesNotExist, + "Service matching query does not exist. (identifier = zaken-test)", + ): + execute_single_step( + ZGWApiConfigurationStep, + yaml_source=CONFIG_FILE_PATH_REQUIRED_FIELDS, + ) + + self.assertEqual(ZGWApiGroupConfig.objects.count(), 0) diff --git a/src/openforms/utils/services.py b/src/openforms/utils/services.py new file mode 100644 index 0000000000..15b18102a1 --- /dev/null +++ b/src/openforms/utils/services.py @@ -0,0 +1,12 @@ +from zgw_consumers.models import Service + + +def get_service(slug: str) -> Service: + """ + Try to find a Service and re-raise DoesNotExist with the identifier to make debugging + easier + """ + try: + return Service.objects.get(slug=slug) + except Service.DoesNotExist as e: + raise Service.DoesNotExist(f"{str(e)} (identifier = {slug})")