infrahub-server 1.1.6__py3-none-any.whl → 1.1.8__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/core/attribute.py +4 -1
- infrahub/core/branch/tasks.py +7 -4
- infrahub/core/diff/combiner.py +11 -7
- infrahub/core/diff/coordinator.py +49 -70
- infrahub/core/diff/data_check_synchronizer.py +86 -7
- infrahub/core/diff/enricher/aggregated.py +3 -3
- infrahub/core/diff/enricher/cardinality_one.py +6 -6
- infrahub/core/diff/enricher/hierarchy.py +17 -4
- infrahub/core/diff/enricher/labels.py +18 -3
- infrahub/core/diff/enricher/path_identifier.py +7 -8
- infrahub/core/diff/merger/merger.py +5 -3
- infrahub/core/diff/model/path.py +66 -25
- infrahub/core/diff/parent_node_adder.py +78 -0
- infrahub/core/diff/payload_builder.py +13 -2
- infrahub/core/diff/query/all_conflicts.py +5 -2
- infrahub/core/diff/query/diff_get.py +2 -1
- infrahub/core/diff/query/field_specifiers.py +2 -0
- infrahub/core/diff/query/field_summary.py +2 -1
- infrahub/core/diff/query/filters.py +12 -1
- infrahub/core/diff/query/has_conflicts_query.py +5 -2
- infrahub/core/diff/query/{drop_tracking_id.py → merge_tracking_id.py} +3 -3
- infrahub/core/diff/query/roots_metadata.py +8 -1
- infrahub/core/diff/query/save.py +230 -139
- infrahub/core/diff/query/summary_counts_enricher.py +267 -0
- infrahub/core/diff/query/time_range_query.py +2 -1
- infrahub/core/diff/query_parser.py +49 -24
- infrahub/core/diff/repository/deserializer.py +31 -27
- infrahub/core/diff/repository/repository.py +215 -41
- infrahub/core/diff/tasks.py +4 -4
- infrahub/core/graph/__init__.py +1 -1
- infrahub/core/graph/index.py +3 -0
- infrahub/core/migrations/graph/__init__.py +4 -0
- infrahub/core/migrations/graph/m019_restore_rels_to_time.py +256 -0
- infrahub/core/migrations/graph/m020_duplicate_edges.py +160 -0
- infrahub/core/migrations/query/node_duplicate.py +38 -18
- infrahub/core/migrations/schema/node_remove.py +26 -12
- infrahub/core/migrations/shared.py +10 -8
- infrahub/core/node/__init__.py +19 -9
- infrahub/core/node/constraints/grouped_uniqueness.py +25 -5
- infrahub/core/node/ipam.py +6 -1
- infrahub/core/node/permissions.py +4 -0
- infrahub/core/query/attribute.py +2 -0
- infrahub/core/query/diff.py +41 -3
- infrahub/core/query/node.py +74 -21
- infrahub/core/query/relationship.py +107 -17
- infrahub/core/query/resource_manager.py +5 -1
- infrahub/core/relationship/model.py +8 -12
- infrahub/core/schema/definitions/core.py +1 -0
- infrahub/core/utils.py +1 -0
- infrahub/core/validators/uniqueness/query.py +20 -17
- infrahub/database/__init__.py +14 -0
- infrahub/dependencies/builder/constraint/grouped/node_runner.py +0 -2
- infrahub/dependencies/builder/diff/coordinator.py +0 -2
- infrahub/dependencies/builder/diff/deserializer.py +3 -1
- infrahub/dependencies/builder/diff/enricher/hierarchy.py +3 -1
- infrahub/dependencies/builder/diff/parent_node_adder.py +8 -0
- infrahub/graphql/mutations/computed_attribute.py +3 -1
- infrahub/graphql/mutations/diff.py +41 -10
- infrahub/graphql/mutations/main.py +11 -6
- infrahub/graphql/mutations/relationship.py +29 -1
- infrahub/graphql/mutations/resource_manager.py +3 -3
- infrahub/graphql/mutations/tasks.py +6 -3
- infrahub/graphql/queries/resource_manager.py +7 -3
- infrahub/permissions/__init__.py +2 -1
- infrahub/permissions/types.py +26 -0
- infrahub_sdk/client.py +10 -2
- infrahub_sdk/config.py +3 -0
- infrahub_sdk/ctl/check.py +3 -3
- infrahub_sdk/ctl/cli_commands.py +16 -11
- infrahub_sdk/ctl/exceptions.py +0 -6
- infrahub_sdk/ctl/exporter.py +1 -1
- infrahub_sdk/ctl/generator.py +5 -5
- infrahub_sdk/ctl/importer.py +3 -2
- infrahub_sdk/ctl/menu.py +1 -1
- infrahub_sdk/ctl/object.py +1 -1
- infrahub_sdk/ctl/repository.py +23 -15
- infrahub_sdk/ctl/schema.py +2 -2
- infrahub_sdk/ctl/utils.py +4 -3
- infrahub_sdk/ctl/validate.py +2 -1
- infrahub_sdk/exceptions.py +12 -0
- infrahub_sdk/generator.py +3 -0
- infrahub_sdk/node.py +7 -4
- infrahub_sdk/testing/schemas/animal.py +9 -0
- infrahub_sdk/utils.py +11 -1
- infrahub_sdk/yaml.py +2 -3
- {infrahub_server-1.1.6.dist-info → infrahub_server-1.1.8.dist-info}/METADATA +41 -7
- {infrahub_server-1.1.6.dist-info → infrahub_server-1.1.8.dist-info}/RECORD +94 -91
- infrahub_testcontainers/container.py +12 -3
- infrahub_testcontainers/docker-compose.test.yml +22 -3
- infrahub_testcontainers/haproxy.cfg +43 -0
- infrahub_testcontainers/helpers.py +85 -1
- infrahub/core/diff/enricher/summary_counts.py +0 -105
- infrahub/dependencies/builder/diff/enricher/summary_counts.py +0 -8
- infrahub_sdk/ctl/_file.py +0 -13
- {infrahub_server-1.1.6.dist-info → infrahub_server-1.1.8.dist-info}/LICENSE.txt +0 -0
- {infrahub_server-1.1.6.dist-info → infrahub_server-1.1.8.dist-info}/WHEEL +0 -0
- {infrahub_server-1.1.6.dist-info → infrahub_server-1.1.8.dist-info}/entry_points.txt +0 -0
infrahub/core/node/__init__.py
CHANGED
|
@@ -7,7 +7,13 @@ from infrahub_sdk.utils import is_valid_uuid
|
|
|
7
7
|
from infrahub_sdk.uuidt import UUIDT
|
|
8
8
|
|
|
9
9
|
from infrahub.core import registry
|
|
10
|
-
from infrahub.core.constants import
|
|
10
|
+
from infrahub.core.constants import (
|
|
11
|
+
GLOBAL_BRANCH_NAME,
|
|
12
|
+
BranchSupportType,
|
|
13
|
+
ComputedAttributeKind,
|
|
14
|
+
InfrahubKind,
|
|
15
|
+
RelationshipCardinality,
|
|
16
|
+
)
|
|
11
17
|
from infrahub.core.constants.schema import SchemaElementPathType
|
|
12
18
|
from infrahub.core.protocols import CoreNumberPool
|
|
13
19
|
from infrahub.core.query.node import NodeCheckIDQuery, NodeCreateAllQuery, NodeDeleteQuery, NodeGetListQuery
|
|
@@ -19,6 +25,7 @@ from infrahub.types import ATTRIBUTE_TYPES
|
|
|
19
25
|
|
|
20
26
|
from ...graphql.constants import KIND_GRAPHQL_FIELD_NAME
|
|
21
27
|
from ...graphql.models import OrderModel
|
|
28
|
+
from ..query.relationship import RelationshipDeleteAllQuery
|
|
22
29
|
from ..relationship import RelationshipManager
|
|
23
30
|
from ..utils import update_relationships_to
|
|
24
31
|
from .base import BaseNode, BaseNodeMeta, BaseNodeOptions
|
|
@@ -599,15 +606,13 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
|
|
|
599
606
|
attr: BaseAttribute = getattr(self, name)
|
|
600
607
|
await attr.delete(at=delete_at, db=db)
|
|
601
608
|
|
|
602
|
-
# Go over the list of relationships and update them one by one
|
|
603
|
-
for name in self._relationships:
|
|
604
|
-
rel: RelationshipManager = getattr(self, name)
|
|
605
|
-
await rel.delete(at=delete_at, db=db)
|
|
606
|
-
|
|
607
|
-
# Need to check if there are some unidirectional relationship as well
|
|
608
|
-
# For example, if we delete a tag, we must check the permissions and update all the relationships pointing at it
|
|
609
609
|
branch = self.get_branch_based_on_support_type()
|
|
610
610
|
|
|
611
|
+
delete_query = await RelationshipDeleteAllQuery.init(
|
|
612
|
+
db=db, node_id=self.get_id(), branch=branch, at=delete_at, branch_agnostic=branch.name == GLOBAL_BRANCH_NAME
|
|
613
|
+
)
|
|
614
|
+
await delete_query.execute(db=db)
|
|
615
|
+
|
|
611
616
|
# Update the relationship to the branch itself
|
|
612
617
|
query = await NodeGetListQuery.init(
|
|
613
618
|
db=db,
|
|
@@ -633,6 +638,7 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
|
|
|
633
638
|
related_node_ids: set | None = None,
|
|
634
639
|
filter_sensitive: bool = False,
|
|
635
640
|
permissions: dict | None = None,
|
|
641
|
+
include_properties: bool = True,
|
|
636
642
|
) -> dict:
|
|
637
643
|
"""Generate GraphQL Payload for all attributes
|
|
638
644
|
|
|
@@ -686,10 +692,14 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
|
|
|
686
692
|
related_node_ids=related_node_ids,
|
|
687
693
|
filter_sensitive=filter_sensitive,
|
|
688
694
|
permissions=permissions,
|
|
695
|
+
include_properties=include_properties,
|
|
689
696
|
)
|
|
690
697
|
else:
|
|
691
698
|
response[field_name] = await field.to_graphql(
|
|
692
|
-
db=db,
|
|
699
|
+
db=db,
|
|
700
|
+
filter_sensitive=filter_sensitive,
|
|
701
|
+
permissions=permissions,
|
|
702
|
+
include_properties=include_properties,
|
|
693
703
|
)
|
|
694
704
|
|
|
695
705
|
for relationship_schema in self.get_schema().relationships:
|
|
@@ -35,7 +35,7 @@ class NodeGroupedUniquenessConstraint(NodeConstraintInterface):
|
|
|
35
35
|
self.branch = branch
|
|
36
36
|
self.schema_branch = registry.schema.get_schema_branch(branch.name)
|
|
37
37
|
|
|
38
|
-
def _build_query_request(
|
|
38
|
+
async def _build_query_request(
|
|
39
39
|
self,
|
|
40
40
|
updated_node: Node,
|
|
41
41
|
node_schema: MainSchemaTypes,
|
|
@@ -51,9 +51,16 @@ class NodeGroupedUniquenessConstraint(NodeConstraintInterface):
|
|
|
51
51
|
if attribute_path.related_schema and attribute_path.relationship_schema:
|
|
52
52
|
if filters and attribute_path.relationship_schema.name in filters:
|
|
53
53
|
include_in_query = True
|
|
54
|
+
|
|
55
|
+
relationship_manager: RelationshipManager = getattr(
|
|
56
|
+
updated_node, attribute_path.relationship_schema.name
|
|
57
|
+
)
|
|
58
|
+
related_node = await relationship_manager.get_peer(db=self.db)
|
|
59
|
+
related_node_id = related_node.get_id() if related_node else None
|
|
54
60
|
query_relationship_paths.add(
|
|
55
61
|
QueryRelationshipAttributePath(
|
|
56
62
|
identifier=attribute_path.relationship_schema.get_identifier(),
|
|
63
|
+
value=related_node_id,
|
|
57
64
|
)
|
|
58
65
|
)
|
|
59
66
|
continue
|
|
@@ -158,7 +165,7 @@ class NodeGroupedUniquenessConstraint(NodeConstraintInterface):
|
|
|
158
165
|
) -> None:
|
|
159
166
|
schema_branch = self.db.schema.get_schema_branch(name=self.branch.name)
|
|
160
167
|
path_groups = node_schema.get_unique_constraint_schema_attribute_paths(schema_branch=schema_branch)
|
|
161
|
-
query_request = self._build_query_request(
|
|
168
|
+
query_request = await self._build_query_request(
|
|
162
169
|
updated_node=node, node_schema=node_schema, path_groups=path_groups, filters=filters
|
|
163
170
|
)
|
|
164
171
|
if not query_request:
|
|
@@ -170,12 +177,25 @@ class NodeGroupedUniquenessConstraint(NodeConstraintInterface):
|
|
|
170
177
|
await self._check_results(updated_node=node, path_groups=path_groups, query_results=query.get_results())
|
|
171
178
|
|
|
172
179
|
async def check(self, node: Node, at: Optional[Timestamp] = None, filters: Optional[list[str]] = None) -> None:
|
|
180
|
+
def _frozen_constraints(schema: MainSchemaTypes) -> frozenset[frozenset[str]]:
|
|
181
|
+
if not schema.uniqueness_constraints:
|
|
182
|
+
return frozenset()
|
|
183
|
+
return frozenset(frozenset(uc) for uc in schema.uniqueness_constraints)
|
|
184
|
+
|
|
173
185
|
node_schema = node.get_schema()
|
|
174
|
-
|
|
186
|
+
include_node_schema = True
|
|
187
|
+
frozen_node_constraints = _frozen_constraints(node_schema)
|
|
188
|
+
schemas_to_check: list[MainSchemaTypes] = []
|
|
175
189
|
if node_schema.inherit_from:
|
|
176
190
|
for parent_schema_name in node_schema.inherit_from:
|
|
177
191
|
parent_schema = self.schema_branch.get(name=parent_schema_name, duplicate=False)
|
|
178
|
-
if parent_schema.uniqueness_constraints:
|
|
179
|
-
|
|
192
|
+
if not parent_schema.uniqueness_constraints:
|
|
193
|
+
continue
|
|
194
|
+
schemas_to_check.append(parent_schema)
|
|
195
|
+
frozen_parent_constraints = _frozen_constraints(parent_schema)
|
|
196
|
+
if frozen_node_constraints <= frozen_parent_constraints:
|
|
197
|
+
include_node_schema = False
|
|
198
|
+
if include_node_schema:
|
|
199
|
+
schemas_to_check.append(node_schema)
|
|
180
200
|
for schema in schemas_to_check:
|
|
181
201
|
await self._check_one_schema(node=node, node_schema=schema, at=at, filters=filters)
|
infrahub/core/node/ipam.py
CHANGED
|
@@ -20,9 +20,14 @@ class BuiltinIPPrefix(Node):
|
|
|
20
20
|
related_node_ids: Optional[set] = None,
|
|
21
21
|
filter_sensitive: bool = False,
|
|
22
22
|
permissions: Optional[dict] = None,
|
|
23
|
+
include_properties: bool = True,
|
|
23
24
|
) -> dict:
|
|
24
25
|
response = await super().to_graphql(
|
|
25
|
-
db,
|
|
26
|
+
db,
|
|
27
|
+
fields=fields,
|
|
28
|
+
related_node_ids=related_node_ids,
|
|
29
|
+
filter_sensitive=filter_sensitive,
|
|
30
|
+
include_properties=include_properties,
|
|
26
31
|
)
|
|
27
32
|
|
|
28
33
|
if fields:
|
|
@@ -18,6 +18,7 @@ class CoreGlobalPermission(Node):
|
|
|
18
18
|
related_node_ids: Optional[set] = None,
|
|
19
19
|
filter_sensitive: bool = False,
|
|
20
20
|
permissions: Optional[dict] = None,
|
|
21
|
+
include_properties: bool = True,
|
|
21
22
|
) -> dict:
|
|
22
23
|
response = await super().to_graphql(
|
|
23
24
|
db,
|
|
@@ -25,6 +26,7 @@ class CoreGlobalPermission(Node):
|
|
|
25
26
|
related_node_ids=related_node_ids,
|
|
26
27
|
filter_sensitive=filter_sensitive,
|
|
27
28
|
permissions=permissions,
|
|
29
|
+
include_properties=include_properties,
|
|
28
30
|
)
|
|
29
31
|
|
|
30
32
|
if fields:
|
|
@@ -43,6 +45,7 @@ class CoreObjectPermission(Node):
|
|
|
43
45
|
related_node_ids: Optional[set] = None,
|
|
44
46
|
filter_sensitive: bool = False,
|
|
45
47
|
permissions: Optional[dict] = None,
|
|
48
|
+
include_properties: bool = True,
|
|
46
49
|
) -> dict:
|
|
47
50
|
response = await super().to_graphql(
|
|
48
51
|
db,
|
|
@@ -50,6 +53,7 @@ class CoreObjectPermission(Node):
|
|
|
50
53
|
related_node_ids=related_node_ids,
|
|
51
54
|
filter_sensitive=filter_sensitive,
|
|
52
55
|
permissions=permissions,
|
|
56
|
+
include_properties=include_properties,
|
|
53
57
|
)
|
|
54
58
|
|
|
55
59
|
if fields:
|
infrahub/core/query/attribute.py
CHANGED
|
@@ -66,6 +66,8 @@ class AttributeUpdateValueQuery(AttributeQuery):
|
|
|
66
66
|
query = """
|
|
67
67
|
MATCH (a:Attribute { uuid: $attr_uuid })
|
|
68
68
|
MERGE (av:%(labels)s { %(props)s } )
|
|
69
|
+
WITH av, a
|
|
70
|
+
LIMIT 1
|
|
69
71
|
CREATE (a)-[r:%(rel_label)s { branch: $branch, branch_level: $branch_level, status: "active", from: $at }]->(av)
|
|
70
72
|
""" % {"rel_label": self.attr._rel_to_value_label, "labels": ":".join(labels), "props": ", ".join(prop_list)}
|
|
71
73
|
|
infrahub/core/query/diff.py
CHANGED
|
@@ -137,6 +137,14 @@ CALL {
|
|
|
137
137
|
r in relationships(latest_base_path)
|
|
138
138
|
WHERE r.from < $branch_from_time
|
|
139
139
|
)
|
|
140
|
+
// ------------------------
|
|
141
|
+
// special handling for nodes that had their kind updated,
|
|
142
|
+
// the migration leaves two nodes with the same UUID linked to the same Relationship
|
|
143
|
+
// ------------------------
|
|
144
|
+
AND (
|
|
145
|
+
n.uuid IS NULL OR base_prop.uuid IS NULL OR n.uuid <> base_prop.uuid
|
|
146
|
+
OR type(base_r_node) <> "IS_RELATED" OR type(base_r_prop) <> "IS_RELATED"
|
|
147
|
+
)
|
|
140
148
|
WITH latest_base_path, base_r_root, base_r_node, base_r_prop
|
|
141
149
|
ORDER BY base_r_prop.from DESC, base_r_node.from DESC, base_r_root.from DESC
|
|
142
150
|
LIMIT 1
|
|
@@ -146,7 +154,7 @@ CALL {
|
|
|
146
154
|
relationship_peer_side_query = """
|
|
147
155
|
WITH diff_path, latest_base_path, has_more_data
|
|
148
156
|
UNWIND [diff_path, latest_base_path] AS penultimate_path
|
|
149
|
-
WITH penultimate_path, has_more_data
|
|
157
|
+
WITH DISTINCT penultimate_path, has_more_data
|
|
150
158
|
CALL {
|
|
151
159
|
WITH penultimate_path
|
|
152
160
|
WITH penultimate_path, nodes(penultimate_path) AS d_nodes, relationships(penultimate_path) AS d_rels
|
|
@@ -175,8 +183,13 @@ CALL {
|
|
|
175
183
|
OR peer_r_node.to IS NULL
|
|
176
184
|
OR peer_r_node.to >= r_peer.from
|
|
177
185
|
)
|
|
186
|
+
// ------------------------
|
|
187
|
+
// special handling for nodes that had their kind updated,
|
|
188
|
+
// the migration leaves two nodes with the same UUID linked to the same Relationship
|
|
189
|
+
// ------------------------
|
|
190
|
+
AND (n.uuid IS NULL OR peer.uuid IS NULL OR n.uuid <> peer.uuid)
|
|
178
191
|
WITH peer_path, r_peer, r_prop
|
|
179
|
-
ORDER BY r_peer.branch = r_prop.branch DESC, r_peer.from DESC
|
|
192
|
+
ORDER BY r_peer.branch = r_prop.branch DESC, r_peer.status = r_prop.status DESC, r_peer.from DESC, r_peer.status ASC
|
|
180
193
|
LIMIT 1
|
|
181
194
|
RETURN peer_path
|
|
182
195
|
}
|
|
@@ -309,6 +322,14 @@ CALL {
|
|
|
309
322
|
AND [%(id_func)s(p), type(r_node)] <> [%(id_func)s(prop), type(r_prop)]
|
|
310
323
|
AND top_diff_rel.status = r_node.status
|
|
311
324
|
AND top_diff_rel.status = r_prop.status
|
|
325
|
+
// ------------------------
|
|
326
|
+
// special handling for nodes that had their kind updated,
|
|
327
|
+
// the migration leaves two nodes with the same UUID linked to the same Relationship
|
|
328
|
+
// ------------------------
|
|
329
|
+
AND (
|
|
330
|
+
p.uuid IS NULL OR prop.uuid IS NULL OR p.uuid <> prop.uuid
|
|
331
|
+
OR type(r_node) <> "IS_RELATED" OR type(r_prop) <> "IS_RELATED"
|
|
332
|
+
)
|
|
312
333
|
WITH path, p, node, prop, r_prop, r_node, type(r_node) AS rel_type, row_from_time
|
|
313
334
|
// -------------------------------------
|
|
314
335
|
// Exclude attributes/relationships added then removed on branch within timeframe
|
|
@@ -483,17 +504,26 @@ CALL {
|
|
|
483
504
|
AND [%(id_func)s(p), type(mid_diff_rel)] <> [%(id_func)s(prop), type(r_prop)]
|
|
484
505
|
// exclude paths where an active edge is below a deleted edge
|
|
485
506
|
AND (mid_diff_rel.status = "active" OR r_prop.status = "deleted")
|
|
507
|
+
// ------------------------
|
|
508
|
+
// special handling for nodes that had their kind updated,
|
|
509
|
+
// the migration leaves two nodes with the same UUID linked to the same Relationship
|
|
510
|
+
// ------------------------
|
|
511
|
+
AND (
|
|
512
|
+
p.uuid IS NULL OR prop.uuid IS NULL OR p.uuid <> prop.uuid
|
|
513
|
+
OR type(mid_diff_rel) <> "IS_RELATED" OR type(r_prop) <> "IS_RELATED"
|
|
514
|
+
)
|
|
486
515
|
WITH path, prop, r_prop, mid_r_root
|
|
487
516
|
ORDER BY
|
|
488
517
|
type(r_prop),
|
|
489
518
|
mid_r_root.branch = mid_diff_rel.branch DESC,
|
|
519
|
+
(mid_diff_rel.status = r_prop.status AND mid_diff_rel.branch = r_prop.branch) DESC,
|
|
490
520
|
r_prop.from DESC,
|
|
491
521
|
mid_r_root.from DESC
|
|
492
522
|
WITH prop, type(r_prop) AS type_r_prop, head(collect(path)) AS latest_prop_path
|
|
493
523
|
RETURN latest_prop_path
|
|
494
524
|
}
|
|
495
525
|
// -------------------------------------
|
|
496
|
-
// Exclude properties within the timeframe
|
|
526
|
+
// Exclude properties added and deleted within the timeframe
|
|
497
527
|
// -------------------------------------
|
|
498
528
|
WITH q, nodes(latest_prop_path)[3] AS prop, type(relationships(latest_prop_path)[2]) AS rel_type, latest_prop_path, has_more_data, row_from_time
|
|
499
529
|
CALL {
|
|
@@ -594,6 +624,14 @@ AND (
|
|
|
594
624
|
)
|
|
595
625
|
)
|
|
596
626
|
)
|
|
627
|
+
// ------------------------
|
|
628
|
+
// special handling for nodes that had their kind updated,
|
|
629
|
+
// the migration leaves two nodes with the same UUID linked to the same Relationship
|
|
630
|
+
// ------------------------
|
|
631
|
+
AND (
|
|
632
|
+
n.uuid IS NULL OR q.uuid IS NULL OR n.uuid <> q.uuid
|
|
633
|
+
OR type(r_node) <> "IS_RELATED" OR type(diff_rel) <> "IS_RELATED"
|
|
634
|
+
)
|
|
597
635
|
AND ALL(
|
|
598
636
|
r_pair IN [[r_root, r_node], [r_node, diff_rel]]
|
|
599
637
|
// filter out paths where a base branch edge follows a branch edge
|
infrahub/core/query/node.py
CHANGED
|
@@ -203,15 +203,16 @@ class NodeCreateAllQuery(NodeQuery):
|
|
|
203
203
|
}
|
|
204
204
|
ipnetwork_prop_list = [f"{key}: {value}" for key, value in ipnetwork_prop.items()]
|
|
205
205
|
|
|
206
|
-
|
|
207
|
-
MATCH (root:Root)
|
|
208
|
-
CREATE (n:Node:%(labels)s $node_prop )
|
|
209
|
-
CREATE (n)-[r:IS_PART_OF $node_branch_prop ]->(root)
|
|
206
|
+
attrs_query = """
|
|
210
207
|
WITH distinct n
|
|
211
|
-
|
|
208
|
+
UNWIND $attrs AS attr
|
|
209
|
+
CALL {
|
|
210
|
+
WITH n, attr
|
|
212
211
|
CREATE (a:Attribute { uuid: attr.uuid, name: attr.name, branch_support: attr.branch_support })
|
|
213
212
|
CREATE (n)-[:HAS_ATTRIBUTE { branch: attr.branch, branch_level: attr.branch_level, status: attr.status, from: $at }]->(a)
|
|
214
213
|
MERGE (av:AttributeValue { value: attr.content.value, is_default: attr.content.is_default })
|
|
214
|
+
WITH n, attr, av, a
|
|
215
|
+
LIMIT 1
|
|
215
216
|
CREATE (a)-[:HAS_VALUE { branch: attr.branch, branch_level: attr.branch_level, status: attr.status, from: $at }]->(av)
|
|
216
217
|
MERGE (ip:Boolean { value: attr.is_protected })
|
|
217
218
|
MERGE (iv:Boolean { value: attr.is_visible })
|
|
@@ -225,11 +226,19 @@ class NodeCreateAllQuery(NodeQuery):
|
|
|
225
226
|
MERGE (peer:Node { uuid: prop.peer_id })
|
|
226
227
|
CREATE (a)-[:HAS_OWNER { branch: attr.branch, branch_level: attr.branch_level, status: attr.status, from: $at }]->(peer)
|
|
227
228
|
)
|
|
228
|
-
|
|
229
|
-
|
|
229
|
+
}"""
|
|
230
|
+
|
|
231
|
+
attrs_iphost_query = """
|
|
232
|
+
WITH distinct n
|
|
233
|
+
UNWIND $attrs_iphost AS attr_iphost
|
|
234
|
+
CALL {
|
|
235
|
+
WITH n, attr_iphost
|
|
236
|
+
WITH n, attr_iphost AS attr
|
|
230
237
|
CREATE (a:Attribute { uuid: attr.uuid, name: attr.name, branch_support: attr.branch_support })
|
|
231
238
|
CREATE (n)-[:HAS_ATTRIBUTE { branch: attr.branch, branch_level: attr.branch_level, status: attr.status, from: $at }]->(a)
|
|
232
239
|
MERGE (av:AttributeValue:AttributeIPHost { %(iphost_prop)s })
|
|
240
|
+
WITH n, attr, av, a
|
|
241
|
+
LIMIT 1
|
|
233
242
|
CREATE (a)-[:HAS_VALUE { branch: attr.branch, branch_level: attr.branch_level, status: attr.status, from: $at }]->(av)
|
|
234
243
|
MERGE (ip:Boolean { value: attr.is_protected })
|
|
235
244
|
MERGE (iv:Boolean { value: attr.is_visible })
|
|
@@ -243,11 +252,20 @@ class NodeCreateAllQuery(NodeQuery):
|
|
|
243
252
|
MERGE (peer:Node { uuid: prop.peer_id })
|
|
244
253
|
CREATE (a)-[:HAS_OWNER { branch: attr.branch, branch_level: attr.branch_level, status: attr.status, from: $at }]->(peer)
|
|
245
254
|
)
|
|
246
|
-
|
|
247
|
-
|
|
255
|
+
}
|
|
256
|
+
""" % {"iphost_prop": ", ".join(iphost_prop_list)}
|
|
257
|
+
|
|
258
|
+
attrs_ipnetwork_query = """
|
|
259
|
+
WITH distinct n
|
|
260
|
+
UNWIND $attrs_ipnetwork AS attr_ipnetwork
|
|
261
|
+
CALL {
|
|
262
|
+
WITH n, attr_ipnetwork
|
|
263
|
+
WITH n, attr_ipnetwork AS attr
|
|
248
264
|
CREATE (a:Attribute { uuid: attr.uuid, name: attr.name, branch_support: attr.branch_support })
|
|
249
265
|
CREATE (n)-[:HAS_ATTRIBUTE { branch: attr.branch, branch_level: attr.branch_level, status: attr.status, from: $at }]->(a)
|
|
250
266
|
MERGE (av:AttributeValue:AttributeIPNetwork { %(ipnetwork_prop)s })
|
|
267
|
+
WITH n, attr, av, a
|
|
268
|
+
LIMIT 1
|
|
251
269
|
CREATE (a)-[:HAS_VALUE { branch: attr.branch, branch_level: attr.branch_level, status: attr.status, from: $at }]->(av)
|
|
252
270
|
MERGE (ip:Boolean { value: attr.is_protected })
|
|
253
271
|
MERGE (iv:Boolean { value: attr.is_visible })
|
|
@@ -261,8 +279,14 @@ class NodeCreateAllQuery(NodeQuery):
|
|
|
261
279
|
MERGE (peer:Node { uuid: prop.peer_id })
|
|
262
280
|
CREATE (a)-[:HAS_OWNER { branch: attr.branch, branch_level: attr.branch_level, status: attr.status, from: $at }]->(peer)
|
|
263
281
|
)
|
|
264
|
-
|
|
265
|
-
|
|
282
|
+
}
|
|
283
|
+
""" % {"ipnetwork_prop": ", ".join(ipnetwork_prop_list)}
|
|
284
|
+
|
|
285
|
+
rels_bidir_query = """
|
|
286
|
+
WITH distinct n
|
|
287
|
+
UNWIND $rels_bidir AS rel
|
|
288
|
+
CALL {
|
|
289
|
+
WITH n, rel
|
|
266
290
|
MERGE (d:Node { uuid: rel.destination_id })
|
|
267
291
|
CREATE (rl:Relationship { uuid: rel.uuid, name: rel.name, branch_support: rel.branch_support })
|
|
268
292
|
CREATE (n)-[:IS_RELATED %(rel_prop)s ]->(rl)
|
|
@@ -279,8 +303,15 @@ class NodeCreateAllQuery(NodeQuery):
|
|
|
279
303
|
MERGE (peer:Node { uuid: prop.peer_id })
|
|
280
304
|
CREATE (rl)-[:HAS_OWNER { branch: rel.branch, branch_level: rel.branch_level, status: rel.status, from: $at }]->(peer)
|
|
281
305
|
)
|
|
282
|
-
|
|
283
|
-
|
|
306
|
+
}
|
|
307
|
+
""" % {"rel_prop": rel_prop_str}
|
|
308
|
+
|
|
309
|
+
rels_out_query = """
|
|
310
|
+
WITH distinct n
|
|
311
|
+
UNWIND $rels_out AS rel_out
|
|
312
|
+
CALL {
|
|
313
|
+
WITH n, rel_out
|
|
314
|
+
WITH n, rel_out as rel
|
|
284
315
|
MERGE (d:Node { uuid: rel.destination_id })
|
|
285
316
|
CREATE (rl:Relationship { uuid: rel.uuid, name: rel.name, branch_support: rel.branch_support })
|
|
286
317
|
CREATE (n)-[:IS_RELATED %(rel_prop)s ]->(rl)
|
|
@@ -297,8 +328,15 @@ class NodeCreateAllQuery(NodeQuery):
|
|
|
297
328
|
MERGE (peer:Node { uuid: prop.peer_id })
|
|
298
329
|
CREATE (rl)-[:HAS_OWNER { branch: rel.branch, branch_level: rel.branch_level, status: rel.status, from: $at }]->(peer)
|
|
299
330
|
)
|
|
300
|
-
|
|
301
|
-
|
|
331
|
+
}
|
|
332
|
+
""" % {"rel_prop": rel_prop_str}
|
|
333
|
+
|
|
334
|
+
rels_in_query = """
|
|
335
|
+
WITH distinct n
|
|
336
|
+
UNWIND $rels_in AS rel_in
|
|
337
|
+
CALL {
|
|
338
|
+
WITH n, rel_in
|
|
339
|
+
WITH n, rel_in AS rel
|
|
302
340
|
MERGE (d:Node { uuid: rel.destination_id })
|
|
303
341
|
CREATE (rl:Relationship { uuid: rel.uuid, name: rel.name, branch_support: rel.branch_support })
|
|
304
342
|
CREATE (n)<-[:IS_RELATED %(rel_prop)s ]-(rl)
|
|
@@ -315,14 +353,23 @@ class NodeCreateAllQuery(NodeQuery):
|
|
|
315
353
|
MERGE (peer:Node { uuid: prop.peer_id })
|
|
316
354
|
CREATE (rl)-[:HAS_OWNER { branch: rel.branch, branch_level: rel.branch_level, status: rel.status, from: $at }]->(peer)
|
|
317
355
|
)
|
|
318
|
-
|
|
356
|
+
}
|
|
357
|
+
""" % {"rel_prop": rel_prop_str}
|
|
358
|
+
|
|
359
|
+
query = f"""
|
|
360
|
+
MATCH (root:Root)
|
|
361
|
+
CREATE (n:Node:%(labels)s $node_prop )
|
|
362
|
+
CREATE (n)-[r:IS_PART_OF $node_branch_prop ]->(root)
|
|
363
|
+
{attrs_query if self.params["attrs"] else ""}
|
|
364
|
+
{attrs_iphost_query if self.params["attrs_iphost"] else ""}
|
|
365
|
+
{attrs_ipnetwork_query if self.params["attrs_ipnetwork"] else ""}
|
|
366
|
+
{rels_bidir_query if self.params["rels_bidir"] else ""}
|
|
367
|
+
{rels_out_query if self.params["rels_out"] else ""}
|
|
368
|
+
{rels_in_query if self.params["rels_in"] else ""}
|
|
319
369
|
WITH distinct n
|
|
320
370
|
MATCH (n)-[:HAS_ATTRIBUTE|IS_RELATED]-(rn)-[:HAS_VALUE|IS_RELATED]-(rv)
|
|
321
371
|
""" % {
|
|
322
372
|
"labels": ":".join(self.node.get_labels()),
|
|
323
|
-
"rel_prop": rel_prop_str,
|
|
324
|
-
"iphost_prop": ", ".join(iphost_prop_list),
|
|
325
|
-
"ipnetwork_prop": ", ".join(ipnetwork_prop_list),
|
|
326
373
|
}
|
|
327
374
|
|
|
328
375
|
self.params["at"] = at.to_string()
|
|
@@ -479,11 +526,17 @@ class NodeListGetAttributeQuery(Query):
|
|
|
479
526
|
self.return_labels = ["n", "a", "av", "r1", "r2"]
|
|
480
527
|
|
|
481
528
|
# Add Is_Protected and Is_visible
|
|
529
|
+
rel_isv_branch_filter, _ = self.branch.get_query_filter_path(
|
|
530
|
+
at=self.at, branch_agnostic=self.branch_agnostic, variable_name="rel_isv"
|
|
531
|
+
)
|
|
532
|
+
rel_isp_branch_filter, _ = self.branch.get_query_filter_path(
|
|
533
|
+
at=self.at, branch_agnostic=self.branch_agnostic, variable_name="rel_isp"
|
|
534
|
+
)
|
|
482
535
|
query = """
|
|
483
536
|
MATCH (a)-[rel_isv:IS_VISIBLE]-(isv:Boolean)
|
|
484
537
|
MATCH (a)-[rel_isp:IS_PROTECTED]-(isp:Boolean)
|
|
485
|
-
WHERE
|
|
486
|
-
""" % {"
|
|
538
|
+
WHERE (%(rel_isv_branch_filter)s) AND (%(rel_isp_branch_filter)s)
|
|
539
|
+
""" % {"rel_isv_branch_filter": rel_isv_branch_filter, "rel_isp_branch_filter": rel_isp_branch_filter}
|
|
487
540
|
self.add_to_query(query)
|
|
488
541
|
|
|
489
542
|
self.return_labels.extend(["isv", "isp", "rel_isv", "rel_isp"])
|
|
@@ -8,6 +8,7 @@ from typing import TYPE_CHECKING, Generator, Optional, Union
|
|
|
8
8
|
from infrahub_sdk.uuidt import UUIDT
|
|
9
9
|
|
|
10
10
|
from infrahub.core.constants import RelationshipDirection, RelationshipStatus
|
|
11
|
+
from infrahub.core.constants.database import DatabaseEdgeType
|
|
11
12
|
from infrahub.core.query import Query, QueryType
|
|
12
13
|
from infrahub.core.query.subquery import build_subquery_filter, build_subquery_order
|
|
13
14
|
from infrahub.core.timestamp import Timestamp
|
|
@@ -24,7 +25,7 @@ if TYPE_CHECKING:
|
|
|
24
25
|
from infrahub.core.schema import RelationshipSchema
|
|
25
26
|
from infrahub.database import InfrahubDatabase
|
|
26
27
|
|
|
27
|
-
# pylint: disable=redefined-builtin
|
|
28
|
+
# pylint: disable=redefined-builtin,too-many-lines
|
|
28
29
|
|
|
29
30
|
|
|
30
31
|
@dataclass
|
|
@@ -583,6 +584,7 @@ class RelationshipGetPeerQuery(Query):
|
|
|
583
584
|
query = """
|
|
584
585
|
MATCH (source_node:Node)%(arrow_left_start)s[:IS_RELATED]%(arrow_left_end)s(rl:Relationship { name: $rel_identifier })
|
|
585
586
|
WHERE source_node.uuid IN $source_ids
|
|
587
|
+
WITH DISTINCT source_node, rl
|
|
586
588
|
CALL {
|
|
587
589
|
WITH rl, source_node
|
|
588
590
|
MATCH path = (source_node)%(path)s(peer:Node)
|
|
@@ -659,10 +661,23 @@ class RelationshipGetPeerQuery(Query):
|
|
|
659
661
|
# QUERY Properties
|
|
660
662
|
# ----------------------------------------------------------------------------
|
|
661
663
|
query = """
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
664
|
+
CALL {
|
|
665
|
+
WITH rl
|
|
666
|
+
MATCH (rl)-[r:IS_VISIBLE]-(is_visible)
|
|
667
|
+
WHERE %(branch_filter)s
|
|
668
|
+
RETURN r AS rel_is_visible, is_visible
|
|
669
|
+
ORDER BY r.branch_level DESC, r.from DESC, r.status ASC
|
|
670
|
+
LIMIT 1
|
|
671
|
+
}
|
|
672
|
+
CALL {
|
|
673
|
+
WITH rl
|
|
674
|
+
MATCH (rl)-[r:IS_PROTECTED]-(is_protected)
|
|
675
|
+
WHERE %(branch_filter)s
|
|
676
|
+
RETURN r AS rel_is_protected, is_protected
|
|
677
|
+
ORDER BY r.branch_level DESC, r.from DESC, r.status ASC
|
|
678
|
+
LIMIT 1
|
|
679
|
+
}
|
|
680
|
+
""" % {"branch_filter": branch_filter}
|
|
666
681
|
|
|
667
682
|
self.add_to_query(query)
|
|
668
683
|
|
|
@@ -672,20 +687,24 @@ class RelationshipGetPeerQuery(Query):
|
|
|
672
687
|
# We must query them one by one otherwise the second one won't return
|
|
673
688
|
for node_prop in ["source", "owner"]:
|
|
674
689
|
query = """
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
690
|
+
CALL {
|
|
691
|
+
WITH rl
|
|
692
|
+
OPTIONAL MATCH (rl)-[r:HAS_%(node_prop_type)s]-(%(node_prop)s)
|
|
693
|
+
WHERE %(branch_filter)s
|
|
694
|
+
RETURN r AS rel_%(node_prop)s, %(node_prop)s
|
|
695
|
+
ORDER BY r.branch_level DESC, r.from DESC, r.status ASC
|
|
696
|
+
LIMIT 1
|
|
697
|
+
}
|
|
698
|
+
""" % {
|
|
699
|
+
"node_prop": node_prop,
|
|
700
|
+
"node_prop_type": node_prop.upper(),
|
|
701
|
+
"branch_filter": branch_filter,
|
|
702
|
+
}
|
|
686
703
|
self.add_to_query(query)
|
|
687
704
|
self.update_return_labels([f"rel_{node_prop}", node_prop])
|
|
688
705
|
|
|
706
|
+
self.add_to_query("WITH " + ",".join(self.return_labels))
|
|
707
|
+
|
|
689
708
|
# ----------------------------------------------------------------------------
|
|
690
709
|
# ORDER Results
|
|
691
710
|
# ----------------------------------------------------------------------------
|
|
@@ -905,7 +924,8 @@ class RelationshipCountPerNodeQuery(Query):
|
|
|
905
924
|
path = "<-[r:IS_RELATED]-"
|
|
906
925
|
|
|
907
926
|
query = """
|
|
908
|
-
MATCH (rl:Relationship { name: $rel_identifier })
|
|
927
|
+
MATCH (peer_node:Node)%(path)s(rl:Relationship { name: $rel_identifier })
|
|
928
|
+
WHERE peer_node.uuid IN $peer_ids AND %(branch_filter)s
|
|
909
929
|
CALL {
|
|
910
930
|
WITH rl
|
|
911
931
|
MATCH path = (peer_node:Node)%(path)s(rl)
|
|
@@ -931,3 +951,73 @@ class RelationshipCountPerNodeQuery(Query):
|
|
|
931
951
|
data[node_id] = 0
|
|
932
952
|
|
|
933
953
|
return data
|
|
954
|
+
|
|
955
|
+
|
|
956
|
+
class RelationshipDeleteAllQuery(Query):
|
|
957
|
+
"""
|
|
958
|
+
Delete all relationships linked to a given node on a given branch at a given time. For every IS_RELATED edge:
|
|
959
|
+
- Set `to` time if an active edge exist on the same branch.
|
|
960
|
+
- Create `deleted` edge.
|
|
961
|
+
- Apply above to every edges linked to any connected Relationship node.
|
|
962
|
+
"""
|
|
963
|
+
|
|
964
|
+
name = "node_delete_all_relationships"
|
|
965
|
+
type = QueryType.WRITE
|
|
966
|
+
insert_return = False
|
|
967
|
+
|
|
968
|
+
def __init__(self, node_id: str, **kwargs):
|
|
969
|
+
self.node_id = node_id
|
|
970
|
+
super().__init__(**kwargs)
|
|
971
|
+
|
|
972
|
+
async def query_init(self, db: InfrahubDatabase, **kwargs) -> None:
|
|
973
|
+
self.params["source_id"] = kwargs["node_id"]
|
|
974
|
+
self.params["branch"] = self.branch.name
|
|
975
|
+
|
|
976
|
+
self.params["rel_prop"] = {
|
|
977
|
+
"branch": self.branch.name,
|
|
978
|
+
"branch_level": self.branch.hierarchy_level,
|
|
979
|
+
"status": RelationshipStatus.DELETED.value,
|
|
980
|
+
"from": self.at.to_string(),
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
self.params["at"] = self.at.to_string()
|
|
984
|
+
|
|
985
|
+
active_rel_filter, rel_params = self.branch.get_query_filter_path(
|
|
986
|
+
at=self.at, variable_name="active_edge", branch_agnostic=self.branch_agnostic
|
|
987
|
+
)
|
|
988
|
+
self.params.update(rel_params)
|
|
989
|
+
|
|
990
|
+
query_match_relationships = """
|
|
991
|
+
MATCH (s:Node { uuid: $source_id })-[active_edge:IS_RELATED]-(rl:Relationship)
|
|
992
|
+
WHERE %(active_rel_filter)s AND active_edge.status = "active"
|
|
993
|
+
WITH DISTINCT rl
|
|
994
|
+
""" % {"active_rel_filter": active_rel_filter}
|
|
995
|
+
|
|
996
|
+
self.add_to_query(query_match_relationships)
|
|
997
|
+
|
|
998
|
+
for arrow_left, arrow_right in (("<-", "-"), ("-", "->")):
|
|
999
|
+
for edge_type in [
|
|
1000
|
+
DatabaseEdgeType.IS_RELATED.value,
|
|
1001
|
+
DatabaseEdgeType.IS_VISIBLE.value,
|
|
1002
|
+
DatabaseEdgeType.IS_PROTECTED.value,
|
|
1003
|
+
DatabaseEdgeType.HAS_OWNER.value,
|
|
1004
|
+
DatabaseEdgeType.HAS_SOURCE.value,
|
|
1005
|
+
]:
|
|
1006
|
+
query = """
|
|
1007
|
+
CALL {
|
|
1008
|
+
WITH rl
|
|
1009
|
+
MATCH (rl)%(arrow_left)s[active_edge:%(edge_type)s]%(arrow_right)s(n)
|
|
1010
|
+
WHERE %(active_rel_filter)s AND active_edge.status ="active"
|
|
1011
|
+
CREATE (rl)%(arrow_left)s[deleted_edge:%(edge_type)s $rel_prop]%(arrow_right)s(n)
|
|
1012
|
+
SET deleted_edge.hierarchy = active_edge.hierarchy
|
|
1013
|
+
WITH active_edge
|
|
1014
|
+
WHERE active_edge.branch = $branch AND active_edge.to IS NULL
|
|
1015
|
+
SET active_edge.to = $at
|
|
1016
|
+
}
|
|
1017
|
+
""" % {
|
|
1018
|
+
"arrow_left": arrow_left,
|
|
1019
|
+
"arrow_right": arrow_right,
|
|
1020
|
+
"active_rel_filter": active_rel_filter,
|
|
1021
|
+
"edge_type": edge_type,
|
|
1022
|
+
}
|
|
1023
|
+
self.add_to_query(query)
|
|
@@ -123,6 +123,7 @@ class NumberPoolGetAllocated(Query):
|
|
|
123
123
|
self.params["node_attribute"] = self.pool.node_attribute.value
|
|
124
124
|
self.params["start_range"] = self.pool.start_range.value
|
|
125
125
|
self.params["end_range"] = self.pool.end_range.value
|
|
126
|
+
self.params["pool_id"] = self.pool.get_id()
|
|
126
127
|
|
|
127
128
|
branch_filter, branch_params = self.branch.get_query_filter_path(
|
|
128
129
|
at=self.at.to_string(), branch_agnostic=self.branch_agnostic
|
|
@@ -133,7 +134,8 @@ class NumberPoolGetAllocated(Query):
|
|
|
133
134
|
MATCH (n:%(node)s)-[ha:HAS_ATTRIBUTE]-(a:Attribute {name: $node_attribute})-[hv:HAS_VALUE]-(av:AttributeValue)
|
|
134
135
|
MATCH (a)-[hs:HAS_SOURCE]-(pool:%(number_pool_kind)s)
|
|
135
136
|
WHERE
|
|
136
|
-
|
|
137
|
+
pool.uuid = $pool_id
|
|
138
|
+
AND av.value >= $start_range and av.value <= $end_range
|
|
137
139
|
AND all(r in [ha, hv, hs] WHERE (%(branch_filter)s))
|
|
138
140
|
AND ha.status = "active"
|
|
139
141
|
AND hv.status = "active"
|
|
@@ -274,6 +276,8 @@ class NumberPoolSetReserved(Query):
|
|
|
274
276
|
query = """
|
|
275
277
|
MATCH (pool:%(number_pool)s { uuid: $pool_id })
|
|
276
278
|
MERGE (value:AttributeValue { value: $reserved, is_default: false })
|
|
279
|
+
WITH value, pool
|
|
280
|
+
LIMIT 1
|
|
277
281
|
CREATE (pool)-[rel:IS_RESERVED $rel_prop]->(value)
|
|
278
282
|
""" % {"number_pool": InfrahubKind.NUMBERPOOL}
|
|
279
283
|
|