Skip to content

Commit 40cc3e7

Browse files
committed
fix: allow List attributes to use regex parameter
Add support for List attributes to accept regex validation via parameters.regex syntax. List attributes can now use TextAttributeParameters which includes regex support. Changes: - Added List to parameter class mapping - Created ListAttributeSchema with regex reconciliation - Updated validation to allow TextAttributeParameters for List - Added tests for List with regex parameter Fixes #7717
1 parent c171357 commit 40cc3e7

File tree

3 files changed

+66
-1
lines changed

3 files changed

+66
-1
lines changed

backend/infrahub/core/schema/attribute_parameters.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ def get_attribute_parameters_class_for_kind(kind: str) -> type[AttributeParamete
1616
"NumberPool": NumberPoolParameters,
1717
"Text": TextAttributeParameters,
1818
"TextArea": TextAttributeParameters,
19+
"List": TextAttributeParameters,
1920
"Number": NumberAttributeParameters,
2021
}
2122
return param_classes.get(kind, AttributeParameters)

backend/infrahub/core/schema/attribute_schema.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ def validate_parameters(self) -> Self:
128128
if isinstance(self.parameters, NumberPoolParameters) and not self.kind == "NumberPool":
129129
raise ValueError(f"NumberPoolParameters can't be used as parameters for {self.kind}")
130130

131-
if isinstance(self.parameters, TextAttributeParameters) and self.kind not in ["Text", "TextArea"]:
131+
if isinstance(self.parameters, TextAttributeParameters) and self.kind not in ["Text", "TextArea", "List"]:
132132
raise ValueError(f"TextAttributeParameters can't be used as parameters for {self.kind}")
133133

134134
return self
@@ -269,9 +269,28 @@ class NumberAttributeSchema(AttributeSchema):
269269
)
270270

271271

272+
class ListAttributeSchema(AttributeSchema):
273+
parameters: TextAttributeParameters = Field(
274+
default_factory=TextAttributeParameters,
275+
description="Extra parameters specific to list attributes",
276+
json_schema_extra={"update": UpdateSupport.VALIDATE_CONSTRAINT.value},
277+
)
278+
279+
@model_validator(mode="after")
280+
def reconcile_parameters(self) -> Self:
281+
if self.regex != self.parameters.regex:
282+
final_regex = self.parameters.regex if self.parameters.regex is not None else self.regex
283+
self.regex = self.parameters.regex = final_regex
284+
return self
285+
286+
def get_regex(self) -> str | None:
287+
return self.parameters.regex
288+
289+
272290
attribute_schema_class_by_kind: dict[str, type[AttributeSchema]] = {
273291
"NumberPool": NumberPoolSchema,
274292
"Text": TextAttributeSchema,
275293
"TextArea": TextAttributeSchema,
294+
"List": ListAttributeSchema,
276295
"Number": NumberAttributeSchema,
277296
}

backend/tests/unit/core/schema/test_attribute_parameters.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,3 +260,48 @@ def test_validate_min_max_text_attribute() -> None:
260260
TextAttributeParameters(min_length=10, max_length=5)
261261

262262
assert config.SETTINGS.main.schema_strict_mode
263+
264+
265+
def test_list_attribute_with_regex_parameter() -> None:
266+
node_schema: dict[str, Any] = {
267+
"name": "Node",
268+
"namespace": "Testing",
269+
"attributes": [
270+
{"name": "name", "kind": "Text"},
271+
{
272+
"name": "protocols",
273+
"kind": "List",
274+
"optional": True,
275+
"parameters": {"regex": "ssh|ping|telnet"},
276+
},
277+
],
278+
}
279+
280+
node = NodeSchema(**node_schema)
281+
protocols_attribute = node.get_attribute("protocols")
282+
assert isinstance(protocols_attribute.parameters, TextAttributeParameters)
283+
assert protocols_attribute.parameters.regex == "ssh|ping|telnet"
284+
assert protocols_attribute.get_regex() == "ssh|ping|telnet"
285+
286+
287+
def test_list_attribute_regex_reconciliation() -> None:
288+
"""Test that regex in parameters and at schema level are reconciled."""
289+
node_schema: dict[str, Any] = {
290+
"name": "Node",
291+
"namespace": "Testing",
292+
"attributes": [
293+
{
294+
"name": "protocols",
295+
"kind": "List",
296+
"optional": True,
297+
"parameters": {"regex": "ssh|ping|telnet"},
298+
"regex": None,
299+
},
300+
],
301+
}
302+
303+
node = NodeSchema(**node_schema)
304+
protocols_attribute = node.get_attribute("protocols")
305+
assert isinstance(protocols_attribute.parameters, TextAttributeParameters)
306+
assert protocols_attribute.regex == "ssh|ping|telnet"
307+
assert protocols_attribute.parameters.regex == "ssh|ping|telnet"

0 commit comments

Comments
 (0)