From c60a0efa61faf53f60d295071c64963b948e9ed5 Mon Sep 17 00:00:00 2001 From: Viicos <65306057+Viicos@users.noreply.github.com> Date: Fri, 9 Feb 2024 14:50:31 +0100 Subject: [PATCH 1/6] [#3688] Add objecttypes client --- .../contrib/objects_api/clients/__init__.py | 3 +- .../contrib/objects_api/clients/objects.py | 4 -- .../objects_api/clients/objecttypes.py | 54 ++++++++++++++++ ...ientTest.test_list_objecttype_version.yaml | 46 ++++++++++++++ ...typesClientTest.test_list_objecttypes.yaml | 36 +++++++++++ ...tTest.test_list_objectypes_pagination.yaml | 36 +++++++++++ .../tests/test_objecttypes_client.py | 62 +++++++++++++++++++ 7 files changed, 236 insertions(+), 5 deletions(-) create mode 100644 src/openforms/contrib/objects_api/clients/objecttypes.py create mode 100644 src/openforms/registrations/contrib/objects_api/tests/files/vcr_cassettes/ObjecttypesClientTest/ObjecttypesClientTest.test_list_objecttype_version.yaml create mode 100644 src/openforms/registrations/contrib/objects_api/tests/files/vcr_cassettes/ObjecttypesClientTest/ObjecttypesClientTest.test_list_objecttypes.yaml create mode 100644 src/openforms/registrations/contrib/objects_api/tests/files/vcr_cassettes/ObjecttypesClientTest/ObjecttypesClientTest.test_list_objectypes_pagination.yaml create mode 100644 src/openforms/registrations/contrib/objects_api/tests/test_objecttypes_client.py diff --git a/src/openforms/contrib/objects_api/clients/__init__.py b/src/openforms/contrib/objects_api/clients/__init__.py index 4621aa9e43..ed05550f8a 100644 --- a/src/openforms/contrib/objects_api/clients/__init__.py +++ b/src/openforms/contrib/objects_api/clients/__init__.py @@ -1,3 +1,4 @@ -from .objects import ObjectsClient, ObjecttypesClient +from .objects import ObjectsClient +from .objecttypes import ObjecttypesClient __all__ = ["ObjectsClient", "ObjecttypesClient"] diff --git a/src/openforms/contrib/objects_api/clients/objects.py b/src/openforms/contrib/objects_api/clients/objects.py index 99feb537bd..a739003aed 100644 --- a/src/openforms/contrib/objects_api/clients/objects.py +++ b/src/openforms/contrib/objects_api/clients/objects.py @@ -9,7 +9,3 @@ def create_object(self, object_data: dict) -> dict: response.raise_for_status() return response.json() - - -class ObjecttypesClient(NLXClient): - pass diff --git a/src/openforms/contrib/objects_api/clients/objecttypes.py b/src/openforms/contrib/objects_api/clients/objecttypes.py new file mode 100644 index 0000000000..0002ac67d1 --- /dev/null +++ b/src/openforms/contrib/objects_api/clients/objecttypes.py @@ -0,0 +1,54 @@ +from typing import Any +from uuid import UUID + +from zgw_consumers.nlx import NLXClient + +from openforms.utils.api_clients import PaginatedResponseData, pagination_helper + + +class ObjecttypesClient(NLXClient): + + def _get_paginated( + self, + endpoint: str, + page: int | None = None, + page_size: int | None = None, + query_params: dict[Any, Any] | None = None, + ): + query_params = query_params or {} + if page is None and page_size is None: + response = self.get(endpoint, params=query_params) + response.raise_for_status() + data: PaginatedResponseData[dict[str, Any]] = response.json() + all_data = pagination_helper(self, data) + return list(all_data) + + if page is not None: + query_params["page"] = page + if page_size is not None: + query_params["pageSize"] = page_size + + response = self.get(endpoint, params=query_params) + response.raise_for_status() + return response.json()["results"] + + def list_objecttypes( + self, + page: int | None = None, + page_size: int | None = None, + ) -> list[dict[str, Any]]: + return self._get_paginated( + "objecttypes", + page=page, + page_size=page_size, + ) + + def list_objecttype_versions( + self, + objecttype_uuid: str | UUID, + page: int | None = None, + page_size: int | None = None, + ) -> list[dict[str, Any]]: + return self._get_paginated( + f"objecttypes/{objecttype_uuid}/versions", page=page, page_size=page_size + ) diff --git a/src/openforms/registrations/contrib/objects_api/tests/files/vcr_cassettes/ObjecttypesClientTest/ObjecttypesClientTest.test_list_objecttype_version.yaml b/src/openforms/registrations/contrib/objects_api/tests/files/vcr_cassettes/ObjecttypesClientTest/ObjecttypesClientTest.test_list_objecttype_version.yaml new file mode 100644 index 0000000000..9e05bcb9b6 --- /dev/null +++ b/src/openforms/registrations/contrib/objects_api/tests/files/vcr_cassettes/ObjecttypesClientTest/ObjecttypesClientTest.test_list_objecttype_version.yaml @@ -0,0 +1,46 @@ +interactions: +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate, br + Authorization: + - Token 171be5abaf41e7856b423ad513df1ef8f867ff48 + Connection: + - keep-alive + User-Agent: + - python-requests/2.31.0 + method: GET + uri: http://localhost:8001/api/v2/objecttypes/8e46e0a5-b1b4-449b-b9e9-fa3cea655f48/versions + response: + body: + string: '{"count":3,"next":null,"previous":null,"results":[{"url":"http://localhost:8001/api/v2/objecttypes/8e46e0a5-b1b4-449b-b9e9-fa3cea655f48/versions/3","version":3,"objectType":"http://localhost:8001/api/v2/objecttypes/8e46e0a5-b1b4-449b-b9e9-fa3cea655f48","status":"draft","jsonSchema":{"$id":"https://example.com/person.schema.json","type":"object","title":"Person","$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"age":{"type":"integer","minimum":18,"description":"Age + in years which must be equal to or greater than 18."},"country":{"type":"string","description":"The + person''s current country."},"lastName":{"type":"string","description":"The + person''s last name."},"firstName":{"type":"string","description":"The person''s + first name."}}},"createdAt":"2024-02-08","modifiedAt":"2024-02-08","publishedAt":"2024-02-08"},{"url":"http://localhost:8001/api/v2/objecttypes/8e46e0a5-b1b4-449b-b9e9-fa3cea655f48/versions/2","version":2,"objectType":"http://localhost:8001/api/v2/objecttypes/8e46e0a5-b1b4-449b-b9e9-fa3cea655f48","status":"published","jsonSchema":{"$id":"https://example.com/person.schema.json","type":"object","title":"Person","$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"age":{"type":"integer","minimum":18,"description":"Age + in years which must be equal to or greater than 18."},"lastName":{"type":"string","description":"The + person''s last name."},"firstName":{"type":"string","description":"The person''s + first name."}}},"createdAt":"2024-02-08","modifiedAt":"2024-02-08","publishedAt":"2024-02-08"},{"url":"http://localhost:8001/api/v2/objecttypes/8e46e0a5-b1b4-449b-b9e9-fa3cea655f48/versions/1","version":1,"objectType":"http://localhost:8001/api/v2/objecttypes/8e46e0a5-b1b4-449b-b9e9-fa3cea655f48","status":"published","jsonSchema":{"$id":"https://example.com/person.schema.json","type":"object","title":"Person","$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"age":{"type":"integer","minimum":0,"description":"Age + in years which must be equal to or greater than zero."},"lastName":{"type":"string","description":"The + person''s last name."},"firstName":{"type":"string","description":"The person''s + first name."}}},"createdAt":"2023-10-24","modifiedAt":"2024-02-08","publishedAt":"2024-02-08"}]}' + headers: + Allow: + - GET, POST, HEAD, OPTIONS + Content-Length: + - '2282' + Content-Type: + - application/json + Referrer-Policy: + - same-origin + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + status: + code: 200 + message: OK +version: 1 diff --git a/src/openforms/registrations/contrib/objects_api/tests/files/vcr_cassettes/ObjecttypesClientTest/ObjecttypesClientTest.test_list_objecttypes.yaml b/src/openforms/registrations/contrib/objects_api/tests/files/vcr_cassettes/ObjecttypesClientTest/ObjecttypesClientTest.test_list_objecttypes.yaml new file mode 100644 index 0000000000..cdabace3c7 --- /dev/null +++ b/src/openforms/registrations/contrib/objects_api/tests/files/vcr_cassettes/ObjecttypesClientTest/ObjecttypesClientTest.test_list_objecttypes.yaml @@ -0,0 +1,36 @@ +interactions: +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate, br + Authorization: + - Token 171be5abaf41e7856b423ad513df1ef8f867ff48 + Connection: + - keep-alive + User-Agent: + - python-requests/2.31.0 + method: GET + uri: http://localhost:8001/api/v2/objecttypes + response: + body: + string: '{"count":2,"next":null,"previous":null,"results":[{"url":"http://localhost:8001/api/v2/objecttypes/3edfdaf7-f469-470b-a391-bb7ea015bd6f","uuid":"3edfdaf7-f469-470b-a391-bb7ea015bd6f","name":"Tree","namePlural":"Trees","description":"","dataClassification":"confidential","maintainerOrganization":"","maintainerDepartment":"","contactPerson":"","contactEmail":"","source":"","updateFrequency":"unknown","providerOrganization":"","documentationUrl":"","labels":{},"createdAt":"2024-02-08","modifiedAt":"2024-02-08","allowGeometry":true,"versions":["http://localhost:8001/api/v2/objecttypes/3edfdaf7-f469-470b-a391-bb7ea015bd6f/versions/1"]},{"url":"http://localhost:8001/api/v2/objecttypes/8e46e0a5-b1b4-449b-b9e9-fa3cea655f48","uuid":"8e46e0a5-b1b4-449b-b9e9-fa3cea655f48","name":"Person","namePlural":"Persons","description":"","dataClassification":"open","maintainerOrganization":"","maintainerDepartment":"","contactPerson":"","contactEmail":"","source":"","updateFrequency":"unknown","providerOrganization":"","documentationUrl":"","labels":{},"createdAt":"2023-10-24","modifiedAt":"2024-02-08","allowGeometry":true,"versions":["http://localhost:8001/api/v2/objecttypes/8e46e0a5-b1b4-449b-b9e9-fa3cea655f48/versions/1","http://localhost:8001/api/v2/objecttypes/8e46e0a5-b1b4-449b-b9e9-fa3cea655f48/versions/2","http://localhost:8001/api/v2/objecttypes/8e46e0a5-b1b4-449b-b9e9-fa3cea655f48/versions/3"]}]}' + headers: + Allow: + - GET, POST, HEAD, OPTIONS + Content-Length: + - '1407' + Content-Type: + - application/json + Referrer-Policy: + - same-origin + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + status: + code: 200 + message: OK +version: 1 diff --git a/src/openforms/registrations/contrib/objects_api/tests/files/vcr_cassettes/ObjecttypesClientTest/ObjecttypesClientTest.test_list_objectypes_pagination.yaml b/src/openforms/registrations/contrib/objects_api/tests/files/vcr_cassettes/ObjecttypesClientTest/ObjecttypesClientTest.test_list_objectypes_pagination.yaml new file mode 100644 index 0000000000..15ff1c8f1d --- /dev/null +++ b/src/openforms/registrations/contrib/objects_api/tests/files/vcr_cassettes/ObjecttypesClientTest/ObjecttypesClientTest.test_list_objectypes_pagination.yaml @@ -0,0 +1,36 @@ +interactions: +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate, br + Authorization: + - Token 171be5abaf41e7856b423ad513df1ef8f867ff48 + Connection: + - keep-alive + User-Agent: + - python-requests/2.31.0 + method: GET + uri: http://localhost:8001/api/v2/objecttypes?page=1&pageSize=1 + response: + body: + string: '{"count":2,"next":"http://localhost:8001/api/v2/objecttypes?page=2&pageSize=1","previous":null,"results":[{"url":"http://localhost:8001/api/v2/objecttypes/3edfdaf7-f469-470b-a391-bb7ea015bd6f","uuid":"3edfdaf7-f469-470b-a391-bb7ea015bd6f","name":"Tree","namePlural":"Trees","description":"","dataClassification":"confidential","maintainerOrganization":"","maintainerDepartment":"","contactPerson":"","contactEmail":"","source":"","updateFrequency":"unknown","providerOrganization":"","documentationUrl":"","labels":{},"createdAt":"2024-02-08","modifiedAt":"2024-02-08","allowGeometry":true,"versions":["http://localhost:8001/api/v2/objecttypes/3edfdaf7-f469-470b-a391-bb7ea015bd6f/versions/1"]}]}' + headers: + Allow: + - GET, POST, HEAD, OPTIONS + Content-Length: + - '696' + Content-Type: + - application/json + Referrer-Policy: + - same-origin + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + status: + code: 200 + message: OK +version: 1 diff --git a/src/openforms/registrations/contrib/objects_api/tests/test_objecttypes_client.py b/src/openforms/registrations/contrib/objects_api/tests/test_objecttypes_client.py new file mode 100644 index 0000000000..a605e00281 --- /dev/null +++ b/src/openforms/registrations/contrib/objects_api/tests/test_objecttypes_client.py @@ -0,0 +1,62 @@ +from pathlib import Path +from unittest.mock import patch + +from django.test import TestCase + +from zgw_consumers.constants import APITypes, AuthTypes +from zgw_consumers.models import Service + +from openforms.utils.tests.vcr import OFVCRMixin + +from ..client import get_objecttypes_client +from ..models import ObjectsAPIConfig + +OBJECTTYPES_API_BASE_URL = "http://localhost:8001/api/v2/" + +# The value is the API key loaded in the docker service: +OBJECTTYPES_API_KEY = "171be5abaf41e7856b423ad513df1ef8f867ff48" + + +class ObjecttypesClientTest(OFVCRMixin, TestCase): + + VCR_TEST_FILES = Path(__file__).parent / "files" + + def setUp(self) -> None: + super().setUp() + + patcher = patch( + "openforms.registrations.contrib.objects_api.client.ObjectsAPIConfig.get_solo", + return_value=ObjectsAPIConfig( + objecttypes_service=Service( + api_root=OBJECTTYPES_API_BASE_URL, + api_type=APITypes.orc, + oas="https://example.com/", + header_key="Authorization", + header_value=f"Token {OBJECTTYPES_API_KEY}", + auth_type=AuthTypes.api_key, + ) + ), + ) + + self.config_mock = patcher.start() + self.addCleanup(patcher.stop) + + def test_list_objecttypes(self): + with get_objecttypes_client() as client: + data = client.list_objecttypes() + + self.assertEqual(len(data), 2) + + def test_list_objectypes_pagination(self): + with get_objecttypes_client() as client: + data = client.list_objecttypes(page=1, page_size=1) + + self.assertEqual(len(data), 1) + + def test_list_objecttype_version(self): + with get_objecttypes_client() as client: + data = client.list_objecttype_versions( + "8e46e0a5-b1b4-449b-b9e9-fa3cea655f48" + ) + + self.assertEqual(len(data), 3) From 2612f1e908833db556a2ad170e967ef9e856c750 Mon Sep 17 00:00:00 2001 From: Viicos <65306057+Viicos@users.noreply.github.com> Date: Fri, 9 Feb 2024 15:04:01 +0100 Subject: [PATCH 2/6] [#3688] Add Objecttypes API endpoints --- src/openapi.yaml | 100 ++++++++++ src/openforms/registrations/api/urls.py | 4 + .../contrib/objects_api/api/__init__.py | 0 .../contrib/objects_api/api/serializers.py | 22 +++ .../contrib/objects_api/api/urls.py | 18 ++ .../contrib/objects_api/api/views.py | 51 +++++ .../objects_api/tests/test_api_endpoints.py | 176 ++++++++++++++++++ 7 files changed, 371 insertions(+) create mode 100644 src/openforms/registrations/contrib/objects_api/api/__init__.py create mode 100644 src/openforms/registrations/contrib/objects_api/api/serializers.py create mode 100644 src/openforms/registrations/contrib/objects_api/api/urls.py create mode 100644 src/openforms/registrations/contrib/objects_api/api/views.py create mode 100644 src/openforms/registrations/contrib/objects_api/tests/test_api_endpoints.py diff --git a/src/openapi.yaml b/src/openapi.yaml index bb360371c0..a8a66ee692 100644 --- a/src/openapi.yaml +++ b/src/openapi.yaml @@ -3801,6 +3801,71 @@ paths: $ref: '#/components/headers/X-Is-Form-Designer' Content-Language: $ref: '#/components/headers/Content-Language' + /api/v2/registration/plugins/objects-api/object-types: + get: + operationId: registration_plugins_objects_api_object_types_list + description: |- + List the available Objecttypes. + + Note that the response data is essentially proxied from the configured Objecttypes API. + tags: + - registration + security: + - cookieAuth: [] + responses: + '200': + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Objecttype' + 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/registration/plugins/objects-api/object-types/{submission_uuid}/versions: + get: + operationId: registration_plugins_objects_api_object_types_versions_list + description: |- + List the available versions for an Objecttype. + + Note that the response data is essentially proxied from the configured Objecttypes API. + parameters: + - in: path + name: submission_uuid + schema: + type: string + format: uuid + required: true + tags: + - registration + security: + - cookieAuth: [] + responses: + '200': + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/ObjecttypeVersion' + 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 @@ -8410,6 +8475,41 @@ components: - isApplicable - name - url + Objecttype: + type: object + properties: + url: + type: string + format: uri + title: URL reference to this object. This is the unique identification and + location of this object. + uuid: + type: string + format: uuid + title: Unique identifier (UUID4). + name: + type: string + title: Name of the object type. + namePlural: + type: string + title: Plural name of the object type. + required: + - name + - namePlural + - url + - uuid + ObjecttypeVersion: + type: object + properties: + version: + type: integer + title: Integer version of the Objecttype. + status: + type: string + title: Status of the object type version + required: + - status + - version PaginatedFormDefinitionList: type: object properties: diff --git a/src/openforms/registrations/api/urls.py b/src/openforms/registrations/api/urls.py index 51f78eac3e..25753a1618 100644 --- a/src/openforms/registrations/api/urls.py +++ b/src/openforms/registrations/api/urls.py @@ -21,4 +21,8 @@ # TODO: make this dynamic and include it through the registry? urlpatterns += [ path("plugins/camunda/", include("openforms.registrations.contrib.camunda.api")), + path( + "plugins/objects-api/", + include("openforms.registrations.contrib.objects_api.api.urls"), + ), ] diff --git a/src/openforms/registrations/contrib/objects_api/api/__init__.py b/src/openforms/registrations/contrib/objects_api/api/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/openforms/registrations/contrib/objects_api/api/serializers.py b/src/openforms/registrations/contrib/objects_api/api/serializers.py new file mode 100644 index 0000000000..f6c098b28f --- /dev/null +++ b/src/openforms/registrations/contrib/objects_api/api/serializers.py @@ -0,0 +1,22 @@ +from django.utils.translation import gettext_lazy as _ + +from rest_framework import serializers + + +class ObjecttypeSerializer(serializers.Serializer): + # Keys are defined in camel case as this is what we get from the Objecttype API + url = serializers.URLField( + label=_( + "URL reference to this object. This is the unique identification and location of this object." + ), + ) + uuid = serializers.UUIDField(label=_("Unique identifier (UUID4).")) + name = serializers.CharField(label=_("Name of the object type.")) + namePlural = serializers.CharField(label=_("Plural name of the object type.")) + + +class ObjecttypeVersionSerializer(serializers.Serializer): + version = serializers.IntegerField( + label=_("Integer version of the Objecttype."), + ) + status = serializers.CharField(label=_("Status of the object type version")) diff --git a/src/openforms/registrations/contrib/objects_api/api/urls.py b/src/openforms/registrations/contrib/objects_api/api/urls.py new file mode 100644 index 0000000000..63dc2bd71d --- /dev/null +++ b/src/openforms/registrations/contrib/objects_api/api/urls.py @@ -0,0 +1,18 @@ +from django.urls import path + +from .views import ObjecttypesListView, ObjecttypeVersionsListView + +app_name = "objects_api" + +urlpatterns = [ + path( + "object-types", + ObjecttypesListView.as_view(), + name="object-types", + ), + path( + "object-types//versions", + ObjecttypeVersionsListView.as_view(), + name="object-type-versions", + ), +] diff --git a/src/openforms/registrations/contrib/objects_api/api/views.py b/src/openforms/registrations/contrib/objects_api/api/views.py new file mode 100644 index 0000000000..5a111b8c37 --- /dev/null +++ b/src/openforms/registrations/contrib/objects_api/api/views.py @@ -0,0 +1,51 @@ +from typing import Any + +from drf_spectacular.utils import extend_schema, extend_schema_view +from rest_framework import authentication, permissions, views + +from openforms.api.views import ListMixin + +from ..client import get_objecttypes_client +from .serializers import ObjecttypeSerializer, ObjecttypeVersionSerializer + + +@extend_schema_view( + get=extend_schema( + tags=["registration"], + ), +) +class ObjecttypesListView(ListMixin, views.APIView): + """ + List the available Objecttypes. + + Note that the response data is essentially proxied from the configured Objecttypes API. + """ + + authentication_classes = (authentication.SessionAuthentication,) + permission_classes = (permissions.IsAdminUser,) + serializer_class = ObjecttypeSerializer + + def get_objects(self) -> list[dict[str, Any]]: + with get_objecttypes_client() as client: + return client.list_objecttypes() + + +@extend_schema_view( + get=extend_schema( + tags=["registration"], + ), +) +class ObjecttypeVersionsListView(ListMixin, views.APIView): + """ + List the available versions for an Objecttype. + + Note that the response data is essentially proxied from the configured Objecttypes API. + """ + + authentication_classes = (authentication.SessionAuthentication,) + permission_classes = (permissions.IsAdminUser,) + serializer_class = ObjecttypeVersionSerializer + + def get_objects(self) -> list[dict[str, Any]]: + with get_objecttypes_client() as client: + return client.list_objecttype_versions(self.kwargs["submission_uuid"]) diff --git a/src/openforms/registrations/contrib/objects_api/tests/test_api_endpoints.py b/src/openforms/registrations/contrib/objects_api/tests/test_api_endpoints.py new file mode 100644 index 0000000000..88a3f12398 --- /dev/null +++ b/src/openforms/registrations/contrib/objects_api/tests/test_api_endpoints.py @@ -0,0 +1,176 @@ +from unittest.mock import patch + +import requests_mock +from rest_framework import status +from rest_framework.reverse import reverse_lazy +from rest_framework.test import APITestCase +from zgw_consumers.models import Service + +from openforms.accounts.tests.factories import StaffUserFactory, UserFactory + +from ..models import ObjectsAPIConfig + + +@requests_mock.Mocker() +class ObjecttypesAPIEndpointsTests(APITestCase): + + def setUp(self) -> None: + super().setUp() + + self.endpoint = reverse_lazy("api:objects_api:object-types") + + patcher = patch( + "openforms.registrations.contrib.objects_api.client.ObjectsAPIConfig.get_solo", + return_value=ObjectsAPIConfig( + objecttypes_service=Service(api_root="https://objecttypen.nl/api/v2/") + ), + ) + + self.config_mock = patcher.start() + self.addCleanup(patcher.stop) + + def test_auth_required(self, m): + response = self.client.get(self.endpoint) + + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + + def test_staff_user_required(self, m: requests_mock.Mocker): + m.get( + "https://objecttypen.nl/api/v2/objecttypes", + json={"count": 0, "next": None, "previous": None, "results": []}, + ) + + user = UserFactory.create() + staff_user = StaffUserFactory.create() + + with self.subTest(staff=False): + self.client.force_authenticate(user=user) + + response = self.client.get(self.endpoint) + + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + + with self.subTest(staff=True): + self.client.force_authenticate(user=staff_user) + + response = self.client.get(self.endpoint) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + + def test_list_objecttypes(self, m: requests_mock.Mocker): + m.get( + "https://objecttypen.nl/api/v2/objecttypes", + json={ + "count": 1, + "next": None, + "previous": None, + "results": [ + { + "url": "https://objecttypen.nl/api/v2/objecttypes/2c77babf-a967-4057-9969-0200320d23f1", + "uuid": "2c77babf-a967-4057-9969-0200320d23f1", + "name": "Tree", + "namePlural": "Trees", + } + ], + }, + ) + + staff_user = StaffUserFactory.create() + self.client.force_authenticate(user=staff_user) + + response = self.client.get(self.endpoint) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual( + response.json(), + [ + { + "url": "https://objecttypen.nl/api/v2/objecttypes/2c77babf-a967-4057-9969-0200320d23f1", + "uuid": "2c77babf-a967-4057-9969-0200320d23f1", + "name": "Tree", + "namePlural": "Trees", + } + ], + ) + + +@requests_mock.Mocker() +class ObjecttypeVersionsAPIEndpointsTests(APITestCase): + + def setUp(self) -> None: + super().setUp() + self.objecttype_uuid = "2c77babf-a967-4057-9969-0200320d23f1" + self.endpoint = reverse_lazy( + "api:objects_api:object-type-versions", args=[self.objecttype_uuid] + ) + + patcher = patch( + "openforms.registrations.contrib.objects_api.client.ObjectsAPIConfig.get_solo", + return_value=ObjectsAPIConfig( + objecttypes_service=Service(api_root="https://objecttypen.nl/api/v2/") + ), + ) + + self.config_mock = patcher.start() + self.addCleanup(patcher.stop) + + def test_auth_required(self, m): + response = self.client.get(self.endpoint) + + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + + def test_staff_user_required(self, m: requests_mock.Mocker): + m.get( + f"https://objecttypen.nl/api/v2/objecttypes/{self.objecttype_uuid}/versions", + json={"count": 0, "next": None, "previous": None, "results": []}, + ) + + user = UserFactory.create() + staff_user = StaffUserFactory.create() + + with self.subTest(staff=False): + self.client.force_authenticate(user=user) + + response = self.client.get(self.endpoint) + + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + + with self.subTest(staff=True): + self.client.force_authenticate(user=staff_user) + + response = self.client.get(self.endpoint) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + + def test_list_objecttype_versions(self, m: requests_mock.Mocker): + m.get( + f"https://objecttypen.nl/api/v2/objecttypes/{self.objecttype_uuid}/versions", + json={ + "count": 1, + "next": None, + "previous": None, + "results": [ + { + "url": f"https://objecttypen.nl/api/v2/objecttypes/{self.objecttype_uuid}/versions", + "version": 1, + "status": "published", + } + ], + }, + ) + + staff_user = StaffUserFactory.create() + self.client.force_authenticate(user=staff_user) + + response = self.client.get(self.endpoint) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual( + response.json(), + [ + { + "version": 1, + "status": "published", + } + ], + ) From 078738462951d35785df02671cbcf75d03d28583 Mon Sep 17 00:00:00 2001 From: Viicos <65306057+Viicos@users.noreply.github.com> Date: Fri, 9 Feb 2024 16:00:18 +0100 Subject: [PATCH 3/6] [#3688] Add `dataClassification` field --- .../registrations/contrib/objects_api/api/serializers.py | 3 +++ .../contrib/objects_api/tests/test_api_endpoints.py | 2 ++ 2 files changed, 5 insertions(+) diff --git a/src/openforms/registrations/contrib/objects_api/api/serializers.py b/src/openforms/registrations/contrib/objects_api/api/serializers.py index f6c098b28f..b9fa46883f 100644 --- a/src/openforms/registrations/contrib/objects_api/api/serializers.py +++ b/src/openforms/registrations/contrib/objects_api/api/serializers.py @@ -13,6 +13,9 @@ class ObjecttypeSerializer(serializers.Serializer): uuid = serializers.UUIDField(label=_("Unique identifier (UUID4).")) name = serializers.CharField(label=_("Name of the object type.")) namePlural = serializers.CharField(label=_("Plural name of the object type.")) + dataClassification = serializers.CharField( + label=_("Confidential level of the object type.") + ) class ObjecttypeVersionSerializer(serializers.Serializer): diff --git a/src/openforms/registrations/contrib/objects_api/tests/test_api_endpoints.py b/src/openforms/registrations/contrib/objects_api/tests/test_api_endpoints.py index 88a3f12398..0ae7df5ec8 100644 --- a/src/openforms/registrations/contrib/objects_api/tests/test_api_endpoints.py +++ b/src/openforms/registrations/contrib/objects_api/tests/test_api_endpoints.py @@ -70,6 +70,7 @@ def test_list_objecttypes(self, m: requests_mock.Mocker): "uuid": "2c77babf-a967-4057-9969-0200320d23f1", "name": "Tree", "namePlural": "Trees", + "dataClassification": "open", } ], }, @@ -89,6 +90,7 @@ def test_list_objecttypes(self, m: requests_mock.Mocker): "uuid": "2c77babf-a967-4057-9969-0200320d23f1", "name": "Tree", "namePlural": "Trees", + "dataClassification": "open", } ], ) From f1320967c1e03429f84fa5c161b6d7186b91edd5 Mon Sep 17 00:00:00 2001 From: Viicos <65306057+Viicos@users.noreply.github.com> Date: Mon, 12 Feb 2024 16:11:33 +0100 Subject: [PATCH 4/6] [#3688] Refactor config to a helper function --- .../tests/test_objecttypes_client.py | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/openforms/registrations/contrib/objects_api/tests/test_objecttypes_client.py b/src/openforms/registrations/contrib/objects_api/tests/test_objecttypes_client.py index a605e00281..9d4993f671 100644 --- a/src/openforms/registrations/contrib/objects_api/tests/test_objecttypes_client.py +++ b/src/openforms/registrations/contrib/objects_api/tests/test_objecttypes_client.py @@ -11,10 +11,19 @@ from ..client import get_objecttypes_client from ..models import ObjectsAPIConfig -OBJECTTYPES_API_BASE_URL = "http://localhost:8001/api/v2/" -# The value is the API key loaded in the docker service: -OBJECTTYPES_API_KEY = "171be5abaf41e7856b423ad513df1ef8f867ff48" +def get_test_config() -> ObjectsAPIConfig: + """Returns a preconfigured ``ObjectsAPIConfig`` instance matching the docker compose configuration.""" + return ObjectsAPIConfig( + objecttypes_service=Service( + api_root="http://localhost:8001/api/v2/", + api_type=APITypes.orc, + oas="https://example.com/", + header_key="Authorization", + header_value="Token 171be5abaf41e7856b423ad513df1ef8f867ff48", + auth_type=AuthTypes.api_key, + ) + ) class ObjecttypesClientTest(OFVCRMixin, TestCase): @@ -26,16 +35,7 @@ def setUp(self) -> None: patcher = patch( "openforms.registrations.contrib.objects_api.client.ObjectsAPIConfig.get_solo", - return_value=ObjectsAPIConfig( - objecttypes_service=Service( - api_root=OBJECTTYPES_API_BASE_URL, - api_type=APITypes.orc, - oas="https://example.com/", - header_key="Authorization", - header_value=f"Token {OBJECTTYPES_API_KEY}", - auth_type=AuthTypes.api_key, - ) - ), + return_value=get_test_config(), ) self.config_mock = patcher.start() From 30d7062825a5d285519e93cb7ae04c4bb3633573 Mon Sep 17 00:00:00 2001 From: Viicos <65306057+Viicos@users.noreply.github.com> Date: Mon, 12 Feb 2024 16:24:44 +0100 Subject: [PATCH 5/6] [#3688] Use VCR for API endpoint tests --- ...objecttype_verions_unknown_objecttype.yaml | 36 ++++++ ...tsTests.test_list_objecttype_versions.yaml | 37 ++++++ ...dpointsTests.test_staff_user_required.yaml | 37 ++++++ ...IEndpointsTests.test_list_objecttypes.yaml | 36 ++++++ ...dpointsTests.test_staff_user_required.yaml | 36 ++++++ .../objects_api/tests/test_api_endpoints.py | 118 +++++++----------- 6 files changed, 225 insertions(+), 75 deletions(-) create mode 100644 src/openforms/registrations/contrib/objects_api/tests/files/vcr_cassettes/ObjecttypeVersionsAPIEndpointsTests/ObjecttypeVersionsAPIEndpointsTests.test_list_objecttype_verions_unknown_objecttype.yaml create mode 100644 src/openforms/registrations/contrib/objects_api/tests/files/vcr_cassettes/ObjecttypeVersionsAPIEndpointsTests/ObjecttypeVersionsAPIEndpointsTests.test_list_objecttype_versions.yaml create mode 100644 src/openforms/registrations/contrib/objects_api/tests/files/vcr_cassettes/ObjecttypeVersionsAPIEndpointsTests/ObjecttypeVersionsAPIEndpointsTests.test_staff_user_required.yaml create mode 100644 src/openforms/registrations/contrib/objects_api/tests/files/vcr_cassettes/ObjecttypesAPIEndpointsTests/ObjecttypesAPIEndpointsTests.test_list_objecttypes.yaml create mode 100644 src/openforms/registrations/contrib/objects_api/tests/files/vcr_cassettes/ObjecttypesAPIEndpointsTests/ObjecttypesAPIEndpointsTests.test_staff_user_required.yaml diff --git a/src/openforms/registrations/contrib/objects_api/tests/files/vcr_cassettes/ObjecttypeVersionsAPIEndpointsTests/ObjecttypeVersionsAPIEndpointsTests.test_list_objecttype_verions_unknown_objecttype.yaml b/src/openforms/registrations/contrib/objects_api/tests/files/vcr_cassettes/ObjecttypeVersionsAPIEndpointsTests/ObjecttypeVersionsAPIEndpointsTests.test_list_objecttype_verions_unknown_objecttype.yaml new file mode 100644 index 0000000000..bbc4f89c37 --- /dev/null +++ b/src/openforms/registrations/contrib/objects_api/tests/files/vcr_cassettes/ObjecttypeVersionsAPIEndpointsTests/ObjecttypeVersionsAPIEndpointsTests.test_list_objecttype_verions_unknown_objecttype.yaml @@ -0,0 +1,36 @@ +interactions: +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate, br + Authorization: + - Token 171be5abaf41e7856b423ad513df1ef8f867ff48 + Connection: + - keep-alive + User-Agent: + - python-requests/2.31.0 + method: GET + uri: http://localhost:8001/api/v2/objecttypes/39da819c-ac6c-4037-ae2b-6bfc39f6564b/versions + response: + body: + string: '{"count":0,"next":null,"previous":null,"results":[]}' + headers: + Allow: + - GET, POST, HEAD, OPTIONS + Content-Length: + - '52' + Content-Type: + - application/json + Referrer-Policy: + - same-origin + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + status: + code: 200 + message: OK +version: 1 diff --git a/src/openforms/registrations/contrib/objects_api/tests/files/vcr_cassettes/ObjecttypeVersionsAPIEndpointsTests/ObjecttypeVersionsAPIEndpointsTests.test_list_objecttype_versions.yaml b/src/openforms/registrations/contrib/objects_api/tests/files/vcr_cassettes/ObjecttypeVersionsAPIEndpointsTests/ObjecttypeVersionsAPIEndpointsTests.test_list_objecttype_versions.yaml new file mode 100644 index 0000000000..f89fab844b --- /dev/null +++ b/src/openforms/registrations/contrib/objects_api/tests/files/vcr_cassettes/ObjecttypeVersionsAPIEndpointsTests/ObjecttypeVersionsAPIEndpointsTests.test_list_objecttype_versions.yaml @@ -0,0 +1,37 @@ +interactions: +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate, br + Authorization: + - Token 171be5abaf41e7856b423ad513df1ef8f867ff48 + Connection: + - keep-alive + User-Agent: + - python-requests/2.31.0 + method: GET + uri: http://localhost:8001/api/v2/objecttypes/3edfdaf7-f469-470b-a391-bb7ea015bd6f/versions + response: + body: + string: '{"count":1,"next":null,"previous":null,"results":[{"url":"http://localhost:8001/api/v2/objecttypes/3edfdaf7-f469-470b-a391-bb7ea015bd6f/versions/1","version":1,"objectType":"http://localhost:8001/api/v2/objecttypes/3edfdaf7-f469-470b-a391-bb7ea015bd6f","status":"published","jsonSchema":{"$id":"https://example.com/tree.schema.json","type":"object","title":"Tree","$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"height":{"type":"integer","description":"The + height of the tree."}}},"createdAt":"2024-02-08","modifiedAt":"2024-02-08","publishedAt":"2024-02-08"}]}' + headers: + Allow: + - GET, POST, HEAD, OPTIONS + Content-Length: + - '585' + Content-Type: + - application/json + Referrer-Policy: + - same-origin + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + status: + code: 200 + message: OK +version: 1 diff --git a/src/openforms/registrations/contrib/objects_api/tests/files/vcr_cassettes/ObjecttypeVersionsAPIEndpointsTests/ObjecttypeVersionsAPIEndpointsTests.test_staff_user_required.yaml b/src/openforms/registrations/contrib/objects_api/tests/files/vcr_cassettes/ObjecttypeVersionsAPIEndpointsTests/ObjecttypeVersionsAPIEndpointsTests.test_staff_user_required.yaml new file mode 100644 index 0000000000..f89fab844b --- /dev/null +++ b/src/openforms/registrations/contrib/objects_api/tests/files/vcr_cassettes/ObjecttypeVersionsAPIEndpointsTests/ObjecttypeVersionsAPIEndpointsTests.test_staff_user_required.yaml @@ -0,0 +1,37 @@ +interactions: +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate, br + Authorization: + - Token 171be5abaf41e7856b423ad513df1ef8f867ff48 + Connection: + - keep-alive + User-Agent: + - python-requests/2.31.0 + method: GET + uri: http://localhost:8001/api/v2/objecttypes/3edfdaf7-f469-470b-a391-bb7ea015bd6f/versions + response: + body: + string: '{"count":1,"next":null,"previous":null,"results":[{"url":"http://localhost:8001/api/v2/objecttypes/3edfdaf7-f469-470b-a391-bb7ea015bd6f/versions/1","version":1,"objectType":"http://localhost:8001/api/v2/objecttypes/3edfdaf7-f469-470b-a391-bb7ea015bd6f","status":"published","jsonSchema":{"$id":"https://example.com/tree.schema.json","type":"object","title":"Tree","$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"height":{"type":"integer","description":"The + height of the tree."}}},"createdAt":"2024-02-08","modifiedAt":"2024-02-08","publishedAt":"2024-02-08"}]}' + headers: + Allow: + - GET, POST, HEAD, OPTIONS + Content-Length: + - '585' + Content-Type: + - application/json + Referrer-Policy: + - same-origin + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + status: + code: 200 + message: OK +version: 1 diff --git a/src/openforms/registrations/contrib/objects_api/tests/files/vcr_cassettes/ObjecttypesAPIEndpointsTests/ObjecttypesAPIEndpointsTests.test_list_objecttypes.yaml b/src/openforms/registrations/contrib/objects_api/tests/files/vcr_cassettes/ObjecttypesAPIEndpointsTests/ObjecttypesAPIEndpointsTests.test_list_objecttypes.yaml new file mode 100644 index 0000000000..cdabace3c7 --- /dev/null +++ b/src/openforms/registrations/contrib/objects_api/tests/files/vcr_cassettes/ObjecttypesAPIEndpointsTests/ObjecttypesAPIEndpointsTests.test_list_objecttypes.yaml @@ -0,0 +1,36 @@ +interactions: +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate, br + Authorization: + - Token 171be5abaf41e7856b423ad513df1ef8f867ff48 + Connection: + - keep-alive + User-Agent: + - python-requests/2.31.0 + method: GET + uri: http://localhost:8001/api/v2/objecttypes + response: + body: + string: '{"count":2,"next":null,"previous":null,"results":[{"url":"http://localhost:8001/api/v2/objecttypes/3edfdaf7-f469-470b-a391-bb7ea015bd6f","uuid":"3edfdaf7-f469-470b-a391-bb7ea015bd6f","name":"Tree","namePlural":"Trees","description":"","dataClassification":"confidential","maintainerOrganization":"","maintainerDepartment":"","contactPerson":"","contactEmail":"","source":"","updateFrequency":"unknown","providerOrganization":"","documentationUrl":"","labels":{},"createdAt":"2024-02-08","modifiedAt":"2024-02-08","allowGeometry":true,"versions":["http://localhost:8001/api/v2/objecttypes/3edfdaf7-f469-470b-a391-bb7ea015bd6f/versions/1"]},{"url":"http://localhost:8001/api/v2/objecttypes/8e46e0a5-b1b4-449b-b9e9-fa3cea655f48","uuid":"8e46e0a5-b1b4-449b-b9e9-fa3cea655f48","name":"Person","namePlural":"Persons","description":"","dataClassification":"open","maintainerOrganization":"","maintainerDepartment":"","contactPerson":"","contactEmail":"","source":"","updateFrequency":"unknown","providerOrganization":"","documentationUrl":"","labels":{},"createdAt":"2023-10-24","modifiedAt":"2024-02-08","allowGeometry":true,"versions":["http://localhost:8001/api/v2/objecttypes/8e46e0a5-b1b4-449b-b9e9-fa3cea655f48/versions/1","http://localhost:8001/api/v2/objecttypes/8e46e0a5-b1b4-449b-b9e9-fa3cea655f48/versions/2","http://localhost:8001/api/v2/objecttypes/8e46e0a5-b1b4-449b-b9e9-fa3cea655f48/versions/3"]}]}' + headers: + Allow: + - GET, POST, HEAD, OPTIONS + Content-Length: + - '1407' + Content-Type: + - application/json + Referrer-Policy: + - same-origin + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + status: + code: 200 + message: OK +version: 1 diff --git a/src/openforms/registrations/contrib/objects_api/tests/files/vcr_cassettes/ObjecttypesAPIEndpointsTests/ObjecttypesAPIEndpointsTests.test_staff_user_required.yaml b/src/openforms/registrations/contrib/objects_api/tests/files/vcr_cassettes/ObjecttypesAPIEndpointsTests/ObjecttypesAPIEndpointsTests.test_staff_user_required.yaml new file mode 100644 index 0000000000..cdabace3c7 --- /dev/null +++ b/src/openforms/registrations/contrib/objects_api/tests/files/vcr_cassettes/ObjecttypesAPIEndpointsTests/ObjecttypesAPIEndpointsTests.test_staff_user_required.yaml @@ -0,0 +1,36 @@ +interactions: +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate, br + Authorization: + - Token 171be5abaf41e7856b423ad513df1ef8f867ff48 + Connection: + - keep-alive + User-Agent: + - python-requests/2.31.0 + method: GET + uri: http://localhost:8001/api/v2/objecttypes + response: + body: + string: '{"count":2,"next":null,"previous":null,"results":[{"url":"http://localhost:8001/api/v2/objecttypes/3edfdaf7-f469-470b-a391-bb7ea015bd6f","uuid":"3edfdaf7-f469-470b-a391-bb7ea015bd6f","name":"Tree","namePlural":"Trees","description":"","dataClassification":"confidential","maintainerOrganization":"","maintainerDepartment":"","contactPerson":"","contactEmail":"","source":"","updateFrequency":"unknown","providerOrganization":"","documentationUrl":"","labels":{},"createdAt":"2024-02-08","modifiedAt":"2024-02-08","allowGeometry":true,"versions":["http://localhost:8001/api/v2/objecttypes/3edfdaf7-f469-470b-a391-bb7ea015bd6f/versions/1"]},{"url":"http://localhost:8001/api/v2/objecttypes/8e46e0a5-b1b4-449b-b9e9-fa3cea655f48","uuid":"8e46e0a5-b1b4-449b-b9e9-fa3cea655f48","name":"Person","namePlural":"Persons","description":"","dataClassification":"open","maintainerOrganization":"","maintainerDepartment":"","contactPerson":"","contactEmail":"","source":"","updateFrequency":"unknown","providerOrganization":"","documentationUrl":"","labels":{},"createdAt":"2023-10-24","modifiedAt":"2024-02-08","allowGeometry":true,"versions":["http://localhost:8001/api/v2/objecttypes/8e46e0a5-b1b4-449b-b9e9-fa3cea655f48/versions/1","http://localhost:8001/api/v2/objecttypes/8e46e0a5-b1b4-449b-b9e9-fa3cea655f48/versions/2","http://localhost:8001/api/v2/objecttypes/8e46e0a5-b1b4-449b-b9e9-fa3cea655f48/versions/3"]}]}' + headers: + Allow: + - GET, POST, HEAD, OPTIONS + Content-Length: + - '1407' + Content-Type: + - application/json + Referrer-Policy: + - same-origin + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + status: + code: 200 + message: OK +version: 1 diff --git a/src/openforms/registrations/contrib/objects_api/tests/test_api_endpoints.py b/src/openforms/registrations/contrib/objects_api/tests/test_api_endpoints.py index 0ae7df5ec8..5f47403d50 100644 --- a/src/openforms/registrations/contrib/objects_api/tests/test_api_endpoints.py +++ b/src/openforms/registrations/contrib/objects_api/tests/test_api_endpoints.py @@ -1,18 +1,19 @@ +from pathlib import Path from unittest.mock import patch -import requests_mock from rest_framework import status from rest_framework.reverse import reverse_lazy from rest_framework.test import APITestCase -from zgw_consumers.models import Service from openforms.accounts.tests.factories import StaffUserFactory, UserFactory +from openforms.utils.tests.vcr import OFVCRMixin -from ..models import ObjectsAPIConfig +from .test_objecttypes_client import get_test_config -@requests_mock.Mocker() -class ObjecttypesAPIEndpointsTests(APITestCase): +class ObjecttypesAPIEndpointsTests(OFVCRMixin, APITestCase): + + VCR_TEST_FILES = Path(__file__).parent / "files" def setUp(self) -> None: super().setUp() @@ -21,25 +22,18 @@ def setUp(self) -> None: patcher = patch( "openforms.registrations.contrib.objects_api.client.ObjectsAPIConfig.get_solo", - return_value=ObjectsAPIConfig( - objecttypes_service=Service(api_root="https://objecttypen.nl/api/v2/") - ), + return_value=get_test_config(), ) self.config_mock = patcher.start() self.addCleanup(patcher.stop) - def test_auth_required(self, m): + def test_auth_required(self): response = self.client.get(self.endpoint) self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) - def test_staff_user_required(self, m: requests_mock.Mocker): - m.get( - "https://objecttypen.nl/api/v2/objecttypes", - json={"count": 0, "next": None, "previous": None, "results": []}, - ) - + def test_staff_user_required(self): user = UserFactory.create() staff_user = StaffUserFactory.create() @@ -57,25 +51,7 @@ def test_staff_user_required(self, m: requests_mock.Mocker): self.assertEqual(response.status_code, status.HTTP_200_OK) - def test_list_objecttypes(self, m: requests_mock.Mocker): - m.get( - "https://objecttypen.nl/api/v2/objecttypes", - json={ - "count": 1, - "next": None, - "previous": None, - "results": [ - { - "url": "https://objecttypen.nl/api/v2/objecttypes/2c77babf-a967-4057-9969-0200320d23f1", - "uuid": "2c77babf-a967-4057-9969-0200320d23f1", - "name": "Tree", - "namePlural": "Trees", - "dataClassification": "open", - } - ], - }, - ) - + def test_list_objecttypes(self): staff_user = StaffUserFactory.create() self.client.force_authenticate(user=staff_user) @@ -86,47 +62,48 @@ def test_list_objecttypes(self, m: requests_mock.Mocker): response.json(), [ { - "url": "https://objecttypen.nl/api/v2/objecttypes/2c77babf-a967-4057-9969-0200320d23f1", - "uuid": "2c77babf-a967-4057-9969-0200320d23f1", + "dataClassification": "confidential", "name": "Tree", "namePlural": "Trees", + "url": "http://localhost:8001/api/v2/objecttypes/3edfdaf7-f469-470b-a391-bb7ea015bd6f", + "uuid": "3edfdaf7-f469-470b-a391-bb7ea015bd6f", + }, + { "dataClassification": "open", - } + "name": "Person", + "namePlural": "Persons", + "url": "http://localhost:8001/api/v2/objecttypes/8e46e0a5-b1b4-449b-b9e9-fa3cea655f48", + "uuid": "8e46e0a5-b1b4-449b-b9e9-fa3cea655f48", + }, ], ) -@requests_mock.Mocker() -class ObjecttypeVersionsAPIEndpointsTests(APITestCase): +class ObjecttypeVersionsAPIEndpointsTests(OFVCRMixin, APITestCase): + + VCR_TEST_FILES = Path(__file__).parent / "files" def setUp(self) -> None: super().setUp() - self.objecttype_uuid = "2c77babf-a967-4057-9969-0200320d23f1" + self.objecttype_uuid = "3edfdaf7-f469-470b-a391-bb7ea015bd6f" self.endpoint = reverse_lazy( "api:objects_api:object-type-versions", args=[self.objecttype_uuid] ) patcher = patch( "openforms.registrations.contrib.objects_api.client.ObjectsAPIConfig.get_solo", - return_value=ObjectsAPIConfig( - objecttypes_service=Service(api_root="https://objecttypen.nl/api/v2/") - ), + return_value=get_test_config(), ) self.config_mock = patcher.start() self.addCleanup(patcher.stop) - def test_auth_required(self, m): + def test_auth_required(self): response = self.client.get(self.endpoint) self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) - def test_staff_user_required(self, m: requests_mock.Mocker): - m.get( - f"https://objecttypen.nl/api/v2/objecttypes/{self.objecttype_uuid}/versions", - json={"count": 0, "next": None, "previous": None, "results": []}, - ) - + def test_staff_user_required(self): user = UserFactory.create() staff_user = StaffUserFactory.create() @@ -144,35 +121,26 @@ def test_staff_user_required(self, m: requests_mock.Mocker): self.assertEqual(response.status_code, status.HTTP_200_OK) - def test_list_objecttype_versions(self, m: requests_mock.Mocker): - m.get( - f"https://objecttypen.nl/api/v2/objecttypes/{self.objecttype_uuid}/versions", - json={ - "count": 1, - "next": None, - "previous": None, - "results": [ - { - "url": f"https://objecttypen.nl/api/v2/objecttypes/{self.objecttype_uuid}/versions", - "version": 1, - "status": "published", - } - ], - }, - ) - + def test_list_objecttype_versions(self): staff_user = StaffUserFactory.create() self.client.force_authenticate(user=staff_user) response = self.client.get(self.endpoint) self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual( - response.json(), - [ - { - "version": 1, - "status": "published", - } - ], + self.assertEqual(response.json(), [{"status": "published", "version": 1}]) + + def test_list_objecttype_verions_unknown_objecttype(self): + staff_user = StaffUserFactory.create() + self.client.force_authenticate(user=staff_user) + + # This UUID doesn't exist: + response = self.client.get( + reverse_lazy( + "api:objects_api:object-type-versions", + args=["39da819c-ac6c-4037-ae2b-6bfc39f6564b"], + ) ) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.json(), []) From c535ba2481783de972c449c4c484b7a9a445d7a2 Mon Sep 17 00:00:00 2001 From: Viicos <65306057+Viicos@users.noreply.github.com> Date: Mon, 12 Feb 2024 17:22:18 +0100 Subject: [PATCH 6/6] [#3688] Generate OAS --- src/openapi.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/openapi.yaml b/src/openapi.yaml index a8a66ee692..3d1e0bd245 100644 --- a/src/openapi.yaml +++ b/src/openapi.yaml @@ -8493,7 +8493,11 @@ components: namePlural: type: string title: Plural name of the object type. + dataClassification: + type: string + title: Confidential level of the object type. required: + - dataClassification - name - namePlural - url