infrahub-server 1.2.10__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/config.py +9 -0
- infrahub/core/account.py +24 -47
- infrahub/core/attribute.py +10 -12
- 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/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/field_summary.py +2 -4
- infrahub/core/diff/query/merge.py +70 -123
- infrahub/core/diff/query/save.py +20 -32
- infrahub/core/diff/query/summary_counts_enricher.py +34 -54
- infrahub/core/diff/query_parser.py +5 -1
- infrahub/core/diff/tasks.py +3 -3
- infrahub/core/manager.py +14 -11
- 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/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/diff.py +26 -32
- infrahub/core/query/ipam.py +10 -20
- infrahub/core/query/node.py +28 -46
- infrahub/core/query/relationship.py +51 -28
- infrahub/core/query/resource_manager.py +1 -2
- infrahub/core/query/subquery.py +2 -4
- infrahub/core/relationship/model.py +3 -0
- 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 +12 -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 +5 -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/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 +18 -2
- infrahub/trigger/tasks.py +3 -1
- infrahub/workflows/catalogue.py +76 -0
- {infrahub_server-1.2.10.dist-info → infrahub_server-1.3.0a0.dist-info}/METADATA +2 -2
- {infrahub_server-1.2.10.dist-info → infrahub_server-1.3.0a0.dist-info}/RECORD +121 -118
- infrahub_testcontainers/container.py +0 -1
- infrahub_testcontainers/docker-compose.test.yml +1 -1
- infrahub_testcontainers/helpers.py +8 -2
- 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.10.dist-info → infrahub_server-1.3.0a0.dist-info}/LICENSE.txt +0 -0
- {infrahub_server-1.2.10.dist-info → infrahub_server-1.3.0a0.dist-info}/WHEEL +0 -0
- {infrahub_server-1.2.10.dist-info → infrahub_server-1.3.0a0.dist-info}/entry_points.txt +0 -0
|
@@ -133,8 +133,7 @@ class NodeDuplicateQuery(Query):
|
|
|
133
133
|
|
|
134
134
|
# ruff: noqa: E501
|
|
135
135
|
query = """
|
|
136
|
-
CALL {
|
|
137
|
-
WITH node
|
|
136
|
+
CALL (node) {
|
|
138
137
|
MATCH (root:Root)<-[r:IS_PART_OF]-(node)
|
|
139
138
|
WHERE %(branch_filter)s
|
|
140
139
|
RETURN node as n1, r as r1
|
|
@@ -147,8 +146,7 @@ class NodeDuplicateQuery(Query):
|
|
|
147
146
|
WITH active_node, new_node
|
|
148
147
|
// Process Outbound Relationship
|
|
149
148
|
MATCH (active_node)-[]->(peer)
|
|
150
|
-
CALL {
|
|
151
|
-
WITH active_node, peer
|
|
149
|
+
CALL (active_node, peer) {
|
|
152
150
|
MATCH (active_node)-[r]->(peer)
|
|
153
151
|
WHERE %(branch_filter)s
|
|
154
152
|
RETURN active_node as n1, r as rel_outband1, peer as p1
|
|
@@ -167,8 +165,7 @@ class NodeDuplicateQuery(Query):
|
|
|
167
165
|
WITH DISTINCT active_node, new_node
|
|
168
166
|
// Process Inbound Relationship
|
|
169
167
|
MATCH (active_node)<-[]-(peer)
|
|
170
|
-
CALL {
|
|
171
|
-
WITH active_node, peer
|
|
168
|
+
CALL (active_node, peer) {
|
|
172
169
|
MATCH (active_node)<-[r]-(peer)
|
|
173
170
|
WHERE %(branch_filter)s
|
|
174
171
|
RETURN active_node as n1, r as rel_inband1, peer as p1
|
|
@@ -116,8 +116,7 @@ class RelationshipDuplicateQuery(Query):
|
|
|
116
116
|
|
|
117
117
|
# ruff: noqa: E501
|
|
118
118
|
query = """
|
|
119
|
-
CALL {
|
|
120
|
-
WITH source, rel, destination
|
|
119
|
+
CALL (source, rel, destination) {
|
|
121
120
|
MATCH path = (source)-[r1:IS_RELATED]-(rel)-[r2:IS_RELATED]-(destination)
|
|
122
121
|
WHERE all(r IN relationships(path) WHERE %(branch_filter)s)
|
|
123
122
|
RETURN rel as rel1, r1 as r11, r2 as r12
|
|
@@ -130,8 +129,7 @@ class RelationshipDuplicateQuery(Query):
|
|
|
130
129
|
WITH DISTINCT(active_rel) as active_rel, new_rel
|
|
131
130
|
// Process Inbound Relationship
|
|
132
131
|
MATCH (active_rel)<-[]-(peer)
|
|
133
|
-
CALL {
|
|
134
|
-
WITH active_rel, peer
|
|
132
|
+
CALL (active_rel, peer) {
|
|
135
133
|
MATCH (active_rel)<-[r]-(peer)
|
|
136
134
|
WHERE %(branch_filter)s
|
|
137
135
|
RETURN active_rel as n1, r as rel_inband1, peer as p1
|
|
@@ -150,8 +148,7 @@ class RelationshipDuplicateQuery(Query):
|
|
|
150
148
|
WITH DISTINCT(active_rel) as active_rel, new_rel
|
|
151
149
|
// Process Outbound Relationship
|
|
152
150
|
MATCH (active_rel)-[]->(peer)
|
|
153
|
-
CALL {
|
|
154
|
-
WITH active_rel, peer
|
|
151
|
+
CALL (active_rel, peer) {
|
|
155
152
|
MATCH (active_rel)-[r]->(peer)
|
|
156
153
|
WHERE %(branch_filter)s
|
|
157
154
|
RETURN active_rel as n1, r as rel_outband1, peer as p1
|
|
@@ -75,8 +75,7 @@ class NodeAttributeRemoveMigrationQuery01(AttributeMigrationQuery):
|
|
|
75
75
|
MATCH (node:%(node_kind)s)
|
|
76
76
|
WHERE (size($kinds_to_ignore) = 0 OR NOT any(l IN labels(node) WHERE l IN $kinds_to_ignore))
|
|
77
77
|
AND exists((node)-[:HAS_ATTRIBUTE]-(:Attribute { name: $attr_name }))
|
|
78
|
-
CALL {
|
|
79
|
-
WITH node
|
|
78
|
+
CALL (node) {
|
|
80
79
|
MATCH (root:Root)<-[r:IS_PART_OF]-(node)
|
|
81
80
|
WHERE %(branch_filter)s
|
|
82
81
|
RETURN node as n1, r as r1
|
|
@@ -86,8 +85,7 @@ class NodeAttributeRemoveMigrationQuery01(AttributeMigrationQuery):
|
|
|
86
85
|
WITH n1 as active_node, r1 as rb
|
|
87
86
|
WHERE rb.status = "active"
|
|
88
87
|
// Find all the attributes that need to be updated
|
|
89
|
-
CALL {
|
|
90
|
-
WITH active_node
|
|
88
|
+
CALL (active_node) {
|
|
91
89
|
MATCH (active_node)-[r:HAS_ATTRIBUTE]-(attr:Attribute { name: $attr_name })
|
|
92
90
|
WHERE %(branch_filter)s
|
|
93
91
|
RETURN active_node as n1, r as r1, attr as attr1
|
|
@@ -98,8 +96,7 @@ class NodeAttributeRemoveMigrationQuery01(AttributeMigrationQuery):
|
|
|
98
96
|
WHERE rb.status = "active"
|
|
99
97
|
WITH active_attr
|
|
100
98
|
MATCH (active_attr)-[]-(peer)
|
|
101
|
-
CALL {
|
|
102
|
-
WITH active_attr, peer
|
|
99
|
+
CALL (active_attr, peer) {
|
|
103
100
|
MATCH (active_attr)-[r]-(peer)
|
|
104
101
|
WHERE %(branch_filter)s
|
|
105
102
|
RETURN active_attr as a1, r as r1, peer as p1
|
|
@@ -63,8 +63,7 @@ class NodeRemoveMigrationBaseQuery(MigrationQuery):
|
|
|
63
63
|
query = """
|
|
64
64
|
// Find all the active nodes
|
|
65
65
|
MATCH (node:%(node_kind)s)
|
|
66
|
-
CALL {
|
|
67
|
-
WITH node
|
|
66
|
+
CALL (node) {
|
|
68
67
|
MATCH (root:Root)<-[r:IS_PART_OF]-(node)
|
|
69
68
|
WHERE %(branch_filter)s
|
|
70
69
|
RETURN node as n1, r as r1
|
|
@@ -96,8 +95,7 @@ class NodeRemoveMigrationQueryIn(NodeRemoveMigrationBaseQuery):
|
|
|
96
95
|
// Process Inbound Relationship
|
|
97
96
|
WITH active_node
|
|
98
97
|
MATCH (active_node)<-[]-(peer)
|
|
99
|
-
CALL {
|
|
100
|
-
WITH active_node, peer
|
|
98
|
+
CALL (active_node, peer) {
|
|
101
99
|
MATCH (active_node)-[r]->(peer)
|
|
102
100
|
WHERE %(branch_filter)s
|
|
103
101
|
RETURN active_node as n1, r as rel_inband1, peer as p1
|
|
@@ -142,8 +140,7 @@ class NodeRemoveMigrationQueryOut(NodeRemoveMigrationBaseQuery):
|
|
|
142
140
|
// Process Outbound Relationship
|
|
143
141
|
WITH active_node
|
|
144
142
|
MATCH (active_node)-[]->(peer)
|
|
145
|
-
CALL {
|
|
146
|
-
WITH active_node, peer
|
|
143
|
+
CALL (active_node, peer) {
|
|
147
144
|
MATCH (active_node)-[r]->(peer)
|
|
148
145
|
WHERE %(branch_filter)s
|
|
149
146
|
RETURN active_node as n1, r as rel_outband1, peer as p1
|
infrahub/core/models.py
CHANGED
|
@@ -20,6 +20,7 @@ if TYPE_CHECKING:
|
|
|
20
20
|
from infrahub.core.schema.schema_branch import SchemaBranch
|
|
21
21
|
|
|
22
22
|
GENERIC_ATTRIBUTES_TO_IGNORE = ["namespace", "name", "branch"]
|
|
23
|
+
PROPERTY_NAMES_TO_IGNORE = ["regex", "min_length", "max_length"]
|
|
23
24
|
|
|
24
25
|
|
|
25
26
|
class NodeKind(BaseModel):
|
|
@@ -252,11 +253,37 @@ class SchemaUpdateValidationResult(BaseModel):
|
|
|
252
253
|
if not sub_field_diff:
|
|
253
254
|
raise ValueError("sub_field_diff must be defined, unexpected situation")
|
|
254
255
|
|
|
255
|
-
for prop_name in sub_field_diff.changed:
|
|
256
|
+
for prop_name, prop_diff in sub_field_diff.changed.items():
|
|
257
|
+
if prop_name in PROPERTY_NAMES_TO_IGNORE:
|
|
258
|
+
continue
|
|
259
|
+
|
|
256
260
|
field_info = field.model_fields[prop_name]
|
|
257
261
|
field_update = str(field_info.json_schema_extra.get("update")) # type: ignore[union-attr]
|
|
258
262
|
|
|
259
|
-
|
|
263
|
+
if isinstance(prop_diff, HashableModelDiff):
|
|
264
|
+
for param_field_name in prop_diff.changed:
|
|
265
|
+
# override field_update if this field has its own json_schema_extra.update
|
|
266
|
+
try:
|
|
267
|
+
prop_field = getattr(field, prop_name)
|
|
268
|
+
param_field_info = prop_field.model_fields[param_field_name]
|
|
269
|
+
param_field_update = str(param_field_info.json_schema_extra.get("update"))
|
|
270
|
+
except (AttributeError, KeyError):
|
|
271
|
+
param_field_update = None
|
|
272
|
+
|
|
273
|
+
schema_path = SchemaPath(
|
|
274
|
+
schema_kind=schema.kind,
|
|
275
|
+
path_type=path_type,
|
|
276
|
+
field_name=field_name,
|
|
277
|
+
property_name=f"{prop_name}.{param_field_name}",
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
self._process_field(
|
|
281
|
+
schema_path=schema_path,
|
|
282
|
+
field_update=param_field_update or field_update,
|
|
283
|
+
)
|
|
284
|
+
continue
|
|
285
|
+
|
|
286
|
+
schema_path = SchemaPath(
|
|
260
287
|
schema_kind=schema.kind,
|
|
261
288
|
path_type=path_type,
|
|
262
289
|
field_name=field_name,
|
infrahub/core/node/__init__.py
CHANGED
|
@@ -22,7 +22,14 @@ from infrahub.core.constants import (
|
|
|
22
22
|
from infrahub.core.constants.schema import SchemaElementPathType
|
|
23
23
|
from infrahub.core.protocols import CoreNumberPool, CoreObjectTemplate
|
|
24
24
|
from infrahub.core.query.node import NodeCheckIDQuery, NodeCreateAllQuery, NodeDeleteQuery, NodeGetListQuery
|
|
25
|
-
from infrahub.core.schema import
|
|
25
|
+
from infrahub.core.schema import (
|
|
26
|
+
AttributeSchema,
|
|
27
|
+
NodeSchema,
|
|
28
|
+
NonGenericSchemaTypes,
|
|
29
|
+
ProfileSchema,
|
|
30
|
+
RelationshipSchema,
|
|
31
|
+
TemplateSchema,
|
|
32
|
+
)
|
|
26
33
|
from infrahub.core.timestamp import Timestamp
|
|
27
34
|
from infrahub.exceptions import InitializationError, NodeNotFoundError, PoolExhaustedError, ValidationError
|
|
28
35
|
from infrahub.types import ATTRIBUTE_TYPES
|
|
@@ -66,7 +73,7 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
|
|
|
66
73
|
_meta.default_filter = default_filter
|
|
67
74
|
super().__init_subclass_with_meta__(_meta=_meta, **options)
|
|
68
75
|
|
|
69
|
-
def get_schema(self) ->
|
|
76
|
+
def get_schema(self) -> NonGenericSchemaTypes:
|
|
70
77
|
return self._schema
|
|
71
78
|
|
|
72
79
|
def get_kind(self) -> str:
|
|
@@ -872,9 +879,11 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
|
|
|
872
879
|
if relationship.kind == RelationshipKind.PARENT:
|
|
873
880
|
return relationship.name
|
|
874
881
|
|
|
875
|
-
async def get_object_template(self, db: InfrahubDatabase) ->
|
|
882
|
+
async def get_object_template(self, db: InfrahubDatabase) -> CoreObjectTemplate | None:
|
|
876
883
|
object_template: RelationshipManager = getattr(self, OBJECT_TEMPLATE_RELATIONSHIP_NAME, None)
|
|
877
|
-
return
|
|
884
|
+
return (
|
|
885
|
+
await object_template.get_peer(db=db, peer_type=CoreObjectTemplate) if object_template is not None else None
|
|
886
|
+
)
|
|
878
887
|
|
|
879
888
|
def get_relationships(
|
|
880
889
|
self, kind: RelationshipKind, exclude: Sequence[str] | None = None
|
|
@@ -888,3 +897,8 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
|
|
|
888
897
|
for relationship in self.get_schema().relationships
|
|
889
898
|
if relationship.name not in exclude and relationship.kind == kind
|
|
890
899
|
]
|
|
900
|
+
|
|
901
|
+
def validate_relationships(self) -> None:
|
|
902
|
+
for name in self._relationships:
|
|
903
|
+
relm: RelationshipManager = getattr(self, name)
|
|
904
|
+
relm.validate()
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING, Any, Mapping
|
|
4
|
+
|
|
5
|
+
from infrahub.core import registry
|
|
6
|
+
from infrahub.core.constants import RelationshipCardinality, RelationshipKind
|
|
7
|
+
from infrahub.core.constraint.node.runner import NodeConstraintRunner
|
|
8
|
+
from infrahub.core.manager import NodeManager
|
|
9
|
+
from infrahub.core.node import Node
|
|
10
|
+
from infrahub.core.protocols import CoreObjectTemplate
|
|
11
|
+
from infrahub.dependencies.registry import get_component_registry
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from infrahub.core.branch import Branch
|
|
15
|
+
from infrahub.core.relationship.model import RelationshipManager
|
|
16
|
+
from infrahub.core.schema import MainSchemaTypes, NonGenericSchemaTypes, RelationshipSchema
|
|
17
|
+
from infrahub.database import InfrahubDatabase
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
async def get_template_relationship_peers(
|
|
21
|
+
db: InfrahubDatabase, template: CoreObjectTemplate, relationship: RelationshipSchema
|
|
22
|
+
) -> Mapping[str, CoreObjectTemplate]:
|
|
23
|
+
"""For a given relationship on the template, fetch the related peers."""
|
|
24
|
+
template_relationship_manager: RelationshipManager = getattr(template, relationship.name)
|
|
25
|
+
if relationship.cardinality == RelationshipCardinality.MANY:
|
|
26
|
+
return await template_relationship_manager.get_peers(db=db, peer_type=CoreObjectTemplate)
|
|
27
|
+
|
|
28
|
+
peers: dict[str, CoreObjectTemplate] = {}
|
|
29
|
+
template_relationship_peer = await template_relationship_manager.get_peer(db=db, peer_type=CoreObjectTemplate)
|
|
30
|
+
if template_relationship_peer:
|
|
31
|
+
peers[template_relationship_peer.id] = template_relationship_peer
|
|
32
|
+
return peers
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
async def extract_peer_data(
|
|
36
|
+
db: InfrahubDatabase,
|
|
37
|
+
template_peer: CoreObjectTemplate,
|
|
38
|
+
obj_peer_schema: MainSchemaTypes,
|
|
39
|
+
parent_obj: Node,
|
|
40
|
+
current_template: CoreObjectTemplate,
|
|
41
|
+
) -> Mapping[str, Any]:
|
|
42
|
+
obj_peer_data: dict[str, Any] = {}
|
|
43
|
+
|
|
44
|
+
for attr in template_peer.get_schema().attribute_names:
|
|
45
|
+
if attr not in obj_peer_schema.attribute_names:
|
|
46
|
+
continue
|
|
47
|
+
obj_peer_data[attr] = {"value": getattr(template_peer, attr).value, "source": template_peer.id}
|
|
48
|
+
|
|
49
|
+
for rel in template_peer.get_schema().relationship_names:
|
|
50
|
+
rel_manager: RelationshipManager = getattr(template_peer, rel)
|
|
51
|
+
if (
|
|
52
|
+
rel_manager.schema.kind not in [RelationshipKind.COMPONENT, RelationshipKind.PARENT]
|
|
53
|
+
or rel_manager.schema.name not in obj_peer_schema.relationship_names
|
|
54
|
+
):
|
|
55
|
+
continue
|
|
56
|
+
|
|
57
|
+
if list(await rel_manager.get_peers(db=db)) == [current_template.id]:
|
|
58
|
+
obj_peer_data[rel] = {"id": parent_obj.id}
|
|
59
|
+
|
|
60
|
+
return obj_peer_data
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
async def handle_template_relationships(
|
|
64
|
+
db: InfrahubDatabase,
|
|
65
|
+
branch: Branch,
|
|
66
|
+
obj: Node,
|
|
67
|
+
template: CoreObjectTemplate,
|
|
68
|
+
fields: list,
|
|
69
|
+
constraint_runner: NodeConstraintRunner | None = None,
|
|
70
|
+
) -> None:
|
|
71
|
+
if constraint_runner is None:
|
|
72
|
+
component_registry = get_component_registry()
|
|
73
|
+
constraint_runner = await component_registry.get_component(NodeConstraintRunner, db=db, branch=branch)
|
|
74
|
+
|
|
75
|
+
for relationship in obj.get_relationships(kind=RelationshipKind.COMPONENT, exclude=fields):
|
|
76
|
+
template_relationship_peers = await get_template_relationship_peers(
|
|
77
|
+
db=db, template=template, relationship=relationship
|
|
78
|
+
)
|
|
79
|
+
if not template_relationship_peers:
|
|
80
|
+
continue
|
|
81
|
+
|
|
82
|
+
for template_relationship_peer in template_relationship_peers.values():
|
|
83
|
+
# We retrieve peer schema for each peer in case we are processing a relationship which is based on a generic
|
|
84
|
+
obj_peer_schema = registry.schema.get_node_schema(
|
|
85
|
+
name=template_relationship_peer.get_schema().kind.removeprefix("Template"),
|
|
86
|
+
branch=branch,
|
|
87
|
+
duplicate=False,
|
|
88
|
+
)
|
|
89
|
+
obj_peer_data = await extract_peer_data(
|
|
90
|
+
db=db,
|
|
91
|
+
template_peer=template_relationship_peer,
|
|
92
|
+
obj_peer_schema=obj_peer_schema,
|
|
93
|
+
parent_obj=obj,
|
|
94
|
+
current_template=template,
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
obj_peer = await Node.init(schema=obj_peer_schema, db=db, branch=branch)
|
|
98
|
+
await obj_peer.new(db=db, **obj_peer_data)
|
|
99
|
+
await constraint_runner.check(node=obj_peer, field_filters=list(obj_peer_data))
|
|
100
|
+
await obj_peer.save(db=db)
|
|
101
|
+
|
|
102
|
+
await handle_template_relationships(
|
|
103
|
+
db=db,
|
|
104
|
+
branch=branch,
|
|
105
|
+
constraint_runner=constraint_runner,
|
|
106
|
+
obj=obj_peer,
|
|
107
|
+
template=template_relationship_peer,
|
|
108
|
+
fields=fields,
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
async def get_profile_ids(db: InfrahubDatabase, obj: Node) -> set[str]:
|
|
113
|
+
if not hasattr(obj, "profiles"):
|
|
114
|
+
return set()
|
|
115
|
+
profile_rels = await obj.profiles.get_relationships(db=db)
|
|
116
|
+
return {pr.peer_id for pr in profile_rels}
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
async def refresh_for_profile_update(
|
|
120
|
+
db: InfrahubDatabase,
|
|
121
|
+
branch: Branch,
|
|
122
|
+
obj: Node,
|
|
123
|
+
schema: NonGenericSchemaTypes,
|
|
124
|
+
previous_profile_ids: set[str] | None = None,
|
|
125
|
+
) -> Node:
|
|
126
|
+
if not hasattr(obj, "profiles"):
|
|
127
|
+
return obj
|
|
128
|
+
current_profile_ids = await get_profile_ids(db=db, obj=obj)
|
|
129
|
+
if previous_profile_ids is None or previous_profile_ids != current_profile_ids:
|
|
130
|
+
refreshed_node = await NodeManager.get_one_by_id_or_default_filter(
|
|
131
|
+
db=db,
|
|
132
|
+
kind=schema.kind,
|
|
133
|
+
id=obj.get_id(),
|
|
134
|
+
branch=branch,
|
|
135
|
+
include_owner=True,
|
|
136
|
+
include_source=True,
|
|
137
|
+
)
|
|
138
|
+
refreshed_node._node_changelog = obj.node_changelog
|
|
139
|
+
return refreshed_node
|
|
140
|
+
return obj
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
async def _do_create_node(
|
|
144
|
+
node_class: type[Node],
|
|
145
|
+
db: InfrahubDatabase,
|
|
146
|
+
data: dict,
|
|
147
|
+
schema: NonGenericSchemaTypes,
|
|
148
|
+
fields_to_validate: list,
|
|
149
|
+
branch: Branch,
|
|
150
|
+
node_constraint_runner: NodeConstraintRunner,
|
|
151
|
+
) -> Node:
|
|
152
|
+
obj = await node_class.init(db=db, schema=schema, branch=branch)
|
|
153
|
+
await obj.new(db=db, **data)
|
|
154
|
+
await node_constraint_runner.check(node=obj, field_filters=fields_to_validate)
|
|
155
|
+
await obj.save(db=db)
|
|
156
|
+
|
|
157
|
+
object_template = await obj.get_object_template(db=db)
|
|
158
|
+
if object_template:
|
|
159
|
+
await handle_template_relationships(
|
|
160
|
+
db=db,
|
|
161
|
+
branch=branch,
|
|
162
|
+
template=object_template,
|
|
163
|
+
obj=obj,
|
|
164
|
+
fields=fields_to_validate,
|
|
165
|
+
)
|
|
166
|
+
return obj
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
async def create_node(
|
|
170
|
+
data: dict,
|
|
171
|
+
db: InfrahubDatabase,
|
|
172
|
+
branch: Branch,
|
|
173
|
+
schema: NonGenericSchemaTypes,
|
|
174
|
+
) -> Node:
|
|
175
|
+
"""Create a node in the database if constraint checks succeed."""
|
|
176
|
+
|
|
177
|
+
component_registry = get_component_registry()
|
|
178
|
+
node_constraint_runner = await component_registry.get_component(
|
|
179
|
+
NodeConstraintRunner, db=db.start_session() if not db.is_transaction else db, branch=branch
|
|
180
|
+
)
|
|
181
|
+
node_class = Node
|
|
182
|
+
if schema.kind in registry.node:
|
|
183
|
+
node_class = registry.node[schema.kind]
|
|
184
|
+
|
|
185
|
+
fields_to_validate = list(data)
|
|
186
|
+
if db.is_transaction:
|
|
187
|
+
obj = await _do_create_node(
|
|
188
|
+
node_class=node_class,
|
|
189
|
+
node_constraint_runner=node_constraint_runner,
|
|
190
|
+
db=db,
|
|
191
|
+
schema=schema,
|
|
192
|
+
branch=branch,
|
|
193
|
+
fields_to_validate=fields_to_validate,
|
|
194
|
+
data=data,
|
|
195
|
+
)
|
|
196
|
+
else:
|
|
197
|
+
async with db.start_transaction() as dbt:
|
|
198
|
+
obj = await _do_create_node(
|
|
199
|
+
node_class=node_class,
|
|
200
|
+
node_constraint_runner=node_constraint_runner,
|
|
201
|
+
db=dbt,
|
|
202
|
+
schema=schema,
|
|
203
|
+
branch=branch,
|
|
204
|
+
fields_to_validate=fields_to_validate,
|
|
205
|
+
data=data,
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
if await get_profile_ids(db=db, obj=obj):
|
|
209
|
+
obj = await refresh_for_profile_update(db=db, branch=branch, schema=schema, obj=obj)
|
|
210
|
+
|
|
211
|
+
return obj
|
infrahub/core/protocols.py
CHANGED
|
@@ -62,6 +62,12 @@ class BuiltinIPPrefix(CoreNode):
|
|
|
62
62
|
children: RelationshipManager
|
|
63
63
|
|
|
64
64
|
|
|
65
|
+
class CoreAction(CoreNode):
|
|
66
|
+
name: String
|
|
67
|
+
description: StringOptional
|
|
68
|
+
triggers: RelationshipManager
|
|
69
|
+
|
|
70
|
+
|
|
65
71
|
class CoreArtifactTarget(CoreNode):
|
|
66
72
|
artifacts: RelationshipManager
|
|
67
73
|
|
|
@@ -148,6 +154,10 @@ class CoreMenu(CoreNode):
|
|
|
148
154
|
children: RelationshipManager
|
|
149
155
|
|
|
150
156
|
|
|
157
|
+
class CoreNodeTriggerMatch(CoreNode):
|
|
158
|
+
trigger: RelationshipManager
|
|
159
|
+
|
|
160
|
+
|
|
151
161
|
class CoreObjectComponentTemplate(CoreNode):
|
|
152
162
|
template_name: String
|
|
153
163
|
|
|
@@ -189,6 +199,14 @@ class CoreTransformation(CoreNode):
|
|
|
189
199
|
tags: RelationshipManager
|
|
190
200
|
|
|
191
201
|
|
|
202
|
+
class CoreTriggerRule(CoreNode):
|
|
203
|
+
name: String
|
|
204
|
+
description: StringOptional
|
|
205
|
+
active: Boolean
|
|
206
|
+
branch_scope: Dropdown
|
|
207
|
+
action: RelationshipManager
|
|
208
|
+
|
|
209
|
+
|
|
192
210
|
class CoreValidator(CoreNode):
|
|
193
211
|
label: StringOptional
|
|
194
212
|
state: Enum
|
|
@@ -322,6 +340,10 @@ class CoreFileThread(CoreThread):
|
|
|
322
340
|
repository: RelationshipManager
|
|
323
341
|
|
|
324
342
|
|
|
343
|
+
class CoreGeneratorAction(CoreAction):
|
|
344
|
+
generator: RelationshipManager
|
|
345
|
+
|
|
346
|
+
|
|
325
347
|
class CoreGeneratorCheck(CoreCheck):
|
|
326
348
|
instance: String
|
|
327
349
|
|
|
@@ -376,6 +398,16 @@ class CoreGraphQLQueryGroup(CoreGroup):
|
|
|
376
398
|
query: RelationshipManager
|
|
377
399
|
|
|
378
400
|
|
|
401
|
+
class CoreGroupAction(CoreAction):
|
|
402
|
+
add_members: Boolean
|
|
403
|
+
group: RelationshipManager
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
class CoreGroupTriggerRule(CoreTriggerRule):
|
|
407
|
+
members_added: Boolean
|
|
408
|
+
group: RelationshipManager
|
|
409
|
+
|
|
410
|
+
|
|
379
411
|
class CoreIPAddressPool(CoreResourcePool, LineageSource):
|
|
380
412
|
default_address_type: String
|
|
381
413
|
default_prefix_length: IntegerOptional
|
|
@@ -395,6 +427,25 @@ class CoreMenuItem(CoreMenu):
|
|
|
395
427
|
pass
|
|
396
428
|
|
|
397
429
|
|
|
430
|
+
class CoreNodeTriggerAttributeMatch(CoreNodeTriggerMatch):
|
|
431
|
+
attribute_name: String
|
|
432
|
+
value: StringOptional
|
|
433
|
+
value_previous: StringOptional
|
|
434
|
+
value_match: Dropdown
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
class CoreNodeTriggerRelationshipMatch(CoreNodeTriggerMatch):
|
|
438
|
+
relationship_name: String
|
|
439
|
+
added: Boolean
|
|
440
|
+
peer: StringOptional
|
|
441
|
+
|
|
442
|
+
|
|
443
|
+
class CoreNodeTriggerRule(CoreTriggerRule):
|
|
444
|
+
node_kind: String
|
|
445
|
+
mutation_action: Enum
|
|
446
|
+
matches: RelationshipManager
|
|
447
|
+
|
|
448
|
+
|
|
398
449
|
class CoreNumberPool(CoreResourcePool, LineageSource):
|
|
399
450
|
node: String
|
|
400
451
|
node_attribute: String
|
infrahub/core/protocols_base.py
CHANGED
|
@@ -7,6 +7,7 @@ from typing_extensions import Self
|
|
|
7
7
|
if TYPE_CHECKING:
|
|
8
8
|
from neo4j import AsyncResult, AsyncSession, AsyncTransaction, Record
|
|
9
9
|
|
|
10
|
+
from infrahub.core.schema import NonGenericSchemaTypes
|
|
10
11
|
from infrahub.core.schema.schema_branch import SchemaBranch
|
|
11
12
|
|
|
12
13
|
|
|
@@ -70,6 +71,8 @@ class CoreNode(Protocol):
|
|
|
70
71
|
|
|
71
72
|
def get_id(self) -> str: ...
|
|
72
73
|
def get_kind(self) -> str: ...
|
|
74
|
+
def get_schema(self) -> NonGenericSchemaTypes: ...
|
|
75
|
+
|
|
73
76
|
@classmethod
|
|
74
77
|
async def init(
|
|
75
78
|
cls,
|
infrahub/core/query/__init__.py
CHANGED
|
@@ -424,8 +424,8 @@ class Query(ABC):
|
|
|
424
424
|
else:
|
|
425
425
|
self.query_lines.extend([line.strip() for line in query.split("\n") if line.strip()])
|
|
426
426
|
|
|
427
|
-
def add_subquery(self, subquery: str, with_clause: str | None = None) -> None:
|
|
428
|
-
self.add_to_query("CALL {")
|
|
427
|
+
def add_subquery(self, subquery: str, node_alias: str, with_clause: str | None = None) -> None:
|
|
428
|
+
self.add_to_query(f"CALL ({node_alias}) {{")
|
|
429
429
|
self.add_to_query(subquery)
|
|
430
430
|
self.add_to_query("}")
|
|
431
431
|
if with_clause:
|