infrahub-server 1.1.6__py3-none-any.whl → 1.2.0b1__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 (407) 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} +5 -3
  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/models.py +1 -1
  16. infrahub/computed_attribute/tasks.py +64 -17
  17. infrahub/computed_attribute/triggers.py +90 -0
  18. infrahub/config.py +1 -1
  19. infrahub/context.py +39 -0
  20. infrahub/core/account.py +5 -8
  21. infrahub/core/attribute.py +54 -22
  22. infrahub/core/branch/models.py +4 -4
  23. infrahub/core/branch/tasks.py +137 -129
  24. infrahub/core/changelog/__init__.py +0 -0
  25. infrahub/core/changelog/diff.py +283 -0
  26. infrahub/core/changelog/models.py +499 -0
  27. infrahub/core/constants/__init__.py +43 -2
  28. infrahub/core/constants/infrahubkind.py +1 -0
  29. infrahub/core/constants/schema.py +2 -0
  30. infrahub/core/diff/combiner.py +12 -8
  31. infrahub/core/diff/coordinator.py +49 -70
  32. infrahub/core/diff/data_check_synchronizer.py +86 -7
  33. infrahub/core/diff/enricher/aggregated.py +3 -3
  34. infrahub/core/diff/enricher/cardinality_one.py +7 -7
  35. infrahub/core/diff/enricher/hierarchy.py +22 -7
  36. infrahub/core/diff/enricher/labels.py +19 -4
  37. infrahub/core/diff/enricher/path_identifier.py +7 -9
  38. infrahub/core/diff/enricher/summary_counts.py +3 -1
  39. infrahub/core/diff/merger/merger.py +8 -4
  40. infrahub/core/diff/model/path.py +76 -35
  41. infrahub/core/diff/parent_node_adder.py +78 -0
  42. infrahub/core/diff/payload_builder.py +13 -2
  43. infrahub/core/diff/query/all_conflicts.py +6 -3
  44. infrahub/core/diff/query/artifact.py +1 -1
  45. infrahub/core/diff/query/delete_query.py +1 -1
  46. infrahub/core/diff/query/diff_get.py +3 -2
  47. infrahub/core/diff/query/diff_summary.py +1 -1
  48. infrahub/core/diff/query/field_specifiers.py +3 -1
  49. infrahub/core/diff/query/field_summary.py +3 -2
  50. infrahub/core/diff/query/filters.py +14 -3
  51. infrahub/core/diff/query/get_conflict_query.py +1 -1
  52. infrahub/core/diff/query/has_conflicts_query.py +6 -3
  53. infrahub/core/diff/query/merge.py +3 -3
  54. infrahub/core/diff/query/{drop_tracking_id.py → merge_tracking_id.py} +4 -4
  55. infrahub/core/diff/query/roots_metadata.py +9 -2
  56. infrahub/core/diff/query/save.py +233 -142
  57. infrahub/core/diff/query/summary_counts_enricher.py +267 -0
  58. infrahub/core/diff/query/time_range_query.py +3 -2
  59. infrahub/core/diff/query/update_conflict_query.py +1 -1
  60. infrahub/core/diff/query_parser.py +49 -24
  61. infrahub/core/diff/repository/deserializer.py +32 -28
  62. infrahub/core/diff/repository/repository.py +215 -41
  63. infrahub/core/diff/tasks.py +13 -12
  64. infrahub/core/enums.py +1 -1
  65. infrahub/core/graph/__init__.py +1 -1
  66. infrahub/core/graph/index.py +3 -0
  67. infrahub/core/integrity/object_conflict/conflict_recorder.py +1 -1
  68. infrahub/core/ipam/reconciler.py +1 -1
  69. infrahub/core/ipam/tasks.py +2 -3
  70. infrahub/core/manager.py +20 -15
  71. infrahub/core/merge.py +5 -2
  72. infrahub/core/migrations/graph/__init__.py +4 -0
  73. infrahub/core/migrations/graph/m001_add_version_to_graph.py +1 -1
  74. infrahub/core/migrations/graph/m002_attribute_is_default.py +2 -2
  75. infrahub/core/migrations/graph/m003_relationship_parent_optional.py +2 -2
  76. infrahub/core/migrations/graph/m004_add_attr_documentation.py +1 -1
  77. infrahub/core/migrations/graph/m005_add_rel_read_only.py +1 -1
  78. infrahub/core/migrations/graph/m006_add_rel_on_delete.py +1 -1
  79. infrahub/core/migrations/graph/m007_add_rel_allow_override.py +1 -1
  80. infrahub/core/migrations/graph/m008_add_human_friendly_id.py +1 -1
  81. infrahub/core/migrations/graph/m009_add_generate_profile_attr.py +1 -1
  82. infrahub/core/migrations/graph/m010_add_generate_profile_attr_generic.py +1 -1
  83. infrahub/core/migrations/graph/m011_remove_profile_relationship_schema.py +2 -2
  84. infrahub/core/migrations/graph/m012_convert_account_generic.py +12 -23
  85. infrahub/core/migrations/graph/m013_convert_git_password_credential.py +7 -11
  86. infrahub/core/migrations/graph/m014_remove_index_attr_value.py +2 -2
  87. infrahub/core/migrations/graph/m015_diff_format_update.py +1 -1
  88. infrahub/core/migrations/graph/m016_diff_delete_bug_fix.py +1 -1
  89. infrahub/core/migrations/graph/m017_add_core_profile.py +1 -1
  90. infrahub/core/migrations/graph/m018_uniqueness_nulls.py +2 -2
  91. infrahub/core/migrations/graph/m019_restore_rels_to_time.py +256 -0
  92. infrahub/core/migrations/graph/m020_add_generate_template_attr.py +48 -0
  93. infrahub/core/migrations/query/attribute_add.py +1 -1
  94. infrahub/core/migrations/query/attribute_rename.py +1 -1
  95. infrahub/core/migrations/query/delete_element_in_schema.py +1 -1
  96. infrahub/core/migrations/query/node_duplicate.py +39 -19
  97. infrahub/core/migrations/query/relationship_duplicate.py +1 -1
  98. infrahub/core/migrations/query/schema_attribute_update.py +1 -1
  99. infrahub/core/migrations/schema/node_attribute_remove.py +1 -1
  100. infrahub/core/migrations/schema/node_remove.py +27 -13
  101. infrahub/core/migrations/schema/tasks.py +5 -5
  102. infrahub/core/migrations/shared.py +4 -4
  103. infrahub/core/models.py +7 -8
  104. infrahub/core/node/__init__.py +170 -46
  105. infrahub/core/node/base.py +1 -1
  106. infrahub/core/node/constraints/grouped_uniqueness.py +9 -2
  107. infrahub/core/node/delete_validator.py +4 -4
  108. infrahub/core/node/ipam.py +13 -8
  109. infrahub/core/node/permissions.py +4 -0
  110. infrahub/core/node/resource_manager/ip_prefix_pool.py +8 -5
  111. infrahub/core/node/standard.py +3 -5
  112. infrahub/core/property.py +1 -1
  113. infrahub/core/protocols.py +6 -0
  114. infrahub/core/protocols_base.py +4 -2
  115. infrahub/core/query/__init__.py +2 -5
  116. infrahub/core/query/attribute.py +9 -9
  117. infrahub/core/query/branch.py +5 -5
  118. infrahub/core/query/delete.py +1 -1
  119. infrahub/core/query/diff.py +45 -7
  120. infrahub/core/query/ipam.py +4 -4
  121. infrahub/core/query/node.py +19 -14
  122. infrahub/core/query/relationship.py +213 -26
  123. infrahub/core/query/resource_manager.py +13 -11
  124. infrahub/core/query/standard_node.py +6 -6
  125. infrahub/core/query/task.py +3 -3
  126. infrahub/core/query/task_log.py +1 -1
  127. infrahub/core/query/utils.py +5 -5
  128. infrahub/core/registry.py +0 -2
  129. infrahub/core/relationship/constraints/count.py +1 -1
  130. infrahub/core/relationship/constraints/peer_kind.py +1 -1
  131. infrahub/core/relationship/model.py +76 -38
  132. infrahub/core/schema/__init__.py +6 -4
  133. infrahub/core/schema/attribute_schema.py +8 -0
  134. infrahub/core/schema/basenode_schema.py +13 -3
  135. infrahub/core/schema/definitions/core/__init__.py +153 -0
  136. infrahub/core/schema/definitions/core/account.py +168 -0
  137. infrahub/core/schema/definitions/core/artifact.py +127 -0
  138. infrahub/core/schema/definitions/core/builtin.py +21 -0
  139. infrahub/core/schema/definitions/core/check.py +60 -0
  140. infrahub/core/schema/definitions/core/generator.py +96 -0
  141. infrahub/core/schema/definitions/core/graphql_query.py +77 -0
  142. infrahub/core/schema/definitions/core/group.py +105 -0
  143. infrahub/core/schema/definitions/core/ipam.py +252 -0
  144. infrahub/core/schema/definitions/core/lineage.py +17 -0
  145. infrahub/core/schema/definitions/core/menu.py +46 -0
  146. infrahub/core/schema/definitions/core/permission.py +161 -0
  147. infrahub/core/schema/definitions/core/profile.py +29 -0
  148. infrahub/core/schema/definitions/core/propose_change.py +88 -0
  149. infrahub/core/schema/definitions/core/propose_change_comment.py +188 -0
  150. infrahub/core/schema/definitions/core/propose_change_validator.py +326 -0
  151. infrahub/core/schema/definitions/core/repository.py +280 -0
  152. infrahub/core/schema/definitions/core/resource_pool.py +180 -0
  153. infrahub/core/schema/definitions/core/template.py +12 -0
  154. infrahub/core/schema/definitions/core/transform.py +87 -0
  155. infrahub/core/schema/definitions/core/webhook.py +108 -0
  156. infrahub/core/schema/definitions/internal.py +16 -0
  157. infrahub/core/schema/generated/genericnode_schema.py +5 -0
  158. infrahub/core/schema/generated/node_schema.py +5 -0
  159. infrahub/core/schema/generic_schema.py +5 -1
  160. infrahub/core/schema/manager.py +45 -42
  161. infrahub/core/schema/node_schema.py +4 -0
  162. infrahub/core/schema/profile_schema.py +4 -0
  163. infrahub/core/schema/relationship_schema.py +10 -2
  164. infrahub/core/schema/schema_branch.py +260 -16
  165. infrahub/core/schema/template_schema.py +36 -0
  166. infrahub/core/task/user_task.py +7 -5
  167. infrahub/core/timestamp.py +3 -3
  168. infrahub/core/utils.py +3 -2
  169. infrahub/core/validators/attribute/choices.py +1 -1
  170. infrahub/core/validators/attribute/enum.py +1 -1
  171. infrahub/core/validators/attribute/kind.py +1 -1
  172. infrahub/core/validators/attribute/length.py +1 -1
  173. infrahub/core/validators/attribute/optional.py +1 -1
  174. infrahub/core/validators/attribute/regex.py +1 -1
  175. infrahub/core/validators/attribute/unique.py +1 -1
  176. infrahub/core/validators/checks_runner.py +37 -0
  177. infrahub/core/validators/node/generate_profile.py +1 -1
  178. infrahub/core/validators/node/hierarchy.py +1 -1
  179. infrahub/core/validators/query.py +1 -1
  180. infrahub/core/validators/relationship/count.py +1 -1
  181. infrahub/core/validators/relationship/optional.py +1 -1
  182. infrahub/core/validators/relationship/peer.py +1 -1
  183. infrahub/core/validators/tasks.py +8 -6
  184. infrahub/core/validators/uniqueness/query.py +20 -17
  185. infrahub/database/__init__.py +16 -2
  186. infrahub/database/memgraph.py +1 -1
  187. infrahub/dependencies/builder/constraint/grouped/node_runner.py +0 -2
  188. infrahub/dependencies/builder/diff/combiner.py +1 -1
  189. infrahub/dependencies/builder/diff/conflicts_enricher.py +1 -1
  190. infrahub/dependencies/builder/diff/coordinator.py +0 -2
  191. infrahub/dependencies/builder/diff/deserializer.py +4 -2
  192. infrahub/dependencies/builder/diff/enricher/hierarchy.py +3 -1
  193. infrahub/dependencies/builder/diff/enricher/summary_counts.py +1 -1
  194. infrahub/dependencies/builder/diff/parent_node_adder.py +8 -0
  195. infrahub/events/artifact_action.py +76 -0
  196. infrahub/events/branch_action.py +50 -21
  197. infrahub/events/group_action.py +117 -0
  198. infrahub/events/models.py +164 -51
  199. infrahub/events/node_action.py +70 -8
  200. infrahub/events/repository_action.py +8 -8
  201. infrahub/events/schema_action.py +21 -8
  202. infrahub/exceptions.py +9 -0
  203. infrahub/generators/models.py +1 -0
  204. infrahub/generators/tasks.py +34 -15
  205. infrahub/git/base.py +3 -5
  206. infrahub/git/constants.py +0 -1
  207. infrahub/git/integrator.py +60 -36
  208. infrahub/git/models.py +80 -1
  209. infrahub/git/repository.py +7 -8
  210. infrahub/git/tasks.py +432 -112
  211. infrahub/git_credential/helper.py +2 -3
  212. infrahub/graphql/analyzer.py +572 -11
  213. infrahub/graphql/app.py +34 -26
  214. infrahub/graphql/auth/query_permission_checker/anonymous_checker.py +5 -5
  215. infrahub/graphql/auth/query_permission_checker/default_branch_checker.py +4 -4
  216. infrahub/graphql/auth/query_permission_checker/merge_operation_checker.py +4 -4
  217. infrahub/graphql/auth/query_permission_checker/object_permission_checker.py +28 -35
  218. infrahub/graphql/auth/query_permission_checker/super_admin_checker.py +5 -5
  219. infrahub/graphql/context.py +33 -0
  220. infrahub/graphql/enums.py +1 -1
  221. infrahub/graphql/initialization.py +5 -1
  222. infrahub/graphql/loaders/node.py +2 -2
  223. infrahub/graphql/manager.py +63 -63
  224. infrahub/graphql/mutations/account.py +20 -13
  225. infrahub/graphql/mutations/artifact_definition.py +16 -12
  226. infrahub/graphql/mutations/branch.py +86 -40
  227. infrahub/graphql/mutations/computed_attribute.py +24 -13
  228. infrahub/graphql/mutations/diff.py +54 -14
  229. infrahub/graphql/mutations/diff_conflict.py +14 -8
  230. infrahub/graphql/mutations/generator.py +83 -0
  231. infrahub/graphql/mutations/graphql_query.py +19 -11
  232. infrahub/graphql/mutations/ipam.py +25 -23
  233. infrahub/graphql/mutations/main.py +243 -50
  234. infrahub/graphql/mutations/menu.py +10 -10
  235. infrahub/graphql/mutations/proposed_change.py +36 -28
  236. infrahub/graphql/mutations/relationship.py +343 -104
  237. infrahub/graphql/mutations/repository.py +41 -35
  238. infrahub/graphql/mutations/resource_manager.py +26 -26
  239. infrahub/graphql/mutations/schema.py +66 -33
  240. infrahub/graphql/mutations/tasks.py +16 -10
  241. infrahub/graphql/parser.py +1 -1
  242. infrahub/graphql/permissions.py +3 -10
  243. infrahub/graphql/queries/account.py +22 -18
  244. infrahub/graphql/queries/branch.py +6 -4
  245. infrahub/graphql/queries/diff/tree.py +63 -52
  246. infrahub/graphql/queries/event.py +115 -0
  247. infrahub/graphql/queries/internal.py +3 -3
  248. infrahub/graphql/queries/ipam.py +23 -18
  249. infrahub/graphql/queries/relationship.py +11 -10
  250. infrahub/graphql/queries/resource_manager.py +43 -27
  251. infrahub/graphql/queries/search.py +9 -8
  252. infrahub/graphql/queries/status.py +12 -9
  253. infrahub/graphql/queries/task.py +11 -9
  254. infrahub/graphql/resolvers/resolver.py +69 -43
  255. infrahub/graphql/resolvers/single_relationship.py +16 -10
  256. infrahub/graphql/schema.py +4 -0
  257. infrahub/graphql/subscription/__init__.py +1 -1
  258. infrahub/graphql/subscription/events.py +1 -1
  259. infrahub/graphql/subscription/graphql_query.py +8 -8
  260. infrahub/graphql/types/branch.py +2 -2
  261. infrahub/graphql/types/common.py +6 -1
  262. infrahub/graphql/types/context.py +12 -0
  263. infrahub/graphql/types/enums.py +2 -0
  264. infrahub/graphql/types/event.py +158 -0
  265. infrahub/graphql/types/interface.py +2 -2
  266. infrahub/graphql/types/node.py +3 -3
  267. infrahub/graphql/types/permission.py +2 -2
  268. infrahub/graphql/types/relationship.py +3 -3
  269. infrahub/graphql/types/standard_node.py +9 -11
  270. infrahub/graphql/utils.py +28 -182
  271. infrahub/groups/tasks.py +2 -3
  272. infrahub/lock.py +21 -21
  273. infrahub/menu/generator.py +0 -1
  274. infrahub/menu/menu.py +116 -138
  275. infrahub/menu/models.py +4 -4
  276. infrahub/message_bus/__init__.py +11 -13
  277. infrahub/message_bus/messages/__init__.py +0 -14
  278. infrahub/message_bus/messages/check_generator_run.py +1 -3
  279. infrahub/message_bus/messages/event_branch_merge.py +3 -0
  280. infrahub/message_bus/messages/proposed_change/request_proposedchange_refreshartifacts.py +6 -0
  281. infrahub/message_bus/messages/request_proposedchange_pipeline.py +2 -0
  282. infrahub/message_bus/messages/send_echo_request.py +1 -1
  283. infrahub/message_bus/operations/__init__.py +4 -13
  284. infrahub/message_bus/operations/check/__init__.py +2 -2
  285. infrahub/message_bus/operations/check/generator.py +1 -3
  286. infrahub/message_bus/operations/event/branch.py +7 -3
  287. infrahub/message_bus/operations/event/schema.py +1 -1
  288. infrahub/message_bus/operations/event/worker.py +0 -3
  289. infrahub/message_bus/operations/finalize/validator.py +1 -1
  290. infrahub/message_bus/operations/git/file.py +2 -2
  291. infrahub/message_bus/operations/git/repository.py +1 -1
  292. infrahub/message_bus/operations/requests/__init__.py +0 -4
  293. infrahub/message_bus/operations/requests/generator_definition.py +2 -4
  294. infrahub/message_bus/operations/requests/proposed_change.py +37 -20
  295. infrahub/message_bus/operations/send/echo.py +1 -1
  296. infrahub/message_bus/types.py +1 -1
  297. infrahub/permissions/__init__.py +2 -1
  298. infrahub/permissions/globals.py +15 -0
  299. infrahub/permissions/types.py +26 -0
  300. infrahub/pools/prefix.py +29 -165
  301. infrahub/prefect_server/__init__.py +0 -0
  302. infrahub/prefect_server/app.py +18 -0
  303. infrahub/prefect_server/database.py +20 -0
  304. infrahub/prefect_server/events.py +28 -0
  305. infrahub/prefect_server/models.py +46 -0
  306. infrahub/proposed_change/models.py +18 -1
  307. infrahub/proposed_change/tasks.py +195 -53
  308. infrahub/pytest_plugin.py +4 -4
  309. infrahub/server.py +13 -12
  310. infrahub/services/__init__.py +148 -63
  311. infrahub/services/adapters/cache/__init__.py +11 -11
  312. infrahub/services/adapters/cache/nats.py +42 -25
  313. infrahub/services/adapters/cache/redis.py +3 -11
  314. infrahub/services/adapters/event/__init__.py +10 -18
  315. infrahub/services/adapters/http/__init__.py +0 -5
  316. infrahub/services/adapters/http/httpx.py +22 -15
  317. infrahub/services/adapters/message_bus/__init__.py +25 -8
  318. infrahub/services/adapters/message_bus/local.py +9 -7
  319. infrahub/services/adapters/message_bus/nats.py +14 -8
  320. infrahub/services/adapters/message_bus/rabbitmq.py +23 -10
  321. infrahub/services/adapters/workflow/__init__.py +11 -8
  322. infrahub/services/adapters/workflow/local.py +27 -6
  323. infrahub/services/adapters/workflow/worker.py +23 -7
  324. infrahub/services/component.py +43 -40
  325. infrahub/services/protocols.py +7 -7
  326. infrahub/services/scheduler.py +30 -29
  327. infrahub/storage.py +2 -4
  328. infrahub/task_manager/constants.py +1 -1
  329. infrahub/task_manager/event.py +261 -0
  330. infrahub/task_manager/models.py +147 -3
  331. infrahub/task_manager/task.py +1 -1
  332. infrahub/tasks/artifact.py +19 -18
  333. infrahub/tasks/registry.py +1 -1
  334. infrahub/tasks/telemetry.py +13 -14
  335. infrahub/transformations/tasks.py +3 -5
  336. infrahub/trigger/__init__.py +0 -0
  337. infrahub/trigger/catalogue.py +16 -0
  338. infrahub/trigger/constants.py +9 -0
  339. infrahub/trigger/models.py +105 -0
  340. infrahub/trigger/tasks.py +91 -0
  341. infrahub/types.py +1 -1
  342. infrahub/utils.py +1 -1
  343. infrahub/webhook/constants.py +0 -2
  344. infrahub/webhook/models.py +161 -40
  345. infrahub/webhook/tasks.py +123 -202
  346. infrahub/webhook/triggers.py +27 -0
  347. infrahub/workers/infrahub_async.py +36 -25
  348. infrahub/workers/utils.py +63 -0
  349. infrahub/workflows/catalogue.py +71 -52
  350. infrahub/workflows/initialization.py +14 -8
  351. infrahub/workflows/models.py +28 -4
  352. infrahub/workflows/utils.py +1 -1
  353. infrahub_sdk/client.py +8 -0
  354. infrahub_sdk/ctl/branch.py +3 -2
  355. infrahub_sdk/ctl/check.py +3 -3
  356. infrahub_sdk/ctl/cli_commands.py +16 -11
  357. infrahub_sdk/ctl/exceptions.py +0 -6
  358. infrahub_sdk/ctl/exporter.py +1 -1
  359. infrahub_sdk/ctl/generator.py +5 -5
  360. infrahub_sdk/ctl/importer.py +3 -2
  361. infrahub_sdk/ctl/menu.py +1 -1
  362. infrahub_sdk/ctl/object.py +1 -1
  363. infrahub_sdk/ctl/repository.py +23 -15
  364. infrahub_sdk/ctl/schema.py +2 -2
  365. infrahub_sdk/ctl/utils.py +4 -19
  366. infrahub_sdk/ctl/validate.py +2 -1
  367. infrahub_sdk/exceptions.py +12 -0
  368. infrahub_sdk/generator.py +3 -0
  369. infrahub_sdk/node.py +4 -4
  370. infrahub_sdk/protocols.py +21 -8
  371. infrahub_sdk/schema/__init__.py +14 -2
  372. infrahub_sdk/schema/main.py +7 -0
  373. infrahub_sdk/task/__init__.py +1 -0
  374. infrahub_sdk/task/constants.py +3 -0
  375. infrahub_sdk/task/exceptions.py +25 -0
  376. infrahub_sdk/task/manager.py +545 -0
  377. infrahub_sdk/task/models.py +74 -0
  378. infrahub_sdk/timestamp.py +134 -33
  379. infrahub_sdk/utils.py +39 -1
  380. infrahub_sdk/yaml.py +2 -3
  381. {infrahub_server-1.1.6.dist-info → infrahub_server-1.2.0b1.dist-info}/METADATA +47 -12
  382. infrahub_server-1.2.0b1.dist-info/RECORD +725 -0
  383. infrahub_testcontainers/container.py +14 -6
  384. infrahub_testcontainers/docker-compose.test.yml +24 -5
  385. infrahub_testcontainers/haproxy.cfg +43 -0
  386. infrahub_testcontainers/helpers.py +85 -1
  387. infrahub/core/branch/constants.py +0 -2
  388. infrahub/core/schema/definitions/core.py +0 -2274
  389. infrahub/graphql/query.py +0 -52
  390. infrahub/message_bus/messages/check_repository_checkdefinition.py +0 -20
  391. infrahub/message_bus/messages/check_repository_mergeconflicts.py +0 -16
  392. infrahub/message_bus/messages/check_repository_usercheck.py +0 -26
  393. infrahub/message_bus/messages/request_artifactdefinition_check.py +0 -17
  394. infrahub/message_bus/messages/request_repository_checks.py +0 -12
  395. infrahub/message_bus/messages/request_repository_userchecks.py +0 -18
  396. infrahub/message_bus/operations/check/repository.py +0 -293
  397. infrahub/message_bus/operations/requests/artifact_definition.py +0 -148
  398. infrahub/message_bus/operations/requests/repository.py +0 -133
  399. infrahub/schema/constants.py +0 -1
  400. infrahub/schema/tasks.py +0 -76
  401. infrahub/services/adapters/database/__init__.py +0 -9
  402. infrahub_sdk/ctl/_file.py +0 -13
  403. infrahub_server-1.1.6.dist-info/RECORD +0 -681
  404. /infrahub/{schema → artifacts}/__init__.py +0 -0
  405. {infrahub_server-1.1.6.dist-info → infrahub_server-1.2.0b1.dist-info}/LICENSE.txt +0 -0
  406. {infrahub_server-1.1.6.dist-info → infrahub_server-1.2.0b1.dist-info}/WHEEL +0 -0
  407. {infrahub_server-1.1.6.dist-info → infrahub_server-1.2.0b1.dist-info}/entry_points.txt +0 -0
@@ -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
@@ -0,0 +1,48 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, Any
4
+
5
+ from typing_extensions import Self
6
+
7
+ from infrahub.core.constants import SchemaPathType
8
+ from infrahub.core.migrations.shared import MigrationResult
9
+ from infrahub.core.path import SchemaPath
10
+
11
+ from ..schema.node_attribute_add import NodeAttributeAddMigration
12
+ from ..shared import InternalSchemaMigration
13
+
14
+ if TYPE_CHECKING:
15
+ from infrahub.database import InfrahubDatabase
16
+
17
+
18
+ class Migration020(InternalSchemaMigration):
19
+ name: str = "020_add_generate_template_attr"
20
+ minimum_version: int = 19
21
+
22
+ @classmethod
23
+ def init(cls, **kwargs: dict[str, Any]) -> Self:
24
+ internal_schema = cls.get_internal_schema()
25
+ schema_node = internal_schema.get_node(name="SchemaNode")
26
+ schema_generic = internal_schema.get_node(name="SchemaGeneric")
27
+
28
+ migrations = [
29
+ NodeAttributeAddMigration(
30
+ new_node_schema=schema_node,
31
+ previous_node_schema=schema_node,
32
+ schema_path=SchemaPath(
33
+ schema_kind="SchemaNode", path_type=SchemaPathType.ATTRIBUTE, field_name="generate_template"
34
+ ),
35
+ ),
36
+ NodeAttributeAddMigration(
37
+ new_node_schema=schema_generic,
38
+ previous_node_schema=schema_generic,
39
+ schema_path=SchemaPath(
40
+ schema_kind="SchemaNode", path_type=SchemaPathType.ATTRIBUTE, field_name="generate_template"
41
+ ),
42
+ ),
43
+ ]
44
+ return cls(migrations=migrations, **kwargs) # type: ignore[arg-type]
45
+
46
+ async def validate_migration(self, db: InfrahubDatabase) -> MigrationResult: # noqa: ARG002
47
+ result = MigrationResult()
48
+ return result
@@ -30,7 +30,7 @@ class AttributeAddQuery(Query):
30
30
 
31
31
  super().__init__(**kwargs)
32
32
 
33
- async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None:
33
+ async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None: # noqa: ARG002
34
34
  branch_filter, branch_params = self.branch.get_query_filter_path(at=self.at.to_string())
35
35
  self.params.update(branch_params)
36
36
 
@@ -86,7 +86,7 @@ class AttributeRenameQuery(Query):
86
86
  subquery.append("RETURN peer_node as p2")
87
87
  return "\n".join(subquery)
88
88
 
89
- async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None:
89
+ async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None: # noqa: ARG002
90
90
  branch_filter, branch_params = self.branch.get_query_filter_path(at=self.at.to_string())
91
91
  self.params.update(branch_params)
92
92
 
@@ -90,7 +90,7 @@ class DeleteElementInSchemaQuery(Query):
90
90
  sub_query_in = "\nUNION\n".join(sub_queries_in)
91
91
  return sub_query_in
92
92
 
93
- async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None:
93
+ async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None: # noqa: ARG002
94
94
  branch_filter, branch_params = self.branch.get_query_filter_path(at=self.at.to_string())
95
95
  self.params.update(branch_params)
96
96
 
@@ -48,19 +48,37 @@ class NodeDuplicateQuery(Query):
48
48
 
49
49
  @staticmethod
50
50
  def _render_sub_query_per_rel_type(
51
- rel_name: str, rel_type: str, rel_def: FieldInfo, direction: GraphRelDirection
51
+ rel_name: str,
52
+ rel_type: str,
53
+ rel_def: FieldInfo,
52
54
  ) -> str:
53
55
  subquery = [
54
56
  f"WITH peer_node, {rel_name}, active_node, new_node",
55
57
  f"WITH peer_node, {rel_name}, active_node, new_node",
56
58
  f'WHERE type({rel_name}) = "{rel_type}"',
57
59
  ]
58
- if rel_def.default.direction in [direction, GraphRelDirection.EITHER]:
59
- subquery.append(f"CREATE (new_node)-[:{rel_type} $rel_props_new ]->(peer_node)")
60
- subquery.append(f"CREATE (active_node)-[:{rel_type} $rel_props_prev ]->(peer_node)")
61
- elif rel_def.default.direction in [direction, GraphRelDirection.EITHER]:
62
- subquery.append(f"CREATE (new_node)<-[:{rel_type} $rel_props_new ]-(peer_node)")
63
- subquery.append(f"CREATE (active_node)<-[:{rel_type} $rel_props_prev ]-(peer_node)")
60
+ if rel_def.default.direction in [GraphRelDirection.OUTBOUND, GraphRelDirection.EITHER]:
61
+ subquery.append(f"""
62
+ CREATE (new_node)-[new_active_edge:{rel_type} $rel_props_new ]->(peer_node)
63
+ SET new_active_edge.branch = CASE WHEN {rel_name}.branch = "-global-" THEN "-global-" ELSE $branch END
64
+ SET new_active_edge.branch_level = CASE WHEN {rel_name}.branch = "-global-" THEN {rel_name}.branch_level ELSE $branch_level END
65
+ """)
66
+ subquery.append(f"""
67
+ CREATE (active_node)-[deleted_edge:{rel_type} $rel_props_prev ]->(peer_node)
68
+ SET deleted_edge.branch = CASE WHEN {rel_name}.branch = "-global-" THEN "-global-" ELSE $branch END
69
+ SET deleted_edge.branch_level = CASE WHEN {rel_name}.branch = "-global-" THEN {rel_name}.branch_level ELSE $branch_level END
70
+ """)
71
+ elif rel_def.default.direction in [GraphRelDirection.INBOUND, GraphRelDirection.EITHER]:
72
+ subquery.append(f"""
73
+ CREATE (new_node)<-[new_active_edge:{rel_type} $rel_props_new ]-(peer_node)
74
+ SET new_active_edge.branch = CASE WHEN {rel_name}.branch = "-global-" THEN "-global-" ELSE $branch END
75
+ SET new_active_edge.branch_level = CASE WHEN {rel_name}.branch = "-global-" THEN {rel_name}.branch_level ELSE $branch_level END
76
+ """)
77
+ subquery.append(f"""
78
+ CREATE (active_node)<-[deleted_edge:{rel_type} $rel_props_prev ]-(peer_node)
79
+ SET new_active_edge.branch = CASE WHEN {rel_name}.branch = "-global-" THEN "-global-" ELSE $branch END
80
+ SET new_active_edge.branch_level = CASE WHEN {rel_name}.branch = "-global-" THEN {rel_name}.branch_level ELSE $branch_level END
81
+ """)
64
82
  subquery.append("RETURN peer_node as p2")
65
83
  return "\n".join(subquery)
66
84
 
@@ -68,7 +86,9 @@ class NodeDuplicateQuery(Query):
68
86
  def _render_sub_query_out(cls) -> str:
69
87
  sub_queries_out = [
70
88
  cls._render_sub_query_per_rel_type(
71
- rel_name="rel_outband", rel_type=rel_type, rel_def=rel_def, direction=GraphRelDirection.OUTBOUND
89
+ rel_name="rel_outband",
90
+ rel_type=rel_type,
91
+ rel_def=rel_def,
72
92
  )
73
93
  for rel_type, rel_def in GraphNodeRelationships.model_fields.items()
74
94
  ]
@@ -79,14 +99,16 @@ class NodeDuplicateQuery(Query):
79
99
  def _render_sub_query_in(cls) -> str:
80
100
  sub_queries_in = [
81
101
  cls._render_sub_query_per_rel_type(
82
- rel_name="rel_inband", rel_type=rel_type, rel_def=rel_def, direction=GraphRelDirection.INBOUND
102
+ rel_name="rel_inband",
103
+ rel_type=rel_type,
104
+ rel_def=rel_def,
83
105
  )
84
106
  for rel_type, rel_def in GraphNodeRelationships.model_fields.items()
85
107
  ]
86
108
  sub_query_in = "\nUNION\n".join(sub_queries_in)
87
109
  return sub_query_in
88
110
 
89
- async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None:
111
+ async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None: # noqa: ARG002
90
112
  branch_filter, branch_params = self.branch.get_query_filter_path(at=self.at.to_string())
91
113
  self.params.update(branch_params)
92
114
 
@@ -94,19 +116,16 @@ class NodeDuplicateQuery(Query):
94
116
  self.params["previous_node"] = self.previous_node.model_dump()
95
117
 
96
118
  self.params["current_time"] = self.at.to_string()
97
- self.params["branch_name"] = self.branch.name
119
+ self.params["branch"] = self.branch.name
120
+ self.params["branch_level"] = self.branch.hierarchy_level
98
121
  self.params["branch_support"] = self.new_node.branch_support
99
122
 
100
123
  self.params["rel_props_new"] = {
101
- "branch": self.branch.name,
102
- "branch_level": self.branch.hierarchy_level,
103
124
  "status": RelationshipStatus.ACTIVE.value,
104
125
  "from": self.at.to_string(),
105
126
  }
106
127
 
107
128
  self.params["rel_props_prev"] = {
108
- "branch": self.branch.name,
109
- "branch_level": self.branch.hierarchy_level,
110
129
  "status": RelationshipStatus.DELETED.value,
111
130
  "from": self.at.to_string(),
112
131
  }
@@ -141,12 +160,12 @@ class NodeDuplicateQuery(Query):
141
160
  LIMIT 1
142
161
  }
143
162
  WITH n1 as active_node, rel_outband1 as rel_outband, p1 as peer_node, new_node
144
- WHERE rel_outband.status = "active"
163
+ WHERE rel_outband.status = "active" AND rel_outband.to IS NULL
145
164
  CALL {
146
165
  %(sub_query_out)s
147
166
  }
148
167
  WITH p2 as peer_node, rel_outband, active_node, new_node
149
- FOREACH (i in CASE WHEN rel_outband.branch = $branch_name THEN [1] ELSE [] END |
168
+ FOREACH (i in CASE WHEN rel_outband.branch IN ["-global-", $branch] THEN [1] ELSE [] END |
150
169
  SET rel_outband.to = $current_time
151
170
  )
152
171
  WITH active_node, new_node
@@ -160,14 +179,15 @@ class NodeDuplicateQuery(Query):
160
179
  LIMIT 1
161
180
  }
162
181
  WITH n1 as active_node, rel_inband1 as rel_inband, p1 as peer_node, new_node
163
- WHERE rel_inband.status = "active"
182
+ WHERE rel_inband.status = "active" AND rel_inband.to IS NULL
164
183
  CALL {
165
184
  %(sub_query_in)s
166
185
  }
167
186
  WITH p2 as peer_node, rel_inband, active_node, new_node
168
- FOREACH (i in CASE WHEN rel_inband.branch = $branch_name THEN [1] ELSE [] END |
187
+ FOREACH (i in CASE WHEN rel_inband.branch IN ["-global-", $branch] THEN [1] ELSE [] END |
169
188
  SET rel_inband.to = $current_time
170
189
  )
190
+
171
191
  RETURN DISTINCT new_node
172
192
  """ % {
173
193
  "branch_filter": branch_filter,
@@ -84,7 +84,7 @@ class RelationshipDuplicateQuery(Query):
84
84
  sub_query_in = "\nUNION\n".join(sub_queries_in)
85
85
  return sub_query_in
86
86
 
87
- async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None:
87
+ async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None: # noqa: ARG002
88
88
  branch_filter, branch_params = self.branch.get_query_filter_path(at=self.at.to_string())
89
89
  self.params.update(branch_params)
90
90
 
@@ -67,7 +67,7 @@ class SchemaAttributeUpdateQuery(Query):
67
67
 
68
68
  return query
69
69
 
70
- async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None:
70
+ async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None: # noqa: ARG002
71
71
  self.params["attr_name"] = self.attr_name
72
72
  self.params["attr_new_value"] = (
73
73
  ujson.dumps(self.attr_new_value) if isinstance(self.attr_new_value, list) else self.attr_new_value
@@ -18,7 +18,7 @@ class NodeAttributeRemoveMigrationQuery01(AttributeMigrationQuery):
18
18
  name = "migration_node_attribute_remove_01"
19
19
  insert_return: bool = False
20
20
 
21
- async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None:
21
+ async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None: # noqa: ARG002
22
22
  branch_filter, branch_params = self.branch.get_query_filter_path(at=self.at.to_string())
23
23
  self.params.update(branch_params)
24
24
 
@@ -15,33 +15,44 @@ if TYPE_CHECKING:
15
15
 
16
16
  class NodeRemoveMigrationBaseQuery(MigrationQuery):
17
17
  def render_sub_query_per_rel_type(
18
- self, rel_name: str, rel_type: str, rel_def: FieldInfo, direction: GraphRelDirection
18
+ self,
19
+ rel_name: str,
20
+ rel_type: str,
21
+ rel_def: FieldInfo,
19
22
  ) -> str:
20
23
  subquery = [
21
24
  f"WITH peer_node, {rel_name}, active_node",
22
25
  f"WITH peer_node, {rel_name}, active_node",
23
26
  f'WHERE type({rel_name}) = "{rel_type}"',
24
27
  ]
25
- if rel_def.default.direction in [direction, GraphRelDirection.EITHER]:
26
- subquery.append(f"CREATE (active_node)-[:{rel_type} $rel_props ]->(peer_node)")
27
- elif rel_def.default.direction in [direction, GraphRelDirection.EITHER]:
28
- subquery.append(f"CREATE (active_node)<-[:{rel_type} $rel_props ]-(peer_node)")
28
+ if rel_def.default.direction in [GraphRelDirection.OUTBOUND, GraphRelDirection.EITHER]:
29
+ subquery.append(f"""
30
+ CREATE (active_node)-[edge:{rel_type} $rel_props ]->(peer_node)
31
+ SET edge.branch = CASE WHEN {rel_name}.branch = "-global-" THEN "-global-" ELSE $branch END
32
+ SET edge.branch_level = CASE WHEN {rel_name}.branch = "-global-" THEN {rel_name}.branch_level ELSE $branch_level END
33
+ """)
34
+ elif rel_def.default.direction in [GraphRelDirection.INBOUND, GraphRelDirection.EITHER]:
35
+ subquery.append(f"""
36
+ CREATE (active_node)<-[edge:{rel_type} $rel_props ]-(peer_node)
37
+ SET edge.branch = CASE WHEN {rel_name}.branch = "-global-" THEN "-global-" ELSE $branch END
38
+ SET edge.branch_level = CASE WHEN {rel_name}.branch = "-global-" THEN {rel_name}.branch_level ELSE $branch_level END
39
+ """)
29
40
  subquery.append("RETURN peer_node as p2")
30
41
  return "\n".join(subquery)
31
42
 
32
43
  def render_node_remove_query(self, branch_filter: str) -> str:
33
44
  raise NotImplementedError()
34
45
 
35
- async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None:
46
+ async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None: # noqa: ARG002
36
47
  branch_filter, branch_params = self.branch.get_query_filter_path(at=self.at.to_string())
37
48
  self.params.update(branch_params)
38
49
 
39
50
  self.params["current_time"] = self.at.to_string()
40
51
  self.params["branch_name"] = self.branch.name
52
+ self.params["branch"] = self.branch.name
53
+ self.params["branch_level"] = self.branch.hierarchy_level
41
54
 
42
55
  self.params["rel_props"] = {
43
- "branch": self.branch.name,
44
- "branch_level": self.branch.hierarchy_level,
45
56
  "status": RelationshipStatus.DELETED.value,
46
57
  "from": self.at.to_string(),
47
58
  }
@@ -99,7 +110,7 @@ class NodeRemoveMigrationQueryIn(NodeRemoveMigrationBaseQuery):
99
110
  %(sub_query)s
100
111
  }
101
112
  WITH p2 as peer_node, rel_inband, active_node
102
- FOREACH (i in CASE WHEN rel_inband.branch = $branch_name THEN [1] ELSE [] END |
113
+ FOREACH (i in CASE WHEN rel_inband.branch IN ["-global-", $branch] THEN [1] ELSE [] END |
103
114
  SET rel_inband.to = $current_time
104
115
  )
105
116
  """ % {"sub_query": sub_query, "branch_filter": branch_filter}
@@ -108,7 +119,9 @@ class NodeRemoveMigrationQueryIn(NodeRemoveMigrationBaseQuery):
108
119
  def render_sub_query_in(self) -> str:
109
120
  sub_queries_in = [
110
121
  self.render_sub_query_per_rel_type(
111
- rel_name="rel_inband", rel_type=rel_type, rel_def=rel_def, direction=GraphRelDirection.INBOUND
122
+ rel_name="rel_inband",
123
+ rel_type=rel_type,
124
+ rel_def=rel_def,
112
125
  )
113
126
  for rel_type, rel_def in GraphNodeRelationships.model_fields.items()
114
127
  ]
@@ -142,8 +155,7 @@ class NodeRemoveMigrationQueryOut(NodeRemoveMigrationBaseQuery):
142
155
  CALL {
143
156
  %(sub_query)s
144
157
  }
145
- WITH p2 as peer_node, rel_outband, active_node
146
- FOREACH (i in CASE WHEN rel_outband.branch = $branch_name THEN [1] ELSE [] END |
158
+ FOREACH (i in CASE WHEN rel_outband.branch IN ["-global-", $branch] THEN [1] ELSE [] END |
147
159
  SET rel_outband.to = $current_time
148
160
  )
149
161
  """ % {"sub_query": sub_query, "branch_filter": branch_filter}
@@ -153,7 +165,9 @@ class NodeRemoveMigrationQueryOut(NodeRemoveMigrationBaseQuery):
153
165
  def render_sub_query_out(self) -> str:
154
166
  sub_queries_out = [
155
167
  self.render_sub_query_per_rel_type(
156
- rel_name="rel_outband", rel_type=rel_type, rel_def=rel_def, direction=GraphRelDirection.OUTBOUND
168
+ rel_name="rel_outband",
169
+ rel_type=rel_type,
170
+ rel_def=rel_def,
157
171
  )
158
172
  for rel_type, rel_def in GraphNodeRelationships.model_fields.items()
159
173
  ]
@@ -10,7 +10,7 @@ from prefect.logging import get_run_logger
10
10
  from infrahub.core.branch import Branch # noqa: TC001
11
11
  from infrahub.core.migrations import MIGRATION_MAP
12
12
  from infrahub.core.path import SchemaPath # noqa: TC001
13
- from infrahub.services import services
13
+ from infrahub.services import InfrahubServices # noqa: TC001 needed for prefect flow
14
14
  from infrahub.workflows.utils import add_branch_tag
15
15
 
16
16
  from .models import SchemaApplyMigrationData, SchemaMigrationPathResponseData
@@ -20,7 +20,7 @@ if TYPE_CHECKING:
20
20
 
21
21
 
22
22
  @flow(name="schema_apply_migrations", flow_run_name="Apply schema migrations", persist_result=True)
23
- async def schema_apply_migrations(message: SchemaApplyMigrationData) -> list[str]:
23
+ async def schema_apply_migrations(message: SchemaApplyMigrationData, service: InfrahubServices) -> list[str]:
24
24
  await add_branch_tag(branch_name=message.branch.name)
25
25
  log = get_run_logger()
26
26
 
@@ -34,7 +34,6 @@ async def schema_apply_migrations(message: SchemaApplyMigrationData) -> list[str
34
34
  log.info(f"Preparing migration for {migration.migration_name!r} ({migration.routing_key})")
35
35
 
36
36
  new_node_schema: Optional[MainSchemaTypes] = None
37
- previous_node_schema: Optional[MainSchemaTypes] = None
38
37
 
39
38
  if message.new_schema.has(name=migration.path.schema_kind):
40
39
  new_node_schema = message.new_schema.get(name=migration.path.schema_kind)
@@ -56,6 +55,7 @@ async def schema_apply_migrations(message: SchemaApplyMigrationData) -> list[str
56
55
  new_node_schema=new_node_schema,
57
56
  previous_node_schema=previous_node_schema,
58
57
  schema_path=migration.path,
58
+ service=service,
59
59
  )
60
60
 
61
61
  async for _, result in batch.execute():
@@ -64,7 +64,7 @@ async def schema_apply_migrations(message: SchemaApplyMigrationData) -> list[str
64
64
  return error_messages
65
65
 
66
66
 
67
- @task(
67
+ @task( # type: ignore[arg-type]
68
68
  name="schema-path-migrate",
69
69
  task_run_name="Migrate Schema Path {migration_name} on {branch.name}",
70
70
  description="Apply a given migration to the database",
@@ -75,10 +75,10 @@ async def schema_path_migrate(
75
75
  branch: Branch,
76
76
  migration_name: str,
77
77
  schema_path: SchemaPath,
78
+ service: InfrahubServices,
78
79
  new_node_schema: MainSchemaTypes | None = None,
79
80
  previous_node_schema: MainSchemaTypes | None = None,
80
81
  ) -> SchemaMigrationPathResponseData:
81
- service = services.service
82
82
  log = get_run_logger()
83
83
 
84
84
  async with service.database.start_session() as db: