From 5ea6f6cf3179739622586ef9c014dc23ce05dd28 Mon Sep 17 00:00:00 2001 From: vasileios Date: Fri, 26 Jul 2024 16:06:01 +0200 Subject: [PATCH] [#4396] First attempt to implement objects_api prefill plugin --- docs/configuration/prefill/index.rst | 1 + docs/developers/plugins/prefill_plugins.rst | 3 + src/openforms/conf/base.py | 1 + src/openforms/prefill/__init__.py | 2 +- .../prefill/contrib/objects_api/__init__.py | 3 + .../prefill/contrib/objects_api/apps.py | 12 ++ .../prefill/contrib/objects_api/constants.py | 19 +++ .../prefill/contrib/objects_api/plugin.py | 108 ++++++++++++++++++ .../submissions/models/submission.py | 6 + 9 files changed, 154 insertions(+), 1 deletion(-) create mode 100644 src/openforms/prefill/contrib/objects_api/__init__.py create mode 100644 src/openforms/prefill/contrib/objects_api/apps.py create mode 100644 src/openforms/prefill/contrib/objects_api/constants.py create mode 100644 src/openforms/prefill/contrib/objects_api/plugin.py diff --git a/docs/configuration/prefill/index.rst b/docs/configuration/prefill/index.rst index a090bbf023..410ec0edd1 100644 --- a/docs/configuration/prefill/index.rst +++ b/docs/configuration/prefill/index.rst @@ -12,3 +12,4 @@ Prefill plugins kvk stuf_bg suwinet + objects_api diff --git a/docs/developers/plugins/prefill_plugins.rst b/docs/developers/plugins/prefill_plugins.rst index e906ef3f5d..004929714e 100644 --- a/docs/developers/plugins/prefill_plugins.rst +++ b/docs/developers/plugins/prefill_plugins.rst @@ -25,6 +25,9 @@ You can find an example implementation in :mod:`openforms.prefill.contrib.demo`. Implementation -------------- +Plugins must be added to the INSTALLED_APPS :mod:`openforms.conf.base`. See the demo app as an example +("openforms.prefill.contrib.demo.apps.DemoApp") + Plugins must implement the interface from :class:`openforms.prefill.base.BasePlugin`. It's safe to use this as a base class. diff --git a/src/openforms/conf/base.py b/src/openforms/conf/base.py index bb02a95b16..691f2f47ae 100644 --- a/src/openforms/conf/base.py +++ b/src/openforms/conf/base.py @@ -228,6 +228,7 @@ "openforms.prefill.contrib.stufbg.apps.StufBgApp", "openforms.prefill.contrib.haalcentraal_brp.apps.HaalCentraalBRPApp", "openforms.prefill.contrib.suwinet.apps.SuwinetApp", + "openforms.prefill.contrib.objects_api.apps.ObjectsApiApp", "openforms.authentication", "openforms.authentication.contrib.demo.apps.DemoApp", "openforms.authentication.contrib.outage.apps.DemoOutageApp", diff --git a/src/openforms/prefill/__init__.py b/src/openforms/prefill/__init__.py index 177a8e13b4..c3c652a8f9 100644 --- a/src/openforms/prefill/__init__.py +++ b/src/openforms/prefill/__init__.py @@ -70,7 +70,7 @@ def invoke_plugin( plugin = register[plugin_id] if not plugin.is_enabled: raise PluginNotEnabled() - + breakpoint() try: values = plugin.get_prefill_values(submission, fields, identifier_role) except Exception as e: diff --git a/src/openforms/prefill/contrib/objects_api/__init__.py b/src/openforms/prefill/contrib/objects_api/__init__.py new file mode 100644 index 0000000000..7c438b5c34 --- /dev/null +++ b/src/openforms/prefill/contrib/objects_api/__init__.py @@ -0,0 +1,3 @@ +""" +Objects API prefill plugin. +""" diff --git a/src/openforms/prefill/contrib/objects_api/apps.py b/src/openforms/prefill/contrib/objects_api/apps.py new file mode 100644 index 0000000000..816875ae66 --- /dev/null +++ b/src/openforms/prefill/contrib/objects_api/apps.py @@ -0,0 +1,12 @@ +from django.apps import AppConfig +from django.utils.translation import gettext_lazy as _ + + +class ObjectsApiApp(AppConfig): + name = "openforms.prefill.contrib.objects_api" + label = "objects_api" + verbose_name = _("Objects API prefill plugin") + + def ready(self): + # register the plugin + from . import plugin # noqa diff --git a/src/openforms/prefill/contrib/objects_api/constants.py b/src/openforms/prefill/contrib/objects_api/constants.py new file mode 100644 index 0000000000..69a439d01c --- /dev/null +++ b/src/openforms/prefill/contrib/objects_api/constants.py @@ -0,0 +1,19 @@ +from django.db import models +from django.utils.translation import gettext_lazy as _ + + +class ObjectsAPIAttributes(models.TextChoices): + url = "url", _("Url") + uuid = "uuid", _("UUID") + type = "type", _("Type") + record_index = "record.index", _("Record > Index") + record_typeVersion = "record.typeVersion", _("Record > Type version") + + record_data_ = "", _("Record > Data") + + record_geometry = "record.geometry", _("Record > Geometry") + record_startAt = "record.startAt", _("Record > Start at") + record_endAt = "record.endAt", _("Record > End at") + record_registrationAt = "record.registrationAt", _("Record > Registration at") + record_correctionFor = "record.correctionFor", _("Record > Correction for") + record_correctedBy = "record_correctedBy", _("Record > Corrected by") diff --git a/src/openforms/prefill/contrib/objects_api/plugin.py b/src/openforms/prefill/contrib/objects_api/plugin.py new file mode 100644 index 0000000000..cd9cfb4f22 --- /dev/null +++ b/src/openforms/prefill/contrib/objects_api/plugin.py @@ -0,0 +1,108 @@ +import logging +from typing import Any, Iterable + +from click import option +from django.urls import reverse +from django.utils.translation import gettext_lazy as _ + +from glom import GlomError, glom + +from openforms.authentication.service import AuthAttribute +from openforms.registrations.contrib.objects_api.client import ( + get_objects_client, + get_objecttypes_client, +) +from openforms.registrations.contrib.objects_api.models import ObjectsAPIGroupConfig +from openforms.submissions.models import Submission +from openforms.typing import JSONEncodable +from ...base import BasePlugin +from ...constants import IdentifierRoles +from ...registry import register +from .constants import ObjectsAPIAttributes + +logger = logging.getLogger(__name__) + +PLUGIN_IDENTIFIER = "objects_api" + + +@register(PLUGIN_IDENTIFIER) +class ObjectsAPIPrefill(BasePlugin): + verbose_name = _("Objects API") + requires_auth = AuthAttribute.bsn + + @classmethod + def parse_schema_properties( + cls, schema, parent_key: str = "" + ) -> list[tuple[str, str]]: + properties = [] + + if schema["type"] == "object": + for prop, prop_schema in schema.get("properties", {}).items(): + full_key = f"{parent_key}.{prop}" if parent_key else prop + prop_type = prop_schema.get("type", "unknown") + properties.append((full_key, prop_type)) + if prop_type == "object" or ( + prop_type == "array" and "items" in prop_schema + ): + properties.extend( + cls.parse_schema_properties(prop_schema, full_key) + ) + elif schema["type"] == "array": + items_schema = schema.get("items", {}) + if isinstance(items_schema, dict): + properties.extend( + cls.parse_schema_properties(items_schema, f"{parent_key}[]") + ) + elif isinstance(items_schema, list): + for i, item_schema in enumerate(items_schema): + properties.extend( + cls.parse_schema_properties(item_schema, f"{parent_key}[{i}]") + ) + else: + properties.append((parent_key, schema["type"])) + + return properties + + @staticmethod + def get_available_attributes() -> Iterable[tuple[str, str]]: + # attrs = ObjectsAPIAttributes.choices + # attrs += dynamic_properties + pass + + @classmethod + def get_prefill_values( + cls, + submission: Submission, + attributes: list[str], + identifier_role: IdentifierRoles = IdentifierRoles.main, + ) -> dict[str, JSONEncodable]: + # for testing purposes, this will be defined in the modal by the user + # frontend is not ready yet + options = submission.registration_backend.options + config = ObjectsAPIGroupConfig.objects.get(pk=options["objects_api_group"]) + + if object_uuid := submission.initial_data_reference: + with get_objecttypes_client(config) as objecttypes_client: + objecttype = objecttypes_client.get_objecttype_version( + options["objecttype"], options["objecttype_version"] + ) + + if objecttype["status"] != "published": + logger.warning( + "object type '%s' is not published yet", objecttype["url"] + ) + return {} + + if json_schema := objecttype.get("jsonSchema"): + properties = cls.parse_schema_properties(json_schema) + + return {} + + @classmethod + def get_co_sign_values( + cls, submission: Submission, identifier: str + ) -> tuple[dict[str, Any], str]: + pass + + def check_config(self): + pass diff --git a/src/openforms/submissions/models/submission.py b/src/openforms/submissions/models/submission.py index c3092db628..a6e19c5e98 100644 --- a/src/openforms/submissions/models/submission.py +++ b/src/openforms/submissions/models/submission.py @@ -360,6 +360,12 @@ class Meta: ), ] + # for testing purposes-need the initial_data_reference + # should be removed + def __init__(self, *args: Any, **kwargs: Any) -> None: + super().__init__(*args, **kwargs) + self.initial_data_reference = "ac1fa3f8-fb2a-4fcb-b715-d480aceeda10" + def __str__(self): return _("{pk} - started on {started}").format( pk=self.pk or _("(unsaved)"),