Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor validation plugins #3701

Closed
wants to merge 17 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"dependencies": {
"@fortawesome/fontawesome-free": "^6.1.1",
"@open-formulieren/design-tokens": "^0.51.0",
"@open-formulieren/formio-builder": "^0.9.0",
"@open-formulieren/formio-builder": "^0.10.1",
"@open-formulieren/leaflet-tools": "^1.0.0",
"@rjsf/core": "^4.2.1",
"@tinymce/tinymce-react": "^3.12.6",
Expand Down
8 changes: 7 additions & 1 deletion src/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5269,6 +5269,7 @@ paths:
schema:
type: string
enum:
- brk-Zaakgerechtigde
- kvk-branchNumber
- kvk-kvkNumber
- kvk-rsin
Expand Down Expand Up @@ -9312,8 +9313,13 @@ components:
properties:
value:
type: string
description: Value to be validated
description: Value to be validated.
submissionUuid:
type: string
format: uuid
description: UUID of the submission.
required:
- submissionUuid
- value
ValidationPlugin:
type: object
Expand Down
21 changes: 20 additions & 1 deletion src/openforms/api/drf_spectacular/plumbing.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
from typing import Any

from drf_spectacular.plumbing import (
ResolvedComponent,
build_basic_type,
build_parameter_type,
)
from drf_spectacular.utils import OpenApiParameter
from drf_spectacular.utils import OpenApiParameter, inline_serializer
from rest_framework.fields import Field
from rest_framework.serializers import Serializer


def build_response_header_parameter(
Expand Down Expand Up @@ -53,3 +57,18 @@ def build_response_header_component(
object=name,
)
return component


def extend_inline_serializer(
serializer: type[Serializer],
fields: dict[str, Field],
name: str | None = None,
**kwargs: Any,
):
"""Return an inline serializer, with fields extended from the provided serializer.

If one of the provided extra fields already exists on the base serializer, it will be overridden.
"""
return inline_serializer(
name or serializer.__name__, serializer().get_fields() | fields, **kwargs
)
1 change: 1 addition & 0 deletions src/openforms/conf/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@
"openforms.ui",
"openforms.submissions",
"openforms.logging.apps.LoggingAppConfig",
"openforms.contrib.brk",
"openforms.contrib.brp",
"openforms.contrib.digid_eherkenning",
"openforms.contrib.haal_centraal",
Expand Down
Empty file.
10 changes: 10 additions & 0 deletions src/openforms/contrib/brk/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from django.contrib import admin

from solo.admin import SingletonModelAdmin

from .models import BRKConfig


@admin.register(BRKConfig)
class BRKConfigAdmin(SingletonModelAdmin):
pass
12 changes: 12 additions & 0 deletions src/openforms/contrib/brk/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 BRKApp(AppConfig):
name = "openforms.contrib.brk"
label = "brk"
verbose_name = _("BRK configuration")

def ready(self):
# register the plugin
from . import validators # noqa
70 changes: 70 additions & 0 deletions src/openforms/contrib/brk/client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import logging
from typing import TypedDict

import requests

from openforms.contrib.hal_client import HALClient
from zgw_consumers_ext.api_client import ServiceClientFactory

from .models import BRKConfig

logger = logging.getLogger(__name__)


class NoServiceConfigured(RuntimeError):
pass


def get_client() -> "BRKClient":
config = BRKConfig.get_solo()
assert isinstance(config, BRKConfig)
if not (service := config.service):
raise NoServiceConfigured("No KVK service configured!")
service_client_factory = ServiceClientFactory(service)
return BRKClient.configure_from(service_client_factory)


class SearchParams(TypedDict, total=False):
postcode: str
huisnummer: str
huisletter: str
huisnummertoevoeging: str


class BRKClient(HALClient):
def get_cadastrals_by_address(self, query_params: SearchParams):
"""
Search for cadastrals by querying for a specifc address.

API docs: https://vng-realisatie.github.io/Haal-Centraal-BRK-bevragen/swagger-ui-2.0#/Kadastraal%20Onroerende%20Zaken/GetKadastraalOnroerendeZaken
"""
assert query_params, "You must provide at least one query parameter"

try:
response = self.get(
"kadastraalonroerendezaken",
params=query_params,
)
response.raise_for_status()
except requests.RequestException as exc:
logger.exception("exception while making BRK request", exc_info=exc)
raise exc

return response.json()

def get_cadastral_titleholders_by_cadastral_id(self, cadastral_id: str):
"""
Search for commercial titleholders of a cadastral immovable property.

API docs: https://vng-realisatie.github.io/Haal-Centraal-BRK-bevragen/swagger-ui-2.0#/Zakelijke%20Gerechtigden/GetZakelijkGerechtigden
"""
try:
response = self.get(
f"kadastraalonroerendezaken/{cadastral_id}/zakelijkgerechtigden",
)
response.raise_for_status()
except requests.RequestException as exc:
logger.exception("exception while making BRK request", exc_info=exc)
raise exc

return response.json()
45 changes: 45 additions & 0 deletions src/openforms/contrib/brk/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Generated by Django 3.2.23 on 2023-11-29 16:05

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

initial = True

dependencies = [
("zgw_consumers", "0019_alter_service_uuid"),
]

operations = [
migrations.CreateModel(
name="BRKConfig",
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"service",
models.OneToOneField(
help_text="Service for API interaction with the BRK.",
limit_choices_to={"api_type": "orc"},
null=True,
on_delete=django.db.models.deletion.PROTECT,
related_name="+",
to="zgw_consumers.service",
verbose_name="BRK API",
),
),
],
options={
"verbose_name": "BRK configuration",
},
),
]
Empty file.
24 changes: 24 additions & 0 deletions src/openforms/contrib/brk/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from django.db import models
from django.utils.translation import gettext_lazy as _

from solo.models import SingletonModel
from zgw_consumers.constants import APITypes


class BRKConfig(SingletonModel):
"""
Global configuration and defaults.
"""

service = models.OneToOneField(
"zgw_consumers.Service",
verbose_name=_("BRK API"),
help_text=_("Service for API interaction with the BRK."),
on_delete=models.PROTECT,
limit_choices_to={"api_type": APITypes.orc},
related_name="+",
null=True,
)

class Meta:
verbose_name = _("BRK configuration")
Empty file.
34 changes: 34 additions & 0 deletions src/openforms/contrib/brk/tests/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
from pathlib import Path
from unittest.mock import patch

from zgw_consumers.constants import APITypes, AuthTypes

from openforms.contrib.brk.models import BRKConfig
from zgw_consumers_ext.tests.factories import ServiceFactory

TEST_FILES = Path(__file__).parent.resolve() / "files"


BRK_SERVICE = ServiceFactory.build(
api_root="https://api.brk.kadaster.nl/esd-eto-apikey/bevragen/v2/",
oas="https://api.brk.kadaster.nl/esd-eto-apikey/bevragen/v2/", # ignored/unused
api_type=APITypes.orc,
auth_type=AuthTypes.api_key,
header_key="X-Api-Key",
header_value="fake",
)


class BRKTestMixin:

api_root = BRK_SERVICE.api_root

def setUp(self):
super().setUp()

patcher = patch(
"openforms.contrib.brk.client.BRKConfig.get_solo",
return_value=BRKConfig(service=BRK_SERVICE),
)
self.config_mock = patcher.start()
self.addCleanup(patcher.stop)
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
interactions:
- request:
body: null
headers:
Accept:
- application/hal+json
Accept-Encoding:
- gzip, deflate, br
Connection:
- keep-alive
Content-Type:
- application/hal+json
User-Agent:
- python-requests/2.31.0
X-Api-Key:
- fake
method: GET
uri: https://api.brk.kadaster.nl/esd-eto-apikey/bevragen/v2/kadastraalonroerendezaken?postcode=7361EW&huisnummer=21
response:
body:
string: '{"_links":{"self":{"href":"/kadastraalonroerendezaken?postcode=7361EW&huisnummer=21"}},"_embedded":{"kadastraalOnroerendeZaken":[{"identificatie":"76870482570000","domein":"NL.IMKAD.KadastraalObject","begrenzingPerceel":{"type":"Polygon","coordinates":[[[194618.354,464161.294],[194619.326,464151.276],[194631.322,464153.988],[194635.099,464154.75],[194633.498,464163.819],[194631.173,464176.992],[194627.691,464176.428],[194624.201,464175.924],[194620.702,464175.479],[194617.197,464175.093],[194616.973,464175.072],[194618.174,464162.759],[194618.354,464161.294]]]},"plaatscoordinaten":{"type":"Point","coordinates":[194625.551,464156.205]},"type":"perceel","kadastraleAanduiding":"Beekbergen
K 4825","kadastraleGrootte":{"soortGrootte":{"code":"1","waarde":"Vastgesteld"},"waarde":350},"adressen":[{"straat":"Atalanta","huisnummer":21,"postcode":"7361EW","woonplaats":"Beekbergen","nummeraanduidingIdentificatie":"0200200000003730","adresregel1":"Atalanta
21","adresregel2":"7361EW Beekbergen","koppelingswijze":{"code":"ADM_GEO","waarde":"administratief
en geometrisch"}}],"zakelijkGerechtigdeIdentificaties":["20170719","20170717","20170718"],"hypotheekIdentificaties":["25381030"],"_links":{"self":{"href":"/kadastraalonroerendezaken/76870482570000"},"zakelijkGerechtigden":[{"href":"/kadastraalonroerendezaken/76870482570000/zakelijkgerechtigden/{zakelijkGerechtigdeIdentificaties}","templated":true}],"hypotheken":[{"href":"/kadastraalonroerendezaken/76870482570000/hypotheken/{hypotheekIdentificaties}","templated":true}],"adressen":[{"href":"https://api.bag.kadaster.nl/lvbag/individuelebevragingen/v2/adressen/{adressen.nummeraanduidingIdentificatie}","templated":true}]}}]}}'
headers:
Api-Version:
- 2.0.0
Connection:
- keep-alive
Content-Crs:
- epsg:28992
Content-Encoding:
- gzip
Content-Length:
- '736'
Content-Type:
- application/hal+json
Date:
- Fri, 15 Dec 2023 16:34:14 GMT
Keep-Alive:
- timeout=60
Server:
- Unspecified
status:
code: 200
message: ''
version: 1
Loading
Loading