infrahub-server 1.4.10__py3-none-any.whl → 1.5.0b1__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/tasks.py +208 -16
- infrahub/api/artifact.py +3 -0
- infrahub/api/diff/diff.py +1 -1
- infrahub/api/query.py +2 -0
- infrahub/api/schema.py +3 -0
- infrahub/auth.py +5 -5
- infrahub/cli/db.py +26 -2
- infrahub/cli/db_commands/clean_duplicate_schema_fields.py +212 -0
- infrahub/config.py +7 -2
- infrahub/core/attribute.py +25 -22
- infrahub/core/branch/models.py +2 -2
- infrahub/core/branch/needs_rebase_status.py +11 -0
- infrahub/core/branch/tasks.py +4 -3
- infrahub/core/changelog/models.py +4 -12
- infrahub/core/constants/__init__.py +1 -0
- infrahub/core/constants/infrahubkind.py +1 -0
- infrahub/core/convert_object_type/object_conversion.py +201 -0
- infrahub/core/convert_object_type/repository_conversion.py +89 -0
- infrahub/core/convert_object_type/schema_mapping.py +27 -3
- infrahub/core/diff/model/path.py +4 -0
- infrahub/core/diff/payload_builder.py +1 -1
- infrahub/core/diff/query/artifact.py +1 -1
- infrahub/core/graph/__init__.py +1 -1
- infrahub/core/initialization.py +2 -2
- infrahub/core/ipam/utilization.py +1 -1
- infrahub/core/manager.py +9 -84
- infrahub/core/migrations/graph/__init__.py +6 -0
- infrahub/core/migrations/graph/m040_profile_attrs_in_db.py +166 -0
- infrahub/core/migrations/graph/m041_create_hfid_display_label_in_db.py +97 -0
- infrahub/core/migrations/graph/m042_backfill_hfid_display_label_in_db.py +86 -0
- infrahub/core/migrations/schema/node_attribute_add.py +5 -2
- infrahub/core/migrations/shared.py +5 -6
- infrahub/core/node/__init__.py +165 -42
- infrahub/core/node/constraints/attribute_uniqueness.py +3 -1
- infrahub/core/node/create.py +67 -35
- infrahub/core/node/lock_utils.py +98 -0
- infrahub/core/node/node_property_attribute.py +230 -0
- infrahub/core/node/standard.py +1 -1
- infrahub/core/property.py +11 -0
- infrahub/core/protocols.py +8 -1
- infrahub/core/query/attribute.py +27 -15
- infrahub/core/query/node.py +61 -185
- infrahub/core/query/relationship.py +43 -26
- infrahub/core/query/subquery.py +0 -8
- infrahub/core/registry.py +2 -2
- infrahub/core/relationship/constraints/count.py +1 -1
- infrahub/core/relationship/model.py +60 -20
- infrahub/core/schema/attribute_schema.py +0 -2
- infrahub/core/schema/basenode_schema.py +42 -2
- infrahub/core/schema/definitions/core/__init__.py +2 -0
- infrahub/core/schema/definitions/core/generator.py +2 -0
- infrahub/core/schema/definitions/core/group.py +16 -2
- infrahub/core/schema/definitions/core/repository.py +7 -0
- infrahub/core/schema/definitions/internal.py +14 -1
- infrahub/core/schema/generated/base_node_schema.py +6 -1
- infrahub/core/schema/node_schema.py +5 -2
- infrahub/core/schema/relationship_schema.py +0 -1
- infrahub/core/schema/schema_branch.py +137 -2
- infrahub/core/schema/schema_branch_display.py +123 -0
- infrahub/core/schema/schema_branch_hfid.py +114 -0
- infrahub/core/validators/aggregated_checker.py +1 -1
- infrahub/core/validators/determiner.py +12 -1
- infrahub/core/validators/relationship/peer.py +1 -1
- infrahub/core/validators/tasks.py +1 -1
- infrahub/display_labels/__init__.py +0 -0
- infrahub/display_labels/gather.py +48 -0
- infrahub/display_labels/models.py +240 -0
- infrahub/display_labels/tasks.py +186 -0
- infrahub/display_labels/triggers.py +22 -0
- infrahub/events/group_action.py +1 -1
- infrahub/events/node_action.py +1 -1
- infrahub/generators/constants.py +7 -0
- infrahub/generators/models.py +38 -12
- infrahub/generators/tasks.py +34 -16
- infrahub/git/base.py +38 -1
- infrahub/git/integrator.py +22 -14
- infrahub/graphql/analyzer.py +1 -1
- infrahub/graphql/api/dependencies.py +2 -4
- infrahub/graphql/api/endpoints.py +2 -2
- infrahub/graphql/app.py +2 -4
- infrahub/graphql/initialization.py +2 -3
- infrahub/graphql/manager.py +212 -137
- infrahub/graphql/middleware.py +12 -0
- infrahub/graphql/mutations/branch.py +11 -0
- infrahub/graphql/mutations/computed_attribute.py +110 -3
- infrahub/graphql/mutations/convert_object_type.py +34 -13
- infrahub/graphql/mutations/display_label.py +111 -0
- infrahub/graphql/mutations/generator.py +25 -7
- infrahub/graphql/mutations/hfid.py +118 -0
- infrahub/graphql/mutations/ipam.py +21 -8
- infrahub/graphql/mutations/main.py +37 -153
- infrahub/graphql/mutations/profile.py +195 -0
- infrahub/graphql/mutations/proposed_change.py +2 -1
- infrahub/graphql/mutations/relationship.py +2 -2
- infrahub/graphql/mutations/repository.py +22 -83
- infrahub/graphql/mutations/resource_manager.py +2 -2
- infrahub/graphql/mutations/schema.py +5 -5
- infrahub/graphql/mutations/webhook.py +1 -1
- infrahub/graphql/queries/resource_manager.py +1 -1
- infrahub/graphql/registry.py +173 -0
- infrahub/graphql/resolvers/resolver.py +2 -0
- infrahub/graphql/schema.py +8 -1
- infrahub/groups/tasks.py +1 -1
- infrahub/hfid/__init__.py +0 -0
- infrahub/hfid/gather.py +48 -0
- infrahub/hfid/models.py +240 -0
- infrahub/hfid/tasks.py +185 -0
- infrahub/hfid/triggers.py +22 -0
- infrahub/lock.py +67 -30
- infrahub/locks/__init__.py +0 -0
- infrahub/locks/tasks.py +37 -0
- infrahub/middleware.py +26 -1
- infrahub/patch/plan_writer.py +2 -2
- infrahub/profiles/__init__.py +0 -0
- infrahub/profiles/node_applier.py +101 -0
- infrahub/profiles/queries/__init__.py +0 -0
- infrahub/profiles/queries/get_profile_data.py +99 -0
- infrahub/profiles/tasks.py +63 -0
- infrahub/proposed_change/tasks.py +10 -1
- infrahub/repositories/__init__.py +0 -0
- infrahub/repositories/create_repository.py +113 -0
- infrahub/server.py +16 -3
- infrahub/services/__init__.py +8 -5
- infrahub/tasks/registry.py +6 -4
- infrahub/trigger/catalogue.py +4 -0
- infrahub/trigger/models.py +2 -0
- infrahub/trigger/tasks.py +3 -0
- infrahub/webhook/models.py +1 -1
- infrahub/workflows/catalogue.py +110 -3
- infrahub/workflows/initialization.py +16 -0
- infrahub/workflows/models.py +17 -2
- infrahub_sdk/branch.py +5 -8
- infrahub_sdk/checks.py +1 -1
- infrahub_sdk/client.py +364 -84
- infrahub_sdk/convert_object_type.py +61 -0
- infrahub_sdk/ctl/check.py +2 -3
- infrahub_sdk/ctl/cli_commands.py +18 -12
- infrahub_sdk/ctl/config.py +8 -2
- infrahub_sdk/ctl/generator.py +6 -3
- infrahub_sdk/ctl/graphql.py +184 -0
- infrahub_sdk/ctl/repository.py +39 -1
- infrahub_sdk/ctl/schema.py +18 -3
- infrahub_sdk/ctl/utils.py +4 -0
- infrahub_sdk/ctl/validate.py +5 -3
- infrahub_sdk/diff.py +4 -5
- infrahub_sdk/exceptions.py +2 -0
- infrahub_sdk/generator.py +7 -1
- infrahub_sdk/graphql/__init__.py +12 -0
- infrahub_sdk/graphql/constants.py +1 -0
- infrahub_sdk/graphql/plugin.py +85 -0
- infrahub_sdk/graphql/query.py +77 -0
- infrahub_sdk/{graphql.py → graphql/renderers.py} +88 -75
- infrahub_sdk/graphql/utils.py +40 -0
- infrahub_sdk/node/attribute.py +2 -0
- infrahub_sdk/node/node.py +28 -20
- infrahub_sdk/playback.py +1 -2
- infrahub_sdk/protocols.py +54 -6
- infrahub_sdk/pytest_plugin/plugin.py +7 -4
- infrahub_sdk/pytest_plugin/utils.py +40 -0
- infrahub_sdk/repository.py +1 -2
- infrahub_sdk/schema/__init__.py +38 -0
- infrahub_sdk/schema/main.py +1 -0
- infrahub_sdk/schema/repository.py +8 -0
- infrahub_sdk/spec/object.py +120 -7
- infrahub_sdk/spec/range_expansion.py +118 -0
- infrahub_sdk/timestamp.py +18 -6
- infrahub_sdk/transforms.py +1 -1
- {infrahub_server-1.4.10.dist-info → infrahub_server-1.5.0b1.dist-info}/METADATA +9 -11
- {infrahub_server-1.4.10.dist-info → infrahub_server-1.5.0b1.dist-info}/RECORD +177 -134
- infrahub_testcontainers/container.py +1 -1
- infrahub_testcontainers/docker-compose-cluster.test.yml +1 -1
- infrahub_testcontainers/docker-compose.test.yml +1 -1
- infrahub_testcontainers/models.py +2 -2
- infrahub_testcontainers/performance_test.py +4 -4
- infrahub/core/convert_object_type/conversion.py +0 -134
- {infrahub_server-1.4.10.dist-info → infrahub_server-1.5.0b1.dist-info}/LICENSE.txt +0 -0
- {infrahub_server-1.4.10.dist-info → infrahub_server-1.5.0b1.dist-info}/WHEEL +0 -0
- {infrahub_server-1.4.10.dist-info → infrahub_server-1.5.0b1.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from infrahub.core.branch.needs_rebase_status import check_need_rebase_status
|
|
2
|
+
|
|
3
|
+
ALLOWED_MUTATIONS_ON_NEED_REBASE_BRANCH = ["BranchRebase", "BranchDelete", "BranchCreate", "ProposedChangeCreate"]
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def raise_on_mutation_on_branch_needing_rebase(next, root, info, **kwargs): # type: ignore # noqa
|
|
7
|
+
if info.operation.operation.value == "mutation":
|
|
8
|
+
mutation_name = info.operation.selection_set.selections[0].name.value
|
|
9
|
+
if mutation_name not in ALLOWED_MUTATIONS_ON_NEED_REBASE_BRANCH:
|
|
10
|
+
check_need_rebase_status(branch=info.context.branch)
|
|
11
|
+
|
|
12
|
+
return next(root, info, **kwargs)
|
|
@@ -7,8 +7,10 @@ from opentelemetry import trace
|
|
|
7
7
|
from typing_extensions import Self
|
|
8
8
|
|
|
9
9
|
from infrahub.branch.merge_mutation_checker import verify_branch_merge_mutation_allowed
|
|
10
|
+
from infrahub.core import registry
|
|
10
11
|
from infrahub.core.branch import Branch
|
|
11
12
|
from infrahub.database import retry_db_transaction
|
|
13
|
+
from infrahub.exceptions import BranchNotFoundError, ValidationError
|
|
12
14
|
from infrahub.graphql.context import apply_external_context
|
|
13
15
|
from infrahub.graphql.field_extractor import extract_graphql_fields
|
|
14
16
|
from infrahub.graphql.types.context import ContextInput
|
|
@@ -66,12 +68,21 @@ class BranchCreate(Mutation):
|
|
|
66
68
|
background_execution: bool = False,
|
|
67
69
|
wait_until_completion: bool = True,
|
|
68
70
|
) -> Self:
|
|
71
|
+
if data.origin_branch and data.origin_branch != registry.default_branch:
|
|
72
|
+
raise ValueError(f"origin_branch must be '{registry.default_branch}'")
|
|
73
|
+
|
|
69
74
|
graphql_context: GraphqlContext = info.context
|
|
70
75
|
task: dict | None = None
|
|
71
76
|
|
|
72
77
|
model = BranchCreateModel(**data)
|
|
73
78
|
await apply_external_context(graphql_context=graphql_context, context_input=context)
|
|
74
79
|
|
|
80
|
+
try:
|
|
81
|
+
await Branch.get_by_name(db=graphql_context.db, name=model.name)
|
|
82
|
+
raise ValidationError(f"The branch {model.name} already exists")
|
|
83
|
+
except BranchNotFoundError:
|
|
84
|
+
pass
|
|
85
|
+
|
|
75
86
|
if background_execution or not wait_until_completion:
|
|
76
87
|
workflow = await graphql_context.active_service.workflow.submit_workflow(
|
|
77
88
|
workflow=BRANCH_CREATE, context=graphql_context.get_context(), parameters={"model": model}
|
|
@@ -2,20 +2,28 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from typing import TYPE_CHECKING, Any
|
|
4
4
|
|
|
5
|
-
from graphene import Boolean, InputObjectType, Mutation, String
|
|
5
|
+
from graphene import Boolean, InputObjectType, List, Mutation, NonNull, String
|
|
6
6
|
|
|
7
7
|
from infrahub.core.account import ObjectPermission
|
|
8
8
|
from infrahub.core.constants import ComputedAttributeKind, PermissionAction, PermissionDecision
|
|
9
9
|
from infrahub.core.manager import NodeManager
|
|
10
|
+
from infrahub.core.protocols import CoreTransformPython
|
|
10
11
|
from infrahub.core.registry import registry
|
|
11
12
|
from infrahub.database import retry_db_transaction
|
|
12
13
|
from infrahub.events import EventMeta
|
|
13
14
|
from infrahub.events.node_action import NodeUpdatedEvent
|
|
14
|
-
from infrahub.exceptions import NodeNotFoundError, ValidationError
|
|
15
|
+
from infrahub.exceptions import NodeNotFoundError, ProcessingError, ValidationError
|
|
15
16
|
from infrahub.graphql.context import apply_external_context
|
|
16
17
|
from infrahub.graphql.types.context import ContextInput
|
|
17
18
|
from infrahub.log import get_log_data
|
|
18
19
|
from infrahub.worker import WORKER_IDENTITY
|
|
20
|
+
from infrahub.workers.dependencies import get_workflow
|
|
21
|
+
from infrahub.workflows.catalogue import (
|
|
22
|
+
COMPUTED_ATTRIBUTE_PROCESS_JINJA2,
|
|
23
|
+
COMPUTED_ATTRIBUTE_PROCESS_TRANSFORM,
|
|
24
|
+
TRIGGER_UPDATE_JINJA_COMPUTED_ATTRIBUTES,
|
|
25
|
+
TRIGGER_UPDATE_PYTHON_COMPUTED_ATTRIBUTES,
|
|
26
|
+
)
|
|
19
27
|
|
|
20
28
|
if TYPE_CHECKING:
|
|
21
29
|
from graphql import GraphQLResolveInfo
|
|
@@ -89,7 +97,7 @@ class UpdateComputedAttribute(Mutation):
|
|
|
89
97
|
raise NodeNotFoundError(
|
|
90
98
|
node_type="target_node",
|
|
91
99
|
identifier=str(data.id),
|
|
92
|
-
message="The indicated
|
|
100
|
+
message="The indicated node does not have the specified attribute_name",
|
|
93
101
|
)
|
|
94
102
|
if attribute_field.value != str(data.value):
|
|
95
103
|
attribute_field.value = str(data.value)
|
|
@@ -117,3 +125,102 @@ class UpdateComputedAttribute(Mutation):
|
|
|
117
125
|
result: dict[str, Any] = {"ok": True}
|
|
118
126
|
|
|
119
127
|
return cls(**result)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
class InfrahubComputedAttributeRecomputeInput(InputObjectType):
|
|
131
|
+
kind = String(required=True, description="Kind of the node to update")
|
|
132
|
+
attribute = String(required=True, description="Name of the computed attribute that must be recomputed")
|
|
133
|
+
node_ids = List(NonNull(String), description="ID of the nodes for which the attribute must be recomputed")
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
class RecomputeComputedAttribute(Mutation):
|
|
137
|
+
class Arguments:
|
|
138
|
+
data = InfrahubComputedAttributeRecomputeInput(required=True)
|
|
139
|
+
context = ContextInput(required=False)
|
|
140
|
+
|
|
141
|
+
ok = Boolean()
|
|
142
|
+
|
|
143
|
+
@classmethod
|
|
144
|
+
@retry_db_transaction(name="update_computed_attribute")
|
|
145
|
+
async def mutate(
|
|
146
|
+
cls,
|
|
147
|
+
_: dict,
|
|
148
|
+
info: GraphQLResolveInfo,
|
|
149
|
+
data: InfrahubComputedAttributeRecomputeInput,
|
|
150
|
+
context: ContextInput | None = None,
|
|
151
|
+
) -> RecomputeComputedAttribute:
|
|
152
|
+
graphql_context: GraphqlContext = info.context
|
|
153
|
+
node_schema = registry.schema.get_node_schema(
|
|
154
|
+
name=str(data.kind), branch=graphql_context.branch.name, duplicate=False
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
graphql_context.active_permissions.raise_for_permission(
|
|
158
|
+
permission=ObjectPermission(
|
|
159
|
+
namespace=node_schema.namespace,
|
|
160
|
+
name=node_schema.name,
|
|
161
|
+
action=PermissionAction.UPDATE.value,
|
|
162
|
+
decision=PermissionDecision.ALLOW_DEFAULT.value
|
|
163
|
+
if graphql_context.branch.name == registry.default_branch
|
|
164
|
+
else PermissionDecision.ALLOW_OTHER.value,
|
|
165
|
+
)
|
|
166
|
+
)
|
|
167
|
+
await apply_external_context(graphql_context=graphql_context, context_input=context)
|
|
168
|
+
|
|
169
|
+
attribute = node_schema.get_attribute(name=str(data.attribute))
|
|
170
|
+
|
|
171
|
+
if not attribute:
|
|
172
|
+
raise ProcessingError(
|
|
173
|
+
message=f"The indicated node does not have the specified attribute '{data.attribute}'"
|
|
174
|
+
)
|
|
175
|
+
if not attribute.computed_attribute:
|
|
176
|
+
raise ProcessingError(
|
|
177
|
+
message=f"The indicated node does not use a computed attribute for the specified attribute '{data.attribute}'"
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
recalculate_single_workflow = COMPUTED_ATTRIBUTE_PROCESS_JINJA2
|
|
181
|
+
recalculate_all_workflow = TRIGGER_UPDATE_JINJA_COMPUTED_ATTRIBUTES
|
|
182
|
+
if attribute.computed_attribute.kind == ComputedAttributeKind.TRANSFORM_PYTHON:
|
|
183
|
+
if not await NodeManager.query(
|
|
184
|
+
db=graphql_context.db,
|
|
185
|
+
branch=graphql_context.branch,
|
|
186
|
+
schema=CoreTransformPython,
|
|
187
|
+
filters={"name__value": attribute.computed_attribute.transform},
|
|
188
|
+
):
|
|
189
|
+
raise ProcessingError(
|
|
190
|
+
message=f"The transform for the indicated node computed attribute for the specified attribute '{data.attribute}' does not exist"
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
recalculate_single_workflow = COMPUTED_ATTRIBUTE_PROCESS_TRANSFORM
|
|
194
|
+
recalculate_all_workflow = TRIGGER_UPDATE_PYTHON_COMPUTED_ATTRIBUTES
|
|
195
|
+
|
|
196
|
+
if data.node_ids:
|
|
197
|
+
nodes = await NodeManager.get_many(
|
|
198
|
+
db=graphql_context.db, branch=graphql_context.branch, ids=list(data.node_ids)
|
|
199
|
+
)
|
|
200
|
+
for node in nodes.values():
|
|
201
|
+
await get_workflow().submit_workflow(
|
|
202
|
+
workflow=recalculate_single_workflow,
|
|
203
|
+
context=graphql_context.get_context(),
|
|
204
|
+
parameters={
|
|
205
|
+
"branch_name": graphql_context.branch.name,
|
|
206
|
+
"computed_attribute_name": str(data.attribute),
|
|
207
|
+
"computed_attribute_kind": node_schema.kind,
|
|
208
|
+
"node_kind": node_schema.kind,
|
|
209
|
+
"object_id": node.id,
|
|
210
|
+
"context": context,
|
|
211
|
+
},
|
|
212
|
+
)
|
|
213
|
+
else:
|
|
214
|
+
await get_workflow().submit_workflow(
|
|
215
|
+
workflow=recalculate_all_workflow,
|
|
216
|
+
context=graphql_context.get_context(),
|
|
217
|
+
parameters={
|
|
218
|
+
"branch_name": graphql_context.branch.name,
|
|
219
|
+
"computed_attribute_name": str(data.attribute),
|
|
220
|
+
"computed_attribute_kind": node_schema.kind,
|
|
221
|
+
"context": context,
|
|
222
|
+
},
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
result: dict[str, Any] = {"ok": True}
|
|
226
|
+
return cls(**result)
|
|
@@ -5,9 +5,12 @@ from graphene.types.generic import GenericScalar
|
|
|
5
5
|
from graphql import GraphQLResolveInfo
|
|
6
6
|
|
|
7
7
|
from infrahub.core import registry
|
|
8
|
-
from infrahub.core.
|
|
8
|
+
from infrahub.core.constants.infrahubkind import READONLYREPOSITORY, REPOSITORY
|
|
9
|
+
from infrahub.core.convert_object_type.object_conversion import ConversionFieldInput, convert_and_validate_object_type
|
|
10
|
+
from infrahub.core.convert_object_type.repository_conversion import convert_repository_type
|
|
9
11
|
from infrahub.core.convert_object_type.schema_mapping import get_schema_mapping
|
|
10
12
|
from infrahub.core.manager import NodeManager
|
|
13
|
+
from infrahub.repositories.create_repository import RepositoryFinalizer
|
|
11
14
|
|
|
12
15
|
if TYPE_CHECKING:
|
|
13
16
|
from infrahub.graphql.initialization import GraphqlContext
|
|
@@ -44,26 +47,44 @@ class ConvertObjectType(Mutation):
|
|
|
44
47
|
source_schema = registry.get_node_schema(name=node_to_convert.get_kind(), branch=graphql_context.branch)
|
|
45
48
|
target_schema = registry.get_node_schema(name=str(data.target_kind), branch=graphql_context.branch)
|
|
46
49
|
|
|
47
|
-
fields_mapping: dict[str,
|
|
50
|
+
fields_mapping: dict[str, ConversionFieldInput] = {}
|
|
48
51
|
if not isinstance(data.fields_mapping, dict):
|
|
49
|
-
raise ValueError(f"Expected `fields_mapping` to be a `dict`, got {type(fields_mapping)}")
|
|
52
|
+
raise ValueError(f"Expected `fields_mapping` to be a `dict`, got {type(data.fields_mapping)}")
|
|
50
53
|
|
|
51
54
|
for field_name, input_for_dest_field_str in data.fields_mapping.items():
|
|
52
|
-
fields_mapping[field_name] =
|
|
55
|
+
fields_mapping[field_name] = ConversionFieldInput(**input_for_dest_field_str)
|
|
56
|
+
|
|
57
|
+
node_to_convert = await NodeManager.get_one(
|
|
58
|
+
id=str(data.node_id), db=graphql_context.db, branch=graphql_context.branch
|
|
59
|
+
)
|
|
53
60
|
|
|
54
61
|
# Complete fields mapping with auto-mapping.
|
|
55
62
|
mapping = get_schema_mapping(source_schema=source_schema, target_schema=target_schema)
|
|
56
63
|
for field_name, mapping_value in mapping.items():
|
|
57
64
|
if mapping_value.source_field_name is not None and field_name not in fields_mapping:
|
|
58
|
-
fields_mapping[field_name] =
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
fields_mapping[field_name] = ConversionFieldInput(source_field=mapping_value.source_field_name)
|
|
66
|
+
|
|
67
|
+
if target_schema.kind in [REPOSITORY, READONLYREPOSITORY]:
|
|
68
|
+
new_node = await convert_repository_type(
|
|
69
|
+
repository=node_to_convert,
|
|
70
|
+
target_schema=target_schema,
|
|
71
|
+
mapping=fields_mapping,
|
|
72
|
+
branch=graphql_context.branch,
|
|
73
|
+
db=graphql_context.db,
|
|
74
|
+
repository_post_creator=RepositoryFinalizer(
|
|
75
|
+
account_session=graphql_context.active_account_session,
|
|
76
|
+
services=graphql_context.active_service,
|
|
77
|
+
context=graphql_context.get_context(),
|
|
78
|
+
),
|
|
79
|
+
)
|
|
80
|
+
else:
|
|
81
|
+
new_node = await convert_and_validate_object_type(
|
|
82
|
+
node=node_to_convert,
|
|
83
|
+
target_schema=target_schema,
|
|
84
|
+
mapping=fields_mapping,
|
|
85
|
+
branch=graphql_context.branch,
|
|
86
|
+
db=graphql_context.db,
|
|
87
|
+
)
|
|
67
88
|
|
|
68
89
|
dict_node = await new_node.to_graphql(db=graphql_context.db, fields={})
|
|
69
90
|
result: dict[str, Any] = {"ok": True, "node": dict_node}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING, Any
|
|
4
|
+
|
|
5
|
+
from graphene import Boolean, InputObjectType, Mutation, String
|
|
6
|
+
|
|
7
|
+
from infrahub.core.account import ObjectPermission
|
|
8
|
+
from infrahub.core.constants import PermissionAction, PermissionDecision
|
|
9
|
+
from infrahub.core.manager import NodeManager
|
|
10
|
+
from infrahub.core.registry import registry
|
|
11
|
+
from infrahub.database import retry_db_transaction
|
|
12
|
+
from infrahub.events import EventMeta
|
|
13
|
+
from infrahub.events.node_action import NodeUpdatedEvent
|
|
14
|
+
from infrahub.exceptions import NodeNotFoundError, ValidationError
|
|
15
|
+
from infrahub.graphql.context import apply_external_context
|
|
16
|
+
from infrahub.graphql.types.context import ContextInput
|
|
17
|
+
from infrahub.log import get_log_data
|
|
18
|
+
from infrahub.worker import WORKER_IDENTITY
|
|
19
|
+
|
|
20
|
+
if TYPE_CHECKING:
|
|
21
|
+
from graphql import GraphQLResolveInfo
|
|
22
|
+
|
|
23
|
+
from infrahub.graphql.initialization import GraphqlContext
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class InfrahubDisplayLabelUpdateInput(InputObjectType):
|
|
27
|
+
id = String(required=True)
|
|
28
|
+
kind = String(required=True)
|
|
29
|
+
value = String(required=True)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class UpdateDisplayLabel(Mutation):
|
|
33
|
+
class Arguments:
|
|
34
|
+
data = InfrahubDisplayLabelUpdateInput(required=True)
|
|
35
|
+
context = ContextInput(required=False)
|
|
36
|
+
|
|
37
|
+
ok = Boolean()
|
|
38
|
+
|
|
39
|
+
@classmethod
|
|
40
|
+
@retry_db_transaction(name="update_display_label")
|
|
41
|
+
async def mutate(
|
|
42
|
+
cls,
|
|
43
|
+
_: dict,
|
|
44
|
+
info: GraphQLResolveInfo,
|
|
45
|
+
data: InfrahubDisplayLabelUpdateInput,
|
|
46
|
+
context: ContextInput | None = None,
|
|
47
|
+
) -> UpdateDisplayLabel:
|
|
48
|
+
graphql_context: GraphqlContext = info.context
|
|
49
|
+
node_schema = registry.schema.get_node_schema(
|
|
50
|
+
name=str(data.kind), branch=graphql_context.branch.name, duplicate=False
|
|
51
|
+
)
|
|
52
|
+
if not node_schema.display_label:
|
|
53
|
+
raise ValidationError(input_value=f"{node_schema.kind}.display_label has not been defined for this kind.")
|
|
54
|
+
|
|
55
|
+
graphql_context.active_permissions.raise_for_permission(
|
|
56
|
+
permission=ObjectPermission(
|
|
57
|
+
namespace=node_schema.namespace,
|
|
58
|
+
name=node_schema.name,
|
|
59
|
+
action=PermissionAction.UPDATE.value,
|
|
60
|
+
decision=PermissionDecision.ALLOW_DEFAULT.value
|
|
61
|
+
if graphql_context.branch.name == registry.default_branch
|
|
62
|
+
else PermissionDecision.ALLOW_OTHER.value,
|
|
63
|
+
)
|
|
64
|
+
)
|
|
65
|
+
await apply_external_context(graphql_context=graphql_context, context_input=context)
|
|
66
|
+
|
|
67
|
+
if not (
|
|
68
|
+
target_node := await NodeManager.get_one(
|
|
69
|
+
db=graphql_context.db,
|
|
70
|
+
kind=node_schema.kind,
|
|
71
|
+
id=str(data.id),
|
|
72
|
+
branch=graphql_context.branch,
|
|
73
|
+
fields={"display_label": None},
|
|
74
|
+
)
|
|
75
|
+
):
|
|
76
|
+
raise NodeNotFoundError(
|
|
77
|
+
node_type=node_schema.kind,
|
|
78
|
+
identifier=str(data.id),
|
|
79
|
+
message="The targeted node was not found in the database",
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
existing_label = (
|
|
83
|
+
await target_node.get_display_label(db=graphql_context.db) if target_node.has_display_label() else None
|
|
84
|
+
)
|
|
85
|
+
if str(data.value) != existing_label:
|
|
86
|
+
await target_node.set_display_label(value=str(data.value))
|
|
87
|
+
|
|
88
|
+
async with graphql_context.db.start_transaction() as dbt:
|
|
89
|
+
await target_node.save(db=dbt, fields=["display_label"])
|
|
90
|
+
|
|
91
|
+
log_data = get_log_data()
|
|
92
|
+
request_id = log_data.get("request_id", "")
|
|
93
|
+
|
|
94
|
+
event = NodeUpdatedEvent(
|
|
95
|
+
kind=node_schema.kind,
|
|
96
|
+
node_id=target_node.get_id(),
|
|
97
|
+
changelog=target_node.node_changelog.model_dump(),
|
|
98
|
+
fields=["display_label"],
|
|
99
|
+
meta=EventMeta(
|
|
100
|
+
context=graphql_context.get_context(),
|
|
101
|
+
initiator_id=WORKER_IDENTITY,
|
|
102
|
+
request_id=request_id,
|
|
103
|
+
account_id=graphql_context.active_account_session.account_id,
|
|
104
|
+
branch=graphql_context.branch,
|
|
105
|
+
),
|
|
106
|
+
)
|
|
107
|
+
await graphql_context.active_service.event.send(event=event)
|
|
108
|
+
|
|
109
|
+
result: dict[str, Any] = {"ok": True}
|
|
110
|
+
|
|
111
|
+
return cls(**result)
|
|
@@ -5,6 +5,7 @@ from typing import TYPE_CHECKING
|
|
|
5
5
|
from graphene import Boolean, Field, InputField, InputObjectType, List, Mutation, NonNull, String
|
|
6
6
|
|
|
7
7
|
from infrahub.core.manager import NodeManager
|
|
8
|
+
from infrahub.core.protocols import CoreGeneratorDefinition, CoreGenericRepository, CoreGraphQLQuery, CoreStandardGroup
|
|
8
9
|
from infrahub.generators.models import ProposedChangeGeneratorDefinition, RequestGeneratorDefinitionRun
|
|
9
10
|
from infrahub.graphql.context import apply_external_context
|
|
10
11
|
from infrahub.graphql.types.context import ContextInput
|
|
@@ -44,11 +45,18 @@ class GeneratorDefinitionRequestRun(Mutation):
|
|
|
44
45
|
db = graphql_context.db
|
|
45
46
|
await apply_external_context(graphql_context=graphql_context, context_input=context)
|
|
46
47
|
generator_definition = await NodeManager.get_one(
|
|
47
|
-
id=str(data.id),
|
|
48
|
+
id=str(data.id),
|
|
49
|
+
kind=CoreGeneratorDefinition,
|
|
50
|
+
db=db,
|
|
51
|
+
branch=graphql_context.branch,
|
|
52
|
+
prefetch_relationships=True,
|
|
53
|
+
raise_on_error=True,
|
|
48
54
|
)
|
|
49
|
-
query = await generator_definition.query.get_peer(db=db)
|
|
50
|
-
repository = await generator_definition.repository.get_peer(
|
|
51
|
-
|
|
55
|
+
query = await generator_definition.query.get_peer(db=db, peer_type=CoreGraphQLQuery, raise_on_error=True)
|
|
56
|
+
repository = await generator_definition.repository.get_peer(
|
|
57
|
+
db=db, peer_type=CoreGenericRepository, raise_on_error=True
|
|
58
|
+
)
|
|
59
|
+
group = await generator_definition.targets.get_peer(db=db, peer_type=CoreStandardGroup, raise_on_error=True)
|
|
52
60
|
|
|
53
61
|
request_model = RequestGeneratorDefinitionRun(
|
|
54
62
|
generator_definition=ProposedChangeGeneratorDefinition(
|
|
@@ -57,11 +65,21 @@ class GeneratorDefinitionRequestRun(Mutation):
|
|
|
57
65
|
class_name=generator_definition.class_name.value,
|
|
58
66
|
file_path=generator_definition.file_path.value,
|
|
59
67
|
query_name=query.name.value,
|
|
60
|
-
query_models=query.models.value,
|
|
68
|
+
query_models=query.models.value or [],
|
|
61
69
|
repository_id=repository.id,
|
|
62
|
-
parameters=generator_definition.parameters.value
|
|
70
|
+
parameters=generator_definition.parameters.value
|
|
71
|
+
if isinstance(generator_definition.parameters.value, dict)
|
|
72
|
+
else {},
|
|
63
73
|
group_id=group.id,
|
|
64
|
-
convert_query_response=generator_definition.convert_query_response.value
|
|
74
|
+
convert_query_response=generator_definition.convert_query_response.value
|
|
75
|
+
if generator_definition.convert_query_response.value is not None
|
|
76
|
+
else False,
|
|
77
|
+
execute_in_proposed_change=generator_definition.execute_in_proposed_change.value
|
|
78
|
+
if generator_definition.execute_in_proposed_change.value is not None
|
|
79
|
+
else True,
|
|
80
|
+
execute_after_merge=generator_definition.execute_after_merge.value
|
|
81
|
+
if generator_definition.execute_after_merge.value is not None
|
|
82
|
+
else True,
|
|
65
83
|
),
|
|
66
84
|
branch=graphql_context.branch.name,
|
|
67
85
|
target_members=data.get("nodes", []),
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING, Any, cast
|
|
4
|
+
|
|
5
|
+
from graphene import Boolean, InputObjectType, List, Mutation, NonNull, String
|
|
6
|
+
|
|
7
|
+
from infrahub.core.account import ObjectPermission
|
|
8
|
+
from infrahub.core.constants import PermissionAction, PermissionDecision
|
|
9
|
+
from infrahub.core.manager import NodeManager
|
|
10
|
+
from infrahub.core.registry import registry
|
|
11
|
+
from infrahub.database import retry_db_transaction
|
|
12
|
+
from infrahub.events import EventMeta
|
|
13
|
+
from infrahub.events.node_action import NodeUpdatedEvent
|
|
14
|
+
from infrahub.exceptions import NodeNotFoundError, ValidationError
|
|
15
|
+
from infrahub.graphql.context import apply_external_context
|
|
16
|
+
from infrahub.graphql.types.context import ContextInput
|
|
17
|
+
from infrahub.log import get_log_data
|
|
18
|
+
from infrahub.worker import WORKER_IDENTITY
|
|
19
|
+
|
|
20
|
+
if TYPE_CHECKING:
|
|
21
|
+
from graphql import GraphQLResolveInfo
|
|
22
|
+
|
|
23
|
+
from infrahub.graphql.initialization import GraphqlContext
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class InfrahubHFIDUpdateInput(InputObjectType):
|
|
27
|
+
id = String(required=True)
|
|
28
|
+
kind = String(required=True)
|
|
29
|
+
value = List(NonNull(String), required=True)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class UpdateHFID(Mutation):
|
|
33
|
+
class Arguments:
|
|
34
|
+
data = InfrahubHFIDUpdateInput(required=True)
|
|
35
|
+
context = ContextInput(required=False)
|
|
36
|
+
|
|
37
|
+
ok = Boolean()
|
|
38
|
+
|
|
39
|
+
@classmethod
|
|
40
|
+
@retry_db_transaction(name="update_hfid")
|
|
41
|
+
async def mutate(
|
|
42
|
+
cls,
|
|
43
|
+
_: dict,
|
|
44
|
+
info: GraphQLResolveInfo,
|
|
45
|
+
data: InfrahubHFIDUpdateInput,
|
|
46
|
+
context: ContextInput | None = None,
|
|
47
|
+
) -> UpdateHFID:
|
|
48
|
+
graphql_context: GraphqlContext = info.context
|
|
49
|
+
node_schema = registry.schema.get_node_schema(
|
|
50
|
+
name=str(data.kind), branch=graphql_context.branch.name, duplicate=False
|
|
51
|
+
)
|
|
52
|
+
if node_schema.human_friendly_id is None:
|
|
53
|
+
raise ValidationError(
|
|
54
|
+
input_value=f"{node_schema.kind}.human_friendly_id has not been defined for this kind."
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
updated_hfid = cast(list[str], data.value)
|
|
58
|
+
|
|
59
|
+
if len(node_schema.human_friendly_id) != len(updated_hfid):
|
|
60
|
+
raise ValidationError(
|
|
61
|
+
input_value=f"{node_schema.kind}.human_friendly_id requires {len(node_schema.human_friendly_id)} parts data has {len(updated_hfid)}"
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
graphql_context.active_permissions.raise_for_permission(
|
|
65
|
+
permission=ObjectPermission(
|
|
66
|
+
namespace=node_schema.namespace,
|
|
67
|
+
name=node_schema.name,
|
|
68
|
+
action=PermissionAction.UPDATE.value,
|
|
69
|
+
decision=PermissionDecision.ALLOW_DEFAULT.value
|
|
70
|
+
if graphql_context.branch.name == registry.default_branch
|
|
71
|
+
else PermissionDecision.ALLOW_OTHER.value,
|
|
72
|
+
)
|
|
73
|
+
)
|
|
74
|
+
await apply_external_context(graphql_context=graphql_context, context_input=context)
|
|
75
|
+
|
|
76
|
+
if not (
|
|
77
|
+
target_node := await NodeManager.get_one(
|
|
78
|
+
db=graphql_context.db,
|
|
79
|
+
kind=node_schema.kind,
|
|
80
|
+
id=str(data.id),
|
|
81
|
+
branch=graphql_context.branch,
|
|
82
|
+
fields={"human_friendly_id": None},
|
|
83
|
+
)
|
|
84
|
+
):
|
|
85
|
+
raise NodeNotFoundError(
|
|
86
|
+
node_type=node_schema.kind,
|
|
87
|
+
identifier=str(data.id),
|
|
88
|
+
message="The targeted node was not found in the database",
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
existing = await target_node.get_hfid(db=graphql_context.db) if target_node.has_human_friendly_id() else None
|
|
92
|
+
if updated_hfid != existing:
|
|
93
|
+
await target_node.set_human_friendly_id(value=updated_hfid)
|
|
94
|
+
|
|
95
|
+
async with graphql_context.db.start_transaction() as dbt:
|
|
96
|
+
await target_node.save(db=dbt, fields=["human_friendly_id"])
|
|
97
|
+
|
|
98
|
+
log_data = get_log_data()
|
|
99
|
+
request_id = log_data.get("request_id", "")
|
|
100
|
+
|
|
101
|
+
event = NodeUpdatedEvent(
|
|
102
|
+
kind=node_schema.kind,
|
|
103
|
+
node_id=target_node.get_id(),
|
|
104
|
+
changelog=target_node.node_changelog.model_dump(),
|
|
105
|
+
fields=["human_friendly_id"],
|
|
106
|
+
meta=EventMeta(
|
|
107
|
+
context=graphql_context.get_context(),
|
|
108
|
+
initiator_id=WORKER_IDENTITY,
|
|
109
|
+
request_id=request_id,
|
|
110
|
+
account_id=graphql_context.active_account_session.account_id,
|
|
111
|
+
branch=graphql_context.branch,
|
|
112
|
+
),
|
|
113
|
+
)
|
|
114
|
+
await graphql_context.active_service.event.send(event=event)
|
|
115
|
+
|
|
116
|
+
result: dict[str, Any] = {"ok": True}
|
|
117
|
+
|
|
118
|
+
return cls(**result)
|
|
@@ -16,10 +16,12 @@ from infrahub.core.node import Node
|
|
|
16
16
|
from infrahub.core.schema import NodeSchema
|
|
17
17
|
from infrahub.database import InfrahubDatabase, retry_db_transaction
|
|
18
18
|
from infrahub.exceptions import NodeNotFoundError, ValidationError
|
|
19
|
-
from infrahub.lock import InfrahubMultiLock
|
|
19
|
+
from infrahub.lock import InfrahubMultiLock
|
|
20
20
|
from infrahub.log import get_logger
|
|
21
21
|
|
|
22
|
-
from .
|
|
22
|
+
from ...core.node.create import create_node
|
|
23
|
+
from ...core.node.lock_utils import build_object_lock_name
|
|
24
|
+
from .main import DeleteResult, InfrahubMutationMixin, InfrahubMutationOptions, build_graphql_response
|
|
23
25
|
from .node_getter.by_default_filter import MutationNodeGetterByDefaultFilter
|
|
24
26
|
|
|
25
27
|
if TYPE_CHECKING:
|
|
@@ -121,7 +123,13 @@ class InfrahubIPAddressMutation(InfrahubMutationMixin, Mutation):
|
|
|
121
123
|
ip_address: IPv4Interface | ipaddress.IPv6Interface,
|
|
122
124
|
namespace_id: str,
|
|
123
125
|
) -> Node:
|
|
124
|
-
address = await
|
|
126
|
+
address = await create_node(
|
|
127
|
+
data=dict(data),
|
|
128
|
+
db=db,
|
|
129
|
+
branch=branch,
|
|
130
|
+
schema=cls._meta.active_schema,
|
|
131
|
+
)
|
|
132
|
+
|
|
125
133
|
reconciler = IpamReconciler(db=db, branch=branch)
|
|
126
134
|
reconciled_address = await reconciler.reconcile(
|
|
127
135
|
ip_value=ip_address, namespace=namespace_id, node_uuid=address.get_id()
|
|
@@ -152,9 +160,9 @@ class InfrahubIPAddressMutation(InfrahubMutationMixin, Mutation):
|
|
|
152
160
|
reconciled_address = await cls._mutate_create_object_and_reconcile(
|
|
153
161
|
data=data, branch=branch, db=dbt, ip_address=ip_address, namespace_id=namespace_id
|
|
154
162
|
)
|
|
155
|
-
|
|
163
|
+
graphql_response = await build_graphql_response(info=info, db=dbt, obj=reconciled_address)
|
|
156
164
|
|
|
157
|
-
return reconciled_address,
|
|
165
|
+
return reconciled_address, cls(**graphql_response)
|
|
158
166
|
|
|
159
167
|
@classmethod
|
|
160
168
|
async def _mutate_update_object_and_reconcile(
|
|
@@ -275,7 +283,12 @@ class InfrahubIPPrefixMutation(InfrahubMutationMixin, Mutation):
|
|
|
275
283
|
db: InfrahubDatabase,
|
|
276
284
|
namespace_id: str,
|
|
277
285
|
) -> Node:
|
|
278
|
-
prefix = await
|
|
286
|
+
prefix = await create_node(
|
|
287
|
+
data=dict(data),
|
|
288
|
+
db=db,
|
|
289
|
+
branch=branch,
|
|
290
|
+
schema=cls._meta.active_schema,
|
|
291
|
+
)
|
|
279
292
|
return await cls._reconcile_prefix(
|
|
280
293
|
branch=branch, db=db, prefix=prefix, namespace_id=namespace_id, is_delete=False
|
|
281
294
|
)
|
|
@@ -300,9 +313,9 @@ class InfrahubIPPrefixMutation(InfrahubMutationMixin, Mutation):
|
|
|
300
313
|
data=data, branch=branch, db=dbt, namespace_id=namespace_id
|
|
301
314
|
)
|
|
302
315
|
|
|
303
|
-
|
|
316
|
+
graphql_response = await build_graphql_response(info=info, db=dbt, obj=reconciled_prefix)
|
|
304
317
|
|
|
305
|
-
return reconciled_prefix,
|
|
318
|
+
return reconciled_prefix, cls(**graphql_response)
|
|
306
319
|
|
|
307
320
|
@classmethod
|
|
308
321
|
async def _mutate_update_object_and_reconcile(
|