Skip to content

Commit

Permalink
Fixes tfranzel#283 -- implement response header parameters
Browse files Browse the repository at this point in the history
Response headers are sourced from the @extend_schema(parameters=[...])
decorator.

Response headers can apply for every response (global, default) or
only be applicable to a particular response status code (e.g. Location
header on HTTP 201 Created responses). Specifying the response as
either a boolean or a dict mapping status code to boolean serves
both situations.
  • Loading branch information
sergei-maertens committed Feb 15, 2021
1 parent 2ce88f0 commit 7497d44
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 1 deletion.
50 changes: 49 additions & 1 deletion drf_spectacular/openapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,11 @@ def _process_override_parameters(self):
result = {}
for parameter in self.get_override_parameters():
if isinstance(parameter, OpenApiParameter):
if parameter.response:
# ignore response headers, they are added in
# :meth:`_get_response_headers_for_code`
continue

if is_basic_type(parameter.type):
schema = build_basic_type(parameter.type)
elif is_serializer(parameter.type):
Expand Down Expand Up @@ -1003,7 +1008,9 @@ def _get_response_for_code(self, serializer, status_code, media_types=None):
if not media_types:
media_types = self.map_renderers('media_type')

return {
headers = self._get_response_headers_for_code(status_code)

response = {
'content': {
media_type: build_media_type_object(
schema,
Expand All @@ -1016,6 +1023,47 @@ def _get_response_for_code(self, serializer, status_code, media_types=None):
# https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#responseObject
'description': ''
}
if headers:
response["headers"] = headers
return response

def _get_response_headers_for_code(self, status_code) -> dict:
"""
Extract the response header parameters.
TODO: on HTTP 201 with generics.CreateView, DRF tries to set the Location header
param automatically. Include that here?
"""
parameters = []

# select the relevant response header parameters
for parameter in self.get_override_parameters():
if not isinstance(parameter, OpenApiParameter):
continue
if not parameter.response:
continue

# check if the parameter is always present, or only for this status code
if parameter.response is True or parameter.response.get(status_code, False):
parameters.append(parameter)

result = {}
for parameter in parameters:
if is_basic_type(parameter.type):
schema = build_basic_type(parameter.type)
elif is_serializer(parameter.type):
warn('complex types for response header parameters are not supported. '
f'got {parameter.type}. skipping.')
continue
else:
schema = parameter.type

result[parameter.name] = {
'schema': schema,
'description': parameter.description or '',
}

return result

def _get_serializer_name(self, serializer, direction):
serializer_extension = OpenApiSerializerExtension.get_match(serializer)
Expand Down
5 changes: 5 additions & 0 deletions drf_spectacular/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ def __init__(
default=None,
examples: Optional[List[OpenApiExample]] = None,
exclude=False,
response: Union[bool, Dict[Union[int, str], bool]] = False,
):
self.name = name
self.type = type
Expand All @@ -129,6 +130,10 @@ def __init__(
self.examples = examples or []
self.exclude = exclude

if isinstance(response, dict):
response = {str(code): include for code, include in response.items()}
self.response = response


def extend_schema(
operation_id=None,
Expand Down

0 comments on commit 7497d44

Please sign in to comment.