Skip to content

Commit

Permalink
add error response examples to the schema only when the corresponding…
Browse files Browse the repository at this point in the history
… status code is part of the allowed ones

fixes #73
  • Loading branch information
ghazi-git committed Jun 18, 2024
1 parent 8f4b383 commit dcb292d
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 2 deletions.
1 change: 1 addition & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
### Fixed
- enforce support of only drf-spectacular 0.27 and newer in pyproject.toml
- ensure examples from `@extend_schema_serializer` are not ignored when adding error response examples
- show default error response examples only when the corresponding status code is allowed

## [0.13.0] - 2024-02-28
### Changed
Expand Down
16 changes: 14 additions & 2 deletions drf_standardized_errors/openapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from drf_spectacular.drainage import warn
from drf_spectacular.extensions import OpenApiFilterExtension
from drf_spectacular.openapi import AutoSchema as BaseAutoSchema
from drf_spectacular.utils import PolymorphicProxySerializer
from drf_spectacular.utils import OpenApiExample, PolymorphicProxySerializer
from inflection import camelize
from rest_framework import serializers
from rest_framework.negotiation import DefaultContentNegotiation
Expand Down Expand Up @@ -338,11 +338,23 @@ def _get_extra_validation_errors(self) -> Dict[str, Set[str]]:
def _get_examples(
self, serializer, direction, media_type, status_code=None, extras=None
):
all_examples = (extras or []) + get_error_examples()
if direction == "response":
all_examples = (extras or []) + self._get_error_response_examples()
else:
all_examples = extras
return super()._get_examples(
serializer,
direction,
media_type,
status_code=status_code,
extras=all_examples,
)

def _get_error_response_examples(self) -> List[OpenApiExample]:
status_codes = set(self._get_allowed_error_status_codes())
examples = get_error_examples()
return [
example
for example in examples
if status_codes.intersection(example.status_codes)
]
74 changes: 74 additions & 0 deletions tests/test_openapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -656,3 +656,77 @@ def test_examples_from_extend_schema_serializer_are_showing_up(api_client):
assert (
examples["ExtendSchemaSerializerExample"]["value"]["field"] == "specific_value"
)


class CustomError403Serializer(serializers.Serializer):
code = serializers.ChoiceField(choices=[("perm_denied", "perm_denied")])
detail = serializers.CharField()
attr = serializers.CharField(allow_null=True)


class CustomErrorResponse403Serializer(serializers.Serializer):
type = serializers.ChoiceField(choices=ClientErrorEnum.choices)
errors = CustomError403Serializer(many=True)


class ExpSerializer(serializers.Serializer):
field = serializers.CharField()


class ExamplesView(GenericAPIView):
authentication_classes = [BasicAuthentication]
permission_classes = [IsAuthenticated, IsAdminUser]
serializer_class = ExpSerializer

@extend_schema(
responses={
403: OpenApiResponse(
response=CustomErrorResponse403Serializer,
description="Registration is disabled",
)
},
examples=[
OpenApiExample(
"Example",
summary="short summary",
description="longer description",
value={
"type": "client_error",
"errors": [
{
"code": "perm_denied",
"detail": "Registration is disabled.",
"attr": None,
}
],
},
status_codes=[403],
),
],
)
def get(self, request, *args, **kwargs):
serializer = self.get_serializer(instance={"field": "value1"})
return Response(serializer.data)


def test_default_examples_are_showing_up_only_when_status_code_is_allowed(
api_client, settings
):
settings.DRF_STANDARDIZED_ERRORS = {"ALLOWED_ERROR_STATUS_CODES": ["403"]}

route = "perm-denied/"
view = ExamplesView.as_view()
schema = generate_view_schema(route, view)
responses = get_responses(schema, route)
examples = responses["403"]["content"]["application/json"]["examples"]
assert "Example" in examples
assert "PermissionDenied" in examples

settings.DRF_STANDARDIZED_ERRORS = {"ALLOWED_ERROR_STATUS_CODES": []}
route = "perm-denied/"
view = ExamplesView.as_view()
schema = generate_view_schema(route, view)
responses = get_responses(schema, route)
examples = responses["403"]["content"]["application/json"]["examples"]
assert "Example" in examples
assert "PermissionDenied" not in examples

0 comments on commit dcb292d

Please sign in to comment.