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
@@ -28,7 +28,6 @@ from .model.path import (
28
28
  if TYPE_CHECKING:
29
29
  from infrahub.core.branch import Branch
30
30
  from infrahub.core.query import QueryResult
31
- from infrahub.core.schema import MainSchemaTypes
32
31
  from infrahub.core.schema.manager import SchemaManager
33
32
  from infrahub.core.schema.relationship_schema import RelationshipSchema
34
33
 
@@ -397,8 +396,12 @@ class DiffNodeIntermediate(TrackedStatusUpdates):
397
396
  force_action: DiffAction | None
398
397
  uuid: str
399
398
  kind: str
399
+ db_id: str
400
+ from_time: Timestamp
401
+ status: RelationshipStatus
400
402
  attributes_by_name: dict[str, DiffAttributeIntermediate] = field(default_factory=dict)
401
- relationships_by_name: dict[str, DiffRelationshipIntermediate] = field(default_factory=dict)
403
+ # {(name, identifier): DiffRelationshipIntermediate}
404
+ relationships_by_identifier: dict[tuple[str, str], DiffRelationshipIntermediate] = field(default_factory=dict)
402
405
 
403
406
  def to_diff_node(self, from_time: Timestamp, include_unchanged: bool) -> DiffNode:
404
407
  attributes = []
@@ -408,7 +411,7 @@ class DiffNodeIntermediate(TrackedStatusUpdates):
408
411
  attributes.append(diff_attr)
409
412
  action, changed_at = self.get_action_and_timestamp(from_time=from_time)
410
413
  relationships = []
411
- for rel in self.relationships_by_name.values():
414
+ for rel in self.relationships_by_identifier.values():
412
415
  diff_rel = rel.to_diff_relationship(include_unchanged=include_unchanged)
413
416
  if include_unchanged or diff_rel.action is not DiffAction.UNCHANGED:
414
417
  relationships.append(diff_rel)
@@ -431,7 +434,7 @@ class DiffNodeIntermediate(TrackedStatusUpdates):
431
434
 
432
435
  @property
433
436
  def is_empty(self) -> bool:
434
- return len(self.attributes_by_name) == 0 and len(self.relationships_by_name) == 0
437
+ return len(self.attributes_by_name) == 0 and len(self.relationships_by_identifier) == 0
435
438
 
436
439
 
437
440
  @dataclass
@@ -495,7 +498,7 @@ class DiffQueryParser:
495
498
  for node in diff_root.nodes_by_id.values():
496
499
  for attribute_name in node.attributes_by_name:
497
500
  node_field_specifiers_map[node.uuid].add(attribute_name)
498
- for relationship_diff in node.relationships_by_name.values():
501
+ for relationship_diff in node.relationships_by_identifier.values():
499
502
  node_field_specifiers_map[node.uuid].add(relationship_diff.identifier)
500
503
  return node_field_specifiers_map
501
504
 
@@ -567,35 +570,53 @@ class DiffQueryParser:
567
570
  diff_root.nodes_by_id[node_id] = DiffNodeIntermediate(
568
571
  uuid=node_id,
569
572
  kind=database_path.node_kind,
573
+ db_id=database_path.node_db_id,
574
+ from_time=database_path.node_changed_at,
575
+ status=database_path.node_status,
570
576
  force_action=DiffAction.UPDATED
571
577
  if database_path.node_branch_support is BranchSupportType.AGNOSTIC
572
578
  else None,
573
579
  )
574
580
  diff_node = diff_root.nodes_by_id[node_id]
581
+ # special handling for nodes that have their kind updated, which results in 2 nodes with the same uuid
582
+ if diff_node.db_id != database_path.node_db_id and (
583
+ database_path.node_changed_at > diff_node.from_time
584
+ or (
585
+ database_path.node_changed_at >= diff_node.from_time
586
+ and (diff_node.status, database_path.node_status)
587
+ == (RelationshipStatus.DELETED, RelationshipStatus.ACTIVE)
588
+ )
589
+ ):
590
+ diff_node.kind = database_path.node_kind
591
+ diff_node.db_id = database_path.node_db_id
592
+ diff_node.from_time = database_path.node_changed_at
593
+ diff_node.status = database_path.node_status
575
594
  diff_node.track_database_path(database_path=database_path)
576
595
  return diff_node
577
596
 
578
- def _get_relationship_schema(
579
- self, database_path: DatabasePath, node_schema: MainSchemaTypes
580
- ) -> RelationshipSchema | None:
581
- relationship_schemas = node_schema.get_relationships_by_identifier(id=database_path.attribute_name)
582
- if len(relationship_schemas) == 1:
583
- return relationship_schemas[0]
584
- possible_path_directions = database_path.possible_relationship_directions
585
- for rel_schema in relationship_schemas:
586
- if rel_schema.direction in possible_path_directions:
587
- return rel_schema
597
+ def _get_relationship_schema(self, database_path: DatabasePath) -> RelationshipSchema | None:
598
+ branches_to_check = [database_path.deepest_branch]
599
+ if database_path.deepest_branch == self.diff_branch_name:
600
+ branches_to_check.append(self.base_branch_name)
601
+ for schema_branch_name in branches_to_check:
602
+ node_schema = self.schema_manager.get(
603
+ name=database_path.node_kind, branch=schema_branch_name, duplicate=False
604
+ )
605
+ relationship_schemas = node_schema.get_relationships_by_identifier(id=database_path.attribute_name)
606
+ if len(relationship_schemas) == 1:
607
+ return relationship_schemas[0]
608
+ possible_path_directions = database_path.possible_relationship_directions
609
+ for rel_schema in relationship_schemas:
610
+ if rel_schema.direction in possible_path_directions:
611
+ return rel_schema
588
612
  return None
589
613
 
590
614
  def _update_attribute_level(self, database_path: DatabasePath, diff_node: DiffNodeIntermediate) -> None:
591
- node_schema = self.schema_manager.get(
592
- name=database_path.node_kind, branch=database_path.deepest_branch, duplicate=False
593
- )
594
615
  if "Attribute" in database_path.attribute_node.labels:
595
616
  diff_attribute = self._get_diff_attribute(database_path=database_path, diff_node=diff_node)
596
617
  self._update_attribute_property(database_path=database_path, diff_attribute=diff_attribute)
597
618
  return
598
- relationship_schema = self._get_relationship_schema(database_path=database_path, node_schema=node_schema)
619
+ relationship_schema = self._get_relationship_schema(database_path=database_path)
599
620
  if not relationship_schema:
600
621
  return
601
622
  diff_relationship = self._get_diff_relationship(
@@ -649,7 +670,9 @@ class DiffQueryParser:
649
670
  relationship_schema: RelationshipSchema,
650
671
  database_path: DatabasePath,
651
672
  ) -> DiffRelationshipIntermediate:
652
- diff_relationship = diff_node.relationships_by_name.get(relationship_schema.name)
673
+ diff_relationship = diff_node.relationships_by_identifier.get(
674
+ (relationship_schema.name, relationship_schema.get_identifier())
675
+ )
653
676
  if not diff_relationship:
654
677
  branch_name = database_path.deepest_branch
655
678
  from_time = self.from_time
@@ -663,7 +686,9 @@ class DiffQueryParser:
663
686
  identifier=relationship_schema.get_identifier(),
664
687
  from_time=from_time,
665
688
  )
666
- diff_node.relationships_by_name[relationship_schema.name] = diff_relationship
689
+ diff_node.relationships_by_identifier[relationship_schema.name, relationship_schema.get_identifier()] = (
690
+ diff_relationship
691
+ )
667
692
  return diff_relationship
668
693
 
669
694
  def _apply_base_branch_previous_values(self) -> None:
@@ -700,8 +725,8 @@ class DiffQueryParser:
700
725
  def _apply_relationship_previous_values(
701
726
  self, diff_node: DiffNodeIntermediate, base_diff_node: DiffNodeIntermediate
702
727
  ) -> None:
703
- for relationship_name, diff_relationship in diff_node.relationships_by_name.items():
704
- base_diff_relationship = base_diff_node.relationships_by_name.get(relationship_name)
728
+ for relationship_key, diff_relationship in diff_node.relationships_by_identifier.items():
729
+ base_diff_relationship = base_diff_node.relationships_by_identifier.get(relationship_key)
705
730
  if not base_diff_relationship:
706
731
  continue
707
732
  for db_id, property_set in diff_relationship.properties_by_db_id.items():
@@ -754,7 +779,7 @@ class DiffQueryParser:
754
779
  continue
755
780
  if ordered_diff_values[-1].changed_at >= self.diff_branched_from_time:
756
781
  return
757
- for relationship_diff in node_diff.relationships_by_name.values():
782
+ for relationship_diff in node_diff.relationships_by_identifier.values():
758
783
  for diff_relationship_property_list in relationship_diff.properties_by_db_id.values():
759
784
  for diff_relationship_property in diff_relationship_property_list:
760
785
  if diff_relationship_property.changed_at >= self.diff_branched_from_time:
@@ -133,7 +133,7 @@ class EnrichedDiffDeserializer:
133
133
 
134
134
  # TODO Ensure the list is even
135
135
  current_node_uuid = node_uuid
136
- for rel, parent in zip(parents_path_slice[::2], parents_path_slice[1::2]):
136
+ for rel, parent in zip(parents_path_slice[::2], parents_path_slice[1::2], strict=False):
137
137
  enriched_root.add_parent(
138
138
  node_id=current_node_uuid,
139
139
  parent_id=parent.get("uuid"),
@@ -164,10 +164,8 @@ class EnrichedDiffDeserializer:
164
164
  def build_diff_root_metadata(cls, root_node: Neo4jNode) -> EnrichedDiffRootMetadata:
165
165
  from_time = Timestamp(str(root_node.get("from_time")))
166
166
  to_time = Timestamp(str(root_node.get("to_time")))
167
- tracking_id_str = cls._get_str_or_none_property_value(node=root_node, property_name="tracking_id")
168
- tracking_id = None
169
- if tracking_id_str:
170
- tracking_id = deserialize_tracking_id(tracking_id_str=tracking_id_str)
167
+ tracking_id_str = str(root_node.get("tracking_id"))
168
+ tracking_id = deserialize_tracking_id(tracking_id_str=tracking_id_str)
171
169
  return EnrichedDiffRootMetadata(
172
170
  base_branch_name=str(root_node.get("base_branch")),
173
171
  diff_branch_name=str(root_node.get("diff_branch")),
@@ -176,11 +174,12 @@ class EnrichedDiffDeserializer:
176
174
  uuid=str(root_node.get("uuid")),
177
175
  partner_uuid=str(root_node.get("partner_uuid")),
178
176
  tracking_id=tracking_id,
179
- num_added=int(root_node.get("num_added")),
180
- num_updated=int(root_node.get("num_updated")),
181
- num_removed=int(root_node.get("num_removed")),
182
- num_conflicts=int(root_node.get("num_conflicts")),
177
+ num_added=int(root_node.get("num_added", 0)),
178
+ num_updated=int(root_node.get("num_updated", 0)),
179
+ num_removed=int(root_node.get("num_removed", 0)),
180
+ num_conflicts=int(root_node.get("num_conflicts", 0)),
183
181
  contains_conflict=str(root_node.get("contains_conflict")).lower() == "true",
182
+ exists_on_database=True,
184
183
  )
185
184
 
186
185
  def _deserialize_diff_node(self, node_node: Neo4jNode, enriched_root: EnrichedDiffRoot) -> EnrichedDiffNode:
@@ -197,10 +196,10 @@ class EnrichedDiffDeserializer:
197
196
  changed_at=Timestamp(timestamp_str) if timestamp_str else None,
198
197
  action=DiffAction(str(node_node.get("action"))),
199
198
  path_identifier=str(node_node.get("path_identifier")),
200
- num_added=int(node_node.get("num_added")),
201
- num_updated=int(node_node.get("num_updated")),
202
- num_removed=int(node_node.get("num_removed")),
203
- num_conflicts=int(node_node.get("num_conflicts")),
199
+ num_added=int(node_node.get("num_added", 0)),
200
+ num_updated=int(node_node.get("num_updated", 0)),
201
+ num_removed=int(node_node.get("num_removed", 0)),
202
+ num_conflicts=int(node_node.get("num_conflicts", 0)),
204
203
  contains_conflict=str(node_node.get("contains_conflict")).lower() == "true",
205
204
  )
206
205
  self._diff_node_map[node_key] = enriched_node
@@ -220,10 +219,10 @@ class EnrichedDiffDeserializer:
220
219
  changed_at=Timestamp(str(diff_attr_node.get("changed_at"))),
221
220
  path_identifier=str(diff_attr_node.get("path_identifier")),
222
221
  action=DiffAction(str(diff_attr_node.get("action"))),
223
- num_added=int(diff_attr_node.get("num_added")),
224
- num_updated=int(diff_attr_node.get("num_updated")),
225
- num_removed=int(diff_attr_node.get("num_removed")),
226
- num_conflicts=int(diff_attr_node.get("num_conflicts")),
222
+ num_added=int(diff_attr_node.get("num_added", 0)),
223
+ num_updated=int(diff_attr_node.get("num_updated", 0)),
224
+ num_removed=int(diff_attr_node.get("num_removed", 0)),
225
+ num_conflicts=int(diff_attr_node.get("num_conflicts", 0)),
227
226
  contains_conflict=str(diff_attr_node.get("contains_conflict")).lower() == "true",
228
227
  )
229
228
  self._diff_node_attr_map[attr_key] = enriched_attr
@@ -247,10 +246,10 @@ class EnrichedDiffDeserializer:
247
246
  changed_at=Timestamp(timestamp_str) if timestamp_str else None,
248
247
  action=DiffAction(str(relationship_group_node.get("action"))),
249
248
  path_identifier=str(relationship_group_node.get("path_identifier")),
250
- num_added=int(relationship_group_node.get("num_added")),
251
- num_conflicts=int(relationship_group_node.get("num_conflicts")),
252
- num_removed=int(relationship_group_node.get("num_removed")),
253
- num_updated=int(relationship_group_node.get("num_updated")),
249
+ num_added=int(relationship_group_node.get("num_added", 0)),
250
+ num_conflicts=int(relationship_group_node.get("num_conflicts", 0)),
251
+ num_removed=int(relationship_group_node.get("num_removed", 0)),
252
+ num_updated=int(relationship_group_node.get("num_updated", 0)),
254
253
  contains_conflict=str(relationship_group_node.get("contains_conflict")).lower() == "true",
255
254
  )
256
255
 
@@ -282,10 +281,10 @@ class EnrichedDiffDeserializer:
282
281
  peer_id=diff_element_peer_id,
283
282
  peer_label=peer_label,
284
283
  path_identifier=str(relationship_element_node.get("path_identifier")),
285
- num_added=int(relationship_element_node.get("num_added")),
286
- num_updated=int(relationship_element_node.get("num_updated")),
287
- num_removed=int(relationship_element_node.get("num_removed")),
288
- num_conflicts=int(relationship_element_node.get("num_conflicts")),
284
+ num_added=int(relationship_element_node.get("num_added", 0)),
285
+ num_updated=int(relationship_element_node.get("num_updated", 0)),
286
+ num_removed=int(relationship_element_node.get("num_removed", 0)),
287
+ num_conflicts=int(relationship_element_node.get("num_conflicts", 0)),
289
288
  contains_conflict=str(relationship_element_node.get("contains_conflict")).lower() == "true",
290
289
  )
291
290
  enriched_relationship_group.relationships.add(enriched_rel_element)
@@ -1,9 +1,10 @@
1
1
  from collections import defaultdict
2
- from typing import AsyncGenerator, Generator
2
+ from typing import AsyncGenerator, Generator, Iterable
3
3
 
4
4
  from infrahub import config
5
5
  from infrahub.core import registry
6
6
  from infrahub.core.diff.query.field_summary import EnrichedDiffNodeFieldSummaryQuery
7
+ from infrahub.core.diff.query.summary_counts_enricher import DiffSummaryCountsEnricherQuery
7
8
  from infrahub.core.query.diff import DiffCountChanges
8
9
  from infrahub.core.timestamp import Timestamp
9
10
  from infrahub.database import InfrahubDatabase, retry_db_transaction
@@ -26,13 +27,13 @@ from ..query.all_conflicts import EnrichedDiffAllConflictsQuery
26
27
  from ..query.delete_query import EnrichedDiffDeleteQuery
27
28
  from ..query.diff_get import EnrichedDiffGetQuery
28
29
  from ..query.diff_summary import DiffSummaryCounters, DiffSummaryQuery
29
- from ..query.drop_tracking_id import EnrichedDiffDropTrackingIdQuery
30
30
  from ..query.field_specifiers import EnrichedDiffFieldSpecifiersQuery
31
31
  from ..query.filters import EnrichedDiffQueryFilters
32
32
  from ..query.get_conflict_query import EnrichedDiffConflictQuery
33
33
  from ..query.has_conflicts_query import EnrichedDiffHasConflictQuery
34
+ from ..query.merge_tracking_id import EnrichedDiffMergedTrackingIdQuery
34
35
  from ..query.roots_metadata import EnrichedDiffRootsMetadataQuery
35
- from ..query.save import EnrichedDiffRootsCreateQuery, EnrichedNodeBatchCreateQuery, EnrichedNodesLinkQuery
36
+ from ..query.save import EnrichedDiffRootsUpsertQuery, EnrichedNodeBatchCreateQuery, EnrichedNodesLinkQuery
36
37
  from ..query.time_range_query import EnrichedDiffTimeRangeQuery
37
38
  from ..query.update_conflict_query import EnrichedDiffConflictUpdateQuery
38
39
  from .deserializer import EnrichedDiffDeserializer
@@ -51,7 +52,8 @@ class DiffRepository:
51
52
  self,
52
53
  base_branch_name: str,
53
54
  diff_branch_names: list[str],
54
- limit: int,
55
+ batch_size_limit: int,
56
+ limit: int | None = None,
55
57
  from_time: Timestamp | None = None,
56
58
  to_time: Timestamp | None = None,
57
59
  filters: EnrichedDiffQueryFilters | None = None,
@@ -62,8 +64,13 @@ class DiffRepository:
62
64
  diff_ids: list[str] | None = None,
63
65
  ) -> list[EnrichedDiffRoot]:
64
66
  self.deserializer.initialize()
67
+ final_row_number = None
68
+ if limit:
69
+ final_row_number = offset + limit
65
70
  has_more_data = True
66
- while has_more_data:
71
+ while has_more_data and (final_row_number is None or offset < final_row_number):
72
+ if final_row_number is not None and offset + batch_size_limit > final_row_number:
73
+ batch_size_limit = final_row_number - offset
67
74
  get_query = await EnrichedDiffGetQuery.init(
68
75
  db=self.db,
69
76
  base_branch_name=base_branch_name,
@@ -72,12 +79,12 @@ class DiffRepository:
72
79
  to_time=to_time,
73
80
  filters=filters,
74
81
  max_depth=max_depth,
75
- limit=limit,
82
+ limit=batch_size_limit,
76
83
  offset=offset,
77
84
  tracking_id=tracking_id,
78
85
  diff_ids=diff_ids,
79
86
  )
80
- log.info(f"Beginning enriched diff get query {limit=}, {offset=}")
87
+ log.info(f"Beginning enriched diff get query {batch_size_limit=}, {offset=}")
81
88
  await get_query.execute(db=self.db)
82
89
  log.info("Enriched diff get query complete")
83
90
  last_result = None
@@ -87,7 +94,7 @@ class DiffRepository:
87
94
  has_more_data = False
88
95
  if last_result:
89
96
  has_more_data = last_result.get_as_type("has_more_data", bool)
90
- offset += limit
97
+ offset += batch_size_limit
91
98
  return await self.deserializer.deserialize()
92
99
 
93
100
  async def get(
@@ -105,16 +112,17 @@ class DiffRepository:
105
112
  include_empty: bool = False,
106
113
  ) -> list[EnrichedDiffRoot]:
107
114
  final_max_depth = config.SETTINGS.database.max_depth_search_hierarchy
108
- limit = limit or int(config.SETTINGS.database.query_size_limit / 10)
115
+ batch_size_limit = int(config.SETTINGS.database.query_size_limit / 10)
109
116
  diff_roots = await self._run_get_diff_query(
110
117
  base_branch_name=base_branch_name,
111
118
  diff_branch_names=diff_branch_names,
119
+ batch_size_limit=batch_size_limit,
120
+ limit=limit,
112
121
  from_time=from_time,
113
122
  to_time=to_time,
114
123
  filters=EnrichedDiffQueryFilters(**dict(filters or {})),
115
124
  include_parents=include_parents,
116
125
  max_depth=final_max_depth,
117
- limit=limit,
118
126
  offset=offset or 0,
119
127
  tracking_id=tracking_id,
120
128
  diff_ids=diff_ids,
@@ -131,21 +139,21 @@ class DiffRepository:
131
139
  to_time: Timestamp,
132
140
  ) -> list[EnrichedDiffs]:
133
141
  max_depth = config.SETTINGS.database.max_depth_search_hierarchy
134
- limit = int(config.SETTINGS.database.query_size_limit / 10)
142
+ batch_size_limit = int(config.SETTINGS.database.query_size_limit / 10)
135
143
  diff_branch_roots = await self._run_get_diff_query(
136
144
  base_branch_name=base_branch_name,
137
145
  diff_branch_names=[diff_branch_name],
138
146
  from_time=from_time,
139
147
  to_time=to_time,
140
148
  max_depth=max_depth,
141
- limit=limit,
149
+ batch_size_limit=batch_size_limit,
142
150
  )
143
151
  diffs_by_uuid = {dbr.uuid: dbr for dbr in diff_branch_roots}
144
152
  base_branch_roots = await self._run_get_diff_query(
145
153
  base_branch_name=base_branch_name,
146
154
  diff_branch_names=[base_branch_name],
147
155
  max_depth=max_depth,
148
- limit=limit,
156
+ batch_size_limit=batch_size_limit,
149
157
  diff_ids=[d.partner_uuid for d in diffs_by_uuid.values()],
150
158
  )
151
159
  diffs_by_uuid.update({bbr.uuid: bbr for bbr in base_branch_roots})
@@ -159,14 +167,23 @@ class DiffRepository:
159
167
  for dbr in diff_branch_roots
160
168
  ]
161
169
 
162
- async def hydrate_diff_pair(self, enriched_diffs_metadata: EnrichedDiffsMetadata) -> EnrichedDiffs:
170
+ async def hydrate_diff_pair(
171
+ self,
172
+ enriched_diffs_metadata: EnrichedDiffsMetadata,
173
+ node_uuids: Iterable[str] | None = None,
174
+ ) -> EnrichedDiffs:
175
+ filters = None
176
+ if node_uuids:
177
+ filters = {"ids": list(node_uuids) if node_uuids is not None else None}
163
178
  hydrated_base_diff = await self.get_one(
164
179
  diff_branch_name=enriched_diffs_metadata.base_branch_name,
165
180
  diff_id=enriched_diffs_metadata.base_branch_diff.uuid,
181
+ filters=filters,
166
182
  )
167
183
  hydrated_branch_diff = await self.get_one(
168
184
  diff_branch_name=enriched_diffs_metadata.diff_branch_name,
169
185
  diff_id=enriched_diffs_metadata.diff_branch_diff.uuid,
186
+ filters=filters,
170
187
  )
171
188
  return EnrichedDiffs(
172
189
  base_branch_name=enriched_diffs_metadata.base_branch_name,
@@ -217,17 +234,34 @@ class DiffRepository:
217
234
  yield node_requests
218
235
 
219
236
  @retry_db_transaction(name="enriched_diff_save")
220
- async def save(self, enriched_diffs: EnrichedDiffs) -> None:
237
+ async def save(self, enriched_diffs: EnrichedDiffs | EnrichedDiffsMetadata, do_summary_counts: bool = True) -> None:
238
+ log.info("Updating diff metadata...")
239
+ root_query = await EnrichedDiffRootsUpsertQuery.init(db=self.db, enriched_diffs=enriched_diffs)
240
+ await root_query.execute(db=self.db)
241
+ log.info("Diff metadata updated.")
242
+ if not isinstance(enriched_diffs, EnrichedDiffs):
243
+ return
221
244
  num_nodes = len(enriched_diffs.base_branch_diff.nodes) + len(enriched_diffs.diff_branch_diff.nodes)
222
245
  log.info(f"Saving diff (num_nodes={num_nodes})...")
223
- root_query = await EnrichedDiffRootsCreateQuery.init(db=self.db, enriched_diffs=enriched_diffs)
224
- await root_query.execute(db=self.db)
225
- for node_create_batch in self._get_node_create_request_batch(enriched_diffs=enriched_diffs):
246
+ for batch_num, node_create_batch in enumerate(
247
+ self._get_node_create_request_batch(enriched_diffs=enriched_diffs)
248
+ ):
249
+ log.info(f"Saving node batch #{batch_num}...")
226
250
  node_query = await EnrichedNodeBatchCreateQuery.init(db=self.db, node_create_batch=node_create_batch)
227
251
  await node_query.execute(db=self.db)
252
+ log.info(f"Batch #{batch_num} saved")
228
253
  link_query = await EnrichedNodesLinkQuery.init(db=self.db, enriched_diffs=enriched_diffs)
229
254
  await link_query.execute(db=self.db)
230
255
  log.info("Diff saved.")
256
+ if do_summary_counts:
257
+ node_uuids: list[str] | None = None
258
+ if enriched_diffs.diff_branch_diff.exists_on_database:
259
+ node_uuids = list(enriched_diffs.branch_node_uuids)
260
+ await self.add_summary_counts(
261
+ diff_branch_name=enriched_diffs.diff_branch_name,
262
+ diff_id=enriched_diffs.diff_branch_diff.uuid,
263
+ node_uuids=node_uuids,
264
+ )
231
265
 
232
266
  async def summary(
233
267
  self,
@@ -277,6 +311,7 @@ class DiffRepository:
277
311
  base_branch_names: list[str] | None = None,
278
312
  from_time: Timestamp | None = None,
279
313
  to_time: Timestamp | None = None,
314
+ tracking_id: TrackingId | None = None,
280
315
  ) -> list[EnrichedDiffsMetadata]:
281
316
  if diff_branch_names and base_branch_names:
282
317
  diff_branch_names += base_branch_names
@@ -285,6 +320,7 @@ class DiffRepository:
285
320
  base_branch_names=base_branch_names,
286
321
  from_time=from_time,
287
322
  to_time=to_time,
323
+ tracking_id=tracking_id,
288
324
  )
289
325
  roots_by_id = {root.uuid: root for root in empty_roots}
290
326
  pairs: list[EnrichedDiffsMetadata] = []
@@ -308,6 +344,7 @@ class DiffRepository:
308
344
  base_branch_names: list[str] | None = None,
309
345
  from_time: Timestamp | None = None,
310
346
  to_time: Timestamp | None = None,
347
+ tracking_id: TrackingId | None = None,
311
348
  ) -> list[EnrichedDiffRootMetadata]:
312
349
  query = await EnrichedDiffRootsMetadataQuery.init(
313
350
  db=self.db,
@@ -315,6 +352,7 @@ class DiffRepository:
315
352
  base_branch_names=base_branch_names,
316
353
  from_time=from_time,
317
354
  to_time=to_time,
355
+ tracking_id=tracking_id,
318
356
  )
319
357
  await query.execute(db=self.db)
320
358
  diff_roots = []
@@ -374,8 +412,8 @@ class DiffRepository:
374
412
  await query.execute(db=self.db)
375
413
  return await query.get_field_summaries()
376
414
 
377
- async def drop_tracking_ids(self, tracking_ids: list[TrackingId]) -> None:
378
- query = await EnrichedDiffDropTrackingIdQuery.init(db=self.db, tracking_ids=tracking_ids)
415
+ async def mark_tracking_ids_merged(self, tracking_ids: list[TrackingId]) -> None:
416
+ query = await EnrichedDiffMergedTrackingIdQuery.init(db=self.db, tracking_ids=tracking_ids)
379
417
  await query.execute(db=self.db)
380
418
 
381
419
  async def get_num_changes_in_time_range_by_branch(
@@ -400,3 +438,21 @@ class DiffRepository:
400
438
  break
401
439
  offset += limit
402
440
  return specifiers
441
+
442
+ async def add_summary_counts(
443
+ self,
444
+ diff_branch_name: str,
445
+ tracking_id: TrackingId | None = None,
446
+ diff_id: str | None = None,
447
+ node_uuids: list[str] | None = None,
448
+ ) -> None:
449
+ log.info("Updating summary counts...")
450
+ query = await DiffSummaryCountsEnricherQuery.init(
451
+ db=self.db,
452
+ diff_branch_name=diff_branch_name,
453
+ tracking_id=tracking_id,
454
+ diff_id=diff_id,
455
+ node_uuids=node_uuids,
456
+ )
457
+ await query.execute(db=self.db)
458
+ log.info("Summary counts updated...")
@@ -1,12 +1,15 @@
1
+ from __future__ import annotations
2
+
1
3
  from prefect import flow
2
4
 
5
+ from infrahub.context import InfrahubContext # noqa: TC001 needed for prefect flow
3
6
  from infrahub.core import registry
4
7
  from infrahub.core.diff.coordinator import DiffCoordinator
5
- from infrahub.core.diff.models import RequestDiffUpdate
8
+ from infrahub.core.diff.models import RequestDiffUpdate # noqa: TC001 needed for prefect flow
6
9
  from infrahub.core.diff.repository.repository import DiffRepository
7
10
  from infrahub.dependencies.registry import get_component_registry
8
11
  from infrahub.log import get_logger
9
- from infrahub.services import services
12
+ from infrahub.services import InfrahubServices # noqa: TC001 needed for prefect flow
10
13
  from infrahub.workflows.catalogue import DIFF_REFRESH
11
14
  from infrahub.workflows.utils import add_branch_tag
12
15
 
@@ -14,8 +17,7 @@ log = get_logger()
14
17
 
15
18
 
16
19
  @flow(name="diff-update", flow_run_name="Update diff for branch {model.branch_name}")
17
- async def update_diff(model: RequestDiffUpdate) -> None:
18
- service = services.service
20
+ async def update_diff(model: RequestDiffUpdate, service: InfrahubServices) -> None:
19
21
  await add_branch_tag(branch_name=model.branch_name)
20
22
 
21
23
  async with service.database.start_session() as db:
@@ -35,8 +37,7 @@ async def update_diff(model: RequestDiffUpdate) -> None:
35
37
 
36
38
 
37
39
  @flow(name="diff-refresh", flow_run_name="Recreate diff for branch {branch_name}")
38
- async def refresh_diff(branch_name: str, diff_id: str) -> None:
39
- service = services.service
40
+ async def refresh_diff(branch_name: str, diff_id: str, service: InfrahubServices) -> None:
40
41
  await add_branch_tag(branch_name=branch_name)
41
42
 
42
43
  async with service.database.start_session() as db:
@@ -49,8 +50,7 @@ async def refresh_diff(branch_name: str, diff_id: str) -> None:
49
50
 
50
51
 
51
52
  @flow(name="diff-refresh-all", flow_run_name="Recreate all diffs for branch {branch_name}")
52
- async def refresh_diff_all(branch_name: str) -> None:
53
- service = services.service
53
+ async def refresh_diff_all(branch_name: str, context: InfrahubContext, service: InfrahubServices) -> None:
54
54
  await add_branch_tag(branch_name=branch_name)
55
55
 
56
56
  async with service.database.start_session() as db:
@@ -63,5 +63,6 @@ async def refresh_diff_all(branch_name: str) -> None:
63
63
  if diff_root.base_branch_name != diff_root.diff_branch_name:
64
64
  await service.workflow.submit_workflow(
65
65
  workflow=DIFF_REFRESH,
66
+ context=context,
66
67
  parameters={"branch_name": diff_root.diff_branch_name, "diff_id": diff_root.uuid},
67
68
  )
infrahub/core/enums.py CHANGED
@@ -2,7 +2,7 @@ import enum
2
2
  import re
3
3
  from typing import Any
4
4
 
5
- ENUM_NAME_REGEX = re.compile("[_a-zA-Z0-9]+")
5
+ ENUM_NAME_REGEX = re.compile(r"[_a-zA-Z0-9]+")
6
6
 
7
7
 
8
8
  def generate_python_enum(name: str, options: list[Any]) -> type[enum.Enum]:
@@ -17,7 +17,7 @@ class ObjectConflictValidatorRecorder:
17
17
  self.validator_label = validator_label
18
18
  self.check_schema_kind = check_schema_kind
19
19
 
20
- async def record_conflicts(self, proposed_change_id: str, conflicts: Sequence[ObjectConflict]) -> list[Node]: # pylint: disable=too-many-branches
20
+ async def record_conflicts(self, proposed_change_id: str, conflicts: Sequence[ObjectConflict]) -> list[Node]:
21
21
  try:
22
22
  proposed_change = await NodeManager.get_one_by_id_or_default_filter(
23
23
  id=proposed_change_id, kind=InfrahubKind.PROPOSEDCHANGE, db=self.db
@@ -104,7 +104,7 @@ class IpamReconciler:
104
104
  ip_node_uuid = query.get_ip_node_uuid()
105
105
  if not ip_node_uuid:
106
106
  node_type = InfrahubKind.IPPREFIX
107
- if isinstance(ip_value, (ipaddress.IPv6Interface, ipaddress.IPv4Interface)):
107
+ if isinstance(ip_value, ipaddress.IPv6Interface | ipaddress.IPv4Interface):
108
108
  node_type = InfrahubKind.IPADDRESS
109
109
  raise NodeNotFoundError(node_type=node_type, identifier=str(ip_value))
110
110
  current_parent_uuid = query.get_current_parent_uuid()
@@ -5,7 +5,7 @@ from prefect import flow
5
5
 
6
6
  from infrahub.core import registry
7
7
  from infrahub.core.ipam.reconciler import IpamReconciler
8
- from infrahub.services import services
8
+ from infrahub.services import InfrahubServices
9
9
  from infrahub.workflows.utils import add_branch_tag
10
10
 
11
11
  from .model import IpamNodeDetails
@@ -20,8 +20,7 @@ if TYPE_CHECKING:
20
20
  description="Ensure the IPAM Tree is up to date",
21
21
  persist_result=False,
22
22
  )
23
- async def ipam_reconciliation(branch: str, ipam_node_details: list[IpamNodeDetails]) -> None:
24
- service = services.service
23
+ async def ipam_reconciliation(branch: str, ipam_node_details: list[IpamNodeDetails], service: InfrahubServices) -> None:
25
24
  async with service.database.start_session() as db:
26
25
  branch_obj = await registry.get_branch(db=db, branch=branch)
27
26