From 1baa3effc815c7ba3f0588509b7a165ede44af7d Mon Sep 17 00:00:00 2001 From: Paul Schilling Date: Thu, 14 Nov 2024 14:50:04 +0100 Subject: [PATCH 1/2] [#2871] Update natural keys for ZGW entities and remove unique constraints - use (domein, rsin) as natural key for CatalogusConfig - use combination of `omschrivjing` and (catalogus_domain, catalogus_rsin) for natural keys of nested ZGW configs (ZaakTypeConfig, ZaakTypeInformatieObjectTypeConfig etc.) and use (domein, rsin) support import/export of ZGW objects across different environments (where the url is different) --- src/open_inwoner/openzaak/import_export.py | 7 ++- src/open_inwoner/openzaak/managers.py | 47 +++++++------- ...que_identificatie_in_catalogus_and_more.py | 29 +++++++++ src/open_inwoner/openzaak/models.py | 36 ++--------- .../openzaak/tests/test_import_export.py | 42 ++++++------- .../openzaak/tests/test_models.py | 61 +++++++++++++------ 6 files changed, 127 insertions(+), 95 deletions(-) create mode 100644 src/open_inwoner/openzaak/migrations/0058_remove_zaaktypeconfig_unique_identificatie_in_catalogus_and_more.py diff --git a/src/open_inwoner/openzaak/import_export.py b/src/open_inwoner/openzaak/import_export.py index 54bdd92e73..f2318ecf15 100644 --- a/src/open_inwoner/openzaak/import_export.py +++ b/src/open_inwoner/openzaak/import_export.py @@ -179,6 +179,8 @@ def _rewrite_jsonl_url_references( lambda row: ('"model": "openzaak.catalogusconfig"' in row), cls._lines_iter_from_jsonl_stream_or_string(stream_or_string), ), + use_natural_foreign_keys=True, + use_natural_primary_keys=True, ): object_type: str = deserialized_object.object.__class__.__name__ @@ -220,7 +222,10 @@ def from_jsonl_stream_or_string(cls, stream_or_string: IO | str) -> Self: object_type_counts = defaultdict(int) for deserialized_object in serializers.deserialize( - "jsonl", cls._rewrite_jsonl_url_references(stream_or_string) + "jsonl", + cls._rewrite_jsonl_url_references(stream_or_string), + use_natural_foreign_keys=True, + use_natural_primary_keys=True, ): deserialized_object.save() object_type = deserialized_object.object.__class__.__name__ diff --git a/src/open_inwoner/openzaak/managers.py b/src/open_inwoner/openzaak/managers.py index dec856fa58..6f515dde64 100644 --- a/src/open_inwoner/openzaak/managers.py +++ b/src/open_inwoner/openzaak/managers.py @@ -85,25 +85,27 @@ def record_if_unique_notification( class CatalogusConfigManager(models.Manager): - def get_by_natural_key(self, url): - return self.get(url=url) + def get_by_natural_key(self, domein, rsin): + return self.get(domein=domein, rsin=rsin) class ZaakTypeInformatieObjectTypeConfigQueryset(models.QuerySet): def get_by_natural_key( self, - informatieobjecttype_url, + omschrijving, zaak_type_config_identificatie, - catalogus_url, + catalogus_domein, + catalogus_rsin, ): from .models import ZaakTypeConfig return self.get( zaaktype_config=ZaakTypeConfig.objects.get( identificatie=zaak_type_config_identificatie, - catalogus__url=catalogus_url, + catalogus__domein=catalogus_domein, + catalogus__rsin=catalogus_rsin, ), - informatieobjecttype_url=informatieobjecttype_url, + omschrijving=omschrijving, ) def filter_catalogus(self, case_type: ZaakType): @@ -144,14 +146,11 @@ def get_for_case_and_info_type( class ZaakTypeConfigQueryset(models.QuerySet): - def get_by_natural_key( - self, - identificatie, - catalogus_url, - ): + def get_by_natural_key(self, identificatie, catalogus_domein, catalogus_rsin): return self.get( identificatie=identificatie, - catalogus__url=catalogus_url, + catalogus__domein=catalogus_domein, + catalogus__rsin=catalogus_rsin, ) def filter_catalogus(self, case_type: ZaakType): @@ -205,18 +204,20 @@ def filter_questions_enabled_for_case_type(self, case_type: ZaakType): class ZaakTypeStatusTypeConfigQuerySet(models.QuerySet): def get_by_natural_key( self, - statustype_url, - zaak_type_config_identificatie, - catalogus_url, + omschrijving, + zaaktype_config_identificatie, + catalogus_domein, + catalogus_rsin, ): from .models import ZaakTypeConfig return self.get( zaaktype_config=ZaakTypeConfig.objects.get( - identificatie=zaak_type_config_identificatie, - catalogus__url=catalogus_url, + identificatie=zaaktype_config_identificatie, + catalogus__domein=catalogus_domein, + catalogus__rsin=catalogus_rsin, ), - statustype_url=statustype_url, + omschrijving=omschrijving, ) def find_for(self, case: Zaak, status: Status): @@ -246,18 +247,20 @@ def find_for_types_from_str( class ZaakTypeResultaatTypeConfigManger(models.Manager): def get_by_natural_key( self, - resultaattype_url, + omschrijving, zaak_type_config_identificatie, - catalogus_url, + catalogus_domein, + catalogus_rsin, ): from .models import ZaakTypeConfig return self.get( zaaktype_config=ZaakTypeConfig.objects.get( identificatie=zaak_type_config_identificatie, - catalogus__url=catalogus_url, + catalogus__domein=catalogus_domein, + catalogus__rsin=catalogus_rsin, ), - resultaattype_url=resultaattype_url, + omschrijving=omschrijving, ) diff --git a/src/open_inwoner/openzaak/migrations/0058_remove_zaaktypeconfig_unique_identificatie_in_catalogus_and_more.py b/src/open_inwoner/openzaak/migrations/0058_remove_zaaktypeconfig_unique_identificatie_in_catalogus_and_more.py new file mode 100644 index 0000000000..ee26bc46db --- /dev/null +++ b/src/open_inwoner/openzaak/migrations/0058_remove_zaaktypeconfig_unique_identificatie_in_catalogus_and_more.py @@ -0,0 +1,29 @@ +# Generated by Django 4.2.16 on 2024-11-22 16:51 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ("openzaak", "0057_openzaakconfig_order_statusses_by_date_set"), + ] + + operations = [ + migrations.RemoveConstraint( + model_name="zaaktypeconfig", + name="unique_identificatie_in_catalogus", + ), + migrations.RemoveConstraint( + model_name="zaaktypeinformatieobjecttypeconfig", + name="unique_zaaktype_config_informatieobjecttype_url", + ), + migrations.RemoveConstraint( + model_name="zaaktyperesultaattypeconfig", + name="unique_zaaktype_config_resultaattype_url", + ), + migrations.RemoveConstraint( + model_name="zaaktypestatustypeconfig", + name="unique_zaaktype_config_statustype_url", + ), + ] diff --git a/src/open_inwoner/openzaak/models.py b/src/open_inwoner/openzaak/models.py index 70d500e168..d0b34fff27 100644 --- a/src/open_inwoner/openzaak/models.py +++ b/src/open_inwoner/openzaak/models.py @@ -4,6 +4,7 @@ from typing import Protocol, cast from urllib.parse import urlparse +from django.core.exceptions import ValidationError from django.db import models, transaction from django.db.models import UniqueConstraint from django.utils import timezone @@ -511,7 +512,7 @@ def __str__(self): return f"{self.domein} - {self.rsin} [{self.base_url}]" def natural_key(self) -> tuple[str]: - return (self.url,) + return (self.domein, self.rsin) class ZaakTypeConfig(models.Model): @@ -589,12 +590,6 @@ class ZaakTypeConfig(models.Model): class Meta: verbose_name = _("Zaaktype Configuration") - constraints = [ - UniqueConstraint( - name="unique_identificatie_in_catalogus", - fields=["catalogus", "identificatie"], - ), - ] @property def catalogus_url(self): @@ -651,13 +646,6 @@ class ZaakTypeInformatieObjectTypeConfig(models.Model): class Meta: verbose_name = _("Zaaktype Information Object Configuration") - constraints = [ - UniqueConstraint( - name="unique_zaaktype_config_informatieobjecttype_url", - fields=["zaaktype_config", "informatieobjecttype_url"], - ) - ] - def informatieobjecttype_uuid(self): if self.informatieobjecttype_url: segments = furl(self.informatieobjecttype_url).path.segments @@ -676,7 +664,7 @@ def __str__(self): return f"{self.omschrijving} [{self.zaaktype_config.catalogus.base_url}]" def natural_key(self): - return (self.informatieobjecttype_url,) + self.zaaktype_config.natural_key() + return (self.omschrijving,) + self.zaaktype_config.natural_key() natural_key.dependencies = ["openzaak.zaaktypeconfig"] @@ -790,18 +778,11 @@ class ZaakTypeStatusTypeConfig(models.Model): class Meta: verbose_name = _("Zaaktype Statustype Configuration") - constraints = [ - UniqueConstraint( - name="unique_zaaktype_config_statustype_url", - fields=["zaaktype_config", "statustype_url"], - ) - ] - def __str__(self): return f"{self.zaaktype_config.identificatie} - {self.omschrijving} [{self.zaaktype_config.catalogus.base_url}]" def natural_key(self): - return (self.statustype_url,) + self.zaaktype_config.natural_key() + return (self.omschrijving,) + self.zaaktype_config.natural_key() natural_key.dependencies = ["openzaak.zaaktypeconfig"] @@ -841,18 +822,11 @@ class ZaakTypeResultaatTypeConfig(models.Model): class Meta: verbose_name = _("Zaaktype Resultaattype Configuration") - constraints = [ - UniqueConstraint( - name="unique_zaaktype_config_resultaattype_url", - fields=["zaaktype_config", "resultaattype_url"], - ) - ] - def __str__(self): return f"{self.zaaktype_config.identificatie} - {self.omschrijving} [{self.zaaktype_config.catalogus.base_url}]" def natural_key(self): - return (self.resultaattype_url,) + self.zaaktype_config.natural_key() + return (self.omschrijving,) + self.zaaktype_config.natural_key() natural_key.dependencies = ["openzaak.zaaktypeconfig"] diff --git a/src/open_inwoner/openzaak/tests/test_import_export.py b/src/open_inwoner/openzaak/tests/test_import_export.py index d7534fb76b..55bb2959d2 100644 --- a/src/open_inwoner/openzaak/tests/test_import_export.py +++ b/src/open_inwoner/openzaak/tests/test_import_export.py @@ -115,9 +115,9 @@ def test_only_models_related_to_exported_catalogus_config_are_included(self): ( "catalogus_configs", "zaak_type_configs", - "zaak_informatie_object_type_configs", "zaak_status_type_configs", "zaak_resultaat_type_configs", + "zaak_informatie_object_type_configs", ), ( "catalogus", @@ -169,7 +169,7 @@ def test_export_catalogus_configs(self): "model": "openzaak.zaaktypeconfig", "fields": { "urls": '["https://foo.0.maykinmedia.nl"]', - "catalogus": ["https://foo.0.maykinmedia.nl"], + "catalogus": ["DM-0", "123456789"], "identificatie": "ztc-id-a-0", "omschrijving": "zaaktypeconfig", "notify_status_changes": False, @@ -184,7 +184,7 @@ def test_export_catalogus_configs(self): { "model": "openzaak.zaaktypeinformatieobjecttypeconfig", "fields": { - "zaaktype_config": ["ztc-id-a-0", "https://foo.0.maykinmedia.nl"], + "zaaktype_config": ["ztc-id-a-0", "DM-0", "123456789"], "informatieobjecttype_url": "http://foo.0.maykinmedia.nl", "omschrijving": "informatieobject", "zaaktype_uuids": "[]", @@ -195,7 +195,7 @@ def test_export_catalogus_configs(self): { "model": "openzaak.zaaktypestatustypeconfig", "fields": { - "zaaktype_config": ["ztc-id-a-0", "https://foo.0.maykinmedia.nl"], + "zaaktype_config": ["ztc-id-a-0", "DM-0", "123456789"], "statustype_url": "https://foo.0.maykinmedia.nl", "omschrijving": "status omschrijving", "statustekst": "", @@ -215,7 +215,7 @@ def test_export_catalogus_configs(self): { "model": "openzaak.zaaktyperesultaattypeconfig", "fields": { - "zaaktype_config": ["ztc-id-a-0", "https://foo.0.maykinmedia.nl"], + "zaaktype_config": ["ztc-id-a-0", "DM-0", "123456789"], "resultaattype_url": "https://foo.0.maykinmedia.nl", "omschrijving": "resultaat", "zaaktype_uuids": "[]", @@ -237,21 +237,21 @@ def test_export_catalogus_configs_as_jsonl(self): "\n", '{"model": "openzaak.catalogusconfig", "fields": {"url": "https://foo.1.maykinmedia.nl", "domein": "DM-1", "rsin": "123456789", "service": ["service-1"]}}', "\n", - '{"model": "openzaak.zaaktypeconfig", "fields": {"urls": "[\\"https://foo.0.maykinmedia.nl\\"]", "catalogus": ["https://foo.0.maykinmedia.nl"], "identificatie": "ztc-id-a-0", "omschrijving": "zaaktypeconfig", "notify_status_changes": false, "description": "", "external_document_upload_url": "", "document_upload_enabled": false, "contact_form_enabled": false, "contact_subject_code": "", "relevante_zaakperiode": null}}', + '{"model": "openzaak.zaaktypeconfig", "fields": {"urls": "[\\"https://foo.0.maykinmedia.nl\\"]", "catalogus": ["DM-0", "123456789"], "identificatie": "ztc-id-a-0", "omschrijving": "zaaktypeconfig", "notify_status_changes": false, "description": "", "external_document_upload_url": "", "document_upload_enabled": false, "contact_form_enabled": false, "contact_subject_code": "", "relevante_zaakperiode": null}}', "\n", - '{"model": "openzaak.zaaktypeconfig", "fields": {"urls": "[\\"https://foo.1.maykinmedia.nl\\"]", "catalogus": ["https://foo.1.maykinmedia.nl"], "identificatie": "ztc-id-a-1", "omschrijving": "zaaktypeconfig", "notify_status_changes": false, "description": "", "external_document_upload_url": "", "document_upload_enabled": false, "contact_form_enabled": false, "contact_subject_code": "", "relevante_zaakperiode": null}}', + '{"model": "openzaak.zaaktypeconfig", "fields": {"urls": "[\\"https://foo.1.maykinmedia.nl\\"]", "catalogus": ["DM-1", "123456789"], "identificatie": "ztc-id-a-1", "omschrijving": "zaaktypeconfig", "notify_status_changes": false, "description": "", "external_document_upload_url": "", "document_upload_enabled": false, "contact_form_enabled": false, "contact_subject_code": "", "relevante_zaakperiode": null}}', "\n", - '{"model": "openzaak.zaaktypeinformatieobjecttypeconfig", "fields": {"zaaktype_config": ["ztc-id-a-0", "https://foo.0.maykinmedia.nl"], "informatieobjecttype_url": "http://foo.0.maykinmedia.nl", "omschrijving": "informatieobject", "zaaktype_uuids": "[]", "document_upload_enabled": false, "document_notification_enabled": false}}', + '{"model": "openzaak.zaaktypeinformatieobjecttypeconfig", "fields": {"zaaktype_config": ["ztc-id-a-0", "DM-0", "123456789"], "informatieobjecttype_url": "http://foo.0.maykinmedia.nl", "omschrijving": "informatieobject", "zaaktype_uuids": "[]", "document_upload_enabled": false, "document_notification_enabled": false}}', "\n", - '{"model": "openzaak.zaaktypeinformatieobjecttypeconfig", "fields": {"zaaktype_config": ["ztc-id-a-1", "https://foo.1.maykinmedia.nl"], "informatieobjecttype_url": "http://foo.1.maykinmedia.nl", "omschrijving": "informatieobject", "zaaktype_uuids": "[]", "document_upload_enabled": false, "document_notification_enabled": false}}', + '{"model": "openzaak.zaaktypeinformatieobjecttypeconfig", "fields": {"zaaktype_config": ["ztc-id-a-1", "DM-1", "123456789"], "informatieobjecttype_url": "http://foo.1.maykinmedia.nl", "omschrijving": "informatieobject", "zaaktype_uuids": "[]", "document_upload_enabled": false, "document_notification_enabled": false}}', "\n", - '{"model": "openzaak.zaaktypestatustypeconfig", "fields": {"zaaktype_config": ["ztc-id-a-0", "https://foo.0.maykinmedia.nl"], "statustype_url": "https://foo.0.maykinmedia.nl", "omschrijving": "status omschrijving", "statustekst": "", "zaaktype_uuids": "[]", "status_indicator": "", "status_indicator_text": "", "document_upload_description": "", "description": "status", "notify_status_change": true, "action_required": false, "document_upload_enabled": true, "call_to_action_url": "", "call_to_action_text": "", "case_link_text": ""}}', + '{"model": "openzaak.zaaktypestatustypeconfig", "fields": {"zaaktype_config": ["ztc-id-a-0", "DM-0", "123456789"], "statustype_url": "https://foo.0.maykinmedia.nl", "omschrijving": "status omschrijving", "statustekst": "", "zaaktype_uuids": "[]", "status_indicator": "", "status_indicator_text": "", "document_upload_description": "", "description": "status", "notify_status_change": true, "action_required": false, "document_upload_enabled": true, "call_to_action_url": "", "call_to_action_text": "", "case_link_text": ""}}', "\n", - '{"model": "openzaak.zaaktypestatustypeconfig", "fields": {"zaaktype_config": ["ztc-id-a-1", "https://foo.1.maykinmedia.nl"], "statustype_url": "https://foo.1.maykinmedia.nl", "omschrijving": "status omschrijving", "statustekst": "", "zaaktype_uuids": "[]", "status_indicator": "", "status_indicator_text": "", "document_upload_description": "", "description": "status", "notify_status_change": true, "action_required": false, "document_upload_enabled": true, "call_to_action_url": "", "call_to_action_text": "", "case_link_text": ""}}', + '{"model": "openzaak.zaaktypestatustypeconfig", "fields": {"zaaktype_config": ["ztc-id-a-1", "DM-1", "123456789"], "statustype_url": "https://foo.1.maykinmedia.nl", "omschrijving": "status omschrijving", "statustekst": "", "zaaktype_uuids": "[]", "status_indicator": "", "status_indicator_text": "", "document_upload_description": "", "description": "status", "notify_status_change": true, "action_required": false, "document_upload_enabled": true, "call_to_action_url": "", "call_to_action_text": "", "case_link_text": ""}}', "\n", - '{"model": "openzaak.zaaktyperesultaattypeconfig", "fields": {"zaaktype_config": ["ztc-id-a-0", "https://foo.0.maykinmedia.nl"], "resultaattype_url": "https://foo.0.maykinmedia.nl", "omschrijving": "resultaat", "zaaktype_uuids": "[]", "description": ""}}', + '{"model": "openzaak.zaaktyperesultaattypeconfig", "fields": {"zaaktype_config": ["ztc-id-a-0", "DM-0", "123456789"], "resultaattype_url": "https://foo.0.maykinmedia.nl", "omschrijving": "resultaat", "zaaktype_uuids": "[]", "description": ""}}', "\n", - '{"model": "openzaak.zaaktyperesultaattypeconfig", "fields": {"zaaktype_config": ["ztc-id-a-1", "https://foo.1.maykinmedia.nl"], "resultaattype_url": "https://foo.1.maykinmedia.nl", "omschrijving": "resultaat", "zaaktype_uuids": "[]", "description": ""}}', + '{"model": "openzaak.zaaktyperesultaattypeconfig", "fields": {"zaaktype_config": ["ztc-id-a-1", "DM-1", "123456789"], "resultaattype_url": "https://foo.1.maykinmedia.nl", "omschrijving": "resultaat", "zaaktype_uuids": "[]", "description": ""}}', "\n", ] @@ -271,14 +271,14 @@ def setUp(self): self.json_lines = [ '{"model": "openzaak.catalogusconfig", "fields": {"url": "https://foo.0.maykinmedia.nl", "domein": "DM-0", "rsin": "123456789", "service": ["service-0"]}}', '{"model": "openzaak.catalogusconfig", "fields": {"url": "https://foo.1.maykinmedia.nl", "domein": "DM-1", "rsin": "123456789", "service": ["service-1"]}}', - '{"model": "openzaak.zaaktypeconfig", "fields": {"urls": "[\\"https://foo.0.maykinmedia.nl\\"]", "catalogus": ["https://foo.0.maykinmedia.nl"], "identificatie": "ztc-id-a-0", "omschrijving": "zaaktypeconfig", "notify_status_changes": false, "description": "", "external_document_upload_url": "", "document_upload_enabled": false, "contact_form_enabled": false, "contact_subject_code": "", "relevante_zaakperiode": null}}', - '{"model": "openzaak.zaaktypeconfig", "fields": {"urls": "[\\"https://foo.1.maykinmedia.nl\\"]", "catalogus": ["https://foo.1.maykinmedia.nl"], "identificatie": "ztc-id-a-1", "omschrijving": "zaaktypeconfig", "notify_status_changes": false, "description": "", "external_document_upload_url": "", "document_upload_enabled": false, "contact_form_enabled": false, "contact_subject_code": "", "relevante_zaakperiode": null}}', - '{"model": "openzaak.zaaktypeinformatieobjecttypeconfig", "fields": {"zaaktype_config": ["ztc-id-a-0", "https://foo.0.maykinmedia.nl"], "informatieobjecttype_url": "http://foo.0.maykinmedia.nl", "omschrijving": "informatieobject", "zaaktype_uuids": "[]", "document_upload_enabled": false, "document_notification_enabled": false}}', - '{"model": "openzaak.zaaktypeinformatieobjecttypeconfig", "fields": {"zaaktype_config": ["ztc-id-a-1", "https://foo.1.maykinmedia.nl"], "informatieobjecttype_url": "http://foo.1.maykinmedia.nl", "omschrijving": "informatieobject", "zaaktype_uuids": "[]", "document_upload_enabled": false, "document_notification_enabled": false}}', - '{"model": "openzaak.zaaktypestatustypeconfig", "fields": {"zaaktype_config": ["ztc-id-a-0", "https://foo.0.maykinmedia.nl"], "statustype_url": "https://foo.0.maykinmedia.nl", "omschrijving": "status omschrijving", "statustekst": "", "zaaktype_uuids": "[]", "status_indicator": "", "status_indicator_text": "", "document_upload_description": "", "description": "status", "notify_status_change": true, "action_required": false, "document_upload_enabled": true, "call_to_action_url": "", "call_to_action_text": "", "case_link_text": ""}}', - '{"model": "openzaak.zaaktypestatustypeconfig", "fields": {"zaaktype_config": ["ztc-id-a-1", "https://foo.1.maykinmedia.nl"], "statustype_url": "https://foo.1.maykinmedia.nl", "omschrijving": "status omschrijving", "statustekst": "", "zaaktype_uuids": "[]", "status_indicator": "", "status_indicator_text": "", "document_upload_description": "", "description": "status", "notify_status_change": true, "action_required": false, "document_upload_enabled": true, "call_to_action_url": "", "call_to_action_text": "", "case_link_text": ""}}', - '{"model": "openzaak.zaaktyperesultaattypeconfig", "fields": {"zaaktype_config": ["ztc-id-a-0", "https://foo.0.maykinmedia.nl"], "resultaattype_url": "https://foo.0.maykinmedia.nl", "omschrijving": "resultaat", "zaaktype_uuids": "[]", "description": ""}}', - '{"model": "openzaak.zaaktyperesultaattypeconfig", "fields": {"zaaktype_config": ["ztc-id-a-1", "https://foo.1.maykinmedia.nl"], "resultaattype_url": "https://foo.1.maykinmedia.nl", "omschrijving": "resultaat", "zaaktype_uuids": "[]", "description": ""}}', + '{"model": "openzaak.zaaktypeconfig", "fields": {"urls": "[\\"https://foo.0.maykinmedia.nl\\"]", "catalogus": ["DM-0", "123456789"], "identificatie": "ztc-id-a-0", "omschrijving": "zaaktypeconfig", "notify_status_changes": false, "description": "", "external_document_upload_url": "", "document_upload_enabled": false, "contact_form_enabled": false, "contact_subject_code": "", "relevante_zaakperiode": null}}', + '{"model": "openzaak.zaaktypeconfig", "fields": {"urls": "[\\"https://foo.1.maykinmedia.nl\\"]", "catalogus": ["DM-1", "123456789"], "identificatie": "ztc-id-a-1", "omschrijving": "zaaktypeconfig", "notify_status_changes": false, "description": "", "external_document_upload_url": "", "document_upload_enabled": false, "contact_form_enabled": false, "contact_subject_code": "", "relevante_zaakperiode": null}}', + '{"model": "openzaak.zaaktypeinformatieobjecttypeconfig", "fields": {"zaaktype_config": ["ztc-id-a-0", "DM-0", "123456789"], "informatieobjecttype_url": "http://foo.0.maykinmedia.nl", "omschrijving": "informatieobject", "zaaktype_uuids": "[]", "document_upload_enabled": false, "document_notification_enabled": false}}', + '{"model": "openzaak.zaaktypeinformatieobjecttypeconfig", "fields": {"zaaktype_config": ["ztc-id-a-1", "DM-1", "123456789"], "informatieobjecttype_url": "http://foo.1.maykinmedia.nl", "omschrijving": "informatieobject", "zaaktype_uuids": "[]", "document_upload_enabled": false, "document_notification_enabled": false}}', + '{"model": "openzaak.zaaktypestatustypeconfig", "fields": {"zaaktype_config": ["ztc-id-a-0", "DM-0", "123456789"], "statustype_url": "https://foo.0.maykinmedia.nl", "omschrijving": "status omschrijving", "statustekst": "", "zaaktype_uuids": "[]", "status_indicator": "", "status_indicator_text": "", "document_upload_description": "", "description": "status", "notify_status_change": true, "action_required": false, "document_upload_enabled": true, "call_to_action_url": "", "call_to_action_text": "", "case_link_text": ""}}', + '{"model": "openzaak.zaaktypestatustypeconfig", "fields": {"zaaktype_config": ["ztc-id-a-1", "DM-1", "123456789"], "statustype_url": "https://foo.1.maykinmedia.nl", "omschrijving": "status omschrijving", "statustekst": "", "zaaktype_uuids": "[]", "status_indicator": "", "status_indicator_text": "", "document_upload_description": "", "description": "status", "notify_status_change": true, "action_required": false, "document_upload_enabled": true, "call_to_action_url": "", "call_to_action_text": "", "case_link_text": ""}}', + '{"model": "openzaak.zaaktyperesultaattypeconfig", "fields": {"zaaktype_config": ["ztc-id-a-0", "DM-0", "123456789"], "resultaattype_url": "https://foo.0.maykinmedia.nl", "omschrijving": "resultaat", "zaaktype_uuids": "[]", "description": ""}}', + '{"model": "openzaak.zaaktyperesultaattypeconfig", "fields": {"zaaktype_config": ["ztc-id-a-1", "DM-1", "123456789"], "resultaattype_url": "https://foo.1.maykinmedia.nl", "omschrijving": "resultaat", "zaaktype_uuids": "[]", "description": ""}}', ] self.jsonl = "\n".join(self.json_lines) diff --git a/src/open_inwoner/openzaak/tests/test_models.py b/src/open_inwoner/openzaak/tests/test_models.py index b0f6851eff..3c12d340e7 100644 --- a/src/open_inwoner/openzaak/tests/test_models.py +++ b/src/open_inwoner/openzaak/tests/test_models.py @@ -34,7 +34,7 @@ def test_fields_used_for_natural_key_are_unique(self): with self.assertRaises(IntegrityError): for _ in range(2): - CatalogusConfigFactory(url="http://foo.maykinmedia.nl") + CatalogusConfigFactory(domein="test", rsin="1234") def test_get_by_natural_key_returns_expected_instance(self): config = CatalogusConfigFactory() @@ -47,7 +47,7 @@ def test_get_by_natural_key_returns_expected_instance(self): def test_get_by_natural_key_not_found(self): with self.assertRaises(CatalogusConfig.DoesNotExist): CatalogusConfig.objects.get_by_natural_key( - "http://non-existent.maykinmedia.nl" + domein="test", rsin="1234" ) @@ -58,7 +58,9 @@ def test_fields_used_for_natural_key_are_unique(self): with self.assertRaises(IntegrityError): for _ in range(2): ZaakTypeConfigFactory( - identificatie="AAAA", catalogus__url=catalogus.url + identificatie="AAAA", + catalogus__domein=catalogus.domein, + catalogus__rsin=catalogus.rsin, ) def test_get_by_natural_key_returns_expected_instance(self): @@ -74,8 +76,10 @@ def test_get_by_natural_key_returns_expected_instance(self): def test_get_by_natural_key_not_found(self): unused_catalogus = CatalogusConfig() with self.assertRaises(ZaakTypeConfig.DoesNotExist): - ZaakTypeConfig.objects.get_by_natural_key( - identificatie="FOO", catalogus_url=unused_catalogus.url + ZaakTypeConfig.objects.get( + identificatie="AAAA", + catalogus__domein="bogus", + catalogus__rsin="bogus", ) def test_queryset_filter_case_type_with_catalog(self): @@ -109,19 +113,22 @@ def test_fields_used_for_natural_key_are_unique(self): for _ in range(2): ZaakTypeStatusTypeConfigFactory( zaaktype_config=zt, - statustype_url="http://foo.maykinmedia.nl", + omschrijving="test", ) def test_get_by_natural_key_returns_expected_instance(self): zt = ZaakTypeConfigFactory() zt_status_type_config = ZaakTypeStatusTypeConfigFactory( zaaktype_config=zt, - statustype_url="http://foo.maykinmedia.nl", + omschrijving="test", ) self.assertEqual( ZaakTypeStatusTypeConfig.objects.get_by_natural_key( - "http://foo.maykinmedia.nl", *zt.natural_key() + omschrijving="test", + zaaktype_config_identificatie=zt.identificatie, + catalogus_domein=zt.catalogus.domein, + catalogus_rsin=zt.catalogus.rsin, ), zt_status_type_config, ) @@ -130,8 +137,10 @@ def test_get_by_natural_key_not_found(self): unused_zt = ZaakTypeConfigFactory() with self.assertRaises(ZaakTypeStatusTypeConfig.DoesNotExist): ZaakTypeStatusTypeConfig.objects.get_by_natural_key( - "http://foo.maykinmedia.nl", - *unused_zt.natural_key(), + omschrijving="test", + zaaktype_config_identificatie=unused_zt.identificatie, + catalogus_domein=unused_zt.catalogus.domein, + catalogus_rsin=unused_zt.catalogus.rsin, ) @@ -141,27 +150,33 @@ def test_fields_used_for_natural_key_are_unique(self): with self.assertRaises(IntegrityError): for _ in range(2): ZaakTypeResultaatTypeConfigFactory( - zaaktype_config=zt, resultaattype_url="http://foo.maykinmedia.nl" + zaaktype_config=zt, omschrijving="test" ) def test_get_by_natural_key_returns_expected_instance(self): zt = ZaakTypeConfigFactory() zt_status_type_config = ZaakTypeResultaatTypeConfigFactory( - zaaktype_config=zt, resultaattype_url="http://foo.maykinmedia.nl" + zaaktype_config=zt, omschrijving="test", ) self.assertEqual( ZaakTypeResultaatTypeConfig.objects.get_by_natural_key( - "http://foo.maykinmedia.nl", *zt.natural_key() + omschrijving="test", + zaak_type_config_identificatie=zt.identificatie, + catalogus_domein=zt.catalogus.domein, + catalogus_rsin=zt.catalogus.rsin, ), zt_status_type_config, ) def test_get_by_natural_key_not_found(self): - unused_zt = ZaakTypeConfigFactory() + zt = ZaakTypeConfigFactory() with self.assertRaises(ZaakTypeResultaatTypeConfig.DoesNotExist): ZaakTypeResultaatTypeConfig.objects.get_by_natural_key( - "http://foo.maykinmedia.nl", *unused_zt.natural_key() + omschrijving="bogus", + zaak_type_config_identificatie=zt.identificatie, + catalogus_domein=zt.catalogus.domein, + catalogus_rsin=zt.catalogus.rsin, ) @@ -172,27 +187,33 @@ def test_fields_used_for_natural_key_are_unique(self): for _ in range(2): ZaakTypeInformatieObjectTypeConfigFactory( zaaktype_config=zt, - informatieobjecttype_url="http://foo.maykinmedia.nl", + omschrijving="test", ) def test_get_by_natural_key_returns_expected_instance(self): zt = ZaakTypeConfigFactory() zt_io_type = ZaakTypeInformatieObjectTypeConfigFactory( - zaaktype_config=zt, informatieobjecttype_url="http://foo.maykinmedia.nl" + zaaktype_config=zt, omschrijving="test" ) self.assertEqual( ZaakTypeInformatieObjectTypeConfig.objects.get_by_natural_key( - "http://foo.maykinmedia.nl", *zt.natural_key() + omschrijving="test", + zaak_type_config_identificatie=zt.identificatie, + catalogus_domein=zt.catalogus.domein, + catalogus_rsin=zt.catalogus.rsin, ), zt_io_type, ) def test_get_by_natural_key_not_found(self): - unused_zt = ZaakTypeConfigFactory() + zt = ZaakTypeConfigFactory() with self.assertRaises(ZaakTypeInformatieObjectTypeConfig.DoesNotExist): ZaakTypeInformatieObjectTypeConfig.objects.get_by_natural_key( - "http://foo.maykinmedia.nl", *unused_zt.natural_key() + omschrijving="bogus", + zaak_type_config_identificatie=zt.identificatie, + catalogus_domein=zt.catalogus.domein, + catalogus_rsin=zt.catalogus.rsin, ) def test_queryset_filter_case_type_with_catalog(self): From 24fffb7de82dd3bf60b4dadfcdfa2f697781a163 Mon Sep 17 00:00:00 2001 From: Paul Schilling Date: Fri, 15 Nov 2024 12:15:08 +0100 Subject: [PATCH 2/2] [#2871] Refactor ZGW import logic - overwrite only editable fields when importing ZGW entities, skip read-only fields (url, domein, rsin) --- src/open_inwoner/openzaak/admin.py | 26 +- src/open_inwoner/openzaak/import_export.py | 269 +++++++--- src/open_inwoner/openzaak/managers.py | 8 +- src/open_inwoner/openzaak/models.py | 1 - src/open_inwoner/openzaak/tests/test_admin.py | 120 ++++- .../openzaak/tests/test_import_export.py | 459 ++++++++++-------- .../openzaak/tests/test_models.py | 59 +-- .../tests/test_zgw_imports_command.py | 5 +- 8 files changed, 604 insertions(+), 343 deletions(-) diff --git a/src/open_inwoner/openzaak/admin.py b/src/open_inwoner/openzaak/admin.py index ffae93e571..9df74ee3e1 100644 --- a/src/open_inwoner/openzaak/admin.py +++ b/src/open_inwoner/openzaak/admin.py @@ -10,6 +10,7 @@ from django.template.defaultfilters import filesizeformat from django.template.response import TemplateResponse from django.urls import path, reverse +from django.utils.html import format_html, format_html_join from django.utils.translation import gettext_lazy as _, ngettext from import_export.admin import ImportExportMixin @@ -174,11 +175,30 @@ def process_file_view(self, request): self.message_user( request, _( - "Successfully processed %(num_rows)d items" - % {"num_rows": import_result.total_rows_processed} + "%(num_rows)d item(s) processed in total, with %(error_rows)d failing row(s)." + % { + "num_rows": import_result.total_rows_processed, + "error_rows": len(import_result.import_errors), + } ), - messages.SUCCESS, + messages.SUCCESS + if not import_result.import_errors + else messages.WARNING, ) + if errors := import_result.import_errors: + msgs_deduped = set(error.__str__() for error in errors) + error_msg_iterator = ([msg] for msg in msgs_deduped) + + error_msg_html = format_html_join( + "\n", "
  • {}
  • ", error_msg_iterator + ) + error_msg_html_ordered = format_html( + _("It was not possible to import the following items:") + + f"
      {error_msg_html}
    " + ) + self.message_user( + request, error_msg_html_ordered, messages.ERROR + ) return HttpResponseRedirect( reverse( diff --git a/src/open_inwoner/openzaak/import_export.py b/src/open_inwoner/openzaak/import_export.py index f2318ecf15..8eaea2414e 100644 --- a/src/open_inwoner/openzaak/import_export.py +++ b/src/open_inwoner/openzaak/import_export.py @@ -6,8 +6,11 @@ from typing import IO, Any, Generator, Self from urllib.parse import urlparse +from django.apps import apps from django.core import serializers +from django.core.exceptions import MultipleObjectsReturned, ObjectDoesNotExist from django.core.files.storage import Storage +from django.core.serializers.base import DeserializationError from django.db import transaction from django.db.models import QuerySet @@ -22,6 +25,132 @@ logger = logging.getLogger(__name__) +class ZGWImportError(Exception): + @classmethod + def extract_error_data(cls, exc: Exception, jsonl: str): + exc_source = type(exc.__context__) + data = json.loads(jsonl) if jsonl else {} + source_config = apps.get_model(data["model"]) + + # error type + if exc_source is CatalogusConfig.DoesNotExist or source_config.DoesNotExist: + error_type = ObjectDoesNotExist + if exc_source is source_config.MultipleObjectsReturned: + error_type = MultipleObjectsReturned + + # metadata about source_config + items = [] + fields = data.get("fields", None) + if source_config is CatalogusConfig: + items = [ + f"Domein = {fields['domein']}", + f"Rsin = {fields['rsin']}", + ] + if source_config is ZaakTypeConfig: + items = [ + f"Identificatie = {fields['identificatie']}", + f"Catalogus domein = {fields['catalogus'][0]}", + f"Catalogus rsin = {fields['catalogus'][1]}", + ] + if source_config in { + ZaakTypeStatusTypeConfig, + ZaakTypeResultaatTypeConfig, + ZaakTypeInformatieObjectTypeConfig, + }: + items = [ + f"omschrijving = {fields['omschrijving']}", + f"ZaakTypeConfig identificatie = {fields['zaaktype_config'][0]}", + f"Catalogus domein = {fields['zaaktype_config'][1]}", + f"Catalogus rsin = {fields['zaaktype_config'][2]}", + ] + + return { + "error_type": error_type, + "source_config_name": source_config.__name__, + "info": ", ".join(items), + } + + @classmethod + def from_exception_and_jsonl(cls, exception: Exception, jsonl: str) -> Self: + error_data = cls.extract_error_data(exception, jsonl) + + error_template = ( + "%(source_config_name)s not found in target environment: %(info)s" + ) + if error_data["error_type"] is MultipleObjectsReturned: + error_template = "Got multiple results for %(source_config_name)s: %(info)s" + + return cls(error_template % error_data) + + +def check_catalogus_config_exists(source_config: CatalogusConfig, jsonl: str): + try: + CatalogusConfig.objects.get_by_natural_key( + domein=source_config.domein, rsin=source_config.rsin + ) + except CatalogusConfig.MultipleObjectsReturned as exc: + raise ZGWImportError.from_exception_and_jsonl(exc, jsonl) + except CatalogusConfig.DoesNotExist as exc: + raise ZGWImportError.from_exception_and_jsonl(exc, jsonl) + + +def _update_config(source, target, exclude_fields): + for field in source._meta.fields: + field_name = field.name + + if field_name in exclude_fields: + continue + + val = getattr(source, field_name, None) + setattr(target, field_name, val) + target.save() + + +def _update_zaaktype_config(source_config: ZaakTypeConfig, jsonl: str): + try: + target = ZaakTypeConfig.objects.get_by_natural_key( + identificatie=source_config.identificatie, + catalogus_domein=source_config.catalogus.domein, + catalogus_rsin=source_config.catalogus.rsin, + ) + except ZaakTypeConfig.MultipleObjectsReturned as exc: + raise ZGWImportError.from_exception_and_jsonl(exc, jsonl) + except (CatalogusConfig.DoesNotExist, ZaakTypeConfig.DoesNotExist) as exc: + raise ZGWImportError.from_exception_and_jsonl(exc, jsonl) + else: + exclude_fields = [ + "id", + "catalogus", + "urls", + "zaaktype_uuids", + ] + _update_config(source_config, target, exclude_fields) + + +def _update_nested_zgw_config( + source_config: ZaakTypeStatusTypeConfig + | ZaakTypeResultaatTypeConfig + | ZaakTypeInformatieObjectTypeConfig, + exclude_fields: list[str], + jsonl: str, +): + zaaktype_config_identificatie = source_config.zaaktype_config.identificatie + catalogus_domein = source_config.zaaktype_config.catalogus.domein + catalogus_rsin = source_config.zaaktype_config.catalogus.rsin + + try: + target = source_config.__class__.objects.get_by_natural_key( + omschrijving=source_config.omschrijving, + zaaktype_config_identificatie=zaaktype_config_identificatie, + catalogus_domein=catalogus_domein, + catalogus_rsin=catalogus_rsin, + ) + except (source_config.DoesNotExist, source_config.MultipleObjectsReturned) as exc: + raise ZGWImportError.from_exception_and_jsonl(exc, jsonl) + else: + _update_config(source_config, target, exclude_fields) + + @dataclasses.dataclass(frozen=True) class CatalogusConfigExport: """Gather and export CatalogusConfig(s) and all associated relations.""" @@ -113,9 +242,10 @@ class CatalogusConfigImport: total_rows_processed: int = 0 catalogus_configs_imported: int = 0 zaaktype_configs_imported: int = 0 - zaak_inormatie_object_type_configs_imported: int = 0 + zaak_informatie_object_type_configs_imported: int = 0 zaak_status_type_configs_imported: int = 0 zaak_resultaat_type_configs_imported: int = 0 + import_errors: list | None = None @staticmethod def _get_url_root(url: str) -> str: @@ -149,90 +279,85 @@ def _lines_iter_from_jsonl_stream_or_string( # Reset the stream in case it gets re-used lines.seek(0) - @classmethod - def _rewrite_jsonl_url_references( - cls, stream_or_string: IO | str - ) -> Generator[str, Any, None]: - # The assumption is that the exporting and importing instance both have - # a `Service` with the same slug as the `Service` referenced in the - # `configued_from` attribute of the imported CatalogusConfig. The - # assumption is further that all URLs in the imported objects are - # prefixed by an URL that matches the API root in the service. Because - # of this, the import file will contain URLs with a base URL pointing to - # the `api_root`` of the `configured_from` Service on the _source_ - # instance, and has to be re-written to match the `api_root` of the - # `configured_from` Service on the _target_ instance. Put differently, - # we assume that we are migrating ZGW objects that _do not differ_ as - # far as the ZGW objects themselves are concerned (apart from the URL, - # they essentially point to the same ZGW backend), but that they _do_ - # differ in terms of additional model fields that do not have their - # source of truth in the ZGW backends. - # - # This expectation is also encoded in our API clients: you can only - # fetch ZGW objects using the ApePie clients if the root of those - # objects matches the configured API root. - - base_url_mapping = {} - for deserialized_object in serializers.deserialize( - "jsonl", - filter( - lambda row: ('"model": "openzaak.catalogusconfig"' in row), - cls._lines_iter_from_jsonl_stream_or_string(stream_or_string), - ), - use_natural_foreign_keys=True, - use_natural_primary_keys=True, - ): - object_type: str = deserialized_object.object.__class__.__name__ - - if object_type == "CatalogusConfig": - target_base_url = cls._get_url_root( - deserialized_object.object.service.api_root - ) - source_base_url = cls._get_url_root(deserialized_object.object.url) - base_url_mapping[source_base_url] = target_base_url - else: - # https://www.xkcd.com/2200/ - logger.error( - "Tried to filter for catalogus config objects, but also got: %s", - object_type, - ) - - for line in cls._lines_iter_from_jsonl_stream_or_string(stream_or_string): - source_url_found = False - for source, target in base_url_mapping.items(): - line = line.replace(source, target) - source_url_found = True - - if not source_url_found: - raise ValueError("Unable to rewrite ZGW urls") - - yield line - @classmethod @transaction.atomic() def from_jsonl_stream_or_string(cls, stream_or_string: IO | str) -> Self: model_to_counter_mapping = { "CatalogusConfig": "catalogus_configs_imported", "ZaakTypeConfig": "zaaktype_configs_imported", - "ZaakTypeInformatieObjectTypeConfig": "zaak_inormatie_object_type_configs_imported", + "ZaakTypeInformatieObjectTypeConfig": "zaak_informatie_object_type_configs_imported", "ZaakTypeStatusTypeConfig": "zaak_status_type_configs_imported", "ZaakTypeResultaatTypeConfig": "zaak_resultaat_type_configs_imported", } - object_type_counts = defaultdict(int) - for deserialized_object in serializers.deserialize( - "jsonl", - cls._rewrite_jsonl_url_references(stream_or_string), - use_natural_foreign_keys=True, - use_natural_primary_keys=True, - ): - deserialized_object.save() - object_type = deserialized_object.object.__class__.__name__ - object_type_counts[object_type] += 1 + rows_successfully_processed = 0 + import_errors = [] + for line in cls._lines_iter_from_jsonl_stream_or_string(stream_or_string): + try: + (deserialized_object,) = serializers.deserialize( + "jsonl", + line, + use_natural_primary_keys=True, + use_natural_foreign_keys=True, + ) + except DeserializationError as exc: + error = ZGWImportError.from_exception_and_jsonl(exc, line) + logger.error(error) + import_errors.append(error) + else: + source_config = deserialized_object.object + try: + match source_config: + case CatalogusConfig(): + check_catalogus_config_exists( + source_config=source_config, jsonl=line + ) + case ZaakTypeConfig(): + _update_zaaktype_config( + source_config=source_config, jsonl=line + ) + case ZaakTypeInformatieObjectTypeConfig(): + exclude_fields = [ + "id", + "zaaktype_config", + "zaaktype_uuids", + "informatieobjecttype_url", + ] + _update_nested_zgw_config( + source_config, exclude_fields, line + ) + case ZaakTypeStatusTypeConfig(): + exclude_fields = [ + "id", + "zaaktype_config", + "zaaktype_uuids", + "statustype_url", + ] + _update_nested_zgw_config( + source_config, exclude_fields, line + ) + case ZaakTypeResultaatTypeConfig(): + exclude_fields = [ + "id", + "zaaktype_config", + "zaaktype_uuids", + "resultaattype_url", + ] + _update_nested_zgw_config( + source_config, exclude_fields, line + ) + except ZGWImportError as exc: + logger.error(exc) + import_errors.append(exc) + else: + object_type = source_config.__class__.__name__ + object_type_counts[object_type] += 1 + rows_successfully_processed += 1 creation_kwargs = { - "total_rows_processed": sum(object_type_counts.values()), + "total_rows_processed": rows_successfully_processed + len(import_errors), + "import_errors": import_errors, } for model_name, counter_field in model_to_counter_mapping.items(): diff --git a/src/open_inwoner/openzaak/managers.py b/src/open_inwoner/openzaak/managers.py index 6f515dde64..7e578f427f 100644 --- a/src/open_inwoner/openzaak/managers.py +++ b/src/open_inwoner/openzaak/managers.py @@ -93,7 +93,7 @@ class ZaakTypeInformatieObjectTypeConfigQueryset(models.QuerySet): def get_by_natural_key( self, omschrijving, - zaak_type_config_identificatie, + zaaktype_config_identificatie, catalogus_domein, catalogus_rsin, ): @@ -101,7 +101,7 @@ def get_by_natural_key( return self.get( zaaktype_config=ZaakTypeConfig.objects.get( - identificatie=zaak_type_config_identificatie, + identificatie=zaaktype_config_identificatie, catalogus__domein=catalogus_domein, catalogus__rsin=catalogus_rsin, ), @@ -248,7 +248,7 @@ class ZaakTypeResultaatTypeConfigManger(models.Manager): def get_by_natural_key( self, omschrijving, - zaak_type_config_identificatie, + zaaktype_config_identificatie, catalogus_domein, catalogus_rsin, ): @@ -256,7 +256,7 @@ def get_by_natural_key( return self.get( zaaktype_config=ZaakTypeConfig.objects.get( - identificatie=zaak_type_config_identificatie, + identificatie=zaaktype_config_identificatie, catalogus__domein=catalogus_domein, catalogus__rsin=catalogus_rsin, ), diff --git a/src/open_inwoner/openzaak/models.py b/src/open_inwoner/openzaak/models.py index d0b34fff27..85eaf4f157 100644 --- a/src/open_inwoner/openzaak/models.py +++ b/src/open_inwoner/openzaak/models.py @@ -4,7 +4,6 @@ from typing import Protocol, cast from urllib.parse import urlparse -from django.core.exceptions import ValidationError from django.db import models, transaction from django.db.models import UniqueConstraint from django.utils import timezone diff --git a/src/open_inwoner/openzaak/tests/test_admin.py b/src/open_inwoner/openzaak/tests/test_admin.py index 5ec4fd7b8f..fe904f1630 100644 --- a/src/open_inwoner/openzaak/tests/test_admin.py +++ b/src/open_inwoner/openzaak/tests/test_admin.py @@ -175,28 +175,34 @@ def test_export_action_returns_correct_export(self): msg="Response should be valid JSONL matching the input object", ) - @mock.patch( - "open_inwoner.openzaak.import_export.CatalogusConfigImport.import_from_jsonl_file_in_django_storage" - ) - def test_import_flow_reports_success(self, m) -> None: + def test_import_flow_reports_success(self) -> None: + service = ServiceFactory.create(slug="service-0") + CatalogusConfigFactory.create( + url="https://foo.0.maykinmedia.nl", + domein="DM-0", + rsin="123456789", + service=service, + ) + import_lines = [ + b'{"model": "openzaak.catalogusconfig", "fields": {"url": "https://foo.0.maykinmedia.nl", "domein": "DM-0", "rsin": "123456789", "service": ["service-0"]}}', + ] + import_line = b"\n".join(import_lines) + mock_file = Upload("dump.json", import_line, "application/json") + form = self.app.get( reverse( "admin:upload_zgw_import_file", ), user=self.user, ).form - form["zgw_export_file"] = self.mock_file + form["zgw_export_file"] = mock_file response = form.submit().follow() - self.assertEqual(m.call_count, 1) - self.assertEqual(m.call_args[0][0], "zgw_import_dump_2024-08-14-17-50-01.json") - self.assertTrue(isinstance(m.call_args[0][1], PrivateMediaFileSystemStorage)) - messages = [str(msg) for msg in response.context["messages"]] self.assertEqual( messages, - [_("Successfully processed 1 items")], + [_("1 item(s) processed in total, with 0 failing row(s).")], ) self.assertFalse( PrivateMediaFileSystemStorage().exists( @@ -251,3 +257,97 @@ def test_import_flow_errors_reports_failure_to_user(self, m) -> None: "admin:upload_zgw_import_file", ), ) + + def test_import_flow_reports_errors(self) -> None: + import_lines = [ + b'{"model": "openzaak.catalogusconfig", "fields": {"url": "https://foo.0.maykinmedia.nl", "domein": "DM-0", "rsin": "123456789", "service": ["service-0"]}}', + b'{"model": "openzaak.zaaktypeconfig", "fields": {"urls": "[\\"https://foo.0.maykinmedia.nl\\"]", "catalogus": ["DM-0", "123456789"], "identificatie": "ztc-id-a-0", "omschrijving": "zaaktypeconfig", "notify_status_changes": false, "description": "", "external_document_upload_url": "", "document_upload_enabled": true, "contact_form_enabled": false, "contact_subject_code": "", "relevante_zaakperiode": null}}', + b'{"model": "openzaak.zaaktypeinformatieobjecttypeconfig", "fields": {"zaaktype_config": ["ztc-id-a-0", "DM-0", "123456789"], "informatieobjecttype_url": "http://foo.0.maykinmedia.nl", "omschrijving": "informatieobject", "zaaktype_uuids": "[]", "document_upload_enabled": true, "document_notification_enabled": true}}', + b'{"model": "openzaak.zaaktypestatustypeconfig", "fields": {"zaaktype_config": ["ztc-id-a-0", "DM-0", "123456789"], "statustype_url": "https://foo.0.maykinmedia.nl", "omschrijving": "status omschrijving", "statustekst": "statustekst nieuw", "zaaktype_uuids": "[]", "status_indicator": "", "status_indicator_text": "", "document_upload_description": "", "description": "status", "notify_status_change": true, "action_required": false, "document_upload_enabled": true, "call_to_action_url": "", "call_to_action_text": "", "case_link_text": ""}}', + b'{"model": "openzaak.zaaktyperesultaattypeconfig", "fields": {"zaaktype_config": ["ztc-id-a-0", "DM-0", "123456789"], "resultaattype_url": "https://foo.0.maykinmedia.nl", "omschrijving": "resultaat", "zaaktype_uuids": "[]", "description": "description new"}}', + b'{"model": "openzaak.zaaktypestatustypeconfig", "fields": {"zaaktype_config": ["ztc-id-a-0", "DM-0", "123456789"], "statustype_url": "https://foo.0.maykinmedia.nl", "omschrijving": "bogus", "statustekst": "bogus", "zaaktype_uuids": "[]", "status_indicator": "", "status_indicator_text": "", "document_upload_description": "", "description": "status", "notify_status_change": true, "action_required": false, "document_upload_enabled": true, "call_to_action_url": "", "call_to_action_text": "", "case_link_text": ""}}', + ] + import_line = b"\n".join(import_lines) + mock_file = Upload("dump.json", import_line, "application/json") + + form = self.app.get( + reverse( + "admin:upload_zgw_import_file", + ), + user=self.user, + ).form + form["zgw_export_file"] = mock_file + + response = form.submit().follow() + + messages = [str(msg) for msg in response.context["messages"]] + self.assertEqual(len(messages), 2) + self.assertEqual( + _("6 item(s) processed in total, with 6 failing row(s)."), + messages[0], + ) + self.assertIn( + "ZaakTypeConfig not found in target environment: Identificatie = ztc-id-a-0, " + "Catalogus domein = DM-0, Catalogus rsin = 123456789", + messages[1], + ) + self.assertIn( + "ZaakTypeStatusTypeConfig not found in target environment: omschrijving = status omschrijving, " + "ZaakTypeConfig identificatie = ztc-id-a-0, Catalogus domein = DM-0, Catalogus rsin = 123456789", + messages[1], + ) + self.assertIn( + "ZaakTypeStatusTypeConfig not found in target environment: omschrijving = bogus, ZaakTypeConfig " + "identificatie = ztc-id-a-0, Catalogus domein = DM-0, Catalogus rsin = 123456789", + messages[1], + ) + self.assertIn( + "ZaakTypeInformatieObjectTypeConfig not found in target environment: omschrijving = informatieobject, " + "ZaakTypeConfig identificatie = ztc-id-a-0, Catalogus domein = DM-0, Catalogus rsin = 123456789", + messages[1], + ) + self.assertIn( + "CatalogusConfig not found in target environment: Domein = DM-0, Rsin = 123456789", + messages[1], + ) + self.assertIn( + "ZaakTypeResultaatTypeConfig not found in target environment: omschrijving = resultaat, " + "ZaakTypeConfig identificatie = ztc-id-a-0, Catalogus domein = DM-0, Catalogus rsin = 123456789", + messages[1], + ) + + def test_import_flow_reports_partial_errors(self) -> None: + service = ServiceFactory.create(slug="service-0") + CatalogusConfigFactory.create( + url="https://foo.0.maykinmedia.nl", + domein="DM-0", + rsin="123456789", + service=service, + ) + import_lines = [ + b'{"model": "openzaak.catalogusconfig", "fields": {"url": "https://foo.0.maykinmedia.nl", "domein": "DM-0", "rsin": "123456789", "service": ["service-0"]}}', + b'{"model": "openzaak.zaaktypeconfig", "fields": {"urls": "[\\"https://foo.0.maykinmedia.nl\\"]", "catalogus": ["DM-0", "123456789"], "identificatie": "ztc-id-a-0", "omschrijving": "zaaktypeconfig", "notify_status_changes": false, "description": "", "external_document_upload_url": "", "document_upload_enabled": true, "contact_form_enabled": false, "contact_subject_code": "", "relevante_zaakperiode": null}}', + ] + import_line = b"\n".join(import_lines) + mock_file = Upload("dump.json", import_line, "application/json") + + form = self.app.get( + reverse( + "admin:upload_zgw_import_file", + ), + user=self.user, + ).form + form["zgw_export_file"] = mock_file + + response = form.submit().follow() + + messages = [str(msg) for msg in response.context["messages"]] + self.assertEqual(len(messages), 2) + self.assertEqual( + _("2 item(s) processed in total, with 1 failing row(s)."), + messages[0], + ) + self.assertEqual( + "It was not possible to import the following items:
    1. ZaakTypeConfig not found in target environment: Identificatie = ztc-id-a-0, Catalogus domein = DM-0, Catalogus rsin = 123456789
    ", + messages[1], + ) diff --git a/src/open_inwoner/openzaak/tests/test_import_export.py b/src/open_inwoner/openzaak/tests/test_import_export.py index 55bb2959d2..c950b825b3 100644 --- a/src/open_inwoner/openzaak/tests/test_import_export.py +++ b/src/open_inwoner/openzaak/tests/test_import_export.py @@ -1,4 +1,6 @@ +import dataclasses import io +import uuid from django.core.files.storage.memory import InMemoryStorage from django.test import TestCase @@ -15,6 +17,7 @@ ZaakTypeStatusTypeConfig, ) +from ..import_export import ZGWImportError from .factories import ( CatalogusConfigFactory, ServiceFactory, @@ -27,9 +30,11 @@ class ZGWExportImportMockData: def __init__(self, count=0): + self.original_url = f"https://foo.{count}.maykinmedia.nl" + self.original_uuid = "a1591906-3368-470a-a957-4b8634c275a1" self.service = ServiceFactory(slug=f"service-{count}") self.catalogus = CatalogusConfigFactory( - url=f"https://foo.{count}.maykinmedia.nl", + url=self.original_url, domein=f"DM-{count}", rsin="123456789", service=self.service, @@ -44,14 +49,14 @@ def __init__(self, count=0): contact_form_enabled=False, contact_subject_code="", relevante_zaakperiode=None, - urls=[f"https://foo.{count}.maykinmedia.nl"], + urls=[self.original_url], ) self.ztc_status = ZaakTypeStatusTypeConfigFactory( zaaktype_config=self.ztc, - statustype_url=f"https://foo.{count}.maykinmedia.nl", + statustype_url=self.original_url, omschrijving="status omschrijving", statustekst="", - zaaktype_uuids=[], + zaaktype_uuids=[self.original_uuid], status_indicator="", status_indicator_text="", document_upload_description="", @@ -65,15 +70,16 @@ def __init__(self, count=0): ) self.ztc_resultaat = ZaakTypeResultaatTypeConfigFactory( zaaktype_config=self.ztc, - resultaattype_url=f"https://foo.{count}.maykinmedia.nl", + resultaattype_url=self.original_url, omschrijving="resultaat", - zaaktype_uuids=[], + zaaktype_uuids=[self.original_uuid], description="", ) self.ztiotc = ZaakTypeInformatieObjectTypeConfigFactory( zaaktype_config=self.ztc, - informatieobjecttype_url=f"http://foo.{count}.maykinmedia.nl", + informatieobjecttype_url=self.original_url, omschrijving="informatieobject", + zaaktype_uuids=[self.original_uuid], ) @@ -185,9 +191,9 @@ def test_export_catalogus_configs(self): "model": "openzaak.zaaktypeinformatieobjecttypeconfig", "fields": { "zaaktype_config": ["ztc-id-a-0", "DM-0", "123456789"], - "informatieobjecttype_url": "http://foo.0.maykinmedia.nl", + "informatieobjecttype_url": "https://foo.0.maykinmedia.nl", "omschrijving": "informatieobject", - "zaaktype_uuids": "[]", + "zaaktype_uuids": '["a1591906-3368-470a-a957-4b8634c275a1"]', "document_upload_enabled": False, "document_notification_enabled": False, }, @@ -199,7 +205,7 @@ def test_export_catalogus_configs(self): "statustype_url": "https://foo.0.maykinmedia.nl", "omschrijving": "status omschrijving", "statustekst": "", - "zaaktype_uuids": "[]", + "zaaktype_uuids": '["a1591906-3368-470a-a957-4b8634c275a1"]', "status_indicator": "", "status_indicator_text": "", "document_upload_description": "", @@ -218,7 +224,7 @@ def test_export_catalogus_configs(self): "zaaktype_config": ["ztc-id-a-0", "DM-0", "123456789"], "resultaattype_url": "https://foo.0.maykinmedia.nl", "omschrijving": "resultaat", - "zaaktype_uuids": "[]", + "zaaktype_uuids": '["a1591906-3368-470a-a957-4b8634c275a1"]', "description": "", }, }, @@ -241,17 +247,17 @@ def test_export_catalogus_configs_as_jsonl(self): "\n", '{"model": "openzaak.zaaktypeconfig", "fields": {"urls": "[\\"https://foo.1.maykinmedia.nl\\"]", "catalogus": ["DM-1", "123456789"], "identificatie": "ztc-id-a-1", "omschrijving": "zaaktypeconfig", "notify_status_changes": false, "description": "", "external_document_upload_url": "", "document_upload_enabled": false, "contact_form_enabled": false, "contact_subject_code": "", "relevante_zaakperiode": null}}', "\n", - '{"model": "openzaak.zaaktypeinformatieobjecttypeconfig", "fields": {"zaaktype_config": ["ztc-id-a-0", "DM-0", "123456789"], "informatieobjecttype_url": "http://foo.0.maykinmedia.nl", "omschrijving": "informatieobject", "zaaktype_uuids": "[]", "document_upload_enabled": false, "document_notification_enabled": false}}', + '{"model": "openzaak.zaaktypeinformatieobjecttypeconfig", "fields": {"zaaktype_config": ["ztc-id-a-0", "DM-0", "123456789"], "informatieobjecttype_url": "https://foo.0.maykinmedia.nl", "omschrijving": "informatieobject", "zaaktype_uuids": "[\\"a1591906-3368-470a-a957-4b8634c275a1\\"]", "document_upload_enabled": false, "document_notification_enabled": false}}', "\n", - '{"model": "openzaak.zaaktypeinformatieobjecttypeconfig", "fields": {"zaaktype_config": ["ztc-id-a-1", "DM-1", "123456789"], "informatieobjecttype_url": "http://foo.1.maykinmedia.nl", "omschrijving": "informatieobject", "zaaktype_uuids": "[]", "document_upload_enabled": false, "document_notification_enabled": false}}', + '{"model": "openzaak.zaaktypeinformatieobjecttypeconfig", "fields": {"zaaktype_config": ["ztc-id-a-1", "DM-1", "123456789"], "informatieobjecttype_url": "https://foo.1.maykinmedia.nl", "omschrijving": "informatieobject", "zaaktype_uuids": "[\\"a1591906-3368-470a-a957-4b8634c275a1\\"]", "document_upload_enabled": false, "document_notification_enabled": false}}', "\n", - '{"model": "openzaak.zaaktypestatustypeconfig", "fields": {"zaaktype_config": ["ztc-id-a-0", "DM-0", "123456789"], "statustype_url": "https://foo.0.maykinmedia.nl", "omschrijving": "status omschrijving", "statustekst": "", "zaaktype_uuids": "[]", "status_indicator": "", "status_indicator_text": "", "document_upload_description": "", "description": "status", "notify_status_change": true, "action_required": false, "document_upload_enabled": true, "call_to_action_url": "", "call_to_action_text": "", "case_link_text": ""}}', + '{"model": "openzaak.zaaktypestatustypeconfig", "fields": {"zaaktype_config": ["ztc-id-a-0", "DM-0", "123456789"], "statustype_url": "https://foo.0.maykinmedia.nl", "omschrijving": "status omschrijving", "statustekst": "", "zaaktype_uuids": "[\\"a1591906-3368-470a-a957-4b8634c275a1\\"]", "status_indicator": "", "status_indicator_text": "", "document_upload_description": "", "description": "status", "notify_status_change": true, "action_required": false, "document_upload_enabled": true, "call_to_action_url": "", "call_to_action_text": "", "case_link_text": ""}}', "\n", - '{"model": "openzaak.zaaktypestatustypeconfig", "fields": {"zaaktype_config": ["ztc-id-a-1", "DM-1", "123456789"], "statustype_url": "https://foo.1.maykinmedia.nl", "omschrijving": "status omschrijving", "statustekst": "", "zaaktype_uuids": "[]", "status_indicator": "", "status_indicator_text": "", "document_upload_description": "", "description": "status", "notify_status_change": true, "action_required": false, "document_upload_enabled": true, "call_to_action_url": "", "call_to_action_text": "", "case_link_text": ""}}', + '{"model": "openzaak.zaaktypestatustypeconfig", "fields": {"zaaktype_config": ["ztc-id-a-1", "DM-1", "123456789"], "statustype_url": "https://foo.1.maykinmedia.nl", "omschrijving": "status omschrijving", "statustekst": "", "zaaktype_uuids": "[\\"a1591906-3368-470a-a957-4b8634c275a1\\"]", "status_indicator": "", "status_indicator_text": "", "document_upload_description": "", "description": "status", "notify_status_change": true, "action_required": false, "document_upload_enabled": true, "call_to_action_url": "", "call_to_action_text": "", "case_link_text": ""}}', "\n", - '{"model": "openzaak.zaaktyperesultaattypeconfig", "fields": {"zaaktype_config": ["ztc-id-a-0", "DM-0", "123456789"], "resultaattype_url": "https://foo.0.maykinmedia.nl", "omschrijving": "resultaat", "zaaktype_uuids": "[]", "description": ""}}', + '{"model": "openzaak.zaaktyperesultaattypeconfig", "fields": {"zaaktype_config": ["ztc-id-a-0", "DM-0", "123456789"], "resultaattype_url": "https://foo.0.maykinmedia.nl", "omschrijving": "resultaat", "zaaktype_uuids": "[\\"a1591906-3368-470a-a957-4b8634c275a1\\"]", "description": ""}}', "\n", - '{"model": "openzaak.zaaktyperesultaattypeconfig", "fields": {"zaaktype_config": ["ztc-id-a-1", "DM-1", "123456789"], "resultaattype_url": "https://foo.1.maykinmedia.nl", "omschrijving": "resultaat", "zaaktype_uuids": "[]", "description": ""}}', + '{"model": "openzaak.zaaktyperesultaattypeconfig", "fields": {"zaaktype_config": ["ztc-id-a-1", "DM-1", "123456789"], "resultaattype_url": "https://foo.1.maykinmedia.nl", "omschrijving": "resultaat", "zaaktype_uuids": "[\\"a1591906-3368-470a-a957-4b8634c275a1\\"]", "description": ""}}', "\n", ] @@ -261,243 +267,302 @@ def test_export_catalogus_configs_as_jsonl(self): class TestCatalogusImport(TestCase): def setUp(self): self.storage = InMemoryStorage() - self.service = ServiceFactory( - slug="service-0", api_root="https://foo.0.maykinmedia.nl" - ) - self.other_service = ServiceFactory( - slug="service-1", api_root="https://foo.1.maykinmedia.nl" - ) - self.json_lines = [ - '{"model": "openzaak.catalogusconfig", "fields": {"url": "https://foo.0.maykinmedia.nl", "domein": "DM-0", "rsin": "123456789", "service": ["service-0"]}}', - '{"model": "openzaak.catalogusconfig", "fields": {"url": "https://foo.1.maykinmedia.nl", "domein": "DM-1", "rsin": "123456789", "service": ["service-1"]}}', - '{"model": "openzaak.zaaktypeconfig", "fields": {"urls": "[\\"https://foo.0.maykinmedia.nl\\"]", "catalogus": ["DM-0", "123456789"], "identificatie": "ztc-id-a-0", "omschrijving": "zaaktypeconfig", "notify_status_changes": false, "description": "", "external_document_upload_url": "", "document_upload_enabled": false, "contact_form_enabled": false, "contact_subject_code": "", "relevante_zaakperiode": null}}', - '{"model": "openzaak.zaaktypeconfig", "fields": {"urls": "[\\"https://foo.1.maykinmedia.nl\\"]", "catalogus": ["DM-1", "123456789"], "identificatie": "ztc-id-a-1", "omschrijving": "zaaktypeconfig", "notify_status_changes": false, "description": "", "external_document_upload_url": "", "document_upload_enabled": false, "contact_form_enabled": false, "contact_subject_code": "", "relevante_zaakperiode": null}}', - '{"model": "openzaak.zaaktypeinformatieobjecttypeconfig", "fields": {"zaaktype_config": ["ztc-id-a-0", "DM-0", "123456789"], "informatieobjecttype_url": "http://foo.0.maykinmedia.nl", "omschrijving": "informatieobject", "zaaktype_uuids": "[]", "document_upload_enabled": false, "document_notification_enabled": false}}', - '{"model": "openzaak.zaaktypeinformatieobjecttypeconfig", "fields": {"zaaktype_config": ["ztc-id-a-1", "DM-1", "123456789"], "informatieobjecttype_url": "http://foo.1.maykinmedia.nl", "omschrijving": "informatieobject", "zaaktype_uuids": "[]", "document_upload_enabled": false, "document_notification_enabled": false}}', - '{"model": "openzaak.zaaktypestatustypeconfig", "fields": {"zaaktype_config": ["ztc-id-a-0", "DM-0", "123456789"], "statustype_url": "https://foo.0.maykinmedia.nl", "omschrijving": "status omschrijving", "statustekst": "", "zaaktype_uuids": "[]", "status_indicator": "", "status_indicator_text": "", "document_upload_description": "", "description": "status", "notify_status_change": true, "action_required": false, "document_upload_enabled": true, "call_to_action_url": "", "call_to_action_text": "", "case_link_text": ""}}', - '{"model": "openzaak.zaaktypestatustypeconfig", "fields": {"zaaktype_config": ["ztc-id-a-1", "DM-1", "123456789"], "statustype_url": "https://foo.1.maykinmedia.nl", "omschrijving": "status omschrijving", "statustekst": "", "zaaktype_uuids": "[]", "status_indicator": "", "status_indicator_text": "", "document_upload_description": "", "description": "status", "notify_status_change": true, "action_required": false, "document_upload_enabled": true, "call_to_action_url": "", "call_to_action_text": "", "case_link_text": ""}}', - '{"model": "openzaak.zaaktyperesultaattypeconfig", "fields": {"zaaktype_config": ["ztc-id-a-0", "DM-0", "123456789"], "resultaattype_url": "https://foo.0.maykinmedia.nl", "omschrijving": "resultaat", "zaaktype_uuids": "[]", "description": ""}}', - '{"model": "openzaak.zaaktyperesultaattypeconfig", "fields": {"zaaktype_config": ["ztc-id-a-1", "DM-1", "123456789"], "resultaattype_url": "https://foo.1.maykinmedia.nl", "omschrijving": "resultaat", "zaaktype_uuids": "[]", "description": ""}}', + '{"model": "openzaak.catalogusconfig", "fields": {"url": "https://bar.maykinmedia.nl", "domein": "DM-0", "rsin": "123456789", "service": ["service-0"]}}', + '{"model": "openzaak.zaaktypeconfig", "fields": {"urls": "[\\"https://bar.maykinmedia.nl\\"]", "catalogus": ["DM-0", "123456789"], "identificatie": "ztc-id-a-0", "omschrijving": "zaaktypeconfig", "notify_status_changes": false, "description": "", "external_document_upload_url": "", "document_upload_enabled": true, "contact_form_enabled": false, "contact_subject_code": "", "relevante_zaakperiode": null}}', + '{"model": "openzaak.zaaktypeinformatieobjecttypeconfig", "fields": {"zaaktype_config": ["ztc-id-a-0", "DM-0", "123456789"], "informatieobjecttype_url": "https://bar.maykinmedia.nl", "omschrijving": "informatieobject", "zaaktype_uuids": "[]", "document_upload_enabled": true, "document_notification_enabled": true}}', + '{"model": "openzaak.zaaktypestatustypeconfig", "fields": {"zaaktype_config": ["ztc-id-a-0", "DM-0", "123456789"], "statustype_url": "https://bar.maykinmedia.nl", "omschrijving": "status omschrijving", "statustekst": "statustekst nieuw", "zaaktype_uuids": "[]", "status_indicator": "", "status_indicator_text": "", "document_upload_description": "", "description": "status", "notify_status_change": true, "action_required": false, "document_upload_enabled": true, "call_to_action_url": "", "call_to_action_text": "", "case_link_text": ""}}', + '{"model": "openzaak.zaaktyperesultaattypeconfig", "fields": {"zaaktype_config": ["ztc-id-a-0", "DM-0", "123456789"], "resultaattype_url": "https://bar.maykinmedia.nl", "omschrijving": "resultaat", "zaaktype_uuids": "[]", "description": "description new"}}', ] self.jsonl = "\n".join(self.json_lines) - def test_import_jsonl_creates_objects(self): + def test_import_jsonl_update_success(self): + mocks = ZGWExportImportMockData() self.storage.save("import.jsonl", io.StringIO(self.jsonl)) import_result = CatalogusConfigImport.import_from_jsonl_file_in_django_storage( "import.jsonl", self.storage ) + + # check import self.assertEqual( import_result, CatalogusConfigImport( - total_rows_processed=10, - catalogus_configs_imported=2, - zaaktype_configs_imported=2, - zaak_inormatie_object_type_configs_imported=2, - zaak_status_type_configs_imported=2, - zaak_resultaat_type_configs_imported=2, + total_rows_processed=5, + catalogus_configs_imported=1, + zaaktype_configs_imported=1, + zaak_informatie_object_type_configs_imported=1, + zaak_status_type_configs_imported=1, + zaak_resultaat_type_configs_imported=1, + import_errors=[], ), ) - self.assertEqual(CatalogusConfig.objects.count(), 2) - self.assertEqual(ZaakTypeConfig.objects.count(), 2) - self.assertEqual(ZaakTypeInformatieObjectTypeConfig.objects.count(), 2) - self.assertEqual(ZaakTypeStatusTypeConfig.objects.count(), 2) - self.assertEqual(ZaakTypeResultaatTypeConfig.objects.count(), 2) + # check number of configs + self.assertEqual(CatalogusConfig.objects.count(), 1) + self.assertEqual(ZaakTypeConfig.objects.count(), 1) + self.assertEqual(ZaakTypeInformatieObjectTypeConfig.objects.count(), 1) + self.assertEqual(ZaakTypeStatusTypeConfig.objects.count(), 1) + self.assertEqual(ZaakTypeResultaatTypeConfig.objects.count(), 1) - def test_import_jsonl_merges_objects(self): - CatalogusConfigFactory( - url="https://foo.0.maykinmedia.nl", - domein="FOO", - rsin="123456789", - service=self.service, + catalogus_config = CatalogusConfig.objects.get() + zt_config = ZaakTypeConfig.objects.get() + zt_informatie_objecttype_config = ( + ZaakTypeInformatieObjectTypeConfig.objects.get() ) - merge_line = '{"model": "openzaak.catalogusconfig", "fields": {"url": "https://foo.0.maykinmedia.nl", "domein": "BAR", "rsin": "987654321", "service": ["service-0"]}}' - - import_result = CatalogusConfigImport.from_jsonl_stream_or_string(merge_line) - - self.assertEqual(import_result.catalogus_configs_imported, 1) - self.assertEqual(import_result.total_rows_processed, 1) + zt_statustype_config = ZaakTypeStatusTypeConfig.objects.get() + zt_resultaattype_config = ZaakTypeResultaatTypeConfig.objects.get() + # check that urls are not overridden + self.assertEqual(catalogus_config.url, mocks.original_url) + self.assertEqual(zt_config.urls, [mocks.original_url]) self.assertEqual( - list(CatalogusConfig.objects.values_list("url", "domein", "rsin")), - [("https://foo.0.maykinmedia.nl", "BAR", "987654321")], - msg="Value of sole CatalogusConfig matches imported values, not original values", + zt_informatie_objecttype_config.informatieobjecttype_url, + mocks.original_url, ) + self.assertEqual(zt_statustype_config.statustype_url, mocks.original_url) + self.assertEqual(zt_resultaattype_config.resultaattype_url, mocks.original_url) - def test_bad_import_types(self): - for bad_type in (set(), list(), b""): - with self.assertRaises(ValueError): - CatalogusConfigImport.from_jsonl_stream_or_string(bad_type) - - def test_valid_input_types_are_accepted(self): - for input in ( - io.StringIO(self.jsonl), - io.BytesIO(self.jsonl.encode("utf-8")), - self.jsonl, - ): - with self.subTest(f"Input type {type(input)}"): - import_result = CatalogusConfigImport.from_jsonl_stream_or_string(input) - self.assertEqual( - import_result, - CatalogusConfigImport( - total_rows_processed=10, - catalogus_configs_imported=2, - zaaktype_configs_imported=2, - zaak_inormatie_object_type_configs_imported=2, - zaak_status_type_configs_imported=2, - zaak_resultaat_type_configs_imported=2, - ), - ) - - self.assertEqual(CatalogusConfig.objects.count(), 2) - self.assertEqual(ZaakTypeConfig.objects.count(), 2) - self.assertEqual(ZaakTypeInformatieObjectTypeConfig.objects.count(), 2) - self.assertEqual(ZaakTypeStatusTypeConfig.objects.count(), 2) - self.assertEqual(ZaakTypeResultaatTypeConfig.objects.count(), 2) + # check that zaaktype uuids are not overridden + self.assertEqual( + zt_informatie_objecttype_config.zaaktype_uuids, + [uuid.UUID(mocks.original_uuid)], + ) + self.assertEqual( + zt_statustype_config.zaaktype_uuids, [uuid.UUID(mocks.original_uuid)] + ) + self.assertEqual( + zt_resultaattype_config.zaaktype_uuids, [uuid.UUID(mocks.original_uuid)] + ) - def test_import_is_atomic(self): - bad_line = '{"model": "openzaak.zaaktyperesultaattypeconfig", "fields": {}}\n' - bad_jsonl = self.jsonl + "\n" + bad_line + # check updated content + zaaktype_statustype_config = ZaakTypeStatusTypeConfig.objects.get() + self.assertEqual(zaaktype_statustype_config.statustekst, "statustekst nieuw") + self.assertEqual(zaaktype_statustype_config.notify_status_change, True) - try: - CatalogusConfigImport.from_jsonl_stream_or_string( - stream_or_string=bad_jsonl - ) - except Exception: - pass + zaaktype_resultaattype_config = ZaakTypeResultaatTypeConfig.objects.get() + self.assertEqual(zaaktype_resultaattype_config.description, "description new") - counts = ( - CatalogusConfig.objects.count(), - ZaakTypeConfig.objects.count(), - ZaakTypeInformatieObjectTypeConfig.objects.count(), - ZaakTypeStatusTypeConfig.objects.count(), - ZaakTypeResultaatTypeConfig.objects.count(), + zaaktype_informatie_objecttype_config = ( + ZaakTypeInformatieObjectTypeConfig.objects.get() ) - expected_counts = (0, 0, 0, 0, 0) - self.assertEqual( - counts, - expected_counts, - msg="Import should have merged, and not created new values", + zaaktype_informatie_objecttype_config.document_upload_enabled, True ) - - -class RewriteUrlsImportTests(TestCase): - def setUp(self): - self.service = ServiceFactory( - slug="constant-api-slug", api_root="http://one.maykinmedia.nl" + self.assertEqual( + zaaktype_informatie_objecttype_config.document_notification_enabled, True ) - import_lines = [ - '{"model": "openzaak.catalogusconfig", "fields": {"url": "http://one.maykinmedia.nl/catalogus/1", "domein": "ALLE", "rsin": "1234568", "service": ["constant-api-slug"]}}', - '{"model": "openzaak.zaaktypeconfig", "fields": {"urls": "[\\"http://one.maykinmedia.nl/types/1\\", \\"http://one.maykinmedia.nl/types/2\\"]", "catalogus": ["http://one.maykinmedia.nl/catalogus/1"], "identificatie": "zt-1", "omschrijving": "iGsHCEkCpEJyDLeAaytskGiAXSAPVVthCvOdbNdpZZcCciXFnZGltXFYsYigSkIZiaqMEvSPftMgIYyW", "notify_status_changes": false, "description": "", "external_document_upload_url": "", "document_upload_enabled": false, "contact_form_enabled": false, "contact_subject_code": "", "relevante_zaakperiode": null}}', - '{"model": "openzaak.zaaktypeinformatieobjecttypeconfig", "fields": {"zaaktype_config": ["zt-1", "http://one.maykinmedia.nl/catalogus/1"], "informatieobjecttype_url": "http://one.maykinmedia.nl/iotype/1", "omschrijving": "IzNqfWpVpbyMEjSXTqQUlslqAUYFdILFlSDAelAkfTROWptqgIRCmaIoWCBMBAozsJLWxGoJqmBLPCHy", "zaaktype_uuids": "[]", "document_upload_enabled": false, "document_notification_enabled": false}}', - '{"model": "openzaak.zaaktypestatustypeconfig", "fields": {"zaaktype_config": ["zt-1", "http://one.maykinmedia.nl/catalogus/1"], "statustype_url": "http://one.maykinmedia.nl/status-type/1", "omschrijving": "BHEJLQkSTdMPGtSzgnIbIdhMvFiNOBHmFQkRvLxHUkmafelprqCpcuAZzqMWBLgqNkGmXpzWPjhWqKjk", "statustekst": "", "zaaktype_uuids": "[]", "status_indicator": "", "status_indicator_text": "", "document_upload_description": "", "description": "", "notify_status_change": true, "action_required": false, "document_upload_enabled": true, "call_to_action_url": "", "call_to_action_text": "", "case_link_text": ""}}', - '{"model": "openzaak.zaaktyperesultaattypeconfig", "fields": {"zaaktype_config": ["zt-1", "http://one.maykinmedia.nl/catalogus/1"], "resultaattype_url": "http://one.maykinmedia.nl/resultaat-type/1", "omschrijving": "", "zaaktype_uuids": "[]", "description": ""}}', + def test_import_jsonl_missing_statustype_config(self): + ZGWExportImportMockData() + + # missing ZaakTypeStatusTypeConfig + json_line_extra = [ + '{"model": "openzaak.zaaktypestatustypeconfig", "fields": {"zaaktype_config": ["ztc-id-a-0", "DM-0", "123456789"], "statustype_url": "https://foo.0.maykinmedia.nl", "omschrijving": "bogus", "statustekst": "bogus", "zaaktype_uuids": "[]", "status_indicator": "", "status_indicator_text": "", "document_upload_description": "", "description": "status", "notify_status_change": true, "action_required": false, "document_upload_enabled": true, "call_to_action_url": "", "call_to_action_text": "", "case_link_text": ""}}', ] - self.jsonl = "\n".join(import_lines) + json_lines = "\n".join(self.json_lines + json_line_extra) - def _create_fixtures(self, base_url: str): - catalogus = CatalogusConfigFactory( - url=f"{base_url}/catalogus/1", - service=self.service, - domein="ALLE", - rsin="1234568", - ) - zt = ZaakTypeConfigFactory( - catalogus=catalogus, - identificatie="zt-1", - urls=[ - f"{base_url}/types/1", - f"{base_url}/types/2", - ], - ) - ZaakTypeInformatieObjectTypeConfigFactory( - zaaktype_config=zt, - informatieobjecttype_url=f"{base_url}/iotype/1", + self.storage.save("import.jsonl", io.StringIO(json_lines)) + + # we use `asdict` and replace the Exceptions with string representations + # because for Exceptions raised from within dataclasses, equality ==/is identity + import_result = dataclasses.asdict( + CatalogusConfigImport.import_from_jsonl_file_in_django_storage( + "import.jsonl", self.storage + ) ) - ZaakTypeStatusTypeConfigFactory( - zaaktype_config=zt, statustype_url=f"{base_url}/status-type/1" + expected_error = ZGWImportError( + "ZaakTypeStatusTypeConfig not found in target environment: omschrijving = bogus, " + "ZaakTypeConfig identificatie = ztc-id-a-0, Catalogus domein = DM-0, Catalogus rsin = 123456789" ) - ZaakTypeResultaatTypeConfigFactory( - zaaktype_config=zt, - resultaattype_url=f"{base_url}/resultaat-type/1", + import_expected = dataclasses.asdict( + CatalogusConfigImport( + total_rows_processed=6, + catalogus_configs_imported=1, + zaaktype_configs_imported=1, + zaak_informatie_object_type_configs_imported=1, + zaak_status_type_configs_imported=1, + zaak_resultaat_type_configs_imported=1, + import_errors=[expected_error], + ), ) + import_result["import_errors"][0] = str(import_result["import_errors"][0]) + import_expected["import_errors"][0] = str(import_expected["import_errors"][0]) - def test_jsonl_url_rewrite(self): - self.service.api_root = "http://two.maykinmedia.nl" - self.service.save() + # check import + self.assertEqual(import_result, import_expected) - rewritten_lines = list( - CatalogusConfigImport._rewrite_jsonl_url_references(self.jsonl) - ) - expected_lines = [ - '{"model": "openzaak.catalogusconfig", "fields": {"url": "http://two.maykinmedia.nl/catalogus/1", "domein": "ALLE", "rsin": "1234568", "service": ["constant-api-slug"]}}', - '{"model": "openzaak.zaaktypeconfig", "fields": {"urls": "[\\"http://two.maykinmedia.nl/types/1\\", \\"http://two.maykinmedia.nl/types/2\\"]", "catalogus": ["http://two.maykinmedia.nl/catalogus/1"], "identificatie": "zt-1", "omschrijving": "iGsHCEkCpEJyDLeAaytskGiAXSAPVVthCvOdbNdpZZcCciXFnZGltXFYsYigSkIZiaqMEvSPftMgIYyW", "notify_status_changes": false, "description": "", "external_document_upload_url": "", "document_upload_enabled": false, "contact_form_enabled": false, "contact_subject_code": "", "relevante_zaakperiode": null}}', - '{"model": "openzaak.zaaktypeinformatieobjecttypeconfig", "fields": {"zaaktype_config": ["zt-1", "http://two.maykinmedia.nl/catalogus/1"], "informatieobjecttype_url": "http://two.maykinmedia.nl/iotype/1", "omschrijving": "IzNqfWpVpbyMEjSXTqQUlslqAUYFdILFlSDAelAkfTROWptqgIRCmaIoWCBMBAozsJLWxGoJqmBLPCHy", "zaaktype_uuids": "[]", "document_upload_enabled": false, "document_notification_enabled": false}}', - '{"model": "openzaak.zaaktypestatustypeconfig", "fields": {"zaaktype_config": ["zt-1", "http://two.maykinmedia.nl/catalogus/1"], "statustype_url": "http://two.maykinmedia.nl/status-type/1", "omschrijving": "BHEJLQkSTdMPGtSzgnIbIdhMvFiNOBHmFQkRvLxHUkmafelprqCpcuAZzqMWBLgqNkGmXpzWPjhWqKjk", "statustekst": "", "zaaktype_uuids": "[]", "status_indicator": "", "status_indicator_text": "", "document_upload_description": "", "description": "", "notify_status_change": true, "action_required": false, "document_upload_enabled": true, "call_to_action_url": "", "call_to_action_text": "", "case_link_text": ""}}', - '{"model": "openzaak.zaaktyperesultaattypeconfig", "fields": {"zaaktype_config": ["zt-1", "http://two.maykinmedia.nl/catalogus/1"], "resultaattype_url": "http://two.maykinmedia.nl/resultaat-type/1", "omschrijving": "", "zaaktype_uuids": "[]", "description": ""}}', - ] + # check number of configs + self.assertEqual(CatalogusConfig.objects.count(), 1) + self.assertEqual(ZaakTypeConfig.objects.count(), 1) + self.assertEqual(ZaakTypeInformatieObjectTypeConfig.objects.count(), 1) + self.assertEqual(ZaakTypeStatusTypeConfig.objects.count(), 1) + self.assertEqual(ZaakTypeResultaatTypeConfig.objects.count(), 1) - self.assertEqual( - rewritten_lines, - expected_lines, - msg="All URLs should be rewritten to match the target service root", - ) + def test_import_jsonl_update_statustype_config_missing_zt_config(self): + ZGWExportImportMockData() - def test_rewrite_target_diverges_from_existing_objects(self): - self._create_fixtures("http://one.maykinmedia.nl/") - self.service.api_root = "http://two.maykinmedia.nl" - self.service.save() + # import fails due to missing ZaakTypeConfig + json_line_extra = [ + '{"model": "openzaak.zaaktypestatustypeconfig", "fields": {"zaaktype_config": ["bogus", "DM-1", "666666666"], "statustype_url": "https://foo.1.maykinmedia.nl", "omschrijving": "status omschrijving", "statustekst": "", "zaaktype_uuids": "[]", "status_indicator": "", "status_indicator_text": "", "document_upload_description": "", "description": "status", "notify_status_change": true, "action_required": false, "document_upload_enabled": true, "call_to_action_url": "", "call_to_action_text": "", "case_link_text": ""}}', + ] + json_lines = "\n".join(self.json_lines + json_line_extra) - import_result = CatalogusConfigImport.from_jsonl_stream_or_string(self.jsonl) + self.storage.save("import.jsonl", io.StringIO(json_lines)) - self.assertEqual( - import_result, + # we use `asdict` and replace the Exceptions with string representations + # because for Exceptions raised from within dataclasses, equality ==/is identity + import_result = dataclasses.asdict( + CatalogusConfigImport.import_from_jsonl_file_in_django_storage( + "import.jsonl", self.storage + ) + ) + expected_error = ZGWImportError( + "ZaakTypeStatusTypeConfig not found in target environment: omschrijving = status omschrijving, " + "ZaakTypeConfig identificatie = bogus, Catalogus domein = DM-1, Catalogus rsin = 666666666" + ) + import_expected = dataclasses.asdict( CatalogusConfigImport( - total_rows_processed=5, + total_rows_processed=6, catalogus_configs_imported=1, zaaktype_configs_imported=1, - zaak_inormatie_object_type_configs_imported=1, + zaak_informatie_object_type_configs_imported=1, zaak_status_type_configs_imported=1, zaak_resultaat_type_configs_imported=1, + import_errors=[expected_error], ), ) + import_result["import_errors"][0] = str(import_result["import_errors"][0]) + import_expected["import_errors"][0] = str(import_expected["import_errors"][0]) - counts = ( - CatalogusConfig.objects.count(), - ZaakTypeConfig.objects.count(), - ZaakTypeInformatieObjectTypeConfig.objects.count(), - ZaakTypeStatusTypeConfig.objects.count(), - ZaakTypeResultaatTypeConfig.objects.count(), - ) - expected_counts = (2, 2, 2, 2, 2) + # check import + self.assertEqual(import_result, import_expected) - self.assertEqual( - counts, - expected_counts, - msg="Import should have merged, and not created new values", - ) + # check number of configs + self.assertEqual(CatalogusConfig.objects.count(), 1) + self.assertEqual(ZaakTypeConfig.objects.count(), 1) + self.assertEqual(ZaakTypeInformatieObjectTypeConfig.objects.count(), 1) + self.assertEqual(ZaakTypeStatusTypeConfig.objects.count(), 1) + self.assertEqual(ZaakTypeResultaatTypeConfig.objects.count(), 1) - def test_rewrite_target_matches_from_existing_objects(self): - self.service.api_root = "http://two.maykinmedia.nl" - self.service.save() - self._create_fixtures("http://two.maykinmedia.nl") + def test_import_jsonl_update_reports_duplicates(self): + mocks = ZGWExportImportMockData() - import_result = CatalogusConfigImport.from_jsonl_stream_or_string(self.jsonl) - self.assertEqual( - import_result, + ZaakTypeResultaatTypeConfigFactory( + resultaattype_url=mocks.ztc_resultaat.resultaattype_url, + zaaktype_config=mocks.ztc_resultaat.zaaktype_config, + omschrijving=mocks.ztc_resultaat.omschrijving, + description=mocks.ztc_resultaat.description, + ) + self.storage.save("import.jsonl", io.StringIO(self.jsonl)) + + # we use `asdict` and replace the Exceptions with string representations + # because for Exceptions raised from within dataclasses, equality ==/is identity + import_result = dataclasses.asdict( + CatalogusConfigImport.import_from_jsonl_file_in_django_storage( + "import.jsonl", self.storage + ) + ) + expected_error = ZGWImportError( + "Got multiple results for ZaakTypeResultaatTypeConfig: omschrijving = resultaat, " + "ZaakTypeConfig identificatie = ztc-id-a-0, Catalogus domein = DM-0, Catalogus rsin = 123456789" + ) + import_expected = dataclasses.asdict( CatalogusConfigImport( total_rows_processed=5, catalogus_configs_imported=1, zaaktype_configs_imported=1, - zaak_inormatie_object_type_configs_imported=1, + zaak_informatie_object_type_configs_imported=1, zaak_status_type_configs_imported=1, - zaak_resultaat_type_configs_imported=1, + zaak_resultaat_type_configs_imported=0, + import_errors=[expected_error], ), ) + import_result["import_errors"][0] = str(import_result["import_errors"][0]) + import_expected["import_errors"][0] = str(import_expected["import_errors"][0]) + + # check import + self.assertEqual(import_result, import_expected) + + def test_import_jsonl_fails_with_catalogus_domein_rsin_mismatch(self): + service = ServiceFactory(slug="service-0") + CatalogusConfigFactory( + url="https://foo.0.maykinmedia.nl", + domein="FOO", + rsin="123456789", + service=service, + ) + import_lines = [ + '{"model": "openzaak.catalogusconfig", "fields": {"url": "https://foo.0.maykinmedia.nl", "domein": "BAR", "rsin": "987654321", "service": ["service-0"]}}', + '{"model": "openzaak.zaaktypeconfig", "fields": {"urls": "[\\"https://foo.0.maykinmedia.nl\\"]", "catalogus": ["DM-0", "123456789"], "identificatie": "ztc-id-a-0", "omschrijving": "zaaktypeconfig", "notify_status_changes": false, "description": "", "external_document_upload_url": "", "document_upload_enabled": false, "contact_form_enabled": false, "contact_subject_code": "", "relevante_zaakperiode": null}}', + ] + import_line = "\n".join(import_lines) + + with self.assertLogs( + logger="open_inwoner.openzaak.import_export", level="ERROR" + ) as cm: + import_result = CatalogusConfigImport.from_jsonl_stream_or_string( + import_line + ) + self.assertEqual( + cm.output, + [ + # error from trying to load existing CatalogusConfig + "ERROR:open_inwoner.openzaak.import_export:" + "CatalogusConfig not found in target environment: Domein = BAR, Rsin = 987654321", + # error from deserializing nested ZGW objects + "ERROR:open_inwoner.openzaak.import_export:" + "ZaakTypeConfig not found in target environment: Identificatie = ztc-id-a-0, Catalogus domein = DM-0, Catalogus rsin = 123456789", + ], + ) + + self.assertEqual(CatalogusConfig.objects.count(), 1) + + self.assertEqual(import_result.catalogus_configs_imported, 0) + self.assertEqual(import_result.total_rows_processed, 2) + + self.assertEqual( + list(CatalogusConfig.objects.values_list("url", "domein", "rsin")), + [("https://foo.0.maykinmedia.nl", "FOO", "123456789")], + msg="Value of sole CatalogusConfig matches imported values, not original values", + ) + + def test_bad_import_types(self): + for bad_type in (set(), list(), b""): + with self.assertRaises(ValueError): + CatalogusConfigImport.from_jsonl_stream_or_string(bad_type) + + def test_valid_input_types_are_accepted(self): + ZGWExportImportMockData() + + for input in ( + io.StringIO(self.jsonl), + io.BytesIO(self.jsonl.encode("utf-8")), + self.jsonl, + ): + with self.subTest(f"Input type {type(input)}"): + import_result = CatalogusConfigImport.from_jsonl_stream_or_string(input) + self.assertEqual( + import_result, + CatalogusConfigImport( + total_rows_processed=5, + catalogus_configs_imported=1, + zaaktype_configs_imported=1, + zaak_informatie_object_type_configs_imported=1, + zaak_status_type_configs_imported=1, + zaak_resultaat_type_configs_imported=1, + import_errors=[], + ), + ) + + def test_import_is_atomic(self): + bad_line = '{"model": "openzaak.zaaktyperesultaattypeconfig", "fields": {}}\n' + bad_jsonl = self.jsonl + "\n" + bad_line + + with self.assertRaises(KeyError): + CatalogusConfigImport.from_jsonl_stream_or_string( + stream_or_string=bad_jsonl + ) counts = ( CatalogusConfig.objects.count(), @@ -506,7 +571,7 @@ def test_rewrite_target_matches_from_existing_objects(self): ZaakTypeStatusTypeConfig.objects.count(), ZaakTypeResultaatTypeConfig.objects.count(), ) - expected_counts = (1, 1, 1, 1, 1) + expected_counts = (0, 0, 0, 0, 0) self.assertEqual( counts, diff --git a/src/open_inwoner/openzaak/tests/test_models.py b/src/open_inwoner/openzaak/tests/test_models.py index 3c12d340e7..402c05c78e 100644 --- a/src/open_inwoner/openzaak/tests/test_models.py +++ b/src/open_inwoner/openzaak/tests/test_models.py @@ -1,7 +1,6 @@ from datetime import timedelta from uuid import uuid4 -from django.db import IntegrityError from django.test import TestCase from freezegun import freeze_time @@ -30,12 +29,6 @@ class CatalogusConfigManagerTestCase(TestCase): - def test_fields_used_for_natural_key_are_unique(self): - - with self.assertRaises(IntegrityError): - for _ in range(2): - CatalogusConfigFactory(domein="test", rsin="1234") - def test_get_by_natural_key_returns_expected_instance(self): config = CatalogusConfigFactory() @@ -46,23 +39,10 @@ def test_get_by_natural_key_returns_expected_instance(self): def test_get_by_natural_key_not_found(self): with self.assertRaises(CatalogusConfig.DoesNotExist): - CatalogusConfig.objects.get_by_natural_key( - domein="test", rsin="1234" - ) + CatalogusConfig.objects.get_by_natural_key(domein="test", rsin="1234") class ZaakTypeConfigModelTestCase(TestCase): - def test_fields_used_for_natural_key_are_unique(self): - catalogus = CatalogusConfigFactory() - - with self.assertRaises(IntegrityError): - for _ in range(2): - ZaakTypeConfigFactory( - identificatie="AAAA", - catalogus__domein=catalogus.domein, - catalogus__rsin=catalogus.rsin, - ) - def test_get_by_natural_key_returns_expected_instance(self): zaak_type_config = ZaakTypeConfigFactory( identificatie="AAAA", @@ -107,15 +87,6 @@ def test_queryset_filter_case_type_with_catalog(self): class ZaakTypeStatusTypeConfigModelTestCase(TestCase): - def test_fields_used_for_natural_key_are_unique(self): - zt = ZaakTypeConfigFactory() - with self.assertRaises(IntegrityError): - for _ in range(2): - ZaakTypeStatusTypeConfigFactory( - zaaktype_config=zt, - omschrijving="test", - ) - def test_get_by_natural_key_returns_expected_instance(self): zt = ZaakTypeConfigFactory() zt_status_type_config = ZaakTypeStatusTypeConfigFactory( @@ -145,24 +116,17 @@ def test_get_by_natural_key_not_found(self): class ZaakTypeResultaatTypeConfigModelTestCase(TestCase): - def test_fields_used_for_natural_key_are_unique(self): - zt = ZaakTypeConfigFactory() - with self.assertRaises(IntegrityError): - for _ in range(2): - ZaakTypeResultaatTypeConfigFactory( - zaaktype_config=zt, omschrijving="test" - ) - def test_get_by_natural_key_returns_expected_instance(self): zt = ZaakTypeConfigFactory() zt_status_type_config = ZaakTypeResultaatTypeConfigFactory( - zaaktype_config=zt, omschrijving="test", + zaaktype_config=zt, + omschrijving="test", ) self.assertEqual( ZaakTypeResultaatTypeConfig.objects.get_by_natural_key( omschrijving="test", - zaak_type_config_identificatie=zt.identificatie, + zaaktype_config_identificatie=zt.identificatie, catalogus_domein=zt.catalogus.domein, catalogus_rsin=zt.catalogus.rsin, ), @@ -174,22 +138,13 @@ def test_get_by_natural_key_not_found(self): with self.assertRaises(ZaakTypeResultaatTypeConfig.DoesNotExist): ZaakTypeResultaatTypeConfig.objects.get_by_natural_key( omschrijving="bogus", - zaak_type_config_identificatie=zt.identificatie, + zaaktype_config_identificatie=zt.identificatie, catalogus_domein=zt.catalogus.domein, catalogus_rsin=zt.catalogus.rsin, ) class ZaakTypeInformatieObjectTypeConfigFactoryModelTestCase(TestCase): - def test_fields_used_for_natural_key_are_unique(self): - zt = ZaakTypeConfigFactory() - with self.assertRaises(IntegrityError): - for _ in range(2): - ZaakTypeInformatieObjectTypeConfigFactory( - zaaktype_config=zt, - omschrijving="test", - ) - def test_get_by_natural_key_returns_expected_instance(self): zt = ZaakTypeConfigFactory() zt_io_type = ZaakTypeInformatieObjectTypeConfigFactory( @@ -199,7 +154,7 @@ def test_get_by_natural_key_returns_expected_instance(self): self.assertEqual( ZaakTypeInformatieObjectTypeConfig.objects.get_by_natural_key( omschrijving="test", - zaak_type_config_identificatie=zt.identificatie, + zaaktype_config_identificatie=zt.identificatie, catalogus_domein=zt.catalogus.domein, catalogus_rsin=zt.catalogus.rsin, ), @@ -211,7 +166,7 @@ def test_get_by_natural_key_not_found(self): with self.assertRaises(ZaakTypeInformatieObjectTypeConfig.DoesNotExist): ZaakTypeInformatieObjectTypeConfig.objects.get_by_natural_key( omschrijving="bogus", - zaak_type_config_identificatie=zt.identificatie, + zaaktype_config_identificatie=zt.identificatie, catalogus_domein=zt.catalogus.domein, catalogus_rsin=zt.catalogus.rsin, ) diff --git a/src/open_inwoner/openzaak/tests/test_zgw_imports_command.py b/src/open_inwoner/openzaak/tests/test_zgw_imports_command.py index 3f091fad70..19db15d804 100644 --- a/src/open_inwoner/openzaak/tests/test_zgw_imports_command.py +++ b/src/open_inwoner/openzaak/tests/test_zgw_imports_command.py @@ -43,11 +43,8 @@ def setUpTestData(cls): def test_zgw_import_data_command(self, m): m.reset_mock() for root in self.roots: - CatalogMockData(root).install_mocks(m) InformationObjectTypeMockData(root).install_mocks(m) - # ZaakTypeMockData(root).install_mocks(m) - - # TODO: ADD CATALOGI like in iotypes + CatalogMockData(root).install_mocks(m) # run it to import our data out = StringIO()