infrahub-server 1.4.5__py3-none-any.whl → 1.4.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -118,6 +118,12 @@ class ValueMatch(DropdownEnum):
118
118
  description="Match against both the current and previous values",
119
119
  color="#5ac8fa",
120
120
  )
121
+ ANY = DropdownChoice(
122
+ name="any",
123
+ label="Any",
124
+ description="Match against any value",
125
+ color="#276cc2",
126
+ )
121
127
 
122
128
 
123
129
  NODES_THAT_TRIGGER_ACTION_RULES_SETUP = [
infrahub/api/query.py CHANGED
@@ -40,7 +40,7 @@ router = APIRouter(prefix="/query")
40
40
 
41
41
 
42
42
  class QueryPayload(BaseModel):
43
- variables: dict[str, str] = Field(default_factory=dict)
43
+ variables: dict[str, Any] = Field(default_factory=dict)
44
44
 
45
45
 
46
46
  async def execute_query(
@@ -48,7 +48,7 @@ async def execute_query(
48
48
  request: Request,
49
49
  branch_params: BranchParams,
50
50
  query_id: str,
51
- params: dict[str, str],
51
+ params: dict[str, Any],
52
52
  update_group: bool,
53
53
  subscribers: list[str],
54
54
  permission_checker: GraphQLQueryPermissionChecker,
@@ -375,6 +375,10 @@ class IPPrefixReconcileQuery(Query):
375
375
  // ------------------
376
376
  MATCH (ip_namespace:%(namespace_kind)s {uuid: $namespace_id})-[r:IS_PART_OF]->(root:Root)
377
377
  WHERE %(branch_filter)s
378
+ ORDER BY r.branch_level DESC, r.from DESC, r.status ASC
379
+ LIMIT 1
380
+ WITH ip_namespace
381
+ WHERE r.status = "active"
378
382
  """ % {"branch_filter": branch_filter, "namespace_kind": self.params["namespace_kind"]}
379
383
  self.add_to_query(namespace_query)
380
384
 
@@ -384,8 +388,13 @@ class IPPrefixReconcileQuery(Query):
384
388
  // ------------------
385
389
  // Get IP Prefix node by UUID
386
390
  // ------------------
387
- MATCH (ip_node:%(ip_kind)s {uuid: $node_uuid})
391
+ OPTIONAL MATCH (ip_node:%(ip_kind)s {uuid: $node_uuid})-[r:IS_PART_OF]->(:Root)
392
+ WHERE %(branch_filter)s
393
+ ORDER BY r.branch_level DESC, r.from DESC, r.status ASC
394
+ LIMIT 1
395
+ WITH ip_namespace, ip_node
388
396
  """ % {
397
+ "branch_filter": branch_filter,
389
398
  "ip_kind": InfrahubKind.IPADDRESS
390
399
  if isinstance(self.ip_value, IPAddressType)
391
400
  else InfrahubKind.IPPREFIX,
@@ -395,17 +404,35 @@ class IPPrefixReconcileQuery(Query):
395
404
  else:
396
405
  get_node_by_prefix_query = """
397
406
  // ------------------
398
- // Get IP Prefix node by prefix value
407
+ // Get IP node with the correct value on this branch
399
408
  // ------------------
400
- OPTIONAL MATCH ip_node_path = (:Root)<-[:IS_PART_OF]-(ip_node:%(ip_kind)s)
401
- -[:HAS_ATTRIBUTE]->(a:Attribute)-[:HAS_VALUE]->(aipn:%(ip_attribute_kind)s),
402
- (ip_namespace)-[:IS_RELATED]-(nsr:Relationship)
403
- -[:IS_RELATED]-(ip_node)
404
- WHERE nsr.name IN ["ip_namespace__ip_prefix", "ip_namespace__ip_address"]
405
- AND aipn.binary_address = $prefix_binary_full
409
+ OPTIONAL MATCH (:Root)<-[r1:IS_PART_OF]-(ip_node:%(ip_kind)s)
410
+ -[r2:HAS_ATTRIBUTE]->(a:Attribute)-[r3:HAS_VALUE]->(aipn:%(ip_attribute_kind)s)
411
+ WHERE aipn.binary_address = $prefix_binary_full
406
412
  AND aipn.prefixlen = $prefixlen
407
413
  AND aipn.version = $ip_version
408
- AND all(r IN relationships(ip_node_path) WHERE (%(branch_filter)s) and r.status = "active")
414
+ AND all(r IN [r1, r2, r3] WHERE (%(branch_filter)s))
415
+ ORDER BY r3.branch_level DESC, r3.from DESC, r3.status ASC,
416
+ r2.branch_level DESC, r2.from DESC, r2.status ASC,
417
+ r1.branch_level DESC, r1.from DESC, r1.status ASC
418
+ LIMIT 1
419
+ WITH ip_namespace, CASE
420
+ WHEN ip_node IS NOT NULL AND r1.status = "active" AND r2.status = "active" AND r3.status = "active" THEN ip_node
421
+ ELSE NULL
422
+ END AS ip_node
423
+ // ------------------
424
+ // Filter to only those that are in the correct namespace
425
+ // ------------------
426
+ OPTIONAL MATCH (ip_namespace)-[r1:IS_RELATED]-(nsr:Relationship)-[r2:IS_RELATED]-(ip_node)
427
+ WHERE nsr.name IN ["ip_namespace__ip_prefix", "ip_namespace__ip_address"]
428
+ AND all(r IN [r1, r2] WHERE (%(branch_filter)s))
429
+ ORDER BY r1.branch_level DESC, r1.from DESC, r1.status ASC,
430
+ r2.branch_level DESC, r2.from DESC, r2.status ASC
431
+ LIMIT 1
432
+ WITH ip_namespace, CASE
433
+ WHEN ip_node IS NOT NULL AND r1.status = "active" AND r2.status = "active" THEN ip_node
434
+ ELSE NULL
435
+ END as ip_node
409
436
  """ % {
410
437
  "branch_filter": branch_filter,
411
438
  "ip_kind": InfrahubKind.IPADDRESS
@@ -425,7 +452,7 @@ class IPPrefixReconcileQuery(Query):
425
452
  OPTIONAL MATCH parent_prefix_path = (ip_node)-[r1:IS_RELATED]->(:Relationship {name: "parent__child"})-[r2:IS_RELATED]->(current_parent:%(ip_prefix_kind)s)
426
453
  WHERE all(r IN relationships(parent_prefix_path) WHERE (%(branch_filter)s))
427
454
  RETURN current_parent, (r1.status = "active" AND r2.status = "active") AS parent_is_active
428
- ORDER BY r1.branch_level DESC, r2.branch_level DESC, r1.from DESC, r2.from DESC
455
+ ORDER BY r1.branch_level DESC, r1.from DESC, r1.status ASC, r2.branch_level DESC, r2.from DESC, r2.status ASC
429
456
  LIMIT 1
430
457
  }
431
458
  WITH ip_namespace, ip_node, CASE WHEN parent_is_active THEN current_parent ELSE NULL END as current_parent
@@ -474,28 +501,66 @@ class IPPrefixReconcileQuery(Query):
474
501
  // Identify the correct parent, if any, for the prefix node
475
502
  // ------------------
476
503
  CALL (ip_namespace) {
477
- OPTIONAL MATCH parent_path = (ip_namespace)-[pr1:IS_RELATED {status: "active"}]-(ns_rel:Relationship {name: "ip_namespace__ip_prefix"})
478
- -[pr2:IS_RELATED {status: "active"}]-(maybe_new_parent:%(ip_prefix_kind)s)
479
- -[har:HAS_ATTRIBUTE]->(:Attribute {name: "prefix"})
480
- -[hvr:HAS_VALUE]->(av:%(ip_prefix_attribute_kind)s)
481
- USING INDEX av:%(ip_prefix_attribute_kind)s(binary_address)
482
- WHERE all(r IN relationships(parent_path) WHERE (%(branch_filter)s))
483
- AND av.version = $ip_version
504
+ // ------------------
505
+ // start with just the AttributeValue vertices b/c we have an index on them
506
+ // ------------------
507
+ OPTIONAL MATCH (av:%(ip_prefix_attribute_kind)s)
508
+ WHERE av.version = $ip_version
484
509
  AND av.binary_address IN $possible_prefix_list
485
510
  AND any(prefix_and_length IN $possible_prefix_and_length_list WHERE av.binary_address = prefix_and_length[0] AND av.prefixlen <= prefix_and_length[1])
511
+ // ------------------
512
+ // now get all the possible IPPrefix nodes for these AttributeValues
513
+ // ------------------
514
+ OPTIONAL MATCH parent_path = (ip_namespace)-[:IS_RELATED]-(:Relationship {name: "ip_namespace__ip_prefix"})
515
+ -[:IS_RELATED]-(maybe_new_parent:%(ip_prefix_kind)s)
516
+ -[:HAS_ATTRIBUTE]->(:Attribute {name: "prefix"})
517
+ -[:HAS_VALUE]->(av:AttributeValue)
518
+ WHERE all(r IN relationships(parent_path) WHERE (%(branch_filter)s))
519
+ RETURN DISTINCT maybe_new_parent
520
+ }
521
+ CALL (ip_namespace, maybe_new_parent) {
522
+ // ------------------
523
+ // filter to only active maybe_new_parent Nodes in the correct namespace
524
+ // ------------------
525
+ OPTIONAL MATCH (ip_namespace)-[r1:IS_RELATED]-(:Relationship {name: "ip_namespace__ip_prefix"})-[r2:IS_RELATED]-(maybe_new_parent)
526
+ WHERE all(r IN [r1, r2] WHERE (%(branch_filter)s))
527
+ WITH maybe_new_parent, r1, r2, r1.status = "active" AND r2.status = "active" AS is_active
528
+ ORDER BY elementId(maybe_new_parent), r1.branch_level DESC, r1.from DESC, r1.status ASC, r2.branch_level DESC, r2.from DESC, r2.status ASC
529
+ WITH maybe_new_parent, head(collect(is_active)) AS is_active
530
+ RETURN is_active = TRUE AS parent_in_namespace
531
+ }
532
+ CALL (maybe_new_parent) {
533
+ // ------------------
534
+ // filter to only active maybe_new_parent Nodes currently linked to one of the allowed AttributeValues
535
+ // ------------------
536
+ OPTIONAL MATCH (maybe_new_parent)-[r1:HAS_ATTRIBUTE]->(:Attribute {name: "prefix"})-[r2:HAS_VALUE]->(av:AttributeValue)
537
+ WHERE all(r IN [r1, r2] WHERE (%(branch_filter)s))
538
+ WITH maybe_new_parent, av, r1, r2, r1.status = "active" AND r2.status = "active" AS is_active
539
+ ORDER BY elementId(maybe_new_parent), r1.branch_level DESC, r1.from DESC, r1.status ASC, r2.branch_level DESC, r2.from DESC, r2.status ASC
540
+ // ------------------
541
+ // get the latest active attribute value for the maybe_new_parent
542
+ // ------------------
543
+ WITH maybe_new_parent, head(collect([av, is_active])) AS av_is_active
486
544
  WITH
487
- maybe_new_parent,
488
- har,
489
- hvr,
490
- av.prefixlen as prefixlen,
491
- (har.status = "active" AND hvr.status = "active") AS is_active,
492
- har.branch_level + hvr.branch_level AS branch_level
493
- ORDER BY branch_level DESC, har.from DESC, hvr.from DESC
494
- WITH maybe_new_parent, prefixlen, is_active
495
- RETURN maybe_new_parent, head(collect(prefixlen)) AS mnp_prefixlen, head(collect(is_active)) AS mnp_is_active
545
+ av_is_active[0] AS av,
546
+ av_is_active[1] AS is_active
547
+ // ------------------
548
+ // return NULL if the value is not allowed or if it is not active
549
+ // ------------------
550
+ WITH av, is_active, (
551
+ av.version = $ip_version
552
+ AND av.binary_address IN $possible_prefix_list
553
+ AND any(prefix_and_length IN $possible_prefix_and_length_list WHERE av.binary_address = prefix_and_length[0] AND av.prefixlen <= prefix_and_length[1])
554
+ ) AS is_allowed_value
555
+ RETURN CASE WHEN is_active = TRUE AND is_allowed_value = TRUE THEN av ELSE NULL END AS allowed_av
496
556
  }
497
- WITH ip_namespace, ip_node, current_parent, current_children, mnp_prefixlen,
498
- CASE WHEN mnp_is_active THEN maybe_new_parent ELSE NULL END AS maybe_new_parent
557
+ // ------------------
558
+ // set inactive maybe_new_parents to NULL
559
+ // ------------------
560
+ WITH ip_namespace, ip_node, current_parent, current_children, allowed_av.prefixlen AS mnp_prefixlen,
561
+ CASE WHEN parent_in_namespace = TRUE AND allowed_av IS NOT NULL
562
+ THEN maybe_new_parent ELSE NULL
563
+ END AS maybe_new_parent
499
564
  WITH ip_namespace, ip_node, current_parent, current_children, maybe_new_parent, mnp_prefixlen
500
565
  ORDER BY ip_node.uuid, mnp_prefixlen DESC
501
566
  WITH ip_namespace, ip_node, current_parent, current_children, head(collect(maybe_new_parent)) as new_parent
@@ -511,19 +576,18 @@ class IPPrefixReconcileQuery(Query):
511
576
  // Identify the correct children, if any, for the prefix node
512
577
  // ------------------
513
578
  CALL (ip_namespace, ip_node) {
579
+ // ------------------
514
580
  // Get ALL possible children for the prefix node
515
- OPTIONAL MATCH child_path = (
516
- (ip_namespace)-[r1:IS_RELATED]
517
- -(ns_rel:Relationship)-[r2:IS_RELATED]
518
- -(maybe_new_child:%(ip_prefix_kind)s|%(ip_address_kind)s)-[har:HAS_ATTRIBUTE]
519
- ->(a:Attribute)-[hvr:HAS_VALUE]
520
- ->(av:%(ip_prefix_attribute_kind)s|%(ip_address_attribute_kind)s)
581
+ // ------------------
582
+ OPTIONAL MATCH (
583
+ (ip_namespace)-[:IS_RELATED]-(ns_rel:Relationship)-[:IS_RELATED]
584
+ -(maybe_new_child:%(ip_prefix_kind)s|%(ip_address_kind)s)-[:HAS_ATTRIBUTE]
585
+ ->(a:Attribute)-[:HAS_VALUE]->(av:%(ip_prefix_attribute_kind)s|%(ip_address_attribute_kind)s)
521
586
  )
522
587
  USING INDEX av:%(ip_prefix_attribute_kind)s(binary_address)
523
588
  USING INDEX av:%(ip_address_attribute_kind)s(binary_address)
524
589
  WHERE $is_prefix // only prefix nodes can have children
525
590
  AND ns_rel.name IN ["ip_namespace__ip_prefix", "ip_namespace__ip_address"]
526
- AND any(child_kind IN [$ip_prefix_kind, $ip_address_kind] WHERE child_kind IN labels(maybe_new_child))
527
591
  AND a.name in ["prefix", "address"]
528
592
  AND (ip_node IS NULL OR maybe_new_child.uuid <> ip_node.uuid)
529
593
  AND (
@@ -532,22 +596,58 @@ class IPPrefixReconcileQuery(Query):
532
596
  )
533
597
  AND av.version = $ip_version
534
598
  AND av.binary_address STARTS WITH $prefix_binary_host
535
- AND all(r IN relationships(child_path) WHERE (%(branch_filter)s))
599
+ RETURN DISTINCT maybe_new_child
600
+ }
601
+ CALL (ip_namespace, maybe_new_child) {
602
+ // ------------------
603
+ // filter to only active maybe_new_child Nodes in the correct namespace
604
+ // ------------------
605
+ OPTIONAL MATCH (ip_namespace)-[r1:IS_RELATED]-(ns_rel:Relationship)-[r2:IS_RELATED]-(maybe_new_child)
606
+ WHERE ns_rel.name IN ["ip_namespace__ip_prefix", "ip_namespace__ip_address"]
607
+ AND all(r IN [r1, r2] WHERE (%(branch_filter)s))
608
+ WITH maybe_new_child, r1, r2, r1.status = "active" AND r2.status = "active" AS is_active
609
+ ORDER BY elementId(maybe_new_child), r1.branch_level DESC, r1.from DESC, r1.status ASC, r2.branch_level DESC, r2.from DESC, r2.status ASC
610
+ WITH maybe_new_child, head(collect(is_active)) AS is_active
611
+ RETURN is_active = TRUE AS child_in_namespace
612
+ }
613
+ CALL (maybe_new_child) {
614
+ // ------------------
615
+ // filter to only active maybe_new_child Nodes currently linked to a possible child AttributeValue
616
+ // ------------------
617
+ OPTIONAL MATCH (maybe_new_child:%(ip_prefix_kind)s|%(ip_address_kind)s)-[r1:HAS_ATTRIBUTE]->(a:Attribute)-[r2:HAS_VALUE]->(av:AttributeValue)
618
+ WHERE a.name in ["prefix", "address"]
619
+ AND all(r IN [r1, r2] WHERE (%(branch_filter)s))
620
+ WITH maybe_new_child, av, r1, r2, r1.status = "active" AND r2.status = "active" AS is_active
621
+ ORDER BY elementId(maybe_new_child), r1.branch_level DESC, r1.from DESC, r1.status ASC, r2.branch_level DESC, r2.from DESC, r2.status ASC
622
+ // ------------------
623
+ // get the latest active attribute value for the maybe_new_child
624
+ // ------------------
625
+ WITH maybe_new_child, head(collect([av, is_active])) AS av_is_active
536
626
  WITH
537
- maybe_new_child,
538
- av AS mnc_attribute,
539
- r1,
540
- r2,
541
- har,
542
- hvr,
543
- all(r in relationships(child_path) WHERE r.status = "active") AS is_active,
544
- reduce(br_lvl = 0, r in relationships(child_path) | br_lvl + r.branch_level) AS branch_level
545
- ORDER BY maybe_new_child.uuid, branch_level DESC, hvr.from DESC, har.from DESC, r2.from DESC, r1.from DESC
546
- WITH maybe_new_child, head(collect([mnc_attribute, is_active])) AS latest_mnc_details
547
- RETURN maybe_new_child, latest_mnc_details[0] AS latest_mnc_attribute, latest_mnc_details[1] AS mnc_is_active
627
+ av_is_active[0] AS av,
628
+ av_is_active[1] AS is_active
629
+ // ------------------
630
+ // return NULL if the value is not allowed or if it is not active
631
+ // ------------------
632
+ WITH av, is_active, (
633
+ (
634
+ ($ip_prefix_kind IN labels(maybe_new_child) AND av.prefixlen > $prefixlen)
635
+ OR ($ip_address_kind IN labels(maybe_new_child) AND av.prefixlen >= $prefixlen)
636
+ )
637
+ AND av.version = $ip_version
638
+ AND av.binary_address STARTS WITH $prefix_binary_host
639
+ ) AS is_allowed_value
640
+ RETURN CASE WHEN is_active = TRUE AND is_allowed_value = TRUE THEN av ELSE NULL END AS latest_mnc_attribute
548
641
  }
549
- WITH ip_namespace, ip_node, current_parent, current_children, new_parent, latest_mnc_attribute,
550
- CASE WHEN mnc_is_active IN [TRUE, NULL] THEN maybe_new_child ELSE NULL END AS maybe_new_child
642
+ // ------------------
643
+ // set inactive/illegal value/wrong namespace maybe_new_children to NULL
644
+ // ------------------
645
+ WITH ip_namespace, ip_node, current_parent, current_children, new_parent,
646
+ CASE
647
+ WHEN child_in_namespace = TRUE AND latest_mnc_attribute IS NOT NULL THEN maybe_new_child
648
+ ELSE NULL
649
+ END AS maybe_new_child,
650
+ CASE WHEN child_in_namespace = TRUE THEN latest_mnc_attribute ELSE NULL END AS latest_mnc_attribute
551
651
  WITH ip_namespace, ip_node, current_parent, current_children, new_parent, collect([maybe_new_child, latest_mnc_attribute]) AS maybe_children_ips
552
652
  WITH ip_namespace, ip_node, current_parent, current_children, new_parent, maybe_children_ips, range(0, size(maybe_children_ips) - 1) AS child_indices
553
653
  UNWIND child_indices as ind
@@ -581,9 +681,9 @@ class IPPrefixReconcileQuery(Query):
581
681
  new_parent,
582
682
  collect(new_child) as new_children
583
683
  """ % {
584
- "branch_filter": branch_filter,
585
684
  "ip_prefix_kind": InfrahubKind.IPPREFIX,
586
685
  "ip_address_kind": InfrahubKind.IPADDRESS,
686
+ "branch_filter": branch_filter,
587
687
  "ip_prefix_attribute_kind": PREFIX_ATTRIBUTE_LABEL,
588
688
  "ip_address_attribute_kind": ADDRESS_ATTRIBUTE_LABEL,
589
689
  }
infrahub_sdk/branch.py CHANGED
@@ -292,14 +292,13 @@ class InfrahubBranchManagerSync(InfraHubBranchManagerBase):
292
292
  },
293
293
  }
294
294
 
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)
295
+ query = Mutation(mutation="BranchCreate", input_data=input_data, query=MUTATION_QUERY_DATA)
297
296
  response = self.client.execute_graphql(query=query.render(), tracker="mutation-branch-create")
298
297
 
299
298
  # Make sure server version is recent enough to support background execution, as previously
300
299
  # using background_execution=True had no effect.
301
300
  if background_execution and "task" in response["BranchCreate"]:
302
- return response["BranchCreate"]["task"]["id"]
301
+ return BranchData(**response["BranchCreate"]["task"]["id"])
303
302
  return BranchData(**response["BranchCreate"]["object"])
304
303
 
305
304
  def delete(self, branch_name: str) -> bool:
infrahub_sdk/client.py CHANGED
@@ -209,7 +209,7 @@ class BaseClient:
209
209
  delete_unused_nodes=delete_unused_nodes,
210
210
  group_type=group_type,
211
211
  group_params=group_params,
212
- branch=branch or self.default_branch,
212
+ branch=branch,
213
213
  )
214
214
 
215
215
  def _graphql_url(
@@ -310,7 +310,8 @@ class InfrahubClient(BaseClient):
310
310
 
311
311
  async def get_user(self) -> dict:
312
312
  """Return user information"""
313
- return await self.execute_graphql(query=QUERY_USER)
313
+ user_info = await self.execute_graphql(query=QUERY_USER)
314
+ return user_info
314
315
 
315
316
  async def get_user_permissions(self) -> dict:
316
317
  """Return user permissions"""
@@ -539,7 +540,6 @@ class InfrahubClient(BaseClient):
539
540
  schema_kind: str,
540
541
  branch: str,
541
542
  prefetch_relationships: bool,
542
- include: list[str] | None,
543
543
  timeout: int | None = None,
544
544
  ) -> ProcessRelationsNode:
545
545
  """Processes InfrahubNode and their Relationships from the GraphQL query response.
@@ -564,12 +564,9 @@ class InfrahubClient(BaseClient):
564
564
  node = await InfrahubNode.from_graphql(client=self, branch=branch, data=item, timeout=timeout)
565
565
  nodes.append(node)
566
566
 
567
- if prefetch_relationships or (include and any(rel in include for rel in node._relationships)):
567
+ if prefetch_relationships:
568
568
  await node._process_relationships(
569
- node_data=item,
570
- branch=branch,
571
- related_nodes=related_nodes,
572
- timeout=timeout,
569
+ node_data=item, branch=branch, related_nodes=related_nodes, timeout=timeout
573
570
  )
574
571
 
575
572
  return ProcessRelationsNode(nodes=nodes, related_nodes=related_nodes)
@@ -819,7 +816,6 @@ class InfrahubClient(BaseClient):
819
816
  branch=branch,
820
817
  prefetch_relationships=prefetch_relationships,
821
818
  timeout=timeout,
822
- include=include,
823
819
  )
824
820
  return response, process_result
825
821
 
@@ -1107,13 +1103,13 @@ class InfrahubClient(BaseClient):
1107
1103
  ) -> dict:
1108
1104
  url = f"{self.address}/api/query/{name}"
1109
1105
  url_params = copy.deepcopy(params or {})
1110
- url_params["branch"] = branch_name or self.default_branch
1111
-
1112
1106
  headers = copy.copy(self.headers or {})
1113
1107
 
1114
1108
  if self.insert_tracker and tracker:
1115
1109
  headers["X-Infrahub-Tracker"] = tracker
1116
1110
 
1111
+ if branch_name:
1112
+ url_params["branch"] = branch_name
1117
1113
  if at:
1118
1114
  url_params["at"] = at
1119
1115
 
@@ -1569,7 +1565,8 @@ class InfrahubClientSync(BaseClient):
1569
1565
 
1570
1566
  def get_user(self) -> dict:
1571
1567
  """Return user information"""
1572
- return self.execute_graphql(query=QUERY_USER)
1568
+ user_info = self.execute_graphql(query=QUERY_USER)
1569
+ return user_info
1573
1570
 
1574
1571
  def get_user_permissions(self) -> dict:
1575
1572
  """Return user permissions"""
@@ -1834,7 +1831,6 @@ class InfrahubClientSync(BaseClient):
1834
1831
  schema_kind: str,
1835
1832
  branch: str,
1836
1833
  prefetch_relationships: bool,
1837
- include: list[str] | None,
1838
1834
  timeout: int | None = None,
1839
1835
  ) -> ProcessRelationsNodeSync:
1840
1836
  """Processes InfrahubNodeSync and their Relationships from the GraphQL query response.
@@ -1859,7 +1855,7 @@ class InfrahubClientSync(BaseClient):
1859
1855
  node = InfrahubNodeSync.from_graphql(client=self, branch=branch, data=item, timeout=timeout)
1860
1856
  nodes.append(node)
1861
1857
 
1862
- if prefetch_relationships or (include and any(rel in include for rel in node._relationships)):
1858
+ if prefetch_relationships:
1863
1859
  node._process_relationships(node_data=item, branch=branch, related_nodes=related_nodes, timeout=timeout)
1864
1860
 
1865
1861
  return ProcessRelationsNodeSync(nodes=nodes, related_nodes=related_nodes)
@@ -1984,7 +1980,6 @@ class InfrahubClientSync(BaseClient):
1984
1980
  branch=branch,
1985
1981
  prefetch_relationships=prefetch_relationships,
1986
1982
  timeout=timeout,
1987
- include=include,
1988
1983
  )
1989
1984
  return response, process_result
1990
1985
 
@@ -2247,13 +2242,13 @@ class InfrahubClientSync(BaseClient):
2247
2242
  ) -> dict:
2248
2243
  url = f"{self.address}/api/query/{name}"
2249
2244
  url_params = copy.deepcopy(params or {})
2250
- url_params["branch"] = branch_name or self.default_branch
2251
-
2252
2245
  headers = copy.copy(self.headers or {})
2253
2246
 
2254
2247
  if self.insert_tracker and tracker:
2255
2248
  headers["X-Infrahub-Tracker"] = tracker
2256
2249
 
2250
+ if branch_name:
2251
+ url_params["branch"] = branch_name
2257
2252
  if at:
2258
2253
  url_params["at"] = at
2259
2254
  if subscribers:
@@ -409,6 +409,7 @@ def info( # noqa: PLR0915
409
409
  _: str = CONFIG_PARAM,
410
410
  ) -> None:
411
411
  """Display the status of the Python SDK."""
412
+
412
413
  info: dict[str, Any] = {
413
414
  "error": None,
414
415
  "status": ":x:",
@@ -416,17 +417,12 @@ def info( # noqa: PLR0915
416
417
  "user_info": {},
417
418
  "groups": {},
418
419
  }
419
- client = initialize_client_sync()
420
- fetch_user_details = bool(client.config.username) or bool(client.config.api_token)
421
-
422
420
  try:
421
+ client = initialize_client_sync()
423
422
  info["infrahub_version"] = client.get_version()
424
-
425
- if fetch_user_details:
426
- info["user_info"] = client.get_user()
427
- info["groups"] = client.get_user_permissions()
428
-
423
+ info["user_info"] = client.get_user()
429
424
  info["status"] = ":white_heavy_check_mark:"
425
+ info["groups"] = client.get_user_permissions()
430
426
  except Exception as e:
431
427
  info["error"] = f"{e!s} ({e.__class__.__name__})"
432
428
 
@@ -473,7 +469,7 @@ def info( # noqa: PLR0915
473
469
  pretty_model = Pretty(client.config.model_dump(), expand_all=True)
474
470
  layout["client_info"].update(Panel(pretty_model, title="Client Info"))
475
471
 
476
- # Infrahub information panel
472
+ # Infrahub information planel
477
473
  infrahub_info = Table(show_header=False, box=None)
478
474
  if info["user_info"]:
479
475
  infrahub_info.add_row("User:", info["user_info"]["AccountProfile"]["display_label"])
@@ -491,8 +487,6 @@ def info( # noqa: PLR0915
491
487
  infrahub_info.add_row("Groups:", "")
492
488
  for group, roles in groups.items():
493
489
  infrahub_info.add_row("", group, ", ".join(roles))
494
- else:
495
- infrahub_info.add_row("User:", "anonymous")
496
490
 
497
491
  layout["infrahub_info"].update(Panel(infrahub_info, title="Infrahub Info"))
498
492
 
infrahub_sdk/graphql.py CHANGED
@@ -8,9 +8,7 @@ from pydantic import BaseModel
8
8
  VARIABLE_TYPE_MAPPING = ((str, "String!"), (int, "Int!"), (float, "Float!"), (bool, "Boolean!"))
9
9
 
10
10
 
11
- def convert_to_graphql_as_string(value: Any, convert_enum: bool = False) -> str: # noqa: PLR0911
12
- if value is None:
13
- return "null"
11
+ def convert_to_graphql_as_string(value: str | bool | list | BaseModel | Enum | Any, convert_enum: bool = False) -> str: # noqa: PLR0911
14
12
  if isinstance(value, str) and value.startswith("$"):
15
13
  return value
16
14
  if isinstance(value, Enum):
infrahub_sdk/node/node.py CHANGED
@@ -234,10 +234,15 @@ class InfrahubNodeBase:
234
234
 
235
235
  rel: RelatedNodeBase | RelationshipManagerBase = getattr(self, item_name)
236
236
 
237
- if rel_schema.cardinality == RelationshipCardinality.ONE and rel_schema.optional and not rel.initialized:
238
- data[item_name] = None
239
- continue
240
-
237
+ # BLOCKED by https://github.com/opsmill/infrahub/issues/330
238
+ # if (
239
+ # item is None
240
+ # and item_name in self._relationships
241
+ # and self._schema.get_relationship(item_name).cardinality == "one"
242
+ # ):
243
+ # data[item_name] = None
244
+ # continue
245
+ # el
241
246
  if rel is None or not rel.initialized:
242
247
  continue
243
248
 
@@ -310,16 +315,7 @@ class InfrahubNodeBase:
310
315
  variables.pop(variable_key)
311
316
 
312
317
  # TODO: I do not feel _great_ about this
313
- # -> I don't even know who you are (but this is not great indeed) -- gmazoyer (quoting Thanos)
314
- original_data_item = original_data.get(item)
315
- original_data_item_is_none = original_data_item is None
316
- if isinstance(original_data_item, dict):
317
- if "node" in original_data_item:
318
- original_data_item_is_none = original_data_item["node"] is None
319
- elif "id" not in original_data_item:
320
- original_data_item_is_none = True
321
-
322
- if item in data and (data_item in ({}, []) or (data_item is None and original_data_item_is_none)):
318
+ if not data_item and data_item != [] and item in data:
323
319
  data.pop(item)
324
320
 
325
321
  def _strip_unmodified(self, data: dict, variables: dict) -> tuple[dict, dict]:
@@ -328,9 +324,7 @@ class InfrahubNodeBase:
328
324
  relationship_property = getattr(self, relationship)
329
325
  if not relationship_property or relationship not in data:
330
326
  continue
331
- if not relationship_property.initialized and (
332
- not isinstance(relationship_property, RelatedNodeBase) or not relationship_property.schema.optional
333
- ):
327
+ if not relationship_property.initialized:
334
328
  data.pop(relationship)
335
329
  elif isinstance(relationship_property, RelationshipManagerBase) and not relationship_property.has_update:
336
330
  data.pop(relationship)
@@ -748,11 +742,12 @@ class InfrahubNode(InfrahubNodeBase):
748
742
  continue
749
743
 
750
744
  peer_data: dict[str, Any] = {}
751
- should_fetch_relationship = prefetch_relationships or (include is not None and rel_name in include)
752
- if rel_schema and should_fetch_relationship:
745
+ if rel_schema and prefetch_relationships:
753
746
  peer_schema = await self._client.schema.get(kind=rel_schema.peer, branch=self._branch)
754
747
  peer_node = InfrahubNode(client=self._client, schema=peer_schema, branch=self._branch)
755
748
  peer_data = await peer_node.generate_query_data_node(
749
+ include=include,
750
+ exclude=exclude,
756
751
  property=property,
757
752
  )
758
753
 
@@ -891,11 +886,7 @@ class InfrahubNode(InfrahubNodeBase):
891
886
  await self._process_mutation_result(mutation_name=mutation_name, response=response, timeout=timeout)
892
887
 
893
888
  async def _process_relationships(
894
- self,
895
- node_data: dict[str, Any],
896
- branch: str,
897
- related_nodes: list[InfrahubNode],
898
- timeout: int | None = None,
889
+ self, node_data: dict[str, Any], branch: str, related_nodes: list[InfrahubNode], timeout: int | None = None
899
890
  ) -> None:
900
891
  """Processes the Relationships of a InfrahubNode and add Related Nodes to a list.
901
892
 
@@ -1372,8 +1363,7 @@ class InfrahubNodeSync(InfrahubNodeBase):
1372
1363
  continue
1373
1364
 
1374
1365
  peer_data: dict[str, Any] = {}
1375
- should_fetch_relationship = prefetch_relationships or (include is not None and rel_name in include)
1376
- if rel_schema and should_fetch_relationship:
1366
+ if rel_schema and prefetch_relationships:
1377
1367
  peer_schema = self._client.schema.get(kind=rel_schema.peer, branch=self._branch)
1378
1368
  peer_node = InfrahubNodeSync(client=self._client, schema=peer_schema, branch=self._branch)
1379
1369
  peer_data = peer_node.generate_query_data_node(include=include, exclude=exclude, property=property)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: infrahub-server
3
- Version: 1.4.5
3
+ Version: 1.4.6
4
4
  Summary: Infrahub is taking a new approach to Infrastructure Management by providing a new generation of datastore to organize and control all the data that defines how an infrastructure should run.
5
5
  License: Apache-2.0
6
6
  Author: OpsMill
@@ -1,6 +1,6 @@
1
1
  infrahub/__init__.py,sha256=OF6hovR3975Zu6-9DOL_Nh_FTgGn8kS_yOfQ-xp-chg,87
2
2
  infrahub/actions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
- infrahub/actions/constants.py,sha256=lNj6Jhe9Sz1LD7mxTpcFu0K9wE0RbYNYzEL5HErMfCI,3645
3
+ infrahub/actions/constants.py,sha256=xrVNdwBwCzG20eXfowV3bia3LW8Kwx-DazS6vALPbEg,3790
4
4
  infrahub/actions/gather.py,sha256=s6RaegkD9zx7WL8ePS6eGam8TlUA3TH38tarLmQeT98,4746
5
5
  infrahub/actions/models.py,sha256=7EVJuGLqEy71sop1XEDLpuAZfLc9ei3hxXPy2FeyY4U,8558
6
6
  infrahub/actions/parsers.py,sha256=L0ZE2gvhSoBZHuHok0wO4UeWo8fyhbnpuMTHHZX_W7c,3997
@@ -21,7 +21,7 @@ infrahub/api/internal.py,sha256=ZlE5BkdGcrmLq1RZOCvv8OBBL7iT7wHKGG9Kqc-TTKg,5339
21
21
  infrahub/api/menu.py,sha256=xp5bj5JXQZA6ZEPWoTSGGSfTXZ1sVmehMxr3VSG7FlQ,1216
22
22
  infrahub/api/oauth2.py,sha256=wFsWrwfyoNBC1JYzbt1nzU-OjjxWPARIBbE_14jzFmI,5493
23
23
  infrahub/api/oidc.py,sha256=3fU-fNOoMkqEzoLuTmlhCVaZvL6M3sAub8RP1_LvCO8,8299
24
- infrahub/api/query.py,sha256=6I95AxNS9O8aIopfObk9hYxlZHawRqESOKjjEDax6-g,7312
24
+ infrahub/api/query.py,sha256=xc5aAY0TLHnswdjiIpvThG974EKGVIXvZCbtdiiZkPw,7312
25
25
  infrahub/api/schema.py,sha256=dUSB51YXaWELZuXYI7UNemd60MJPPBv4m6-MexXOE0k,17450
26
26
  infrahub/api/static/redoc.standalone.js,sha256=77kGx7mVN9EcdER2ZM4gQ-E-ra_N6AZq9QseAeD6kt0,1042008
27
27
  infrahub/api/static/swagger-ui-bundle.js,sha256=wuSp7wgUSDn_R8FCAgY-z-TlnnCk5xVKJr1Q2IDIi6E,1452753
@@ -241,7 +241,7 @@ infrahub/core/query/attribute.py,sha256=xojZIHX-XfXlN_jgM1TQ1Bp4dXr4oLEWlr2A7igT
241
241
  infrahub/core/query/branch.py,sha256=7gj83jDWPWjFUZud7lMQ0xwl9ag3FL-ZOlmY5Kuq7UU,4307
242
242
  infrahub/core/query/delete.py,sha256=7tPP1qtNV6QGYtmgE1RKsuQ9oxENnMTVkttLvJ2PiKg,1927
243
243
  infrahub/core/query/diff.py,sha256=jJCkZRo5jGaf-yPAnQ_5ju6sCnknDK0E7vGpqEnaU_k,36881
244
- infrahub/core/query/ipam.py,sha256=dvP5K34Oj5cA1B42YhJml0fwSsnt-rt_ZWgcun8bFNU,28728
244
+ infrahub/core/query/ipam.py,sha256=dOs_LZr-DONrCPw6t5Ug9mBPn8a-S2NKja3Vr-zIeaM,34523
245
245
  infrahub/core/query/node.py,sha256=OfWS9PfltP89aU4n0KhEjrvAkhAGj9Vl4hcfKcE9LD8,70969
246
246
  infrahub/core/query/relationship.py,sha256=KmS9zrcr-RViXxiITXOjq1t0s-AfsICHk3wyyirZBfA,47817
247
247
  infrahub/core/query/resource_manager.py,sha256=uSvs1WZmdbyt_PjaUi9lXnYdPt-lhJV1RjYoUHYjQdk,16620
@@ -709,9 +709,9 @@ infrahub_sdk/_importer.py,sha256=8oHTMxa_AMO_qbfb3UXNfjSr31S5YJTcqe-YMrixY_E,225
709
709
  infrahub_sdk/analyzer.py,sha256=UDJN372vdAiuAv2TEyPUlsSVoUfZN6obWkIokNNaHbA,4148
710
710
  infrahub_sdk/async_typer.py,sha256=Gj7E8EGdjA-XF404vr9cBt20mmbroQh7N68HXhWYx00,892
711
711
  infrahub_sdk/batch.py,sha256=LRZ_04ic56ll9FBjgXCYrJRDJcwB3wR1yX4grrQutDQ,3795
712
- infrahub_sdk/branch.py,sha256=nj0rAOFSqZiAZ5xjwjwTSXdNSso5s7zm6y_6ts6ieSQ,12791
712
+ infrahub_sdk/branch.py,sha256=hmtoIekQ1uusoJ6yEKlw6vrFMTAHJrXu-YsqqCQC_kc,12716
713
713
  infrahub_sdk/checks.py,sha256=rFHlEY8XEYcjpLCg6gd9a0R8vPnkxNp0OnXk-odsZKY,5707
714
- infrahub_sdk/client.py,sha256=qO5Sd8M1Y5YcZeWXWa6ECExshJS78qajAKpwctZwQas,101899
714
+ infrahub_sdk/client.py,sha256=0llKuTQFKRwtxjsWvavw2brOF5B0gJdxn8iIhxAcQzM,101611
715
715
  infrahub_sdk/config.py,sha256=wnVRkaVO4Nd2IBLRVpLtrC-jjW399mgr1DprohTEzQQ,7936
716
716
  infrahub_sdk/constants.py,sha256=Ca66r09eDzpmMhfFAspKFSehSxOmoflVongP-UuBDc4,138
717
717
  infrahub_sdk/context.py,sha256=QgXZvtUrKolp6ML8TguVK87Wuu-3KyizZVV_N2F4oCw,400
@@ -719,7 +719,7 @@ infrahub_sdk/ctl/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,
719
719
  infrahub_sdk/ctl/branch.py,sha256=GeGDNGNpew93MZblqhG0r45wqSz_p8CcQ9R8zuj_jmg,4742
720
720
  infrahub_sdk/ctl/check.py,sha256=HWsK1rTpGF2VvRBiS5KZrRxXrsAHDXoFS3wJkmq8pik,7895
721
721
  infrahub_sdk/ctl/cli.py,sha256=A9jJKYBo5opzIIyWYf6niyAHhy49V59g6biueMDFbpE,328
722
- infrahub_sdk/ctl/cli_commands.py,sha256=fExd8OiXwzfDXPPq_JC9jPKH58k3o9JK9-yf18te2nU,18753
722
+ infrahub_sdk/ctl/cli_commands.py,sha256=ShgWESs4_DjBK4nttXdVAYrJoTaxOvpEtMwSxft0Rrg,18560
723
723
  infrahub_sdk/ctl/client.py,sha256=6bmXmQta9qQCJ8HybQwt2uSF2X1Em91xNFpwiKFujxs,2083
724
724
  infrahub_sdk/ctl/config.py,sha256=y3kTvfxDO2FKzgvaIXKPKOES7BqXT-s9Kuww7ROfs-4,3039
725
725
  infrahub_sdk/ctl/exceptions.py,sha256=RPdBtIj5qVvNqNR9Y-mPNF7kDUxXUUCac5msyitrBXo,272
@@ -739,13 +739,13 @@ infrahub_sdk/data.py,sha256=4d8Fd1s7lTeOu8JWXsK2m2BM8t_5HG0Z73fnCZGc7Pc,841
739
739
  infrahub_sdk/diff.py,sha256=Ms-3YyXo-DoF1feV9qP7GKakBYUNFsULZdy-yMEG71w,4258
740
740
  infrahub_sdk/exceptions.py,sha256=gZLfZhyDd0M-w5WMzzfPkFwha-aZirJ739N_OMN7Ujs,5728
741
741
  infrahub_sdk/generator.py,sha256=I00G7BdQohJFZ7wQru1SWcwO41gPbuQ3ZGEDVkLIn60,3403
742
- infrahub_sdk/graphql.py,sha256=UQValA_5oEtdm2VC2tuzUnjm4CEj3J0qk79f70cLr84,7024
742
+ infrahub_sdk/graphql.py,sha256=zrxRveg8-t0FbLtOEMDiiW0vqtBHc2qaFRkiHF9Bp6g,7019
743
743
  infrahub_sdk/groups.py,sha256=GL14ByW4GHrkqOLJ-_vGhu6bkYDxljqPtkErcQVehv0,711
744
744
  infrahub_sdk/jinja2.py,sha256=lTfV9E_P5gApaX6RW9M8U8oixQi-0H3U8wcs8fdGVaU,1150
745
745
  infrahub_sdk/node/__init__.py,sha256=clAUZ9lNVPFguelR5Sg9PzklAZruTKEm2xk-BaO68l8,1262
746
746
  infrahub_sdk/node/attribute.py,sha256=oEY1qxip8ETEx9Q33NhSQo013zmzrmpVIFzSkEMUY8M,4547
747
747
  infrahub_sdk/node/constants.py,sha256=TJO4uxvv7sc3FjoLdQdV7Ccymqz8AqxDenARst8awb4,775
748
- infrahub_sdk/node/node.py,sha256=9ZDySD3NLRgu9hDsw71YO_LLUGKsT0JOMy2kOXzvyFU,74201
748
+ infrahub_sdk/node/node.py,sha256=LPl9w9CySrZSEXuTn6jXuSshQCzAPyehc8Kyzpsq5dE,73518
749
749
  infrahub_sdk/node/parsers.py,sha256=sLDdT6neoYSZIjOCmq8Bgd0LK8FFoasjvJLuSz0whSU,543
750
750
  infrahub_sdk/node/property.py,sha256=8Mjkc8bp3kLlHyllwxDJlpJTuOA1ciMgY8mtH3dFVLM,728
751
751
  infrahub_sdk/node/related_node.py,sha256=fPMnZ83OZnnbimaPC14MdE3lR-kumAA6hbOhRlo1gms,10093
@@ -827,8 +827,8 @@ infrahub_testcontainers/models.py,sha256=ASYyvl7d_WQz_i7y8-3iab9hwwmCl3OCJavqVbe
827
827
  infrahub_testcontainers/performance_test.py,sha256=hvwiy6tc_lWniYqGkqfOXVGAmA_IV15VOZqbiD9ezno,6149
828
828
  infrahub_testcontainers/plugin.py,sha256=I3RuZQ0dARyKHuqCf0y1Yj731P2Mwf3BJUehRJKeWrs,5645
829
829
  infrahub_testcontainers/prometheus.yml,sha256=610xQEyj3xuVJMzPkC4m1fRnCrjGpiRBrXA2ytCLa54,599
830
- infrahub_server-1.4.5.dist-info/LICENSE.txt,sha256=7GQO7kxVoQYnZtFrjZBKLRXbrGwwwimHPPOJtqXsozQ,11340
831
- infrahub_server-1.4.5.dist-info/METADATA,sha256=MHlD-kNOoe00t0MsyjiyHHPyfF4WkAneljzGh6VJg90,8277
832
- infrahub_server-1.4.5.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
833
- infrahub_server-1.4.5.dist-info/entry_points.txt,sha256=UXIeFWDsrV-4IllNvUEd6KieYGzQfn9paga2YyABOQI,393
834
- infrahub_server-1.4.5.dist-info/RECORD,,
830
+ infrahub_server-1.4.6.dist-info/LICENSE.txt,sha256=7GQO7kxVoQYnZtFrjZBKLRXbrGwwwimHPPOJtqXsozQ,11340
831
+ infrahub_server-1.4.6.dist-info/METADATA,sha256=O5wCV3t2TuX9o4kj49ujsXIigL20YFpkMBU6-PWY7kE,8277
832
+ infrahub_server-1.4.6.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
833
+ infrahub_server-1.4.6.dist-info/entry_points.txt,sha256=UXIeFWDsrV-4IllNvUEd6KieYGzQfn9paga2YyABOQI,393
834
+ infrahub_server-1.4.6.dist-info/RECORD,,