Skip to content

Commit

Permalink
Merge pull request #3954 from open-formulieren/feature/3688-refactor-…
Browse files Browse the repository at this point in the history
…reg-plugin

[#3688] Refactor Objects API plugin to allow for v2
  • Loading branch information
Viicos authored Mar 6, 2024
2 parents eda569e + 32485fd commit a9fe217
Show file tree
Hide file tree
Showing 13 changed files with 346 additions and 222 deletions.
43 changes: 11 additions & 32 deletions src/openforms/contrib/objects_api/helpers.py
Original file line number Diff line number Diff line change
@@ -1,41 +1,20 @@
from typing import TypedDict
from typing import Any

from openforms.contrib.zgw.clients.utils import ( # TODO: should be moved somewhere more generic?
get_today,
)
from openforms.registrations.constants import REGISTRATION_ATTRIBUTE
from openforms.submissions.mapping import MappingConfig, apply_data_mapping
from openforms.submissions.models import Submission

from .rendering import render_to_json


class PrepareOptions(TypedDict):
content_json: str # Django template syntax
objecttype: str # URL to the objecttype in the Objecttypes API
objecttype_version: int
from openforms.utils.date import get_today


def prepare_data_for_registration(
submission: Submission,
context: dict,
options: PrepareOptions,
object_mapping: MappingConfig,
) -> dict:
# Prepare the submission data for sending it to the Objects API. This requires
# rendering the configured JSON template and running some basic checks for
# security and validity, before throwing it over the fence to the Objects API.

record_data = render_to_json(options["content_json"], context)

object_data = {
"type": options["objecttype"],
record_data: dict[str, Any],
objecttype: str,
objecttype_version: int,
) -> dict[str, Any]:
"""Prepare the submission data for sending it to the Objects API."""

return {
"type": objecttype,
"record": {
"typeVersion": options["objecttype_version"],
"typeVersion": objecttype_version,
"data": record_data,
"startAt": get_today(),
},
}
apply_data_mapping(submission, object_mapping, REGISTRATION_ATTRIBUTE, object_data)

return object_data
3 changes: 1 addition & 2 deletions src/openforms/contrib/zgw/clients/documenten.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@
from zgw_consumers.nlx import NLXClient

from openforms.translations.utils import to_iso639_2b

from .utils import get_today
from openforms.utils.date import get_today

DocumentStatus: TypeAlias = Literal[
"in_bewerking",
Expand Down
8 changes: 0 additions & 8 deletions src/openforms/contrib/zgw/clients/utils.py

This file was deleted.

3 changes: 2 additions & 1 deletion src/openforms/contrib/zgw/clients/zaken.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@
from furl import furl
from zgw_consumers.nlx import NLXClient

from openforms.utils.date import get_today

from .catalogi import CatalogiClient
from .utils import get_today

logger = logging.getLogger(__name__)

Expand Down
1 change: 1 addition & 0 deletions src/openforms/registrations/contrib/objects_api/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ def clean(self) -> None:
)

def apply_defaults_to(self, options):
options.setdefault("version", 1)
options.setdefault("objecttype", self.objecttype)
options.setdefault("objecttype_version", self.objecttype_version)
options.setdefault("productaanvraag_type", self.productaanvraag_type)
Expand Down
188 changes: 31 additions & 157 deletions src/openforms/registrations/contrib/objects_api/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,42 +5,27 @@
from django.urls import reverse
from django.utils.translation import gettext_lazy as _

from openforms.contrib.objects_api.helpers import prepare_data_for_registration
from openforms.contrib.objects_api.rendering import render_to_json
from openforms.contrib.zgw.clients import DocumentenClient
from openforms.contrib.zgw.clients.utils import get_today
from openforms.contrib.zgw.service import (
create_attachment_document,
create_csv_document,
create_report_document,
)
from typing_extensions import override

from openforms.registrations.utils import execute_unless_result_exists
from openforms.submissions.exports import create_submission_export
from openforms.submissions.mapping import SKIP, FieldConf
from openforms.submissions.models import Submission, SubmissionReport
from openforms.variables.utils import get_variables_for_context
from openforms.submissions.models import Submission
from openforms.utils.date import get_today

from ...base import BasePlugin
from ...constants import RegistrationAttribute
from ...registry import register
from .checks import check_config
from .client import get_documents_client, get_objects_client
from .client import get_objects_client
from .config import ObjectsAPIOptionsSerializer
from .models import ObjectsAPIConfig
from .utils import get_payment_context_data
from .submission_registration import HANDLER_MAPPING
from .typing import RegistrationOptions

PLUGIN_IDENTIFIER = "objects_api"

logger = logging.getLogger(__name__)


def _point_coordinate(value):
if not value or not isinstance(value, list) or len(value) != 2:
return SKIP
return {"type": "Point", "coordinates": [value[0], value[1]]}


def build_options(plugin_options: dict, key_mapping: dict) -> dict:
def build_options(plugin_options: RegistrationOptions, key_mapping: dict) -> dict:
"""
Construct options from plugin options dict, allowing renaming of keys
"""
Expand All @@ -57,105 +42,25 @@ class ObjectsAPIRegistration(BasePlugin):
verbose_name = _("Objects API registration")
configuration_options = ObjectsAPIOptionsSerializer

object_mapping = {
"record.geometry": FieldConf(
RegistrationAttribute.locatie_coordinaat, transform=_point_coordinate
),
}

@override
def register_submission(
self, submission: Submission, options: dict
self, submission: Submission, options: RegistrationOptions
) -> dict[str, Any]:
"""Register a submission using the ObjectsAPI backend
"""Register a submission using the Objects API backend.
Depending on the options version (legacy or mapped variables), the payload
will be created differently. The actual logic lives in the ``submission_registration`` submodule.
"""

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."""
config = ObjectsAPIConfig.get_solo()
assert isinstance(config, ObjectsAPIConfig)
config.apply_defaults_to(options)

# 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 = build_options(
options,
{
"informatieobjecttype": "informatieobjecttype_submission_report",
"organisatie_rsin": "organisatie_rsin",
"doc_vertrouwelijkheidaanduiding": "doc_vertrouwelijkheidaanduiding",
},
)

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.
attachments = []
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
)
attachments.append(attachment_document["url"])
handler = HANDLER_MAPPING[options["version"]]

# Create the CSV submission export, if requested.
# If no CSV is being uploaded, then `assert csv_url == ""` applies.
csv_url = register_submission_csv(submission, options, documents_client)

context = {
"_submission": submission,
"productaanvraag_type": options["productaanvraag_type"],
"payment": get_payment_context_data(submission),
"variables": get_variables_for_context(submission),
# Github issue #661, nested for namespacing note: other templates and context expose all submission
# variables in the top level namespace, but that is due for refactor
"submission": {
"public_reference": submission.public_registration_reference,
"kenmerk": str(submission.uuid),
"language_code": submission.language_code,
"uploaded_attachment_urls": attachments,
"pdf_url": document["url"],
"csv_url": csv_url,
},
}

object_data = prepare_data_for_registration(
submission, context, options, self.object_mapping
object_data = handler.get_object_data(
submission=submission,
options=options,
)

with get_objects_client() as objects_client:
Expand All @@ -167,9 +72,11 @@ def register_submission(

return response

@override
def check_config(self):
check_config()

@override
def get_config_actions(self):
return [
(
Expand All @@ -181,13 +88,17 @@ def get_config_actions(self):
),
]

@override
def get_custom_templatetags_libraries(self) -> list[str]:
prefix = "openforms.registrations.contrib.objects_api.templatetags.registrations.contrib"
return [
f"{prefix}.objects_api.json_tags",
]

def update_payment_status(self, submission: "Submission", options: dict) -> None:
@override
def update_payment_status(
self, submission: Submission, options: RegistrationOptions
) -> None:
config = ObjectsAPIConfig.get_solo()
assert isinstance(config, ObjectsAPIConfig)
config.apply_defaults_to(options)
Expand All @@ -198,17 +109,14 @@ def update_payment_status(self, submission: "Submission", options: dict) -> None
)
return

context = {
"variables": get_variables_for_context(submission),
"payment": get_payment_context_data(submission),
}

updated_record_data = render_to_json(
options["payment_status_update_json"], context
handler = HANDLER_MAPPING[options["version"]]
updated_object_data = handler.get_update_payment_status_data(
submission, options
)

updated_object_data = {
"record": {
"data": updated_record_data,
"data": updated_object_data,
"startAt": get_today(),
},
}
Expand All @@ -221,37 +129,3 @@ def update_payment_status(self, submission: "Submission", options: dict) -> None
headers={"Content-Crs": "EPSG:4326"},
)
response.raise_for_status()


def register_submission_csv(
submission: Submission,
options: dict,
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"]
Loading

0 comments on commit a9fe217

Please sign in to comment.