Skip to content

Commit

Permalink
✨ [#4606] Implement API endpoint to retrieve role types
Browse files Browse the repository at this point in the history
Given a particular catalogue and case type identification, retrieve the
role types defined within.

We exclude the initiator role type from this list, since creating any
additional roles with type initiator would lead to broken integrity,
as the authentication details/registration attributes are used by the
plugin already to set the initiator, and only one initiator can (and
must) be set on case.
  • Loading branch information
sergei-maertens committed Nov 29, 2024
1 parent a481f81 commit 96bfd9e
Show file tree
Hide file tree
Showing 5 changed files with 150 additions and 6 deletions.
17 changes: 17 additions & 0 deletions src/openforms/contrib/zgw/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
Expand Down
76 changes: 74 additions & 2 deletions src/openforms/contrib/zgw/clients/catalogi.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand All @@ -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: str # since 1.2
omschrijving: str
omschrijvingGeneriek: RoleDescriptionGeneric


class EigenschapSpecificatie(TypedDict):
groep: NotRequired[str]
formaat: Literal["tekst", "getal", "datum", "datum_tijd"]
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -325,6 +357,46 @@ 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]:
params: RoleTypeListParams = {

Check warning on line 366 in src/openforms/contrib/zgw/clients/catalogi.py

View check run for this annotation

Codecov / codecov/patch

src/openforms/contrib/zgw/clients/catalogi.py#L366

Added line #L366 was not covered by tests
"zaaktypeIdentificatie": within_casetype,
}
if self.allow_drafts:
params["status"] = "alles"

Check warning on line 370 in src/openforms/contrib/zgw/clients/catalogi.py

View check run for this annotation

Codecov / codecov/patch

src/openforms/contrib/zgw/clients/catalogi.py#L370

Added line #L370 was not covered by tests

# 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 = (

Check warning on line 374 in src/openforms/contrib/zgw/clients/catalogi.py

View check run for this annotation

Codecov / codecov/patch

src/openforms/contrib/zgw/clients/catalogi.py#L374

Added line #L374 was not covered by tests
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 []

Check warning on line 389 in src/openforms/contrib/zgw/clients/catalogi.py

View check run for this annotation

Codecov / codecov/patch

src/openforms/contrib/zgw/clients/catalogi.py#L389

Added line #L389 was not covered by tests

response = self.get("roltypen", params=params) # type: ignore
response.raise_for_status()
data: PaginatedResponseData[RoleType] = response.json()

Check warning on line 393 in src/openforms/contrib/zgw/clients/catalogi.py

View check run for this annotation

Codecov / codecov/patch

src/openforms/contrib/zgw/clients/catalogi.py#L391-L393

Added lines #L391 - L393 were not covered by tests

for role_type in pagination_helper(self, data):
if role_type["url"] not in all_valid_roltype_urls:
continue
yield role_type

Check warning on line 398 in src/openforms/contrib/zgw/clients/catalogi.py

View check run for this annotation

Codecov / codecov/patch

src/openforms/contrib/zgw/clients/catalogi.py#L397-L398

Added lines #L397 - L398 were not covered by tests

def list_roltypen(
self,
zaaktype: str,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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."),
Expand Down
2 changes: 2 additions & 0 deletions src/openforms/registrations/contrib/zgw_apis/api/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
CatalogueListView,
DocumentTypesListView,
ProductsListView,
RoleTypeListView,
)

app_name = "zgw_apis"
Expand All @@ -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"),
]
59 changes: 56 additions & 3 deletions src/openforms/registrations/contrib/zgw_apis/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from openforms.contrib.zgw.api.serializers import (
CaseTypeProductSerializer,
CaseTypeSerializer,
RoleTypeSerializer,
)
from openforms.contrib.zgw.api.views import (
BaseCatalogueListView,
Expand All @@ -21,9 +22,9 @@

from .filters import (
APIGroupQueryParamsSerializer,
FilterForCaseTypeQueryParamsSerializer,
ListCaseTypesQueryParamsSerializer,
ListDocumentTypesQueryParamsSerializer,
ListProductsQueryParamsSerializer,
)


Expand Down Expand Up @@ -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(

Check warning on line 115 in src/openforms/registrations/contrib/zgw_apis/api/views.py

View check run for this annotation

Codecov / codecov/patch

src/openforms/registrations/contrib/zgw_apis/api/views.py#L115

Added line #L115 was not covered by tests
data=self.request.query_params
)
filter_serializer.is_valid(raise_exception=True)

Check warning on line 118 in src/openforms/registrations/contrib/zgw_apis/api/views.py

View check run for this annotation

Codecov / codecov/patch

src/openforms/registrations/contrib/zgw_apis/api/views.py#L118

Added line #L118 was not covered by tests

catalogue_url = filter_serializer.validated_data["catalogue_url"]
case_type_identification = filter_serializer.validated_data[

Check warning on line 121 in src/openforms/registrations/contrib/zgw_apis/api/views.py

View check run for this annotation

Codecov / codecov/patch

src/openforms/registrations/contrib/zgw_apis/api/views.py#L120-L121

Added lines #L120 - L121 were not covered by tests
"case_type_identification"
]
role_types: list[RoleType] = []

Check warning on line 124 in src/openforms/registrations/contrib/zgw_apis/api/views.py

View check run for this annotation

Codecov / codecov/patch

src/openforms/registrations/contrib/zgw_apis/api/views.py#L124

Added line #L124 was not covered by tests
with filter_serializer.get_ztc_client() as client:
_role_types = client.get_all_role_types(

Check warning on line 126 in src/openforms/registrations/contrib/zgw_apis/api/views.py

View check run for this annotation

Codecov / codecov/patch

src/openforms/registrations/contrib/zgw_apis/api/views.py#L126

Added line #L126 was not covered by tests
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(

Check warning on line 137 in src/openforms/registrations/contrib/zgw_apis/api/views.py

View check run for this annotation

Codecov / codecov/patch

src/openforms/registrations/contrib/zgw_apis/api/views.py#L136-L137

Added lines #L136 - L137 were not covered by tests
description=_role_type["omschrijving"],
description_generic=description_generic,
)
if role_type not in role_types:
role_types.append(role_type)
return role_types

Check warning on line 143 in src/openforms/registrations/contrib/zgw_apis/api/views.py

View check run for this annotation

Codecov / codecov/patch

src/openforms/registrations/contrib/zgw_apis/api/views.py#L142-L143

Added lines #L142 - L143 were not covered by tests


@dataclass
class Product:
url: str
Expand All @@ -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):
Expand All @@ -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)
Expand Down

0 comments on commit 96bfd9e

Please sign in to comment.