infrahub-server 1.2.9rc0__py3-none-any.whl → 1.2.10__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.
Files changed (52) hide show
  1. infrahub/computed_attribute/models.py +13 -0
  2. infrahub/computed_attribute/tasks.py +48 -26
  3. infrahub/core/attribute.py +43 -2
  4. infrahub/core/branch/models.py +8 -9
  5. infrahub/core/branch/tasks.py +0 -2
  6. infrahub/core/diff/calculator.py +65 -11
  7. infrahub/core/diff/combiner.py +38 -31
  8. infrahub/core/diff/coordinator.py +44 -28
  9. infrahub/core/diff/data_check_synchronizer.py +3 -2
  10. infrahub/core/diff/enricher/hierarchy.py +36 -27
  11. infrahub/core/diff/ipam_diff_parser.py +5 -4
  12. infrahub/core/diff/merger/merger.py +46 -16
  13. infrahub/core/diff/merger/serializer.py +1 -0
  14. infrahub/core/diff/model/field_specifiers_map.py +64 -0
  15. infrahub/core/diff/model/path.py +58 -58
  16. infrahub/core/diff/parent_node_adder.py +14 -16
  17. infrahub/core/diff/query/drop_nodes.py +42 -0
  18. infrahub/core/diff/query/field_specifiers.py +8 -7
  19. infrahub/core/diff/query/filters.py +15 -1
  20. infrahub/core/diff/query/merge.py +264 -28
  21. infrahub/core/diff/query/save.py +6 -2
  22. infrahub/core/diff/query_parser.py +50 -64
  23. infrahub/core/diff/repository/deserializer.py +38 -24
  24. infrahub/core/diff/repository/repository.py +31 -12
  25. infrahub/core/graph/__init__.py +1 -1
  26. infrahub/core/migrations/graph/__init__.py +2 -0
  27. infrahub/core/migrations/graph/m027_delete_isolated_nodes.py +50 -0
  28. infrahub/core/migrations/graph/m028_delete_diffs.py +38 -0
  29. infrahub/core/query/branch.py +27 -17
  30. infrahub/core/query/diff.py +162 -51
  31. infrahub/core/query/node.py +39 -5
  32. infrahub/core/query/relationship.py +105 -30
  33. infrahub/core/query/subquery.py +2 -2
  34. infrahub/core/relationship/model.py +1 -1
  35. infrahub/core/schema/schema_branch.py +3 -0
  36. infrahub/core/validators/uniqueness/query.py +7 -0
  37. infrahub/graphql/queries/diff/tree.py +2 -1
  38. infrahub/trigger/models.py +11 -1
  39. infrahub/trigger/setup.py +51 -15
  40. infrahub/trigger/tasks.py +1 -4
  41. infrahub/types.py +1 -1
  42. infrahub/webhook/models.py +2 -1
  43. infrahub/workflows/catalogue.py +9 -0
  44. infrahub/workflows/initialization.py +1 -3
  45. infrahub_sdk/timestamp.py +2 -2
  46. {infrahub_server-1.2.9rc0.dist-info → infrahub_server-1.2.10.dist-info}/METADATA +3 -3
  47. {infrahub_server-1.2.9rc0.dist-info → infrahub_server-1.2.10.dist-info}/RECORD +52 -48
  48. infrahub_testcontainers/docker-compose.test.yml +3 -3
  49. infrahub_testcontainers/performance_test.py +6 -3
  50. {infrahub_server-1.2.9rc0.dist-info → infrahub_server-1.2.10.dist-info}/LICENSE.txt +0 -0
  51. {infrahub_server-1.2.9rc0.dist-info → infrahub_server-1.2.10.dist-info}/WHEEL +0 -0
  52. {infrahub_server-1.2.9rc0.dist-info → infrahub_server-1.2.10.dist-info}/entry_points.txt +0 -0
@@ -51,23 +51,33 @@ class DeleteBranchRelationshipsQuery(Query):
51
51
  super().__init__(**kwargs)
52
52
 
53
53
  async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None: # noqa: ARG002
54
- if config.SETTINGS.database.db_type == config.DatabaseType.MEMGRAPH:
55
- query = """
56
- MATCH p = (s)-[r]-(d)
57
- WHERE r.branch = $branch_name
58
- DELETE r
59
- """
60
- else:
61
- query = """
62
- MATCH p = (s)-[r]-(d)
63
- WHERE r.branch = $branch_name
64
- DELETE r
65
- WITH *
66
- UNWIND nodes(p) AS n
67
- MATCH (n)
68
- WHERE NOT exists((n)--())
69
- DELETE n
70
- """
54
+ query = """
55
+ MATCH (s)-[r1]-(d)
56
+ WHERE r1.branch = $branch_name
57
+ DELETE r1
58
+
59
+ WITH collect(DISTINCT s) + collect(DISTINCT d) AS nodes
60
+
61
+ // Collect node IDs for filtering
62
+ WITH nodes, [n in nodes | n.uuid] as nodes_uuids
63
+
64
+ // Also delete agnostic relationships that would not have been deleted above
65
+ MATCH (s2: Node)-[r2]-(d2)
66
+ WHERE NOT exists((s2)-[:IS_PART_OF]-(:Root))
67
+ AND s2.uuid IN nodes_uuids
68
+ DELETE r2
69
+
70
+ WITH nodes, collect(DISTINCT s2) + collect(DISTINCT d2) as additional_nodes
71
+
72
+ WITH nodes + additional_nodes as nodes
73
+
74
+ // Delete nodes that are no longer connected to any other nodes
75
+ UNWIND nodes AS n
76
+ WITH DISTINCT n
77
+ MATCH (n)
78
+ WHERE NOT exists((n)--())
79
+ DELETE n
80
+ """
71
81
  self.params["branch_name"] = self.branch_name
72
82
  self.add_to_query(query)
73
83
 
@@ -1,14 +1,16 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import TYPE_CHECKING, Any
3
+ from dataclasses import dataclass
4
+ from typing import TYPE_CHECKING, Any, Generator
4
5
 
5
6
  from infrahub import config
6
- from infrahub.core.constants import GLOBAL_BRANCH_NAME, BranchSupportType
7
+ from infrahub.core.constants import GLOBAL_BRANCH_NAME, BranchSupportType, DiffAction, RelationshipStatus
7
8
  from infrahub.core.query import Query, QueryType
8
9
  from infrahub.core.timestamp import Timestamp
9
10
 
10
11
  if TYPE_CHECKING:
11
12
  from infrahub.core.branch import Branch
13
+ from infrahub.core.diff.model.field_specifiers_map import NodeFieldSpecifierMap
12
14
  from infrahub.database import InfrahubDatabase
13
15
 
14
16
 
@@ -106,8 +108,8 @@ class DiffCalculationQuery(DiffQuery):
106
108
  self,
107
109
  base_branch: Branch,
108
110
  diff_branch_from_time: Timestamp,
109
- current_node_field_specifiers: dict[str, set[str]] | None = None,
110
- new_node_field_specifiers: dict[str, set[str]] | None = None,
111
+ current_node_field_specifiers: NodeFieldSpecifierMap | None = None,
112
+ new_node_field_specifiers: NodeFieldSpecifierMap | None = None,
111
113
  **kwargs: Any,
112
114
  ):
113
115
  self.base_branch = base_branch
@@ -127,12 +129,13 @@ CALL {
127
129
  // add base branch paths before branched_from, if they exist
128
130
  // -------------------------------------
129
131
  WITH n, attr_rel, r_node, r_prop
132
+ // 'base_n' instead of 'n' here to get previous value for node with a migrated kind/inheritance
130
133
  OPTIONAL MATCH latest_base_path = (:Root)<-[base_r_root:IS_PART_OF {branch: $base_branch_name}]
131
- -(n)-[base_r_node {branch: $base_branch_name}]
134
+ -(base_n {uuid: n.uuid})-[base_r_node {branch: $base_branch_name}]
132
135
  -(attr_rel)-[base_r_prop {branch: $base_branch_name}]->(base_prop)
133
136
  WHERE type(base_r_node) = type(r_node)
134
137
  AND type(base_r_prop) = type(r_prop)
135
- AND [%(id_func)s(n), type(base_r_node)] <> [%(id_func)s(base_prop), type(base_r_prop)]
138
+ AND [%(id_func)s(base_n), type(base_r_node)] <> [%(id_func)s(base_prop), type(base_r_prop)]
136
139
  AND all(
137
140
  r in relationships(latest_base_path)
138
141
  WHERE r.from < $branch_from_time
@@ -142,7 +145,7 @@ CALL {
142
145
  // the migration leaves two nodes with the same UUID linked to the same Relationship
143
146
  // ------------------------
144
147
  AND (
145
- n.uuid IS NULL OR base_prop.uuid IS NULL OR n.uuid <> base_prop.uuid
148
+ base_n.uuid IS NULL OR base_prop.uuid IS NULL OR base_n.uuid <> base_prop.uuid
146
149
  OR type(base_r_node) <> "IS_RELATED" OR type(base_r_prop) <> "IS_RELATED"
147
150
  )
148
151
  WITH latest_base_path, base_r_root, base_r_node, base_r_prop
@@ -231,10 +234,10 @@ class DiffNodePathsQuery(DiffCalculationQuery):
231
234
  self.params.update(params_dict)
232
235
  self.params.update(
233
236
  {
234
- "new_node_ids_list": list(self.new_node_field_specifiers.keys())
237
+ "new_node_ids_list": self.new_node_field_specifiers.get_uuids_list()
235
238
  if self.new_node_field_specifiers
236
239
  else None,
237
- "current_node_ids_list": list(self.current_node_field_specifiers.keys())
240
+ "current_node_ids_list": self.current_node_field_specifiers.get_uuids_list()
238
241
  if self.current_node_field_specifiers
239
242
  else None,
240
243
  }
@@ -276,7 +279,7 @@ WITH p, q, diff_rel, CASE
276
279
  WHEN $new_node_ids_list IS NOT NULL AND p.uuid IN $new_node_ids_list THEN $branch_from_time
277
280
  ELSE $from_time
278
281
  END AS row_from_time
279
- ORDER BY p.uuid DESC
282
+ ORDER BY %(id_func)s(p) DESC
280
283
  SKIP $offset
281
284
  LIMIT $limit
282
285
  // -------------------------------------
@@ -313,15 +316,15 @@ CALL {
313
316
  AND node.branch_support IN [$branch_aware, $branch_agnostic]
314
317
  AND type(r_prop) IN ["IS_VISIBLE", "IS_PROTECTED", "HAS_SOURCE", "HAS_OWNER", "HAS_VALUE", "IS_RELATED"]
315
318
  AND any(l in labels(prop) WHERE l in ["Boolean", "Node", "AttributeValue"])
316
- AND ALL(
317
- r in [r_node, r_prop]
318
- WHERE r.from < $to_time AND r.branch = top_diff_rel.branch
319
- )
320
319
  AND (top_diff_rel.to IS NULL OR top_diff_rel.to >= r_node.from)
321
320
  AND (r_node.to IS NULL OR r_node.to >= r_prop.from)
322
321
  AND [%(id_func)s(p), type(r_node)] <> [%(id_func)s(prop), type(r_prop)]
323
- AND top_diff_rel.status = r_node.status
324
- AND top_diff_rel.status = r_prop.status
322
+ AND r_node.from < $to_time
323
+ AND r_node.branch = top_diff_rel.branch
324
+ AND r_node.status = top_diff_rel.status
325
+ AND r_prop.from < $to_time
326
+ AND r_prop.branch = top_diff_rel.branch
327
+ AND r_prop.status = top_diff_rel.status
325
328
  // ------------------------
326
329
  // special handling for nodes that had their kind updated,
327
330
  // the migration leaves two nodes with the same UUID linked to the same Relationship
@@ -371,15 +374,16 @@ class DiffFieldPathsQuery(DiffCalculationQuery):
371
374
 
372
375
  self.params.update(
373
376
  {
374
- "current_node_field_specifiers_map": {
375
- node_uuid: list(field_names)
376
- for node_uuid, field_names in self.current_node_field_specifiers.items()
377
- }
377
+ "current_node_ids_list": self.current_node_field_specifiers.get_uuids_list()
378
+ if self.current_node_field_specifiers
379
+ else None,
380
+ "new_node_ids_list": self.new_node_field_specifiers.get_uuids_list()
381
+ if self.new_node_field_specifiers
382
+ else None,
383
+ "current_node_field_specifiers_map": self.current_node_field_specifiers.get_uuid_field_names_map()
378
384
  if self.current_node_field_specifiers is not None
379
385
  else None,
380
- "new_node_field_specifiers_map": {
381
- node_uuid: list(field_names) for node_uuid, field_names in self.new_node_field_specifiers.items()
382
- }
386
+ "new_node_field_specifiers_map": self.new_node_field_specifiers.get_uuid_field_names_map()
383
387
  if self.new_node_field_specifiers is not None
384
388
  else None,
385
389
  }
@@ -400,16 +404,16 @@ AND (r_root.to IS NULL OR diff_rel.branch <> r_root.branch OR r_root.to >= diff_
400
404
  // node ID and field name filtering first pass
401
405
  AND (
402
406
  (
403
- $current_node_field_specifiers_map IS NOT NULL
404
- AND $current_node_field_specifiers_map[p.uuid] IS NOT NULL
407
+ $current_node_ids_list IS NOT NULL
408
+ AND p.uuid IN $current_node_ids_list
405
409
  AND q.name IN $current_node_field_specifiers_map[p.uuid]
406
410
  ) OR (
407
- $new_node_field_specifiers_map IS NOT NULL
408
- AND $new_node_field_specifiers_map[p.uuid] IS NOT NULL
411
+ $new_node_ids_list IS NOT NULL
412
+ AND p.uuid IN $new_node_ids_list
409
413
  AND q.name IN $new_node_field_specifiers_map[p.uuid]
410
414
  ) OR (
411
- $current_node_field_specifiers_map IS NULL
412
- AND $new_node_field_specifiers_map IS NULL
415
+ $new_node_ids_list IS NULL
416
+ AND $current_node_ids_list IS NULL
413
417
  )
414
418
  )
415
419
  // node ID and field name filtering second pass
@@ -417,8 +421,12 @@ AND (
417
421
  // time-based filters for nodes already included in the diff or fresh changes
418
422
  (
419
423
  (
420
- ($current_node_field_specifiers_map IS NOT NULL AND q.name IN $current_node_field_specifiers_map[p.uuid])
421
- OR ($current_node_field_specifiers_map IS NULL AND $new_node_field_specifiers_map IS NULL)
424
+ (
425
+ $current_node_ids_list IS NOT NULL
426
+ AND p.uuid IN $current_node_ids_list
427
+ AND q.name IN $current_node_field_specifiers_map[p.uuid]
428
+ )
429
+ OR ($current_node_ids_list IS NULL AND $new_node_ids_list IS NULL)
422
430
  )
423
431
  AND (r_root.from < $from_time OR p.branch_support = $branch_agnostic)
424
432
  AND (
@@ -428,7 +436,11 @@ AND (
428
436
  )
429
437
  // time-based filters for new nodes
430
438
  OR (
431
- ($new_node_field_specifiers_map IS NOT NULL AND q.name IN $new_node_field_specifiers_map[p.uuid])
439
+ (
440
+ $new_node_ids_list IS NOT NULL
441
+ AND p.uuid IN $new_node_ids_list
442
+ AND q.name IN $new_node_field_specifiers_map[p.uuid]
443
+ )
432
444
  AND (r_root.from < $branch_from_time OR p.branch_support = $branch_agnostic)
433
445
  AND (
434
446
  ($branch_from_time <= diff_rel.from < $to_time AND (diff_rel.to IS NULL OR diff_rel.to > $to_time))
@@ -454,7 +466,11 @@ WITH one_result[0] AS root, one_result[1] AS r_root, one_result[2] AS p, one_res
454
466
  // Add correct from_time for row
455
467
  // -------------------------------------
456
468
  WITH root, r_root, p, diff_rel, q, has_more_data, CASE
457
- WHEN $new_node_field_specifiers_map IS NOT NULL AND q.name IN $new_node_field_specifiers_map[p.uuid] THEN $branch_from_time
469
+ WHEN
470
+ $new_node_ids_list IS NOT NULL
471
+ AND p.uuid IN $new_node_ids_list
472
+ AND q.name IN $new_node_field_specifiers_map[p.uuid]
473
+ THEN $branch_from_time
458
474
  ELSE $from_time
459
475
  END AS row_from_time
460
476
  // -------------------------------------
@@ -554,15 +570,16 @@ class DiffPropertyPathsQuery(DiffCalculationQuery):
554
570
 
555
571
  self.params.update(
556
572
  {
557
- "current_node_field_specifiers_map": {
558
- node_uuid: list(field_names)
559
- for node_uuid, field_names in self.current_node_field_specifiers.items()
560
- }
573
+ "current_node_ids_list": self.current_node_field_specifiers.get_uuids_list()
574
+ if self.current_node_field_specifiers
575
+ else None,
576
+ "new_node_ids_list": self.new_node_field_specifiers.get_uuids_list()
577
+ if self.new_node_field_specifiers
578
+ else None,
579
+ "current_node_field_specifiers_map": self.current_node_field_specifiers.get_uuid_field_names_map()
561
580
  if self.current_node_field_specifiers is not None
562
581
  else None,
563
- "new_node_field_specifiers_map": {
564
- node_uuid: list(field_names) for node_uuid, field_names in self.new_node_field_specifiers.items()
565
- }
582
+ "new_node_field_specifiers_map": self.new_node_field_specifiers.get_uuid_field_names_map()
566
583
  if self.new_node_field_specifiers is not None
567
584
  else None,
568
585
  }
@@ -580,16 +597,16 @@ AND type(r_node) IN ["HAS_ATTRIBUTE", "IS_RELATED"]
580
597
  // node ID and field name filtering first pass
581
598
  AND (
582
599
  (
583
- $current_node_field_specifiers_map IS NOT NULL
584
- AND $current_node_field_specifiers_map[n.uuid] IS NOT NULL
600
+ $current_node_ids_list IS NOT NULL
601
+ AND n.uuid IN $current_node_ids_list
585
602
  AND p.name IN $current_node_field_specifiers_map[n.uuid]
586
603
  ) OR (
587
- $new_node_field_specifiers_map IS NOT NULL
588
- AND $new_node_field_specifiers_map[n.uuid] IS NOT NULL
604
+ $new_node_ids_list IS NOT NULL
605
+ AND n.uuid IN $new_node_ids_list
589
606
  AND p.name IN $new_node_field_specifiers_map[n.uuid]
590
607
  ) OR (
591
- $current_node_field_specifiers_map IS NULL
592
- AND $new_node_field_specifiers_map IS NULL
608
+ $new_node_ids_list IS NULL
609
+ AND $current_node_ids_list IS NULL
593
610
  )
594
611
  )
595
612
  // node ID and field name filtering second pass
@@ -597,8 +614,12 @@ AND (
597
614
  // time-based filters for nodes already included in the diff or fresh changes
598
615
  (
599
616
  (
600
- ($current_node_field_specifiers_map IS NOT NULL AND p.name IN $current_node_field_specifiers_map[n.uuid])
601
- OR ($current_node_field_specifiers_map IS NULL AND $new_node_field_specifiers_map IS NULL)
617
+ (
618
+ $current_node_ids_list IS NOT NULL
619
+ AND n.uuid IN $current_node_ids_list
620
+ AND p.name IN $current_node_field_specifiers_map[n.uuid]
621
+ )
622
+ OR ($current_node_ids_list IS NULL AND $new_node_ids_list IS NULL)
602
623
  )
603
624
  AND (
604
625
  ($from_time <= diff_rel.from < $to_time AND (diff_rel.to IS NULL OR diff_rel.to > $to_time))
@@ -612,7 +633,11 @@ AND (
612
633
  )
613
634
  // time-based filters for new nodes
614
635
  OR (
615
- ($new_node_field_specifiers_map IS NOT NULL AND p.name IN $new_node_field_specifiers_map[n.uuid])
636
+ (
637
+ $new_node_ids_list IS NOT NULL
638
+ AND n.uuid IN $new_node_ids_list
639
+ AND p.name IN $new_node_field_specifiers_map[n.uuid]
640
+ )
616
641
  AND (
617
642
  ($branch_from_time <= diff_rel.from < $to_time AND (diff_rel.to IS NULL OR diff_rel.to > $to_time))
618
643
  OR ($branch_from_time <= diff_rel.to < $to_time)
@@ -667,7 +692,11 @@ WITH one_result[0] AS diff_rel_path, one_result[1] AS r_root, one_result[2] AS n
667
692
  // Add correct from_time for row
668
693
  // -------------------------------------
669
694
  WITH diff_rel_path, r_root, n, r_node, p, diff_rel, has_more_data, CASE
670
- WHEN $new_node_field_specifiers_map IS NOT NULL AND p.name IN $new_node_field_specifiers_map[n.uuid] THEN $branch_from_time
695
+ WHEN
696
+ $new_node_ids_list IS NOT NULL
697
+ AND n.uuid IN $new_node_ids_list
698
+ AND p.name IN $new_node_field_specifiers_map[n.uuid]
699
+ THEN $branch_from_time
671
700
  ELSE $from_time
672
701
  END AS row_from_time
673
702
  WITH diff_rel_path, r_root, n, r_node, p, diff_rel, has_more_data, row_from_time
@@ -690,7 +719,7 @@ CALL {
690
719
  CALL {
691
720
  WITH n, row_from_time
692
721
  OPTIONAL MATCH (root:Root)<-[r_root_deleted:IS_PART_OF {branch: $branch_name}]-(n)
693
- WHERE row_from_time <= r_root_deleted.from < $to_time
722
+ WHERE r_root_deleted.from < $to_time
694
723
  WITH r_root_deleted
695
724
  ORDER BY r_root_deleted.status DESC
696
725
  LIMIT 1
@@ -718,3 +747,85 @@ WITH n, p, type(diff_rel) AS drt, head(collect(diff_rel_path)) AS diff_path, has
718
747
  self.add_to_query(self.get_relationship_peer_side_query(db=db))
719
748
  self.add_to_query("UNWIND diff_rel_paths AS diff_path")
720
749
  self.return_labels = ["DISTINCT diff_path AS diff_path", "has_more_data"]
750
+
751
+
752
+ @dataclass
753
+ class MigratedKindNode:
754
+ uuid: str
755
+ kind: str
756
+ db_id: str
757
+ from_time: Timestamp
758
+ action: DiffAction
759
+ has_more_data: bool
760
+
761
+
762
+ class DiffMigratedKindNodesQuery(DiffCalculationQuery):
763
+ name = "diff_migrated_kind_nodes_query"
764
+
765
+ async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None: # noqa: ARG002
766
+ params_dict = self.get_params()
767
+ self.params.update(params_dict)
768
+ migrated_kind_nodes_query = """
769
+ // -------------------------------------
770
+ // Identify nodes added/removed on branch in the time frame
771
+ // -------------------------------------
772
+ MATCH (:Root)<-[diff_rel:IS_PART_OF {branch: $branch_name}]-(n:Node)
773
+ WHERE (
774
+ ($from_time <= diff_rel.from < $to_time AND (diff_rel.to IS NULL OR diff_rel.to > $to_time))
775
+ OR ($from_time <= diff_rel.to < $to_time)
776
+ )
777
+ AND n.branch_support = $branch_aware
778
+ WITH DISTINCT n.uuid AS node_uuid, %(id_func)s(n) AS db_id
779
+ WITH node_uuid, count(*) AS num_nodes_with_uuid
780
+ WHERE num_nodes_with_uuid > 1
781
+ // -------------------------------------
782
+ // Limit the number of nodes
783
+ // -------------------------------------
784
+ WITH node_uuid
785
+ ORDER BY node_uuid
786
+ SKIP $offset
787
+ LIMIT $limit
788
+ WITH collect(node_uuid) AS node_uuids
789
+ WITH node_uuids, size(node_uuids) = $limit AS has_more_data
790
+ MATCH (:Root)<-[diff_rel:IS_PART_OF {branch: $branch_name}]-(n:Node)
791
+ WHERE n.uuid IN node_uuids
792
+ AND (
793
+ ($from_time <= diff_rel.from < $to_time AND (diff_rel.to IS NULL OR diff_rel.to > $to_time))
794
+ OR ($from_time <= diff_rel.to < $to_time)
795
+ )
796
+ // -------------------------------------
797
+ // Ignore node created and deleted on this branch
798
+ // -------------------------------------
799
+ CALL {
800
+ WITH n
801
+ OPTIONAL MATCH (:Root)<-[diff_rel:IS_PART_OF {branch: $branch_name}]-(n)
802
+ WITH diff_rel
803
+ ORDER BY diff_rel.from ASC
804
+ WITH collect(diff_rel.status) AS statuses
805
+ RETURN statuses = ["active", "deleted"] AS intra_branch_update
806
+ }
807
+ WITH n.uuid AS uuid, n.kind AS kind, %(id_func)s(n) AS db_id, diff_rel.from_time AS from_time, diff_rel.status AS status, has_more_data
808
+ WHERE intra_branch_update = FALSE
809
+ """ % {"id_func": db.get_id_function_name()}
810
+ self.add_to_query(query=migrated_kind_nodes_query)
811
+ self.return_labels = [
812
+ "uuid",
813
+ "kind",
814
+ "db_id",
815
+ "from_time",
816
+ "status",
817
+ "has_more_data",
818
+ ]
819
+
820
+ def get_migrated_kind_nodes(self) -> Generator[MigratedKindNode, None, None]:
821
+ for result in self.get_results():
822
+ yield MigratedKindNode(
823
+ uuid=result.get_as_type("uuid", return_type=str),
824
+ kind=result.get_as_type("kind", return_type=str),
825
+ db_id=result.get_as_type("db_id", return_type=str),
826
+ from_time=result.get_as_type("from_time", return_type=Timestamp),
827
+ action=DiffAction.REMOVED
828
+ if result.get_as_type("status", return_type=str).lower() == RelationshipStatus.DELETED.value
829
+ else DiffAction.ADDED,
830
+ has_more_data=result.get_as_type("has_more_data", bool),
831
+ )
@@ -92,6 +92,7 @@ class NodeAttributesFromDB:
92
92
  class PeerInfo:
93
93
  uuid: str
94
94
  kind: str
95
+ db_id: str
95
96
 
96
97
 
97
98
  class NodeQuery(Query):
@@ -412,9 +413,32 @@ class NodeDeleteQuery(NodeQuery):
412
413
  self.params["branch"] = self.branch.name
413
414
  self.params["branch_level"] = self.branch.hierarchy_level
414
415
 
416
+ if self.branch.is_global or self.branch.is_default:
417
+ node_query_match = """
418
+ MATCH (n:Node { uuid: $uuid })
419
+ OPTIONAL MATCH (n)-[delete_edge:IS_PART_OF {status: "deleted", branch: $branch}]->(:Root)
420
+ WHERE delete_edge.from <= $at
421
+ WITH n WHERE delete_edge IS NULL
422
+ """
423
+ else:
424
+ node_filter, node_filter_params = self.branch.get_query_filter_path(at=self.at, variable_name="r")
425
+ node_query_match = """
426
+ MATCH (n:Node { uuid: $uuid })
427
+ CALL {
428
+ WITH n
429
+ MATCH (n)-[r:IS_PART_OF]->(:Root)
430
+ WHERE %(node_filter)s
431
+ RETURN r.status = "active" AS is_active
432
+ ORDER BY r.from DESC
433
+ LIMIT 1
434
+ }
435
+ WITH n WHERE is_active = TRUE
436
+ """ % {"node_filter": node_filter}
437
+ self.params.update(node_filter_params)
438
+ self.add_to_query(node_query_match)
439
+
415
440
  query = """
416
441
  MATCH (root:Root)
417
- MATCH (n:Node { uuid: $uuid })
418
442
  CREATE (n)-[r:IS_PART_OF { branch: $branch, branch_level: $branch_level, status: "deleted", from: $at }]->(root)
419
443
  """
420
444
 
@@ -1405,7 +1429,7 @@ class NodeGetHierarchyQuery(Query):
1405
1429
 
1406
1430
  super().__init__(**kwargs)
1407
1431
 
1408
- async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None: # noqa: ARG002
1432
+ async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None: # noqa: ARG002,PLR0915
1409
1433
  hierarchy_schema = self.node_schema.get_hierarchy_schema(db=db, branch=self.branch)
1410
1434
  branch_filter, branch_params = self.branch.get_query_filter_path(at=self.at.to_string())
1411
1435
  self.params.update(branch_params)
@@ -1438,6 +1462,10 @@ class NodeGetHierarchyQuery(Query):
1438
1462
  UNWIND peers_with_duplicates AS pwd
1439
1463
  RETURN DISTINCT pwd AS peer
1440
1464
  }
1465
+ """ % {"filter": filter_str, "branch_filter": branch_filter}
1466
+
1467
+ if not self.branch.is_default:
1468
+ query += """
1441
1469
  CALL {
1442
1470
  WITH n, peer
1443
1471
  MATCH path = (n)%(filter)s(peer)
@@ -1448,10 +1476,14 @@ class NodeGetHierarchyQuery(Query):
1448
1476
  LIMIT 1
1449
1477
  }
1450
1478
  WITH peer1 as peer, is_active
1451
- """ % {"filter": filter_str, "branch_filter": branch_filter, "with_clause": with_clause}
1479
+ """ % {"filter": filter_str, "branch_filter": branch_filter, "with_clause": with_clause}
1480
+ else:
1481
+ query += """
1482
+ WITH peer
1483
+ """
1452
1484
 
1453
1485
  self.add_to_query(query)
1454
- where_clause = ["is_active = TRUE"]
1486
+ where_clause = ["is_active = TRUE"] if not self.branch.is_default else []
1455
1487
 
1456
1488
  clean_filters = extract_field_filters(field_name=self.direction.value, filters=self.filters)
1457
1489
 
@@ -1461,7 +1493,8 @@ class NodeGetHierarchyQuery(Query):
1461
1493
  if clean_filters.get("id", None):
1462
1494
  self.params["peer_ids"].append(clean_filters.get("id"))
1463
1495
 
1464
- self.add_to_query("WHERE " + " AND ".join(where_clause))
1496
+ if where_clause:
1497
+ self.add_to_query("WHERE " + " AND ".join(where_clause))
1465
1498
 
1466
1499
  self.return_labels = ["peer"]
1467
1500
 
@@ -1544,4 +1577,5 @@ class NodeGetHierarchyQuery(Query):
1544
1577
  yield PeerInfo(
1545
1578
  uuid=peer_node.get("uuid"),
1546
1579
  kind=peer_node.get("kind"),
1580
+ db_id=peer_node.element_id,
1547
1581
  )