Skip to content

Commit 522587f

Browse files
Implement support for VPC Dual Stack
1 parent dd94cde commit 522587f

File tree

8 files changed

+208
-104
lines changed

8 files changed

+208
-104
lines changed

Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ test-int:
6666

6767
.PHONY: test-unit
6868
test-unit:
69-
$(PYTHON) -m pytest test/unit -vv
69+
$(PYTHON) -m pytest test/unit
7070

7171
.PHONY: test-smoke
7272
test-smoke:

linode_api4/groups/vpc.py

+13-7
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22

33
from linode_api4.errors import UnexpectedResponseError
44
from linode_api4.groups import Group
5-
from linode_api4.objects import VPC, Region, VPCIPAddress
5+
from linode_api4.objects import VPC, Region, VPCIPAddress, VPCIPv6RangeOptions
6+
from linode_api4.objects.base import _flatten_request_body_recursive
67
from linode_api4.paginated_list import PaginatedList
8+
from linode_api4.util import drop_null_keys
79

810

911
class VPCGroup(Group):
@@ -33,6 +35,7 @@ def create(
3335
region: Union[Region, str],
3436
description: Optional[str] = None,
3537
subnets: Optional[List[Dict[str, Any]]] = None,
38+
ipv6: Optional[List[Union[VPCIPv6RangeOptions, Dict[str, Any]]]] = None,
3639
**kwargs,
3740
) -> VPC:
3841
"""
@@ -48,30 +51,33 @@ def create(
4851
:type description: Optional[str]
4952
:param subnets: A list of subnets to create under this VPC.
5053
:type subnets: List[Dict[str, Any]]
54+
:param ipv6: The IPv6 address ranges for this VPC.
55+
:type ipv6: List[Union[VPCIPv6RangeOptions, Dict[str, Any]]]
5156
5257
:returns: The new VPC object.
5358
:rtype: VPC
5459
"""
5560
params = {
5661
"label": label,
5762
"region": region.id if isinstance(region, Region) else region,
63+
"description": description,
64+
"ipv6": ipv6,
65+
"subnets": subnets,
5866
}
5967

60-
if description is not None:
61-
params["description"] = description
62-
6368
if subnets is not None and len(subnets) > 0:
6469
for subnet in subnets:
6570
if not isinstance(subnet, dict):
6671
raise ValueError(
6772
f"Unsupported type for subnet: {type(subnet)}"
6873
)
6974

70-
params["subnets"] = subnets
71-
7275
params.update(kwargs)
7376

74-
result = self.client.post("/vpcs", data=params)
77+
result = self.client.post(
78+
"/vpcs",
79+
data=drop_null_keys(_flatten_request_body_recursive(params)),
80+
)
7581

7682
if not "id" in result:
7783
raise UnexpectedResponseError(

linode_api4/objects/account.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -601,7 +601,7 @@ def entity(self):
601601
)
602602
return self.cls(self._client, self.id)
603603

604-
def _serialize(self):
604+
def _serialize(self, *args, **kwargs):
605605
"""
606606
Returns this grant in as JSON the api will accept. This is only relevant
607607
in the context of UserGrants.save
@@ -668,7 +668,7 @@ def _grants_dict(self):
668668

669669
return grants
670670

671-
def _serialize(self):
671+
def _serialize(self, *args, **kwargs):
672672
"""
673673
Returns the user grants in as JSON the api will accept.
674674
This is only relevant in the context of UserGrants.save

linode_api4/objects/base.py

+16-25
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ def __init__(
3535
nullable=False,
3636
unordered=False,
3737
json_object=None,
38-
json_object_options=None,
3938
):
4039
"""
4140
A Property is an attribute returned from the API, and defines metadata
@@ -57,8 +56,6 @@ def __init__(
5756
NOTE: This field is currently only for annotations purposes
5857
and does not influence any update or decoding/encoding logic.
5958
json_object - The JSONObject class this property should be decoded into.
60-
json_object_options - The JSONObject class this property should use when
61-
serializing for PUT requests.
6259
"""
6360
self.mutable = mutable
6461
self.identifier = identifier
@@ -71,7 +68,6 @@ def __init__(
7168
self.nullable = nullable
7269
self.unordered = unordered
7370
self.json_class = json_object
74-
self.json_class_options = json_object_options
7571

7672

7773
class MappedObject:
@@ -118,6 +114,9 @@ def _flatten_base_subclass(obj: "Base") -> Optional[Dict[str, Any]]:
118114

119115
@property
120116
def dict(self):
117+
return self._serialize()
118+
119+
def _serialize(self, is_put: bool = False) -> Dict[str, Any]:
121120
result = vars(self).copy()
122121
cls = type(self)
123122

@@ -127,7 +126,7 @@ def dict(self):
127126
elif isinstance(v, list):
128127
result[k] = [
129128
(
130-
item.dict
129+
item._serialize(is_put=is_put)
131130
if isinstance(item, (cls, JSONObject))
132131
else (
133132
self._flatten_base_subclass(item)
@@ -140,7 +139,7 @@ def dict(self):
140139
elif isinstance(v, Base):
141140
result[k] = self._flatten_base_subclass(v)
142141
elif isinstance(v, JSONObject):
143-
result[k] = v.dict
142+
result[k] = v._serialize(is_put=is_put)
144143

145144
return result
146145

@@ -282,20 +281,9 @@ def save(self, force=True) -> bool:
282281
data[key] = None
283282

284283
# Ensure we serialize any values that may not be already serialized
285-
data = _flatten_request_body_recursive(data)
284+
data = _flatten_request_body_recursive(data, is_put=True)
286285
else:
287-
data = self._serialize()
288-
289-
for key, value in data.items():
290-
key_property = getattr(type(self).properties, key, None)
291-
if key_property is None:
292-
continue
293-
294-
json_class = key_property.json_class
295-
if json_class is None:
296-
continue
297-
298-
data[key] = json_class.from_json(value).dict
286+
data = self._serialize(is_put=True)
299287

300288
resp = self._client.put(type(self).api_endpoint, model=self, data=data)
301289

@@ -331,7 +319,7 @@ def invalidate(self):
331319

332320
self._set("_populated", False)
333321

334-
def _serialize(self):
322+
def _serialize(self, is_put: bool = False):
335323
"""
336324
A helper method to build a dict of all mutable Properties of
337325
this object
@@ -360,7 +348,7 @@ def _serialize(self):
360348

361349
# Resolve the underlying IDs of results
362350
for k, v in result.items():
363-
result[k] = _flatten_request_body_recursive(v)
351+
result[k] = _flatten_request_body_recursive(v, is_put=is_put)
364352

365353
return result
366354

@@ -518,7 +506,7 @@ def make_instance(cls, id, client, parent_id=None, json=None):
518506
return Base.make(id, client, cls, parent_id=parent_id, json=json)
519507

520508

521-
def _flatten_request_body_recursive(data: Any) -> Any:
509+
def _flatten_request_body_recursive(data: Any, is_put: bool = False) -> Any:
522510
"""
523511
This is a helper recursively flatten the given data for use in an API request body.
524512
@@ -530,15 +518,18 @@ def _flatten_request_body_recursive(data: Any) -> Any:
530518
"""
531519

532520
if isinstance(data, dict):
533-
return {k: _flatten_request_body_recursive(v) for k, v in data.items()}
521+
return {
522+
k: _flatten_request_body_recursive(v, is_put=is_put)
523+
for k, v in data.items()
524+
}
534525

535526
if isinstance(data, list):
536-
return [_flatten_request_body_recursive(v) for v in data]
527+
return [_flatten_request_body_recursive(v, is_put=is_put) for v in data]
537528

538529
if isinstance(data, Base):
539530
return data.id
540531

541532
if isinstance(data, MappedObject) or issubclass(type(data), JSONObject):
542-
return data.dict
533+
return data._serialize(is_put=is_put)
543534

544535
return data

0 commit comments

Comments
 (0)