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
infrahub/events/models.py CHANGED
@@ -1,93 +1,201 @@
1
- from typing import Any
1
+ from __future__ import annotations
2
2
 
3
- from pydantic import BaseModel, Field
3
+ from copy import deepcopy
4
+ from typing import Any, Self, cast, final
5
+ from uuid import UUID, uuid4
4
6
 
7
+ from pydantic import BaseModel, Field, PrivateAttr, computed_field, model_validator
8
+
9
+ from infrahub import __version__
10
+ from infrahub.auth import AccountSession, AuthType
11
+ from infrahub.context import InfrahubContext # noqa: TC001
12
+ from infrahub.core.branch import Branch # noqa: TC001
5
13
  from infrahub.message_bus import InfrahubMessage, Meta
14
+ from infrahub.worker import WORKER_IDENTITY
6
15
 
7
16
  from .constants import EVENT_NAMESPACE
8
17
 
9
18
 
19
+ class EventNode(BaseModel):
20
+ id: str
21
+ kind: str
22
+
23
+
24
+ class ParentEvent(BaseModel):
25
+ id: str
26
+ name: str
27
+
28
+
10
29
  class EventMeta(BaseModel):
30
+ branch: Branch | None = Field(default=None)
11
31
  request_id: str = ""
12
- account_id: str = ""
13
- initiator_id: str | None = Field(
14
- default=None, description="The worker identity of the initial sender of this message"
32
+ account_id: str | None = Field(default=None, description="The ID of the account triggering this event")
33
+ initiator_id: str = Field(
34
+ default=WORKER_IDENTITY, description="The worker identity of the initial sender of this message"
35
+ )
36
+ context: InfrahubContext = Field(..., description="The context used when originating this event")
37
+ level: int = Field(default=0)
38
+ has_children: bool = Field(
39
+ default=False, description="Indicates if this event might potentially have child events under it."
15
40
  )
16
41
 
42
+ id: UUID = Field(
43
+ default_factory=uuid4,
44
+ description="UUID of the event",
45
+ )
17
46
 
18
- class InfrahubEvent(BaseModel):
19
- meta: EventMeta | None = None
47
+ parent: UUID | None = Field(default=None, description="The UUID of the parent event if applicable")
48
+ ancestors: list[ParentEvent] = Field(default_factory=list, description="Any event used to trigger this event")
49
+ _created_with_context: bool = PrivateAttr(default=False)
20
50
 
21
- def get_event_namespace(self) -> str:
22
- return EVENT_NAMESPACE
51
+ def get_branch_id(self) -> str:
52
+ if self.context.branch.id:
53
+ return self.context.branch.id
23
54
 
24
- def get_name(self) -> str:
25
- return f"{self.get_event_namespace()}.unknown"
55
+ if self.branch:
56
+ return str(self.branch.get_uuid())
26
57
 
27
- def get_resource(self) -> dict[str, str]:
28
- raise NotImplementedError
58
+ return ""
29
59
 
30
- def get_messages(self) -> list[InfrahubMessage]:
31
- raise NotImplementedError
60
+ def get_id(self) -> str:
61
+ return str(self.id)
32
62
 
33
63
  def get_related(self) -> list[dict[str, str]]:
34
- related: list[dict[str, str]] = []
35
-
36
- if not self.meta:
37
- return related
38
-
39
- if self.meta.account_id:
40
- related.append(
41
- {
42
- "prefect.resource.id": f"infrahub.account.{self.meta.account_id}",
43
- "prefect.resource.role": "account",
44
- }
45
- )
46
-
47
- if self.meta.request_id:
64
+ related: list[dict[str, str]] = [
65
+ {"prefect.resource.id": __version__, "prefect.resource.role": "infrahub.version"},
66
+ {
67
+ "prefect.resource.id": self.get_id(),
68
+ "prefect.resource.role": "infrahub.event",
69
+ "infrahub.event.has_children": str(self.has_children).lower(),
70
+ "infrahub.event.level": str(self.level),
71
+ },
72
+ {
73
+ "prefect.resource.id": f"infrahub.account.{self.context.account.account_id}",
74
+ "prefect.resource.role": "infrahub.account",
75
+ "infrahub.resource.id": self.context.account.account_id,
76
+ },
77
+ {
78
+ "prefect.resource.id": f"infrahub.branch.{self.get_branch_id()}",
79
+ "prefect.resource.role": "infrahub.branch",
80
+ "infrahub.resource.id": self.get_branch_id(),
81
+ "infrahub.resource.label": self.context.branch.name,
82
+ },
83
+ ]
84
+
85
+ if self.parent:
48
86
  related.append(
49
87
  {
50
- "prefect.resource.id": f"infrahub.request.{self.meta.request_id}",
51
- "prefect.resource.role": "request",
88
+ "prefect.resource.id": self.get_id(),
89
+ "prefect.resource.role": "infrahub.child_event",
90
+ "infrahub.event_parent.id": str(self.parent),
52
91
  }
53
92
  )
54
93
 
55
- if self.meta.initiator_id:
94
+ for ancestor in self.ancestors:
56
95
  related.append(
57
96
  {
58
- "prefect.resource.id": f"infrahub.source.{self.meta.initiator_id}",
59
- "prefect.resource.role": "event_source",
97
+ "prefect.resource.id": ancestor.id,
98
+ "prefect.resource.role": "infrahub.ancestor_event",
99
+ "infrahub.ancestor_event.name": ancestor.name,
60
100
  }
61
101
  )
62
102
 
63
103
  return related
64
104
 
105
+ @classmethod
106
+ def with_dummy_context(cls, branch: Branch) -> EventMeta:
107
+ return cls(
108
+ branch=branch,
109
+ context=InfrahubContext.init(
110
+ branch=branch, account=AccountSession(auth_type=AuthType.NONE, authenticated=False, account_id="")
111
+ ),
112
+ )
113
+
114
+ @classmethod
115
+ def from_parent(cls, parent: InfrahubEvent) -> EventMeta:
116
+ """Create the metadata from an existing event
117
+
118
+ Note that this action will modify the existing event to indicate that children might be attached to the event
119
+ """
120
+ parent.meta.has_children = True
121
+ return cls(
122
+ parent=parent.meta.id,
123
+ branch=parent.meta.branch,
124
+ request_id=parent.meta.request_id,
125
+ initiator_id=parent.meta.initiator_id,
126
+ account_id=parent.meta.account_id,
127
+ level=parent.meta.level + 1,
128
+ context=deepcopy(parent.meta.context),
129
+ ancestors=[ParentEvent(id=parent.get_id(), name=parent.get_name())] + parent.meta.ancestors,
130
+ )
131
+
132
+ @classmethod
133
+ def from_context(cls, context: InfrahubContext, branch: Branch | None = None) -> EventMeta:
134
+ # Create a copy of the context so local changes aren't brought back to a parent object
135
+ meta = cls(context=deepcopy(context))
136
+ meta._created_with_context = True
137
+ if branch:
138
+ meta.context.branch.name = branch.name
139
+ meta.context.branch.id = str(branch.get_uuid())
140
+ return meta
141
+
142
+
143
+ class InfrahubEvent(BaseModel):
144
+ meta: EventMeta = Field(..., description="Metadata for the event")
145
+
146
+ def get_id(self) -> str:
147
+ return self.meta.get_id()
148
+
149
+ def get_event_namespace(self) -> str:
150
+ return EVENT_NAMESPACE
151
+
152
+ def get_name(self) -> str:
153
+ # Convince linters that @computed_field is a property and not a method...
154
+ return cast(str, self.event_name)
155
+
156
+ def get_resource(self) -> dict[str, str]:
157
+ raise NotImplementedError
158
+
159
+ def get_messages(self) -> list[InfrahubMessage]:
160
+ raise NotImplementedError
161
+
162
+ def get_related(self) -> list[dict[str, str]]:
163
+ if not self.meta:
164
+ return []
165
+ return self.meta.get_related()
166
+
65
167
  def get_payload(self) -> dict[str, Any]:
168
+ """The purpose if this method is to allow subclasses to define their own payload.
169
+
170
+ It should not be used to get the complete payload instead .get_event_payload() should
171
+ be used for that as it will always contain the 'context' key regardless of changes
172
+ in child classes
173
+ """
66
174
  return {}
67
175
 
176
+ @final
177
+ def get_event_payload(self) -> dict[str, Any]:
178
+ """This method should be used when emitting the event to the event broker"""
179
+ event_payload = self.get_payload()
180
+ event_payload["context"] = self.meta.context.model_dump(mode="json")
181
+ return event_payload
182
+
68
183
  def get_message_meta(self) -> Meta:
69
184
  meta = Meta()
70
- if not self.meta:
71
- return meta
72
185
 
73
- if self.meta.initiator_id:
74
- meta.initiator_id = self.meta.initiator_id
186
+ meta.initiator_id = self.meta.initiator_id
75
187
  if self.meta.request_id:
76
188
  meta.initiator_id = self.meta.request_id
77
189
 
78
190
  return meta
79
191
 
192
+ @computed_field
193
+ def event_name(self) -> str:
194
+ raise NotImplementedError("The event name has not been defined")
80
195
 
81
- class InfrahubBranchEvent(InfrahubEvent): # pylint: disable=abstract-method
82
- branch: str = Field(..., description="The branch on which the event happend")
83
-
84
- def get_related(self) -> list[dict[str, str]]:
85
- related = super().get_related()
86
- related.append(
87
- {
88
- "prefect.resource.id": "infrahub.branch",
89
- "prefect.resource.name": self.branch,
90
- "prefect.resource.role": "branch",
91
- }
92
- )
93
- return related
196
+ @model_validator(mode="after")
197
+ def update_context(self) -> Self:
198
+ """Update the context object using this event provided that the meta data was created with a context."""
199
+ if self.meta._created_with_context:
200
+ self.meta.context.set_event(self.get_name(), id=self.get_id())
201
+ return self
@@ -1,24 +1,74 @@
1
1
  from typing import Any
2
2
 
3
- from pydantic import Field
3
+ from pydantic import Field, computed_field
4
4
 
5
+ from infrahub.core.branch import Branch
6
+ from infrahub.core.changelog.models import NodeChangelog
5
7
  from infrahub.core.constants import MutationAction
6
8
  from infrahub.message_bus import InfrahubMessage
7
9
 
8
- from .models import InfrahubBranchEvent
10
+ from .constants import EVENT_NAMESPACE
11
+ from .models import InfrahubEvent
9
12
 
10
13
 
11
- class NodeMutatedEvent(InfrahubBranchEvent):
14
+ class NodeMutatedEvent(InfrahubEvent):
12
15
  """Event generated when a node has been mutated"""
13
16
 
14
17
  kind: str = Field(..., description="The type of object modified")
15
18
  node_id: str = Field(..., description="The ID of the mutated node")
16
19
  action: MutationAction = Field(..., description="The action taken on the node")
17
- data: dict[str, Any] = Field(..., description="Data on modified object")
20
+ data: NodeChangelog = Field(..., description="Data on modified object")
18
21
  fields: list[str] = Field(default_factory=list, description="Fields provided in the mutation")
19
22
 
20
- def get_name(self) -> str:
21
- return f"{self.get_event_namespace()}.node.{self.action.value}"
23
+ def get_related(self) -> list[dict[str, str]]:
24
+ related = super().get_related()
25
+ for attribute in self.data.attributes.values():
26
+ related.append(
27
+ {
28
+ "prefect.resource.id": f"infrahub.node.{self.node_id}",
29
+ "prefect.resource.role": "infrahub.node.field_update",
30
+ "infrahub.attribute.name": attribute.name,
31
+ "infrahub.attribute.value": "NULL" if attribute.value is None else str(attribute.value),
32
+ "infrahub.attribute.kind": attribute.kind,
33
+ "infrahub.attribute.value_previous": "NULL"
34
+ if attribute.value_previous is None
35
+ else str(attribute.value_previous),
36
+ # Mypy doesn't understand that .value_update_status is a @computed_attribute
37
+ "infrahub.attribute.action": attribute.value_update_status.value, # type: ignore[attr-defined]
38
+ }
39
+ )
40
+ if self.data.parent:
41
+ related.append(
42
+ {
43
+ "prefect.resource.id": self.data.parent.node_id,
44
+ "prefect.resource.role": "infrahub.node.parent",
45
+ "infrahub.parent.kind": self.data.parent.node_kind,
46
+ "infrahub.parent.id": self.data.parent.node_id,
47
+ }
48
+ )
49
+
50
+ related.append(
51
+ {
52
+ "prefect.resource.id": self.node_id,
53
+ "prefect.resource.role": "infrahub.related.node",
54
+ "infrahub.node.kind": self.kind,
55
+ }
56
+ )
57
+
58
+ for related_node in self.data.get_related_nodes():
59
+ related.append(
60
+ {
61
+ "prefect.resource.id": related_node.node_id,
62
+ "prefect.resource.role": "infrahub.related.node",
63
+ "infrahub.node.kind": related_node.node_kind,
64
+ }
65
+ )
66
+
67
+ return related
68
+
69
+ @computed_field
70
+ def event_name(self) -> str:
71
+ return f"{EVENT_NAMESPACE}.node.{self.action.value}"
22
72
 
23
73
  def get_resource(self) -> dict[str, str]:
24
74
  return {
@@ -26,11 +76,11 @@ class NodeMutatedEvent(InfrahubBranchEvent):
26
76
  "infrahub.node.kind": self.kind,
27
77
  "infrahub.node.id": self.node_id,
28
78
  "infrahub.node.action": self.action.value,
29
- "infrahub.branch.name": self.branch,
79
+ "infrahub.node.root_id": self.data.root_node_id,
30
80
  }
31
81
 
32
82
  def get_payload(self) -> dict[str, Any]:
33
- return {"data": self.data, "fields": self.fields}
83
+ return {"data": self.data.model_dump(), "fields": self.fields}
34
84
 
35
85
  def get_messages(self) -> list[InfrahubMessage]:
36
86
  return [
@@ -43,3 +93,19 @@ class NodeMutatedEvent(InfrahubBranchEvent):
43
93
  # meta=self.get_message_meta(),
44
94
  # )
45
95
  ]
96
+
97
+ def set_context_branch(self, branch: Branch) -> None:
98
+ self.meta.context.branch.id = str(branch.get_uuid())
99
+ self.meta.context.branch.name = branch.name
100
+
101
+
102
+ class NodeCreatedEvent(NodeMutatedEvent):
103
+ action: MutationAction = MutationAction.CREATED
104
+
105
+
106
+ class NodeUpdatedEvent(NodeMutatedEvent):
107
+ action: MutationAction = MutationAction.UPDATED
108
+
109
+
110
+ class NodeDeletedEvent(NodeMutatedEvent):
111
+ action: MutationAction = MutationAction.DELETED
@@ -1,33 +1,29 @@
1
1
  from typing import Any
2
2
 
3
- from pydantic import Field
3
+ from pydantic import Field, computed_field
4
4
 
5
5
  from infrahub.message_bus import InfrahubMessage
6
6
 
7
- from .models import InfrahubBranchEvent
7
+ from .constants import EVENT_NAMESPACE
8
+ from .models import InfrahubEvent
8
9
 
9
10
 
10
- class CommitUpdatedEvent(InfrahubBranchEvent):
11
+ class CommitUpdatedEvent(InfrahubEvent):
11
12
  """Event generated when the the commit within a repository has been updated."""
12
13
 
13
14
  commit: str = Field(..., description="The commit the repository was updated to")
14
15
  repository_id: str = Field(..., description="The ID of the repository")
15
16
  repository_name: str = Field(..., description="The name of the repository")
16
17
 
17
- def get_name(self) -> str:
18
- return f"{self.get_event_namespace()}.repository.update_commit"
19
-
20
18
  def get_resource(self) -> dict[str, str]:
21
19
  return {
22
20
  "prefect.resource.id": f"infrahub.repository.{self.repository_id}",
23
- "infrahub.branch.name": self.branch,
24
21
  "infrahub.repository.name": self.repository_name,
25
22
  "infrahub.repository.id": self.repository_id,
26
23
  }
27
24
 
28
25
  def get_payload(self) -> dict[str, Any]:
29
26
  return {
30
- "branch": self.branch,
31
27
  "commit": self.commit,
32
28
  "repository_id": self.repository_id,
33
29
  "repository_name": self.repository_name,
@@ -35,3 +31,7 @@ class CommitUpdatedEvent(InfrahubBranchEvent):
35
31
 
36
32
  def get_messages(self) -> list[InfrahubMessage]:
37
33
  return []
34
+
35
+ @computed_field
36
+ def event_name(self) -> str:
37
+ return f"{EVENT_NAMESPACE}.repository.update_commit"
@@ -1,30 +1,39 @@
1
1
  from typing import Any
2
2
 
3
- from pydantic import Field
3
+ from pydantic import Field, computed_field
4
4
 
5
5
  from infrahub.message_bus import InfrahubMessage
6
6
  from infrahub.message_bus.messages.refresh_registry_branches import RefreshRegistryBranches
7
7
 
8
- from .models import InfrahubBranchEvent
8
+ from .constants import EVENT_NAMESPACE
9
+ from .models import InfrahubEvent
9
10
 
10
11
 
11
- class SchemaUpdatedEvent(InfrahubBranchEvent):
12
+ class SchemaUpdatedEvent(InfrahubEvent):
12
13
  """Event generated when the schema within a branch has been updated."""
13
14
 
15
+ branch_name: str = Field(..., description="The name of the branch")
14
16
  schema_hash: str = Field(..., description="Schema hash after the update")
15
17
 
16
- def get_name(self) -> str:
17
- return f"{self.get_event_namespace()}.schema.update"
18
+ # NOTE
19
+ # We could add to the payload
20
+ # - Hash before and after the change
21
+ # - List of nodes and generics that have been modified
22
+ # - Diff of the change
23
+
24
+ # NOTE 2
25
+ # Should schema_update be a branch event ?
26
+ # if feels like the main resource should be the branch
18
27
 
19
28
  def get_resource(self) -> dict[str, str]:
20
29
  return {
21
- "prefect.resource.id": f"infrahub.schema_branch.{self.branch}",
22
- "infrahub.branch.name": self.branch,
30
+ "prefect.resource.id": f"infrahub.schema_branch.{self.branch_name}",
31
+ "infrahub.branch.name": self.branch_name,
23
32
  "infrahub.branch.schema_hash": self.schema_hash,
24
33
  }
25
34
 
26
35
  def get_payload(self) -> dict[str, Any]:
27
- return {"branch": self.branch, "schema_hash": self.schema_hash}
36
+ return {"branch": self.branch_name, "schema_hash": self.schema_hash}
28
37
 
29
38
  def get_messages(self) -> list[InfrahubMessage]:
30
39
  return [
@@ -34,3 +43,7 @@ class SchemaUpdatedEvent(InfrahubBranchEvent):
34
43
  # meta=self.get_message_meta(),
35
44
  # )
36
45
  ]
46
+
47
+ @computed_field
48
+ def event_name(self) -> str:
49
+ return f"{EVENT_NAMESPACE}.schema.update"
@@ -8,6 +8,7 @@ from prefect import flow, task
8
8
  from prefect.cache_policies import NONE
9
9
 
10
10
  from infrahub import lock
11
+ from infrahub.context import InfrahubContext # noqa: TC001 needed for prefect flow
11
12
  from infrahub.core.constants import GeneratorInstanceStatus, InfrahubKind
12
13
  from infrahub.generators.models import (
13
14
  ProposedChangeGeneratorDefinition,
@@ -16,7 +17,7 @@ from infrahub.generators.models import (
16
17
  )
17
18
  from infrahub.git.base import extract_repo_file_information
18
19
  from infrahub.git.repository import get_initialized_repo
19
- from infrahub.services import InfrahubServices, services
20
+ from infrahub.services import InfrahubServices # noqa: TC001 needed for prefect flow
20
21
  from infrahub.workflows.catalogue import REQUEST_GENERATOR_DEFINITION_RUN, REQUEST_GENERATOR_RUN
21
22
  from infrahub.workflows.utils import add_tags
22
23
 
@@ -25,9 +26,7 @@ from infrahub.workflows.utils import add_tags
25
26
  name="generator-run",
26
27
  flow_run_name="Run generator {model.generator_definition.definition_name}",
27
28
  )
28
- async def run_generator(model: RequestGeneratorRun) -> None:
29
- service = services.service
30
-
29
+ async def run_generator(model: RequestGeneratorRun, service: InfrahubServices) -> None:
31
30
  await add_tags(branches=[model.branch_name], nodes=[model.target_id])
32
31
 
33
32
  repository = await get_initialized_repo(
@@ -80,7 +79,7 @@ async def run_generator(model: RequestGeneratorRun) -> None:
80
79
  await generator_instance.update(do_full_update=True)
81
80
 
82
81
 
83
- @task(name="generator-define-instance", task_run_name="Define Instance", cache_policy=NONE)
82
+ @task(name="generator-define-instance", task_run_name="Define Instance", cache_policy=NONE) # type: ignore[arg-type]
84
83
  async def _define_instance(model: RequestGeneratorRun, service: InfrahubServices) -> CoreGeneratorInstance:
85
84
  if model.generator_instance:
86
85
  instance = await service.client.get(
@@ -119,9 +118,7 @@ async def _define_instance(model: RequestGeneratorRun, service: InfrahubServices
119
118
 
120
119
 
121
120
  @flow(name="generator-definition-run", flow_run_name="Run all generators")
122
- async def run_generator_definition(branch: str) -> None:
123
- service = services.service
124
-
121
+ async def run_generator_definition(branch: str, context: InfrahubContext, service: InfrahubServices) -> None:
125
122
  await add_tags(branches=[branch])
126
123
 
127
124
  generators = await service.client.filters(
@@ -146,16 +143,18 @@ async def run_generator_definition(branch: str) -> None:
146
143
 
147
144
  for generator_definition in generator_definitions:
148
145
  model = RequestGeneratorDefinitionRun(branch=branch, generator_definition=generator_definition)
149
- await service.workflow.submit_workflow(workflow=REQUEST_GENERATOR_DEFINITION_RUN, parameters={"model": model})
146
+ await service.workflow.submit_workflow(
147
+ workflow=REQUEST_GENERATOR_DEFINITION_RUN, context=context, parameters={"model": model}
148
+ )
150
149
 
151
150
 
152
151
  @flow(
153
152
  name="request-generator-definition-run",
154
153
  flow_run_name="Execute generator {model.generator_definition.definition_name}",
155
154
  )
156
- async def request_generator_definition_run(model: RequestGeneratorDefinitionRun) -> None:
157
- service = services.service
158
-
155
+ async def request_generator_definition_run(
156
+ model: RequestGeneratorDefinitionRun, context: InfrahubContext, service: InfrahubServices
157
+ ) -> None:
159
158
  await add_tags(branches=[model.branch], nodes=[model.generator_definition.definition_id])
160
159
 
161
160
  group = await service.client.get(
@@ -208,5 +207,5 @@ async def request_generator_definition_run(model: RequestGeneratorDefinitionRun)
208
207
  target_name=member.display_label,
209
208
  )
210
209
  await service.workflow.submit_workflow(
211
- workflow=REQUEST_GENERATOR_RUN, parameters={"model": request_generator_run_model}
210
+ workflow=REQUEST_GENERATOR_RUN, context=context, parameters={"model": request_generator_run_model}
212
211
  )
infrahub/git/base.py CHANGED
@@ -122,7 +122,7 @@ class BranchInLocal(BaseModel):
122
122
  has_worktree: bool = False
123
123
 
124
124
 
125
- class InfrahubRepositoryBase(BaseModel, ABC): # pylint: disable=too-many-public-methods
125
+ class InfrahubRepositoryBase(BaseModel, ABC):
126
126
  """
127
127
  Local version of a Git repository organized to work with Infrahub.
128
128
  The idea is that all commits that are being tracked in the graph will be checkout out
@@ -395,9 +395,7 @@ class InfrahubRepositoryBase(BaseModel, ABC): # pylint: disable=too-many-public
395
395
  return [Worktree.init(response) for response in responses]
396
396
 
397
397
  def get_client(self) -> InfrahubClient:
398
- if self.client:
399
- return self.client
400
- return self.service.client
398
+ return self.sdk
401
399
 
402
400
  def get_location(self) -> str:
403
401
  if self.location:
@@ -702,7 +700,7 @@ class InfrahubRepositoryBase(BaseModel, ABC): # pylint: disable=too-many-public
702
700
  log.info("New commit detected", repository=self.name, branch=branch_name)
703
701
  updated_branches.append(branch_name)
704
702
 
705
- return sorted(list(new_branches)), sorted(updated_branches)
703
+ return sorted(new_branches), sorted(updated_branches)
706
704
 
707
705
  def validate_remote_branch(self, branch_name: str) -> bool:
708
706
  """Process a remote branch to validate that we can use it safely.
infrahub/git/constants.py CHANGED
@@ -1,4 +1,3 @@
1
- AUTOMATION_NAME = "Trigger-repository-commit-update-event"
2
1
  COMMITS_DIRECTORY_NAME = "commits"
3
2
  BRANCHES_DIRECTORY_NAME = "branches"
4
3
  TEMPORARY_DIRECTORY_NAME = "temp"