Skip to content

Commit 32460c1

Browse files
committed
Search anywhere supports IPV6 extended format
1 parent 93c4695 commit 32460c1

File tree

5 files changed

+126
-2
lines changed

5 files changed

+126
-2
lines changed

backend/infrahub/core/attribute.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -872,7 +872,7 @@ def validate_format(cls, value: Any, name: str, schema: AttributeSchema) -> None
872872
raise ValidationError({name: f"{value} is not a valid {schema.kind}"}) from exc
873873

874874
def serialize_value(self) -> str:
875-
"""Serialize the value before storing it in the database."""
875+
"""Serialize the value before storing it in the database. If network is an IPv6 network, it is converted to collapsed form."""
876876

877877
return ipaddress.ip_network(self.value).with_prefixlen
878878

@@ -998,7 +998,7 @@ def validate_format(cls, value: Any, name: str, schema: AttributeSchema) -> None
998998
raise ValidationError({name: f"{value} is not a valid {schema.kind}"}) from exc
999999

10001000
def serialize_value(self) -> str:
1001-
"""Serialize the value before storing it in the database."""
1001+
"""Adds a prefix to address before storing it in the database. If address in an IPv6 address, it is converted to collapsed form."""
10021002

10031003
return ipaddress.ip_interface(self.value).with_prefixlen
10041004

backend/infrahub/core/utils.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,12 @@ def convert_ip_to_binary_str(
191191
return ip_bin.zfill(obj.max_prefixlen)
192192

193193

194+
def collapse_ipv6_address_or_network(address_or_network: str) -> str:
195+
if "/" in address_or_network:
196+
return ipaddress.IPv6Network(address_or_network).with_prefixlen
197+
return str(ipaddress.IPv6Address(address_or_network))
198+
199+
194200
# --------------------------------------------------------------------------------
195201
# CODE IMPORTED FROM:
196202
# https://github.com/graphql-python/graphene/blob/9c3e4bb7da001aac48002a3b7d83dcd072087770/graphene/utils/subclass_with_meta.py#L18

backend/infrahub/graphql/queries/search.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
from infrahub.core.constants import InfrahubKind
99
from infrahub.core.manager import NodeManager
10+
from infrahub.core.utils import collapse_ipv6_address_or_network
1011

1112
if TYPE_CHECKING:
1213
from graphql import GraphQLResolveInfo
@@ -49,6 +50,12 @@ async def search_resolver(
4950
if matching:
5051
result.append(matching)
5152
else:
53+
try:
54+
# Convert any IPv6 address/network to collapsed format as it might be stored in db.
55+
q = collapse_ipv6_address_or_network(q)
56+
except ValueError:
57+
pass
58+
5259
result.extend(
5360
await NodeManager.query(
5461
db=context.db,

backend/tests/unit/graphql/queries/test_search.py

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,3 +93,113 @@ async def test_search_anywhere_by_string(
9393

9494
assert sorted(node_ids) == sorted([person_john_main.id, person_jane_main.id])
9595
assert sorted(node_kinds) == sorted([person_john_main.get_kind(), person_jane_main.get_kind()])
96+
97+
98+
async def test_search_ipv6_address_extended_format(
99+
db: InfrahubDatabase,
100+
ip_dataset_01,
101+
branch: Branch,
102+
):
103+
gql_params = prepare_graphql_params(db=db, include_subscription=False, branch=branch)
104+
105+
res_collapsed = await graphql(
106+
schema=gql_params.schema,
107+
source=SEARCH_QUERY,
108+
context_value=gql_params.context,
109+
root_value=None,
110+
variable_values={"search": "2001:db8::"},
111+
)
112+
113+
res_extended = await graphql(
114+
schema=gql_params.schema,
115+
source=SEARCH_QUERY,
116+
context_value=gql_params.context,
117+
root_value=None,
118+
variable_values={"search": "2001:0db8:0000:0000:0000:0000:0000:0000"},
119+
)
120+
121+
assert (
122+
res_extended.data["InfrahubSearchAnywhere"]["count"]
123+
== res_collapsed.data["InfrahubSearchAnywhere"]["count"]
124+
== 2
125+
)
126+
127+
assert (
128+
res_extended.data["InfrahubSearchAnywhere"]["edges"][0]["node"]["id"]
129+
== res_collapsed.data["InfrahubSearchAnywhere"]["edges"][0]["node"]["id"]
130+
)
131+
132+
assert (
133+
res_extended.data["InfrahubSearchAnywhere"]["edges"][1]["node"]["id"]
134+
== res_collapsed.data["InfrahubSearchAnywhere"]["edges"][1]["node"]["id"]
135+
)
136+
137+
138+
async def test_search_ipv6_network_extended_format(
139+
db: InfrahubDatabase,
140+
ip_dataset_01,
141+
branch: Branch,
142+
):
143+
gql_params = prepare_graphql_params(db=db, include_subscription=False, branch=branch)
144+
145+
res_collapsed = await graphql(
146+
schema=gql_params.schema,
147+
source=SEARCH_QUERY,
148+
context_value=gql_params.context,
149+
root_value=None,
150+
variable_values={"search": "2001:db8::/48"},
151+
)
152+
153+
res_extended = await graphql(
154+
schema=gql_params.schema,
155+
source=SEARCH_QUERY,
156+
context_value=gql_params.context,
157+
root_value=None,
158+
variable_values={"search": "2001:0db8:0000:0000:0000:0000:0000:0000/48"},
159+
)
160+
161+
assert (
162+
res_extended.data["InfrahubSearchAnywhere"]["count"]
163+
== res_collapsed.data["InfrahubSearchAnywhere"]["count"]
164+
== 1
165+
)
166+
167+
assert (
168+
res_extended.data["InfrahubSearchAnywhere"]["edges"][0]["node"]["id"]
169+
== res_collapsed.data["InfrahubSearchAnywhere"]["edges"][0]["node"]["id"]
170+
)
171+
172+
173+
async def test_search_ipv4(
174+
db: InfrahubDatabase,
175+
ip_dataset_01,
176+
branch: Branch,
177+
):
178+
gql_params = prepare_graphql_params(db=db, include_subscription=False, branch=branch)
179+
180+
result_address = await graphql(
181+
schema=gql_params.schema,
182+
source=SEARCH_QUERY,
183+
context_value=gql_params.context,
184+
root_value=None,
185+
variable_values={"search": "10.0.0.0"},
186+
)
187+
188+
result_network = await graphql(
189+
schema=gql_params.schema,
190+
source=SEARCH_QUERY,
191+
context_value=gql_params.context,
192+
root_value=None,
193+
variable_values={"search": "10.0.0.0/8"},
194+
)
195+
196+
assert (
197+
result_address.data["InfrahubSearchAnywhere"]["count"]
198+
== result_network.data["InfrahubSearchAnywhere"]["count"]
199+
== 1
200+
)
201+
202+
assert (
203+
result_address.data["InfrahubSearchAnywhere"]["edges"][0]["node"]["id"]
204+
== result_network.data["InfrahubSearchAnywhere"]["edges"][0]["node"]["id"]
205+
)

changelog/4613.fixed.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Search anywhere now supports IPv6 extended format

0 commit comments

Comments
 (0)