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
@@ -11,38 +11,32 @@ from ..model.path import (
11
11
  EnrichedDiffRelationship,
12
12
  EnrichedDiffs,
13
13
  EnrichedDiffSingleRelationship,
14
+ EnrichedDiffsMetadata,
14
15
  EnrichedNodeCreateRequest,
15
16
  )
16
17
 
17
18
 
18
- class EnrichedDiffRootsCreateQuery(Query):
19
+ class EnrichedDiffRootsUpsertQuery(Query):
19
20
  name = "enriched_roots_create"
20
21
  type = QueryType.WRITE
21
22
  insert_return = False
22
23
 
23
- def __init__(self, enriched_diffs: EnrichedDiffs, **kwargs: Any) -> None:
24
+ def __init__(self, enriched_diffs: EnrichedDiffs | EnrichedDiffsMetadata, **kwargs: Any) -> None:
24
25
  super().__init__(**kwargs)
25
26
  self.enriched_diffs = enriched_diffs
26
27
 
27
- async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None:
28
+ async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None: # noqa: ARG002
28
29
  self.params = self._build_diff_root_params(enriched_diffs=self.enriched_diffs)
29
30
  query = """
30
31
  UNWIND $diff_root_list AS diff_root_map
31
32
  WITH diff_root_map
32
33
  CALL {
33
34
  WITH diff_root_map
34
- MERGE (diff_root:DiffRoot {
35
- base_branch: diff_root_map.base_branch,
36
- diff_branch: diff_root_map.diff_branch,
37
- from_time: diff_root_map.from_time,
38
- to_time: diff_root_map.to_time,
39
- uuid: diff_root_map.uuid,
40
- num_added: diff_root_map.num_added,
41
- num_updated: diff_root_map.num_updated,
42
- num_removed: diff_root_map.num_removed,
43
- num_conflicts: diff_root_map.num_conflicts,
44
- contains_conflict: diff_root_map.contains_conflict
45
- })
35
+ MERGE (diff_root:DiffRoot {uuid: diff_root_map.uuid})
36
+ SET diff_root.base_branch = diff_root_map.base_branch
37
+ SET diff_root.diff_branch = diff_root_map.diff_branch
38
+ SET diff_root.from_time = diff_root_map.from_time
39
+ SET diff_root.to_time = diff_root_map.to_time
46
40
  SET diff_root.tracking_id = diff_root_map.tracking_id
47
41
  RETURN diff_root
48
42
  }
@@ -58,7 +52,7 @@ CALL {
58
52
  """
59
53
  self.add_to_query(query)
60
54
 
61
- def _build_diff_root_params(self, enriched_diffs: EnrichedDiffs) -> dict[str, Any]:
55
+ def _build_diff_root_params(self, enriched_diffs: EnrichedDiffs | EnrichedDiffsMetadata) -> dict[str, Any]:
62
56
  diff_root_list: list[dict[str, Any]] = []
63
57
  for enriched_diff in (enriched_diffs.base_branch_diff, enriched_diffs.diff_branch_diff):
64
58
  diff_root_list.append(
@@ -69,11 +63,6 @@ CALL {
69
63
  "to_time": enriched_diff.to_time.to_string(),
70
64
  "uuid": enriched_diff.uuid,
71
65
  "tracking_id": enriched_diff.tracking_id.serialize() if enriched_diff.tracking_id else None,
72
- "num_added": enriched_diff.num_added,
73
- "num_updated": enriched_diff.num_updated,
74
- "num_removed": enriched_diff.num_removed,
75
- "num_conflicts": enriched_diff.num_conflicts,
76
- "contains_conflict": enriched_diff.contains_conflict,
77
66
  }
78
67
  )
79
68
  return {"diff_root_list": diff_root_list}
@@ -88,7 +77,7 @@ class EnrichedNodeBatchCreateQuery(Query):
88
77
  super().__init__(**kwargs)
89
78
  self.node_create_batch = node_create_batch
90
79
 
91
- async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None:
80
+ async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None: # noqa: ARG002
92
81
  self.params = self._build_node_batch_params()
93
82
  query = """
94
83
  UNWIND $node_details_list AS node_details
@@ -96,79 +85,195 @@ WITH node_details.root_uuid AS root_uuid, node_details.node_map AS node_map
96
85
  CALL {
97
86
  WITH root_uuid, node_map
98
87
  MATCH (diff_root {uuid: root_uuid})
99
- CREATE (diff_root)-[:DIFF_HAS_NODE]->(diff_node:DiffNode)
100
- SET diff_node = node_map.node_properties
88
+ MERGE (diff_root)-[:DIFF_HAS_NODE]->(diff_node:DiffNode {uuid: node_map.node_properties.uuid})
89
+ SET
90
+ diff_node.kind = node_map.node_properties.kind,
91
+ diff_node.label = node_map.node_properties.label,
92
+ diff_node.changed_at = node_map.node_properties.changed_at,
93
+ diff_node.action = node_map.node_properties.action,
94
+ diff_node.path_identifier = node_map.node_properties.path_identifier
101
95
  // -------------------------
102
- // add node-level conflict
96
+ // add/remove node-level conflict
103
97
  // -------------------------
104
- FOREACH (i in CASE WHEN node_map.conflict_params IS NOT NULL THEN [1] ELSE [] END |
105
- CREATE (diff_node)-[:DIFF_HAS_CONFLICT]->(diff_node_conflict:DiffConflict)
98
+ WITH diff_node, node_map
99
+ OPTIONAL MATCH (diff_node)-[:DIFF_HAS_CONFLICT]->(current_diff_node_conflict:DiffConflict)
100
+ WITH diff_node, node_map, current_diff_node_conflict, (node_map.conflict_params IS NOT NULL) AS has_node_conflict
101
+ FOREACH (i in CASE WHEN has_node_conflict = FALSE THEN [1] ELSE [] END |
102
+ DETACH DELETE current_diff_node_conflict
103
+ )
104
+ FOREACH (i in CASE WHEN has_node_conflict = TRUE THEN [1] ELSE [] END |
105
+ MERGE (diff_node)-[:DIFF_HAS_CONFLICT]->(diff_node_conflict:DiffConflict)
106
106
  SET diff_node_conflict = node_map.conflict_params
107
107
  )
108
+ }
109
+ CALL {
110
+ WITH root_uuid, node_map
111
+ MATCH (diff_root {uuid: root_uuid})-[:DIFF_HAS_NODE]->(diff_node:DiffNode {uuid: node_map.node_properties.uuid})
108
112
  // -------------------------
109
- // add attributes for this node
113
+ // remove stale attributes for this node
110
114
  // -------------------------
111
115
  WITH diff_node, node_map
116
+ CALL {
117
+ WITH diff_node, node_map
118
+ WITH diff_node, %(attr_name_list_comp)s AS attr_names
119
+ OPTIONAL MATCH (diff_node)-[:DIFF_HAS_ATTRIBUTE]->(attr_to_delete:DiffAttribute)
120
+ WHERE NOT (attr_to_delete.name IN attr_names)
121
+ OPTIONAL MATCH (attr_to_delete)-[*..6]->(next_to_delete)
122
+ DETACH DELETE next_to_delete
123
+ DETACH DELETE attr_to_delete
124
+ }
125
+ // -------------------------
126
+ // add attributes for this node
127
+ // -------------------------
112
128
  CALL {
113
129
  WITH diff_node, node_map
114
130
  UNWIND node_map.attributes AS node_attribute
115
- CREATE (diff_node)-[:DIFF_HAS_ATTRIBUTE]->(diff_attribute:DiffAttribute)
131
+ MERGE (diff_node)-[:DIFF_HAS_ATTRIBUTE]->(diff_attribute:DiffAttribute {name: node_attribute.node_properties.name})
116
132
  SET diff_attribute = node_attribute.node_properties
117
133
  // -------------------------
118
134
  // add properties for this attribute
119
135
  // -------------------------
120
136
  WITH diff_attribute, node_attribute
137
+ // -------------------------
138
+ // remove stale properties for this attribute
139
+ // -------------------------
140
+ CALL {
141
+ WITH diff_attribute, node_attribute
142
+ WITH diff_attribute, %(attr_props_list_comp)s AS prop_types
143
+ OPTIONAL MATCH (diff_attribute)-[:DIFF_HAS_PROPERTY]->(prop_to_delete:DiffProperty)
144
+ WHERE NOT (prop_to_delete.property_type IN prop_types)
145
+ OPTIONAL MATCH (prop_to_delete)-[*..4]->(next_to_delete)
146
+ DETACH DELETE next_to_delete
147
+ DETACH DELETE prop_to_delete
148
+ }
121
149
  UNWIND node_attribute.properties AS attr_property
122
- CREATE (diff_attribute)-[:DIFF_HAS_PROPERTY]->(diff_attr_prop:DiffProperty)
150
+ MERGE (diff_attribute)-[:DIFF_HAS_PROPERTY]->(diff_attr_prop:DiffProperty {property_type: attr_property.node_properties.property_type})
123
151
  SET diff_attr_prop = attr_property.node_properties
124
152
  // -------------------------
125
- // add conflict for this property
153
+ // add/remove conflict for this property
126
154
  // -------------------------
127
- FOREACH (i in CASE WHEN attr_property.conflict_params IS NOT NULL THEN [1] ELSE [] END |
128
- CREATE (diff_attr_prop)-[:DIFF_HAS_CONFLICT]->(diff_attribute_property_conflict:DiffConflict)
129
- SET diff_attribute_property_conflict = attr_property.conflict_params
155
+ WITH diff_attr_prop, attr_property
156
+ OPTIONAL MATCH (diff_attr_prop)-[:DIFF_HAS_CONFLICT]->(current_attr_prop_conflict:DiffConflict)
157
+ WITH diff_attr_prop, attr_property, current_attr_prop_conflict, (attr_property.conflict_params IS NOT NULL) AS has_prop_conflict
158
+ FOREACH (i in CASE WHEN has_prop_conflict = FALSE THEN [1] ELSE [] END |
159
+ DETACH DELETE current_attr_prop_conflict
160
+ )
161
+ FOREACH (i in CASE WHEN has_prop_conflict = TRUE THEN [1] ELSE [] END |
162
+ MERGE (diff_attr_prop)-[:DIFF_HAS_CONFLICT]->(diff_attr_prop_conflict:DiffConflict)
163
+ SET diff_attr_prop_conflict = attr_property.conflict_params
130
164
  )
131
165
  }
132
166
  // -------------------------
167
+ // remove stale relationships for this node
168
+ // -------------------------
169
+ WITH diff_node, node_map
170
+ CALL {
171
+ WITH diff_node, node_map
172
+ WITH diff_node, %(rel_name_list_comp)s AS rel_names
173
+ OPTIONAL MATCH (diff_node)-[:DIFF_HAS_RELATIONSHIP]->(rel_to_delete:DiffRelationship)
174
+ WHERE NOT (rel_to_delete.name IN rel_names)
175
+ OPTIONAL MATCH (rel_to_delete)-[*..8]->(next_to_delete)
176
+ DETACH DELETE next_to_delete
177
+ DETACH DELETE rel_to_delete
178
+ }
179
+ // -------------------------
133
180
  // add relationships for this node
134
181
  // -------------------------
135
182
  WITH diff_node, node_map
136
183
  CALL {
137
184
  WITH diff_node, node_map
138
185
  UNWIND node_map.relationships as node_relationship
139
- CREATE (diff_node)-[:DIFF_HAS_RELATIONSHIP]->(diff_relationship:DiffRelationship)
186
+ MERGE (diff_node)-[:DIFF_HAS_RELATIONSHIP]->(diff_relationship:DiffRelationship {name: node_relationship.node_properties.name})
140
187
  SET diff_relationship = node_relationship.node_properties
141
188
  // -------------------------
189
+ // remove stale elements for this relationship group
190
+ // -------------------------
191
+ WITH diff_relationship, node_relationship
192
+ CALL {
193
+ WITH diff_relationship, node_relationship
194
+ WITH diff_relationship, %(rel_peers_list_comp)s AS rel_peers
195
+ OPTIONAL MATCH (diff_relationship)-[:DIFF_HAS_ELEMENT]->(element_to_delete:DiffRelationshipElement)
196
+ WHERE NOT (element_to_delete.peer_id IN rel_peers)
197
+ OPTIONAL MATCH (element_to_delete)-[*..6]->(next_to_delete)
198
+ DETACH DELETE next_to_delete
199
+ DETACH DELETE element_to_delete
200
+ }
201
+ // -------------------------
142
202
  // add elements for this relationship group
143
203
  // -------------------------
144
204
  WITH diff_relationship, node_relationship
145
205
  UNWIND node_relationship.relationships as node_single_relationship
146
- CREATE (diff_relationship)-[:DIFF_HAS_ELEMENT]->(diff_relationship_element:DiffRelationshipElement)
206
+ MERGE (diff_relationship)-[:DIFF_HAS_ELEMENT]
207
+ ->(diff_relationship_element:DiffRelationshipElement {peer_id: node_single_relationship.node_properties.peer_id})
147
208
  SET diff_relationship_element = node_single_relationship.node_properties
148
209
  // -------------------------
149
- // add conflict for this relationship element
210
+ // add/remove conflict for this relationship element
150
211
  // -------------------------
151
- FOREACH (i in CASE WHEN node_single_relationship.conflict_params IS NOT NULL THEN [1] ELSE [] END |
152
- CREATE (diff_relationship_element)-[:DIFF_HAS_CONFLICT]->(diff_relationship_conflict:DiffConflict)
153
- SET diff_relationship_conflict = node_single_relationship.conflict_params
212
+ WITH diff_relationship_element, node_single_relationship
213
+ OPTIONAL MATCH (diff_relationship_element)-[:DIFF_HAS_CONFLICT]->(current_element_conflict:DiffConflict)
214
+ WITH diff_relationship_element, node_single_relationship, current_element_conflict,
215
+ (node_single_relationship.conflict_params IS NOT NULL) AS has_element_conflict
216
+ FOREACH (i in CASE WHEN has_element_conflict = FALSE THEN [1] ELSE [] END |
217
+ DETACH DELETE current_element_conflict
154
218
  )
219
+ FOREACH (i in CASE WHEN has_element_conflict = TRUE THEN [1] ELSE [] END |
220
+ MERGE (diff_relationship_element)-[:DIFF_HAS_CONFLICT]->(element_conflict:DiffConflict)
221
+ SET element_conflict = node_single_relationship.conflict_params
222
+ )
223
+ // -------------------------
224
+ // remove stale properties for this relationship element
225
+ // -------------------------
226
+ WITH diff_relationship_element, node_single_relationship
227
+ CALL {
228
+ WITH diff_relationship_element, node_single_relationship
229
+ WITH diff_relationship_element, %(element_props_list_comp)s AS element_props
230
+ OPTIONAL MATCH (diff_relationship_element)-[:DIFF_HAS_PROPERTY]->(property_to_delete:DiffProperty)
231
+ WHERE NOT (property_to_delete.property_type IN element_props)
232
+ OPTIONAL MATCH (property_to_delete)-[*..4]->(next_to_delete)
233
+ DETACH DELETE next_to_delete
234
+ DETACH DELETE property_to_delete
235
+ }
155
236
  // -------------------------
156
237
  // add properties for this relationship element
157
238
  // -------------------------
158
239
  WITH diff_relationship_element, node_single_relationship
159
240
  UNWIND node_single_relationship.properties as node_relationship_property
160
- CREATE (diff_relationship_element)-[:DIFF_HAS_PROPERTY]->(diff_relationship_property:DiffProperty)
241
+ MERGE (diff_relationship_element)-[:DIFF_HAS_PROPERTY]
242
+ ->(diff_relationship_property:DiffProperty {property_type: node_relationship_property.node_properties.property_type})
161
243
  SET diff_relationship_property = node_relationship_property.node_properties
162
244
  // -------------------------
163
245
  // add conflict for this relationship element
164
246
  // -------------------------
165
- FOREACH (i in CASE WHEN node_relationship_property.conflict_params IS NOT NULL THEN [1] ELSE [] END |
166
- CREATE (diff_relationship_property)-[:DIFF_HAS_CONFLICT]->(diff_relationship_property_conflict:DiffConflict)
167
- SET diff_relationship_property_conflict = node_relationship_property.conflict_params
247
+ WITH diff_relationship_property, node_relationship_property
248
+ OPTIONAL MATCH (diff_relationship_property)-[:DIFF_HAS_CONFLICT]->(diff_relationship_property_conflict:DiffConflict)
249
+ WITH diff_relationship_property, node_relationship_property, diff_relationship_property_conflict,
250
+ (node_relationship_property.conflict_params IS NOT NULL) AS has_property_conflict
251
+ FOREACH (i in CASE WHEN has_property_conflict = FALSE THEN [1] ELSE [] END |
252
+ DETACH DELETE diff_relationship_property_conflict
253
+ )
254
+ FOREACH (i in CASE WHEN has_property_conflict = TRUE THEN [1] ELSE [] END |
255
+ MERGE (diff_relationship_property)-[:DIFF_HAS_CONFLICT]->(property_conflict:DiffConflict)
256
+ SET property_conflict = node_relationship_property.conflict_params
168
257
  )
169
258
  }
170
259
  }
171
- """
260
+ """ % {
261
+ "attr_name_list_comp": db.render_list_comprehension(
262
+ items="node_map.attributes", item_name="node_properties.name"
263
+ ),
264
+ "attr_props_list_comp": db.render_list_comprehension(
265
+ items="node_attribute.properties", item_name="node_properties.property_type"
266
+ ),
267
+ "rel_name_list_comp": db.render_list_comprehension(
268
+ items="node_map.relationships", item_name="node_properties.name"
269
+ ),
270
+ "rel_peers_list_comp": db.render_list_comprehension(
271
+ items="node_relationship.relationships", item_name="node_properties.peer_id"
272
+ ),
273
+ "element_props_list_comp": db.render_list_comprehension(
274
+ items="node_single_relationship.properties", item_name="node_properties.property_type"
275
+ ),
276
+ }
172
277
  self.add_to_query(query)
173
278
 
174
279
  def _build_conflict_params(self, enriched_conflict: EnrichedDiffConflict) -> dict[str, Any]:
@@ -218,11 +323,6 @@ CALL {
218
323
  "changed_at": enriched_attribute.changed_at.to_string(),
219
324
  "action": enriched_attribute.action.value,
220
325
  "path_identifier": enriched_attribute.path_identifier,
221
- "num_added": enriched_attribute.num_added,
222
- "num_updated": enriched_attribute.num_updated,
223
- "num_removed": enriched_attribute.num_removed,
224
- "num_conflicts": enriched_attribute.num_conflicts,
225
- "contains_conflict": enriched_attribute.contains_conflict,
226
326
  },
227
327
  "properties": property_props,
228
328
  }
@@ -243,11 +343,6 @@ CALL {
243
343
  "peer_id": enriched_single_relationship.peer_id,
244
344
  "peer_label": enriched_single_relationship.peer_label,
245
345
  "path_identifier": enriched_single_relationship.path_identifier,
246
- "num_added": enriched_single_relationship.num_added,
247
- "num_updated": enriched_single_relationship.num_updated,
248
- "num_removed": enriched_single_relationship.num_removed,
249
- "num_conflicts": enriched_single_relationship.num_conflicts,
250
- "contains_conflict": enriched_single_relationship.contains_conflict,
251
346
  },
252
347
  "conflict_params": conflict_params,
253
348
  "properties": property_props,
@@ -269,11 +364,6 @@ CALL {
269
364
  else None,
270
365
  "action": enriched_relationship.action,
271
366
  "path_identifier": enriched_relationship.path_identifier,
272
- "num_added": enriched_relationship.num_added,
273
- "num_updated": enriched_relationship.num_updated,
274
- "num_removed": enriched_relationship.num_removed,
275
- "num_conflicts": enriched_relationship.num_conflicts,
276
- "contains_conflict": enriched_relationship.contains_conflict,
277
367
  },
278
368
  "relationships": single_relationship_props,
279
369
  }
@@ -296,11 +386,6 @@ CALL {
296
386
  "changed_at": enriched_node.changed_at.to_string() if enriched_node.changed_at else None,
297
387
  "action": enriched_node.action.value,
298
388
  "path_identifier": enriched_node.path_identifier,
299
- "num_added": enriched_node.num_added,
300
- "num_updated": enriched_node.num_updated,
301
- "num_removed": enriched_node.num_removed,
302
- "num_conflicts": enriched_node.num_conflicts,
303
- "contains_conflict": enriched_node.contains_conflict,
304
389
  },
305
390
  "conflict_params": conflict_params,
306
391
  "attributes": attribute_props,
@@ -328,7 +413,7 @@ class EnrichedNodesLinkQuery(Query):
328
413
  super().__init__(**kwargs)
329
414
  self.enriched_diffs = enriched_diffs
330
415
 
331
- async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None:
416
+ async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None: # noqa: ARG002
332
417
  parent_links_list = []
333
418
  for diff_root in (self.enriched_diffs.base_branch_diff, self.enriched_diffs.diff_branch_diff):
334
419
  for node in diff_root.nodes:
@@ -0,0 +1,220 @@
1
+ from typing import Any
2
+
3
+ from infrahub.core.query import Query, QueryType
4
+ from infrahub.database import InfrahubDatabase
5
+
6
+ from ..model.path import TrackingId
7
+
8
+
9
+ class DiffSummaryCountsEnricherQuery(Query):
10
+ """Update summary counters for a given diff"""
11
+
12
+ name = "diff_summary_count_enricher"
13
+ type = QueryType.WRITE
14
+ insert_return = False
15
+
16
+ def __init__(
17
+ self,
18
+ diff_branch_name: str,
19
+ tracking_id: TrackingId | None = None,
20
+ diff_id: str | None = None,
21
+ node_uuids: list[str] | None = None,
22
+ **kwargs: Any,
23
+ ) -> None:
24
+ super().__init__(**kwargs)
25
+ if (diff_id is None and tracking_id is None) or (diff_id and tracking_id):
26
+ raise ValueError("EnrichedDiffAllConflictsQuery requires one and only one of `tracking_id` or `diff_id`")
27
+ self.diff_branch_name = diff_branch_name
28
+ self.tracking_id = tracking_id
29
+ self.diff_id = diff_id
30
+ if self.tracking_id is None and self.diff_id is None:
31
+ raise RuntimeError("tracking_id or diff_id is required")
32
+ self.node_uuids = node_uuids
33
+
34
+ async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None: # noqa: ARG002
35
+ self.params = {
36
+ "diff_branch_name": self.diff_branch_name,
37
+ "diff_id": self.diff_id,
38
+ "tracking_id": self.tracking_id.serialize() if self.tracking_id else None,
39
+ "node_uuids": self.node_uuids,
40
+ }
41
+ query = """
42
+ MATCH (root:DiffRoot)
43
+ WHERE ($diff_id IS NOT NULL AND root.uuid = $diff_id)
44
+ OR ($tracking_id IS NOT NULL AND root.tracking_id = $tracking_id AND root.diff_branch = $diff_branch_name)
45
+ MATCH (root)-[:DIFF_HAS_NODE]->(dn:DiffNode)
46
+ WHERE $node_uuids IS NULL OR dn.uuid IN $node_uuids
47
+ CALL {
48
+ // ----------------------
49
+ // handle attribute count updates
50
+ // ----------------------
51
+ WITH dn
52
+ MATCH (dn)-[:DIFF_HAS_ATTRIBUTE]->(da:DiffAttribute)
53
+ CALL {
54
+ WITH da
55
+ OPTIONAL MATCH (da)-[:DIFF_HAS_PROPERTY]->(dp:DiffProperty)-[:DIFF_HAS_CONFLICT]->(dc:DiffConflict)
56
+ WITH da, count(dc) AS num_conflicts
57
+ SET da.num_conflicts = num_conflicts
58
+ SET da.contains_conflict = (num_conflicts > 0)
59
+ }
60
+ CALL {
61
+ WITH da
62
+ OPTIONAL MATCH (da)-[:DIFF_HAS_PROPERTY]->(dp:DiffProperty {action: "added"})
63
+ WITH da, count(dp.action) AS num_added
64
+ SET da.num_added = num_added
65
+ }
66
+ CALL {
67
+ WITH da
68
+ OPTIONAL MATCH (da)-[:DIFF_HAS_PROPERTY]->(dp:DiffProperty {action: "updated"})
69
+ WITH da, count(dp.action) AS num_updated
70
+ SET da.num_updated = num_updated
71
+ }
72
+ CALL {
73
+ WITH da
74
+ OPTIONAL MATCH (da)-[:DIFF_HAS_PROPERTY]->(dp:DiffProperty {action: "removed"})
75
+ WITH da, count(dp.action) AS num_removed
76
+ SET da.num_removed = num_removed
77
+ }
78
+ }
79
+ CALL {
80
+ WITH dn
81
+ MATCH (dn)-[:DIFF_HAS_RELATIONSHIP]->(dr:DiffRelationship)
82
+ CALL {
83
+ // ----------------------
84
+ // handle relationship element count updates
85
+ // ----------------------
86
+ WITH dr
87
+ MATCH (dr)-[:DIFF_HAS_ELEMENT]->(dre:DiffRelationshipElement)
88
+ CALL {
89
+ WITH dre
90
+ OPTIONAL MATCH (dre)-[*..4]->(dc:DiffConflict)
91
+ WITH dre, count(dc) AS num_conflicts
92
+ SET dre.num_conflicts = num_conflicts
93
+ SET dre.contains_conflict = (num_conflicts > 0)
94
+ }
95
+ CALL {
96
+ WITH dre
97
+ OPTIONAL MATCH (dre)-[:DIFF_HAS_PROPERTY]->(dp:DiffProperty {action: "added"})
98
+ WITH dre, count(dp.action) AS num_added
99
+ SET dre.num_added = num_added
100
+ }
101
+ CALL {
102
+ WITH dre
103
+ OPTIONAL MATCH (dre)-[:DIFF_HAS_PROPERTY]->(dp:DiffProperty {action: "updated"})
104
+ WITH dre, count(dp.action) AS num_updated
105
+ SET dre.num_updated = num_updated
106
+ }
107
+ CALL {
108
+ WITH dre
109
+ OPTIONAL MATCH (dre)-[:DIFF_HAS_PROPERTY]->(dp:DiffProperty {action: "removed"})
110
+ WITH dre, count(dp.action) AS num_removed
111
+ SET dre.num_removed = num_removed
112
+ }
113
+ }
114
+ // ----------------------
115
+ // handle relationship count updates
116
+ // ----------------------
117
+ OPTIONAL MATCH (dr)-[:DIFF_HAS_ELEMENT]->(conflict_dre:DiffRelationshipElement {contains_conflict: TRUE})
118
+ WITH dr, sum(conflict_dre.num_conflicts) AS num_conflicts
119
+ SET dr.num_conflicts = num_conflicts
120
+ SET dr.contains_conflict = (num_conflicts > 0)
121
+ WITH dr
122
+ CALL {
123
+ WITH dr
124
+ OPTIONAL MATCH (dr)-[:DIFF_HAS_ELEMENT]->(dre:DiffRelationshipElement {action: "added"})
125
+ WITH dr, count(dre.action) AS num_added
126
+ SET dr.num_added = num_added
127
+ }
128
+ CALL {
129
+ WITH dr
130
+ OPTIONAL MATCH (dr)-[:DIFF_HAS_ELEMENT]->(dre:DiffRelationshipElement {action: "updated"})
131
+ WITH dr, count(dre.action) AS num_updated
132
+ SET dr.num_updated = num_updated
133
+ }
134
+ CALL {
135
+ WITH dr
136
+ OPTIONAL MATCH (dr)-[:DIFF_HAS_ELEMENT]->(dre:DiffRelationshipElement {action: "removed"})
137
+ WITH dr, count(dre.action) AS num_removed
138
+ SET dr.num_removed = num_removed
139
+ }
140
+ }
141
+ // ----------------------
142
+ // handle node count updates
143
+ // ----------------------
144
+ WITH root, dn, coalesce(dn.num_conflicts, 0) AS previous_num_conflicts
145
+ CALL {
146
+ // ----------------------
147
+ // handle node num_conflicts update
148
+ // ----------------------
149
+ WITH dn
150
+ OPTIONAL MATCH (dn)-[:DIFF_HAS_ATTRIBUTE]->(da:DiffAttribute {contains_conflict: TRUE})
151
+ RETURN sum(da.num_conflicts) AS num_conflicts
152
+ UNION ALL
153
+ WITH dn
154
+ OPTIONAL MATCH (dn)-[:DIFF_HAS_RELATIONSHIP]->(dr:DiffRelationship {contains_conflict: TRUE})
155
+ RETURN sum(dr.num_conflicts) AS num_conflicts
156
+ UNION ALL
157
+ WITH dn
158
+ OPTIONAL MATCH (dn)-[:DIFF_HAS_CONFLICT]->(dc:DiffConflict)
159
+ RETURN count(dc) AS num_conflicts
160
+ }
161
+ WITH root, dn, previous_num_conflicts, sum(num_conflicts) AS updated_num_conflicts
162
+ SET dn.num_conflicts = updated_num_conflicts
163
+ SET dn.contains_conflict = (updated_num_conflicts > 0)
164
+ WITH root, dn, updated_num_conflicts - previous_num_conflicts AS num_conflicts_delta
165
+ CALL {
166
+ // ----------------------
167
+ // handle node added/updated/removed updates
168
+ // ----------------------
169
+ WITH dn
170
+ OPTIONAL MATCH (dn)-[:DIFF_HAS_ATTRIBUTE]->(da:DiffAttribute)
171
+ WITH dn, collect(da.action) AS attr_actions
172
+ OPTIONAL MATCH (dn)-[:DIFF_HAS_RELATIONSHIP]->(dr:DiffRelationship)
173
+ WITH dn, attr_actions, collect(dr.action) AS rel_actions
174
+ WITH dn, attr_actions + rel_actions AS actions
175
+ WITH dn, reduce(counts = [0,0,0], a IN actions |
176
+ CASE
177
+ WHEN a = "added" THEN [counts[0] + 1, counts[1], counts[2]]
178
+ WHEN a = "updated" THEN [counts[0], counts[1] + 1, counts[2]]
179
+ WHEN a = "removed" THEN [counts[0], counts[1], counts[2] + 1]
180
+ ELSE counts
181
+ END
182
+ ) AS action_counts
183
+ WITH dn, action_counts[0] AS num_added, action_counts[1] AS num_updated, action_counts[2] AS num_removed
184
+ SET dn.num_added = num_added
185
+ SET dn.num_updated = num_updated
186
+ SET dn.num_removed = num_removed
187
+ }
188
+ // ----------------------
189
+ // handle conflict updates for parent nodes
190
+ // ----------------------
191
+ WITH root, dn, num_conflicts_delta
192
+ CALL {
193
+ WITH dn, num_conflicts_delta
194
+ OPTIONAL MATCH (dn)-[:DIFF_HAS_RELATIONSHIP|DIFF_HAS_NODE*1..]->(parent_node:DiffNode)
195
+ SET parent_node.num_conflicts = parent_node.num_conflicts + num_conflicts_delta
196
+ SET parent_node.contains_conflict = (parent_node.num_conflicts > 0)
197
+ }
198
+ // ----------------------
199
+ // handle root count updates
200
+ // ----------------------
201
+ WITH root, sum(num_conflicts_delta) AS total_conflicts_delta
202
+ CALL {
203
+ WITH root, total_conflicts_delta
204
+ SET root.num_conflicts = coalesce(root.num_conflicts, 0) + total_conflicts_delta
205
+ SET root.contains_conflict = root.num_conflicts > 0
206
+ WITH root
207
+ OPTIONAL MATCH (root)-[:DIFF_HAS_NODE]->(dn:DiffNode {action: "added"})
208
+ WITH root, count(dn.action) AS num_added
209
+ SET root.num_added = num_added
210
+ WITH root
211
+ OPTIONAL MATCH (root)-[:DIFF_HAS_NODE]->(dn:DiffNode {action: "updated"})
212
+ WITH root, count(dn.action) AS num_updated
213
+ SET root.num_updated = num_updated
214
+ WITH root
215
+ OPTIONAL MATCH (root)-[:DIFF_HAS_NODE]->(dn:DiffNode {action: "removed"})
216
+ WITH root, count(dn.action) AS num_removed
217
+ SET root.num_removed = num_removed
218
+ }
219
+ """
220
+ self.add_to_query(query)
@@ -30,7 +30,7 @@ class EnrichedDiffTimeRangeQuery(Query):
30
30
  self.from_time = from_time
31
31
  self.to_time = to_time
32
32
 
33
- async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None:
33
+ async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None: # noqa: ARG002
34
34
  self.params = {
35
35
  "base_branch": self.base_branch_name,
36
36
  "diff_branch": self.diff_branch_name,
@@ -41,7 +41,8 @@ class EnrichedDiffTimeRangeQuery(Query):
41
41
  query = """
42
42
  // get the roots of all diffs in the query
43
43
  MATCH (diff_root:DiffRoot)
44
- WHERE diff_root.base_branch = $base_branch
44
+ WHERE (diff_root.is_merged IS NULL OR diff_root.is_merged <> TRUE)
45
+ AND diff_root.base_branch = $base_branch
45
46
  AND diff_root.diff_branch = $diff_branch
46
47
  AND diff_root.from_time >= $from_time
47
48
  AND diff_root.to_time <= $to_time
@@ -21,7 +21,7 @@ class EnrichedDiffConflictUpdateQuery(Query):
21
21
  self.conflict_id = conflict_id
22
22
  self.selection = selection
23
23
 
24
- async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None:
24
+ async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None: # noqa: ARG002
25
25
  self.params = {"conflict_id": self.conflict_id, "selection": self.selection.value if self.selection else None}
26
26
  query = """
27
27
  MATCH (conflict:DiffConflict {uuid: $conflict_id})