infrahub-server 1.4.10__py3-none-any.whl → 1.5.0b0__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 +200 -16
- infrahub/api/artifact.py +3 -0
- infrahub/api/query.py +2 -0
- infrahub/api/schema.py +3 -0
- infrahub/auth.py +5 -5
- infrahub/cli/db.py +2 -2
- infrahub/config.py +7 -2
- infrahub/core/attribute.py +22 -19
- infrahub/core/branch/models.py +2 -2
- infrahub/core/branch/needs_rebase_status.py +11 -0
- infrahub/core/branch/tasks.py +2 -2
- infrahub/core/constants/__init__.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/query/artifact.py +1 -1
- infrahub/core/graph/__init__.py +1 -1
- infrahub/core/initialization.py +2 -2
- infrahub/core/manager.py +3 -81
- infrahub/core/migrations/graph/__init__.py +2 -0
- infrahub/core/migrations/graph/m040_profile_attrs_in_db.py +166 -0
- infrahub/core/node/__init__.py +23 -2
- infrahub/core/node/create.py +67 -35
- infrahub/core/node/lock_utils.py +98 -0
- infrahub/core/property.py +11 -0
- infrahub/core/protocols.py +1 -0
- infrahub/core/query/attribute.py +27 -15
- infrahub/core/query/node.py +47 -184
- infrahub/core/query/relationship.py +43 -26
- infrahub/core/query/subquery.py +0 -8
- infrahub/core/relationship/model.py +59 -19
- infrahub/core/schema/attribute_schema.py +0 -2
- infrahub/core/schema/definitions/core/repository.py +7 -0
- infrahub/core/schema/relationship_schema.py +0 -1
- infrahub/core/schema/schema_branch.py +3 -2
- infrahub/generators/models.py +31 -12
- infrahub/generators/tasks.py +3 -1
- infrahub/git/base.py +38 -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/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/repository.py +22 -83
- infrahub/graphql/mutations/webhook.py +1 -1
- infrahub/graphql/registry.py +173 -0
- infrahub/graphql/schema.py +4 -1
- infrahub/lock.py +52 -26
- infrahub/locks/__init__.py +0 -0
- infrahub/locks/tasks.py +37 -0
- 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/repositories/__init__.py +0 -0
- infrahub/repositories/create_repository.py +113 -0
- infrahub/tasks/registry.py +6 -4
- infrahub/webhook/models.py +1 -1
- infrahub/workflows/catalogue.py +38 -3
- infrahub/workflows/models.py +17 -2
- infrahub_sdk/branch.py +5 -8
- 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 +16 -12
- infrahub_sdk/ctl/config.py +8 -2
- infrahub_sdk/ctl/generator.py +2 -3
- infrahub_sdk/ctl/repository.py +39 -1
- infrahub_sdk/ctl/schema.py +12 -1
- 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/graphql.py +7 -2
- infrahub_sdk/node/attribute.py +2 -0
- infrahub_sdk/node/node.py +28 -20
- infrahub_sdk/playback.py +1 -2
- infrahub_sdk/protocols.py +40 -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/main.py +1 -0
- infrahub_sdk/spec/object.py +43 -4
- infrahub_sdk/spec/range_expansion.py +118 -0
- infrahub_sdk/timestamp.py +18 -6
- {infrahub_server-1.4.10.dist-info → infrahub_server-1.5.0b0.dist-info}/METADATA +6 -9
- {infrahub_server-1.4.10.dist-info → infrahub_server-1.5.0b0.dist-info}/RECORD +102 -84
- 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.0b0.dist-info}/LICENSE.txt +0 -0
- {infrahub_server-1.4.10.dist-info → infrahub_server-1.5.0b0.dist-info}/WHEEL +0 -0
- {infrahub_server-1.4.10.dist-info → infrahub_server-1.5.0b0.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING, Any
|
|
4
|
+
|
|
5
|
+
from graphene import Boolean, InputObjectType, Mutation, String
|
|
6
|
+
from graphql import GraphQLResolveInfo
|
|
7
|
+
from opentelemetry import trace
|
|
8
|
+
from typing_extensions import Self
|
|
9
|
+
|
|
10
|
+
from infrahub.core.manager import NodeManager
|
|
11
|
+
from infrahub.core.schema import ProfileSchema
|
|
12
|
+
from infrahub.graphql.types.context import ContextInput
|
|
13
|
+
from infrahub.log import get_logger
|
|
14
|
+
from infrahub.profiles.node_applier import NodeProfilesApplier
|
|
15
|
+
from infrahub.workflows.catalogue import PROFILE_REFRESH_MULTIPLE
|
|
16
|
+
|
|
17
|
+
from .main import InfrahubMutationMixin, InfrahubMutationOptions
|
|
18
|
+
|
|
19
|
+
if TYPE_CHECKING:
|
|
20
|
+
from graphql import GraphQLResolveInfo
|
|
21
|
+
|
|
22
|
+
from infrahub.core.branch import Branch
|
|
23
|
+
from infrahub.core.node import Node
|
|
24
|
+
from infrahub.database import InfrahubDatabase
|
|
25
|
+
from infrahub.graphql.initialization import GraphqlContext
|
|
26
|
+
from infrahub.services.adapters.workflow import InfrahubWorkflow
|
|
27
|
+
|
|
28
|
+
log = get_logger()
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class InfrahubProfileMutation(InfrahubMutationMixin, Mutation):
|
|
32
|
+
@classmethod
|
|
33
|
+
def __init_subclass_with_meta__(
|
|
34
|
+
cls,
|
|
35
|
+
schema: ProfileSchema,
|
|
36
|
+
_meta: InfrahubMutationOptions | None = None,
|
|
37
|
+
**options: dict[str, Any],
|
|
38
|
+
) -> None:
|
|
39
|
+
# Make sure schema is a valid NodeSchema Node Class
|
|
40
|
+
if not isinstance(schema, ProfileSchema):
|
|
41
|
+
raise ValueError(f"You need to pass a valid ProfileSchema in '{cls.__name__}.Meta', received '{schema}'")
|
|
42
|
+
|
|
43
|
+
if not _meta:
|
|
44
|
+
_meta = InfrahubMutationOptions(cls)
|
|
45
|
+
_meta.schema = schema
|
|
46
|
+
|
|
47
|
+
super().__init_subclass_with_meta__(_meta=_meta, **options)
|
|
48
|
+
|
|
49
|
+
@classmethod
|
|
50
|
+
async def _send_profile_refresh_workflows(
|
|
51
|
+
cls,
|
|
52
|
+
db: InfrahubDatabase,
|
|
53
|
+
workflow_service: InfrahubWorkflow,
|
|
54
|
+
branch_name: str,
|
|
55
|
+
obj: Node,
|
|
56
|
+
node_ids: list[str] | None = None,
|
|
57
|
+
) -> None:
|
|
58
|
+
if not node_ids:
|
|
59
|
+
related_nodes = await obj.related_nodes.get_relationships(db=db) # type: ignore[attr-defined]
|
|
60
|
+
node_ids = [rel.peer_id for rel in related_nodes]
|
|
61
|
+
if node_ids:
|
|
62
|
+
await workflow_service.submit_workflow(
|
|
63
|
+
workflow=PROFILE_REFRESH_MULTIPLE,
|
|
64
|
+
parameters={
|
|
65
|
+
"branch_name": branch_name,
|
|
66
|
+
"node_ids": node_ids,
|
|
67
|
+
},
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
@classmethod
|
|
71
|
+
def _get_profile_attr_values_map(cls, obj: Node) -> dict[str, Any]:
|
|
72
|
+
attr_values_map = {}
|
|
73
|
+
for attr_schema in obj.get_schema().attributes:
|
|
74
|
+
# profile name update can be ignored
|
|
75
|
+
if attr_schema.name == "profile_name":
|
|
76
|
+
continue
|
|
77
|
+
attr_values_map[attr_schema.name] = getattr(obj, attr_schema.name).value
|
|
78
|
+
return attr_values_map
|
|
79
|
+
|
|
80
|
+
@classmethod
|
|
81
|
+
async def _get_profile_related_node_ids(cls, db: InfrahubDatabase, obj: Node) -> set[str]:
|
|
82
|
+
related_nodes = await obj.related_nodes.get_relationships(db=db) # type: ignore[attr-defined]
|
|
83
|
+
if related_nodes:
|
|
84
|
+
related_node_ids = {rel.peer_id for rel in related_nodes}
|
|
85
|
+
else:
|
|
86
|
+
related_node_ids = set()
|
|
87
|
+
return related_node_ids
|
|
88
|
+
|
|
89
|
+
@classmethod
|
|
90
|
+
async def mutate_create(
|
|
91
|
+
cls,
|
|
92
|
+
info: GraphQLResolveInfo,
|
|
93
|
+
data: InputObjectType,
|
|
94
|
+
branch: Branch,
|
|
95
|
+
database: InfrahubDatabase | None = None,
|
|
96
|
+
override_data: dict[str, Any] | None = None,
|
|
97
|
+
) -> tuple[Node, Self]:
|
|
98
|
+
graphql_context: GraphqlContext = info.context
|
|
99
|
+
db = database or graphql_context.db
|
|
100
|
+
workflow_service = graphql_context.active_service.workflow
|
|
101
|
+
|
|
102
|
+
obj, mutation = await super().mutate_create(
|
|
103
|
+
info=info, data=data, branch=branch, database=database, override_data=override_data
|
|
104
|
+
)
|
|
105
|
+
await cls._send_profile_refresh_workflows(
|
|
106
|
+
db=db, workflow_service=workflow_service, branch_name=branch.name, obj=obj
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
return obj, mutation
|
|
110
|
+
|
|
111
|
+
@classmethod
|
|
112
|
+
async def _call_mutate_update(
|
|
113
|
+
cls,
|
|
114
|
+
info: GraphQLResolveInfo,
|
|
115
|
+
data: InputObjectType,
|
|
116
|
+
branch: Branch,
|
|
117
|
+
db: InfrahubDatabase,
|
|
118
|
+
obj: Node,
|
|
119
|
+
skip_uniqueness_check: bool = False,
|
|
120
|
+
) -> tuple[Node, Self]:
|
|
121
|
+
workflow_service = info.context.active_service.workflow
|
|
122
|
+
original_attr_values = cls._get_profile_attr_values_map(obj=obj)
|
|
123
|
+
original_related_node_ids = await cls._get_profile_related_node_ids(db=db, obj=obj)
|
|
124
|
+
|
|
125
|
+
obj, mutation = await super()._call_mutate_update(
|
|
126
|
+
info=info, data=data, branch=branch, db=db, obj=obj, skip_uniqueness_check=skip_uniqueness_check
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
updated_attr_values = cls._get_profile_attr_values_map(obj=obj)
|
|
130
|
+
updated_related_node_ids = await cls._get_profile_related_node_ids(db=db, obj=obj)
|
|
131
|
+
|
|
132
|
+
if original_attr_values != updated_attr_values:
|
|
133
|
+
await cls._send_profile_refresh_workflows(
|
|
134
|
+
db=db, workflow_service=workflow_service, branch_name=branch.name, obj=obj
|
|
135
|
+
)
|
|
136
|
+
elif updated_related_node_ids != original_related_node_ids:
|
|
137
|
+
removed_node_ids = original_related_node_ids - updated_related_node_ids
|
|
138
|
+
added_node_ids = updated_related_node_ids - original_related_node_ids
|
|
139
|
+
await cls._send_profile_refresh_workflows(
|
|
140
|
+
db=db,
|
|
141
|
+
workflow_service=workflow_service,
|
|
142
|
+
branch_name=branch.name,
|
|
143
|
+
obj=obj,
|
|
144
|
+
node_ids=list(removed_node_ids) + list(added_node_ids),
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
return obj, mutation
|
|
148
|
+
|
|
149
|
+
@classmethod
|
|
150
|
+
async def _delete_obj(cls, graphql_context: GraphqlContext, branch: Branch, obj: Node) -> list[Node]:
|
|
151
|
+
db = graphql_context.db
|
|
152
|
+
workflow_service = graphql_context.active_service.workflow
|
|
153
|
+
related_node_ids = await cls._get_profile_related_node_ids(db=db, obj=obj)
|
|
154
|
+
deleted = await super()._delete_obj(graphql_context=graphql_context, branch=branch, obj=obj)
|
|
155
|
+
await cls._send_profile_refresh_workflows(
|
|
156
|
+
db=db, workflow_service=workflow_service, branch_name=branch.name, obj=obj, node_ids=list(related_node_ids)
|
|
157
|
+
)
|
|
158
|
+
return deleted
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
class ProfilesRefreshInput(InputObjectType):
|
|
162
|
+
id = String(required=False)
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
class InfrahubProfilesRefresh(Mutation):
|
|
166
|
+
class Arguments:
|
|
167
|
+
data = ProfilesRefreshInput(required=True)
|
|
168
|
+
context = ContextInput(required=False)
|
|
169
|
+
|
|
170
|
+
ok = Boolean()
|
|
171
|
+
|
|
172
|
+
@classmethod
|
|
173
|
+
@trace.get_tracer(__name__).start_as_current_span("profiles_refresh")
|
|
174
|
+
async def mutate(
|
|
175
|
+
cls,
|
|
176
|
+
root: dict, # noqa: ARG003
|
|
177
|
+
info: GraphQLResolveInfo,
|
|
178
|
+
data: ProfilesRefreshInput,
|
|
179
|
+
context: ContextInput | None = None, # noqa: ARG003
|
|
180
|
+
) -> Self:
|
|
181
|
+
graphql_context: GraphqlContext = info.context
|
|
182
|
+
db = graphql_context.db
|
|
183
|
+
branch = graphql_context.branch
|
|
184
|
+
obj = await NodeManager.get_one(
|
|
185
|
+
db=db,
|
|
186
|
+
branch=branch,
|
|
187
|
+
id=str(data.id),
|
|
188
|
+
include_source=True,
|
|
189
|
+
)
|
|
190
|
+
node_profiles_applier = NodeProfilesApplier(db=db, branch=branch)
|
|
191
|
+
updated_fields = await node_profiles_applier.apply_profiles(node=obj)
|
|
192
|
+
if updated_fields:
|
|
193
|
+
await obj.save(db=db, fields=updated_fields)
|
|
194
|
+
|
|
195
|
+
return cls(ok=True)
|
|
@@ -29,13 +29,14 @@ from infrahub.exceptions import BranchNotFoundError, PermissionDeniedError, Vali
|
|
|
29
29
|
from infrahub.graphql.mutations.main import InfrahubMutationMixin
|
|
30
30
|
from infrahub.graphql.types.enums import CheckType as GraphQLCheckType
|
|
31
31
|
from infrahub.graphql.types.task import TaskInfo
|
|
32
|
-
from infrahub.lock import InfrahubLock
|
|
32
|
+
from infrahub.lock import InfrahubLock
|
|
33
33
|
from infrahub.proposed_change.approval_revoker import do_revoke_approvals_on_updated_pcs
|
|
34
34
|
from infrahub.proposed_change.constants import ProposedChangeApprovalDecision, ProposedChangeState
|
|
35
35
|
from infrahub.proposed_change.models import RequestProposedChangePipeline
|
|
36
36
|
from infrahub.workers.dependencies import get_event_service
|
|
37
37
|
from infrahub.workflows.catalogue import PROPOSED_CHANGE_MERGE, REQUEST_PROPOSED_CHANGE_PIPELINE
|
|
38
38
|
|
|
39
|
+
from ...core.node.lock_utils import build_object_lock_name
|
|
39
40
|
from .main import InfrahubMutationOptions
|
|
40
41
|
|
|
41
42
|
if TYPE_CHECKING:
|
|
@@ -7,14 +7,11 @@ import httpx
|
|
|
7
7
|
from graphene import Boolean, Field, InputObjectType, Mutation, String
|
|
8
8
|
|
|
9
9
|
from infrahub import config
|
|
10
|
-
from infrahub.core.constants import InfrahubKind
|
|
10
|
+
from infrahub.core.constants import InfrahubKind
|
|
11
11
|
from infrahub.core.manager import NodeManager
|
|
12
|
-
from infrahub.core.protocols import
|
|
12
|
+
from infrahub.core.protocols import CoreReadOnlyRepository, CoreRepository
|
|
13
13
|
from infrahub.core.schema import NodeSchema
|
|
14
|
-
from infrahub.exceptions import ValidationError
|
|
15
14
|
from infrahub.git.models import (
|
|
16
|
-
GitRepositoryAdd,
|
|
17
|
-
GitRepositoryAddReadOnly,
|
|
18
15
|
GitRepositoryImportObjects,
|
|
19
16
|
GitRepositoryPullReadOnly,
|
|
20
17
|
)
|
|
@@ -22,15 +19,15 @@ from infrahub.graphql.types.common import IdentifierInput
|
|
|
22
19
|
from infrahub.log import get_logger
|
|
23
20
|
from infrahub.message_bus import messages
|
|
24
21
|
from infrahub.message_bus.messages.git_repository_connectivity import GitRepositoryConnectivityResponse
|
|
22
|
+
from infrahub.repositories.create_repository import RepositoryFinalizer
|
|
25
23
|
from infrahub.workflows.catalogue import (
|
|
26
24
|
GIT_REPOSITORIES_IMPORT_OBJECTS,
|
|
27
25
|
GIT_REPOSITORIES_PULL_READ_ONLY,
|
|
28
|
-
GIT_REPOSITORY_ADD,
|
|
29
|
-
GIT_REPOSITORY_ADD_READ_ONLY,
|
|
30
26
|
)
|
|
31
27
|
|
|
28
|
+
from ...core.node.create import create_node
|
|
32
29
|
from ..types.task import TaskInfo
|
|
33
|
-
from .main import InfrahubMutationMixin, InfrahubMutationOptions
|
|
30
|
+
from .main import InfrahubMutationMixin, InfrahubMutationOptions, build_graphql_response
|
|
34
31
|
|
|
35
32
|
if TYPE_CHECKING:
|
|
36
33
|
from graphql import GraphQLResolveInfo
|
|
@@ -63,86 +60,28 @@ class InfrahubRepositoryMutation(InfrahubMutationMixin, Mutation):
|
|
|
63
60
|
info: GraphQLResolveInfo,
|
|
64
61
|
data: InputObjectType,
|
|
65
62
|
branch: Branch,
|
|
66
|
-
database: InfrahubDatabase | None = None,
|
|
63
|
+
database: InfrahubDatabase | None = None,
|
|
67
64
|
override_data: dict[str, Any] | None = None,
|
|
68
65
|
) -> tuple[Node, Self]:
|
|
69
66
|
graphql_context: GraphqlContext = info.context
|
|
70
|
-
|
|
71
67
|
cleanup_payload(data)
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
if response.data.success is False:
|
|
88
|
-
await obj.delete(db=graphql_context.db)
|
|
89
|
-
raise ValidationError(response.data.message)
|
|
90
|
-
|
|
91
|
-
# If we are in the default branch, we set the sync status to Active
|
|
92
|
-
# If we are in another branch, we set the sync status to Staging
|
|
93
|
-
if branch.is_default:
|
|
94
|
-
obj.internal_status.value = RepositoryInternalStatus.ACTIVE.value
|
|
95
|
-
else:
|
|
96
|
-
obj.internal_status.value = RepositoryInternalStatus.STAGING.value
|
|
97
|
-
await obj.save(db=graphql_context.db)
|
|
98
|
-
|
|
99
|
-
# Create the new repository in the filesystem.
|
|
100
|
-
log.info("create_repository", name=obj.name.value)
|
|
101
|
-
authenticated_user = None
|
|
102
|
-
if graphql_context.account_session and graphql_context.account_session.authenticated:
|
|
103
|
-
authenticated_user = graphql_context.account_session.account_id
|
|
104
|
-
if obj.get_kind() == InfrahubKind.READONLYREPOSITORY:
|
|
105
|
-
obj = cast(CoreReadOnlyRepository, obj)
|
|
106
|
-
model = GitRepositoryAddReadOnly(
|
|
107
|
-
repository_id=obj.id,
|
|
108
|
-
repository_name=obj.name.value,
|
|
109
|
-
location=obj.location.value,
|
|
110
|
-
ref=obj.ref.value,
|
|
111
|
-
infrahub_branch_name=branch.name,
|
|
112
|
-
infrahub_branch_id=str(branch.get_uuid()),
|
|
113
|
-
internal_status=obj.internal_status.value,
|
|
114
|
-
created_by=authenticated_user,
|
|
115
|
-
)
|
|
116
|
-
if graphql_context.service:
|
|
117
|
-
await graphql_context.service.workflow.submit_workflow(
|
|
118
|
-
workflow=GIT_REPOSITORY_ADD_READ_ONLY,
|
|
119
|
-
context=graphql_context.get_context(),
|
|
120
|
-
parameters={"model": model},
|
|
121
|
-
)
|
|
122
|
-
|
|
123
|
-
else:
|
|
124
|
-
obj = cast(CoreRepository, obj)
|
|
125
|
-
git_repo_add_model = GitRepositoryAdd(
|
|
126
|
-
repository_id=obj.id,
|
|
127
|
-
repository_name=obj.name.value,
|
|
128
|
-
location=obj.location.value,
|
|
129
|
-
default_branch_name=obj.default_branch.value,
|
|
130
|
-
infrahub_branch_name=branch.name,
|
|
131
|
-
infrahub_branch_id=str(branch.get_uuid()),
|
|
132
|
-
internal_status=obj.internal_status.value,
|
|
133
|
-
created_by=authenticated_user,
|
|
134
|
-
)
|
|
135
|
-
|
|
136
|
-
if graphql_context.service:
|
|
137
|
-
await graphql_context.service.workflow.submit_workflow(
|
|
138
|
-
workflow=GIT_REPOSITORY_ADD,
|
|
139
|
-
context=graphql_context.get_context(),
|
|
140
|
-
parameters={"model": git_repo_add_model},
|
|
141
|
-
)
|
|
142
|
-
|
|
143
|
-
# TODO Validate that the creation of the repository went as expected
|
|
68
|
+
db = database or graphql_context.db
|
|
69
|
+
create_data = dict(data)
|
|
70
|
+
create_data.update(override_data or {})
|
|
71
|
+
obj = await create_node(data=create_data, db=db, branch=branch, schema=cls._meta.active_schema)
|
|
72
|
+
|
|
73
|
+
await RepositoryFinalizer(
|
|
74
|
+
account_session=graphql_context.active_account_session,
|
|
75
|
+
services=graphql_context.active_service,
|
|
76
|
+
context=graphql_context.get_context(),
|
|
77
|
+
).post_create(
|
|
78
|
+
obj=obj, # type: ignore
|
|
79
|
+
branch=branch,
|
|
80
|
+
db=db,
|
|
81
|
+
)
|
|
144
82
|
|
|
145
|
-
|
|
83
|
+
graphql_response = await build_graphql_response(info=info, db=db, obj=obj)
|
|
84
|
+
return obj, cls(**graphql_response)
|
|
146
85
|
|
|
147
86
|
@classmethod
|
|
148
87
|
async def mutate_update(
|
|
@@ -127,7 +127,7 @@ class InfrahubWebhookMutation(InfrahubMutationMixin, Mutation):
|
|
|
127
127
|
|
|
128
128
|
|
|
129
129
|
def _validate_input(graphql_context: GraphqlContext, branch: Branch, input_data: WebhookCreate) -> None:
|
|
130
|
-
if input_data.node_kind:
|
|
130
|
+
if input_data.node_kind and input_data.node_kind.value:
|
|
131
131
|
# Validate that the requested node_kind exists, will raise an error if not
|
|
132
132
|
graphql_context.db.schema.get(name=input_data.node_kind.value, branch=branch, duplicate=False)
|
|
133
133
|
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
from infrahub.core.timestamp import Timestamp
|
|
7
|
+
from infrahub.exceptions import InitializationError
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
import graphene
|
|
11
|
+
|
|
12
|
+
from infrahub.core.branch import Branch
|
|
13
|
+
from infrahub.core.schema.schema_branch import SchemaBranch
|
|
14
|
+
from infrahub.graphql.manager import GraphQLSchemaManager, InterfaceReference
|
|
15
|
+
|
|
16
|
+
from .mutations.main import InfrahubMutation
|
|
17
|
+
from .types import InfrahubObject
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass
|
|
21
|
+
class BranchDetails:
|
|
22
|
+
schema_changed_at: Timestamp
|
|
23
|
+
schema_hash: str
|
|
24
|
+
gql_manager: GraphQLSchemaManager
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@dataclass
|
|
28
|
+
class GraphQLSchemaRegistry:
|
|
29
|
+
_branch_details_by_hash: dict[str, BranchDetails] = field(default_factory=dict)
|
|
30
|
+
_branch_name_by_hash: dict[str, set[str]] = field(default_factory=dict)
|
|
31
|
+
_branch_hash_activation_by_branch_name: dict[str, dict[str, str]] = field(default_factory=dict)
|
|
32
|
+
_registered_interface_types: dict[str, type[graphene.Interface]] = field(default_factory=dict)
|
|
33
|
+
_reference_hash_schema_map: dict[str, set[str]] = field(default_factory=dict)
|
|
34
|
+
_registered_edge_types: dict[str, type[InfrahubObject]] = field(default_factory=dict)
|
|
35
|
+
_registered_paginated_types: dict[str, type[InfrahubObject]] = field(default_factory=dict)
|
|
36
|
+
_registered_input_types: dict[str, type[graphene.InputObjectType]] = field(default_factory=dict)
|
|
37
|
+
_registered_object_types: dict[str, type[InfrahubObject]] = field(default_factory=dict)
|
|
38
|
+
_registered_mutation_types: dict[str, type[InfrahubMutation]] = field(default_factory=dict)
|
|
39
|
+
|
|
40
|
+
_manager_class: type[GraphQLSchemaManager] | None = None
|
|
41
|
+
|
|
42
|
+
def _add_branch_hash(self, branch_name: str, schema_hash: str) -> None:
|
|
43
|
+
if schema_hash not in self._branch_name_by_hash:
|
|
44
|
+
self._branch_name_by_hash[schema_hash] = set()
|
|
45
|
+
|
|
46
|
+
self._branch_name_by_hash[schema_hash].add(branch_name)
|
|
47
|
+
|
|
48
|
+
def _register_manager(self, manager: type[GraphQLSchemaManager]) -> None:
|
|
49
|
+
self._manager_class = manager
|
|
50
|
+
|
|
51
|
+
@property
|
|
52
|
+
def manager(self) -> type[GraphQLSchemaManager]:
|
|
53
|
+
if self._manager_class:
|
|
54
|
+
return self._manager_class
|
|
55
|
+
raise InitializationError
|
|
56
|
+
|
|
57
|
+
def clear_cache(self) -> None:
|
|
58
|
+
"""Clear internal cache stored within this registry."""
|
|
59
|
+
self._branch_details_by_hash = {}
|
|
60
|
+
self._branch_name_by_hash = {}
|
|
61
|
+
self._branch_hash_activation_by_branch_name = {}
|
|
62
|
+
self._registered_interface_types = {}
|
|
63
|
+
self._reference_hash_schema_map = {}
|
|
64
|
+
self._registered_edge_types = {}
|
|
65
|
+
self._registered_paginated_types = {}
|
|
66
|
+
self._registered_input_types = {}
|
|
67
|
+
self._registered_object_types = {}
|
|
68
|
+
self._registered_mutation_types = {}
|
|
69
|
+
|
|
70
|
+
def _add_schema_to_reference_hash(self, reference_hash: str, schema_hash: str) -> None:
|
|
71
|
+
"""Add the schema hash to a map containing the referenced object.
|
|
72
|
+
|
|
73
|
+
The goal of this is to be able to see all schemas that use a given reference type,
|
|
74
|
+
once no schemas use a specific type it's safe to remove the type from the registry.
|
|
75
|
+
"""
|
|
76
|
+
if reference_hash not in self._reference_hash_schema_map:
|
|
77
|
+
self._reference_hash_schema_map[reference_hash] = set()
|
|
78
|
+
self._reference_hash_schema_map[reference_hash].add(schema_hash)
|
|
79
|
+
|
|
80
|
+
def get_edge_type(self, reference_hash: str, schema_hash: str) -> type[InfrahubObject] | None:
|
|
81
|
+
self._add_schema_to_reference_hash(reference_hash=reference_hash, schema_hash=schema_hash)
|
|
82
|
+
return self._registered_edge_types.get(reference_hash)
|
|
83
|
+
|
|
84
|
+
def set_edge_type(self, reference: type[InfrahubObject], reference_hash: str, schema_hash: str) -> None:
|
|
85
|
+
self._add_schema_to_reference_hash(reference_hash=reference_hash, schema_hash=schema_hash)
|
|
86
|
+
self._registered_edge_types[reference_hash] = reference
|
|
87
|
+
|
|
88
|
+
def get_input_type(self, reference_hash: str, schema_hash: str) -> type[graphene.InputObjectType] | None:
|
|
89
|
+
self._add_schema_to_reference_hash(reference_hash=reference_hash, schema_hash=schema_hash)
|
|
90
|
+
return self._registered_input_types.get(reference_hash)
|
|
91
|
+
|
|
92
|
+
def set_input_type(self, reference: type[graphene.InputObjectType], reference_hash: str, schema_hash: str) -> None:
|
|
93
|
+
self._add_schema_to_reference_hash(reference_hash=reference_hash, schema_hash=schema_hash)
|
|
94
|
+
self._registered_input_types[reference_hash] = reference
|
|
95
|
+
|
|
96
|
+
def get_interface_type(self, reference_hash: str, schema_hash: str) -> type[graphene.Interface] | None:
|
|
97
|
+
self._add_schema_to_reference_hash(reference_hash=reference_hash, schema_hash=schema_hash)
|
|
98
|
+
return self._registered_interface_types.get(reference_hash)
|
|
99
|
+
|
|
100
|
+
def set_interface_type(self, reference: InterfaceReference, schema_hash: str) -> None:
|
|
101
|
+
self._add_schema_to_reference_hash(reference_hash=reference.reference_hash, schema_hash=schema_hash)
|
|
102
|
+
self._registered_interface_types[reference.reference_hash] = reference.reference
|
|
103
|
+
|
|
104
|
+
def get_mutation_type(self, reference_hash: str, schema_hash: str) -> type[InfrahubMutation] | None:
|
|
105
|
+
self._add_schema_to_reference_hash(reference_hash=reference_hash, schema_hash=schema_hash)
|
|
106
|
+
return self._registered_mutation_types.get(reference_hash)
|
|
107
|
+
|
|
108
|
+
def set_mutation_type(self, reference: type[InfrahubMutation], reference_hash: str, schema_hash: str) -> None:
|
|
109
|
+
self._add_schema_to_reference_hash(reference_hash=reference_hash, schema_hash=schema_hash)
|
|
110
|
+
self._registered_mutation_types[reference_hash] = reference
|
|
111
|
+
|
|
112
|
+
def get_object_type(self, reference_hash: str, schema_hash: str) -> type[InfrahubObject] | None:
|
|
113
|
+
self._add_schema_to_reference_hash(reference_hash=reference_hash, schema_hash=schema_hash)
|
|
114
|
+
return self._registered_object_types.get(reference_hash)
|
|
115
|
+
|
|
116
|
+
def set_object_type(self, reference: type[InfrahubObject], reference_hash: str, schema_hash: str) -> None:
|
|
117
|
+
self._add_schema_to_reference_hash(reference_hash=reference_hash, schema_hash=schema_hash)
|
|
118
|
+
self._registered_object_types[reference_hash] = reference
|
|
119
|
+
|
|
120
|
+
def get_paginated_type(self, reference_hash: str, schema_hash: str) -> type[InfrahubObject] | None:
|
|
121
|
+
self._add_schema_to_reference_hash(reference_hash=reference_hash, schema_hash=schema_hash)
|
|
122
|
+
return self._registered_paginated_types.get(reference_hash)
|
|
123
|
+
|
|
124
|
+
def set_paginated_type(self, reference: type[InfrahubObject], reference_hash: str, schema_hash: str) -> None:
|
|
125
|
+
self._add_schema_to_reference_hash(reference_hash=reference_hash, schema_hash=schema_hash)
|
|
126
|
+
self._registered_paginated_types[reference_hash] = reference
|
|
127
|
+
|
|
128
|
+
def purge_inactive(self, active_branches: list[str]) -> set[str]:
|
|
129
|
+
"""Return inactive branches that were purged"""
|
|
130
|
+
inactive_branches: set[str] = set()
|
|
131
|
+
for schema_hash in list(self._branch_name_by_hash.keys()):
|
|
132
|
+
branches = list(self._branch_name_by_hash[schema_hash])
|
|
133
|
+
for branch in branches:
|
|
134
|
+
if branch not in active_branches and branch in self._branch_name_by_hash[schema_hash]:
|
|
135
|
+
inactive_branches.add(branch)
|
|
136
|
+
self._branch_name_by_hash[schema_hash].discard(branch)
|
|
137
|
+
|
|
138
|
+
for schema_hash in list(self._branch_name_by_hash.keys()):
|
|
139
|
+
if not self._branch_name_by_hash[schema_hash]:
|
|
140
|
+
# If no remaining branch is using the schema remove it completely
|
|
141
|
+
del self._branch_name_by_hash[schema_hash]
|
|
142
|
+
del self._branch_details_by_hash[schema_hash]
|
|
143
|
+
|
|
144
|
+
return inactive_branches
|
|
145
|
+
|
|
146
|
+
def cache_branch(self, branch: Branch, schema_branch: SchemaBranch, schema_hash: str) -> BranchDetails:
|
|
147
|
+
branch_details = BranchDetails(
|
|
148
|
+
schema_changed_at=Timestamp(branch.schema_changed_at) if branch.schema_changed_at else Timestamp(),
|
|
149
|
+
schema_hash=schema_hash,
|
|
150
|
+
gql_manager=self.manager(schema=schema_branch),
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
self._branch_details_by_hash[schema_hash] = branch_details
|
|
154
|
+
|
|
155
|
+
return branch_details
|
|
156
|
+
|
|
157
|
+
def get_manager_for_branch(self, branch: Branch, schema_branch: SchemaBranch) -> GraphQLSchemaManager:
|
|
158
|
+
if branch.schema_hash:
|
|
159
|
+
schema_hash = branch.schema_hash.main
|
|
160
|
+
else:
|
|
161
|
+
schema_hash = schema_branch.get_hash()
|
|
162
|
+
|
|
163
|
+
if schema_hash in self._branch_details_by_hash:
|
|
164
|
+
branch_details = self._branch_details_by_hash[schema_hash]
|
|
165
|
+
else:
|
|
166
|
+
branch_details = self.cache_branch(branch=branch, schema_branch=schema_branch, schema_hash=schema_hash)
|
|
167
|
+
|
|
168
|
+
self._add_branch_hash(branch_name=branch.name, schema_hash=schema_hash)
|
|
169
|
+
|
|
170
|
+
return branch_details.gql_manager
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
registry = GraphQLSchemaRegistry()
|
infrahub/graphql/schema.py
CHANGED
|
@@ -15,11 +15,12 @@ from .mutations.branch import (
|
|
|
15
15
|
BranchUpdate,
|
|
16
16
|
BranchValidate,
|
|
17
17
|
)
|
|
18
|
-
from .mutations.computed_attribute import UpdateComputedAttribute
|
|
18
|
+
from .mutations.computed_attribute import RecomputeComputedAttribute, UpdateComputedAttribute
|
|
19
19
|
from .mutations.convert_object_type import ConvertObjectType
|
|
20
20
|
from .mutations.diff import DiffUpdateMutation
|
|
21
21
|
from .mutations.diff_conflict import ResolveDiffConflict
|
|
22
22
|
from .mutations.generator import GeneratorDefinitionRequestRun
|
|
23
|
+
from .mutations.profile import InfrahubProfilesRefresh
|
|
23
24
|
from .mutations.proposed_change import (
|
|
24
25
|
ProposedChangeCheckForApprovalRevoke,
|
|
25
26
|
ProposedChangeMerge,
|
|
@@ -113,6 +114,7 @@ class InfrahubBaseMutation(ObjectType):
|
|
|
113
114
|
InfrahubRepositoryProcess = ProcessRepository.Field()
|
|
114
115
|
InfrahubRepositoryConnectivity = ValidateRepositoryConnectivity.Field()
|
|
115
116
|
InfrahubUpdateComputedAttribute = UpdateComputedAttribute.Field()
|
|
117
|
+
InfrahubRecomputeComputedAttribute = RecomputeComputedAttribute.Field()
|
|
116
118
|
|
|
117
119
|
RelationshipAdd = RelationshipAdd.Field()
|
|
118
120
|
RelationshipRemove = RelationshipRemove.Field()
|
|
@@ -124,3 +126,4 @@ class InfrahubBaseMutation(ObjectType):
|
|
|
124
126
|
|
|
125
127
|
ConvertObjectType = ConvertObjectType.Field()
|
|
126
128
|
CoreProposedChangeCheckForApprovalRevoke = ProposedChangeCheckForApprovalRevoke.Field()
|
|
129
|
+
InfrahubProfilesRefresh = InfrahubProfilesRefresh.Field()
|