From 66f9340406d63f4ca138cb2f7a96484f41bb45b1 Mon Sep 17 00:00:00 2001
From: Viicos <65306057+Viicos@users.noreply.github.com>
Date: Thu, 7 Mar 2024 17:28:20 +0100
Subject: [PATCH 01/11] [#3688] Add `StrOrPromise` as a public type hint
---
src/openforms/typing.py | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/src/openforms/typing.py b/src/openforms/typing.py
index d3ea109569..ec45cd8708 100644
--- a/src/openforms/typing.py
+++ b/src/openforms/typing.py
@@ -1,7 +1,7 @@
import datetime
import decimal
import uuid
-from typing import Any, NewType, Protocol, TypeAlias
+from typing import TYPE_CHECKING, Any, NewType, Protocol, TypeAlias
from django.http import HttpRequest
from django.http.response import HttpResponseBase
@@ -9,6 +9,11 @@
from rest_framework.request import Request
+if TYPE_CHECKING:
+ from django.utils.functional import _StrOrPromise
+else:
+ _StrOrPromise = str
+
JSONPrimitive: TypeAlias = str | int | float | bool | None
JSONValue: TypeAlias = "JSONPrimitive | JSONObject | list[JSONValue]"
@@ -21,6 +26,9 @@
RegistrationBackendKey = NewType("RegistrationBackendKey", str)
+StrOrPromise: TypeAlias = _StrOrPromise
+"""Either ``str`` or a ``Promise`` object returned by the lazy ``gettext`` functions."""
+
class RequestHandler(Protocol):
def __call__(self, request: HttpRequest) -> HttpResponseBase: ...
From d3b6e287d78e9350bb8ec8b037b32b930e5883d6 Mon Sep 17 00:00:00 2001
From: Viicos <65306057+Viicos@users.noreply.github.com>
Date: Thu, 7 Mar 2024 17:29:44 +0100
Subject: [PATCH 02/11] [#3688] Refactor base static variable a bit
Drop the dataclass, makes it confusing with field excluded
from init, and the fact that the identifier needs to be
specified again
Use class vars instead
Make use of newly added type alias
---
src/openforms/variables/base.py | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/src/openforms/variables/base.py b/src/openforms/variables/base.py
index cb5e1e0c95..3ff4ef83c6 100644
--- a/src/openforms/variables/base.py
+++ b/src/openforms/variables/base.py
@@ -1,16 +1,17 @@
from abc import ABC, abstractmethod
-from dataclasses import dataclass, field
+from typing import ClassVar
from openforms.forms.models import FormVariable
from openforms.plugins.plugin import AbstractBasePlugin
from openforms.submissions.models import Submission
+from openforms.typing import StrOrPromise
+
+from .constants import FormVariableDataTypes
-@dataclass
class BaseStaticVariable(ABC, AbstractBasePlugin):
- identifier: str
- name: str = field(init=False)
- data_type: str = field(init=False)
+ name: ClassVar[StrOrPromise]
+ data_type: ClassVar[FormVariableDataTypes]
@abstractmethod
def get_initial_value(self, submission: Submission | None = None):
From 6a162c7159354fc8c692a6f44994bd1a788b8793 Mon Sep 17 00:00:00 2001
From: Viicos <65306057+Viicos@users.noreply.github.com>
Date: Wed, 13 Mar 2024 15:02:55 +0100
Subject: [PATCH 03/11] [#3688] Add a new `get_variables_registry` method to
registration plugins
Update tests that where affected
For the objects API, the `variables_registry` will be defined in
the upcoming commits
---
.../test_variables_in_logic_trigger.py | 4 +-
src/openforms/registrations/base.py | 18 +++++---
.../contrib/objects_api/plugin.py | 14 +++++-
src/openforms/variables/service.py | 43 +++++++++++++++++--
4 files changed, 67 insertions(+), 12 deletions(-)
diff --git a/src/openforms/forms/tests/variables/test_variables_in_logic_trigger.py b/src/openforms/forms/tests/variables/test_variables_in_logic_trigger.py
index 4dcd61a31c..4ac5f31fa3 100644
--- a/src/openforms/forms/tests/variables/test_variables_in_logic_trigger.py
+++ b/src/openforms/forms/tests/variables/test_variables_in_logic_trigger.py
@@ -220,7 +220,9 @@ def test_static_variable_in_trigger(self):
self.client.force_authenticate(user=user)
url = reverse("api:form-logic-rules", kwargs={"uuid_or_slug": form.uuid})
- with patch("openforms.variables.service.register", new=register):
+ with patch(
+ "openforms.variables.service.static_variables_registry", new=register
+ ):
response = self.client.put(url, data=form_logic_data)
self.assertEqual(status.HTTP_200_OK, response.status_code)
diff --git a/src/openforms/registrations/base.py b/src/openforms/registrations/base.py
index a4a52a3d8b..3d3098fc0e 100644
--- a/src/openforms/registrations/base.py
+++ b/src/openforms/registrations/base.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
from abc import ABC, abstractmethod
from dataclasses import dataclass
from typing import TYPE_CHECKING
@@ -8,7 +10,9 @@
from openforms.utils.mixins import JsonSchemaSerializerMixin
if TYPE_CHECKING:
+ from openforms.plugins.registry import BaseRegistry
from openforms.submissions.models import Submission
+ from openforms.variables.base import BaseStaticVariable
SerializerCls = type[serializers.Serializer]
@@ -42,16 +46,14 @@ class BasePlugin(ABC, AbstractBasePlugin):
"""
@abstractmethod
- def register_submission(
- self, submission: "Submission", options: dict
- ) -> dict | None:
+ def register_submission(self, submission: Submission, options: dict) -> dict | None:
raise NotImplementedError()
- def update_payment_status(self, submission: "Submission", options: dict):
+ def update_payment_status(self, submission: Submission, options: dict):
raise NotImplementedError()
def pre_register_submission(
- self, submission: "Submission", options: dict
+ self, submission: Submission, options: dict
) -> PreRegistrationResult:
"""Perform any tasks before registering the submission
@@ -65,3 +67,9 @@ def get_custom_templatetags_libraries(self) -> list[str]:
Return a list of custom templatetags libraries that will be added to the 'sandboxed' Django templates backend.
"""
return []
+
+ def get_variables_registry(self) -> BaseRegistry[BaseStaticVariable] | None:
+ """
+ Return the static variables registry for this registration puglin.
+ """
+ return None
diff --git a/src/openforms/registrations/contrib/objects_api/plugin.py b/src/openforms/registrations/contrib/objects_api/plugin.py
index fa74f37135..ebf84ce753 100644
--- a/src/openforms/registrations/contrib/objects_api/plugin.py
+++ b/src/openforms/registrations/contrib/objects_api/plugin.py
@@ -1,6 +1,8 @@
+from __future__ import annotations
+
import logging
from functools import partial
-from typing import Any
+from typing import TYPE_CHECKING, Any
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
@@ -8,7 +10,6 @@
from typing_extensions import override
from openforms.registrations.utils import execute_unless_result_exists
-from openforms.submissions.models import Submission
from openforms.utils.date import get_today
from ...base import BasePlugin
@@ -17,9 +18,14 @@
from .client import get_objects_client
from .config import ObjectsAPIOptionsSerializer
from .models import ObjectsAPIConfig
+from .registration_variables import Registry, register as variables_registry
from .submission_registration import HANDLER_MAPPING
from .typing import RegistrationOptions
+if TYPE_CHECKING:
+ from openforms.submissions.models import Submission
+
+
PLUGIN_IDENTIFIER = "objects_api"
logger = logging.getLogger(__name__)
@@ -129,3 +135,7 @@ def update_payment_status(
headers={"Content-Crs": "EPSG:4326"},
)
response.raise_for_status()
+
+ @override
+ def get_variables_registry(self) -> Registry:
+ return variables_registry
diff --git a/src/openforms/variables/service.py b/src/openforms/variables/service.py
index 50d0c6ea3f..bb8421959a 100644
--- a/src/openforms/variables/service.py
+++ b/src/openforms/variables/service.py
@@ -2,15 +2,25 @@
Public Python API to access (static) form variables.
"""
+from typing import TypedDict
+
from openforms.forms.models import FormVariable
+from openforms.plugins.registry import BaseRegistry
+from openforms.registrations.registry import register as registration_plugins_registry
from openforms.submissions.models import Submission
+from openforms.typing import StrOrPromise
-from .registry import register_static_variable as register
+from .base import BaseStaticVariable
+from .registry import register_static_variable as static_variables_registry
-__all__ = ["get_static_variables"]
+__all__ = ["get_static_variables", "get_registration_plugins_variables"]
-def get_static_variables(*, submission: Submission | None = None) -> list[FormVariable]:
+def get_static_variables(
+ *,
+ submission: Submission | None = None,
+ variables_registry: BaseRegistry[BaseStaticVariable] | None = None,
+) -> list[FormVariable]:
"""
Return the full collection of static variables registered by all apps.
@@ -18,11 +28,36 @@ def get_static_variables(*, submission: Submission | None = None) -> list[FormVa
variables for. Many of the static variables require the submission for sufficient
context to be able to produce a value. Variables are static within that submission
context, i.e. they don't change because of filling out the form.
+ :param variables_registry: The static variables registry to use. If not provided,
+ the default registry will be used.
You should not rely on the order of returned variables, as they are registered in
the order the Django apps are loaded - and this may change without notice.
"""
return [
registered_variable.get_static_variable(submission=submission)
- for registered_variable in register
+ for registered_variable in variables_registry or static_variables_registry
+ ]
+
+
+class PluginInfo(TypedDict):
+ plugin_identifier: str
+ plugin_verbose_name: StrOrPromise
+ plugin_variables: list[FormVariable]
+
+
+def get_registration_plugins_variables() -> list[PluginInfo]:
+ """Return the registration plugin variables from the enabled plugins.
+
+ The initial values aren't populated.
+ """
+
+ return [
+ {
+ "plugin_identifier": plugin.identifier,
+ "plugin_verbose_name": plugin.verbose_name,
+ "plugin_variables": get_static_variables(variables_registry=registry),
+ }
+ for plugin in registration_plugins_registry.iter_enabled_plugins()
+ if (registry := plugin.get_variables_registry()) is not None
]
From 33b4fb131b6714de02ba1e0236d7fd71813e6001 Mon Sep 17 00:00:00 2001
From: Viicos <65306057+Viicos@users.noreply.github.com>
Date: Wed, 13 Mar 2024 15:04:41 +0100
Subject: [PATCH 04/11] [#3688] Add a new model to store temporary results
Will be used by objects API registration variables
---
.../0013_objectsapiregistrationdata.py | 70 +++++++++++++++++++
.../contrib/objects_api/models.py | 36 ++++++++++
2 files changed, 106 insertions(+)
create mode 100644 src/openforms/registrations/contrib/objects_api/migrations/0013_objectsapiregistrationdata.py
diff --git a/src/openforms/registrations/contrib/objects_api/migrations/0013_objectsapiregistrationdata.py b/src/openforms/registrations/contrib/objects_api/migrations/0013_objectsapiregistrationdata.py
new file mode 100644
index 0000000000..aa45209fbc
--- /dev/null
+++ b/src/openforms/registrations/contrib/objects_api/migrations/0013_objectsapiregistrationdata.py
@@ -0,0 +1,70 @@
+# Generated by Django 4.2.10 on 2024-03-13 10:39
+
+import django.contrib.postgres.fields
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ("submissions", "0004_auto_20231128_1536"),
+ ("registrations_objects_api", "0012_fill_required_fields"),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name="ObjectsAPIRegistrationData",
+ fields=[
+ (
+ "id",
+ models.AutoField(
+ auto_created=True,
+ primary_key=True,
+ serialize=False,
+ verbose_name="ID",
+ ),
+ ),
+ (
+ "pdf_url",
+ models.URLField(
+ blank=True,
+ help_text="The PDF URL of the document on the Documents API.",
+ verbose_name="pdf url",
+ ),
+ ),
+ (
+ "csv_url",
+ models.URLField(
+ blank=True,
+ help_text="The CSV URL of the document on the Documents API.",
+ verbose_name="csv url",
+ ),
+ ),
+ (
+ "attachment_urls",
+ django.contrib.postgres.fields.ArrayField(
+ base_field=models.URLField(
+ help_text="The attachment URL on the Documents API.",
+ verbose_name="attachment url",
+ ),
+ blank=True,
+ default=list,
+ help_text="The list of attachment URLs on the Documents API.",
+ size=None,
+ verbose_name="attachment urls",
+ ),
+ ),
+ (
+ "submission",
+ models.OneToOneField(
+ help_text="The submission linked to the registration data.",
+ on_delete=django.db.models.deletion.CASCADE,
+ related_name="objects_api_registration_data",
+ to="submissions.submission",
+ verbose_name="submission",
+ ),
+ ),
+ ],
+ ),
+ ]
diff --git a/src/openforms/registrations/contrib/objects_api/models.py b/src/openforms/registrations/contrib/objects_api/models.py
index 9e0d7e920e..5449920690 100644
--- a/src/openforms/registrations/contrib/objects_api/models.py
+++ b/src/openforms/registrations/contrib/objects_api/models.py
@@ -1,3 +1,4 @@
+from django.contrib.postgres.fields import ArrayField
from django.core.exceptions import ValidationError
from django.db import models
from django.template.loader import render_to_string
@@ -195,3 +196,38 @@ def apply_defaults_to(self, options):
options["payment_status_update_json"] = self.payment_status_update_json
options.setdefault("auteur", "Aanvrager")
+
+
+class ObjectsAPIRegistrationData(models.Model):
+ """Holds the temporary data available when registering a submission to the Objects API.
+
+ When starting the submission registration, this model is first populated. The Objects API
+ registration variables can then safely make use of this model to provide their data.
+ """
+
+ submission = models.OneToOneField(
+ "submissions.Submission",
+ on_delete=models.CASCADE,
+ verbose_name=_("submission"),
+ help_text=_("The submission linked to the registration data."),
+ related_name="objects_api_registration_data",
+ )
+ pdf_url = models.URLField(
+ _("pdf url"),
+ help_text=_("The PDF URL of the document on the Documents API."),
+ blank=True,
+ )
+ csv_url = models.URLField(
+ _("csv url"),
+ help_text=_("The CSV URL of the document on the Documents API."),
+ blank=True,
+ )
+ attachment_urls = ArrayField(
+ models.URLField(
+ _("attachment url"), help_text=_("The attachment URL on the Documents API.")
+ ),
+ verbose_name=_("attachment urls"),
+ help_text=_("The list of attachment URLs on the Documents API."),
+ blank=True,
+ default=list,
+ )
From e509514508235406eedaafcb85556b9db96526f0 Mon Sep 17 00:00:00 2001
From: Viicos <65306057+Viicos@users.noreply.github.com>
Date: Wed, 13 Mar 2024 15:06:26 +0100
Subject: [PATCH 05/11] [#3688] Add registry and static variables for Objects
API
---
.../objects_api/registration_variables.py | 94 +++++++++++++++++++
1 file changed, 94 insertions(+)
create mode 100644 src/openforms/registrations/contrib/objects_api/registration_variables.py
diff --git a/src/openforms/registrations/contrib/objects_api/registration_variables.py b/src/openforms/registrations/contrib/objects_api/registration_variables.py
new file mode 100644
index 0000000000..72c61e7d3a
--- /dev/null
+++ b/src/openforms/registrations/contrib/objects_api/registration_variables.py
@@ -0,0 +1,94 @@
+from __future__ import annotations
+
+from typing import TYPE_CHECKING
+
+from django.utils.translation import gettext_lazy as _
+
+from openforms.plugins.registry import BaseRegistry
+from openforms.variables.base import BaseStaticVariable
+from openforms.variables.constants import FormVariableDataTypes
+
+if TYPE_CHECKING:
+ from openforms.submissions.models import Submission
+
+
+class Registry(BaseRegistry[BaseStaticVariable]):
+ """
+ A registry for the Objects API registration variables.
+ """
+
+ module = "objects_api"
+
+
+register = Registry()
+"""The Objects API registration variables registry."""
+
+
+@register("pdf_url")
+class PdfUrl(BaseStaticVariable):
+ name = _("PDF Url")
+ data_type = FormVariableDataTypes.string
+
+ def get_initial_value(self, submission: Submission | None = None):
+ return (
+ submission.objects_api_registration_data.pdf_url
+ if submission is not None
+ else None
+ )
+
+
+@register("csv_url")
+class CsvUrl(BaseStaticVariable):
+ name = _("CSV Url")
+ data_type = FormVariableDataTypes.string
+
+ def get_initial_value(self, submission: Submission | None = None):
+ return (
+ submission.objects_api_registration_data.csv_url
+ if submission is not None
+ else None
+ )
+
+
+@register("attachment_urls")
+class AttachmentUrls(BaseStaticVariable):
+ name = _("Attachment Urls")
+ data_type = FormVariableDataTypes.array
+
+ def get_initial_value(self, submission: Submission | None = None):
+ return (
+ submission.objects_api_registration_data.attachment_urls
+ if submission is not None
+ else None
+ )
+
+
+@register("payment_completed")
+class PaymentCompleted(BaseStaticVariable):
+ name = _("Payment completed")
+ data_type = FormVariableDataTypes.boolean
+
+ def get_initial_value(self, submission: Submission | None = None):
+ return submission.payment_user_has_paid if submission is not None else None
+
+
+@register("payment_amount")
+class PaymentAmount(BaseStaticVariable):
+ name = _("Payment amount")
+ data_type = FormVariableDataTypes.string
+
+ def get_initial_value(self, submission: Submission | None = None):
+ return str(submission.payments.sum_amount()) if submission is not None else None
+
+
+@register("payment_public_order_ids")
+class PaymentPublicOrderIds(BaseStaticVariable):
+ name = _("Payment public order IDs")
+ data_type = FormVariableDataTypes.array
+
+ def get_initial_value(self, submission: Submission | None = None):
+ return (
+ submission.payments.get_completed_public_order_ids()
+ if submission is not None
+ else None
+ )
From 11bca9aec629c5e24ed891b1c136c339476f3211 Mon Sep 17 00:00:00 2001
From: Viicos <65306057+Viicos@users.noreply.github.com>
Date: Wed, 13 Mar 2024 15:10:23 +0100
Subject: [PATCH 06/11] [#3688] Refactor registration handlers
Define a new step, `save_registration_data`, that will
populate the `ObjectsAPIRegistrationData`.
---
.../contrib/objects_api/plugin.py | 2 +
.../objects_api/submission_registration.py | 321 +++++++++++-------
2 files changed, 199 insertions(+), 124 deletions(-)
diff --git a/src/openforms/registrations/contrib/objects_api/plugin.py b/src/openforms/registrations/contrib/objects_api/plugin.py
index ebf84ce753..eaac4a8d3b 100644
--- a/src/openforms/registrations/contrib/objects_api/plugin.py
+++ b/src/openforms/registrations/contrib/objects_api/plugin.py
@@ -64,6 +64,8 @@ def register_submission(
handler = HANDLER_MAPPING[options["version"]]
+ handler.save_registration_data(submission, options)
+
object_data = handler.get_object_data(
submission=submission,
options=options,
diff --git a/src/openforms/registrations/contrib/objects_api/submission_registration.py b/src/openforms/registrations/contrib/objects_api/submission_registration.py
index 0e1868e5b9..6151e6ed2a 100644
--- a/src/openforms/registrations/contrib/objects_api/submission_registration.py
+++ b/src/openforms/registrations/contrib/objects_api/submission_registration.py
@@ -1,4 +1,6 @@
-from typing import Any, Protocol, TypeVar, cast
+from abc import ABC, abstractmethod
+from contextlib import contextmanager
+from typing import Any, Generic, Iterator, TypeVar, cast
from typing_extensions import override
@@ -9,6 +11,7 @@
create_csv_document,
create_report_document,
)
+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
@@ -16,27 +19,208 @@
from ...constants import REGISTRATION_ATTRIBUTE, RegistrationAttribute
from .client import DocumentenClient, get_documents_client
-from .typing import ConfigVersion, RegistrationOptionsV1, RegistrationOptionsV2
+from .models import ObjectsAPIRegistrationData
+from .typing import (
+ ConfigVersion,
+ RegistrationOptions,
+ RegistrationOptionsV1,
+ RegistrationOptionsV2,
+)
+
+
+def build_options(
+ plugin_options: RegistrationOptions, key_mapping: dict[str, str]
+) -> dict[str, Any]:
+ """
+ Construct options from plugin options dict, allowing renaming of keys
+ """
+ options = {
+ new_key: plugin_options[key_in_opts]
+ for new_key, key_in_opts in key_mapping.items()
+ if key_in_opts in plugin_options
+ }
+ return options
+
+
+def registration_submission_pdf(
+ submission: Submission,
+ options: RegistrationOptions,
+ documents_client: DocumentenClient,
+) -> str:
+ submission_report = SubmissionReport.objects.get(submission=submission)
+ submission_report_options = build_options(
+ options,
+ {
+ "informatieobjecttype": "informatieobjecttype_submission_report",
+ "organisatie_rsin": "organisatie_rsin",
+ "doc_vertrouwelijkheidaanduiding": "doc_vertrouwelijkheidaanduiding",
+ },
+ )
+
+ report_document = create_report_document(
+ client=documents_client,
+ name=submission.form.admin_name,
+ submission_report=submission_report,
+ options=submission_report_options,
+ language=submission_report.submission.language_code,
+ )
+ return report_document["url"]
+
+
+def register_submission_csv(
+ submission: Submission,
+ options: RegistrationOptions,
+ documents_client: DocumentenClient,
+) -> str:
+ if not options.get("upload_submission_csv", False):
+ return ""
+
+ if not options["informatieobjecttype_submission_csv"]:
+ return ""
+
+ submission_csv_options = build_options(
+ options,
+ {
+ "informatieobjecttype": "informatieobjecttype_submission_csv",
+ "organisatie_rsin": "organisatie_rsin",
+ "doc_vertrouwelijkheidaanduiding": "doc_vertrouwelijkheidaanduiding",
+ "auteur": "auteur",
+ },
+ )
+ qs = Submission.objects.filter(pk=submission.pk).select_related("auth_info")
+ submission_csv = create_submission_export(qs).export("csv") # type: ignore
+
+ submission_csv_document = create_csv_document(
+ client=documents_client,
+ name=f"{submission.form.admin_name} (csv)",
+ csv_data=submission_csv,
+ options=submission_csv_options,
+ language=submission.language_code,
+ )
+
+ return submission_csv_document["url"]
+
+
+def register_submission_attachments(
+ submission: Submission,
+ options: RegistrationOptions,
+ documents_client: DocumentenClient,
+) -> list[str]:
+ attachment_urls: list[str] = []
+ for attachment in submission.attachments:
+ attachment_options = build_options(
+ options,
+ {
+ "informatieobjecttype": "informatieobjecttype_attachment", # Different IOT than for the report
+ "organisatie_rsin": "organisatie_rsin",
+ "doc_vertrouwelijkheidaanduiding": "doc_vertrouwelijkheidaanduiding",
+ },
+ )
+
+ component_overwrites = {
+ "doc_vertrouwelijkheidaanduiding": attachment.doc_vertrouwelijkheidaanduiding,
+ "titel": attachment.titel,
+ "organisatie_rsin": attachment.bronorganisatie,
+ "informatieobjecttype": attachment.informatieobjecttype,
+ }
+
+ for key, value in component_overwrites.items():
+ if value:
+ attachment_options[key] = value
+
+ attachment_document = create_attachment_document(
+ client=documents_client,
+ name=submission.form.admin_name,
+ submission_attachment=attachment,
+ options=attachment_options,
+ language=attachment.submission_step.submission.language_code, # assume same as submission
+ )
+ attachment_urls.append(attachment_document["url"])
+
+ return attachment_urls
+
+
+@contextmanager
+def save_and_raise(registration_data: ObjectsAPIRegistrationData) -> Iterator[None]:
+ """Save the registration data before raising a :class:`~openforms.registrations.exceptions.RegistrationFailed` exception."""
+
+ try:
+ yield
+ except Exception as e:
+ raise RegistrationFailed() from e
+ finally:
+ registration_data.save()
+
OptionsT = TypeVar(
"OptionsT", RegistrationOptionsV1, RegistrationOptionsV2, contravariant=True
)
-class ObjectsAPIRegistrationHandler(Protocol[OptionsT]):
- """Provide the registration data to be sent to the Objects API."""
+class ObjectsAPIRegistrationHandler(ABC, Generic[OptionsT]):
+ """Provide the registration data to be sent to the Objects API.
+
+ When registering a submission to the Objects API, the following happens:
+ - Depending on the version (v1 or v2) of the options, the correct handler is instantiated.
+ - ``save_registration_data`` is called, creating an instance of ``ObjectsAPIRegistrationData``.
+ - ``get_object_data`` is called, and should make use of the saved registration data to build the payload
+ to be sent to the Objects API.
+ - Similarly, ``get_update_payment_status_data`` is called to get the PATCH payload to be sent
+ to the Objects API.
+ """
+
+ def save_registration_data(
+ self,
+ submission: Submission,
+ options: OptionsT,
+ ) -> None:
+ """Save the registration data.
+
+ The creation of submission documents (report, attachment, csv) makes use of ZGW
+ service functions (e.g. :func:`create_report_document`) and involves a mapping
+ (and in some cases renaming) of variables which would otherwise not be
+ accessible from here. For example, 'vertrouwelijkheidaanduiding' must be named
+ 'doc_vertrouwelijkheidaanduiding' because this is what the ZGW service functions
+ use.
+ """
+ registration_data, _ = ObjectsAPIRegistrationData.objects.get_or_create(
+ submission=submission
+ )
+
+ with get_documents_client() as documents_client, save_and_raise(
+ registration_data
+ ):
+ if not registration_data.pdf_url:
+ registration_data.pdf_url = registration_submission_pdf(
+ submission, options, documents_client
+ )
+
+ if not registration_data.csv_url:
+ registration_data.csv_url = register_submission_csv(
+ submission, options, documents_client
+ )
+
+ if not registration_data.attachment_urls:
+ # TODO turn attachments into a dictionary when giving users more options than
+ # just urls.
+ registration_data.attachment_urls = register_submission_attachments(
+ submission, options, documents_client
+ )
+ @abstractmethod
def get_object_data(
self,
submission: Submission,
options: OptionsT,
) -> dict[str, Any]:
"""Get the object data payload to be sent to the Objects API."""
- ...
+ pass
+ @abstractmethod
def get_update_payment_status_data(
self, submission: Submission, options: OptionsT
- ) -> dict[str, Any]: ...
+ ) -> dict[str, Any]:
+ pass
class ObjectsAPIV1Handler(ObjectsAPIRegistrationHandler[RegistrationOptionsV1]):
@@ -56,128 +240,17 @@ def get_payment_context_data(submission: Submission) -> dict[str, Any]:
"public_order_ids": submission.payments.get_completed_public_order_ids(),
}
- @staticmethod
- def build_options(plugin_options: RegistrationOptionsV1, key_mapping: dict) -> dict:
- """
- Construct options from plugin options dict, allowing renaming of keys
- """
- options = {
- new_key: plugin_options[key_in_opts]
- for new_key, key_in_opts in key_mapping.items()
- if key_in_opts in plugin_options
- }
- return options
-
- def register_submission_csv(
- self,
- submission: Submission,
- options: RegistrationOptionsV1,
- documents_client: DocumentenClient,
- ) -> str:
- if not options.get("upload_submission_csv", False):
- return ""
-
- if not options["informatieobjecttype_submission_csv"]:
- return ""
-
- submission_csv_options = self.build_options(
- options,
- {
- "informatieobjecttype": "informatieobjecttype_submission_csv",
- "organisatie_rsin": "organisatie_rsin",
- "doc_vertrouwelijkheidaanduiding": "doc_vertrouwelijkheidaanduiding",
- "auteur": "auteur",
- },
- )
- qs = Submission.objects.filter(pk=submission.pk).select_related("auth_info")
- submission_csv = create_submission_export(qs).export("csv") # type: ignore
-
- submission_csv_document = create_csv_document(
- client=documents_client,
- name=f"{submission.form.admin_name} (csv)",
- csv_data=submission_csv,
- options=submission_csv_options,
- language=submission.language_code,
- )
-
- return submission_csv_document["url"]
-
@override
def get_object_data(
self,
submission: Submission,
options: RegistrationOptionsV1,
) -> dict[str, Any]:
- """Get the object data payload to be sent to the Objects API.
-
- The creation of submission documents (report, attachment, csv) makes use of ZGW
- service functions (e.g. :func:`create_report_document`) and involves a mapping
- (and in some cases renaming) of variables which would otherwise not be
- accessible from here. For example, 'vertrouwelijkheidaanduiding' must be named
- 'doc_vertrouwelijkheidaanduiding' because this is what the ZGW service functions
- use.
- """
-
- # Prepare all documents to relate to the Objects API record
- with get_documents_client() as documents_client:
- # Create the document for the PDF summary
- submission_report = SubmissionReport.objects.get(submission=submission)
- submission_report_options = self.build_options(
- options,
- {
- "informatieobjecttype": "informatieobjecttype_submission_report",
- "organisatie_rsin": "organisatie_rsin",
- "doc_vertrouwelijkheidaanduiding": "doc_vertrouwelijkheidaanduiding",
- },
- )
-
- report_document = create_report_document(
- client=documents_client,
- name=submission.form.admin_name,
- submission_report=submission_report,
- options=submission_report_options,
- language=submission_report.submission.language_code,
- )
-
- # Register the attachments
- # TODO turn attachments into dictionary when giving users more options then
- # just urls.
- attachment_urls: list[str] = []
- for attachment in submission.attachments:
- attachment_options = self.build_options(
- options,
- {
- "informatieobjecttype": "informatieobjecttype_attachment", # Different IOT than for the report
- "organisatie_rsin": "organisatie_rsin",
- "doc_vertrouwelijkheidaanduiding": "doc_vertrouwelijkheidaanduiding",
- },
- )
-
- component_overwrites = {
- "doc_vertrouwelijkheidaanduiding": attachment.doc_vertrouwelijkheidaanduiding,
- "titel": attachment.titel,
- "organisatie_rsin": attachment.bronorganisatie,
- "informatieobjecttype": attachment.informatieobjecttype,
- }
-
- for key, value in component_overwrites.items():
- if value:
- attachment_options[key] = value
-
- attachment_document = create_attachment_document(
- client=documents_client,
- name=submission.form.admin_name,
- submission_attachment=attachment,
- options=attachment_options,
- language=attachment.submission_step.submission.language_code, # assume same as submission
- )
- attachment_urls.append(attachment_document["url"])
+ """Get the object data payload to be sent to the Objects API."""
- # Create the CSV submission export, if requested.
- # If no CSV is being uploaded, then `assert csv_url == ""` applies.
- csv_url = self.register_submission_csv(
- submission, options, documents_client
- )
+ registration_data = ObjectsAPIRegistrationData.objects.get(
+ submission=submission
+ )
context = {
"_submission": submission,
@@ -190,9 +263,9 @@ def get_object_data(
"public_reference": submission.public_registration_reference,
"kenmerk": str(submission.uuid),
"language_code": submission.language_code,
- "uploaded_attachment_urls": attachment_urls,
- "pdf_url": report_document["url"],
- "csv_url": csv_url,
+ "uploaded_attachment_urls": registration_data.attachment_urls,
+ "pdf_url": registration_data.pdf_url,
+ "csv_url": registration_data.csv_url,
},
}
From b8e0de705ea91b9310912bd1952fb6ca7765cf05 Mon Sep 17 00:00:00 2001
From: Viicos <65306057+Viicos@users.noreply.github.com>
Date: Wed, 13 Mar 2024 15:12:11 +0100
Subject: [PATCH 07/11] [#3688] Add endpoint to get available registration
variables
---
src/openapi.yaml | 47 ++++++++++++++++++-
.../variables/api/registration_serializer.py | 23 +++++++++
src/openforms/variables/api/views.py | 30 ++++++++++--
src/openforms/variables/urls.py | 9 +++-
4 files changed, 102 insertions(+), 7 deletions(-)
create mode 100644 src/openforms/variables/api/registration_serializer.py
diff --git a/src/openapi.yaml b/src/openapi.yaml
index e3a04eeee6..00858e2067 100644
--- a/src/openapi.yaml
+++ b/src/openapi.yaml
@@ -5619,11 +5619,54 @@ paths:
$ref: '#/components/headers/X-Is-Form-Designer'
Content-Language:
$ref: '#/components/headers/Content-Language'
+ /api/v2/variables/registration:
+ get:
+ operationId: variables_registration_list
+ description: List the registration static variables that will be associated
+ with every form
+ summary: Get registration static variables
+ tags:
+ - variables
+ security:
+ - cookieAuth: []
+ responses:
+ '200':
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ $ref: '#/components/schemas/FormVariable'
+ description: ''
+ headers:
+ X-Session-Expires-In:
+ $ref: '#/components/headers/X-Session-Expires-In'
+ X-CSRFToken:
+ $ref: '#/components/headers/X-CSRFToken'
+ X-Is-Form-Designer:
+ $ref: '#/components/headers/X-Is-Form-Designer'
+ Content-Language:
+ $ref: '#/components/headers/Content-Language'
+ '403':
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Exception'
+ description: ''
+ headers:
+ X-Session-Expires-In:
+ $ref: '#/components/headers/X-Session-Expires-In'
+ X-CSRFToken:
+ $ref: '#/components/headers/X-CSRFToken'
+ X-Is-Form-Designer:
+ $ref: '#/components/headers/X-Is-Form-Designer'
+ Content-Language:
+ $ref: '#/components/headers/Content-Language'
/api/v2/variables/static:
get:
operationId: variables_static_list
- description: List the static data that will be associated with every form
- summary: Get static data
+ description: List the static variables that will be associated with every form
+ summary: Get static variables
tags:
- variables
security:
diff --git a/src/openforms/variables/api/registration_serializer.py b/src/openforms/variables/api/registration_serializer.py
new file mode 100644
index 0000000000..230310956f
--- /dev/null
+++ b/src/openforms/variables/api/registration_serializer.py
@@ -0,0 +1,23 @@
+# The serializer is defined in a separate module to avoid circular imports.
+
+from django.utils.translation import gettext_lazy as _
+
+from rest_framework import serializers
+
+from openforms.forms.api.serializers import FormVariableSerializer
+
+
+class RegistrationPluginVariablesSerializer(serializers.Serializer):
+ plugin_identifier = serializers.CharField(
+ label=_("backend identifier"),
+ help_text=_("The identifier of the registration plugin."),
+ )
+ plugin_verbose_name = serializers.CharField(
+ label=_("backend verbose name"),
+ help_text=_("The verbose name of the registration plugin."),
+ )
+ plugin_variables = serializers.ListSerializer(
+ child=FormVariableSerializer(),
+ label=_("variables"),
+ help_text=_("The list of corresponding registration variables."),
+ )
diff --git a/src/openforms/variables/api/views.py b/src/openforms/variables/api/views.py
index d6221e129a..a6acc9d174 100644
--- a/src/openforms/variables/api/views.py
+++ b/src/openforms/variables/api/views.py
@@ -8,13 +8,16 @@
from openforms.api.views import ListMixin
from openforms.forms.api.serializers import FormVariableSerializer
-from ..service import get_static_variables
+from ..service import get_registration_plugins_variables, get_static_variables
+from .registration_serializer import RegistrationPluginVariablesSerializer
@extend_schema_view(
get=extend_schema(
- summary=_("Get static data"),
- description=_("List the static data that will be associated with every form"),
+ summary=_("Get static variables"),
+ description=_(
+ "List the static variables that will be associated with every form"
+ ),
responses={
200: FormVariableSerializer(many=True),
403: ExceptionSerializer,
@@ -28,3 +31,24 @@ class StaticFormVariablesView(ListMixin, APIView):
def get_objects(self):
return get_static_variables()
+
+
+@extend_schema_view(
+ get=extend_schema(
+ summary=_("Get registration static variables"),
+ description=_(
+ "List the registration static variables that will be associated with every form"
+ ),
+ responses={
+ 200: FormVariableSerializer(many=True),
+ 403: ExceptionSerializer,
+ },
+ ),
+)
+class RegistrationPluginVariablesView(ListMixin, APIView):
+ authentication_classes = (authentication.SessionAuthentication,)
+ permission_classes = (permissions.IsAdminUser,)
+ serializer_class = RegistrationPluginVariablesSerializer
+
+ def get_objects(self):
+ return get_registration_plugins_variables()
diff --git a/src/openforms/variables/urls.py b/src/openforms/variables/urls.py
index 45c9c67c84..7161165c82 100644
--- a/src/openforms/variables/urls.py
+++ b/src/openforms/variables/urls.py
@@ -1,6 +1,6 @@
from django.urls import path
-from .api.views import StaticFormVariablesView
+from .api.views import RegistrationPluginVariablesView, StaticFormVariablesView
app_name = "variables"
@@ -9,5 +9,10 @@
"static",
StaticFormVariablesView.as_view(),
name="static",
- )
+ ),
+ path(
+ "registration",
+ RegistrationPluginVariablesView.as_view(),
+ name="registration",
+ ),
]
From 44bca039d34f96657130ce7b14a1cae26b6da7ed Mon Sep 17 00:00:00 2001
From: Viicos <65306057+Viicos@users.noreply.github.com>
Date: Wed, 13 Mar 2024 15:12:25 +0100
Subject: [PATCH 08/11] [#3688] Add tests for new endpoint
---
src/openforms/variables/tests/test_views.py | 101 +++++++++++++++++++-
1 file changed, 98 insertions(+), 3 deletions(-)
diff --git a/src/openforms/variables/tests/test_views.py b/src/openforms/variables/tests/test_views.py
index 5ffcb298f6..d3c03d55cd 100644
--- a/src/openforms/variables/tests/test_views.py
+++ b/src/openforms/variables/tests/test_views.py
@@ -7,9 +7,11 @@
from openforms.accounts.tests.factories import StaffUserFactory, UserFactory
from openforms.prefill.constants import IdentifierRoles
+from openforms.registrations.base import BasePlugin
+from openforms.registrations.registry import Registry as RegistrationRegistry
from openforms.variables.base import BaseStaticVariable
from openforms.variables.constants import FormVariableDataTypes
-from openforms.variables.registry import Registry
+from openforms.variables.registry import Registry as VariableRegistry
class GetStaticVariablesViewTest(APITestCase):
@@ -52,10 +54,12 @@ class DemoNow(BaseStaticVariable):
def get_initial_value(self, *args, **kwargs):
return "2021-07-16T21:15:00+00:00"
- register = Registry()
+ register = VariableRegistry()
register("now")(DemoNow)
- with patch("openforms.variables.service.register", new=register):
+ with patch(
+ "openforms.variables.service.static_variables_registry", new=register
+ ):
response = self.client.get(url)
self.assertEqual(status.HTTP_200_OK, response.status_code)
@@ -81,3 +85,94 @@ def get_initial_value(self, *args, **kwargs):
},
data[0],
)
+
+
+class GetRegistrationPluginVariablesViewTest(APITestCase):
+ def test_auth_required(self):
+ url = reverse(
+ "api:variables:registration",
+ )
+
+ response = self.client.get(url)
+
+ self.assertEqual(status.HTTP_403_FORBIDDEN, response.status_code)
+
+ def test_staff_required(self):
+ # add the permissions to verify we specifically check is_staff
+ user = UserFactory.create(
+ is_staff=False, user_permissions=["view_formvariable"]
+ )
+ url = reverse(
+ "api:variables:registration",
+ )
+
+ self.client.force_authenticate(user=user)
+ response = self.client.get(url)
+
+ self.assertEqual(status.HTTP_403_FORBIDDEN, response.status_code)
+
+ def test_get_registration_plugin_variable(self):
+ user = StaffUserFactory.create(user_permissions=["change_form"])
+ url = reverse(
+ "api:variables:registration",
+ )
+
+ self.client.force_authenticate(user=user)
+
+ class DemoNow(BaseStaticVariable):
+ name = "Now"
+ data_type = FormVariableDataTypes.string
+
+ def get_initial_value(self, *args, **kwargs):
+ return "demo initial value"
+
+ variable_registry = VariableRegistry()
+ variable_registry("now")(DemoNow)
+
+ class DemoRegistrationPlugin(BasePlugin):
+ verbose_name = "Demo verbose name"
+
+ def register_submission(self, submission, options):
+ pass
+
+ def get_variables_registry(self):
+ return variable_registry
+
+ plugin_registry = RegistrationRegistry()
+ plugin_registry("demo")(DemoRegistrationPlugin)
+
+ with patch(
+ "openforms.variables.service.registration_plugins_registry",
+ new=plugin_registry,
+ ):
+ response = self.client.get(url)
+
+ self.assertEqual(status.HTTP_200_OK, response.status_code)
+
+ data = response.data
+
+ self.assertEqual(1, len(data))
+ self.assertEqual(
+ {
+ "plugin_identifier": "demo",
+ "plugin_verbose_name": "Demo verbose name",
+ "plugin_variables": [
+ {
+ "form": None,
+ "form_definition": None,
+ "name": "Now",
+ "key": "now",
+ "source": "",
+ "service_fetch_configuration": None,
+ "prefill_plugin": "",
+ "prefill_attribute": "",
+ "prefill_identifier_role": IdentifierRoles.main,
+ "data_type": FormVariableDataTypes.string,
+ "data_format": "",
+ "is_sensitive_data": False,
+ "initial_value": "demo initial value",
+ }
+ ],
+ },
+ data[0],
+ )
From a8a34cd26e460ab12bf350d1e0951a2875fc26a6 Mon Sep 17 00:00:00 2001
From: Viicos <65306057+Viicos@users.noreply.github.com>
Date: Wed, 13 Mar 2024 16:55:01 +0100
Subject: [PATCH 09/11] [#3688] Add test for `save_and_raise` ctxmanager
---
.../contrib/objects_api/tests/test_backend.py | 69 +++++++++++++++++++
1 file changed, 69 insertions(+)
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 68b7069b39..b0987ad8ac 100644
--- a/src/openforms/registrations/contrib/objects_api/tests/test_backend.py
+++ b/src/openforms/registrations/contrib/objects_api/tests/test_backend.py
@@ -11,6 +11,10 @@
from openforms.payments.constants import PaymentStatus
from openforms.payments.tests.factories import SubmissionPaymentFactory
+from openforms.registrations.contrib.objects_api.models import (
+ ObjectsAPIRegistrationData,
+)
+from openforms.registrations.exceptions import RegistrationFailed
from openforms.submissions.tests.factories import (
SubmissionFactory,
SubmissionFileAttachmentFactory,
@@ -1253,3 +1257,68 @@ def test_submission_with_payment(self, m):
"public_order_ids": [],
},
)
+
+ def test_csv_creation_fails_pdf_still_saved(self, m: requests_mock.Mocker):
+ """Test the behavior when one of the API calls fails.
+
+ The exception should be caught, the intermediate data saved, and a
+ ``RegistrationFailed`` should be raised in the end.
+ """
+
+ submission = SubmissionFactory.from_components(
+ [
+ {
+ "key": "voornaam",
+ "registration": {
+ "attribute": RegistrationAttribute.initiator_voornamen,
+ },
+ },
+ ],
+ submitted_data={"voornaam": "Foo"},
+ )
+
+ pdf = generate_oas_component(
+ "documenten",
+ "schemas/EnkelvoudigInformatieObject",
+ url="https://documenten.nl/api/v1/enkelvoudiginformatieobjecten/1",
+ )
+
+ csv = generate_oas_component(
+ "documenten",
+ "schemas/EnkelvoudigInformatieObject",
+ url="https://documenten.nl/api/v1/enkelvoudiginformatieobjecten/2",
+ )
+
+ # OK on PDF request
+ m.post(
+ "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten",
+ status_code=201,
+ json=pdf,
+ additional_matcher=lambda req: req.json()["bestandsnaam"].endswith(".pdf"),
+ )
+
+ # Failure on CSV request
+ m.post(
+ "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten",
+ status_code=500,
+ json=csv,
+ additional_matcher=lambda req: "csv" in req.json()["bestandsnaam"],
+ )
+
+ plugin = ObjectsAPIRegistration(PLUGIN_IDENTIFIER)
+
+ with self.assertRaises(RegistrationFailed):
+ plugin.register_submission(
+ submission,
+ {
+ "upload_submission_csv": True,
+ "informatieobjecttype_submission_csv": "dummy",
+ },
+ )
+
+ registration_data = ObjectsAPIRegistrationData.objects.get(
+ submission=submission
+ )
+
+ self.assertEqual(registration_data.pdf_url, pdf["url"])
+ self.assertEqual(registration_data.csv_url, "")
From d4845fcad94b9e6f61e646d5f5a2641348cc2188 Mon Sep 17 00:00:00 2001
From: Viicos <65306057+Viicos@users.noreply.github.com>
Date: Thu, 14 Mar 2024 14:57:08 +0100
Subject: [PATCH 10/11] [#3688] PR feedback
---
src/openforms/registrations/base.py | 10 ++---
.../contrib/objects_api/plugin.py | 8 ++--
.../objects_api/registration_variables.py | 40 +++++++++----------
.../objects_api/submission_registration.py | 4 +-
.../variables/api/registration_serializer.py | 7 +++-
src/openforms/variables/api/views.py | 14 ++++---
src/openforms/variables/service.py | 33 ++-------------
7 files changed, 48 insertions(+), 68 deletions(-)
diff --git a/src/openforms/registrations/base.py b/src/openforms/registrations/base.py
index 3d3098fc0e..3ba4e0cfa9 100644
--- a/src/openforms/registrations/base.py
+++ b/src/openforms/registrations/base.py
@@ -10,9 +10,9 @@
from openforms.utils.mixins import JsonSchemaSerializerMixin
if TYPE_CHECKING:
- from openforms.plugins.registry import BaseRegistry
+ from openforms.forms.models import FormVariable
from openforms.submissions.models import Submission
- from openforms.variables.base import BaseStaticVariable
+
SerializerCls = type[serializers.Serializer]
@@ -68,8 +68,8 @@ def get_custom_templatetags_libraries(self) -> list[str]:
"""
return []
- def get_variables_registry(self) -> BaseRegistry[BaseStaticVariable] | None:
+ def get_variables(self) -> list[FormVariable]:
"""
- Return the static variables registry for this registration puglin.
+ Return the static variables for this registration plugin.
"""
- return None
+ return []
diff --git a/src/openforms/registrations/contrib/objects_api/plugin.py b/src/openforms/registrations/contrib/objects_api/plugin.py
index eaac4a8d3b..20645458cf 100644
--- a/src/openforms/registrations/contrib/objects_api/plugin.py
+++ b/src/openforms/registrations/contrib/objects_api/plugin.py
@@ -11,6 +11,7 @@
from openforms.registrations.utils import execute_unless_result_exists
from openforms.utils.date import get_today
+from openforms.variables.service import get_static_variables
from ...base import BasePlugin
from ...registry import register
@@ -18,11 +19,12 @@
from .client import get_objects_client
from .config import ObjectsAPIOptionsSerializer
from .models import ObjectsAPIConfig
-from .registration_variables import Registry, register as variables_registry
+from .registration_variables import register as variables_registry
from .submission_registration import HANDLER_MAPPING
from .typing import RegistrationOptions
if TYPE_CHECKING:
+ from openforms.forms.models import FormVariable
from openforms.submissions.models import Submission
@@ -139,5 +141,5 @@ def update_payment_status(
response.raise_for_status()
@override
- def get_variables_registry(self) -> Registry:
- return variables_registry
+ def get_variables(self) -> list[FormVariable]:
+ return get_static_variables(variables_registry=variables_registry)
diff --git a/src/openforms/registrations/contrib/objects_api/registration_variables.py b/src/openforms/registrations/contrib/objects_api/registration_variables.py
index 72c61e7d3a..e9b503d35e 100644
--- a/src/openforms/registrations/contrib/objects_api/registration_variables.py
+++ b/src/openforms/registrations/contrib/objects_api/registration_variables.py
@@ -30,11 +30,9 @@ class PdfUrl(BaseStaticVariable):
data_type = FormVariableDataTypes.string
def get_initial_value(self, submission: Submission | None = None):
- return (
- submission.objects_api_registration_data.pdf_url
- if submission is not None
- else None
- )
+ if submission is None:
+ return None
+ return submission.objects_api_registration_data.pdf_url
@register("csv_url")
@@ -43,11 +41,9 @@ class CsvUrl(BaseStaticVariable):
data_type = FormVariableDataTypes.string
def get_initial_value(self, submission: Submission | None = None):
- return (
- submission.objects_api_registration_data.csv_url
- if submission is not None
- else None
- )
+ if submission is None:
+ return None
+ return submission.objects_api_registration_data.csv_url
@register("attachment_urls")
@@ -56,11 +52,9 @@ class AttachmentUrls(BaseStaticVariable):
data_type = FormVariableDataTypes.array
def get_initial_value(self, submission: Submission | None = None):
- return (
- submission.objects_api_registration_data.attachment_urls
- if submission is not None
- else None
- )
+ if submission is None:
+ return None
+ return submission.objects_api_registration_data.attachment_urls
@register("payment_completed")
@@ -69,7 +63,9 @@ class PaymentCompleted(BaseStaticVariable):
data_type = FormVariableDataTypes.boolean
def get_initial_value(self, submission: Submission | None = None):
- return submission.payment_user_has_paid if submission is not None else None
+ if submission is None:
+ return None
+ return submission.payment_user_has_paid
@register("payment_amount")
@@ -78,7 +74,9 @@ class PaymentAmount(BaseStaticVariable):
data_type = FormVariableDataTypes.string
def get_initial_value(self, submission: Submission | None = None):
- return str(submission.payments.sum_amount()) if submission is not None else None
+ if submission is None:
+ return None
+ return str(submission.payments.sum_amount())
@register("payment_public_order_ids")
@@ -87,8 +85,6 @@ class PaymentPublicOrderIds(BaseStaticVariable):
data_type = FormVariableDataTypes.array
def get_initial_value(self, submission: Submission | None = None):
- return (
- submission.payments.get_completed_public_order_ids()
- if submission is not None
- else None
- )
+ if submission is None:
+ return None
+ return submission.payments.get_completed_public_order_ids()
diff --git a/src/openforms/registrations/contrib/objects_api/submission_registration.py b/src/openforms/registrations/contrib/objects_api/submission_registration.py
index 6151e6ed2a..59b350f418 100644
--- a/src/openforms/registrations/contrib/objects_api/submission_registration.py
+++ b/src/openforms/registrations/contrib/objects_api/submission_registration.py
@@ -42,7 +42,7 @@ def build_options(
return options
-def registration_submission_pdf(
+def register_submission_pdf(
submission: Submission,
options: RegistrationOptions,
documents_client: DocumentenClient,
@@ -191,7 +191,7 @@ def save_registration_data(
registration_data
):
if not registration_data.pdf_url:
- registration_data.pdf_url = registration_submission_pdf(
+ registration_data.pdf_url = register_submission_pdf(
submission, options, documents_client
)
diff --git a/src/openforms/variables/api/registration_serializer.py b/src/openforms/variables/api/registration_serializer.py
index 230310956f..cb1bef3432 100644
--- a/src/openforms/variables/api/registration_serializer.py
+++ b/src/openforms/variables/api/registration_serializer.py
@@ -11,13 +11,16 @@ class RegistrationPluginVariablesSerializer(serializers.Serializer):
plugin_identifier = serializers.CharField(
label=_("backend identifier"),
help_text=_("The identifier of the registration plugin."),
+ source="identifier",
)
plugin_verbose_name = serializers.CharField(
label=_("backend verbose name"),
help_text=_("The verbose name of the registration plugin."),
+ source="verbose_name",
)
- plugin_variables = serializers.ListSerializer(
- child=FormVariableSerializer(),
+ plugin_variables = FormVariableSerializer(
+ many=True,
label=_("variables"),
help_text=_("The list of corresponding registration variables."),
+ source="get_variables",
)
diff --git a/src/openforms/variables/api/views.py b/src/openforms/variables/api/views.py
index a6acc9d174..61c538a7d2 100644
--- a/src/openforms/variables/api/views.py
+++ b/src/openforms/variables/api/views.py
@@ -2,13 +2,15 @@
from drf_spectacular.utils import extend_schema, extend_schema_view
from rest_framework import authentication, permissions
+from rest_framework.response import Response
from rest_framework.views import APIView
from openforms.api.serializers import ExceptionSerializer
from openforms.api.views import ListMixin
from openforms.forms.api.serializers import FormVariableSerializer
+from openforms.registrations.registry import register as registration_plugins_registry
-from ..service import get_registration_plugins_variables, get_static_variables
+from ..service import get_static_variables
from .registration_serializer import RegistrationPluginVariablesSerializer
@@ -45,10 +47,12 @@ def get_objects(self):
},
),
)
-class RegistrationPluginVariablesView(ListMixin, APIView):
+class RegistrationPluginVariablesView(APIView):
authentication_classes = (authentication.SessionAuthentication,)
permission_classes = (permissions.IsAdminUser,)
- serializer_class = RegistrationPluginVariablesSerializer
- def get_objects(self):
- return get_registration_plugins_variables()
+ def get(self, request, *args, **kwargs):
+ serializer = RegistrationPluginVariablesSerializer(
+ registration_plugins_registry.iter_enabled_plugins(), many=True
+ )
+ return Response(serializer.data)
diff --git a/src/openforms/variables/service.py b/src/openforms/variables/service.py
index bb8421959a..bfc9ace622 100644
--- a/src/openforms/variables/service.py
+++ b/src/openforms/variables/service.py
@@ -2,18 +2,14 @@
Public Python API to access (static) form variables.
"""
-from typing import TypedDict
-
from openforms.forms.models import FormVariable
from openforms.plugins.registry import BaseRegistry
-from openforms.registrations.registry import register as registration_plugins_registry
from openforms.submissions.models import Submission
-from openforms.typing import StrOrPromise
from .base import BaseStaticVariable
from .registry import register_static_variable as static_variables_registry
-__all__ = ["get_static_variables", "get_registration_plugins_variables"]
+__all__ = ["get_static_variables"]
def get_static_variables(
@@ -34,30 +30,9 @@ def get_static_variables(
You should not rely on the order of returned variables, as they are registered in
the order the Django apps are loaded - and this may change without notice.
"""
+ if variables_registry is None:
+ variables_registry = static_variables_registry
return [
registered_variable.get_static_variable(submission=submission)
- for registered_variable in variables_registry or static_variables_registry
- ]
-
-
-class PluginInfo(TypedDict):
- plugin_identifier: str
- plugin_verbose_name: StrOrPromise
- plugin_variables: list[FormVariable]
-
-
-def get_registration_plugins_variables() -> list[PluginInfo]:
- """Return the registration plugin variables from the enabled plugins.
-
- The initial values aren't populated.
- """
-
- return [
- {
- "plugin_identifier": plugin.identifier,
- "plugin_verbose_name": plugin.verbose_name,
- "plugin_variables": get_static_variables(variables_registry=registry),
- }
- for plugin in registration_plugins_registry.iter_enabled_plugins()
- if (registry := plugin.get_variables_registry()) is not None
+ for registered_variable in variables_registry
]
From 86401a6aa76878408e5fdf50a9526d8115a0a9ff Mon Sep 17 00:00:00 2001
From: Viicos <65306057+Viicos@users.noreply.github.com>
Date: Thu, 14 Mar 2024 15:19:06 +0100
Subject: [PATCH 11/11] [#3688] Adapt tests
---
.../contrib/objects_api/tests/test_backend.py | 1246 +---------------
.../objects_api/tests/test_backend_v1.py | 1255 +++++++++++++++++
src/openforms/variables/tests/test_views.py | 12 +-
3 files changed, 1298 insertions(+), 1215 deletions(-)
create mode 100644 src/openforms/registrations/contrib/objects_api/tests/test_backend_v1.py
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 b0987ad8ac..0db4c9cd50 100644
--- a/src/openforms/registrations/contrib/objects_api/tests/test_backend.py
+++ b/src/openforms/registrations/contrib/objects_api/tests/test_backend.py
@@ -1,49 +1,29 @@
-import textwrap
-from datetime import date
from unittest.mock import patch
-from django.test import TestCase, override_settings
+from django.test import TestCase
import requests_mock
from zgw_consumers.constants import APITypes
from zgw_consumers.test import generate_oas_component
from zgw_consumers.test.factories import ServiceFactory
-from openforms.payments.constants import PaymentStatus
-from openforms.payments.tests.factories import SubmissionPaymentFactory
from openforms.registrations.contrib.objects_api.models import (
ObjectsAPIRegistrationData,
)
from openforms.registrations.exceptions import RegistrationFailed
-from openforms.submissions.tests.factories import (
- SubmissionFactory,
- SubmissionFileAttachmentFactory,
-)
+from openforms.submissions.tests.factories import SubmissionFactory
-from ....constants import RegistrationAttribute
from ..models import ObjectsAPIConfig
from ..plugin import PLUGIN_IDENTIFIER, ObjectsAPIRegistration
-def get_create_json(req, ctx):
- request_body = req.json()
- return {
- "url": "https://objecten.nl/api/v1/objects/1",
- "uuid": "095be615-a8ad-4c33-8e9c-c7612fbf6c9f",
- "type": request_body["type"],
- "record": {
- "index": 0,
- **request_body["record"], # typeVersion, data and startAt keys
- "endAt": None, # see https://github.com/maykinmedia/objects-api/issues/349
- "registrationAt": date.today().isoformat(),
- "correctionFor": 0,
- "correctedBy": "",
- },
- }
-
-
@requests_mock.Mocker()
class ObjectsAPIBackendTests(TestCase):
+ """General tests for the Objects API registration backend.
+
+ These tests don't depend on the options version (v1 or v2).
+ """
+
maxDiff = None
def setUp(self):
@@ -58,39 +38,6 @@ def setUp(self):
api_root="https://documenten.nl/api/v1/",
api_type=APITypes.drc,
),
- objecttype="https://objecttypen.nl/api/v1/objecttypes/1",
- objecttype_version=1,
- productaanvraag_type="terugbelnotitie",
- informatieobjecttype_submission_report="https://catalogi.nl/api/v1/informatieobjecttypen/1",
- informatieobjecttype_submission_csv="https://catalogi.nl/api/v1/informatieobjecttypen/4",
- informatieobjecttype_attachment="https://catalogi.nl/api/v1/informatieobjecttypen/3",
- organisatie_rsin="000000000",
- content_json=textwrap.dedent(
- """
- {
- "bron": {
- "naam": "Open Formulieren",
- "kenmerk": "{{ submission.kenmerk }}"
- },
- "type": "{{ productaanvraag_type }}",
- "aanvraaggegevens": {% json_summary %},
- "taal": "{{ submission.language_code }}",
- "betrokkenen": [
- {
- "inpBsn" : "{{ variables.auth_bsn }}",
- "rolOmschrijvingGeneriek" : "initiator"
- }
- ],
- "pdf": "{{ submission.pdf_url }}",
- "csv": "{{ submission.csv_url }}",
- "bijlagen": {% uploaded_attachment_urls %},
- "payment": {
- "completed": {% if payment.completed %}true{% else %}false{% endif %},
- "amount": {{ payment.amount }},
- "public_order_ids": {{ payment.public_order_ids }}
- }
- }"""
- ),
)
config_patcher = patch(
@@ -100,1208 +47,87 @@ def setUp(self):
self.mock_get_config = config_patcher.start()
self.addCleanup(config_patcher.stop)
- def test_submission_with_objects_api_backend_override_defaults(self, m):
- submission = SubmissionFactory.from_components(
- [
- {
- "key": "voornaam",
- "type": "textfield",
- "registration": {
- "attribute": RegistrationAttribute.initiator_voornamen,
- },
- },
- {
- "key": "achternaam",
- "type": "textfield",
- "registration": {
- "attribute": RegistrationAttribute.initiator_geslachtsnaam,
- },
- },
- {
- "key": "tussenvoegsel",
- "type": "textfield",
- "registration": {
- "attribute": RegistrationAttribute.initiator_tussenvoegsel,
- },
- },
- {
- "key": "geboortedatum",
- "type": "date",
- "registration": {
- "attribute": RegistrationAttribute.initiator_geboortedatum,
- },
- },
- {
- "key": "coordinaat",
- "type": "map",
- "registration": {
- "attribute": RegistrationAttribute.locatie_coordinaat,
- },
- },
- ],
- submitted_data={
- "voornaam": "Foo",
- "achternaam": "Bar",
- "tussenvoegsel": "de",
- "geboortedatum": "2000-12-31",
- "coordinaat": [52.36673378967122, 4.893164274470299],
- },
- language_code="en",
- )
- submission_step = submission.steps[0]
- assert submission_step.form_step
- step_slug = submission_step.form_step.slug
-
- objects_form_options = dict(
- objecttype="https://objecttypen.nl/api/v1/objecttypes/2",
- objecttype_version=2,
- productaanvraag_type="testproduct",
- informatieobjecttype_submission_report="https://catalogi.nl/api/v1/informatieobjecttypen/2",
- upload_submission_csv=True,
- informatieobjecttype_submission_csv="https://catalogi.nl/api/v1/informatieobjecttypen/5",
- organisatie_rsin="123456782",
- zaak_vertrouwelijkheidaanduiding="geheim",
- doc_vertrouwelijkheidaanduiding="geheim",
- )
-
- # Set up API mocks
- expected_document_result = generate_oas_component(
- "documenten",
- "schemas/EnkelvoudigInformatieObject",
- url="https://documenten.nl/api/v1/enkelvoudiginformatieobjecten/1",
- )
- expected_csv_document_result = generate_oas_component(
- "documenten",
- "schemas/EnkelvoudigInformatieObject",
- url="https://documenten.nl/api/v1/enkelvoudiginformatieobjecten/2",
- )
- m.post(
- "https://objecten.nl/api/v1/objects",
- status_code=201,
- json=get_create_json,
- )
- m.post(
- "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten",
- status_code=201,
- json=expected_document_result,
- )
- m.post(
- "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten",
- status_code=201,
- json=expected_csv_document_result,
- additional_matcher=lambda req: "csv" in req.json()["bestandsnaam"],
- )
- plugin = ObjectsAPIRegistration(PLUGIN_IDENTIFIER)
-
- # Run the registration
- result = plugin.register_submission(submission, objects_form_options)
-
- # check the requests made
- self.assertEqual(len(m.request_history), 3)
- document_create, csv_document_create, object_create = m.request_history
-
- with self.subTest("object create call and registration result"):
- submitted_object_data = object_create.json()
- expected_object_body = {
- "type": "https://objecttypen.nl/api/v1/objecttypes/2",
- "record": {
- "typeVersion": 2,
- "data": {
- "bron": {
- "naam": "Open Formulieren",
- "kenmerk": str(submission.uuid),
- },
- "type": "testproduct",
- "aanvraaggegevens": {
- step_slug: {
- "voornaam": "Foo",
- "achternaam": "Bar",
- "tussenvoegsel": "de",
- "geboortedatum": "2000-12-31",
- "coordinaat": [52.36673378967122, 4.893164274470299],
- }
- },
- "taal": "en",
- "betrokkenen": [
- {"inpBsn": "", "rolOmschrijvingGeneriek": "initiator"}
- ],
- "pdf": expected_document_result["url"],
- "csv": expected_csv_document_result["url"],
- "bijlagen": [],
- "payment": {
- "completed": False,
- "amount": 0,
- "public_order_ids": [],
- },
- },
- "startAt": date.today().isoformat(),
- "geometry": {
- "type": "Point",
- "coordinates": [52.36673378967122, 4.893164274470299],
- },
- },
- }
- self.assertEqual(object_create.method, "POST")
- self.assertEqual(object_create.url, "https://objecten.nl/api/v1/objects")
- self.assertEqual(submitted_object_data, expected_object_body)
-
- # NOTE: the backend adds additional metadata that is not in the request body.
- expected_result = {
- "url": "https://objecten.nl/api/v1/objects/1",
- "uuid": "095be615-a8ad-4c33-8e9c-c7612fbf6c9f",
- "type": objects_form_options["objecttype"],
- "record": {
- "index": 0,
- "typeVersion": objects_form_options["objecttype_version"],
- "data": submitted_object_data["record"]["data"],
- "geometry": {
- "type": "Point",
- "coordinates": [52.36673378967122, 4.893164274470299],
- },
- "startAt": date.today().isoformat(),
- "endAt": None,
- "registrationAt": date.today().isoformat(),
- "correctionFor": 0,
- "correctedBy": "",
- },
- }
- # Result is simply the created object
- self.assertEqual(result, expected_result)
-
- with self.subTest("Document create (PDF summary)"):
- document_create_body = document_create.json()
-
- self.assertEqual(document_create.method, "POST")
- self.assertEqual(
- document_create.url,
- "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten",
- )
- self.assertEqual(document_create_body["bronorganisatie"], "123456782")
- self.assertEqual(
- document_create_body["informatieobjecttype"],
- "https://catalogi.nl/api/v1/informatieobjecttypen/2",
- )
- self.assertEqual(
- document_create_body["vertrouwelijkheidaanduiding"],
- "geheim",
- )
-
- with self.subTest("Document create (CSV export)"):
- csv_document_create_body = csv_document_create.json()
-
- self.assertEqual(csv_document_create.method, "POST")
- self.assertEqual(
- csv_document_create.url,
- "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten",
- )
- # Overridden informatieobjecttype used
- self.assertEqual(
- csv_document_create_body["informatieobjecttype"],
- "https://catalogi.nl/api/v1/informatieobjecttypen/5",
- )
-
- def test_submission_with_objects_api_backend_override_defaults_upload_csv_default_type(
- self, m
- ):
- submission = SubmissionFactory.from_components(
- [
- {
- "key": "voornaam",
- "registration": {
- "attribute": RegistrationAttribute.initiator_voornamen,
- },
- },
- ],
- submitted_data={"voornaam": "Foo"},
- )
- objects_form_options = dict(
- objecttype="https://objecttypen.nl/api/v1/objecttypes/2",
- objecttype_version=2,
- productaanvraag_type="testproduct",
- informatieobjecttype_submission_report="https://catalogi.nl/api/v1/informatieobjecttypen/2",
- upload_submission_csv=True,
- organisatie_rsin="123456782",
- zaak_vertrouwelijkheidaanduiding="geheim",
- doc_vertrouwelijkheidaanduiding="geheim",
- )
-
- # Set up API mocks
- expected_document_result = generate_oas_component(
- "documenten",
- "schemas/EnkelvoudigInformatieObject",
- url="https://documenten.nl/api/v1/enkelvoudiginformatieobjecten/1",
- )
- expected_csv_document_result = generate_oas_component(
- "documenten",
- "schemas/EnkelvoudigInformatieObject",
- url="https://documenten.nl/api/v1/enkelvoudiginformatieobjecten/2",
- )
- m.post(
- "https://objecten.nl/api/v1/objects",
- status_code=201,
- json=get_create_json,
- )
- m.post(
- "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten",
- status_code=201,
- json=expected_document_result,
- )
- m.post(
- "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten",
- status_code=201,
- json=expected_csv_document_result,
- additional_matcher=lambda req: "csv" in req.json()["bestandsnaam"],
- )
- plugin = ObjectsAPIRegistration(PLUGIN_IDENTIFIER)
-
- # Run the registration
- plugin.register_submission(submission, objects_form_options)
-
- # check the requests made
- self.assertEqual(len(m.request_history), 3)
- document_create, csv_document_create, object_create = m.request_history
-
- with self.subTest("object create call and registration result"):
- submitted_object_data = object_create.json()
-
- self.assertEqual(
- submitted_object_data["type"],
- "https://objecttypen.nl/api/v1/objecttypes/2",
- )
- self.assertEqual(submitted_object_data["record"]["typeVersion"], 2)
- self.assertEqual(
- submitted_object_data["record"]["data"]["type"], "testproduct"
- )
-
- with self.subTest("Document create (PDF summary)"):
- document_create_body = document_create.json()
-
- self.assertEqual(document_create_body["bronorganisatie"], "123456782")
- self.assertEqual(
- document_create_body["informatieobjecttype"],
- "https://catalogi.nl/api/v1/informatieobjecttypen/2",
- )
- self.assertEqual(
- document_create_body["vertrouwelijkheidaanduiding"],
- "geheim",
- )
-
- with self.subTest("Document create (CSV export)"):
- csv_document_create_body = csv_document_create.json()
-
- self.assertEqual(
- csv_document_create.url,
- "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten",
- )
- # Default informatieobjecttype used
- self.assertEqual(
- csv_document_create_body["informatieobjecttype"],
- "https://catalogi.nl/api/v1/informatieobjecttypen/4",
- )
-
- def test_submission_with_objects_api_backend_override_defaults_do_not_upload_csv(
- self, m
- ):
- submission = SubmissionFactory.from_components(
- [
- {
- "key": "voornaam",
- "registration": {
- "attribute": RegistrationAttribute.initiator_voornamen,
- },
- },
- ],
- submitted_data={"voornaam": "Foo"},
- )
- # Set up API mocks
- expected_document_result = generate_oas_component(
- "documenten",
- "schemas/EnkelvoudigInformatieObject",
- url="https://documenten.nl/api/v1/enkelvoudiginformatieobjecten/1",
- )
- m.post(
- "https://objecten.nl/api/v1/objects",
- status_code=201,
- json=get_create_json,
- )
- m.post(
- "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten",
- status_code=201,
- json=expected_document_result,
- )
- plugin = ObjectsAPIRegistration(PLUGIN_IDENTIFIER)
-
- # Run the registration
- plugin.register_submission(submission, {"upload_submission_csv": False})
-
- # check the requests made
- self.assertEqual(len(m.request_history), 2)
- object_create = m.last_request
-
- with self.subTest("object create call and registration result"):
- submitted_object_data = object_create.json()
-
- self.assertEqual(submitted_object_data["record"]["data"]["csv"], "")
- self.assertEqual(
- submitted_object_data["record"]["data"]["pdf"],
- expected_document_result["url"],
- )
-
- def test_submission_with_objects_api_backend_missing_csv_iotype(self, m):
- submission = SubmissionFactory.create(with_report=True, completed=True)
- # Set up API mocks
- expected_document_result = generate_oas_component(
- "documenten",
- "schemas/EnkelvoudigInformatieObject",
- url="https://documenten.nl/api/v1/enkelvoudiginformatieobjecten/1",
- )
- m.post(
- "https://objecten.nl/api/v1/objects",
- status_code=201,
- json=get_create_json,
- )
- m.post(
- "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten",
- status_code=201,
- json=expected_document_result,
- )
- plugin = ObjectsAPIRegistration(PLUGIN_IDENTIFIER)
-
- # Run the registration
- plugin.register_submission(
- submission,
- {
- "upload_submission_csv": True,
- "informatieobjecttype_submission_csv": "",
- },
- )
-
- # check the requests made
- self.assertEqual(len(m.request_history), 2)
- object_create = m.last_request
-
- with self.subTest("object create call and registration result"):
- submitted_object_data = object_create.json()
+ def test_csv_creation_fails_pdf_still_saved(self, m: requests_mock.Mocker):
+ """Test the behavior when one of the API calls fails.
- self.assertEqual(submitted_object_data["record"]["data"]["csv"], "")
- self.assertEqual(
- submitted_object_data["record"]["data"]["pdf"],
- expected_document_result["url"],
- )
+ The exception should be caught, the intermediate data saved, and a
+ ``RegistrationFailed`` should be raised in the end.
+ """
- def test_submission_with_objects_api_backend_override_content_json(self, m):
submission = SubmissionFactory.from_components(
[
{
"key": "voornaam",
"type": "textfield",
- "registration": {
- "attribute": RegistrationAttribute.initiator_voornamen,
- },
- },
- ],
- submitted_data={"voornaam": "Foo"},
- language_code="en",
- )
- submission_step = submission.steps[0]
- assert submission_step.form_step
- step_slug = submission_step.form_step.slug
- objects_form_options = dict(
- upload_submission_csv=False,
- content_json=textwrap.dedent(
- """
- {
- "bron": {
- "naam": "Open Formulieren",
- "kenmerk": "{{ submission.kenmerk }}"
- },
- "type": "{{ productaanvraag_type }}",
- "aanvraaggegevens": {% json_summary %},
- "taal": "{{ submission.language_code }}"
- }
- """
- ),
- )
- # Set up API mocks
- expected_document_result = generate_oas_component(
- "documenten",
- "schemas/EnkelvoudigInformatieObject",
- url="https://documenten.nl/api/v1/enkelvoudiginformatieobjecten/1",
- )
- m.post(
- "https://objecten.nl/api/v1/objects",
- status_code=201,
- json=get_create_json,
- )
- m.post(
- "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten",
- status_code=201,
- json=expected_document_result,
- )
- plugin = ObjectsAPIRegistration(PLUGIN_IDENTIFIER)
-
- # Run the registration
- plugin.register_submission(submission, objects_form_options)
-
- # check the requests made
- self.assertEqual(len(m.request_history), 2)
-
- with self.subTest("object create call"):
- object_create = m.last_request
- expected_record_data = {
- "bron": {
- "naam": "Open Formulieren",
- "kenmerk": str(submission.uuid),
},
- "type": "terugbelnotitie",
- "aanvraaggegevens": {step_slug: {"voornaam": "Foo"}},
- "taal": "en",
- }
-
- self.assertEqual(object_create.url, "https://objecten.nl/api/v1/objects")
- object_create_body = object_create.json()
- self.assertEqual(object_create_body["record"]["data"], expected_record_data)
-
- def test_submission_with_objects_api_backend_use_config_defaults(self, m):
- submission = SubmissionFactory.from_components(
- [
- {
- "key": "voornaam",
- "registration": {
- "attribute": RegistrationAttribute.initiator_voornamen,
- },
- }
],
submitted_data={"voornaam": "Foo"},
- language_code="en",
)
- submission_step = submission.steps[0]
- assert submission_step.form_step
- step_slug = submission_step.form_step.slug
- # Set up API mocks
- expected_document_result = generate_oas_component(
+ pdf = generate_oas_component(
"documenten",
"schemas/EnkelvoudigInformatieObject",
url="https://documenten.nl/api/v1/enkelvoudiginformatieobjecten/1",
)
- expected_csv_document_result = generate_oas_component(
- "documenten",
- "schemas/EnkelvoudigInformatieObject",
- url="https://documenten.nl/api/v1/enkelvoudiginformatieobjecten/2",
- )
- m.post(
- "https://objecten.nl/api/v1/objects",
- status_code=201,
- json=get_create_json,
- )
- m.post(
- "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten",
- status_code=201,
- json=expected_document_result,
- )
- m.post(
- "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten",
- status_code=201,
- json=expected_csv_document_result,
- additional_matcher=lambda req: "csv" in req.json()["bestandsnaam"],
- )
- plugin = ObjectsAPIRegistration(PLUGIN_IDENTIFIER)
-
- # Run the registration, applying default options from the config
- plugin.register_submission(submission, {})
-
- # check the requests made
- self.assertEqual(len(m.request_history), 2)
- document_create, object_create = m.request_history
- with self.subTest("Document create (PDF summary)"):
- document_create_body = document_create.json()
-
- self.assertEqual(
- document_create.url,
- "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten",
- )
- self.assertEqual(document_create_body["taal"], "eng")
- self.assertEqual(document_create_body["bronorganisatie"], "000000000")
- self.assertEqual(
- document_create_body["informatieobjecttype"],
- "https://catalogi.nl/api/v1/informatieobjecttypen/1",
- )
- self.assertNotIn("vertrouwelijkheidaanduiding", document_create_body)
-
- with self.subTest("object create call"):
- object_create_body = object_create.json()
-
- expected_record_data = {
- "typeVersion": 1,
- "data": {
- "aanvraaggegevens": {step_slug: {"voornaam": "Foo"}},
- "betrokkenen": [
- {"inpBsn": "", "rolOmschrijvingGeneriek": "initiator"}
- ],
- "bijlagen": [],
- "bron": {
- "kenmerk": str(submission.uuid),
- "naam": "Open Formulieren",
- },
- "csv": "",
- "pdf": expected_document_result["url"],
- "taal": "en",
- "type": "terugbelnotitie",
- "payment": {
- "completed": False,
- "amount": 0,
- "public_order_ids": [],
- },
- },
- "startAt": date.today().isoformat(),
- }
- self.assertEqual(object_create.url, "https://objecten.nl/api/v1/objects")
- self.assertEqual(object_create_body["record"], expected_record_data)
-
- def test_submission_with_objects_api_backend_attachments(self, m):
- # Form.io configuration is irrelevant for this test, but normally you'd have
- # set up some file upload components.
- submission = SubmissionFactory.from_components(
- [],
- submitted_data={},
- language_code="en",
- completed=True,
- )
- submission_step = submission.steps[0]
- # Set up two attachments to upload to the documents API
- SubmissionFileAttachmentFactory.create(
- submission_step=submission_step, file_name="attachment1.jpg"
- )
- SubmissionFileAttachmentFactory.create(
- submission_step=submission_step, file_name="attachment2.jpg"
- )
-
- # Set up API mocks
- pdf, attachment1, attachment2 = [
- generate_oas_component(
- "documenten",
- "schemas/EnkelvoudigInformatieObject",
- url="https://documenten.nl/api/v1/enkelvoudiginformatieobjecten/1",
- ),
- generate_oas_component(
- "documenten",
- "schemas/EnkelvoudigInformatieObject",
- url="https://documenten.nl/api/v1/enkelvoudiginformatieobjecten/2",
- ),
- generate_oas_component(
- "documenten",
- "schemas/EnkelvoudigInformatieObject",
- url="https://documenten.nl/api/v1/enkelvoudiginformatieobjecten/3",
- ),
- ]
- m.post(
- "https://objecten.nl/api/v1/objects",
- status_code=201,
- json=get_create_json,
- )
- m.post(
- "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten",
- status_code=201,
- json=pdf,
- additional_matcher=lambda req: req.json()["bestandsnaam"].endswith(".pdf"),
- )
- m.post(
- "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten",
- status_code=201,
- json=attachment1,
- additional_matcher=lambda req: req.json()["bestandsnaam"]
- == "attachment1.jpg",
- )
- m.post(
- "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten",
- status_code=201,
- json=attachment2,
- additional_matcher=lambda req: req.json()["bestandsnaam"]
- == "attachment2.jpg",
- )
- plugin = ObjectsAPIRegistration(PLUGIN_IDENTIFIER)
-
- # Run the registration
- plugin.register_submission(submission, {})
-
- # check the requests made
- self.assertEqual(len(m.request_history), 4)
- (
- pdf_create,
- attachment1_create,
- attachment2_create,
- object_create,
- ) = m.request_history
-
- with self.subTest("object create call"):
- record_data = object_create.json()["record"]["data"]
-
- self.assertEqual(object_create.url, "https://objecten.nl/api/v1/objects")
- self.assertEqual(
- record_data["pdf"],
- "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten/1",
- )
- self.assertEqual(
- record_data["bijlagen"],
- [
- "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten/2",
- "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten/3",
- ],
- )
-
- with self.subTest("Document create (PDF summary)"):
- pdf_create_data = pdf_create.json()
-
- self.assertEqual(
- pdf_create.url,
- "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten",
- )
- self.assertEqual(pdf_create_data["bronorganisatie"], "000000000")
- self.assertEqual(
- pdf_create_data["informatieobjecttype"],
- "https://catalogi.nl/api/v1/informatieobjecttypen/1",
- )
- self.assertNotIn("vertrouwelijkheidaanduiding", pdf_create_data)
-
- with self.subTest("Document create (attachment 1)"):
- attachment1_create_data = attachment1_create.json()
-
- self.assertEqual(
- attachment1_create.url,
- "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten",
- )
- self.assertEqual(attachment1_create_data["bronorganisatie"], "000000000")
- self.assertEqual(attachment1_create_data["taal"], "eng")
- self.assertEqual(
- attachment1_create_data["informatieobjecttype"],
- "https://catalogi.nl/api/v1/informatieobjecttypen/3",
- )
- self.assertNotIn("vertrouwelijkheidaanduiding", attachment1_create_data)
-
- with self.subTest("Document create (attachment 2)"):
- attachment2_create_data = attachment2_create.json()
-
- self.assertEqual(
- attachment1_create.url,
- "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten",
- )
- self.assertEqual(attachment2_create_data["bronorganisatie"], "000000000")
- self.assertEqual(attachment2_create_data["taal"], "eng")
- self.assertEqual(
- attachment2_create_data["informatieobjecttype"],
- "https://catalogi.nl/api/v1/informatieobjecttypen/3",
- )
- self.assertNotIn("vertrouwelijkheidaanduiding", attachment2_create_data)
-
- def test_submission_with_objects_api_backend_attachments_specific_iotypen(self, m):
- submission = SubmissionFactory.from_components(
- [
- {
- "key": "field1",
- "type": "file",
- "registration": {
- "informatieobjecttype": "https://catalogi.nl/api/v1/informatieobjecttypen/10",
- },
- },
- {
- "key": "field2",
- "type": "file",
- "registration": {
- "informatieobjecttype": "",
- },
- },
- ],
- language_code="en",
- )
- submission_step = submission.steps[0]
- SubmissionFileAttachmentFactory.create(
- submission_step=submission_step,
- file_name="attachment1.jpg",
- form_key="field1",
- _component_configuration_path="components.0",
- )
- SubmissionFileAttachmentFactory.create(
- submission_step=submission_step,
- file_name="attachment2.jpg",
- form_key="field2",
- _component_configuration_path="component.1",
- )
-
- # Set up API mocks
- pdf, attachment1, attachment2 = [
- generate_oas_component(
- "documenten",
- "schemas/EnkelvoudigInformatieObject",
- url="https://documenten.nl/api/v1/enkelvoudiginformatieobjecten/1",
- ),
- generate_oas_component(
- "documenten",
- "schemas/EnkelvoudigInformatieObject",
- url="https://documenten.nl/api/v1/enkelvoudiginformatieobjecten/2",
- ),
- generate_oas_component(
- "documenten",
- "schemas/EnkelvoudigInformatieObject",
- url="https://documenten.nl/api/v1/enkelvoudiginformatieobjecten/3",
- ),
- ]
- m.post(
- "https://objecten.nl/api/v1/objects",
- status_code=201,
- json=get_create_json,
- )
- m.post(
- "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten",
- status_code=201,
- json=pdf,
- additional_matcher=lambda req: req.json()["bestandsnaam"].endswith(".pdf"),
- )
- m.post(
- "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten",
- status_code=201,
- json=attachment1,
- additional_matcher=lambda req: req.json()["bestandsnaam"]
- == "attachment1.jpg",
- )
- m.post(
- "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten",
- status_code=201,
- json=attachment2,
- additional_matcher=lambda req: req.json()["bestandsnaam"]
- == "attachment2.jpg",
- )
- plugin = ObjectsAPIRegistration(PLUGIN_IDENTIFIER)
-
- # Run the registration
- plugin.register_submission(submission, {})
-
- # check the requests made
- self.assertEqual(len(m.request_history), 4)
- attachment1_create = m.request_history[1]
- attachment2_create = m.request_history[2]
-
- with self.subTest("Document create (attachment 1)"):
- attachment1_create_data = attachment1_create.json()
-
- self.assertEqual(
- attachment1_create.url,
- "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten",
- )
- self.assertEqual(attachment1_create_data["bronorganisatie"], "000000000")
- self.assertEqual(attachment1_create_data["taal"], "eng")
- # Use override IOType
- self.assertEqual(
- attachment1_create_data["informatieobjecttype"],
- "https://catalogi.nl/api/v1/informatieobjecttypen/10",
- )
- self.assertNotIn("vertrouwelijkheidaanduiding", attachment1_create_data)
-
- with self.subTest("Document create (attachment 2)"):
- attachment2_create_data = attachment2_create.json()
-
- self.assertEqual(
- attachment1_create.url,
- "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten",
- )
- self.assertEqual(attachment2_create_data["bronorganisatie"], "000000000")
- self.assertEqual(attachment2_create_data["taal"], "eng")
- # Fallback to default IOType
- self.assertEqual(
- attachment2_create_data["informatieobjecttype"],
- "https://catalogi.nl/api/v1/informatieobjecttypen/3",
- )
- self.assertNotIn("vertrouwelijkheidaanduiding", attachment2_create_data)
-
- def test_submission_with_objects_api_backend_attachments_component_overwrites(
- self, m
- ):
- submission = SubmissionFactory.from_components(
- [
- {
- "key": "fileUpload",
- "type": "file",
- "registration": {
- "informatieobjecttype": "https://catalogi.nl/api/v1/informatieobjecttypen/10",
- "bronorganisatie": "123123123",
- "docVertrouwelijkheidaanduiding": "geheim",
- "titel": "A Custom Title",
- },
- },
- ],
- submitted_data={
- "fileUpload": [
- {
- "url": "http://server/api/v2/submissions/files/62f2ec22-da7d-4385-b719-b8637c1cd483",
- "data": {
- "url": "http://server/api/v2/submissions/files/62f2ec22-da7d-4385-b719-b8637c1cd483",
- "form": "",
- "name": "some-attachment.jpg",
- "size": 46114,
- "baseUrl": "http://server/form",
- "project": "",
- },
- "name": "my-image-12305610-2da4-4694-a341-ccb919c3d543.jpg",
- "size": 46114,
- "type": "image/jpg",
- "storage": "url",
- "originalName": "some-attachment.jpg",
- }
- ],
- },
- language_code="en",
- )
- submission_step = submission.steps[0]
- SubmissionFileAttachmentFactory.create(
- submission_step=submission_step,
- file_name="some-attachment.jpg",
- form_key="fileUpload",
- _component_configuration_path="components.0",
- )
-
- # Set up API mocks
- pdf, attachment = [
- generate_oas_component(
- "documenten",
- "schemas/EnkelvoudigInformatieObject",
- url="https://documenten.nl/api/v1/enkelvoudiginformatieobjecten/1",
- ),
- generate_oas_component(
- "documenten",
- "schemas/EnkelvoudigInformatieObject",
- url="https://documenten.nl/api/v1/enkelvoudiginformatieobjecten/2",
- ),
- ]
- m.post(
- "https://objecten.nl/api/v1/objects",
- status_code=201,
- json=get_create_json,
- )
+ # OK on PDF request
m.post(
"https://documenten.nl/api/v1/enkelvoudiginformatieobjecten",
status_code=201,
json=pdf,
additional_matcher=lambda req: req.json()["bestandsnaam"].endswith(".pdf"),
)
- m.post(
- "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten",
- status_code=201,
- json=attachment,
- additional_matcher=lambda req: req.json()["bestandsnaam"]
- == "some-attachment.jpg",
- )
- plugin = ObjectsAPIRegistration(PLUGIN_IDENTIFIER)
-
- # Run the registration
- plugin.register_submission(submission, {})
-
- # check the requests made
- self.assertEqual(len(m.request_history), 3)
- document_create_attachment = m.request_history[1]
-
- document_create_attachment_body = document_create_attachment.json()
- self.assertEqual(document_create_attachment.method, "POST")
- self.assertEqual(
- document_create_attachment.url,
- "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten",
- )
- # Check use of override settings
- self.assertEqual(
- document_create_attachment_body["informatieobjecttype"],
- "https://catalogi.nl/api/v1/informatieobjecttypen/10",
- )
- self.assertEqual(
- document_create_attachment_body["bronorganisatie"], "123123123"
- )
- self.assertEqual(
- document_create_attachment_body["vertrouwelijkheidaanduiding"], "geheim"
- )
- self.assertEqual(document_create_attachment_body["titel"], "A Custom Title")
- def test_submission_with_objects_api_backend_attachments_component_inside_fieldset_overwrites(
- self, m
- ):
- submission = SubmissionFactory.from_components(
- [
- {
- "key": "fieldset",
- "type": "fieldset",
- "label": "A fieldset",
- "components": [
- {
- "key": "fileUpload",
- "type": "file",
- "registration": {
- "informatieobjecttype": "https://catalogi.nl/api/v1/informatieobjecttypen/10",
- "bronorganisatie": "123123123",
- "docVertrouwelijkheidaanduiding": "geheim",
- "titel": "A Custom Title",
- },
- },
- ],
- },
- ],
- submitted_data={
- "fileUpload": [
- {
- "url": "http://server/api/v2/submissions/files/62f2ec22-da7d-4385-b719-b8637c1cd483",
- "data": {
- "url": "http://server/api/v2/submissions/files/62f2ec22-da7d-4385-b719-b8637c1cd483",
- "form": "",
- "name": "some-attachment.jpg",
- "size": 46114,
- "baseUrl": "http://server/form",
- "project": "",
- },
- "name": "my-image-12305610-2da4-4694-a341-ccb919c3d543.jpg",
- "size": 46114,
- "type": "image/jpg",
- "storage": "url",
- "originalName": "some-attachment.jpg",
- }
- ],
- },
- language_code="en",
- )
- submission_step = submission.steps[0]
- SubmissionFileAttachmentFactory.create(
- submission_step=submission_step,
- file_name="some-attachment.jpg",
- form_key="fileUpload",
- _component_configuration_path="components.0.components.0",
- )
- # Set up API mocks
- pdf, attachment = [
- generate_oas_component(
- "documenten",
- "schemas/EnkelvoudigInformatieObject",
- url="https://documenten.nl/api/v1/enkelvoudiginformatieobjecten/1",
- ),
- generate_oas_component(
- "documenten",
- "schemas/EnkelvoudigInformatieObject",
- url="https://documenten.nl/api/v1/enkelvoudiginformatieobjecten/2",
- ),
- ]
- m.post(
- "https://objecten.nl/api/v1/objects",
- status_code=201,
- json=get_create_json,
- )
+ # Failure on CSV request (which is dispatched after the PDF one)
m.post(
"https://documenten.nl/api/v1/enkelvoudiginformatieobjecten",
- status_code=201,
- json=pdf,
- additional_matcher=lambda req: req.json()["bestandsnaam"].endswith(".pdf"),
- )
- m.post(
- "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten",
- status_code=201,
- json=attachment,
- additional_matcher=lambda req: req.json()["bestandsnaam"]
- == "some-attachment.jpg",
- )
- plugin = ObjectsAPIRegistration(PLUGIN_IDENTIFIER)
-
- # Run the registration
- plugin.register_submission(submission, {})
-
- # check the requests made
- self.assertEqual(len(m.request_history), 3)
- document_create_attachment = m.request_history[1]
-
- document_create_attachment_body = document_create_attachment.json()
- self.assertEqual(document_create_attachment.method, "POST")
- self.assertEqual(
- document_create_attachment.url,
- "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten",
- )
- # Check use of override settings
- self.assertEqual(
- document_create_attachment_body["informatieobjecttype"],
- "https://catalogi.nl/api/v1/informatieobjecttypen/10",
- )
- self.assertEqual(
- document_create_attachment_body["bronorganisatie"], "123123123"
- )
- self.assertEqual(
- document_create_attachment_body["vertrouwelijkheidaanduiding"], "geheim"
- )
- self.assertEqual(document_create_attachment_body["titel"], "A Custom Title")
-
- @override_settings(ESCAPE_REGISTRATION_OUTPUT=True)
- def test_submission_with_objects_api_escapes_html(self, m):
- content_template = textwrap.dedent(
- """
- {
- "summary": {% json_summary %},
- "manual_variable": "{{ variables.voornaam }}"
- }
- """
- )
- submission = SubmissionFactory.from_components(
- [
- {
- "key": "voornaam",
- "type": "textfield",
- "registration": {
- "attribute": RegistrationAttribute.initiator_voornamen,
- },
- },
- ],
- submitted_data={"voornaam": ""},
- language_code="en",
+ status_code=500,
+ additional_matcher=lambda req: "csv" in req.json()["bestandsnaam"],
)
- submission_step = submission.steps[0]
- assert submission_step.form_step
- step_slug = submission_step.form_step.slug
- # Set up API mocks
- expected_document_result = generate_oas_component(
- "documenten",
- "schemas/EnkelvoudigInformatieObject",
- url="https://documenten.nl/api/v1/enkelvoudiginformatieobjecten/1",
- )
- m.post(
- "https://objecten.nl/api/v1/objects",
- status_code=201,
- json=get_create_json,
- )
- m.post(
- "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten",
- status_code=201,
- json=expected_document_result,
- )
plugin = ObjectsAPIRegistration(PLUGIN_IDENTIFIER)
- # Run the registration
- plugin.register_submission(
- submission,
- {
- "content_json": content_template,
- "upload_submission_csv": False,
- },
- )
-
- self.assertEqual(len(m.request_history), 2)
-
- object_create = m.last_request
- expected_record_data = {
- "summary": {
- step_slug: {
- "voornaam": "<script>alert();</script>",
- },
- },
- "manual_variable": "<script>alert();</script>",
- }
- object_create_body = object_create.json()
- posted_record_data = object_create_body["record"]["data"]
- self.assertEqual(object_create.method, "POST")
- self.assertEqual(object_create.url, "https://objecten.nl/api/v1/objects")
- self.assertEqual(posted_record_data, expected_record_data)
-
- def test_submission_with_payment(self, m):
- submission = SubmissionFactory.from_components(
- [
+ with self.assertRaises(RegistrationFailed):
+ plugin.register_submission(
+ submission,
{
- "key": "test",
- "type": "textfield",
+ "upload_submission_csv": True,
+ "informatieobjecttype_submission_csv": "dummy",
},
- ],
- registration_success=True,
- submitted_data={"test": "some test data"},
- language_code="en",
- registration_result={
- "url": "https://objecten.nl/api/v1/objects/111-222-333"
- },
- )
- SubmissionPaymentFactory.create(
- submission=submission,
- status=PaymentStatus.started,
- amount=10,
- public_order_id="",
- )
-
- m.post(
- "https://objecten.nl/api/v1/objects",
- status_code=201,
- json=get_create_json,
- )
- m.post(
- "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten",
- status_code=201,
- json=generate_oas_component(
- "documenten",
- "schemas/EnkelvoudigInformatieObject",
- url="https://documenten.nl/api/v1/enkelvoudiginformatieobjecten/1",
- ),
- )
+ )
- plugin = ObjectsAPIRegistration(PLUGIN_IDENTIFIER)
- plugin.register_submission(
- submission,
- {},
+ registration_data = ObjectsAPIRegistrationData.objects.get(
+ submission=submission
)
- self.assertEqual(len(m.request_history), 2)
-
- object_create = m.last_request
- body = object_create.json()
-
- self.assertEqual(
- body["record"]["data"]["payment"],
- {
- "completed": False,
- "amount": 10.00,
- "public_order_ids": [],
- },
- )
+ self.assertEqual(registration_data.pdf_url, pdf["url"])
+ self.assertEqual(registration_data.csv_url, "")
- def test_csv_creation_fails_pdf_still_saved(self, m: requests_mock.Mocker):
- """Test the behavior when one of the API calls fails.
+ def test_registration_works_after_failure(self, m: requests_mock.Mocker):
+ """Test the registration behavior after a failure.
- The exception should be caught, the intermediate data saved, and a
- ``RegistrationFailed`` should be raised in the end.
+ As a ``ObjectsAPIRegistrationData`` instance was already created, it shouldn't crash.
"""
submission = SubmissionFactory.from_components(
[
{
"key": "voornaam",
- "registration": {
- "attribute": RegistrationAttribute.initiator_voornamen,
- },
+ "type": "textfield",
},
],
submitted_data={"voornaam": "Foo"},
)
- pdf = generate_oas_component(
- "documenten",
- "schemas/EnkelvoudigInformatieObject",
- url="https://documenten.nl/api/v1/enkelvoudiginformatieobjecten/1",
+ # Instance created from a previous attempt
+ registration_data = ObjectsAPIRegistrationData.objects.create(
+ submission=submission, pdf_url="https://example.com"
)
- csv = generate_oas_component(
- "documenten",
- "schemas/EnkelvoudigInformatieObject",
- url="https://documenten.nl/api/v1/enkelvoudiginformatieobjecten/2",
- )
-
- # OK on PDF request
- m.post(
- "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten",
- status_code=201,
- json=pdf,
- additional_matcher=lambda req: req.json()["bestandsnaam"].endswith(".pdf"),
- )
-
- # Failure on CSV request
+ # Failure on CSV request (no mocks for the PDF one, we assume it was already created)
m.post(
"https://documenten.nl/api/v1/enkelvoudiginformatieobjecten",
status_code=500,
- json=csv,
additional_matcher=lambda req: "csv" in req.json()["bestandsnaam"],
)
@@ -1320,5 +146,5 @@ def test_csv_creation_fails_pdf_still_saved(self, m: requests_mock.Mocker):
submission=submission
)
- self.assertEqual(registration_data.pdf_url, pdf["url"])
+ self.assertEqual(registration_data.pdf_url, "https://example.com")
self.assertEqual(registration_data.csv_url, "")
diff --git a/src/openforms/registrations/contrib/objects_api/tests/test_backend_v1.py b/src/openforms/registrations/contrib/objects_api/tests/test_backend_v1.py
new file mode 100644
index 0000000000..266b1118e5
--- /dev/null
+++ b/src/openforms/registrations/contrib/objects_api/tests/test_backend_v1.py
@@ -0,0 +1,1255 @@
+import textwrap
+from datetime import date
+from unittest.mock import patch
+
+from django.test import TestCase, override_settings
+
+import requests_mock
+from zgw_consumers.constants import APITypes
+from zgw_consumers.test import generate_oas_component
+from zgw_consumers.test.factories import ServiceFactory
+
+from openforms.payments.constants import PaymentStatus
+from openforms.payments.tests.factories import SubmissionPaymentFactory
+from openforms.submissions.tests.factories import (
+ SubmissionFactory,
+ SubmissionFileAttachmentFactory,
+)
+
+from ....constants import RegistrationAttribute
+from ..models import ObjectsAPIConfig
+from ..plugin import PLUGIN_IDENTIFIER, ObjectsAPIRegistration
+
+
+def get_create_json(req, ctx):
+ request_body = req.json()
+ return {
+ "url": "https://objecten.nl/api/v1/objects/1",
+ "uuid": "095be615-a8ad-4c33-8e9c-c7612fbf6c9f",
+ "type": request_body["type"],
+ "record": {
+ "index": 0,
+ **request_body["record"], # typeVersion, data and startAt keys
+ "endAt": None, # see https://github.com/maykinmedia/objects-api/issues/349
+ "registrationAt": date.today().isoformat(),
+ "correctionFor": 0,
+ "correctedBy": "",
+ },
+ }
+
+
+@requests_mock.Mocker()
+class ObjectsAPIBackendV1Tests(TestCase):
+ maxDiff = None
+
+ def setUp(self):
+ super().setUp()
+
+ config = ObjectsAPIConfig(
+ objects_service=ServiceFactory.build(
+ api_root="https://objecten.nl/api/v1/",
+ api_type=APITypes.orc,
+ ),
+ drc_service=ServiceFactory.build(
+ api_root="https://documenten.nl/api/v1/",
+ api_type=APITypes.drc,
+ ),
+ objecttype="https://objecttypen.nl/api/v1/objecttypes/1",
+ objecttype_version=1,
+ productaanvraag_type="terugbelnotitie",
+ informatieobjecttype_submission_report="https://catalogi.nl/api/v1/informatieobjecttypen/1",
+ informatieobjecttype_submission_csv="https://catalogi.nl/api/v1/informatieobjecttypen/4",
+ informatieobjecttype_attachment="https://catalogi.nl/api/v1/informatieobjecttypen/3",
+ organisatie_rsin="000000000",
+ content_json=textwrap.dedent(
+ """
+ {
+ "bron": {
+ "naam": "Open Formulieren",
+ "kenmerk": "{{ submission.kenmerk }}"
+ },
+ "type": "{{ productaanvraag_type }}",
+ "aanvraaggegevens": {% json_summary %},
+ "taal": "{{ submission.language_code }}",
+ "betrokkenen": [
+ {
+ "inpBsn" : "{{ variables.auth_bsn }}",
+ "rolOmschrijvingGeneriek" : "initiator"
+ }
+ ],
+ "pdf": "{{ submission.pdf_url }}",
+ "csv": "{{ submission.csv_url }}",
+ "bijlagen": {% uploaded_attachment_urls %},
+ "payment": {
+ "completed": {% if payment.completed %}true{% else %}false{% endif %},
+ "amount": {{ payment.amount }},
+ "public_order_ids": {{ payment.public_order_ids }}
+ }
+ }"""
+ ),
+ )
+
+ 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_backend_override_defaults(self, m):
+ submission = SubmissionFactory.from_components(
+ [
+ {
+ "key": "voornaam",
+ "type": "textfield",
+ "registration": {
+ "attribute": RegistrationAttribute.initiator_voornamen,
+ },
+ },
+ {
+ "key": "achternaam",
+ "type": "textfield",
+ "registration": {
+ "attribute": RegistrationAttribute.initiator_geslachtsnaam,
+ },
+ },
+ {
+ "key": "tussenvoegsel",
+ "type": "textfield",
+ "registration": {
+ "attribute": RegistrationAttribute.initiator_tussenvoegsel,
+ },
+ },
+ {
+ "key": "geboortedatum",
+ "type": "date",
+ "registration": {
+ "attribute": RegistrationAttribute.initiator_geboortedatum,
+ },
+ },
+ {
+ "key": "coordinaat",
+ "type": "map",
+ "registration": {
+ "attribute": RegistrationAttribute.locatie_coordinaat,
+ },
+ },
+ ],
+ submitted_data={
+ "voornaam": "Foo",
+ "achternaam": "Bar",
+ "tussenvoegsel": "de",
+ "geboortedatum": "2000-12-31",
+ "coordinaat": [52.36673378967122, 4.893164274470299],
+ },
+ language_code="en",
+ )
+ submission_step = submission.steps[0]
+ assert submission_step.form_step
+ step_slug = submission_step.form_step.slug
+
+ objects_form_options = dict(
+ objecttype="https://objecttypen.nl/api/v1/objecttypes/2",
+ objecttype_version=2,
+ productaanvraag_type="testproduct",
+ informatieobjecttype_submission_report="https://catalogi.nl/api/v1/informatieobjecttypen/2",
+ upload_submission_csv=True,
+ informatieobjecttype_submission_csv="https://catalogi.nl/api/v1/informatieobjecttypen/5",
+ organisatie_rsin="123456782",
+ zaak_vertrouwelijkheidaanduiding="geheim",
+ doc_vertrouwelijkheidaanduiding="geheim",
+ )
+
+ # Set up API mocks
+ expected_document_result = generate_oas_component(
+ "documenten",
+ "schemas/EnkelvoudigInformatieObject",
+ url="https://documenten.nl/api/v1/enkelvoudiginformatieobjecten/1",
+ )
+ expected_csv_document_result = generate_oas_component(
+ "documenten",
+ "schemas/EnkelvoudigInformatieObject",
+ url="https://documenten.nl/api/v1/enkelvoudiginformatieobjecten/2",
+ )
+ m.post(
+ "https://objecten.nl/api/v1/objects",
+ status_code=201,
+ json=get_create_json,
+ )
+ m.post(
+ "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten",
+ status_code=201,
+ json=expected_document_result,
+ )
+ m.post(
+ "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten",
+ status_code=201,
+ json=expected_csv_document_result,
+ additional_matcher=lambda req: "csv" in req.json()["bestandsnaam"],
+ )
+ plugin = ObjectsAPIRegistration(PLUGIN_IDENTIFIER)
+
+ # Run the registration
+ result = plugin.register_submission(submission, objects_form_options)
+
+ # check the requests made
+ self.assertEqual(len(m.request_history), 3)
+ document_create, csv_document_create, object_create = m.request_history
+
+ with self.subTest("object create call and registration result"):
+ submitted_object_data = object_create.json()
+ expected_object_body = {
+ "type": "https://objecttypen.nl/api/v1/objecttypes/2",
+ "record": {
+ "typeVersion": 2,
+ "data": {
+ "bron": {
+ "naam": "Open Formulieren",
+ "kenmerk": str(submission.uuid),
+ },
+ "type": "testproduct",
+ "aanvraaggegevens": {
+ step_slug: {
+ "voornaam": "Foo",
+ "achternaam": "Bar",
+ "tussenvoegsel": "de",
+ "geboortedatum": "2000-12-31",
+ "coordinaat": [52.36673378967122, 4.893164274470299],
+ }
+ },
+ "taal": "en",
+ "betrokkenen": [
+ {"inpBsn": "", "rolOmschrijvingGeneriek": "initiator"}
+ ],
+ "pdf": expected_document_result["url"],
+ "csv": expected_csv_document_result["url"],
+ "bijlagen": [],
+ "payment": {
+ "completed": False,
+ "amount": 0,
+ "public_order_ids": [],
+ },
+ },
+ "startAt": date.today().isoformat(),
+ "geometry": {
+ "type": "Point",
+ "coordinates": [52.36673378967122, 4.893164274470299],
+ },
+ },
+ }
+ self.assertEqual(object_create.method, "POST")
+ self.assertEqual(object_create.url, "https://objecten.nl/api/v1/objects")
+ self.assertEqual(submitted_object_data, expected_object_body)
+
+ # NOTE: the backend adds additional metadata that is not in the request body.
+ expected_result = {
+ "url": "https://objecten.nl/api/v1/objects/1",
+ "uuid": "095be615-a8ad-4c33-8e9c-c7612fbf6c9f",
+ "type": objects_form_options["objecttype"],
+ "record": {
+ "index": 0,
+ "typeVersion": objects_form_options["objecttype_version"],
+ "data": submitted_object_data["record"]["data"],
+ "geometry": {
+ "type": "Point",
+ "coordinates": [52.36673378967122, 4.893164274470299],
+ },
+ "startAt": date.today().isoformat(),
+ "endAt": None,
+ "registrationAt": date.today().isoformat(),
+ "correctionFor": 0,
+ "correctedBy": "",
+ },
+ }
+ # Result is simply the created object
+ self.assertEqual(result, expected_result)
+
+ with self.subTest("Document create (PDF summary)"):
+ document_create_body = document_create.json()
+
+ self.assertEqual(document_create.method, "POST")
+ self.assertEqual(
+ document_create.url,
+ "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten",
+ )
+ self.assertEqual(document_create_body["bronorganisatie"], "123456782")
+ self.assertEqual(
+ document_create_body["informatieobjecttype"],
+ "https://catalogi.nl/api/v1/informatieobjecttypen/2",
+ )
+ self.assertEqual(
+ document_create_body["vertrouwelijkheidaanduiding"],
+ "geheim",
+ )
+
+ with self.subTest("Document create (CSV export)"):
+ csv_document_create_body = csv_document_create.json()
+
+ self.assertEqual(csv_document_create.method, "POST")
+ self.assertEqual(
+ csv_document_create.url,
+ "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten",
+ )
+ # Overridden informatieobjecttype used
+ self.assertEqual(
+ csv_document_create_body["informatieobjecttype"],
+ "https://catalogi.nl/api/v1/informatieobjecttypen/5",
+ )
+
+ def test_submission_with_objects_api_backend_override_defaults_upload_csv_default_type(
+ self, m
+ ):
+ submission = SubmissionFactory.from_components(
+ [
+ {
+ "key": "voornaam",
+ "registration": {
+ "attribute": RegistrationAttribute.initiator_voornamen,
+ },
+ },
+ ],
+ submitted_data={"voornaam": "Foo"},
+ )
+ objects_form_options = dict(
+ objecttype="https://objecttypen.nl/api/v1/objecttypes/2",
+ objecttype_version=2,
+ productaanvraag_type="testproduct",
+ informatieobjecttype_submission_report="https://catalogi.nl/api/v1/informatieobjecttypen/2",
+ upload_submission_csv=True,
+ organisatie_rsin="123456782",
+ zaak_vertrouwelijkheidaanduiding="geheim",
+ doc_vertrouwelijkheidaanduiding="geheim",
+ )
+
+ # Set up API mocks
+ expected_document_result = generate_oas_component(
+ "documenten",
+ "schemas/EnkelvoudigInformatieObject",
+ url="https://documenten.nl/api/v1/enkelvoudiginformatieobjecten/1",
+ )
+ expected_csv_document_result = generate_oas_component(
+ "documenten",
+ "schemas/EnkelvoudigInformatieObject",
+ url="https://documenten.nl/api/v1/enkelvoudiginformatieobjecten/2",
+ )
+ m.post(
+ "https://objecten.nl/api/v1/objects",
+ status_code=201,
+ json=get_create_json,
+ )
+ m.post(
+ "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten",
+ status_code=201,
+ json=expected_document_result,
+ )
+ m.post(
+ "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten",
+ status_code=201,
+ json=expected_csv_document_result,
+ additional_matcher=lambda req: "csv" in req.json()["bestandsnaam"],
+ )
+ plugin = ObjectsAPIRegistration(PLUGIN_IDENTIFIER)
+
+ # Run the registration
+ plugin.register_submission(submission, objects_form_options)
+
+ # check the requests made
+ self.assertEqual(len(m.request_history), 3)
+ document_create, csv_document_create, object_create = m.request_history
+
+ with self.subTest("object create call and registration result"):
+ submitted_object_data = object_create.json()
+
+ self.assertEqual(
+ submitted_object_data["type"],
+ "https://objecttypen.nl/api/v1/objecttypes/2",
+ )
+ self.assertEqual(submitted_object_data["record"]["typeVersion"], 2)
+ self.assertEqual(
+ submitted_object_data["record"]["data"]["type"], "testproduct"
+ )
+
+ with self.subTest("Document create (PDF summary)"):
+ document_create_body = document_create.json()
+
+ self.assertEqual(document_create_body["bronorganisatie"], "123456782")
+ self.assertEqual(
+ document_create_body["informatieobjecttype"],
+ "https://catalogi.nl/api/v1/informatieobjecttypen/2",
+ )
+ self.assertEqual(
+ document_create_body["vertrouwelijkheidaanduiding"],
+ "geheim",
+ )
+
+ with self.subTest("Document create (CSV export)"):
+ csv_document_create_body = csv_document_create.json()
+
+ self.assertEqual(
+ csv_document_create.url,
+ "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten",
+ )
+ # Default informatieobjecttype used
+ self.assertEqual(
+ csv_document_create_body["informatieobjecttype"],
+ "https://catalogi.nl/api/v1/informatieobjecttypen/4",
+ )
+
+ def test_submission_with_objects_api_backend_override_defaults_do_not_upload_csv(
+ self, m
+ ):
+ submission = SubmissionFactory.from_components(
+ [
+ {
+ "key": "voornaam",
+ "registration": {
+ "attribute": RegistrationAttribute.initiator_voornamen,
+ },
+ },
+ ],
+ submitted_data={"voornaam": "Foo"},
+ )
+ # Set up API mocks
+ expected_document_result = generate_oas_component(
+ "documenten",
+ "schemas/EnkelvoudigInformatieObject",
+ url="https://documenten.nl/api/v1/enkelvoudiginformatieobjecten/1",
+ )
+ m.post(
+ "https://objecten.nl/api/v1/objects",
+ status_code=201,
+ json=get_create_json,
+ )
+ m.post(
+ "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten",
+ status_code=201,
+ json=expected_document_result,
+ )
+ plugin = ObjectsAPIRegistration(PLUGIN_IDENTIFIER)
+
+ # Run the registration
+ plugin.register_submission(submission, {"upload_submission_csv": False})
+
+ # check the requests made
+ self.assertEqual(len(m.request_history), 2)
+ object_create = m.last_request
+
+ with self.subTest("object create call and registration result"):
+ submitted_object_data = object_create.json()
+
+ self.assertEqual(submitted_object_data["record"]["data"]["csv"], "")
+ self.assertEqual(
+ submitted_object_data["record"]["data"]["pdf"],
+ expected_document_result["url"],
+ )
+
+ def test_submission_with_objects_api_backend_missing_csv_iotype(self, m):
+ submission = SubmissionFactory.create(with_report=True, completed=True)
+ # Set up API mocks
+ expected_document_result = generate_oas_component(
+ "documenten",
+ "schemas/EnkelvoudigInformatieObject",
+ url="https://documenten.nl/api/v1/enkelvoudiginformatieobjecten/1",
+ )
+ m.post(
+ "https://objecten.nl/api/v1/objects",
+ status_code=201,
+ json=get_create_json,
+ )
+ m.post(
+ "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten",
+ status_code=201,
+ json=expected_document_result,
+ )
+ plugin = ObjectsAPIRegistration(PLUGIN_IDENTIFIER)
+
+ # Run the registration
+ plugin.register_submission(
+ submission,
+ {
+ "upload_submission_csv": True,
+ "informatieobjecttype_submission_csv": "",
+ },
+ )
+
+ # check the requests made
+ self.assertEqual(len(m.request_history), 2)
+ object_create = m.last_request
+
+ with self.subTest("object create call and registration result"):
+ submitted_object_data = object_create.json()
+
+ self.assertEqual(submitted_object_data["record"]["data"]["csv"], "")
+ self.assertEqual(
+ submitted_object_data["record"]["data"]["pdf"],
+ expected_document_result["url"],
+ )
+
+ def test_submission_with_objects_api_backend_override_content_json(self, m):
+ submission = SubmissionFactory.from_components(
+ [
+ {
+ "key": "voornaam",
+ "type": "textfield",
+ "registration": {
+ "attribute": RegistrationAttribute.initiator_voornamen,
+ },
+ },
+ ],
+ submitted_data={"voornaam": "Foo"},
+ language_code="en",
+ )
+ submission_step = submission.steps[0]
+ assert submission_step.form_step
+ step_slug = submission_step.form_step.slug
+ objects_form_options = dict(
+ upload_submission_csv=False,
+ content_json=textwrap.dedent(
+ """
+ {
+ "bron": {
+ "naam": "Open Formulieren",
+ "kenmerk": "{{ submission.kenmerk }}"
+ },
+ "type": "{{ productaanvraag_type }}",
+ "aanvraaggegevens": {% json_summary %},
+ "taal": "{{ submission.language_code }}"
+ }
+ """
+ ),
+ )
+ # Set up API mocks
+ expected_document_result = generate_oas_component(
+ "documenten",
+ "schemas/EnkelvoudigInformatieObject",
+ url="https://documenten.nl/api/v1/enkelvoudiginformatieobjecten/1",
+ )
+ m.post(
+ "https://objecten.nl/api/v1/objects",
+ status_code=201,
+ json=get_create_json,
+ )
+ m.post(
+ "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten",
+ status_code=201,
+ json=expected_document_result,
+ )
+ plugin = ObjectsAPIRegistration(PLUGIN_IDENTIFIER)
+
+ # Run the registration
+ plugin.register_submission(submission, objects_form_options)
+
+ # check the requests made
+ self.assertEqual(len(m.request_history), 2)
+
+ with self.subTest("object create call"):
+ object_create = m.last_request
+ expected_record_data = {
+ "bron": {
+ "naam": "Open Formulieren",
+ "kenmerk": str(submission.uuid),
+ },
+ "type": "terugbelnotitie",
+ "aanvraaggegevens": {step_slug: {"voornaam": "Foo"}},
+ "taal": "en",
+ }
+
+ self.assertEqual(object_create.url, "https://objecten.nl/api/v1/objects")
+ object_create_body = object_create.json()
+ self.assertEqual(object_create_body["record"]["data"], expected_record_data)
+
+ def test_submission_with_objects_api_backend_use_config_defaults(self, m):
+ submission = SubmissionFactory.from_components(
+ [
+ {
+ "key": "voornaam",
+ "registration": {
+ "attribute": RegistrationAttribute.initiator_voornamen,
+ },
+ }
+ ],
+ submitted_data={"voornaam": "Foo"},
+ language_code="en",
+ )
+ submission_step = submission.steps[0]
+ assert submission_step.form_step
+ step_slug = submission_step.form_step.slug
+
+ # Set up API mocks
+ expected_document_result = generate_oas_component(
+ "documenten",
+ "schemas/EnkelvoudigInformatieObject",
+ url="https://documenten.nl/api/v1/enkelvoudiginformatieobjecten/1",
+ )
+ expected_csv_document_result = generate_oas_component(
+ "documenten",
+ "schemas/EnkelvoudigInformatieObject",
+ url="https://documenten.nl/api/v1/enkelvoudiginformatieobjecten/2",
+ )
+ m.post(
+ "https://objecten.nl/api/v1/objects",
+ status_code=201,
+ json=get_create_json,
+ )
+ m.post(
+ "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten",
+ status_code=201,
+ json=expected_document_result,
+ )
+ m.post(
+ "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten",
+ status_code=201,
+ json=expected_csv_document_result,
+ additional_matcher=lambda req: "csv" in req.json()["bestandsnaam"],
+ )
+ plugin = ObjectsAPIRegistration(PLUGIN_IDENTIFIER)
+
+ # Run the registration, applying default options from the config
+ plugin.register_submission(submission, {})
+
+ # check the requests made
+ self.assertEqual(len(m.request_history), 2)
+ document_create, object_create = m.request_history
+
+ with self.subTest("Document create (PDF summary)"):
+ document_create_body = document_create.json()
+
+ self.assertEqual(
+ document_create.url,
+ "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten",
+ )
+ self.assertEqual(document_create_body["taal"], "eng")
+ self.assertEqual(document_create_body["bronorganisatie"], "000000000")
+ self.assertEqual(
+ document_create_body["informatieobjecttype"],
+ "https://catalogi.nl/api/v1/informatieobjecttypen/1",
+ )
+ self.assertNotIn("vertrouwelijkheidaanduiding", document_create_body)
+
+ with self.subTest("object create call"):
+ object_create_body = object_create.json()
+
+ expected_record_data = {
+ "typeVersion": 1,
+ "data": {
+ "aanvraaggegevens": {step_slug: {"voornaam": "Foo"}},
+ "betrokkenen": [
+ {"inpBsn": "", "rolOmschrijvingGeneriek": "initiator"}
+ ],
+ "bijlagen": [],
+ "bron": {
+ "kenmerk": str(submission.uuid),
+ "naam": "Open Formulieren",
+ },
+ "csv": "",
+ "pdf": expected_document_result["url"],
+ "taal": "en",
+ "type": "terugbelnotitie",
+ "payment": {
+ "completed": False,
+ "amount": 0,
+ "public_order_ids": [],
+ },
+ },
+ "startAt": date.today().isoformat(),
+ }
+ self.assertEqual(object_create.url, "https://objecten.nl/api/v1/objects")
+ self.assertEqual(object_create_body["record"], expected_record_data)
+
+ def test_submission_with_objects_api_backend_attachments(self, m):
+ # Form.io configuration is irrelevant for this test, but normally you'd have
+ # set up some file upload components.
+ submission = SubmissionFactory.from_components(
+ [],
+ submitted_data={},
+ language_code="en",
+ completed=True,
+ )
+ submission_step = submission.steps[0]
+ # Set up two attachments to upload to the documents API
+ SubmissionFileAttachmentFactory.create(
+ submission_step=submission_step, file_name="attachment1.jpg"
+ )
+ SubmissionFileAttachmentFactory.create(
+ submission_step=submission_step, file_name="attachment2.jpg"
+ )
+
+ # Set up API mocks
+ pdf, attachment1, attachment2 = [
+ generate_oas_component(
+ "documenten",
+ "schemas/EnkelvoudigInformatieObject",
+ url="https://documenten.nl/api/v1/enkelvoudiginformatieobjecten/1",
+ ),
+ generate_oas_component(
+ "documenten",
+ "schemas/EnkelvoudigInformatieObject",
+ url="https://documenten.nl/api/v1/enkelvoudiginformatieobjecten/2",
+ ),
+ generate_oas_component(
+ "documenten",
+ "schemas/EnkelvoudigInformatieObject",
+ url="https://documenten.nl/api/v1/enkelvoudiginformatieobjecten/3",
+ ),
+ ]
+ m.post(
+ "https://objecten.nl/api/v1/objects",
+ status_code=201,
+ json=get_create_json,
+ )
+ m.post(
+ "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten",
+ status_code=201,
+ json=pdf,
+ additional_matcher=lambda req: req.json()["bestandsnaam"].endswith(".pdf"),
+ )
+ m.post(
+ "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten",
+ status_code=201,
+ json=attachment1,
+ additional_matcher=lambda req: req.json()["bestandsnaam"]
+ == "attachment1.jpg",
+ )
+ m.post(
+ "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten",
+ status_code=201,
+ json=attachment2,
+ additional_matcher=lambda req: req.json()["bestandsnaam"]
+ == "attachment2.jpg",
+ )
+ plugin = ObjectsAPIRegistration(PLUGIN_IDENTIFIER)
+
+ # Run the registration
+ plugin.register_submission(submission, {})
+
+ # check the requests made
+ self.assertEqual(len(m.request_history), 4)
+ (
+ pdf_create,
+ attachment1_create,
+ attachment2_create,
+ object_create,
+ ) = m.request_history
+
+ with self.subTest("object create call"):
+ record_data = object_create.json()["record"]["data"]
+
+ self.assertEqual(object_create.url, "https://objecten.nl/api/v1/objects")
+ self.assertEqual(
+ record_data["pdf"],
+ "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten/1",
+ )
+ self.assertEqual(
+ record_data["bijlagen"],
+ [
+ "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten/2",
+ "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten/3",
+ ],
+ )
+
+ with self.subTest("Document create (PDF summary)"):
+ pdf_create_data = pdf_create.json()
+
+ self.assertEqual(
+ pdf_create.url,
+ "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten",
+ )
+ self.assertEqual(pdf_create_data["bronorganisatie"], "000000000")
+ self.assertEqual(
+ pdf_create_data["informatieobjecttype"],
+ "https://catalogi.nl/api/v1/informatieobjecttypen/1",
+ )
+ self.assertNotIn("vertrouwelijkheidaanduiding", pdf_create_data)
+
+ with self.subTest("Document create (attachment 1)"):
+ attachment1_create_data = attachment1_create.json()
+
+ self.assertEqual(
+ attachment1_create.url,
+ "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten",
+ )
+ self.assertEqual(attachment1_create_data["bronorganisatie"], "000000000")
+ self.assertEqual(attachment1_create_data["taal"], "eng")
+ self.assertEqual(
+ attachment1_create_data["informatieobjecttype"],
+ "https://catalogi.nl/api/v1/informatieobjecttypen/3",
+ )
+ self.assertNotIn("vertrouwelijkheidaanduiding", attachment1_create_data)
+
+ with self.subTest("Document create (attachment 2)"):
+ attachment2_create_data = attachment2_create.json()
+
+ self.assertEqual(
+ attachment1_create.url,
+ "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten",
+ )
+ self.assertEqual(attachment2_create_data["bronorganisatie"], "000000000")
+ self.assertEqual(attachment2_create_data["taal"], "eng")
+ self.assertEqual(
+ attachment2_create_data["informatieobjecttype"],
+ "https://catalogi.nl/api/v1/informatieobjecttypen/3",
+ )
+ self.assertNotIn("vertrouwelijkheidaanduiding", attachment2_create_data)
+
+ def test_submission_with_objects_api_backend_attachments_specific_iotypen(self, m):
+ submission = SubmissionFactory.from_components(
+ [
+ {
+ "key": "field1",
+ "type": "file",
+ "registration": {
+ "informatieobjecttype": "https://catalogi.nl/api/v1/informatieobjecttypen/10",
+ },
+ },
+ {
+ "key": "field2",
+ "type": "file",
+ "registration": {
+ "informatieobjecttype": "",
+ },
+ },
+ ],
+ language_code="en",
+ )
+ submission_step = submission.steps[0]
+ SubmissionFileAttachmentFactory.create(
+ submission_step=submission_step,
+ file_name="attachment1.jpg",
+ form_key="field1",
+ _component_configuration_path="components.0",
+ )
+ SubmissionFileAttachmentFactory.create(
+ submission_step=submission_step,
+ file_name="attachment2.jpg",
+ form_key="field2",
+ _component_configuration_path="component.1",
+ )
+
+ # Set up API mocks
+ pdf, attachment1, attachment2 = [
+ generate_oas_component(
+ "documenten",
+ "schemas/EnkelvoudigInformatieObject",
+ url="https://documenten.nl/api/v1/enkelvoudiginformatieobjecten/1",
+ ),
+ generate_oas_component(
+ "documenten",
+ "schemas/EnkelvoudigInformatieObject",
+ url="https://documenten.nl/api/v1/enkelvoudiginformatieobjecten/2",
+ ),
+ generate_oas_component(
+ "documenten",
+ "schemas/EnkelvoudigInformatieObject",
+ url="https://documenten.nl/api/v1/enkelvoudiginformatieobjecten/3",
+ ),
+ ]
+ m.post(
+ "https://objecten.nl/api/v1/objects",
+ status_code=201,
+ json=get_create_json,
+ )
+ m.post(
+ "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten",
+ status_code=201,
+ json=pdf,
+ additional_matcher=lambda req: req.json()["bestandsnaam"].endswith(".pdf"),
+ )
+ m.post(
+ "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten",
+ status_code=201,
+ json=attachment1,
+ additional_matcher=lambda req: req.json()["bestandsnaam"]
+ == "attachment1.jpg",
+ )
+ m.post(
+ "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten",
+ status_code=201,
+ json=attachment2,
+ additional_matcher=lambda req: req.json()["bestandsnaam"]
+ == "attachment2.jpg",
+ )
+ plugin = ObjectsAPIRegistration(PLUGIN_IDENTIFIER)
+
+ # Run the registration
+ plugin.register_submission(submission, {})
+
+ # check the requests made
+ self.assertEqual(len(m.request_history), 4)
+ attachment1_create = m.request_history[1]
+ attachment2_create = m.request_history[2]
+
+ with self.subTest("Document create (attachment 1)"):
+ attachment1_create_data = attachment1_create.json()
+
+ self.assertEqual(
+ attachment1_create.url,
+ "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten",
+ )
+ self.assertEqual(attachment1_create_data["bronorganisatie"], "000000000")
+ self.assertEqual(attachment1_create_data["taal"], "eng")
+ # Use override IOType
+ self.assertEqual(
+ attachment1_create_data["informatieobjecttype"],
+ "https://catalogi.nl/api/v1/informatieobjecttypen/10",
+ )
+ self.assertNotIn("vertrouwelijkheidaanduiding", attachment1_create_data)
+
+ with self.subTest("Document create (attachment 2)"):
+ attachment2_create_data = attachment2_create.json()
+
+ self.assertEqual(
+ attachment1_create.url,
+ "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten",
+ )
+ self.assertEqual(attachment2_create_data["bronorganisatie"], "000000000")
+ self.assertEqual(attachment2_create_data["taal"], "eng")
+ # Fallback to default IOType
+ self.assertEqual(
+ attachment2_create_data["informatieobjecttype"],
+ "https://catalogi.nl/api/v1/informatieobjecttypen/3",
+ )
+ self.assertNotIn("vertrouwelijkheidaanduiding", attachment2_create_data)
+
+ def test_submission_with_objects_api_backend_attachments_component_overwrites(
+ self, m
+ ):
+ submission = SubmissionFactory.from_components(
+ [
+ {
+ "key": "fileUpload",
+ "type": "file",
+ "registration": {
+ "informatieobjecttype": "https://catalogi.nl/api/v1/informatieobjecttypen/10",
+ "bronorganisatie": "123123123",
+ "docVertrouwelijkheidaanduiding": "geheim",
+ "titel": "A Custom Title",
+ },
+ },
+ ],
+ submitted_data={
+ "fileUpload": [
+ {
+ "url": "http://server/api/v2/submissions/files/62f2ec22-da7d-4385-b719-b8637c1cd483",
+ "data": {
+ "url": "http://server/api/v2/submissions/files/62f2ec22-da7d-4385-b719-b8637c1cd483",
+ "form": "",
+ "name": "some-attachment.jpg",
+ "size": 46114,
+ "baseUrl": "http://server/form",
+ "project": "",
+ },
+ "name": "my-image-12305610-2da4-4694-a341-ccb919c3d543.jpg",
+ "size": 46114,
+ "type": "image/jpg",
+ "storage": "url",
+ "originalName": "some-attachment.jpg",
+ }
+ ],
+ },
+ language_code="en",
+ )
+ submission_step = submission.steps[0]
+ SubmissionFileAttachmentFactory.create(
+ submission_step=submission_step,
+ file_name="some-attachment.jpg",
+ form_key="fileUpload",
+ _component_configuration_path="components.0",
+ )
+
+ # Set up API mocks
+ pdf, attachment = [
+ generate_oas_component(
+ "documenten",
+ "schemas/EnkelvoudigInformatieObject",
+ url="https://documenten.nl/api/v1/enkelvoudiginformatieobjecten/1",
+ ),
+ generate_oas_component(
+ "documenten",
+ "schemas/EnkelvoudigInformatieObject",
+ url="https://documenten.nl/api/v1/enkelvoudiginformatieobjecten/2",
+ ),
+ ]
+ m.post(
+ "https://objecten.nl/api/v1/objects",
+ status_code=201,
+ json=get_create_json,
+ )
+ m.post(
+ "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten",
+ status_code=201,
+ json=pdf,
+ additional_matcher=lambda req: req.json()["bestandsnaam"].endswith(".pdf"),
+ )
+ m.post(
+ "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten",
+ status_code=201,
+ json=attachment,
+ additional_matcher=lambda req: req.json()["bestandsnaam"]
+ == "some-attachment.jpg",
+ )
+ plugin = ObjectsAPIRegistration(PLUGIN_IDENTIFIER)
+
+ # Run the registration
+ plugin.register_submission(submission, {})
+
+ # check the requests made
+ self.assertEqual(len(m.request_history), 3)
+ document_create_attachment = m.request_history[1]
+
+ document_create_attachment_body = document_create_attachment.json()
+ self.assertEqual(document_create_attachment.method, "POST")
+ self.assertEqual(
+ document_create_attachment.url,
+ "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten",
+ )
+ # Check use of override settings
+ self.assertEqual(
+ document_create_attachment_body["informatieobjecttype"],
+ "https://catalogi.nl/api/v1/informatieobjecttypen/10",
+ )
+ self.assertEqual(
+ document_create_attachment_body["bronorganisatie"], "123123123"
+ )
+ self.assertEqual(
+ document_create_attachment_body["vertrouwelijkheidaanduiding"], "geheim"
+ )
+ self.assertEqual(document_create_attachment_body["titel"], "A Custom Title")
+
+ def test_submission_with_objects_api_backend_attachments_component_inside_fieldset_overwrites(
+ self, m
+ ):
+ submission = SubmissionFactory.from_components(
+ [
+ {
+ "key": "fieldset",
+ "type": "fieldset",
+ "label": "A fieldset",
+ "components": [
+ {
+ "key": "fileUpload",
+ "type": "file",
+ "registration": {
+ "informatieobjecttype": "https://catalogi.nl/api/v1/informatieobjecttypen/10",
+ "bronorganisatie": "123123123",
+ "docVertrouwelijkheidaanduiding": "geheim",
+ "titel": "A Custom Title",
+ },
+ },
+ ],
+ },
+ ],
+ submitted_data={
+ "fileUpload": [
+ {
+ "url": "http://server/api/v2/submissions/files/62f2ec22-da7d-4385-b719-b8637c1cd483",
+ "data": {
+ "url": "http://server/api/v2/submissions/files/62f2ec22-da7d-4385-b719-b8637c1cd483",
+ "form": "",
+ "name": "some-attachment.jpg",
+ "size": 46114,
+ "baseUrl": "http://server/form",
+ "project": "",
+ },
+ "name": "my-image-12305610-2da4-4694-a341-ccb919c3d543.jpg",
+ "size": 46114,
+ "type": "image/jpg",
+ "storage": "url",
+ "originalName": "some-attachment.jpg",
+ }
+ ],
+ },
+ language_code="en",
+ )
+ submission_step = submission.steps[0]
+ SubmissionFileAttachmentFactory.create(
+ submission_step=submission_step,
+ file_name="some-attachment.jpg",
+ form_key="fileUpload",
+ _component_configuration_path="components.0.components.0",
+ )
+ # Set up API mocks
+ pdf, attachment = [
+ generate_oas_component(
+ "documenten",
+ "schemas/EnkelvoudigInformatieObject",
+ url="https://documenten.nl/api/v1/enkelvoudiginformatieobjecten/1",
+ ),
+ generate_oas_component(
+ "documenten",
+ "schemas/EnkelvoudigInformatieObject",
+ url="https://documenten.nl/api/v1/enkelvoudiginformatieobjecten/2",
+ ),
+ ]
+ m.post(
+ "https://objecten.nl/api/v1/objects",
+ status_code=201,
+ json=get_create_json,
+ )
+ m.post(
+ "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten",
+ status_code=201,
+ json=pdf,
+ additional_matcher=lambda req: req.json()["bestandsnaam"].endswith(".pdf"),
+ )
+ m.post(
+ "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten",
+ status_code=201,
+ json=attachment,
+ additional_matcher=lambda req: req.json()["bestandsnaam"]
+ == "some-attachment.jpg",
+ )
+ plugin = ObjectsAPIRegistration(PLUGIN_IDENTIFIER)
+
+ # Run the registration
+ plugin.register_submission(submission, {})
+
+ # check the requests made
+ self.assertEqual(len(m.request_history), 3)
+ document_create_attachment = m.request_history[1]
+
+ document_create_attachment_body = document_create_attachment.json()
+ self.assertEqual(document_create_attachment.method, "POST")
+ self.assertEqual(
+ document_create_attachment.url,
+ "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten",
+ )
+ # Check use of override settings
+ self.assertEqual(
+ document_create_attachment_body["informatieobjecttype"],
+ "https://catalogi.nl/api/v1/informatieobjecttypen/10",
+ )
+ self.assertEqual(
+ document_create_attachment_body["bronorganisatie"], "123123123"
+ )
+ self.assertEqual(
+ document_create_attachment_body["vertrouwelijkheidaanduiding"], "geheim"
+ )
+ self.assertEqual(document_create_attachment_body["titel"], "A Custom Title")
+
+ @override_settings(ESCAPE_REGISTRATION_OUTPUT=True)
+ def test_submission_with_objects_api_escapes_html(self, m):
+ content_template = textwrap.dedent(
+ """
+ {
+ "summary": {% json_summary %},
+ "manual_variable": "{{ variables.voornaam }}"
+ }
+ """
+ )
+ submission = SubmissionFactory.from_components(
+ [
+ {
+ "key": "voornaam",
+ "type": "textfield",
+ "registration": {
+ "attribute": RegistrationAttribute.initiator_voornamen,
+ },
+ },
+ ],
+ submitted_data={"voornaam": ""},
+ language_code="en",
+ )
+
+ submission_step = submission.steps[0]
+ assert submission_step.form_step
+ step_slug = submission_step.form_step.slug
+ # Set up API mocks
+ expected_document_result = generate_oas_component(
+ "documenten",
+ "schemas/EnkelvoudigInformatieObject",
+ url="https://documenten.nl/api/v1/enkelvoudiginformatieobjecten/1",
+ )
+ m.post(
+ "https://objecten.nl/api/v1/objects",
+ status_code=201,
+ json=get_create_json,
+ )
+ m.post(
+ "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten",
+ status_code=201,
+ json=expected_document_result,
+ )
+ plugin = ObjectsAPIRegistration(PLUGIN_IDENTIFIER)
+
+ # Run the registration
+ plugin.register_submission(
+ submission,
+ {
+ "content_json": content_template,
+ "upload_submission_csv": False,
+ },
+ )
+
+ self.assertEqual(len(m.request_history), 2)
+
+ object_create = m.last_request
+ expected_record_data = {
+ "summary": {
+ step_slug: {
+ "voornaam": "<script>alert();</script>",
+ },
+ },
+ "manual_variable": "<script>alert();</script>",
+ }
+ object_create_body = object_create.json()
+ posted_record_data = object_create_body["record"]["data"]
+ self.assertEqual(object_create.method, "POST")
+ self.assertEqual(object_create.url, "https://objecten.nl/api/v1/objects")
+ self.assertEqual(posted_record_data, expected_record_data)
+
+ def test_submission_with_payment(self, m):
+ submission = SubmissionFactory.from_components(
+ [
+ {
+ "key": "test",
+ "type": "textfield",
+ },
+ ],
+ registration_success=True,
+ submitted_data={"test": "some test data"},
+ language_code="en",
+ registration_result={
+ "url": "https://objecten.nl/api/v1/objects/111-222-333"
+ },
+ )
+ SubmissionPaymentFactory.create(
+ submission=submission,
+ status=PaymentStatus.started,
+ amount=10,
+ public_order_id="",
+ )
+
+ m.post(
+ "https://objecten.nl/api/v1/objects",
+ status_code=201,
+ json=get_create_json,
+ )
+ m.post(
+ "https://documenten.nl/api/v1/enkelvoudiginformatieobjecten",
+ status_code=201,
+ json=generate_oas_component(
+ "documenten",
+ "schemas/EnkelvoudigInformatieObject",
+ url="https://documenten.nl/api/v1/enkelvoudiginformatieobjecten/1",
+ ),
+ )
+
+ plugin = ObjectsAPIRegistration(PLUGIN_IDENTIFIER)
+ plugin.register_submission(
+ submission,
+ {},
+ )
+
+ self.assertEqual(len(m.request_history), 2)
+
+ object_create = m.last_request
+ body = object_create.json()
+
+ self.assertEqual(
+ body["record"]["data"]["payment"],
+ {
+ "completed": False,
+ "amount": 10.00,
+ "public_order_ids": [],
+ },
+ )
diff --git a/src/openforms/variables/tests/test_views.py b/src/openforms/variables/tests/test_views.py
index d3c03d55cd..3168aceb3f 100644
--- a/src/openforms/variables/tests/test_views.py
+++ b/src/openforms/variables/tests/test_views.py
@@ -6,12 +6,14 @@
from rest_framework.test import APITestCase
from openforms.accounts.tests.factories import StaffUserFactory, UserFactory
+from openforms.forms.models import FormVariable
from openforms.prefill.constants import IdentifierRoles
from openforms.registrations.base import BasePlugin
from openforms.registrations.registry import Registry as RegistrationRegistry
from openforms.variables.base import BaseStaticVariable
from openforms.variables.constants import FormVariableDataTypes
from openforms.variables.registry import Registry as VariableRegistry
+from openforms.variables.service import get_static_variables
class GetStaticVariablesViewTest(APITestCase):
@@ -126,8 +128,8 @@ class DemoNow(BaseStaticVariable):
def get_initial_value(self, *args, **kwargs):
return "demo initial value"
- variable_registry = VariableRegistry()
- variable_registry("now")(DemoNow)
+ variables_registry = VariableRegistry()
+ variables_registry("now")(DemoNow)
class DemoRegistrationPlugin(BasePlugin):
verbose_name = "Demo verbose name"
@@ -135,14 +137,14 @@ class DemoRegistrationPlugin(BasePlugin):
def register_submission(self, submission, options):
pass
- def get_variables_registry(self):
- return variable_registry
+ def get_variables(self) -> list[FormVariable]:
+ return get_static_variables(variables_registry=variables_registry)
plugin_registry = RegistrationRegistry()
plugin_registry("demo")(DemoRegistrationPlugin)
with patch(
- "openforms.variables.service.registration_plugins_registry",
+ "openforms.variables.api.views.registration_plugins_registry",
new=plugin_registry,
):
response = self.client.get(url)