infrahub-server 1.2.9rc0__py3-none-any.whl → 1.2.11__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- infrahub/computed_attribute/models.py +13 -0
- infrahub/computed_attribute/tasks.py +48 -26
- infrahub/config.py +9 -0
- infrahub/core/attribute.py +43 -2
- infrahub/core/branch/models.py +8 -9
- infrahub/core/branch/tasks.py +0 -2
- infrahub/core/constants/infrahubkind.py +1 -0
- infrahub/core/constraint/node/runner.py +1 -1
- infrahub/core/diff/calculator.py +65 -11
- infrahub/core/diff/combiner.py +38 -31
- infrahub/core/diff/coordinator.py +44 -28
- infrahub/core/diff/data_check_synchronizer.py +3 -2
- infrahub/core/diff/enricher/hierarchy.py +36 -27
- infrahub/core/diff/ipam_diff_parser.py +5 -4
- infrahub/core/diff/merger/merger.py +46 -16
- infrahub/core/diff/merger/serializer.py +1 -0
- infrahub/core/diff/model/field_specifiers_map.py +64 -0
- infrahub/core/diff/model/path.py +58 -58
- infrahub/core/diff/parent_node_adder.py +14 -16
- infrahub/core/diff/query/drop_nodes.py +42 -0
- infrahub/core/diff/query/field_specifiers.py +8 -7
- infrahub/core/diff/query/filters.py +15 -1
- infrahub/core/diff/query/merge.py +264 -28
- infrahub/core/diff/query/save.py +6 -2
- infrahub/core/diff/query_parser.py +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/migrations/graph/__init__.py +2 -0
- infrahub/core/migrations/graph/m027_delete_isolated_nodes.py +50 -0
- infrahub/core/migrations/graph/m028_delete_diffs.py +38 -0
- infrahub/core/node/resource_manager/ip_address_pool.py +6 -2
- infrahub/core/node/resource_manager/ip_prefix_pool.py +6 -2
- infrahub/core/protocols.py +4 -0
- infrahub/core/query/branch.py +27 -17
- infrahub/core/query/diff.py +169 -51
- infrahub/core/query/node.py +39 -5
- infrahub/core/query/relationship.py +105 -30
- infrahub/core/query/subquery.py +2 -2
- infrahub/core/relationship/model.py +1 -1
- infrahub/core/schema/definitions/core/__init__.py +8 -1
- infrahub/core/schema/definitions/core/resource_pool.py +20 -0
- infrahub/core/schema/schema_branch.py +3 -0
- infrahub/core/validators/tasks.py +1 -1
- infrahub/core/validators/uniqueness/query.py +7 -0
- infrahub/database/__init__.py +5 -4
- infrahub/graphql/app.py +1 -1
- infrahub/graphql/loaders/node.py +1 -1
- infrahub/graphql/loaders/peers.py +1 -1
- infrahub/graphql/mutations/proposed_change.py +1 -1
- infrahub/graphql/queries/diff/tree.py +2 -1
- infrahub/graphql/queries/relationship.py +1 -1
- infrahub/graphql/queries/task.py +10 -0
- 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/subscription/graphql_query.py +2 -2
- infrahub/graphql/types/branch.py +1 -1
- infrahub/graphql/types/task_log.py +3 -2
- infrahub/message_bus/operations/refresh/registry.py +1 -1
- infrahub/task_manager/task.py +44 -4
- infrahub/telemetry/database.py +1 -1
- infrahub/telemetry/tasks.py +1 -1
- infrahub/trigger/models.py +11 -1
- infrahub/trigger/setup.py +51 -15
- infrahub/trigger/tasks.py +1 -4
- infrahub/types.py +1 -1
- infrahub/webhook/models.py +2 -1
- infrahub/workflows/catalogue.py +9 -0
- infrahub/workflows/initialization.py +1 -3
- infrahub_sdk/timestamp.py +2 -2
- {infrahub_server-1.2.9rc0.dist-info → infrahub_server-1.2.11.dist-info}/METADATA +3 -3
- {infrahub_server-1.2.9rc0.dist-info → infrahub_server-1.2.11.dist-info}/RECORD +79 -75
- infrahub_testcontainers/docker-compose.test.yml +3 -3
- infrahub_testcontainers/performance_test.py +6 -3
- {infrahub_server-1.2.9rc0.dist-info → infrahub_server-1.2.11.dist-info}/LICENSE.txt +0 -0
- {infrahub_server-1.2.9rc0.dist-info → infrahub_server-1.2.11.dist-info}/WHEEL +0 -0
- {infrahub_server-1.2.9rc0.dist-info → infrahub_server-1.2.11.dist-info}/entry_points.txt +0 -0
|
@@ -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()
|
|
@@ -81,11 +81,15 @@ class CoreIPAddressPool(Node):
|
|
|
81
81
|
return node
|
|
82
82
|
|
|
83
83
|
async def get_next(self, db: InfrahubDatabase, prefixlen: int | None = None) -> IPAddressType:
|
|
84
|
-
# Measure utilization of all prefixes identified as resources
|
|
85
84
|
resources = await self.resources.get_peers(db=db) # type: ignore[attr-defined]
|
|
86
85
|
ip_namespace = await self.ip_namespace.get_peer(db=db) # type: ignore[attr-defined]
|
|
87
86
|
|
|
88
|
-
|
|
87
|
+
try:
|
|
88
|
+
weighted_resources = sorted(resources.values(), key=lambda r: r.allocation_weight.value or 0, reverse=True)
|
|
89
|
+
except AttributeError:
|
|
90
|
+
weighted_resources = list(resources.values())
|
|
91
|
+
|
|
92
|
+
for resource in weighted_resources:
|
|
89
93
|
ip_prefix = ipaddress.ip_network(resource.prefix.value) # type: ignore[attr-defined]
|
|
90
94
|
prefix_length = prefixlen or ip_prefix.prefixlen
|
|
91
95
|
|
|
@@ -88,11 +88,15 @@ class CoreIPPrefixPool(Node):
|
|
|
88
88
|
return node
|
|
89
89
|
|
|
90
90
|
async def get_next(self, db: InfrahubDatabase, prefixlen: int) -> IPNetworkType:
|
|
91
|
-
# Measure utilization of all prefixes identified as resources
|
|
92
91
|
resources = await self.resources.get_peers(db=db) # type: ignore[attr-defined]
|
|
93
92
|
ip_namespace = await self.ip_namespace.get_peer(db=db) # type: ignore[attr-defined]
|
|
94
93
|
|
|
95
|
-
|
|
94
|
+
try:
|
|
95
|
+
weighted_resources = sorted(resources.values(), key=lambda r: r.allocation_weight.value or 0, reverse=True)
|
|
96
|
+
except AttributeError:
|
|
97
|
+
weighted_resources = list(resources.values())
|
|
98
|
+
|
|
99
|
+
for resource in weighted_resources:
|
|
96
100
|
subnets = await get_subnets(
|
|
97
101
|
db=db,
|
|
98
102
|
ip_prefix=ipaddress.ip_network(resource.prefix.value), # type: ignore[attr-defined]
|
infrahub/core/protocols.py
CHANGED
infrahub/core/query/branch.py
CHANGED
|
@@ -51,23 +51,33 @@ class DeleteBranchRelationshipsQuery(Query):
|
|
|
51
51
|
super().__init__(**kwargs)
|
|
52
52
|
|
|
53
53
|
async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None: # noqa: ARG002
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
54
|
+
query = """
|
|
55
|
+
MATCH (s)-[r1]-(d)
|
|
56
|
+
WHERE r1.branch = $branch_name
|
|
57
|
+
DELETE r1
|
|
58
|
+
|
|
59
|
+
WITH collect(DISTINCT s) + collect(DISTINCT d) AS nodes
|
|
60
|
+
|
|
61
|
+
// Collect node IDs for filtering
|
|
62
|
+
WITH nodes, [n in nodes | n.uuid] as nodes_uuids
|
|
63
|
+
|
|
64
|
+
// Also delete agnostic relationships that would not have been deleted above
|
|
65
|
+
MATCH (s2: Node)-[r2]-(d2)
|
|
66
|
+
WHERE NOT exists((s2)-[:IS_PART_OF]-(:Root))
|
|
67
|
+
AND s2.uuid IN nodes_uuids
|
|
68
|
+
DELETE r2
|
|
69
|
+
|
|
70
|
+
WITH nodes, collect(DISTINCT s2) + collect(DISTINCT d2) as additional_nodes
|
|
71
|
+
|
|
72
|
+
WITH nodes + additional_nodes as nodes
|
|
73
|
+
|
|
74
|
+
// Delete nodes that are no longer connected to any other nodes
|
|
75
|
+
UNWIND nodes AS n
|
|
76
|
+
WITH DISTINCT n
|
|
77
|
+
MATCH (n)
|
|
78
|
+
WHERE NOT exists((n)--())
|
|
79
|
+
DELETE n
|
|
80
|
+
"""
|
|
71
81
|
self.params["branch_name"] = self.branch_name
|
|
72
82
|
self.add_to_query(query)
|
|
73
83
|
|
infrahub/core/query/diff.py
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import TYPE_CHECKING, Any, Generator
|
|
4
5
|
|
|
5
6
|
from infrahub import config
|
|
6
|
-
from infrahub.core.constants import GLOBAL_BRANCH_NAME, BranchSupportType
|
|
7
|
+
from infrahub.core.constants import GLOBAL_BRANCH_NAME, BranchSupportType, DiffAction, RelationshipStatus
|
|
7
8
|
from infrahub.core.query import Query, QueryType
|
|
8
9
|
from infrahub.core.timestamp import Timestamp
|
|
9
10
|
|
|
10
11
|
if TYPE_CHECKING:
|
|
11
12
|
from infrahub.core.branch import Branch
|
|
13
|
+
from infrahub.core.diff.model.field_specifiers_map import NodeFieldSpecifierMap
|
|
12
14
|
from infrahub.database import InfrahubDatabase
|
|
13
15
|
|
|
14
16
|
|
|
@@ -106,8 +108,8 @@ class DiffCalculationQuery(DiffQuery):
|
|
|
106
108
|
self,
|
|
107
109
|
base_branch: Branch,
|
|
108
110
|
diff_branch_from_time: Timestamp,
|
|
109
|
-
current_node_field_specifiers:
|
|
110
|
-
new_node_field_specifiers:
|
|
111
|
+
current_node_field_specifiers: NodeFieldSpecifierMap | None = None,
|
|
112
|
+
new_node_field_specifiers: NodeFieldSpecifierMap | None = None,
|
|
111
113
|
**kwargs: Any,
|
|
112
114
|
):
|
|
113
115
|
self.base_branch = base_branch
|
|
@@ -127,12 +129,13 @@ CALL {
|
|
|
127
129
|
// add base branch paths before branched_from, if they exist
|
|
128
130
|
// -------------------------------------
|
|
129
131
|
WITH n, attr_rel, r_node, r_prop
|
|
132
|
+
// 'base_n' instead of 'n' here to get previous value for node with a migrated kind/inheritance
|
|
130
133
|
OPTIONAL MATCH latest_base_path = (:Root)<-[base_r_root:IS_PART_OF {branch: $base_branch_name}]
|
|
131
|
-
-(n)-[base_r_node {branch: $base_branch_name}]
|
|
134
|
+
-(base_n {uuid: n.uuid})-[base_r_node {branch: $base_branch_name}]
|
|
132
135
|
-(attr_rel)-[base_r_prop {branch: $base_branch_name}]->(base_prop)
|
|
133
136
|
WHERE type(base_r_node) = type(r_node)
|
|
134
137
|
AND type(base_r_prop) = type(r_prop)
|
|
135
|
-
AND [%(id_func)s(
|
|
138
|
+
AND [%(id_func)s(base_n), type(base_r_node)] <> [%(id_func)s(base_prop), type(base_r_prop)]
|
|
136
139
|
AND all(
|
|
137
140
|
r in relationships(latest_base_path)
|
|
138
141
|
WHERE r.from < $branch_from_time
|
|
@@ -142,7 +145,7 @@ CALL {
|
|
|
142
145
|
// the migration leaves two nodes with the same UUID linked to the same Relationship
|
|
143
146
|
// ------------------------
|
|
144
147
|
AND (
|
|
145
|
-
|
|
148
|
+
base_n.uuid IS NULL OR base_prop.uuid IS NULL OR base_n.uuid <> base_prop.uuid
|
|
146
149
|
OR type(base_r_node) <> "IS_RELATED" OR type(base_r_prop) <> "IS_RELATED"
|
|
147
150
|
)
|
|
148
151
|
WITH latest_base_path, base_r_root, base_r_node, base_r_prop
|
|
@@ -198,6 +201,13 @@ WITH reduce(
|
|
|
198
201
|
diff_rel_paths = [], item IN [penultimate_path, peer_path] |
|
|
199
202
|
CASE WHEN item IS NULL THEN diff_rel_paths ELSE diff_rel_paths + [item] END
|
|
200
203
|
) AS diff_rel_paths, has_more_data
|
|
204
|
+
// ------------------------
|
|
205
|
+
// make sure we still include has_more_data if diff_rel_paths is empty
|
|
206
|
+
// ------------------------
|
|
207
|
+
WITH CASE
|
|
208
|
+
WHEN diff_rel_paths = [] THEN [NULL]
|
|
209
|
+
ELSE diff_rel_paths
|
|
210
|
+
END AS diff_rel_paths, has_more_data
|
|
201
211
|
"""
|
|
202
212
|
|
|
203
213
|
def get_previous_base_path_query(self, db: InfrahubDatabase) -> str:
|
|
@@ -231,10 +241,10 @@ class DiffNodePathsQuery(DiffCalculationQuery):
|
|
|
231
241
|
self.params.update(params_dict)
|
|
232
242
|
self.params.update(
|
|
233
243
|
{
|
|
234
|
-
"new_node_ids_list":
|
|
244
|
+
"new_node_ids_list": self.new_node_field_specifiers.get_uuids_list()
|
|
235
245
|
if self.new_node_field_specifiers
|
|
236
246
|
else None,
|
|
237
|
-
"current_node_ids_list":
|
|
247
|
+
"current_node_ids_list": self.current_node_field_specifiers.get_uuids_list()
|
|
238
248
|
if self.current_node_field_specifiers
|
|
239
249
|
else None,
|
|
240
250
|
}
|
|
@@ -276,7 +286,7 @@ WITH p, q, diff_rel, CASE
|
|
|
276
286
|
WHEN $new_node_ids_list IS NOT NULL AND p.uuid IN $new_node_ids_list THEN $branch_from_time
|
|
277
287
|
ELSE $from_time
|
|
278
288
|
END AS row_from_time
|
|
279
|
-
ORDER BY p
|
|
289
|
+
ORDER BY %(id_func)s(p) DESC
|
|
280
290
|
SKIP $offset
|
|
281
291
|
LIMIT $limit
|
|
282
292
|
// -------------------------------------
|
|
@@ -313,15 +323,15 @@ CALL {
|
|
|
313
323
|
AND node.branch_support IN [$branch_aware, $branch_agnostic]
|
|
314
324
|
AND type(r_prop) IN ["IS_VISIBLE", "IS_PROTECTED", "HAS_SOURCE", "HAS_OWNER", "HAS_VALUE", "IS_RELATED"]
|
|
315
325
|
AND any(l in labels(prop) WHERE l in ["Boolean", "Node", "AttributeValue"])
|
|
316
|
-
AND ALL(
|
|
317
|
-
r in [r_node, r_prop]
|
|
318
|
-
WHERE r.from < $to_time AND r.branch = top_diff_rel.branch
|
|
319
|
-
)
|
|
320
326
|
AND (top_diff_rel.to IS NULL OR top_diff_rel.to >= r_node.from)
|
|
321
327
|
AND (r_node.to IS NULL OR r_node.to >= r_prop.from)
|
|
322
328
|
AND [%(id_func)s(p), type(r_node)] <> [%(id_func)s(prop), type(r_prop)]
|
|
323
|
-
AND
|
|
324
|
-
AND
|
|
329
|
+
AND r_node.from < $to_time
|
|
330
|
+
AND r_node.branch = top_diff_rel.branch
|
|
331
|
+
AND r_node.status = top_diff_rel.status
|
|
332
|
+
AND r_prop.from < $to_time
|
|
333
|
+
AND r_prop.branch = top_diff_rel.branch
|
|
334
|
+
AND r_prop.status = top_diff_rel.status
|
|
325
335
|
// ------------------------
|
|
326
336
|
// special handling for nodes that had their kind updated,
|
|
327
337
|
// the migration leaves two nodes with the same UUID linked to the same Relationship
|
|
@@ -371,15 +381,16 @@ class DiffFieldPathsQuery(DiffCalculationQuery):
|
|
|
371
381
|
|
|
372
382
|
self.params.update(
|
|
373
383
|
{
|
|
374
|
-
"
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
384
|
+
"current_node_ids_list": self.current_node_field_specifiers.get_uuids_list()
|
|
385
|
+
if self.current_node_field_specifiers
|
|
386
|
+
else None,
|
|
387
|
+
"new_node_ids_list": self.new_node_field_specifiers.get_uuids_list()
|
|
388
|
+
if self.new_node_field_specifiers
|
|
389
|
+
else None,
|
|
390
|
+
"current_node_field_specifiers_map": self.current_node_field_specifiers.get_uuid_field_names_map()
|
|
378
391
|
if self.current_node_field_specifiers is not None
|
|
379
392
|
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
|
-
}
|
|
393
|
+
"new_node_field_specifiers_map": self.new_node_field_specifiers.get_uuid_field_names_map()
|
|
383
394
|
if self.new_node_field_specifiers is not None
|
|
384
395
|
else None,
|
|
385
396
|
}
|
|
@@ -400,16 +411,16 @@ AND (r_root.to IS NULL OR diff_rel.branch <> r_root.branch OR r_root.to >= diff_
|
|
|
400
411
|
// node ID and field name filtering first pass
|
|
401
412
|
AND (
|
|
402
413
|
(
|
|
403
|
-
$
|
|
404
|
-
AND
|
|
414
|
+
$current_node_ids_list IS NOT NULL
|
|
415
|
+
AND p.uuid IN $current_node_ids_list
|
|
405
416
|
AND q.name IN $current_node_field_specifiers_map[p.uuid]
|
|
406
417
|
) OR (
|
|
407
|
-
$
|
|
408
|
-
AND
|
|
418
|
+
$new_node_ids_list IS NOT NULL
|
|
419
|
+
AND p.uuid IN $new_node_ids_list
|
|
409
420
|
AND q.name IN $new_node_field_specifiers_map[p.uuid]
|
|
410
421
|
) OR (
|
|
411
|
-
$
|
|
412
|
-
AND $
|
|
422
|
+
$new_node_ids_list IS NULL
|
|
423
|
+
AND $current_node_ids_list IS NULL
|
|
413
424
|
)
|
|
414
425
|
)
|
|
415
426
|
// node ID and field name filtering second pass
|
|
@@ -417,8 +428,12 @@ AND (
|
|
|
417
428
|
// time-based filters for nodes already included in the diff or fresh changes
|
|
418
429
|
(
|
|
419
430
|
(
|
|
420
|
-
(
|
|
421
|
-
|
|
431
|
+
(
|
|
432
|
+
$current_node_ids_list IS NOT NULL
|
|
433
|
+
AND p.uuid IN $current_node_ids_list
|
|
434
|
+
AND q.name IN $current_node_field_specifiers_map[p.uuid]
|
|
435
|
+
)
|
|
436
|
+
OR ($current_node_ids_list IS NULL AND $new_node_ids_list IS NULL)
|
|
422
437
|
)
|
|
423
438
|
AND (r_root.from < $from_time OR p.branch_support = $branch_agnostic)
|
|
424
439
|
AND (
|
|
@@ -428,7 +443,11 @@ AND (
|
|
|
428
443
|
)
|
|
429
444
|
// time-based filters for new nodes
|
|
430
445
|
OR (
|
|
431
|
-
(
|
|
446
|
+
(
|
|
447
|
+
$new_node_ids_list IS NOT NULL
|
|
448
|
+
AND p.uuid IN $new_node_ids_list
|
|
449
|
+
AND q.name IN $new_node_field_specifiers_map[p.uuid]
|
|
450
|
+
)
|
|
432
451
|
AND (r_root.from < $branch_from_time OR p.branch_support = $branch_agnostic)
|
|
433
452
|
AND (
|
|
434
453
|
($branch_from_time <= diff_rel.from < $to_time AND (diff_rel.to IS NULL OR diff_rel.to > $to_time))
|
|
@@ -454,7 +473,11 @@ WITH one_result[0] AS root, one_result[1] AS r_root, one_result[2] AS p, one_res
|
|
|
454
473
|
// Add correct from_time for row
|
|
455
474
|
// -------------------------------------
|
|
456
475
|
WITH root, r_root, p, diff_rel, q, has_more_data, CASE
|
|
457
|
-
WHEN
|
|
476
|
+
WHEN
|
|
477
|
+
$new_node_ids_list IS NOT NULL
|
|
478
|
+
AND p.uuid IN $new_node_ids_list
|
|
479
|
+
AND q.name IN $new_node_field_specifiers_map[p.uuid]
|
|
480
|
+
THEN $branch_from_time
|
|
458
481
|
ELSE $from_time
|
|
459
482
|
END AS row_from_time
|
|
460
483
|
// -------------------------------------
|
|
@@ -554,15 +577,16 @@ class DiffPropertyPathsQuery(DiffCalculationQuery):
|
|
|
554
577
|
|
|
555
578
|
self.params.update(
|
|
556
579
|
{
|
|
557
|
-
"
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
580
|
+
"current_node_ids_list": self.current_node_field_specifiers.get_uuids_list()
|
|
581
|
+
if self.current_node_field_specifiers
|
|
582
|
+
else None,
|
|
583
|
+
"new_node_ids_list": self.new_node_field_specifiers.get_uuids_list()
|
|
584
|
+
if self.new_node_field_specifiers
|
|
585
|
+
else None,
|
|
586
|
+
"current_node_field_specifiers_map": self.current_node_field_specifiers.get_uuid_field_names_map()
|
|
561
587
|
if self.current_node_field_specifiers is not None
|
|
562
588
|
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
|
-
}
|
|
589
|
+
"new_node_field_specifiers_map": self.new_node_field_specifiers.get_uuid_field_names_map()
|
|
566
590
|
if self.new_node_field_specifiers is not None
|
|
567
591
|
else None,
|
|
568
592
|
}
|
|
@@ -580,16 +604,16 @@ AND type(r_node) IN ["HAS_ATTRIBUTE", "IS_RELATED"]
|
|
|
580
604
|
// node ID and field name filtering first pass
|
|
581
605
|
AND (
|
|
582
606
|
(
|
|
583
|
-
$
|
|
584
|
-
AND
|
|
607
|
+
$current_node_ids_list IS NOT NULL
|
|
608
|
+
AND n.uuid IN $current_node_ids_list
|
|
585
609
|
AND p.name IN $current_node_field_specifiers_map[n.uuid]
|
|
586
610
|
) OR (
|
|
587
|
-
$
|
|
588
|
-
AND
|
|
611
|
+
$new_node_ids_list IS NOT NULL
|
|
612
|
+
AND n.uuid IN $new_node_ids_list
|
|
589
613
|
AND p.name IN $new_node_field_specifiers_map[n.uuid]
|
|
590
614
|
) OR (
|
|
591
|
-
$
|
|
592
|
-
AND $
|
|
615
|
+
$new_node_ids_list IS NULL
|
|
616
|
+
AND $current_node_ids_list IS NULL
|
|
593
617
|
)
|
|
594
618
|
)
|
|
595
619
|
// node ID and field name filtering second pass
|
|
@@ -597,8 +621,12 @@ AND (
|
|
|
597
621
|
// time-based filters for nodes already included in the diff or fresh changes
|
|
598
622
|
(
|
|
599
623
|
(
|
|
600
|
-
(
|
|
601
|
-
|
|
624
|
+
(
|
|
625
|
+
$current_node_ids_list IS NOT NULL
|
|
626
|
+
AND n.uuid IN $current_node_ids_list
|
|
627
|
+
AND p.name IN $current_node_field_specifiers_map[n.uuid]
|
|
628
|
+
)
|
|
629
|
+
OR ($current_node_ids_list IS NULL AND $new_node_ids_list IS NULL)
|
|
602
630
|
)
|
|
603
631
|
AND (
|
|
604
632
|
($from_time <= diff_rel.from < $to_time AND (diff_rel.to IS NULL OR diff_rel.to > $to_time))
|
|
@@ -612,7 +640,11 @@ AND (
|
|
|
612
640
|
)
|
|
613
641
|
// time-based filters for new nodes
|
|
614
642
|
OR (
|
|
615
|
-
(
|
|
643
|
+
(
|
|
644
|
+
$new_node_ids_list IS NOT NULL
|
|
645
|
+
AND n.uuid IN $new_node_ids_list
|
|
646
|
+
AND p.name IN $new_node_field_specifiers_map[n.uuid]
|
|
647
|
+
)
|
|
616
648
|
AND (
|
|
617
649
|
($branch_from_time <= diff_rel.from < $to_time AND (diff_rel.to IS NULL OR diff_rel.to > $to_time))
|
|
618
650
|
OR ($branch_from_time <= diff_rel.to < $to_time)
|
|
@@ -667,7 +699,11 @@ WITH one_result[0] AS diff_rel_path, one_result[1] AS r_root, one_result[2] AS n
|
|
|
667
699
|
// Add correct from_time for row
|
|
668
700
|
// -------------------------------------
|
|
669
701
|
WITH diff_rel_path, r_root, n, r_node, p, diff_rel, has_more_data, CASE
|
|
670
|
-
WHEN
|
|
702
|
+
WHEN
|
|
703
|
+
$new_node_ids_list IS NOT NULL
|
|
704
|
+
AND n.uuid IN $new_node_ids_list
|
|
705
|
+
AND p.name IN $new_node_field_specifiers_map[n.uuid]
|
|
706
|
+
THEN $branch_from_time
|
|
671
707
|
ELSE $from_time
|
|
672
708
|
END AS row_from_time
|
|
673
709
|
WITH diff_rel_path, r_root, n, r_node, p, diff_rel, has_more_data, row_from_time
|
|
@@ -690,7 +726,7 @@ CALL {
|
|
|
690
726
|
CALL {
|
|
691
727
|
WITH n, row_from_time
|
|
692
728
|
OPTIONAL MATCH (root:Root)<-[r_root_deleted:IS_PART_OF {branch: $branch_name}]-(n)
|
|
693
|
-
WHERE
|
|
729
|
+
WHERE r_root_deleted.from < $to_time
|
|
694
730
|
WITH r_root_deleted
|
|
695
731
|
ORDER BY r_root_deleted.status DESC
|
|
696
732
|
LIMIT 1
|
|
@@ -718,3 +754,85 @@ WITH n, p, type(diff_rel) AS drt, head(collect(diff_rel_path)) AS diff_path, has
|
|
|
718
754
|
self.add_to_query(self.get_relationship_peer_side_query(db=db))
|
|
719
755
|
self.add_to_query("UNWIND diff_rel_paths AS diff_path")
|
|
720
756
|
self.return_labels = ["DISTINCT diff_path AS diff_path", "has_more_data"]
|
|
757
|
+
|
|
758
|
+
|
|
759
|
+
@dataclass
|
|
760
|
+
class MigratedKindNode:
|
|
761
|
+
uuid: str
|
|
762
|
+
kind: str
|
|
763
|
+
db_id: str
|
|
764
|
+
from_time: Timestamp
|
|
765
|
+
action: DiffAction
|
|
766
|
+
has_more_data: bool
|
|
767
|
+
|
|
768
|
+
|
|
769
|
+
class DiffMigratedKindNodesQuery(DiffCalculationQuery):
|
|
770
|
+
name = "diff_migrated_kind_nodes_query"
|
|
771
|
+
|
|
772
|
+
async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None: # noqa: ARG002
|
|
773
|
+
params_dict = self.get_params()
|
|
774
|
+
self.params.update(params_dict)
|
|
775
|
+
migrated_kind_nodes_query = """
|
|
776
|
+
// -------------------------------------
|
|
777
|
+
// Identify nodes added/removed on branch in the time frame
|
|
778
|
+
// -------------------------------------
|
|
779
|
+
MATCH (:Root)<-[diff_rel:IS_PART_OF {branch: $branch_name}]-(n:Node)
|
|
780
|
+
WHERE (
|
|
781
|
+
($from_time <= diff_rel.from < $to_time AND (diff_rel.to IS NULL OR diff_rel.to > $to_time))
|
|
782
|
+
OR ($from_time <= diff_rel.to < $to_time)
|
|
783
|
+
)
|
|
784
|
+
AND n.branch_support = $branch_aware
|
|
785
|
+
WITH DISTINCT n.uuid AS node_uuid, %(id_func)s(n) AS db_id
|
|
786
|
+
WITH node_uuid, count(*) AS num_nodes_with_uuid
|
|
787
|
+
WHERE num_nodes_with_uuid > 1
|
|
788
|
+
// -------------------------------------
|
|
789
|
+
// Limit the number of nodes
|
|
790
|
+
// -------------------------------------
|
|
791
|
+
WITH node_uuid
|
|
792
|
+
ORDER BY node_uuid
|
|
793
|
+
SKIP $offset
|
|
794
|
+
LIMIT $limit
|
|
795
|
+
WITH collect(node_uuid) AS node_uuids
|
|
796
|
+
WITH node_uuids, size(node_uuids) = $limit AS has_more_data
|
|
797
|
+
MATCH (:Root)<-[diff_rel:IS_PART_OF {branch: $branch_name}]-(n:Node)
|
|
798
|
+
WHERE n.uuid IN node_uuids
|
|
799
|
+
AND (
|
|
800
|
+
($from_time <= diff_rel.from < $to_time AND (diff_rel.to IS NULL OR diff_rel.to > $to_time))
|
|
801
|
+
OR ($from_time <= diff_rel.to < $to_time)
|
|
802
|
+
)
|
|
803
|
+
// -------------------------------------
|
|
804
|
+
// Ignore node created and deleted on this branch
|
|
805
|
+
// -------------------------------------
|
|
806
|
+
CALL {
|
|
807
|
+
WITH n
|
|
808
|
+
OPTIONAL MATCH (:Root)<-[diff_rel:IS_PART_OF {branch: $branch_name}]-(n)
|
|
809
|
+
WITH diff_rel
|
|
810
|
+
ORDER BY diff_rel.from ASC
|
|
811
|
+
WITH collect(diff_rel.status) AS statuses
|
|
812
|
+
RETURN statuses = ["active", "deleted"] AS intra_branch_update
|
|
813
|
+
}
|
|
814
|
+
WITH n.uuid AS uuid, n.kind AS kind, %(id_func)s(n) AS db_id, diff_rel.from_time AS from_time, diff_rel.status AS status, has_more_data
|
|
815
|
+
WHERE intra_branch_update = FALSE
|
|
816
|
+
""" % {"id_func": db.get_id_function_name()}
|
|
817
|
+
self.add_to_query(query=migrated_kind_nodes_query)
|
|
818
|
+
self.return_labels = [
|
|
819
|
+
"uuid",
|
|
820
|
+
"kind",
|
|
821
|
+
"db_id",
|
|
822
|
+
"from_time",
|
|
823
|
+
"status",
|
|
824
|
+
"has_more_data",
|
|
825
|
+
]
|
|
826
|
+
|
|
827
|
+
def get_migrated_kind_nodes(self) -> Generator[MigratedKindNode, None, None]:
|
|
828
|
+
for result in self.get_results():
|
|
829
|
+
yield MigratedKindNode(
|
|
830
|
+
uuid=result.get_as_type("uuid", return_type=str),
|
|
831
|
+
kind=result.get_as_type("kind", return_type=str),
|
|
832
|
+
db_id=result.get_as_type("db_id", return_type=str),
|
|
833
|
+
from_time=result.get_as_type("from_time", return_type=Timestamp),
|
|
834
|
+
action=DiffAction.REMOVED
|
|
835
|
+
if result.get_as_type("status", return_type=str).lower() == RelationshipStatus.DELETED.value
|
|
836
|
+
else DiffAction.ADDED,
|
|
837
|
+
has_more_data=result.get_as_type("has_more_data", bool),
|
|
838
|
+
)
|
infrahub/core/query/node.py
CHANGED
|
@@ -92,6 +92,7 @@ class NodeAttributesFromDB:
|
|
|
92
92
|
class PeerInfo:
|
|
93
93
|
uuid: str
|
|
94
94
|
kind: str
|
|
95
|
+
db_id: str
|
|
95
96
|
|
|
96
97
|
|
|
97
98
|
class NodeQuery(Query):
|
|
@@ -412,9 +413,32 @@ class NodeDeleteQuery(NodeQuery):
|
|
|
412
413
|
self.params["branch"] = self.branch.name
|
|
413
414
|
self.params["branch_level"] = self.branch.hierarchy_level
|
|
414
415
|
|
|
416
|
+
if self.branch.is_global or self.branch.is_default:
|
|
417
|
+
node_query_match = """
|
|
418
|
+
MATCH (n:Node { uuid: $uuid })
|
|
419
|
+
OPTIONAL MATCH (n)-[delete_edge:IS_PART_OF {status: "deleted", branch: $branch}]->(:Root)
|
|
420
|
+
WHERE delete_edge.from <= $at
|
|
421
|
+
WITH n WHERE delete_edge IS NULL
|
|
422
|
+
"""
|
|
423
|
+
else:
|
|
424
|
+
node_filter, node_filter_params = self.branch.get_query_filter_path(at=self.at, variable_name="r")
|
|
425
|
+
node_query_match = """
|
|
426
|
+
MATCH (n:Node { uuid: $uuid })
|
|
427
|
+
CALL {
|
|
428
|
+
WITH n
|
|
429
|
+
MATCH (n)-[r:IS_PART_OF]->(:Root)
|
|
430
|
+
WHERE %(node_filter)s
|
|
431
|
+
RETURN r.status = "active" AS is_active
|
|
432
|
+
ORDER BY r.from DESC
|
|
433
|
+
LIMIT 1
|
|
434
|
+
}
|
|
435
|
+
WITH n WHERE is_active = TRUE
|
|
436
|
+
""" % {"node_filter": node_filter}
|
|
437
|
+
self.params.update(node_filter_params)
|
|
438
|
+
self.add_to_query(node_query_match)
|
|
439
|
+
|
|
415
440
|
query = """
|
|
416
441
|
MATCH (root:Root)
|
|
417
|
-
MATCH (n:Node { uuid: $uuid })
|
|
418
442
|
CREATE (n)-[r:IS_PART_OF { branch: $branch, branch_level: $branch_level, status: "deleted", from: $at }]->(root)
|
|
419
443
|
"""
|
|
420
444
|
|
|
@@ -1405,7 +1429,7 @@ class NodeGetHierarchyQuery(Query):
|
|
|
1405
1429
|
|
|
1406
1430
|
super().__init__(**kwargs)
|
|
1407
1431
|
|
|
1408
|
-
async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None: # noqa: ARG002
|
|
1432
|
+
async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None: # noqa: ARG002,PLR0915
|
|
1409
1433
|
hierarchy_schema = self.node_schema.get_hierarchy_schema(db=db, branch=self.branch)
|
|
1410
1434
|
branch_filter, branch_params = self.branch.get_query_filter_path(at=self.at.to_string())
|
|
1411
1435
|
self.params.update(branch_params)
|
|
@@ -1438,6 +1462,10 @@ class NodeGetHierarchyQuery(Query):
|
|
|
1438
1462
|
UNWIND peers_with_duplicates AS pwd
|
|
1439
1463
|
RETURN DISTINCT pwd AS peer
|
|
1440
1464
|
}
|
|
1465
|
+
""" % {"filter": filter_str, "branch_filter": branch_filter}
|
|
1466
|
+
|
|
1467
|
+
if not self.branch.is_default:
|
|
1468
|
+
query += """
|
|
1441
1469
|
CALL {
|
|
1442
1470
|
WITH n, peer
|
|
1443
1471
|
MATCH path = (n)%(filter)s(peer)
|
|
@@ -1448,10 +1476,14 @@ class NodeGetHierarchyQuery(Query):
|
|
|
1448
1476
|
LIMIT 1
|
|
1449
1477
|
}
|
|
1450
1478
|
WITH peer1 as peer, is_active
|
|
1451
|
-
|
|
1479
|
+
""" % {"filter": filter_str, "branch_filter": branch_filter, "with_clause": with_clause}
|
|
1480
|
+
else:
|
|
1481
|
+
query += """
|
|
1482
|
+
WITH peer
|
|
1483
|
+
"""
|
|
1452
1484
|
|
|
1453
1485
|
self.add_to_query(query)
|
|
1454
|
-
where_clause = ["is_active = TRUE"]
|
|
1486
|
+
where_clause = ["is_active = TRUE"] if not self.branch.is_default else []
|
|
1455
1487
|
|
|
1456
1488
|
clean_filters = extract_field_filters(field_name=self.direction.value, filters=self.filters)
|
|
1457
1489
|
|
|
@@ -1461,7 +1493,8 @@ class NodeGetHierarchyQuery(Query):
|
|
|
1461
1493
|
if clean_filters.get("id", None):
|
|
1462
1494
|
self.params["peer_ids"].append(clean_filters.get("id"))
|
|
1463
1495
|
|
|
1464
|
-
|
|
1496
|
+
if where_clause:
|
|
1497
|
+
self.add_to_query("WHERE " + " AND ".join(where_clause))
|
|
1465
1498
|
|
|
1466
1499
|
self.return_labels = ["peer"]
|
|
1467
1500
|
|
|
@@ -1544,4 +1577,5 @@ class NodeGetHierarchyQuery(Query):
|
|
|
1544
1577
|
yield PeerInfo(
|
|
1545
1578
|
uuid=peer_node.get("uuid"),
|
|
1546
1579
|
kind=peer_node.get("kind"),
|
|
1580
|
+
db_id=peer_node.element_id,
|
|
1547
1581
|
)
|