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+
17from 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+ )
425from 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
932from 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
1541from ..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 \n Uitleg 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