Skip to content

Commit

Permalink
fix API schema for error serializers of list serializers and dict/lis…
Browse files Browse the repository at this point in the history
…t fields.

For example, the regex `\d+\.field1` is used in the API schema to indicate that the API will return attribute values like `0.field1`, `1.field1`. Previously, attr was incorrectly specified as an enum with the value `INDEX.field1` that represents the actual API output.

fix #76
  • Loading branch information
ghazi-git committed Jul 25, 2024
1 parent 9753b3b commit 08191e9
Showing 1 changed file with 53 additions and 13 deletions.
66 changes: 53 additions & 13 deletions drf_standardized_errors/openapi_utils.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import re
from dataclasses import dataclass, field as dataclass_field
from typing import Any, Dict, List, Optional, Set, Type, Union
from typing import Dict, List, Optional, Set, Type, Union

from django import forms
from django.core.validators import (
DecimalValidator,
RegexValidator,
validate_image_file_extension,
validate_integer,
validate_ipv4_address,
Expand Down Expand Up @@ -421,21 +423,16 @@ class Meta:
def get_error_serializer(
operation_id: str, attr: Optional[str], error_codes: Set[str]
) -> Type[serializers.Serializer]:
attr_kwargs: Dict[str, Any] = {"choices": [(attr, attr)]}
if not attr:
attr_kwargs["allow_null"] = True
if attr is not None:
attr_regex = _get_attr_regex(attr)
attr_field = serializers.CharField(validators=[RegexValidator(attr_regex)])
else:
attr_field = serializers.CharField(allow_null=True)
error_code_choices = sorted(zip(error_codes, error_codes))

camelcase_operation_id = camelize(operation_id)
attr_with_underscores = (attr or "").replace(
package_settings.NESTED_FIELD_SEPARATOR, "_"
)
camelcase_attr = camelize(attr_with_underscores)
suffix = package_settings.ERROR_COMPONENT_NAME_SUFFIX
component_name = f"{camelcase_operation_id}{camelcase_attr}{suffix}"
component_name = _get_error_component_name(operation_id, attr)

class ErrorSerializer(serializers.Serializer):
attr = serializers.ChoiceField(**attr_kwargs)
attr = attr_field
code = serializers.ChoiceField(choices=error_code_choices)
detail = serializers.CharField()

Expand All @@ -445,6 +442,49 @@ class Meta:
return ErrorSerializer


def _get_attr_regex(attr: str) -> str:
r"""
- For ListSerializers:
- input attr: "INDEX.field1", "INDEX.field2", ...
- regex generated: "\d+\.field1", "\d+\.field2", ...
- actual field name: "0.field1", "1.field2", ...
- For ListFields:
- input attr: "field.INDEX"
- regex generated: "field\.\d+"
- actual field name: "0.field1", "1.field2", ...
- For DictFields:
- input attr: "field.KEY"
- regex generated: "field\..+"
- actual field name: "field.key1", "field.key2", ...
- For other cases
- input attr: "field.nested_field"
- regex generated: "field\.nested_field"
- actual field name: "field.nested_field"
"""
parts = attr.split(package_settings.NESTED_FIELD_SEPARATOR)
regex_parts = []
for part in parts:
if part == package_settings.LIST_INDEX_IN_API_SCHEMA:
regex_parts.append(r"\d+")
elif part == package_settings.DICT_KEY_IN_API_SCHEMA:
regex_parts.append(".+")
else:
regex_parts.append(re.escape(part))

escaped_separator = re.escape(package_settings.NESTED_FIELD_SEPARATOR)
return escaped_separator.join(regex_parts)


def _get_error_component_name(operation_id: str, attr: Optional[str]) -> str:
camelcase_operation_id = camelize(operation_id)
attr_with_underscores = (attr or "").replace(
package_settings.NESTED_FIELD_SEPARATOR, "_"
)
camelcase_attr = camelize(attr_with_underscores)
suffix = package_settings.ERROR_COMPONENT_NAME_SUFFIX
return f"{camelcase_operation_id}{camelcase_attr}{suffix}"


@dataclass
class InputDataField:
name: str
Expand Down

0 comments on commit 08191e9

Please sign in to comment.