Skip to content

Commit 828e81c

Browse files
authored
Merge pull request #525 from opsmill/stable
Merge stable into develop
2 parents 82556f1 + f4035f0 commit 828e81c

File tree

11 files changed

+126
-36
lines changed

11 files changed

+126
-36
lines changed

changelog/236.fixed.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Respect default branch for client.query_gql_query() and client.set_context_properties()

changelog/374.fixed.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix branch creation with the sync client while setting `wait_until_completion=False`

changelog/398.fixed.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix `infrahubctl info` command when run as an anonymous user

infrahub_sdk/branch.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -292,13 +292,14 @@ def create(
292292
},
293293
}
294294

295-
query = Mutation(mutation="BranchCreate", input_data=input_data, query=MUTATION_QUERY_DATA)
295+
mutation_query = MUTATION_QUERY_TASK if background_execution else MUTATION_QUERY_DATA
296+
query = Mutation(mutation="BranchCreate", input_data=input_data, query=mutation_query)
296297
response = self.client.execute_graphql(query=query.render(), tracker="mutation-branch-create")
297298

298299
# Make sure server version is recent enough to support background execution, as previously
299300
# using background_execution=True had no effect.
300301
if background_execution and "task" in response["BranchCreate"]:
301-
return BranchData(**response["BranchCreate"]["task"]["id"])
302+
return response["BranchCreate"]["task"]["id"]
302303
return BranchData(**response["BranchCreate"]["object"])
303304

304305
def delete(self, branch_name: str) -> bool:

infrahub_sdk/client.py

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ def set_context_properties(
219219
delete_unused_nodes=delete_unused_nodes,
220220
group_type=group_type,
221221
group_params=group_params,
222-
branch=branch,
222+
branch=branch or self.default_branch,
223223
)
224224

225225
def _graphql_url(
@@ -320,8 +320,7 @@ async def get_version(self) -> str:
320320

321321
async def get_user(self) -> dict:
322322
"""Return user information"""
323-
user_info = await self.execute_graphql(query=QUERY_USER)
324-
return user_info
323+
return await self.execute_graphql(query=QUERY_USER)
325324

326325
async def get_user_permissions(self) -> dict:
327326
"""Return user permissions"""
@@ -550,6 +549,7 @@ async def _process_nodes_and_relationships(
550549
schema_kind: str,
551550
branch: str,
552551
prefetch_relationships: bool,
552+
include: list[str] | None,
553553
timeout: int | None = None,
554554
) -> ProcessRelationsNode:
555555
"""Processes InfrahubNode and their Relationships from the GraphQL query response.
@@ -574,9 +574,12 @@ async def _process_nodes_and_relationships(
574574
node = await InfrahubNode.from_graphql(client=self, branch=branch, data=item, timeout=timeout)
575575
nodes.append(node)
576576

577-
if prefetch_relationships:
577+
if prefetch_relationships or (include and any(rel in include for rel in node._relationships)):
578578
await node._process_relationships(
579-
node_data=item, branch=branch, related_nodes=related_nodes, timeout=timeout
579+
node_data=item,
580+
branch=branch,
581+
related_nodes=related_nodes,
582+
timeout=timeout,
580583
)
581584

582585
return ProcessRelationsNode(nodes=nodes, related_nodes=related_nodes)
@@ -826,6 +829,7 @@ async def process_page(page_offset: int, page_number: int) -> tuple[dict, Proces
826829
branch=branch,
827830
prefetch_relationships=prefetch_relationships,
828831
timeout=timeout,
832+
include=include,
829833
)
830834
return response, process_result
831835

@@ -1119,13 +1123,13 @@ async def query_gql_query(
11191123

11201124
url = f"{self.address}/api/query/{name}"
11211125
url_params = copy.deepcopy(params or {})
1126+
url_params["branch"] = branch_name or self.default_branch
1127+
11221128
headers = copy.copy(self.headers or {})
11231129

11241130
if self.insert_tracker and tracker:
11251131
headers["X-Infrahub-Tracker"] = tracker
11261132

1127-
if branch_name:
1128-
url_params["branch"] = branch_name
11291133
if at:
11301134
url_params["at"] = at
11311135

@@ -1581,8 +1585,7 @@ def get_version(self) -> str:
15811585

15821586
def get_user(self) -> dict:
15831587
"""Return user information"""
1584-
user_info = self.execute_graphql(query=QUERY_USER)
1585-
return user_info
1588+
return self.execute_graphql(query=QUERY_USER)
15861589

15871590
def get_user_permissions(self) -> dict:
15881591
"""Return user permissions"""
@@ -1852,6 +1855,7 @@ def _process_nodes_and_relationships(
18521855
schema_kind: str,
18531856
branch: str,
18541857
prefetch_relationships: bool,
1858+
include: list[str] | None,
18551859
timeout: int | None = None,
18561860
) -> ProcessRelationsNodeSync:
18571861
"""Processes InfrahubNodeSync and their Relationships from the GraphQL query response.
@@ -1876,7 +1880,7 @@ def _process_nodes_and_relationships(
18761880
node = InfrahubNodeSync.from_graphql(client=self, branch=branch, data=item, timeout=timeout)
18771881
nodes.append(node)
18781882

1879-
if prefetch_relationships:
1883+
if prefetch_relationships or (include and any(rel in include for rel in node._relationships)):
18801884
node._process_relationships(node_data=item, branch=branch, related_nodes=related_nodes, timeout=timeout)
18811885

18821886
return ProcessRelationsNodeSync(nodes=nodes, related_nodes=related_nodes)
@@ -2001,6 +2005,7 @@ def process_page(page_offset: int, page_number: int) -> tuple[dict, ProcessRelat
20012005
branch=branch,
20022006
prefetch_relationships=prefetch_relationships,
20032007
timeout=timeout,
2008+
include=include,
20042009
)
20052010
return response, process_result
20062011

@@ -2265,13 +2270,13 @@ def query_gql_query(
22652270

22662271
url = f"{self.address}/api/query/{name}"
22672272
url_params = copy.deepcopy(params or {})
2273+
url_params["branch"] = branch_name or self.default_branch
2274+
22682275
headers = copy.copy(self.headers or {})
22692276

22702277
if self.insert_tracker and tracker:
22712278
headers["X-Infrahub-Tracker"] = tracker
22722279

2273-
if branch_name:
2274-
url_params["branch"] = branch_name
22752280
if at:
22762281
url_params["at"] = at
22772282
if subscribers:

infrahub_sdk/ctl/cli_commands.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -409,20 +409,24 @@ def info( # noqa: PLR0915
409409
_: str = CONFIG_PARAM,
410410
) -> None:
411411
"""Display the status of the Python SDK."""
412-
413412
info: dict[str, Any] = {
414413
"error": None,
415414
"status": ":x:",
416415
"infrahub_version": "N/A",
417416
"user_info": {},
418417
"groups": {},
419418
}
419+
client = initialize_client_sync()
420+
fetch_user_details = bool(client.config.username) or bool(client.config.api_token)
421+
420422
try:
421-
client = initialize_client_sync()
422423
info["infrahub_version"] = client.get_version()
423-
info["user_info"] = client.get_user()
424+
425+
if fetch_user_details:
426+
info["user_info"] = client.get_user()
427+
info["groups"] = client.get_user_permissions()
428+
424429
info["status"] = ":white_heavy_check_mark:"
425-
info["groups"] = client.get_user_permissions()
426430
except Exception as e:
427431
info["error"] = f"{e!s} ({e.__class__.__name__})"
428432

@@ -469,7 +473,7 @@ def info( # noqa: PLR0915
469473
pretty_model = Pretty(client.config.model_dump(), expand_all=True)
470474
layout["client_info"].update(Panel(pretty_model, title="Client Info"))
471475

472-
# Infrahub information planel
476+
# Infrahub information panel
473477
infrahub_info = Table(show_header=False, box=None)
474478
if info["user_info"]:
475479
infrahub_info.add_row("User:", info["user_info"]["AccountProfile"]["display_label"])
@@ -487,6 +491,8 @@ def info( # noqa: PLR0915
487491
infrahub_info.add_row("Groups:", "")
488492
for group, roles in groups.items():
489493
infrahub_info.add_row("", group, ", ".join(roles))
494+
else:
495+
infrahub_info.add_row("User:", "anonymous")
490496

491497
layout["infrahub_info"].update(Panel(infrahub_info, title="Infrahub Info"))
492498

infrahub_sdk/node/node.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -748,12 +748,11 @@ async def generate_query_data_node(
748748
continue
749749

750750
peer_data: dict[str, Any] = {}
751-
if rel_schema and prefetch_relationships:
751+
should_fetch_relationship = prefetch_relationships or (include is not None and rel_name in include)
752+
if rel_schema and should_fetch_relationship:
752753
peer_schema = await self._client.schema.get(kind=rel_schema.peer, branch=self._branch)
753754
peer_node = InfrahubNode(client=self._client, schema=peer_schema, branch=self._branch)
754755
peer_data = await peer_node.generate_query_data_node(
755-
include=include,
756-
exclude=exclude,
757756
property=property,
758757
)
759758

@@ -892,7 +891,11 @@ async def update(
892891
await self._process_mutation_result(mutation_name=mutation_name, response=response, timeout=timeout)
893892

894893
async def _process_relationships(
895-
self, node_data: dict[str, Any], branch: str, related_nodes: list[InfrahubNode], timeout: int | None = None
894+
self,
895+
node_data: dict[str, Any],
896+
branch: str,
897+
related_nodes: list[InfrahubNode],
898+
timeout: int | None = None,
896899
) -> None:
897900
"""Processes the Relationships of a InfrahubNode and add Related Nodes to a list.
898901
@@ -1369,7 +1372,8 @@ def generate_query_data_node(
13691372
continue
13701373

13711374
peer_data: dict[str, Any] = {}
1372-
if rel_schema and prefetch_relationships:
1375+
should_fetch_relationship = prefetch_relationships or (include is not None and rel_name in include)
1376+
if rel_schema and should_fetch_relationship:
13731377
peer_schema = self._client.schema.get(kind=rel_schema.peer, branch=self._branch)
13741378
peer_node = InfrahubNodeSync(client=self._client, schema=peer_schema, branch=self._branch)
13751379
peer_data = peer_node.generate_query_data_node(include=include, exclude=exclude, property=property)

tests/integration/test_node.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,40 @@ async def test_node_create_with_relationships_using_related_node(
8585
assert node_after.owner.peer.id == person_joe.id
8686
assert node_after.owner.peer.typename == "TestingPerson"
8787

88+
async def test_node_filters_include(
89+
self,
90+
default_branch: str,
91+
client: InfrahubClient,
92+
initial_schema: None,
93+
manufacturer_mercedes,
94+
person_joe,
95+
tag_red,
96+
) -> None:
97+
car = await client.create(
98+
kind=TESTING_CAR,
99+
name="Tiguan2",
100+
color="Black",
101+
manufacturer=manufacturer_mercedes,
102+
owner=person_joe,
103+
tags=[tag_red],
104+
)
105+
await car.save(allow_upsert=True)
106+
assert car.id is not None
107+
108+
# Clear store, as when we call `owner.peer`, we actually rely on the peer having being stored in store.
109+
client.store._branches = {}
110+
node_after = await client.get(kind=TESTING_CAR, id=car.id)
111+
112+
with pytest.raises(NodeNotFoundError, match=f"Unable to find the node '{person_joe.id}' in the store"):
113+
_ = node_after.owner.peer
114+
115+
assert len(node_after.tags.peers) == 0
116+
117+
# Test both one and many relationships
118+
node_after = await client.get(kind=TESTING_CAR, id=car.id, include=["tags", "owner"])
119+
assert [tag.id for tag in node_after.tags.peers] == [tag_red.id]
120+
assert node_after.owner.peer.id == person_joe.id, f"{person_joe.id=}"
121+
88122
async def test_node_update_with_original_data(
89123
self,
90124
default_branch: str,

tests/unit/ctl/test_cli.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ def test_version_command() -> None:
3333

3434

3535
def test_info_command_success(mock_query_infrahub_version, mock_query_infrahub_user) -> None:
36-
result = runner.invoke(app, ["info"])
36+
result = runner.invoke(app, ["info"], env={"INFRAHUB_API_TOKEN": "foo"})
3737
assert result.exit_code == 0
3838
for expected in ["Connection Status", "Python Version", "SDK Version", "Infrahub Version"]:
3939
assert expected in result.stdout, f"'{expected}' not found in info command output"
@@ -46,15 +46,16 @@ def test_info_command_failure() -> None:
4646

4747

4848
def test_info_detail_command_success(mock_query_infrahub_version, mock_query_infrahub_user) -> None:
49+
result = runner.invoke(app, ["info", "--detail"], env={"INFRAHUB_API_TOKEN": "foo"})
50+
assert result.exit_code == 0
51+
for expected in ["Connection Status", "Version Information", "Client Info", "Infrahub Info", "Groups:"]:
52+
assert expected in result.stdout, f"'{expected}' not found in detailed info command output"
53+
54+
55+
def test_anonymous_info_detail_command_success(mock_query_infrahub_version) -> None:
4956
result = runner.invoke(app, ["info", "--detail"])
5057
assert result.exit_code == 0
51-
for expected in [
52-
"Connection Status",
53-
"Version Information",
54-
"Client Info",
55-
"Infrahub Info",
56-
"Groups:",
57-
]:
58+
for expected in ["Connection Status", "Version Information", "Client Info", "Infrahub Info", "anonymous"]:
5859
assert expected in result.stdout, f"'{expected}' not found in detailed info command output"
5960

6061

tests/unit/sdk/conftest.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@ async def client() -> InfrahubClient:
3333
return InfrahubClient(config=Config(address="http://mock", insert_tracker=True, pagination_size=3))
3434

3535

36+
@pytest.fixture
37+
async def client_sync() -> InfrahubClientSync:
38+
return InfrahubClientSync(config=Config(address="http://mock", insert_tracker=True, pagination_size=3))
39+
40+
3641
@pytest.fixture
3742
async def clients() -> BothClients:
3843
both = BothClients(

0 commit comments

Comments
 (0)