From 0ac007dc1d407a60d09a76a863b8346f33c1135e Mon Sep 17 00:00:00 2001 From: Viicos <65306057+Viicos@users.noreply.github.com> Date: Tue, 19 Mar 2024 14:46:38 +0100 Subject: [PATCH 1/7] [#3688] Add docker compose for Open Zaak --- docker/docker-compose.open-zaak.yml | 70 +++++++++++++ docker/open-zaak/README.md | 21 ++++ .../0001-docker-init-open-zaak-db.sql | 5 + .../0002-extensions.sh | 7 ++ .../fixtures/open_zaak_fixtures.json | 99 +++++++++++++++++++ 5 files changed, 202 insertions(+) create mode 100644 docker/docker-compose.open-zaak.yml create mode 100644 docker/open-zaak/README.md create mode 100644 docker/open-zaak/docker-init-open-zaak-db/0001-docker-init-open-zaak-db.sql create mode 100755 docker/open-zaak/docker-init-open-zaak-db/0002-extensions.sh create mode 100644 docker/open-zaak/fixtures/open_zaak_fixtures.json diff --git a/docker/docker-compose.open-zaak.yml b/docker/docker-compose.open-zaak.yml new file mode 100644 index 0000000000..a64eed8e31 --- /dev/null +++ b/docker/docker-compose.open-zaak.yml @@ -0,0 +1,70 @@ +version: '3' + +name: open-zaak + +services: + openzaak-redis: + image: redis:7 + command: ["redis-server", "--appendonly", "yes"] + networks: + - open-forms-dev + + openzaak-db: + image: postgis/postgis:${PG_VERSION:-14-master} + environment: + - POSTGRES_HOST_AUTH_METHOD=trust + volumes: + - ./open-zaak/docker-init-open-zaak-db/:/docker-entrypoint-initdb.d + - open-zaak-db:/var/lib/postgresql/data + networks: + - open-forms-dev + + openzaak-web.local: + image: openzaak/open-zaak:${OPENZAAK_VERSION:-1.11.0} + environment: &openzaak_web_env + - DJANGO_SETTINGS_MODULE=openzaak.conf.docker + - SECRET_KEY=${SECRET_KEY:-fgv=c0hz&tl*8*3m3893@m+1pstrvidc9e^5@fpspmg%cy$15d} + - ALLOWED_HOSTS=openzaak-web.local,localhost + - DB_NAME=openzaak + - DB_USER=openzaak + - DB_HOST=openzaak-db + - IS_HTTPS=no + - CACHE_DEFAULT=openzaak-redis:6379/0 + - CACHE_AXES=openzaak-redis:6379/0 + - SUBPATH=${SUBPATH:-/} + - OPENZAAK_SUPERUSER_USERNAME=admin + - DJANGO_SUPERUSER_PASSWORD=admin + - OPENZAAK_SUPERUSER_EMAIL=admin@localhost + - CELERY_BROKER_URL=redis://openzaak-redis:6379/1 + - CELERY_RESULT_BACKEND=redis://openzaak-redis:6379/1 + - NOTIFICATIONS_DISABLED=true + ports: + - 8003:8000 + volumes: &openzaak_web_volumes + - media:/app/media # Shared media volume to get access to saved OAS files + - private-media:/app/private-media + depends_on: + - openzaak-db + - openzaak-redis + networks: + - open-forms-dev + + celery: + image: openzaak/open-zaak:${OPENZAAK_VERSION:-1.11.0} + environment: *openzaak_web_env + volumes: *openzaak_web_volumes + command: /celery_worker.sh + depends_on: + - openzaak-db + - openzaak-redis + networks: + - open-forms-dev + +volumes: + open-zaak-db: + media: + private-media: + +networks: + open-forms-dev: + name: open-forms-dev diff --git a/docker/open-zaak/README.md b/docker/open-zaak/README.md new file mode 100644 index 0000000000..46d0179582 --- /dev/null +++ b/docker/open-zaak/README.md @@ -0,0 +1,21 @@ +# Open Zaak + +The `docker-compose.open-zaak.yml` compose file is available to run an instance of Open Zaak. + +## docker compose + +Start an instance in your local environment from the parent directory: + +```bash +docker compose -f docker-compose.open-zaak.yml up -d +``` + +This brings up the admin at http://localhost:8003/admin/. + +## Load fixtures + +Before re-recording the related VCR tests, you must load some fixtures: + +```bash +cat open-zaak/fixtures/open_zaak_fixtures.json | docker compose -f docker-compose.open-zaak.yml exec -T openzaak-web.local src/manage.py loaddata --format=json - +``` diff --git a/docker/open-zaak/docker-init-open-zaak-db/0001-docker-init-open-zaak-db.sql b/docker/open-zaak/docker-init-open-zaak-db/0001-docker-init-open-zaak-db.sql new file mode 100644 index 0000000000..6126e3e18a --- /dev/null +++ b/docker/open-zaak/docker-init-open-zaak-db/0001-docker-init-open-zaak-db.sql @@ -0,0 +1,5 @@ +CREATE USER openzaak; +CREATE DATABASE openzaak; +GRANT ALL PRIVILEGES ON DATABASE openzaak TO openzaak; +-- On Postgres 15+, connect to the database and grant schema permissions. +-- GRANT USAGE, CREATE ON SCHEMA public TO openzaak; diff --git a/docker/open-zaak/docker-init-open-zaak-db/0002-extensions.sh b/docker/open-zaak/docker-init-open-zaak-db/0002-extensions.sh new file mode 100755 index 0000000000..1adfaeec04 --- /dev/null +++ b/docker/open-zaak/docker-init-open-zaak-db/0002-extensions.sh @@ -0,0 +1,7 @@ +#!/bin/bash +set -e + +psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "openzaak" <<-EOSQL + CREATE EXTENSION postgis; + CREATE EXTENSION pg_trgm; +EOSQL diff --git a/docker/open-zaak/fixtures/open_zaak_fixtures.json b/docker/open-zaak/fixtures/open_zaak_fixtures.json new file mode 100644 index 0000000000..81c3de0595 --- /dev/null +++ b/docker/open-zaak/fixtures/open_zaak_fixtures.json @@ -0,0 +1,99 @@ +[ + { + "model": "authorizations.applicatie", + "pk": 1, + "fields": { + "uuid": "c2f70b46-14cc-4c22-87ce-31b72b83dd62", + "client_ids": "[\"test_client_id\"]", + "label": "test application", + "heeft_alle_autorisaties": true + } + }, + { + "model": "vng_api_common.jwtsecret", + "pk": 1, + "fields": { + "identifier": "test_client_id", + "secret": "test_secret_key" + } + }, + { + "model": "catalogi.catalogus", + "pk": 1, + "fields": { + "_etag": "a7068427bca54eb6f5e23d2b1f9961ff", + "naam": "Test catalog", + "uuid": "bd58635c-793e-446d-a7e0-460d7b04829d", + "domein": "TEST", + "rsin": "000000000", + "contactpersoon_beheer_naam": "Test name", + "contactpersoon_beheer_telefoonnummer": "", + "contactpersoon_beheer_emailadres": "", + "versie": "", + "begindatum_versie": null + } + }, + { + "model": "catalogi.informatieobjecttype", + "pk": 1, + "fields": { + "_etag": "89c597c58e42b804148e6867c3480ed1", + "datum_begin_geldigheid": "2024-03-19", + "datum_einde_geldigheid": null, + "concept": false, + "uuid": "7a474713-0833-402a-8441-e467c08ac55b", + "omschrijving": "PDF Informatieobjecttype", + "informatieobjectcategorie": "Test category", + "trefwoord": "[]", + "vertrouwelijkheidaanduiding": "openbaar", + "omschrijving_generiek_informatieobjecttype": "", + "omschrijving_generiek_definitie": "", + "omschrijving_generiek_herkomst": "", + "omschrijving_generiek_hierarchie": "", + "omschrijving_generiek_opmerking": "", + "catalogus": 1 + } + }, + { + "model": "catalogi.informatieobjecttype", + "pk": 2, + "fields": { + "_etag": "89d53439caa01c2f00ab822679e80c7b", + "datum_begin_geldigheid": "2024-03-19", + "datum_einde_geldigheid": null, + "concept": false, + "uuid": "b2d83b94-9b9b-4e80-a82f-73ff993c62f3", + "omschrijving": "CSV Informatieobjecttype", + "informatieobjectcategorie": "Test category", + "trefwoord": "[]", + "vertrouwelijkheidaanduiding": "openbaar", + "omschrijving_generiek_informatieobjecttype": "", + "omschrijving_generiek_definitie": "", + "omschrijving_generiek_herkomst": "", + "omschrijving_generiek_hierarchie": "", + "omschrijving_generiek_opmerking": "", + "catalogus": 1 + } + }, + { + "model": "catalogi.informatieobjecttype", + "pk": 3, + "fields": { + "_etag": "783e0cef431e02aeb91d56074271d3c6", + "datum_begin_geldigheid": "2024-03-19", + "datum_einde_geldigheid": null, + "concept": false, + "uuid": "531f6c1a-97f7-478c-85f0-67d2f23661c7", + "omschrijving": "Attachment Informatieobjecttype", + "informatieobjectcategorie": "Test category", + "trefwoord": "[]", + "vertrouwelijkheidaanduiding": "openbaar", + "omschrijving_generiek_informatieobjecttype": "", + "omschrijving_generiek_definitie": "", + "omschrijving_generiek_herkomst": "", + "omschrijving_generiek_hierarchie": "", + "omschrijving_generiek_opmerking": "", + "catalogus": 1 + } + } +] From 3006d39a965381611cb6f402b50ceeb66531224d Mon Sep 17 00:00:00 2001 From: Viicos <65306057+Viicos@users.noreply.github.com> Date: Tue, 19 Mar 2024 14:46:48 +0100 Subject: [PATCH 2/7] [#3688] Update Object(types) API docker compose and fixtures Mainly to avoid conflicts between the two docker compose files --- docker/docker-compose.objects-apis.yml | 25 +++++++++++----- .../fixtures/objects_api_fixtures.json | 13 +++++++- .../fixtures/objecttypes_api_fixtures.json | 30 ++++++++++++++----- 3 files changed, 51 insertions(+), 17 deletions(-) diff --git a/docker/docker-compose.objects-apis.yml b/docker/docker-compose.objects-apis.yml index 0e62a94689..c7deb778c7 100644 --- a/docker/docker-compose.objects-apis.yml +++ b/docker/docker-compose.objects-apis.yml @@ -1,11 +1,15 @@ version: '3' +name: objects-apis + services: - redis: + objects-redis: image: redis:7 command: ["redis-server", "--appendonly", "yes"] + networks: + - open-forms-dev - db: + objects-objecttypes-db: image: postgis/postgis:${PG_VERSION:-14-master} environment: - POSTGRES_HOST_AUTH_METHOD=trust @@ -21,10 +25,11 @@ services: - DJANGO_SETTINGS_MODULE=objecttypes.conf.docker - SECRET_KEY=${SECRET_KEY:-fgv=c0hz&tl*8*3m3893@m+1pstrvidc9e^5@fpspmg%cy$15d} - ALLOWED_HOSTS=* + - DB_HOST=objects-objecttypes-db ports: - 8001:8000 depends_on: - - db + - objects-objecttypes-db networks: - open-forms-dev @@ -34,22 +39,26 @@ services: - DJANGO_SETINGS_MODULE=objects.conf.docker - SECRET_KEY=${SECRET_KEY:-fgv=c0hz&tl*8*3m3893@m+1pstrvidc9e^5@fpspmg%cy$15d} - ALLOWED_HOSTS=* + - DB_HOST=objects-objecttypes-db - DISABLE_2FA=True + - CELERY_BROKER_URL=redis://objects-redis:6379/0 + - CELERY_RESULT_BACKEND=redis://objects-redis:6379/0 + - NOTIFICATIONS_DISABLED=True ports: - 8002:8000 depends_on: - - db - - redis + - objects-objecttypes-db + - objects-redis networks: - open-forms-dev - celery: + objects-celery: image: maykinmedia/objects-api:${OBJECTS_VERSION:-2.3.0} environment: *objects_web_env command: /celery_worker.sh depends_on: - - db - - redis + - objects-objecttypes-db + - objects-redis networks: - open-forms-dev diff --git a/docker/objects-apis/fixtures/objects_api_fixtures.json b/docker/objects-apis/fixtures/objects_api_fixtures.json index 611352e686..aad3e55bb7 100644 --- a/docker/objects-apis/fixtures/objects_api_fixtures.json +++ b/docker/objects-apis/fixtures/objects_api_fixtures.json @@ -21,6 +21,17 @@ "administration": "" } }, + { + "model": "token.permission", + "pk": 1, + "fields": { + "token_auth": "7657474c3d75f56ae0abd0d1bf7994b09964dca9", + "object_type": 1, + "mode": "read_and_write", + "use_fields": false, + "fields": null + } + }, { "model": "zgw_consumers.service", "pk": 1, @@ -30,7 +41,7 @@ "oas_file": "", "uuid": "ee08faa2-4cd1-45f6-ba16-d50872cf5cf2", "api_type": "orc", - "api_root": "http://localhost:8001/api/v2/", + "api_root": "http://objecttypes-web:8000/api/v2/", "client_id": "", "secret": "", "auth_type": "api_key", diff --git a/docker/objects-apis/fixtures/objecttypes_api_fixtures.json b/docker/objects-apis/fixtures/objecttypes_api_fixtures.json index dca2f7cb13..988cf20f44 100644 --- a/docker/objects-apis/fixtures/objecttypes_api_fixtures.json +++ b/docker/objects-apis/fixtures/objecttypes_api_fixtures.json @@ -128,20 +128,34 @@ "properties": { "age": { "type": "integer", - "minimum": 18, - "description": "Age in years which must be equal to or greater than 18." + "minimum": 18 + }, + "name": { + "type": "object", + "properties": { + "last.name": {"type": "string"} + } }, - "country": { + "submission_pdf_url": { "type": "string", - "description": "The person's current country." + "format": "uri" }, - "lastName": { + "submission_csv_url": { "type": "string", - "description": "The person's last name." + "format": "uri" }, - "firstName": { + "submission_payment_completed": { + "type": "boolean" + }, + "submission_payment_amount": { + "type": "string" + }, + "submission_payment_public_ids": { + "type": "array" + }, + "submission_date": { "type": "string", - "description": "The person's first name." + "format": "date-time" } } }, From ea5c501c57ffc9b06a841f05de0bd9e44192142e Mon Sep 17 00:00:00 2001 From: Viicos <65306057+Viicos@users.noreply.github.com> Date: Tue, 19 Mar 2024 14:48:49 +0100 Subject: [PATCH 3/7] [#3688] Handle geometry mapping in the backend --- .../registrations/contrib/objects_api/config.py | 12 +++++++++++- .../registrations/contrib/objects_api/typing.py | 7 ++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/openforms/registrations/contrib/objects_api/config.py b/src/openforms/registrations/contrib/objects_api/config.py index d10c9c6aae..3419bb7768 100644 --- a/src/openforms/registrations/contrib/objects_api/config.py +++ b/src/openforms/registrations/contrib/objects_api/config.py @@ -150,9 +150,19 @@ class ObjectsAPIOptionsSerializer(JsonSchemaSerializerMixin, serializers.Seriali required=False, ) + # As `record.geometry` is outside `record.data`, we special case this attribute: + geometry_variable_key = FormioVariableKeyField( + label=_("geometry variable"), + help_text=_( + "The 'dotted' path to a form variable key that should be mapped to the `record.geometry` attribute." + ), + required=False, + allow_blank=True, + ) + def validate(self, attrs: dict[str, Any]) -> dict[str, Any]: v1_only_fields = {"content_json", "payment_status_update_json"} - v2_only_fields = {"variables_mapping"} + v2_only_fields = {"variables_mapping", "geometry_variable_key"} version = get_from_serializer_data_or_instance("version", attrs, self) diff --git a/src/openforms/registrations/contrib/objects_api/typing.py b/src/openforms/registrations/contrib/objects_api/typing.py index 57988ddbdb..2182d613fb 100644 --- a/src/openforms/registrations/contrib/objects_api/typing.py +++ b/src/openforms/registrations/contrib/objects_api/typing.py @@ -27,9 +27,10 @@ class ObjecttypeVariableMapping(TypedDict): target_path: list[str] -class RegistrationOptionsV2(_BaseRegistrationOptions): - version: Literal[2] - variables_mapping: list[ObjecttypeVariableMapping] +class RegistrationOptionsV2(_BaseRegistrationOptions, total=False): + version: Required[Literal[2]] + variables_mapping: Required[list[ObjecttypeVariableMapping]] + geometry_variable_key: str RegistrationOptions: TypeAlias = RegistrationOptionsV1 | RegistrationOptionsV2 From 55fd297c76c84b4b49bf728990ec7970e65f8a32 Mon Sep 17 00:00:00 2001 From: Viicos <65306057+Viicos@users.noreply.github.com> Date: Tue, 19 Mar 2024 14:49:05 +0100 Subject: [PATCH 4/7] [#3688] Fix typing of static variables --- .../variables/static_variables/static_variables.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/openforms/variables/static_variables/static_variables.py b/src/openforms/variables/static_variables/static_variables.py index ef5899f23d..e842c0d2af 100644 --- a/src/openforms/variables/static_variables/static_variables.py +++ b/src/openforms/variables/static_variables/static_variables.py @@ -1,3 +1,5 @@ +from datetime import date, datetime + from django.conf import settings from django.utils import timezone from django.utils.translation import gettext_lazy as _ @@ -14,7 +16,7 @@ class Now(BaseStaticVariable): name = _("Now") data_type = FormVariableDataTypes.datetime - def get_initial_value(self, submission: Submission | None = None): + def get_initial_value(self, submission: Submission | None = None) -> datetime: # Issue #2827 - the frontend schedules a new logic check when data is changed, # but the value of 'now' changes every time that it's called, so this leads to # infinite logic checking. As a workaround, we truncate the value of seconds/ @@ -28,7 +30,7 @@ class Today(BaseStaticVariable): name = _("Today") data_type = FormVariableDataTypes.date - def get_initial_value(self, submission: Submission | None = None) -> str: + def get_initial_value(self, submission: Submission | None = None) -> date: now_utc = timezone.now() return timezone.localtime(now_utc).date() From 70d69b99cb7c042ff56ee3eaa46eec664506af0c Mon Sep 17 00:00:00 2001 From: Viicos <65306057+Viicos@users.noreply.github.com> Date: Tue, 19 Mar 2024 14:49:37 +0100 Subject: [PATCH 5/7] [#3688] Add logic to register submission with v2 options --- .../contrib/objects_api/plugin.py | 9 +- .../objects_api/submission_registration.py | 104 ++++++++++++++++-- 2 files changed, 98 insertions(+), 15 deletions(-) diff --git a/src/openforms/registrations/contrib/objects_api/plugin.py b/src/openforms/registrations/contrib/objects_api/plugin.py index 20645458cf..c4094bffa3 100644 --- a/src/openforms/registrations/contrib/objects_api/plugin.py +++ b/src/openforms/registrations/contrib/objects_api/plugin.py @@ -113,17 +113,14 @@ def update_payment_status( assert isinstance(config, ObjectsAPIConfig) config.apply_defaults_to(options) - if not options["payment_status_update_json"]: - logger.warning( - "Skipping payment status update because no template was configured." - ) - return - handler = HANDLER_MAPPING[options["version"]] updated_object_data = handler.get_update_payment_status_data( submission, options ) + if updated_object_data is None: + return + updated_object_data = { "record": { "data": updated_object_data, diff --git a/src/openforms/registrations/contrib/objects_api/submission_registration.py b/src/openforms/registrations/contrib/objects_api/submission_registration.py index 59b350f418..15647acafc 100644 --- a/src/openforms/registrations/contrib/objects_api/submission_registration.py +++ b/src/openforms/registrations/contrib/objects_api/submission_registration.py @@ -1,7 +1,10 @@ +import logging from abc import ABC, abstractmethod from contextlib import contextmanager +from datetime import date, datetime from typing import Any, Generic, Iterator, TypeVar, cast +import glom from typing_extensions import override from openforms.contrib.objects_api.helpers import prepare_data_for_registration @@ -11,15 +14,19 @@ create_csv_document, create_report_document, ) +from openforms.formio.service import FormioData from openforms.registrations.exceptions import RegistrationFailed from openforms.submissions.exports import create_submission_export from openforms.submissions.mapping import SKIP, FieldConf, apply_data_mapping from openforms.submissions.models import Submission, SubmissionReport +from openforms.typing import JSONValue +from openforms.variables.service import get_static_variables from openforms.variables.utils import get_variables_for_context from ...constants import REGISTRATION_ATTRIBUTE, RegistrationAttribute from .client import DocumentenClient, get_documents_client from .models import ObjectsAPIRegistrationData +from .registration_variables import register as variables_registry from .typing import ( ConfigVersion, RegistrationOptions, @@ -27,6 +34,14 @@ RegistrationOptionsV2, ) +logger = logging.getLogger(__name__) + + +def _point_coordinate(value: Any) -> dict[str, Any] | object: + if not value or not isinstance(value, list) or len(value) != 2: + return SKIP + return {"type": "Point", "coordinates": [value[0], value[1]]} + def build_options( plugin_options: RegistrationOptions, key_mapping: dict[str, str] @@ -47,6 +62,9 @@ def register_submission_pdf( options: RegistrationOptions, documents_client: DocumentenClient, ) -> str: + if not options["informatieobjecttype_submission_report"]: + return "" + submission_report = SubmissionReport.objects.get(submission=submission) submission_report_options = build_options( options, @@ -106,6 +124,9 @@ def register_submission_attachments( options: RegistrationOptions, documents_client: DocumentenClient, ) -> list[str]: + if not options["informatieobjecttype_attachment"]: + return [] + attachment_urls: list[str] = [] for attachment in submission.attachments: attachment_options = build_options( @@ -219,19 +240,13 @@ def get_object_data( @abstractmethod def get_update_payment_status_data( self, submission: Submission, options: OptionsT - ) -> dict[str, Any]: + ) -> dict[str, Any] | None: pass class ObjectsAPIV1Handler(ObjectsAPIRegistrationHandler[RegistrationOptionsV1]): """Provide the registration data for legacy (v1) registration options, using JSON templates.""" - @staticmethod - def _point_coordinate(value: Any) -> dict[str, Any] | object: - if not value or not isinstance(value, list) or len(value) != 2: - return SKIP - return {"type": "Point", "coordinates": [value[0], value[1]]} - @staticmethod def get_payment_context_data(submission: Submission) -> dict[str, Any]: return { @@ -272,7 +287,7 @@ def get_object_data( object_mapping = { "record.geometry": FieldConf( RegistrationAttribute.locatie_coordinaat, - transform=self._point_coordinate, + transform=_point_coordinate, ), } @@ -292,7 +307,14 @@ def get_object_data( @override def get_update_payment_status_data( self, submission: Submission, options: RegistrationOptionsV1 - ) -> dict[str, Any]: + ) -> dict[str, Any] | None: + + if not options["payment_status_update_json"]: + logger.warning( + "Skipping payment status update because no template was configured." + ) + return + context = { "variables": get_variables_for_context(submission), "payment": self.get_payment_context_data(submission), @@ -301,6 +323,70 @@ def get_update_payment_status_data( return render_to_json(options["payment_status_update_json"], context) +class ObjectsAPIV2Handler(ObjectsAPIRegistrationHandler[RegistrationOptionsV2]): + + @override + def get_object_data( + self, submission: Submission, options: RegistrationOptionsV2 + ) -> dict[str, Any]: + state = submission.load_submission_value_variables_state() + dynamic_values = state.get_data() + static_values = state.static_data() + static_values.update( + { + variable.key: variable.initial_value + for variable in get_static_variables( + submission=submission, + variables_registry=variables_registry, + ) + } + ) + + variables_values = FormioData( + { + **dynamic_values, + **static_values, + } + ).data + + variables_mapping = options["variables_mapping"] + record_data: dict[str, JSONValue] = {} + + for mapping in variables_mapping: + variable_key = mapping["variable_key"] + target_path = mapping["target_path"] + + # Type hint is wrong: currently some static variables are of type date/datetime + value = cast(Any, variables_values[variable_key]) + + # Comply with JSON Schema "format" specs: + if isinstance(value, (datetime, date)): + value = value.isoformat() + + glom.assign(record_data, glom.Path(*target_path), value, missing=dict) + + object_data = prepare_data_for_registration( + record_data=record_data, + objecttype=options["objecttype"], + objecttype_version=options["objecttype_version"], + ) + + if geometry_variable_key := options.get("geometry_variable_key"): + object_data["record"]["geometry"] = _point_coordinate( + variables_values[geometry_variable_key] + ) + + return object_data + + @override + def get_update_payment_status_data( + self, submission: Submission, options: RegistrationOptionsV2 + ) -> None: + # TODO + return None + + HANDLER_MAPPING: dict[ConfigVersion, ObjectsAPIRegistrationHandler[Any]] = { 1: ObjectsAPIV1Handler(), + 2: ObjectsAPIV2Handler(), } From eab17c56c18a98fe10884f3b7fa5c11d417f794f Mon Sep 17 00:00:00 2001 From: Viicos <65306057+Viicos@users.noreply.github.com> Date: Tue, 19 Mar 2024 14:51:03 +0100 Subject: [PATCH 6/7] [#3688] Add test Adapt existing tests, they should provide a informatieobjecttype --- ...s.test_submission_with_objects_api_v2.yaml | 164 +++++++++++++++++ .../contrib/objects_api/tests/test_backend.py | 1 + .../objects_api/tests/test_backend_v2.py | 171 ++++++++++++++++++ .../objects_api/tests/test_template.py | 4 + 4 files changed, 340 insertions(+) create mode 100644 src/openforms/registrations/contrib/objects_api/tests/files/vcr_cassettes/ObjectsAPIBackendV2Tests/ObjectsAPIBackendV2Tests.test_submission_with_objects_api_v2.yaml create mode 100644 src/openforms/registrations/contrib/objects_api/tests/test_backend_v2.py diff --git a/src/openforms/registrations/contrib/objects_api/tests/files/vcr_cassettes/ObjectsAPIBackendV2Tests/ObjectsAPIBackendV2Tests.test_submission_with_objects_api_v2.yaml b/src/openforms/registrations/contrib/objects_api/tests/files/vcr_cassettes/ObjectsAPIBackendV2Tests/ObjectsAPIBackendV2Tests.test_submission_with_objects_api_v2.yaml new file mode 100644 index 0000000000..1c5e9a6cee --- /dev/null +++ b/src/openforms/registrations/contrib/objects_api/tests/files/vcr_cassettes/ObjectsAPIBackendV2Tests/ObjectsAPIBackendV2Tests.test_submission_with_objects_api_v2.yaml @@ -0,0 +1,164 @@ +interactions: +- request: + body: '{"informatieobjecttype": "http://localhost:8003/catalogi/api/v1/informatieobjecttypen/7a474713-0833-402a-8441-e467c08ac55b", + "bronorganisatie": "000000000", "creatiedatum": "2024-03-19", "titel": "Form + 000", "auteur": "Aanvrager", "taal": "nld", "formaat": "application/pdf", "inhoud": + "", "status": "definitief", "bestandsnaam": "open-forms-Form 000.pdf", "beschrijving": + "Ingezonden formulier", "indicatieGebruiksrecht": false, "bestandsomvang": 0}' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate, br + Authorization: + - Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ0ZXN0X2NsaWVudF9pZCIsImlhdCI6MTcxMDg1NTYzNCwiY2xpZW50X2lkIjoidGVzdF9jbGllbnRfaWQiLCJ1c2VyX2lkIjoiIiwidXNlcl9yZXByZXNlbnRhdGlvbiI6IiJ9.NJjZEPSwh9eGzntjwUB7rFAiaq_5DqYgVylWuKdtdMA + Connection: + - keep-alive + Content-Length: + - '450' + Content-Type: + - application/json + User-Agent: + - python-requests/2.31.0 + method: POST + uri: http://localhost:8003/documenten/api/v1/enkelvoudiginformatieobjecten + response: + body: + string: '{"url":"http://localhost:8003/documenten/api/v1/enkelvoudiginformatieobjecten/0c5dbf44-ed54-4b11-8974-4f7370a892c4","identificatie":"DOCUMENT-2024-0000000011","bronorganisatie":"000000000","creatiedatum":"2024-03-19","titel":"Form + 000","vertrouwelijkheidaanduiding":"openbaar","auteur":"Aanvrager","status":"definitief","formaat":"application/pdf","taal":"nld","versie":1,"beginRegistratie":"2024-03-19T13:40:44.807631Z","bestandsnaam":"open-forms-Form + 000.pdf","inhoud":"http://localhost:8003/documenten/api/v1/enkelvoudiginformatieobjecten/0c5dbf44-ed54-4b11-8974-4f7370a892c4/download?versie=1","bestandsomvang":0,"link":"","beschrijving":"Ingezonden + formulier","ontvangstdatum":null,"verzenddatum":null,"indicatieGebruiksrecht":false,"verschijningsvorm":"","ondertekening":{"soort":"","datum":null},"integriteit":{"algoritme":"","waarde":"","datum":null},"informatieobjecttype":"http://localhost:8003/catalogi/api/v1/informatieobjecttypen/7a474713-0833-402a-8441-e467c08ac55b","locked":false,"bestandsdelen":[],"trefwoorden":[],"lock":""}' + headers: + API-version: + - 1.4.2 + Allow: + - GET, POST, HEAD, OPTIONS + Content-Length: + - '1042' + Content-Type: + - application/json + Location: + - http://localhost:8003/documenten/api/v1/enkelvoudiginformatieobjecten/0c5dbf44-ed54-4b11-8974-4f7370a892c4 + Referrer-Policy: + - same-origin + Vary: + - Origin + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-XSS-Protection: + - 1; mode=block + status: + code: 201 + message: Created +- request: + body: '{"informatieobjecttype": "http://localhost:8003/catalogi/api/v1/informatieobjecttypen/b2d83b94-9b9b-4e80-a82f-73ff993c62f3", + "bronorganisatie": "000000000", "creatiedatum": "2024-03-19", "titel": "Form + 000 (csv)", "auteur": "Aanvrager", "taal": "nld", "formaat": "text/csv", "inhoud": + "Rm9ybXVsaWVybmFhbSxJbnplbmRpbmdkYXR1bSxhZ2UsbGFzdG5hbWUsbG9jYXRpb24NCkZvcm0gMDAwLDIwMjQtMDMtMDMgMTg6MzQ6MzMuMzUwMDk2LDIwLE15IGxhc3QgbmFtZSwiWzUyLjM2NjczMzc4OTY3MTIyLCA0Ljg5MzE2NDI3NDQ3MDI5OV0iDQo=", + "status": "definitief", "bestandsnaam": "open-forms-Form 000 (csv).csv", "beschrijving": + "Ingezonden formulierdata", "indicatieGebruiksrecht": false, "bestandsomvang": + 146}' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate, br + Authorization: + - Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ0ZXN0X2NsaWVudF9pZCIsImlhdCI6MTcxMDg1NTYzNCwiY2xpZW50X2lkIjoidGVzdF9jbGllbnRfaWQiLCJ1c2VyX2lkIjoiIiwidXNlcl9yZXByZXNlbnRhdGlvbiI6IiJ9.NJjZEPSwh9eGzntjwUB7rFAiaq_5DqYgVylWuKdtdMA + Connection: + - keep-alive + Content-Length: + - '657' + Content-Type: + - application/json + User-Agent: + - python-requests/2.31.0 + method: POST + uri: http://localhost:8003/documenten/api/v1/enkelvoudiginformatieobjecten + response: + body: + string: '{"url":"http://localhost:8003/documenten/api/v1/enkelvoudiginformatieobjecten/de312c5c-a423-4629-afd3-1adf9d7be55e","identificatie":"DOCUMENT-2024-0000000012","bronorganisatie":"000000000","creatiedatum":"2024-03-19","titel":"Form + 000 (csv)","vertrouwelijkheidaanduiding":"openbaar","auteur":"Aanvrager","status":"definitief","formaat":"text/csv","taal":"nld","versie":1,"beginRegistratie":"2024-03-19T13:40:44.854574Z","bestandsnaam":"open-forms-Form + 000 (csv).csv","inhoud":"http://localhost:8003/documenten/api/v1/enkelvoudiginformatieobjecten/de312c5c-a423-4629-afd3-1adf9d7be55e/download?versie=1","bestandsomvang":146,"link":"","beschrijving":"Ingezonden + formulierdata","ontvangstdatum":null,"verzenddatum":null,"indicatieGebruiksrecht":false,"verschijningsvorm":"","ondertekening":{"soort":"","datum":null},"integriteit":{"algoritme":"","waarde":"","datum":null},"informatieobjecttype":"http://localhost:8003/catalogi/api/v1/informatieobjecttypen/b2d83b94-9b9b-4e80-a82f-73ff993c62f3","locked":false,"bestandsdelen":[],"trefwoorden":[],"lock":""}' + headers: + API-version: + - 1.4.2 + Allow: + - GET, POST, HEAD, OPTIONS + Content-Length: + - '1053' + Content-Type: + - application/json + Location: + - http://localhost:8003/documenten/api/v1/enkelvoudiginformatieobjecten/de312c5c-a423-4629-afd3-1adf9d7be55e + Referrer-Policy: + - same-origin + Vary: + - Origin + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-XSS-Protection: + - 1; mode=block + status: + code: 201 + message: Created +- request: + body: '{"type": "http://objecttypes-web:8000/api/v2/objecttypes/8e46e0a5-b1b4-449b-b9e9-fa3cea655f48", + "record": {"typeVersion": 3, "data": {"age": 20, "name": {"last.name": "My last + name"}, "submission_date": "2024-03-19T13:40:34.222258+00:00", "submission_pdf_url": + "http://localhost:8003/documenten/api/v1/enkelvoudiginformatieobjecten/0c5dbf44-ed54-4b11-8974-4f7370a892c4", + "submission_csv_url": "http://localhost:8003/documenten/api/v1/enkelvoudiginformatieobjecten/de312c5c-a423-4629-afd3-1adf9d7be55e", + "submission_payment_completed": false, "submission_payment_amount": "0", "submission_payment_public_ids": + []}, "startAt": "2024-03-19", "geometry": {"type": "Point", "coordinates": [52.36673378967122, + 4.893164274470299]}}}' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate, br + Authorization: + - Token 7657474c3d75f56ae0abd0d1bf7994b09964dca9 + Connection: + - keep-alive + Content-Crs: + - EPSG:4326 + Content-Length: + - '725' + Content-Type: + - application/json + User-Agent: + - python-requests/2.31.0 + method: POST + uri: http://localhost:8002/api/v2/objects + response: + body: + string: '{"url":"http://localhost:8002/api/v2/objects/8ce8a87a-da7b-45ca-8f17-10b491d5da1f","uuid":"8ce8a87a-da7b-45ca-8f17-10b491d5da1f","type":"http://objecttypes-web:8000/api/v2/objecttypes/8e46e0a5-b1b4-449b-b9e9-fa3cea655f48","record":{"index":1,"typeVersion":3,"data":{"age":20,"name":{"last.name":"My + last name"},"submission_date":"2024-03-19T13:40:34.222258+00:00","submission_pdf_url":"http://localhost:8003/documenten/api/v1/enkelvoudiginformatieobjecten/0c5dbf44-ed54-4b11-8974-4f7370a892c4","submission_csv_url":"http://localhost:8003/documenten/api/v1/enkelvoudiginformatieobjecten/de312c5c-a423-4629-afd3-1adf9d7be55e","submission_payment_completed":false,"submission_payment_amount":"0","submission_payment_public_ids":[]},"geometry":{"type":"Point","coordinates":[52.36673378967122,4.893164274470299]},"startAt":"2024-03-19","endAt":null,"registrationAt":"2024-03-19","correctionFor":null,"correctedBy":null}}' + headers: + Allow: + - GET, POST, HEAD, OPTIONS + Content-Crs: + - EPSG:4326 + Content-Length: + - '916' + Content-Type: + - application/json + Cross-Origin-Opener-Policy: + - same-origin + Location: + - http://localhost:8002/api/v2/objects/8ce8a87a-da7b-45ca-8f17-10b491d5da1f + Referrer-Policy: + - same-origin + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + status: + code: 201 + message: Created +version: 1 diff --git a/src/openforms/registrations/contrib/objects_api/tests/test_backend.py b/src/openforms/registrations/contrib/objects_api/tests/test_backend.py index 0db4c9cd50..13697f233a 100644 --- a/src/openforms/registrations/contrib/objects_api/tests/test_backend.py +++ b/src/openforms/registrations/contrib/objects_api/tests/test_backend.py @@ -93,6 +93,7 @@ def test_csv_creation_fails_pdf_still_saved(self, m: requests_mock.Mocker): { "upload_submission_csv": True, "informatieobjecttype_submission_csv": "dummy", + "informatieobjecttype_submission_report": "dummy", }, ) diff --git a/src/openforms/registrations/contrib/objects_api/tests/test_backend_v2.py b/src/openforms/registrations/contrib/objects_api/tests/test_backend_v2.py new file mode 100644 index 0000000000..eee2caf955 --- /dev/null +++ b/src/openforms/registrations/contrib/objects_api/tests/test_backend_v2.py @@ -0,0 +1,171 @@ +from pathlib import Path +from unittest.mock import patch + +from django.test import TestCase +from django.utils import timezone + +from freezegun import freeze_time +from zgw_consumers.constants import APITypes, AuthTypes +from zgw_consumers.test.factories import ServiceFactory + +from openforms.registrations.contrib.objects_api.models import ( + ObjectsAPIRegistrationData, +) +from openforms.submissions.tests.factories import SubmissionFactory +from openforms.utils.tests.vcr import OFVCRMixin + +from ..models import ObjectsAPIConfig +from ..plugin import PLUGIN_IDENTIFIER, ObjectsAPIRegistration +from ..typing import RegistrationOptionsV2 + + +@freeze_time("2024-03-19T13:40:34.222258+00:00") +class ObjectsAPIBackendV2Tests(OFVCRMixin, TestCase): + """This test case requires the Objects & Objecttypes API and Open Zaak to be running. + + See the relevant Docker compose in the ``docker/`` folder. + """ + + maxDiff = None + VCR_TEST_FILES = Path(__file__).parent / "files" + + def setUp(self): + super().setUp() + + config = ObjectsAPIConfig( + objects_service=ServiceFactory.build( + api_root="http://localhost:8002/api/v2/", + api_type=APITypes.orc, + oas="https://example.com/", + header_key="Authorization", + # See the docker compose fixtures: + header_value="Token 7657474c3d75f56ae0abd0d1bf7994b09964dca9", + auth_type=AuthTypes.api_key, + ), + drc_service=ServiceFactory.build( + api_root="http://localhost:8003/documenten/api/v1/", + api_type=APITypes.drc, + # See the docker compose fixtures: + client_id="test_client_id", + secret="test_secret_key", + auth_type=AuthTypes.zgw, + ), + ) + + config_patcher = patch( + "openforms.registrations.contrib.objects_api.models.ObjectsAPIConfig.get_solo", + return_value=config, + ) + self.mock_get_config = config_patcher.start() + self.addCleanup(config_patcher.stop) + + def test_submission_with_objects_api_v2(self): + submission = SubmissionFactory.from_components( + [ + # fmt: off + { + "key": "age", + "type": "number" + }, + { + "key": "lastname", + "type": "textfield", + }, + { + "key": "location", + "type": "map", + }, + # fmt: on + ], + completed=True, + submitted_data={ + "age": 20, + "lastname": "My last name", + "location": [52.36673378967122, 4.893164274470299], + }, + ) + + v2_options: RegistrationOptionsV2 = { + "version": 2, + # See the docker compose fixtures for more info on these values: + "objecttype": "http://objecttypes-web:8000/api/v2/objecttypes/8e46e0a5-b1b4-449b-b9e9-fa3cea655f48", + "objecttype_version": 3, + "upload_submission_csv": True, + "informatieobjecttype_submission_report": "http://localhost:8003/catalogi/api/v1/informatieobjecttypen/7a474713-0833-402a-8441-e467c08ac55b", + "informatieobjecttype_submission_csv": "http://localhost:8003/catalogi/api/v1/informatieobjecttypen/b2d83b94-9b9b-4e80-a82f-73ff993c62f3", + "informatieobjecttype_attachment": "http://localhost:8003/catalogi/api/v1/informatieobjecttypen/531f6c1a-97f7-478c-85f0-67d2f23661c7", + "organisatie_rsin": "000000000", + "variables_mapping": [ + # fmt: off + { + "variable_key": "age", + "target_path": ["age"], + }, + { + "variable_key": "lastname", + "target_path": ["name", "last.name"] + }, + { + "variable_key": "now", + "target_path": ["submission_date"], + }, + { + "variable_key": "pdf_url", + "target_path": ["submission_pdf_url"], + }, + { + "variable_key": "csv_url", + "target_path": ["submission_csv_url"], + }, + { + "variable_key": "payment_completed", + "target_path": ["submission_payment_completed"], + }, + { + "variable_key": "payment_amount", + "target_path": ["submission_payment_amount"], + }, + { + "variable_key": "payment_public_order_ids", + "target_path": ["submission_payment_public_ids"], + }, + # fmt: on + ], + "geometry_variable_key": "location", + } + + plugin = ObjectsAPIRegistration(PLUGIN_IDENTIFIER) + + # Run the registration + result = plugin.register_submission(submission, v2_options) + + registration_data = ObjectsAPIRegistrationData.objects.get( + submission=submission + ) + + self.assertEqual(result["type"], v2_options["objecttype"]) + self.assertEqual( + result["record"]["typeVersion"], v2_options["objecttype_version"] + ) + self.assertEqual( + result["record"]["data"], + { + "age": 20, + "name": { + "last.name": "My last name", + }, + "submission_pdf_url": registration_data.pdf_url, + "submission_csv_url": registration_data.csv_url, + "submission_payment_completed": False, + "submission_payment_amount": "0", + "submission_payment_public_ids": [], + "submission_date": timezone.now().isoformat(), + }, + ) + self.assertEqual( + result["record"]["geometry"], + { + "type": "Point", + "coordinates": [52.36673378967122, 4.893164274470299], + }, + ) diff --git a/src/openforms/registrations/contrib/objects_api/tests/test_template.py b/src/openforms/registrations/contrib/objects_api/tests/test_template.py index 2622c54eb9..01539cec94 100644 --- a/src/openforms/registrations/contrib/objects_api/tests/test_template.py +++ b/src/openforms/registrations/contrib/objects_api/tests/test_template.py @@ -68,6 +68,8 @@ def test_default_template(self, m): { "objecttype": "https://objecttypen.nl/api/v1/objecttypes/1", "objecttype_version": 300, + "informatieobjecttype_submission_report": "https://catalogi.nl/api/v1/informatieobjecttypen/1", + "informatieobjecttype_attachment": "https://catalogi.nl/api/v1/informatieobjecttypen/2", }, ) @@ -180,6 +182,8 @@ def test_custom_template(self, m): "productaanvraag_type": "tralala-type", "upload_submission_csv": True, "informatieobjecttype_submission_csv": "http://oz.nl/informatieobjecttype/1", + "informatieobjecttype_submission_report": "http://oz.nl/informatieobjecttype/2", + "informatieobjecttype_attachment": "http://oz.nl/informatieobjecttype/3", }, ) From 33f70dfd52c30b1482daccb481d6ebf7ec2e1e84 Mon Sep 17 00:00:00 2001 From: Viicos <65306057+Viicos@users.noreply.github.com> Date: Wed, 20 Mar 2024 12:06:17 +0100 Subject: [PATCH 7/7] [#3688] PR feedback --- docker/docker-compose.objects-apis.yml | 2 +- docker/docker-compose.open-zaak.yml | 2 +- .../contrib/objects_api/submission_registration.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docker/docker-compose.objects-apis.yml b/docker/docker-compose.objects-apis.yml index c7deb778c7..67a7e8b46e 100644 --- a/docker/docker-compose.objects-apis.yml +++ b/docker/docker-compose.objects-apis.yml @@ -1,4 +1,4 @@ -version: '3' +version: '3.8' name: objects-apis diff --git a/docker/docker-compose.open-zaak.yml b/docker/docker-compose.open-zaak.yml index a64eed8e31..c80e778fe7 100644 --- a/docker/docker-compose.open-zaak.yml +++ b/docker/docker-compose.open-zaak.yml @@ -1,4 +1,4 @@ -version: '3' +version: '3.8' name: open-zaak diff --git a/src/openforms/registrations/contrib/objects_api/submission_registration.py b/src/openforms/registrations/contrib/objects_api/submission_registration.py index 15647acafc..9b7d2e01db 100644 --- a/src/openforms/registrations/contrib/objects_api/submission_registration.py +++ b/src/openforms/registrations/contrib/objects_api/submission_registration.py @@ -38,7 +38,7 @@ def _point_coordinate(value: Any) -> dict[str, Any] | object: - if not value or not isinstance(value, list) or len(value) != 2: + if not isinstance(value, list) or len(value) != 2: return SKIP return {"type": "Point", "coordinates": [value[0], value[1]]}