infrahub-server 1.2.0rc0__py3-none-any.whl → 1.2.2__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 (365) hide show
  1. infrahub/api/dependencies.py +6 -6
  2. infrahub/api/diff/validation_models.py +7 -7
  3. infrahub/api/schema.py +1 -1
  4. infrahub/artifacts/models.py +5 -3
  5. infrahub/artifacts/tasks.py +3 -5
  6. infrahub/cli/__init__.py +13 -9
  7. infrahub/cli/constants.py +3 -0
  8. infrahub/cli/db.py +165 -183
  9. infrahub/cli/upgrade.py +146 -0
  10. infrahub/computed_attribute/gather.py +185 -0
  11. infrahub/computed_attribute/models.py +240 -12
  12. infrahub/computed_attribute/tasks.py +72 -432
  13. infrahub/computed_attribute/triggers.py +13 -47
  14. infrahub/config.py +43 -32
  15. infrahub/context.py +14 -0
  16. infrahub/core/account.py +4 -4
  17. infrahub/core/attribute.py +58 -58
  18. infrahub/core/branch/tasks.py +74 -22
  19. infrahub/core/changelog/diff.py +95 -36
  20. infrahub/core/changelog/models.py +217 -43
  21. infrahub/core/constants/__init__.py +28 -0
  22. infrahub/core/constants/infrahubkind.py +2 -0
  23. infrahub/core/constants/schema.py +2 -0
  24. infrahub/core/constraint/node/runner.py +9 -8
  25. infrahub/core/diff/branch_differ.py +10 -10
  26. infrahub/core/diff/enricher/cardinality_one.py +5 -0
  27. infrahub/core/diff/enricher/hierarchy.py +17 -4
  28. infrahub/core/diff/enricher/labels.py +5 -0
  29. infrahub/core/diff/enricher/path_identifier.py +4 -0
  30. infrahub/core/diff/ipam_diff_parser.py +4 -5
  31. infrahub/core/diff/model/diff.py +27 -27
  32. infrahub/core/diff/model/path.py +32 -9
  33. infrahub/core/diff/parent_node_adder.py +78 -0
  34. infrahub/core/diff/payload_builder.py +13 -2
  35. infrahub/core/diff/query/filters.py +2 -2
  36. infrahub/core/diff/query/merge.py +20 -17
  37. infrahub/core/diff/query/save.py +188 -182
  38. infrahub/core/diff/query/summary_counts_enricher.py +51 -4
  39. infrahub/core/diff/query_parser.py +4 -4
  40. infrahub/core/diff/repository/deserializer.py +8 -3
  41. infrahub/core/diff/repository/repository.py +156 -38
  42. infrahub/core/diff/tasks.py +4 -4
  43. infrahub/core/graph/__init__.py +1 -1
  44. infrahub/core/graph/index.py +3 -0
  45. infrahub/core/initialization.py +1 -10
  46. infrahub/core/ipam/constants.py +3 -4
  47. infrahub/core/ipam/reconciler.py +12 -12
  48. infrahub/core/ipam/utilization.py +10 -13
  49. infrahub/core/manager.py +36 -36
  50. infrahub/core/merge.py +7 -7
  51. infrahub/core/migrations/__init__.py +2 -3
  52. infrahub/core/migrations/graph/__init__.py +12 -3
  53. infrahub/core/migrations/graph/m017_add_core_profile.py +1 -5
  54. infrahub/core/migrations/graph/m018_uniqueness_nulls.py +4 -4
  55. infrahub/core/migrations/graph/m019_restore_rels_to_time.py +256 -0
  56. infrahub/core/migrations/graph/m020_duplicate_edges.py +160 -0
  57. infrahub/core/migrations/graph/m021_missing_hierarchy_merge.py +51 -0
  58. infrahub/core/migrations/graph/m022_add_generate_template_attr.py +48 -0
  59. infrahub/core/migrations/graph/m023_deduplicate_cardinality_one_relationships.py +96 -0
  60. infrahub/core/migrations/query/attribute_add.py +2 -2
  61. infrahub/core/migrations/query/node_duplicate.py +43 -26
  62. infrahub/core/migrations/query/schema_attribute_update.py +2 -2
  63. infrahub/core/migrations/schema/models.py +19 -4
  64. infrahub/core/migrations/schema/node_remove.py +26 -12
  65. infrahub/core/migrations/schema/tasks.py +2 -2
  66. infrahub/core/migrations/shared.py +16 -16
  67. infrahub/core/models.py +15 -6
  68. infrahub/core/node/__init__.py +43 -39
  69. infrahub/core/node/base.py +2 -4
  70. infrahub/core/node/constraints/attribute_uniqueness.py +2 -2
  71. infrahub/core/node/constraints/grouped_uniqueness.py +99 -47
  72. infrahub/core/node/constraints/interface.py +1 -2
  73. infrahub/core/node/delete_validator.py +3 -5
  74. infrahub/core/node/ipam.py +4 -4
  75. infrahub/core/node/permissions.py +7 -7
  76. infrahub/core/node/resource_manager/ip_address_pool.py +6 -6
  77. infrahub/core/node/resource_manager/ip_prefix_pool.py +6 -6
  78. infrahub/core/node/resource_manager/number_pool.py +3 -3
  79. infrahub/core/path.py +12 -12
  80. infrahub/core/property.py +11 -11
  81. infrahub/core/protocols.py +7 -0
  82. infrahub/core/protocols_base.py +21 -21
  83. infrahub/core/query/__init__.py +33 -33
  84. infrahub/core/query/attribute.py +6 -4
  85. infrahub/core/query/diff.py +3 -3
  86. infrahub/core/query/node.py +98 -37
  87. infrahub/core/query/relationship.py +228 -40
  88. infrahub/core/query/resource_manager.py +2 -0
  89. infrahub/core/query/standard_node.py +3 -3
  90. infrahub/core/query/subquery.py +9 -9
  91. infrahub/core/registry.py +13 -15
  92. infrahub/core/relationship/constraints/count.py +3 -4
  93. infrahub/core/relationship/constraints/peer_kind.py +3 -4
  94. infrahub/core/relationship/constraints/profiles_kind.py +2 -2
  95. infrahub/core/relationship/model.py +51 -59
  96. infrahub/core/schema/attribute_schema.py +16 -8
  97. infrahub/core/schema/basenode_schema.py +105 -44
  98. infrahub/core/schema/computed_attribute.py +3 -3
  99. infrahub/core/schema/definitions/core/__init__.py +147 -0
  100. infrahub/core/schema/definitions/core/account.py +171 -0
  101. infrahub/core/schema/definitions/core/artifact.py +136 -0
  102. infrahub/core/schema/definitions/core/builtin.py +24 -0
  103. infrahub/core/schema/definitions/core/check.py +68 -0
  104. infrahub/core/schema/definitions/core/core.py +17 -0
  105. infrahub/core/schema/definitions/core/generator.py +100 -0
  106. infrahub/core/schema/definitions/core/graphql_query.py +79 -0
  107. infrahub/core/schema/definitions/core/group.py +108 -0
  108. infrahub/core/schema/definitions/core/ipam.py +193 -0
  109. infrahub/core/schema/definitions/core/lineage.py +19 -0
  110. infrahub/core/schema/definitions/core/menu.py +48 -0
  111. infrahub/core/schema/definitions/core/permission.py +163 -0
  112. infrahub/core/schema/definitions/core/profile.py +18 -0
  113. infrahub/core/schema/definitions/core/propose_change.py +97 -0
  114. infrahub/core/schema/definitions/core/propose_change_comment.py +193 -0
  115. infrahub/core/schema/definitions/core/propose_change_validator.py +328 -0
  116. infrahub/core/schema/definitions/core/repository.py +286 -0
  117. infrahub/core/schema/definitions/core/resource_pool.py +170 -0
  118. infrahub/core/schema/definitions/core/template.py +27 -0
  119. infrahub/core/schema/definitions/core/transform.py +96 -0
  120. infrahub/core/schema/definitions/core/webhook.py +134 -0
  121. infrahub/core/schema/definitions/internal.py +16 -16
  122. infrahub/core/schema/dropdown.py +3 -4
  123. infrahub/core/schema/generated/attribute_schema.py +15 -18
  124. infrahub/core/schema/generated/base_node_schema.py +12 -14
  125. infrahub/core/schema/generated/node_schema.py +3 -5
  126. infrahub/core/schema/generated/relationship_schema.py +9 -11
  127. infrahub/core/schema/generic_schema.py +2 -2
  128. infrahub/core/schema/manager.py +20 -9
  129. infrahub/core/schema/node_schema.py +4 -2
  130. infrahub/core/schema/relationship_schema.py +14 -6
  131. infrahub/core/schema/schema_branch.py +292 -144
  132. infrahub/core/schema/schema_branch_computed.py +41 -4
  133. infrahub/core/task/task.py +3 -3
  134. infrahub/core/task/user_task.py +15 -15
  135. infrahub/core/timestamp.py +3 -3
  136. infrahub/core/utils.py +20 -18
  137. infrahub/core/validators/__init__.py +1 -3
  138. infrahub/core/validators/aggregated_checker.py +2 -2
  139. infrahub/core/validators/attribute/choices.py +2 -2
  140. infrahub/core/validators/attribute/enum.py +2 -2
  141. infrahub/core/validators/attribute/kind.py +2 -2
  142. infrahub/core/validators/attribute/length.py +2 -2
  143. infrahub/core/validators/attribute/optional.py +2 -2
  144. infrahub/core/validators/attribute/regex.py +2 -2
  145. infrahub/core/validators/attribute/unique.py +2 -2
  146. infrahub/core/validators/checks_runner.py +25 -2
  147. infrahub/core/validators/determiner.py +1 -3
  148. infrahub/core/validators/interface.py +6 -2
  149. infrahub/core/validators/model.py +22 -3
  150. infrahub/core/validators/models/validate_migration.py +17 -4
  151. infrahub/core/validators/node/attribute.py +2 -2
  152. infrahub/core/validators/node/generate_profile.py +2 -2
  153. infrahub/core/validators/node/hierarchy.py +3 -5
  154. infrahub/core/validators/node/inherit_from.py +27 -5
  155. infrahub/core/validators/node/relationship.py +2 -2
  156. infrahub/core/validators/relationship/count.py +4 -4
  157. infrahub/core/validators/relationship/optional.py +2 -2
  158. infrahub/core/validators/relationship/peer.py +2 -2
  159. infrahub/core/validators/shared.py +2 -2
  160. infrahub/core/validators/tasks.py +8 -0
  161. infrahub/core/validators/uniqueness/checker.py +22 -21
  162. infrahub/core/validators/uniqueness/index.py +2 -2
  163. infrahub/core/validators/uniqueness/model.py +11 -11
  164. infrahub/database/__init__.py +27 -22
  165. infrahub/database/metrics.py +7 -1
  166. infrahub/dependencies/builder/constraint/grouped/node_runner.py +1 -3
  167. infrahub/dependencies/builder/diff/deserializer.py +3 -1
  168. infrahub/dependencies/builder/diff/enricher/hierarchy.py +3 -1
  169. infrahub/dependencies/builder/diff/parent_node_adder.py +8 -0
  170. infrahub/dependencies/component/registry.py +2 -2
  171. infrahub/events/__init__.py +25 -2
  172. infrahub/events/artifact_action.py +64 -0
  173. infrahub/events/branch_action.py +33 -22
  174. infrahub/events/generator.py +71 -0
  175. infrahub/events/group_action.py +51 -21
  176. infrahub/events/models.py +18 -19
  177. infrahub/events/node_action.py +88 -37
  178. infrahub/events/repository_action.py +5 -18
  179. infrahub/events/schema_action.py +4 -9
  180. infrahub/events/utils.py +16 -0
  181. infrahub/events/validator_action.py +55 -0
  182. infrahub/exceptions.py +62 -26
  183. infrahub/generators/models.py +2 -3
  184. infrahub/generators/tasks.py +24 -4
  185. infrahub/git/base.py +87 -36
  186. infrahub/git/integrator.py +48 -48
  187. infrahub/git/models.py +101 -9
  188. infrahub/git/repository.py +3 -3
  189. infrahub/git/tasks.py +408 -6
  190. infrahub/git/utils.py +48 -0
  191. infrahub/git/worktree.py +1 -2
  192. infrahub/git_credential/askpass.py +1 -2
  193. infrahub/graphql/analyzer.py +12 -0
  194. infrahub/graphql/app.py +13 -15
  195. infrahub/graphql/context.py +39 -0
  196. infrahub/graphql/initialization.py +3 -0
  197. infrahub/graphql/loaders/node.py +2 -12
  198. infrahub/graphql/loaders/peers.py +77 -0
  199. infrahub/graphql/loaders/shared.py +13 -0
  200. infrahub/graphql/manager.py +17 -19
  201. infrahub/graphql/mutations/artifact_definition.py +5 -5
  202. infrahub/graphql/mutations/branch.py +26 -1
  203. infrahub/graphql/mutations/computed_attribute.py +9 -5
  204. infrahub/graphql/mutations/diff.py +23 -11
  205. infrahub/graphql/mutations/diff_conflict.py +5 -0
  206. infrahub/graphql/mutations/generator.py +83 -0
  207. infrahub/graphql/mutations/graphql_query.py +5 -5
  208. infrahub/graphql/mutations/ipam.py +54 -74
  209. infrahub/graphql/mutations/main.py +195 -132
  210. infrahub/graphql/mutations/menu.py +7 -7
  211. infrahub/graphql/mutations/models.py +2 -4
  212. infrahub/graphql/mutations/node_getter/by_default_filter.py +10 -10
  213. infrahub/graphql/mutations/node_getter/by_hfid.py +1 -3
  214. infrahub/graphql/mutations/node_getter/by_id.py +1 -3
  215. infrahub/graphql/mutations/node_getter/interface.py +1 -2
  216. infrahub/graphql/mutations/proposed_change.py +7 -7
  217. infrahub/graphql/mutations/relationship.py +93 -19
  218. infrahub/graphql/mutations/repository.py +8 -8
  219. infrahub/graphql/mutations/resource_manager.py +3 -3
  220. infrahub/graphql/mutations/schema.py +19 -4
  221. infrahub/graphql/mutations/webhook.py +137 -0
  222. infrahub/graphql/parser.py +4 -4
  223. infrahub/graphql/permissions.py +1 -10
  224. infrahub/graphql/queries/diff/tree.py +19 -14
  225. infrahub/graphql/queries/event.py +5 -2
  226. infrahub/graphql/queries/ipam.py +2 -2
  227. infrahub/graphql/queries/relationship.py +2 -2
  228. infrahub/graphql/queries/search.py +2 -2
  229. infrahub/graphql/resolvers/many_relationship.py +264 -0
  230. infrahub/graphql/resolvers/resolver.py +13 -110
  231. infrahub/graphql/schema.py +2 -0
  232. infrahub/graphql/subscription/graphql_query.py +2 -0
  233. infrahub/graphql/types/context.py +12 -0
  234. infrahub/graphql/types/event.py +84 -17
  235. infrahub/graphql/types/node.py +2 -2
  236. infrahub/graphql/utils.py +2 -2
  237. infrahub/groups/ancestors.py +29 -0
  238. infrahub/groups/parsers.py +107 -0
  239. infrahub/lock.py +20 -20
  240. infrahub/menu/constants.py +0 -1
  241. infrahub/menu/generator.py +9 -21
  242. infrahub/menu/menu.py +17 -38
  243. infrahub/menu/models.py +117 -16
  244. infrahub/menu/repository.py +111 -0
  245. infrahub/menu/utils.py +5 -8
  246. infrahub/message_bus/__init__.py +11 -13
  247. infrahub/message_bus/messages/__init__.py +1 -21
  248. infrahub/message_bus/messages/check_generator_run.py +3 -3
  249. infrahub/message_bus/messages/finalize_validator_execution.py +3 -0
  250. infrahub/message_bus/messages/proposed_change/request_proposedchange_refreshartifacts.py +6 -0
  251. infrahub/message_bus/messages/request_generatordefinition_check.py +2 -0
  252. infrahub/message_bus/messages/send_echo_request.py +1 -1
  253. infrahub/message_bus/operations/__init__.py +1 -10
  254. infrahub/message_bus/operations/check/__init__.py +2 -2
  255. infrahub/message_bus/operations/check/generator.py +1 -0
  256. infrahub/message_bus/operations/event/__init__.py +2 -2
  257. infrahub/message_bus/operations/event/worker.py +0 -3
  258. infrahub/message_bus/operations/finalize/validator.py +51 -1
  259. infrahub/message_bus/operations/requests/__init__.py +0 -2
  260. infrahub/message_bus/operations/requests/generator_definition.py +21 -23
  261. infrahub/message_bus/operations/requests/proposed_change.py +14 -10
  262. infrahub/permissions/globals.py +15 -0
  263. infrahub/pools/number.py +2 -4
  264. infrahub/proposed_change/models.py +3 -0
  265. infrahub/proposed_change/tasks.py +58 -45
  266. infrahub/pytest_plugin.py +13 -10
  267. infrahub/server.py +2 -3
  268. infrahub/services/__init__.py +2 -2
  269. infrahub/services/adapters/cache/__init__.py +4 -6
  270. infrahub/services/adapters/cache/nats.py +4 -5
  271. infrahub/services/adapters/cache/redis.py +3 -7
  272. infrahub/services/adapters/event/__init__.py +1 -1
  273. infrahub/services/adapters/message_bus/__init__.py +3 -3
  274. infrahub/services/adapters/message_bus/local.py +2 -2
  275. infrahub/services/adapters/message_bus/nats.py +4 -4
  276. infrahub/services/adapters/message_bus/rabbitmq.py +4 -4
  277. infrahub/services/adapters/workflow/local.py +2 -2
  278. infrahub/services/component.py +5 -5
  279. infrahub/services/protocols.py +7 -7
  280. infrahub/services/scheduler.py +1 -3
  281. infrahub/task_manager/event.py +102 -9
  282. infrahub/task_manager/models.py +27 -7
  283. infrahub/tasks/artifact.py +7 -6
  284. infrahub/telemetry/__init__.py +0 -0
  285. infrahub/telemetry/constants.py +9 -0
  286. infrahub/telemetry/database.py +86 -0
  287. infrahub/telemetry/models.py +65 -0
  288. infrahub/telemetry/task_manager.py +77 -0
  289. infrahub/{tasks/telemetry.py → telemetry/tasks.py} +49 -56
  290. infrahub/telemetry/utils.py +11 -0
  291. infrahub/trace.py +4 -4
  292. infrahub/transformations/tasks.py +2 -2
  293. infrahub/trigger/catalogue.py +4 -6
  294. infrahub/trigger/constants.py +0 -8
  295. infrahub/trigger/models.py +54 -5
  296. infrahub/trigger/setup.py +90 -0
  297. infrahub/trigger/tasks.py +35 -83
  298. infrahub/utils.py +11 -1
  299. infrahub/validators/__init__.py +0 -0
  300. infrahub/validators/events.py +42 -0
  301. infrahub/validators/tasks.py +41 -0
  302. infrahub/webhook/gather.py +17 -0
  303. infrahub/webhook/models.py +176 -44
  304. infrahub/webhook/tasks.py +154 -155
  305. infrahub/webhook/triggers.py +31 -7
  306. infrahub/workers/infrahub_async.py +2 -2
  307. infrahub/workers/utils.py +2 -2
  308. infrahub/workflows/catalogue.py +86 -35
  309. infrahub/workflows/initialization.py +8 -2
  310. infrahub/workflows/models.py +27 -1
  311. infrahub/workflows/utils.py +15 -6
  312. infrahub_sdk/client.py +35 -8
  313. infrahub_sdk/config.py +3 -0
  314. infrahub_sdk/context.py +13 -0
  315. infrahub_sdk/ctl/branch.py +3 -2
  316. infrahub_sdk/ctl/cli_commands.py +5 -1
  317. infrahub_sdk/ctl/utils.py +0 -16
  318. infrahub_sdk/exceptions.py +12 -0
  319. infrahub_sdk/generator.py +4 -1
  320. infrahub_sdk/graphql.py +45 -13
  321. infrahub_sdk/node.py +73 -22
  322. infrahub_sdk/protocols.py +21 -8
  323. infrahub_sdk/protocols_base.py +32 -11
  324. infrahub_sdk/query_groups.py +6 -35
  325. infrahub_sdk/schema/__init__.py +55 -26
  326. infrahub_sdk/schema/main.py +8 -0
  327. infrahub_sdk/task/__init__.py +11 -0
  328. infrahub_sdk/task/constants.py +3 -0
  329. infrahub_sdk/task/exceptions.py +25 -0
  330. infrahub_sdk/task/manager.py +551 -0
  331. infrahub_sdk/task/models.py +74 -0
  332. infrahub_sdk/testing/schemas/animal.py +9 -0
  333. infrahub_sdk/timestamp.py +142 -33
  334. infrahub_sdk/utils.py +29 -1
  335. {infrahub_server-1.2.0rc0.dist-info → infrahub_server-1.2.2.dist-info}/METADATA +8 -6
  336. {infrahub_server-1.2.0rc0.dist-info → infrahub_server-1.2.2.dist-info}/RECORD +349 -293
  337. {infrahub_server-1.2.0rc0.dist-info → infrahub_server-1.2.2.dist-info}/entry_points.txt +1 -0
  338. infrahub_testcontainers/constants.py +2 -0
  339. infrahub_testcontainers/container.py +157 -12
  340. infrahub_testcontainers/docker-compose.test.yml +31 -6
  341. infrahub_testcontainers/helpers.py +18 -73
  342. infrahub_testcontainers/host.py +41 -0
  343. infrahub_testcontainers/measurements.py +93 -0
  344. infrahub_testcontainers/models.py +38 -0
  345. infrahub_testcontainers/performance_test.py +166 -0
  346. infrahub_testcontainers/plugin.py +136 -0
  347. infrahub_testcontainers/prometheus.yml +30 -0
  348. infrahub/core/schema/definitions/core.py +0 -2286
  349. infrahub/message_bus/messages/check_repository_checkdefinition.py +0 -20
  350. infrahub/message_bus/messages/check_repository_mergeconflicts.py +0 -16
  351. infrahub/message_bus/messages/check_repository_usercheck.py +0 -26
  352. infrahub/message_bus/messages/event_branch_create.py +0 -11
  353. infrahub/message_bus/messages/event_branch_delete.py +0 -11
  354. infrahub/message_bus/messages/event_branch_rebased.py +0 -9
  355. infrahub/message_bus/messages/event_node_mutated.py +0 -15
  356. infrahub/message_bus/messages/event_schema_update.py +0 -9
  357. infrahub/message_bus/messages/request_repository_checks.py +0 -12
  358. infrahub/message_bus/messages/request_repository_userchecks.py +0 -18
  359. infrahub/message_bus/operations/check/repository.py +0 -293
  360. infrahub/message_bus/operations/event/node.py +0 -20
  361. infrahub/message_bus/operations/event/schema.py +0 -17
  362. infrahub/message_bus/operations/requests/repository.py +0 -133
  363. infrahub/webhook/constants.py +0 -1
  364. {infrahub_server-1.2.0rc0.dist-info → infrahub_server-1.2.2.dist-info}/LICENSE.txt +0 -0
  365. {infrahub_server-1.2.0rc0.dist-info → infrahub_server-1.2.2.dist-info}/WHEEL +0 -0
@@ -1,4 +1,4 @@
1
- from typing import Optional
1
+ from copy import copy
2
2
 
3
3
  from graphene import InputObjectType
4
4
 
@@ -21,21 +21,21 @@ class MutationNodeGetterByDefaultFilter(MutationNodeGetterInterface):
21
21
  node_schema: MainSchemaTypes,
22
22
  data: InputObjectType,
23
23
  branch: Branch,
24
- ) -> Optional[Node]:
25
- node = None
26
- default_filter_value = None
24
+ ) -> Node | None:
27
25
  if not node_schema.default_filter:
28
- return node
29
- this_datum = data
26
+ return None
27
+
28
+ data = copy(data)
30
29
 
31
30
  for filter_key in node_schema.default_filter.split("__"):
32
- if filter_key not in this_datum:
31
+ if filter_key not in data:
33
32
  break
34
- this_datum = this_datum[filter_key]
35
- default_filter_value = this_datum
33
+ data = data[filter_key]
34
+
35
+ default_filter_value = data
36
36
 
37
37
  if not default_filter_value:
38
- return node
38
+ return None
39
39
 
40
40
  return await self.node_manager.get_one_by_default_filter(
41
41
  db=self.db,
@@ -1,5 +1,3 @@
1
- from typing import Optional
2
-
3
1
  from graphene import InputObjectType
4
2
 
5
3
  from infrahub.core.branch import Branch
@@ -22,7 +20,7 @@ class MutationNodeGetterByHfid(MutationNodeGetterInterface):
22
20
  node_schema: MainSchemaTypes,
23
21
  data: InputObjectType,
24
22
  branch: Branch,
25
- ) -> Optional[Node]:
23
+ ) -> Node | None:
26
24
  if not node_schema.human_friendly_id:
27
25
  return None
28
26
 
@@ -1,5 +1,3 @@
1
- from typing import Optional
2
-
3
1
  from graphene import InputObjectType
4
2
 
5
3
  from infrahub.core.branch import Branch
@@ -21,7 +19,7 @@ class MutationNodeGetterById(MutationNodeGetterInterface):
21
19
  node_schema: MainSchemaTypes,
22
20
  data: InputObjectType,
23
21
  branch: Branch,
24
- ) -> Optional[Node]:
22
+ ) -> Node | None:
25
23
  node = None
26
24
  if "id" not in data:
27
25
  return node
@@ -1,5 +1,4 @@
1
1
  from abc import ABC, abstractmethod
2
- from typing import Optional
3
2
 
4
3
  from graphene import InputObjectType
5
4
 
@@ -15,4 +14,4 @@ class MutationNodeGetterInterface(ABC):
15
14
  node_schema: MainSchemaTypes,
16
15
  data: InputObjectType,
17
16
  branch: Branch,
18
- ) -> Optional[Node]: ...
17
+ ) -> Node | None: ...
@@ -1,4 +1,4 @@
1
- from typing import TYPE_CHECKING, Any, Optional
1
+ from typing import TYPE_CHECKING, Any, Self
2
2
 
3
3
  from graphene import Boolean, Field, InputObjectType, Mutation, String
4
4
  from graphql import GraphQLResolveInfo
@@ -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):
34
+ def __init_subclass_with_meta__(cls, schema: NodeSchema = None, _meta=None, **options) -> None:
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,8 +49,8 @@ class InfrahubProposedChangeMutation(InfrahubMutationMixin, Mutation):
49
49
  info: GraphQLResolveInfo,
50
50
  data: InputObjectType,
51
51
  branch: Branch,
52
- database: Optional[InfrahubDatabase] = None, # noqa: ARG003
53
- ):
52
+ database: InfrahubDatabase | None = None, # noqa: ARG003
53
+ ) -> tuple[Node, Self]:
54
54
  graphql_context: GraphqlContext = info.context
55
55
 
56
56
  async with graphql_context.db.start_transaction() as dbt:
@@ -86,9 +86,9 @@ class InfrahubProposedChangeMutation(InfrahubMutationMixin, Mutation):
86
86
  info: GraphQLResolveInfo,
87
87
  data: InputObjectType,
88
88
  branch: Branch,
89
- database: Optional[InfrahubDatabase] = None, # noqa: ARG003
90
- node: Optional[Node] = None, # noqa: ARG003
91
- ):
89
+ database: InfrahubDatabase | None = None, # noqa: ARG003
90
+ node: Node | None = None, # noqa: ARG003
91
+ ) -> tuple[Node, Self]:
92
92
  graphql_context: GraphqlContext = info.context
93
93
 
94
94
  obj = await NodeManager.get_one_by_id_or_default_filter(
@@ -8,10 +8,9 @@ from infrahub_sdk.utils import compare_lists
8
8
 
9
9
  from infrahub import config
10
10
  from infrahub.core.account import GlobalPermission, ObjectPermission
11
- from infrahub.core.changelog.models import NodeChangelog
11
+ from infrahub.core.changelog.models import NodeChangelog, RelationshipChangelogGetter
12
12
  from infrahub.core.constants import (
13
13
  InfrahubKind,
14
- MutationAction,
15
14
  PermissionAction,
16
15
  PermissionDecision,
17
16
  RelationshipCardinality,
@@ -24,10 +23,14 @@ from infrahub.core.query.relationship import (
24
23
  )
25
24
  from infrahub.core.relationship import Relationship
26
25
  from infrahub.database import retry_db_transaction
27
- from infrahub.events import EventMeta, NodeMutatedEvent
26
+ from infrahub.events import EventMeta
28
27
  from infrahub.events.group_action import GroupMemberAddedEvent, GroupMemberRemovedEvent
29
28
  from infrahub.events.models import EventNode
29
+ from infrahub.events.node_action import NodeUpdatedEvent
30
30
  from infrahub.exceptions import NodeNotFoundError, ValidationError
31
+ from infrahub.graphql.context import apply_external_context
32
+ from infrahub.graphql.types.context import ContextInput
33
+ from infrahub.groups.ancestors import collect_ancestors
31
34
  from infrahub.permissions import get_global_permission_for_kind
32
35
 
33
36
  from ..types import RelatedNodeInput
@@ -62,6 +65,7 @@ class RelationshipNodesInput(InputObjectType):
62
65
  class RelationshipAdd(Mutation):
63
66
  class Arguments:
64
67
  data = RelationshipNodesInput(required=True)
68
+ context = ContextInput(required=False)
65
69
 
66
70
  ok = Boolean()
67
71
 
@@ -72,6 +76,7 @@ class RelationshipAdd(Mutation):
72
76
  root: dict, # noqa: ARG003
73
77
  info: GraphQLResolveInfo,
74
78
  data: RelationshipNodesInput,
79
+ context: ContextInput | None = None,
75
80
  ) -> Self:
76
81
  graphql_context: GraphqlContext = info.context
77
82
  relationship_name = str(data.name)
@@ -81,6 +86,9 @@ class RelationshipAdd(Mutation):
81
86
  await _validate_permissions(info=info, source_node=source, peers=nodes)
82
87
  await _validate_peer_types(info=info, data=data, source_node=source, peers=nodes)
83
88
 
89
+ # This has to be done after validating the permissions
90
+ await apply_external_context(graphql_context=graphql_context, context_input=context)
91
+
84
92
  rel_schema = source.get_schema().get_relationship(name=relationship_name)
85
93
  display_label: str = await source.render_display_label(db=graphql_context.db)
86
94
  node_changelog = NodeChangelog(
@@ -89,6 +97,10 @@ class RelationshipAdd(Mutation):
89
97
 
90
98
  existing_peers = await _collect_current_peers(info=info, data=data, source_node=source)
91
99
 
100
+ group_event_type = _get_group_event_type(
101
+ node=source, relationship_schema=rel_schema, relationship_name=relationship_name
102
+ )
103
+
92
104
  async with graphql_context.db.start_transaction() as db:
93
105
  peers: list[EventNode] = []
94
106
  for node_data in data.get("nodes"):
@@ -99,19 +111,24 @@ class RelationshipAdd(Mutation):
99
111
  await rel.resolve(db=db)
100
112
  # Save it only if it does not exist
101
113
  if rel.get_peer_id() not in existing_peers.keys():
102
- peers.append(EventNode(id=rel.get_peer_id(), kind=rel.get_peer_kind()))
114
+ if group_event_type != GroupUpdateType.NONE:
115
+ peers.append(EventNode(id=rel.get_peer_id(), kind=nodes[rel.get_peer_id()].get_kind()))
103
116
  node_changelog.create_relationship(relationship=rel)
104
117
  await rel.save(db=db)
105
118
 
106
119
  if config.SETTINGS.broker.enable and graphql_context.background and node_changelog.has_changes:
107
- group_event_type = _get_group_event_type(
108
- node=source, relationship_schema=rel_schema, relationship_name=relationship_name
109
- )
110
120
  if group_event_type == GroupUpdateType.MEMBERS:
121
+ ancestors = await collect_ancestors(
122
+ db=graphql_context.db,
123
+ branch=graphql_context.branch,
124
+ node_kind=source.get_schema().kind,
125
+ node_id=source.id,
126
+ )
111
127
  group_add_event = GroupMemberAddedEvent(
112
128
  node_id=source.id,
113
129
  kind=source.get_schema().kind,
114
130
  members=peers,
131
+ ancestors=ancestors,
115
132
  meta=EventMeta(branch=graphql_context.branch, context=graphql_context.get_context()),
116
133
  )
117
134
  graphql_context.background.add_task(graphql_context.active_service.event.send, group_add_event)
@@ -124,24 +141,46 @@ class RelationshipAdd(Mutation):
124
141
  node_kind_map = await node_kind_query.get_node_kind_map()
125
142
 
126
143
  for node_id, node_kind in node_kind_map.items():
144
+ ancestors = await collect_ancestors(
145
+ db=graphql_context.db, branch=graphql_context.branch, node_kind=node_kind, node_id=node_id
146
+ )
127
147
  group_add_event = GroupMemberAddedEvent(
128
148
  node_id=node_id,
129
149
  kind=node_kind,
150
+ ancestors=ancestors,
130
151
  members=[EventNode(id=source.get_id(), kind=source.get_kind())],
131
152
  meta=EventMeta(branch=graphql_context.branch, context=graphql_context.get_context()),
132
153
  )
133
154
  graphql_context.background.add_task(graphql_context.active_service.event.send, group_add_event)
134
155
 
135
156
  else:
136
- event = NodeMutatedEvent(
157
+ main_event = NodeUpdatedEvent(
137
158
  kind=source.get_schema().kind,
138
159
  node_id=source.id,
139
- data=node_changelog,
140
- action=MutationAction.UPDATED,
160
+ changelog=node_changelog,
141
161
  fields=[relationship_name],
142
162
  meta=EventMeta(branch=graphql_context.branch, context=graphql_context.get_context()),
143
163
  )
144
- graphql_context.background.add_task(graphql_context.active_service.event.send, event)
164
+ relationship_changelogs = RelationshipChangelogGetter(
165
+ db=graphql_context.db, branch=graphql_context.branch
166
+ )
167
+ node_changelogs = await relationship_changelogs.get_changelogs(primary_changelog=node_changelog)
168
+
169
+ events = [main_event]
170
+
171
+ for node_changelog in node_changelogs:
172
+ meta = EventMeta.from_parent(parent=main_event)
173
+ event = NodeUpdatedEvent(
174
+ kind=node_changelog.node_kind,
175
+ node_id=node_changelog.node_id,
176
+ changelog=node_changelog,
177
+ fields=node_changelog.updated_fields,
178
+ meta=meta,
179
+ )
180
+ events.append(event)
181
+
182
+ for event in events:
183
+ graphql_context.background.add_task(graphql_context.active_service.event.send, event)
145
184
 
146
185
  return cls(ok=True)
147
186
 
@@ -149,6 +188,7 @@ class RelationshipAdd(Mutation):
149
188
  class RelationshipRemove(Mutation):
150
189
  class Arguments:
151
190
  data = RelationshipNodesInput(required=True)
191
+ context = ContextInput(required=False)
152
192
 
153
193
  ok = Boolean()
154
194
 
@@ -159,6 +199,7 @@ class RelationshipRemove(Mutation):
159
199
  root: dict, # noqa: ARG003
160
200
  info: GraphQLResolveInfo,
161
201
  data: RelationshipNodesInput,
202
+ context: ContextInput | None = None,
162
203
  ) -> Self:
163
204
  graphql_context: GraphqlContext = info.context
164
205
  relationship_name = str(data.name)
@@ -168,6 +209,9 @@ class RelationshipRemove(Mutation):
168
209
  await _validate_permissions(info=info, source_node=source, peers=nodes)
169
210
  await _validate_peer_types(info=info, data=data, source_node=source, peers=nodes)
170
211
 
212
+ # This has to be done after validating the permissions
213
+ await apply_external_context(graphql_context=graphql_context, context_input=context)
214
+
171
215
  rel_schema = source.get_schema().get_relationship(name=relationship_name)
172
216
  display_label: str = await source.render_display_label(db=graphql_context.db)
173
217
  node_changelog = NodeChangelog(
@@ -175,6 +219,9 @@ class RelationshipRemove(Mutation):
175
219
  )
176
220
 
177
221
  existing_peers = await _collect_current_peers(info=info, data=data, source_node=source)
222
+ group_event_type = _get_group_event_type(
223
+ node=source, relationship_schema=rel_schema, relationship_name=relationship_name
224
+ )
178
225
 
179
226
  async with graphql_context.db.start_transaction() as db:
180
227
  peers: list[EventNode] = []
@@ -186,19 +233,24 @@ class RelationshipRemove(Mutation):
186
233
  # it would be more query efficient
187
234
  rel = Relationship(schema=rel_schema, branch=graphql_context.branch, node=source)
188
235
  await rel.load(db=db, data=existing_peers[node_data.get("id")])
189
- peers.append(EventNode(id=rel.get_peer_id(), kind=rel.get_peer_kind()))
236
+ if group_event_type != GroupUpdateType.NONE:
237
+ peers.append(EventNode(id=rel.get_peer_id(), kind=nodes[rel.get_peer_id()].get_kind()))
190
238
  node_changelog.delete_relationship(relationship=rel)
191
239
  await rel.delete(db=db)
192
240
 
193
241
  if config.SETTINGS.broker.enable and graphql_context.background and node_changelog.has_changes:
194
- group_event_type = _get_group_event_type(
195
- node=source, relationship_schema=rel_schema, relationship_name=relationship_name
196
- )
197
242
  if group_event_type == GroupUpdateType.MEMBERS:
243
+ ancestors = await collect_ancestors(
244
+ db=graphql_context.db,
245
+ branch=graphql_context.branch,
246
+ node_kind=source.get_schema().kind,
247
+ node_id=source.id,
248
+ )
198
249
  group_remove_event = GroupMemberRemovedEvent(
199
250
  node_id=source.id,
200
251
  kind=source.get_schema().kind,
201
252
  members=peers,
253
+ ancestors=ancestors,
202
254
  meta=EventMeta(branch=graphql_context.branch, context=graphql_context.get_context()),
203
255
  )
204
256
  graphql_context.background.add_task(graphql_context.active_service.event.send, group_remove_event)
@@ -210,6 +262,9 @@ class RelationshipRemove(Mutation):
210
262
  node_kind_map = await node_kind_query.get_node_kind_map()
211
263
 
212
264
  for node_id, node_kind in node_kind_map.items():
265
+ ancestors = await collect_ancestors(
266
+ db=graphql_context.db, branch=graphql_context.branch, node_kind=node_kind, node_id=node_id
267
+ )
213
268
  group_remove_event = GroupMemberRemovedEvent(
214
269
  node_id=node_id,
215
270
  kind=node_kind,
@@ -220,15 +275,34 @@ class RelationshipRemove(Mutation):
220
275
  graphql_context.active_service.event.send, group_remove_event
221
276
  )
222
277
  else:
223
- event = NodeMutatedEvent(
278
+ main_event = NodeUpdatedEvent(
224
279
  kind=source.get_schema().kind,
225
280
  node_id=source.id,
226
- data=node_changelog,
227
- action=MutationAction.UPDATED,
281
+ changelog=node_changelog,
228
282
  fields=[relationship_name],
229
283
  meta=EventMeta(branch=graphql_context.branch, context=graphql_context.get_context()),
230
284
  )
231
- graphql_context.background.add_task(graphql_context.active_service.event.send, event)
285
+
286
+ relationship_changelogs = RelationshipChangelogGetter(
287
+ db=graphql_context.db, branch=graphql_context.branch
288
+ )
289
+ node_changelogs = await relationship_changelogs.get_changelogs(primary_changelog=node_changelog)
290
+
291
+ events = [main_event]
292
+
293
+ for node_changelog in node_changelogs:
294
+ meta = EventMeta.from_parent(parent=main_event)
295
+ event = NodeUpdatedEvent(
296
+ kind=node_changelog.node_kind,
297
+ node_id=node_changelog.node_id,
298
+ changelog=node_changelog,
299
+ fields=node_changelog.updated_fields,
300
+ meta=meta,
301
+ )
302
+ events.append(event)
303
+
304
+ for event in events:
305
+ graphql_context.background.add_task(graphql_context.active_service.event.send, event)
232
306
 
233
307
  return cls(ok=True)
234
308
 
@@ -1,7 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import re
4
- from typing import TYPE_CHECKING, Any, Optional, cast
4
+ from typing import TYPE_CHECKING, Any, Self, cast
5
5
 
6
6
  import httpx
7
7
  from graphene import Boolean, Field, InputObjectType, Mutation, String
@@ -45,7 +45,7 @@ log = get_logger()
45
45
 
46
46
  class InfrahubRepositoryMutation(InfrahubMutationMixin, Mutation):
47
47
  @classmethod
48
- def __init_subclass_with_meta__(cls, schema: Optional[NodeSchema] = None, _meta=None, **options):
48
+ def __init_subclass_with_meta__(cls, schema: NodeSchema | None = None, _meta=None, **options) -> None:
49
49
  # Make sure schema is a valid NodeSchema Node Class
50
50
  if not isinstance(schema, NodeSchema):
51
51
  raise ValueError(f"You need to pass a valid NodeSchema in '{cls.__name__}.Meta', received '{schema}'")
@@ -63,8 +63,8 @@ class InfrahubRepositoryMutation(InfrahubMutationMixin, Mutation):
63
63
  info: GraphQLResolveInfo,
64
64
  data: InputObjectType,
65
65
  branch: Branch,
66
- database: Optional[InfrahubDatabase] = None, # noqa: ARG003
67
- ):
66
+ database: InfrahubDatabase | None = None, # noqa: ARG003
67
+ ) -> tuple[Node, Self]:
68
68
  graphql_context: GraphqlContext = info.context
69
69
 
70
70
  cleanup_payload(data)
@@ -149,9 +149,9 @@ class InfrahubRepositoryMutation(InfrahubMutationMixin, Mutation):
149
149
  info: GraphQLResolveInfo,
150
150
  data: InputObjectType,
151
151
  branch: Branch,
152
- database: Optional[InfrahubDatabase] = None, # noqa: ARG003
153
- node: Optional[Node] = None,
154
- ):
152
+ database: InfrahubDatabase | None = None, # noqa: ARG003
153
+ node: Node | None = None,
154
+ ) -> tuple[Node, Self]:
155
155
  graphql_context: GraphqlContext = info.context
156
156
 
157
157
  cleanup_payload(data)
@@ -219,7 +219,7 @@ def cleanup_payload(data: InputObjectType | dict[str, Any]) -> None:
219
219
  ):
220
220
  url = httpx.URL(data["location"]["value"])
221
221
  if url.host in config.SETTINGS.git.append_git_suffix:
222
- data["location"]["value"] = f'{data["location"]["value"]}.git'
222
+ data["location"]["value"] = f"{data['location']['value']}.git"
223
223
 
224
224
 
225
225
  class ProcessRepository(Mutation):
@@ -2,7 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  from typing import TYPE_CHECKING, Any
4
4
 
5
- from graphene import Boolean, Field, InputField, InputObjectType, Int, Mutation, String
5
+ from graphene import Boolean, Field, InputField, InputObjectType, Int, List, Mutation, String
6
6
  from graphene.types.generic import GenericScalar
7
7
  from typing_extensions import Self
8
8
 
@@ -30,7 +30,7 @@ if TYPE_CHECKING:
30
30
 
31
31
  class IPPrefixPoolGetResourceInput(InputObjectType):
32
32
  id = InputField(String(required=False), description="ID of the pool to allocate from")
33
- hfid = InputField(String(required=False), description="HFID of the pool to allocate from")
33
+ hfid = InputField(List(of_type=String, required=False), description="HFID of the pool to allocate from")
34
34
  identifier = InputField(String(required=False), description="Identifier for the allocated resource")
35
35
  prefix_length = InputField(Int(required=False), description="Size of the prefix to allocate")
36
36
  member_type = InputField(String(required=False), description="Type of members for the newly created prefix")
@@ -40,7 +40,7 @@ class IPPrefixPoolGetResourceInput(InputObjectType):
40
40
 
41
41
  class IPAddressPoolGetResourceInput(InputObjectType):
42
42
  id = InputField(String(required=False), description="ID of the pool to allocate from")
43
- hfid = InputField(String(required=False), description="HFID of the pool to allocate from")
43
+ hfid = InputField(List(of_type=String, required=False), description="HFID of the pool to allocate from")
44
44
  identifier = InputField(String(required=False), description="Identifier for the allocated resource")
45
45
  prefix_length = InputField(
46
46
  Int(required=False), description="Size of the prefix mask to allocate on the new IP address"
@@ -1,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import TYPE_CHECKING, Self, Union
3
+ from typing import TYPE_CHECKING, Self
4
4
 
5
5
  from graphene import Boolean, Field, InputObjectType, Mutation, String
6
6
 
@@ -13,6 +13,8 @@ from infrahub.database import InfrahubDatabase, retry_db_transaction
13
13
  from infrahub.events import EventMeta
14
14
  from infrahub.events.schema_action import SchemaUpdatedEvent
15
15
  from infrahub.exceptions import ValidationError
16
+ from infrahub.graphql.context import apply_external_context
17
+ from infrahub.graphql.types.context import ContextInput
16
18
  from infrahub.log import get_log_data, get_logger
17
19
  from infrahub.worker import WORKER_IDENTITY
18
20
 
@@ -51,6 +53,7 @@ class SchemaDropdownAddInput(SchemaDropdownRemoveInput):
51
53
  class SchemaDropdownAdd(Mutation):
52
54
  class Arguments:
53
55
  data = SchemaDropdownAddInput(required=True)
56
+ context = ContextInput(required=False)
54
57
 
55
58
  ok = Boolean()
56
59
  object = Field(DropdownFields)
@@ -62,9 +65,12 @@ class SchemaDropdownAdd(Mutation):
62
65
  root: dict, # noqa: ARG003
63
66
  info: GraphQLResolveInfo,
64
67
  data: SchemaDropdownAddInput,
68
+ context: ContextInput | None = None,
65
69
  ) -> Self:
66
70
  graphql_context: GraphqlContext = info.context
67
71
 
72
+ await apply_external_context(graphql_context=graphql_context, context_input=context)
73
+
68
74
  kind = graphql_context.db.schema.get(name=str(data.kind), branch=graphql_context.branch.name)
69
75
  attribute = str(data.attribute)
70
76
  validate_kind_dropdown(kind=kind, attribute=attribute)
@@ -109,6 +115,7 @@ class SchemaDropdownAdd(Mutation):
109
115
  class SchemaDropdownRemove(Mutation):
110
116
  class Arguments:
111
117
  data = SchemaDropdownRemoveInput(required=True)
118
+ context = ContextInput(required=False)
112
119
 
113
120
  ok = Boolean()
114
121
 
@@ -119,10 +126,12 @@ class SchemaDropdownRemove(Mutation):
119
126
  root: dict, # noqa: ARG003
120
127
  info: GraphQLResolveInfo,
121
128
  data: SchemaDropdownRemoveInput,
129
+ context: ContextInput | None = None,
122
130
  ) -> dict[str, bool]:
123
131
  graphql_context: GraphqlContext = info.context
124
132
 
125
133
  kind = graphql_context.db.schema.get(name=str(data.kind), branch=graphql_context.branch.name)
134
+ await apply_external_context(graphql_context=graphql_context, context_input=context)
126
135
 
127
136
  attribute = str(data.attribute)
128
137
  validate_kind_dropdown(kind=kind, attribute=attribute)
@@ -161,6 +170,7 @@ class SchemaDropdownRemove(Mutation):
161
170
  class SchemaEnumAdd(Mutation):
162
171
  class Arguments:
163
172
  data = SchemaEnumInput(required=True)
173
+ context = ContextInput(required=False)
164
174
 
165
175
  ok = Boolean()
166
176
 
@@ -171,10 +181,12 @@ class SchemaEnumAdd(Mutation):
171
181
  root: dict, # noqa: ARG003
172
182
  info: GraphQLResolveInfo,
173
183
  data: SchemaEnumInput,
184
+ context: ContextInput | None = None,
174
185
  ) -> dict[str, bool]:
175
186
  graphql_context: GraphqlContext = info.context
176
187
 
177
188
  kind = graphql_context.db.schema.get(name=str(data.kind), branch=graphql_context.branch.name)
189
+ await apply_external_context(graphql_context=graphql_context, context_input=context)
178
190
 
179
191
  attribute = str(data.attribute)
180
192
  enum = str(data.enum)
@@ -203,6 +215,7 @@ class SchemaEnumAdd(Mutation):
203
215
  class SchemaEnumRemove(Mutation):
204
216
  class Arguments:
205
217
  data = SchemaEnumInput(required=True)
218
+ context = ContextInput(required=False)
206
219
 
207
220
  ok = Boolean()
208
221
 
@@ -213,10 +226,12 @@ class SchemaEnumRemove(Mutation):
213
226
  root: dict, # noqa: ARG003
214
227
  info: GraphQLResolveInfo,
215
228
  data: SchemaEnumInput,
229
+ context: ContextInput | None = None,
216
230
  ) -> dict[str, bool]:
217
231
  graphql_context: GraphqlContext = info.context
218
232
 
219
233
  kind = graphql_context.db.schema.get(name=str(data.kind), branch=graphql_context.branch.name)
234
+ await apply_external_context(graphql_context=graphql_context, context_input=context)
220
235
 
221
236
  attribute = str(data.attribute)
222
237
  enum = str(data.enum)
@@ -252,21 +267,21 @@ class SchemaEnumRemove(Mutation):
252
267
  return {"ok": True}
253
268
 
254
269
 
255
- def validate_kind_dropdown(kind: Union[GenericSchema, NodeSchema], attribute: str) -> None:
270
+ def validate_kind_dropdown(kind: GenericSchema | NodeSchema, attribute: str) -> None:
256
271
  validate_kind(kind=kind, attribute=attribute)
257
272
  matching_attribute = [attrib for attrib in kind.attributes if attrib.name == attribute]
258
273
  if matching_attribute and matching_attribute[0].kind != "Dropdown":
259
274
  raise ValidationError(f"Attribute {attribute} on {kind.kind} is not a Dropdown")
260
275
 
261
276
 
262
- def validate_kind_enum(kind: Union[GenericSchema, NodeSchema], attribute: str) -> None:
277
+ def validate_kind_enum(kind: GenericSchema | NodeSchema, attribute: str) -> None:
263
278
  validate_kind(kind=kind, attribute=attribute)
264
279
  matching_attribute = [attrib for attrib in kind.attributes if attrib.name == attribute]
265
280
  if not matching_attribute[0].enum:
266
281
  raise ValidationError(f"Attribute {attribute} on {kind.kind} is not an enum")
267
282
 
268
283
 
269
- def validate_kind(kind: Union[GenericSchema, NodeSchema], attribute: str) -> None:
284
+ def validate_kind(kind: GenericSchema | NodeSchema, attribute: str) -> None:
270
285
  if kind.namespace in RESTRICTED_NAMESPACES:
271
286
  raise ValidationError(f"Operation not allowed for {kind.kind} in restricted namespace {kind.namespace}")
272
287
  if attribute not in kind.attribute_names: