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,64 +1,175 @@
1
+ from __future__ import annotations
2
+
1
3
  import base64
2
4
  import hashlib
3
5
  import hmac
4
- from datetime import datetime, timezone
5
- from math import floor
6
- from typing import Any, Optional, Union
7
- from uuid import uuid4
6
+ from typing import TYPE_CHECKING, Any
7
+ from uuid import UUID, uuid4
8
8
 
9
- from infrahub_sdk.protocols import CoreTransformPython
10
- from pydantic import BaseModel, ConfigDict, Field
9
+ from pydantic import BaseModel, ConfigDict, Field, computed_field
10
+ from typing_extensions import Self
11
11
 
12
+ from infrahub.core import registry
12
13
  from infrahub.core.constants import InfrahubKind
14
+ from infrahub.core.timestamp import Timestamp
15
+ from infrahub.events.utils import get_all_infrahub_node_kind_events
13
16
  from infrahub.git.repository import InfrahubReadOnlyRepository, InfrahubRepository
14
- from infrahub.services import InfrahubServices
15
- from infrahub.transformations.constants import DEFAULT_TRANSFORM_TIMEOUT
17
+ from infrahub.trigger.constants import NAME_SEPARATOR
18
+ from infrahub.trigger.models import EventTrigger, ExecuteWorkflow, TriggerDefinition, TriggerType
19
+ from infrahub.workflows.catalogue import WEBHOOK_PROCESS
20
+
21
+ if TYPE_CHECKING:
22
+ from httpx import Response
23
+ from infrahub_sdk.protocols import CoreCustomWebhook, CoreStandardWebhook, CoreTransformPython, CoreWebhook
24
+
25
+ from infrahub.core.protocols import CoreWebhook as CoreWebhookNode
26
+ from infrahub.services import InfrahubServices
27
+
28
+
29
+ class WebhookTriggerDefinition(TriggerDefinition):
30
+ id: str
31
+ type: TriggerType = TriggerType.WEBHOOK
32
+
33
+ def generate_name(self) -> str:
34
+ return f"{self.type.value}{NAME_SEPARATOR}{self.id}"
16
35
 
36
+ @classmethod
37
+ def generate_name_from_id(cls, id: str) -> str:
38
+ return f"{TriggerType.WEBHOOK.value}{NAME_SEPARATOR}{id}"
17
39
 
18
- class SendWebhookData(BaseModel):
19
- """Sent a webhook to an external source."""
40
+ @classmethod
41
+ def from_object(cls, obj: CoreWebhook | CoreWebhookNode) -> Self:
42
+ event_trigger = EventTrigger()
43
+ if obj.event_type.value == "all":
44
+ event_trigger.events.add("infrahub.*")
45
+ else:
46
+ event_trigger.events.add(obj.event_type.value)
47
+
48
+ if obj.branch_scope.value == "default_branch":
49
+ event_trigger.match_related = {
50
+ "prefect.resource.role": "infrahub.branch",
51
+ "infrahub.resource.label": registry.default_branch,
52
+ }
53
+ elif obj.branch_scope.value == "other_branches":
54
+ event_trigger.match_related = {
55
+ "prefect.resource.role": "infrahub.branch",
56
+ "infrahub.resource.label": f"!{registry.default_branch}",
57
+ }
58
+
59
+ if obj.node_kind.value and obj.event_type.value in get_all_infrahub_node_kind_events():
60
+ event_trigger.match = {"infrahub.node.kind": obj.node_kind.value}
61
+
62
+ definition = cls(
63
+ id=obj.id,
64
+ name=obj.name.value,
65
+ trigger=event_trigger,
66
+ actions=[
67
+ ExecuteWorkflow(
68
+ workflow=WEBHOOK_PROCESS,
69
+ parameters={
70
+ "webhook_id": obj.id,
71
+ "webhook_name": obj.name.value,
72
+ "webhook_kind": obj.get_kind(),
73
+ "branch_name": "{{ event.resource['infrahub.branch.name'] }}",
74
+ "event_id": "{{ event.id }}",
75
+ "event_type": "{{ event.event }}",
76
+ "event_occured_at": "{{ event.occurred }}",
77
+ "event_payload": {
78
+ "__prefect_kind": "json",
79
+ "value": {"__prefect_kind": "jinja", "template": "{{ event.payload | tojson }}"},
80
+ },
81
+ },
82
+ ),
83
+ ],
84
+ )
85
+
86
+ return definition
20
87
 
21
- webhook_id: str = Field(..., description="The unique ID of the webhook")
22
- event_type: str = Field(..., description="The event type")
23
- event_data: dict = Field(..., description="The data tied to the event")
88
+
89
+ class EventContext(BaseModel):
90
+ id: str = Field(..., description="The internal id of the event")
91
+ branch: str | None = Field(None, description="The branch associated with the event")
92
+ account_id: str | None = Field(None, description="The id of the account associated with the event")
93
+ occured_at: str = Field(..., description="The time when the event occurred")
94
+ event: str = Field(..., description="The event type")
95
+
96
+ @classmethod
97
+ def from_event(cls, event_id: str, event_type: str, event_occured_at: str, event_payload: dict[str, Any]) -> Self:
98
+ """Extract the context from the raw event we are getting from Prefect."""
99
+
100
+ infrahub_context: dict[str, Any] = event_payload.get("context", {})
101
+ account_info: dict[str, Any] = infrahub_context.get("account", {})
102
+ branch_info: dict[str, Any] = infrahub_context.get("branch", {})
103
+
104
+ return cls(
105
+ id=event_id,
106
+ branch=branch_info.get("name")
107
+ if branch_info and branch_info.get("name") != registry.get_global_branch().name
108
+ else None,
109
+ account_id=account_info.get("account_id"),
110
+ occured_at=event_occured_at,
111
+ event=event_type,
112
+ )
24
113
 
25
114
 
26
115
  class Webhook(BaseModel):
27
116
  model_config = ConfigDict(arbitrary_types_allowed=True)
28
- service: InfrahubServices = Field(...)
117
+ name: str = Field(...)
29
118
  url: str = Field(...)
30
119
  event_type: str = Field(...)
31
- data: dict[str, Any] = Field(...)
32
120
  validate_certificates: bool = Field(...)
33
121
  _payload: Any = None
34
- _headers: Optional[dict[str, Any]] = None
122
+ _headers: dict[str, Any] | None = None
35
123
 
36
- async def _prepare_payload(self) -> None:
37
- self._payload = {"event_type": self.event_type, "data": self.data}
124
+ async def _prepare_payload(self, data: dict[str, Any], context: EventContext, service: InfrahubServices) -> None: # noqa: ARG002
125
+ self._payload = {"data": data, **context.model_dump()}
38
126
 
39
127
  def _assign_headers(self) -> None:
40
128
  self._headers = {}
41
129
 
130
+ @computed_field # type: ignore[prop-decorator]
42
131
  @property
43
132
  def webhook_type(self) -> str:
44
133
  return self.__class__.__name__
45
134
 
46
- async def send(self) -> None:
47
- await self._prepare_payload()
135
+ async def prepare(self, data: dict[str, Any], context: EventContext, service: InfrahubServices) -> None:
136
+ await self._prepare_payload(data=data, context=context, service=service)
48
137
  self._assign_headers()
49
- await self.service.http.post(url=self.url, json=self._payload, headers=self._headers)
138
+
139
+ async def send(self, data: dict[str, Any], context: EventContext, service: InfrahubServices) -> Response:
140
+ await self.prepare(data=data, context=context, service=service)
141
+ return await service.http.post(url=self.url, json=self.get_payload(), headers=self._headers)
142
+
143
+ def get_payload(self) -> dict[str, Any]:
144
+ return self._payload
145
+
146
+ def to_cache(self) -> dict[str, Any]:
147
+ return self.model_dump()
148
+
149
+ @classmethod
150
+ def from_cache(cls, data: dict[str, Any]) -> Self:
151
+ return cls(**data)
50
152
 
51
153
 
52
154
  class CustomWebhook(Webhook):
53
155
  """Custom webhook"""
54
156
 
157
+ @classmethod
158
+ def from_object(cls, obj: CoreCustomWebhook) -> Self:
159
+ return cls(
160
+ name=obj.name.value,
161
+ url=obj.url.value,
162
+ event_type=obj.event_type.value,
163
+ validate_certificates=obj.validate_certificates.value or False,
164
+ )
165
+
55
166
 
56
167
  class StandardWebhook(Webhook):
57
- shared_key: bytes = Field(...)
168
+ shared_key: str = Field(...)
58
169
 
59
- def _assign_headers(self) -> None:
60
- message_id = f"msg_{uuid4().hex}"
61
- timestamp = str(floor(datetime.now(tz=timezone.utc).timestamp()))
170
+ def _assign_headers(self, uuid: UUID | None = None, at: Timestamp | None = None) -> None:
171
+ message_id = f"msg_{uuid.hex}" if uuid else f"msg_{uuid4().hex}"
172
+ timestamp = str(at.to_timestamp()) if at else str(Timestamp().to_timestamp())
62
173
  payload = self._payload or {}
63
174
  unsigned_data = f"{message_id}.{timestamp}.{payload}".encode()
64
175
  signature = self._sign(data=unsigned_data)
@@ -72,7 +183,17 @@ class StandardWebhook(Webhook):
72
183
  }
73
184
 
74
185
  def _sign(self, data: bytes) -> bytes:
75
- return hmac.new(key=self.shared_key, msg=data, digestmod=hashlib.sha256).digest()
186
+ return hmac.new(key=self.shared_key.encode(), msg=data, digestmod=hashlib.sha256).digest()
187
+
188
+ @classmethod
189
+ def from_object(cls, obj: CoreStandardWebhook) -> Self:
190
+ return cls(
191
+ name=obj.name.value,
192
+ url=obj.url.value,
193
+ event_type=obj.event_type.value,
194
+ validate_certificates=obj.validate_certificates.value or False,
195
+ shared_key=obj.shared_key.value,
196
+ )
76
197
 
77
198
 
78
199
  class TransformWebhook(Webhook):
@@ -82,37 +203,48 @@ class TransformWebhook(Webhook):
82
203
  transform_name: str = Field(...)
83
204
  transform_class: str = Field(...)
84
205
  transform_file: str = Field(...)
206
+ transform_timeout: int = Field(...)
85
207
 
86
- async def _prepare_payload(self) -> None:
87
- repo: Union[InfrahubReadOnlyRepository, InfrahubRepository]
208
+ async def _prepare_payload(self, data: dict[str, Any], context: EventContext, service: InfrahubServices) -> None:
209
+ repo: InfrahubReadOnlyRepository | InfrahubRepository
88
210
  if self.repository_kind == InfrahubKind.READONLYREPOSITORY:
89
211
  repo = await InfrahubReadOnlyRepository.init(
90
212
  id=self.repository_id,
91
213
  name=self.repository_name,
92
- client=self.service.client,
93
- service=self.service,
214
+ client=service.client,
215
+ service=service,
94
216
  )
95
217
  else:
96
218
  repo = await InfrahubRepository.init(
97
219
  id=self.repository_id,
98
220
  name=self.repository_name,
99
- client=self.service.client,
100
- service=self.service,
221
+ client=service.client,
222
+ service=service,
101
223
  )
102
224
 
103
- default_branch = repo.default_branch
104
- commit = repo.get_commit_value(branch_name=default_branch)
105
-
106
- timeout = DEFAULT_TRANSFORM_TIMEOUT
107
- if transform := await self.service.client.get(
108
- kind=CoreTransformPython, name__value=self.transform_name, raise_when_missing=False
109
- ):
110
- timeout = transform.timeout.value
225
+ branch = context.branch or repo.default_branch
226
+ commit = repo.get_commit_value(branch_name=branch)
111
227
 
112
- self._payload = await repo.execute_python_transform.with_options(timeout_seconds=timeout)(
113
- branch_name=default_branch,
228
+ self._payload = await repo.execute_python_transform.with_options(timeout_seconds=self.transform_timeout)(
229
+ branch_name=branch,
114
230
  commit=commit,
115
231
  location=f"{self.transform_file}::{self.transform_class}",
116
- data={"event_type": self.event_type, "data": self.data},
117
- client=self.service.client,
232
+ data={"data": data, **context.model_dump()},
233
+ client=service.client,
234
+ ) # type: ignore[misc]
235
+
236
+ @classmethod
237
+ def from_object(cls, obj: CoreCustomWebhook, transform: CoreTransformPython) -> Self:
238
+ return cls(
239
+ name=obj.name.value,
240
+ url=obj.url.value,
241
+ event_type=obj.event_type.value,
242
+ validate_certificates=obj.validate_certificates.value or False,
243
+ repository_id=transform.repository.id,
244
+ repository_name=transform.repository.peer.name.value,
245
+ repository_kind=transform.repository.peer.get_kind(),
246
+ transform_name=transform.name.value,
247
+ transform_class=transform.class_name.value,
248
+ transform_file=transform.file_path.value,
249
+ transform_timeout=transform.timeout.value,
118
250
  )
infrahub/webhook/tasks.py CHANGED
@@ -1,182 +1,181 @@
1
- from datetime import timedelta
2
- from typing import TYPE_CHECKING, Any
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
3
4
 
4
5
  import ujson
5
- from infrahub_sdk.protocols import CoreCustomWebhook, CoreStandardWebhook, CoreTransformPython
6
- from prefect import flow
6
+ from infrahub_sdk import InfrahubClient # noqa: TC002 needed for prefect flow
7
+ from infrahub_sdk.protocols import CoreTransformPython, CoreWebhook
8
+ from prefect import flow, task
7
9
  from prefect.automations import AutomationCore
10
+ from prefect.cache_policies import NONE
8
11
  from prefect.client.orchestration import get_client
9
- from prefect.client.schemas.filters import DeploymentFilter, DeploymentFilterName
10
- from prefect.events.actions import RunDeployment
11
- from prefect.events.schemas.automations import EventTrigger, Posture
12
- from prefect.events.schemas.events import ResourceSpecification
13
12
  from prefect.logging import get_run_logger
14
13
 
15
- from infrahub.core.constants import MutationAction
16
- from infrahub.exceptions import NodeNotFoundError
17
- from infrahub.services import InfrahubServices
18
- from infrahub.workflows.catalogue import WEBHOOK_SEND, WEBHOOK_TRIGGER
14
+ from infrahub.message_bus.types import KVTTL
15
+ from infrahub.services import InfrahubServices # noqa: TC001 needed for prefect flow
16
+ from infrahub.trigger.models import TriggerType
17
+ from infrahub.trigger.setup import setup_triggers
18
+ from infrahub.workflows.utils import add_tags
19
19
 
20
- from .constants import AUTOMATION_NAME_RUN
21
- from .models import CustomWebhook, SendWebhookData, StandardWebhook, TransformWebhook, Webhook
20
+ from .gather import gather_trigger_webhook
21
+ from .models import CustomWebhook, EventContext, StandardWebhook, TransformWebhook, Webhook, WebhookTriggerDefinition
22
22
 
23
23
  if TYPE_CHECKING:
24
- from prefect.events.schemas.automations import Automation
24
+ from httpx import Response
25
25
 
26
+ WEBHOOK_MAP: dict[str, type[Webhook]] = {
27
+ "StandardWebhook": StandardWebhook,
28
+ "CustomWebhook": CustomWebhook,
29
+ "TransformWebhook": TransformWebhook,
30
+ }
26
31
 
27
- @flow(name="event-send-webhook", flow_run_name="Send Webhook")
28
- async def send_webhook(model: SendWebhookData, service: InfrahubServices) -> None:
29
- log = get_run_logger()
30
32
 
31
- webhook_definition = await service.cache.get(key=f"webhook:active:{model.webhook_id}")
32
- if not webhook_definition:
33
- log.warning("Webhook not found")
34
- raise NodeNotFoundError(
35
- node_type="Webhook", identifier=model.webhook_id, message="The requested Webhook was not found"
33
+ @task(name="webhook-send", task_run_name="Send Standard Webhook {webhook.name}", cache_policy=NONE, retries=3)
34
+ async def webhook_send(
35
+ webhook: Webhook, context: EventContext, event_data: dict, service: InfrahubServices
36
+ ) -> Response:
37
+ response = await webhook.send(data=event_data, context=context, service=service)
38
+ response.raise_for_status()
39
+ return response
40
+
41
+
42
+ @task(name="webhook-convert-node", task_run_name="Convert node to webhook", cache_policy=NONE)
43
+ async def convert_node_to_webhook(webhook_node: CoreWebhook, client: InfrahubClient) -> Webhook:
44
+ webhook_kind = webhook_node.get_kind()
45
+
46
+ if webhook_kind not in ["CoreStandardWebhook", "CoreCustomWebhook"]:
47
+ raise ValueError(f"Unsupported webhook kind: {webhook_kind}")
48
+
49
+ if webhook_kind == "CoreStandardWebhook":
50
+ return StandardWebhook.from_object(obj=webhook_node)
51
+
52
+ # Processing Custom Webhook
53
+ if webhook_node.transformation.id:
54
+ transform = await client.get(
55
+ kind=CoreTransformPython,
56
+ id=webhook_node.transformation.id,
57
+ prefetch_relationships=True,
58
+ include=["name", "class_name", "file_path", "repository"],
36
59
  )
60
+ return TransformWebhook.from_object(obj=webhook_node, transform=transform)
61
+
62
+ return CustomWebhook.from_object(obj=webhook_node)
63
+
64
+
65
+ @flow(name="webhook-process", flow_run_name="Send webhook for {webhook_name}")
66
+ async def webhook_process(
67
+ webhook_id: str,
68
+ webhook_name: str, # noqa: ARG001
69
+ webhook_kind: str,
70
+ event_id: str,
71
+ event_type: str,
72
+ event_occured_at: str,
73
+ event_payload: dict,
74
+ service: InfrahubServices,
75
+ branch_name: str | None = None,
76
+ ) -> None:
77
+ log = get_run_logger()
78
+
79
+ if branch_name:
80
+ await add_tags(branches=[branch_name])
81
+
82
+ webhook_data_str = await service.cache.get(key=f"webhook:{webhook_id}")
83
+ if not webhook_data_str:
84
+ log.info(f"Webhook {webhook_id} not found in cache")
85
+ webhook_node = await service.client.get(kind=webhook_kind, id=webhook_id)
86
+ webhook = await convert_node_to_webhook(webhook_node=webhook_node, client=service.client)
87
+ webhook_data = webhook.to_cache()
88
+ await service.cache.set(key=f"webhook:{webhook_id}", value=ujson.dumps(webhook_data), expires=KVTTL.TWO_HOURS)
89
+
90
+ else:
91
+ webhook_data = ujson.loads(webhook_data_str)
92
+
93
+ if webhook_data["webhook_type"] not in WEBHOOK_MAP:
94
+ raise ValueError(f"Unsupported webhook kind: {webhook_data['webhook_type']}")
95
+
96
+ webhook_class = WEBHOOK_MAP[webhook_data["webhook_type"]]
97
+ webhook = webhook_class.from_cache(webhook_data)
37
98
 
38
- webhook_data = ujson.loads(webhook_definition)
39
- payload: dict[str, Any] = {"event_type": model.event_type, "data": model.event_data, "service": service}
40
- webhook_map: dict[str, type[Webhook]] = {
41
- "standard": StandardWebhook,
42
- "custom": CustomWebhook,
43
- "transform": TransformWebhook,
44
- }
45
- webhook_class = webhook_map[webhook_data["webhook_type"]]
46
- payload.update(webhook_data["webhook_configuration"])
47
- webhook = webhook_class(**payload)
48
- await webhook.send()
99
+ webhook_context = EventContext.from_event(
100
+ event_id=event_id,
101
+ event_type=event_type,
102
+ event_occured_at=event_occured_at,
103
+ event_payload=event_payload,
104
+ )
105
+ event_data = event_payload.get("data", {})
106
+ response = await webhook_send(webhook=webhook, context=webhook_context, event_data=event_data, service=service)
107
+ log.info(f"Successfully sent webhook to {response.url} with status {response.status_code}")
49
108
 
50
- log.info("Successfully sent webhook")
51
109
 
110
+ @flow(name="webhook-setup-automation-all", flow_run_name="Configure all webhooks")
111
+ async def configure_webhook_all(service: InfrahubServices) -> None:
112
+ log = get_run_logger()
113
+
114
+ triggers = await gather_trigger_webhook(db=service.database)
52
115
 
53
- @flow(name="webhook-trigger-actions", flow_run_name="Trigger configured webhooks")
54
- async def trigger_webhooks(event_type: str, event_data: dict, service: InfrahubServices) -> None:
55
- webhooks = await service.cache.list_keys(filter_pattern="webhook:active:*")
56
- for webhook in webhooks:
57
- webhook_id = webhook.split(":")[-1]
58
- model = SendWebhookData(webhook_id=webhook_id, event_type=event_type, event_data=event_data)
59
- await service.workflow.submit_workflow(workflow=WEBHOOK_SEND, parameters={"model": model})
116
+ async with get_client(sync_client=False) as prefect_client:
117
+ await setup_triggers(
118
+ client=prefect_client,
119
+ triggers=triggers,
120
+ trigger_type=TriggerType.WEBHOOK,
121
+ ) # type: ignore[misc]
60
122
 
123
+ log.info(f"{len(triggers)} Webhooks automation configuration completed")
61
124
 
62
- @flow(name="webhook-setup-automations", flow_run_name="Configuration webhook automation and populate cache")
63
- async def configure_webhooks(service: InfrahubServices) -> None:
125
+
126
+ @flow(name="webhook-setup-automation-one", flow_run_name="Configurate webhook for {webhook_name}")
127
+ async def configure_webhook_one(
128
+ webhook_name: str, # noqa: ARG001
129
+ event_data: dict,
130
+ service: InfrahubServices,
131
+ ) -> None:
64
132
  log = get_run_logger()
65
133
 
66
- log.debug("Refreshing webhook configuration")
67
- standard_webhooks = await service.client.all(kind=CoreStandardWebhook)
68
- custom_webhooks = await service.client.all(kind=CoreCustomWebhook)
69
-
70
- expected_webhooks = []
71
- for webhook in standard_webhooks:
72
- webhook_key = f"webhook:active:{webhook.id}"
73
- expected_webhooks.append(webhook_key)
74
- standard_payload = {
75
- "webhook_type": "standard",
76
- "webhook_configuration": {
77
- "url": webhook.url.value,
78
- "shared_key": webhook.shared_key.value,
79
- "validate_certificates": webhook.validate_certificates.value,
80
- },
81
- }
82
- await service.cache.set(key=webhook_key, value=ujson.dumps(standard_payload))
83
-
84
- for webhook in custom_webhooks:
85
- webhook_key = f"webhook:active:{webhook.id}"
86
- expected_webhooks.append(webhook_key)
87
- payload: dict[str, Any] = {
88
- "webhook_type": "custom",
89
- "webhook_configuration": {
90
- "url": webhook.url.value,
91
- "validate_certificates": webhook.validate_certificates.value,
92
- },
93
- }
94
- if webhook.transformation.id:
95
- transform = await service.client.get(
96
- kind=CoreTransformPython,
97
- id=webhook.transformation.id,
98
- prefetch_relationships=True,
99
- populate_store=True,
100
- include=["name", "class_name", "file_path", "repository"],
101
- )
102
- payload["webhook_type"] = "transform"
103
- payload["webhook_configuration"]["transform_name"] = transform.name.value
104
- payload["webhook_configuration"]["transform_class"] = transform.class_name.value
105
- payload["webhook_configuration"]["transform_file"] = transform.file_path.value
106
- payload["webhook_configuration"]["repository_id"] = transform.repository.id
107
- payload["webhook_configuration"]["repository_name"] = transform.repository.peer.name.value
108
-
109
- await service.cache.set(key=webhook_key, value=ujson.dumps(payload))
110
-
111
- cached_webhooks = await service.cache.list_keys(filter_pattern="webhook:active:*")
112
- for cached_webhook in cached_webhooks:
113
- if cached_webhook not in expected_webhooks:
114
- await service.cache.delete(key=cached_webhook)
115
-
116
- has_webhooks = bool(expected_webhooks)
117
-
118
- async with get_client(sync_client=False) as client:
119
- deployments = {
120
- item.name: item
121
- for item in await client.read_deployments(
122
- deployment_filter=DeploymentFilter(
123
- name=DeploymentFilterName(
124
- any_=[
125
- WEBHOOK_TRIGGER.name,
126
- ]
127
- )
128
- )
129
- )
130
- }
131
- if WEBHOOK_TRIGGER.name not in deployments:
132
- raise ValueError("Unable to find the deployment for WEBHOOK_TRIGGER")
133
-
134
- deployment_id_webhook_trigger = deployments[WEBHOOK_TRIGGER.name].id
135
-
136
- webhook_configure_automation: Automation | None = None
137
- automations = await client.read_automations_by_name(name=AUTOMATION_NAME_RUN)
138
- if automations:
139
- webhook_configure_automation = automations[0]
140
-
141
- if not has_webhooks:
142
- if webhook_configure_automation:
143
- await client.delete_automation(automation_id=webhook_configure_automation.id)
144
- return
134
+ webhook = await service.client.get(kind=CoreWebhook, id=event_data["node_id"])
135
+ trigger = WebhookTriggerDefinition.from_object(webhook)
136
+
137
+ async with get_client(sync_client=False) as prefect_client:
138
+ # Query the deployment associated with the trigger to have its ID
139
+ deployment_name = trigger.get_deployment_names()[0]
140
+ deployment = await prefect_client.read_deployment_by_name(name=f"{deployment_name}/{deployment_name}")
145
141
 
146
142
  automation = AutomationCore(
147
- name=AUTOMATION_NAME_RUN,
148
- description="Trigger all configured webhooks on mutations",
143
+ name=trigger.generate_name(),
144
+ description=trigger.get_description(),
149
145
  enabled=True,
150
- trigger=EventTrigger(
151
- posture=Posture.Reactive,
152
- expect={"infrahub.node.*"},
153
- within=timedelta(0),
154
- match=ResourceSpecification(
155
- {
156
- "infrahub.node.action": MutationAction.available_types(),
157
- }
158
- ),
159
- threshold=1,
160
- ),
161
- actions=[
162
- RunDeployment(
163
- source="selected",
164
- deployment_id=deployment_id_webhook_trigger,
165
- parameters={
166
- "event_type": "{{ event.resource['infrahub.node.kind'] }}.{{ event.resource['infrahub.node.action'] }}",
167
- "event_data": {
168
- "__prefect_kind": "json",
169
- "value": {"__prefect_kind": "jinja", "template": "{{ event.payload['data'] | tojson }}"},
170
- },
171
- },
172
- job_variables={},
173
- ),
174
- ],
146
+ trigger=trigger.trigger.get_prefect(),
147
+ actions=[action.get(deployment.id) for action in trigger.actions],
175
148
  )
176
149
 
177
- if webhook_configure_automation:
178
- await client.update_automation(automation_id=webhook_configure_automation.id, automation=automation)
179
- log.info(f"{AUTOMATION_NAME_RUN} Updated")
150
+ existing_automations = await prefect_client.read_automations_by_name(trigger.generate_name())
151
+ existing_automation = existing_automations[0] if existing_automations else None
152
+
153
+ if existing_automation:
154
+ await prefect_client.update_automation(automation_id=existing_automation.id, automation=automation)
155
+ log.info(f"Automation {trigger.generate_name()} updated")
180
156
  else:
181
- await client.create_automation(automation=automation)
182
- log.info(f"{AUTOMATION_NAME_RUN} Created")
157
+ await prefect_client.create_automation(automation=automation)
158
+ log.info(f"Automation {trigger.generate_name()} created")
159
+
160
+ await service.cache.delete(key=f"webhook:{webhook.id}")
161
+
162
+
163
+ @flow(name="webhook-delete-automation", flow_run_name="Delete webhook automation for {webhook_name}")
164
+ async def delete_webhook_automation(
165
+ webhook_id: str,
166
+ webhook_name: str, # noqa: ARG001
167
+ service: InfrahubServices,
168
+ ) -> None:
169
+ log = get_run_logger()
170
+
171
+ async with get_client(sync_client=False) as prefect_client:
172
+ automation_name = WebhookTriggerDefinition.generate_name_from_id(id=webhook_id)
173
+
174
+ existing_automations = await prefect_client.read_automations_by_name(automation_name)
175
+ existing_automation = existing_automations[0] if existing_automations else None
176
+
177
+ if existing_automation:
178
+ await prefect_client.delete_automation(automation_id=existing_automation.id)
179
+ log.info(f"Automation {automation_name} deleted")
180
+
181
+ await service.cache.delete(key=f"webhook:{webhook_id}")