|
1 | 1 | from typing import Any, ClassVar, Self, override |
2 | 2 |
|
3 | 3 | from box import Box |
| 4 | +from box.box import _camel_killer # type: ignore[attr-defined] # noqa: PLC2701 |
4 | 5 |
|
5 | 6 | from mpt_api_client.http.types import Response |
6 | 7 | from mpt_api_client.models.meta import Meta |
7 | 8 |
|
8 | 9 | ResourceData = dict[str, Any] |
9 | 10 |
|
10 | 11 |
|
| 12 | +class MptBox(Box): |
| 13 | + """python-box that preserves camelCase keys when converted to json.""" |
| 14 | + |
| 15 | + def __init__(self, *args, key_mapping: dict[str, str] | None, **kwargs): # type: ignore[no-untyped-def] |
| 16 | + super().__init__(*args, **kwargs) |
| 17 | + key_mapping = key_mapping or {} |
| 18 | + if self._box_config.get("key_mapping") is None: |
| 19 | + self._box_config["key_mapping"] = key_mapping |
| 20 | + else: |
| 21 | + self._box_config.get("key_mapping").update(key_mapping) |
| 22 | + |
| 23 | + @override |
| 24 | + def __setitem__(self, key, value): # type: ignore[no-untyped-def] # noqa: WPS110 |
| 25 | + try: |
| 26 | + mapped_key = self._box_config["key_mapping"][key] |
| 27 | + except KeyError as error: |
| 28 | + if key == "key_mapping" and "key_mapping" in self._box_config: |
| 29 | + return |
| 30 | + if error.args[0] == "key_mapping" and "key_mapping" not in self._box_config: |
| 31 | + self._box_config["key_mapping"] = self._box_config.get("default_key_mappings", {}) |
| 32 | + |
| 33 | + mapped_key = _camel_killer(key) |
| 34 | + self._box_config["key_mapping"][key] = mapped_key |
| 35 | + super().__setitem__(mapped_key, value) # type: ignore[no-untyped-call] |
| 36 | + |
| 37 | + @override |
| 38 | + def to_dict(self) -> dict[str, Any]: # noqa: WPS210 |
| 39 | + reverse_mapping = { |
| 40 | + mapped_key: original_key |
| 41 | + for original_key, mapped_key in self._box_config.get("key_mapping", {}).items() |
| 42 | + } |
| 43 | + out_dict = {} |
| 44 | + for parsed_key, item_value in super().to_dict().items(): |
| 45 | + original_key = reverse_mapping[parsed_key] |
| 46 | + out_dict[original_key] = item_value |
| 47 | + return out_dict |
| 48 | + |
| 49 | + |
11 | 50 | class Model: # noqa: WPS214 |
12 | 51 | """Provides a resource to interact with api data using fluent interfaces.""" |
13 | 52 |
|
14 | 53 | _data_key: ClassVar[str | None] = None |
15 | 54 | _safe_attributes: ClassVar[list[str]] = ["meta", "_box"] |
| 55 | + _case_mappings: ClassVar[dict[str, str]] = {} |
16 | 56 |
|
17 | 57 | def __init__(self, resource_data: ResourceData | None = None, meta: Meta | None = None) -> None: |
18 | 58 | self.meta = meta |
19 | | - self._box = Box(resource_data or {}, camel_killer_box=False, default_box=False) |
| 59 | + self._box = MptBox( |
| 60 | + resource_data or {}, |
| 61 | + camel_killer_box=False, |
| 62 | + default_box=False, |
| 63 | + default_box_create_on_get=False, |
| 64 | + key_mapping=self._case_mappings, |
| 65 | + ) |
20 | 66 |
|
21 | 67 | @classmethod |
22 | 68 | def new(cls, resource_data: ResourceData | None = None, meta: Meta | None = None) -> Self: |
|
0 commit comments