infrahub-server 1.2.0rc0__py3-none-any.whl → 1.2.1__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 +77 -441
  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 +82 -32
  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 +32 -24
  183. infrahub/generators/models.py +2 -3
  184. infrahub/generators/tasks.py +24 -4
  185. infrahub/git/base.py +7 -7
  186. infrahub/git/integrator.py +48 -24
  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 -84
  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 +10 -1
  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 +71 -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.1.dist-info}/METADATA +8 -6
  336. {infrahub_server-1.2.0rc0.dist-info → infrahub_server-1.2.1.dist-info}/RECORD +349 -293
  337. {infrahub_server-1.2.0rc0.dist-info → infrahub_server-1.2.1.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.1.dist-info}/LICENSE.txt +0 -0
  365. {infrahub_server-1.2.0rc0.dist-info → infrahub_server-1.2.1.dist-info}/WHEEL +0 -0
@@ -1,19 +1,18 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import TYPE_CHECKING, Any, Optional
3
+ from typing import TYPE_CHECKING, Any
4
4
 
5
5
  from graphql.type.definition import GraphQLNonNull
6
6
  from infrahub_sdk.utils import extract_fields
7
+ from opentelemetry import trace
7
8
 
8
9
  from infrahub.core.constants import BranchSupportType, InfrahubKind, RelationshipHierarchyDirection
9
10
  from infrahub.core.manager import NodeManager
10
- from infrahub.core.query.node import NodeGetHierarchyQuery
11
11
  from infrahub.exceptions import NodeNotFoundError
12
12
 
13
13
  from ..models import OrderModel
14
14
  from ..parser import extract_selection
15
15
  from ..permissions import get_permissions
16
- from ..types import RELATIONS_PROPERTY_MAP, RELATIONS_PROPERTY_MAP_REVERSED
17
16
 
18
17
  if TYPE_CHECKING:
19
18
  from graphql import GraphQLResolveInfo
@@ -22,6 +21,7 @@ if TYPE_CHECKING:
22
21
  from infrahub.graphql.initialization import GraphqlContext
23
22
 
24
23
 
24
+ @trace.get_tracer(__name__).start_as_current_span("account_resolver")
25
25
  async def account_resolver(
26
26
  root: dict, # noqa: ARG001
27
27
  info: GraphQLResolveInfo,
@@ -46,6 +46,7 @@ async def account_resolver(
46
46
  )
47
47
 
48
48
 
49
+ @trace.get_tracer(__name__).start_as_current_span("default_resolver")
49
50
  async def default_resolver(*args: Any, **kwargs) -> dict | list[dict] | None:
50
51
  """Not sure why but the default resolver returns sometime 4 positional args and sometime 2.
51
52
 
@@ -128,6 +129,7 @@ async def default_resolver(*args: Any, **kwargs) -> dict | list[dict] | None:
128
129
  return await objs[0].to_graphql(db=db, fields=fields, related_node_ids=graphql_context.related_node_ids)
129
130
 
130
131
 
132
+ @trace.get_tracer(__name__).start_as_current_span("parent_field_name_resolver")
131
133
  async def parent_field_name_resolver(parent: dict[str, dict], info: GraphQLResolveInfo) -> dict:
132
134
  """This resolver gets used when we know that the parent resolver has already gathered the required information.
133
135
 
@@ -137,6 +139,7 @@ async def parent_field_name_resolver(parent: dict[str, dict], info: GraphQLResol
137
139
  return parent[info.field_name]
138
140
 
139
141
 
142
+ @trace.get_tracer(__name__).start_as_current_span("default_paginated_list_resolver")
140
143
  async def default_paginated_list_resolver(
141
144
  root: dict, # noqa: ARG001
142
145
  info: GraphQLResolveInfo,
@@ -164,7 +167,7 @@ async def default_paginated_list_resolver(
164
167
  edges = fields.get("edges", {})
165
168
  node_fields = edges.get("node", {})
166
169
 
167
- permission_set: Optional[dict[str, Any]] = None
170
+ permission_set: dict[str, Any] | None = None
168
171
  permissions = (
169
172
  await get_permissions(schema=schema, graphql_context=graphql_context)
170
173
  if graphql_context.permissions
@@ -227,121 +230,20 @@ async def default_paginated_list_resolver(
227
230
  return response
228
231
 
229
232
 
233
+ @trace.get_tracer(__name__).start_as_current_span("single_relationship_resolver")
230
234
  async def single_relationship_resolver(parent: dict, info: GraphQLResolveInfo, **kwargs: Any) -> dict[str, Any]:
231
235
  graphql_context: GraphqlContext = info.context
232
236
  resolver = graphql_context.single_relationship_resolver
233
237
  return await resolver.resolve(parent=parent, info=info, **kwargs)
234
238
 
235
239
 
240
+ @trace.get_tracer(__name__).start_as_current_span("many_relationship_resolver")
236
241
  async def many_relationship_resolver(
237
- parent: dict, info: GraphQLResolveInfo, include_descendants: Optional[bool] = False, **kwargs: Any
242
+ parent: dict, info: GraphQLResolveInfo, include_descendants: bool | None = False, **kwargs: Any
238
243
  ) -> dict[str, Any]:
239
- """Resolver for relationships of cardinality=many for Edged responses
240
-
241
- This resolver is used for paginated responses and as such we redefined the requested
242
- fields by only reusing information below the 'node' key.
243
- """
244
- # Extract the InfraHub schema by inspecting the GQL Schema
245
- node_schema: NodeSchema = (
246
- info.parent_type.of_type.graphene_type._meta.schema
247
- if isinstance(info.parent_type, GraphQLNonNull)
248
- else info.parent_type.graphene_type._meta.schema
249
- )
250
-
251
244
  graphql_context: GraphqlContext = info.context
252
-
253
- # Extract the name of the fields in the GQL query
254
- fields = await extract_fields(info.field_nodes[0].selection_set)
255
- edges = fields.get("edges", {})
256
- node_fields = edges.get("node", {})
257
- property_fields = edges.get("properties", {})
258
- for key, value in property_fields.items():
259
- mapped_name = RELATIONS_PROPERTY_MAP[key]
260
- node_fields[mapped_name] = value
261
-
262
- # Extract the schema of the node on the other end of the relationship from the GQL Schema
263
- node_rel = node_schema.get_relationship(info.field_name)
264
-
265
- # Extract only the filters from the kwargs and prepend the name of the field to the filters
266
- offset = kwargs.pop("offset", None)
267
- limit = kwargs.pop("limit", None)
268
-
269
- filters = {
270
- f"{info.field_name}__{key}": value
271
- for key, value in kwargs.items()
272
- if "__" in key and value or key in ["id", "ids"]
273
- }
274
-
275
- response: dict[str, Any] = {"edges": [], "count": None}
276
-
277
- source_kind = node_schema.kind
278
-
279
- async with graphql_context.db.start_session() as db:
280
- ids = [parent["id"]]
281
- if include_descendants:
282
- query = await NodeGetHierarchyQuery.init(
283
- db=db,
284
- direction=RelationshipHierarchyDirection.DESCENDANTS,
285
- node_id=parent["id"],
286
- node_schema=node_schema,
287
- at=graphql_context.at,
288
- branch=graphql_context.branch,
289
- )
290
- if node_schema.hierarchy:
291
- source_kind = node_schema.hierarchy
292
- await query.execute(db=db)
293
- descendants_ids = list(query.get_peer_ids())
294
- ids.extend(descendants_ids)
295
-
296
- if "count" in fields:
297
- response["count"] = await NodeManager.count_peers(
298
- db=db,
299
- ids=ids,
300
- source_kind=source_kind,
301
- schema=node_rel,
302
- filters=filters,
303
- at=graphql_context.at,
304
- branch=graphql_context.branch,
305
- branch_agnostic=node_rel.branch is BranchSupportType.AGNOSTIC,
306
- )
307
-
308
- if not node_fields:
309
- return response
310
-
311
- objs = await NodeManager.query_peers(
312
- db=db,
313
- ids=ids,
314
- source_kind=source_kind,
315
- schema=node_rel,
316
- filters=filters,
317
- fields=node_fields,
318
- offset=offset,
319
- limit=limit,
320
- at=graphql_context.at,
321
- branch=graphql_context.branch,
322
- branch_agnostic=node_rel.branch is BranchSupportType.AGNOSTIC,
323
- fetch_peers=True,
324
- )
325
-
326
- if not objs:
327
- return response
328
- node_graph = [
329
- await obj.to_graphql(db=db, fields=node_fields, related_node_ids=graphql_context.related_node_ids)
330
- for obj in objs
331
- ]
332
-
333
- entries = []
334
- for node in node_graph:
335
- entry = {"node": {}, "properties": {}}
336
- for key, mapped in RELATIONS_PROPERTY_MAP_REVERSED.items():
337
- value = node.pop(key, None)
338
- if value:
339
- entry["properties"][mapped] = value
340
- entry["node"] = node
341
- entries.append(entry)
342
- response["edges"] = entries
343
-
344
- return response
245
+ resolver = graphql_context.many_relationship_resolver
246
+ return await resolver.resolve(parent=parent, info=info, include_descendants=include_descendants, **kwargs)
345
247
 
346
248
 
347
249
  async def ancestors_resolver(parent: dict, info: GraphQLResolveInfo, **kwargs) -> dict[str, Any]:
@@ -356,6 +258,7 @@ async def descendants_resolver(parent: dict, info: GraphQLResolveInfo, **kwargs)
356
258
  )
357
259
 
358
260
 
261
+ @trace.get_tracer(__name__).start_as_current_span("hierarchy_resolver")
359
262
  async def hierarchy_resolver(
360
263
  direction: RelationshipHierarchyDirection, parent: dict, info: GraphQLResolveInfo, **kwargs
361
264
  ) -> dict[str, Any]:
@@ -18,6 +18,7 @@ from .mutations.branch import (
18
18
  from .mutations.computed_attribute import UpdateComputedAttribute
19
19
  from .mutations.diff import DiffUpdateMutation
20
20
  from .mutations.diff_conflict import ResolveDiffConflict
21
+ from .mutations.generator import GeneratorDefinitionRequestRun
21
22
  from .mutations.proposed_change import ProposedChangeMerge, ProposedChangeRequestRunCheck
22
23
  from .mutations.relationship import (
23
24
  RelationshipAdd,
@@ -83,6 +84,7 @@ class InfrahubBaseMutation(ObjectType):
83
84
  InfrahubAccountTokenDelete = InfrahubAccountTokenDelete.Field()
84
85
  CoreProposedChangeRunCheck = ProposedChangeRequestRunCheck.Field()
85
86
  CoreProposedChangeMerge = ProposedChangeMerge.Field()
87
+ CoreGeneratorDefinitionRun = GeneratorDefinitionRequestRun.Field()
86
88
 
87
89
  IPPrefixPoolGetResource = IPPrefixPoolGetResource.Field()
88
90
  IPAddressPoolGetResource = IPAddressPoolGetResource.Field()
@@ -8,6 +8,7 @@ from infrahub.core.constants import InfrahubKind
8
8
  from infrahub.core.manager import NodeManager
9
9
  from infrahub.core.protocols import CoreGraphQLQuery
10
10
  from infrahub.core.timestamp import Timestamp
11
+ from infrahub.graphql.resolvers.many_relationship import ManyRelationshipResolver
11
12
  from infrahub.graphql.resolvers.single_relationship import SingleRelationshipResolver
12
13
  from infrahub.log import get_logger
13
14
 
@@ -48,6 +49,7 @@ async def resolver_graphql_query(
48
49
  related_node_ids=set(),
49
50
  types=graphql_context.types,
50
51
  single_relationship_resolver=SingleRelationshipResolver(),
52
+ many_relationship_resolver=ManyRelationshipResolver(),
51
53
  ),
52
54
  root_value=None,
53
55
  variable_values=params or {},
@@ -0,0 +1,12 @@
1
+ from graphene import InputObjectType, String
2
+
3
+
4
+ class ContextAccountInput(InputObjectType):
5
+ id = String(required=True, description="The Infrahub ID of the account")
6
+
7
+
8
+ class ContextInput(InputObjectType):
9
+ account = ContextAccountInput(
10
+ required=False,
11
+ description="The account context can be used to override the account information that will be associated with the mutation",
12
+ )
@@ -2,9 +2,11 @@ from __future__ import annotations
2
2
 
3
3
  from typing import TYPE_CHECKING, Any
4
4
 
5
- from graphene import Boolean, DateTime, Field, Int, Interface, List, NonNull, ObjectType, String
5
+ from graphene import Boolean, DateTime, Field, InputObjectType, Int, Interface, List, NonNull, ObjectType, String
6
6
  from graphene.types.generic import GenericScalar
7
7
 
8
+ from infrahub import events
9
+
8
10
  from .common import RelatedNode
9
11
  from .enums import DiffAction
10
12
 
@@ -20,17 +22,32 @@ class InfrahubMutatedAttribute(ObjectType):
20
22
  value_previous = String(required=False)
21
23
 
22
24
 
25
+ class InfrahubMutatedRelationship(ObjectType):
26
+ name = String(required=True)
27
+ action = DiffAction(required=True)
28
+ peer = Field(RelatedNode, required=True)
29
+
30
+
23
31
  class EventNodeInterface(Interface):
24
- id = String(required=True)
25
- event = String(required=True)
26
- branch = String(required=False)
27
- account_id = String(required=False)
28
- occurred_at = DateTime(required=True)
29
- level = Int(required=True)
30
- primary_node = Field(RelatedNode, required=False)
31
- related_nodes = List(NonNull(RelatedNode), required=True)
32
- has_children = Boolean(required=True)
33
- parent_id = String(required=False)
32
+ id = String(required=True, description="The ID of the event.")
33
+ event = String(required=True, description="The name of the event.")
34
+ branch = String(required=False, description="The branch where the event occurred.")
35
+ account_id = String(required=False, description="The account ID that triggered the event.")
36
+ occurred_at = DateTime(required=True, description="The timestamp when the event occurred.")
37
+ level = Int(
38
+ required=True,
39
+ description="The level of the event 0 is a root level event, the child events will have 1 and grand children 2.",
40
+ )
41
+ primary_node = Field(
42
+ RelatedNode, required=False, description="The primary Infrahub node this event is associated with."
43
+ )
44
+ related_nodes = List(
45
+ NonNull(RelatedNode), required=True, description="Related Infrahub nodes this event is associated with."
46
+ )
47
+ has_children = Boolean(
48
+ required=True, description="Indicates if the event is expected to have child events under it"
49
+ )
50
+ parent_id = String(required=False, description="The event ID of the direct parent to this event.")
34
51
 
35
52
  @classmethod
36
53
  def resolve_type(
@@ -47,6 +64,19 @@ class EventNodes(ObjectType):
47
64
  node = Field(EventNodeInterface)
48
65
 
49
66
 
67
+ class BranchEventTypeFilter(InputObjectType):
68
+ branches = List(NonNull(String), required=True, description="Name of impacted branches")
69
+
70
+
71
+ class EventTypeFilter(InputObjectType):
72
+ branch_merged = Field(
73
+ BranchEventTypeFilter, required=False, description="Filters specific to infrahub.branch.merged events"
74
+ )
75
+ branch_rebased = Field(
76
+ BranchEventTypeFilter, required=False, description="Filters specific to infrahub.branch.rebased events"
77
+ )
78
+
79
+
50
80
  # ---------------------------------------
51
81
  # Branch events
52
82
  # ---------------------------------------
@@ -54,13 +84,24 @@ class BranchCreatedEvent(ObjectType):
54
84
  class Meta:
55
85
  interfaces = (EventNodeInterface,)
56
86
 
87
+ created_branch = String(required=True, description="The name of the branch that was created")
57
88
  payload = Field(GenericScalar, required=True)
58
89
 
59
90
 
91
+ class BranchMergedEvent(ObjectType):
92
+ class Meta:
93
+ interfaces = (EventNodeInterface,)
94
+
95
+ source_branch = String(required=True, description="The name of the branch that was merged into the default branch")
96
+
97
+
60
98
  class BranchRebasedEvent(ObjectType):
61
99
  class Meta:
62
100
  interfaces = (EventNodeInterface,)
63
101
 
102
+ rebased_branch = String(
103
+ required=True, description="The name of the branch that was rebased and aligned with the default branch"
104
+ )
64
105
  payload = Field(GenericScalar, required=True)
65
106
 
66
107
 
@@ -68,6 +109,7 @@ class BranchDeletedEvent(ObjectType):
68
109
  class Meta:
69
110
  interfaces = (EventNodeInterface,)
70
111
 
112
+ deleted_branch = String(required=True, description="The name of the branch that was deleted")
71
113
  payload = Field(GenericScalar, required=True)
72
114
 
73
115
 
@@ -80,6 +122,26 @@ class NodeMutatedEvent(ObjectType):
80
122
 
81
123
  payload = Field(GenericScalar, required=True)
82
124
  attributes = Field(List(of_type=NonNull(InfrahubMutatedAttribute), required=True), required=True)
125
+ relationships = Field(List(of_type=NonNull(InfrahubMutatedRelationship), required=True), required=True)
126
+
127
+
128
+ class ArtifactEvent(ObjectType):
129
+ class Meta:
130
+ interfaces = (EventNodeInterface,)
131
+
132
+ checksum = String(required=True, description="The current checksum of the artifact")
133
+ checksum_previous = String(required=False, description="The previous checksum of the artifact")
134
+ storage_id = String(required=True, description="The current storage_id of the artifact")
135
+ storage_id_previous = String(required=False, description="The previous storage_id of the artifact")
136
+ artifact_definition_id = String(required=True, description="Artifact definition ID")
137
+
138
+
139
+ class GroupEvent(ObjectType):
140
+ class Meta:
141
+ interfaces = (EventNodeInterface,)
142
+
143
+ members = List(NonNull(RelatedNode), required=True, description="Group members modified in this event")
144
+ ancestors = List(NonNull(RelatedNode), required=True, description="Ancestor groups of this impacted group")
83
145
 
84
146
 
85
147
  class StandardEvent(ObjectType):
@@ -90,11 +152,16 @@ class StandardEvent(ObjectType):
90
152
 
91
153
 
92
154
  EVENT_TYPES: dict[str, type[ObjectType]] = {
93
- "infrahub.node.created": NodeMutatedEvent,
94
- "infrahub.node.updated": NodeMutatedEvent,
95
- "infrahub.node.deleted": NodeMutatedEvent,
96
- "infrahub.branch.created": BranchCreatedEvent,
97
- "infrahub.branch.rebased": BranchRebasedEvent,
98
- "infrahub.branch.deleted": BranchDeletedEvent,
155
+ events.ArtifactCreatedEvent.event_name: ArtifactEvent,
156
+ events.ArtifactUpdatedEvent.event_name: ArtifactEvent,
157
+ events.NodeCreatedEvent.event_name: NodeMutatedEvent,
158
+ events.NodeUpdatedEvent.event_name: NodeMutatedEvent,
159
+ events.NodeDeletedEvent.event_name: NodeMutatedEvent,
160
+ events.BranchCreatedEvent.event_name: BranchCreatedEvent,
161
+ events.BranchMergedEvent.event_name: BranchMergedEvent,
162
+ events.BranchRebasedEvent.event_name: BranchRebasedEvent,
163
+ events.BranchDeletedEvent.event_name: BranchDeletedEvent,
164
+ events.GroupMemberAddedEvent.event_name: GroupEvent,
165
+ events.GroupMemberRemovedEvent.event_name: GroupEvent,
99
166
  "undefined": StandardEvent,
100
167
  }
@@ -1,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import Any, Optional
3
+ from typing import Any
4
4
 
5
5
  from graphene import ObjectType
6
6
  from graphene.types.objecttype import ObjectTypeOptions
@@ -16,7 +16,7 @@ class InfrahubObject(ObjectType):
16
16
  @classmethod
17
17
  def __init_subclass_with_meta__(
18
18
  cls,
19
- schema: Optional[MainSchemaTypes] = None,
19
+ schema: MainSchemaTypes | None = None,
20
20
  interfaces: tuple = (),
21
21
  _meta: InfrahubObjectOptions | None = None,
22
22
  **options: Any,
infrahub/graphql/utils.py CHANGED
@@ -1,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import TYPE_CHECKING, Union
3
+ from typing import TYPE_CHECKING
4
4
 
5
5
  from graphene.types.definitions import GrapheneInterfaceType, GrapheneObjectType
6
6
  from graphql import (
@@ -50,7 +50,7 @@ def find_types_implementing_interface(
50
50
 
51
51
 
52
52
  async def extract_schema_models(
53
- fields: dict, schema: Union[GrapheneObjectType, GraphQLUnionType], root_schema: GraphQLSchema
53
+ fields: dict, schema: GrapheneObjectType | GraphQLUnionType, root_schema: GraphQLSchema
54
54
  ) -> set[str]:
55
55
  response = set()
56
56
 
@@ -0,0 +1,29 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ from infrahub.core.constants import RelationshipHierarchyDirection
6
+ from infrahub.core.manager import NodeManager
7
+ from infrahub.core.schema import NodeSchema
8
+ from infrahub.events.models import EventNode
9
+
10
+ if TYPE_CHECKING:
11
+ from infrahub.core.branch import Branch
12
+ from infrahub.database import InfrahubDatabase
13
+
14
+
15
+ async def collect_ancestors(db: InfrahubDatabase, branch: Branch, node_kind: str, node_id: str) -> list[EventNode]:
16
+ schema = db.schema.get(name=node_kind, branch=branch, duplicate=False)
17
+
18
+ if not isinstance(schema, NodeSchema):
19
+ return []
20
+
21
+ ancestors = await NodeManager.query_hierarchy(
22
+ db=db,
23
+ branch=branch,
24
+ direction=RelationshipHierarchyDirection.ANCESTORS,
25
+ id=node_id,
26
+ node_schema=schema,
27
+ filters={"id": None},
28
+ )
29
+ return [EventNode(id=ancestor.get_id(), kind=ancestor.get_kind()) for ancestor in ancestors.values()]
@@ -0,0 +1,107 @@
1
+ from dataclasses import dataclass
2
+
3
+ from infrahub.core.branch import Branch
4
+ from infrahub.core.changelog.models import RelationshipCardinalityManyChangelog
5
+ from infrahub.core.constants import DiffAction, InfrahubKind
6
+ from infrahub.core.schema import NodeSchema
7
+ from infrahub.database import InfrahubDatabase
8
+ from infrahub.events.group_action import GroupMemberAddedEvent, GroupMemberRemovedEvent, GroupMutatedEvent
9
+ from infrahub.events.models import EventMeta, EventNode, InfrahubEvent
10
+ from infrahub.events.node_action import NodeCreatedEvent, NodeMutatedEvent, NodeUpdatedEvent
11
+ from infrahub.exceptions import SchemaNotFoundError
12
+
13
+ from .ancestors import collect_ancestors
14
+
15
+
16
+ @dataclass
17
+ class ApplicableEvent:
18
+ event: NodeMutatedEvent
19
+ node_schema: NodeSchema
20
+ relationship: RelationshipCardinalityManyChangelog
21
+
22
+
23
+ class GroupNodeMutationParser:
24
+ def __init__(self, db: InfrahubDatabase, branch: Branch) -> None:
25
+ self._db = db
26
+ self._branch = branch
27
+
28
+ def _get_schema(self, kind: str) -> NodeSchema | None:
29
+ try:
30
+ node_schema = self._db.schema.get_node_schema(name=kind, branch=self._branch, duplicate=False)
31
+ except (ValueError, SchemaNotFoundError):
32
+ return None
33
+ return node_schema
34
+
35
+ def _get_applicable_events(self, events: list[NodeMutatedEvent]) -> list[ApplicableEvent]:
36
+ applicable: list[ApplicableEvent] = []
37
+ for event in events:
38
+ if event_kind := self._get_schema(kind=event.kind):
39
+ if (
40
+ InfrahubKind.GENERICGROUP in event_kind.inherit_from
41
+ and "members" in event.changelog.relationships
42
+ and isinstance(
43
+ event.changelog.relationships["members"],
44
+ RelationshipCardinalityManyChangelog,
45
+ )
46
+ ):
47
+ applicable.append(
48
+ ApplicableEvent(
49
+ event=event,
50
+ node_schema=event_kind,
51
+ relationship=event.changelog.relationships["members"],
52
+ )
53
+ )
54
+ return applicable
55
+
56
+ async def group_events_from_node_actions(self, events: list[NodeMutatedEvent]) -> list[InfrahubEvent]:
57
+ group_events: list[InfrahubEvent] = []
58
+
59
+ for applicable_event in self._get_applicable_events(events=events):
60
+ added_peers = [
61
+ EventNode(id=peer.peer_id, kind=peer.peer_kind)
62
+ for peer in applicable_event.relationship.peers
63
+ if peer.peer_status == DiffAction.ADDED
64
+ ]
65
+ removed_peers = [
66
+ EventNode(id=peer.peer_id, kind=peer.peer_kind)
67
+ for peer in applicable_event.relationship.peers
68
+ if peer.peer_status == DiffAction.REMOVED
69
+ ]
70
+ if added_peers:
71
+ group_events.append(
72
+ await self._define_group_event(
73
+ event_type=GroupMemberAddedEvent, parent=applicable_event.event, peers=added_peers
74
+ )
75
+ )
76
+
77
+ if removed_peers:
78
+ group_events.append(
79
+ await self._define_group_event(
80
+ event_type=GroupMemberRemovedEvent, parent=applicable_event.event, peers=removed_peers
81
+ )
82
+ )
83
+
84
+ return group_events
85
+
86
+ async def _define_group_event(
87
+ self,
88
+ event_type: type[GroupMemberAddedEvent | GroupMemberRemovedEvent],
89
+ parent: NodeMutatedEvent,
90
+ peers: list[EventNode],
91
+ ) -> GroupMutatedEvent:
92
+ event_meta = EventMeta.from_parent(parent=parent)
93
+ group_event = event_type(
94
+ meta=event_meta,
95
+ kind=parent.kind,
96
+ node_id=parent.node_id,
97
+ members=peers,
98
+ )
99
+ if isinstance(parent, NodeCreatedEvent | NodeUpdatedEvent):
100
+ # Avoid trying to find ancestors for deleted nodes
101
+ group_event.ancestors = await collect_ancestors(
102
+ db=self._db,
103
+ branch=self._branch,
104
+ node_kind=parent.kind,
105
+ node_id=parent.node_id,
106
+ )
107
+ return group_event