infrahub-server 1.2.11__py3-none-any.whl → 1.3.0__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 +130 -0
- infrahub/actions/gather.py +114 -0
- infrahub/actions/models.py +243 -0
- infrahub/actions/parsers.py +104 -0
- infrahub/actions/schema.py +393 -0
- infrahub/actions/tasks.py +119 -0
- infrahub/actions/triggers.py +21 -0
- infrahub/branch/__init__.py +0 -0
- infrahub/branch/tasks.py +29 -0
- infrahub/branch/triggers.py +22 -0
- infrahub/cli/db.py +3 -4
- infrahub/computed_attribute/gather.py +3 -1
- infrahub/computed_attribute/tasks.py +23 -29
- infrahub/core/account.py +24 -47
- infrahub/core/attribute.py +13 -15
- infrahub/core/constants/__init__.py +10 -0
- infrahub/core/constants/database.py +1 -0
- infrahub/core/constants/infrahubkind.py +9 -0
- infrahub/core/constraint/node/runner.py +3 -1
- infrahub/core/convert_object_type/__init__.py +0 -0
- infrahub/core/convert_object_type/conversion.py +124 -0
- infrahub/core/convert_object_type/schema_mapping.py +56 -0
- infrahub/core/diff/coordinator.py +8 -1
- infrahub/core/diff/query/all_conflicts.py +1 -5
- infrahub/core/diff/query/artifact.py +10 -20
- infrahub/core/diff/query/delete_query.py +8 -4
- infrahub/core/diff/query/diff_get.py +3 -6
- infrahub/core/diff/query/field_specifiers.py +1 -1
- infrahub/core/diff/query/field_summary.py +2 -4
- infrahub/core/diff/query/merge.py +72 -125
- infrahub/core/diff/query/save.py +83 -68
- infrahub/core/diff/query/summary_counts_enricher.py +34 -54
- infrahub/core/diff/query/time_range_query.py +0 -1
- infrahub/core/diff/repository/repository.py +4 -0
- infrahub/core/graph/__init__.py +1 -1
- infrahub/core/manager.py +14 -11
- infrahub/core/migrations/graph/__init__.py +6 -0
- infrahub/core/migrations/graph/m003_relationship_parent_optional.py +1 -2
- infrahub/core/migrations/graph/m012_convert_account_generic.py +1 -1
- infrahub/core/migrations/graph/m013_convert_git_password_credential.py +2 -6
- infrahub/core/migrations/graph/m015_diff_format_update.py +1 -2
- infrahub/core/migrations/graph/m016_diff_delete_bug_fix.py +1 -2
- 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/m023_deduplicate_cardinality_one_relationships.py +2 -2
- infrahub/core/migrations/graph/m024_missing_hierarchy_backfill.py +1 -2
- infrahub/core/migrations/graph/m028_delete_diffs.py +1 -2
- infrahub/core/migrations/graph/m029_duplicates_cleanup.py +662 -0
- infrahub/core/migrations/graph/m030_illegal_edges.py +82 -0
- infrahub/core/migrations/query/attribute_add.py +14 -11
- infrahub/core/migrations/query/attribute_rename.py +6 -11
- infrahub/core/migrations/query/delete_element_in_schema.py +19 -17
- infrahub/core/migrations/query/node_duplicate.py +19 -21
- infrahub/core/migrations/query/relationship_duplicate.py +19 -18
- infrahub/core/migrations/schema/node_attribute_remove.py +4 -8
- infrahub/core/migrations/schema/node_remove.py +19 -20
- infrahub/core/models.py +29 -2
- infrahub/core/node/__init__.py +131 -28
- infrahub/core/node/base.py +1 -1
- infrahub/core/node/create.py +211 -0
- infrahub/core/node/resource_manager/number_pool.py +31 -5
- infrahub/core/node/standard.py +6 -1
- infrahub/core/path.py +15 -1
- infrahub/core/protocols.py +57 -0
- infrahub/core/protocols_base.py +3 -0
- infrahub/core/query/__init__.py +2 -2
- infrahub/core/query/delete.py +3 -3
- infrahub/core/query/diff.py +19 -32
- infrahub/core/query/ipam.py +10 -20
- infrahub/core/query/node.py +29 -47
- infrahub/core/query/relationship.py +55 -34
- infrahub/core/query/resource_manager.py +1 -2
- infrahub/core/query/standard_node.py +19 -5
- infrahub/core/query/subquery.py +2 -4
- infrahub/core/relationship/constraints/count.py +10 -9
- infrahub/core/relationship/constraints/interface.py +2 -1
- infrahub/core/relationship/constraints/peer_kind.py +2 -1
- infrahub/core/relationship/constraints/peer_parent.py +56 -0
- infrahub/core/relationship/constraints/peer_relatives.py +72 -0
- infrahub/core/relationship/constraints/profiles_kind.py +1 -1
- infrahub/core/relationship/model.py +4 -1
- infrahub/core/schema/__init__.py +2 -1
- infrahub/core/schema/attribute_parameters.py +160 -0
- infrahub/core/schema/attribute_schema.py +130 -7
- infrahub/core/schema/basenode_schema.py +27 -3
- infrahub/core/schema/definitions/core/__init__.py +29 -1
- infrahub/core/schema/definitions/core/group.py +45 -0
- infrahub/core/schema/definitions/core/resource_pool.py +9 -0
- infrahub/core/schema/definitions/internal.py +43 -5
- infrahub/core/schema/generated/attribute_schema.py +16 -3
- infrahub/core/schema/generated/relationship_schema.py +11 -1
- infrahub/core/schema/manager.py +7 -2
- infrahub/core/schema/schema_branch.py +109 -12
- infrahub/core/validators/__init__.py +15 -2
- infrahub/core/validators/attribute/choices.py +1 -3
- infrahub/core/validators/attribute/enum.py +1 -3
- infrahub/core/validators/attribute/kind.py +1 -3
- infrahub/core/validators/attribute/length.py +13 -7
- infrahub/core/validators/attribute/min_max.py +118 -0
- infrahub/core/validators/attribute/number_pool.py +106 -0
- infrahub/core/validators/attribute/optional.py +1 -4
- infrahub/core/validators/attribute/regex.py +5 -6
- infrahub/core/validators/attribute/unique.py +1 -3
- infrahub/core/validators/determiner.py +18 -2
- infrahub/core/validators/enum.py +12 -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 +177 -12
- infrahub/core/validators/tasks.py +1 -1
- infrahub/core/validators/uniqueness/query.py +5 -9
- infrahub/database/__init__.py +12 -4
- infrahub/database/validation.py +100 -0
- infrahub/dependencies/builder/constraint/grouped/node_runner.py +4 -0
- infrahub/dependencies/builder/constraint/relationship_manager/peer_parent.py +8 -0
- infrahub/dependencies/builder/constraint/relationship_manager/peer_relatives.py +8 -0
- infrahub/dependencies/builder/constraint/schema/aggregated.py +2 -0
- infrahub/dependencies/builder/constraint/schema/relationship_peer.py +8 -0
- infrahub/dependencies/builder/diff/deserializer.py +1 -1
- infrahub/dependencies/registry.py +4 -0
- infrahub/events/group_action.py +1 -0
- infrahub/events/models.py +1 -1
- infrahub/git/base.py +5 -3
- infrahub/git/integrator.py +96 -5
- infrahub/git/tasks.py +1 -0
- infrahub/graphql/analyzer.py +139 -18
- infrahub/graphql/manager.py +4 -0
- infrahub/graphql/mutations/action.py +164 -0
- infrahub/graphql/mutations/convert_object_type.py +71 -0
- infrahub/graphql/mutations/main.py +25 -176
- infrahub/graphql/mutations/proposed_change.py +20 -17
- infrahub/graphql/mutations/relationship.py +32 -0
- infrahub/graphql/mutations/resource_manager.py +63 -7
- infrahub/graphql/queries/convert_object_type_mapping.py +34 -0
- infrahub/graphql/queries/resource_manager.py +7 -1
- infrahub/graphql/resolvers/many_relationship.py +1 -1
- infrahub/graphql/resolvers/resolver.py +2 -2
- infrahub/graphql/resolvers/single_relationship.py +1 -1
- infrahub/graphql/schema.py +6 -0
- infrahub/menu/menu.py +34 -2
- infrahub/message_bus/messages/__init__.py +0 -10
- infrahub/message_bus/operations/__init__.py +0 -8
- infrahub/message_bus/operations/refresh/registry.py +4 -7
- infrahub/patch/queries/delete_duplicated_edges.py +45 -39
- infrahub/pools/models.py +14 -0
- infrahub/pools/number.py +5 -3
- infrahub/pools/registration.py +22 -0
- infrahub/pools/tasks.py +126 -0
- infrahub/prefect_server/models.py +1 -19
- infrahub/proposed_change/models.py +68 -3
- infrahub/proposed_change/tasks.py +911 -34
- infrahub/schema/__init__.py +0 -0
- infrahub/schema/tasks.py +27 -0
- infrahub/schema/triggers.py +23 -0
- infrahub/task_manager/models.py +10 -6
- infrahub/trigger/catalogue.py +6 -0
- infrahub/trigger/models.py +23 -6
- infrahub/trigger/setup.py +26 -2
- infrahub/trigger/tasks.py +4 -2
- infrahub/types.py +6 -0
- infrahub/webhook/tasks.py +6 -9
- infrahub/workflows/catalogue.py +103 -1
- infrahub_sdk/client.py +43 -10
- infrahub_sdk/ctl/generator.py +4 -4
- infrahub_sdk/ctl/repository.py +1 -1
- infrahub_sdk/node/__init__.py +39 -0
- infrahub_sdk/node/attribute.py +122 -0
- infrahub_sdk/node/constants.py +21 -0
- infrahub_sdk/{node.py → node/node.py} +158 -803
- infrahub_sdk/node/parsers.py +15 -0
- infrahub_sdk/node/property.py +24 -0
- infrahub_sdk/node/related_node.py +266 -0
- infrahub_sdk/node/relationship.py +302 -0
- infrahub_sdk/protocols.py +112 -0
- infrahub_sdk/protocols_base.py +34 -2
- infrahub_sdk/pytest_plugin/items/python_transform.py +2 -1
- infrahub_sdk/query_groups.py +17 -5
- infrahub_sdk/schema/main.py +1 -0
- infrahub_sdk/schema/repository.py +16 -0
- infrahub_sdk/spec/object.py +1 -1
- infrahub_sdk/store.py +1 -1
- infrahub_sdk/testing/schemas/car_person.py +1 -0
- infrahub_sdk/utils.py +7 -20
- infrahub_sdk/yaml.py +6 -5
- {infrahub_server-1.2.11.dist-info → infrahub_server-1.3.0.dist-info}/METADATA +5 -5
- {infrahub_server-1.2.11.dist-info → infrahub_server-1.3.0.dist-info}/RECORD +197 -168
- {infrahub_server-1.2.11.dist-info → infrahub_server-1.3.0.dist-info}/WHEEL +1 -1
- infrahub_testcontainers/container.py +239 -65
- infrahub_testcontainers/docker-compose-cluster.test.yml +321 -0
- infrahub_testcontainers/docker-compose.test.yml +2 -1
- infrahub_testcontainers/helpers.py +23 -3
- infrahub_testcontainers/plugin.py +9 -0
- 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/patch/queries/consolidate_duplicated_nodes.py +0 -109
- /infrahub/{message_bus/messages/proposed_change → actions}/__init__.py +0 -0
- {infrahub_server-1.2.11.dist-info → infrahub_server-1.3.0.dist-info}/LICENSE.txt +0 -0
- {infrahub_server-1.2.11.dist-info → infrahub_server-1.3.0.dist-info}/entry_points.txt +0 -0
|
@@ -21,7 +21,6 @@ class NodeRemoveMigrationBaseQuery(MigrationQuery):
|
|
|
21
21
|
rel_def: FieldInfo,
|
|
22
22
|
) -> str:
|
|
23
23
|
subquery = [
|
|
24
|
-
f"WITH peer_node, {rel_name}, active_node",
|
|
25
24
|
f"WITH peer_node, {rel_name}, active_node",
|
|
26
25
|
f'WHERE type({rel_name}) = "{rel_type}"',
|
|
27
26
|
]
|
|
@@ -59,12 +58,10 @@ class NodeRemoveMigrationBaseQuery(MigrationQuery):
|
|
|
59
58
|
|
|
60
59
|
node_remove_query = self.render_node_remove_query(branch_filter=branch_filter)
|
|
61
60
|
|
|
62
|
-
# ruff: noqa: E501
|
|
63
61
|
query = """
|
|
64
62
|
// Find all the active nodes
|
|
65
63
|
MATCH (node:%(node_kind)s)
|
|
66
|
-
CALL {
|
|
67
|
-
WITH node
|
|
64
|
+
CALL (node) {
|
|
68
65
|
MATCH (root:Root)<-[r:IS_PART_OF]-(node)
|
|
69
66
|
WHERE %(branch_filter)s
|
|
70
67
|
RETURN node as n1, r as r1
|
|
@@ -91,13 +88,12 @@ class NodeRemoveMigrationQueryIn(NodeRemoveMigrationBaseQuery):
|
|
|
91
88
|
insert_return: bool = False
|
|
92
89
|
|
|
93
90
|
def render_node_remove_query(self, branch_filter: str) -> str:
|
|
94
|
-
sub_query = self.render_sub_query_in()
|
|
91
|
+
sub_query, sub_query_args = self.render_sub_query_in()
|
|
95
92
|
query = """
|
|
96
93
|
// Process Inbound Relationship
|
|
97
94
|
WITH active_node
|
|
98
95
|
MATCH (active_node)<-[]-(peer)
|
|
99
|
-
CALL {
|
|
100
|
-
WITH active_node, peer
|
|
96
|
+
CALL (active_node, peer) {
|
|
101
97
|
MATCH (active_node)-[r]->(peer)
|
|
102
98
|
WHERE %(branch_filter)s
|
|
103
99
|
RETURN active_node as n1, r as rel_inband1, peer as p1
|
|
@@ -106,27 +102,29 @@ class NodeRemoveMigrationQueryIn(NodeRemoveMigrationBaseQuery):
|
|
|
106
102
|
}
|
|
107
103
|
WITH n1 as active_node, rel_inband1 as rel_inband, p1 as peer_node
|
|
108
104
|
WHERE rel_inband.status = "active"
|
|
109
|
-
CALL {
|
|
105
|
+
CALL (%(sub_query_args)s) {
|
|
110
106
|
%(sub_query)s
|
|
111
107
|
}
|
|
112
108
|
WITH p2 as peer_node, rel_inband, active_node
|
|
113
109
|
FOREACH (i in CASE WHEN rel_inband.branch IN ["-global-", $branch] THEN [1] ELSE [] END |
|
|
114
110
|
SET rel_inband.to = $current_time
|
|
115
111
|
)
|
|
116
|
-
""" % {"sub_query": sub_query, "branch_filter": branch_filter}
|
|
112
|
+
""" % {"sub_query": sub_query, "sub_query_args": sub_query_args, "branch_filter": branch_filter}
|
|
117
113
|
return query
|
|
118
114
|
|
|
119
|
-
def render_sub_query_in(self) -> str:
|
|
115
|
+
def render_sub_query_in(self) -> tuple[str, str]:
|
|
116
|
+
rel_name = "rel_inband"
|
|
117
|
+
sub_query_in_args = f"peer_node, {rel_name}, active_node"
|
|
120
118
|
sub_queries_in = [
|
|
121
119
|
self.render_sub_query_per_rel_type(
|
|
122
|
-
rel_name=
|
|
120
|
+
rel_name=rel_name,
|
|
123
121
|
rel_type=rel_type,
|
|
124
122
|
rel_def=rel_def,
|
|
125
123
|
)
|
|
126
124
|
for rel_type, rel_def in GraphNodeRelationships.model_fields.items()
|
|
127
125
|
]
|
|
128
126
|
sub_query_in = "\nUNION\n".join(sub_queries_in)
|
|
129
|
-
return sub_query_in
|
|
127
|
+
return sub_query_in, sub_query_in_args
|
|
130
128
|
|
|
131
129
|
def get_nbr_migrations_executed(self) -> int:
|
|
132
130
|
return 0
|
|
@@ -137,13 +135,12 @@ class NodeRemoveMigrationQueryOut(NodeRemoveMigrationBaseQuery):
|
|
|
137
135
|
insert_return: bool = False
|
|
138
136
|
|
|
139
137
|
def render_node_remove_query(self, branch_filter: str) -> str:
|
|
140
|
-
sub_query = self.render_sub_query_out()
|
|
138
|
+
sub_query, sub_query_args = self.render_sub_query_out()
|
|
141
139
|
query = """
|
|
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
|
|
@@ -152,27 +149,29 @@ class NodeRemoveMigrationQueryOut(NodeRemoveMigrationBaseQuery):
|
|
|
152
149
|
}
|
|
153
150
|
WITH n1 as active_node, rel_outband1 as rel_outband, p1 as peer_node
|
|
154
151
|
WHERE rel_outband.status = "active"
|
|
155
|
-
CALL {
|
|
152
|
+
CALL (%(sub_query_args)s) {
|
|
156
153
|
%(sub_query)s
|
|
157
154
|
}
|
|
158
155
|
FOREACH (i in CASE WHEN rel_outband.branch IN ["-global-", $branch] THEN [1] ELSE [] END |
|
|
159
156
|
SET rel_outband.to = $current_time
|
|
160
157
|
)
|
|
161
|
-
""" % {"sub_query": sub_query, "branch_filter": branch_filter}
|
|
158
|
+
""" % {"sub_query": sub_query, "sub_query_args": sub_query_args, "branch_filter": branch_filter}
|
|
162
159
|
|
|
163
160
|
return query
|
|
164
161
|
|
|
165
|
-
def render_sub_query_out(self) -> str:
|
|
162
|
+
def render_sub_query_out(self) -> tuple[str, str]:
|
|
163
|
+
rel_name = "rel_outband"
|
|
164
|
+
sub_query_out_args = f"peer_node, {rel_name}, active_node"
|
|
166
165
|
sub_queries_out = [
|
|
167
166
|
self.render_sub_query_per_rel_type(
|
|
168
|
-
rel_name=
|
|
167
|
+
rel_name=rel_name,
|
|
169
168
|
rel_type=rel_type,
|
|
170
169
|
rel_def=rel_def,
|
|
171
170
|
)
|
|
172
171
|
for rel_type, rel_def in GraphNodeRelationships.model_fields.items()
|
|
173
172
|
]
|
|
174
173
|
sub_query_out = "\nUNION\n".join(sub_queries_out)
|
|
175
|
-
return sub_query_out
|
|
174
|
+
return sub_query_out, sub_query_out_args
|
|
176
175
|
|
|
177
176
|
def get_nbr_migrations_executed(self) -> int:
|
|
178
177
|
return self.num_of_results
|
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
|
@@ -7,6 +7,7 @@ from infrahub_sdk.template import Jinja2Template
|
|
|
7
7
|
from infrahub_sdk.utils import is_valid_uuid
|
|
8
8
|
from infrahub_sdk.uuidt import UUIDT
|
|
9
9
|
|
|
10
|
+
from infrahub import lock
|
|
10
11
|
from infrahub.core import registry
|
|
11
12
|
from infrahub.core.changelog.models import NodeChangelog
|
|
12
13
|
from infrahub.core.constants import (
|
|
@@ -16,15 +17,25 @@ from infrahub.core.constants import (
|
|
|
16
17
|
BranchSupportType,
|
|
17
18
|
ComputedAttributeKind,
|
|
18
19
|
InfrahubKind,
|
|
20
|
+
NumberPoolType,
|
|
19
21
|
RelationshipCardinality,
|
|
20
22
|
RelationshipKind,
|
|
21
23
|
)
|
|
22
24
|
from infrahub.core.constants.schema import SchemaElementPathType
|
|
23
25
|
from infrahub.core.protocols import CoreNumberPool, CoreObjectTemplate
|
|
24
26
|
from infrahub.core.query.node import NodeCheckIDQuery, NodeCreateAllQuery, NodeDeleteQuery, NodeGetListQuery
|
|
25
|
-
from infrahub.core.schema import
|
|
27
|
+
from infrahub.core.schema import (
|
|
28
|
+
AttributeSchema,
|
|
29
|
+
NodeSchema,
|
|
30
|
+
NonGenericSchemaTypes,
|
|
31
|
+
ProfileSchema,
|
|
32
|
+
RelationshipSchema,
|
|
33
|
+
TemplateSchema,
|
|
34
|
+
)
|
|
35
|
+
from infrahub.core.schema.attribute_parameters import NumberPoolParameters
|
|
26
36
|
from infrahub.core.timestamp import Timestamp
|
|
27
37
|
from infrahub.exceptions import InitializationError, NodeNotFoundError, PoolExhaustedError, ValidationError
|
|
38
|
+
from infrahub.pools.models import NumberPoolLockDefinition
|
|
28
39
|
from infrahub.types import ATTRIBUTE_TYPES
|
|
29
40
|
|
|
30
41
|
from ...graphql.constants import KIND_GRAPHQL_FIELD_NAME
|
|
@@ -66,7 +77,7 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
|
|
|
66
77
|
_meta.default_filter = default_filter
|
|
67
78
|
super().__init_subclass_with_meta__(_meta=_meta, **options)
|
|
68
79
|
|
|
69
|
-
def get_schema(self) ->
|
|
80
|
+
def get_schema(self) -> NonGenericSchemaTypes:
|
|
70
81
|
return self._schema
|
|
71
82
|
|
|
72
83
|
def get_kind(self) -> str:
|
|
@@ -247,6 +258,12 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
|
|
|
247
258
|
within the create code.
|
|
248
259
|
"""
|
|
249
260
|
|
|
261
|
+
number_pool_parameters: NumberPoolParameters | None = None
|
|
262
|
+
if attribute.schema.kind == "NumberPool" and isinstance(attribute.schema.parameters, NumberPoolParameters):
|
|
263
|
+
attribute.from_pool = {"id": attribute.schema.parameters.number_pool_id}
|
|
264
|
+
attribute.is_default = False
|
|
265
|
+
number_pool_parameters = attribute.schema.parameters
|
|
266
|
+
|
|
250
267
|
if not attribute.from_pool:
|
|
251
268
|
return
|
|
252
269
|
|
|
@@ -255,19 +272,25 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
|
|
|
255
272
|
db=db, id=attribute.from_pool["id"], kind=CoreNumberPool
|
|
256
273
|
)
|
|
257
274
|
except NodeNotFoundError:
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
275
|
+
if number_pool_parameters:
|
|
276
|
+
number_pool = await self._fetch_or_create_number_pool(
|
|
277
|
+
db=db, attribute=attribute, number_pool_parameters=number_pool_parameters
|
|
261
278
|
)
|
|
262
|
-
|
|
263
|
-
|
|
279
|
+
|
|
280
|
+
else:
|
|
281
|
+
errors.append(
|
|
282
|
+
ValidationError(
|
|
283
|
+
{f"{attribute.name}.from_pool": f"The pool requested {attribute.from_pool} was not found."}
|
|
284
|
+
)
|
|
285
|
+
)
|
|
286
|
+
return
|
|
264
287
|
|
|
265
288
|
if (
|
|
266
289
|
number_pool.node.value in [self._schema.kind] + self._schema.inherit_from
|
|
267
290
|
and number_pool.node_attribute.value == attribute.name
|
|
268
291
|
):
|
|
269
292
|
try:
|
|
270
|
-
next_free = await number_pool.get_resource(db=db, branch=self._branch, node=self)
|
|
293
|
+
next_free = await number_pool.get_resource(db=db, branch=self._branch, node=self, attribute=attribute)
|
|
271
294
|
except PoolExhaustedError:
|
|
272
295
|
errors.append(
|
|
273
296
|
ValidationError({f"{attribute.name}.from_pool": f"The pool {number_pool.node.value} is exhausted."})
|
|
@@ -285,6 +308,50 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
|
|
|
285
308
|
)
|
|
286
309
|
)
|
|
287
310
|
|
|
311
|
+
async def _fetch_or_create_number_pool(
|
|
312
|
+
self, db: InfrahubDatabase, attribute: BaseAttribute, number_pool_parameters: NumberPoolParameters
|
|
313
|
+
) -> CoreNumberPool:
|
|
314
|
+
number_pool_from_db: CoreNumberPool | None = None
|
|
315
|
+
lock_definition = NumberPoolLockDefinition(pool_id=str(number_pool_parameters.number_pool_id))
|
|
316
|
+
async with lock.registry.get(
|
|
317
|
+
name=lock_definition.lock_name, namespace=lock_definition.namespace_name, local=False
|
|
318
|
+
):
|
|
319
|
+
try:
|
|
320
|
+
number_pool_from_db = await registry.manager.get_one_by_id_or_default_filter(
|
|
321
|
+
db=db, id=str(number_pool_parameters.number_pool_id), kind=CoreNumberPool
|
|
322
|
+
)
|
|
323
|
+
except NodeNotFoundError:
|
|
324
|
+
schema = db.schema.get_node_schema(name="CoreNumberPool", duplicate=False)
|
|
325
|
+
|
|
326
|
+
pool_node = self._schema.kind
|
|
327
|
+
schema_attribute = self._schema.get_attribute(attribute.schema.name)
|
|
328
|
+
if schema_attribute.inherited:
|
|
329
|
+
for generic_name in self._schema.inherit_from:
|
|
330
|
+
generic_node = db.schema.get_generic_schema(name=generic_name, duplicate=False)
|
|
331
|
+
if attribute.schema.name in generic_node.attribute_names:
|
|
332
|
+
pool_node = generic_node.kind
|
|
333
|
+
break
|
|
334
|
+
|
|
335
|
+
number_pool = await Node.init(db=db, schema=schema, branch=self._branch)
|
|
336
|
+
await number_pool.new(
|
|
337
|
+
db=db,
|
|
338
|
+
id=number_pool_parameters.number_pool_id,
|
|
339
|
+
name=f"{pool_node}.{attribute.schema.name} [{number_pool_parameters.number_pool_id}]",
|
|
340
|
+
node=pool_node,
|
|
341
|
+
node_attribute=attribute.schema.name,
|
|
342
|
+
start_range=number_pool_parameters.start_range,
|
|
343
|
+
end_range=number_pool_parameters.end_range,
|
|
344
|
+
pool_type=NumberPoolType.SCHEMA.value,
|
|
345
|
+
)
|
|
346
|
+
await number_pool.save(db=db)
|
|
347
|
+
|
|
348
|
+
# Do a lookup of the number pool to get the correct mapped type from the registry
|
|
349
|
+
# without this we don't get access to the .get_resource() method.
|
|
350
|
+
created_pool: CoreNumberPool = number_pool_from_db or await registry.manager.get_one_by_id_or_default_filter(
|
|
351
|
+
db=db, id=number_pool.id, kind=CoreNumberPool
|
|
352
|
+
)
|
|
353
|
+
return created_pool
|
|
354
|
+
|
|
288
355
|
async def handle_object_template(self, fields: dict, db: InfrahubDatabase, errors: list) -> None:
|
|
289
356
|
"""Fill the `fields` parameters with values from an object template if one is in use."""
|
|
290
357
|
object_template_field = fields.get(OBJECT_TEMPLATE_RELATIONSHIP_NAME)
|
|
@@ -369,6 +436,9 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
|
|
|
369
436
|
self._computed_jinja2_attributes.append(mandatory_attr)
|
|
370
437
|
continue
|
|
371
438
|
|
|
439
|
+
if mandatory_attribute.kind == "NumberPool":
|
|
440
|
+
continue
|
|
441
|
+
|
|
372
442
|
errors.append(
|
|
373
443
|
ValidationError({mandatory_attr: f"{mandatory_attr} is mandatory for {self.get_kind()}"})
|
|
374
444
|
)
|
|
@@ -385,6 +455,21 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
|
|
|
385
455
|
# -------------------------------------------
|
|
386
456
|
# Generate Attribute and Relationship and assign them
|
|
387
457
|
# -------------------------------------------
|
|
458
|
+
errors.extend(await self._process_fields_relationships(fields=fields, db=db))
|
|
459
|
+
errors.extend(await self._process_fields_attributes(fields=fields, db=db))
|
|
460
|
+
|
|
461
|
+
if errors:
|
|
462
|
+
raise ValidationError(errors)
|
|
463
|
+
|
|
464
|
+
# Check if any post processor have been defined
|
|
465
|
+
# A processor can be used for example to assigne a default value
|
|
466
|
+
for name in self._attributes + self._relationships:
|
|
467
|
+
if hasattr(self, f"process_{name}"):
|
|
468
|
+
await getattr(self, f"process_{name}")(db=db)
|
|
469
|
+
|
|
470
|
+
async def _process_fields_relationships(self, fields: dict, db: InfrahubDatabase) -> list[ValidationError]:
|
|
471
|
+
errors: list[ValidationError] = []
|
|
472
|
+
|
|
388
473
|
for rel_schema in self._schema.relationships:
|
|
389
474
|
self._relationships.append(rel_schema.name)
|
|
390
475
|
|
|
@@ -406,6 +491,11 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
|
|
|
406
491
|
except ValidationError as exc:
|
|
407
492
|
errors.append(exc)
|
|
408
493
|
|
|
494
|
+
return errors
|
|
495
|
+
|
|
496
|
+
async def _process_fields_attributes(self, fields: dict, db: InfrahubDatabase) -> list[ValidationError]:
|
|
497
|
+
errors: list[ValidationError] = []
|
|
498
|
+
|
|
409
499
|
for attr_schema in self._schema.attributes:
|
|
410
500
|
self._attributes.append(attr_schema.name)
|
|
411
501
|
if not self._existing and attr_schema.name in self._computed_jinja2_attributes:
|
|
@@ -434,14 +524,7 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
|
|
|
434
524
|
except ValidationError as exc:
|
|
435
525
|
errors.append(exc)
|
|
436
526
|
|
|
437
|
-
|
|
438
|
-
raise ValidationError(errors)
|
|
439
|
-
|
|
440
|
-
# Check if any post processor have been defined
|
|
441
|
-
# A processor can be used for example to assigne a default value
|
|
442
|
-
for name in self._attributes + self._relationships:
|
|
443
|
-
if hasattr(self, f"process_{name}"):
|
|
444
|
-
await getattr(self, f"process_{name}")(db=db)
|
|
527
|
+
return errors
|
|
445
528
|
|
|
446
529
|
async def _process_macros(self, db: InfrahubDatabase) -> None:
|
|
447
530
|
schema_branch = db.schema.get_schema_branch(self._branch.name)
|
|
@@ -474,17 +557,21 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
|
|
|
474
557
|
relationship_attribute: RelationshipManager = getattr(
|
|
475
558
|
self, attribute_path.active_relationship_schema.name
|
|
476
559
|
)
|
|
477
|
-
peer
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
560
|
+
if peer := await relationship_attribute.get_peer(db=db, raise_on_error=False):
|
|
561
|
+
related_node = await registry.manager.get_one_by_id_or_default_filter(
|
|
562
|
+
db=db,
|
|
563
|
+
id=peer.id,
|
|
564
|
+
kind=attribute_path.active_relationship_schema.peer,
|
|
565
|
+
branch=self._branch.name,
|
|
566
|
+
)
|
|
482
567
|
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
568
|
+
attribute: BaseAttribute = getattr(
|
|
569
|
+
getattr(related_node, attribute_path.active_attribute_schema.name),
|
|
570
|
+
attribute_path.active_attribute_property_name,
|
|
571
|
+
)
|
|
572
|
+
variables[variable] = attribute
|
|
573
|
+
else:
|
|
574
|
+
variables[variable] = None
|
|
488
575
|
|
|
489
576
|
elif attribute_path.is_type_attribute:
|
|
490
577
|
attribute = getattr(
|
|
@@ -872,9 +959,11 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
|
|
|
872
959
|
if relationship.kind == RelationshipKind.PARENT:
|
|
873
960
|
return relationship.name
|
|
874
961
|
|
|
875
|
-
async def get_object_template(self, db: InfrahubDatabase) ->
|
|
962
|
+
async def get_object_template(self, db: InfrahubDatabase) -> CoreObjectTemplate | None:
|
|
876
963
|
object_template: RelationshipManager = getattr(self, OBJECT_TEMPLATE_RELATIONSHIP_NAME, None)
|
|
877
|
-
return
|
|
964
|
+
return (
|
|
965
|
+
await object_template.get_peer(db=db, peer_type=CoreObjectTemplate) if object_template is not None else None
|
|
966
|
+
)
|
|
878
967
|
|
|
879
968
|
def get_relationships(
|
|
880
969
|
self, kind: RelationshipKind, exclude: Sequence[str] | None = None
|
|
@@ -888,3 +977,17 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
|
|
|
888
977
|
for relationship in self.get_schema().relationships
|
|
889
978
|
if relationship.name not in exclude and relationship.kind == kind
|
|
890
979
|
]
|
|
980
|
+
|
|
981
|
+
def validate_relationships(self) -> None:
|
|
982
|
+
for name in self._relationships:
|
|
983
|
+
relm: RelationshipManager = getattr(self, name)
|
|
984
|
+
relm.validate()
|
|
985
|
+
|
|
986
|
+
async def get_parent_relationship_peer(self, db: InfrahubDatabase, name: str) -> Node | None:
|
|
987
|
+
"""When a node has a parent relationship of a given name, this method returns the peer of that relationship."""
|
|
988
|
+
relationship = self.get_schema().get_relationship(name=name)
|
|
989
|
+
if relationship.kind != RelationshipKind.PARENT:
|
|
990
|
+
raise ValueError(f"Relationship '{name}' is not of kind 'parent'")
|
|
991
|
+
|
|
992
|
+
relm: RelationshipManager = getattr(self, name)
|
|
993
|
+
return await relm.get_peer(db=db)
|
infrahub/core/node/base.py
CHANGED
|
@@ -52,7 +52,7 @@ class BaseNodeOptions(BaseOptions):
|
|
|
52
52
|
|
|
53
53
|
|
|
54
54
|
class ObjectNodeMeta(BaseNodeMeta):
|
|
55
|
-
def __new__(mcs, name_, bases, namespace, **options):
|
|
55
|
+
def __new__(mcs, name_, bases, namespace, **options):
|
|
56
56
|
# Note: it's safe to pass options as keyword arguments as they are still type-checked by NodeOptions.
|
|
57
57
|
|
|
58
58
|
# We create this type, to then overload it with the dataclass attrs
|
|
@@ -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
|