infrahub-server 1.2.0rc0__py3-none-any.whl → 1.2.1__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 (365) hide show
  1. infrahub/api/dependencies.py +6 -6
  2. infrahub/api/diff/validation_models.py +7 -7
  3. infrahub/api/schema.py +1 -1
  4. infrahub/artifacts/models.py +5 -3
  5. infrahub/artifacts/tasks.py +3 -5
  6. infrahub/cli/__init__.py +13 -9
  7. infrahub/cli/constants.py +3 -0
  8. infrahub/cli/db.py +165 -183
  9. infrahub/cli/upgrade.py +146 -0
  10. infrahub/computed_attribute/gather.py +185 -0
  11. infrahub/computed_attribute/models.py +240 -12
  12. infrahub/computed_attribute/tasks.py +77 -441
  13. infrahub/computed_attribute/triggers.py +13 -47
  14. infrahub/config.py +43 -32
  15. infrahub/context.py +14 -0
  16. infrahub/core/account.py +4 -4
  17. infrahub/core/attribute.py +58 -58
  18. infrahub/core/branch/tasks.py +74 -22
  19. infrahub/core/changelog/diff.py +95 -36
  20. infrahub/core/changelog/models.py +217 -43
  21. infrahub/core/constants/__init__.py +28 -0
  22. infrahub/core/constants/infrahubkind.py +2 -0
  23. infrahub/core/constants/schema.py +2 -0
  24. infrahub/core/constraint/node/runner.py +9 -8
  25. infrahub/core/diff/branch_differ.py +10 -10
  26. infrahub/core/diff/enricher/cardinality_one.py +5 -0
  27. infrahub/core/diff/enricher/hierarchy.py +17 -4
  28. infrahub/core/diff/enricher/labels.py +5 -0
  29. infrahub/core/diff/enricher/path_identifier.py +4 -0
  30. infrahub/core/diff/ipam_diff_parser.py +4 -5
  31. infrahub/core/diff/model/diff.py +27 -27
  32. infrahub/core/diff/model/path.py +32 -9
  33. infrahub/core/diff/parent_node_adder.py +78 -0
  34. infrahub/core/diff/payload_builder.py +13 -2
  35. infrahub/core/diff/query/filters.py +2 -2
  36. infrahub/core/diff/query/merge.py +20 -17
  37. infrahub/core/diff/query/save.py +188 -182
  38. infrahub/core/diff/query/summary_counts_enricher.py +51 -4
  39. infrahub/core/diff/query_parser.py +4 -4
  40. infrahub/core/diff/repository/deserializer.py +8 -3
  41. infrahub/core/diff/repository/repository.py +156 -38
  42. infrahub/core/diff/tasks.py +4 -4
  43. infrahub/core/graph/__init__.py +1 -1
  44. infrahub/core/graph/index.py +3 -0
  45. infrahub/core/initialization.py +1 -10
  46. infrahub/core/ipam/constants.py +3 -4
  47. infrahub/core/ipam/reconciler.py +12 -12
  48. infrahub/core/ipam/utilization.py +10 -13
  49. infrahub/core/manager.py +36 -36
  50. infrahub/core/merge.py +7 -7
  51. infrahub/core/migrations/__init__.py +2 -3
  52. infrahub/core/migrations/graph/__init__.py +12 -3
  53. infrahub/core/migrations/graph/m017_add_core_profile.py +1 -5
  54. infrahub/core/migrations/graph/m018_uniqueness_nulls.py +4 -4
  55. infrahub/core/migrations/graph/m019_restore_rels_to_time.py +256 -0
  56. infrahub/core/migrations/graph/m020_duplicate_edges.py +160 -0
  57. infrahub/core/migrations/graph/m021_missing_hierarchy_merge.py +51 -0
  58. infrahub/core/migrations/graph/m022_add_generate_template_attr.py +48 -0
  59. infrahub/core/migrations/graph/m023_deduplicate_cardinality_one_relationships.py +96 -0
  60. infrahub/core/migrations/query/attribute_add.py +2 -2
  61. infrahub/core/migrations/query/node_duplicate.py +43 -26
  62. infrahub/core/migrations/query/schema_attribute_update.py +2 -2
  63. infrahub/core/migrations/schema/models.py +19 -4
  64. infrahub/core/migrations/schema/node_remove.py +26 -12
  65. infrahub/core/migrations/schema/tasks.py +2 -2
  66. infrahub/core/migrations/shared.py +16 -16
  67. infrahub/core/models.py +15 -6
  68. infrahub/core/node/__init__.py +43 -39
  69. infrahub/core/node/base.py +2 -4
  70. infrahub/core/node/constraints/attribute_uniqueness.py +2 -2
  71. infrahub/core/node/constraints/grouped_uniqueness.py +99 -47
  72. infrahub/core/node/constraints/interface.py +1 -2
  73. infrahub/core/node/delete_validator.py +3 -5
  74. infrahub/core/node/ipam.py +4 -4
  75. infrahub/core/node/permissions.py +7 -7
  76. infrahub/core/node/resource_manager/ip_address_pool.py +6 -6
  77. infrahub/core/node/resource_manager/ip_prefix_pool.py +6 -6
  78. infrahub/core/node/resource_manager/number_pool.py +3 -3
  79. infrahub/core/path.py +12 -12
  80. infrahub/core/property.py +11 -11
  81. infrahub/core/protocols.py +7 -0
  82. infrahub/core/protocols_base.py +21 -21
  83. infrahub/core/query/__init__.py +33 -33
  84. infrahub/core/query/attribute.py +6 -4
  85. infrahub/core/query/diff.py +3 -3
  86. infrahub/core/query/node.py +82 -32
  87. infrahub/core/query/relationship.py +228 -40
  88. infrahub/core/query/resource_manager.py +2 -0
  89. infrahub/core/query/standard_node.py +3 -3
  90. infrahub/core/query/subquery.py +9 -9
  91. infrahub/core/registry.py +13 -15
  92. infrahub/core/relationship/constraints/count.py +3 -4
  93. infrahub/core/relationship/constraints/peer_kind.py +3 -4
  94. infrahub/core/relationship/constraints/profiles_kind.py +2 -2
  95. infrahub/core/relationship/model.py +51 -59
  96. infrahub/core/schema/attribute_schema.py +16 -8
  97. infrahub/core/schema/basenode_schema.py +105 -44
  98. infrahub/core/schema/computed_attribute.py +3 -3
  99. infrahub/core/schema/definitions/core/__init__.py +147 -0
  100. infrahub/core/schema/definitions/core/account.py +171 -0
  101. infrahub/core/schema/definitions/core/artifact.py +136 -0
  102. infrahub/core/schema/definitions/core/builtin.py +24 -0
  103. infrahub/core/schema/definitions/core/check.py +68 -0
  104. infrahub/core/schema/definitions/core/core.py +17 -0
  105. infrahub/core/schema/definitions/core/generator.py +100 -0
  106. infrahub/core/schema/definitions/core/graphql_query.py +79 -0
  107. infrahub/core/schema/definitions/core/group.py +108 -0
  108. infrahub/core/schema/definitions/core/ipam.py +193 -0
  109. infrahub/core/schema/definitions/core/lineage.py +19 -0
  110. infrahub/core/schema/definitions/core/menu.py +48 -0
  111. infrahub/core/schema/definitions/core/permission.py +163 -0
  112. infrahub/core/schema/definitions/core/profile.py +18 -0
  113. infrahub/core/schema/definitions/core/propose_change.py +97 -0
  114. infrahub/core/schema/definitions/core/propose_change_comment.py +193 -0
  115. infrahub/core/schema/definitions/core/propose_change_validator.py +328 -0
  116. infrahub/core/schema/definitions/core/repository.py +286 -0
  117. infrahub/core/schema/definitions/core/resource_pool.py +170 -0
  118. infrahub/core/schema/definitions/core/template.py +27 -0
  119. infrahub/core/schema/definitions/core/transform.py +96 -0
  120. infrahub/core/schema/definitions/core/webhook.py +134 -0
  121. infrahub/core/schema/definitions/internal.py +16 -16
  122. infrahub/core/schema/dropdown.py +3 -4
  123. infrahub/core/schema/generated/attribute_schema.py +15 -18
  124. infrahub/core/schema/generated/base_node_schema.py +12 -14
  125. infrahub/core/schema/generated/node_schema.py +3 -5
  126. infrahub/core/schema/generated/relationship_schema.py +9 -11
  127. infrahub/core/schema/generic_schema.py +2 -2
  128. infrahub/core/schema/manager.py +20 -9
  129. infrahub/core/schema/node_schema.py +4 -2
  130. infrahub/core/schema/relationship_schema.py +14 -6
  131. infrahub/core/schema/schema_branch.py +292 -144
  132. infrahub/core/schema/schema_branch_computed.py +41 -4
  133. infrahub/core/task/task.py +3 -3
  134. infrahub/core/task/user_task.py +15 -15
  135. infrahub/core/timestamp.py +3 -3
  136. infrahub/core/utils.py +20 -18
  137. infrahub/core/validators/__init__.py +1 -3
  138. infrahub/core/validators/aggregated_checker.py +2 -2
  139. infrahub/core/validators/attribute/choices.py +2 -2
  140. infrahub/core/validators/attribute/enum.py +2 -2
  141. infrahub/core/validators/attribute/kind.py +2 -2
  142. infrahub/core/validators/attribute/length.py +2 -2
  143. infrahub/core/validators/attribute/optional.py +2 -2
  144. infrahub/core/validators/attribute/regex.py +2 -2
  145. infrahub/core/validators/attribute/unique.py +2 -2
  146. infrahub/core/validators/checks_runner.py +25 -2
  147. infrahub/core/validators/determiner.py +1 -3
  148. infrahub/core/validators/interface.py +6 -2
  149. infrahub/core/validators/model.py +22 -3
  150. infrahub/core/validators/models/validate_migration.py +17 -4
  151. infrahub/core/validators/node/attribute.py +2 -2
  152. infrahub/core/validators/node/generate_profile.py +2 -2
  153. infrahub/core/validators/node/hierarchy.py +3 -5
  154. infrahub/core/validators/node/inherit_from.py +27 -5
  155. infrahub/core/validators/node/relationship.py +2 -2
  156. infrahub/core/validators/relationship/count.py +4 -4
  157. infrahub/core/validators/relationship/optional.py +2 -2
  158. infrahub/core/validators/relationship/peer.py +2 -2
  159. infrahub/core/validators/shared.py +2 -2
  160. infrahub/core/validators/tasks.py +8 -0
  161. infrahub/core/validators/uniqueness/checker.py +22 -21
  162. infrahub/core/validators/uniqueness/index.py +2 -2
  163. infrahub/core/validators/uniqueness/model.py +11 -11
  164. infrahub/database/__init__.py +27 -22
  165. infrahub/database/metrics.py +7 -1
  166. infrahub/dependencies/builder/constraint/grouped/node_runner.py +1 -3
  167. infrahub/dependencies/builder/diff/deserializer.py +3 -1
  168. infrahub/dependencies/builder/diff/enricher/hierarchy.py +3 -1
  169. infrahub/dependencies/builder/diff/parent_node_adder.py +8 -0
  170. infrahub/dependencies/component/registry.py +2 -2
  171. infrahub/events/__init__.py +25 -2
  172. infrahub/events/artifact_action.py +64 -0
  173. infrahub/events/branch_action.py +33 -22
  174. infrahub/events/generator.py +71 -0
  175. infrahub/events/group_action.py +51 -21
  176. infrahub/events/models.py +18 -19
  177. infrahub/events/node_action.py +88 -37
  178. infrahub/events/repository_action.py +5 -18
  179. infrahub/events/schema_action.py +4 -9
  180. infrahub/events/utils.py +16 -0
  181. infrahub/events/validator_action.py +55 -0
  182. infrahub/exceptions.py +32 -24
  183. infrahub/generators/models.py +2 -3
  184. infrahub/generators/tasks.py +24 -4
  185. infrahub/git/base.py +7 -7
  186. infrahub/git/integrator.py +48 -24
  187. infrahub/git/models.py +101 -9
  188. infrahub/git/repository.py +3 -3
  189. infrahub/git/tasks.py +408 -6
  190. infrahub/git/utils.py +48 -0
  191. infrahub/git/worktree.py +1 -2
  192. infrahub/git_credential/askpass.py +1 -2
  193. infrahub/graphql/analyzer.py +12 -0
  194. infrahub/graphql/app.py +13 -15
  195. infrahub/graphql/context.py +39 -0
  196. infrahub/graphql/initialization.py +3 -0
  197. infrahub/graphql/loaders/node.py +2 -12
  198. infrahub/graphql/loaders/peers.py +77 -0
  199. infrahub/graphql/loaders/shared.py +13 -0
  200. infrahub/graphql/manager.py +17 -19
  201. infrahub/graphql/mutations/artifact_definition.py +5 -5
  202. infrahub/graphql/mutations/branch.py +26 -1
  203. infrahub/graphql/mutations/computed_attribute.py +9 -5
  204. infrahub/graphql/mutations/diff.py +23 -11
  205. infrahub/graphql/mutations/diff_conflict.py +5 -0
  206. infrahub/graphql/mutations/generator.py +83 -0
  207. infrahub/graphql/mutations/graphql_query.py +5 -5
  208. infrahub/graphql/mutations/ipam.py +54 -74
  209. infrahub/graphql/mutations/main.py +195 -132
  210. infrahub/graphql/mutations/menu.py +7 -7
  211. infrahub/graphql/mutations/models.py +2 -4
  212. infrahub/graphql/mutations/node_getter/by_default_filter.py +10 -10
  213. infrahub/graphql/mutations/node_getter/by_hfid.py +1 -3
  214. infrahub/graphql/mutations/node_getter/by_id.py +1 -3
  215. infrahub/graphql/mutations/node_getter/interface.py +1 -2
  216. infrahub/graphql/mutations/proposed_change.py +7 -7
  217. infrahub/graphql/mutations/relationship.py +93 -19
  218. infrahub/graphql/mutations/repository.py +8 -8
  219. infrahub/graphql/mutations/resource_manager.py +3 -3
  220. infrahub/graphql/mutations/schema.py +19 -4
  221. infrahub/graphql/mutations/webhook.py +137 -0
  222. infrahub/graphql/parser.py +4 -4
  223. infrahub/graphql/permissions.py +1 -10
  224. infrahub/graphql/queries/diff/tree.py +19 -14
  225. infrahub/graphql/queries/event.py +5 -2
  226. infrahub/graphql/queries/ipam.py +2 -2
  227. infrahub/graphql/queries/relationship.py +2 -2
  228. infrahub/graphql/queries/search.py +2 -2
  229. infrahub/graphql/resolvers/many_relationship.py +264 -0
  230. infrahub/graphql/resolvers/resolver.py +13 -110
  231. infrahub/graphql/schema.py +2 -0
  232. infrahub/graphql/subscription/graphql_query.py +2 -0
  233. infrahub/graphql/types/context.py +12 -0
  234. infrahub/graphql/types/event.py +84 -17
  235. infrahub/graphql/types/node.py +2 -2
  236. infrahub/graphql/utils.py +2 -2
  237. infrahub/groups/ancestors.py +29 -0
  238. infrahub/groups/parsers.py +107 -0
  239. infrahub/lock.py +20 -20
  240. infrahub/menu/constants.py +0 -1
  241. infrahub/menu/generator.py +9 -21
  242. infrahub/menu/menu.py +17 -38
  243. infrahub/menu/models.py +117 -16
  244. infrahub/menu/repository.py +111 -0
  245. infrahub/menu/utils.py +5 -8
  246. infrahub/message_bus/__init__.py +11 -13
  247. infrahub/message_bus/messages/__init__.py +1 -21
  248. infrahub/message_bus/messages/check_generator_run.py +3 -3
  249. infrahub/message_bus/messages/finalize_validator_execution.py +3 -0
  250. infrahub/message_bus/messages/proposed_change/request_proposedchange_refreshartifacts.py +6 -0
  251. infrahub/message_bus/messages/request_generatordefinition_check.py +2 -0
  252. infrahub/message_bus/messages/send_echo_request.py +1 -1
  253. infrahub/message_bus/operations/__init__.py +1 -10
  254. infrahub/message_bus/operations/check/__init__.py +2 -2
  255. infrahub/message_bus/operations/check/generator.py +1 -0
  256. infrahub/message_bus/operations/event/__init__.py +2 -2
  257. infrahub/message_bus/operations/event/worker.py +0 -3
  258. infrahub/message_bus/operations/finalize/validator.py +51 -1
  259. infrahub/message_bus/operations/requests/__init__.py +0 -2
  260. infrahub/message_bus/operations/requests/generator_definition.py +21 -23
  261. infrahub/message_bus/operations/requests/proposed_change.py +14 -10
  262. infrahub/permissions/globals.py +15 -0
  263. infrahub/pools/number.py +2 -4
  264. infrahub/proposed_change/models.py +3 -0
  265. infrahub/proposed_change/tasks.py +58 -45
  266. infrahub/pytest_plugin.py +13 -10
  267. infrahub/server.py +2 -3
  268. infrahub/services/__init__.py +2 -2
  269. infrahub/services/adapters/cache/__init__.py +4 -6
  270. infrahub/services/adapters/cache/nats.py +4 -5
  271. infrahub/services/adapters/cache/redis.py +3 -7
  272. infrahub/services/adapters/event/__init__.py +1 -1
  273. infrahub/services/adapters/message_bus/__init__.py +3 -3
  274. infrahub/services/adapters/message_bus/local.py +2 -2
  275. infrahub/services/adapters/message_bus/nats.py +4 -4
  276. infrahub/services/adapters/message_bus/rabbitmq.py +4 -4
  277. infrahub/services/adapters/workflow/local.py +2 -2
  278. infrahub/services/component.py +5 -5
  279. infrahub/services/protocols.py +7 -7
  280. infrahub/services/scheduler.py +1 -3
  281. infrahub/task_manager/event.py +102 -9
  282. infrahub/task_manager/models.py +27 -7
  283. infrahub/tasks/artifact.py +7 -6
  284. infrahub/telemetry/__init__.py +0 -0
  285. infrahub/telemetry/constants.py +9 -0
  286. infrahub/telemetry/database.py +86 -0
  287. infrahub/telemetry/models.py +65 -0
  288. infrahub/telemetry/task_manager.py +77 -0
  289. infrahub/{tasks/telemetry.py → telemetry/tasks.py} +49 -56
  290. infrahub/telemetry/utils.py +11 -0
  291. infrahub/trace.py +4 -4
  292. infrahub/transformations/tasks.py +2 -2
  293. infrahub/trigger/catalogue.py +4 -6
  294. infrahub/trigger/constants.py +0 -8
  295. infrahub/trigger/models.py +54 -5
  296. infrahub/trigger/setup.py +90 -0
  297. infrahub/trigger/tasks.py +35 -84
  298. infrahub/utils.py +11 -1
  299. infrahub/validators/__init__.py +0 -0
  300. infrahub/validators/events.py +42 -0
  301. infrahub/validators/tasks.py +41 -0
  302. infrahub/webhook/gather.py +17 -0
  303. infrahub/webhook/models.py +176 -44
  304. infrahub/webhook/tasks.py +154 -155
  305. infrahub/webhook/triggers.py +31 -7
  306. infrahub/workers/infrahub_async.py +2 -2
  307. infrahub/workers/utils.py +2 -2
  308. infrahub/workflows/catalogue.py +86 -35
  309. infrahub/workflows/initialization.py +8 -2
  310. infrahub/workflows/models.py +27 -1
  311. infrahub/workflows/utils.py +10 -1
  312. infrahub_sdk/client.py +35 -8
  313. infrahub_sdk/config.py +3 -0
  314. infrahub_sdk/context.py +13 -0
  315. infrahub_sdk/ctl/branch.py +3 -2
  316. infrahub_sdk/ctl/cli_commands.py +5 -1
  317. infrahub_sdk/ctl/utils.py +0 -16
  318. infrahub_sdk/exceptions.py +12 -0
  319. infrahub_sdk/generator.py +4 -1
  320. infrahub_sdk/graphql.py +45 -13
  321. infrahub_sdk/node.py +71 -22
  322. infrahub_sdk/protocols.py +21 -8
  323. infrahub_sdk/protocols_base.py +32 -11
  324. infrahub_sdk/query_groups.py +6 -35
  325. infrahub_sdk/schema/__init__.py +55 -26
  326. infrahub_sdk/schema/main.py +8 -0
  327. infrahub_sdk/task/__init__.py +11 -0
  328. infrahub_sdk/task/constants.py +3 -0
  329. infrahub_sdk/task/exceptions.py +25 -0
  330. infrahub_sdk/task/manager.py +551 -0
  331. infrahub_sdk/task/models.py +74 -0
  332. infrahub_sdk/testing/schemas/animal.py +9 -0
  333. infrahub_sdk/timestamp.py +142 -33
  334. infrahub_sdk/utils.py +29 -1
  335. {infrahub_server-1.2.0rc0.dist-info → infrahub_server-1.2.1.dist-info}/METADATA +8 -6
  336. {infrahub_server-1.2.0rc0.dist-info → infrahub_server-1.2.1.dist-info}/RECORD +349 -293
  337. {infrahub_server-1.2.0rc0.dist-info → infrahub_server-1.2.1.dist-info}/entry_points.txt +1 -0
  338. infrahub_testcontainers/constants.py +2 -0
  339. infrahub_testcontainers/container.py +157 -12
  340. infrahub_testcontainers/docker-compose.test.yml +31 -6
  341. infrahub_testcontainers/helpers.py +18 -73
  342. infrahub_testcontainers/host.py +41 -0
  343. infrahub_testcontainers/measurements.py +93 -0
  344. infrahub_testcontainers/models.py +38 -0
  345. infrahub_testcontainers/performance_test.py +166 -0
  346. infrahub_testcontainers/plugin.py +136 -0
  347. infrahub_testcontainers/prometheus.yml +30 -0
  348. infrahub/core/schema/definitions/core.py +0 -2286
  349. infrahub/message_bus/messages/check_repository_checkdefinition.py +0 -20
  350. infrahub/message_bus/messages/check_repository_mergeconflicts.py +0 -16
  351. infrahub/message_bus/messages/check_repository_usercheck.py +0 -26
  352. infrahub/message_bus/messages/event_branch_create.py +0 -11
  353. infrahub/message_bus/messages/event_branch_delete.py +0 -11
  354. infrahub/message_bus/messages/event_branch_rebased.py +0 -9
  355. infrahub/message_bus/messages/event_node_mutated.py +0 -15
  356. infrahub/message_bus/messages/event_schema_update.py +0 -9
  357. infrahub/message_bus/messages/request_repository_checks.py +0 -12
  358. infrahub/message_bus/messages/request_repository_userchecks.py +0 -18
  359. infrahub/message_bus/operations/check/repository.py +0 -293
  360. infrahub/message_bus/operations/event/node.py +0 -20
  361. infrahub/message_bus/operations/event/schema.py +0 -17
  362. infrahub/message_bus/operations/requests/repository.py +0 -133
  363. infrahub/webhook/constants.py +0 -1
  364. {infrahub_server-1.2.0rc0.dist-info → infrahub_server-1.2.1.dist-info}/LICENSE.txt +0 -0
  365. {infrahub_server-1.2.0rc0.dist-info → infrahub_server-1.2.1.dist-info}/WHEEL +0 -0
infrahub/core/manager.py CHANGED
@@ -1,7 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from functools import reduce
4
- from typing import TYPE_CHECKING, Any, Iterable, Literal, Optional, TypeVar, Union, overload
4
+ from typing import TYPE_CHECKING, Any, Iterable, Literal, TypeVar, overload
5
5
 
6
6
  from infrahub_sdk.utils import deep_merge_dict, is_valid_uuid
7
7
 
@@ -93,7 +93,7 @@ class ProfileAttributeIndex:
93
93
  self._profile_attributes_id_map[p_id] for p_id in profile_ids if p_id in self._profile_attributes_id_map
94
94
  ]
95
95
 
96
- def get_profile_priority(nafd: NodeAttributesFromDB) -> tuple[Union[int, float], str]:
96
+ def get_profile_priority(nafd: NodeAttributesFromDB) -> tuple[int | float, str]:
97
97
  try:
98
98
  return (int(nafd.attrs.get("profile_priority").value), nafd.node.get("uuid"))
99
99
  except (TypeError, AttributeError):
@@ -134,13 +134,13 @@ class NodeManager:
134
134
  async def query(
135
135
  cls,
136
136
  db: InfrahubDatabase,
137
- schema: Union[NodeSchema, GenericSchema, ProfileSchema, TemplateSchema, str],
137
+ schema: NodeSchema | GenericSchema | ProfileSchema | TemplateSchema | str,
138
138
  filters: dict | None = ...,
139
139
  fields: dict | None = ...,
140
140
  offset: int | None = ...,
141
141
  limit: int | None = ...,
142
- at: Union[Timestamp, str] | None = ...,
143
- branch: Union[Branch, str] | None = ...,
142
+ at: Timestamp | str | None = ...,
143
+ branch: Branch | str | None = ...,
144
144
  include_source: bool = ...,
145
145
  include_owner: bool = ...,
146
146
  prefetch_relationships: bool = ...,
@@ -160,8 +160,8 @@ class NodeManager:
160
160
  fields: dict | None = ...,
161
161
  offset: int | None = ...,
162
162
  limit: int | None = ...,
163
- at: Union[Timestamp, str] | None = ...,
164
- branch: Union[Branch, str] | None = ...,
163
+ at: Timestamp | str | None = ...,
164
+ branch: Branch | str | None = ...,
165
165
  include_source: bool = ...,
166
166
  include_owner: bool = ...,
167
167
  prefetch_relationships: bool = ...,
@@ -180,8 +180,8 @@ class NodeManager:
180
180
  fields: dict | None = None,
181
181
  offset: int | None = None,
182
182
  limit: int | None = None,
183
- at: Union[Timestamp, str] | None = None,
184
- branch: Union[Branch, str] | None = None,
183
+ at: Timestamp | str | None = None,
184
+ branch: Branch | str | None = None,
185
185
  include_source: bool = False,
186
186
  include_owner: bool = False,
187
187
  prefetch_relationships: bool = False,
@@ -272,10 +272,10 @@ class NodeManager:
272
272
  async def count(
273
273
  cls,
274
274
  db: InfrahubDatabase,
275
- schema: Union[type[SchemaProtocol], NodeSchema, GenericSchema, ProfileSchema, TemplateSchema, str],
276
- filters: Optional[dict] = None,
277
- at: Optional[Union[Timestamp, str]] = None,
278
- branch: Optional[Union[Branch, str]] = None,
275
+ schema: type[SchemaProtocol] | NodeSchema | GenericSchema | ProfileSchema | TemplateSchema | str,
276
+ filters: dict | None = None,
277
+ at: Timestamp | str | None = None,
278
+ branch: Branch | str | None = None,
279
279
  account=None, # noqa: ARG003
280
280
  partial_match: bool = False,
281
281
  branch_agnostic: bool = False,
@@ -317,8 +317,8 @@ class NodeManager:
317
317
  schema: RelationshipSchema,
318
318
  filters: dict,
319
319
  db: InfrahubDatabase,
320
- at: Optional[Union[Timestamp, str]] = None,
321
- branch: Optional[Union[Branch, str]] = None,
320
+ at: Timestamp | str | None = None,
321
+ branch: Branch | str | None = None,
322
322
  branch_agnostic: bool = False,
323
323
  ) -> int:
324
324
  branch = await registry.get_branch(branch=branch, db=db)
@@ -346,11 +346,11 @@ class NodeManager:
346
346
  source_kind: str,
347
347
  schema: RelationshipSchema,
348
348
  filters: dict,
349
- fields: Optional[dict] = None,
350
- offset: Optional[int] = None,
351
- limit: Optional[int] = None,
352
- at: Optional[Union[Timestamp, str]] = None,
353
- branch: Optional[Union[Branch, str]] = None,
349
+ fields: dict | None = None,
350
+ offset: int | None = None,
351
+ limit: int | None = None,
352
+ at: Timestamp | str | None = None,
353
+ branch: Branch | str | None = None,
354
354
  branch_agnostic: bool = False,
355
355
  fetch_peers: bool = False,
356
356
  ) -> list[Relationship]:
@@ -420,8 +420,8 @@ class NodeManager:
420
420
  node_schema: NodeSchema,
421
421
  filters: dict,
422
422
  db: InfrahubDatabase,
423
- at: Optional[Union[Timestamp, str]] = None,
424
- branch: Optional[Union[Branch, str]] = None,
423
+ at: Timestamp | str | None = None,
424
+ branch: Branch | str | None = None,
425
425
  ) -> int:
426
426
  branch = await registry.get_branch(branch=branch, db=db)
427
427
  at = Timestamp(at)
@@ -446,12 +446,12 @@ class NodeManager:
446
446
  direction: RelationshipHierarchyDirection,
447
447
  node_schema: NodeSchema,
448
448
  filters: dict,
449
- fields: Optional[dict] = None,
450
- offset: Optional[int] = None,
451
- limit: Optional[int] = None,
452
- at: Optional[Union[Timestamp, str]] = None,
453
- branch: Optional[Union[Branch, str]] = None,
454
- ) -> dict[str, Any]:
449
+ fields: dict | None = None,
450
+ offset: int | None = None,
451
+ limit: int | None = None,
452
+ at: Timestamp | str | None = None,
453
+ branch: Branch | str | None = None,
454
+ ) -> dict[str, Node]:
455
455
  branch = await registry.get_branch(branch=branch, db=db)
456
456
  at = Timestamp(at)
457
457
 
@@ -1039,7 +1039,7 @@ class NodeManager:
1039
1039
  prefetch_relationships: bool = False,
1040
1040
  account=None,
1041
1041
  branch_agnostic: bool = False,
1042
- ) -> Optional[Any]:
1042
+ ) -> Any | None:
1043
1043
  """Return one node based on its ID."""
1044
1044
  branch = await registry.get_branch(branch=branch, db=db)
1045
1045
 
@@ -1095,9 +1095,9 @@ class NodeManager:
1095
1095
  cls,
1096
1096
  db: InfrahubDatabase,
1097
1097
  ids: list[str],
1098
- fields: Optional[dict] = None,
1099
- at: Optional[Union[Timestamp, str]] = None,
1100
- branch: Optional[Union[Branch, str]] = None,
1098
+ fields: dict | None = None,
1099
+ at: Timestamp | str | None = None,
1100
+ branch: Branch | str | None = None,
1101
1101
  include_source: bool = False,
1102
1102
  include_owner: bool = False,
1103
1103
  prefetch_relationships: bool = False,
@@ -1158,7 +1158,7 @@ class NodeManager:
1158
1158
  continue
1159
1159
 
1160
1160
  node = nodes_info_by_id[node_id]
1161
- new_node_data: dict[str, Union[str, AttributeFromDB]] = {
1161
+ new_node_data: dict[str, str | AttributeFromDB] = {
1162
1162
  "db_id": node.node_id,
1163
1163
  "id": node_id,
1164
1164
  "updated_at": node.updated_at,
@@ -1313,9 +1313,9 @@ class NodeManager:
1313
1313
  cls,
1314
1314
  db: InfrahubDatabase,
1315
1315
  nodes: list[Node],
1316
- branch: Optional[Union[Branch, str]] = None,
1317
- at: Optional[Union[Timestamp, str]] = None,
1318
- ) -> list[Any]:
1316
+ branch: Branch | str | None = None,
1317
+ at: Timestamp | str | None = None,
1318
+ ) -> list[Node]:
1319
1319
  """Returns list of deleted nodes because of cascading deletes"""
1320
1320
  branch = await registry.get_branch(branch=branch, db=db)
1321
1321
  node_delete_validator = NodeDeleteValidator(db=db, branch=branch)
infrahub/core/merge.py CHANGED
@@ -1,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import TYPE_CHECKING, Optional, Union
3
+ from typing import TYPE_CHECKING
4
4
 
5
5
  from infrahub.core.constants import RepositoryInternalStatus
6
6
  from infrahub.core.diff.model.path import BranchTrackingId
@@ -39,8 +39,8 @@ class BranchMerger:
39
39
  diff_coordinator: DiffCoordinator,
40
40
  diff_merger: DiffMerger,
41
41
  diff_repository: DiffRepository,
42
- destination_branch: Optional[Branch] = None,
43
- service: Optional[InfrahubServices] = None,
42
+ destination_branch: Branch | None = None,
43
+ service: InfrahubServices | None = None,
44
44
  ):
45
45
  self.source_branch = source_branch
46
46
  self.destination_branch: Branch = destination_branch or registry.get_branch_from_registry()
@@ -51,9 +51,9 @@ class BranchMerger:
51
51
  self.migrations: list[SchemaUpdateMigrationInfo] = []
52
52
  self._merge_at = Timestamp()
53
53
 
54
- self._source_schema: Optional[SchemaBranch] = None
55
- self._destination_schema: Optional[SchemaBranch] = None
56
- self._initial_source_schema: Optional[SchemaBranch] = None
54
+ self._source_schema: SchemaBranch | None = None
55
+ self._destination_schema: SchemaBranch | None = None
56
+ self._initial_source_schema: SchemaBranch | None = None
57
57
 
58
58
  self._service = service
59
59
 
@@ -173,7 +173,7 @@ class BranchMerger:
173
173
 
174
174
  async def merge(
175
175
  self,
176
- at: Optional[Union[str, Timestamp]] = None,
176
+ at: str | Timestamp | None = None,
177
177
  ) -> EnrichedDiffRoot:
178
178
  """Merge the current branch into main."""
179
179
  if self.source_branch.name == registry.default_branch:
@@ -1,5 +1,3 @@
1
- from typing import Optional
2
-
3
1
  from .schema.attribute_name_update import AttributeNameUpdateMigration
4
2
  from .schema.node_attribute_add import NodeAttributeAddMigration
5
3
  from .schema.node_attribute_remove import NodeAttributeRemoveMigration
@@ -8,11 +6,12 @@ from .schema.node_remove import NodeRemoveMigration
8
6
  from .schema.placeholder_dummy import PlaceholderDummyMigration
9
7
  from .shared import SchemaMigration
10
8
 
11
- MIGRATION_MAP: dict[str, Optional[type[SchemaMigration]]] = {
9
+ MIGRATION_MAP: dict[str, type[SchemaMigration] | None] = {
12
10
  "node.remove": NodeRemoveMigration,
13
11
  "node.branch.update": None,
14
12
  "node.attribute.add": NodeAttributeAddMigration,
15
13
  "node.attribute.remove": NodeAttributeRemoveMigration,
14
+ "node.inherit_from.update": NodeKindUpdateMigration,
16
15
  "node.name.update": NodeKindUpdateMigration,
17
16
  "node.namespace.update": NodeKindUpdateMigration,
18
17
  "node.relationship.remove": PlaceholderDummyMigration,
@@ -1,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import TYPE_CHECKING, Sequence, Union
3
+ from typing import TYPE_CHECKING, Sequence
4
4
 
5
5
  from .m001_add_version_to_graph import Migration001
6
6
  from .m002_attribute_is_default import Migration002
@@ -20,13 +20,17 @@ from .m015_diff_format_update import Migration015
20
20
  from .m016_diff_delete_bug_fix import Migration016
21
21
  from .m017_add_core_profile import Migration017
22
22
  from .m018_uniqueness_nulls import Migration018
23
+ from .m019_restore_rels_to_time import Migration019
24
+ from .m020_duplicate_edges import Migration020
25
+ from .m021_missing_hierarchy_merge import Migration021
26
+ from .m022_add_generate_template_attr import Migration022
23
27
 
24
28
  if TYPE_CHECKING:
25
29
  from infrahub.core.root import Root
26
30
 
27
31
  from ..shared import ArbitraryMigration, GraphMigration, InternalSchemaMigration
28
32
 
29
- MIGRATIONS: list[type[Union[GraphMigration, InternalSchemaMigration, ArbitraryMigration]]] = [
33
+ MIGRATIONS: list[type[GraphMigration | InternalSchemaMigration | ArbitraryMigration]] = [
30
34
  Migration001,
31
35
  Migration002,
32
36
  Migration003,
@@ -45,12 +49,17 @@ MIGRATIONS: list[type[Union[GraphMigration, InternalSchemaMigration, ArbitraryMi
45
49
  Migration016,
46
50
  Migration017,
47
51
  Migration018,
52
+ Migration019,
53
+ Migration020,
54
+ Migration021,
55
+ Migration022,
56
+ # Migration023, Enable this migration once it has been tested on bigger databases
48
57
  ]
49
58
 
50
59
 
51
60
  async def get_graph_migrations(
52
61
  root: Root,
53
- ) -> Sequence[Union[GraphMigration, InternalSchemaMigration, ArbitraryMigration]]:
62
+ ) -> Sequence[GraphMigration | InternalSchemaMigration | ArbitraryMigration]:
54
63
  applicable_migrations = []
55
64
  for migration_class in MIGRATIONS:
56
65
  migration = migration_class.init()
@@ -4,7 +4,6 @@ from typing import TYPE_CHECKING, Sequence
4
4
 
5
5
  from infrahub.core import registry
6
6
  from infrahub.core.migrations.shared import MigrationResult
7
- from infrahub.core.schema import GenericSchema
8
7
  from infrahub.core.schema.definitions.core import core_profile_schema_definition
9
8
  from infrahub.core.schema.manager import SchemaManager
10
9
  from infrahub.log import get_logger
@@ -31,14 +30,11 @@ class Migration017(InternalSchemaMigration):
31
30
  """
32
31
  Load CoreProfile schema node in db.
33
32
  """
34
-
35
- core_profile = GenericSchema(**core_profile_schema_definition)
36
-
37
33
  default_branch = registry.get_branch_from_registry()
38
34
  manager = SchemaManager()
39
35
  manager.set_schema_branch(name=default_branch.name, schema=self.get_internal_schema())
40
36
 
41
37
  db.add_schema(manager.get_schema_branch(default_branch.name))
42
- await manager.load_node_to_db(node=core_profile, db=db, branch=default_branch)
38
+ await manager.load_node_to_db(node=core_profile_schema_definition, db=db, branch=default_branch)
43
39
 
44
40
  return MigrationResult()
@@ -50,18 +50,18 @@ class Migration018(InternalSchemaMigration):
50
50
  schema_branch = await manager.load_schema_from_db(db=db, branch=default_branch)
51
51
  manager.set_schema_branch(name=default_branch.name, schema=schema_branch)
52
52
 
53
- for schema_kind in schema_branch.node_names + schema_branch.generic_names:
53
+ for schema_kind in schema_branch.node_names + schema_branch.generic_names_without_templates:
54
54
  schema = schema_branch.get(name=schema_kind, duplicate=False)
55
55
  if not isinstance(schema, NodeSchema | GenericSchema):
56
56
  continue
57
57
 
58
- schema_constraint_path_groups = schema.get_unique_constraint_schema_attribute_paths(
58
+ uniqueness_constraint_paths = schema.get_unique_constraint_schema_attribute_paths(
59
59
  schema_branch=schema_branch
60
60
  )
61
61
  includes_optional_attr: bool = False
62
62
 
63
- for constraint_group in schema_constraint_path_groups:
64
- for schema_attribute_path in constraint_group:
63
+ for uniqueness_constraint_path in uniqueness_constraint_paths:
64
+ for schema_attribute_path in uniqueness_constraint_path.attributes_paths:
65
65
  if (
66
66
  schema_attribute_path.attribute_schema
67
67
  and schema_attribute_path.attribute_schema.optional is True
@@ -0,0 +1,256 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, Any, Sequence
4
+
5
+ from infrahub.core.migrations.shared import GraphMigration, MigrationResult
6
+ from infrahub.log import get_logger
7
+
8
+ from ...constants import GLOBAL_BRANCH_NAME, BranchSupportType
9
+ from ...query import Query, QueryType
10
+
11
+ if TYPE_CHECKING:
12
+ from infrahub.database import InfrahubDatabase
13
+
14
+ log = get_logger()
15
+
16
+
17
+ class FixBranchAwareEdgesQuery(Query):
18
+ name = "replace_global_edges"
19
+ type = QueryType.WRITE
20
+ insert_return = False
21
+
22
+ async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None: # noqa: ARG002
23
+ """
24
+ Between a Node and a Relationship, if Relationship.branch_support=aware, replace any global edge
25
+ to the branch of a non-global edge leaving out of the Relationship node. Note that there can't
26
+ be multiple non-global branches on these edges, as a dedicated Relationship node would exist for that.
27
+ """
28
+
29
+ query = """
30
+ MATCH (node:Node)-[global_edge:IS_RELATED {branch: $global_branch}]-(rel:Relationship)
31
+ WHERE rel.branch_support=$branch_aware
32
+ MATCH (rel)-[non_global_edge:IS_RELATED]-(node_2: Node)
33
+ WHERE non_global_edge.branch <> $global_branch
34
+ SET global_edge.branch = non_global_edge.branch
35
+ """
36
+
37
+ params = {
38
+ "global_branch": GLOBAL_BRANCH_NAME,
39
+ "branch_aware": BranchSupportType.AWARE.value,
40
+ "branch_agnostic": BranchSupportType.AGNOSTIC.value,
41
+ }
42
+
43
+ self.params.update(params)
44
+ self.add_to_query(query)
45
+
46
+
47
+ class SetMissingToTimeQuery(Query):
48
+ name = "set_missing_to_time"
49
+ type = QueryType.WRITE
50
+ insert_return = False
51
+
52
+ async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None: # noqa: ARG002
53
+ """
54
+ If both a deleted edge and an active edge with no time exist between 2 nodes on the same branch,
55
+ set `to` time of active edge using `from` time of the deleted one. This would typically happen after having
56
+ replaced a deleted edge on global branch by correct branch with above query.
57
+ """
58
+
59
+ query = """
60
+ MATCH (node:Node)-[deleted_edge:IS_RELATED {status: "deleted"}]-(rel:Relationship)
61
+ MATCH (rel)-[active_edge:IS_RELATED {status: "active"}]-(node)
62
+ WHERE active_edge.to IS NULL AND deleted_edge.branch = active_edge.branch
63
+ SET active_edge.to = deleted_edge.from
64
+ """
65
+
66
+ self.add_to_query(query)
67
+
68
+
69
+ class DeleteNodesRelsQuery(Query):
70
+ name = "delete_relationships_of_deleted_nodes"
71
+ type = QueryType.WRITE
72
+ insert_return = False
73
+
74
+ async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None: # noqa: ARG002
75
+ """
76
+ Some nodes may have been incorrectly deleted, typically, while these nodes edges connected to Root
77
+ are correctly deleted, edges connected to other `Node` through a `Relationship` node may still be active.
78
+ Following query correctly deletes these edges by both setting correct to time and creating corresponding deleted edge.
79
+ """
80
+
81
+ query = """
82
+ MATCH (deleted_node: Node)-[deleted_edge:IS_PART_OF {status: "deleted"}]->(:Root)
83
+ MATCH (deleted_node)-[:IS_RELATED]-(rel:Relationship)
84
+
85
+ // exclude nodes having been deleted through migration. find those with same uuid and exclude the one with earlier
86
+ // timestamp on active branch
87
+ WHERE NOT EXISTS {
88
+ MATCH (deleted_node)-[e1:IS_RELATED]-(rel)-[e2:IS_RELATED]-(other_node)
89
+ WITH deleted_node, other_node, MIN(e1.from) AS min_e1_from, MIN(e2.from) AS min_e2_from
90
+ WHERE deleted_node <> other_node AND deleted_node.uuid = other_node.uuid AND min_e1_from < min_e2_from
91
+ }
92
+
93
+ // Note that if an AWARE node has been deleted on a branch and relationship is AGNOSTIC, we do not "delete" this relationship
94
+ // right now as this aware node might exist on another branch.
95
+
96
+ // Set to time if there is an active edge:
97
+ // - on deleted edge branch
98
+ // - or on any branch and deleted node is agnostic
99
+ // - or deleted node is aware and rel is agnostic
100
+ CALL {
101
+ WITH rel, deleted_edge
102
+ OPTIONAL MATCH (rel)-[peer_active_edge {status: "active"}]-(peer_1)
103
+ WHERE (peer_active_edge.branch = deleted_edge.branch OR (rel.branch_support <> $branch_agnostic AND deleted_edge.branch = $global_branch))
104
+ AND peer_active_edge.to IS NULL
105
+ SET peer_active_edge.to = deleted_edge.from
106
+ }
107
+
108
+ // Get distinct rel nodes linked to a deleted node, with the time at which we should delete rel edges.
109
+ // Take the MAX time so if it does not take the deleted time of a node deleted through a duplication migration.
110
+ WITH DISTINCT rel,
111
+ deleted_edge.branch AS deleted_edge_branch,
112
+ deleted_edge.branch_level AS branch_level,
113
+ MAX(deleted_edge.from) as deleted_time,
114
+ deleted_node.branch_support as deleted_node_branch_support
115
+
116
+
117
+ // No need to check deleted edge branch because
118
+ // If deleted_node has different branch support type (agnostic/aware) than rel type,
119
+ // there might already be a deleted edge that we would not match if we filter on deleted_edge_branch.
120
+ // If both are aware, it still works, as we would have one Relationship node for each branch on which this relationship exists.
121
+ MATCH (rel)-[]-(peer_2)
122
+ WHERE NOT exists((rel)-[{status: "deleted"}]-(peer_2))
123
+
124
+
125
+ // If res is agnostic and delete node is agnostic, we should delete on global branch
126
+ // If rel is aware and deleted node is aware, we should use deleted edge branch
127
+ // If rel is aware and delete node is agnostic, we need to create deleted edges for every distinct branch on which this relationship exists.
128
+ WITH DISTINCT
129
+ CASE
130
+ // Branch on which `deleted` edge should be created depends on rel.branch_support.
131
+ WHEN rel.branch_support = $branch_agnostic
132
+ THEN CASE
133
+ WHEN deleted_node_branch_support = $branch_agnostic THEN [$global_branch]
134
+ ELSE []
135
+ END
136
+ ELSE
137
+ CASE
138
+ WHEN deleted_node_branch_support = $branch_agnostic
139
+ THEN COLLECT {
140
+ WITH rel
141
+ MATCH (rel)-[active_edge {status: "active"}]-(peer_2)
142
+ RETURN DISTINCT active_edge.branch
143
+ }
144
+ ELSE
145
+ CASE
146
+ // if no active edge on this branch exists it means this relationship node is dedicated for another branch
147
+ WHEN exists((rel)-[{status: "active", branch: deleted_edge_branch}]-(peer_2)) THEN [deleted_edge_branch]
148
+ ELSE []
149
+ END
150
+ END
151
+ END AS branches,
152
+ branch_level,
153
+ deleted_time,
154
+ peer_2,
155
+ rel
156
+
157
+ UNWIND branches as branch
158
+
159
+ // Then creates `deleted` edge.
160
+ // Below CALL subqueries are called once for each rel-peer_2 pair for which we want to create a deleted edge.
161
+ // Note that with current infrahub relationships edges design, only one of this CALL should be matched per pair.
162
+
163
+ CALL {
164
+ WITH rel, peer_2, branch, branch_level, deleted_time
165
+ MATCH (rel)-[:IS_RELATED]->(peer_2)
166
+ MERGE (rel)-[:IS_RELATED {status: "deleted", branch: branch, branch_level: branch_level, from: deleted_time}]->(peer_2)
167
+ }
168
+
169
+ CALL {
170
+ WITH rel, peer_2, branch, branch_level, deleted_time
171
+ MATCH (rel)-[:IS_PROTECTED]->(peer_2)
172
+ MERGE (rel)-[:IS_PROTECTED {status: "deleted", branch: branch, branch_level: branch_level, from: deleted_time}]->(peer_2)
173
+ }
174
+
175
+ CALL {
176
+ WITH rel, peer_2, branch, branch_level, deleted_time
177
+ MATCH (rel)-[:IS_VISIBLE]->(peer_2)
178
+ MERGE (rel)-[:IS_VISIBLE {status: "deleted", branch: branch, branch_level: branch_level, from: deleted_time}]->(peer_2)
179
+ }
180
+
181
+ CALL {
182
+ WITH rel, peer_2, branch, branch_level, deleted_time
183
+ MATCH (rel)-[:HAS_OWNER]->(peer_2)
184
+ MERGE (rel)-[:HAS_OWNER {status: "deleted", branch: branch, branch_level: branch_level, from: deleted_time}]->(peer_2)
185
+ }
186
+
187
+ CALL {
188
+ WITH rel, peer_2, branch, branch_level, deleted_time
189
+ MATCH (rel)-[:HAS_SOURCE]->(peer_2)
190
+ MERGE (rel)-[:HAS_SOURCE {status: "deleted", branch: branch, branch_level: branch_level, from: deleted_time}]->(peer_2)
191
+ }
192
+
193
+ CALL {
194
+ WITH rel, peer_2, branch, branch_level, deleted_time
195
+ MATCH (rel)<-[:IS_RELATED]-(peer_2)
196
+ MERGE (rel)<-[:IS_RELATED {status: "deleted", branch: branch, branch_level: branch_level, from: deleted_time}]-(peer_2)
197
+ }
198
+
199
+ CALL {
200
+ WITH rel, peer_2, branch, branch_level, deleted_time
201
+ MATCH (rel)<-[:IS_PROTECTED]-(peer_2)
202
+ MERGE (rel)<-[:IS_PROTECTED {status: "deleted", branch: branch, branch_level: branch_level, from: deleted_time}]-(peer_2)
203
+ }
204
+
205
+ CALL {
206
+ WITH rel, peer_2, branch, branch_level, deleted_time
207
+ MATCH (rel)<-[:IS_VISIBLE]-(peer_2)
208
+ MERGE (rel)<-[:IS_VISIBLE {status: "deleted", branch: branch, branch_level: branch_level, from: deleted_time}]-(peer_2)
209
+ }
210
+
211
+ CALL {
212
+ WITH rel, peer_2, branch, branch_level, deleted_time
213
+ MATCH (rel)<-[:HAS_OWNER]-(peer_2)
214
+ MERGE (rel)<-[:HAS_OWNER {status: "deleted", branch: branch, branch_level: branch_level, from: deleted_time}]-(peer_2)
215
+ }
216
+
217
+ CALL {
218
+ WITH rel, peer_2, branch, branch_level, deleted_time
219
+ MATCH (rel)<-[:HAS_SOURCE]-(peer_2)
220
+ MERGE (rel)<-[:HAS_SOURCE {status: "deleted", branch: branch, branch_level: branch_level, from: deleted_time}]-(peer_2)
221
+ }
222
+ """
223
+
224
+ params = {
225
+ "global_branch": GLOBAL_BRANCH_NAME,
226
+ "branch_aware": BranchSupportType.AWARE.value,
227
+ "branch_agnostic": BranchSupportType.AGNOSTIC.value,
228
+ }
229
+
230
+ self.params.update(params)
231
+ self.add_to_query(query)
232
+
233
+
234
+ class Migration019(GraphMigration):
235
+ """
236
+ Fix corrupted state introduced by Migration012 when duplicating a CoreAccount (branch Aware)
237
+ being part of a CoreStandardGroup (branch Agnostic). Database is corrupted at multiple points:
238
+ - Old CoreAccount node <> group_member node `active` edge has no `to` time (possibly because of #5590).
239
+ - Old CoreAccount node <> group_member node `deleted` edge is on `$global_branch` branch instead of `main`.
240
+ - New CoreAccount node <> group_member node `active` edge is on `$global_branch` branch instead of `main`.
241
+
242
+ Also, users having deleted corresponding CoreStandardGroup will also have the following data corruption,
243
+ as deletion did not happen correctly due to above issues:
244
+ - Both CoreAccount <> group_member and CoreStandardGroup <> group_member edges
245
+ have not been deleted (ie status is `active` without `to` time and no additional `deleted` edge).
246
+
247
+ This migration fixes all above issues to have consistent edges, and fixes IFC-1204.
248
+ """
249
+
250
+ name: str = "019_fix_edges_state"
251
+ minimum_version: int = 18
252
+ queries: Sequence[type[Query]] = [FixBranchAwareEdgesQuery, SetMissingToTimeQuery, DeleteNodesRelsQuery]
253
+
254
+ async def validate_migration(self, db: InfrahubDatabase) -> MigrationResult: # noqa: ARG002
255
+ result = MigrationResult()
256
+ return result