Skip to content

Commit

Permalink
feat: add to_dict method to domain classes
Browse files Browse the repository at this point in the history
  • Loading branch information
jooola committed Sep 25, 2023
1 parent 95e7d03 commit b5369df
Show file tree
Hide file tree
Showing 5 changed files with 262 additions and 3 deletions.
28 changes: 28 additions & 0 deletions hcloud/core/domain.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
from __future__ import annotations

from datetime import datetime
from typing import Any

from .client import BoundModelBase


class BaseDomain:
__slots__: tuple[str, ...] = ()
Expand All @@ -12,11 +17,34 @@ def from_dict(cls, data: dict): # type: ignore[no-untyped-def]
supported_data = {k: v for k, v in data.items() if k in cls.__slots__}
return cls(**supported_data)

def to_dict(self) -> dict:
"""Recursively convert a domain object to a dict."""
return _make_serializable(self) # type: ignore

def __repr__(self) -> str:
kwargs = [f"{key}={getattr(self, key)!r}" for key in self.__slots__]
return f"{self.__class__.__qualname__}({', '.join(kwargs)})"


def _make_serializable(value: Any) -> dict | list | str | int | float:
if isinstance(value, (BaseDomain, BoundModelBase)):
if isinstance(value, BoundModelBase):
value = value.data_model

return {key: _make_serializable(getattr(value, key)) for key in value.__slots__}

if isinstance(value, dict):
return {key: _make_serializable(value[key]) for key in value}

if isinstance(value, list):
return [_make_serializable(child) for child in value]

if isinstance(value, datetime):
return value.isoformat()

return value


class DomainIdentityMixin:
__slots__ = ()

Expand Down
2 changes: 1 addition & 1 deletion hcloud/datacenters/domain.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def __init__(
self.server_types = server_types


class DatacenterServerTypes:
class DatacenterServerTypes(BaseDomain):
"""DatacenterServerTypes Domain
:param available: List[:class:`BoundServerTypes <hcloud.server_types.client.BoundServerTypes>`]
Expand Down
4 changes: 2 additions & 2 deletions hcloud/firewalls/domain.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def __init__(
self.created = isoparse(created) if created else None


class FirewallRule:
class FirewallRule(BaseDomain):
"""Firewall Rule Domain
:param direction: str
Expand Down Expand Up @@ -125,7 +125,7 @@ def to_payload(self) -> dict[str, Any]:
return payload


class FirewallResource:
class FirewallResource(BaseDomain):
"""Firewall Used By Domain
:param type: str
Expand Down
193 changes: 193 additions & 0 deletions tests/unit/core/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
from __future__ import annotations

import pytest


@pytest.fixture()
def server_dict():
return {
"id": 35555937,
"name": "tmp",
"status": "running",
"created": "2023-08-03T13:06:14+00:00",
"public_net": {
"ipv4": {
"ip": "95.217.11.207",
"blocked": False,
"dns_ptr": "static.207.11.217.95.clients.your-server.de",
"id": 36311011,
},
"ipv6": {
"ip": "2a01:4f9:c012:63c9::/64",
"blocked": False,
"dns_ptr": [],
"id": 36311012,
},
"floating_ips": [],
"firewalls": [],
},
"private_net": [],
"server_type": {
"id": 1,
"name": "cx11",
"description": "CX11",
"cores": 1,
"memory": 2.0,
"disk": 20,
"deprecated": False,
"prices": [
{
"location": "fsn1",
"price_hourly": {
"net": "0.0052000000",
"gross": "0.0061880000000000",
},
"price_monthly": {
"net": "3.2900000000",
"gross": "3.9151000000000000",
},
},
{
"location": "hel1",
"price_hourly": {
"net": "0.0052000000",
"gross": "0.0061880000000000",
},
"price_monthly": {
"net": "3.2900000000",
"gross": "3.9151000000000000",
},
},
{
"location": "nbg1",
"price_hourly": {
"net": "0.0052000000",
"gross": "0.0061880000000000",
},
"price_monthly": {
"net": "3.2900000000",
"gross": "3.9151000000000000",
},
},
],
"storage_type": "local",
"cpu_type": "shared",
"architecture": "x86",
"included_traffic": 21990232555520,
"deprecation": None,
},
"datacenter": {
"id": 3,
"name": "hel1-dc2",
"description": "Helsinki 1 virtual DC 2",
"location": {
"id": 3,
"name": "hel1",
"description": "Helsinki DC Park 1",
"country": "FI",
"city": "Helsinki",
"latitude": 60.169855,
"longitude": 24.938379,
"network_zone": "eu-central",
},
"server_types": {
"supported": [
1,
3,
5,
7,
9,
22,
23,
24,
25,
26,
33,
34,
35,
36,
37,
38,
45,
93,
94,
95,
],
"available": [
1,
3,
5,
7,
9,
22,
23,
24,
25,
26,
33,
34,
35,
36,
37,
38,
],
"available_for_migration": [
1,
3,
5,
7,
9,
22,
23,
24,
25,
26,
33,
34,
35,
36,
37,
38,
96,
97,
98,
99,
100,
101,
],
},
},
"image": {
"id": 114690387,
"type": "system",
"status": "available",
"name": "debian-12",
"description": "Debian 12",
"image_size": None,
"disk_size": 5,
"created": "2023-06-13T06:00:02+00:00",
"created_from": None,
"bound_to": None,
"os_flavor": "debian",
"os_version": "12",
"rapid_deploy": True,
"protection": {"delete": False},
"deprecated": None,
"labels": {},
"deleted": None,
"architecture": "x86",
},
"iso": None,
"rescue_enabled": False,
"locked": False,
"backup_window": None,
"outgoing_traffic": None,
"ingoing_traffic": None,
"included_traffic": 21990232555520,
"protection": {"delete": False, "rebuild": False},
"labels": {},
"volumes": [],
# "load_balancers": [],
"primary_disk_size": 20,
"placement_group": None,
}
38 changes: 38 additions & 0 deletions tests/unit/core/test_domain.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,44 @@ def test_from_dict_ok(self, data_dict, expected_result):
for k, v in expected_result.items():
assert getattr(model, k) == v

@pytest.mark.parametrize(
"data,expected",
[
(
SomeOtherDomain(id=1, name="name1"),
{"id": 1, "name": "name1", "child": None},
),
(
SomeOtherDomain(
id=2, name="name2", child=SomeOtherDomain(id=3, name="name3")
),
{
"id": 2,
"name": "name2",
"child": {"id": 3, "name": "name3", "child": None},
},
),
(
SomeOtherDomain(
id=2, name="name2", child=[SomeOtherDomain(id=3, name="name3")]
),
{
"id": 2,
"name": "name2",
"child": [{"id": 3, "name": "name3", "child": None}],
},
),
],
)
def test_to_dict_ok(self, data, expected):
assert data.to_dict() == expected

def test_from_dict_and_to_dict_ok(self, server_dict: dict):
from hcloud.servers import Server

server = Server.from_dict(server_dict)
assert server.to_dict() == server_dict

@pytest.mark.parametrize(
"data,expected",
[
Expand Down

0 comments on commit b5369df

Please sign in to comment.