Skip to content

Commit 14cc82e

Browse files
committed
✨ [#236] feat: Handle Eigenschap POST, PATCH and PUT with formaat
Frontend doesn't support nested structures like Eigenschappen.specificatie. This changes the frontend facing side so formaat becomes an attribute on Eigenschap
1 parent 6909572 commit 14cc82e

File tree

6 files changed

+791
-15
lines changed

6 files changed

+791
-15
lines changed

backend/setup.cfg

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,4 @@ exclude_also =
1919
raise NotImplementedError
2020
\.\.\.
2121
pass
22+
(case _:\n\s*)?assert_never\(.*\)

backend/src/openbeheer/eigenschappen/api/views.py

Lines changed: 159 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,150 @@
1+
import re
2+
from typing import Annotated, assert_never
3+
from uuid import UUID
4+
5+
from django.utils.translation import gettext as __
6+
17
from drf_spectacular.utils import extend_schema, extend_schema_view
8+
from msgspec import (
9+
UNSET,
10+
Meta,
11+
Struct,
12+
ValidationError,
13+
convert,
14+
to_builtins,
15+
)
16+
from rest_framework.request import Request
17+
from rest_framework.response import Response
218

3-
from openbeheer.api.views import DetailView, DetailViewWithoutVersions, ListView
19+
from openbeheer.api.views import (
20+
DetailView,
21+
DetailViewWithoutVersions,
22+
ListView,
23+
reverse,
24+
)
425
from openbeheer.types import (
526
EigenschapWithUUID,
627
ExternalServiceError,
728
ZGWError,
829
)
30+
from openbeheer.types._open_beheer import make_fields_optional
31+
from openbeheer.types._zgw import InvalidParam
932
from openbeheer.types.ztc import (
1033
Eigenschap,
1134
EigenschapRequest,
35+
EigenschapSpecificatieRequest,
36+
FormaatEnum,
1237
PatchedEigenschapRequest,
1338
)
39+
from openbeheer.utils.decorators import handle_service_errors
1440

1541
from ..types import (
1642
EigenschappenGetParametersQuery,
1743
)
1844

1945

46+
def field_name_of_error(error: ValidationError):
47+
return match.group(1) if (match := re.search(r"`\$.(.*)`$", str(error))) else ""
48+
49+
50+
class OBEigenschap(Struct):
51+
"An intermediate Eigenschap used by Open Beheer"
52+
53+
naam: Annotated[str, Meta(description="De naam van de EIGENSCHAP", max_length=20)]
54+
definitie: Annotated[
55+
str,
56+
Meta(
57+
description="De beschrijving van de betekenis van deze EIGENSCHAP",
58+
max_length=255,
59+
),
60+
]
61+
formaat: Annotated[
62+
FormaatEnum,
63+
Meta(
64+
description="Het soort tekens waarmee waarden van de EIGENSCHAP kunnen worden vastgelegd.\n\nUitleg bij mogelijke waarden:\n\n* `tekst` - Tekst\n* `getal` - Getal\n* `datum` - Datum\n* `datum_tijd` - Datum/tijd"
65+
),
66+
]
67+
68+
def as_spec(self) -> EigenschapSpecificatieRequest:
69+
match self.formaat:
70+
case FormaatEnum.tekst:
71+
lengte = "255"
72+
case FormaatEnum.getal:
73+
lengte = "255" # XXX what??
74+
case FormaatEnum.datum:
75+
lengte = "8" # YYYYMMDD?
76+
case FormaatEnum.datum_tijd:
77+
lengte = "14" # YYYYMMDD HH:mm?
78+
case _:
79+
assert_never(self.formaat)
80+
81+
return EigenschapSpecificatieRequest(
82+
formaat=self.formaat,
83+
lengte=lengte,
84+
kardinaliteit="1",
85+
groep=UNSET, # type: ignore
86+
waardenverzameling=UNSET, # type: ignore
87+
)
88+
89+
90+
def fix_input(
91+
request: Request,
92+
zaaktype_url: str,
93+
t: type[EigenschapRequest | PatchedEigenschapRequest] = EigenschapRequest,
94+
) -> Response | None:
95+
"Update request.data return a Response on error"
96+
try:
97+
input = convert(
98+
dict(request.data.items()),
99+
OBEigenschap
100+
if t is not PatchedEigenschapRequest
101+
else make_fields_optional(OBEigenschap),
102+
)
103+
except ValidationError as e:
104+
return Response(
105+
data=ZGWError(
106+
code="invalid",
107+
status=400,
108+
title=__("Invalid input."),
109+
detail="",
110+
invalid_params=[
111+
InvalidParam(
112+
field_name_of_error(e),
113+
"invalid",
114+
reason=str(e).split(" - at ")[0],
115+
)
116+
],
117+
instance="", # TODO: log and add id
118+
),
119+
status=400,
120+
)
121+
122+
match input, t:
123+
case OBEigenschap(), _ if t is EigenschapRequest:
124+
eigenschap = t(
125+
naam=input.naam,
126+
definitie=input.definitie,
127+
specificatie=input.as_spec(),
128+
zaaktype=zaaktype_url,
129+
)
130+
case _ if t is PatchedEigenschapRequest:
131+
args = {
132+
name: value
133+
for name in dir(input)
134+
if not name.startswith("_")
135+
and (value := getattr(input, name, UNSET))
136+
and value is not UNSET
137+
and not callable(value)
138+
}
139+
eigenschap = t(**args)
140+
case _:
141+
assert_never(t) # pyright: ignore[reportArgumentType]
142+
143+
request.data.clear()
144+
data = to_builtins(eigenschap)
145+
request.data.update(data)
146+
147+
20148
@extend_schema_view(
21149
get=extend_schema(
22150
tags=["eigenschappen"],
@@ -41,25 +169,37 @@ class EigenschappenListView(
41169
ListView[
42170
EigenschappenGetParametersQuery,
43171
Eigenschap,
44-
Eigenschap,
172+
EigenschapRequest,
45173
]
46174
):
47175
"""
48176
Endpoint for eigenschappen attached to a particular Zaaktype
49177
"""
50178

51-
data_type = Eigenschap
179+
data_type = EigenschapRequest
52180
return_data_type = EigenschapWithUUID
53181
query_type = EigenschappenGetParametersQuery
54182
endpoint_path = "eigenschappen"
55183

184+
@handle_service_errors
185+
def post(self, request: Request, slug: str = "", **path_params) -> Response:
186+
# super().post just sends the request.data as-is
187+
# this patches up some malformed things we receive
188+
189+
zaaktype_url = reverse(slug)("zaaktype", path_params.get("zaaktype"))
190+
assert zaaktype_url
191+
if error := fix_input(request, zaaktype_url, t=EigenschapRequest):
192+
return error
193+
194+
return super().post(request, slug, **path_params)
195+
56196

57197
@extend_schema_view(
58198
get=extend_schema(
59199
operation_id="service_eigenschappen_retrieve_one",
60200
tags=["eigenschappen"],
61201
summary="Get an eigenschappe",
62-
description="Retrieve an eigenschappe from Open Zaak.",
202+
description="Retrieve an eigenschap from Open Zaak.",
63203
responses={
64204
"200": Eigenschap,
65205
"400": ZGWError,
@@ -68,7 +208,7 @@ class EigenschappenListView(
68208
patch=extend_schema(
69209
tags=["eigenschappen"],
70210
summary="Patch an eigenschappe",
71-
description="Partially update a eigenschappe from Open Zaak.",
211+
description="Partially update a eigenschap from Open Zaak.",
72212
request=PatchedEigenschapRequest,
73213
responses={
74214
"200": Eigenschap,
@@ -78,7 +218,7 @@ class EigenschappenListView(
78218
put=extend_schema(
79219
tags=["eigenschappen"],
80220
summary="Put an eigenschappe",
81-
description="Fully update a eigenschappe from Open Zaak.",
221+
description="Fully update a eigenschap from Open Zaak.",
82222
request=EigenschapRequest,
83223
responses={
84224
"200": Eigenschap,
@@ -88,7 +228,7 @@ class EigenschappenListView(
88228
delete=extend_schema(
89229
tags=["eigenschappen"],
90230
summary="Delete an eigenschappe",
91-
description="Remove permanently a eigenschappe from Open Zaak.",
231+
description="Remove permanently a eigenschap from Open Zaak.",
92232
responses={
93233
"204": None,
94234
"400": ZGWError,
@@ -103,3 +243,15 @@ class EigenschappenDetailView(DetailViewWithoutVersions, DetailView[Eigenschap])
103243
return_data_type = data_type = Eigenschap
104244
endpoint_path = "eigenschappen/{uuid}"
105245
expansions = {}
246+
247+
def update(
248+
self, request: Request, slug: str, uuid: UUID, is_partial: bool = True
249+
) -> Response:
250+
t = PatchedEigenschapRequest if is_partial else EigenschapRequest
251+
252+
zaaktype_url = request.data.get("zaaktype")
253+
assert zaaktype_url
254+
if error := fix_input(request, zaaktype_url, t):
255+
return error
256+
257+
return super().update(request, slug, uuid, is_partial)

0 commit comments

Comments
 (0)