diff --git a/docker/open-zaak/fixtures/open_zaak_fixtures.json b/docker/open-zaak/fixtures/open_zaak_fixtures.json index a6052675ec..f775fa0987 100644 --- a/docker/open-zaak/fixtures/open_zaak_fixtures.json +++ b/docker/open-zaak/fixtures/open_zaak_fixtures.json @@ -28,7 +28,7 @@ "model": "catalogi.catalogus", "pk": 1, "fields": { - "_etag": "ef26c6f8e32dd9cd50809442b635bb26", + "_etag": "6cecca4fea6af5d8acc582e1c52ce24b", "naam": "Test catalog", "uuid": "bd58635c-793e-446d-a7e0-460d7b04829d", "domein": "TEST", @@ -44,7 +44,7 @@ "model": "catalogi.catalogus", "pk": 2, "fields": { - "_etag": "bed1ecff53402298e98e22eaa1756480", + "_etag": "c7d9351cfa9d1da17c623527f985d3ab", "naam": "Test catalog 2", "uuid": "630271f6-568a-485e-b1c4-4ed2d6ab3a58", "domein": "OTHER", @@ -428,6 +428,66 @@ "zaakobjecttypen": [] } }, +{ + "model": "catalogi.resultaattype", + "pk": 5, + "fields": { + "_etag": "8a969419db8fe099962d12872966b597", + "datum_begin_geldigheid": null, + "datum_einde_geldigheid": null, + "uuid": "5c8c7613-8ccc-4f17-836b-bd92489c77fd", + "zaaktype": 6, + "omschrijving": "Done", + "resultaattypeomschrijving": "https://selectielijst.openzaak.nl/api/v1/resultaattypeomschrijvingen/43ac0469-e0c0-4985-a6ce-8ec285d2df47", + "omschrijving_generiek": "Vastgesteld", + "selectielijstklasse": "https://selectielijst.openzaak.nl/api/v1/resultaten/afa30940-855b-4a7e-aa21-9e15a8078814", + "archiefnominatie": "vernietigen", + "archiefactietermijn": "P10Y", + "brondatum_archiefprocedure_afleidingswijze": "afgehandeld", + "brondatum_archiefprocedure_datumkenmerk": "", + "brondatum_archiefprocedure_einddatum_bekend": false, + "brondatum_archiefprocedure_objecttype": "", + "brondatum_archiefprocedure_registratie": "", + "brondatum_archiefprocedure_procestermijn": "P0D", + "toelichting": "", + "procesobjectaard": "", + "indicatie_specifiek": null, + "procestermijn": null, + "informatieobjecttypen": [], + "besluittypen": [], + "zaakobjecttypen": [] + } +}, +{ + "model": "catalogi.resultaattype", + "pk": 6, + "fields": { + "_etag": "4571f087efcb36b2bcfb8e1ea6b46d62", + "datum_begin_geldigheid": null, + "datum_einde_geldigheid": null, + "uuid": "dddb1de8-a50a-477a-8e2e-ca0f69809033", + "zaaktype": 7, + "omschrijving": "Geslaagd", + "resultaattypeomschrijving": "https://selectielijst.openzaak.nl/api/v1/resultaattypeomschrijvingen/ce8cf476-0b59-496f-8eee-957a7c6e2506", + "omschrijving_generiek": "Afgebroken", + "selectielijstklasse": "https://selectielijst.openzaak.nl/api/v1/resultaten/afa30940-855b-4a7e-aa21-9e15a8078814", + "archiefnominatie": "vernietigen", + "archiefactietermijn": "P10Y", + "brondatum_archiefprocedure_afleidingswijze": "afgehandeld", + "brondatum_archiefprocedure_datumkenmerk": "", + "brondatum_archiefprocedure_einddatum_bekend": false, + "brondatum_archiefprocedure_objecttype": "", + "brondatum_archiefprocedure_registratie": "", + "brondatum_archiefprocedure_procestermijn": "P0D", + "toelichting": "", + "procesobjectaard": "", + "indicatie_specifiek": null, + "procestermijn": null, + "informatieobjecttypen": [], + "besluittypen": [], + "zaakobjecttypen": [] + } +}, { "model": "catalogi.roltype", "pk": 1, @@ -506,6 +566,84 @@ "zaaktype": 5 } }, +{ + "model": "catalogi.roltype", + "pk": 9, + "fields": { + "_etag": "9d04e7c378c56302eb00326e6ab1d3f7", + "datum_begin_geldigheid": null, + "datum_einde_geldigheid": null, + "uuid": "5cdeb40f-1e28-415b-b826-1b36dac54232", + "omschrijving": "Draft", + "omschrijving_generiek": "belanghebbende", + "zaaktype": 2 + } +}, +{ + "model": "catalogi.roltype", + "pk": 10, + "fields": { + "_etag": "aa54e599dca5d07546697d0b179834b4", + "datum_begin_geldigheid": null, + "datum_einde_geldigheid": null, + "uuid": "b79db762-5f02-43ea-b550-6de3519e7057", + "omschrijving": "Baliemedewerker", + "omschrijving_generiek": "klantcontacter", + "zaaktype": 2 + } +}, +{ + "model": "catalogi.roltype", + "pk": 11, + "fields": { + "_etag": "4c6a59a0e427496fd8975b70486fcfc7", + "datum_begin_geldigheid": null, + "datum_einde_geldigheid": null, + "uuid": "d2a3dcde-e555-4a4a-83f3-7b1505ec6c9b", + "omschrijving": "Initiator", + "omschrijving_generiek": "initiator", + "zaaktype": 6 + } +}, +{ + "model": "catalogi.roltype", + "pk": 12, + "fields": { + "_etag": "f0130713b20054fcb8dcdb6f658449b9", + "datum_begin_geldigheid": null, + "datum_einde_geldigheid": null, + "uuid": "a2700808-67e1-4db5-847e-2fb223b6c8f0", + "omschrijving": "Baliemedewerker", + "omschrijving_generiek": "klantcontacter", + "zaaktype": 6 + } +}, +{ + "model": "catalogi.roltype", + "pk": 13, + "fields": { + "_etag": "cd5809383c4e56c095fd273b5258b475", + "datum_begin_geldigheid": null, + "datum_einde_geldigheid": null, + "uuid": "51199bc6-024f-4997-8199-733fd2d478f0", + "omschrijving": "Initiator", + "omschrijving_generiek": "initiator", + "zaaktype": 7 + } +}, +{ + "model": "catalogi.roltype", + "pk": 14, + "fields": { + "_etag": "9dfa8ad719882e96bc078be4f351aca3", + "datum_begin_geldigheid": null, + "datum_einde_geldigheid": null, + "uuid": "877f0745-5292-4336-a6da-a5596c564aae", + "omschrijving": "Baliemedewerker", + "omschrijving_generiek": "klantcontacter", + "zaaktype": 7 + } +}, { "model": "catalogi.statustype", "pk": 1, @@ -614,6 +752,78 @@ "toelichting": null } }, +{ + "model": "catalogi.statustype", + "pk": 9, + "fields": { + "_etag": "0e25982cec3c27f4b7936e139318c251", + "datum_begin_geldigheid": null, + "datum_einde_geldigheid": null, + "uuid": "ec6587a5-0b91-427b-88a8-7aec1c10ff83", + "zaaktype": 6, + "statustype_omschrijving": "Opened", + "statustype_omschrijving_generiek": "", + "statustypevolgnummer": 10, + "doorlooptijd": null, + "informeren": false, + "statustekst": "", + "toelichting": null + } +}, +{ + "model": "catalogi.statustype", + "pk": 10, + "fields": { + "_etag": "6e489710f26b99e30e07b92fea5a575b", + "datum_begin_geldigheid": null, + "datum_einde_geldigheid": null, + "uuid": "16de698f-cc2f-4aa8-bb07-8ccfc71a4da3", + "zaaktype": 6, + "statustype_omschrijving": "Closed", + "statustype_omschrijving_generiek": "", + "statustypevolgnummer": 90, + "doorlooptijd": null, + "informeren": false, + "statustekst": "", + "toelichting": null + } +}, +{ + "model": "catalogi.statustype", + "pk": 11, + "fields": { + "_etag": "3d70d877cb2dcbfaf0d3932dc8145c34", + "datum_begin_geldigheid": null, + "datum_einde_geldigheid": null, + "uuid": "b4bfbe27-a332-4e48-adf3-b4005d5c5fb5", + "zaaktype": 7, + "statustype_omschrijving": "Ontvangen", + "statustype_omschrijving_generiek": "", + "statustypevolgnummer": 1, + "doorlooptijd": null, + "informeren": false, + "statustekst": "", + "toelichting": null + } +}, +{ + "model": "catalogi.statustype", + "pk": 12, + "fields": { + "_etag": "26f40737919a7f9817d845cc4c42a9a3", + "datum_begin_geldigheid": null, + "datum_einde_geldigheid": null, + "uuid": "a36f3a85-7499-4d09-b154-cdf832ebb96d", + "zaaktype": 7, + "statustype_omschrijving": "Afgerond", + "statustype_omschrijving_generiek": "", + "statustypevolgnummer": 5, + "doorlooptijd": null, + "informeren": false, + "statustekst": "", + "toelichting": null + } +}, { "model": "catalogi.zaaktype", "pk": 1, @@ -664,7 +874,7 @@ "model": "catalogi.zaaktype", "pk": 2, "fields": { - "_etag": "0a14ada23852340b534f64d20764ac78", + "_etag": "6655847c7dcad71dba48bcfc3c07a0c3", "datum_begin_geldigheid": "2023-01-01", "datum_einde_geldigheid": null, "concept": true, @@ -797,5 +1007,97 @@ "catalogus": 1, "deelzaaktypen": [] } +}, +{ + "model": "catalogi.zaaktype", + "pk": 6, + "fields": { + "_etag": "40da09c62d48854f504ac9dd5a3a3222", + "datum_begin_geldigheid": "2024-11-29", + "datum_einde_geldigheid": null, + "concept": false, + "uuid": "fcc3499a-04d4-421c-b115-7e3cba2127ac", + "identificatie": "ZT-002", + "zaaktype_omschrijving": "Test 2", + "zaaktype_omschrijving_generiek": "", + "vertrouwelijkheidaanduiding": "zaakvertrouwelijk", + "doel": "Tests", + "aanleiding": "Tests", + "toelichting": "", + "indicatie_intern_of_extern": "intern", + "handeling_initiator": "Tests", + "onderwerp": "Tests", + "handeling_behandelaar": "Tests", + "doorlooptijd_behandeling": "P1M", + "servicenorm_behandeling": "P0D", + "opschorting_en_aanhouding_mogelijk": false, + "verlenging_mogelijk": false, + "verlengingstermijn": null, + "trefwoorden": "[]", + "publicatie_indicatie": false, + "publicatietekst": "", + "verantwoordingsrelatie": "[]", + "versiedatum": "2024-11-29", + "verantwoordelijke": "Ontwikkelaar", + "producten_of_diensten": "[]", + "selectielijst_procestype": "https://selectielijst.openzaak.nl/api/v1/procestypen/aa8aa2fd-b9c6-4e34-9a6c-58a677f60ea0", + "selectielijst_procestype_jaar": 2020, + "referentieproces_naam": "Testen", + "referentieproces_link": "", + "broncatalogus_url": "", + "broncatalogus_domein": "", + "broncatalogus_rsin": "", + "bronzaaktype_url": "", + "bronzaaktype_identificatie": "", + "bronzaaktype_omschrijving": "", + "catalogus": 1, + "deelzaaktypen": [] + } +}, +{ + "model": "catalogi.zaaktype", + "pk": 7, + "fields": { + "_etag": "5ed05ed8660025b30056f24ace9af15e", + "datum_begin_geldigheid": "2024-11-29", + "datum_einde_geldigheid": null, + "concept": false, + "uuid": "cc1d39d9-05a2-45f9-8cfe-230790d36f71", + "identificatie": "ZT-001", + "zaaktype_omschrijving": "Duplicate identification, other catalogue", + "zaaktype_omschrijving_generiek": "", + "vertrouwelijkheidaanduiding": "zaakvertrouwelijk", + "doel": "Tests", + "aanleiding": "Tests", + "toelichting": "", + "indicatie_intern_of_extern": "intern", + "handeling_initiator": "Tests", + "onderwerp": "Tests", + "handeling_behandelaar": "Tests", + "doorlooptijd_behandeling": "P1M", + "servicenorm_behandeling": "P0D", + "opschorting_en_aanhouding_mogelijk": false, + "verlenging_mogelijk": false, + "verlengingstermijn": null, + "trefwoorden": "[]", + "publicatie_indicatie": false, + "publicatietekst": "", + "verantwoordingsrelatie": "[]", + "versiedatum": "2024-11-29", + "verantwoordelijke": "Ontwikkelaar", + "producten_of_diensten": "[]", + "selectielijst_procestype": "https://selectielijst.openzaak.nl/api/v1/procestypen/aa8aa2fd-b9c6-4e34-9a6c-58a677f60ea0", + "selectielijst_procestype_jaar": 2020, + "referentieproces_naam": "Testen", + "referentieproces_link": "", + "broncatalogus_url": "", + "broncatalogus_domein": "", + "broncatalogus_rsin": "", + "bronzaaktype_url": "", + "bronzaaktype_identificatie": "", + "bronzaaktype_omschrijving": "", + "catalogus": 2, + "deelzaaktypen": [] + } } ] diff --git a/src/openapi.yaml b/src/openapi.yaml index 2a1e223c33..1d81cf358d 100644 --- a/src/openapi.yaml +++ b/src/openapi.yaml @@ -4087,6 +4087,62 @@ paths: $ref: '#/components/headers/X-Is-Form-Designer' Content-Language: $ref: '#/components/headers/Content-Language' + /api/v2/registration/plugins/zgw-api/role-types: + get: + operationId: registration_plugins_zgw_api_role_types_list + description: |- + Fetch and serialize a list of objects. + + Alternative to :class:`rest_framework.mixins.ListModelMixin` for non-database + backed collections of objects. + summary: List the available role types bound to a case type within a catalogue + (ZGW APIs) + parameters: + - in: query + name: case_type_identification + schema: + type: string + minLength: 1 + description: Filter case types against this identification. + required: true + - in: query + name: catalogue_url + schema: + type: string + format: uri + title: catalogus URL + minLength: 1 + description: Filter case types against this catalogue URL. + required: true + - in: query + name: zgw_api_group + schema: + type: integer + description: The primary key of the ZGW API group to use. The informatieobjecttypen + from the Catalogi API in this group will be returned. + required: true + tags: + - registration + security: + - cookieAuth: [] + responses: + '200': + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/RoleType' + description: '' + headers: + X-Session-Expires-In: + $ref: '#/components/headers/X-Session-Expires-In' + X-CSRFToken: + $ref: '#/components/headers/X-CSRFToken' + X-Is-Form-Designer: + $ref: '#/components/headers/X-Is-Form-Designer' + Content-Language: + $ref: '#/components/headers/Content-Language' /api/v2/service-fetch-configurations: get: operationId: service_fetch_configurations_list @@ -9728,6 +9784,21 @@ components: required: - x - y + RoleType: + type: object + properties: + description: + type: string + description: The description/label given to the role type in the Catalogi + API. It identifies the role type within a case type. + descriptionGeneric: + type: string + title: generic description + description: One of the pre-determined generic descriptions, such as 'behandelaar' + or 'belanghebbende'. + required: + - description + - descriptionGeneric Service: type: object properties: diff --git a/src/openforms/contrib/zgw/api/serializers.py b/src/openforms/contrib/zgw/api/serializers.py index 79ed39bbe9..d5e5f3bacb 100644 --- a/src/openforms/contrib/zgw/api/serializers.py +++ b/src/openforms/contrib/zgw/api/serializers.py @@ -81,6 +81,23 @@ class DocumentTypeSerializer(serializers.Serializer): ) +class RoleTypeSerializer(serializers.Serializer): + description = serializers.CharField( + label=_("description"), + help_text=_( + "The description/label given to the role type in the Catalogi API. It " + "identifies the role type within a case type." + ), + ) + description_generic = serializers.CharField( + label=_("generic description"), + help_text=_( + "One of the pre-determined generic descriptions, such as 'behandelaar' " + "or 'belanghebbende'." + ), + ) + + class CaseTypeProductSerializer(serializers.Serializer): url = serializers.CharField( label=_("url"), diff --git a/src/openforms/contrib/zgw/clients/catalogi.py b/src/openforms/contrib/zgw/clients/catalogi.py index a6ab02329f..352ea7beca 100644 --- a/src/openforms/contrib/zgw/clients/catalogi.py +++ b/src/openforms/contrib/zgw/clients/catalogi.py @@ -50,6 +50,7 @@ class CaseType(TypedDict): concept: NotRequired[bool] productenOfDiensten: list[str] # URL pointers to products informatieobjecttypen: NotRequired[list[str]] # URL pointers to document types + roltypen: NotRequired[list[str]] # URL pointers to role types class InformatieObjectType(TypedDict): @@ -63,6 +64,28 @@ class InformatieObjectType(TypedDict): concept: NotRequired[bool] +type PublicationStatusFilter = Literal["alles", "concept", "definitief"] + +type RoleDescriptionGeneric = Literal[ + "adviseur", + "behandelaar", + "belanghebbende", + "beslisser", + "initiator", + "klantcontacter", + "zaakcoordinator", + "mede_initiator", +] + + +class RoleType(TypedDict): + url: str + zaaktype: str + zaaktypeIdentificatie: NotRequired[str] # since 1.2 + omschrijving: str + omschrijvingGeneriek: RoleDescriptionGeneric + + class EigenschapSpecificatie(TypedDict): groep: NotRequired[str] formaat: Literal["tekst", "getal", "datum", "datum_tijd"] @@ -91,19 +114,28 @@ class Eigenschap(TypedDict): class CaseTypeListParams(TypedDict, total=False): catalogus: str identificatie: str - status: Literal["alles", "concept", "definitief"] + status: PublicationStatusFilter datumGeldigheid: str page: int class InformatieObjectTypeListParams(TypedDict, total=False): catalogus: str - status: Literal["alles", "concept", "definitief"] + status: PublicationStatusFilter omschrijving: str datumGeldigheid: str page: int +class RoleTypeListParams(TypedDict, total=False): + zaaktype: str + zaaktypeIdentificatie: str # from 1.2 onwards + omschrijvingGeneriek: RoleDescriptionGeneric + status: PublicationStatusFilter + datumGeldigheid: str + page: int + + class CatalogiClient(NLXClient): _api_version: CatalogiAPIVersion | None = None @@ -325,6 +357,69 @@ def list_statustypen(self, zaaktype: str) -> list[dict]: results = response.json()["results"] return results + def get_all_role_types( + self, + *, + catalogus: str, + within_casetype: str, + ) -> Iterator[RoleType]: + # get the case types so that we are filtering within the right catalogue, as + # the same case type identification may be defined in different catalogues + case_type_versions = ( + self.find_case_types( + catalogus=catalogus, + identification=within_casetype, + ) + or [] + ) + all_valid_roltype_urls: list[str] = sum( + ( + case_type_version.get("roltypen", []) + for case_type_version in case_type_versions + ), + [], + ) + if not all_valid_roltype_urls: + return [] + + _supports_filtering_case_type_identification = self.api_version >= (1, 2, 0) + params: RoleTypeListParams = {} + if self.allow_drafts: + params["status"] = "alles" + + def _iter_case_type_versions() -> Iterator[PaginatedResponseData[RoleType]]: + data: PaginatedResponseData[RoleType] + + if _supports_filtering_case_type_identification: + params["zaaktypeIdentificatie"] = within_casetype + + response = self.get("roltypen", params=params) # type: ignore + response.raise_for_status() + data = response.json() + yield data + + # fallback for old versions of the Catalogi API - get the roltypes for each + # retrieved version of the case type + else: + for case_type_version in case_type_versions: + params["zaaktype"] = case_type_version["url"] + + response = self.get("roltypen", params=params) # type: ignore + response.raise_for_status() + data = response.json() + yield data + + # for 1.2.0+, this effectively just gets all the role types in one batch by + # looping over all pages. + # for older versions, this will loop over all versions of the case type in the + # outer loop, and the inner loop will process all result pages for that + # particular case type version before moving on to the next version + for data in _iter_case_type_versions(): + for role_type in pagination_helper(self, data): + if role_type["url"] not in all_valid_roltype_urls: + continue + yield role_type + def list_roltypen( self, zaaktype: str, diff --git a/src/openforms/contrib/zgw/tests/test_catalogi_client.py b/src/openforms/contrib/zgw/tests/test_catalogi_client.py index 01a34cc25f..68e2b88218 100644 --- a/src/openforms/contrib/zgw/tests/test_catalogi_client.py +++ b/src/openforms/contrib/zgw/tests/test_catalogi_client.py @@ -293,3 +293,95 @@ def test_server_supports_filtering_case_types_but_doesnt_enforce_unique_versions identification="ZT-007", valid_on=date(2024, 8, 8), ) + + @requests_mock.Mocker() + def test_get_role_types_before_v12(self, m: requests_mock.Mocker): + client = CatalogiClient(base_url="https://dummy/") + client._api_version = (1, 0, 0) + # mocks to get available zaaktypen + zt_endpoint = furl("https://dummy/zaaktypen").set( + { + "catalogus": "https://dummy/catalogus", + "identificatie": "ZT-007", + } + ) + m.get( + str(zt_endpoint), + headers={"API-Version": "1.0.0"}, + json={ + "next": None, + "previous": None, + "count": 2, + "results": [ + { + "url": "https://dummy/api/v1/zaaktypen/1", + "identificatie": "ZT-007", + "beginGeldigheid": "2023-01-01", + "eindeGeldigheid": "2023-12-31", + "roltypen": [ + "https://dummy/api/v1/roltypen/1", + ], + }, + { + "url": "https://dummy/api/v1/zaaktypen/2", + "identificatie": "ZT-007", + "beginGeldigheid": "2024-01-01", + "roltypen": [ + "https://dummy/api/v1/roltypen/2", + "https://dummy/api/v1/roltypen/3", + ], + }, + ], + }, + ) + + # mocks for two zaaktype versions + endpoint = furl("https://dummy/roltypen") + m.get( + str(endpoint.set({"zaaktype": "https://dummy/api/v1/zaaktypen/1"})), + headers={"API-Version": "1.0.0"}, + json={ + "next": None, + "previous": None, + "count": 1, + "results": [ + { + "url": "https://dummy/api/v1/roltypen/1", + "zaaktype": "https://dummy/api/v1/zaaktypen/1", + "omschrijving": "Baliemedewerker", + "omschrijvingGeneriek": "klantcontacter", + }, + ], + }, + ) + m.get( + str(endpoint.set({"zaaktype": "https://dummy/api/v1/zaaktypen/2"})), + headers={"API-Version": "1.0.0"}, + json={ + "next": None, + "previous": None, + "count": 2, + "results": [ + { + "url": "https://dummy/api/v1/roltypen/2", + "zaaktype": "https://dummy/api/v1/zaaktypen/2", + "omschrijving": "Baliemedewerker", + "omschrijvingGeneriek": "klantcontacter", + }, + { + "url": "https://dummy/api/v1/roltypen/3", + "zaaktype": "https://dummy/api/v1/zaaktypen/2", + "omschrijving": "Behandelaar", + "omschrijvingGeneriek": "behandelaar", + }, + ], + }, + ) + + results = client.get_all_role_types( + catalogus="https://dummy/catalogus", + within_casetype="ZT-007", + ) + + all_results = list(results) + self.assertEqual(len(all_results), 3) diff --git a/src/openforms/js/compiled-lang/en.json b/src/openforms/js/compiled-lang/en.json index 56c6b49f27..19f6fc0f02 100644 --- a/src/openforms/js/compiled-lang/en.json +++ b/src/openforms/js/compiled-lang/en.json @@ -1237,6 +1237,12 @@ "value": "Confidentiality" } ], + "AdyBdF": [ + { + "type": 0, + "value": "Something went wrong while retrieving the available role types defined in the selected case. Please check that the services in the selected API group are configured correctly." + } + ], "AtBVAV": [ { "type": 0, diff --git a/src/openforms/js/compiled-lang/nl.json b/src/openforms/js/compiled-lang/nl.json index 5adf4e2df3..0c6c0e3eca 100644 --- a/src/openforms/js/compiled-lang/nl.json +++ b/src/openforms/js/compiled-lang/nl.json @@ -1241,6 +1241,12 @@ "value": "Vertrouwelijkheidaanduiding" } ], + "AdyBdF": [ + { + "type": 0, + "value": "Something went wrong while retrieving the available role types defined in the selected case. Please check that the services in the selected API group are configured correctly." + } + ], "AtBVAV": [ { "type": 0, diff --git a/src/openforms/js/components/admin/form_design/RegistrationFields.stories.js b/src/openforms/js/components/admin/form_design/RegistrationFields.stories.js index a2ba06bcbc..54035a01e9 100644 --- a/src/openforms/js/components/admin/form_design/RegistrationFields.stories.js +++ b/src/openforms/js/components/admin/form_design/RegistrationFields.stories.js @@ -9,8 +9,10 @@ import { } from 'components/admin/form_design/registrations/objectsapi/mocks'; import { mockCaseTypesGet, - mockProductsGet, mockCataloguesGet as mockZGWApisCataloguesGet, + mockDocumenTypesGet as mockZGWApisDocumenTypesGet, + mockProductsGet as mockZGWApisProductsGet, + mockRoleTypesGet as mockZGWApisRoleTypesGet, } from 'components/admin/form_design/registrations/zgw/mocks'; import { FormDecorator, @@ -511,7 +513,13 @@ export default { mockObjectsApiCataloguesGet(), mockDocumentTypesGet(), ], - zgwMocks: [mockZGWApisCataloguesGet(), mockCaseTypesGet(), mockProductsGet()], + zgwMocks: [ + mockZGWApisCataloguesGet(), + mockCaseTypesGet(), + mockZGWApisDocumenTypesGet(), + mockZGWApisRoleTypesGet(), + mockZGWApisProductsGet(), + ], camundaMocks: [mockProcessDefinitionsGet()], }, }, diff --git a/src/openforms/js/components/admin/form_design/registrations/zgw/OptionalOptionsFieldset.js b/src/openforms/js/components/admin/form_design/registrations/zgw/OptionalOptionsFieldset.js index 156cd40ecd..7f3a41c0b7 100644 --- a/src/openforms/js/components/admin/form_design/registrations/zgw/OptionalOptionsFieldset.js +++ b/src/openforms/js/components/admin/form_design/registrations/zgw/OptionalOptionsFieldset.js @@ -31,7 +31,19 @@ const OptionalOptionsFieldset = ({confidentialityLevelChoices, catalogueUrl}) => - + + + } + > + + ( - +
{ + play: async ({canvasElement, step}) => { const canvas = within(canvasElement); - const catalogueSelect = canvas.getByLabelText('Catalogus'); - await rsSelect(catalogueSelect, 'Catalogus 1'); + await step('Select catalogue', async () => { + const catalogueSelect = canvas.getByLabelText('Catalogus'); + await rsSelect(catalogueSelect, 'Catalogus 1'); + }); + + await step('Select case type', async () => { + const caseTypeSelect = canvas.getByLabelText('Zaaktype', { + selector: '#id_caseTypeIdentification', + }); + await rsSelect(caseTypeSelect, 'Request passport'); + }); + + await step('Select document type', async () => { + const documentTypeSelect = canvas.getByLabelText('Documenttype', { + selector: '#id_documentTypeDescription', + }); + await rsSelect(documentTypeSelect, 'Attachment'); + }); - const caseTypeSelect = canvas.getByLabelText('Zaaktype', { - selector: '#id_caseTypeIdentification', + await step('Select employee role type', async () => { + const roleTypeSelect = canvas.getByLabelText('Medewerkerroltype'); + await rsSelect(roleTypeSelect, 'Baliemedewerker'); }); - await rsSelect(caseTypeSelect, 'Request passport'); - const documentTypeSelect = canvas.getByLabelText('Documenttype', { - selector: '#id_documentTypeDescription', + await step('Select product', async () => { + const productSelect = canvas.getByLabelText('Product'); + await rsSelect(productSelect, 'Product 1423'); }); - await rsSelect(documentTypeSelect, 'Attachment'); }, }; @@ -165,3 +202,14 @@ export const CataloguesLoadingFails = { }, }, }; + +export const RenderLegacyRoltype = { + args: { + formData: { + zgwApiGroup: 1, + zaaktype: 'https://example.com/catalogi/api/v1/zaaktypen/123', + propertyMappings: [], + medewerkerRoltype: 'Baliemedewerker', + }, + }, +}; diff --git a/src/openforms/js/components/admin/form_design/registrations/zgw/fields/MedewerkerRoltype.js b/src/openforms/js/components/admin/form_design/registrations/zgw/fields/MedewerkerRoltype.js index dfbcb0a187..6e461438fe 100644 --- a/src/openforms/js/components/admin/form_design/registrations/zgw/fields/MedewerkerRoltype.js +++ b/src/openforms/js/components/admin/form_design/registrations/zgw/fields/MedewerkerRoltype.js @@ -1,12 +1,87 @@ -import {useField} from 'formik'; +import {useField, useFormikContext} from 'formik'; +import PropTypes from 'prop-types'; import {FormattedMessage} from 'react-intl'; +import useAsync from 'react-use/esm/useAsync'; import Field from 'components/admin/forms/Field'; import FormRow from 'components/admin/forms/FormRow'; import {TextInput} from 'components/admin/forms/Inputs'; +import ReactSelect from 'components/admin/forms/ReactSelect'; +import {get} from 'utils/fetch'; -const MedewerkerRoltype = () => { +// Data fetching + +const ROLE_TYPES_ENDPOINT = '/api/v2/registration/plugins/zgw-api/role-types'; + +const getAvailableRoleTypes = async (apiGroupID, catalogueUrl, caseTypeIdentification) => { + const response = await get(ROLE_TYPES_ENDPOINT, { + zgw_api_group: apiGroupID, + catalogue_url: catalogueUrl, + case_type_identification: caseTypeIdentification, + }); + if (!response.ok) { + throw new Error('Loading available object types failed'); + } + const roleTypes = response.data.sort((a, b) => a.description.localeCompare(b.description)); + return roleTypes.map(({description, descriptionGeneric}) => ({ + value: description, + label: description, + descriptionGeneric: descriptionGeneric, // omschrijvingGeneriek, which is an enum + })); +}; + +// Components + +/** + * @deprecated + */ +const MedewerkerRoltypeLegacy = () => { const [fieldProps] = useField('medewerkerRoltype'); + return ; +}; + +const MedewerkerRoltypeSelect = ({catalogueUrl = ''}) => { + const { + values: {zgwApiGroup = null, caseTypeIdentification = ''}, + } = useFormikContext(); + const [, , fieldHelpers] = useField('medewerkerRoltype'); + const {setValue} = fieldHelpers; + + const { + loading, + value: roleTypes = [], + error, + } = useAsync(async () => { + if (!zgwApiGroup || !catalogueUrl || !caseTypeIdentification) return []; + return await getAvailableRoleTypes(zgwApiGroup, catalogueUrl, caseTypeIdentification); + }, [zgwApiGroup, catalogueUrl, caseTypeIdentification]); + if (error) throw error; + + return ( + { + setValue(selectedOption ? selectedOption.value : ''); + }} + /> + ); +}; + +MedewerkerRoltypeSelect.propTypes = { + catalogueUrl: PropTypes.string, +}; + +const MedewerkerRoltype = ({catalogueUrl = ''}) => { + const { + values: {caseTypeIdentification = '', zaaktype = ''}, + } = useFormikContext(); + // render legacy field if a case type URL is used instead of a identification reference + const renderLegacy = !!zaaktype && !caseTypeIdentification; return ( { employees filling in a form for a citizen/company.`} /> } + noManageChildProps > - + {renderLegacy ? ( + + ) : ( + + )} ); }; -MedewerkerRoltype.propTypes = {}; +MedewerkerRoltype.propTypes = { + catalogueUrl: PropTypes.string, +}; export default MedewerkerRoltype; diff --git a/src/openforms/js/components/admin/form_design/registrations/zgw/mocks.js b/src/openforms/js/components/admin/form_design/registrations/zgw/mocks.js index 72b5d26331..b4273dfa37 100644 --- a/src/openforms/js/components/admin/form_design/registrations/zgw/mocks.js +++ b/src/openforms/js/components/admin/form_design/registrations/zgw/mocks.js @@ -146,6 +146,23 @@ export const mockDocumenTypesGet = () => return HttpResponse.json(match); }); +// The backend must filter out the 'initiator' as there can only be one. +const ROLE_TYPES = [ + { + description: 'Baliemedewerker', + descriptionGeneric: 'klantcontacter', + }, + { + description: 'Belanghebbende', + descriptionGeneric: 'belanghebbende', + }, +]; + +export const mockRoleTypesGet = () => + http.get(`${API_BASE_URL}/api/v2/registration/plugins/zgw-api/role-types`, () => + HttpResponse.json(ROLE_TYPES) + ); + const PRODUCTS = [ { url: 'https://example.com/product/1234', diff --git a/src/openforms/js/lang/en.json b/src/openforms/js/lang/en.json index e4b6a7dbf7..fdcbd2d0b0 100644 --- a/src/openforms/js/lang/en.json +++ b/src/openforms/js/lang/en.json @@ -554,6 +554,11 @@ "description": "ZGW APIs registration options 'zaakVertrouwelijkheidaanduiding' label", "originalDefault": "Confidentiality" }, + "AdyBdF": { + "defaultMessage": "Something went wrong while retrieving the available role types defined in the selected case. Please check that the services in the selected API group are configured correctly.", + "description": "ZGW APIs registrations options: role type error", + "originalDefault": "Something went wrong while retrieving the available role types defined in the selected case. Please check that the services in the selected API group are configured correctly." + }, "AtBVAV": { "defaultMessage": "Confirm", "description": "Form definition select confirm button", diff --git a/src/openforms/js/lang/nl.json b/src/openforms/js/lang/nl.json index 763208f174..fcd8b35b7b 100644 --- a/src/openforms/js/lang/nl.json +++ b/src/openforms/js/lang/nl.json @@ -559,6 +559,11 @@ "description": "ZGW APIs registration options 'zaakVertrouwelijkheidaanduiding' label", "originalDefault": "Confidentiality" }, + "AdyBdF": { + "defaultMessage": "Something went wrong while retrieving the available role types defined in the selected case. Please check that the services in the selected API group are configured correctly.", + "description": "ZGW APIs registrations options: role type error", + "originalDefault": "Something went wrong while retrieving the available role types defined in the selected case. Please check that the services in the selected API group are configured correctly." + }, "AtBVAV": { "defaultMessage": "Bevestigen", "description": "Form definition select confirm button", diff --git a/src/openforms/registrations/contrib/zgw_apis/api/filters.py b/src/openforms/registrations/contrib/zgw_apis/api/filters.py index 43682c1de5..255f51381f 100644 --- a/src/openforms/registrations/contrib/zgw_apis/api/filters.py +++ b/src/openforms/registrations/contrib/zgw_apis/api/filters.py @@ -54,7 +54,7 @@ def get_fields(self): return fields -class ListProductsQueryParamsSerializer(ZGWAPIGroupMixin, serializers.Serializer): +class FilterForCaseTypeQueryParamsSerializer(ZGWAPIGroupMixin, serializers.Serializer): catalogue_url = serializers.URLField( label=_("catalogus URL"), help_text=_("Filter case types against this catalogue URL."), diff --git a/src/openforms/registrations/contrib/zgw_apis/api/urls.py b/src/openforms/registrations/contrib/zgw_apis/api/urls.py index 8dce4caece..407d30b817 100644 --- a/src/openforms/registrations/contrib/zgw_apis/api/urls.py +++ b/src/openforms/registrations/contrib/zgw_apis/api/urls.py @@ -5,6 +5,7 @@ CatalogueListView, DocumentTypesListView, ProductsListView, + RoleTypeListView, ) app_name = "zgw_apis" @@ -13,5 +14,6 @@ path("catalogues", CatalogueListView.as_view(), name="catalogue-list"), path("case-types", CaseTypesListView.as_view(), name="case-type-list"), path("document-types", DocumentTypesListView.as_view(), name="document-type-list"), + path("role-types", RoleTypeListView.as_view(), name="role-type-list"), path("products", ProductsListView.as_view(), name="product-list"), ] diff --git a/src/openforms/registrations/contrib/zgw_apis/api/views.py b/src/openforms/registrations/contrib/zgw_apis/api/views.py index 21a64f3510..278aa9a4ac 100644 --- a/src/openforms/registrations/contrib/zgw_apis/api/views.py +++ b/src/openforms/registrations/contrib/zgw_apis/api/views.py @@ -11,6 +11,7 @@ from openforms.contrib.zgw.api.serializers import ( CaseTypeProductSerializer, CaseTypeSerializer, + RoleTypeSerializer, ) from openforms.contrib.zgw.api.views import ( BaseCatalogueListView, @@ -21,9 +22,9 @@ from .filters import ( APIGroupQueryParamsSerializer, + FilterForCaseTypeQueryParamsSerializer, ListCaseTypesQueryParamsSerializer, ListDocumentTypesQueryParamsSerializer, - ListProductsQueryParamsSerializer, ) @@ -90,6 +91,58 @@ class DocumentTypesListView(BaseDocumentTypesListView): filter_serializer_class = ListDocumentTypesQueryParamsSerializer +@dataclass +class RoleType: + description: str + description_generic: str + + +@extend_schema_view( + get=extend_schema( + summary=_( + "List the available role types bound to a case type within a catalogue " + "(ZGW APIs)" + ), + parameters=[FilterForCaseTypeQueryParamsSerializer], + ), +) +class RoleTypeListView(ListMixin[RoleType], APIView): + authentication_classes = (authentication.SessionAuthentication,) + permission_classes = (permissions.IsAdminUser,) + serializer_class = RoleTypeSerializer + + def get_objects(self) -> list[RoleType]: + filter_serializer = FilterForCaseTypeQueryParamsSerializer( + data=self.request.query_params + ) + filter_serializer.is_valid(raise_exception=True) + + catalogue_url = filter_serializer.validated_data["catalogue_url"] + case_type_identification = filter_serializer.validated_data[ + "case_type_identification" + ] + role_types: list[RoleType] = [] + with filter_serializer.get_ztc_client() as client: + _role_types = client.get_all_role_types( + catalogus=catalogue_url, + within_casetype=case_type_identification, + ) + for _role_type in _role_types: + # skip over initiator, since that one is set automatically and there can + # only be one + if ( + description_generic := _role_type["omschrijvingGeneriek"] + ) == "initiator": + continue + role_type = RoleType( + description=_role_type["omschrijving"], + description_generic=description_generic, + ) + if role_type not in role_types: + role_types.append(role_type) + return role_types + + @dataclass class Product: url: str @@ -101,7 +154,7 @@ class Product: summary=_( "List the available products bound to a case type within a catalogue (ZGW APIs)" ), - parameters=[ListProductsQueryParamsSerializer], + parameters=[FilterForCaseTypeQueryParamsSerializer], ), ) class ProductsListView(ListMixin[Product], APIView): @@ -110,7 +163,7 @@ class ProductsListView(ListMixin[Product], APIView): serializer_class = CaseTypeProductSerializer def get_objects(self): - filter_serializer = ListProductsQueryParamsSerializer( + filter_serializer = FilterForCaseTypeQueryParamsSerializer( data=self.request.query_params ) filter_serializer.is_valid(raise_exception=True) diff --git a/src/openforms/registrations/contrib/zgw_apis/tests/files/vcr_cassettes/GetRoleTypesViewTests/GetRoleTypesViewTests.test_drafts_included_with_feature_flag_on.yaml b/src/openforms/registrations/contrib/zgw_apis/tests/files/vcr_cassettes/GetRoleTypesViewTests/GetRoleTypesViewTests.test_drafts_included_with_feature_flag_on.yaml new file mode 100644 index 0000000000..de8f46a46a --- /dev/null +++ b/src/openforms/registrations/contrib/zgw_apis/tests/files/vcr_cassettes/GetRoleTypesViewTests/GetRoleTypesViewTests.test_drafts_included_with_feature_flag_on.yaml @@ -0,0 +1,123 @@ +interactions: +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate, br + Authorization: + - Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ0ZXN0X2NsaWVudF9pZCIsImlhdCI6MTczMjg3OTU1OSwiY2xpZW50X2lkIjoidGVzdF9jbGllbnRfaWQiLCJ1c2VyX2lkIjoiIiwidXNlcl9yZXByZXNlbnRhdGlvbiI6IiJ9.OshyROitdmBDABRyqlX81xMaHYyaTvMPCq2PBDFSevM + Connection: + - keep-alive + User-Agent: + - python-requests/2.32.2 + method: GET + uri: http://localhost:8003/catalogi/api/v1/catalogussen?domein=VRSN + 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 +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate, br + Authorization: + - Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ0ZXN0X2NsaWVudF9pZCIsImlhdCI6MTczMjg3OTU1OSwiY2xpZW50X2lkIjoidGVzdF9jbGllbnRfaWQiLCJ1c2VyX2lkIjoiIiwidXNlcl9yZXByZXNlbnRhdGlvbiI6IiJ9.OshyROitdmBDABRyqlX81xMaHYyaTvMPCq2PBDFSevM + 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%2Faa0e0a50-33f6-4473-99a1-b92bab94e749&identificatie=DRAFT-01&status=alles + response: + body: + string: '{"count":1,"next":null,"previous":null,"results":[{"url":"http://localhost:8003/catalogi/api/v1/zaaktypen/cf903a2f-0acd-4dbf-9c77-6e5e35d794e1","identificatie":"DRAFT-01","omschrijving":"Case + type with drafts","omschrijvingGeneriek":"","vertrouwelijkheidaanduiding":"zaakvertrouwelijk","doel":"Testing","aanleiding":"Test","toelichting":"","indicatieInternOfExtern":"extern","handelingInitiator":"Testen","onderwerp":"test","handelingBehandelaar":"Testen","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":true,"verantwoordelijke":"Ontwikkelaar","beginGeldigheid":"2023-01-01","eindeGeldigheid":null,"versiedatum":"2023-01-01","beginObject":"2023-01-01","eindeObject":null,"catalogus":"http://localhost:8003/catalogi/api/v1/catalogussen/aa0e0a50-33f6-4473-99a1-b92bab94e749","statustypen":[],"resultaattypen":[],"eigenschappen":[],"informatieobjecttypen":[],"roltypen":["http://localhost:8003/catalogi/api/v1/roltypen/5cdeb40f-1e28-415b-b826-1b36dac54232","http://localhost:8003/catalogi/api/v1/roltypen/b79db762-5f02-43ea-b550-6de3519e7057"],"besluittypen":[],"deelzaaktypen":[],"gerelateerdeZaaktypen":[],"zaakobjecttypen":[]}]}' + headers: + API-version: + - 1.3.1 + Allow: + - GET, POST, HEAD, OPTIONS + Content-Length: + - '1507' + 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.eyJpc3MiOiJ0ZXN0X2NsaWVudF9pZCIsImlhdCI6MTczMjg3OTU1OSwiY2xpZW50X2lkIjoidGVzdF9jbGllbnRfaWQiLCJ1c2VyX2lkIjoiIiwidXNlcl9yZXByZXNlbnRhdGlvbiI6IiJ9.OshyROitdmBDABRyqlX81xMaHYyaTvMPCq2PBDFSevM + Connection: + - keep-alive + User-Agent: + - python-requests/2.32.2 + method: GET + uri: http://localhost:8003/catalogi/api/v1/roltypen?zaaktypeIdentificatie=DRAFT-01&status=alles + response: + body: + string: '{"count":2,"next":null,"previous":null,"results":[{"url":"http://localhost:8003/catalogi/api/v1/roltypen/b79db762-5f02-43ea-b550-6de3519e7057","zaaktype":"http://localhost:8003/catalogi/api/v1/zaaktypen/cf903a2f-0acd-4dbf-9c77-6e5e35d794e1","zaaktypeIdentificatie":"DRAFT-01","omschrijving":"Baliemedewerker","omschrijvingGeneriek":"klantcontacter","catalogus":"http://localhost:8003/catalogi/api/v1/catalogussen/aa0e0a50-33f6-4473-99a1-b92bab94e749","beginGeldigheid":null,"eindeGeldigheid":null,"beginObject":null,"eindeObject":null},{"url":"http://localhost:8003/catalogi/api/v1/roltypen/5cdeb40f-1e28-415b-b826-1b36dac54232","zaaktype":"http://localhost:8003/catalogi/api/v1/zaaktypen/cf903a2f-0acd-4dbf-9c77-6e5e35d794e1","zaaktypeIdentificatie":"DRAFT-01","omschrijving":"Draft","omschrijvingGeneriek":"belanghebbende","catalogus":"http://localhost:8003/catalogi/api/v1/catalogussen/aa0e0a50-33f6-4473-99a1-b92bab94e749","beginGeldigheid":null,"eindeGeldigheid":null,"beginObject":null,"eindeObject":null}]}' + headers: + API-version: + - 1.3.1 + Allow: + - GET, POST, HEAD, OPTIONS + Content-Length: + - '1013' + 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 diff --git a/src/openforms/registrations/contrib/zgw_apis/tests/files/vcr_cassettes/GetRoleTypesViewTests/GetRoleTypesViewTests.test_no_results_invalid_case_type_reference.yaml b/src/openforms/registrations/contrib/zgw_apis/tests/files/vcr_cassettes/GetRoleTypesViewTests/GetRoleTypesViewTests.test_no_results_invalid_case_type_reference.yaml new file mode 100644 index 0000000000..98ab215f87 --- /dev/null +++ b/src/openforms/registrations/contrib/zgw_apis/tests/files/vcr_cassettes/GetRoleTypesViewTests/GetRoleTypesViewTests.test_no_results_invalid_case_type_reference.yaml @@ -0,0 +1,82 @@ +interactions: +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate, br + Authorization: + - Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ0ZXN0X2NsaWVudF9pZCIsImlhdCI6MTczMjg3OTU3MiwiY2xpZW50X2lkIjoidGVzdF9jbGllbnRfaWQiLCJ1c2VyX2lkIjoiIiwidXNlcl9yZXByZXNlbnRhdGlvbiI6IiJ9.egyGn7SOXKjNXa8xWvPs0qja7PsZnWjH0-aPB6wqaTE + Connection: + - keep-alive + User-Agent: + - python-requests/2.32.2 + method: GET + uri: http://localhost:8003/catalogi/api/v1/catalogussen?domein=VRSN + 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 +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate, br + Authorization: + - Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ0ZXN0X2NsaWVudF9pZCIsImlhdCI6MTczMjg3OTU3MiwiY2xpZW50X2lkIjoidGVzdF9jbGllbnRfaWQiLCJ1c2VyX2lkIjoiIiwidXNlcl9yZXByZXNlbnRhdGlvbiI6IiJ9.egyGn7SOXKjNXa8xWvPs0qja7PsZnWjH0-aPB6wqaTE + 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-do-not-exist + 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 diff --git a/src/openforms/registrations/contrib/zgw_apis/tests/files/vcr_cassettes/GetRoleTypesViewTests/GetRoleTypesViewTests.test_retrieve_filter_by_catalogus_and_case_type.yaml b/src/openforms/registrations/contrib/zgw_apis/tests/files/vcr_cassettes/GetRoleTypesViewTests/GetRoleTypesViewTests.test_retrieve_filter_by_catalogus_and_case_type.yaml new file mode 100644 index 0000000000..b26d2027df --- /dev/null +++ b/src/openforms/registrations/contrib/zgw_apis/tests/files/vcr_cassettes/GetRoleTypesViewTests/GetRoleTypesViewTests.test_retrieve_filter_by_catalogus_and_case_type.yaml @@ -0,0 +1,125 @@ +interactions: +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate, br + Authorization: + - Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ0ZXN0X2NsaWVudF9pZCIsImlhdCI6MTczMjg3OTU3MiwiY2xpZW50X2lkIjoidGVzdF9jbGllbnRfaWQiLCJ1c2VyX2lkIjoiIiwidXNlcl9yZXByZXNlbnRhdGlvbiI6IiJ9.egyGn7SOXKjNXa8xWvPs0qja7PsZnWjH0-aPB6wqaTE + Connection: + - keep-alive + User-Agent: + - python-requests/2.32.2 + method: GET + uri: http://localhost:8003/catalogi/api/v1/catalogussen?domein=VRSN + 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 +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate, br + Authorization: + - Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ0ZXN0X2NsaWVudF9pZCIsImlhdCI6MTczMjg3OTU3MiwiY2xpZW50X2lkIjoidGVzdF9jbGllbnRfaWQiLCJ1c2VyX2lkIjoiIiwidXNlcl9yZXByZXNlbnRhdGlvbiI6IiJ9.egyGn7SOXKjNXa8xWvPs0qja7PsZnWjH0-aPB6wqaTE + 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":3,"next":null,"previous":null,"results":[{"url":"http://localhost:8003/catalogi/api/v1/zaaktypen/f609b6fe-449a-46dc-a0af-de55dc5f6774","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":["http://localhost/product/1234abcd-12ab-34cd-56ef-12345abcde10"],"selectielijstProcestype":"https://selectielijst.openzaak.nl/api/v1/procestypen/aa8aa2fd-b9c6-4e34-9a6c-58a677f60ea0","referentieproces":{"naam":"Testen","link":""},"concept":false,"verantwoordelijke":"Ontwikkelaar","beginGeldigheid":"2024-10-31","eindeGeldigheid":null,"versiedatum":"2024-10-31","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/018ec0c2-50d0-4225-a5bb-5ad7c48d6f2b","http://localhost:8003/catalogi/api/v1/statustypen/2937ee1d-9ea1-4048-b642-5a4dfd51fb47"],"resultaattypen":["http://localhost:8003/catalogi/api/v1/resultaattypen/e84e6028-c52a-420b-a098-d10897395c52"],"eigenschappen":["http://localhost:8003/catalogi/api/v1/eigenschappen/3a094e33-7a20-4018-bd60-3c15d06a1555"],"informatieobjecttypen":["http://localhost:8003/catalogi/api/v1/informatieobjecttypen/531f6c1a-97f7-478c-85f0-67d2f23661c7"],"roltypen":["http://localhost:8003/catalogi/api/v1/roltypen/bd37e337-e0fd-4e11-a6ed-45a71e1c7aa8","http://localhost:8003/catalogi/api/v1/roltypen/d44d68c2-1e06-461e-9657-adf174a922fb"],"besluittypen":[],"deelzaaktypen":[],"gerelateerdeZaaktypen":[],"zaakobjecttypen":[]},{"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":"2024-10-29","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: + - '5887' + 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.eyJpc3MiOiJ0ZXN0X2NsaWVudF9pZCIsImlhdCI6MTczMjg3OTU3MiwiY2xpZW50X2lkIjoidGVzdF9jbGllbnRfaWQiLCJ1c2VyX2lkIjoiIiwidXNlcl9yZXByZXNlbnRhdGlvbiI6IiJ9.egyGn7SOXKjNXa8xWvPs0qja7PsZnWjH0-aPB6wqaTE + Connection: + - keep-alive + User-Agent: + - python-requests/2.32.2 + method: GET + uri: http://localhost:8003/catalogi/api/v1/roltypen?zaaktypeIdentificatie=ZT-001 + response: + body: + string: '{"count":8,"next":null,"previous":null,"results":[{"url":"http://localhost:8003/catalogi/api/v1/roltypen/877f0745-5292-4336-a6da-a5596c564aae","zaaktype":"http://localhost:8003/catalogi/api/v1/zaaktypen/cc1d39d9-05a2-45f9-8cfe-230790d36f71","zaaktypeIdentificatie":"ZT-001","omschrijving":"Baliemedewerker","omschrijvingGeneriek":"klantcontacter","catalogus":"http://localhost:8003/catalogi/api/v1/catalogussen/630271f6-568a-485e-b1c4-4ed2d6ab3a58","beginGeldigheid":null,"eindeGeldigheid":null,"beginObject":null,"eindeObject":null},{"url":"http://localhost:8003/catalogi/api/v1/roltypen/51199bc6-024f-4997-8199-733fd2d478f0","zaaktype":"http://localhost:8003/catalogi/api/v1/zaaktypen/cc1d39d9-05a2-45f9-8cfe-230790d36f71","zaaktypeIdentificatie":"ZT-001","omschrijving":"Initiator","omschrijvingGeneriek":"initiator","catalogus":"http://localhost:8003/catalogi/api/v1/catalogussen/630271f6-568a-485e-b1c4-4ed2d6ab3a58","beginGeldigheid":null,"eindeGeldigheid":null,"beginObject":null,"eindeObject":null},{"url":"http://localhost:8003/catalogi/api/v1/roltypen/d44d68c2-1e06-461e-9657-adf174a922fb","zaaktype":"http://localhost:8003/catalogi/api/v1/zaaktypen/f609b6fe-449a-46dc-a0af-de55dc5f6774","zaaktypeIdentificatie":"ZT-001","omschrijving":"Baliemedewerker","omschrijvingGeneriek":"klantcontacter","catalogus":"http://localhost:8003/catalogi/api/v1/catalogussen/bd58635c-793e-446d-a7e0-460d7b04829d","beginGeldigheid":null,"eindeGeldigheid":null,"beginObject":null,"eindeObject":null},{"url":"http://localhost:8003/catalogi/api/v1/roltypen/bd37e337-e0fd-4e11-a6ed-45a71e1c7aa8","zaaktype":"http://localhost:8003/catalogi/api/v1/zaaktypen/f609b6fe-449a-46dc-a0af-de55dc5f6774","zaaktypeIdentificatie":"ZT-001","omschrijving":"Initiator","omschrijvingGeneriek":"initiator","catalogus":"http://localhost:8003/catalogi/api/v1/catalogussen/bd58635c-793e-446d-a7e0-460d7b04829d","beginGeldigheid":null,"eindeGeldigheid":null,"beginObject":null,"eindeObject":null},{"url":"http://localhost:8003/catalogi/api/v1/roltypen/cebf7a53-297d-4308-869a-4385cfc42549","zaaktype":"http://localhost:8003/catalogi/api/v1/zaaktypen/b79d9c2f-5ec4-4e23-bb66-ec6dd959a400","zaaktypeIdentificatie":"ZT-001","omschrijving":"Baliemedewerker","omschrijvingGeneriek":"klantcontacter","catalogus":"http://localhost:8003/catalogi/api/v1/catalogussen/bd58635c-793e-446d-a7e0-460d7b04829d","beginGeldigheid":null,"eindeGeldigheid":null,"beginObject":null,"eindeObject":null},{"url":"http://localhost:8003/catalogi/api/v1/roltypen/0333eec0-6240-4f90-adf9-8f98edcd5563","zaaktype":"http://localhost:8003/catalogi/api/v1/zaaktypen/b79d9c2f-5ec4-4e23-bb66-ec6dd959a400","zaaktypeIdentificatie":"ZT-001","omschrijving":"Initiator","omschrijvingGeneriek":"initiator","catalogus":"http://localhost:8003/catalogi/api/v1/catalogussen/bd58635c-793e-446d-a7e0-460d7b04829d","beginGeldigheid":null,"eindeGeldigheid":null,"beginObject":null,"eindeObject":null},{"url":"http://localhost:8003/catalogi/api/v1/roltypen/7f1887e8-bf22-47e7-ae52-ed6848d7e70e","zaaktype":"http://localhost:8003/catalogi/api/v1/zaaktypen/1f41885e-23fc-4462-bbc8-80be4ae484dc","zaaktypeIdentificatie":"ZT-001","omschrijving":"Baliemedewerker","omschrijvingGeneriek":"klantcontacter","catalogus":"http://localhost:8003/catalogi/api/v1/catalogussen/bd58635c-793e-446d-a7e0-460d7b04829d","beginGeldigheid":null,"eindeGeldigheid":null,"beginObject":null,"eindeObject":null},{"url":"http://localhost:8003/catalogi/api/v1/roltypen/43e8026c-8abd-4b29-8a4c-ac2a37bc6f5b","zaaktype":"http://localhost:8003/catalogi/api/v1/zaaktypen/1f41885e-23fc-4462-bbc8-80be4ae484dc","zaaktypeIdentificatie":"ZT-001","omschrijving":"Initiator","omschrijvingGeneriek":"initiator","catalogus":"http://localhost:8003/catalogi/api/v1/catalogussen/bd58635c-793e-446d-a7e0-460d7b04829d","beginGeldigheid":null,"eindeGeldigheid":null,"beginObject":null,"eindeObject":null}]}' + headers: + API-version: + - 1.3.1 + Allow: + - GET, POST, HEAD, OPTIONS + Content-Length: + - '3879' + 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 diff --git a/src/openforms/registrations/contrib/zgw_apis/tests/test_api_endpoints.py b/src/openforms/registrations/contrib/zgw_apis/tests/test_api_endpoints.py index 36e2ef8438..6ff1c8f08f 100644 --- a/src/openforms/registrations/contrib/zgw_apis/tests/test_api_endpoints.py +++ b/src/openforms/registrations/contrib/zgw_apis/tests/test_api_endpoints.py @@ -319,6 +319,127 @@ def test_retrieve_filter_by_catalogus_and_case_type_doesnt_exist(self): self.assertEqual(data, []) +class GetRoleTypesViewTests(OFVCRMixin, APITestCase): + VCR_TEST_FILES = TEST_FILES + endpoint = reverse_lazy("api:zgw_apis:role-type-list") + + @classmethod + def setUpTestData(cls): + super().setUpTestData() + + # create services for the docker-compose Open Zaak instance. + catalogi_service = ServiceFactory.create( + api_root="http://localhost:8003/catalogi/api/v1/", + api_type=APITypes.ztc, + auth_type=AuthTypes.zgw, + client_id="test_client_id", + secret="test_secret_key", + ) + cls.zgw_api_group = ZGWApiGroupConfigFactory.create( + ztc_service=catalogi_service, + ) + + def test_must_be_logged_in_as_admin(self): + user = UserFactory.create() + self.client.force_login(user) + + response = self.client.get(self.endpoint) + + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + + def test_filter_with_invalid_zgw_api_group(self): + user = StaffUserFactory.create() + self.client.force_login(user) + + response = self.client.get(self.endpoint, {"zgw_api_group": "INVALID"}) + + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + def test_missing_zgw_api_group(self): + user = StaffUserFactory.create() + self.client.force_login(user) + + response = self.client.get(self.endpoint) + + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + def test_missing_catalogue_url_with_case_type_identification(self): + user = StaffUserFactory.create() + self.client.force_login(user) + + response = self.client.get( + self.endpoint, + { + "zgw_api_group": self.zgw_api_group.pk, + "case_type_identification": "ZT-001", + }, + ) + + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + def test_retrieve_filter_by_catalogus_and_case_type(self): + user = StaffUserFactory.create() + self.client.force_login(user) + + response = self.client.get( + self.endpoint, + { + "zgw_api_group": self.zgw_api_group.pk, + "catalogue_url": "http://localhost:8003/catalogi/api/v1/catalogussen/bd58635c-793e-446d-a7e0-460d7b04829d", + "case_type_identification": "ZT-001", + }, + ) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + data = response.json() + self.assertGreaterEqual(len(data), 1) + baliemedewerker = next( + (item for item in data if item["description"] == "Baliemedewerker"), None + ) + self.assertIsNotNone(baliemedewerker) + + def test_no_results_invalid_case_type_reference(self): + user = StaffUserFactory.create() + self.client.force_login(user) + + response = self.client.get( + self.endpoint, + { + "zgw_api_group": self.zgw_api_group.pk, + "catalogue_url": "http://localhost:8003/catalogi/api/v1/catalogussen/bd58635c-793e-446d-a7e0-460d7b04829d", + "case_type_identification": "i-do-not-exist", + }, + ) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + data = response.json() + self.assertEqual(data, []) + + @enable_feature_flag("ZGW_APIS_INCLUDE_DRAFTS") + def test_drafts_included_with_feature_flag_on(self): + user = StaffUserFactory.create() + self.client.force_login(user) + + response = self.client.get( + self.endpoint, + { + "zgw_api_group": self.zgw_api_group.pk, + # DRAFTS catalogus + "catalogue_url": ( + "http://localhost:8003/catalogi/api/v1/" + "catalogussen/aa0e0a50-33f6-4473-99a1-b92bab94e749" + ), + "case_type_identification": "DRAFT-01", + }, + ) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + data = response.json() + self.assertGreaterEqual(len(data), 1) + draft = next((item for item in data if item["description"] == "Draft"), None) + self.assertIsNotNone(draft) + + class GetProductsListViewTests(OFVCRMixin, APITestCase): VCR_TEST_FILES = TEST_FILES endpoint = reverse_lazy("api:zgw_apis:product-list")