Skip to content

Commit 329e626

Browse files
minitrigaclaude
andcommitted
Add description support for Python transforms in repository integrator
Thread the optional `description` field through the Python transform pipeline: get_python_transforms → create/compare/update methods. This achieves parity with Jinja2 transform description handling. The SDK-side change (InfrahubPythonTransformConfig.description) is in opsmill/infrahub-sdk-python#887. Closes #6382 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 75b3fc7 commit 329e626

4 files changed

Lines changed: 103 additions & 1 deletion

File tree

backend/infrahub/git/integrator.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,9 @@ class TransformPythonInformation(BaseModel):
135135
..., description="Indicate if the transform should convert the query response to InfrahubNode objects"
136136
)
137137

138+
description: str | None = None
139+
"""Description of the Transform"""
140+
138141

139142
class InfrahubRepositoryIntegrator(InfrahubRepositoryBase):
140143
"""
@@ -1001,6 +1004,7 @@ async def get_python_transforms(
10011004
query=str(graphql_query.id),
10021005
timeout=transform_class.timeout,
10031006
convert_query_response=transform.convert_query_response,
1007+
description=transform.description,
10041008
)
10051009
)
10061010

@@ -1133,6 +1137,7 @@ async def create_python_transform(
11331137
schema = await self.sdk.schema.get(kind=InfrahubKind.TRANSFORMPYTHON, branch=branch_name)
11341138
data = {
11351139
"name": transform.name,
1140+
"description": transform.description,
11361141
"repository": transform.repository,
11371142
"query": transform.query,
11381143
"file_path": transform.file_path,
@@ -1153,6 +1158,9 @@ async def create_python_transform(
11531158
async def update_python_transform(
11541159
self, existing_transform: CoreTransformPython, local_transform: TransformPythonInformation
11551160
) -> None:
1161+
if existing_transform.description.value != local_transform.description:
1162+
existing_transform.description.value = local_transform.description
1163+
11561164
if existing_transform.query.id != local_transform.query:
11571165
existing_transform.query = {"id": local_transform.query, "source": str(self.id), "is_protected": True}
11581166

@@ -1172,7 +1180,8 @@ async def compare_python_transform(
11721180
cls, existing_transform: CoreTransformPython, local_transform: TransformPythonInformation
11731181
) -> bool:
11741182
if (
1175-
existing_transform.query.id != local_transform.query
1183+
existing_transform.description.value != local_transform.description
1184+
or existing_transform.query.id != local_transform.query
11761185
or existing_transform.file_path.value != local_transform.file_path
11771186
or existing_transform.timeout.value != local_transform.timeout
11781187
or existing_transform.convert_query_response.value != local_transform.convert_query_response

backend/tests/fixtures/repos/car-dealership/initial__main/.infrahub.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ jinja2_transforms:
2424

2525
python_transforms:
2626
- name: PersonWithCarsTransform
27+
description: "Transform to generate a JSON report of a person and the cars they own"
2728
class_name: PersonWithCarsTransform
2829
file_path: "transforms/person_with_cars_transform.py"
2930
- name: ConvertedPersonWithCarsTransform

backend/tests/unit/git/__init__.py

Whitespace-only changes.
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
from unittest.mock import MagicMock
2+
3+
import pytest
4+
5+
from infrahub.git.integrator import InfrahubRepositoryIntegrator, TransformPythonInformation
6+
7+
8+
class TestTransformPythonInformation:
9+
def test_description_field_default(self) -> None:
10+
info = TransformPythonInformation(
11+
name="test",
12+
repository="repo-id",
13+
file_path="transforms/test.py",
14+
query="query-id",
15+
class_name="TestTransform",
16+
transform_class=MagicMock(),
17+
timeout=10,
18+
convert_query_response=False,
19+
)
20+
assert info.description is None
21+
22+
def test_description_field_with_value(self) -> None:
23+
info = TransformPythonInformation(
24+
name="test",
25+
repository="repo-id",
26+
file_path="transforms/test.py",
27+
query="query-id",
28+
class_name="TestTransform",
29+
transform_class=MagicMock(),
30+
timeout=10,
31+
convert_query_response=False,
32+
description="A useful transform",
33+
)
34+
assert info.description == "A useful transform"
35+
36+
37+
def _make_mock_transform(
38+
query_id: str = "query-id",
39+
file_path: str = "transforms/test.py",
40+
timeout: int = 10,
41+
convert_query_response: bool = False,
42+
description: str | None = None,
43+
) -> MagicMock:
44+
mock = MagicMock()
45+
mock.query.id = query_id
46+
mock.file_path.value = file_path
47+
mock.timeout.value = timeout
48+
mock.convert_query_response.value = convert_query_response
49+
mock.description.value = description
50+
return mock
51+
52+
53+
def _make_local_transform(**kwargs: object) -> TransformPythonInformation:
54+
defaults: dict[str, object] = {
55+
"name": "test",
56+
"repository": "repo-id",
57+
"file_path": "transforms/test.py",
58+
"query": "query-id",
59+
"class_name": "TestTransform",
60+
"transform_class": MagicMock(),
61+
"timeout": 10,
62+
"convert_query_response": False,
63+
"description": None,
64+
}
65+
defaults.update(kwargs)
66+
return TransformPythonInformation(**defaults) # type: ignore[arg-type]
67+
68+
69+
class TestComparePythonTransform:
70+
@pytest.mark.anyio
71+
async def test_identical_returns_true(self) -> None:
72+
existing = _make_mock_transform()
73+
local = _make_local_transform()
74+
assert await InfrahubRepositoryIntegrator.compare_python_transform(existing, local) is True
75+
76+
@pytest.mark.anyio
77+
async def test_description_changed_returns_false(self) -> None:
78+
existing = _make_mock_transform(description=None)
79+
local = _make_local_transform(description="New description")
80+
assert await InfrahubRepositoryIntegrator.compare_python_transform(existing, local) is False
81+
82+
@pytest.mark.anyio
83+
async def test_description_removed_returns_false(self) -> None:
84+
existing = _make_mock_transform(description="Old description")
85+
local = _make_local_transform(description=None)
86+
assert await InfrahubRepositoryIntegrator.compare_python_transform(existing, local) is False
87+
88+
@pytest.mark.anyio
89+
async def test_description_same_returns_true(self) -> None:
90+
existing = _make_mock_transform(description="Same")
91+
local = _make_local_transform(description="Same")
92+
assert await InfrahubRepositoryIntegrator.compare_python_transform(existing, local) is True

0 commit comments

Comments
 (0)