Skip to content

Commit f929568

Browse files
Added support for custom JSON field names in dataclasses
1 parent 9ebc6cb commit f929568

4 files changed

Lines changed: 77 additions & 34 deletions

File tree

linode_api4/objects/database.py

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from dataclasses import dataclass
1+
from dataclasses import dataclass, field
22
from typing import Optional
33

44
from deprecated import deprecated
@@ -231,11 +231,22 @@ class PostgreSQLDatabaseConfigPGOptions(JSONObject):
231231
max_wal_senders: Optional[int] = None
232232
max_worker_processes: Optional[int] = None
233233
password_encryption: Optional[str] = None
234-
pg_partman_bgw_interval: Optional[int] = None
235-
pg_partman_bgw_role: Optional[str] = None
236-
pg_stat_monitor_pgsm_enable_query_plan: Optional[bool] = None
237-
pg_stat_monitor_pgsm_max_buckets: Optional[int] = None
238-
pg_stat_statements_track: Optional[str] = None
234+
pg_partman_bgw_interval: Optional[int] = field(
235+
default=None, metadata={"json_key": "pg_partman_bgw.interval"}
236+
)
237+
pg_partman_bgw_role: Optional[str] = field(
238+
default=None, metadata={"json_key": "pg_partman_bgw.role"}
239+
)
240+
pg_stat_monitor_pgsm_enable_query_plan: Optional[bool] = field(
241+
default=None,
242+
metadata={"json_key": "pg_stat_monitor.pgsm_enable_query_plan"},
243+
)
244+
pg_stat_monitor_pgsm_max_buckets: Optional[int] = field(
245+
default=None, metadata={"json_key": "pg_stat_monitor.pgsm_max_buckets"}
246+
)
247+
pg_stat_statements_track: Optional[str] = field(
248+
default=None, metadata={"json_key": "pg_stat_statements.track"}
249+
)
239250
temp_file_limit: Optional[int] = None
240251
timezone: Optional[str] = None
241252
track_activity_query_size: Optional[int] = None
@@ -290,7 +301,9 @@ class MySQLDatabase(Base):
290301
"updated": Property(volatile=True, is_datetime=True),
291302
"updates": Property(mutable=True),
292303
"version": Property(),
293-
"engine_config": Property(mutable=True),
304+
"engine_config": Property(
305+
mutable=True, json_object=MySQLDatabaseConfigOptions
306+
),
294307
}
295308

296309
@property
@@ -454,7 +467,9 @@ class PostgreSQLDatabase(Base):
454467
"updated": Property(volatile=True, is_datetime=True),
455468
"updates": Property(mutable=True),
456469
"version": Property(),
457-
"engine_config": Property(mutable=True),
470+
"engine_config": Property(
471+
mutable=True, json_object=PostgreSQLDatabaseConfigOptions
472+
),
458473
}
459474

460475
@property

linode_api4/objects/serializable.py

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import inspect
22
from dataclasses import dataclass
3+
from dataclasses import fields as dataclass_fields
34
from enum import Enum
45
from types import SimpleNamespace
56
from typing import (
@@ -140,7 +141,7 @@ def _parse_attr(cls, json_value: Any, field_type: type):
140141
@classmethod
141142
def from_json(cls, json: Dict[str, Any]) -> Optional["JSONObject"]:
142143
"""
143-
Creates an instance of this class from a JSON dict.
144+
Creates an instance of this class from a JSON dict, respecting json_key metadata.
144145
"""
145146
if json is None:
146147
return None
@@ -149,8 +150,12 @@ def from_json(cls, json: Dict[str, Any]) -> Optional["JSONObject"]:
149150

150151
type_hints = get_type_hints(cls)
151152

152-
for k in vars(obj):
153-
setattr(obj, k, cls._parse_attr(json.get(k), type_hints.get(k)))
153+
for f in dataclass_fields(cls):
154+
json_key = f.metadata.get("json_key", f.name)
155+
field_type = type_hints.get(f.name)
156+
value = json.get(json_key)
157+
parsed_value = cls._parse_attr(value, field_type)
158+
setattr(obj, f.name, parsed_value)
154159

155160
return obj
156161

@@ -193,7 +198,11 @@ def should_include(key: str, value: Any) -> bool:
193198

194199
result = {}
195200

196-
for k, v in vars(self).items():
201+
for f in dataclass_fields(self):
202+
k = f.name
203+
json_key = f.metadata.get("json_key", k)
204+
v = getattr(self, k)
205+
197206
if not should_include(k, v):
198207
continue
199208

@@ -204,7 +213,7 @@ def should_include(key: str, value: Any) -> bool:
204213
else:
205214
v = attempt_serialize(v)
206215

207-
result[k] = v
216+
result[json_key] = v
208217

209218
return result
210219

test/integration/models/database/helpers.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -104,12 +104,11 @@ def make_full_postgres_engine_config():
104104
track_functions="all",
105105
wal_sender_timeout=60000,
106106
wal_writer_delay=200,
107-
# # not valid
108-
# pg_partman_bgw_interval=30,
109-
# pg_partman_bgw_role="asdf",
110-
# pg_stat_monitor_pgsm_enable_query_plan=True,
111-
# pg_stat_monitor_pgsm_max_buckets=2,
112-
# pg_stat_statements_track="asdf",
107+
pg_partman_bgw_interval=30,
108+
pg_partman_bgw_role="myrolename",
109+
pg_stat_monitor_pgsm_enable_query_plan=True,
110+
pg_stat_monitor_pgsm_max_buckets=2,
111+
pg_stat_statements_track="top",
113112
),
114113
pg_stat_monitor_enable=True,
115114
shared_buffers_percentage=25.0,

test/unit/objects/database_test.py

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -469,27 +469,18 @@ def test_get_instances(self):
469469
self.assertEqual(
470470
dbs[0].engine_config.pg.password_encryption, "scram-sha-256"
471471
)
472+
self.assertEqual(dbs[0].engine_config.pg.pg_partman_bgw_interval, 3600)
472473
self.assertEqual(
473-
getattr(dbs[0].engine_config.pg, "pg_partman_bgw.interval"), 3600
474-
)
475-
self.assertEqual(
476-
getattr(dbs[0].engine_config.pg, "pg_partman_bgw.role"),
477-
"myrolename",
474+
dbs[0].engine_config.pg.pg_partman_bgw_role, "myrolename"
478475
)
479476
self.assertFalse(
480-
getattr(
481-
dbs[0].engine_config.pg,
482-
"pg_stat_monitor.pgsm_enable_query_plan",
483-
)
477+
dbs[0].engine_config.pg.pg_stat_monitor_pgsm_enable_query_plan
484478
)
485479
self.assertEqual(
486-
getattr(
487-
dbs[0].engine_config.pg, "pg_stat_monitor.pgsm_max_buckets"
488-
),
489-
10,
480+
dbs[0].engine_config.pg.pg_stat_monitor_pgsm_max_buckets, 10
490481
)
491482
self.assertEqual(
492-
getattr(dbs[0].engine_config.pg, "pg_stat_statements.track"), "top"
483+
dbs[0].engine_config.pg.pg_stat_statements_track, "top"
493484
)
494485
self.assertEqual(dbs[0].engine_config.pg.temp_file_limit, 5000000)
495486
self.assertEqual(dbs[0].engine_config.pg.timezone, "Europe/Helsinki")
@@ -519,7 +510,12 @@ def test_create(self):
519510
cluster_size=3,
520511
engine_config=PostgreSQLDatabaseConfigOptions(
521512
pg=PostgreSQLDatabaseConfigPGOptions(
522-
autovacuum_analyze_scale_factor=0.5
513+
autovacuum_analyze_scale_factor=0.5,
514+
pg_partman_bgw_interval=3600,
515+
pg_partman_bgw_role="myrolename",
516+
pg_stat_monitor_pgsm_enable_query_plan=False,
517+
pg_stat_monitor_pgsm_max_buckets=10,
518+
pg_stat_statements_track="top",
523519
),
524520
work_mem=4,
525521
),
@@ -540,6 +536,30 @@ def test_create(self):
540536
],
541537
0.5,
542538
)
539+
self.assertEqual(
540+
m.call_data["engine_config"]["pg"]["pg_partman_bgw.interval"],
541+
3600,
542+
)
543+
self.assertEqual(
544+
m.call_data["engine_config"]["pg"]["pg_partman_bgw.role"],
545+
"myrolename",
546+
)
547+
self.assertEqual(
548+
m.call_data["engine_config"]["pg"][
549+
"pg_stat_monitor.pgsm_enable_query_plan"
550+
],
551+
False,
552+
)
553+
self.assertEqual(
554+
m.call_data["engine_config"]["pg"][
555+
"pg_stat_monitor.pgsm_max_buckets"
556+
],
557+
10,
558+
)
559+
self.assertEqual(
560+
m.call_data["engine_config"]["pg"]["pg_stat_statements.track"],
561+
"top",
562+
)
543563
self.assertEqual(m.call_data["engine_config"]["work_mem"], 4)
544564

545565
def test_update(self):

0 commit comments

Comments
 (0)