Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#3688] Phase 1 - Implement Objecttypes API client #3847

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 104 additions & 0 deletions src/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -8410,6 +8475,45 @@ 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.
dataClassification:
type: string
title: Confidential level of the object type.
required:
- dataClassification
- 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:
Expand Down
3 changes: 2 additions & 1 deletion src/openforms/contrib/objects_api/clients/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from .objects import ObjectsClient, ObjecttypesClient
from .objects import ObjectsClient
from .objecttypes import ObjecttypesClient

__all__ = ["ObjectsClient", "ObjecttypesClient"]
4 changes: 0 additions & 4 deletions src/openforms/contrib/objects_api/clients/objects.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,3 @@ def create_object(self, object_data: dict) -> dict:
response.raise_for_status()

return response.json()


class ObjecttypesClient(NLXClient):
pass
54 changes: 54 additions & 0 deletions src/openforms/contrib/objects_api/clients/objecttypes.py
Original file line number Diff line number Diff line change
@@ -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
)
4 changes: 4 additions & 0 deletions src/openforms/registrations/api/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
),
]
Empty file.
25 changes: 25 additions & 0 deletions src/openforms/registrations/contrib/objects_api/api/serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
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."))
dataClassification = serializers.CharField(
label=_("Confidential level 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"))
18 changes: 18 additions & 0 deletions src/openforms/registrations/contrib/objects_api/api/urls.py
Original file line number Diff line number Diff line change
@@ -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/<uuid:submission_uuid>/versions",
ObjecttypeVersionsListView.as_view(),
name="object-type-versions",
),
]
51 changes: 51 additions & 0 deletions src/openforms/registrations/contrib/objects_api/api/views.py
Original file line number Diff line number Diff line change
@@ -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"])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm interested to see how this handles 404s for bogus UUIDs

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Objecttypes API raises a ValidationError but for some reason a 500 is returned. Should we validate on our side that it is a correct UUID?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no, a 500 is never an intended/deliberate outcome. Create an issue in the objecttypes API for this case, and I think a 404 is more appropriate.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Loading
Loading