infrahub-server 1.2.8__py3-none-any.whl → 1.2.9__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/cli/db.py +14 -5
- infrahub/computed_attribute/tasks.py +33 -12
- infrahub/core/branch/tasks.py +0 -2
- infrahub/core/diff/calculator.py +4 -3
- infrahub/core/diff/combiner.py +1 -2
- infrahub/core/diff/coordinator.py +44 -28
- infrahub/core/diff/data_check_synchronizer.py +3 -2
- infrahub/core/diff/enricher/hierarchy.py +38 -27
- infrahub/core/diff/ipam_diff_parser.py +5 -4
- infrahub/core/diff/merger/merger.py +20 -18
- infrahub/core/diff/model/field_specifiers_map.py +64 -0
- infrahub/core/diff/model/path.py +55 -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/save.py +3 -0
- infrahub/core/diff/query_parser.py +49 -52
- infrahub/core/diff/repository/deserializer.py +36 -23
- infrahub/core/diff/repository/repository.py +31 -12
- infrahub/core/graph/__init__.py +1 -1
- infrahub/core/initialization.py +23 -7
- infrahub/core/migrations/graph/__init__.py +2 -0
- infrahub/core/migrations/graph/m014_remove_index_attr_value.py +4 -2
- infrahub/core/migrations/graph/m027_delete_isolated_nodes.py +50 -0
- infrahub/core/query/branch.py +27 -17
- infrahub/core/query/diff.py +65 -38
- infrahub/core/query/node.py +15 -4
- infrahub/core/query/relationship.py +17 -3
- infrahub/core/query/subquery.py +2 -2
- infrahub/core/schema/schema_branch.py +3 -0
- infrahub/core/validators/uniqueness/query.py +24 -8
- infrahub/database/__init__.py +0 -7
- infrahub/database/memgraph.py +0 -12
- infrahub/database/neo4j.py +0 -12
- infrahub/graphql/mutations/computed_attribute.py +5 -1
- infrahub/graphql/queries/diff/tree.py +2 -1
- infrahub/server.py +1 -5
- infrahub/trigger/models.py +11 -1
- infrahub/trigger/setup.py +38 -13
- infrahub/trigger/tasks.py +1 -4
- infrahub/workflows/initialization.py +1 -3
- {infrahub_server-1.2.8.dist-info → infrahub_server-1.2.9.dist-info}/METADATA +1 -1
- {infrahub_server-1.2.8.dist-info → infrahub_server-1.2.9.dist-info}/RECORD +49 -47
- infrahub_testcontainers/container.py +1 -0
- infrahub_testcontainers/docker-compose.test.yml +3 -1
- infrahub/database/manager.py +0 -15
- {infrahub_server-1.2.8.dist-info → infrahub_server-1.2.9.dist-info}/LICENSE.txt +0 -0
- {infrahub_server-1.2.8.dist-info → infrahub_server-1.2.9.dist-info}/WHEEL +0 -0
- {infrahub_server-1.2.8.dist-info → infrahub_server-1.2.9.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
|
@@ -9,6 +9,7 @@ from infrahub.core.timestamp import Timestamp
|
|
|
9
9
|
|
|
10
10
|
if TYPE_CHECKING:
|
|
11
11
|
from infrahub.core.branch import Branch
|
|
12
|
+
from infrahub.core.diff.model.field_specifiers_map import NodeFieldSpecifierMap
|
|
12
13
|
from infrahub.database import InfrahubDatabase
|
|
13
14
|
|
|
14
15
|
|
|
@@ -106,8 +107,8 @@ class DiffCalculationQuery(DiffQuery):
|
|
|
106
107
|
self,
|
|
107
108
|
base_branch: Branch,
|
|
108
109
|
diff_branch_from_time: Timestamp,
|
|
109
|
-
current_node_field_specifiers:
|
|
110
|
-
new_node_field_specifiers:
|
|
110
|
+
current_node_field_specifiers: NodeFieldSpecifierMap | None = None,
|
|
111
|
+
new_node_field_specifiers: NodeFieldSpecifierMap | None = None,
|
|
111
112
|
**kwargs: Any,
|
|
112
113
|
):
|
|
113
114
|
self.base_branch = base_branch
|
|
@@ -231,10 +232,10 @@ class DiffNodePathsQuery(DiffCalculationQuery):
|
|
|
231
232
|
self.params.update(params_dict)
|
|
232
233
|
self.params.update(
|
|
233
234
|
{
|
|
234
|
-
"new_node_ids_list":
|
|
235
|
+
"new_node_ids_list": self.new_node_field_specifiers.get_uuids_list()
|
|
235
236
|
if self.new_node_field_specifiers
|
|
236
237
|
else None,
|
|
237
|
-
"current_node_ids_list":
|
|
238
|
+
"current_node_ids_list": self.current_node_field_specifiers.get_uuids_list()
|
|
238
239
|
if self.current_node_field_specifiers
|
|
239
240
|
else None,
|
|
240
241
|
}
|
|
@@ -371,15 +372,16 @@ class DiffFieldPathsQuery(DiffCalculationQuery):
|
|
|
371
372
|
|
|
372
373
|
self.params.update(
|
|
373
374
|
{
|
|
374
|
-
"
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
375
|
+
"current_node_ids_list": self.current_node_field_specifiers.get_uuids_list()
|
|
376
|
+
if self.current_node_field_specifiers
|
|
377
|
+
else None,
|
|
378
|
+
"new_node_ids_list": self.new_node_field_specifiers.get_uuids_list()
|
|
379
|
+
if self.new_node_field_specifiers
|
|
380
|
+
else None,
|
|
381
|
+
"current_node_field_specifiers_map": self.current_node_field_specifiers.get_uuid_field_names_map()
|
|
378
382
|
if self.current_node_field_specifiers is not None
|
|
379
383
|
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
|
-
}
|
|
384
|
+
"new_node_field_specifiers_map": self.new_node_field_specifiers.get_uuid_field_names_map()
|
|
383
385
|
if self.new_node_field_specifiers is not None
|
|
384
386
|
else None,
|
|
385
387
|
}
|
|
@@ -400,16 +402,16 @@ AND (r_root.to IS NULL OR diff_rel.branch <> r_root.branch OR r_root.to >= diff_
|
|
|
400
402
|
// node ID and field name filtering first pass
|
|
401
403
|
AND (
|
|
402
404
|
(
|
|
403
|
-
$
|
|
404
|
-
AND
|
|
405
|
+
$current_node_ids_list IS NOT NULL
|
|
406
|
+
AND p.uuid IN $current_node_ids_list
|
|
405
407
|
AND q.name IN $current_node_field_specifiers_map[p.uuid]
|
|
406
408
|
) OR (
|
|
407
|
-
$
|
|
408
|
-
AND
|
|
409
|
+
$new_node_ids_list IS NOT NULL
|
|
410
|
+
AND p.uuid IN $new_node_ids_list
|
|
409
411
|
AND q.name IN $new_node_field_specifiers_map[p.uuid]
|
|
410
412
|
) OR (
|
|
411
|
-
$
|
|
412
|
-
AND $
|
|
413
|
+
$new_node_ids_list IS NULL
|
|
414
|
+
AND $current_node_ids_list IS NULL
|
|
413
415
|
)
|
|
414
416
|
)
|
|
415
417
|
// node ID and field name filtering second pass
|
|
@@ -417,8 +419,12 @@ AND (
|
|
|
417
419
|
// time-based filters for nodes already included in the diff or fresh changes
|
|
418
420
|
(
|
|
419
421
|
(
|
|
420
|
-
(
|
|
421
|
-
|
|
422
|
+
(
|
|
423
|
+
$current_node_ids_list IS NOT NULL
|
|
424
|
+
AND p.uuid IN $current_node_ids_list
|
|
425
|
+
AND q.name IN $current_node_field_specifiers_map[p.uuid]
|
|
426
|
+
)
|
|
427
|
+
OR ($current_node_ids_list IS NULL AND $new_node_ids_list IS NULL)
|
|
422
428
|
)
|
|
423
429
|
AND (r_root.from < $from_time OR p.branch_support = $branch_agnostic)
|
|
424
430
|
AND (
|
|
@@ -428,7 +434,11 @@ AND (
|
|
|
428
434
|
)
|
|
429
435
|
// time-based filters for new nodes
|
|
430
436
|
OR (
|
|
431
|
-
(
|
|
437
|
+
(
|
|
438
|
+
$new_node_ids_list IS NOT NULL
|
|
439
|
+
AND p.uuid IN $new_node_ids_list
|
|
440
|
+
AND q.name IN $new_node_field_specifiers_map[p.uuid]
|
|
441
|
+
)
|
|
432
442
|
AND (r_root.from < $branch_from_time OR p.branch_support = $branch_agnostic)
|
|
433
443
|
AND (
|
|
434
444
|
($branch_from_time <= diff_rel.from < $to_time AND (diff_rel.to IS NULL OR diff_rel.to > $to_time))
|
|
@@ -454,7 +464,11 @@ WITH one_result[0] AS root, one_result[1] AS r_root, one_result[2] AS p, one_res
|
|
|
454
464
|
// Add correct from_time for row
|
|
455
465
|
// -------------------------------------
|
|
456
466
|
WITH root, r_root, p, diff_rel, q, has_more_data, CASE
|
|
457
|
-
WHEN
|
|
467
|
+
WHEN
|
|
468
|
+
$new_node_ids_list IS NOT NULL
|
|
469
|
+
AND p.uuid IN $new_node_ids_list
|
|
470
|
+
AND q.name IN $new_node_field_specifiers_map[p.uuid]
|
|
471
|
+
THEN $branch_from_time
|
|
458
472
|
ELSE $from_time
|
|
459
473
|
END AS row_from_time
|
|
460
474
|
// -------------------------------------
|
|
@@ -554,15 +568,16 @@ class DiffPropertyPathsQuery(DiffCalculationQuery):
|
|
|
554
568
|
|
|
555
569
|
self.params.update(
|
|
556
570
|
{
|
|
557
|
-
"
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
571
|
+
"current_node_ids_list": self.current_node_field_specifiers.get_uuids_list()
|
|
572
|
+
if self.current_node_field_specifiers
|
|
573
|
+
else None,
|
|
574
|
+
"new_node_ids_list": self.new_node_field_specifiers.get_uuids_list()
|
|
575
|
+
if self.new_node_field_specifiers
|
|
576
|
+
else None,
|
|
577
|
+
"current_node_field_specifiers_map": self.current_node_field_specifiers.get_uuid_field_names_map()
|
|
561
578
|
if self.current_node_field_specifiers is not None
|
|
562
579
|
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
|
-
}
|
|
580
|
+
"new_node_field_specifiers_map": self.new_node_field_specifiers.get_uuid_field_names_map()
|
|
566
581
|
if self.new_node_field_specifiers is not None
|
|
567
582
|
else None,
|
|
568
583
|
}
|
|
@@ -580,16 +595,16 @@ AND type(r_node) IN ["HAS_ATTRIBUTE", "IS_RELATED"]
|
|
|
580
595
|
// node ID and field name filtering first pass
|
|
581
596
|
AND (
|
|
582
597
|
(
|
|
583
|
-
$
|
|
584
|
-
AND
|
|
598
|
+
$current_node_ids_list IS NOT NULL
|
|
599
|
+
AND n.uuid IN $current_node_ids_list
|
|
585
600
|
AND p.name IN $current_node_field_specifiers_map[n.uuid]
|
|
586
601
|
) OR (
|
|
587
|
-
$
|
|
588
|
-
AND
|
|
602
|
+
$new_node_ids_list IS NOT NULL
|
|
603
|
+
AND n.uuid IN $new_node_ids_list
|
|
589
604
|
AND p.name IN $new_node_field_specifiers_map[n.uuid]
|
|
590
605
|
) OR (
|
|
591
|
-
$
|
|
592
|
-
AND $
|
|
606
|
+
$new_node_ids_list IS NULL
|
|
607
|
+
AND $current_node_ids_list IS NULL
|
|
593
608
|
)
|
|
594
609
|
)
|
|
595
610
|
// node ID and field name filtering second pass
|
|
@@ -597,8 +612,12 @@ AND (
|
|
|
597
612
|
// time-based filters for nodes already included in the diff or fresh changes
|
|
598
613
|
(
|
|
599
614
|
(
|
|
600
|
-
(
|
|
601
|
-
|
|
615
|
+
(
|
|
616
|
+
$current_node_ids_list IS NOT NULL
|
|
617
|
+
AND n.uuid IN $current_node_ids_list
|
|
618
|
+
AND p.name IN $current_node_field_specifiers_map[n.uuid]
|
|
619
|
+
)
|
|
620
|
+
OR ($current_node_ids_list IS NULL AND $new_node_ids_list IS NULL)
|
|
602
621
|
)
|
|
603
622
|
AND (
|
|
604
623
|
($from_time <= diff_rel.from < $to_time AND (diff_rel.to IS NULL OR diff_rel.to > $to_time))
|
|
@@ -612,7 +631,11 @@ AND (
|
|
|
612
631
|
)
|
|
613
632
|
// time-based filters for new nodes
|
|
614
633
|
OR (
|
|
615
|
-
(
|
|
634
|
+
(
|
|
635
|
+
$new_node_ids_list IS NOT NULL
|
|
636
|
+
AND n.uuid IN $new_node_ids_list
|
|
637
|
+
AND p.name IN $new_node_field_specifiers_map[n.uuid]
|
|
638
|
+
)
|
|
616
639
|
AND (
|
|
617
640
|
($branch_from_time <= diff_rel.from < $to_time AND (diff_rel.to IS NULL OR diff_rel.to > $to_time))
|
|
618
641
|
OR ($branch_from_time <= diff_rel.to < $to_time)
|
|
@@ -667,7 +690,11 @@ WITH one_result[0] AS diff_rel_path, one_result[1] AS r_root, one_result[2] AS n
|
|
|
667
690
|
// Add correct from_time for row
|
|
668
691
|
// -------------------------------------
|
|
669
692
|
WITH diff_rel_path, r_root, n, r_node, p, diff_rel, has_more_data, CASE
|
|
670
|
-
WHEN
|
|
693
|
+
WHEN
|
|
694
|
+
$new_node_ids_list IS NOT NULL
|
|
695
|
+
AND n.uuid IN $new_node_ids_list
|
|
696
|
+
AND p.name IN $new_node_field_specifiers_map[n.uuid]
|
|
697
|
+
THEN $branch_from_time
|
|
671
698
|
ELSE $from_time
|
|
672
699
|
END AS row_from_time
|
|
673
700
|
WITH diff_rel_path, r_root, n, r_node, p, diff_rel, has_more_data, row_from_time
|
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
|
+
labels: frozenset[str]
|
|
95
96
|
|
|
96
97
|
|
|
97
98
|
class NodeQuery(Query):
|
|
@@ -1405,7 +1406,7 @@ class NodeGetHierarchyQuery(Query):
|
|
|
1405
1406
|
|
|
1406
1407
|
super().__init__(**kwargs)
|
|
1407
1408
|
|
|
1408
|
-
async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None: # noqa: ARG002
|
|
1409
|
+
async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None: # noqa: ARG002,PLR0915
|
|
1409
1410
|
hierarchy_schema = self.node_schema.get_hierarchy_schema(db=db, branch=self.branch)
|
|
1410
1411
|
branch_filter, branch_params = self.branch.get_query_filter_path(at=self.at.to_string())
|
|
1411
1412
|
self.params.update(branch_params)
|
|
@@ -1438,6 +1439,10 @@ class NodeGetHierarchyQuery(Query):
|
|
|
1438
1439
|
UNWIND peers_with_duplicates AS pwd
|
|
1439
1440
|
RETURN DISTINCT pwd AS peer
|
|
1440
1441
|
}
|
|
1442
|
+
""" % {"filter": filter_str, "branch_filter": branch_filter}
|
|
1443
|
+
|
|
1444
|
+
if not self.branch.is_default:
|
|
1445
|
+
query += """
|
|
1441
1446
|
CALL {
|
|
1442
1447
|
WITH n, peer
|
|
1443
1448
|
MATCH path = (n)%(filter)s(peer)
|
|
@@ -1448,10 +1453,14 @@ class NodeGetHierarchyQuery(Query):
|
|
|
1448
1453
|
LIMIT 1
|
|
1449
1454
|
}
|
|
1450
1455
|
WITH peer1 as peer, is_active
|
|
1451
|
-
|
|
1456
|
+
""" % {"filter": filter_str, "branch_filter": branch_filter, "with_clause": with_clause}
|
|
1457
|
+
else:
|
|
1458
|
+
query += """
|
|
1459
|
+
WITH peer
|
|
1460
|
+
"""
|
|
1452
1461
|
|
|
1453
1462
|
self.add_to_query(query)
|
|
1454
|
-
where_clause = ["is_active = TRUE"]
|
|
1463
|
+
where_clause = ["is_active = TRUE"] if not self.branch.is_default else []
|
|
1455
1464
|
|
|
1456
1465
|
clean_filters = extract_field_filters(field_name=self.direction.value, filters=self.filters)
|
|
1457
1466
|
|
|
@@ -1461,7 +1470,8 @@ class NodeGetHierarchyQuery(Query):
|
|
|
1461
1470
|
if clean_filters.get("id", None):
|
|
1462
1471
|
self.params["peer_ids"].append(clean_filters.get("id"))
|
|
1463
1472
|
|
|
1464
|
-
|
|
1473
|
+
if where_clause:
|
|
1474
|
+
self.add_to_query("WHERE " + " AND ".join(where_clause))
|
|
1465
1475
|
|
|
1466
1476
|
self.return_labels = ["peer"]
|
|
1467
1477
|
|
|
@@ -1544,4 +1554,5 @@ class NodeGetHierarchyQuery(Query):
|
|
|
1544
1554
|
yield PeerInfo(
|
|
1545
1555
|
uuid=peer_node.get("uuid"),
|
|
1546
1556
|
kind=peer_node.get("kind"),
|
|
1557
|
+
labels=peer_node.labels,
|
|
1547
1558
|
)
|
|
@@ -73,12 +73,21 @@ class RelationshipPeerData:
|
|
|
73
73
|
source_id: UUID
|
|
74
74
|
"""UUID of the Source Node."""
|
|
75
75
|
|
|
76
|
+
source_kind: str
|
|
77
|
+
"""Kind of the Source Node."""
|
|
78
|
+
|
|
79
|
+
source_labels: frozenset[str]
|
|
80
|
+
"""Labels of the Source Node."""
|
|
81
|
+
|
|
76
82
|
peer_id: UUID
|
|
77
83
|
"""UUID of the Peer Node."""
|
|
78
84
|
|
|
79
85
|
peer_kind: str
|
|
80
86
|
"""Kind of the Peer Node."""
|
|
81
87
|
|
|
88
|
+
peer_labels: frozenset[str]
|
|
89
|
+
"""Labels of the Peer Node."""
|
|
90
|
+
|
|
82
91
|
properties: dict[str, FlagPropertyData | NodePropertyData]
|
|
83
92
|
"""UUID of the Relationship Node."""
|
|
84
93
|
|
|
@@ -752,10 +761,15 @@ class RelationshipGetPeerQuery(Query):
|
|
|
752
761
|
def get_peers(self) -> Generator[RelationshipPeerData, None, None]:
|
|
753
762
|
for result in self.get_results_group_by(("peer", "uuid"), ("source_node", "uuid")):
|
|
754
763
|
rels = result.get("rels")
|
|
764
|
+
source_node = result.get_node("source_node")
|
|
765
|
+
peer_node = result.get_node("peer")
|
|
755
766
|
data = RelationshipPeerData(
|
|
756
|
-
source_id=
|
|
757
|
-
|
|
758
|
-
|
|
767
|
+
source_id=source_node.get("uuid"),
|
|
768
|
+
source_kind=source_node.get("kind"),
|
|
769
|
+
source_labels=source_node.labels,
|
|
770
|
+
peer_id=peer_node.get("uuid"),
|
|
771
|
+
peer_kind=peer_node.get("kind"),
|
|
772
|
+
peer_labels=peer_node.labels,
|
|
759
773
|
rel_node_db_id=result.get("rl").element_id,
|
|
760
774
|
rel_node_id=result.get("rl").get("uuid"),
|
|
761
775
|
updated_at=rels[0]["from"],
|
infrahub/core/query/subquery.py
CHANGED
|
@@ -57,7 +57,7 @@ async def build_subquery_filter(
|
|
|
57
57
|
params.update(field_params)
|
|
58
58
|
|
|
59
59
|
field_where.append("all(r IN relationships(path) WHERE (%s))" % branch_filter)
|
|
60
|
-
filter_str = f"({node_alias})" + "".join([str(item) for item in field_filter])
|
|
60
|
+
filter_str = f"({node_alias}:Node {{uuid: {node_alias}.uuid}})" + "".join([str(item) for item in field_filter])
|
|
61
61
|
where_str = " AND ".join(field_where)
|
|
62
62
|
branch_level_str = "reduce(br_lvl = 0, r in relationships(path) | br_lvl + r.branch_level)"
|
|
63
63
|
froms_str = db.render_list_comprehension(items="relationships(path)", item_name="from")
|
|
@@ -138,7 +138,7 @@ async def build_subquery_order(
|
|
|
138
138
|
field_filter[-1].name = "last"
|
|
139
139
|
|
|
140
140
|
field_where.append("all(r IN relationships(path) WHERE (%s))" % branch_filter)
|
|
141
|
-
filter_str = f"({node_alias})" + "".join([str(item) for item in field_filter])
|
|
141
|
+
filter_str = f"({node_alias}:Node {{uuid: {node_alias}.uuid}})" + "".join([str(item) for item in field_filter])
|
|
142
142
|
where_str = " AND ".join(field_where)
|
|
143
143
|
branch_level_str = "reduce(br_lvl = 0, r in relationships(path) | br_lvl + r.branch_level)"
|
|
144
144
|
froms_str = db.render_list_comprehension(items="relationships(path)", item_name="from")
|
|
@@ -2050,6 +2050,9 @@ class SchemaBranch:
|
|
|
2050
2050
|
|
|
2051
2051
|
identified.add(node_schema)
|
|
2052
2052
|
|
|
2053
|
+
if node_schema.is_node_schema:
|
|
2054
|
+
identified.update([self.get(name=kind, duplicate=False) for kind in node_schema.inherit_from])
|
|
2055
|
+
|
|
2053
2056
|
for relationship in node_schema.relationships:
|
|
2054
2057
|
if (
|
|
2055
2058
|
relationship.peer in [InfrahubKind.GENERICGROUP, InfrahubKind.PROFILE]
|
|
@@ -30,7 +30,7 @@ class NodeUniqueAttributeConstraintQuery(Query):
|
|
|
30
30
|
def get_context(self) -> dict[str, str]:
|
|
31
31
|
return {"kind": self.query_request.kind}
|
|
32
32
|
|
|
33
|
-
async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None: # noqa: ARG002
|
|
33
|
+
async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None: # noqa: ARG002,PLR0915
|
|
34
34
|
branch_filter, branch_params = self.branch.get_query_filter_path(at=self.at.to_string(), is_isolated=False)
|
|
35
35
|
self.params.update(branch_params)
|
|
36
36
|
from_times = db.render_list_comprehension(items="relationships(potential_path)", item_name="from")
|
|
@@ -105,16 +105,28 @@ class NodeUniqueAttributeConstraintQuery(Query):
|
|
|
105
105
|
attr_paths_subquery = """
|
|
106
106
|
MATCH attr_path = (start_node:%(node_kind)s)-[:HAS_ATTRIBUTE]->(attr:Attribute)-[r:HAS_VALUE]->(attr_value:AttributeValue)
|
|
107
107
|
WHERE attr.name in $attribute_names
|
|
108
|
-
AND
|
|
109
|
-
OR (attr_value.value in $attr_values AND [attr.name, type(r), attr_value.value] in $attr_paths_with_value))
|
|
108
|
+
AND [attr.name, type(r)] in $attr_paths
|
|
110
109
|
RETURN start_node, attr_path as potential_path, NULL as rel_identifier, attr.name as potential_attr, attr_value.value as potential_attr_value
|
|
111
110
|
""" % {"node_kind": self.query_request.kind}
|
|
112
111
|
|
|
113
|
-
|
|
112
|
+
attr_paths_with_value_subquery = """
|
|
113
|
+
MATCH attr_path = (start_node:%(node_kind)s)-[:HAS_ATTRIBUTE]->(attr:Attribute)-[r:HAS_VALUE]->(attr_value:AttributeValue)
|
|
114
|
+
WHERE attr.name in $attribute_names AND attr_value.value in $attr_values
|
|
115
|
+
AND [attr.name, type(r), attr_value.value] in $attr_paths_with_value
|
|
116
|
+
RETURN start_node, attr_path as potential_path, NULL as rel_identifier, attr.name as potential_attr, attr_value.value as potential_attr_value
|
|
117
|
+
""" % {"node_kind": self.query_request.kind}
|
|
118
|
+
|
|
119
|
+
relationship_attr_paths_subquery = """
|
|
114
120
|
MATCH rel_path = (start_node:%(node_kind)s)-[:IS_RELATED]-(relationship_node:Relationship)-[:IS_RELATED]-(related_n:Node)-[:HAS_ATTRIBUTE]->(rel_attr:Attribute)-[:HAS_VALUE]->(rel_attr_value:AttributeValue)
|
|
115
121
|
WHERE relationship_node.name in $relationship_names
|
|
116
|
-
AND
|
|
117
|
-
|
|
122
|
+
AND [relationship_node.name, rel_attr.name] in $relationship_attr_paths
|
|
123
|
+
RETURN start_node, rel_path as potential_path, relationship_node.name as rel_identifier, rel_attr.name as potential_attr, rel_attr_value.value as potential_attr_value
|
|
124
|
+
""" % {"node_kind": self.query_request.kind}
|
|
125
|
+
|
|
126
|
+
relationship_attr_paths_with_value_subquery = """
|
|
127
|
+
MATCH rel_path = (start_node:%(node_kind)s)-[:IS_RELATED]-(relationship_node:Relationship)-[:IS_RELATED]-(related_n:Node)-[:HAS_ATTRIBUTE]->(rel_attr:Attribute)-[:HAS_VALUE]->(rel_attr_value:AttributeValue)
|
|
128
|
+
WHERE relationship_node.name in $relationship_names AND rel_attr_value.value in $relationship_attr_values
|
|
129
|
+
AND [relationship_node.name, rel_attr.name, rel_attr_value.value] in $relationship_attr_paths_with_value
|
|
118
130
|
RETURN start_node, rel_path as potential_path, relationship_node.name as rel_identifier, rel_attr.name as potential_attr, rel_attr_value.value as potential_attr_value
|
|
119
131
|
""" % {"node_kind": self.query_request.kind}
|
|
120
132
|
|
|
@@ -130,9 +142,13 @@ class NodeUniqueAttributeConstraintQuery(Query):
|
|
|
130
142
|
}
|
|
131
143
|
|
|
132
144
|
select_subqueries = []
|
|
133
|
-
if attr_paths
|
|
145
|
+
if attr_paths:
|
|
134
146
|
select_subqueries.append(attr_paths_subquery)
|
|
135
|
-
if
|
|
147
|
+
if attr_paths_with_value:
|
|
148
|
+
select_subqueries.append(attr_paths_with_value_subquery)
|
|
149
|
+
if relationship_attr_paths:
|
|
150
|
+
select_subqueries.append(relationship_attr_paths_subquery)
|
|
151
|
+
if relationship_attr_paths_with_value:
|
|
136
152
|
select_subqueries.append(relationship_attr_paths_with_value_subquery)
|
|
137
153
|
if relationship_only_attr_paths:
|
|
138
154
|
select_subqueries.append(relationship_only_attr_paths_subquery)
|
infrahub/database/__init__.py
CHANGED
|
@@ -33,9 +33,7 @@ from infrahub.exceptions import DatabaseError
|
|
|
33
33
|
from infrahub.log import get_logger
|
|
34
34
|
from infrahub.utils import InfrahubStringEnum
|
|
35
35
|
|
|
36
|
-
from .memgraph import DatabaseManagerMemgraph
|
|
37
36
|
from .metrics import CONNECTION_POOL_USAGE, QUERY_EXECUTION_METRICS, TRANSACTION_RETRIES
|
|
38
|
-
from .neo4j import DatabaseManagerNeo4j
|
|
39
37
|
|
|
40
38
|
if TYPE_CHECKING:
|
|
41
39
|
from types import TracebackType
|
|
@@ -158,11 +156,6 @@ class InfrahubDatabase:
|
|
|
158
156
|
else:
|
|
159
157
|
self.db_type = config.SETTINGS.database.db_type
|
|
160
158
|
|
|
161
|
-
if self.db_type == DatabaseType.NEO4J:
|
|
162
|
-
self.manager = DatabaseManagerNeo4j(db=self)
|
|
163
|
-
elif self.db_type == DatabaseType.MEMGRAPH:
|
|
164
|
-
self.manager = DatabaseManagerMemgraph(db=self)
|
|
165
|
-
|
|
166
159
|
def __del__(self) -> None:
|
|
167
160
|
if not self._session or not self._is_session_local or self._session.closed():
|
|
168
161
|
return
|
infrahub/database/memgraph.py
CHANGED
|
@@ -1,14 +1,8 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from typing import TYPE_CHECKING
|
|
4
|
-
|
|
5
3
|
from infrahub.constants.database import EntityType, IndexType
|
|
6
4
|
|
|
7
5
|
from .index import IndexInfo, IndexItem, IndexManagerBase
|
|
8
|
-
from .manager import DatabaseManager
|
|
9
|
-
|
|
10
|
-
if TYPE_CHECKING:
|
|
11
|
-
from . import InfrahubDatabase
|
|
12
6
|
|
|
13
7
|
|
|
14
8
|
class IndexNodeMemgraph(IndexItem):
|
|
@@ -52,9 +46,3 @@ class IndexManagerMemgraph(IndexManagerBase):
|
|
|
52
46
|
)
|
|
53
47
|
|
|
54
48
|
return results
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
class DatabaseManagerMemgraph(DatabaseManager):
|
|
58
|
-
def __init__(self, db: InfrahubDatabase) -> None:
|
|
59
|
-
super().__init__(db=db)
|
|
60
|
-
self.index = IndexManagerMemgraph(db=db)
|
infrahub/database/neo4j.py
CHANGED
|
@@ -1,15 +1,9 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from typing import TYPE_CHECKING
|
|
4
|
-
|
|
5
3
|
from infrahub.constants.database import EntityType, IndexType
|
|
6
4
|
from infrahub.core.query import QueryType
|
|
7
5
|
|
|
8
6
|
from .index import IndexInfo, IndexItem, IndexManagerBase
|
|
9
|
-
from .manager import DatabaseManager
|
|
10
|
-
|
|
11
|
-
if TYPE_CHECKING:
|
|
12
|
-
from . import InfrahubDatabase
|
|
13
7
|
|
|
14
8
|
|
|
15
9
|
class IndexRelNeo4j(IndexItem):
|
|
@@ -68,9 +62,3 @@ class IndexManagerNeo4j(IndexManagerBase):
|
|
|
68
62
|
)
|
|
69
63
|
|
|
70
64
|
return results
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
class DatabaseManagerNeo4j(DatabaseManager):
|
|
74
|
-
def __init__(self, db: InfrahubDatabase) -> None:
|
|
75
|
-
super().__init__(db=db)
|
|
76
|
-
self.index = IndexManagerNeo4j(db=db)
|
|
@@ -71,7 +71,11 @@ class UpdateComputedAttribute(Mutation):
|
|
|
71
71
|
|
|
72
72
|
if not (
|
|
73
73
|
target_node := await NodeManager.get_one(
|
|
74
|
-
db=graphql_context.db,
|
|
74
|
+
db=graphql_context.db,
|
|
75
|
+
kind=node_schema.kind,
|
|
76
|
+
id=str(data.id),
|
|
77
|
+
branch=graphql_context.branch,
|
|
78
|
+
fields={target_attribute.name: None},
|
|
75
79
|
)
|
|
76
80
|
):
|
|
77
81
|
raise NodeNotFoundError(
|
|
@@ -10,6 +10,7 @@ from infrahub.core import registry
|
|
|
10
10
|
from infrahub.core.constants import DiffAction, RelationshipCardinality
|
|
11
11
|
from infrahub.core.constants.database import DatabaseEdgeType
|
|
12
12
|
from infrahub.core.diff.model.path import NameTrackingId
|
|
13
|
+
from infrahub.core.diff.query.filters import EnrichedDiffQueryFilters
|
|
13
14
|
from infrahub.core.diff.repository.repository import DiffRepository
|
|
14
15
|
from infrahub.core.query.diff import DiffCountChanges
|
|
15
16
|
from infrahub.core.timestamp import Timestamp
|
|
@@ -415,7 +416,7 @@ class DiffTreeResolver:
|
|
|
415
416
|
diff_branch_names=[diff_branch.name],
|
|
416
417
|
from_time=from_timestamp,
|
|
417
418
|
to_time=to_timestamp,
|
|
418
|
-
filters=filters_dict,
|
|
419
|
+
filters=EnrichedDiffQueryFilters(**filters_dict),
|
|
419
420
|
include_parents=include_parents,
|
|
420
421
|
limit=limit,
|
|
421
422
|
offset=offset,
|
infrahub/server.py
CHANGED
|
@@ -23,7 +23,6 @@ from infrahub import __version__, config
|
|
|
23
23
|
from infrahub.api import router as api
|
|
24
24
|
from infrahub.api.exception_handlers import generic_api_exception_handler
|
|
25
25
|
from infrahub.components import ComponentType
|
|
26
|
-
from infrahub.core.graph.index import attr_value_index, node_indexes, rel_indexes
|
|
27
26
|
from infrahub.core.initialization import initialization
|
|
28
27
|
from infrahub.database import InfrahubDatabase, InfrahubDatabaseMode, get_db
|
|
29
28
|
from infrahub.dependencies.registry import build_component_registry
|
|
@@ -58,9 +57,6 @@ async def app_initialization(application: FastAPI, enable_scheduler: bool = True
|
|
|
58
57
|
|
|
59
58
|
# Initialize database Driver and load local registry
|
|
60
59
|
database = application.state.db = InfrahubDatabase(mode=InfrahubDatabaseMode.DRIVER, driver=await get_db())
|
|
61
|
-
if config.SETTINGS.experimental_features.value_db_index:
|
|
62
|
-
node_indexes.append(attr_value_index)
|
|
63
|
-
database.manager.index.init(nodes=node_indexes, rels=rel_indexes)
|
|
64
60
|
|
|
65
61
|
build_component_registry()
|
|
66
62
|
|
|
@@ -85,7 +81,7 @@ async def app_initialization(application: FastAPI, enable_scheduler: bool = True
|
|
|
85
81
|
initialize_lock(service=service)
|
|
86
82
|
# We must initialize DB after initialize lock and initialize lock depends on cache initialization
|
|
87
83
|
async with application.state.db.start_session() as db:
|
|
88
|
-
await initialization(db=db)
|
|
84
|
+
await initialization(db=db, add_database_indexes=True)
|
|
89
85
|
|
|
90
86
|
application.state.service = service
|
|
91
87
|
application.state.response_delay = config.SETTINGS.miscellaneous.response_delay
|
infrahub/trigger/models.py
CHANGED
|
@@ -5,8 +5,11 @@ from enum import Enum
|
|
|
5
5
|
from typing import TYPE_CHECKING, Any
|
|
6
6
|
|
|
7
7
|
from prefect.events.actions import RunDeployment
|
|
8
|
+
from prefect.events.schemas.automations import (
|
|
9
|
+
Automation, # noqa: TC002
|
|
10
|
+
Posture,
|
|
11
|
+
)
|
|
8
12
|
from prefect.events.schemas.automations import EventTrigger as PrefectEventTrigger
|
|
9
|
-
from prefect.events.schemas.automations import Posture
|
|
10
13
|
from prefect.events.schemas.events import ResourceSpecification
|
|
11
14
|
from pydantic import BaseModel, Field
|
|
12
15
|
|
|
@@ -19,6 +22,13 @@ if TYPE_CHECKING:
|
|
|
19
22
|
from uuid import UUID
|
|
20
23
|
|
|
21
24
|
|
|
25
|
+
class TriggerSetupReport(BaseModel):
|
|
26
|
+
created: list[TriggerDefinition] = Field(default_factory=list)
|
|
27
|
+
updated: list[TriggerDefinition] = Field(default_factory=list)
|
|
28
|
+
deleted: list[Automation] = Field(default_factory=list)
|
|
29
|
+
unchanged: list[TriggerDefinition] = Field(default_factory=list)
|
|
30
|
+
|
|
31
|
+
|
|
22
32
|
class TriggerType(str, Enum):
|
|
23
33
|
BUILTIN = "builtin"
|
|
24
34
|
WEBHOOK = "webhook"
|