infrahub-server 1.2.9rc0__py3-none-any.whl → 1.3.0a0__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/actions/constants.py +86 -0
- infrahub/actions/gather.py +114 -0
- infrahub/actions/models.py +241 -0
- infrahub/actions/parsers.py +104 -0
- infrahub/actions/schema.py +382 -0
- infrahub/actions/tasks.py +126 -0
- infrahub/actions/triggers.py +21 -0
- infrahub/cli/db.py +1 -2
- infrahub/computed_attribute/models.py +13 -0
- infrahub/computed_attribute/tasks.py +48 -26
- infrahub/config.py +9 -0
- infrahub/core/account.py +24 -47
- infrahub/core/attribute.py +53 -14
- infrahub/core/branch/models.py +8 -9
- infrahub/core/branch/tasks.py +0 -2
- infrahub/core/constants/infrahubkind.py +8 -0
- infrahub/core/constraint/node/runner.py +1 -1
- infrahub/core/convert_object_type/__init__.py +0 -0
- infrahub/core/convert_object_type/conversion.py +122 -0
- infrahub/core/convert_object_type/schema_mapping.py +56 -0
- 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/all_conflicts.py +1 -5
- infrahub/core/diff/query/artifact.py +10 -20
- infrahub/core/diff/query/diff_get.py +3 -6
- infrahub/core/diff/query/drop_nodes.py +42 -0
- infrahub/core/diff/query/field_specifiers.py +8 -7
- infrahub/core/diff/query/field_summary.py +2 -4
- infrahub/core/diff/query/filters.py +15 -1
- infrahub/core/diff/query/merge.py +284 -101
- infrahub/core/diff/query/save.py +26 -34
- infrahub/core/diff/query/summary_counts_enricher.py +34 -54
- infrahub/core/diff/query_parser.py +55 -65
- infrahub/core/diff/repository/deserializer.py +38 -24
- infrahub/core/diff/repository/repository.py +31 -12
- infrahub/core/diff/tasks.py +3 -3
- infrahub/core/graph/__init__.py +1 -1
- infrahub/core/manager.py +14 -11
- infrahub/core/migrations/graph/__init__.py +2 -0
- infrahub/core/migrations/graph/m003_relationship_parent_optional.py +1 -2
- infrahub/core/migrations/graph/m013_convert_git_password_credential.py +2 -4
- infrahub/core/migrations/graph/m019_restore_rels_to_time.py +11 -22
- infrahub/core/migrations/graph/m020_duplicate_edges.py +3 -6
- infrahub/core/migrations/graph/m021_missing_hierarchy_merge.py +1 -2
- infrahub/core/migrations/graph/m024_missing_hierarchy_backfill.py +1 -2
- infrahub/core/migrations/graph/m027_delete_isolated_nodes.py +50 -0
- infrahub/core/migrations/graph/m028_delete_diffs.py +38 -0
- infrahub/core/migrations/query/attribute_add.py +1 -2
- infrahub/core/migrations/query/attribute_rename.py +3 -6
- infrahub/core/migrations/query/delete_element_in_schema.py +3 -6
- infrahub/core/migrations/query/node_duplicate.py +3 -6
- infrahub/core/migrations/query/relationship_duplicate.py +3 -6
- infrahub/core/migrations/schema/node_attribute_remove.py +3 -6
- infrahub/core/migrations/schema/node_remove.py +3 -6
- infrahub/core/models.py +29 -2
- infrahub/core/node/__init__.py +18 -4
- infrahub/core/node/create.py +211 -0
- infrahub/core/protocols.py +51 -0
- infrahub/core/protocols_base.py +3 -0
- infrahub/core/query/__init__.py +2 -2
- infrahub/core/query/branch.py +27 -17
- infrahub/core/query/diff.py +186 -81
- infrahub/core/query/ipam.py +10 -20
- infrahub/core/query/node.py +65 -49
- infrahub/core/query/relationship.py +156 -58
- infrahub/core/query/resource_manager.py +1 -2
- infrahub/core/query/subquery.py +4 -6
- infrahub/core/relationship/model.py +4 -1
- infrahub/core/schema/__init__.py +2 -1
- infrahub/core/schema/attribute_parameters.py +36 -0
- infrahub/core/schema/attribute_schema.py +83 -8
- infrahub/core/schema/basenode_schema.py +25 -1
- infrahub/core/schema/definitions/core/__init__.py +21 -0
- infrahub/core/schema/definitions/internal.py +13 -3
- infrahub/core/schema/generated/attribute_schema.py +9 -3
- infrahub/core/schema/schema_branch.py +15 -7
- infrahub/core/validators/__init__.py +5 -1
- infrahub/core/validators/attribute/choices.py +1 -2
- infrahub/core/validators/attribute/enum.py +1 -2
- infrahub/core/validators/attribute/kind.py +1 -2
- infrahub/core/validators/attribute/length.py +13 -6
- infrahub/core/validators/attribute/optional.py +1 -2
- infrahub/core/validators/attribute/regex.py +5 -5
- infrahub/core/validators/attribute/unique.py +1 -3
- infrahub/core/validators/determiner.py +18 -2
- infrahub/core/validators/enum.py +7 -0
- infrahub/core/validators/node/hierarchy.py +3 -6
- infrahub/core/validators/query.py +1 -3
- infrahub/core/validators/relationship/count.py +6 -12
- infrahub/core/validators/relationship/optional.py +2 -4
- infrahub/core/validators/relationship/peer.py +3 -8
- infrahub/core/validators/tasks.py +1 -1
- infrahub/core/validators/uniqueness/query.py +12 -9
- infrahub/database/__init__.py +1 -3
- infrahub/events/group_action.py +1 -0
- infrahub/graphql/analyzer.py +139 -18
- infrahub/graphql/app.py +1 -1
- infrahub/graphql/loaders/node.py +1 -1
- infrahub/graphql/loaders/peers.py +1 -1
- infrahub/graphql/manager.py +4 -0
- infrahub/graphql/mutations/action.py +164 -0
- infrahub/graphql/mutations/convert_object_type.py +62 -0
- infrahub/graphql/mutations/main.py +24 -175
- infrahub/graphql/mutations/proposed_change.py +21 -18
- infrahub/graphql/queries/convert_object_type_mapping.py +36 -0
- infrahub/graphql/queries/diff/tree.py +2 -1
- infrahub/graphql/queries/relationship.py +1 -1
- infrahub/graphql/resolvers/many_relationship.py +4 -4
- infrahub/graphql/resolvers/resolver.py +4 -4
- infrahub/graphql/resolvers/single_relationship.py +2 -2
- infrahub/graphql/schema.py +6 -0
- infrahub/graphql/subscription/graphql_query.py +2 -2
- infrahub/graphql/types/branch.py +1 -1
- infrahub/menu/menu.py +31 -0
- infrahub/message_bus/messages/__init__.py +0 -10
- infrahub/message_bus/operations/__init__.py +0 -8
- infrahub/message_bus/operations/refresh/registry.py +1 -1
- infrahub/patch/queries/consolidate_duplicated_nodes.py +3 -6
- infrahub/patch/queries/delete_duplicated_edges.py +5 -10
- infrahub/prefect_server/models.py +1 -19
- infrahub/proposed_change/models.py +68 -3
- infrahub/proposed_change/tasks.py +907 -30
- infrahub/task_manager/models.py +10 -6
- infrahub/telemetry/database.py +1 -1
- infrahub/telemetry/tasks.py +1 -1
- infrahub/trigger/catalogue.py +2 -0
- infrahub/trigger/models.py +29 -3
- infrahub/trigger/setup.py +51 -15
- infrahub/trigger/tasks.py +4 -5
- infrahub/types.py +1 -1
- infrahub/webhook/models.py +2 -1
- infrahub/workflows/catalogue.py +85 -0
- infrahub/workflows/initialization.py +1 -3
- infrahub_sdk/timestamp.py +2 -2
- {infrahub_server-1.2.9rc0.dist-info → infrahub_server-1.3.0a0.dist-info}/METADATA +4 -4
- {infrahub_server-1.2.9rc0.dist-info → infrahub_server-1.3.0a0.dist-info}/RECORD +153 -146
- infrahub_testcontainers/container.py +0 -1
- infrahub_testcontainers/docker-compose.test.yml +4 -4
- infrahub_testcontainers/helpers.py +8 -2
- infrahub_testcontainers/performance_test.py +6 -3
- infrahub/message_bus/messages/check_generator_run.py +0 -26
- infrahub/message_bus/messages/finalize_validator_execution.py +0 -15
- infrahub/message_bus/messages/proposed_change/base_with_diff.py +0 -16
- infrahub/message_bus/messages/proposed_change/request_proposedchange_refreshartifacts.py +0 -11
- infrahub/message_bus/messages/request_generatordefinition_check.py +0 -20
- infrahub/message_bus/messages/request_proposedchange_pipeline.py +0 -23
- infrahub/message_bus/operations/check/__init__.py +0 -3
- infrahub/message_bus/operations/check/generator.py +0 -156
- infrahub/message_bus/operations/finalize/__init__.py +0 -3
- infrahub/message_bus/operations/finalize/validator.py +0 -133
- infrahub/message_bus/operations/requests/__init__.py +0 -9
- infrahub/message_bus/operations/requests/generator_definition.py +0 -140
- infrahub/message_bus/operations/requests/proposed_change.py +0 -629
- /infrahub/{message_bus/messages/proposed_change → actions}/__init__.py +0 -0
- {infrahub_server-1.2.9rc0.dist-info → infrahub_server-1.3.0a0.dist-info}/LICENSE.txt +0 -0
- {infrahub_server-1.2.9rc0.dist-info → infrahub_server-1.3.0a0.dist-info}/WHEEL +0 -0
- {infrahub_server-1.2.9rc0.dist-info → infrahub_server-1.3.0a0.dist-info}/entry_points.txt +0 -0
|
@@ -16,6 +16,7 @@ from ..model.path import (
|
|
|
16
16
|
EnrichedDiffRoot,
|
|
17
17
|
EnrichedDiffRootMetadata,
|
|
18
18
|
EnrichedDiffSingleRelationship,
|
|
19
|
+
NodeIdentifier,
|
|
19
20
|
deserialize_tracking_id,
|
|
20
21
|
)
|
|
21
22
|
from ..parent_node_adder import DiffParentNodeAdder, ParentNodeAddRequest
|
|
@@ -25,13 +26,15 @@ class EnrichedDiffDeserializer:
|
|
|
25
26
|
def __init__(self, parent_adder: DiffParentNodeAdder) -> None:
|
|
26
27
|
self.parent_adder = parent_adder
|
|
27
28
|
self._diff_root_map: dict[str, EnrichedDiffRoot] = {}
|
|
28
|
-
self._diff_node_map: dict[tuple[str,
|
|
29
|
-
self._diff_node_attr_map: dict[tuple[str,
|
|
30
|
-
self._diff_node_rel_group_map: dict[tuple[str,
|
|
31
|
-
self._diff_node_rel_element_map: dict[tuple[str,
|
|
32
|
-
self._diff_prop_map: dict[
|
|
33
|
-
|
|
34
|
-
|
|
29
|
+
self._diff_node_map: dict[tuple[str, NodeIdentifier], EnrichedDiffNode] = {}
|
|
30
|
+
self._diff_node_attr_map: dict[tuple[str, NodeIdentifier, str], EnrichedDiffAttribute] = {}
|
|
31
|
+
self._diff_node_rel_group_map: dict[tuple[str, NodeIdentifier, str], EnrichedDiffRelationship] = {}
|
|
32
|
+
self._diff_node_rel_element_map: dict[tuple[str, NodeIdentifier, str, str], EnrichedDiffSingleRelationship] = {}
|
|
33
|
+
self._diff_prop_map: dict[
|
|
34
|
+
tuple[str, NodeIdentifier, str, str] | tuple[str, str, str, str, str], EnrichedDiffProperty
|
|
35
|
+
] = {}
|
|
36
|
+
# {EnrichedDiffRoot: [(NodeIdentifier, parents_path: Neo4jPath), ...]}
|
|
37
|
+
self._parents_path_map: dict[EnrichedDiffRoot, list[tuple[NodeIdentifier, Neo4jPath]]] = {}
|
|
35
38
|
|
|
36
39
|
def initialize(self) -> None:
|
|
37
40
|
self._diff_root_map = {}
|
|
@@ -42,10 +45,12 @@ class EnrichedDiffDeserializer:
|
|
|
42
45
|
self._diff_prop_map = {}
|
|
43
46
|
self._parents_path_map = {}
|
|
44
47
|
|
|
45
|
-
def _track_parents_path(
|
|
48
|
+
def _track_parents_path(
|
|
49
|
+
self, enriched_root: EnrichedDiffRoot, node_identifier: NodeIdentifier, parents_path: Neo4jPath
|
|
50
|
+
) -> None:
|
|
46
51
|
if enriched_root not in self._parents_path_map:
|
|
47
52
|
self._parents_path_map[enriched_root] = []
|
|
48
|
-
self._parents_path_map[enriched_root].append((
|
|
53
|
+
self._parents_path_map[enriched_root].append((node_identifier, parents_path))
|
|
49
54
|
|
|
50
55
|
async def read_result(self, result: QueryResult, include_parents: bool) -> None:
|
|
51
56
|
enriched_root = self._deserialize_diff_root(root_node=result.get_node("diff_root"))
|
|
@@ -58,7 +63,7 @@ class EnrichedDiffDeserializer:
|
|
|
58
63
|
parents_path = result.get("parents_path")
|
|
59
64
|
if parents_path and isinstance(parents_path, Neo4jPath):
|
|
60
65
|
self._track_parents_path(
|
|
61
|
-
enriched_root=enriched_root,
|
|
66
|
+
enriched_root=enriched_root, node_identifier=enriched_node.identifier, parents_path=parents_path
|
|
62
67
|
)
|
|
63
68
|
|
|
64
69
|
node_conflict_node = result.get(label="diff_node_conflict")
|
|
@@ -79,11 +84,13 @@ class EnrichedDiffDeserializer:
|
|
|
79
84
|
) -> None:
|
|
80
85
|
for attribute_result in result.get_nested_node_collection("diff_attributes"):
|
|
81
86
|
diff_attr_node, diff_attr_property_node, diff_attr_property_conflict = attribute_result
|
|
82
|
-
if diff_attr_node is None
|
|
87
|
+
if diff_attr_node is None:
|
|
83
88
|
continue
|
|
84
89
|
enriched_attribute = self._deserialize_diff_attr(
|
|
85
90
|
diff_attr_node=diff_attr_node, enriched_root=enriched_root, enriched_node=enriched_node
|
|
86
91
|
)
|
|
92
|
+
if diff_attr_property_node is None:
|
|
93
|
+
continue
|
|
87
94
|
enriched_property = self._deserialize_diff_attr_property(
|
|
88
95
|
diff_attr_property_node=diff_attr_property_node,
|
|
89
96
|
enriched_attr=enriched_attribute,
|
|
@@ -130,17 +137,21 @@ class EnrichedDiffDeserializer:
|
|
|
130
137
|
def _deserialize_parents(self) -> None:
|
|
131
138
|
for enriched_root, node_path_tuples in self._parents_path_map.items():
|
|
132
139
|
self.parent_adder.initialize(enriched_diff_root=enriched_root)
|
|
133
|
-
for
|
|
140
|
+
for node_identifier, parents_path in node_path_tuples:
|
|
134
141
|
# Remove the node itself from the path
|
|
135
142
|
parents_path_slice = parents_path.nodes[1:]
|
|
136
143
|
|
|
137
144
|
# TODO Ensure the list is even
|
|
138
|
-
|
|
145
|
+
current_node_identifier = node_identifier
|
|
139
146
|
for rel, parent in zip(parents_path_slice[::2], parents_path_slice[1::2], strict=False):
|
|
147
|
+
parent_identifier = NodeIdentifier(
|
|
148
|
+
uuid=parent.get("uuid"),
|
|
149
|
+
kind=parent.get("kind"),
|
|
150
|
+
db_id=parent.get("db_id"),
|
|
151
|
+
)
|
|
140
152
|
parent_request = ParentNodeAddRequest(
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
parent_kind=parent.get("kind"),
|
|
153
|
+
node_identifier=current_node_identifier,
|
|
154
|
+
parent_identifier=parent_identifier,
|
|
144
155
|
parent_label=parent.get("label"),
|
|
145
156
|
parent_rel_name=rel.get("name"),
|
|
146
157
|
parent_rel_identifier=rel.get("identifier"),
|
|
@@ -148,7 +159,7 @@ class EnrichedDiffDeserializer:
|
|
|
148
159
|
parent_rel_label=rel.get("label"),
|
|
149
160
|
)
|
|
150
161
|
self.parent_adder.add_parent(parent_request=parent_request)
|
|
151
|
-
|
|
162
|
+
current_node_identifier = parent_identifier
|
|
152
163
|
|
|
153
164
|
@classmethod
|
|
154
165
|
def _get_str_or_none_property_value(cls, node: Neo4jNode, property_name: str) -> str | None:
|
|
@@ -189,17 +200,20 @@ class EnrichedDiffDeserializer:
|
|
|
189
200
|
|
|
190
201
|
def _deserialize_diff_node(self, node_node: Neo4jNode, enriched_root: EnrichedDiffRoot) -> EnrichedDiffNode:
|
|
191
202
|
node_uuid = str(node_node.get("uuid"))
|
|
192
|
-
|
|
203
|
+
node_kind = str(node_node.get("kind"))
|
|
204
|
+
node_db_id = node_node.get("db_id")
|
|
205
|
+
node_identifier = NodeIdentifier(uuid=node_uuid, kind=node_kind, db_id=node_db_id)
|
|
206
|
+
node_key = (enriched_root.uuid, node_identifier)
|
|
193
207
|
if node_key in self._diff_node_map:
|
|
194
208
|
return self._diff_node_map[node_key]
|
|
195
209
|
|
|
196
210
|
timestamp_str = self._get_str_or_none_property_value(node=node_node, property_name="changed_at")
|
|
197
211
|
enriched_node = EnrichedDiffNode(
|
|
198
|
-
|
|
199
|
-
kind=str(node_node.get("kind")),
|
|
212
|
+
identifier=node_identifier,
|
|
200
213
|
label=str(node_node.get("label")),
|
|
201
214
|
changed_at=Timestamp(timestamp_str) if timestamp_str else None,
|
|
202
215
|
action=DiffAction(str(node_node.get("action"))),
|
|
216
|
+
is_node_kind_migration=bool(node_node.get("is_node_kind_migration")),
|
|
203
217
|
path_identifier=str(node_node.get("path_identifier")),
|
|
204
218
|
num_added=int(node_node.get("num_added", 0)),
|
|
205
219
|
num_updated=int(node_node.get("num_updated", 0)),
|
|
@@ -215,7 +229,7 @@ class EnrichedDiffDeserializer:
|
|
|
215
229
|
self, diff_attr_node: Neo4jNode, enriched_root: EnrichedDiffRoot, enriched_node: EnrichedDiffNode
|
|
216
230
|
) -> EnrichedDiffAttribute:
|
|
217
231
|
attr_name = str(diff_attr_node.get("name"))
|
|
218
|
-
attr_key = (enriched_root.uuid, enriched_node.
|
|
232
|
+
attr_key = (enriched_root.uuid, enriched_node.identifier, attr_name)
|
|
219
233
|
if attr_key in self._diff_node_attr_map:
|
|
220
234
|
return self._diff_node_attr_map[attr_key]
|
|
221
235
|
|
|
@@ -238,7 +252,7 @@ class EnrichedDiffDeserializer:
|
|
|
238
252
|
self, relationship_group_node: Neo4jNode, enriched_root: EnrichedDiffRoot, enriched_node: EnrichedDiffNode
|
|
239
253
|
) -> EnrichedDiffRelationship:
|
|
240
254
|
diff_rel_name = str(relationship_group_node.get("name"))
|
|
241
|
-
rel_key = (enriched_root.uuid, enriched_node.
|
|
255
|
+
rel_key = (enriched_root.uuid, enriched_node.identifier, diff_rel_name)
|
|
242
256
|
if rel_key in self._diff_node_rel_group_map:
|
|
243
257
|
return self._diff_node_rel_group_map[rel_key]
|
|
244
258
|
|
|
@@ -272,7 +286,7 @@ class EnrichedDiffDeserializer:
|
|
|
272
286
|
diff_element_peer_id = str(relationship_element_node.get("peer_id"))
|
|
273
287
|
rel_element_key = (
|
|
274
288
|
enriched_root.uuid,
|
|
275
|
-
enriched_node.
|
|
289
|
+
enriched_node.identifier,
|
|
276
290
|
enriched_relationship_group.name,
|
|
277
291
|
diff_element_peer_id,
|
|
278
292
|
)
|
|
@@ -320,7 +334,7 @@ class EnrichedDiffDeserializer:
|
|
|
320
334
|
enriched_root: EnrichedDiffRoot,
|
|
321
335
|
) -> EnrichedDiffProperty:
|
|
322
336
|
diff_prop_type = str(diff_attr_property_node.get("property_type"))
|
|
323
|
-
attr_property_key = (enriched_root.uuid, enriched_node.
|
|
337
|
+
attr_property_key = (enriched_root.uuid, enriched_node.identifier, enriched_attr.name, diff_prop_type)
|
|
324
338
|
if attr_property_key in self._diff_prop_map:
|
|
325
339
|
return self._diff_prop_map[attr_property_key]
|
|
326
340
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
from collections import defaultdict
|
|
2
1
|
from typing import AsyncGenerator, Generator, Iterable
|
|
3
2
|
|
|
4
3
|
from neo4j.exceptions import TransientError
|
|
5
4
|
|
|
6
5
|
from infrahub import config
|
|
7
6
|
from infrahub.core import registry
|
|
7
|
+
from infrahub.core.diff.query.drop_nodes import EnrichedDiffDropNodesQuery
|
|
8
8
|
from infrahub.core.diff.query.field_summary import EnrichedDiffNodeFieldSummaryQuery
|
|
9
9
|
from infrahub.core.diff.query.summary_counts_enricher import (
|
|
10
10
|
DiffFieldsSummaryCountsEnricherQuery,
|
|
@@ -16,6 +16,7 @@ from infrahub.database import InfrahubDatabase, retry_db_transaction
|
|
|
16
16
|
from infrahub.exceptions import ResourceNotFoundError
|
|
17
17
|
from infrahub.log import get_logger
|
|
18
18
|
|
|
19
|
+
from ..model.field_specifiers_map import NodeFieldSpecifierMap
|
|
19
20
|
from ..model.path import (
|
|
20
21
|
ConflictSelection,
|
|
21
22
|
EnrichedDiffConflict,
|
|
@@ -26,6 +27,7 @@ from ..model.path import (
|
|
|
26
27
|
EnrichedDiffsMetadata,
|
|
27
28
|
EnrichedNodeCreateRequest,
|
|
28
29
|
NodeDiffFieldSummary,
|
|
30
|
+
NodeIdentifier,
|
|
29
31
|
TimeRange,
|
|
30
32
|
TrackingId,
|
|
31
33
|
)
|
|
@@ -108,7 +110,7 @@ class DiffRepository:
|
|
|
108
110
|
diff_branch_names: list[str],
|
|
109
111
|
from_time: Timestamp | None = None,
|
|
110
112
|
to_time: Timestamp | None = None,
|
|
111
|
-
filters:
|
|
113
|
+
filters: EnrichedDiffQueryFilters | None = None,
|
|
112
114
|
include_parents: bool = True,
|
|
113
115
|
limit: int | None = None,
|
|
114
116
|
offset: int | None = None,
|
|
@@ -180,11 +182,11 @@ class DiffRepository:
|
|
|
180
182
|
async def hydrate_diff_pair(
|
|
181
183
|
self,
|
|
182
184
|
enriched_diffs_metadata: EnrichedDiffsMetadata,
|
|
183
|
-
|
|
185
|
+
node_identifiers: Iterable[NodeIdentifier] | None = None,
|
|
184
186
|
) -> EnrichedDiffs:
|
|
185
|
-
filters =
|
|
186
|
-
if
|
|
187
|
-
filters =
|
|
187
|
+
filters = EnrichedDiffQueryFilters()
|
|
188
|
+
if node_identifiers:
|
|
189
|
+
filters.identifiers = list(node_identifiers)
|
|
188
190
|
hydrated_base_diff = await self.get_one(
|
|
189
191
|
diff_branch_name=enriched_diffs_metadata.base_branch_name,
|
|
190
192
|
diff_id=enriched_diffs_metadata.base_branch_diff.uuid,
|
|
@@ -207,7 +209,7 @@ class DiffRepository:
|
|
|
207
209
|
diff_branch_name: str,
|
|
208
210
|
tracking_id: TrackingId | None = None,
|
|
209
211
|
diff_id: str | None = None,
|
|
210
|
-
filters:
|
|
212
|
+
filters: EnrichedDiffQueryFilters | None = None,
|
|
211
213
|
include_parents: bool = True,
|
|
212
214
|
) -> EnrichedDiffRoot:
|
|
213
215
|
enriched_diffs = await self.get(
|
|
@@ -274,6 +276,12 @@ class DiffRepository:
|
|
|
274
276
|
)
|
|
275
277
|
await single_node_query.execute(db=self.db)
|
|
276
278
|
|
|
279
|
+
async def _drop_nodes(self, diff_root: EnrichedDiffRoot, node_identifiers: list[NodeIdentifier]) -> None:
|
|
280
|
+
drop_node_query = await EnrichedDiffDropNodesQuery.init(
|
|
281
|
+
db=self.db, enriched_diff_uuid=diff_root.uuid, node_identifiers=node_identifiers
|
|
282
|
+
)
|
|
283
|
+
await drop_node_query.execute(db=self.db)
|
|
284
|
+
|
|
277
285
|
@retry_db_transaction(name="enriched_diff_hierarchy_update")
|
|
278
286
|
async def _run_hierarchy_links_update_query(self, diff_root_uuid: str, diff_nodes: list[EnrichedDiffNode]) -> None:
|
|
279
287
|
log.info(f"Updating diff hierarchy links, num_nodes={len(diff_nodes)}")
|
|
@@ -321,7 +329,12 @@ class DiffRepository:
|
|
|
321
329
|
node_uuids=node_uuids,
|
|
322
330
|
)
|
|
323
331
|
|
|
324
|
-
async def save(
|
|
332
|
+
async def save(
|
|
333
|
+
self,
|
|
334
|
+
enriched_diffs: EnrichedDiffs | EnrichedDiffsMetadata,
|
|
335
|
+
do_summary_counts: bool = True,
|
|
336
|
+
node_identifiers_to_drop: list[NodeIdentifier] | None = None,
|
|
337
|
+
) -> None:
|
|
325
338
|
# metadata-only update
|
|
326
339
|
if not isinstance(enriched_diffs, EnrichedDiffs):
|
|
327
340
|
await self._save_root_metadata(enriched_diffs=enriched_diffs)
|
|
@@ -336,6 +349,8 @@ class DiffRepository:
|
|
|
336
349
|
await self._save_node_batch(node_create_batch=node_create_batch)
|
|
337
350
|
count_nodes_remaining -= len(node_create_batch)
|
|
338
351
|
log.info(f"Batch saved. {count_nodes_remaining=}")
|
|
352
|
+
if node_identifiers_to_drop:
|
|
353
|
+
await self._drop_nodes(diff_root=enriched_diffs.diff_branch_diff, node_identifiers=node_identifiers_to_drop)
|
|
339
354
|
await self._update_hierarchy_links(enriched_diffs=enriched_diffs)
|
|
340
355
|
if do_summary_counts:
|
|
341
356
|
await self._update_summary_counts(diff_root=enriched_diffs.diff_branch_diff)
|
|
@@ -501,21 +516,25 @@ class DiffRepository:
|
|
|
501
516
|
await query.execute(db=self.db)
|
|
502
517
|
return query.get_num_changes_by_branch()
|
|
503
518
|
|
|
504
|
-
async def get_node_field_specifiers(self, diff_id: str) ->
|
|
519
|
+
async def get_node_field_specifiers(self, diff_id: str) -> NodeFieldSpecifierMap:
|
|
505
520
|
limit = config.SETTINGS.database.query_size_limit
|
|
506
521
|
offset = 0
|
|
507
|
-
|
|
522
|
+
specifiers_map = NodeFieldSpecifierMap()
|
|
508
523
|
while True:
|
|
509
524
|
query = await EnrichedDiffFieldSpecifiersQuery.init(db=self.db, diff_id=diff_id, offset=offset, limit=limit)
|
|
510
525
|
await query.execute(db=self.db)
|
|
511
526
|
has_data = False
|
|
512
527
|
for field_specifier_tuple in query.get_node_field_specifier_tuples():
|
|
513
|
-
|
|
528
|
+
specifiers_map.add_entry(
|
|
529
|
+
node_uuid=field_specifier_tuple[0],
|
|
530
|
+
kind=field_specifier_tuple[1],
|
|
531
|
+
field_name=field_specifier_tuple[2],
|
|
532
|
+
)
|
|
514
533
|
has_data = True
|
|
515
534
|
if not has_data:
|
|
516
535
|
break
|
|
517
536
|
offset += limit
|
|
518
|
-
return
|
|
537
|
+
return specifiers_map
|
|
519
538
|
|
|
520
539
|
async def add_summary_counts(
|
|
521
540
|
self,
|
infrahub/core/diff/tasks.py
CHANGED
|
@@ -20,7 +20,7 @@ log = get_logger()
|
|
|
20
20
|
async def update_diff(model: RequestDiffUpdate, service: InfrahubServices) -> None:
|
|
21
21
|
await add_tags(branches=[model.branch_name])
|
|
22
22
|
|
|
23
|
-
async with service.database.start_session() as db:
|
|
23
|
+
async with service.database.start_session(read_only=False) as db:
|
|
24
24
|
component_registry = get_component_registry()
|
|
25
25
|
base_branch = await registry.get_branch(db=db, branch=registry.default_branch)
|
|
26
26
|
diff_branch = await registry.get_branch(db=db, branch=model.branch_name)
|
|
@@ -40,7 +40,7 @@ async def update_diff(model: RequestDiffUpdate, service: InfrahubServices) -> No
|
|
|
40
40
|
async def refresh_diff(branch_name: str, diff_id: str, service: InfrahubServices) -> None:
|
|
41
41
|
await add_tags(branches=[branch_name])
|
|
42
42
|
|
|
43
|
-
async with service.database.start_session() as db:
|
|
43
|
+
async with service.database.start_session(read_only=False) as db:
|
|
44
44
|
component_registry = get_component_registry()
|
|
45
45
|
base_branch = await registry.get_branch(db=db, branch=registry.default_branch)
|
|
46
46
|
diff_branch = await registry.get_branch(db=db, branch=branch_name)
|
|
@@ -53,7 +53,7 @@ async def refresh_diff(branch_name: str, diff_id: str, service: InfrahubServices
|
|
|
53
53
|
async def refresh_diff_all(branch_name: str, context: InfrahubContext, service: InfrahubServices) -> None:
|
|
54
54
|
await add_tags(branches=[branch_name])
|
|
55
55
|
|
|
56
|
-
async with service.database.start_session() as db:
|
|
56
|
+
async with service.database.start_session(read_only=True) as db:
|
|
57
57
|
component_registry = get_component_registry()
|
|
58
58
|
default_branch = registry.get_branch_from_registry()
|
|
59
59
|
diff_repository = await component_registry.get_component(DiffRepository, db=db, branch=default_branch)
|
infrahub/core/graph/__init__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
GRAPH_VERSION =
|
|
1
|
+
GRAPH_VERSION = 28
|
infrahub/core/manager.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
from copy import copy
|
|
3
4
|
from functools import reduce
|
|
4
5
|
from typing import TYPE_CHECKING, Any, Iterable, Literal, TypeVar, overload
|
|
5
6
|
|
|
@@ -1339,22 +1340,24 @@ class NodeManager:
|
|
|
1339
1340
|
nodes: list[Node],
|
|
1340
1341
|
branch: Branch | str | None = None,
|
|
1341
1342
|
at: Timestamp | str | None = None,
|
|
1343
|
+
cascade_delete: bool = True,
|
|
1342
1344
|
) -> list[Node]:
|
|
1343
1345
|
"""Returns list of deleted nodes because of cascading deletes"""
|
|
1344
1346
|
branch = await registry.get_branch(branch=branch, db=db)
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1347
|
+
nodes_to_delete = copy(nodes)
|
|
1348
|
+
if cascade_delete:
|
|
1349
|
+
node_delete_validator = NodeDeleteValidator(db=db, branch=branch)
|
|
1350
|
+
ids_to_delete = await node_delete_validator.get_ids_to_delete(nodes=nodes, at=at)
|
|
1351
|
+
node_ids = {node.get_id() for node in nodes}
|
|
1352
|
+
missing_ids_to_delete = ids_to_delete - node_ids
|
|
1353
|
+
if missing_ids_to_delete:
|
|
1354
|
+
node_map = await cls.get_many(db=db, ids=list(missing_ids_to_delete), branch=branch, at=at)
|
|
1355
|
+
nodes_to_delete += list(node_map.values())
|
|
1356
|
+
|
|
1357
|
+
for node in nodes_to_delete:
|
|
1354
1358
|
await node.delete(db=db, at=at)
|
|
1355
|
-
deleted_nodes.append(node)
|
|
1356
1359
|
|
|
1357
|
-
return
|
|
1360
|
+
return nodes_to_delete
|
|
1358
1361
|
|
|
1359
1362
|
|
|
1360
1363
|
def _get_cardinality_one_identifiers_by_kind(
|
|
@@ -28,6 +28,7 @@ from .m023_deduplicate_cardinality_one_relationships import Migration023
|
|
|
28
28
|
from .m024_missing_hierarchy_backfill import Migration024
|
|
29
29
|
from .m025_uniqueness_nulls import Migration025
|
|
30
30
|
from .m026_0000_prefix_fix import Migration026
|
|
31
|
+
from .m027_delete_isolated_nodes import Migration027
|
|
31
32
|
|
|
32
33
|
if TYPE_CHECKING:
|
|
33
34
|
from infrahub.core.root import Root
|
|
@@ -61,6 +62,7 @@ MIGRATIONS: list[type[GraphMigration | InternalSchemaMigration | ArbitraryMigrat
|
|
|
61
62
|
Migration024,
|
|
62
63
|
Migration025,
|
|
63
64
|
Migration026,
|
|
65
|
+
Migration027,
|
|
64
66
|
]
|
|
65
67
|
|
|
66
68
|
|
|
@@ -26,8 +26,7 @@ class Migration003Query01(Query):
|
|
|
26
26
|
query = """
|
|
27
27
|
MATCH path = (av2:AttributeValue)-[:HAS_VALUE]-(:Attribute {name: "optional"})-[:HAS_ATTRIBUTE]-(n:SchemaRelationship)-[:HAS_ATTRIBUTE]-(:Attribute {name: "kind"})-[:HAS_VALUE]-(av1:AttributeValue)
|
|
28
28
|
WHERE av1.value = "Parent" AND av2.value = true AND all(r IN relationships(path) WHERE ( %(filters)s ))
|
|
29
|
-
CALL {
|
|
30
|
-
WITH n
|
|
29
|
+
CALL (n) {
|
|
31
30
|
MATCH path22 = (av22:AttributeValue)-[r22:HAS_VALUE]-(a2:Attribute {name: "optional"})-[:HAS_ATTRIBUTE]-(n:SchemaRelationship)-[:HAS_ATTRIBUTE]-(:Attribute {name:"kind"})-[:HAS_VALUE]-(av11:AttributeValue)
|
|
32
31
|
WHERE av11.value = "Parent" AND av22.value = true AND all(r IN relationships(path22) WHERE ( %(filters)s ))
|
|
33
32
|
RETURN av22 as av2_sub, r22, a2, path22 as path2
|
|
@@ -98,8 +98,7 @@ class Migration013ConvertCoreRepositoryWithCred(Query):
|
|
|
98
98
|
// --------------------------------
|
|
99
99
|
MATCH (git_repo)-[:HAS_ATTRIBUTE]-(git_attr_name:Attribute)-[:HAS_VALUE]->(git_name_value:AttributeValue)
|
|
100
100
|
WHERE git_attr_name.name = "name"
|
|
101
|
-
CALL {
|
|
102
|
-
WITH git_repo
|
|
101
|
+
CALL (git_repo) {
|
|
103
102
|
MATCH path1 = (git_repo)-[r1:HAS_ATTRIBUTE]-(git_attr_name2:Attribute)-[r2:HAS_VALUE]->(git_name_value2:AttributeValue)
|
|
104
103
|
WHERE git_attr_name2.name = "name"
|
|
105
104
|
AND all(r IN relationships(path1) WHERE %(filters)s)
|
|
@@ -204,8 +203,7 @@ class Migration013ConvertCoreRepositoryWithoutCred(Query):
|
|
|
204
203
|
WHERE a.name in ["username", "password"]
|
|
205
204
|
AND av.value = "NULL"
|
|
206
205
|
AND all(r IN relationships(path) WHERE %(filters)s AND r.status = "active")
|
|
207
|
-
CALL {
|
|
208
|
-
WITH node
|
|
206
|
+
CALL (node) {
|
|
209
207
|
MATCH (root:Root)<-[r:IS_PART_OF]-(node)
|
|
210
208
|
WHERE %(filters)s
|
|
211
209
|
RETURN node as n1, r as r1
|
|
@@ -97,8 +97,7 @@ class DeleteNodesRelsQuery(Query):
|
|
|
97
97
|
// - on deleted edge branch
|
|
98
98
|
// - or on any branch and deleted node is agnostic
|
|
99
99
|
// - or deleted node is aware and rel is agnostic
|
|
100
|
-
CALL {
|
|
101
|
-
WITH rel, deleted_edge
|
|
100
|
+
CALL (rel, deleted_edge) {
|
|
102
101
|
OPTIONAL MATCH (rel)-[peer_active_edge {status: "active"}]-(peer_1)
|
|
103
102
|
WHERE (peer_active_edge.branch = deleted_edge.branch OR (rel.branch_support <> $branch_agnostic AND deleted_edge.branch = $global_branch))
|
|
104
103
|
AND peer_active_edge.to IS NULL
|
|
@@ -160,62 +159,52 @@ class DeleteNodesRelsQuery(Query):
|
|
|
160
159
|
// Below CALL subqueries are called once for each rel-peer_2 pair for which we want to create a deleted edge.
|
|
161
160
|
// Note that with current infrahub relationships edges design, only one of this CALL should be matched per pair.
|
|
162
161
|
|
|
163
|
-
CALL {
|
|
164
|
-
WITH rel, peer_2, branch, branch_level, deleted_time
|
|
162
|
+
CALL (rel, peer_2, branch, branch_level, deleted_time) {
|
|
165
163
|
MATCH (rel)-[:IS_RELATED]->(peer_2)
|
|
166
164
|
MERGE (rel)-[:IS_RELATED {status: "deleted", branch: branch, branch_level: branch_level, from: deleted_time}]->(peer_2)
|
|
167
165
|
}
|
|
168
166
|
|
|
169
|
-
CALL {
|
|
170
|
-
WITH rel, peer_2, branch, branch_level, deleted_time
|
|
167
|
+
CALL (rel, peer_2, branch, branch_level, deleted_time) {
|
|
171
168
|
MATCH (rel)-[:IS_PROTECTED]->(peer_2)
|
|
172
169
|
MERGE (rel)-[:IS_PROTECTED {status: "deleted", branch: branch, branch_level: branch_level, from: deleted_time}]->(peer_2)
|
|
173
170
|
}
|
|
174
171
|
|
|
175
|
-
CALL {
|
|
176
|
-
WITH rel, peer_2, branch, branch_level, deleted_time
|
|
172
|
+
CALL (rel, peer_2, branch, branch_level, deleted_time) {
|
|
177
173
|
MATCH (rel)-[:IS_VISIBLE]->(peer_2)
|
|
178
174
|
MERGE (rel)-[:IS_VISIBLE {status: "deleted", branch: branch, branch_level: branch_level, from: deleted_time}]->(peer_2)
|
|
179
175
|
}
|
|
180
176
|
|
|
181
|
-
CALL {
|
|
182
|
-
WITH rel, peer_2, branch, branch_level, deleted_time
|
|
177
|
+
CALL (rel, peer_2, branch, branch_level, deleted_time) {
|
|
183
178
|
MATCH (rel)-[:HAS_OWNER]->(peer_2)
|
|
184
179
|
MERGE (rel)-[:HAS_OWNER {status: "deleted", branch: branch, branch_level: branch_level, from: deleted_time}]->(peer_2)
|
|
185
180
|
}
|
|
186
181
|
|
|
187
|
-
CALL {
|
|
188
|
-
WITH rel, peer_2, branch, branch_level, deleted_time
|
|
182
|
+
CALL (rel, peer_2, branch, branch_level, deleted_time) {
|
|
189
183
|
MATCH (rel)-[:HAS_SOURCE]->(peer_2)
|
|
190
184
|
MERGE (rel)-[:HAS_SOURCE {status: "deleted", branch: branch, branch_level: branch_level, from: deleted_time}]->(peer_2)
|
|
191
185
|
}
|
|
192
186
|
|
|
193
|
-
CALL {
|
|
194
|
-
WITH rel, peer_2, branch, branch_level, deleted_time
|
|
187
|
+
CALL (rel, peer_2, branch, branch_level, deleted_time) {
|
|
195
188
|
MATCH (rel)<-[:IS_RELATED]-(peer_2)
|
|
196
189
|
MERGE (rel)<-[:IS_RELATED {status: "deleted", branch: branch, branch_level: branch_level, from: deleted_time}]-(peer_2)
|
|
197
190
|
}
|
|
198
191
|
|
|
199
|
-
CALL {
|
|
200
|
-
WITH rel, peer_2, branch, branch_level, deleted_time
|
|
192
|
+
CALL (rel, peer_2, branch, branch_level, deleted_time) {
|
|
201
193
|
MATCH (rel)<-[:IS_PROTECTED]-(peer_2)
|
|
202
194
|
MERGE (rel)<-[:IS_PROTECTED {status: "deleted", branch: branch, branch_level: branch_level, from: deleted_time}]-(peer_2)
|
|
203
195
|
}
|
|
204
196
|
|
|
205
|
-
CALL {
|
|
206
|
-
WITH rel, peer_2, branch, branch_level, deleted_time
|
|
197
|
+
CALL (rel, peer_2, branch, branch_level, deleted_time) {
|
|
207
198
|
MATCH (rel)<-[:IS_VISIBLE]-(peer_2)
|
|
208
199
|
MERGE (rel)<-[:IS_VISIBLE {status: "deleted", branch: branch, branch_level: branch_level, from: deleted_time}]-(peer_2)
|
|
209
200
|
}
|
|
210
201
|
|
|
211
|
-
CALL {
|
|
212
|
-
WITH rel, peer_2, branch, branch_level, deleted_time
|
|
202
|
+
CALL (rel, peer_2, branch, branch_level, deleted_time) {
|
|
213
203
|
MATCH (rel)<-[:HAS_OWNER]-(peer_2)
|
|
214
204
|
MERGE (rel)<-[:HAS_OWNER {status: "deleted", branch: branch, branch_level: branch_level, from: deleted_time}]-(peer_2)
|
|
215
205
|
}
|
|
216
206
|
|
|
217
|
-
CALL {
|
|
218
|
-
WITH rel, peer_2, branch, branch_level, deleted_time
|
|
207
|
+
CALL (rel, peer_2, branch, branch_level, deleted_time) {
|
|
219
208
|
MATCH (rel)<-[:HAS_SOURCE]-(peer_2)
|
|
220
209
|
MERGE (rel)<-[:HAS_SOURCE {status: "deleted", branch: branch, branch_level: branch_level, from: deleted_time}]-(peer_2)
|
|
221
210
|
}
|
|
@@ -33,8 +33,7 @@ WHERE num_duplicate_edges > 1
|
|
|
33
33
|
// -------------------
|
|
34
34
|
WITH DISTINCT a, branch, branch_level, status, from, to, attr_val, attr_default
|
|
35
35
|
WITH attr_val, attr_default, collect([a, branch, branch_level, status, from, to]) AS details_list
|
|
36
|
-
CALL {
|
|
37
|
-
WITH attr_val, attr_default
|
|
36
|
+
CALL (attr_val, attr_default) {
|
|
38
37
|
MATCH (av:AttributeValue {value: attr_val, is_default: attr_default})
|
|
39
38
|
RETURN av AS the_one_av
|
|
40
39
|
ORDER by %(id_func)s(av) ASC
|
|
@@ -53,11 +52,10 @@ WITH a, branch, status, from, to, attr_val, attr_default, %(id_func)s(fresh_e) A
|
|
|
53
52
|
// -------------------
|
|
54
53
|
// get the identical edges for a given set of Attribute node, edge properties, AttributeValue.value
|
|
55
54
|
// -------------------
|
|
56
|
-
CALL {
|
|
55
|
+
CALL (a, branch, status, from, to, attr_val, attr_default, e_id_to_keep) {
|
|
57
56
|
// -------------------
|
|
58
57
|
// delete the duplicate edges a given set of Attribute node, edge properties, AttributeValue.value
|
|
59
58
|
// -------------------
|
|
60
|
-
WITH a, branch, status, from, to, attr_val, attr_default, e_id_to_keep
|
|
61
59
|
MATCH (a)-[e:HAS_VALUE]->(av:AttributeValue {value: attr_val, is_default: attr_default})
|
|
62
60
|
WHERE %(id_func)s(e) <> e_id_to_keep
|
|
63
61
|
AND e.branch = branch AND e.status = status AND e.from = from
|
|
@@ -99,8 +97,7 @@ WITH DISTINCT a, branch, branch_level, status, from, to, b
|
|
|
99
97
|
CREATE (a)-[fresh_e:%(edge_type)s {branch: branch, branch_level: branch_level, status: status, from: from}]->(b)
|
|
100
98
|
SET fresh_e.to = to
|
|
101
99
|
WITH a, branch, status, from, to, b, %(id_func)s(fresh_e) AS e_id_to_keep
|
|
102
|
-
CALL {
|
|
103
|
-
WITH a, branch, status, from, to, b, e_id_to_keep
|
|
100
|
+
CALL (a, branch, status, from, to, b, e_id_to_keep) {
|
|
104
101
|
MATCH (a)-[e:%(edge_type)s]->(b)
|
|
105
102
|
WHERE %(id_func)s(e) <> e_id_to_keep
|
|
106
103
|
AND e.branch = branch AND e.status = status AND e.from = from
|
|
@@ -24,8 +24,7 @@ class SetMissingHierarchyQuery(Query):
|
|
|
24
24
|
WITH r.default_branch AS default_branch
|
|
25
25
|
MATCH (n:Node)-[main_e:IS_RELATED {branch: default_branch}]-(rel:Relationship)
|
|
26
26
|
WHERE main_e.hierarchy IS NULL
|
|
27
|
-
CALL {
|
|
28
|
-
WITH n, main_e, rel
|
|
27
|
+
CALL (n, main_e, rel) {
|
|
29
28
|
MATCH (n)-[branch_e:IS_RELATED]-(rel)
|
|
30
29
|
WHERE branch_e.hierarchy IS NOT NULL
|
|
31
30
|
AND branch_e.branch <> main_e.branch
|
|
@@ -39,8 +39,7 @@ class BackfillMissingHierarchyQuery(Query):
|
|
|
39
39
|
MATCH (rel:Relationship {name: "parent__child"})-[e:IS_RELATED]-(n:Node)
|
|
40
40
|
WHERE e.hierarchy IS NULL
|
|
41
41
|
WITH DISTINCT rel, n, default_branch
|
|
42
|
-
CALL {
|
|
43
|
-
WITH rel, n, default_branch
|
|
42
|
+
CALL (rel, n, default_branch) {
|
|
44
43
|
MATCH (rel)-[e:IS_RELATED {branch: default_branch}]-(n)
|
|
45
44
|
RETURN e
|
|
46
45
|
ORDER BY e.from DESC
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING, Any, Sequence
|
|
4
|
+
|
|
5
|
+
from infrahub.core.migrations.shared import GraphMigration, MigrationResult
|
|
6
|
+
from infrahub.log import get_logger
|
|
7
|
+
|
|
8
|
+
from ...query import Query, QueryType
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from infrahub.database import InfrahubDatabase
|
|
12
|
+
|
|
13
|
+
log = get_logger()
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class DeleteIsolatedNodesQuery(Query):
|
|
17
|
+
name = "delete_isolated_nodes_query"
|
|
18
|
+
type = QueryType.WRITE
|
|
19
|
+
insert_return = False
|
|
20
|
+
|
|
21
|
+
async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None: # noqa: ARG002
|
|
22
|
+
query = """
|
|
23
|
+
MATCH p = (s: Node)-[r]-(d)
|
|
24
|
+
WHERE NOT exists((s)-[:IS_PART_OF]-(:Root))
|
|
25
|
+
DELETE r
|
|
26
|
+
|
|
27
|
+
WITH p
|
|
28
|
+
UNWIND nodes(p) AS n
|
|
29
|
+
MATCH (n)
|
|
30
|
+
WHERE NOT exists((n)--())
|
|
31
|
+
DELETE n
|
|
32
|
+
"""
|
|
33
|
+
self.add_to_query(query)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class Migration027(GraphMigration):
|
|
37
|
+
"""
|
|
38
|
+
While deleting a branch containing some allocated nodes from a resource pool, relationship
|
|
39
|
+
between pool node and resource node might be agnostic (eg: for IPPrefixPool) and incorrectly deleted,
|
|
40
|
+
resulting in a node still linked to the resource pool but not linked to Root anymore.
|
|
41
|
+
This query deletes nodes not linked to Root and their relationships (supposed to be agnostic).
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
name: str = "027_deleted_isolated_nodes"
|
|
45
|
+
minimum_version: int = 26
|
|
46
|
+
queries: Sequence[type[Query]] = [DeleteIsolatedNodesQuery]
|
|
47
|
+
|
|
48
|
+
async def validate_migration(self, db: InfrahubDatabase) -> MigrationResult: # noqa: ARG002
|
|
49
|
+
result = MigrationResult()
|
|
50
|
+
return result
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
from infrahub.core import registry
|
|
6
|
+
from infrahub.core.diff.repository.repository import DiffRepository
|
|
7
|
+
from infrahub.core.migrations.shared import MigrationResult
|
|
8
|
+
from infrahub.dependencies.registry import build_component_registry, get_component_registry
|
|
9
|
+
from infrahub.log import get_logger
|
|
10
|
+
|
|
11
|
+
from ..shared import ArbitraryMigration
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from infrahub.database import InfrahubDatabase
|
|
15
|
+
|
|
16
|
+
log = get_logger()
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class Migration028(ArbitraryMigration):
|
|
20
|
+
"""Delete all diffs because of an update to how we store diff information. All diffs will need to be recalculated"""
|
|
21
|
+
|
|
22
|
+
name: str = "028_diff_delete_bug_fix_update"
|
|
23
|
+
minimum_version: int = 27
|
|
24
|
+
|
|
25
|
+
async def validate_migration(self, db: InfrahubDatabase) -> MigrationResult: # noqa: ARG002
|
|
26
|
+
result = MigrationResult()
|
|
27
|
+
|
|
28
|
+
return result
|
|
29
|
+
|
|
30
|
+
async def execute(self, db: InfrahubDatabase) -> MigrationResult:
|
|
31
|
+
default_branch = registry.get_branch_from_registry()
|
|
32
|
+
build_component_registry()
|
|
33
|
+
component_registry = get_component_registry()
|
|
34
|
+
diff_repo = await component_registry.get_component(DiffRepository, db=db, branch=default_branch)
|
|
35
|
+
|
|
36
|
+
diff_roots = await diff_repo.get_roots_metadata()
|
|
37
|
+
await diff_repo.delete_diff_roots(diff_root_uuids=[d.uuid for d in diff_roots])
|
|
38
|
+
return MigrationResult()
|
|
@@ -61,8 +61,7 @@ class AttributeAddQuery(Query):
|
|
|
61
61
|
MERGE (is_visible_value:Boolean { value: $is_visible_default })
|
|
62
62
|
WITH av, is_protected_value, is_visible_value
|
|
63
63
|
MATCH p = (n:%(node_kind)s)
|
|
64
|
-
CALL {
|
|
65
|
-
WITH n
|
|
64
|
+
CALL (n) {
|
|
66
65
|
MATCH (root:Root)<-[r1:IS_PART_OF]-(n)
|
|
67
66
|
OPTIONAL MATCH (n)-[r2:HAS_ATTRIBUTE]-(:Attribute { name: $attr_name })
|
|
68
67
|
WHERE all(r in [r1, r2] WHERE (%(branch_filter)s))
|