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/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..3ba4e0cfa9 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,8 +10,10 @@
from openforms.utils.mixins import JsonSchemaSerializerMixin
if TYPE_CHECKING:
+ from openforms.forms.models import FormVariable
from openforms.submissions.models import Submission
+
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(self) -> list[FormVariable]:
+ """
+ Return the static variables for this registration plugin.
+ """
+ return []
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,
+ )
diff --git a/src/openforms/registrations/contrib/objects_api/plugin.py b/src/openforms/registrations/contrib/objects_api/plugin.py
index fa74f37135..20645458cf 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,8 +10,8 @@
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 openforms.variables.service import get_static_variables
from ...base import BasePlugin
from ...registry import register
@@ -17,9 +19,15 @@
from .client import get_objects_client
from .config import ObjectsAPIOptionsSerializer
from .models import ObjectsAPIConfig
+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
+
+
PLUGIN_IDENTIFIER = "objects_api"
logger = logging.getLogger(__name__)
@@ -58,6 +66,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,
@@ -129,3 +139,7 @@ def update_payment_status(
headers={"Content-Crs": "EPSG:4326"},
)
response.raise_for_status()
+
+ @override
+ 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
new file mode 100644
index 0000000000..e9b503d35e
--- /dev/null
+++ b/src/openforms/registrations/contrib/objects_api/registration_variables.py
@@ -0,0 +1,90 @@
+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):
+ if submission is None:
+ return None
+ return submission.objects_api_registration_data.pdf_url
+
+
+@register("csv_url")
+class CsvUrl(BaseStaticVariable):
+ name = _("CSV Url")
+ data_type = FormVariableDataTypes.string
+
+ def get_initial_value(self, submission: Submission | None = None):
+ if submission is None:
+ return None
+ return submission.objects_api_registration_data.csv_url
+
+
+@register("attachment_urls")
+class AttachmentUrls(BaseStaticVariable):
+ name = _("Attachment Urls")
+ data_type = FormVariableDataTypes.array
+
+ def get_initial_value(self, submission: Submission | None = None):
+ if submission is None:
+ return None
+ return submission.objects_api_registration_data.attachment_urls
+
+
+@register("payment_completed")
+class PaymentCompleted(BaseStaticVariable):
+ name = _("Payment completed")
+ data_type = FormVariableDataTypes.boolean
+
+ def get_initial_value(self, submission: Submission | None = None):
+ if submission is None:
+ return None
+ return submission.payment_user_has_paid
+
+
+@register("payment_amount")
+class PaymentAmount(BaseStaticVariable):
+ name = _("Payment amount")
+ data_type = FormVariableDataTypes.string
+
+ def get_initial_value(self, submission: Submission | None = None):
+ if submission is None:
+ return None
+ return str(submission.payments.sum_amount())
+
+
+@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):
+ 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 0e1868e5b9..59b350f418 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 register_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 = register_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,
},
}
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..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,45 +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.submissions.tests.factories import (
- SubmissionFactory,
- SubmissionFileAttachmentFactory,
+from openforms.registrations.contrib.objects_api.models import (
+ ObjectsAPIRegistrationData,
)
+from openforms.registrations.exceptions import RegistrationFailed
+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):
@@ -54,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(
@@ -96,1160 +47,104 @@ 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()
+ 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(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",
- )
+ 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_defaults_upload_csv_default_type(
- self, m
- ):
submission = SubmissionFactory.from_components(
[
{
"key": "voornaam",
- "registration": {
- "attribute": RegistrationAttribute.initiator_voornamen,
- },
+ "type": "textfield",
},
],
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(
+ 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,
- )
+
+ # OK on PDF request
m.post(
"https://documenten.nl/api/v1/enkelvoudiginformatieobjecten",
status_code=201,
- json=expected_document_result,
+ json=pdf,
+ additional_matcher=lambda req: req.json()["bestandsnaam"].endswith(".pdf"),
)
+
+ # Failure on CSV request (which is dispatched after the PDF one)
m.post(
"https://documenten.nl/api/v1/enkelvoudiginformatieobjecten",
- status_code=201,
- json=expected_csv_document_result,
+ status_code=500,
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",
- )
+ plugin = ObjectsAPIRegistration(PLUGIN_IDENTIFIER)
- def test_submission_with_objects_api_backend_override_defaults_do_not_upload_csv(
- self, m
- ):
- submission = SubmissionFactory.from_components(
- [
+ with self.assertRaises(RegistrationFailed):
+ plugin.register_submission(
+ submission,
{
- "key": "voornaam",
- "registration": {
- "attribute": RegistrationAttribute.initiator_voornamen,
- },
+ "upload_submission_csv": True,
+ "informatieobjecttype_submission_csv": "dummy",
},
- ],
- 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": "",
- },
+ registration_data = ObjectsAPIRegistrationData.objects.get(
+ submission=submission
)
- # check the requests made
- self.assertEqual(len(m.request_history), 2)
- object_create = m.last_request
+ self.assertEqual(registration_data.pdf_url, pdf["url"])
+ self.assertEqual(registration_data.csv_url, "")
- with self.subTest("object create call and registration result"):
- submitted_object_data = object_create.json()
+ def test_registration_works_after_failure(self, m: requests_mock.Mocker):
+ """Test the registration behavior after a failure.
- self.assertEqual(submitted_object_data["record"]["data"]["csv"], "")
- self.assertEqual(
- submitted_object_data["record"]["data"]["pdf"],
- expected_document_result["url"],
- )
+ As a ``ObjectsAPIRegistrationData`` instance was already created, it shouldn't crash.
+ """
- 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",
+ # Instance created from a previous attempt
+ registration_data = ObjectsAPIRegistrationData.objects.create(
+ submission=submission, pdf_url="https://example.com"
)
- 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,
- )
+ # 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=201,
- json=expected_csv_document_result,
+ status_code=500,
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",
- },
- },
+ with self.assertRaises(RegistrationFailed):
+ plugin.register_submission(
+ submission,
{
- "key": "field2",
- "type": "file",
- "registration": {
- "informatieobjecttype": "",
- },
+ "upload_submission_csv": True,
+ "informatieobjecttype_submission_csv": "dummy",
},
- ],
- 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",
+ registration_data = ObjectsAPIRegistrationData.objects.get(
+ submission=submission
)
- # 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": [],
- },
- )
+ 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/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: ...
diff --git a/src/openforms/variables/api/registration_serializer.py b/src/openforms/variables/api/registration_serializer.py
new file mode 100644
index 0000000000..cb1bef3432
--- /dev/null
+++ b/src/openforms/variables/api/registration_serializer.py
@@ -0,0 +1,26 @@
+# 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."),
+ 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 = 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 d6221e129a..61c538a7d2 100644
--- a/src/openforms/variables/api/views.py
+++ b/src/openforms/variables/api/views.py
@@ -2,19 +2,24 @@
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_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 +33,26 @@ 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(APIView):
+ authentication_classes = (authentication.SessionAuthentication,)
+ permission_classes = (permissions.IsAdminUser,)
+
+ 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/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):
diff --git a/src/openforms/variables/service.py b/src/openforms/variables/service.py
index 50d0c6ea3f..bfc9ace622 100644
--- a/src/openforms/variables/service.py
+++ b/src/openforms/variables/service.py
@@ -3,14 +3,20 @@
"""
from openforms.forms.models import FormVariable
+from openforms.plugins.registry import BaseRegistry
from openforms.submissions.models import Submission
-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"]
-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 +24,15 @@ 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.
"""
+ if variables_registry is None:
+ variables_registry = static_variables_registry
return [
registered_variable.get_static_variable(submission=submission)
- for registered_variable in register
+ for registered_variable in variables_registry
]
diff --git a/src/openforms/variables/tests/test_views.py b/src/openforms/variables/tests/test_views.py
index 5ffcb298f6..3168aceb3f 100644
--- a/src/openforms/variables/tests/test_views.py
+++ b/src/openforms/variables/tests/test_views.py
@@ -6,10 +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
+from openforms.variables.registry import Registry as VariableRegistry
+from openforms.variables.service import get_static_variables
class GetStaticVariablesViewTest(APITestCase):
@@ -52,10 +56,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 +87,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"
+
+ variables_registry = VariableRegistry()
+ variables_registry("now")(DemoNow)
+
+ class DemoRegistrationPlugin(BasePlugin):
+ verbose_name = "Demo verbose name"
+
+ def register_submission(self, submission, options):
+ pass
+
+ 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.api.views.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],
+ )
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",
+ ),
]