diff --git a/src/opendms/api/clients/zaken.py b/src/opendms/api/clients/zaken.py index c1994abd..9670417c 100644 --- a/src/opendms/api/clients/zaken.py +++ b/src/opendms/api/clients/zaken.py @@ -2,6 +2,7 @@ from typing import NotRequired from msgspec.json import decode +from zgw_consumers.api_models.zaken import ZaakInformatieObject from zgw_consumers.client import build_client from zgw_consumers.models import Service from zgw_consumers.nlx import NLXClient @@ -95,6 +96,40 @@ def create_zaak(self, data: dict) -> Zaak: response.raise_for_status() return decode(response.content, type=Zaak) + def create_zaakinformatieobject(self, data: dict) -> dict: + response = self.post( + "zaakinformatieobjecten", + json=data, + ) + response.raise_for_status() + return response.json() + + def get_paginated_zaakinformatieobjecten( + self, + params: Mapping[str, object] = {}, + ) -> dict: + results = self.make_request( + "zaakinformatieobjecten", + params=params, + headers=CRS_HEADERS, + parse_into=list[ZaakInformatieObject], + ) + + return { + "count": len(results), + "results": results, + } + + def get_zaakinformatieobject( + self, + uuid: str, + ) -> ZaakInformatieObject: + return self.make_request( + f"zaakinformatieobjecten/{uuid}", + headers=CRS_HEADERS, + parse_into=ZaakInformatieObject, + ) + def get_zaken_client(service: Service) -> ZaakClient: return build_client(service, client_factory=ZaakClient) diff --git a/src/opendms/api/openapi.yaml b/src/opendms/api/openapi.yaml index 59b083f5..d6176e7d 100644 --- a/src/opendms/api/openapi.yaml +++ b/src/opendms/api/openapi.yaml @@ -296,6 +296,110 @@ paths: schema: $ref: '#/components/schemas/StatusType' description: '' + /api/v1/services/{serviceSlug}/zaakinformatieobjecten: + get: + operationId: apiV1ServicesZaakinformatieobjectenList + parameters: + - name: page + required: false + in: query + description: A page number within the paginated result set. + schema: + type: integer + - name: pageSize + required: false + in: query + description: 'Het aantal resultaten terug te geven per pagina. (default: 100, + maximum: 500).' + schema: + type: integer + - in: path + name: serviceSlug + schema: + type: string + required: true + tags: + - api + security: + - cookieAuth: [] + responses: + '200': + headers: + API-version: + schema: + type: string + description: 'Geeft een specifieke API-versie aan in de context van + een specifieke aanroep. Voorbeeld: 1.2.1.' + content: + application/json: + schema: + $ref: '#/components/schemas/PaginatedZaakInformatieObjectCreateList' + description: '' + post: + operationId: apiV1ServicesZaakinformatieobjectenCreate + summary: zaakinformatieobjectCreate + parameters: + - in: path + name: serviceSlug + schema: + type: string + required: true + tags: + - api + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ZaakInformatieObjectCreate' + required: true + security: + - cookieAuth: [] + responses: + '201': + headers: + API-version: + schema: + type: string + description: 'Geeft een specifieke API-versie aan in de context van + een specifieke aanroep. Voorbeeld: 1.2.1.' + content: + application/json: + schema: + type: object + additionalProperties: {} + description: '' + /api/v1/services/{serviceSlug}/zaakinformatieobjecten/{id}: + get: + operationId: apiV1ServicesZaakinformatieobjectenRetrieve + parameters: + - in: path + name: id + schema: + type: string + format: uuid + required: true + - in: path + name: serviceSlug + schema: + type: string + required: true + tags: + - api + security: + - cookieAuth: [] + responses: + '200': + headers: + API-version: + schema: + type: string + description: 'Geeft een specifieke API-versie aan in de context van + een specifieke aanroep. Voorbeeld: 1.2.1.' + content: + application/json: + schema: + $ref: '#/components/schemas/ZaakInformatieObjectCreate' + description: '' /api/v1/services/{serviceSlug}/zaaktypen: get: operationId: apiV1ServicesZaaktypenList @@ -1599,6 +1703,19 @@ components: type: array items: $ref: '#/components/schemas/StatusType' + PaginatedZaakInformatieObjectCreateList: + type: object + required: + - count + - results + properties: + count: + type: integer + example: 123 + results: + type: array + items: + $ref: '#/components/schemas/ZaakInformatieObjectCreate' PaginatedZaakList: type: object required: @@ -1979,6 +2096,22 @@ components: - startdatum - verantwoordelijkeOrganisatie - zaaktype + ZaakInformatieObjectCreate: + type: object + properties: + zaak: + type: string + format: uri + informatieobject: + type: string + format: uri + titel: + type: string + beschrijving: + type: string + required: + - informatieobject + - zaak ZaakType: type: object properties: diff --git a/src/opendms/api/serializers.py b/src/opendms/api/serializers.py index 6ae6d7dd..750848fd 100644 --- a/src/opendms/api/serializers.py +++ b/src/opendms/api/serializers.py @@ -697,3 +697,10 @@ class DocumentCreateSerializer(serializers.Serializer): child=serializers.CharField(), required=False, ) + + +class ZaakInformatieObjectCreateSerializer(serializers.Serializer): + zaak = serializers.URLField() + informatieobject = serializers.URLField() + titel = serializers.CharField(required=False) + beschrijving = serializers.CharField(required=False) diff --git a/src/opendms/api/tests/files/vcr_cassettes/ServiceZaakInformatieObjectViewSetTests/test_create_zaakinformatieobject.yaml b/src/opendms/api/tests/files/vcr_cassettes/ServiceZaakInformatieObjectViewSetTests/test_create_zaakinformatieobject.yaml new file mode 100644 index 00000000..455714ce --- /dev/null +++ b/src/opendms/api/tests/files/vcr_cassettes/ServiceZaakInformatieObjectViewSetTests/test_create_zaakinformatieobject.yaml @@ -0,0 +1,55 @@ +interactions: +- request: + body: '{"zaak": "http://localhost:8003/zaken/api/v1/zaken/840a1355-dd6d-48a4-a849-38fbf832db44", + "informatieobject": "http://localhost:8003/documenten/api/v1/enkelvoudiginformatieobjecten/ea16fa8c-4bab-4065-a28a-f6574625205d"}' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate, br, zstd + Connection: + - keep-alive + Content-Length: + - '219' + Content-Type: + - application/json + User-Agent: + - python-requests/2.33.1 + method: POST + uri: http://localhost:8003/zaken/api/v1/zaakinformatieobjecten + response: + body: + string: '{"url":"http://localhost:8003/zaken/api/v1/zaakinformatieobjecten/7c01c899-b44e-4cb2-8ebc-9d2a48b6a186","uuid":"7c01c899-b44e-4cb2-8ebc-9d2a48b6a186","informatieobject":"http://localhost:8003/documenten/api/v1/enkelvoudiginformatieobjecten/ea16fa8c-4bab-4065-a28a-f6574625205d","zaak":"http://localhost:8003/zaken/api/v1/zaken/840a1355-dd6d-48a4-a849-38fbf832db44","aardRelatieWeergave":"Hoort + bij, omgekeerd: kent","titel":"","beschrijving":"","registratiedatum":"2026-06-10T10:47:07.902351Z","vernietigingsdatum":null,"status":null}' + headers: + API-version: + - 1.5.1 + Allow: + - GET, POST, HEAD, OPTIONS + Content-Length: + - '534' + Content-Security-Policy: + - 'object-src ''none''; frame-src ''self''; script-src ''self'' ''unsafe-inline'' + cdnjs.cloudflare.com cdn.jsdelivr.net; font-src ''self'' fonts.gstatic.com; + form-action ''self''; base-uri ''self''; frame-ancestors ''none''; worker-src + ''self'' blob:; default-src ''self''; style-src ''self'' ''unsafe-inline'' + fonts.googleapis.com cdnjs.cloudflare.com cdn.jsdelivr.net; img-src ''self'' + data: cdn.redoc.ly cdnjs.cloudflare.com tile.openstreetmap.org' + Content-Type: + - application/json + Cross-Origin-Opener-Policy: + - same-origin + Location: + - http://localhost:8003/zaken/api/v1/zaakinformatieobjecten/7c01c899-b44e-4cb2-8ebc-9d2a48b6a186 + Referrer-Policy: + - same-origin + Vary: + - Accept, origin, Cookie + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + status: + code: 201 + message: Created +version: 1 diff --git a/src/opendms/api/tests/files/vcr_cassettes/ServiceZaakInformatieObjectViewSetTests/test_detail_not_found.yaml b/src/opendms/api/tests/files/vcr_cassettes/ServiceZaakInformatieObjectViewSetTests/test_detail_not_found.yaml new file mode 100644 index 00000000..e4d9e59f --- /dev/null +++ b/src/opendms/api/tests/files/vcr_cassettes/ServiceZaakInformatieObjectViewSetTests/test_detail_not_found.yaml @@ -0,0 +1,52 @@ +interactions: +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Crs: + - EPSG:4326 + Accept-Encoding: + - gzip, deflate, br, zstd + Connection: + - keep-alive + Content-Crs: + - EPSG:4326 + User-Agent: + - python-requests/2.33.1 + method: GET + uri: http://localhost:8003/zaken/api/v1/zaakinformatieobjecten/00000000-0000-0000-0000-000000000000 + response: + body: + string: '{"type":"http://localhost:8003/ref/fouten/NotFound/","code":"not_found","title":"Niet + gevonden.","status":404,"detail":"Niet gevonden.","instance":"urn:uuid:8ab7e2b1-7e64-4792-be23-166f51aacb62"}' + headers: + API-version: + - 1.5.1 + Allow: + - GET, PUT, PATCH, DELETE, HEAD, OPTIONS + Content-Length: + - '195' + Content-Security-Policy: + - 'object-src ''none''; frame-src ''self''; script-src ''self'' ''unsafe-inline'' + cdnjs.cloudflare.com cdn.jsdelivr.net; font-src ''self'' fonts.gstatic.com; + form-action ''self''; base-uri ''self''; frame-ancestors ''none''; worker-src + ''self'' blob:; default-src ''self''; style-src ''self'' ''unsafe-inline'' + fonts.googleapis.com cdnjs.cloudflare.com cdn.jsdelivr.net; img-src ''self'' + data: cdn.redoc.ly cdnjs.cloudflare.com tile.openstreetmap.org' + Content-Type: + - application/json + Cross-Origin-Opener-Policy: + - same-origin + Referrer-Policy: + - same-origin + Vary: + - Accept, origin, Cookie + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + status: + code: 404 + message: Not Found +version: 1 diff --git a/src/opendms/api/tests/files/vcr_cassettes/ServiceZaakInformatieObjectViewSetTests/test_detail_zaakinformatieobject.yaml b/src/opendms/api/tests/files/vcr_cassettes/ServiceZaakInformatieObjectViewSetTests/test_detail_zaakinformatieobject.yaml new file mode 100644 index 00000000..3072f4ae --- /dev/null +++ b/src/opendms/api/tests/files/vcr_cassettes/ServiceZaakInformatieObjectViewSetTests/test_detail_zaakinformatieobject.yaml @@ -0,0 +1,54 @@ +interactions: +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Crs: + - EPSG:4326 + Accept-Encoding: + - gzip, deflate, br, zstd + Connection: + - keep-alive + Content-Crs: + - EPSG:4326 + User-Agent: + - python-requests/2.33.1 + method: GET + uri: http://localhost:8003/zaken/api/v1/zaakinformatieobjecten/f931b3f6-f92e-4c46-b484-f68aa68acc18 + response: + body: + string: '{"url":"http://localhost:8003/zaken/api/v1/zaakinformatieobjecten/f931b3f6-f92e-4c46-b484-f68aa68acc18","uuid":"f931b3f6-f92e-4c46-b484-f68aa68acc18","informatieobject":"http://localhost:8003/documenten/api/v1/enkelvoudiginformatieobjecten/a9c82615-cee7-4e9d-b7a8-460623882c38","zaak":"http://localhost:8003/zaken/api/v1/zaken/840a1355-dd6d-48a4-a849-38fbf832db44","aardRelatieWeergave":"Hoort + bij, omgekeerd: kent","titel":"","beschrijving":"","registratiedatum":"2026-03-25T11:39:18.968000Z","vernietigingsdatum":null,"status":null}' + headers: + API-version: + - 1.5.1 + Allow: + - GET, PUT, PATCH, DELETE, HEAD, OPTIONS + Content-Length: + - '534' + Content-Security-Policy: + - 'object-src ''none''; frame-src ''self''; script-src ''self'' ''unsafe-inline'' + cdnjs.cloudflare.com cdn.jsdelivr.net; font-src ''self'' fonts.gstatic.com; + form-action ''self''; base-uri ''self''; frame-ancestors ''none''; worker-src + ''self'' blob:; default-src ''self''; style-src ''self'' ''unsafe-inline'' + fonts.googleapis.com cdnjs.cloudflare.com cdn.jsdelivr.net; img-src ''self'' + data: cdn.redoc.ly cdnjs.cloudflare.com tile.openstreetmap.org' + Content-Type: + - application/json + Cross-Origin-Opener-Policy: + - same-origin + ETag: + - '"8c0b5e87d0ec32426a0574fcd7f272fd"' + Referrer-Policy: + - same-origin + Vary: + - Accept, origin, Cookie + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + status: + code: 200 + message: OK +version: 1 diff --git a/src/opendms/api/tests/files/vcr_cassettes/ServiceZaakInformatieObjectViewSetTests/test_list_zaakinformatieobjecten.yaml b/src/opendms/api/tests/files/vcr_cassettes/ServiceZaakInformatieObjectViewSetTests/test_list_zaakinformatieobjecten.yaml new file mode 100644 index 00000000..1610cb19 --- /dev/null +++ b/src/opendms/api/tests/files/vcr_cassettes/ServiceZaakInformatieObjectViewSetTests/test_list_zaakinformatieobjecten.yaml @@ -0,0 +1,73 @@ +interactions: +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Crs: + - EPSG:4326 + Accept-Encoding: + - gzip, deflate, br, zstd + Connection: + - keep-alive + Content-Crs: + - EPSG:4326 + User-Agent: + - python-requests/2.33.1 + method: GET + uri: http://localhost:8003/zaken/api/v1/zaakinformatieobjecten + response: + body: + string: '[{"url":"http://localhost:8003/zaken/api/v1/zaakinformatieobjecten/82810fe8-ad64-4a4c-829b-44b1c4af2d03","uuid":"82810fe8-ad64-4a4c-829b-44b1c4af2d03","informatieobject":"http://localhost:8003/documenten/api/v1/enkelvoudiginformatieobjecten/a9c82615-cee7-4e9d-b7a8-460623882c38","zaak":"http://localhost:8003/zaken/api/v1/zaken/da18b89e-e7ac-49b2-9f5d-d6ef327e1b1d","aardRelatieWeergave":"Hoort + bij, omgekeerd: kent","titel":"","beschrijving":"","registratiedatum":"2026-06-10T10:06:46.779152Z","vernietigingsdatum":null,"status":null},{"url":"http://localhost:8003/zaken/api/v1/zaakinformatieobjecten/f477afb0-24c1-47c2-8d74-c424da4e9e61","uuid":"f477afb0-24c1-47c2-8d74-c424da4e9e61","informatieobject":"http://localhost:8003/documenten/api/v1/enkelvoudiginformatieobjecten/547fd056-0bcc-486b-b02a-18b40457d56f","zaak":"http://localhost:8003/zaken/api/v1/zaken/da18b89e-e7ac-49b2-9f5d-d6ef327e1b1d","aardRelatieWeergave":"Hoort + bij, omgekeerd: kent","titel":"","beschrijving":"","registratiedatum":"2026-06-04T15:18:16.863518Z","vernietigingsdatum":null,"status":null},{"url":"http://localhost:8003/zaken/api/v1/zaakinformatieobjecten/e92f4253-83a6-4f78-8249-458b81b32b91","uuid":"e92f4253-83a6-4f78-8249-458b81b32b91","informatieobject":"http://localhost:8003/documenten/api/v1/enkelvoudiginformatieobjecten/d34816d6-50c6-49ea-9b84-4803cbc45a3a","zaak":"http://localhost:8003/zaken/api/v1/zaken/2ae1c6df-c7f7-43c4-8d69-40db7022ccc2","aardRelatieWeergave":"Hoort + bij, omgekeerd: kent","titel":"","beschrijving":"","registratiedatum":"2026-03-25T11:39:20.571000Z","vernietigingsdatum":null,"status":null},{"url":"http://localhost:8003/zaken/api/v1/zaakinformatieobjecten/f40d0d92-56b4-4972-a01c-f5989f9c3f5d","uuid":"f40d0d92-56b4-4972-a01c-f5989f9c3f5d","informatieobject":"http://localhost:8003/documenten/api/v1/enkelvoudiginformatieobjecten/ef55f9ea-4004-4832-803e-2672bb1c6d4d","zaak":"http://localhost:8003/zaken/api/v1/zaken/85169f14-b860-4590-8787-4e026f90703e","aardRelatieWeergave":"Hoort + bij, omgekeerd: kent","titel":"","beschrijving":"","registratiedatum":"2026-03-25T11:39:20.525000Z","vernietigingsdatum":null,"status":null},{"url":"http://localhost:8003/zaken/api/v1/zaakinformatieobjecten/36f57df2-e0d6-4c7c-979b-905a37aee3f9","uuid":"36f57df2-e0d6-4c7c-979b-905a37aee3f9","informatieobject":"http://localhost:8003/documenten/api/v1/enkelvoudiginformatieobjecten/2f3722d8-c919-495b-a973-0802bd678887","zaak":"http://localhost:8003/zaken/api/v1/zaken/8c8375cf-f780-4c97-9ef5-630d55423a2e","aardRelatieWeergave":"Hoort + bij, omgekeerd: kent","titel":"","beschrijving":"","registratiedatum":"2026-03-25T11:39:20.418000Z","vernietigingsdatum":null,"status":null},{"url":"http://localhost:8003/zaken/api/v1/zaakinformatieobjecten/1075043c-ee8a-4392-8397-e4ca0a886ac2","uuid":"1075043c-ee8a-4392-8397-e4ca0a886ac2","informatieobject":"http://localhost:8003/documenten/api/v1/enkelvoudiginformatieobjecten/6df0ae09-0f21-4c8e-bc49-5bb23915f606","zaak":"http://localhost:8003/zaken/api/v1/zaken/bc5d35a8-956d-4b94-80af-86796d626f08","aardRelatieWeergave":"Hoort + bij, omgekeerd: kent","titel":"","beschrijving":"","registratiedatum":"2026-03-25T11:39:20.269000Z","vernietigingsdatum":null,"status":null},{"url":"http://localhost:8003/zaken/api/v1/zaakinformatieobjecten/1cdc89db-7678-46ea-b2cb-03b3d80b1819","uuid":"1cdc89db-7678-46ea-b2cb-03b3d80b1819","informatieobject":"http://localhost:8003/documenten/api/v1/enkelvoudiginformatieobjecten/819c31fc-81e0-4d96-990f-6221cbe987c6","zaak":"http://localhost:8003/zaken/api/v1/zaken/df01f13a-844d-4901-9fe4-dd19603557c2","aardRelatieWeergave":"Hoort + bij, omgekeerd: kent","titel":"","beschrijving":"","registratiedatum":"2026-03-25T11:39:20.187000Z","vernietigingsdatum":null,"status":null},{"url":"http://localhost:8003/zaken/api/v1/zaakinformatieobjecten/b489a7de-3f66-4fa6-9bb1-12c01befbc35","uuid":"b489a7de-3f66-4fa6-9bb1-12c01befbc35","informatieobject":"http://localhost:8003/documenten/api/v1/enkelvoudiginformatieobjecten/fc58a8f1-f2f0-4eb8-919c-4360f91a3415","zaak":"http://localhost:8003/zaken/api/v1/zaken/2c8c15af-4fdc-4b6a-a29c-75df40df38ed","aardRelatieWeergave":"Hoort + bij, omgekeerd: kent","titel":"","beschrijving":"","registratiedatum":"2026-03-25T11:39:20.095000Z","vernietigingsdatum":null,"status":null},{"url":"http://localhost:8003/zaken/api/v1/zaakinformatieobjecten/b647cdee-9a95-4681-861d-b57e8eea5021","uuid":"b647cdee-9a95-4681-861d-b57e8eea5021","informatieobject":"http://localhost:8003/documenten/api/v1/enkelvoudiginformatieobjecten/801ddfea-a4a6-4846-9887-1efc0adb910b","zaak":"http://localhost:8003/zaken/api/v1/zaken/3c3f01b0-359b-4f68-9b0a-813e6cce4817","aardRelatieWeergave":"Hoort + bij, omgekeerd: kent","titel":"","beschrijving":"","registratiedatum":"2026-03-25T11:39:19.985000Z","vernietigingsdatum":null,"status":null},{"url":"http://localhost:8003/zaken/api/v1/zaakinformatieobjecten/c077f3c3-02ad-44d6-a242-54c7a0413bee","uuid":"c077f3c3-02ad-44d6-a242-54c7a0413bee","informatieobject":"http://localhost:8003/documenten/api/v1/enkelvoudiginformatieobjecten/c4a2d123-1817-4b3a-a330-67c1282b1594","zaak":"http://localhost:8003/zaken/api/v1/zaken/71f05dee-6ded-4e81-bef8-4a141912d73d","aardRelatieWeergave":"Hoort + bij, omgekeerd: kent","titel":"","beschrijving":"","registratiedatum":"2026-03-25T11:39:19.890000Z","vernietigingsdatum":null,"status":null},{"url":"http://localhost:8003/zaken/api/v1/zaakinformatieobjecten/5a9acb7b-db93-49b5-9b52-f263a00e1dea","uuid":"5a9acb7b-db93-49b5-9b52-f263a00e1dea","informatieobject":"http://localhost:8003/documenten/api/v1/enkelvoudiginformatieobjecten/dbfe1f09-2803-4a40-862c-f2ca75f384d1","zaak":"http://localhost:8003/zaken/api/v1/zaken/fc35705e-6a27-46c8-8cf1-b3119dba7bde","aardRelatieWeergave":"Hoort + bij, omgekeerd: kent","titel":"","beschrijving":"","registratiedatum":"2026-03-25T11:39:19.808000Z","vernietigingsdatum":null,"status":null},{"url":"http://localhost:8003/zaken/api/v1/zaakinformatieobjecten/f7b8d99e-37b9-420f-a74a-5d8a03e5b391","uuid":"f7b8d99e-37b9-420f-a74a-5d8a03e5b391","informatieobject":"http://localhost:8003/documenten/api/v1/enkelvoudiginformatieobjecten/f7eec7ce-9bae-4b15-8928-2c87d0071def","zaak":"http://localhost:8003/zaken/api/v1/zaken/30876d53-51f3-4d75-a132-d8764f55e1e5","aardRelatieWeergave":"Hoort + bij, omgekeerd: kent","titel":"","beschrijving":"","registratiedatum":"2026-03-25T11:39:19.702000Z","vernietigingsdatum":null,"status":null},{"url":"http://localhost:8003/zaken/api/v1/zaakinformatieobjecten/e7a265d7-36ef-4c34-9dea-eff00e8723c6","uuid":"e7a265d7-36ef-4c34-9dea-eff00e8723c6","informatieobject":"http://localhost:8003/documenten/api/v1/enkelvoudiginformatieobjecten/0faee041-27ec-4b81-99a0-42ebab29a7db","zaak":"http://localhost:8003/zaken/api/v1/zaken/511f30e1-7538-48c0-af85-41e76a38cd24","aardRelatieWeergave":"Hoort + bij, omgekeerd: kent","titel":"","beschrijving":"","registratiedatum":"2026-03-25T11:39:19.563000Z","vernietigingsdatum":null,"status":null},{"url":"http://localhost:8003/zaken/api/v1/zaakinformatieobjecten/d4ddeace-80fc-4cd4-8c86-e6a4761e7d94","uuid":"d4ddeace-80fc-4cd4-8c86-e6a4761e7d94","informatieobject":"http://localhost:8003/documenten/api/v1/enkelvoudiginformatieobjecten/f8c1d2cc-780e-452a-8871-187048341200","zaak":"http://localhost:8003/zaken/api/v1/zaken/f0502f2c-ace2-42b0-9d3d-84e3543d8b6a","aardRelatieWeergave":"Hoort + bij, omgekeerd: kent","titel":"","beschrijving":"","registratiedatum":"2026-03-25T11:39:19.484000Z","vernietigingsdatum":null,"status":null},{"url":"http://localhost:8003/zaken/api/v1/zaakinformatieobjecten/49b83ac3-95ff-4343-b3f7-7adf7bd8efc6","uuid":"49b83ac3-95ff-4343-b3f7-7adf7bd8efc6","informatieobject":"http://localhost:8003/documenten/api/v1/enkelvoudiginformatieobjecten/10e3074d-c0ee-49e0-b099-6abe066cd56a","zaak":"http://localhost:8003/zaken/api/v1/zaken/8687acc6-3386-4a84-98f0-07a1d53db5ae","aardRelatieWeergave":"Hoort + bij, omgekeerd: kent","titel":"","beschrijving":"","registratiedatum":"2026-03-25T11:39:19.443000Z","vernietigingsdatum":null,"status":null},{"url":"http://localhost:8003/zaken/api/v1/zaakinformatieobjecten/b7886b37-06d6-4bf3-99b3-4206f194e23a","uuid":"b7886b37-06d6-4bf3-99b3-4206f194e23a","informatieobject":"http://localhost:8003/documenten/api/v1/enkelvoudiginformatieobjecten/a2d46f98-baa0-4b33-9c87-c14517ae695b","zaak":"http://localhost:8003/zaken/api/v1/zaken/918b7c32-c0f1-4a1f-b50b-e803e6694fa4","aardRelatieWeergave":"Hoort + bij, omgekeerd: kent","titel":"","beschrijving":"","registratiedatum":"2026-03-25T11:39:19.334000Z","vernietigingsdatum":null,"status":null},{"url":"http://localhost:8003/zaken/api/v1/zaakinformatieobjecten/984cc103-cf37-40cb-81d4-14bcbc51d03e","uuid":"984cc103-cf37-40cb-81d4-14bcbc51d03e","informatieobject":"http://localhost:8003/documenten/api/v1/enkelvoudiginformatieobjecten/435b77c7-db8d-47cf-b2df-eea3a91089ee","zaak":"http://localhost:8003/zaken/api/v1/zaken/d89fc6fd-29ce-493d-b27d-b2e4fd4424fe","aardRelatieWeergave":"Hoort + bij, omgekeerd: kent","titel":"","beschrijving":"","registratiedatum":"2026-03-25T11:39:19.317000Z","vernietigingsdatum":null,"status":null},{"url":"http://localhost:8003/zaken/api/v1/zaakinformatieobjecten/f3c93780-6fd8-47fb-9af6-9b18f81b3b88","uuid":"f3c93780-6fd8-47fb-9af6-9b18f81b3b88","informatieobject":"http://localhost:8003/documenten/api/v1/enkelvoudiginformatieobjecten/630f20a5-b099-4d84-a504-51796b6c5504","zaak":"http://localhost:8003/zaken/api/v1/zaken/6ef989c2-4df9-44fa-8b8a-e48a87a12b3f","aardRelatieWeergave":"Hoort + bij, omgekeerd: kent","titel":"","beschrijving":"","registratiedatum":"2026-03-25T11:39:19.255000Z","vernietigingsdatum":null,"status":null},{"url":"http://localhost:8003/zaken/api/v1/zaakinformatieobjecten/2151e4f5-b7a3-4d23-9644-431727cb5b40","uuid":"2151e4f5-b7a3-4d23-9644-431727cb5b40","informatieobject":"http://localhost:8003/documenten/api/v1/enkelvoudiginformatieobjecten/4d5e39b0-e83f-4ff3-b8c8-6b3eb5b24867","zaak":"http://localhost:8003/zaken/api/v1/zaken/94bbcc1c-24ce-49ee-8855-62aed026a830","aardRelatieWeergave":"Hoort + bij, omgekeerd: kent","titel":"","beschrijving":"","registratiedatum":"2026-03-25T11:39:19.157000Z","vernietigingsdatum":null,"status":null},{"url":"http://localhost:8003/zaken/api/v1/zaakinformatieobjecten/190f1178-4701-438b-840e-16a1fadaaf10","uuid":"190f1178-4701-438b-840e-16a1fadaaf10","informatieobject":"http://localhost:8003/documenten/api/v1/enkelvoudiginformatieobjecten/bd173e9d-d97d-4a2c-858d-d0c9ba72f7cc","zaak":"http://localhost:8003/zaken/api/v1/zaken/34faf0d8-7dce-460f-8e63-063b137374fb","aardRelatieWeergave":"Hoort + bij, omgekeerd: kent","titel":"","beschrijving":"","registratiedatum":"2026-03-25T11:39:19.043000Z","vernietigingsdatum":null,"status":null},{"url":"http://localhost:8003/zaken/api/v1/zaakinformatieobjecten/f931b3f6-f92e-4c46-b484-f68aa68acc18","uuid":"f931b3f6-f92e-4c46-b484-f68aa68acc18","informatieobject":"http://localhost:8003/documenten/api/v1/enkelvoudiginformatieobjecten/a9c82615-cee7-4e9d-b7a8-460623882c38","zaak":"http://localhost:8003/zaken/api/v1/zaken/840a1355-dd6d-48a4-a849-38fbf832db44","aardRelatieWeergave":"Hoort + bij, omgekeerd: kent","titel":"","beschrijving":"","registratiedatum":"2026-03-25T11:39:18.968000Z","vernietigingsdatum":null,"status":null},{"url":"http://localhost:8003/zaken/api/v1/zaakinformatieobjecten/efef805d-87f2-4e9a-86ed-98a6dfe9deb1","uuid":"efef805d-87f2-4e9a-86ed-98a6dfe9deb1","informatieobject":"http://localhost:8003/documenten/api/v1/enkelvoudiginformatieobjecten/ea16fa8c-4bab-4065-a28a-f6574625205d","zaak":"http://localhost:8003/zaken/api/v1/zaken/da18b89e-e7ac-49b2-9f5d-d6ef327e1b1d","aardRelatieWeergave":"Hoort + bij, omgekeerd: kent","titel":"","beschrijving":"","registratiedatum":"2026-03-25T11:39:18.944000Z","vernietigingsdatum":null,"status":null}]' + headers: + API-version: + - 1.5.1 + Allow: + - GET, POST, HEAD, OPTIONS + Content-Length: + - '11771' + Content-Security-Policy: + - 'object-src ''none''; frame-src ''self''; script-src ''self'' ''unsafe-inline'' + cdnjs.cloudflare.com cdn.jsdelivr.net; font-src ''self'' fonts.gstatic.com; + form-action ''self''; base-uri ''self''; frame-ancestors ''none''; worker-src + ''self'' blob:; default-src ''self''; style-src ''self'' ''unsafe-inline'' + fonts.googleapis.com cdnjs.cloudflare.com cdn.jsdelivr.net; img-src ''self'' + data: cdn.redoc.ly cdnjs.cloudflare.com tile.openstreetmap.org' + Content-Type: + - application/json + Cross-Origin-Opener-Policy: + - same-origin + Referrer-Policy: + - same-origin + Vary: + - Accept, origin, Cookie + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + status: + code: 200 + message: OK +version: 1 diff --git a/src/opendms/api/tests/test_zaak.py b/src/opendms/api/tests/test_zaak.py index 9ddea5f5..9f02e49c 100644 --- a/src/opendms/api/tests/test_zaak.py +++ b/src/opendms/api/tests/test_zaak.py @@ -1,6 +1,9 @@ +from unittest.mock import Mock, patch + from django.urls import NoReverseMatch from maykin_common.vcr import VCRMixin +from requests import HTTPError from requests.exceptions import RequestException from rest_framework import status from vng_api_common.tests import reverse @@ -548,3 +551,140 @@ def test_create_zaak(self): self.assertIn("uuid", data) self.assertIn("url", data) + + +class ServiceZaakInformatieObjectViewSetTests(VCRMixin, APITestCase): + @classmethod + def setUpTestData(cls): + super().setUpTestData() + + cls.ztc_service = ServiceFactory.create(for_ztc_service_docker_compose=True) + cls.zrc_service = ServiceFactory.create(for_zrc_service_docker_compose=True) + + cls.configuration = ZGWApiGroupConfigFactory.create( + ztc_service=cls.ztc_service, + zrc_service=cls.zrc_service, + ) + + cls.list_url = reverse( + "api:service-zaakinformatieobjecten-list", + kwargs={"service_slug": cls.ztc_service.slug}, + ) + + def test_list_zaakinformatieobjecten(self): + response = self.client.get(self.list_url) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + + data = response.json() + + self.assertGreater(data["count"], 0) + self.assertGreater(len(data["results"]), 0) + + def test_create_requires_required_fields(self): + response = self.client.post( + self.list_url, + {}, + format="json", + ) + + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + invalid_params = response.json()["invalidParams"] + invalid_param_names = {param["name"] for param in invalid_params} + + self.assertIn("zaak", invalid_param_names) + self.assertIn("informatieobject", invalid_param_names) + + def test_create_zaakinformatieobject(self): + payload = { + "zaak": "http://localhost:8003/zaken/api/v1/zaken/840a1355-dd6d-48a4-a849-38fbf832db44", + "informatieobject": "http://localhost:8003/documenten/api/v1/enkelvoudiginformatieobjecten/ea16fa8c-4bab-4065-a28a-f6574625205d", + } + + response = self.client.post( + self.list_url, + payload, + format="json", + ) + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + + data = response.json() + + self.assertEqual(data["zaak"], payload["zaak"]) + self.assertEqual( + data["informatieobject"], + payload["informatieobject"], + ) + + @patch("opendms.api.viewsets.get_zaken_client") + def test_create_forwards_remote_validation_errors( + self, + mock_get_zaken_client, + ): + response_mock = Mock() + response_mock.status_code = 400 + response_mock.json.return_value = { + "invalidParams": [ + { + "name": "nonFieldErrors", + "code": "unique", + } + ] + } + + client = Mock() + client.create_zaakinformatieobject.side_effect = HTTPError( + response=response_mock + ) + mock_get_zaken_client.return_value.__enter__.return_value = client + + response = self.client.post( + self.list_url, + { + "zaak": "http://example.com/zaak", + "informatieobject": "http://example.com/document", + }, + format="json", + ) + + self.assertEqual(response.status_code, 400) + self.assertEqual( + response.json()["invalidParams"][0]["code"], + "unique", + ) + + def test_detail_zaakinformatieobject(self): + zio_uuid = "f931b3f6-f92e-4c46-b484-f68aa68acc18" + + detail_url = reverse( + "api:service-zaakinformatieobjecten-detail", + kwargs={ + "service_slug": self.ztc_service.slug, + "pk": zio_uuid, + }, + ) + + response = self.client.get(detail_url) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual( + response.json()["zaak"], + "http://localhost:8003/zaken/api/v1/zaken/840a1355-dd6d-48a4-a849-38fbf832db44", + ) + + def test_detail_not_found(self): + response = self.client.get( + reverse( + "api:service-zaakinformatieobjecten-detail", + kwargs={ + "service_slug": self.ztc_service.slug, + "pk": "00000000-0000-0000-0000-000000000000", + }, + ) + ) + + self.assertEqual( + response.status_code, + status.HTTP_404_NOT_FOUND, + ) diff --git a/src/opendms/api/urls.py b/src/opendms/api/urls.py index 8e9f8506..6e7ed030 100644 --- a/src/opendms/api/urls.py +++ b/src/opendms/api/urls.py @@ -10,6 +10,7 @@ InformatieObjectTypeViewSet, SearchView, ServiceViewSet, + ServiceZaakInformatieObjectViewSet, ServiceZaakViewSet, StatusTypeListViewSet, StatusTypeViewSet, @@ -44,7 +45,11 @@ ServiceZaakViewSet, basename="service-zaken", ) - +service_router.register( + r"zaakinformatieobjecten", + ServiceZaakInformatieObjectViewSet, + basename="service-zaakinformatieobjecten", +) service_router.register( r"statustypen", StatusTypeViewSet, diff --git a/src/opendms/api/utils/schema.py b/src/opendms/api/utils/schema.py index b5baac44..8adb5be3 100644 --- a/src/opendms/api/utils/schema.py +++ b/src/opendms/api/utils/schema.py @@ -84,7 +84,6 @@ def param( location: str = OpenApiParameter.QUERY, required: bool = False, ) -> OpenApiParameter: - return OpenApiParameter( name=name, type=type_param, diff --git a/src/opendms/api/viewsets.py b/src/opendms/api/viewsets.py index 09df548d..af8184c2 100644 --- a/src/opendms/api/viewsets.py +++ b/src/opendms/api/viewsets.py @@ -18,6 +18,7 @@ extend_schema, extend_schema_view, ) +from requests import HTTPError from rest_framework import status, viewsets from rest_framework.decorators import action from rest_framework.filters import SearchFilter @@ -50,6 +51,7 @@ ServiceSerializer, StatusTypeSerializer, ZaakCreateSerializer, + ZaakInformatieObjectCreateSerializer, ZaakSerializer, ZaakTypeSerializer, ) @@ -906,3 +908,82 @@ def get_object(self) -> InformatieObjectType: with get_informatieobjecttypen_client(self.service) as client: return client.get_item_by_uuid(uuid) + + +@extend_schema_view( + list=extend_schema( + parameters=[ + OpenApiParameter( + name="service_slug", + type=OpenApiTypes.STR, + location=OpenApiParameter.PATH, + required=True, + ), + ] + ), + retrieve=extend_schema( + parameters=[ + OpenApiParameter( + name="service_slug", + type=OpenApiTypes.STR, + location=OpenApiParameter.PATH, + required=True, + ), + OpenApiParameter( + name="id", + type=OpenApiTypes.UUID, + location=OpenApiParameter.PATH, + required=True, + ), + ], + ), +) +class ServiceZaakInformatieObjectViewSet( + ReadOnlyViewSetMixin, + viewsets.ViewSet, +): + serializer_class = ZaakInformatieObjectCreateSerializer + pagination_class = CountedPagination + queryset = None + lookup_field = "pk" + + def get_paginated_queryset(self, params): + with get_zaken_client(self.zgw_group.zrc_service) as client: + return client.get_paginated_zaakinformatieobjecten(params) + + def get_object(self): + uuid = self.kwargs[self.lookup_field] + + with get_zaken_client(self.zgw_group.zrc_service) as client: + return client.get_zaakinformatieobject(uuid) + + @extend_schema( + summary="zaakinformatieobjectCreate", + parameters=[ + OpenApiParameter( + name="service_slug", + type=OpenApiTypes.STR, + location=OpenApiParameter.PATH, + required=True, + ), + ], + request=ZaakInformatieObjectCreateSerializer, + responses={201: OpenApiTypes.OBJECT}, + ) + def create(self, request, *args, **kwargs): + serializer = self.serializer_class(data=request.data) + serializer.is_valid(raise_exception=True) + + try: + with get_zaken_client(self.zgw_group.zrc_service) as client: + result = client.create_zaakinformatieobject(serializer.validated_data) + except HTTPError as exc: + return Response( + exc.response.json(), + status=exc.response.status_code, + ) + + return Response( + result, + status=status.HTTP_201_CREATED, + ) diff --git a/src/opendms/frontend/src/types/schema.d.ts b/src/opendms/frontend/src/types/schema.d.ts index d9ea4ed0..611060e6 100644 --- a/src/opendms/frontend/src/types/schema.d.ts +++ b/src/opendms/frontend/src/types/schema.d.ts @@ -172,6 +172,39 @@ export interface paths { patch?: never; trace?: never; }; + "/api/v1/services/{serviceSlug}/zaakinformatieobjecten": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["apiV1ServicesZaakinformatieobjectenList"]; + put?: never; + /** zaakinformatieobjectCreate */ + post: operations["apiV1ServicesZaakinformatieobjectenCreate"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/v1/services/{serviceSlug}/zaakinformatieobjecten/{id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["apiV1ServicesZaakinformatieobjectenRetrieve"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; "/api/v1/services/{serviceSlug}/zaaktypen": { parameters: { query?: never; @@ -750,6 +783,11 @@ export interface components { count: number; results: components["schemas"]["StatusType"][]; }; + PaginatedZaakInformatieObjectCreateList: { + /** @example 123 */ + count: number; + results: components["schemas"]["ZaakInformatieObjectCreate"][]; + }; PaginatedZaakList: { /** @example 123 */ count: number; @@ -966,6 +1004,14 @@ export interface components { bronorganisatie: string; verantwoordelijkeOrganisatie: string; }; + ZaakInformatieObjectCreate: { + /** Format: uri */ + zaak: string; + /** Format: uri */ + informatieobject: string; + titel?: string; + beschrijving?: string; + }; ZaakType: { /** * Format: uuid @@ -1246,6 +1292,87 @@ export interface operations { }; }; }; + apiV1ServicesZaakinformatieobjectenList: { + parameters: { + query?: { + /** @description A page number within the paginated result set. */ + page?: number; + /** @description Het aantal resultaten terug te geven per pagina. (default: 100, maximum: 500). */ + pageSize?: number; + }; + header?: never; + path: { + serviceSlug: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + 200: { + headers: { + /** @description Geeft een specifieke API-versie aan in de context van een specifieke aanroep. Voorbeeld: 1.2.1. */ + "API-version"?: string; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["PaginatedZaakInformatieObjectCreateList"]; + }; + }; + }; + }; + apiV1ServicesZaakinformatieobjectenCreate: { + parameters: { + query?: never; + header?: never; + path: { + serviceSlug: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["ZaakInformatieObjectCreate"]; + }; + }; + responses: { + 201: { + headers: { + /** @description Geeft een specifieke API-versie aan in de context van een specifieke aanroep. Voorbeeld: 1.2.1. */ + "API-version"?: string; + [name: string]: unknown; + }; + content: { + "application/json": { + [key: string]: unknown; + }; + }; + }; + }; + }; + apiV1ServicesZaakinformatieobjectenRetrieve: { + parameters: { + query?: never; + header?: never; + path: { + id: string; + serviceSlug: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + 200: { + headers: { + /** @description Geeft een specifieke API-versie aan in de context van een specifieke aanroep. Voorbeeld: 1.2.1. */ + "API-version"?: string; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ZaakInformatieObjectCreate"]; + }; + }; + }; + }; apiV1ServicesZaaktypenList: { parameters: { query?: {