Skip to content

Commit

Permalink
[VNG-Realisatie#210] add headers for geo fields wherever applicable
Browse files Browse the repository at this point in the history
  • Loading branch information
Sonny Bakker committed Sep 19, 2022
1 parent 22e9470 commit 2925f7a
Show file tree
Hide file tree
Showing 2 changed files with 133 additions and 45 deletions.
23 changes: 22 additions & 1 deletion vng_api_common/inspectors/geojson.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,32 @@
from drf_spectacular.extensions import OpenApiSerializerFieldExtension
from drf_spectacular.plumbing import ResolvedComponent
from rest_framework import serializers
from rest_framework_gis.fields import GeometryField

from vng_api_common.oas import TYPE_ARRAY, TYPE_NUMBER, TYPE_OBJECT, TYPE_STRING


# TODO: Add response/request headers
def has_geo_fields(serializer) -> bool:
"""
Check if any of the serializer fields are a GeometryField.
If the serializer has nested serializers, a depth-first search is done
to check if the nested serializers has `GeometryField`\ s.
"""
for field in serializer.fields.values():
if isinstance(field, serializers.Serializer):
has_nested_geo_fields = has_geo_fields(field)
if has_nested_geo_fields:
return True

elif isinstance(field, (serializers.ListSerializer, serializers.ListField)):
field = field.child

if isinstance(field, GeometryField):
return True

return False


class GeometryFieldExtension(OpenApiSerializerFieldExtension):
target_class = GeometryField
match_subclasses = True
Expand Down
155 changes: 111 additions & 44 deletions vng_api_common/inspectors/view.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@
from drf_spectacular.utils import OpenApiParameter, OpenApiResponse
from rest_framework import exceptions, status, viewsets

from vng_api_common.inspectors.geojson import has_geo_fields

from ..constants import HEADER_AUDIT, HEADER_LOGRECORD_ID, VERSION_HEADER
from ..exceptions import Conflict, Gone, PreconditionFailed
from ..geo import GeoMixin
from ..geo import DEFAULT_CRS, HEADER_ACCEPT, HEADER_CONTENT, GeoMixin
from ..permissions import BaseAuthRequired, get_required_scopes
from ..serializers import FoutSerializer, ValidatieFoutSerializer
from .cache import CACHE_REQUEST_HEADERS, get_cache_headers, has_cache_header
Expand Down Expand Up @@ -187,61 +189,126 @@ def get_auth(self):
auths = [{list(spectacular_settings.SECURITY[0])[0]: scopes}]
return auths

def get_override_parameters(self):
"""
Note that request and response headers are mixed here. These are filtered
when spectacular retrieves the available parameters for a request/response body.
"""
custom_headers = []

if has_cache_header(self.view):
custom_headers += CACHE_REQUEST_HEADERS
custom_headers += get_cache_headers(self.view)

if _view_supports_audittrail(self.view):
custom_headers += AUDIT_REQUEST_HEADERS
def get_request_parameters(self):
serializer = self.get_request_serializer()
headers = []

if self.method in (
"POST",
"PUT",
"PATCH",
):
custom_headers += [
headers.extend(
[
OpenApiParameter(
name="Content-Type",
type=OpenApiTypes.STR,
location=OpenApiParameter.HEADER,
required=True,
enum=self.map_renderers("media_type"),
description=_("Content type of the request body."),
)
]
)

if has_cache_header(self.view):
headers.extend(CACHE_REQUEST_HEADERS)

if _view_supports_audittrail(self.view):
headers.extend(AUDIT_REQUEST_HEADERS)

if has_geo_fields(serializer):
headers.extend(
[
OpenApiParameter(
name=HEADER_ACCEPT,
type=OpenApiTypes.STR,
location=OpenApiParameter.HEADER,
required=True,
description="Het gewenste 'Coordinate Reference System' (CRS) van de "
"geometrie in het antwoord (response body). Volgens de "
"GeoJSON spec is WGS84 de default (EPSG:4326 is "
"hetzelfde als WGS84).",
enum=[DEFAULT_CRS],
),
OpenApiParameter(
name=HEADER_CONTENT,
type=OpenApiTypes.STR,
location=OpenApiParameter.HEADER,
description="Het 'Coordinate Reference System' (CRS) van de "
"geometrie in de vraag (request body). Volgens de "
"GeoJSON spec is WGS84 de default (EPSG:4326 is "
"hetzelfde als WGS84).",
enum=[DEFAULT_CRS],
required=True,
),
]
)

return headers

def get_response_parameters(self):
serializer = self.get_request_serializer()
headers = []

if has_cache_header(self.view):
headers.extend(get_cache_headers(self.view))

if has_geo_fields(serializer):
headers.extend(
[
OpenApiParameter(
name=HEADER_CONTENT,
type=OpenApiTypes.STR,
location=OpenApiParameter.HEADER,
description="Het 'Coordinate Reference System' (CRS) van de "
"geometrie in de vraag (request body). Volgens de "
"GeoJSON spec is WGS84 de default (EPSG:4326 is "
"hetzelfde als WGS84).",
enum=[DEFAULT_CRS],
response=[
status.HTTP_200_OK,
status.HTTP_201_CREATED,
status.HTTP_204_NO_CONTENT,
],
),
]
)

error_responses = self.get_error_codes()

headers.extend(
[
OpenApiParameter(
name="Content-Type",
name="Location",
type=OpenApiTypes.URI,
location=OpenApiParameter.HEADER,
description=location_header,
response=[status.HTTP_201_CREATED],
),
OpenApiParameter(
name=VERSION_HEADER,
type=OpenApiTypes.STR,
location=OpenApiParameter.HEADER,
required=True,
enum=self.map_renderers("media_type"),
description=_("Content type of the request body."),
)
description=version_header,
response=[
status.HTTP_200_OK,
status.HTTP_201_CREATED,
status.HTTP_204_NO_CONTENT,
*error_responses,
],
),
]
)

error_responses = self.get_error_codes()
return headers

custom_headers += [
OpenApiParameter(
name="Location",
type=OpenApiTypes.URI,
location=OpenApiParameter.HEADER,
description=location_header,
response=[status.HTTP_201_CREATED],
),
OpenApiParameter(
name=VERSION_HEADER,
type=OpenApiTypes.STR,
location=OpenApiParameter.HEADER,
description=version_header,
response=[
status.HTTP_200_OK,
status.HTTP_201_CREATED,
status.HTTP_204_NO_CONTENT,
*error_responses,
],
),
]

return custom_headers
def get_override_parameters(self):
"""
Note that request and response headers are mixed here. These are filtered
when spectacular retrieves the available parameters for a request/response body.
"""
return self.get_request_parameters() + self.get_response_parameters()

def _get_response_bodies(self, direction="response"):
response_bodies = super()._get_response_bodies(direction=direction)
Expand Down

0 comments on commit 2925f7a

Please sign in to comment.