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/openapi.yaml b/src/openapi.yaml index fe69ae7eb4..25ad7756c3 100644 --- a/src/openapi.yaml +++ b/src/openapi.yaml @@ -3586,6 +3586,123 @@ paths: $ref: '#/components/headers/X-Is-Form-Designer' Content-Language: $ref: '#/components/headers/Content-Language' + /api/v2/prefill/plugins/objects-api/objecttypes: + get: + operationId: prefill_plugins_objects_api_objecttypes_list + description: List the available prefill objecttypes for Objects API plugin. + summary: List available objecttypes for Objects API + parameters: + - in: query + name: objects_api_group + schema: + type: string + description: Which Objects API group to use. + tags: + - registration + security: + - cookieAuth: [] + responses: + '200': + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Objecttype' + 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/prefill/plugins/objects-api/objecttypes/{objects_api_objecttype_uuid}/versions: + get: + operationId: prefill_plugins_objects_api_objecttypes_versions_list + description: List the available prefill objecttype versions for Objects API + plugin. + summary: List available objecttype versions for Objects API + parameters: + - in: query + name: objects_api_group + schema: + type: string + description: Which Objects API group to use. + - in: path + name: objects_api_objecttype_uuid + schema: + type: string + format: uuid + required: true + tags: + - registration + security: + - cookieAuth: [] + responses: + '200': + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/ObjecttypeVersion' + 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/prefill/plugins/objects-api/objecttypes/{objects_api_objecttype_uuid}/versions/{objects_api_objecttype_version}/attributes + : get: + operationId: prefill_plugins_objects_api_objecttypes_versions_attributes_list + description: List the available attributes for Objects API plugin. + summary: List available attributes for Objects API + parameters: + - in: query + name: objects_api_group + schema: + type: string + description: Which Objects API group to use. + - in: path + name: objects_api_objecttype_uuid + schema: + type: string + format: uuid + required: true + - in: path + name: objects_api_objecttype_version + schema: + type: integer + required: true + tags: + - prefill + security: + - cookieAuth: [] + responses: + '200': + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/PrefillObjectsAPIAttribute' + 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/products: get: operationId: products_list @@ -9398,6 +9515,18 @@ components: description: |- * `main` - Main * `authorizee` - Authorizee + PrefillObjectsAPIAttribute: + type: object + properties: + id: + type: string + description: The unique attribute identifier + label: + type: string + description: The human-readable name for an attribute. + required: + - id + - label PrefillPlugin: type: object properties: diff --git a/src/openforms/conf/base.py b/src/openforms/conf/base.py index e6411494aa..53c8847e0c 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/js/compiled-lang/en.json b/src/openforms/js/compiled-lang/en.json index 32b7cf654a..df4fa27a2b 100644 --- a/src/openforms/js/compiled-lang/en.json +++ b/src/openforms/js/compiled-lang/en.json @@ -131,18 +131,6 @@ "value": "Whether to show this value in the confirmation PDF" } ], - "0D+m56": [ - { - "type": 0, - "value": "Regular expression for city" - } - ], - "0FD2pY": [ - { - "type": 0, - "value": "Validation for the postcode field" - } - ], "0M2B7B": [ { "type": 0, @@ -295,12 +283,6 @@ "value": "The minimum value this field can have before the form can be submitted." } ], - "205QX5": [ - { - "type": 0, - "value": "When email address verification is enabled, the user must verify their email address before they can submit the form. This proves the email address exists and that they have access to the account." - } - ], "20K9To": [ { "type": 0, @@ -473,12 +455,6 @@ "value": "Days" } ], - "4DrI94": [ - { - "type": 0, - "value": "Regular expression for postcode" - } - ], "4FQxD/": [ { "type": 0, @@ -1397,12 +1373,6 @@ "value": "length" } ], - "Cf5zSF": [ - { - "type": 0, - "value": "Verify" - } - ], "CiaAYL": [ { "type": 0, @@ -1997,26 +1967,6 @@ "value": "Remove" } ], - "ISVTEk": [ - { - "type": 0, - "value": "The component type " - }, - { - "children": [ - { - "type": 1, - "value": "type" - } - ], - "type": 8, - "value": "code" - }, - { - "type": 0, - "value": " is unknown. We can only display the JSON definition." - } - ], "IhIqdj": [ { "type": 0, @@ -3297,12 +3247,6 @@ "value": "In the past" } ], - "Wb+QGm": [ - { - "type": 0, - "value": "Require verification" - } - ], "WdVKZw": [ { "type": 0, @@ -3321,12 +3265,6 @@ "value": "URL that points to the INFORMATIEOBJECTTYPE in the Catalogi API to be used for the submission attachments" } ], - "WxwqZJ": [ - { - "type": 0, - "value": "The regular expression pattern test that the postcode field value must pass before the form can be submitted." - } - ], "Wz5QZo": [ { "type": 0, @@ -4021,12 +3959,6 @@ "value": "Remove item" } ], - "dD9O3Q": [ - { - "type": 0, - "value": "Regular expression for postcode" - } - ], "dGBYF7": [ { "options": { @@ -4297,12 +4229,6 @@ "value": "A JSON logic expression returning a variable (of array type) whose items should be used as the options for this component." } ], - "fhVFUY": [ - { - "type": 0, - "value": "City" - } - ], "fpX4ei": [ { "type": 0, @@ -4437,20 +4363,6 @@ "value": "JSON content template" } ], - "hEVgKd": [ - { - "type": 0, - "value": "Error message for \"" - }, - { - "type": 1, - "value": "key" - }, - { - "type": 0, - "value": "\"" - } - ], "hJtTwo": [ { "type": 0, @@ -4933,12 +4845,6 @@ "value": "Form field" } ], - "mck25o": [ - { - "type": 0, - "value": "Validation for the city field" - } - ], "mf9eF+": [ { "type": 0, @@ -5077,12 +4983,6 @@ "value": "City" } ], - "osl4X2": [ - { - "type": 0, - "value": "The regular expression pattern test that the city field value must pass before the form can be submitted." - } - ], "ow5AAO": [ { "type": 0, @@ -5345,12 +5245,6 @@ "value": "Update existing object" } ], - "rW1edF": [ - { - "type": 0, - "value": "Postcode" - } - ], "rZfe8Z": [ { "type": 0, @@ -5421,12 +5315,6 @@ "value": "Switching to the new registration options will remove the existing JSON templates. You will also not be able to save the form until the variables are correctly mapped. Are you sure you want to continue?" } ], - "swKpQE": [ - { - "type": 0, - "value": "Regular expression for city" - } - ], "t5fg/K": [ { "type": 0, @@ -5835,12 +5723,6 @@ "value": "Maximum length" } ], - "yGAl1a": [ - { - "type": 0, - "value": "Close" - } - ], "yIPUtA": [ { "type": 0, diff --git a/src/openforms/js/compiled-lang/nl.json b/src/openforms/js/compiled-lang/nl.json index 6e81a9add1..aecf78d70d 100644 --- a/src/openforms/js/compiled-lang/nl.json +++ b/src/openforms/js/compiled-lang/nl.json @@ -131,18 +131,6 @@ "value": "Geef aan of deze waarde in de (bevestigings-)PDF moet getoond worden. Deze PDF wordt vaak ook als bijlage meegestuurd tijdens de registratie." } ], - "0D+m56": [ - { - "type": 0, - "value": "Patroon of waarde voor de stad" - } - ], - "0FD2pY": [ - { - "type": 0, - "value": "Validatieregels voor het postcodeveld" - } - ], "0M2B7B": [ { "type": 0, @@ -295,12 +283,6 @@ "value": "De minimale waarde die dit veld kan hebben voordat het formulier kan worden verzonden." } ], - "205QX5": [ - { - "type": 0, - "value": "Wanneer e-mailadresverificatie ingeschakeld is, dan moet de gebruiker hun e-mail verifiëren voor ze het formulier kunnen insturen. Dit bewijst dat het e-mailadres geldig is en dat de gebruiker er toegang tot heeft." - } - ], "20K9To": [ { "type": 0, @@ -473,12 +455,6 @@ "value": "Dagen" } ], - "4DrI94": [ - { - "type": 0, - "value": "Reguliere expressie" - } - ], "4FQxD/": [ { "type": 0, @@ -1401,12 +1377,6 @@ "value": "length" } ], - "Cf5zSF": [ - { - "type": 0, - "value": "Verifiëren" - } - ], "CiaAYL": [ { "type": 0, @@ -2018,26 +1988,6 @@ "value": "Verwijderen" } ], - "ISVTEk": [ - { - "type": 0, - "value": "Het componenttype " - }, - { - "children": [ - { - "type": 1, - "value": "type" - } - ], - "type": 8, - "value": "code" - }, - { - "type": 0, - "value": " is niet bekend. We kunnen enkel de JSON-definitie weergeven." - } - ], "IhIqdj": [ { "type": 0, @@ -3314,12 +3264,6 @@ "value": "In het verleden" } ], - "Wb+QGm": [ - { - "type": 0, - "value": "Verplicht verificatie" - } - ], "WdVKZw": [ { "type": 0, @@ -3338,12 +3282,6 @@ "value": "URL naar het INFORMATIEOBJECTTYPE in een Catalogi API die wordt gebruikt voor de bijlagen." } ], - "WxwqZJ": [ - { - "type": 0, - "value": "Het patroon van reguliere expressie waar de postcode aan moet voldoen voor het formulier kan verstuurd worden." - } - ], "Wz5QZo": [ { "type": 0, @@ -4043,12 +3981,6 @@ "value": "Item verwijderen" } ], - "dD9O3Q": [ - { - "type": 0, - "value": "Patroon of waarde voor de postcode" - } - ], "dGBYF7": [ { "options": { @@ -4319,12 +4251,6 @@ "value": "Geef een JsonLogic expressie voor de lijst van mogelijke opties. Evaluatie van de expressie moet een lijst teruggeven, waarbij elk element een lijst (array) is van [waarde, label] combinaties." } ], - "fhVFUY": [ - { - "type": 0, - "value": "Stad" - } - ], "fpX4ei": [ { "type": 0, @@ -4459,20 +4385,6 @@ "value": "JSON-inhoud sjabloon" } ], - "hEVgKd": [ - { - "type": 0, - "value": "Foutmelding voor \"" - }, - { - "type": 1, - "value": "key" - }, - { - "type": 0, - "value": "\"" - } - ], "hJtTwo": [ { "type": 0, @@ -4955,12 +4867,6 @@ "value": "Formulierveld" } ], - "mck25o": [ - { - "type": 0, - "value": "Validatieregels voor het stad-veld" - } - ], "mf9eF+": [ { "type": 0, @@ -5099,12 +5005,6 @@ "value": "Stad" } ], - "osl4X2": [ - { - "type": 0, - "value": "Het patroon van reguliere expressie waar de stad aan moet voldoen voor het formulier kan verstuurd worden." - } - ], "ow5AAO": [ { "type": 0, @@ -5367,12 +5267,6 @@ "value": "Bestaand object bijwerken" } ], - "rW1edF": [ - { - "type": 0, - "value": "Postcode" - } - ], "rZfe8Z": [ { "type": 0, @@ -5443,12 +5337,6 @@ "value": "Let op! Migreren naar het nieuwe configuratieformaat maakt de bestaande JSON-sjablonen leeg. Daarnaast kan je het formulier pas opslaan als alle verplichte variabelen goed gekoppeld zijn. Ben je zeker dat je wil migreren?" } ], - "swKpQE": [ - { - "type": 0, - "value": "Reguliere expressie" - } - ], "t5fg/K": [ { "type": 0, @@ -5857,12 +5745,6 @@ "value": "Maximale lengte" } ], - "yGAl1a": [ - { - "type": 0, - "value": "Sluiten" - } - ], "yIPUtA": [ { "type": 0, diff --git a/src/openforms/prefill/api/serializers.py b/src/openforms/prefill/api/serializers.py index a5f902ca43..c7c28729e1 100644 --- a/src/openforms/prefill/api/serializers.py +++ b/src/openforms/prefill/api/serializers.py @@ -64,3 +64,14 @@ class PrefillAttributeSerializer(serializers.Serializer): label=_("Label"), help_text=_("The human-readable name for an attribute."), ) + + +class PrefillObjectsAPIAttributeSerializer(serializers.Serializer): + id = serializers.CharField( + label=_("ID"), + help_text=_("The unique attribute identifier"), + ) + label = serializers.CharField( + label=_("Label"), + help_text=_("The human-readable name for an attribute."), + ) diff --git a/src/openforms/prefill/api/urls.py b/src/openforms/prefill/api/urls.py index a43ea07f59..eb695f69f4 100644 --- a/src/openforms/prefill/api/urls.py +++ b/src/openforms/prefill/api/urls.py @@ -1,8 +1,29 @@ from django.urls import path -from .views import PluginAttributesListView, PluginListView +from .views import ( + PluginAttributesListView, + PluginListView, + PluginObjectsAPIAttributesListView, + PluginObjectsAPIObjecttypeListView, + PluginObjectsAPIObjecttypeVersionListView, +) urlpatterns = [ + path( + "plugins/objects-api/objecttypes", + PluginObjectsAPIObjecttypeListView.as_view(), + name="prefill-objects-api-objecttype-list", + ), + path( + "plugins/objects-api/objecttypes//versions", + PluginObjectsAPIObjecttypeVersionListView.as_view(), + name="prefill-objects-api-objecttype-version-list", + ), + path( + "plugins/objects-api/objecttypes//versions//attributes", + PluginObjectsAPIAttributesListView.as_view(), + name="prefill-objects-api-objecttype-attribute-list", + ), path("plugins", PluginListView.as_view(), name="prefill-plugin-list"), path( "plugins//attributes", diff --git a/src/openforms/prefill/api/views.py b/src/openforms/prefill/api/views.py index b0ecba2f6b..fb06cb33c4 100644 --- a/src/openforms/prefill/api/views.py +++ b/src/openforms/prefill/api/views.py @@ -6,11 +6,21 @@ from rest_framework.views import APIView from openforms.api.views import ListMixin +from openforms.registrations.contrib.objects_api.api.serializers import ( + ObjectsAPIGroupInputSerializer, +) +from openforms.registrations.contrib.objects_api.api.views import ( + OBJECTS_API_GROUP_QUERY_PARAMETER, + ObjecttypesListView, + ObjecttypeVersionsListView, +) +from openforms.registrations.contrib.objects_api.client import get_objecttypes_client from ..registry import register from .serializers import ( ChoiceWrapper, PrefillAttributeSerializer, + PrefillObjectsAPIAttributeSerializer, PrefillPluginQueryParameterSerializer, PrefillPluginSerializer, ) @@ -67,3 +77,73 @@ def get_objects(self): choices = plugin.get_available_attributes() return [ChoiceWrapper(choice) for choice in choices] + + +@extend_schema( + summary=_("List available objecttypes for Objects API"), + parameters=[OBJECTS_API_GROUP_QUERY_PARAMETER], +) +class PluginObjectsAPIObjecttypeListView(ObjecttypesListView): + """ + List the available prefill objecttypes for Objects API plugin. + """ + + pass + + +@extend_schema( + summary=_("List available objecttype versions for Objects API"), + parameters=[OBJECTS_API_GROUP_QUERY_PARAMETER], +) +class PluginObjectsAPIObjecttypeVersionListView(ObjecttypeVersionsListView): + """ + List the available prefill objecttype versions for Objects API plugin. + """ + + def get_objects(self): + input_serializer = ObjectsAPIGroupInputSerializer( + data=self.request.query_params + ) + input_serializer.is_valid(raise_exception=True) + + config_group = input_serializer.validated_data["objects_api_group"] + objecttype_uuid = self.kwargs["objects_api_objecttype_uuid"] + + with get_objecttypes_client(config_group) as client: + return client.list_objecttype_versions(objecttype_uuid) + + +@extend_schema( + summary=_("List available attributes for Objects API"), + parameters=[OBJECTS_API_GROUP_QUERY_PARAMETER], +) +class PluginObjectsAPIAttributesListView(ListMixin, APIView): + """ + List the available attributes for Objects API plugin. + """ + + authentication_classes = (authentication.SessionAuthentication,) + permission_classes = (permissions.IsAdminUser,) + serializer_class = PrefillObjectsAPIAttributeSerializer + + def get_objects(self): + plugin = register["objects_api"] + input_serializer = ObjectsAPIGroupInputSerializer( + data=self.request.query_params + ) + input_serializer.is_valid(raise_exception=True) + + config_group = input_serializer.validated_data["objects_api_group"] + choices = plugin.get_available_attributes( + reference={ + "objects_api_group": config_group, + "objects_api_objecttype_uuid": self.kwargs[ + "objects_api_objecttype_uuid" + ], + "objects_api_objecttype_version": self.kwargs[ + "objects_api_objecttype_version" + ], + } + ) + + return choices diff --git a/src/openforms/prefill/base.py b/src/openforms/prefill/base.py index 4d7eacda8c..0eb373770c 100644 --- a/src/openforms/prefill/base.py +++ b/src/openforms/prefill/base.py @@ -19,9 +19,14 @@ class BasePlugin(AbstractBasePlugin): for_components: Container[str] = AllComponentTypes() @staticmethod - def get_available_attributes() -> Iterable[tuple[str, str]]: + def get_available_attributes( + reference: dict[str, str] | None = None, + ) -> Iterable[tuple[str, str]]: """ Return a choice list of available attributes this plugin offers. + + :param reference: a dict based on which we retrieve the available attributes. + Can be used when we have dynamic lists of attributes. """ raise NotImplementedError( "You must implement the 'get_available_attributes' method." 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/plugin.py b/src/openforms/prefill/contrib/objects_api/plugin.py new file mode 100644 index 0000000000..e56f3fb26b --- /dev/null +++ b/src/openforms/prefill/contrib/objects_api/plugin.py @@ -0,0 +1,43 @@ +import logging +from typing import Any, Iterable + +from django.utils.translation import gettext_lazy as _ + +from openforms.authentication.service import AuthAttribute +from openforms.submissions.models import Submission +from openforms.typing import JSONEncodable + +from ...base import BasePlugin +from ...constants import IdentifierRoles +from ...registry import register + +logger = logging.getLogger(__name__) + +PLUGIN_IDENTIFIER = "objects_api" + + +@register(PLUGIN_IDENTIFIER) +class ObjectsAPIPrefill(BasePlugin): + verbose_name = _("Objects API") + requires_auth = AuthAttribute.bsn + + @staticmethod + def get_available_attributes( + reference: dict[str, Any] | None = None, + ) -> Iterable[tuple[str, str]]: + pass + + @classmethod + def get_prefill_values( + cls, + submission: Submission, + attributes: list[str], + identifier_role: IdentifierRoles = IdentifierRoles.main, + ) -> dict[str, JSONEncodable]: + pass + + @classmethod + def get_co_sign_values( + cls, submission: Submission, identifier: str + ) -> tuple[dict[str, Any], str]: + pass