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
@@ -19,10 +19,11 @@ from prefect.events.schemas.automations import EventTrigger, Posture
19
19
  from prefect.events.schemas.events import ResourceSpecification
20
20
  from prefect.logging import get_run_logger
21
21
 
22
+ from infrahub.context import InfrahubContext # noqa: TC001 needed for prefect flow
22
23
  from infrahub.core.constants import ComputedAttributeKind, InfrahubKind
23
24
  from infrahub.core.registry import registry
24
25
  from infrahub.git.repository import get_initialized_repo
25
- from infrahub.services import services
26
+ from infrahub.services import InfrahubServices # noqa: TC001 needed for prefect flow
26
27
  from infrahub.support.macro import MacroDefinition
27
28
  from infrahub.workflows.catalogue import (
28
29
  PROCESS_COMPUTED_MACRO,
@@ -46,7 +47,6 @@ if TYPE_CHECKING:
46
47
  import logging
47
48
 
48
49
  from infrahub.core.schema.computed_attribute import ComputedAttribute
49
- from infrahub.services import InfrahubServices
50
50
 
51
51
  UPDATE_ATTRIBUTE = """
52
52
  mutation UpdateAttribute(
@@ -72,13 +72,14 @@ async def process_transform(
72
72
  branch_name: str,
73
73
  node_kind: str,
74
74
  object_id: str,
75
- computed_attribute_name: str, # pylint: disable=unused-argument
76
- computed_attribute_kind: str, # pylint: disable=unused-argument
77
- updated_fields: list[str] | None = None, # pylint: disable=unused-argument
75
+ computed_attribute_name: str, # noqa: ARG001
76
+ computed_attribute_kind: str, # noqa: ARG001
77
+ context: InfrahubContext, # noqa: ARG001
78
+ service: InfrahubServices,
79
+ updated_fields: list[str] | None = None, # noqa: ARG001
78
80
  ) -> None:
79
81
  await add_tags(branches=[branch_name], nodes=[object_id])
80
82
 
81
- service = services.service
82
83
  schema_branch = registry.schema.get_schema_branch(name=branch_name)
83
84
  node_schema = schema_branch.get_node(name=node_kind, duplicate=False)
84
85
  transform_attributes: dict[str, ComputedAttribute] = {}
@@ -152,8 +153,9 @@ async def trigger_update_python_computed_attributes(
152
153
  branch_name: str,
153
154
  computed_attribute_name: str,
154
155
  computed_attribute_kind: str,
156
+ context: InfrahubContext,
157
+ service: InfrahubServices,
155
158
  ) -> None:
156
- service = services.service
157
159
  await add_tags(branches=[branch_name])
158
160
 
159
161
  nodes = await service.client.all(kind=computed_attribute_kind, branch=branch_name)
@@ -161,12 +163,14 @@ async def trigger_update_python_computed_attributes(
161
163
  for node in nodes:
162
164
  await service.workflow.submit_workflow(
163
165
  workflow=UPDATE_COMPUTED_ATTRIBUTE_TRANSFORM,
166
+ context=context,
164
167
  parameters={
165
168
  "branch_name": branch_name,
166
169
  "node_kind": computed_attribute_kind,
167
170
  "object_id": node.id,
168
171
  "computed_attribute_name": computed_attribute_name,
169
172
  "computed_attribute_kind": computed_attribute_kind,
173
+ "context": context,
170
174
  },
171
175
  )
172
176
 
@@ -176,10 +180,9 @@ async def trigger_update_python_computed_attributes(
176
180
  flow_run_name="Update value for computed attribute {attribute_name}",
177
181
  )
178
182
  async def update_computed_attribute_value_jinja2(
179
- branch_name: str, obj: CoreNode, attribute_name: str, template_value: str
183
+ branch_name: str, obj: CoreNode, attribute_name: str, template_value: str, service: InfrahubServices
180
184
  ) -> None:
181
185
  log = get_run_logger()
182
- service = services.service
183
186
 
184
187
  await add_tags(branches=[branch_name], nodes=[obj.id], db_change=True)
185
188
 
@@ -232,10 +235,11 @@ async def process_jinja2(
232
235
  object_id: str,
233
236
  computed_attribute_name: str,
234
237
  computed_attribute_kind: str,
238
+ context: InfrahubContext, # noqa: ARG001
239
+ service: InfrahubServices,
235
240
  updated_fields: str | None = None,
236
241
  ) -> None:
237
242
  log = get_run_logger()
238
- service = services.service
239
243
 
240
244
  await add_tags(branches=[branch_name])
241
245
  updates: list[str] = []
@@ -281,6 +285,7 @@ async def process_jinja2(
281
285
  obj=node,
282
286
  attribute_name=computed_macro.attribute.name,
283
287
  template_value=template_string,
288
+ service=service,
284
289
  )
285
290
 
286
291
  _ = [response async for _, response in batch.execute()]
@@ -294,8 +299,9 @@ async def trigger_update_jinja2_computed_attributes(
294
299
  branch_name: str,
295
300
  computed_attribute_name: str,
296
301
  computed_attribute_kind: str,
302
+ context: InfrahubContext,
303
+ service: InfrahubServices,
297
304
  ) -> None:
298
- service = services.service
299
305
  await add_tags(branches=[branch_name])
300
306
 
301
307
  nodes = await service.client.all(kind=computed_attribute_kind, branch=branch_name)
@@ -303,19 +309,22 @@ async def trigger_update_jinja2_computed_attributes(
303
309
  for node in nodes:
304
310
  await service.workflow.submit_workflow(
305
311
  workflow=PROCESS_COMPUTED_MACRO,
312
+ context=context,
306
313
  parameters={
307
314
  "branch_name": branch_name,
308
315
  "computed_attribute_name": computed_attribute_name,
309
316
  "computed_attribute_kind": computed_attribute_kind,
310
317
  "node_kind": computed_attribute_kind,
311
318
  "object_id": node.id,
319
+ "context": context,
312
320
  },
313
321
  )
314
322
 
315
323
 
316
324
  @flow(name="computed-attribute-setup", flow_run_name="Setup computed attributes in task-manager")
317
- async def computed_attribute_setup(branch_name: str | None = None) -> None: # pylint: disable=too-many-statements
318
- service = services.service
325
+ async def computed_attribute_setup(
326
+ service: InfrahubServices, context: InfrahubContext, branch_name: str | None = None
327
+ ) -> None:
319
328
  branch_name = branch_name or registry.default_branch
320
329
 
321
330
  await add_tags(branches=[branch_name])
@@ -376,6 +385,13 @@ async def computed_attribute_setup(branch_name: str | None = None) -> None: # p
376
385
  "computed_attribute_name": computed_attribute.attribute.name,
377
386
  "computed_attribute_kind": computed_attribute.kind,
378
387
  "updated_fields": "{{ event.payload['fields'] | tojson }}",
388
+ "context": {
389
+ "__prefect_kind": "json",
390
+ "value": {
391
+ "__prefect_kind": "jinja",
392
+ "template": "{{ event.payload['context'] | tojson }}",
393
+ },
394
+ },
379
395
  },
380
396
  job_variables={},
381
397
  )
@@ -395,10 +411,12 @@ async def computed_attribute_setup(branch_name: str | None = None) -> None: # p
395
411
  if branch_name == registry.default_branch:
396
412
  await service.workflow.submit_workflow(
397
413
  workflow=TRIGGER_UPDATE_JINJA_COMPUTED_ATTRIBUTES,
414
+ context=context,
398
415
  parameters={
399
416
  "branch_name": registry.default_branch,
400
417
  "computed_attribute_name": computed_attribute.attribute.name,
401
418
  "computed_attribute_kind": computed_attribute.kind,
419
+ "context": context,
402
420
  },
403
421
  )
404
422
 
@@ -440,6 +458,13 @@ async def computed_attribute_setup(branch_name: str | None = None) -> None: # p
440
458
  "computed_attribute_name": computed_attribute.attribute.name,
441
459
  "computed_attribute_kind": computed_attribute.kind,
442
460
  "updated_fields": "{{ event.payload['fields'] | tojson }}",
461
+ "context": {
462
+ "__prefect_kind": "json",
463
+ "value": {
464
+ "__prefect_kind": "jinja",
465
+ "template": "{{ event.payload['context'] | tojson }}",
466
+ },
467
+ },
443
468
  },
444
469
  job_variables={},
445
470
  )
@@ -461,10 +486,12 @@ async def computed_attribute_setup(branch_name: str | None = None) -> None: # p
461
486
  if branch_name == diff_branch:
462
487
  await service.workflow.submit_workflow(
463
488
  workflow=TRIGGER_UPDATE_JINJA_COMPUTED_ATTRIBUTES,
489
+ context=context,
464
490
  parameters={
465
491
  "branch_name": branch_name,
466
492
  "computed_attribute_name": computed_attribute.attribute.name,
467
493
  "computed_attribute_kind": computed_attribute.kind,
494
+ "context": context,
468
495
  },
469
496
  )
470
497
 
@@ -478,12 +505,13 @@ async def computed_attribute_setup(branch_name: str | None = None) -> None: # p
478
505
  flow_run_name="Setup computed attributes for Python transforms in task-manager",
479
506
  )
480
507
  async def computed_attribute_setup_python(
508
+ service: InfrahubServices,
509
+ context: InfrahubContext,
481
510
  branch_name: str | None = None,
482
- commit: str | None = None, # pylint: disable=unused-argument
511
+ commit: str | None = None, # noqa: ARG001
483
512
  trigger_updates: bool = True,
484
513
  ) -> None:
485
514
  log = get_run_logger()
486
- service = services.service
487
515
 
488
516
  branch_name = branch_name or registry.default_branch
489
517
 
@@ -555,6 +583,13 @@ async def computed_attribute_setup_python(
555
583
  "object_id": "{{ event.resource['infrahub.node.id'] }}",
556
584
  "computed_attribute_name": computed_attribute.computed_attribute.attribute.name,
557
585
  "computed_attribute_kind": computed_attribute.computed_attribute.kind,
586
+ "context": {
587
+ "__prefect_kind": "json",
588
+ "value": {
589
+ "__prefect_kind": "jinja",
590
+ "template": "{{ event.payload['context'] | tojson }}",
591
+ },
592
+ },
558
593
  },
559
594
  job_variables={},
560
595
  )
@@ -603,6 +638,13 @@ async def computed_attribute_setup_python(
603
638
  "branch_name": "{{ event.resource['infrahub.branch.name'] }}",
604
639
  "node_kind": "{{ event.resource['infrahub.node.kind'] }}",
605
640
  "object_id": "{{ event.resource['infrahub.node.id'] }}",
641
+ "context": {
642
+ "__prefect_kind": "json",
643
+ "value": {
644
+ "__prefect_kind": "jinja",
645
+ "template": "{{ event.payload['context'] | tojson }}",
646
+ },
647
+ },
606
648
  },
607
649
  job_variables={},
608
650
  )
@@ -626,10 +668,12 @@ async def computed_attribute_setup_python(
626
668
  if trigger_updates:
627
669
  await service.workflow.submit_workflow(
628
670
  workflow=TRIGGER_UPDATE_PYTHON_COMPUTED_ATTRIBUTES,
671
+ context=context,
629
672
  parameters={
630
673
  "branch_name": branch_name,
631
674
  "computed_attribute_name": computed_attribute.computed_attribute.attribute.name,
632
675
  "computed_attribute_kind": computed_attribute.computed_attribute.kind,
676
+ "context": context,
633
677
  },
634
678
  )
635
679
 
@@ -671,11 +715,12 @@ async def computed_attribute_remove_python(
671
715
  )
672
716
  async def query_transform_targets(
673
717
  branch_name: str,
674
- node_kind: str, # pylint: disable=unused-argument
718
+ node_kind: str, # noqa: ARG001
675
719
  object_id: str,
720
+ context: InfrahubContext,
721
+ service: InfrahubServices,
676
722
  ) -> None:
677
723
  await add_tags(branches=[branch_name])
678
- service = services.service
679
724
  schema_branch = registry.schema.get_schema_branch(name=branch_name)
680
725
  targets = await service.client.execute_graphql(
681
726
  query=GATHER_GRAPHQL_QUERY_SUBSCRIBERS, variables={"members": [object_id]}, branch_name=branch_name
@@ -695,6 +740,7 @@ async def query_transform_targets(
695
740
  for computed_attribute in nodes_with_computed_attributes[subscriber.kind]:
696
741
  await service.workflow.submit_workflow(
697
742
  workflow=UPDATE_COMPUTED_ATTRIBUTE_TRANSFORM,
743
+ context=context,
698
744
  parameters={
699
745
  "branch_name": branch_name,
700
746
  "node_kind": subscriber.kind,
@@ -0,0 +1,90 @@
1
+ from infrahub.trigger.models import BuiltinTriggerDefinition, EventTrigger, ExecuteWorkflow
2
+ from infrahub.workflows.catalogue import (
3
+ COMPUTED_ATTRIBUTE_REMOVE_PYTHON,
4
+ COMPUTED_ATTRIBUTE_SETUP,
5
+ COMPUTED_ATTRIBUTE_SETUP_PYTHON,
6
+ )
7
+
8
+ TRIGGER_COMPUTED_ATTRIBUTE_PYTHON_SETUP_BRANCH = BuiltinTriggerDefinition(
9
+ name="computed-attribute-python-setup-on-branch-creation",
10
+ previous_names={"Trigger-schema-update-event"},
11
+ description="Trigger actions on branch create event",
12
+ trigger=EventTrigger(events={"infrahub.branch.created"}),
13
+ actions=[
14
+ ExecuteWorkflow(
15
+ name=COMPUTED_ATTRIBUTE_SETUP_PYTHON.name,
16
+ parameters={
17
+ "branch_name": "{{ event.resource['infrahub.branch.name'] }}",
18
+ "trigger_updates": False,
19
+ "context": {
20
+ "__prefect_kind": "json",
21
+ "value": {"__prefect_kind": "jinja", "template": "{{ event.payload['context'] | tojson }}"},
22
+ },
23
+ },
24
+ )
25
+ ],
26
+ )
27
+
28
+ TRIGGER_COMPUTED_ATTRIBUTE_PYTHON_SETUP_COMMIT = BuiltinTriggerDefinition(
29
+ name="computed-attribute-python-setup-on-commit",
30
+ description="Trigger actions on branch create event",
31
+ trigger=EventTrigger(events={"infrahub.repository.update_commit"}),
32
+ actions=[
33
+ ExecuteWorkflow(
34
+ name=COMPUTED_ATTRIBUTE_SETUP_PYTHON.name,
35
+ parameters={
36
+ "branch_name": "{{ event.resource['infrahub.branch.name'] }}",
37
+ "commit": "{{ event.payload['commit'] }}",
38
+ "context": {
39
+ "__prefect_kind": "json",
40
+ "value": {"__prefect_kind": "jinja", "template": "{{ event.payload['context'] | tojson }}"},
41
+ },
42
+ },
43
+ )
44
+ ],
45
+ )
46
+
47
+ TRIGGER_COMPUTED_ATTRIBUTE_PYTHON_CLEAN_BRANCH = BuiltinTriggerDefinition(
48
+ name="computed-attribute-python-cleanup-on-branch-deletion",
49
+ description="Trigger actions on branch delete event",
50
+ trigger=EventTrigger(events={"infrahub.branch.deleted"}),
51
+ actions=[
52
+ ExecuteWorkflow(
53
+ name=COMPUTED_ATTRIBUTE_REMOVE_PYTHON.name,
54
+ parameters={
55
+ "branch_name": "{{ event.resource['infrahub.branch.name'] }}",
56
+ "context": {
57
+ "__prefect_kind": "json",
58
+ "value": {"__prefect_kind": "jinja", "template": "{{ event.payload['context'] | tojson }}"},
59
+ },
60
+ },
61
+ )
62
+ ],
63
+ )
64
+
65
+ TRIGGER_COMPUTED_ATTRIBUTE_ALL_SCHEMA = BuiltinTriggerDefinition(
66
+ name="computed-attribute-all-setup-on-schema-update",
67
+ trigger=EventTrigger(events={"infrahub.schema.update"}),
68
+ actions=[
69
+ ExecuteWorkflow(
70
+ name=COMPUTED_ATTRIBUTE_SETUP.name,
71
+ parameters={
72
+ "branch_name": "{{ event.resource['infrahub.branch.name'] }}",
73
+ "context": {
74
+ "__prefect_kind": "json",
75
+ "value": {"__prefect_kind": "jinja", "template": "{{ event.payload['context'] | tojson }}"},
76
+ },
77
+ },
78
+ ),
79
+ ExecuteWorkflow(
80
+ name=COMPUTED_ATTRIBUTE_SETUP_PYTHON.name,
81
+ parameters={
82
+ "branch_name": "{{ event.resource['infrahub.branch.name'] }}",
83
+ "context": {
84
+ "__prefect_kind": "json",
85
+ "value": {"__prefect_kind": "jinja", "template": "{{ event.payload['context'] | tojson }}"},
86
+ },
87
+ },
88
+ ),
89
+ ],
90
+ )
infrahub/config.py CHANGED
@@ -718,7 +718,7 @@ class Override:
718
718
 
719
719
 
720
720
  @dataclass
721
- class ConfiguredSettings: # pylint: disable=too-many-public-methods
721
+ class ConfiguredSettings:
722
722
  settings: Optional[Settings] = None
723
723
 
724
724
  def initialize(self, config_file: Path | str | None = None) -> None:
infrahub/context.py ADDED
@@ -0,0 +1,39 @@
1
+ from pydantic import BaseModel, Field
2
+ from typing_extensions import Self
3
+
4
+ from infrahub.auth import AccountSession
5
+ from infrahub.core.branch import Branch
6
+
7
+
8
+ class ParentEvent(BaseModel):
9
+ id: str
10
+ name: str
11
+
12
+
13
+ class EventContext(BaseModel):
14
+ name: str = Field(..., description="The name of the event")
15
+ id: str = Field(..., description="The ID of the event")
16
+ parent_id: str | None = Field(default=None)
17
+ ancestors: list[ParentEvent] = Field(default_factory=list)
18
+
19
+
20
+ class BranchContext(BaseModel):
21
+ name: str
22
+ id: str | None = None
23
+
24
+
25
+ class InfrahubContext(BaseModel):
26
+ branch: BranchContext
27
+ account: AccountSession
28
+ event: EventContext | None = Field(default=None)
29
+
30
+ @classmethod
31
+ def init(cls, branch: Branch, account: AccountSession) -> Self:
32
+ return cls(branch=BranchContext(name=branch.name, id=str(branch.uuid)), account=account)
33
+
34
+ def set_event(self, name: str, id: str) -> None:
35
+ if self.event:
36
+ self.event.name = name
37
+ self.event.id = id
38
+ else:
39
+ self.event = EventContext(name=name, id=id)
infrahub/core/account.py CHANGED
@@ -15,9 +15,6 @@ if TYPE_CHECKING:
15
15
  from infrahub.permissions import AssignedPermissions
16
16
 
17
17
 
18
- # pylint: disable=redefined-builtin
19
-
20
-
21
18
  @dataclass
22
19
  class GlobalPermission:
23
20
  action: str
@@ -60,7 +57,7 @@ class AccountGlobalPermissionQuery(Query):
60
57
  self.account_id = account_id
61
58
  super().__init__(**kwargs)
62
59
 
63
- async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None:
60
+ async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None: # noqa: ARG002
64
61
  self.params["account_id"] = self.account_id
65
62
 
66
63
  branch_filter, branch_params = self.branch.get_query_filter_path(
@@ -175,7 +172,7 @@ class AccountObjectPermissionQuery(Query):
175
172
  self.account_id = account_id
176
173
  super().__init__(**kwargs)
177
174
 
178
- async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None:
175
+ async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None: # noqa: ARG002
179
176
  self.params["account_id"] = self.account_id
180
177
 
181
178
  branch_filter, branch_params = self.branch.get_query_filter_path(
@@ -327,7 +324,7 @@ class AccountRoleGlobalPermissionQuery(Query):
327
324
  self.role_id = role_id
328
325
  super().__init__(**kwargs)
329
326
 
330
- async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None:
327
+ async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None: # noqa: ARG002
331
328
  self.params["role_id"] = self.role_id
332
329
 
333
330
  branch_filter, branch_params = self.branch.get_query_filter_path(
@@ -417,7 +414,7 @@ class AccountRoleObjectPermissionQuery(Query):
417
414
  self.role_id = role_id
418
415
  super().__init__(**kwargs)
419
416
 
420
- async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None:
417
+ async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None: # noqa: ARG002
421
418
  self.params["role_id"] = self.role_id
422
419
 
423
420
  branch_filter, branch_params = self.branch.get_query_filter_path(
@@ -544,7 +541,7 @@ class AccountTokenValidateQuery(Query):
544
541
  self.token = token
545
542
  super().__init__(**kwargs)
546
543
 
547
- async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None:
544
+ async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None: # noqa: ARG002
548
545
  token_filter_perms, token_params = self.branch.get_query_filter_relationships(
549
546
  rel_labels=["r1", "r2"], at=self.at, include_outside_parentheses=True
550
547
  )
@@ -14,6 +14,7 @@ from pydantic import BaseModel, Field
14
14
 
15
15
  from infrahub import config
16
16
  from infrahub.core import registry
17
+ from infrahub.core.changelog.models import AttributeChangelog
17
18
  from infrahub.core.constants import NULL_VALUE, AttributeDBNodeType, BranchSupportType, RelationshipStatus
18
19
  from infrahub.core.property import FlagPropertyMixin, NodePropertyData, NodePropertyMixin
19
20
  from infrahub.core.query.attribute import (
@@ -37,8 +38,6 @@ if TYPE_CHECKING:
37
38
  from infrahub.core.schema import AttributeSchema
38
39
  from infrahub.database import InfrahubDatabase
39
40
 
40
- # pylint: disable=redefined-builtin,c-extension-no-member,too-many-lines,too-many-public-methods
41
-
42
41
 
43
42
  # Use a more user-friendly threshold than Neo4j one (8167 bytes).
44
43
  MAX_STRING_LENGTH = 4096
@@ -81,7 +80,7 @@ class BaseAttribute(FlagPropertyMixin, NodePropertyMixin):
81
80
  _rel_to_node_label: str = RELATIONSHIP_TO_NODE_LABEL
82
81
  _rel_to_value_label: str = RELATIONSHIP_TO_VALUE_LABEL
83
82
 
84
- def __init__( # pylint: disable=too-many-branches
83
+ def __init__(
85
84
  self,
86
85
  name: str,
87
86
  schema: AttributeSchema,
@@ -218,7 +217,7 @@ class BaseAttribute(FlagPropertyMixin, NodePropertyMixin):
218
217
  value_to_check = value
219
218
  if schema.enum and isinstance(value, Enum):
220
219
  value_to_check = value.value
221
- if not isinstance(value_to_check, cls.type): # pylint: disable=isinstance-second-argument-not-valid-type
220
+ if not isinstance(value_to_check, cls.type):
222
221
  raise ValidationError({name: f"{value} is not a valid {schema.kind}"})
223
222
 
224
223
  @classmethod
@@ -267,7 +266,7 @@ class BaseAttribute(FlagPropertyMixin, NodePropertyMixin):
267
266
  @classmethod
268
267
  def deserialize_from_string(cls, value_as_string: str) -> Any:
269
268
  """Return a value corresponding to the attribute type given it formatted as a string."""
270
- return cls.type(value_as_string) # pylint: disable=not-callable
269
+ return cls.type(value_as_string)
271
270
 
272
271
  def to_db(self) -> dict[str, Any]:
273
272
  """Return the properties of the AttributeValue node in Dict format."""
@@ -318,19 +317,19 @@ class BaseAttribute(FlagPropertyMixin, NodePropertyMixin):
318
317
  """Deserialize the value coming from the database."""
319
318
  return data.value
320
319
 
321
- async def save(self, db: InfrahubDatabase, at: Optional[Timestamp] = None) -> bool:
320
+ async def save(self, db: InfrahubDatabase, at: Optional[Timestamp] = None) -> AttributeChangelog | None:
322
321
  """Create or Update the Attribute in the database."""
323
322
 
324
323
  save_at = Timestamp(at)
325
324
 
326
325
  if not self.id or self.is_from_profile:
327
- return False
326
+ return None
328
327
 
329
328
  return await self._update(at=save_at, db=db)
330
329
 
331
- async def delete(self, db: InfrahubDatabase, at: Optional[Timestamp] = None) -> bool:
330
+ async def delete(self, db: InfrahubDatabase, at: Optional[Timestamp] = None) -> AttributeChangelog | None:
332
331
  if not self.db_id:
333
- return False
332
+ return None
334
333
 
335
334
  delete_at = Timestamp(at)
336
335
 
@@ -339,7 +338,14 @@ class BaseAttribute(FlagPropertyMixin, NodePropertyMixin):
339
338
  results = query.get_results()
340
339
 
341
340
  if not results:
342
- return False
341
+ return None
342
+
343
+ changelog = AttributeChangelog(
344
+ name=self.name,
345
+ value=None,
346
+ value_previous=None,
347
+ kind=self.schema.kind,
348
+ )
343
349
 
344
350
  properties_to_delete = []
345
351
  branch = self.get_branch_based_on_support_type()
@@ -347,6 +353,8 @@ class BaseAttribute(FlagPropertyMixin, NodePropertyMixin):
347
353
  # Check all the relationship and update the one that are in the same branch
348
354
  rel_ids_to_update = set()
349
355
  for result in results:
356
+ if result.get_rel("r2").type == "HAS_VALUE":
357
+ changelog.value_previous = result.get_node("ap").get("value")
350
358
  properties_to_delete.append((result.get_rel("r2").type, result.get_node("ap").element_id))
351
359
 
352
360
  await add_relationship(
@@ -378,9 +386,9 @@ class BaseAttribute(FlagPropertyMixin, NodePropertyMixin):
378
386
  db=db,
379
387
  )
380
388
 
381
- return True
389
+ return changelog
382
390
 
383
- async def _update(self, db: InfrahubDatabase, at: Optional[Timestamp] = None) -> bool:
391
+ async def _update(self, db: InfrahubDatabase, at: Optional[Timestamp] = None) -> AttributeChangelog | None:
384
392
  """Update the attribute in the database.
385
393
 
386
394
  Get the current value
@@ -420,6 +428,13 @@ class BaseAttribute(FlagPropertyMixin, NodePropertyMixin):
420
428
 
421
429
  branch = self.get_branch_based_on_support_type()
422
430
 
431
+ changelog = AttributeChangelog(
432
+ name=self.name,
433
+ value=self.to_db().get("value"),
434
+ value_previous=current_attr_data.value,
435
+ kind=self.schema.kind,
436
+ )
437
+
423
438
  # ---------- Update the Value ----------
424
439
  if current_attr_data.content != self.to_db():
425
440
  # Create the new AttributeValue and update the existing relationship
@@ -439,6 +454,11 @@ class BaseAttribute(FlagPropertyMixin, NodePropertyMixin):
439
454
 
440
455
  for flag_name, _, rel_name in SUPPORTED_FLAGS:
441
456
  if current_attr_data.flag_properties[flag_name] != getattr(self, flag_name):
457
+ changelog.add_property(
458
+ name=flag_name,
459
+ value_current=getattr(self, flag_name),
460
+ value_previous=current_attr_data.flag_properties[flag_name],
461
+ )
442
462
  query = await AttributeUpdateFlagQuery.init(db=db, attr=self, at=update_at, flag_name=flag_name)
443
463
  await query.execute(db=db)
444
464
 
@@ -452,6 +472,16 @@ class BaseAttribute(FlagPropertyMixin, NodePropertyMixin):
452
472
  prop_name in current_attr_data.node_properties
453
473
  and current_attr_data.node_properties[prop_name].uuid == getattr(self, f"{prop_name}_id")
454
474
  ):
475
+ previous_attribute_node_property = current_attr_data.node_properties.get(prop_name)
476
+ previous_value = None
477
+ if previous_attribute_node_property:
478
+ previous_value = previous_attribute_node_property.uuid
479
+
480
+ changelog.add_property(
481
+ name=prop_name,
482
+ value_current=getattr(self, f"{prop_name}_id"),
483
+ value_previous=previous_value,
484
+ )
455
485
  query = await AttributeUpdateNodePropertyQuery.init(
456
486
  db=db, attr=self, at=update_at, prop_name=prop_name, prop_id=getattr(self, f"{prop_name}_id")
457
487
  )
@@ -461,7 +491,8 @@ class BaseAttribute(FlagPropertyMixin, NodePropertyMixin):
461
491
  if rel and rel.get("branch") == branch.name:
462
492
  await update_relationships_to([rel.element_id], to=update_at, db=db)
463
493
 
464
- return True
494
+ if changelog.has_updates:
495
+ return changelog
465
496
 
466
497
  async def to_graphql(
467
498
  self,
@@ -470,9 +501,9 @@ class BaseAttribute(FlagPropertyMixin, NodePropertyMixin):
470
501
  related_node_ids: Optional[set] = None,
471
502
  filter_sensitive: bool = False,
472
503
  permissions: Optional[dict] = None,
504
+ include_properties: bool = True,
473
505
  ) -> dict:
474
506
  """Generate GraphQL Payload for this attribute."""
475
- # pylint: disable=too-many-branches
476
507
 
477
508
  response: dict[str, Any] = {"id": self.id}
478
509
 
@@ -480,7 +511,9 @@ class BaseAttribute(FlagPropertyMixin, NodePropertyMixin):
480
511
  field_names = fields.keys()
481
512
  else:
482
513
  # REMOVED updated_at for now, need to investigate further how it's being used today
483
- field_names = ["__typename", "value"] + self._node_properties + self._flag_properties
514
+ field_names = ["__typename", "value"]
515
+ if include_properties:
516
+ field_names += self._node_properties + self._flag_properties
484
517
 
485
518
  for field_name in field_names:
486
519
  if field_name == "updated_at":
@@ -527,11 +560,11 @@ class BaseAttribute(FlagPropertyMixin, NodePropertyMixin):
527
560
  field = field.value
528
561
  if isinstance(field, str):
529
562
  response[field_name] = self._filter_sensitive(value=field, filter_sensitive=filter_sensitive)
530
- elif isinstance(field, (int, bool, dict, list)):
563
+ elif isinstance(field, int | bool | dict | list):
531
564
  response[field_name] = field
532
565
 
533
- if related_node_ids and self.is_from_profile and getattr(self, "source_id"):
534
- related_node_ids.add(getattr(self, "source_id"))
566
+ if related_node_ids and self.is_from_profile and self.source_id:
567
+ related_node_ids.add(self.source_id)
535
568
 
536
569
  return response
537
570
 
@@ -589,7 +622,6 @@ class BaseAttribute(FlagPropertyMixin, NodePropertyMixin):
589
622
  return AttributeDBNodeType.DEFAULT
590
623
 
591
624
  def get_create_data(self) -> AttributeCreateData:
592
- # pylint: disable=no-member
593
625
  branch = self.branch
594
626
  hierarchy_level = branch.hierarchy_level
595
627
  if self.schema.branch == BranchSupportType.AGNOSTIC:
@@ -1159,7 +1191,7 @@ class ListAttribute(BaseAttribute):
1159
1191
 
1160
1192
  def deserialize_value(self, data: AttributeFromDB) -> Any:
1161
1193
  """Deserialize the value (potentially) coming from the database."""
1162
- if isinstance(data.value, (str, bytes)):
1194
+ if isinstance(data.value, str | bytes):
1163
1195
  return ujson.loads(data.value)
1164
1196
  return data.value
1165
1197
 
@@ -1195,7 +1227,7 @@ class JSONAttribute(BaseAttribute):
1195
1227
 
1196
1228
  def deserialize_value(self, data: AttributeFromDB) -> Any:
1197
1229
  """Deserialize the value (potentially) coming from the database."""
1198
- if data.value and isinstance(data.value, (str, bytes)):
1230
+ if data.value and isinstance(data.value, str | bytes):
1199
1231
  return ujson.loads(data.value)
1200
1232
  return data.value
1201
1233