Skip to content

Commit 84f1c09

Browse files
authored
Merge pull request #5874 from opsmill/pog-external-graphql-context-IFC-1303
Provide option to set GraphQL context information as input to mutations
2 parents 50b47f3 + 5e0857b commit 84f1c09

File tree

12 files changed

+215
-12
lines changed

12 files changed

+215
-12
lines changed
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
from __future__ import annotations
2+
3+
from typing import TYPE_CHECKING
4+
5+
from infrahub.core.constants import InfrahubKind
6+
from infrahub.core.manager import NodeManager
7+
from infrahub.exceptions import NodeNotFoundError, ValidationError
8+
9+
if TYPE_CHECKING:
10+
from .initialization import GraphqlContext
11+
from .types.context import ContextInput
12+
13+
14+
async def apply_external_context(graphql_context: GraphqlContext, context_input: ContextInput | None) -> None:
15+
"""Applies context provided by an external mutation to the GraphQL context"""
16+
if not context_input or not context_input.account:
17+
return
18+
19+
try:
20+
account = await NodeManager.get_one_by_id_or_default_filter(
21+
db=graphql_context.db, id=str(context_input.account.id), kind=InfrahubKind.GENERICACCOUNT
22+
)
23+
except NodeNotFoundError as exc:
24+
raise ValidationError(input_value="Unable to set context for account that doesn't exist") from exc
25+
26+
graphql_context.active_account_session.account_id = account.id

backend/infrahub/graphql/manager.py

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
)
6161
from .types.attribute import BaseAttribute as BaseAttributeType
6262
from .types.attribute import TextAttributeType
63+
from .types.context import ContextInput
6364
from .types.event import EVENT_TYPES
6465

6566
if TYPE_CHECKING:
@@ -809,9 +810,7 @@ def generate_graphql_mutation_create(
809810
meta_attrs: dict[str, Any] = {"schema": schema, "name": name, "description": schema.description}
810811
main_attrs["Meta"] = type("Meta", (object,), meta_attrs)
811812

812-
args_attrs = {
813-
"data": input_type(required=True),
814-
}
813+
args_attrs = {"data": input_type(required=True), "context": ContextInput(required=False)}
815814
main_attrs["Arguments"] = type("Arguments", (object,), args_attrs)
816815

817816
return type(name, (base_class,), main_attrs)
@@ -832,9 +831,7 @@ def generate_graphql_mutation_update(
832831
meta_attrs: dict[str, Any] = {"schema": schema, "name": name, "description": schema.description}
833832
main_attrs["Meta"] = type("Meta", (object,), meta_attrs)
834833

835-
args_attrs = {
836-
"data": input_type(required=True),
837-
}
834+
args_attrs = {"data": input_type(required=True), "context": ContextInput(required=False)}
838835
main_attrs["Arguments"] = type("Arguments", (object,), args_attrs)
839836

840837
return type(name, (base_class,), main_attrs)
@@ -851,9 +848,7 @@ def generate_graphql_mutation_delete(
851848
meta_attrs = {"schema": schema, "name": name, "description": schema.description}
852849
main_attrs["Meta"] = type("Meta", (object,), meta_attrs)
853850

854-
args_attrs: dict[str, Any] = {
855-
"data": DeleteInput(required=True),
856-
}
851+
args_attrs: dict[str, Any] = {"data": DeleteInput(required=True), "context": ContextInput(required=False)}
857852
main_attrs["Arguments"] = type("Arguments", (object,), args_attrs)
858853

859854
return type(name, (base_class,), main_attrs)

backend/infrahub/graphql/mutations/branch.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99

1010
from infrahub.core.branch import Branch
1111
from infrahub.database import retry_db_transaction
12+
from infrahub.graphql.context import apply_external_context
13+
from infrahub.graphql.types.context import ContextInput
1214
from infrahub.log import get_logger
1315
from infrahub.workflows.catalogue import (
1416
BRANCH_CREATE,
@@ -44,6 +46,7 @@ class BranchCreateInput(InputObjectType):
4446
class BranchCreate(Mutation):
4547
class Arguments:
4648
data = BranchCreateInput(required=True)
49+
context = ContextInput(required=False)
4750
background_execution = Boolean(required=False, deprecation_reason="Please use `wait_until_completion` instead")
4851
wait_until_completion = Boolean(required=False)
4952

@@ -58,13 +61,15 @@ async def mutate(
5861
root: dict, # noqa: ARG003
5962
info: GraphQLResolveInfo,
6063
data: BranchCreateInput,
64+
context: ContextInput | None = None,
6165
background_execution: bool = False,
6266
wait_until_completion: bool = True,
6367
) -> Self:
6468
graphql_context: GraphqlContext = info.context
6569
task: dict | None = None
6670

6771
model = BranchCreateModel(**data)
72+
await apply_external_context(graphql_context=graphql_context, context_input=context)
6873

6974
if background_execution or not wait_until_completion:
7075
workflow = await graphql_context.active_service.workflow.submit_workflow(
@@ -96,6 +101,7 @@ class BranchUpdateInput(InputObjectType):
96101
class BranchDelete(Mutation):
97102
class Arguments:
98103
data = BranchNameInput(required=True)
104+
context = ContextInput(required=False)
99105
wait_until_completion = Boolean(required=False)
100106

101107
ok = Boolean()
@@ -107,10 +113,12 @@ async def mutate(
107113
root: dict, # noqa: ARG003
108114
info: GraphQLResolveInfo,
109115
data: BranchNameInput,
116+
context: ContextInput | None = None,
110117
wait_until_completion: bool = True,
111118
) -> Self:
112119
graphql_context: GraphqlContext = info.context
113120
obj = await Branch.get_by_name(db=graphql_context.db, name=str(data.name))
121+
await apply_external_context(graphql_context=graphql_context, context_input=context)
114122

115123
if wait_until_completion:
116124
await graphql_context.active_service.workflow.execute_workflow(
@@ -127,15 +135,23 @@ async def mutate(
127135
class BranchUpdate(Mutation):
128136
class Arguments:
129137
data = BranchUpdateInput(required=True)
138+
context = ContextInput(required=False)
130139

131140
ok = Boolean()
132141

133142
@classmethod
134143
@retry_db_transaction(name="branch_update")
135-
async def mutate(cls, root: dict, info: GraphQLResolveInfo, data: BranchNameInput) -> Self: # noqa: ARG003
144+
async def mutate(
145+
cls,
146+
root: dict, # noqa: ARG003
147+
info: GraphQLResolveInfo,
148+
data: BranchNameInput,
149+
context: ContextInput | None = None,
150+
) -> Self:
136151
graphql_context: GraphqlContext = info.context
137152

138153
obj = await Branch.get_by_name(db=graphql_context.db, name=data["name"])
154+
await apply_external_context(graphql_context=graphql_context, context_input=context)
139155

140156
to_extract = ["description"]
141157
for field_name in to_extract:
@@ -151,6 +167,7 @@ async def mutate(cls, root: dict, info: GraphQLResolveInfo, data: BranchNameInpu
151167
class BranchRebase(Mutation):
152168
class Arguments:
153169
data = BranchNameInput(required=True)
170+
context = ContextInput(required=False)
154171
wait_until_completion = Boolean(required=False)
155172

156173
ok = Boolean()
@@ -163,11 +180,13 @@ async def mutate(
163180
root: dict, # noqa: ARG003
164181
info: GraphQLResolveInfo,
165182
data: BranchNameInput,
183+
context: ContextInput | None = None,
166184
wait_until_completion: bool = True,
167185
) -> Self:
168186
graphql_context: GraphqlContext = info.context
169187

170188
obj = await Branch.get_by_name(db=graphql_context.db, name=str(data.name))
189+
await apply_external_context(graphql_context=graphql_context, context_input=context)
171190
task: dict | None = None
172191

173192
if wait_until_completion:
@@ -192,6 +211,7 @@ async def mutate(
192211
class BranchValidate(Mutation):
193212
class Arguments:
194213
data = BranchNameInput(required=True)
214+
context = ContextInput(required=False)
195215
wait_until_completion = Boolean(required=False)
196216

197217
ok = Boolean()
@@ -205,11 +225,13 @@ async def mutate(
205225
root: dict, # noqa: ARG003
206226
info: GraphQLResolveInfo,
207227
data: BranchNameInput,
228+
context: ContextInput | None = None,
208229
wait_until_completion: bool = True,
209230
) -> Self:
210231
graphql_context: GraphqlContext = info.context
211232

212233
obj = await Branch.get_by_name(db=graphql_context.db, name=str(data.name))
234+
await apply_external_context(graphql_context=graphql_context, context_input=context)
213235
task: dict | None = None
214236
ok = True
215237

@@ -231,6 +253,7 @@ async def mutate(
231253
class BranchMerge(Mutation):
232254
class Arguments:
233255
data = BranchNameInput(required=True)
256+
context = ContextInput(required=False)
234257
wait_until_completion = Boolean(required=False)
235258

236259
ok = Boolean()
@@ -243,11 +266,13 @@ async def mutate(
243266
root: dict, # noqa: ARG003
244267
info: GraphQLResolveInfo,
245268
data: BranchNameInput,
269+
context: ContextInput | None = None,
246270
wait_until_completion: bool = True,
247271
) -> Self:
248272
branch_name = data["name"]
249273
task: dict | None = None
250274
graphql_context: GraphqlContext = info.context
275+
await apply_external_context(graphql_context=graphql_context, context_input=context)
251276

252277
if wait_until_completion:
253278
await graphql_context.active_service.workflow.execute_workflow(

backend/infrahub/graphql/mutations/computed_attribute.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
from infrahub.events import EventMeta
1313
from infrahub.events.node_action import NodeMutatedEvent
1414
from infrahub.exceptions import NodeNotFoundError, ValidationError
15+
from infrahub.graphql.context import apply_external_context
16+
from infrahub.graphql.types.context import ContextInput
1517
from infrahub.log import get_log_data
1618
from infrahub.worker import WORKER_IDENTITY
1719

@@ -31,6 +33,7 @@ class InfrahubComputedAttributeUpdateInput(InputObjectType):
3133
class UpdateComputedAttribute(Mutation):
3234
class Arguments:
3335
data = InfrahubComputedAttributeUpdateInput(required=True)
36+
context = ContextInput(required=False)
3437

3538
ok = Boolean()
3639

@@ -41,6 +44,7 @@ async def mutate(
4144
_: dict,
4245
info: GraphQLResolveInfo,
4346
data: InfrahubComputedAttributeUpdateInput,
47+
context: ContextInput | None = None,
4448
) -> UpdateComputedAttribute:
4549
graphql_context: GraphqlContext = info.context
4650
node_schema = registry.schema.get_node_schema(
@@ -63,6 +67,7 @@ async def mutate(
6367
else PermissionDecision.ALLOW_OTHER.value,
6468
)
6569
)
70+
await apply_external_context(graphql_context=graphql_context, context_input=context)
6671

6772
if not (
6873
target_node := await NodeManager.get_one(

backend/infrahub/graphql/mutations/diff.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
from infrahub.core.timestamp import Timestamp
1212
from infrahub.dependencies.registry import get_component_registry
1313
from infrahub.exceptions import ValidationError
14+
from infrahub.graphql.context import apply_external_context
15+
from infrahub.graphql.types.context import ContextInput
1416
from infrahub.workflows.catalogue import DIFF_UPDATE
1517

1618
from ..types.task import TaskInfo
@@ -30,6 +32,7 @@ class DiffUpdateInput(InputObjectType):
3032
class DiffUpdateMutation(Mutation):
3133
class Arguments:
3234
data = DiffUpdateInput(required=True)
35+
context = ContextInput(required=False)
3336
wait_until_completion = Boolean(required=False)
3437

3538
ok = Boolean()
@@ -41,9 +44,11 @@ async def mutate(
4144
root: dict, # noqa: ARG003
4245
info: GraphQLResolveInfo,
4346
data: DiffUpdateInput,
47+
context: ContextInput | None = None,
4448
wait_until_completion: bool = False,
4549
) -> dict[str, bool | dict[str, str]]:
4650
graphql_context: GraphqlContext = info.context
51+
await apply_external_context(graphql_context=graphql_context, context_input=context)
4752

4853
if data.wait_for_completion is True:
4954
wait_until_completion = True

backend/infrahub/graphql/mutations/diff_conflict.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@
1111
from infrahub.database import retry_db_transaction
1212
from infrahub.dependencies.registry import get_component_registry
1313
from infrahub.exceptions import ProcessingError
14+
from infrahub.graphql.context import apply_external_context
1415
from infrahub.graphql.enums import ConflictSelection as GraphQlConflictSelection
16+
from infrahub.graphql.types.context import ContextInput
1517

1618
if TYPE_CHECKING:
1719
from graphql import GraphQLResolveInfo
@@ -29,6 +31,7 @@ class ResolveDiffConflictInput(InputObjectType):
2931
class ResolveDiffConflict(Mutation):
3032
class Arguments:
3133
data = ResolveDiffConflictInput(required=True)
34+
context = ContextInput(required=False)
3235

3336
ok = Boolean()
3437

@@ -39,8 +42,10 @@ async def mutate(
3942
root: dict, # noqa: ARG003
4043
info: GraphQLResolveInfo,
4144
data: ResolveDiffConflictInput,
45+
context: ContextInput | None = None,
4246
) -> ResolveDiffConflict:
4347
graphql_context: GraphqlContext = info.context
48+
await apply_external_context(graphql_context=graphql_context, context_input=context)
4449

4550
component_registry = get_component_registry()
4651
diff_repo = await component_registry.get_component(

backend/infrahub/graphql/mutations/generator.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
from infrahub.core.manager import NodeManager
88
from infrahub.generators.models import ProposedChangeGeneratorDefinition, RequestGeneratorDefinitionRun
9+
from infrahub.graphql.context import apply_external_context
10+
from infrahub.graphql.types.context import ContextInput
911
from infrahub.graphql.types.task import TaskInfo
1012
from infrahub.workflows.catalogue import REQUEST_GENERATOR_DEFINITION_RUN
1113

@@ -23,6 +25,7 @@ class GeneratorDefinitionRequestRunInput(InputObjectType):
2325
class GeneratorDefinitionRequestRun(Mutation):
2426
class Arguments:
2527
data = GeneratorDefinitionRequestRunInput(required=True)
28+
context = ContextInput(required=False)
2629
wait_until_completion = Boolean(required=False)
2730

2831
ok = Boolean()
@@ -34,11 +37,12 @@ async def mutate(
3437
root: dict, # noqa: ARG003
3538
info: GraphQLResolveInfo,
3639
data: GeneratorDefinitionRequestRunInput,
40+
context: ContextInput | None = None,
3741
wait_until_completion: bool = True,
3842
) -> GeneratorDefinitionRequestRun:
3943
graphql_context: GraphqlContext = info.context
4044
db = graphql_context.db
41-
45+
await apply_external_context(graphql_context=graphql_context, context_input=context)
4246
generator_definition = await NodeManager.get_one(
4347
id=str(data.id), db=db, branch=graphql_context.branch, prefetch_relationships=True, raise_on_error=True
4448
)

backend/infrahub/graphql/mutations/main.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
from infrahub.dependencies.registry import get_component_registry
2525
from infrahub.events import EventMeta, NodeMutatedEvent
2626
from infrahub.exceptions import ValidationError
27+
from infrahub.graphql.context import apply_external_context
2728
from infrahub.lock import InfrahubMultiLock, build_object_lock_name
2829
from infrahub.log import get_log_data, get_logger
2930
from infrahub.worker import WORKER_IDENTITY
@@ -40,6 +41,7 @@
4041
from infrahub.core.relationship.model import RelationshipManager
4142
from infrahub.core.schema.schema_branch import SchemaBranch
4243
from infrahub.database import InfrahubDatabase
44+
from infrahub.graphql.types.context import ContextInput
4345

4446
from ..initialization import GraphqlContext
4547
from .node_getter.interface import MutationNodeGetterInterface
@@ -66,8 +68,17 @@ class InfrahubMutationOptions(MutationOptions):
6668

6769
class InfrahubMutationMixin:
6870
@classmethod
69-
async def mutate(cls, root: dict, info: GraphQLResolveInfo, data: InputObjectType, *args: Any, **kwargs): # noqa: ARG003
71+
async def mutate(
72+
cls,
73+
root: dict, # noqa: ARG003
74+
info: GraphQLResolveInfo,
75+
data: InputObjectType,
76+
context: ContextInput | None = None,
77+
*args: Any, # noqa: ARG003
78+
**kwargs,
79+
):
7080
graphql_context: GraphqlContext = info.context
81+
await apply_external_context(graphql_context=graphql_context, context_input=context)
7182

7283
obj = None
7384
mutation = None

0 commit comments

Comments
 (0)