infrahub-server 1.1.6__py3-none-any.whl → 1.2.0rc0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (346) hide show
  1. infrahub/api/artifact.py +16 -4
  2. infrahub/api/dependencies.py +8 -0
  3. infrahub/api/oauth2.py +0 -1
  4. infrahub/api/oidc.py +0 -1
  5. infrahub/api/query.py +18 -7
  6. infrahub/api/schema.py +32 -6
  7. infrahub/api/transformation.py +12 -5
  8. infrahub/{message_bus/messages/check_artifact_create.py → artifacts/models.py} +2 -4
  9. infrahub/{message_bus/operations/check/artifact.py → artifacts/tasks.py} +26 -25
  10. infrahub/cli/__init__.py +0 -2
  11. infrahub/cli/db.py +6 -7
  12. infrahub/cli/events.py +8 -3
  13. infrahub/cli/git_agent.py +9 -7
  14. infrahub/cli/tasks.py +4 -6
  15. infrahub/computed_attribute/tasks.py +63 -17
  16. infrahub/computed_attribute/triggers.py +90 -0
  17. infrahub/config.py +1 -1
  18. infrahub/context.py +39 -0
  19. infrahub/core/account.py +5 -8
  20. infrahub/core/attribute.py +53 -21
  21. infrahub/core/branch/models.py +4 -4
  22. infrahub/core/branch/tasks.py +89 -130
  23. infrahub/core/changelog/__init__.py +0 -0
  24. infrahub/core/changelog/diff.py +232 -0
  25. infrahub/core/changelog/models.py +488 -0
  26. infrahub/core/constants/__init__.py +19 -2
  27. infrahub/core/constants/infrahubkind.py +1 -0
  28. infrahub/core/diff/combiner.py +12 -8
  29. infrahub/core/diff/coordinator.py +49 -70
  30. infrahub/core/diff/data_check_synchronizer.py +86 -7
  31. infrahub/core/diff/enricher/aggregated.py +3 -3
  32. infrahub/core/diff/enricher/cardinality_one.py +2 -7
  33. infrahub/core/diff/enricher/hierarchy.py +5 -3
  34. infrahub/core/diff/enricher/labels.py +14 -4
  35. infrahub/core/diff/enricher/path_identifier.py +3 -9
  36. infrahub/core/diff/enricher/summary_counts.py +3 -1
  37. infrahub/core/diff/merger/merger.py +8 -4
  38. infrahub/core/diff/model/path.py +47 -29
  39. infrahub/core/diff/query/all_conflicts.py +6 -3
  40. infrahub/core/diff/query/artifact.py +1 -1
  41. infrahub/core/diff/query/delete_query.py +1 -1
  42. infrahub/core/diff/query/diff_get.py +3 -2
  43. infrahub/core/diff/query/diff_summary.py +1 -1
  44. infrahub/core/diff/query/field_specifiers.py +3 -1
  45. infrahub/core/diff/query/field_summary.py +3 -2
  46. infrahub/core/diff/query/filters.py +12 -1
  47. infrahub/core/diff/query/get_conflict_query.py +1 -1
  48. infrahub/core/diff/query/has_conflicts_query.py +6 -3
  49. infrahub/core/diff/query/merge.py +3 -3
  50. infrahub/core/diff/query/{drop_tracking_id.py → merge_tracking_id.py} +4 -4
  51. infrahub/core/diff/query/roots_metadata.py +9 -2
  52. infrahub/core/diff/query/save.py +151 -66
  53. infrahub/core/diff/query/summary_counts_enricher.py +220 -0
  54. infrahub/core/diff/query/time_range_query.py +3 -2
  55. infrahub/core/diff/query/update_conflict_query.py +1 -1
  56. infrahub/core/diff/query_parser.py +49 -24
  57. infrahub/core/diff/repository/deserializer.py +24 -25
  58. infrahub/core/diff/repository/repository.py +76 -20
  59. infrahub/core/diff/tasks.py +9 -8
  60. infrahub/core/enums.py +1 -1
  61. infrahub/core/integrity/object_conflict/conflict_recorder.py +1 -1
  62. infrahub/core/ipam/reconciler.py +1 -1
  63. infrahub/core/ipam/tasks.py +2 -3
  64. infrahub/core/manager.py +18 -13
  65. infrahub/core/merge.py +5 -2
  66. infrahub/core/migrations/graph/m001_add_version_to_graph.py +1 -1
  67. infrahub/core/migrations/graph/m002_attribute_is_default.py +2 -2
  68. infrahub/core/migrations/graph/m003_relationship_parent_optional.py +2 -2
  69. infrahub/core/migrations/graph/m004_add_attr_documentation.py +1 -1
  70. infrahub/core/migrations/graph/m005_add_rel_read_only.py +1 -1
  71. infrahub/core/migrations/graph/m006_add_rel_on_delete.py +1 -1
  72. infrahub/core/migrations/graph/m007_add_rel_allow_override.py +1 -1
  73. infrahub/core/migrations/graph/m008_add_human_friendly_id.py +1 -1
  74. infrahub/core/migrations/graph/m009_add_generate_profile_attr.py +1 -1
  75. infrahub/core/migrations/graph/m010_add_generate_profile_attr_generic.py +1 -1
  76. infrahub/core/migrations/graph/m011_remove_profile_relationship_schema.py +2 -2
  77. infrahub/core/migrations/graph/m012_convert_account_generic.py +12 -23
  78. infrahub/core/migrations/graph/m013_convert_git_password_credential.py +7 -11
  79. infrahub/core/migrations/graph/m014_remove_index_attr_value.py +2 -2
  80. infrahub/core/migrations/graph/m015_diff_format_update.py +1 -1
  81. infrahub/core/migrations/graph/m016_diff_delete_bug_fix.py +1 -1
  82. infrahub/core/migrations/graph/m017_add_core_profile.py +1 -1
  83. infrahub/core/migrations/graph/m018_uniqueness_nulls.py +2 -2
  84. infrahub/core/migrations/query/attribute_add.py +1 -1
  85. infrahub/core/migrations/query/attribute_rename.py +1 -1
  86. infrahub/core/migrations/query/delete_element_in_schema.py +1 -1
  87. infrahub/core/migrations/query/node_duplicate.py +1 -1
  88. infrahub/core/migrations/query/relationship_duplicate.py +1 -1
  89. infrahub/core/migrations/query/schema_attribute_update.py +1 -1
  90. infrahub/core/migrations/schema/node_attribute_remove.py +1 -1
  91. infrahub/core/migrations/schema/node_remove.py +1 -1
  92. infrahub/core/migrations/schema/tasks.py +5 -5
  93. infrahub/core/migrations/shared.py +4 -4
  94. infrahub/core/models.py +7 -8
  95. infrahub/core/node/__init__.py +161 -40
  96. infrahub/core/node/base.py +1 -1
  97. infrahub/core/node/constraints/grouped_uniqueness.py +9 -2
  98. infrahub/core/node/delete_validator.py +4 -4
  99. infrahub/core/node/ipam.py +13 -8
  100. infrahub/core/node/permissions.py +4 -0
  101. infrahub/core/node/resource_manager/ip_prefix_pool.py +8 -5
  102. infrahub/core/node/standard.py +3 -5
  103. infrahub/core/property.py +1 -1
  104. infrahub/core/protocols.py +4 -0
  105. infrahub/core/protocols_base.py +4 -2
  106. infrahub/core/query/__init__.py +2 -5
  107. infrahub/core/query/attribute.py +9 -9
  108. infrahub/core/query/branch.py +5 -5
  109. infrahub/core/query/delete.py +1 -1
  110. infrahub/core/query/diff.py +45 -7
  111. infrahub/core/query/ipam.py +4 -4
  112. infrahub/core/query/node.py +19 -14
  113. infrahub/core/query/relationship.py +10 -11
  114. infrahub/core/query/resource_manager.py +13 -11
  115. infrahub/core/query/standard_node.py +6 -6
  116. infrahub/core/query/task.py +3 -3
  117. infrahub/core/query/task_log.py +1 -1
  118. infrahub/core/query/utils.py +5 -5
  119. infrahub/core/registry.py +0 -2
  120. infrahub/core/relationship/constraints/count.py +1 -1
  121. infrahub/core/relationship/constraints/peer_kind.py +1 -1
  122. infrahub/core/relationship/model.py +66 -26
  123. infrahub/core/schema/__init__.py +6 -4
  124. infrahub/core/schema/basenode_schema.py +1 -3
  125. infrahub/core/schema/definitions/core.py +14 -2
  126. infrahub/core/schema/definitions/internal.py +16 -0
  127. infrahub/core/schema/generated/genericnode_schema.py +5 -0
  128. infrahub/core/schema/generated/node_schema.py +5 -0
  129. infrahub/core/schema/generic_schema.py +5 -1
  130. infrahub/core/schema/manager.py +45 -42
  131. infrahub/core/schema/node_schema.py +4 -0
  132. infrahub/core/schema/profile_schema.py +4 -0
  133. infrahub/core/schema/relationship_schema.py +2 -2
  134. infrahub/core/schema/schema_branch.py +248 -14
  135. infrahub/core/schema/template_schema.py +36 -0
  136. infrahub/core/task/user_task.py +7 -5
  137. infrahub/core/timestamp.py +1 -1
  138. infrahub/core/utils.py +3 -2
  139. infrahub/core/validators/attribute/choices.py +1 -1
  140. infrahub/core/validators/attribute/enum.py +1 -1
  141. infrahub/core/validators/attribute/kind.py +1 -1
  142. infrahub/core/validators/attribute/length.py +1 -1
  143. infrahub/core/validators/attribute/optional.py +1 -1
  144. infrahub/core/validators/attribute/regex.py +1 -1
  145. infrahub/core/validators/attribute/unique.py +1 -1
  146. infrahub/core/validators/checks_runner.py +37 -0
  147. infrahub/core/validators/node/generate_profile.py +1 -1
  148. infrahub/core/validators/node/hierarchy.py +1 -1
  149. infrahub/core/validators/query.py +1 -1
  150. infrahub/core/validators/relationship/count.py +1 -1
  151. infrahub/core/validators/relationship/optional.py +1 -1
  152. infrahub/core/validators/relationship/peer.py +1 -1
  153. infrahub/core/validators/tasks.py +8 -6
  154. infrahub/core/validators/uniqueness/query.py +20 -17
  155. infrahub/database/__init__.py +15 -2
  156. infrahub/database/memgraph.py +1 -1
  157. infrahub/dependencies/builder/constraint/grouped/node_runner.py +0 -2
  158. infrahub/dependencies/builder/diff/combiner.py +1 -1
  159. infrahub/dependencies/builder/diff/conflicts_enricher.py +1 -1
  160. infrahub/dependencies/builder/diff/coordinator.py +0 -2
  161. infrahub/dependencies/builder/diff/deserializer.py +1 -1
  162. infrahub/dependencies/builder/diff/enricher/summary_counts.py +1 -1
  163. infrahub/events/branch_action.py +47 -21
  164. infrahub/events/group_action.py +73 -0
  165. infrahub/events/models.py +159 -51
  166. infrahub/events/node_action.py +74 -8
  167. infrahub/events/repository_action.py +8 -8
  168. infrahub/events/schema_action.py +21 -8
  169. infrahub/generators/tasks.py +12 -13
  170. infrahub/git/base.py +3 -5
  171. infrahub/git/constants.py +0 -1
  172. infrahub/git/integrator.py +36 -35
  173. infrahub/git/repository.py +7 -8
  174. infrahub/git/tasks.py +43 -107
  175. infrahub/git_credential/helper.py +2 -3
  176. infrahub/graphql/analyzer.py +572 -11
  177. infrahub/graphql/app.py +34 -26
  178. infrahub/graphql/auth/query_permission_checker/anonymous_checker.py +5 -5
  179. infrahub/graphql/auth/query_permission_checker/default_branch_checker.py +4 -4
  180. infrahub/graphql/auth/query_permission_checker/merge_operation_checker.py +4 -4
  181. infrahub/graphql/auth/query_permission_checker/object_permission_checker.py +28 -35
  182. infrahub/graphql/auth/query_permission_checker/super_admin_checker.py +5 -5
  183. infrahub/graphql/enums.py +1 -1
  184. infrahub/graphql/initialization.py +5 -1
  185. infrahub/graphql/loaders/node.py +2 -2
  186. infrahub/graphql/manager.py +59 -54
  187. infrahub/graphql/mutations/account.py +20 -13
  188. infrahub/graphql/mutations/artifact_definition.py +16 -12
  189. infrahub/graphql/mutations/branch.py +61 -40
  190. infrahub/graphql/mutations/computed_attribute.py +19 -13
  191. infrahub/graphql/mutations/diff.py +37 -9
  192. infrahub/graphql/mutations/diff_conflict.py +9 -8
  193. infrahub/graphql/mutations/graphql_query.py +19 -11
  194. infrahub/graphql/mutations/ipam.py +21 -19
  195. infrahub/graphql/mutations/main.py +197 -44
  196. infrahub/graphql/mutations/menu.py +8 -8
  197. infrahub/graphql/mutations/proposed_change.py +36 -28
  198. infrahub/graphql/mutations/relationship.py +302 -105
  199. infrahub/graphql/mutations/repository.py +41 -35
  200. infrahub/graphql/mutations/resource_manager.py +26 -26
  201. infrahub/graphql/mutations/schema.py +51 -33
  202. infrahub/graphql/mutations/tasks.py +16 -10
  203. infrahub/graphql/parser.py +1 -1
  204. infrahub/graphql/permissions.py +6 -4
  205. infrahub/graphql/queries/account.py +22 -18
  206. infrahub/graphql/queries/branch.py +6 -4
  207. infrahub/graphql/queries/diff/tree.py +48 -42
  208. infrahub/graphql/queries/event.py +112 -0
  209. infrahub/graphql/queries/internal.py +3 -3
  210. infrahub/graphql/queries/ipam.py +23 -18
  211. infrahub/graphql/queries/relationship.py +11 -10
  212. infrahub/graphql/queries/resource_manager.py +43 -27
  213. infrahub/graphql/queries/search.py +9 -8
  214. infrahub/graphql/queries/status.py +12 -9
  215. infrahub/graphql/queries/task.py +11 -9
  216. infrahub/graphql/resolvers/resolver.py +69 -43
  217. infrahub/graphql/resolvers/single_relationship.py +16 -10
  218. infrahub/graphql/schema.py +2 -0
  219. infrahub/graphql/subscription/__init__.py +1 -1
  220. infrahub/graphql/subscription/events.py +1 -1
  221. infrahub/graphql/subscription/graphql_query.py +8 -8
  222. infrahub/graphql/types/branch.py +2 -2
  223. infrahub/graphql/types/common.py +6 -1
  224. infrahub/graphql/types/enums.py +2 -0
  225. infrahub/graphql/types/event.py +100 -0
  226. infrahub/graphql/types/interface.py +2 -2
  227. infrahub/graphql/types/node.py +3 -3
  228. infrahub/graphql/types/permission.py +2 -2
  229. infrahub/graphql/types/relationship.py +3 -3
  230. infrahub/graphql/types/standard_node.py +9 -11
  231. infrahub/graphql/utils.py +28 -182
  232. infrahub/groups/tasks.py +2 -3
  233. infrahub/lock.py +1 -1
  234. infrahub/menu/constants.py +1 -0
  235. infrahub/menu/generator.py +14 -3
  236. infrahub/menu/menu.py +116 -127
  237. infrahub/menu/models.py +4 -4
  238. infrahub/message_bus/messages/__init__.py +0 -4
  239. infrahub/message_bus/messages/event_branch_merge.py +3 -0
  240. infrahub/message_bus/messages/request_proposedchange_pipeline.py +2 -0
  241. infrahub/message_bus/operations/__init__.py +3 -5
  242. infrahub/message_bus/operations/check/__init__.py +2 -2
  243. infrahub/message_bus/operations/check/generator.py +1 -3
  244. infrahub/message_bus/operations/check/repository.py +1 -1
  245. infrahub/message_bus/operations/event/branch.py +7 -3
  246. infrahub/message_bus/operations/event/schema.py +1 -1
  247. infrahub/message_bus/operations/finalize/validator.py +1 -1
  248. infrahub/message_bus/operations/git/file.py +2 -2
  249. infrahub/message_bus/operations/git/repository.py +1 -1
  250. infrahub/message_bus/operations/requests/__init__.py +0 -2
  251. infrahub/message_bus/operations/requests/generator_definition.py +1 -1
  252. infrahub/message_bus/operations/requests/proposed_change.py +26 -11
  253. infrahub/message_bus/operations/requests/repository.py +2 -2
  254. infrahub/message_bus/operations/send/echo.py +1 -1
  255. infrahub/message_bus/types.py +1 -1
  256. infrahub/permissions/__init__.py +2 -1
  257. infrahub/permissions/types.py +26 -0
  258. infrahub/pools/prefix.py +29 -165
  259. infrahub/prefect_server/__init__.py +0 -0
  260. infrahub/prefect_server/app.py +18 -0
  261. infrahub/prefect_server/database.py +20 -0
  262. infrahub/prefect_server/events.py +28 -0
  263. infrahub/prefect_server/models.py +46 -0
  264. infrahub/proposed_change/models.py +15 -1
  265. infrahub/proposed_change/tasks.py +173 -35
  266. infrahub/pytest_plugin.py +4 -4
  267. infrahub/server.py +12 -11
  268. infrahub/services/__init__.py +147 -62
  269. infrahub/services/adapters/cache/__init__.py +7 -5
  270. infrahub/services/adapters/cache/nats.py +40 -22
  271. infrahub/services/adapters/cache/redis.py +0 -4
  272. infrahub/services/adapters/event/__init__.py +10 -18
  273. infrahub/services/adapters/http/__init__.py +0 -5
  274. infrahub/services/adapters/http/httpx.py +22 -15
  275. infrahub/services/adapters/message_bus/__init__.py +23 -6
  276. infrahub/services/adapters/message_bus/local.py +8 -6
  277. infrahub/services/adapters/message_bus/nats.py +12 -6
  278. infrahub/services/adapters/message_bus/rabbitmq.py +22 -9
  279. infrahub/services/adapters/workflow/__init__.py +11 -8
  280. infrahub/services/adapters/workflow/local.py +28 -7
  281. infrahub/services/adapters/workflow/worker.py +23 -7
  282. infrahub/services/component.py +38 -35
  283. infrahub/services/scheduler.py +32 -29
  284. infrahub/storage.py +2 -4
  285. infrahub/task_manager/constants.py +1 -1
  286. infrahub/task_manager/event.py +182 -0
  287. infrahub/task_manager/models.py +125 -1
  288. infrahub/task_manager/task.py +1 -1
  289. infrahub/tasks/artifact.py +14 -16
  290. infrahub/tasks/registry.py +1 -1
  291. infrahub/tasks/telemetry.py +13 -14
  292. infrahub/transformations/tasks.py +3 -5
  293. infrahub/trigger/__init__.py +0 -0
  294. infrahub/trigger/catalogue.py +15 -0
  295. infrahub/trigger/constants.py +9 -0
  296. infrahub/trigger/models.py +69 -0
  297. infrahub/trigger/tasks.py +85 -0
  298. infrahub/types.py +1 -1
  299. infrahub/utils.py +1 -1
  300. infrahub/webhook/constants.py +0 -2
  301. infrahub/webhook/models.py +8 -2
  302. infrahub/webhook/tasks.py +20 -73
  303. infrahub/webhook/triggers.py +20 -0
  304. infrahub/workers/infrahub_async.py +36 -25
  305. infrahub/workers/utils.py +63 -0
  306. infrahub/workflows/catalogue.py +13 -37
  307. infrahub/workflows/initialization.py +6 -8
  308. infrahub/workflows/models.py +3 -5
  309. infrahub/workflows/utils.py +1 -1
  310. infrahub_sdk/ctl/check.py +3 -3
  311. infrahub_sdk/ctl/cli_commands.py +11 -10
  312. infrahub_sdk/ctl/exceptions.py +0 -6
  313. infrahub_sdk/ctl/exporter.py +1 -1
  314. infrahub_sdk/ctl/generator.py +5 -5
  315. infrahub_sdk/ctl/importer.py +3 -2
  316. infrahub_sdk/ctl/menu.py +1 -1
  317. infrahub_sdk/ctl/object.py +1 -1
  318. infrahub_sdk/ctl/repository.py +23 -15
  319. infrahub_sdk/ctl/schema.py +2 -2
  320. infrahub_sdk/ctl/utils.py +4 -3
  321. infrahub_sdk/ctl/validate.py +2 -1
  322. infrahub_sdk/exceptions.py +6 -0
  323. infrahub_sdk/generator.py +3 -0
  324. infrahub_sdk/node.py +2 -2
  325. infrahub_sdk/schema/__init__.py +14 -2
  326. infrahub_sdk/schema/main.py +7 -0
  327. infrahub_sdk/utils.py +11 -1
  328. infrahub_sdk/yaml.py +2 -3
  329. {infrahub_server-1.1.6.dist-info → infrahub_server-1.2.0rc0.dist-info}/METADATA +46 -12
  330. {infrahub_server-1.1.6.dist-info → infrahub_server-1.2.0rc0.dist-info}/RECORD +338 -321
  331. infrahub_testcontainers/container.py +14 -6
  332. infrahub_testcontainers/docker-compose.test.yml +24 -5
  333. infrahub_testcontainers/haproxy.cfg +43 -0
  334. infrahub_testcontainers/helpers.py +85 -1
  335. infrahub/core/branch/constants.py +0 -2
  336. infrahub/graphql/query.py +0 -52
  337. infrahub/message_bus/messages/request_artifactdefinition_check.py +0 -17
  338. infrahub/message_bus/operations/requests/artifact_definition.py +0 -148
  339. infrahub/schema/constants.py +0 -1
  340. infrahub/schema/tasks.py +0 -76
  341. infrahub/services/adapters/database/__init__.py +0 -9
  342. infrahub_sdk/ctl/_file.py +0 -13
  343. /infrahub/{schema → artifacts}/__init__.py +0 -0
  344. {infrahub_server-1.1.6.dist-info → infrahub_server-1.2.0rc0.dist-info}/LICENSE.txt +0 -0
  345. {infrahub_server-1.1.6.dist-info → infrahub_server-1.2.0rc0.dist-info}/WHEEL +0 -0
  346. {infrahub_server-1.1.6.dist-info → infrahub_server-1.2.0rc0.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,182 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, Any
4
+
5
+ from prefect.client.orchestration import PrefectClient, get_client
6
+ from prefect.events.schemas.events import Event as PrefectEventModel
7
+ from pydantic import BaseModel, Field, TypeAdapter
8
+
9
+ from infrahub.log import get_logger
10
+ from infrahub.utils import get_nested_dict
11
+
12
+ log = get_logger()
13
+
14
+ if TYPE_CHECKING:
15
+ from .models import InfrahubEventFilter
16
+
17
+
18
+ class PrefectEventData(PrefectEventModel):
19
+ def get_branch(self) -> str | None:
20
+ for resource in self.related:
21
+ if resource.get("prefect.resource.role") != "infrahub.branch":
22
+ continue
23
+ if "infrahub.resource.label" not in resource:
24
+ continue
25
+ return resource.get("infrahub.resource.label")
26
+ return None
27
+
28
+ def get_level(self) -> int:
29
+ for resource in self.related:
30
+ level = resource.get("infrahub.event.level")
31
+ if level is None:
32
+ continue
33
+ try:
34
+ return int(level)
35
+ except ValueError:
36
+ return 0
37
+
38
+ return 0
39
+
40
+ def get_parent(self) -> str | None:
41
+ for resource in self.related:
42
+ if resource.get("prefect.resource.role") != "infrahub.child_event":
43
+ continue
44
+ return resource.get("infrahub.event_parent.id")
45
+ return None
46
+
47
+ def get_primary_node(self) -> dict[str, str] | None:
48
+ node_id = self.resource.get("infrahub.node.id")
49
+ node_kind = self.resource.get("infrahub.node.kind")
50
+ if node_id and node_kind:
51
+ return {"id": node_id, "kind": node_kind}
52
+
53
+ return None
54
+
55
+ def get_related_nodes(self) -> list[dict[str, str]]:
56
+ related_nodes = []
57
+ for resource in self.related:
58
+ if resource.get("prefect.resource.role") != "infrahub.related.node":
59
+ continue
60
+
61
+ node_id = resource.get("prefect.resource.id")
62
+ node_kind = resource.get("infrahub.node.kind")
63
+ if node_id == self.resource.get("infrahub.node.id"):
64
+ # Don't include the primary node as a related node.
65
+ continue
66
+ if node_id and node_kind:
67
+ related_nodes.append({"id": node_id, "kind": node_kind})
68
+
69
+ return related_nodes
70
+
71
+ def get_account_id(self) -> str | None:
72
+ for resource in self.related:
73
+ if resource.get("prefect.resource.role") != "infrahub.account":
74
+ continue
75
+ return resource.get("infrahub.resource.id")
76
+ return None
77
+
78
+ def has_children(self) -> bool:
79
+ for resource in self.related:
80
+ if resource.get("prefect.resource.role") != "infrahub.event":
81
+ continue
82
+ if resource.get("infrahub.event.has_children") == "true":
83
+ return True
84
+ return False
85
+ return False
86
+
87
+ def _return_node_mutation(self) -> dict[str, Any]:
88
+ attributes = []
89
+
90
+ for resource in self.related:
91
+ if resource.get("prefect.resource.role") == "infrahub.node.field_update" and resource.get(
92
+ "infrahub.attribute.name"
93
+ ):
94
+ attributes.append(
95
+ {
96
+ "name": resource.get("infrahub.attribute.name", ""),
97
+ "kind": resource.get("infrahub.attribute.kind", ""),
98
+ "value": None
99
+ if resource.get("infrahub.attribute.value") == "NULL"
100
+ else resource.get("infrahub.attribute.value"),
101
+ "value_previous": None
102
+ if resource.get("infrahub.attribute.value_previous") == "NULL"
103
+ else resource.get("infrahub.attribute.value_previous"),
104
+ "action": resource.get("infrahub.attribute.action", "unchanged"),
105
+ }
106
+ )
107
+
108
+ return {"attributes": attributes}
109
+
110
+ def _return_event_specifics(self) -> dict[str, Any]:
111
+ match self.event:
112
+ case "infrahub.node.created" | "infrahub.node.updated" | "infrahub.node.deleted":
113
+ return self._return_node_mutation()
114
+
115
+ return {}
116
+
117
+ def to_graphql(self) -> dict[str, Any]:
118
+ response = {
119
+ "id": str(self.id),
120
+ "event": self.event,
121
+ "branch": self.get_branch(),
122
+ "account_id": self.get_account_id(),
123
+ "occurred_at": self.occurred,
124
+ "has_children": self.has_children(),
125
+ "payload": self.payload,
126
+ "level": self.get_level(),
127
+ "primary_node": self.get_primary_node(),
128
+ "parent_id": self.get_parent(),
129
+ "related_nodes": self.get_related_nodes(),
130
+ }
131
+ response.update(self._return_event_specifics())
132
+ return response
133
+
134
+
135
+ class PrefectEventResponse(BaseModel):
136
+ count: int = Field(..., description="Number of matching events")
137
+ events: list[PrefectEventData] = Field(..., description="Returned events")
138
+
139
+
140
+ class PrefectEvent:
141
+ @classmethod
142
+ async def query_events(
143
+ cls,
144
+ client: PrefectClient,
145
+ limit: int,
146
+ filters: InfrahubEventFilter,
147
+ offset: int | None = None,
148
+ ) -> PrefectEventResponse:
149
+ body = {"limit": limit, "filter": filters.model_dump(mode="json", exclude_none=True), "offset": offset}
150
+
151
+ response = await client._client.post("/infrahub/events/filter", json=body)
152
+ response.raise_for_status()
153
+ data: dict[str, Any] = response.json()
154
+
155
+ return PrefectEventResponse(
156
+ count=data.get("total", 0),
157
+ events=TypeAdapter(list[PrefectEventData]).validate_python(data.get("events")),
158
+ )
159
+
160
+ @classmethod
161
+ async def query(
162
+ cls,
163
+ fields: dict[str, Any],
164
+ event_filter: InfrahubEventFilter,
165
+ limit: int | None = None,
166
+ offset: int | None = None,
167
+ ) -> dict[str, Any]:
168
+ nodes: list[dict] = []
169
+ limit = limit or 50
170
+
171
+ node_fields = get_nested_dict(nested_dict=fields, keys=["edges", "node"])
172
+
173
+ if not node_fields:
174
+ # This means that it's purely a count query and as such we can override the limit to avoid
175
+ # returning data that will only be discarded
176
+ limit = 1
177
+
178
+ async with get_client(sync_client=False) as client:
179
+ response = await cls.query_events(client=client, filters=event_filter, limit=limit, offset=offset)
180
+ nodes = [{"node": event.to_graphql()} for event in response.events]
181
+
182
+ return {"count": response.count, "edges": nodes}
@@ -1,11 +1,29 @@
1
+ from __future__ import annotations
2
+
3
+ import uuid
1
4
  from collections import defaultdict
5
+ from typing import TYPE_CHECKING
2
6
  from uuid import UUID
3
7
 
4
- from prefect.client.schemas.objects import Log as PrefectLog
8
+ from prefect.client.schemas.objects import Log as PrefectLog # noqa: TC002
9
+ from prefect.events.filters import (
10
+ EventFilter,
11
+ EventIDFilter,
12
+ EventNameFilter,
13
+ EventOccurredFilter,
14
+ EventRelatedFilter,
15
+ EventResourceFilter,
16
+ )
17
+ from prefect.events.schemas.events import ResourceSpecification
5
18
  from pydantic import BaseModel, Field
6
19
 
20
+ from infrahub.core.timestamp import Timestamp
21
+
7
22
  from .constants import LOG_LEVEL_MAPPING
8
23
 
24
+ if TYPE_CHECKING:
25
+ from datetime import datetime
26
+
9
27
 
10
28
  class RelatedNodeInfo(BaseModel):
11
29
  id: str
@@ -63,3 +81,109 @@ class FlowLogs(BaseModel):
63
81
 
64
82
  class FlowProgress(BaseModel):
65
83
  data: dict[UUID, float] = Field(default_factory=dict)
84
+
85
+
86
+ class InfrahubEventFilter(EventFilter):
87
+ matching_related: list[EventRelatedFilter] = Field(default_factory=list)
88
+
89
+ def add_account_filter(self, account__ids: list[str] | None) -> None:
90
+ if account__ids:
91
+ self.matching_related.append(
92
+ EventRelatedFilter(
93
+ labels=ResourceSpecification(
94
+ {"prefect.resource.role": "infrahub.account", "infrahub.resource.id": account__ids}
95
+ )
96
+ )
97
+ )
98
+
99
+ def add_branch_filter(self, branches: list[str] | None = None) -> None:
100
+ if branches:
101
+ self.matching_related.append(
102
+ EventRelatedFilter(
103
+ labels=ResourceSpecification(
104
+ {"prefect.resource.role": "infrahub.branch", "infrahub.resource.label": branches}
105
+ )
106
+ )
107
+ )
108
+
109
+ def add_event_filter(self, level: int | None = None, has_children: bool | None = None) -> None:
110
+ event_filter: dict[str, list[str] | str] = {}
111
+ if level is not None:
112
+ event_filter["infrahub.event.level"] = str(level)
113
+
114
+ if has_children is not None:
115
+ event_filter["infrahub.event.has_children"] = str(has_children).lower()
116
+
117
+ if event_filter:
118
+ event_filter["prefect.resource.role"] = "infrahub.event"
119
+ self.matching_related.append(EventRelatedFilter(labels=ResourceSpecification(event_filter)))
120
+
121
+ def add_event_id_filter(self, ids: list[str] | None = None) -> None:
122
+ if ids:
123
+ self.id = EventIDFilter(id=[uuid.UUID(id) for id in ids])
124
+
125
+ def add_event_type_filter(self, event_type: list[str] | None = None) -> None:
126
+ if event_type:
127
+ self.event = EventNameFilter(name=event_type)
128
+
129
+ def add_primary_node_filter(self, primary_node__ids: list[str] | None) -> None:
130
+ if primary_node__ids:
131
+ self.resource = EventResourceFilter(labels=ResourceSpecification({"infrahub.node.id": primary_node__ids}))
132
+
133
+ def add_parent_filter(self, parent__ids: list[str] | None) -> None:
134
+ if parent__ids:
135
+ self.matching_related.append(
136
+ EventRelatedFilter(
137
+ labels=ResourceSpecification(
138
+ {"prefect.resource.role": "infrahub.child_event", "infrahub.event_parent.id": parent__ids}
139
+ )
140
+ )
141
+ )
142
+
143
+ def add_related_node_filter(self, related_node__ids: list[str] | None) -> None:
144
+ if related_node__ids:
145
+ self.matching_related.append(
146
+ EventRelatedFilter(
147
+ labels=ResourceSpecification(
148
+ {"prefect.resource.role": "infrahub.related.node", "prefect.resource.id": related_node__ids}
149
+ )
150
+ )
151
+ )
152
+
153
+ @classmethod
154
+ def from_filters(
155
+ cls,
156
+ ids: list[str] | None = None,
157
+ account__ids: list[str] | None = None,
158
+ related_node__ids: list[str] | None = None,
159
+ parent__ids: list[str] | None = None,
160
+ primary_node__ids: list[str] | None = None,
161
+ event_type: list[str] | None = None,
162
+ branches: list[str] | None = None,
163
+ level: int | None = None,
164
+ has_children: bool | None = None,
165
+ since: datetime | None = None,
166
+ until: datetime | None = None,
167
+ ) -> InfrahubEventFilter:
168
+ occurred_filter = {}
169
+ if since:
170
+ occurred_filter["since"] = Timestamp(since.isoformat()).obj
171
+
172
+ if until:
173
+ occurred_filter["until"] = Timestamp(until.isoformat()).obj
174
+
175
+ if occurred_filter:
176
+ filters = cls(occurred=EventOccurredFilter(**occurred_filter))
177
+ else:
178
+ filters = cls()
179
+
180
+ filters.add_event_filter(level=level, has_children=has_children)
181
+ filters.add_event_id_filter(ids=ids)
182
+ filters.add_event_type_filter(event_type=event_type)
183
+ filters.add_branch_filter(branches=branches)
184
+ filters.add_account_filter(account__ids=account__ids)
185
+ filters.add_parent_filter(parent__ids=parent__ids)
186
+ filters.add_primary_node_filter(primary_node__ids=primary_node__ids)
187
+ filters.add_related_node_filter(related_node__ids=related_node__ids)
188
+
189
+ return filters
@@ -250,7 +250,7 @@ class PrefectTask:
250
250
  "node": {
251
251
  "title": flow.name,
252
252
  "conclusion": CONCLUSION_STATE_MAPPING.get(
253
- flow.state_name, TaskConclusion.UNKNOWN
253
+ str(flow.state_name), TaskConclusion.UNKNOWN
254
254
  ).value,
255
255
  "state": flow.state_type,
256
256
  "progress": progress_flow.data.get(flow.id, None),
@@ -5,40 +5,38 @@ from prefect import task
5
5
  from prefect.cache_policies import NONE
6
6
 
7
7
  from infrahub import lock
8
+ from infrahub.artifacts.models import CheckArtifactCreate
8
9
  from infrahub.core.constants import InfrahubKind
9
10
  from infrahub.git.models import RequestArtifactGenerate
10
- from infrahub.message_bus import messages
11
11
  from infrahub.services import InfrahubServices
12
12
 
13
13
 
14
- @task(name="define-artifact", task_run_name="Define Artifact", cache_policy=NONE)
14
+ @task(name="define-artifact", task_run_name="Define Artifact", cache_policy=NONE) # type: ignore[arg-type]
15
15
  async def define_artifact(
16
- message: Union[messages.CheckArtifactCreate, RequestArtifactGenerate], service: InfrahubServices
16
+ model: Union[CheckArtifactCreate, RequestArtifactGenerate], service: InfrahubServices
17
17
  ) -> InfrahubNode:
18
- if message.artifact_id:
19
- artifact = await service.client.get(
20
- kind=InfrahubKind.ARTIFACT, id=message.artifact_id, branch=message.branch_name
21
- )
18
+ if model.artifact_id:
19
+ artifact = await service.client.get(kind=InfrahubKind.ARTIFACT, id=model.artifact_id, branch=model.branch_name)
22
20
  else:
23
- async with lock.registry.get(f"{message.target_id}-{message.artifact_definition}", namespace="artifact"):
21
+ async with lock.registry.get(f"{model.target_id}-{model.artifact_definition}", namespace="artifact"):
24
22
  artifacts = await service.client.filters(
25
23
  kind=InfrahubKind.ARTIFACT,
26
- branch=message.branch_name,
27
- definition__ids=[message.artifact_definition],
28
- object__ids=[message.target_id],
24
+ branch=model.branch_name,
25
+ definition__ids=[model.artifact_definition],
26
+ object__ids=[model.target_id],
29
27
  )
30
28
  if artifacts:
31
29
  artifact = artifacts[0]
32
30
  else:
33
31
  artifact = await service.client.create(
34
32
  kind=InfrahubKind.ARTIFACT,
35
- branch=message.branch_name,
33
+ branch=model.branch_name,
36
34
  data={
37
- "name": message.artifact_name,
35
+ "name": model.artifact_name,
38
36
  "status": "Pending",
39
- "object": message.target_id,
40
- "definition": message.artifact_definition,
41
- "content_type": message.content_type,
37
+ "object": model.target_id,
38
+ "definition": model.artifact_definition,
39
+ "content_type": model.content_type,
42
40
  },
43
41
  )
44
42
  await artifact.save()
@@ -18,7 +18,7 @@ async def refresh_branches(db: InfrahubDatabase) -> None:
18
18
  If a branch is already present with a different value for the hash
19
19
  We pull the new schema from the database and we update the registry.
20
20
  """
21
- from infrahub.graphql.manager import GraphQLSchemaManager # pylint: disable=import-outside-toplevel,cyclic-import
21
+ from infrahub.graphql.manager import GraphQLSchemaManager
22
22
 
23
23
  async with lock.registry.local_schema_lock():
24
24
  branches = await registry.branch_object.get_list(db=db)
@@ -13,14 +13,14 @@ from infrahub.core import registry, utils
13
13
  from infrahub.core.branch import Branch
14
14
  from infrahub.core.constants import InfrahubKind
15
15
  from infrahub.core.graph.schema import GRAPH_SCHEMA
16
- from infrahub.services import InfrahubServices, services
16
+ from infrahub.services import InfrahubServices
17
17
 
18
18
  TELEMETRY_KIND: str = "community"
19
19
  TELEMETRY_VERSION: str = "20240524"
20
20
 
21
21
 
22
- @task(name="telemetry-gather-db", task_run_name="Gather Database Information", cache_policy=NONE)
23
- async def gather_database_information(service: InfrahubServices, branch: Branch) -> dict: # pylint: disable=unused-argument
22
+ @task(name="telemetry-gather-db", task_run_name="Gather Database Information", cache_policy=NONE) # type: ignore[arg-type]
23
+ async def gather_database_information(service: InfrahubServices) -> dict:
24
24
  async with service.database.start_session() as db:
25
25
  data: dict[str, Any] = {
26
26
  "database_type": db.db_type.value,
@@ -37,8 +37,8 @@ async def gather_database_information(service: InfrahubServices, branch: Branch)
37
37
  return data
38
38
 
39
39
 
40
- @task(name="telemetry-schema-information", task_run_name="Gather Schema Information", cache_policy=NONE)
41
- async def gather_schema_information(service: InfrahubServices, branch: Branch) -> dict: # pylint: disable=unused-argument
40
+ @task(name="telemetry-schema-information", task_run_name="Gather Schema Information", cache_policy=NONE) # type: ignore[arg-type]
41
+ async def gather_schema_information(branch: Branch) -> dict:
42
42
  data: dict[str, Any] = {}
43
43
  main_schema = registry.schema.get_schema_branch(name=branch.name)
44
44
  data["node_count"] = len(main_schema.node_names)
@@ -48,8 +48,8 @@ async def gather_schema_information(service: InfrahubServices, branch: Branch) -
48
48
  return data
49
49
 
50
50
 
51
- @task(name="telemetry-feature-information", task_run_name="Gather Feature Information", cache_policy=NONE)
52
- async def gather_feature_information(service: InfrahubServices, branch: Branch) -> dict: # pylint: disable=unused-argument
51
+ @task(name="telemetry-feature-information", task_run_name="Gather Feature Information", cache_policy=NONE) # type: ignore[arg-type]
52
+ async def gather_feature_information(service: InfrahubServices) -> dict:
53
53
  async with service.database.start_session() as db:
54
54
  data = {}
55
55
  features_to_count = [
@@ -67,7 +67,7 @@ async def gather_feature_information(service: InfrahubServices, branch: Branch)
67
67
  return data
68
68
 
69
69
 
70
- @task(name="telemetry-gather-data", task_run_name="Gather Anonynous Data", cache_policy=NONE)
70
+ @task(name="telemetry-gather-data", task_run_name="Gather Anonynous Data", cache_policy=NONE) # type: ignore[arg-type]
71
71
  async def gather_anonymous_telemetry_data(service: InfrahubServices) -> dict:
72
72
  start_time = time.time()
73
73
 
@@ -87,9 +87,9 @@ async def gather_anonymous_telemetry_data(service: InfrahubServices) -> dict:
87
87
  "branches": {
88
88
  "total": len(registry.branch),
89
89
  },
90
- "features": await gather_feature_information(service=service, branch=default_branch),
91
- "schema": await gather_schema_information(service=service, branch=default_branch),
92
- "database": await gather_database_information(service=service, branch=default_branch),
90
+ "features": await gather_feature_information(service=service),
91
+ "schema": await gather_schema_information(branch=default_branch),
92
+ "database": await gather_database_information(service=service),
93
93
  }
94
94
 
95
95
  data["execution_time"] = time.time() - start_time
@@ -97,7 +97,7 @@ async def gather_anonymous_telemetry_data(service: InfrahubServices) -> dict:
97
97
  return data
98
98
 
99
99
 
100
- @task(name="telemetry-post-data", task_run_name="Upload data", retries=5, cache_policy=NONE)
100
+ @task(name="telemetry-post-data", task_run_name="Upload data", retries=5, cache_policy=NONE) # type: ignore[arg-type]
101
101
  async def post_telemetry_data(service: InfrahubServices, url: str, payload: dict[str, Any]) -> None:
102
102
  """Send the telemetry data to the specified URL, using HTTP POST."""
103
103
  response = await service.http.post(url=url, json=payload)
@@ -105,8 +105,7 @@ async def post_telemetry_data(service: InfrahubServices, url: str, payload: dict
105
105
 
106
106
 
107
107
  @flow(name="anonymous_telemetry_send", flow_run_name="Send anonymous telemetry")
108
- async def send_telemetry_push() -> None:
109
- service = services.service
108
+ async def send_telemetry_push(service: InfrahubServices) -> None:
110
109
  log = get_run_logger()
111
110
  if config.SETTINGS.main.telemetry_optout:
112
111
  log.info("Skipping, User opted out of this service.")
@@ -4,7 +4,7 @@ from prefect import flow
4
4
 
5
5
  from infrahub.git.repository import get_initialized_repo
6
6
  from infrahub.log import get_logger
7
- from infrahub.services import services
7
+ from infrahub.services import InfrahubServices
8
8
  from infrahub.workflows.utils import add_branch_tag
9
9
 
10
10
  from .models import TransformJinjaTemplateData, TransformPythonData
@@ -13,8 +13,7 @@ log = get_logger()
13
13
 
14
14
 
15
15
  @flow(name="transform_render_python", flow_run_name="Render transform python", persist_result=True)
16
- async def transform_python(message: TransformPythonData) -> Any:
17
- service = services.service
16
+ async def transform_python(message: TransformPythonData, service: InfrahubServices) -> Any:
18
17
  await add_branch_tag(branch_name=message.branch)
19
18
 
20
19
  repo = await get_initialized_repo(
@@ -37,8 +36,7 @@ async def transform_python(message: TransformPythonData) -> Any:
37
36
 
38
37
 
39
38
  @flow(name="transform_render_jinja2_template", flow_run_name="Render transform Jinja2", persist_result=True)
40
- async def transform_render_jinja2_template(message: TransformJinjaTemplateData) -> str:
41
- service = services.service
39
+ async def transform_render_jinja2_template(message: TransformJinjaTemplateData, service: InfrahubServices) -> str:
42
40
  await add_branch_tag(branch_name=message.branch)
43
41
 
44
42
  repo = await get_initialized_repo(
File without changes
@@ -0,0 +1,15 @@
1
+ from infrahub.computed_attribute.triggers import (
2
+ TRIGGER_COMPUTED_ATTRIBUTE_ALL_SCHEMA,
3
+ TRIGGER_COMPUTED_ATTRIBUTE_PYTHON_CLEAN_BRANCH,
4
+ TRIGGER_COMPUTED_ATTRIBUTE_PYTHON_SETUP_BRANCH,
5
+ TRIGGER_COMPUTED_ATTRIBUTE_PYTHON_SETUP_COMMIT,
6
+ )
7
+ from infrahub.webhook.triggers import TRIGGER_WEBHOOK_SETUP_UPDATE
8
+
9
+ triggers = [
10
+ TRIGGER_COMPUTED_ATTRIBUTE_ALL_SCHEMA,
11
+ TRIGGER_COMPUTED_ATTRIBUTE_PYTHON_CLEAN_BRANCH,
12
+ TRIGGER_COMPUTED_ATTRIBUTE_PYTHON_SETUP_BRANCH,
13
+ TRIGGER_COMPUTED_ATTRIBUTE_PYTHON_SETUP_COMMIT,
14
+ TRIGGER_WEBHOOK_SETUP_UPDATE,
15
+ ]
@@ -0,0 +1,9 @@
1
+ DEPRECATED_STATIC_TRIGGER_NAMES = [
2
+ "Trigger-branch-create-events", # used in 1.1.x
3
+ "Trigger-branch-remove-events", # used in 1.1.x
4
+ "Trigger-webhook-update-configuration", # used in 1.1.x
5
+ "Trigger-repository-commit-update-event", # used in 1.1.x
6
+ "Trigger-schema-update-event", # used in 1.1.x
7
+ ]
8
+
9
+ NAME_SEPARATOR = "::"
@@ -0,0 +1,69 @@
1
+ from datetime import timedelta
2
+ from enum import Enum
3
+ from typing import Any
4
+ from uuid import UUID
5
+
6
+ from prefect.events.actions import RunDeployment
7
+ from prefect.events.schemas.automations import EventTrigger as PrefectEventTrigger
8
+ from prefect.events.schemas.automations import Posture
9
+ from prefect.events.schemas.events import ResourceSpecification
10
+ from pydantic import BaseModel, Field
11
+
12
+ from .constants import NAME_SEPARATOR
13
+
14
+
15
+ class TriggerType(str, Enum):
16
+ BUILTIN = "builtin"
17
+ # OBJECT = "object"
18
+ # COMPUTED_ATTR = "computed_attr"
19
+
20
+
21
+ class EventTrigger(BaseModel):
22
+ events: set = Field(default_factory=set)
23
+ match: dict[str, Any] = Field(default_factory=dict)
24
+ match_related: dict[str, Any] = Field(default_factory=dict)
25
+
26
+ def get_prefect(self) -> PrefectEventTrigger:
27
+ return PrefectEventTrigger(
28
+ posture=Posture.Reactive,
29
+ expect=self.events,
30
+ within=timedelta(0),
31
+ match=ResourceSpecification(self.match),
32
+ match_related=ResourceSpecification(self.match_related),
33
+ threshold=1,
34
+ )
35
+
36
+
37
+ class ExecuteWorkflow(BaseModel):
38
+ name: str
39
+ parameters: dict[str, Any] = Field(default_factory=dict)
40
+
41
+ def get_prefect(self, mapping: dict[str, UUID]) -> RunDeployment:
42
+ deployment_id = mapping[self.name]
43
+
44
+ return RunDeployment(
45
+ source="selected",
46
+ deployment_id=deployment_id,
47
+ parameters=self.parameters,
48
+ job_variables={},
49
+ )
50
+
51
+
52
+ class TriggerDefinition(BaseModel):
53
+ name: str
54
+ type: TriggerType
55
+ previous_names: set = Field(default_factory=set)
56
+ description: str = ""
57
+ trigger: EventTrigger
58
+ actions: list[ExecuteWorkflow]
59
+
60
+ def get_deployment_names(self) -> list[str]:
61
+ """Return the name of all deployments used by this trigger"""
62
+ return [action.name for action in self.actions]
63
+
64
+ def generate_name(self) -> str:
65
+ return f"{self.type.value}{NAME_SEPARATOR}{self.name}"
66
+
67
+
68
+ class BuiltinTriggerDefinition(TriggerDefinition):
69
+ type: TriggerType = TriggerType.BUILTIN