Skip to content

Commit

Permalink
[#4396] First attempt for objects_api prefill plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
vaszig committed Aug 22, 2024
1 parent 2752e19 commit a912c98
Show file tree
Hide file tree
Showing 18 changed files with 395 additions and 8 deletions.
1 change: 1 addition & 0 deletions docs/configuration/prefill/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ Prefill plugins
kvk
stuf_bg
suwinet
objects_api
3 changes: 3 additions & 0 deletions docs/developers/plugins/prefill_plugins.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
1 change: 1 addition & 0 deletions src/openforms/conf/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
2 changes: 2 additions & 0 deletions src/openforms/forms/admin/form_variable.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class FormVariableAdmin(admin.ModelAdmin):
"prefill_plugin",
"prefill_attribute",
"prefill_identifier_role",
"prefill_options",
"data_type",
"is_sensitive_data",
"initial_value",
Expand All @@ -31,6 +32,7 @@ class FormVariableAdmin(admin.ModelAdmin):
"prefill_plugin",
"prefill_attribute",
"prefill_identifier_role",
"prefill_options",
"data_type",
"data_format",
"is_sensitive_data",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Generated by Django 4.2.15 on 2024-08-21 11:04

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("forms", "0097_v267_to_v270"),
]

operations = [
migrations.AddField(
model_name="formvariable",
name="prefill_options",
field=models.JSONField(
blank=True, default=dict, verbose_name="prefill options"
),
),
]
5 changes: 5 additions & 0 deletions src/openforms/forms/models/form_variable.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,11 @@ class FormVariable(models.Model):
default=IdentifierRoles.main,
max_length=100,
)
prefill_options = models.JSONField(
_("prefill options"),
default=dict,
blank=True,
)
data_type = models.CharField(
verbose_name=_("data type"),
help_text=_("The type of the value that will be associated with this variable"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,9 +143,10 @@ const AttributeField = () => {
} = useAsync(async () => {
if (!plugin) return [];

const endpoint = `/api/v2/prefill/plugins/${plugin}/attributes`;
// const endpoint = `/api/v2/prefill/plugins/${plugin}/attributes`;
const endpoint = `/api/v2/prefill/plugins/objects-api/objecttypes/ac1fa3f8-fb2a-4fcb-b715-d480aceeda10/versions/1/attributes`;
// XXX: clean up error handling here at some point...
const response = await get(endpoint);
const response = await get(endpoint, {objects_api_group: '2'});
if (!response.ok) throw response.data;
return response.data.map(attribute => [attribute.id, attribute.label]);
}, [plugin]);
Expand Down
5 changes: 4 additions & 1 deletion src/openforms/js/components/formio_builder/WebformBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ import {currentTheme} from 'utils/theme';

import {
getPrefillAttributes,
getPrefillObjectsAPIGroups,
getPrefillObjectsAPIObjecttypeVersions,
getPrefillObjectsAPIObjecttypes,
getPrefillPlugins,
getRegistrationAttributes,
getValidatorPlugins,
Expand Down Expand Up @@ -166,7 +169,7 @@ class WebformBuilder extends WebformBuilderFormio {
getValidatorPlugins={getValidatorPlugins}
getRegistrationAttributes={getRegistrationAttributes}
getPrefillPlugins={getPrefillPlugins}
getPrefillAttributes={getPrefillAttributes}
getPrefillAttributes={getPrefillObjectsAPIObjecttypeVersions}
getFileTypes={async () => FILE_TYPES}
serverUploadLimit={MAX_FILE_UPLOAD_SIZE}
getDocumentTypes={async () => await getAvailableDocumentTypes(this)}
Expand Down
32 changes: 30 additions & 2 deletions src/openforms/js/components/formio_builder/plugins.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,35 @@ export const getPrefillPlugins = async () => {
return resp.data;
};

export const getPrefillAttributes = async plugin => {
const resp = await get(`/api/v2/prefill/plugins/${plugin}/attributes`);
// export const getPrefillAttributes = async plugin => {
// // const resp = await get(`/api/v2/prefill/plugins/${plugin}/attributes`);
// const resp = await get(`/api/v2/prefill/plugins/objects-api/groups`);
// return resp.data;
// };

// export const getPrefillObjectsAPIGroups = async () => {
// const resp = await get(`/api/v2/prefill/plugins/objects-api/groups`);
// return resp.data;
// };

// export const getPrefillObjectsAPIObjecttypes = async () => {
// const resp = await get(`/api/v2/prefill/plugins/objects-api/objecttypes/2`);
// return resp.data;
// };

// export const getPrefillObjectsAPIObjecttypeVersions = async () => {
// const resp = await get(
// `/api/v2/prefill/plugins/objects-api/objecttypes/2/ac1fa3f8-fb2a-4fcb-b715-d480aceeda10/versions`
// );
// return resp.data;
// };

export const getPrefillObjectsAPIObjecttypeVersions = async () => {
const resp = await get(
`/api/v2/prefill/plugins/objects-api/objecttypes/ac1fa3f8-fb2a-4fcb-b715-d480aceeda10/versions/1/attributes`,
{
objects_api_group: '2',
}
);
return resp.data;
};
33 changes: 33 additions & 0 deletions src/openforms/prefill/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,36 @@ class PrefillAttributeSerializer(serializers.Serializer):
label=_("Label"),
help_text=_("The human-readable name for an attribute."),
)


class PrefillObjectsAPIObjecttypeSerializer(serializers.Serializer):
# Keys are defined in camel case as this is what we get from the Objecttype API
url = serializers.URLField(
label=_(
"URL reference to this object. This is the unique identification and location of this object."
),
)
uuid = serializers.UUIDField(label=_("Unique identifier (UUID4)."))
name = serializers.CharField(label=_("Name of the object type."))
namePlural = serializers.CharField(label=_("Plural name of the object type."))
dataClassification = serializers.CharField(
label=_("Confidential level of the object type.")
)


class PrefillObjectsAPIObjecttypeVersionSerializer(serializers.Serializer):
version = serializers.IntegerField(
label=_("Integer version of the Objecttype."),
)
status = serializers.CharField(label=_("Status of the object type version"))


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."),
)
24 changes: 23 additions & 1 deletion src/openforms/prefill/api/urls.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,30 @@
from django.urls import path

from .views import PluginAttributesListView, PluginListView
from .views import (
PluginAttributesListView,
PluginListView,
PluginObjectsAPIAttributesListView,
PluginObjectsAPIObjecttypeListView,
PluginObjectsAPIObjecttypeVersionListView,
)

urlpatterns = [
path(
# "plugins/objects-api/objecttypes/<str:objects_api_group>",
"plugins/objects-api/objecttypes",
PluginObjectsAPIObjecttypeListView.as_view(),
name="prefill-objects-api-objecttype-list",
),
path(
"plugins/objects-api/objecttypes/<uuid:objects_api_objecttype_uuid>/versions",
PluginObjectsAPIObjecttypeVersionListView.as_view(),
name="prefill-objects-api-objecttype-version-list",
),
path(
"plugins/objects-api/objecttypes/<uuid:objects_api_objecttype_uuid>/versions/<int:objects_api_objecttype_version>/attributes",
PluginObjectsAPIAttributesListView.as_view(),
name="prefill-objects-api-objecttype-attribute-list",
),
path("plugins", PluginListView.as_view(), name="prefill-plugin-list"),
path(
"plugins/<slug:plugin>/attributes",
Expand Down
106 changes: 105 additions & 1 deletion src/openforms/prefill/api/views.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,37 @@
from typing import Any

from django.utils.translation import gettext_lazy as _

from drf_spectacular.utils import extend_schema, extend_schema_view
from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import OpenApiParameter, extend_schema, extend_schema_view
from rest_framework import authentication, permissions
from rest_framework.exceptions import NotFound
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.client import get_objecttypes_client

from ..registry import register
from .serializers import (
ChoiceWrapper,
PrefillAttributeSerializer,
PrefillObjectsAPIAttributeSerializer,
PrefillObjectsAPIObjecttypeSerializer,
PrefillObjectsAPIObjecttypeVersionSerializer,
PrefillPluginQueryParameterSerializer,
PrefillPluginSerializer,
)

OBJECTS_API_GROUP_QUERY_PARAMETER = OpenApiParameter(
name="objects_api_group",
type=OpenApiTypes.STR,
location=OpenApiParameter.QUERY,
description=_("Which Objects API group to use."),
)


@extend_schema_view(
get=extend_schema(
Expand Down Expand Up @@ -67,3 +84,90 @@ def get_objects(self):
choices = plugin.get_available_attributes()

return [ChoiceWrapper(choice) for choice in choices]


@extend_schema_view(
get=extend_schema(summary=_("List available objecttypes for Objects API")),
parameters=[OBJECTS_API_GROUP_QUERY_PARAMETER],
)
class PluginObjectsAPIObjecttypeListView(ListMixin, APIView):
"""
List the available prefill objecttypes for Objects API plugin.
"""

authentication_classes = (authentication.SessionAuthentication,)
permission_classes = (permissions.IsAdminUser,)
serializer_class = PrefillObjectsAPIObjecttypeSerializer

def get_objects(self) -> list[dict[str, Any]]:
input_serializer = ObjectsAPIGroupInputSerializer(
data=self.request.query_params
)
input_serializer.is_valid(raise_exception=True)

config_group = input_serializer.validated_data["objects_api_group"]

with get_objecttypes_client(config_group) as client:
return client.list_objecttypes()


@extend_schema_view(
get=extend_schema(summary=_("List available objecttype versions for Objects API")),
parameters=[OBJECTS_API_GROUP_QUERY_PARAMETER],
)
class PluginObjectsAPIObjecttypeVersionListView(ListMixin, APIView):
"""
List the available prefill objecttype versions for Objects API plugin.
"""

authentication_classes = (authentication.SessionAuthentication,)
permission_classes = (permissions.IsAdminUser,)
serializer_class = PrefillObjectsAPIObjecttypeVersionSerializer

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_view(
get=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
7 changes: 6 additions & 1 deletion src/openforms/prefill/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -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."
Expand Down
3 changes: 3 additions & 0 deletions src/openforms/prefill/contrib/objects_api/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
"""
Objects API prefill plugin.
"""
12 changes: 12 additions & 0 deletions src/openforms/prefill/contrib/objects_api/apps.py
Original file line number Diff line number Diff line change
@@ -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
19 changes: 19 additions & 0 deletions src/openforms/prefill/contrib/objects_api/constants.py
Original file line number Diff line number Diff line change
@@ -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")
Loading

0 comments on commit a912c98

Please sign in to comment.