infrahub-server 1.2.7__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/api/transformation.py +1 -0
- infrahub/artifacts/models.py +4 -0
- infrahub/cli/db.py +15 -6
- infrahub/computed_attribute/tasks.py +34 -12
- infrahub/config.py +2 -1
- infrahub/constants/__init__.py +0 -0
- infrahub/core/branch/tasks.py +0 -2
- infrahub/core/constants/__init__.py +1 -0
- 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/graph/index.py +3 -1
- infrahub/core/initialization.py +23 -7
- infrahub/core/manager.py +16 -5
- infrahub/core/migrations/graph/__init__.py +2 -0
- infrahub/core/migrations/graph/m014_remove_index_attr_value.py +9 -8
- infrahub/core/migrations/graph/m027_delete_isolated_nodes.py +50 -0
- infrahub/core/protocols.py +1 -0
- infrahub/core/query/branch.py +27 -17
- infrahub/core/query/diff.py +65 -38
- infrahub/core/query/node.py +111 -33
- infrahub/core/query/relationship.py +17 -3
- infrahub/core/query/subquery.py +2 -2
- infrahub/core/schema/definitions/core/builtin.py +2 -4
- infrahub/core/schema/definitions/core/transform.py +1 -0
- infrahub/core/schema/schema_branch.py +3 -0
- infrahub/core/validators/aggregated_checker.py +2 -2
- infrahub/core/validators/uniqueness/query.py +30 -9
- infrahub/database/__init__.py +1 -16
- infrahub/database/index.py +1 -1
- infrahub/database/memgraph.py +1 -12
- infrahub/database/neo4j.py +1 -13
- infrahub/git/integrator.py +27 -3
- infrahub/git/models.py +4 -0
- infrahub/git/tasks.py +3 -0
- infrahub/git_credential/helper.py +2 -2
- infrahub/graphql/mutations/computed_attribute.py +5 -1
- infrahub/graphql/queries/diff/tree.py +2 -1
- infrahub/message_bus/operations/requests/proposed_change.py +6 -0
- infrahub/message_bus/types.py +3 -0
- infrahub/patch/queries/consolidate_duplicated_nodes.py +109 -0
- infrahub/patch/queries/delete_duplicated_edges.py +138 -0
- infrahub/proposed_change/tasks.py +1 -0
- infrahub/server.py +1 -3
- infrahub/transformations/models.py +3 -0
- infrahub/transformations/tasks.py +1 -0
- infrahub/trigger/models.py +11 -1
- infrahub/trigger/setup.py +38 -13
- infrahub/trigger/tasks.py +1 -4
- infrahub/webhook/models.py +3 -0
- infrahub/workflows/initialization.py +1 -3
- infrahub_sdk/client.py +4 -4
- infrahub_sdk/config.py +17 -0
- infrahub_sdk/ctl/cli_commands.py +7 -1
- infrahub_sdk/ctl/generator.py +2 -2
- infrahub_sdk/generator.py +12 -66
- infrahub_sdk/operation.py +80 -0
- infrahub_sdk/protocols.py +12 -0
- infrahub_sdk/recorder.py +3 -0
- infrahub_sdk/schema/repository.py +4 -0
- infrahub_sdk/transforms.py +15 -27
- {infrahub_server-1.2.7.dist-info → infrahub_server-1.2.9.dist-info}/METADATA +2 -2
- {infrahub_server-1.2.7.dist-info → infrahub_server-1.2.9.dist-info}/RECORD +84 -78
- infrahub_testcontainers/container.py +1 -0
- infrahub_testcontainers/docker-compose.test.yml +5 -1
- infrahub/database/manager.py +0 -15
- /infrahub/{database/constants.py → constants/database.py} +0 -0
- {infrahub_server-1.2.7.dist-info → infrahub_server-1.2.9.dist-info}/LICENSE.txt +0 -0
- {infrahub_server-1.2.7.dist-info → infrahub_server-1.2.9.dist-info}/WHEEL +0 -0
- {infrahub_server-1.2.7.dist-info → infrahub_server-1.2.9.dist-info}/entry_points.txt +0 -0
|
@@ -12,6 +12,7 @@ from infrahub.log import get_logger
|
|
|
12
12
|
from ..model.path import (
|
|
13
13
|
CalculatedDiffs,
|
|
14
14
|
EnrichedDiffRoot,
|
|
15
|
+
NodeIdentifier,
|
|
15
16
|
)
|
|
16
17
|
from ..parent_node_adder import DiffParentNodeAdder, ParentNodeAddRequest
|
|
17
18
|
from .interface import DiffEnricherInterface
|
|
@@ -37,8 +38,8 @@ class DiffHierarchyEnricher(DiffEnricherInterface):
|
|
|
37
38
|
|
|
38
39
|
log.info("Beginning hierarchical diff enrichment...")
|
|
39
40
|
self.parent_adder.initialize(enriched_diff_root=enriched_diff_root)
|
|
40
|
-
node_rel_parent_map: dict[str, list[
|
|
41
|
-
node_hierarchy_map: dict[str, list[
|
|
41
|
+
node_rel_parent_map: dict[str, list[NodeIdentifier]] = defaultdict(list)
|
|
42
|
+
node_hierarchy_map: dict[str, list[NodeIdentifier]] = defaultdict(list)
|
|
42
43
|
|
|
43
44
|
for node in enriched_diff_root.nodes:
|
|
44
45
|
schema_node = self.db.schema.get(
|
|
@@ -49,14 +50,14 @@ class DiffHierarchyEnricher(DiffEnricherInterface):
|
|
|
49
50
|
continue
|
|
50
51
|
|
|
51
52
|
if schema_node.has_parent_relationship:
|
|
52
|
-
node_rel_parent_map[node.kind].append(node.
|
|
53
|
+
node_rel_parent_map[node.kind].append(node.identifier)
|
|
53
54
|
continue
|
|
54
55
|
|
|
55
56
|
try:
|
|
56
57
|
hierarchy_schema = schema_node.get_hierarchy_schema(
|
|
57
58
|
db=self.db, branch=enriched_diff_root.diff_branch_name
|
|
58
59
|
)
|
|
59
|
-
node_hierarchy_map[hierarchy_schema.kind].append(node.
|
|
60
|
+
node_hierarchy_map[hierarchy_schema.kind].append(node.identifier)
|
|
60
61
|
except ValueError:
|
|
61
62
|
pass
|
|
62
63
|
|
|
@@ -67,21 +68,21 @@ class DiffHierarchyEnricher(DiffEnricherInterface):
|
|
|
67
68
|
async def _enrich_hierarchical_nodes(
|
|
68
69
|
self,
|
|
69
70
|
enriched_diff_root: EnrichedDiffRoot,
|
|
70
|
-
node_map: dict[str, list[
|
|
71
|
+
node_map: dict[str, list[NodeIdentifier]],
|
|
71
72
|
) -> None:
|
|
72
73
|
diff_branch = registry.get_branch_from_registry(branch=enriched_diff_root.diff_branch_name)
|
|
73
74
|
|
|
74
75
|
# Retrieve the ID of all ancestors
|
|
75
|
-
for kind,
|
|
76
|
-
log.info(f"Beginning hierarchy enrichment for {kind} node, num_nodes={len(
|
|
76
|
+
for kind, node_identifiers in node_map.items():
|
|
77
|
+
log.info(f"Beginning hierarchy enrichment for {kind} node, num_nodes={len(node_identifiers)}...")
|
|
77
78
|
hierarchy_schema = self.db.schema.get(
|
|
78
79
|
name=kind, branch=enriched_diff_root.diff_branch_name, duplicate=False
|
|
79
80
|
)
|
|
80
|
-
for
|
|
81
|
+
for node_identifier in node_identifiers:
|
|
81
82
|
query = await NodeGetHierarchyQuery.init(
|
|
82
83
|
db=self.db,
|
|
83
84
|
direction=RelationshipHierarchyDirection.ANCESTORS,
|
|
84
|
-
node_id=
|
|
85
|
+
node_id=node_identifier.uuid,
|
|
85
86
|
node_schema=hierarchy_schema,
|
|
86
87
|
branch=diff_branch,
|
|
87
88
|
hierarchical_ordering=True,
|
|
@@ -93,15 +94,15 @@ class DiffHierarchyEnricher(DiffEnricherInterface):
|
|
|
93
94
|
if not ancestors:
|
|
94
95
|
continue
|
|
95
96
|
|
|
96
|
-
node = enriched_diff_root.get_node(
|
|
97
|
+
node = enriched_diff_root.get_node(node_identifier=node_identifier)
|
|
97
98
|
parent_rel = hierarchy_schema.get_relationship(name="parent")
|
|
98
99
|
|
|
99
100
|
current_node = node
|
|
100
101
|
for ancestor in ancestors:
|
|
102
|
+
ancestor_identifier = NodeIdentifier(uuid=ancestor.uuid, kind=ancestor.kind, labels=ancestor.labels)
|
|
101
103
|
parent_request = ParentNodeAddRequest(
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
parent_kind=ancestor.kind,
|
|
104
|
+
node_identifier=current_node.identifier,
|
|
105
|
+
parent_identifier=ancestor_identifier,
|
|
105
106
|
parent_label="",
|
|
106
107
|
parent_rel_name=parent_rel.name,
|
|
107
108
|
parent_rel_identifier=parent_rel.get_identifier(),
|
|
@@ -113,20 +114,20 @@ class DiffHierarchyEnricher(DiffEnricherInterface):
|
|
|
113
114
|
current_node = parent
|
|
114
115
|
|
|
115
116
|
async def _enrich_nodes_with_parent(
|
|
116
|
-
self, enriched_diff_root: EnrichedDiffRoot, node_map: dict[str, list[
|
|
117
|
+
self, enriched_diff_root: EnrichedDiffRoot, node_map: dict[str, list[NodeIdentifier]]
|
|
117
118
|
) -> None:
|
|
118
119
|
diff_branch = registry.get_branch_from_registry(branch=enriched_diff_root.diff_branch_name)
|
|
119
120
|
|
|
120
|
-
parent_peers: dict[
|
|
121
|
+
parent_peers: dict[NodeIdentifier, RelationshipPeerData] = {}
|
|
121
122
|
|
|
122
123
|
# Prepare a map to capture all parents that also have a parent
|
|
123
|
-
node_parent_with_parent_map: dict[str, list[
|
|
124
|
+
node_parent_with_parent_map: dict[str, list[NodeIdentifier]] = defaultdict(list)
|
|
124
125
|
|
|
125
126
|
# TODO Not gonna implement it now but technically we could check the content of the node to see if the parent relationship is present
|
|
126
127
|
|
|
127
128
|
# Query the UUID of the parent
|
|
128
|
-
for kind,
|
|
129
|
-
log.info(f"Beginning parent enrichment for {kind} node, num_nodes={len(
|
|
129
|
+
for kind, node_identifiers in node_map.items():
|
|
130
|
+
log.info(f"Beginning parent enrichment for {kind} node, num_nodes={len(node_identifiers)}...")
|
|
130
131
|
schema_node = self.db.schema.get(name=kind, branch=enriched_diff_root.diff_branch_name, duplicate=False)
|
|
131
132
|
|
|
132
133
|
parent_rel = [rel for rel in schema_node.relationships if rel.kind == RelationshipKind.PARENT][0]
|
|
@@ -137,33 +138,43 @@ class DiffHierarchyEnricher(DiffEnricherInterface):
|
|
|
137
138
|
query = await RelationshipGetPeerQuery.init(
|
|
138
139
|
db=self.db,
|
|
139
140
|
branch=diff_branch,
|
|
140
|
-
source_ids=
|
|
141
|
+
source_ids=[ni.uuid for ni in node_identifiers],
|
|
141
142
|
rel_type=DatabaseEdgeType.IS_RELATED.value,
|
|
142
143
|
schema=parent_rel,
|
|
143
144
|
)
|
|
144
145
|
await query.execute(db=self.db)
|
|
145
146
|
|
|
146
147
|
for peer in query.get_peers():
|
|
147
|
-
|
|
148
|
+
source_identifier = NodeIdentifier(
|
|
149
|
+
uuid=str(peer.source_id), kind=peer.source_kind, labels=peer.source_labels
|
|
150
|
+
)
|
|
151
|
+
parent_peers[source_identifier] = peer
|
|
148
152
|
if parent_schema.has_parent_relationship:
|
|
149
|
-
|
|
153
|
+
peer_identifier = NodeIdentifier(
|
|
154
|
+
uuid=str(peer.peer_id), kind=peer.peer_kind, labels=peer.peer_labels
|
|
155
|
+
)
|
|
156
|
+
node_parent_with_parent_map[parent_schema.kind].append(peer_identifier)
|
|
150
157
|
|
|
151
158
|
# Check if the parent are already present
|
|
152
159
|
# If parent is already in the list of node we need to add a relationship
|
|
153
160
|
# If parent is not in the list of node, we need to add it
|
|
154
|
-
diff_node_map = enriched_diff_root.get_node_map(
|
|
155
|
-
|
|
161
|
+
diff_node_map = enriched_diff_root.get_node_map(
|
|
162
|
+
node_uuids={source_identifier.uuid for source_identifier in parent_peers.keys()}
|
|
163
|
+
)
|
|
164
|
+
for node_identifier, peer_parent in parent_peers.items():
|
|
156
165
|
# TODO check if we can optimize this part to avoid querying this multiple times
|
|
157
|
-
node = diff_node_map[
|
|
166
|
+
node = diff_node_map[node_identifier]
|
|
158
167
|
schema_node = self.db.schema.get(
|
|
159
168
|
name=node.kind, branch=enriched_diff_root.diff_branch_name, duplicate=False
|
|
160
169
|
)
|
|
161
170
|
parent_rel = [rel for rel in schema_node.relationships if rel.kind == RelationshipKind.PARENT][0]
|
|
162
171
|
|
|
172
|
+
peer_identifier = NodeIdentifier(
|
|
173
|
+
uuid=str(peer_parent.peer_id), kind=peer_parent.peer_kind, labels=peer_parent.peer_labels
|
|
174
|
+
)
|
|
163
175
|
parent_request = ParentNodeAddRequest(
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
parent_kind=peer_parent.peer_kind,
|
|
176
|
+
node_identifier=node.identifier,
|
|
177
|
+
parent_identifier=peer_identifier,
|
|
167
178
|
parent_label="",
|
|
168
179
|
parent_rel_name=parent_rel.name,
|
|
169
180
|
parent_rel_identifier=parent_rel.get_identifier(),
|
|
@@ -3,6 +3,7 @@ from dataclasses import dataclass
|
|
|
3
3
|
from infrahub.core.constants import DiffAction
|
|
4
4
|
from infrahub.core.constants.database import DatabaseEdgeType
|
|
5
5
|
from infrahub.core.diff.model.path import BranchTrackingId
|
|
6
|
+
from infrahub.core.diff.query.filters import EnrichedDiffQueryFilters, IncExclActionFilterOptions, IncExclFilterOptions
|
|
6
7
|
from infrahub.core.ipam.kinds_getter import IpamKindsGetter
|
|
7
8
|
from infrahub.core.ipam.model import IpamNodeDetails
|
|
8
9
|
from infrahub.core.manager import NodeManager
|
|
@@ -48,10 +49,10 @@ class IpamDiffParser:
|
|
|
48
49
|
base_branch_name=target_branch_name,
|
|
49
50
|
diff_branch_names=[source_branch_name],
|
|
50
51
|
tracking_id=BranchTrackingId(name=source_branch_name),
|
|
51
|
-
filters=
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
52
|
+
filters=EnrichedDiffQueryFilters(
|
|
53
|
+
kind=IncExclFilterOptions(includes=list(ip_address_kinds | ip_prefix_kinds)),
|
|
54
|
+
status=IncExclActionFilterOptions(excludes={DiffAction.UNCHANGED}),
|
|
55
|
+
),
|
|
55
56
|
)
|
|
56
57
|
changed_node_details: list[ChangedIpamNodeDetails] = []
|
|
57
58
|
for diff in enriched_diffs:
|
|
@@ -54,24 +54,26 @@ class DiffMerger:
|
|
|
54
54
|
log.info(f"Diff {latest_diff.uuid} retrieved")
|
|
55
55
|
batch_num = 0
|
|
56
56
|
async for node_diff_dicts, property_diff_dicts in self.serializer.serialize_diff(diff=enriched_diff):
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
57
|
+
if node_diff_dicts:
|
|
58
|
+
log.info(f"Merging batch of nodes #{batch_num}")
|
|
59
|
+
merge_query = await DiffMergeQuery.init(
|
|
60
|
+
db=self.db,
|
|
61
|
+
branch=self.source_branch,
|
|
62
|
+
at=at,
|
|
63
|
+
target_branch=self.destination_branch,
|
|
64
|
+
node_diff_dicts=node_diff_dicts,
|
|
65
|
+
)
|
|
66
|
+
await merge_query.execute(db=self.db)
|
|
67
|
+
if property_diff_dicts:
|
|
68
|
+
log.info(f"Merging batch of properties #{batch_num}")
|
|
69
|
+
merge_properties_query = await DiffMergePropertiesQuery.init(
|
|
70
|
+
db=self.db,
|
|
71
|
+
branch=self.source_branch,
|
|
72
|
+
at=at,
|
|
73
|
+
target_branch=self.destination_branch,
|
|
74
|
+
property_diff_dicts=property_diff_dicts,
|
|
75
|
+
)
|
|
76
|
+
await merge_properties_query.execute(db=self.db)
|
|
75
77
|
log.info(f"Batch #{batch_num} merged")
|
|
76
78
|
batch_num += 1
|
|
77
79
|
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class NodeFieldSpecifierMap:
|
|
5
|
+
def __init__(self) -> None:
|
|
6
|
+
# {uuid: {kind: {field_name, ...}}}
|
|
7
|
+
self._map: dict[str, dict[str, set[str]]] = {}
|
|
8
|
+
|
|
9
|
+
def __len__(self) -> int:
|
|
10
|
+
return len(self._map)
|
|
11
|
+
|
|
12
|
+
def __hash__(self) -> int:
|
|
13
|
+
full_node_hash_sum = 0
|
|
14
|
+
for node_uuid, node_dict in self._map.items():
|
|
15
|
+
node_kinds_hash_sum = 0
|
|
16
|
+
for kind, field_names in node_dict.items():
|
|
17
|
+
fields_hash = hash(frozenset(field_names))
|
|
18
|
+
node_kinds_hash_sum += hash(f"{hash(kind)}:{fields_hash}")
|
|
19
|
+
full_node_hash_sum += hash(f"{node_uuid}:{node_kinds_hash_sum}")
|
|
20
|
+
return hash(full_node_hash_sum)
|
|
21
|
+
|
|
22
|
+
def __eq__(self, other: object) -> bool:
|
|
23
|
+
if not isinstance(other, NodeFieldSpecifierMap):
|
|
24
|
+
return False
|
|
25
|
+
return self._map == other._map
|
|
26
|
+
|
|
27
|
+
def __sub__(self, other: NodeFieldSpecifierMap) -> NodeFieldSpecifierMap:
|
|
28
|
+
subtracted = NodeFieldSpecifierMap()
|
|
29
|
+
for node_uuid, node_dict in self._map.items():
|
|
30
|
+
if node_uuid not in other._map:
|
|
31
|
+
subtracted._map[node_uuid] = {**node_dict}
|
|
32
|
+
continue
|
|
33
|
+
subtracted_node_map = {}
|
|
34
|
+
for kind, field_names in node_dict.items():
|
|
35
|
+
subtracted_field_names = field_names - other._map[node_uuid].get(kind, set())
|
|
36
|
+
if not subtracted_field_names:
|
|
37
|
+
continue
|
|
38
|
+
subtracted_node_map[kind] = subtracted_field_names
|
|
39
|
+
if not subtracted_node_map:
|
|
40
|
+
continue
|
|
41
|
+
subtracted._map[node_uuid] = subtracted_node_map
|
|
42
|
+
return subtracted
|
|
43
|
+
|
|
44
|
+
def add_entry(self, node_uuid: str, kind: str, field_name: str) -> None:
|
|
45
|
+
if node_uuid not in self._map:
|
|
46
|
+
self._map[node_uuid] = {}
|
|
47
|
+
if kind not in self._map[node_uuid]:
|
|
48
|
+
self._map[node_uuid][kind] = set()
|
|
49
|
+
self._map[node_uuid][kind].add(field_name)
|
|
50
|
+
|
|
51
|
+
def has_entry(self, node_uuid: str, kind: str, field_name: str) -> bool:
|
|
52
|
+
return field_name in self._map.get(node_uuid, {}).get(kind, set())
|
|
53
|
+
|
|
54
|
+
def get_uuids_list(self) -> list[str]:
|
|
55
|
+
return list(self._map.keys())
|
|
56
|
+
|
|
57
|
+
def get_uuid_field_names_map(self) -> dict[str, list[str]]:
|
|
58
|
+
uuid_field_names_map: dict[str, list[str]] = {}
|
|
59
|
+
for node_uuid, node_dict in self._map.items():
|
|
60
|
+
field_names_set: set[str] = set()
|
|
61
|
+
for field_names in node_dict.values():
|
|
62
|
+
field_names_set |= field_names
|
|
63
|
+
uuid_field_names_map[node_uuid] = list(field_names_set)
|
|
64
|
+
return uuid_field_names_map
|
infrahub/core/diff/model/path.py
CHANGED
|
@@ -81,6 +81,21 @@ def deserialize_tracking_id(tracking_id_str: str) -> TrackingId:
|
|
|
81
81
|
raise ValueError(f"{tracking_id_str} is not a valid TrackingId")
|
|
82
82
|
|
|
83
83
|
|
|
84
|
+
@dataclass
|
|
85
|
+
class NodeIdentifier:
|
|
86
|
+
"""Uniquely identifying nodes that have had their kind or inheritance updated requires all of these fields"""
|
|
87
|
+
|
|
88
|
+
uuid: str
|
|
89
|
+
kind: str
|
|
90
|
+
labels: frozenset[str]
|
|
91
|
+
|
|
92
|
+
def __hash__(self) -> int:
|
|
93
|
+
return hash(f"{self.uuid}:{self.kind}:{hash(self.labels)}")
|
|
94
|
+
|
|
95
|
+
def __str__(self) -> str:
|
|
96
|
+
return f"{self.kind} '{self.uuid}' ({','.join(self.labels)})"
|
|
97
|
+
|
|
98
|
+
|
|
84
99
|
@dataclass
|
|
85
100
|
class NodeDiffFieldSummary:
|
|
86
101
|
kind: str
|
|
@@ -307,8 +322,7 @@ class ParentNodeInfo:
|
|
|
307
322
|
|
|
308
323
|
@dataclass
|
|
309
324
|
class EnrichedDiffNode(BaseSummary):
|
|
310
|
-
|
|
311
|
-
kind: str
|
|
325
|
+
identifier: NodeIdentifier
|
|
312
326
|
label: str
|
|
313
327
|
path_identifier: str = field(default="", kw_only=True)
|
|
314
328
|
changed_at: Timestamp | None = field(default=None, kw_only=True)
|
|
@@ -318,7 +332,15 @@ class EnrichedDiffNode(BaseSummary):
|
|
|
318
332
|
relationships: set[EnrichedDiffRelationship] = field(default_factory=set)
|
|
319
333
|
|
|
320
334
|
def __hash__(self) -> int:
|
|
321
|
-
return hash(self.
|
|
335
|
+
return hash(self.identifier)
|
|
336
|
+
|
|
337
|
+
@property
|
|
338
|
+
def uuid(self) -> str:
|
|
339
|
+
return self.identifier.uuid
|
|
340
|
+
|
|
341
|
+
@property
|
|
342
|
+
def kind(self) -> str:
|
|
343
|
+
return self.identifier.kind
|
|
322
344
|
|
|
323
345
|
@property
|
|
324
346
|
def num_properties(self) -> int:
|
|
@@ -402,8 +424,7 @@ class EnrichedDiffNode(BaseSummary):
|
|
|
402
424
|
@classmethod
|
|
403
425
|
def from_calculated_node(cls, calculated_node: DiffNode) -> EnrichedDiffNode:
|
|
404
426
|
return EnrichedDiffNode(
|
|
405
|
-
|
|
406
|
-
kind=calculated_node.kind,
|
|
427
|
+
identifier=calculated_node.identifier,
|
|
407
428
|
label="",
|
|
408
429
|
changed_at=calculated_node.changed_at,
|
|
409
430
|
action=calculated_node.action,
|
|
@@ -473,24 +494,24 @@ class EnrichedDiffRoot(EnrichedDiffRootMetadata):
|
|
|
473
494
|
nodes_with_parent_uuids |= {child_n.uuid for child_n in r.nodes}
|
|
474
495
|
return {node for node in self.nodes if node.uuid not in nodes_with_parent_uuids}
|
|
475
496
|
|
|
476
|
-
def get_node(self,
|
|
497
|
+
def get_node(self, node_identifier: NodeIdentifier) -> EnrichedDiffNode:
|
|
477
498
|
for n in self.nodes:
|
|
478
|
-
if n.
|
|
499
|
+
if n.identifier == node_identifier:
|
|
479
500
|
return n
|
|
480
|
-
raise ValueError(f"No node {
|
|
501
|
+
raise ValueError(f"No node {node_identifier} in diff root")
|
|
481
502
|
|
|
482
|
-
def has_node(self,
|
|
503
|
+
def has_node(self, node_identifier: NodeIdentifier) -> bool:
|
|
483
504
|
try:
|
|
484
|
-
self.get_node(
|
|
505
|
+
self.get_node(node_identifier=node_identifier)
|
|
485
506
|
return True
|
|
486
507
|
except ValueError:
|
|
487
508
|
return False
|
|
488
509
|
|
|
489
|
-
def get_node_map(self, node_uuids: set[str] | None = None) -> dict[
|
|
510
|
+
def get_node_map(self, node_uuids: set[str] | None = None) -> dict[NodeIdentifier, EnrichedDiffNode]:
|
|
490
511
|
node_map = {}
|
|
491
512
|
for node in self.nodes:
|
|
492
|
-
if node_uuids is None or node.uuid in node_uuids:
|
|
493
|
-
node_map[node.
|
|
513
|
+
if node_uuids is None or node.identifier.uuid in node_uuids:
|
|
514
|
+
node_map[node.identifier] = node
|
|
494
515
|
return node_map
|
|
495
516
|
|
|
496
517
|
def get_all_conflicts(self) -> dict[str, EnrichedDiffConflict]:
|
|
@@ -522,49 +543,6 @@ class EnrichedDiffRoot(EnrichedDiffRootMetadata):
|
|
|
522
543
|
nodes={EnrichedDiffNode.from_calculated_node(calculated_node=n) for n in calculated_diff.nodes},
|
|
523
544
|
)
|
|
524
545
|
|
|
525
|
-
def add_parent(
|
|
526
|
-
self,
|
|
527
|
-
node_id: str,
|
|
528
|
-
parent_id: str,
|
|
529
|
-
parent_kind: str,
|
|
530
|
-
parent_label: str,
|
|
531
|
-
parent_rel_name: str,
|
|
532
|
-
parent_rel_identifier: str,
|
|
533
|
-
parent_rel_cardinality: RelationshipCardinality,
|
|
534
|
-
parent_rel_label: str = "",
|
|
535
|
-
) -> EnrichedDiffNode:
|
|
536
|
-
node = self.get_node(node_uuid=node_id)
|
|
537
|
-
if not self.has_node(node_uuid=parent_id):
|
|
538
|
-
parent = EnrichedDiffNode(
|
|
539
|
-
uuid=parent_id,
|
|
540
|
-
kind=parent_kind,
|
|
541
|
-
label=parent_label,
|
|
542
|
-
action=DiffAction.UNCHANGED,
|
|
543
|
-
changed_at=None,
|
|
544
|
-
)
|
|
545
|
-
self.nodes.add(parent)
|
|
546
|
-
|
|
547
|
-
else:
|
|
548
|
-
parent = self.get_node(node_uuid=parent_id)
|
|
549
|
-
|
|
550
|
-
if node.has_relationship(name=parent_rel_name):
|
|
551
|
-
rel = node.get_relationship(name=parent_rel_name)
|
|
552
|
-
rel.nodes.add(parent)
|
|
553
|
-
else:
|
|
554
|
-
node.relationships.add(
|
|
555
|
-
EnrichedDiffRelationship(
|
|
556
|
-
name=parent_rel_name,
|
|
557
|
-
identifier=parent_rel_identifier,
|
|
558
|
-
label=parent_rel_label,
|
|
559
|
-
cardinality=parent_rel_cardinality,
|
|
560
|
-
changed_at=None,
|
|
561
|
-
action=DiffAction.UNCHANGED,
|
|
562
|
-
nodes={parent},
|
|
563
|
-
)
|
|
564
|
-
)
|
|
565
|
-
|
|
566
|
-
return parent
|
|
567
|
-
|
|
568
546
|
|
|
569
547
|
@dataclass
|
|
570
548
|
class EnrichedDiffsMetadata:
|
|
@@ -650,6 +628,14 @@ class EnrichedDiffs(EnrichedDiffsMetadata):
|
|
|
650
628
|
def branch_node_uuids(self) -> set[str]:
|
|
651
629
|
return {n.uuid for n in self.diff_branch_diff.nodes}
|
|
652
630
|
|
|
631
|
+
@property
|
|
632
|
+
def base_node_identifiers(self) -> set[NodeIdentifier]:
|
|
633
|
+
return {n.identifier for n in self.base_branch_diff.nodes}
|
|
634
|
+
|
|
635
|
+
@property
|
|
636
|
+
def branch_node_identifiers(self) -> set[NodeIdentifier]:
|
|
637
|
+
return {n.identifier for n in self.diff_branch_diff.nodes}
|
|
638
|
+
|
|
653
639
|
|
|
654
640
|
@dataclass
|
|
655
641
|
class CalculatedDiffs:
|
|
@@ -697,13 +683,20 @@ class DiffRelationship:
|
|
|
697
683
|
|
|
698
684
|
@dataclass
|
|
699
685
|
class DiffNode:
|
|
700
|
-
|
|
701
|
-
kind: str
|
|
686
|
+
identifier: NodeIdentifier
|
|
702
687
|
changed_at: Timestamp
|
|
703
688
|
action: DiffAction
|
|
704
689
|
attributes: list[DiffAttribute] = field(default_factory=list)
|
|
705
690
|
relationships: list[DiffRelationship] = field(default_factory=list)
|
|
706
691
|
|
|
692
|
+
@property
|
|
693
|
+
def uuid(self) -> str:
|
|
694
|
+
return self.identifier.uuid
|
|
695
|
+
|
|
696
|
+
@property
|
|
697
|
+
def kind(self) -> str:
|
|
698
|
+
return self.identifier.kind
|
|
699
|
+
|
|
707
700
|
|
|
708
701
|
@dataclass
|
|
709
702
|
class DiffRoot:
|
|
@@ -780,6 +773,10 @@ class DatabasePath:
|
|
|
780
773
|
def node_db_id(self) -> str:
|
|
781
774
|
return self.node_node.element_id
|
|
782
775
|
|
|
776
|
+
@property
|
|
777
|
+
def node_labels(self) -> frozenset[str]:
|
|
778
|
+
return self.node_node.labels
|
|
779
|
+
|
|
783
780
|
@property
|
|
784
781
|
def node_kind(self) -> str:
|
|
785
782
|
return str(self.node_node.get("kind"))
|
|
@@ -2,14 +2,13 @@ from dataclasses import dataclass, field
|
|
|
2
2
|
|
|
3
3
|
from infrahub.core.constants import DiffAction, RelationshipCardinality
|
|
4
4
|
|
|
5
|
-
from .model.path import EnrichedDiffNode, EnrichedDiffRelationship, EnrichedDiffRoot
|
|
5
|
+
from .model.path import EnrichedDiffNode, EnrichedDiffRelationship, EnrichedDiffRoot, NodeIdentifier
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
@dataclass
|
|
9
9
|
class ParentNodeAddRequest:
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
parent_kind: str
|
|
10
|
+
node_identifier: NodeIdentifier
|
|
11
|
+
parent_identifier: NodeIdentifier
|
|
13
12
|
parent_label: str
|
|
14
13
|
parent_rel_name: str
|
|
15
14
|
parent_rel_identifier: str
|
|
@@ -20,7 +19,7 @@ class ParentNodeAddRequest:
|
|
|
20
19
|
class DiffParentNodeAdder:
|
|
21
20
|
def __init__(self) -> None:
|
|
22
21
|
self._diff_root: EnrichedDiffRoot | None = None
|
|
23
|
-
self._node_map: dict[
|
|
22
|
+
self._node_map: dict[NodeIdentifier, EnrichedDiffNode] = {}
|
|
24
23
|
|
|
25
24
|
def initialize(self, enriched_diff_root: EnrichedDiffRoot) -> None:
|
|
26
25
|
self._diff_root = enriched_diff_root
|
|
@@ -31,33 +30,32 @@ class DiffParentNodeAdder:
|
|
|
31
30
|
raise RuntimeError("Must call initialize before using")
|
|
32
31
|
return self._diff_root
|
|
33
32
|
|
|
34
|
-
def get_node(self,
|
|
35
|
-
return self._node_map[
|
|
33
|
+
def get_node(self, identifier: NodeIdentifier) -> EnrichedDiffNode:
|
|
34
|
+
return self._node_map[identifier]
|
|
36
35
|
|
|
37
|
-
def has_node(self,
|
|
38
|
-
return
|
|
36
|
+
def has_node(self, identifier: NodeIdentifier) -> bool:
|
|
37
|
+
return identifier in self._node_map
|
|
39
38
|
|
|
40
39
|
def add_node(self, node: EnrichedDiffNode) -> None:
|
|
41
|
-
if node.
|
|
40
|
+
if node.identifier in self._node_map:
|
|
42
41
|
return
|
|
43
|
-
self._node_map[node.
|
|
42
|
+
self._node_map[node.identifier] = node
|
|
44
43
|
self.get_root().nodes.add(node)
|
|
45
44
|
|
|
46
45
|
def add_parent(self, parent_request: ParentNodeAddRequest) -> EnrichedDiffNode:
|
|
47
46
|
if not self._diff_root:
|
|
48
47
|
raise RuntimeError("Must call initialize before using")
|
|
49
|
-
node = self.get_node(
|
|
50
|
-
if not self.has_node(
|
|
48
|
+
node = self.get_node(identifier=parent_request.node_identifier)
|
|
49
|
+
if not self.has_node(identifier=parent_request.parent_identifier):
|
|
51
50
|
parent = EnrichedDiffNode(
|
|
52
|
-
|
|
53
|
-
kind=parent_request.parent_kind,
|
|
51
|
+
identifier=parent_request.parent_identifier,
|
|
54
52
|
label=parent_request.parent_label,
|
|
55
53
|
action=DiffAction.UNCHANGED,
|
|
56
54
|
changed_at=None,
|
|
57
55
|
)
|
|
58
56
|
self.add_node(parent)
|
|
59
57
|
else:
|
|
60
|
-
parent = self.get_node(
|
|
58
|
+
parent = self.get_node(identifier=parent_request.parent_identifier)
|
|
61
59
|
|
|
62
60
|
try:
|
|
63
61
|
rel = node.get_relationship(name=parent_request.parent_rel_name)
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
from infrahub.core.diff.model.path import NodeIdentifier
|
|
4
|
+
from infrahub.core.query import Query, QueryType
|
|
5
|
+
from infrahub.database import InfrahubDatabase
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class EnrichedDiffDropNodesQuery(Query):
|
|
9
|
+
name = "enriched_diff_drop_nodes"
|
|
10
|
+
type = QueryType.WRITE
|
|
11
|
+
insert_return = False
|
|
12
|
+
|
|
13
|
+
def __init__(self, enriched_diff_uuid: str, node_identifiers: list[NodeIdentifier], **kwargs: Any) -> None:
|
|
14
|
+
super().__init__(**kwargs)
|
|
15
|
+
self.enriched_diff_uuid = enriched_diff_uuid
|
|
16
|
+
self.node_identifiers = node_identifiers
|
|
17
|
+
|
|
18
|
+
async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None: # noqa: ARG002
|
|
19
|
+
self.params = {
|
|
20
|
+
"diff_root_uuid": self.enriched_diff_uuid,
|
|
21
|
+
"node_uuids": [ni.uuid for ni in self.node_identifiers],
|
|
22
|
+
"node_identifiers_map": {ni.uuid: ni.kind for ni in self.node_identifiers},
|
|
23
|
+
}
|
|
24
|
+
query = """
|
|
25
|
+
MATCH (d_root:DiffRoot {uuid: $diff_root_uuid})
|
|
26
|
+
MATCH (d_root)-[:DIFF_HAS_NODE]->(dn:DiffNode)
|
|
27
|
+
WHERE dn.uuid IN $node_uuids
|
|
28
|
+
AND dn.kind IN $node_identifiers_map[dn.uuid]
|
|
29
|
+
OPTIONAL MATCH (dn)-[:DIFF_HAS_ATTRIBUTE]-(da:DiffAttribute)
|
|
30
|
+
OPTIONAL MATCH (da)-[*]->(diff_thing)
|
|
31
|
+
DETACH DELETE diff_thing
|
|
32
|
+
DETACH DELETE da
|
|
33
|
+
WITH dn
|
|
34
|
+
OPTIONAL MATCH (dn)-[:DIFF_HAS_RELATIONSHIP]->(dr:DiffRelationship)
|
|
35
|
+
OPTIONAL MATCH (dr)-[:DIFF_HAS_ELEMENT]->(dre:DiffRelationshipElement)
|
|
36
|
+
OPTIONAL MATCH (dre)-[*]->(diff_thing)
|
|
37
|
+
DETACH DELETE diff_thing
|
|
38
|
+
DETACH DELETE dre
|
|
39
|
+
DETACH DELETE dr
|
|
40
|
+
DETACH DELETE dn
|
|
41
|
+
"""
|
|
42
|
+
self.add_to_query(query=query)
|
|
@@ -18,20 +18,21 @@ class EnrichedDiffFieldSpecifiersQuery(Query):
|
|
|
18
18
|
CALL {
|
|
19
19
|
MATCH (root:DiffRoot {uuid: $diff_id})-[:DIFF_HAS_NODE]->(node:DiffNode)-[:DIFF_HAS_ATTRIBUTE]->(attr:DiffAttribute)
|
|
20
20
|
WHERE (root.is_merged IS NULL OR root.is_merged <> TRUE)
|
|
21
|
-
RETURN node.uuid AS node_uuid, attr.name AS field_name
|
|
21
|
+
RETURN node.uuid AS node_uuid, node.kind AS node_kind, attr.name AS field_name
|
|
22
22
|
UNION
|
|
23
23
|
MATCH (root:DiffRoot {uuid: $diff_id})-[:DIFF_HAS_NODE]->(node:DiffNode)-[:DIFF_HAS_RELATIONSHIP]->(rel:DiffRelationship)
|
|
24
24
|
WHERE (root.is_merged IS NULL OR root.is_merged <> TRUE)
|
|
25
|
-
RETURN node.uuid AS node_uuid, rel.identifier AS field_name
|
|
25
|
+
RETURN node.uuid AS node_uuid, node.kind AS node_kind, rel.identifier AS field_name
|
|
26
26
|
}
|
|
27
27
|
"""
|
|
28
28
|
self.add_to_query(query=query)
|
|
29
|
-
self.return_labels = ["node_uuid", "field_name"]
|
|
30
|
-
self.order_by = ["node_uuid", "field_name"]
|
|
29
|
+
self.return_labels = ["node_uuid", "node_kind", "field_name"]
|
|
30
|
+
self.order_by = ["node_uuid", "node_kind", "field_name"]
|
|
31
31
|
|
|
32
|
-
def get_node_field_specifier_tuples(self) -> Generator[tuple[str, str], None, None]:
|
|
32
|
+
def get_node_field_specifier_tuples(self) -> Generator[tuple[str, str, str], None, None]:
|
|
33
33
|
for result in self.get_results():
|
|
34
34
|
node_uuid = result.get_as_str("node_uuid")
|
|
35
|
+
node_kind = result.get_as_str("node_kind")
|
|
35
36
|
field_name = result.get_as_str("field_name")
|
|
36
|
-
if node_uuid and field_name:
|
|
37
|
-
yield (node_uuid, field_name)
|
|
37
|
+
if node_uuid and node_kind and field_name:
|
|
38
|
+
yield (node_uuid, node_kind, field_name)
|