Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions linode_api4/objects/linode_interfaces.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from dataclasses import dataclass, field
from typing import List, Optional
from typing import List, Optional, Union

from linode_api4.objects.base import Base, Property
from linode_api4.objects.base import Base, ExplicitNullValue, Property
from linode_api4.objects.dbase import DerivedBase
from linode_api4.objects.networking import Firewall
from linode_api4.objects.serializable import JSONObject
Expand Down Expand Up @@ -193,13 +193,13 @@ class LinodeInterfaceOptions(JSONObject):
NOTE: Linode interfaces may not currently be available to all users.
"""

always_include = {
# If a default firewall_id isn't configured, the API requires that
# firewall_id is defined in the LinodeInterface POST body.
"firewall_id"
}
# If a default firewall_id isn't configured, the API requires that
# firewall_id is defined in the LinodeInterface POST body.
#
# To create a Linode Interface without a firewall, this field should
# be set to `ExplicitNullValue()`.
firewall_id: Union[int, ExplicitNullValue, None] = None

firewall_id: Optional[int] = None
default_route: Optional[LinodeInterfaceDefaultRouteOptions] = None
vpc: Optional[LinodeInterfaceVPCOptions] = None
public: Optional[LinodeInterfacePublicOptions] = None
Expand Down
10 changes: 10 additions & 0 deletions linode_api4/objects/serializable.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,16 @@ def attempt_serialize(value: Any) -> Any:
if issubclass(type(value), JSONObject):
return value._serialize(is_put=is_put)

# Needed to avoid circular imports without a breaking change
from linode_api4.objects.base import ( # pylint: disable=import-outside-toplevel
ExplicitNullValue,
)

if value == ExplicitNullValue or isinstance(
value, ExplicitNullValue
):
return None

return value

def should_include(key: str, value: Any) -> bool:
Expand Down
2 changes: 2 additions & 0 deletions test/integration/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from requests.exceptions import ConnectionError, RequestException

from linode_api4 import (
ExplicitNullValue,
InterfaceGeneration,
LinodeInterfaceDefaultRouteOptions,
LinodeInterfaceOptions,
Expand Down Expand Up @@ -585,6 +586,7 @@ def linode_with_linode_interfaces(
public=LinodeInterfacePublicOptions(),
),
LinodeInterfaceOptions(
firewall_id=ExplicitNullValue,
vpc=LinodeInterfaceVPCOptions(
subnet_id=subnet.id,
),
Expand Down
24 changes: 12 additions & 12 deletions test/integration/models/linode/interfaces/test_interfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
LinodeInterfacePublicIPv6RangeOptions,
LinodeInterfacePublicOptions,
LinodeInterfaceVLANOptions,
LinodeInterfaceVPCIPv4AddressOptions,
LinodeInterfaceVPCIPv4Options,
LinodeInterfaceVPCIPv4RangeOptions,
LinodeInterfaceVPCOptions,
Expand Down Expand Up @@ -145,19 +146,18 @@ def linode_interface_vpc(
vpc=LinodeInterfaceVPCOptions(
subnet_id=subnet.id,
ipv4=LinodeInterfaceVPCIPv4Options(
# TODO (Enhanced Interfaces): Not currently working as expected
# addresses=[
# LinodeInterfaceVPCIPv4AddressOptions(
# address="auto",
# primary=True,
# nat_1_1_address="any",
# )
# ],
addresses=[
LinodeInterfaceVPCIPv4AddressOptions(
address="auto",
primary=True,
nat_1_1_address="auto",
)
],
ranges=[
LinodeInterfaceVPCIPv4RangeOptions(
range="/29",
range="/32",
)
]
],
),
),
), instance, vpc, subnet
Expand Down Expand Up @@ -263,9 +263,9 @@ def test_linode_interface_create_vpc(linode_interface_vpc):

assert len(iface.vpc.ipv4.addresses[0].address) > 0
assert iface.vpc.ipv4.addresses[0].primary
assert iface.vpc.ipv4.addresses[0].nat_1_1_address is None
assert iface.vpc.ipv4.addresses[0].nat_1_1_address is not None

assert iface.vpc.ipv4.ranges[0].range.split("/")[1] == "29"
assert iface.vpc.ipv4.ranges[0].range.split("/")[1] == "32"


def test_linode_interface_update_vpc(linode_interface_vpc):
Expand Down
16 changes: 12 additions & 4 deletions test/unit/objects/serializable_test.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from dataclasses import dataclass
from test.unit.base import ClientBaseCase
from typing import Optional
from typing import Optional, Union

from linode_api4 import Base, JSONObject, Property
from linode_api4 import Base, ExplicitNullValue, JSONObject, Property


class JSONObjectTest(ClientBaseCase):
Expand All @@ -14,18 +14,21 @@ class Foo(JSONObject):
foo: Optional[str] = None
bar: Optional[str] = None
baz: str = None
foobar: Union[str, ExplicitNullValue, None] = None

foo = Foo().dict

assert foo["foo"] is None
assert "bar" not in foo
assert foo["baz"] is None
assert "foobar" not in foo

foo = Foo(foo="test", bar="test2", baz="test3").dict
foo = Foo(foo="test", bar="test2", baz="test3", foobar="test4").dict

assert foo["foo"] == "test"
assert foo["bar"] == "test2"
assert foo["baz"] == "test3"
assert foo["foobar"] == "test4"

def test_serialize_optional_include_None(self):
@dataclass
Expand All @@ -35,18 +38,23 @@ class Foo(JSONObject):
foo: Optional[str] = None
bar: Optional[str] = None
baz: str = None
foobar: Union[str, ExplicitNullValue, None] = None

foo = Foo().dict

assert foo["foo"] is None
assert foo["bar"] is None
assert foo["baz"] is None
assert foo["foobar"] is None

foo = Foo(foo="test", bar="test2", baz="test3").dict
foo = Foo(
foo="test", bar="test2", baz="test3", foobar=ExplicitNullValue()
).dict

assert foo["foo"] == "test"
assert foo["bar"] == "test2"
assert foo["baz"] == "test3"
assert foo["foobar"] is None

def test_serialize_put_class(self):
"""
Expand Down