infrahub-server 1.1.6__py3-none-any.whl → 1.2.0rc0__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.
Files changed (346) hide show
  1. infrahub/api/artifact.py +16 -4
  2. infrahub/api/dependencies.py +8 -0
  3. infrahub/api/oauth2.py +0 -1
  4. infrahub/api/oidc.py +0 -1
  5. infrahub/api/query.py +18 -7
  6. infrahub/api/schema.py +32 -6
  7. infrahub/api/transformation.py +12 -5
  8. infrahub/{message_bus/messages/check_artifact_create.py → artifacts/models.py} +2 -4
  9. infrahub/{message_bus/operations/check/artifact.py → artifacts/tasks.py} +26 -25
  10. infrahub/cli/__init__.py +0 -2
  11. infrahub/cli/db.py +6 -7
  12. infrahub/cli/events.py +8 -3
  13. infrahub/cli/git_agent.py +9 -7
  14. infrahub/cli/tasks.py +4 -6
  15. infrahub/computed_attribute/tasks.py +63 -17
  16. infrahub/computed_attribute/triggers.py +90 -0
  17. infrahub/config.py +1 -1
  18. infrahub/context.py +39 -0
  19. infrahub/core/account.py +5 -8
  20. infrahub/core/attribute.py +53 -21
  21. infrahub/core/branch/models.py +4 -4
  22. infrahub/core/branch/tasks.py +89 -130
  23. infrahub/core/changelog/__init__.py +0 -0
  24. infrahub/core/changelog/diff.py +232 -0
  25. infrahub/core/changelog/models.py +488 -0
  26. infrahub/core/constants/__init__.py +19 -2
  27. infrahub/core/constants/infrahubkind.py +1 -0
  28. infrahub/core/diff/combiner.py +12 -8
  29. infrahub/core/diff/coordinator.py +49 -70
  30. infrahub/core/diff/data_check_synchronizer.py +86 -7
  31. infrahub/core/diff/enricher/aggregated.py +3 -3
  32. infrahub/core/diff/enricher/cardinality_one.py +2 -7
  33. infrahub/core/diff/enricher/hierarchy.py +5 -3
  34. infrahub/core/diff/enricher/labels.py +14 -4
  35. infrahub/core/diff/enricher/path_identifier.py +3 -9
  36. infrahub/core/diff/enricher/summary_counts.py +3 -1
  37. infrahub/core/diff/merger/merger.py +8 -4
  38. infrahub/core/diff/model/path.py +47 -29
  39. infrahub/core/diff/query/all_conflicts.py +6 -3
  40. infrahub/core/diff/query/artifact.py +1 -1
  41. infrahub/core/diff/query/delete_query.py +1 -1
  42. infrahub/core/diff/query/diff_get.py +3 -2
  43. infrahub/core/diff/query/diff_summary.py +1 -1
  44. infrahub/core/diff/query/field_specifiers.py +3 -1
  45. infrahub/core/diff/query/field_summary.py +3 -2
  46. infrahub/core/diff/query/filters.py +12 -1
  47. infrahub/core/diff/query/get_conflict_query.py +1 -1
  48. infrahub/core/diff/query/has_conflicts_query.py +6 -3
  49. infrahub/core/diff/query/merge.py +3 -3
  50. infrahub/core/diff/query/{drop_tracking_id.py → merge_tracking_id.py} +4 -4
  51. infrahub/core/diff/query/roots_metadata.py +9 -2
  52. infrahub/core/diff/query/save.py +151 -66
  53. infrahub/core/diff/query/summary_counts_enricher.py +220 -0
  54. infrahub/core/diff/query/time_range_query.py +3 -2
  55. infrahub/core/diff/query/update_conflict_query.py +1 -1
  56. infrahub/core/diff/query_parser.py +49 -24
  57. infrahub/core/diff/repository/deserializer.py +24 -25
  58. infrahub/core/diff/repository/repository.py +76 -20
  59. infrahub/core/diff/tasks.py +9 -8
  60. infrahub/core/enums.py +1 -1
  61. infrahub/core/integrity/object_conflict/conflict_recorder.py +1 -1
  62. infrahub/core/ipam/reconciler.py +1 -1
  63. infrahub/core/ipam/tasks.py +2 -3
  64. infrahub/core/manager.py +18 -13
  65. infrahub/core/merge.py +5 -2
  66. infrahub/core/migrations/graph/m001_add_version_to_graph.py +1 -1
  67. infrahub/core/migrations/graph/m002_attribute_is_default.py +2 -2
  68. infrahub/core/migrations/graph/m003_relationship_parent_optional.py +2 -2
  69. infrahub/core/migrations/graph/m004_add_attr_documentation.py +1 -1
  70. infrahub/core/migrations/graph/m005_add_rel_read_only.py +1 -1
  71. infrahub/core/migrations/graph/m006_add_rel_on_delete.py +1 -1
  72. infrahub/core/migrations/graph/m007_add_rel_allow_override.py +1 -1
  73. infrahub/core/migrations/graph/m008_add_human_friendly_id.py +1 -1
  74. infrahub/core/migrations/graph/m009_add_generate_profile_attr.py +1 -1
  75. infrahub/core/migrations/graph/m010_add_generate_profile_attr_generic.py +1 -1
  76. infrahub/core/migrations/graph/m011_remove_profile_relationship_schema.py +2 -2
  77. infrahub/core/migrations/graph/m012_convert_account_generic.py +12 -23
  78. infrahub/core/migrations/graph/m013_convert_git_password_credential.py +7 -11
  79. infrahub/core/migrations/graph/m014_remove_index_attr_value.py +2 -2
  80. infrahub/core/migrations/graph/m015_diff_format_update.py +1 -1
  81. infrahub/core/migrations/graph/m016_diff_delete_bug_fix.py +1 -1
  82. infrahub/core/migrations/graph/m017_add_core_profile.py +1 -1
  83. infrahub/core/migrations/graph/m018_uniqueness_nulls.py +2 -2
  84. infrahub/core/migrations/query/attribute_add.py +1 -1
  85. infrahub/core/migrations/query/attribute_rename.py +1 -1
  86. infrahub/core/migrations/query/delete_element_in_schema.py +1 -1
  87. infrahub/core/migrations/query/node_duplicate.py +1 -1
  88. infrahub/core/migrations/query/relationship_duplicate.py +1 -1
  89. infrahub/core/migrations/query/schema_attribute_update.py +1 -1
  90. infrahub/core/migrations/schema/node_attribute_remove.py +1 -1
  91. infrahub/core/migrations/schema/node_remove.py +1 -1
  92. infrahub/core/migrations/schema/tasks.py +5 -5
  93. infrahub/core/migrations/shared.py +4 -4
  94. infrahub/core/models.py +7 -8
  95. infrahub/core/node/__init__.py +161 -40
  96. infrahub/core/node/base.py +1 -1
  97. infrahub/core/node/constraints/grouped_uniqueness.py +9 -2
  98. infrahub/core/node/delete_validator.py +4 -4
  99. infrahub/core/node/ipam.py +13 -8
  100. infrahub/core/node/permissions.py +4 -0
  101. infrahub/core/node/resource_manager/ip_prefix_pool.py +8 -5
  102. infrahub/core/node/standard.py +3 -5
  103. infrahub/core/property.py +1 -1
  104. infrahub/core/protocols.py +4 -0
  105. infrahub/core/protocols_base.py +4 -2
  106. infrahub/core/query/__init__.py +2 -5
  107. infrahub/core/query/attribute.py +9 -9
  108. infrahub/core/query/branch.py +5 -5
  109. infrahub/core/query/delete.py +1 -1
  110. infrahub/core/query/diff.py +45 -7
  111. infrahub/core/query/ipam.py +4 -4
  112. infrahub/core/query/node.py +19 -14
  113. infrahub/core/query/relationship.py +10 -11
  114. infrahub/core/query/resource_manager.py +13 -11
  115. infrahub/core/query/standard_node.py +6 -6
  116. infrahub/core/query/task.py +3 -3
  117. infrahub/core/query/task_log.py +1 -1
  118. infrahub/core/query/utils.py +5 -5
  119. infrahub/core/registry.py +0 -2
  120. infrahub/core/relationship/constraints/count.py +1 -1
  121. infrahub/core/relationship/constraints/peer_kind.py +1 -1
  122. infrahub/core/relationship/model.py +66 -26
  123. infrahub/core/schema/__init__.py +6 -4
  124. infrahub/core/schema/basenode_schema.py +1 -3
  125. infrahub/core/schema/definitions/core.py +14 -2
  126. infrahub/core/schema/definitions/internal.py +16 -0
  127. infrahub/core/schema/generated/genericnode_schema.py +5 -0
  128. infrahub/core/schema/generated/node_schema.py +5 -0
  129. infrahub/core/schema/generic_schema.py +5 -1
  130. infrahub/core/schema/manager.py +45 -42
  131. infrahub/core/schema/node_schema.py +4 -0
  132. infrahub/core/schema/profile_schema.py +4 -0
  133. infrahub/core/schema/relationship_schema.py +2 -2
  134. infrahub/core/schema/schema_branch.py +248 -14
  135. infrahub/core/schema/template_schema.py +36 -0
  136. infrahub/core/task/user_task.py +7 -5
  137. infrahub/core/timestamp.py +1 -1
  138. infrahub/core/utils.py +3 -2
  139. infrahub/core/validators/attribute/choices.py +1 -1
  140. infrahub/core/validators/attribute/enum.py +1 -1
  141. infrahub/core/validators/attribute/kind.py +1 -1
  142. infrahub/core/validators/attribute/length.py +1 -1
  143. infrahub/core/validators/attribute/optional.py +1 -1
  144. infrahub/core/validators/attribute/regex.py +1 -1
  145. infrahub/core/validators/attribute/unique.py +1 -1
  146. infrahub/core/validators/checks_runner.py +37 -0
  147. infrahub/core/validators/node/generate_profile.py +1 -1
  148. infrahub/core/validators/node/hierarchy.py +1 -1
  149. infrahub/core/validators/query.py +1 -1
  150. infrahub/core/validators/relationship/count.py +1 -1
  151. infrahub/core/validators/relationship/optional.py +1 -1
  152. infrahub/core/validators/relationship/peer.py +1 -1
  153. infrahub/core/validators/tasks.py +8 -6
  154. infrahub/core/validators/uniqueness/query.py +20 -17
  155. infrahub/database/__init__.py +15 -2
  156. infrahub/database/memgraph.py +1 -1
  157. infrahub/dependencies/builder/constraint/grouped/node_runner.py +0 -2
  158. infrahub/dependencies/builder/diff/combiner.py +1 -1
  159. infrahub/dependencies/builder/diff/conflicts_enricher.py +1 -1
  160. infrahub/dependencies/builder/diff/coordinator.py +0 -2
  161. infrahub/dependencies/builder/diff/deserializer.py +1 -1
  162. infrahub/dependencies/builder/diff/enricher/summary_counts.py +1 -1
  163. infrahub/events/branch_action.py +47 -21
  164. infrahub/events/group_action.py +73 -0
  165. infrahub/events/models.py +159 -51
  166. infrahub/events/node_action.py +74 -8
  167. infrahub/events/repository_action.py +8 -8
  168. infrahub/events/schema_action.py +21 -8
  169. infrahub/generators/tasks.py +12 -13
  170. infrahub/git/base.py +3 -5
  171. infrahub/git/constants.py +0 -1
  172. infrahub/git/integrator.py +36 -35
  173. infrahub/git/repository.py +7 -8
  174. infrahub/git/tasks.py +43 -107
  175. infrahub/git_credential/helper.py +2 -3
  176. infrahub/graphql/analyzer.py +572 -11
  177. infrahub/graphql/app.py +34 -26
  178. infrahub/graphql/auth/query_permission_checker/anonymous_checker.py +5 -5
  179. infrahub/graphql/auth/query_permission_checker/default_branch_checker.py +4 -4
  180. infrahub/graphql/auth/query_permission_checker/merge_operation_checker.py +4 -4
  181. infrahub/graphql/auth/query_permission_checker/object_permission_checker.py +28 -35
  182. infrahub/graphql/auth/query_permission_checker/super_admin_checker.py +5 -5
  183. infrahub/graphql/enums.py +1 -1
  184. infrahub/graphql/initialization.py +5 -1
  185. infrahub/graphql/loaders/node.py +2 -2
  186. infrahub/graphql/manager.py +59 -54
  187. infrahub/graphql/mutations/account.py +20 -13
  188. infrahub/graphql/mutations/artifact_definition.py +16 -12
  189. infrahub/graphql/mutations/branch.py +61 -40
  190. infrahub/graphql/mutations/computed_attribute.py +19 -13
  191. infrahub/graphql/mutations/diff.py +37 -9
  192. infrahub/graphql/mutations/diff_conflict.py +9 -8
  193. infrahub/graphql/mutations/graphql_query.py +19 -11
  194. infrahub/graphql/mutations/ipam.py +21 -19
  195. infrahub/graphql/mutations/main.py +197 -44
  196. infrahub/graphql/mutations/menu.py +8 -8
  197. infrahub/graphql/mutations/proposed_change.py +36 -28
  198. infrahub/graphql/mutations/relationship.py +302 -105
  199. infrahub/graphql/mutations/repository.py +41 -35
  200. infrahub/graphql/mutations/resource_manager.py +26 -26
  201. infrahub/graphql/mutations/schema.py +51 -33
  202. infrahub/graphql/mutations/tasks.py +16 -10
  203. infrahub/graphql/parser.py +1 -1
  204. infrahub/graphql/permissions.py +6 -4
  205. infrahub/graphql/queries/account.py +22 -18
  206. infrahub/graphql/queries/branch.py +6 -4
  207. infrahub/graphql/queries/diff/tree.py +48 -42
  208. infrahub/graphql/queries/event.py +112 -0
  209. infrahub/graphql/queries/internal.py +3 -3
  210. infrahub/graphql/queries/ipam.py +23 -18
  211. infrahub/graphql/queries/relationship.py +11 -10
  212. infrahub/graphql/queries/resource_manager.py +43 -27
  213. infrahub/graphql/queries/search.py +9 -8
  214. infrahub/graphql/queries/status.py +12 -9
  215. infrahub/graphql/queries/task.py +11 -9
  216. infrahub/graphql/resolvers/resolver.py +69 -43
  217. infrahub/graphql/resolvers/single_relationship.py +16 -10
  218. infrahub/graphql/schema.py +2 -0
  219. infrahub/graphql/subscription/__init__.py +1 -1
  220. infrahub/graphql/subscription/events.py +1 -1
  221. infrahub/graphql/subscription/graphql_query.py +8 -8
  222. infrahub/graphql/types/branch.py +2 -2
  223. infrahub/graphql/types/common.py +6 -1
  224. infrahub/graphql/types/enums.py +2 -0
  225. infrahub/graphql/types/event.py +100 -0
  226. infrahub/graphql/types/interface.py +2 -2
  227. infrahub/graphql/types/node.py +3 -3
  228. infrahub/graphql/types/permission.py +2 -2
  229. infrahub/graphql/types/relationship.py +3 -3
  230. infrahub/graphql/types/standard_node.py +9 -11
  231. infrahub/graphql/utils.py +28 -182
  232. infrahub/groups/tasks.py +2 -3
  233. infrahub/lock.py +1 -1
  234. infrahub/menu/constants.py +1 -0
  235. infrahub/menu/generator.py +14 -3
  236. infrahub/menu/menu.py +116 -127
  237. infrahub/menu/models.py +4 -4
  238. infrahub/message_bus/messages/__init__.py +0 -4
  239. infrahub/message_bus/messages/event_branch_merge.py +3 -0
  240. infrahub/message_bus/messages/request_proposedchange_pipeline.py +2 -0
  241. infrahub/message_bus/operations/__init__.py +3 -5
  242. infrahub/message_bus/operations/check/__init__.py +2 -2
  243. infrahub/message_bus/operations/check/generator.py +1 -3
  244. infrahub/message_bus/operations/check/repository.py +1 -1
  245. infrahub/message_bus/operations/event/branch.py +7 -3
  246. infrahub/message_bus/operations/event/schema.py +1 -1
  247. infrahub/message_bus/operations/finalize/validator.py +1 -1
  248. infrahub/message_bus/operations/git/file.py +2 -2
  249. infrahub/message_bus/operations/git/repository.py +1 -1
  250. infrahub/message_bus/operations/requests/__init__.py +0 -2
  251. infrahub/message_bus/operations/requests/generator_definition.py +1 -1
  252. infrahub/message_bus/operations/requests/proposed_change.py +26 -11
  253. infrahub/message_bus/operations/requests/repository.py +2 -2
  254. infrahub/message_bus/operations/send/echo.py +1 -1
  255. infrahub/message_bus/types.py +1 -1
  256. infrahub/permissions/__init__.py +2 -1
  257. infrahub/permissions/types.py +26 -0
  258. infrahub/pools/prefix.py +29 -165
  259. infrahub/prefect_server/__init__.py +0 -0
  260. infrahub/prefect_server/app.py +18 -0
  261. infrahub/prefect_server/database.py +20 -0
  262. infrahub/prefect_server/events.py +28 -0
  263. infrahub/prefect_server/models.py +46 -0
  264. infrahub/proposed_change/models.py +15 -1
  265. infrahub/proposed_change/tasks.py +173 -35
  266. infrahub/pytest_plugin.py +4 -4
  267. infrahub/server.py +12 -11
  268. infrahub/services/__init__.py +147 -62
  269. infrahub/services/adapters/cache/__init__.py +7 -5
  270. infrahub/services/adapters/cache/nats.py +40 -22
  271. infrahub/services/adapters/cache/redis.py +0 -4
  272. infrahub/services/adapters/event/__init__.py +10 -18
  273. infrahub/services/adapters/http/__init__.py +0 -5
  274. infrahub/services/adapters/http/httpx.py +22 -15
  275. infrahub/services/adapters/message_bus/__init__.py +23 -6
  276. infrahub/services/adapters/message_bus/local.py +8 -6
  277. infrahub/services/adapters/message_bus/nats.py +12 -6
  278. infrahub/services/adapters/message_bus/rabbitmq.py +22 -9
  279. infrahub/services/adapters/workflow/__init__.py +11 -8
  280. infrahub/services/adapters/workflow/local.py +28 -7
  281. infrahub/services/adapters/workflow/worker.py +23 -7
  282. infrahub/services/component.py +38 -35
  283. infrahub/services/scheduler.py +32 -29
  284. infrahub/storage.py +2 -4
  285. infrahub/task_manager/constants.py +1 -1
  286. infrahub/task_manager/event.py +182 -0
  287. infrahub/task_manager/models.py +125 -1
  288. infrahub/task_manager/task.py +1 -1
  289. infrahub/tasks/artifact.py +14 -16
  290. infrahub/tasks/registry.py +1 -1
  291. infrahub/tasks/telemetry.py +13 -14
  292. infrahub/transformations/tasks.py +3 -5
  293. infrahub/trigger/__init__.py +0 -0
  294. infrahub/trigger/catalogue.py +15 -0
  295. infrahub/trigger/constants.py +9 -0
  296. infrahub/trigger/models.py +69 -0
  297. infrahub/trigger/tasks.py +85 -0
  298. infrahub/types.py +1 -1
  299. infrahub/utils.py +1 -1
  300. infrahub/webhook/constants.py +0 -2
  301. infrahub/webhook/models.py +8 -2
  302. infrahub/webhook/tasks.py +20 -73
  303. infrahub/webhook/triggers.py +20 -0
  304. infrahub/workers/infrahub_async.py +36 -25
  305. infrahub/workers/utils.py +63 -0
  306. infrahub/workflows/catalogue.py +13 -37
  307. infrahub/workflows/initialization.py +6 -8
  308. infrahub/workflows/models.py +3 -5
  309. infrahub/workflows/utils.py +1 -1
  310. infrahub_sdk/ctl/check.py +3 -3
  311. infrahub_sdk/ctl/cli_commands.py +11 -10
  312. infrahub_sdk/ctl/exceptions.py +0 -6
  313. infrahub_sdk/ctl/exporter.py +1 -1
  314. infrahub_sdk/ctl/generator.py +5 -5
  315. infrahub_sdk/ctl/importer.py +3 -2
  316. infrahub_sdk/ctl/menu.py +1 -1
  317. infrahub_sdk/ctl/object.py +1 -1
  318. infrahub_sdk/ctl/repository.py +23 -15
  319. infrahub_sdk/ctl/schema.py +2 -2
  320. infrahub_sdk/ctl/utils.py +4 -3
  321. infrahub_sdk/ctl/validate.py +2 -1
  322. infrahub_sdk/exceptions.py +6 -0
  323. infrahub_sdk/generator.py +3 -0
  324. infrahub_sdk/node.py +2 -2
  325. infrahub_sdk/schema/__init__.py +14 -2
  326. infrahub_sdk/schema/main.py +7 -0
  327. infrahub_sdk/utils.py +11 -1
  328. infrahub_sdk/yaml.py +2 -3
  329. {infrahub_server-1.1.6.dist-info → infrahub_server-1.2.0rc0.dist-info}/METADATA +46 -12
  330. {infrahub_server-1.1.6.dist-info → infrahub_server-1.2.0rc0.dist-info}/RECORD +338 -321
  331. infrahub_testcontainers/container.py +14 -6
  332. infrahub_testcontainers/docker-compose.test.yml +24 -5
  333. infrahub_testcontainers/haproxy.cfg +43 -0
  334. infrahub_testcontainers/helpers.py +85 -1
  335. infrahub/core/branch/constants.py +0 -2
  336. infrahub/graphql/query.py +0 -52
  337. infrahub/message_bus/messages/request_artifactdefinition_check.py +0 -17
  338. infrahub/message_bus/operations/requests/artifact_definition.py +0 -148
  339. infrahub/schema/constants.py +0 -1
  340. infrahub/schema/tasks.py +0 -76
  341. infrahub/services/adapters/database/__init__.py +0 -9
  342. infrahub_sdk/ctl/_file.py +0 -13
  343. /infrahub/{schema → artifacts}/__init__.py +0 -0
  344. {infrahub_server-1.1.6.dist-info → infrahub_server-1.2.0rc0.dist-info}/LICENSE.txt +0 -0
  345. {infrahub_server-1.1.6.dist-info → infrahub_server-1.2.0rc0.dist-info}/WHEEL +0 -0
  346. {infrahub_server-1.1.6.dist-info → infrahub_server-1.2.0rc0.dist-info}/entry_points.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import TYPE_CHECKING, Any, Optional, Union
3
+ from typing import TYPE_CHECKING, Any, Mapping, Optional, Union
4
4
 
5
5
  from graphene import InputObjectType, Mutation
6
6
  from graphene.types.mutation import MutationOptions
@@ -9,13 +9,15 @@ from typing_extensions import Self
9
9
 
10
10
  from infrahub import config, lock
11
11
  from infrahub.core import registry
12
- from infrahub.core.constants import InfrahubKind, MutationAction
12
+ from infrahub.core.changelog.models import RelationshipChangelogGetter
13
+ from infrahub.core.constants import InfrahubKind, MutationAction, RelationshipCardinality, RelationshipKind
13
14
  from infrahub.core.constraint.node.runner import NodeConstraintRunner
14
15
  from infrahub.core.manager import NodeManager
15
16
  from infrahub.core.node import Node
16
- from infrahub.core.schema import NodeSchema
17
+ from infrahub.core.schema import NodeSchema, RelationshipSchema
17
18
  from infrahub.core.schema.generic_schema import GenericSchema
18
19
  from infrahub.core.schema.profile_schema import ProfileSchema
20
+ from infrahub.core.schema.template_schema import TemplateSchema
19
21
  from infrahub.core.timestamp import Timestamp
20
22
  from infrahub.database import retry_db_transaction
21
23
  from infrahub.dependencies.registry import get_component_registry
@@ -33,13 +35,14 @@ if TYPE_CHECKING:
33
35
  from graphql import GraphQLResolveInfo
34
36
 
35
37
  from infrahub.core.branch import Branch
38
+ from infrahub.core.protocols import CoreObjectTemplate
39
+ from infrahub.core.relationship.model import RelationshipManager
36
40
  from infrahub.core.schema.schema_branch import SchemaBranch
37
41
  from infrahub.database import InfrahubDatabase
38
42
 
39
43
  from ..initialization import GraphqlContext
40
44
  from .node_getter.interface import MutationNodeGetterInterface
41
45
 
42
- # pylint: disable=unused-argument
43
46
 
44
47
  log = get_logger()
45
48
 
@@ -55,60 +58,86 @@ class InfrahubMutationOptions(MutationOptions):
55
58
 
56
59
  class InfrahubMutationMixin:
57
60
  @classmethod
58
- async def mutate(cls, root: dict, info: GraphQLResolveInfo, data: InputObjectType, *args: Any, **kwargs):
59
- context: GraphqlContext = info.context
61
+ async def mutate(cls, root: dict, info: GraphQLResolveInfo, data: InputObjectType, *args: Any, **kwargs): # noqa: ARG003
62
+ graphql_context: GraphqlContext = info.context
60
63
 
61
64
  obj = None
62
65
  mutation = None
63
66
  action = MutationAction.UNDEFINED
64
67
 
65
68
  if "Create" in cls.__name__:
66
- obj, mutation = await cls.mutate_create(info=info, branch=context.branch, data=data, **kwargs)
67
- action = MutationAction.ADDED
69
+ obj, mutation = await cls.mutate_create(info=info, branch=graphql_context.branch, data=data, **kwargs)
70
+ action = MutationAction.CREATED
68
71
  elif "Update" in cls.__name__:
69
- obj, mutation = await cls.mutate_update(info=info, branch=context.branch, data=data, **kwargs)
72
+ obj, mutation = await cls.mutate_update(info=info, branch=graphql_context.branch, data=data, **kwargs)
70
73
  action = MutationAction.UPDATED
71
74
  elif "Upsert" in cls.__name__:
72
75
  node_manager = NodeManager()
73
76
  node_getters = [
74
- MutationNodeGetterById(db=context.db, node_manager=node_manager),
75
- MutationNodeGetterByHfid(db=context.db, node_manager=node_manager),
76
- MutationNodeGetterByDefaultFilter(db=context.db, node_manager=node_manager),
77
+ MutationNodeGetterById(db=graphql_context.db, node_manager=node_manager),
78
+ MutationNodeGetterByHfid(db=graphql_context.db, node_manager=node_manager),
79
+ MutationNodeGetterByDefaultFilter(db=graphql_context.db, node_manager=node_manager),
77
80
  ]
78
81
  obj, mutation, created = await cls.mutate_upsert(
79
- info=info, branch=context.branch, data=data, node_getters=node_getters, **kwargs
82
+ info=info, branch=graphql_context.branch, data=data, node_getters=node_getters, **kwargs
80
83
  )
81
84
  if created:
82
- action = MutationAction.ADDED
85
+ action = MutationAction.CREATED
83
86
  else:
84
87
  action = MutationAction.UPDATED
85
88
  elif "Delete" in cls.__name__:
86
- obj, mutation = await cls.mutate_delete(info=info, branch=context.branch, data=data, **kwargs)
87
- action = MutationAction.REMOVED
89
+ obj, mutation = await cls.mutate_delete(info=info, branch=graphql_context.branch, data=data, **kwargs)
90
+ action = MutationAction.DELETED
88
91
  else:
89
92
  raise ValueError(
90
93
  f"Unexpected class Name: {cls.__name__}, should end with Create, Update, Upsert, or Delete"
91
94
  )
92
95
 
93
96
  # Reset the time of the query to guarantee that all resolvers executed after this point will account for the changes
94
- context.at = Timestamp()
97
+ graphql_context.at = Timestamp()
95
98
 
96
- if config.SETTINGS.broker.enable and context.background:
99
+ if config.SETTINGS.broker.enable and graphql_context.background and obj.node_changelog.has_changes:
97
100
  log_data = get_log_data()
98
101
  request_id = log_data.get("request_id", "")
99
102
 
100
- graphql_payload = await obj.to_graphql(db=context.db, filter_sensitive=True)
101
- event = NodeMutatedEvent(
102
- branch=context.branch.name,
103
+ account_id: str | None = None
104
+ if graphql_context.account_session:
105
+ account_id = graphql_context.account_session.account_id
106
+
107
+ meta = EventMeta(
108
+ account_id=account_id,
109
+ initiator_id=WORKER_IDENTITY,
110
+ request_id=request_id,
111
+ branch=graphql_context.branch,
112
+ context=graphql_context.get_context(),
113
+ )
114
+ main_event = NodeMutatedEvent(
103
115
  kind=obj._schema.kind,
104
116
  node_id=obj.id,
105
- data=graphql_payload,
117
+ data=obj.node_changelog,
106
118
  action=action,
107
119
  fields=_get_data_fields(data),
108
- meta=EventMeta(initiator_id=WORKER_IDENTITY, request_id=request_id),
120
+ meta=meta,
109
121
  )
110
-
111
- context.background.add_task(context.active_service.event.send, event)
122
+ relationship_changelogs = RelationshipChangelogGetter(db=graphql_context.db, branch=graphql_context.branch)
123
+ node_changelogs = await relationship_changelogs.get_changelogs(primary_changelog=obj.node_changelog)
124
+
125
+ events = [main_event]
126
+
127
+ for node_changelog in node_changelogs:
128
+ meta = EventMeta.from_parent(parent=main_event)
129
+ event = NodeMutatedEvent(
130
+ kind=node_changelog.node_kind,
131
+ node_id=node_changelog.node_id,
132
+ data=node_changelog,
133
+ action=MutationAction.UPDATED,
134
+ fields=node_changelog.updated_fields,
135
+ meta=meta,
136
+ )
137
+ events.append(event)
138
+
139
+ for event in events:
140
+ graphql_context.background.add_task(graphql_context.active_service.event.send, event)
112
141
 
113
142
  return mutation
114
143
 
@@ -127,7 +156,7 @@ class InfrahubMutationMixin:
127
156
  return obj
128
157
  current_profile_ids = await cls._get_profile_ids(db=db, obj=obj)
129
158
  if previous_profile_ids is None or previous_profile_ids != current_profile_ids:
130
- return await NodeManager.get_one_by_id_or_default_filter(
159
+ refreshed_node = await NodeManager.get_one_by_id_or_default_filter(
131
160
  db=db,
132
161
  kind=cls._meta.schema.kind,
133
162
  id=obj.get_id(),
@@ -135,6 +164,8 @@ class InfrahubMutationMixin:
135
164
  include_owner=True,
136
165
  include_source=True,
137
166
  )
167
+ refreshed_node._node_changelog = obj.node_changelog
168
+ return refreshed_node
138
169
  return obj
139
170
 
140
171
  @classmethod
@@ -152,6 +183,95 @@ class InfrahubMutationMixin:
152
183
 
153
184
  return await cls.mutate_create_object(data=data, db=db, branch=branch)
154
185
 
186
+ @classmethod
187
+ async def _get_template_relationship_peers(
188
+ cls, db: InfrahubDatabase, template: CoreObjectTemplate, relationship: RelationshipSchema
189
+ ) -> Mapping[str, Node]:
190
+ """For a given relationship on the template, fetch the related peers."""
191
+ template_relationship_manager: RelationshipManager = getattr(template, relationship.name)
192
+ if relationship.cardinality == RelationshipCardinality.MANY:
193
+ return await template_relationship_manager.get_peers(db=db)
194
+
195
+ peers: dict[str, Node] = {}
196
+ template_relationship_peer = await template_relationship_manager.get_peer(db=db)
197
+ if template_relationship_peer:
198
+ peers[template_relationship_peer.id] = template_relationship_peer
199
+ return peers
200
+
201
+ @classmethod
202
+ async def _extract_peer_data(
203
+ cls,
204
+ db: InfrahubDatabase,
205
+ template_peer: Node,
206
+ obj_peer_schema,
207
+ parent_obj: Node,
208
+ current_template: CoreObjectTemplate,
209
+ ) -> Mapping[str, Any]:
210
+ obj_peer_data: dict[str, Any] = {}
211
+
212
+ for attr in template_peer.get_schema().attribute_names:
213
+ if attr not in obj_peer_schema.attribute_names:
214
+ continue
215
+ obj_peer_data[attr] = {"value": getattr(template_peer, attr).value}
216
+
217
+ for rel in template_peer.get_schema().relationship_names:
218
+ rel_manager: RelationshipManager = getattr(template_peer, rel)
219
+ if (
220
+ rel_manager.schema.kind not in [RelationshipKind.COMPONENT, RelationshipKind.PARENT]
221
+ or rel_manager.schema.name not in obj_peer_schema.relationship_names
222
+ ):
223
+ continue
224
+
225
+ if list(await rel_manager.get_peers(db=db)) == [current_template.id]:
226
+ obj_peer_data[rel] = {"id": parent_obj.id}
227
+
228
+ return obj_peer_data
229
+
230
+ @classmethod
231
+ async def _handle_template_relationships(
232
+ cls,
233
+ db: InfrahubDatabase,
234
+ branch: Branch,
235
+ obj: Node,
236
+ template: CoreObjectTemplate,
237
+ data: InputObjectType,
238
+ constraint_runner: NodeConstraintRunner | None = None,
239
+ ) -> None:
240
+ if constraint_runner is None:
241
+ component_registry = get_component_registry()
242
+ constraint_runner = await component_registry.get_component(NodeConstraintRunner, db=db, branch=branch)
243
+
244
+ for relationship in obj.get_relationships(kind=RelationshipKind.COMPONENT, exclude=list(data)):
245
+ template_relationship_peers = await cls._get_template_relationship_peers(
246
+ db=db, template=template, relationship=relationship
247
+ )
248
+ if not template_relationship_peers:
249
+ continue
250
+
251
+ obj_peer_schema = relationship.get_peer_schema(db=db, branch=branch)
252
+ for template_relationship_peer in template_relationship_peers.values():
253
+ obj_peer_data = await cls._extract_peer_data(
254
+ db=db,
255
+ template_peer=template_relationship_peer,
256
+ obj_peer_schema=obj_peer_schema,
257
+ parent_obj=obj,
258
+ current_template=template,
259
+ )
260
+
261
+ obj_peer = await Node.init(schema=obj_peer_schema, db=db)
262
+ await obj_peer.new(db=db, **obj_peer_data)
263
+ await constraint_runner.check(node=obj_peer, field_filters=list(obj_peer_data))
264
+ await obj_peer.save(db=db)
265
+
266
+ await cls._handle_template_relationships(
267
+ db=db,
268
+ branch=branch,
269
+ constraint_runner=constraint_runner,
270
+ obj=obj_peer,
271
+ template=template_relationship_peer,
272
+ data=data,
273
+ )
274
+
155
275
  @classmethod
156
276
  async def mutate_create(
157
277
  cls,
@@ -160,8 +280,8 @@ class InfrahubMutationMixin:
160
280
  branch: Branch,
161
281
  database: Optional[InfrahubDatabase] = None,
162
282
  ) -> tuple[Node, Self]:
163
- context: GraphqlContext = info.context
164
- db = database or context.db
283
+ graphql_context: GraphqlContext = info.context
284
+ db = database or graphql_context.db
165
285
  obj = await cls._call_mutate_create_object(data=data, db=db, branch=branch)
166
286
  result = await cls.mutate_create_to_graphql(info=info, db=db, obj=obj)
167
287
  return obj, result
@@ -175,22 +295,46 @@ class InfrahubMutationMixin:
175
295
  branch: Branch,
176
296
  ) -> Node:
177
297
  component_registry = get_component_registry()
178
- node_constraint_runner = await component_registry.get_component(NodeConstraintRunner, db=db, branch=branch)
298
+ node_constraint_runner = await component_registry.get_component(
299
+ NodeConstraintRunner, db=db.start_session(), branch=branch
300
+ )
179
301
  node_class = Node
180
302
  if cls._meta.schema.kind in registry.node:
181
303
  node_class = registry.node[cls._meta.schema.kind]
182
304
 
305
+ fields_to_validate = list(data)
183
306
  try:
184
- obj = await node_class.init(db=db, schema=cls._meta.schema, branch=branch)
185
- await obj.new(db=db, **data)
186
- fields_to_validate = list(data)
187
- await node_constraint_runner.check(node=obj, field_filters=fields_to_validate)
188
307
  if db.is_transaction:
308
+ obj = await node_class.init(db=db, schema=cls._meta.schema, branch=branch)
309
+ await obj.new(db=db, **data)
310
+ await node_constraint_runner.check(node=obj, field_filters=fields_to_validate)
189
311
  await obj.save(db=db)
312
+
313
+ object_template = await obj.get_object_template(db=db)
314
+ if object_template:
315
+ await cls._handle_template_relationships(
316
+ db=db,
317
+ branch=branch,
318
+ template=object_template,
319
+ obj=obj,
320
+ data=data,
321
+ )
190
322
  else:
191
323
  async with db.start_transaction() as dbt:
324
+ obj = await node_class.init(db=dbt, schema=cls._meta.schema, branch=branch)
325
+ await obj.new(db=dbt, **data)
326
+ await node_constraint_runner.check(node=obj, field_filters=fields_to_validate)
192
327
  await obj.save(db=dbt)
193
328
 
329
+ object_template = await obj.get_object_template(db=dbt)
330
+ if object_template:
331
+ await cls._handle_template_relationships(
332
+ db=dbt,
333
+ branch=branch,
334
+ template=object_template,
335
+ obj=obj,
336
+ data=data,
337
+ )
194
338
  except ValidationError as exc:
195
339
  raise ValueError(str(exc)) from exc
196
340
 
@@ -253,8 +397,8 @@ class InfrahubMutationMixin:
253
397
  database: Optional[InfrahubDatabase] = None,
254
398
  node: Optional[Node] = None,
255
399
  ) -> tuple[Node, Self]:
256
- context: GraphqlContext = info.context
257
- db = database or context.db
400
+ graphql_context: GraphqlContext = info.context
401
+ db = database or graphql_context.db
258
402
 
259
403
  obj = node or await NodeManager.find_object(
260
404
  db=db, kind=cls._meta.schema.kind, id=data.get("id"), hfid=data.get("hfid"), branch=branch
@@ -269,7 +413,12 @@ class InfrahubMutationMixin:
269
413
 
270
414
  @classmethod
271
415
  async def mutate_update_object(
272
- cls, db: InfrahubDatabase, info: GraphQLResolveInfo, data: InputObjectType, branch: Branch, obj: Node
416
+ cls,
417
+ db: InfrahubDatabase,
418
+ info: GraphQLResolveInfo, # noqa: ARG003
419
+ data: InputObjectType,
420
+ branch: Branch,
421
+ obj: Node,
273
422
  ) -> Node:
274
423
  component_registry = get_component_registry()
275
424
  node_constraint_runner = await component_registry.get_component(NodeConstraintRunner, db=db, branch=branch)
@@ -285,6 +434,7 @@ class InfrahubMutationMixin:
285
434
  fields.remove(field)
286
435
 
287
436
  await obj.save(db=db, fields=fields)
437
+
288
438
  obj = await cls._refresh_for_profile_update(
289
439
  db=db, branch=branch, obj=obj, previous_profile_ids=before_mutate_profile_ids
290
440
  )
@@ -316,8 +466,8 @@ class InfrahubMutationMixin:
316
466
  ) -> tuple[Node, Self, bool]:
317
467
  schema_name = cls._meta.schema.kind
318
468
 
319
- context: GraphqlContext = info.context
320
- db = database or context.db
469
+ graphql_context: GraphqlContext = info.context
470
+ db = database or graphql_context.db
321
471
 
322
472
  node_schema = db.schema.get(name=schema_name, branch=branch)
323
473
 
@@ -345,14 +495,14 @@ class InfrahubMutationMixin:
345
495
  data: InputObjectType,
346
496
  branch: Branch,
347
497
  ) -> tuple[Node, Self]:
348
- context: GraphqlContext = info.context
498
+ graphql_context: GraphqlContext = info.context
349
499
 
350
500
  obj = await NodeManager.find_object(
351
- db=context.db, kind=cls._meta.schema.kind, id=data.get("id"), hfid=data.get("hfid"), branch=branch
501
+ db=graphql_context.db, kind=cls._meta.schema.kind, id=data.get("id"), hfid=data.get("hfid"), branch=branch
352
502
  )
353
503
 
354
504
  try:
355
- async with context.db.start_transaction() as db:
505
+ async with graphql_context.db.start_transaction() as db:
356
506
  deleted = await NodeManager.delete(db=db, branch=branch, nodes=[obj])
357
507
  except ValidationError as exc:
358
508
  raise ValueError(str(exc)) from exc
@@ -367,11 +517,14 @@ class InfrahubMutationMixin:
367
517
 
368
518
  class InfrahubMutation(InfrahubMutationMixin, Mutation):
369
519
  @classmethod
370
- def __init_subclass_with_meta__( # pylint: disable=arguments-differ
371
- cls, schema: Optional[Union[NodeSchema, GenericSchema, ProfileSchema]] = None, _meta=None, **options
520
+ def __init_subclass_with_meta__(
521
+ cls,
522
+ schema: Optional[Union[NodeSchema, GenericSchema, ProfileSchema, TemplateSchema]] = None,
523
+ _meta=None,
524
+ **options,
372
525
  ) -> None:
373
526
  # Make sure schema is a valid NodeSchema Node Class
374
- if not isinstance(schema, (NodeSchema, GenericSchema, ProfileSchema)):
527
+ if not isinstance(schema, NodeSchema | GenericSchema | ProfileSchema | TemplateSchema):
375
528
  raise ValueError(f"You need to pass a valid NodeSchema in '{cls.__name__}.Meta', received '{schema}'")
376
529
 
377
530
  if not _meta:
@@ -34,7 +34,7 @@ def validate_namespace(data: InputObjectType) -> None:
34
34
 
35
35
  class InfrahubCoreMenuMutation(InfrahubMutationMixin, Mutation):
36
36
  @classmethod
37
- def __init_subclass_with_meta__( # pylint: disable=arguments-differ
37
+ def __init_subclass_with_meta__(
38
38
  cls, schema: NodeSchema, _meta: Optional[Any] = None, **options: dict[str, Any]
39
39
  ) -> None:
40
40
  # Make sure schema is a valid NodeSchema Node Class
@@ -53,7 +53,7 @@ class InfrahubCoreMenuMutation(InfrahubMutationMixin, Mutation):
53
53
  info: GraphQLResolveInfo,
54
54
  data: InputObjectType,
55
55
  branch: Branch,
56
- database: Optional[InfrahubDatabase] = None,
56
+ database: Optional[InfrahubDatabase] = None, # noqa: ARG003
57
57
  ) -> tuple[Node, Self]:
58
58
  validate_namespace(data=data)
59
59
 
@@ -67,13 +67,13 @@ class InfrahubCoreMenuMutation(InfrahubMutationMixin, Mutation):
67
67
  info: GraphQLResolveInfo,
68
68
  data: InputObjectType,
69
69
  branch: Branch,
70
- database: Optional[InfrahubDatabase] = None,
71
- node: Optional[Node] = None,
70
+ database: Optional[InfrahubDatabase] = None, # noqa: ARG003
71
+ node: Optional[Node] = None, # noqa: ARG003
72
72
  ) -> tuple[Node, Self]:
73
- context: GraphqlContext = info.context
73
+ graphql_context: GraphqlContext = info.context
74
74
 
75
75
  obj = await NodeManager.find_object(
76
- db=context.db, kind=CoreMenuItem, id=data.get("id"), hfid=data.get("hfid"), branch=branch
76
+ db=graphql_context.db, kind=CoreMenuItem, id=data.get("id"), hfid=data.get("hfid"), branch=branch
77
77
  )
78
78
  validate_namespace(data=data)
79
79
 
@@ -90,9 +90,9 @@ class InfrahubCoreMenuMutation(InfrahubMutationMixin, Mutation):
90
90
  data: InputObjectType,
91
91
  branch: Branch,
92
92
  ) -> tuple[Node, Self]:
93
- context: GraphqlContext = info.context
93
+ graphql_context: GraphqlContext = info.context
94
94
  obj = await NodeManager.find_object(
95
- db=context.db, kind=CoreMenuItem, id=data.get("id"), hfid=data.get("hfid"), branch=branch
95
+ db=graphql_context.db, kind=CoreMenuItem, id=data.get("id"), hfid=data.get("hfid"), branch=branch
96
96
  )
97
97
  if obj.protected.value:
98
98
  raise ValidationError(input_value="This object is protected, it can't be deleted.")
@@ -31,7 +31,7 @@ if TYPE_CHECKING:
31
31
 
32
32
  class InfrahubProposedChangeMutation(InfrahubMutationMixin, Mutation):
33
33
  @classmethod
34
- def __init_subclass_with_meta__(cls, schema: NodeSchema = None, _meta=None, **options): # pylint: disable=arguments-differ
34
+ def __init_subclass_with_meta__(cls, schema: NodeSchema = None, _meta=None, **options):
35
35
  # Make sure schema is a valid NodeSchema Node Class
36
36
  if not isinstance(schema, NodeSchema):
37
37
  raise ValueError(f"You need to pass a valid NodeSchema in '{cls.__name__}.Meta', received '{schema}'")
@@ -49,12 +49,11 @@ class InfrahubProposedChangeMutation(InfrahubMutationMixin, Mutation):
49
49
  info: GraphQLResolveInfo,
50
50
  data: InputObjectType,
51
51
  branch: Branch,
52
- database: Optional[InfrahubDatabase] = None,
52
+ database: Optional[InfrahubDatabase] = None, # noqa: ARG003
53
53
  ):
54
- context: GraphqlContext = info.context
55
- db: InfrahubDatabase = info.context.db
54
+ graphql_context: GraphqlContext = info.context
56
55
 
57
- async with db.start_transaction() as dbt:
56
+ async with graphql_context.db.start_transaction() as dbt:
58
57
  proposed_change, result = await super().mutate_create(info=info, data=data, branch=branch, database=dbt)
59
58
  destination_branch = proposed_change.destination_branch.value
60
59
  source_branch = await _get_source_branch(db=dbt, name=proposed_change.source_branch.value)
@@ -65,34 +64,35 @@ class InfrahubProposedChangeMutation(InfrahubMutationMixin, Mutation):
65
64
  input_value="Currently only the 'main' branch is supported as a destination for a proposed change"
66
65
  )
67
66
 
68
- if context.service:
67
+ if graphql_context.service:
69
68
  message_list = [
70
69
  messages.RequestProposedChangePipeline(
71
70
  proposed_change=proposed_change.id,
72
71
  source_branch=source_branch.name,
73
72
  source_branch_sync_with_git=source_branch.sync_with_git,
74
73
  destination_branch=destination_branch,
74
+ context=graphql_context.get_context(),
75
75
  ),
76
76
  ]
77
77
 
78
78
  for message in message_list:
79
- await context.service.send(message=message)
79
+ await graphql_context.service.message_bus.send(message=message)
80
80
 
81
81
  return proposed_change, result
82
82
 
83
83
  @classmethod
84
- async def mutate_update( # pylint: disable=too-many-branches
84
+ async def mutate_update(
85
85
  cls,
86
86
  info: GraphQLResolveInfo,
87
87
  data: InputObjectType,
88
88
  branch: Branch,
89
- database: Optional[InfrahubDatabase] = None,
90
- node: Optional[Node] = None,
89
+ database: Optional[InfrahubDatabase] = None, # noqa: ARG003
90
+ node: Optional[Node] = None, # noqa: ARG003
91
91
  ):
92
- context: GraphqlContext = info.context
92
+ graphql_context: GraphqlContext = info.context
93
93
 
94
94
  obj = await NodeManager.get_one_by_id_or_default_filter(
95
- db=context.db,
95
+ db=graphql_context.db,
96
96
  kind=cls._meta.schema.kind,
97
97
  id=data.get("id"),
98
98
  branch=branch,
@@ -108,9 +108,9 @@ class InfrahubProposedChangeMutation(InfrahubMutationMixin, Mutation):
108
108
  state.validate_state_transition(updated_state)
109
109
 
110
110
  # Check before starting a transaction, stopping in the middle of the transaction seems to break with memgraph
111
- if updated_state == ProposedChangeState.MERGED and context.account_session:
111
+ if updated_state == ProposedChangeState.MERGED and graphql_context.account_session:
112
112
  try:
113
- context.active_permissions.raise_for_permission(
113
+ graphql_context.active_permissions.raise_for_permission(
114
114
  permission=GlobalPermission(
115
115
  action=GlobalPermissions.MERGE_PROPOSED_CHANGE.value,
116
116
  decision=PermissionDecision.ALLOW_ALL.value,
@@ -123,17 +123,22 @@ class InfrahubProposedChangeMutation(InfrahubMutationMixin, Mutation):
123
123
  data["state"]["value"] = ProposedChangeState.MERGING.value
124
124
 
125
125
  proposed_change, result = await super().mutate_update(
126
- info=info, data=data, branch=branch, database=context.db, node=obj
126
+ info=info, data=data, branch=branch, database=graphql_context.db, node=obj
127
127
  )
128
128
 
129
129
  if updated_state == ProposedChangeState.MERGED:
130
- await context.service.workflow.execute_workflow(
130
+ await graphql_context.service.workflow.execute_workflow(
131
131
  workflow=PROPOSED_CHANGE_MERGE,
132
+ context=graphql_context.get_context(),
132
133
  parameters={
133
134
  "proposed_change_id": proposed_change.id,
134
135
  "proposed_change_name": proposed_change.name.value,
135
136
  },
136
137
  )
138
+ # When the PROPOSED_CHANGE_MERGE succeeds it will have correctly changed the state
139
+ # from the overridden "merging" value, so here we change it back to reflect the
140
+ # correct value for the event that will be generated.
141
+ proposed_change.node_changelog.attributes["state"].value = ProposedChangeState.MERGED.value
137
142
 
138
143
  return proposed_change, result
139
144
 
@@ -152,23 +157,23 @@ class ProposedChangeRequestRunCheck(Mutation):
152
157
  @classmethod
153
158
  async def mutate(
154
159
  cls,
155
- root: dict, # pylint: disable=unused-argument
160
+ root: dict, # noqa: ARG003
156
161
  info: GraphQLResolveInfo,
157
162
  data: dict[str, Any],
158
163
  ) -> dict[str, bool]:
159
- context: GraphqlContext = info.context
164
+ graphql_context: GraphqlContext = info.context
160
165
 
161
166
  check_type = data.get("check_type") or CheckType.ALL
162
167
 
163
168
  identifier = data.get("id", "")
164
169
  proposed_change = await NodeManager.get_one_by_id_or_default_filter(
165
- id=identifier, kind=InfrahubKind.PROPOSEDCHANGE, db=context.db
170
+ id=identifier, kind=InfrahubKind.PROPOSEDCHANGE, db=graphql_context.db
166
171
  )
167
172
  state = ProposedChangeState(proposed_change.state.value.value)
168
173
  state.validate_state_check_run()
169
174
 
170
175
  destination_branch = proposed_change.destination_branch.value
171
- source_branch = await _get_source_branch(db=context.db, name=proposed_change.source_branch.value)
176
+ source_branch = await _get_source_branch(db=graphql_context.db, name=proposed_change.source_branch.value)
172
177
 
173
178
  message = messages.RequestProposedChangePipeline(
174
179
  proposed_change=proposed_change.id,
@@ -176,9 +181,10 @@ class ProposedChangeRequestRunCheck(Mutation):
176
181
  source_branch_sync_with_git=source_branch.sync_with_git,
177
182
  destination_branch=destination_branch,
178
183
  check_type=check_type,
184
+ context=graphql_context.get_context(),
179
185
  )
180
- if context.service:
181
- await context.service.send(message=message)
186
+ if graphql_context.service:
187
+ await graphql_context.service.message_bus.send(message=message)
182
188
 
183
189
  return {"ok": True}
184
190
 
@@ -198,37 +204,39 @@ class ProposedChangeMerge(Mutation):
198
204
  @classmethod
199
205
  async def mutate(
200
206
  cls,
201
- root: dict, # pylint: disable=unused-argument
207
+ root: dict, # noqa: ARG003
202
208
  info: GraphQLResolveInfo,
203
209
  data: dict[str, Any],
204
210
  wait_until_completion: bool = True,
205
211
  ) -> dict[str, bool]:
206
- context: GraphqlContext = info.context
212
+ graphql_context: GraphqlContext = info.context
207
213
  task: dict | None = None
208
214
 
209
215
  identifier = data.get("id", "")
210
216
  proposed_change = await NodeManager.get_one(
211
- id=identifier, kind=InfrahubKind.PROPOSEDCHANGE, db=context.db, raise_on_error=True
217
+ id=identifier, kind=InfrahubKind.PROPOSEDCHANGE, db=graphql_context.db, raise_on_error=True
212
218
  )
213
219
  state = ProposedChangeState(proposed_change.state.value.value)
214
220
  if state != ProposedChangeState.OPEN:
215
221
  raise ValidationError("Only proposed change in OPEN state can be merged")
216
222
 
217
- async with context.db.start_session() as db:
223
+ async with graphql_context.db.start_session() as db:
218
224
  proposed_change.state.value = ProposedChangeState.MERGING.value
219
225
  proposed_change.save(db=db)
220
226
 
221
227
  if wait_until_completion:
222
- await context.service.workflow.execute_workflow(
228
+ await graphql_context.service.workflow.execute_workflow(
223
229
  workflow=PROPOSED_CHANGE_MERGE,
230
+ context=graphql_context.get_context(),
224
231
  parameters={
225
232
  "proposed_change_id": proposed_change.id,
226
233
  "proposed_change_name": proposed_change.name.value,
227
234
  },
228
235
  )
229
236
  else:
230
- workflow = await context.service.workflow.submit_workflow(
237
+ workflow = await graphql_context.service.workflow.submit_workflow(
231
238
  workflow=PROPOSED_CHANGE_MERGE,
239
+ context=graphql_context.get_context(),
232
240
  parameters={
233
241
  "proposed_change_id": proposed_change.id,
234
242
  "proposed_change_name": proposed_change.name.value,