Skip to content

Commit

Permalink
🦺 [#4606] Support storing zaaktype identification config option
Browse files Browse the repository at this point in the history
Specifying the identification will supersede setting up the URLs that
pin the case type to a fixed version.

When an identification is provided, we must:

* allow the 'zaaktype' field to be empty or omitted
* ensure that either 'zaaktype' or the identification are provided
* ensure that a catalogue is specified, since the case type is
  uniquely identified by identification only WITHIN its catalogue
* validate that a case type with this identification can indeed be
  found
  • Loading branch information
sergei-maertens committed Sep 8, 2024
1 parent 48d088e commit 8c4c27d
Show file tree
Hide file tree
Showing 7 changed files with 330 additions and 11 deletions.
27 changes: 23 additions & 4 deletions src/openforms/contrib/zgw/clients/catalogi.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,17 +154,36 @@ def find_catalogus(self, *, domain: str, rsin: str) -> Catalogus | None:
return None
return data["results"][0]

def get_all_case_types(self, *, catalogus: str = "") -> Iterator[CaseType]:
params: CaseTypeListParams = {}
if catalogus:
params["catalogus"] = catalogus
def get_all_case_types(self, *, catalogus: str) -> Iterator[CaseType]:
params: CaseTypeListParams = {
"catalogus": catalogus,
}
if self.allow_drafts:
params["status"] = "alles"
response = self.get("zaaktypen", params=params) # type: ignore
response.raise_for_status()
data = response.json()
yield from pagination_helper(self, data)

def find_case_types(
self,
*,
catalogus: str,
identification: str,
) -> list[CaseType] | None:
params: CaseTypeListParams = {
"catalogus": catalogus,
"identificatie": identification,
}
response = self.get("zaaktypen", params=params) # type: ignore
response.raise_for_status()

data: PaginatedResponseData[CaseType] = response.json()
if data["count"] == 0:
return None

return list(pagination_helper(self, data))

def get_all_informatieobjecttypen(
self, *, catalogus: str = ""
) -> Iterator[InformatieObjectType]:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ import {
mockObjecttypeVersionsGet,
mockObjecttypesGet,
} from 'components/admin/form_design/registrations/objectsapi/mocks';
import {mockCataloguesGet as mockZGWApisCataloguesGet} from 'components/admin/form_design/registrations/zgw/mocks';
import {
mockCaseTypesGet,
mockCataloguesGet as mockZGWApisCataloguesGet,
} from 'components/admin/form_design/registrations/zgw/mocks';
import {
FormDecorator,
ValidationErrorsDecorator,
Expand Down Expand Up @@ -204,7 +207,7 @@ export default {
mockObjectsApiCataloguesGet(),
mockDocumentTypesGet(),
],
zgwMocks: [mockZGWApisCataloguesGet()],
zgwMocks: [mockZGWApisCataloguesGet(), mockCaseTypesGet()],
},
},
},
Expand Down
58 changes: 54 additions & 4 deletions src/openforms/registrations/contrib/zgw_apis/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,22 @@ class ZaakOptionsSerializer(JsonSchemaSerializerMixin, serializers.Serializer):
"and document types specified will be resolved against this catalogue."
),
)
case_type_identification = serializers.CharField(
required=False, # either this field or zaaktype (legacy) must be provided
label=_("Case type identification"),
help_text=_(
"The case type will be retrieved in the specified catalogue. The version "
"will automatically be selected based on the submission completion "
"timestamp. When you specify this field, you MUST also specify a catalogue."
),
default="",
)

# DeprecationWarning - deprecated, will be removed in OF 3.0 or 4.0
zaaktype = serializers.URLField(
required=True, help_text=_("URL of the ZAAKTYPE in the Catalogi API")
required=False,
help_text=_("URL of the ZAAKTYPE in the Catalogi API"),
default="",
)
informatieobjecttype = serializers.URLField(
required=True,
Expand Down Expand Up @@ -131,6 +145,16 @@ def get_fields(self):
return fields

def validate(self, attrs: RegistrationOptions) -> RegistrationOptions:
# Legacy forms will have zaaktype set, new forms can set case_type_identification.
# Both may be set - in that case, `case_type_identification` is preferred.
if not attrs["case_type_identification"] and not attrs["zaaktype"]:
raise serializers.ValidationError(
{
"case_type_identification": _("You must specify a case type."),
},
code="required",
)

validate_business_logic = self.context.get("validate_business_logic", True)
if not validate_business_logic:
return attrs
Expand Down Expand Up @@ -194,6 +218,9 @@ def _validate_catalogue_case_and_doc_type(
catalogus = None
catalogue_option = attrs.get("catalogue")

case_type_identification = attrs["case_type_identification"]

# legacy
case_type_url = attrs["zaaktype"]
document_type_url = attrs["informatieobjecttype"]

Expand Down Expand Up @@ -221,8 +248,19 @@ def _validate_catalogue_case_and_doc_type(
code="not-found",
)

# DB check constraint + serializer validation guarantee that both or none
# are empty at the same time
# DB check constraint + serializer validation guarantee that both `domain` and
# `rsin` or none of them are empty at the same time
if case_type_identification and (not domain):
raise serializers.ValidationError(
{
"catalogue": _(
"You must specify a catalogue when passing a case type "
"identification"
),
},
code="required",
)

if domain and rsin:
catalogus = client.find_catalogus(domain=domain, rsin=rsin)
if catalogus is None:
Expand All @@ -236,8 +274,20 @@ def _validate_catalogue_case_and_doc_type(
code="invalid-catalogue",
)

if case_type_url not in catalogus["zaaktypen"]:
# if a case type identification is provided, we validate (and use) it, otherwise
# we must fall back to the legacy zaaktype url. Earlier validation guarantees
# either one is provided (possibly both, but then we ignore the legacy URL).
if case_type_identification:
case_type_versions = client.find_case_types(
catalogus=catalogus["url"],
identification=case_type_identification,
)
if case_type_versions is None:
_errors["case_type_identification"] = err_invalid_case_type
elif case_type_url not in catalogus["zaaktypen"]:
_errors["zaaktype"] = err_invalid_case_type

# Validate document type reference
if document_type_url not in catalogus["informatieobjecttypen"]:
_errors["informatieobjecttype"] = err_invalid_document_type

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
interactions:
- request:
body: null
headers:
Accept:
- '*/*'
Accept-Encoding:
- gzip, deflate, br
Authorization:
- Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ0ZXN0X2NsaWVudF9pZCIsImlhdCI6MTcyNTYzMzg5OCwiY2xpZW50X2lkIjoidGVzdF9jbGllbnRfaWQiLCJ1c2VyX2lkIjoiIiwidXNlcl9yZXByZXNlbnRhdGlvbiI6IiJ9.rI4Ta2RDZrQEx6ZBKtg9jkgA8rpDt8CoAiSXzK6I-Zk
Connection:
- keep-alive
User-Agent:
- python-requests/2.32.2
method: GET
uri: http://localhost:8003/catalogi/api/v1/catalogussen?domein=TEST&rsin=000000000
response:
body:
string: '{"count":1,"next":null,"previous":null,"results":[{"url":"http://localhost:8003/catalogi/api/v1/catalogussen/bd58635c-793e-446d-a7e0-460d7b04829d","domein":"TEST","rsin":"000000000","contactpersoonBeheerNaam":"Test
name","contactpersoonBeheerTelefoonnummer":"","contactpersoonBeheerEmailadres":"","zaaktypen":["http://localhost:8003/catalogi/api/v1/zaaktypen/b79d9c2f-5ec4-4e23-bb66-ec6dd959a400","http://localhost:8003/catalogi/api/v1/zaaktypen/1f41885e-23fc-4462-bbc8-80be4ae484dc"],"besluittypen":[],"informatieobjecttypen":["http://localhost:8003/catalogi/api/v1/informatieobjecttypen/7a474713-0833-402a-8441-e467c08ac55b","http://localhost:8003/catalogi/api/v1/informatieobjecttypen/b2d83b94-9b9b-4e80-a82f-73ff993c62f3","http://localhost:8003/catalogi/api/v1/informatieobjecttypen/531f6c1a-97f7-478c-85f0-67d2f23661c7","http://localhost:8003/catalogi/api/v1/informatieobjecttypen/29b63e5c-3835-4f68-8fad-f2aea9ae6b71"],"naam":"Test
catalog","versie":"","begindatumVersie":null}]}'
headers:
API-version:
- 1.3.1
Allow:
- GET, POST, HEAD, OPTIONS
Content-Length:
- '985'
Content-Type:
- application/json
Cross-Origin-Opener-Policy:
- same-origin
Referrer-Policy:
- same-origin
Vary:
- Accept, origin
X-Content-Type-Options:
- nosniff
X-Frame-Options:
- DENY
status:
code: 200
message: OK
- request:
body: null
headers:
Accept:
- '*/*'
Accept-Encoding:
- gzip, deflate, br
Authorization:
- Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ0ZXN0X2NsaWVudF9pZCIsImlhdCI6MTcyNTYzMzg5OCwiY2xpZW50X2lkIjoidGVzdF9jbGllbnRfaWQiLCJ1c2VyX2lkIjoiIiwidXNlcl9yZXByZXNlbnRhdGlvbiI6IiJ9.rI4Ta2RDZrQEx6ZBKtg9jkgA8rpDt8CoAiSXzK6I-Zk
Connection:
- keep-alive
User-Agent:
- python-requests/2.32.2
method: GET
uri: http://localhost:8003/catalogi/api/v1/zaaktypen?catalogus=http%3A%2F%2Flocalhost%3A8003%2Fcatalogi%2Fapi%2Fv1%2Fcatalogussen%2Fbd58635c-793e-446d-a7e0-460d7b04829d&identificatie=ZT-001
response:
body:
string: '{"count":2,"next":null,"previous":null,"results":[{"url":"http://localhost:8003/catalogi/api/v1/zaaktypen/b79d9c2f-5ec4-4e23-bb66-ec6dd959a400","identificatie":"ZT-001","omschrijving":"Test","omschrijvingGeneriek":"","vertrouwelijkheidaanduiding":"intern","doel":"testen","aanleiding":"integratietests","toelichting":"","indicatieInternOfExtern":"intern","handelingInitiator":"Formulier
indienen","onderwerp":"Testformulier","handelingBehandelaar":"Controleren","doorlooptijd":"P1D","servicenorm":null,"opschortingEnAanhoudingMogelijk":false,"verlengingMogelijk":false,"verlengingstermijn":null,"trefwoorden":[],"publicatieIndicatie":false,"publicatietekst":"","verantwoordingsrelatie":[],"productenOfDiensten":[],"selectielijstProcestype":"https://selectielijst.openzaak.nl/api/v1/procestypen/aa8aa2fd-b9c6-4e34-9a6c-58a677f60ea0","referentieproces":{"naam":"Testen","link":""},"concept":false,"verantwoordelijke":"Ontwikkelaar","beginGeldigheid":"2023-01-01","eindeGeldigheid":"2024-03-26","versiedatum":"2023-01-01","beginObject":"2023-01-01","eindeObject":null,"catalogus":"http://localhost:8003/catalogi/api/v1/catalogussen/bd58635c-793e-446d-a7e0-460d7b04829d","statustypen":["http://localhost:8003/catalogi/api/v1/statustypen/d1ce96ef-325d-4e2f-a325-d7ed017f3b81","http://localhost:8003/catalogi/api/v1/statustypen/658becc5-fab6-43ad-8289-2beff5c65945"],"resultaattypen":["http://localhost:8003/catalogi/api/v1/resultaattypen/ec03d4e6-c010-4163-9c05-8247def5f45c"],"eigenschappen":["http://localhost:8003/catalogi/api/v1/eigenschappen/7eada907-ffb7-4846-9494-68eb1452182c"],"informatieobjecttypen":["http://localhost:8003/catalogi/api/v1/informatieobjecttypen/531f6c1a-97f7-478c-85f0-67d2f23661c7"],"roltypen":["http://localhost:8003/catalogi/api/v1/roltypen/0333eec0-6240-4f90-adf9-8f98edcd5563","http://localhost:8003/catalogi/api/v1/roltypen/cebf7a53-297d-4308-869a-4385cfc42549"],"besluittypen":[],"deelzaaktypen":[],"gerelateerdeZaaktypen":[],"zaakobjecttypen":[]},{"url":"http://localhost:8003/catalogi/api/v1/zaaktypen/1f41885e-23fc-4462-bbc8-80be4ae484dc","identificatie":"ZT-001","omschrijving":"Test","omschrijvingGeneriek":"","vertrouwelijkheidaanduiding":"intern","doel":"testen","aanleiding":"integratietests","toelichting":"","indicatieInternOfExtern":"intern","handelingInitiator":"Formulier
indienen","onderwerp":"Testformulier","handelingBehandelaar":"Controleren","doorlooptijd":"P1D","servicenorm":null,"opschortingEnAanhoudingMogelijk":false,"verlengingMogelijk":false,"verlengingstermijn":null,"trefwoorden":[],"publicatieIndicatie":false,"publicatietekst":"","verantwoordingsrelatie":[],"productenOfDiensten":[],"selectielijstProcestype":"https://selectielijst.openzaak.nl/api/v1/procestypen/aa8aa2fd-b9c6-4e34-9a6c-58a677f60ea0","referentieproces":{"naam":"Testen","link":""},"concept":false,"verantwoordelijke":"Ontwikkelaar","beginGeldigheid":"2024-03-26","eindeGeldigheid":null,"versiedatum":"2024-03-26","beginObject":"2023-01-01","eindeObject":null,"catalogus":"http://localhost:8003/catalogi/api/v1/catalogussen/bd58635c-793e-446d-a7e0-460d7b04829d","statustypen":["http://localhost:8003/catalogi/api/v1/statustypen/1de05b57-a938-47e4-b808-f129c6406b60","http://localhost:8003/catalogi/api/v1/statustypen/6443ac1a-04a1-4335-9db2-5f3c998dbb34"],"resultaattypen":["http://localhost:8003/catalogi/api/v1/resultaattypen/65b7cedd-5729-41bd-b9c7-1f51d7583340"],"eigenschappen":["http://localhost:8003/catalogi/api/v1/eigenschappen/b659caed-e39e-47e3-ac51-bc8bd2ad797e"],"informatieobjecttypen":["http://localhost:8003/catalogi/api/v1/informatieobjecttypen/531f6c1a-97f7-478c-85f0-67d2f23661c7"],"roltypen":["http://localhost:8003/catalogi/api/v1/roltypen/43e8026c-8abd-4b29-8a4c-ac2a37bc6f5b","http://localhost:8003/catalogi/api/v1/roltypen/7f1887e8-bf22-47e7-ae52-ed6848d7e70e"],"besluittypen":[],"deelzaaktypen":[],"gerelateerdeZaaktypen":[],"zaakobjecttypen":[]}]}'
headers:
API-version:
- 1.3.1
Allow:
- GET, POST, HEAD, OPTIONS
Content-Length:
- '3897'
Content-Type:
- application/json
Cross-Origin-Opener-Policy:
- same-origin
Referrer-Policy:
- same-origin
Vary:
- Accept, origin
X-Content-Type-Options:
- nosniff
X-Frame-Options:
- DENY
status:
code: 200
message: OK
- request:
body: null
headers:
Accept:
- '*/*'
Accept-Encoding:
- gzip, deflate, br
Authorization:
- Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ0ZXN0X2NsaWVudF9pZCIsImlhdCI6MTcyNTYzMzg5OSwiY2xpZW50X2lkIjoidGVzdF9jbGllbnRfaWQiLCJ1c2VyX2lkIjoiIiwidXNlcl9yZXByZXNlbnRhdGlvbiI6IiJ9.Fl9CZ5wz2siFyBGc-VoMQ3Zve4XWYtm288sAgjVfkWE
Connection:
- keep-alive
User-Agent:
- python-requests/2.32.2
method: GET
uri: http://localhost:8003/catalogi/api/v1/catalogussen?domein=TEST&rsin=000000000
response:
body:
string: '{"count":1,"next":null,"previous":null,"results":[{"url":"http://localhost:8003/catalogi/api/v1/catalogussen/bd58635c-793e-446d-a7e0-460d7b04829d","domein":"TEST","rsin":"000000000","contactpersoonBeheerNaam":"Test
name","contactpersoonBeheerTelefoonnummer":"","contactpersoonBeheerEmailadres":"","zaaktypen":["http://localhost:8003/catalogi/api/v1/zaaktypen/b79d9c2f-5ec4-4e23-bb66-ec6dd959a400","http://localhost:8003/catalogi/api/v1/zaaktypen/1f41885e-23fc-4462-bbc8-80be4ae484dc"],"besluittypen":[],"informatieobjecttypen":["http://localhost:8003/catalogi/api/v1/informatieobjecttypen/7a474713-0833-402a-8441-e467c08ac55b","http://localhost:8003/catalogi/api/v1/informatieobjecttypen/b2d83b94-9b9b-4e80-a82f-73ff993c62f3","http://localhost:8003/catalogi/api/v1/informatieobjecttypen/531f6c1a-97f7-478c-85f0-67d2f23661c7","http://localhost:8003/catalogi/api/v1/informatieobjecttypen/29b63e5c-3835-4f68-8fad-f2aea9ae6b71"],"naam":"Test
catalog","versie":"","begindatumVersie":null}]}'
headers:
API-version:
- 1.3.1
Allow:
- GET, POST, HEAD, OPTIONS
Content-Length:
- '985'
Content-Type:
- application/json
Cross-Origin-Opener-Policy:
- same-origin
Referrer-Policy:
- same-origin
Vary:
- Accept, origin
X-Content-Type-Options:
- nosniff
X-Frame-Options:
- DENY
status:
code: 200
message: OK
- request:
body: null
headers:
Accept:
- '*/*'
Accept-Encoding:
- gzip, deflate, br
Authorization:
- Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ0ZXN0X2NsaWVudF9pZCIsImlhdCI6MTcyNTYzMzg5OSwiY2xpZW50X2lkIjoidGVzdF9jbGllbnRfaWQiLCJ1c2VyX2lkIjoiIiwidXNlcl9yZXByZXNlbnRhdGlvbiI6IiJ9.Fl9CZ5wz2siFyBGc-VoMQ3Zve4XWYtm288sAgjVfkWE
Connection:
- keep-alive
User-Agent:
- python-requests/2.32.2
method: GET
uri: http://localhost:8003/catalogi/api/v1/zaaktypen?catalogus=http%3A%2F%2Flocalhost%3A8003%2Fcatalogi%2Fapi%2Fv1%2Fcatalogussen%2Fbd58635c-793e-446d-a7e0-460d7b04829d&identificatie=i-am-a-bad-reference
response:
body:
string: '{"count":0,"next":null,"previous":null,"results":[]}'
headers:
API-version:
- 1.3.1
Allow:
- GET, POST, HEAD, OPTIONS
Content-Length:
- '52'
Content-Type:
- application/json
Cross-Origin-Opener-Policy:
- same-origin
Referrer-Policy:
- same-origin
Vary:
- Accept, origin
X-Content-Type-Options:
- nosniff
X-Frame-Options:
- DENY
status:
code: 200
message: OK
version: 1
Loading

0 comments on commit 8c4c27d

Please sign in to comment.