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.
- infrahub/computed_attribute/models.py +13 -0
- infrahub/computed_attribute/tasks.py +48 -26
- infrahub/core/attribute.py +43 -2
- infrahub/core/branch/models.py +8 -9
- infrahub/core/branch/tasks.py +0 -2
- infrahub/core/diff/calculator.py +65 -11
- infrahub/core/diff/combiner.py +38 -31
- infrahub/core/diff/coordinator.py +44 -28
- infrahub/core/diff/data_check_synchronizer.py +3 -2
- infrahub/core/diff/enricher/hierarchy.py +36 -27
- infrahub/core/diff/ipam_diff_parser.py +5 -4
- infrahub/core/diff/merger/merger.py +46 -16
- infrahub/core/diff/merger/serializer.py +1 -0
- infrahub/core/diff/model/field_specifiers_map.py +64 -0
- infrahub/core/diff/model/path.py +58 -58
- infrahub/core/diff/parent_node_adder.py +14 -16
- infrahub/core/diff/query/drop_nodes.py +42 -0
- infrahub/core/diff/query/field_specifiers.py +8 -7
- infrahub/core/diff/query/filters.py +15 -1
- infrahub/core/diff/query/merge.py +264 -28
- infrahub/core/diff/query/save.py +6 -2
- infrahub/core/diff/query_parser.py +50 -64
- infrahub/core/diff/repository/deserializer.py +38 -24
- infrahub/core/diff/repository/repository.py +31 -12
- infrahub/core/graph/__init__.py +1 -1
- infrahub/core/migrations/graph/__init__.py +2 -0
- infrahub/core/migrations/graph/m027_delete_isolated_nodes.py +50 -0
- infrahub/core/migrations/graph/m028_delete_diffs.py +38 -0
- infrahub/core/query/branch.py +27 -17
- infrahub/core/query/diff.py +162 -51
- infrahub/core/query/node.py +39 -5
- infrahub/core/query/relationship.py +105 -30
- infrahub/core/query/subquery.py +2 -2
- infrahub/core/relationship/model.py +1 -1
- infrahub/core/schema/schema_branch.py +3 -0
- infrahub/core/validators/uniqueness/query.py +7 -0
- infrahub/graphql/queries/diff/tree.py +2 -1
- infrahub/trigger/models.py +11 -1
- infrahub/trigger/setup.py +51 -15
- infrahub/trigger/tasks.py +1 -4
- infrahub/types.py +1 -1
- infrahub/webhook/models.py +2 -1
- infrahub/workflows/catalogue.py +9 -0
- infrahub/workflows/initialization.py +1 -3
- infrahub_sdk/timestamp.py +2 -2
- {infrahub_server-1.2.9rc0.dist-info → infrahub_server-1.2.10.dist-info}/METADATA +3 -3
- {infrahub_server-1.2.9rc0.dist-info → infrahub_server-1.2.10.dist-info}/RECORD +52 -48
- infrahub_testcontainers/docker-compose.test.yml +3 -3
- infrahub_testcontainers/performance_test.py +6 -3
- {infrahub_server-1.2.9rc0.dist-info → infrahub_server-1.2.10.dist-info}/LICENSE.txt +0 -0
- {infrahub_server-1.2.9rc0.dist-info → infrahub_server-1.2.10.dist-info}/WHEEL +0 -0
- {infrahub_server-1.2.9rc0.dist-info → infrahub_server-1.2.10.dist-info}/entry_points.txt +0 -0
infrahub/core/query/branch.py
CHANGED
|
@@ -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
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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
|
|
infrahub/core/query/diff.py
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from
|
|
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:
|
|
110
|
-
new_node_field_specifiers:
|
|
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(
|
|
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
|
-
|
|
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":
|
|
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":
|
|
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
|
|
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
|
|
324
|
-
AND
|
|
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
|
-
"
|
|
375
|
-
|
|
376
|
-
|
|
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
|
-
$
|
|
404
|
-
AND
|
|
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
|
-
$
|
|
408
|
-
AND
|
|
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
|
-
$
|
|
412
|
-
AND $
|
|
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
|
-
(
|
|
421
|
-
|
|
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
|
-
(
|
|
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
|
|
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
|
-
"
|
|
558
|
-
|
|
559
|
-
|
|
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
|
-
$
|
|
584
|
-
AND
|
|
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
|
-
$
|
|
588
|
-
AND
|
|
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
|
-
$
|
|
592
|
-
AND $
|
|
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
|
-
(
|
|
601
|
-
|
|
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
|
-
(
|
|
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
|
|
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
|
|
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
|
+
)
|
infrahub/core/query/node.py
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
)
|