Skip to content

Commit bd89b0e

Browse files
committed
Update utils.camelize to update string based in settings
1 parent e0d3d7b commit bd89b0e

File tree

3 files changed

+68
-17
lines changed

3 files changed

+68
-17
lines changed

drf_simple_api_errors/formatter.py

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,17 @@
1+
"""
2+
This module provides functionality to format API exceptions into a structured error response.
3+
4+
It defines the `APIErrorResponse` class, which represents
5+
the structure of the error response, and the `format_exc` function, which
6+
formats exceptions into this structure according to the type of exception, its detail,
7+
and any additional settings defined in the settings.
8+
9+
Functions:
10+
- `format_exc`:
11+
Formats the given exception into a structured API error response.
12+
Used by the exception handler to return a consistent error format.
13+
"""
14+
115
import copy
216
import logging
317
from dataclasses import asdict, dataclass, field as dataclass_field
@@ -7,9 +21,9 @@
721
from rest_framework import exceptions
822
from rest_framework.settings import api_settings as drf_api_settings
923

24+
from drf_simple_api_errors import utils
1025
from drf_simple_api_errors.settings import api_settings
1126
from drf_simple_api_errors.types import APIErrorResponseDict
12-
from drf_simple_api_errors.utils import camelize, flatten_dict
1327

1428
logger = logging.getLogger(__name__)
1529

@@ -46,9 +60,8 @@ def to_dict(self) -> APIErrorResponseDict:
4660
"""Convert the APIErrorResponse instance to a dictionary."""
4761
response_dict = asdict(self)
4862

49-
if api_settings.CAMELIZE:
50-
for key in list(response_dict.keys()):
51-
response_dict[camelize(key)] = response_dict.pop(key)
63+
for key in list(response_dict.keys()):
64+
response_dict[utils.camelize(key)] = response_dict.pop(key)
5265

5366
return response_dict
5467

@@ -112,7 +125,7 @@ def _format_exc_detail_dict(
112125
# Start by flattening the exc dict.
113126
# This is necessary as the exception detail can be nested and
114127
# we want to flatten it to a single level dict as part of this library design.
115-
exc_detail = flatten_dict(copy.deepcopy(exc_detail))
128+
exc_detail = utils.flatten_dict(copy.deepcopy(exc_detail))
116129

117130
# Track the invalid params.
118131
# This represents the fields that are invalid and have errors associated with them.
@@ -136,7 +149,7 @@ def _format_exc_detail_dict(
136149
# N.B. If the error is a string, we will convert it to a list
137150
# to keep the consistency with the InvalidParamDict type.
138151
invalid_param = InvalidParam(
139-
name=field if not api_settings.CAMELIZE else camelize(field),
152+
name=utils.camelize(field),
140153
reason=error if isinstance(error, list) else [error],
141154
)
142155
invalid_params.append(invalid_param)

drf_simple_api_errors/utils.py

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,16 @@
33
from drf_simple_api_errors.settings import api_settings
44

55

6-
def camelize(field: str) -> str:
7-
"""Convert a snake_case string to camelCase."""
6+
def camelize(s: str) -> str:
7+
"""
8+
Convert a snake_case string to camelCase according to
9+
the CAMELIZE setting (default to `False`).
10+
11+
Args:
12+
s (str): The string to convert.
13+
Returns:
14+
str: The camelCase version of the string, or the original if CAMELIZE is `False`.
15+
"""
816

917
def underscore_to_camel(match: re.Match) -> str:
1018
group = match.group()
@@ -13,14 +21,25 @@ def underscore_to_camel(match: re.Match) -> str:
1321
else:
1422
return group[1].upper()
1523

24+
if not api_settings.CAMELIZE:
25+
return s
26+
1627
camelize_re = re.compile(r"[a-z0-9]?_[a-z0-9]")
17-
return re.sub(camelize_re, underscore_to_camel, field)
28+
return re.sub(camelize_re, underscore_to_camel, s)
1829

1930

2031
def flatten_dict(data: dict, parent_key: str = "") -> dict:
2132
"""
2233
Flatten a nested dictionary into a single-level dictionary according to
23-
the specified FIELDS_SEPARATOR in your settings (default to '.').
34+
the specified FIELDS_SEPARATOR setting (default to `'.'`).
35+
36+
Args:
37+
data (dict): The dictionary to flatten.
38+
parent_key (str):
39+
The base key to prepend to each key in the flattened dictionary.
40+
This is used for recursive calls to maintain the hierarchy.
41+
Returns:
42+
dict: A flattened dictionary with keys joined by the FIELDS_SEPARATOR.
2443
"""
2544
sep = api_settings.FIELDS_SEPARATOR
2645

test_project/test_app/tests.py

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -517,16 +517,35 @@ def test_validation_error_ok(self, book_model_serializer, faker, mocker, user):
517517

518518
class TestUtils:
519519
@pytest.mark.parametrize(
520-
"field_input, expected_output",
520+
"field_input, expected_output, camelize_enabled",
521521
[
522-
("", ""),
523-
("name", "name"),
524-
("first_name", "firstName"),
525-
("family_tree_name", "familyTreeName"),
526-
("very_long_last_name_and_first_name", "veryLongLastNameAndFirstName"),
522+
("", "", True),
523+
("name", "name", True),
524+
("first_name", "firstName", True),
525+
("family_tree_name", "familyTreeName", True),
526+
(
527+
"very_long_last_name_and_first_name",
528+
"veryLongLastNameAndFirstName",
529+
True,
530+
),
531+
("", "", False),
532+
("name", "name", False),
533+
("first_name", "first_name", False),
534+
("family_tree_name", "family_tree_name", False),
535+
(
536+
"very_long_last_name_and_first_name",
537+
"very_long_last_name_and_first_name",
538+
False,
539+
),
527540
],
528541
)
529-
def test_camelize(self, field_input, expected_output):
542+
def test_camelize_with_settings_set(
543+
self, mocker, field_input, expected_output, camelize_enabled
544+
):
545+
mocker.patch(
546+
"drf_simple_api_errors.settings.api_settings.CAMELIZE", camelize_enabled
547+
)
548+
530549
assert utils.camelize(field_input) == expected_output
531550

532551
@pytest.mark.parametrize(

0 commit comments

Comments
 (0)