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
@@ -7,6 +7,8 @@ from infrahub_sdk.utils import str_to_bool
7
7
 
8
8
  from infrahub.core.constants import DiffAction, RelationshipCardinality
9
9
  from infrahub.core.constants.database import DatabaseEdgeType
10
+ from infrahub.core.diff.model.path import ConflictSelection
11
+ from infrahub.exceptions import SchemaNotFoundError
10
12
 
11
13
  from .models import (
12
14
  AttributeChangelog,
@@ -21,9 +23,11 @@ if TYPE_CHECKING:
21
23
  from infrahub.core.diff.model.path import (
22
24
  EnrichedDiffAttribute,
23
25
  EnrichedDiffNode,
26
+ EnrichedDiffProperty,
24
27
  EnrichedDiffRelationship,
25
28
  EnrichedDiffRoot,
26
29
  )
30
+ from infrahub.core.models import SchemaUpdateMigrationInfo
27
31
  from infrahub.core.schema import MainSchemaTypes
28
32
  from infrahub.database import InfrahubDatabase
29
33
 
@@ -36,11 +40,18 @@ class NodeInDiff:
36
40
 
37
41
 
38
42
  class DiffChangelogCollector:
39
- def __init__(self, diff: EnrichedDiffRoot, branch: Branch, db: InfrahubDatabase) -> None:
43
+ def __init__(
44
+ self,
45
+ diff: EnrichedDiffRoot,
46
+ branch: Branch,
47
+ db: InfrahubDatabase,
48
+ migration_tracker: MigrationTracker | None = None,
49
+ ) -> None:
40
50
  self._diff = diff
41
51
  self._branch = branch
42
52
  self._db = db
43
53
  self._diff_nodes: dict[str, NodeInDiff]
54
+ self.migration = migration_tracker or MigrationTracker()
44
55
 
45
56
  def _populate_diff_nodes(self) -> None:
46
57
  self._diff_nodes = {
@@ -61,7 +72,11 @@ class DiffChangelogCollector:
61
72
 
62
73
  def _process_node(self, node: EnrichedDiffNode) -> NodeChangelog:
63
74
  node_changelog = NodeChangelog(node_id=node.uuid, node_kind=node.kind, display_label=node.label)
64
- schema = self._db.schema.get(node_changelog.node_kind, branch=self._branch, duplicate=False)
75
+ try:
76
+ schema = self._db.schema.get(node_changelog.node_kind, branch=self._branch, duplicate=False)
77
+ except SchemaNotFoundError:
78
+ # if the schema has been deleted on self._branch
79
+ schema = None
65
80
  for attribute in node.attributes:
66
81
  self._process_node_attribute(node=node_changelog, attribute=attribute, schema=schema)
67
82
 
@@ -71,49 +86,59 @@ class DiffChangelogCollector:
71
86
  return node_changelog
72
87
 
73
88
  def _process_node_attribute(
74
- self, node: NodeChangelog, attribute: EnrichedDiffAttribute, schema: MainSchemaTypes
89
+ self, node: NodeChangelog, attribute: EnrichedDiffAttribute, schema: MainSchemaTypes | None
75
90
  ) -> None:
76
- try:
77
- schema_attribute = schema.get_attribute(name=attribute.name)
78
- attribute_kind = schema_attribute.kind
79
- except ValueError:
80
- # This would currently happen if there has been a schema migration as part of the merge
81
- # then we don't have access to the attribute kind
91
+ if schema is None:
82
92
  attribute_kind = "n/a"
93
+ else:
94
+ try:
95
+ schema_attribute = schema.get_attribute(name=attribute.name)
96
+ attribute_kind = schema_attribute.kind
97
+ except ValueError:
98
+ # This would currently happen if there has been a schema migration as part of the merge
99
+ # then we don't have access to the attribute kind
100
+ attribute_kind = "n/a"
83
101
 
84
- changelog_attribute = AttributeChangelog(name=attribute.name, kind=attribute_kind)
102
+ changelog_attribute = AttributeChangelog(
103
+ name=self.migration.get_attribute_name(node=node, attribute=attribute), kind=attribute_kind
104
+ )
85
105
  for attr_property in attribute.properties:
86
106
  match attr_property.property_type:
87
107
  case DatabaseEdgeType.HAS_VALUE:
88
108
  # TODO deserialize correct value type from string
89
- changelog_attribute.value = attr_property.new_value
90
- changelog_attribute.value_previous = attr_property.previous_value
109
+ if _keep_branch_update(diff_property=attr_property):
110
+ changelog_attribute.set_value(value=attr_property.new_value)
111
+ changelog_attribute.set_value_previous(value=attr_property.previous_value)
91
112
  case DatabaseEdgeType.IS_PROTECTED:
92
- changelog_attribute.add_property(
93
- name="is_protected",
94
- value_current=self._convert_string_boolean_value(value=attr_property.new_value),
95
- value_previous=self._convert_string_boolean_value(value=attr_property.previous_value),
96
- )
113
+ if _keep_branch_update(diff_property=attr_property):
114
+ changelog_attribute.add_property(
115
+ name="is_protected",
116
+ value_current=self._convert_string_boolean_value(value=attr_property.new_value),
117
+ value_previous=self._convert_string_boolean_value(value=attr_property.previous_value),
118
+ )
97
119
  case DatabaseEdgeType.IS_VISIBLE:
98
- changelog_attribute.add_property(
99
- name="is_visible",
100
- value_current=self._convert_string_boolean_value(value=attr_property.new_value),
101
- value_previous=self._convert_string_boolean_value(value=attr_property.previous_value),
102
- )
120
+ if _keep_branch_update(diff_property=attr_property):
121
+ changelog_attribute.add_property(
122
+ name="is_visible",
123
+ value_current=self._convert_string_boolean_value(value=attr_property.new_value),
124
+ value_previous=self._convert_string_boolean_value(value=attr_property.previous_value),
125
+ )
103
126
  case DatabaseEdgeType.HAS_SOURCE:
104
- changelog_attribute.add_property(
105
- name="source",
106
- value_current=attr_property.new_value,
107
- value_previous=attr_property.previous_value,
108
- )
127
+ if _keep_branch_update(diff_property=attr_property):
128
+ changelog_attribute.add_property(
129
+ name="source",
130
+ value_current=attr_property.new_value,
131
+ value_previous=attr_property.previous_value,
132
+ )
109
133
  case DatabaseEdgeType.HAS_OWNER:
110
- changelog_attribute.add_property(
111
- name="owner",
112
- value_current=attr_property.new_value,
113
- value_previous=attr_property.previous_value,
114
- )
134
+ if _keep_branch_update(diff_property=attr_property):
135
+ changelog_attribute.add_property(
136
+ name="owner",
137
+ value_current=attr_property.new_value,
138
+ value_previous=attr_property.previous_value,
139
+ )
115
140
 
116
- node.attributes[attribute.name] = changelog_attribute
141
+ node.add_attribute(attribute=changelog_attribute)
117
142
 
118
143
  def _process_node_relationship(self, node: NodeChangelog, relationship: EnrichedDiffRelationship) -> None:
119
144
  match relationship.cardinality:
@@ -170,7 +195,7 @@ class DiffChangelogCollector:
170
195
  value_previous=rel_prop.previous_value,
171
196
  )
172
197
 
173
- node.add_relationship(relationship=changelog_rel)
198
+ node.add_relationship(relationship_changelog=changelog_rel)
174
199
 
175
200
  def _convert_string_boolean_value(self, value: str | None) -> bool | None:
176
201
  """Convert string based boolean for is_protected and is_visible."""
@@ -220,7 +245,7 @@ class DiffChangelogCollector:
220
245
 
221
246
  changelog_rel.peers.append(peer_log)
222
247
 
223
- node.add_relationship(relationship=changelog_rel)
248
+ node.add_relationship(relationship_changelog=changelog_rel)
224
249
 
225
250
  def collect_changelogs(self) -> Sequence[tuple[DiffAction, NodeChangelog]]:
226
251
  self._populate_diff_nodes()
@@ -229,4 +254,38 @@ class DiffChangelogCollector:
229
254
  for node in self._diff.nodes
230
255
  if node.action != DiffAction.UNCHANGED
231
256
  ]
232
- return changelogs
257
+ return [(action, node_changelog) for action, node_changelog in changelogs if node_changelog.has_changes]
258
+
259
+
260
+ def _keep_branch_update(diff_property: EnrichedDiffProperty) -> bool:
261
+ if diff_property.conflict and diff_property.conflict.selected_branch == ConflictSelection.BASE_BRANCH:
262
+ return False
263
+ return True
264
+
265
+
266
+ class MigrationTracker:
267
+ """Keeps track of schema updates that happened as part of a migration"""
268
+
269
+ def __init__(self, migrations: list[SchemaUpdateMigrationInfo] | None = None) -> None:
270
+ # A dictionary of Node kind, previous attribute name and new attribute
271
+ # {"TestPerson": {"old_attribute_name": "new_attribute_name"}}
272
+ self._migrations_attribute_map: dict[str, dict[str, str]] = {}
273
+
274
+ migrations = migrations or []
275
+ for migration in migrations:
276
+ if migration.migration_name == "attribute.name.update":
277
+ if migration.path.schema_kind not in self._migrations_attribute_map:
278
+ self._migrations_attribute_map[migration.path.schema_kind] = {}
279
+ if migration.path.property_name and migration.path.field_name:
280
+ self._migrations_attribute_map[migration.path.schema_kind][migration.path.property_name] = (
281
+ migration.path.field_name
282
+ )
283
+
284
+ def get_attribute_name(self, node: NodeChangelog, attribute: EnrichedDiffAttribute) -> str:
285
+ """Return the current name of the requested attribute"""
286
+ if node.node_kind not in self._migrations_attribute_map:
287
+ return attribute.name
288
+ if attribute.name not in self._migrations_attribute_map[node.node_kind]:
289
+ return attribute.name
290
+
291
+ return self._migrations_attribute_map[node.node_kind][attribute.name]
@@ -13,6 +13,8 @@ if TYPE_CHECKING:
13
13
  from infrahub.core.manager import RelationshipSchema
14
14
  from infrahub.core.query.relationship import RelationshipPeerData
15
15
  from infrahub.core.relationship.model import Relationship
16
+ from infrahub.core.schema import MainSchemaTypes
17
+ from infrahub.core.schema.schema_branch import SchemaBranch
16
18
  from infrahub.database import InfrahubDatabase
17
19
 
18
20
 
@@ -75,6 +77,18 @@ class AttributeChangelog(BaseModel):
75
77
  return True
76
78
  return False
77
79
 
80
+ def set_value(self, value: Any) -> None:
81
+ if isinstance(value, str) and value == NULL_VALUE:
82
+ self.value = None
83
+ return
84
+ self.value = value
85
+
86
+ def set_value_previous(self, value: Any) -> None:
87
+ if isinstance(value, str) and value == NULL_VALUE:
88
+ self.value_previous = None
89
+ return
90
+ self.value_previous = value
91
+
78
92
  @field_validator("value", "value_previous")
79
93
  @classmethod
80
94
  def convert_null_values(cls, value: Any) -> Any:
@@ -129,8 +143,8 @@ class RelationshipCardinalityOneChangelog(BaseModel):
129
143
  def set_parent(self, parent_id: str, parent_kind: str) -> None:
130
144
  self._parent = ChangelogRelatedNode(node_id=parent_id, node_kind=parent_kind)
131
145
 
132
- def set_parent_from_relationship(self, relationship: Relationship) -> None:
133
- if relationship.schema.kind == RelationshipKind.PARENT:
146
+ def set_parent_from_relationship(self, rel_kind: RelationshipKind) -> None:
147
+ if rel_kind == RelationshipKind.PARENT:
134
148
  if (
135
149
  self.peer_status in [DiffAction.ADDED, DiffAction.UNCHANGED, DiffAction.UPDATED]
136
150
  and self.peer_id
@@ -304,17 +318,18 @@ class NodeChangelog(BaseModel):
304
318
  )
305
319
 
306
320
  def add_attribute(self, attribute: AttributeChangelog) -> None:
307
- self.attributes[attribute.name] = attribute
321
+ if attribute.has_updates:
322
+ self.attributes[attribute.name] = attribute
308
323
 
309
324
  def add_relationship(
310
- self, relationship: RelationshipCardinalityOneChangelog | RelationshipCardinalityManyChangelog
325
+ self, relationship_changelog: RelationshipCardinalityOneChangelog | RelationshipCardinalityManyChangelog
311
326
  ) -> None:
312
- if isinstance(relationship, RelationshipCardinalityOneChangelog) and relationship.parent:
313
- self.add_parent(parent=relationship.parent)
314
- if relationship.is_empty:
327
+ if isinstance(relationship_changelog, RelationshipCardinalityOneChangelog) and relationship_changelog.parent:
328
+ self.add_parent(parent=relationship_changelog.parent)
329
+ if relationship_changelog.is_empty:
315
330
  return
316
331
 
317
- self.relationships[relationship.name] = relationship
332
+ self.relationships[relationship_changelog.name] = relationship_changelog
318
333
 
319
334
  def create_attribute(self, attribute: BaseAttribute) -> None:
320
335
  changelog_attribute = AttributeChangelog(
@@ -382,7 +397,7 @@ class ChangelogRelationshipMapper:
382
397
  def _set_cardinality_one_peer(self, relationship: Relationship) -> None:
383
398
  self.cardinality_one_relationship.peer_id = relationship.peer_id
384
399
  self.cardinality_one_relationship.peer_kind = relationship.get_peer_kind()
385
- self.cardinality_one_relationship.set_parent_from_relationship(relationship=relationship)
400
+ self.cardinality_one_relationship.set_parent_from_relationship(rel_kind=relationship.schema.kind)
386
401
 
387
402
  def add_parent_from_relationship(self, relationship: Relationship) -> None:
388
403
  if self.schema.cardinality == RelationshipCardinality.ONE:
@@ -419,18 +434,16 @@ class ChangelogRelationshipMapper:
419
434
  value_current=getattr(relationship, property_name),
420
435
  value_previous=previous_value,
421
436
  )
422
- self.cardinality_one_relationship.set_parent_from_relationship(relationship=relationship)
437
+ self.cardinality_one_relationship.set_parent_from_relationship(rel_kind=relationship.schema.kind)
423
438
 
424
- def delete_relationship(self, relationship: Relationship) -> None:
439
+ def delete_relationship(self, peer_id: str, peer_kind: str, rel_schema: RelationshipSchema) -> None:
425
440
  if self.schema.cardinality == RelationshipCardinality.ONE:
426
- self.cardinality_one_relationship.peer_id_previous = relationship.get_peer_id()
427
- self.cardinality_one_relationship.peer_kind_previous = relationship.get_peer_kind()
428
- self.cardinality_one_relationship.set_parent_from_relationship(relationship=relationship)
441
+ self.cardinality_one_relationship.peer_id_previous = peer_id
442
+ self.cardinality_one_relationship.peer_kind_previous = peer_kind
443
+ self.cardinality_one_relationship.set_parent_from_relationship(rel_kind=rel_schema.kind)
429
444
 
430
445
  elif self.schema.cardinality == RelationshipCardinality.MANY:
431
- self.cardinality_many_relationship.remove_peer(
432
- peer_id=relationship.get_peer_id(), peer_kind=relationship.get_peer_kind()
433
- )
446
+ self.cardinality_many_relationship.remove_peer(peer_id=peer_id, peer_kind=peer_kind)
434
447
 
435
448
  @property
436
449
  def changelog(self) -> RelationshipCardinalityOneChangelog | RelationshipCardinalityManyChangelog:
@@ -452,37 +465,198 @@ class RelationshipChangelogGetter:
452
465
  These will typically include updates to relationships on other nodes.
453
466
  """
454
467
  schema_branch = self._db.schema.get_schema_branch(name=self._branch.name)
455
- node_schema = schema_branch.get(name=primary_changelog.node_kind)
468
+ node_schema = schema_branch.get(name=primary_changelog.node_kind, duplicate=False)
456
469
  secondaries: list[NodeChangelog] = []
457
470
 
458
471
  for relationship in primary_changelog.relationships.values():
459
- rel_schema = node_schema.get_relationship(name=relationship.name)
460
472
  if isinstance(relationship, RelationshipCardinalityOneChangelog):
461
- # For now this code only looks at the scenario when a cardinality=one relationship
462
- # is added to a node and it has a cardinality=many relationship coming back from
463
- # another node, it will be expanded to include all variations.
464
- if relationship.peer_status == DiffAction.ADDED:
465
- peer_schema = schema_branch.get(name=str(relationship.peer_kind))
466
- peer_relation = peer_schema.get_relationship_by_identifier(
467
- id=str(rel_schema.identifier), raise_on_error=False
473
+ secondaries.extend(
474
+ self._parse_cardinality_one_relationship(
475
+ relationship=relationship,
476
+ node_schema=node_schema,
477
+ primary_changelog=primary_changelog,
478
+ schema_branch=schema_branch,
468
479
  )
469
- if peer_relation:
470
- node_changelog = NodeChangelog(
471
- node_id=str(relationship.peer_id),
472
- node_kind=str(relationship.peer_kind),
473
- display_label="n/a",
480
+ )
481
+ elif isinstance(relationship, RelationshipCardinalityManyChangelog):
482
+ secondaries.extend(
483
+ self._parse_cardinality_many_relationship(
484
+ relationship=relationship,
485
+ node_schema=node_schema,
486
+ primary_changelog=primary_changelog,
487
+ schema_branch=schema_branch,
488
+ )
489
+ )
490
+
491
+ return secondaries
492
+
493
+ def _parse_cardinality_one_relationship(
494
+ self,
495
+ relationship: RelationshipCardinalityOneChangelog,
496
+ node_schema: MainSchemaTypes,
497
+ primary_changelog: NodeChangelog,
498
+ schema_branch: SchemaBranch,
499
+ ) -> list[NodeChangelog]:
500
+ secondaries: list[NodeChangelog] = []
501
+ rel_schema = node_schema.get_relationship(name=relationship.name)
502
+
503
+ if relationship.peer_status == DiffAction.ADDED:
504
+ peer_schema = schema_branch.get(name=str(relationship.peer_kind), duplicate=False)
505
+ secondaries.extend(
506
+ self._process_added_peers(
507
+ peer_id=str(relationship.peer_id),
508
+ peer_kind=str(relationship.peer_kind),
509
+ peer_schema=peer_schema,
510
+ rel_schema=rel_schema,
511
+ primary_changelog=primary_changelog,
512
+ )
513
+ )
514
+
515
+ elif relationship.peer_status == DiffAction.UPDATED:
516
+ peer_schema = schema_branch.get(name=str(relationship.peer_kind), duplicate=False)
517
+ secondaries.extend(
518
+ self._process_added_peers(
519
+ peer_id=str(relationship.peer_id),
520
+ peer_kind=str(relationship.peer_kind),
521
+ peer_schema=peer_schema,
522
+ rel_schema=rel_schema,
523
+ primary_changelog=primary_changelog,
524
+ )
525
+ )
526
+ secondaries.extend(
527
+ self._process_removed_peers(
528
+ peer_schema=peer_schema,
529
+ peer_id=str(relationship.peer_id_previous),
530
+ peer_kind=str(relationship.peer_kind_previous),
531
+ rel_schema=rel_schema,
532
+ primary_changelog=primary_changelog,
533
+ )
534
+ )
535
+
536
+ elif relationship.peer_status == DiffAction.REMOVED:
537
+ peer_schema = schema_branch.get(name=str(relationship.peer_kind_previous), duplicate=False)
538
+
539
+ secondaries.extend(
540
+ self._process_removed_peers(
541
+ peer_id=str(relationship.peer_id_previous),
542
+ peer_kind=str(relationship.peer_kind_previous),
543
+ peer_schema=peer_schema,
544
+ rel_schema=rel_schema,
545
+ primary_changelog=primary_changelog,
546
+ )
547
+ )
548
+
549
+ return secondaries
550
+
551
+ def _parse_cardinality_many_relationship(
552
+ self,
553
+ relationship: RelationshipCardinalityManyChangelog,
554
+ node_schema: MainSchemaTypes,
555
+ primary_changelog: NodeChangelog,
556
+ schema_branch: SchemaBranch,
557
+ ) -> list[NodeChangelog]:
558
+ secondaries: list[NodeChangelog] = []
559
+ rel_schema = node_schema.get_relationship(name=relationship.name)
560
+
561
+ for peer in relationship.peers:
562
+ if peer.peer_status == DiffAction.ADDED:
563
+ peer_schema = schema_branch.get(name=peer.peer_kind)
564
+ secondaries.extend(
565
+ self._process_added_peers(
566
+ peer_id=peer.peer_id,
567
+ peer_kind=peer.peer_kind,
568
+ peer_schema=peer_schema,
569
+ rel_schema=rel_schema,
570
+ primary_changelog=primary_changelog,
571
+ )
572
+ )
573
+
574
+ elif peer.peer_status == DiffAction.REMOVED:
575
+ peer_schema = schema_branch.get(name=peer.peer_kind)
576
+ secondaries.extend(
577
+ self._process_removed_peers(
578
+ peer_id=peer.peer_id,
579
+ peer_kind=peer.peer_kind,
580
+ peer_schema=peer_schema,
581
+ rel_schema=rel_schema,
582
+ primary_changelog=primary_changelog,
583
+ )
584
+ )
585
+
586
+ return secondaries
587
+
588
+ def _process_added_peers(
589
+ self,
590
+ peer_id: str,
591
+ peer_kind: str,
592
+ peer_schema: MainSchemaTypes,
593
+ rel_schema: RelationshipSchema,
594
+ primary_changelog: NodeChangelog,
595
+ ) -> list[NodeChangelog]:
596
+ secondaries: list[NodeChangelog] = []
597
+ peer_relation = peer_schema.get_relationship_by_identifier(id=str(rel_schema.identifier), raise_on_error=False)
598
+ if peer_relation:
599
+ node_changelog = NodeChangelog(
600
+ node_id=peer_id,
601
+ node_kind=peer_kind,
602
+ display_label="n/a",
603
+ )
604
+ if peer_relation.cardinality == RelationshipCardinality.ONE:
605
+ node_changelog.relationships[peer_relation.name] = RelationshipCardinalityOneChangelog(
606
+ name=peer_relation.name,
607
+ peer_id=primary_changelog.node_id,
608
+ peer_kind=primary_changelog.node_kind,
609
+ )
610
+ secondaries.append(node_changelog)
611
+ elif peer_relation.cardinality == RelationshipCardinality.MANY:
612
+ node_changelog.relationships[peer_relation.name] = RelationshipCardinalityManyChangelog(
613
+ name=peer_relation.name,
614
+ peers=[
615
+ RelationshipPeerChangelog(
616
+ peer_id=primary_changelog.node_id,
617
+ peer_kind=primary_changelog.node_kind,
618
+ peer_status=DiffAction.ADDED,
474
619
  )
475
- if peer_relation.cardinality == RelationshipCardinality.MANY:
476
- node_changelog.relationships[peer_relation.name] = RelationshipCardinalityManyChangelog(
477
- name=peer_relation.name,
478
- peers=[
479
- RelationshipPeerChangelog(
480
- peer_id=primary_changelog.node_id,
481
- peer_kind=primary_changelog.node_kind,
482
- peer_status=DiffAction.ADDED,
483
- )
484
- ],
485
- )
486
- secondaries.append(node_changelog)
620
+ ],
621
+ )
622
+ secondaries.append(node_changelog)
623
+
624
+ return secondaries
625
+
626
+ def _process_removed_peers(
627
+ self,
628
+ peer_id: str,
629
+ peer_kind: str,
630
+ peer_schema: MainSchemaTypes,
631
+ rel_schema: RelationshipSchema,
632
+ primary_changelog: NodeChangelog,
633
+ ) -> list[NodeChangelog]:
634
+ secondaries: list[NodeChangelog] = []
635
+ peer_relation = peer_schema.get_relationship_by_identifier(id=str(rel_schema.identifier), raise_on_error=False)
636
+ if peer_relation:
637
+ node_changelog = NodeChangelog(
638
+ node_id=peer_id,
639
+ node_kind=peer_kind,
640
+ display_label="n/a",
641
+ )
642
+ if peer_relation.cardinality == RelationshipCardinality.ONE:
643
+ node_changelog.relationships[peer_relation.name] = RelationshipCardinalityOneChangelog(
644
+ name=peer_relation.name,
645
+ peer_id_previous=primary_changelog.node_id,
646
+ peer_kind_previous=primary_changelog.node_kind,
647
+ )
648
+ secondaries.append(node_changelog)
649
+ elif peer_relation.cardinality == RelationshipCardinality.MANY:
650
+ node_changelog.relationships[peer_relation.name] = RelationshipCardinalityManyChangelog(
651
+ name=peer_relation.name,
652
+ peers=[
653
+ RelationshipPeerChangelog(
654
+ peer_id=primary_changelog.node_id,
655
+ peer_kind=primary_changelog.node_kind,
656
+ peer_status=DiffAction.REMOVED,
657
+ )
658
+ ],
659
+ )
660
+ secondaries.append(node_changelog)
487
661
 
488
662
  return secondaries
@@ -43,6 +43,33 @@ RESERVED_ATTR_GEN_NAMES = ["type"]
43
43
 
44
44
  NULL_VALUE = "NULL"
45
45
 
46
+ EVENT_NAMESPACE = "infrahub"
47
+
48
+
49
+ class EventType(InfrahubStringEnum):
50
+ BRANCH_CREATED = f"{EVENT_NAMESPACE}.branch.created"
51
+ BRANCH_DELETED = f"{EVENT_NAMESPACE}.branch.deleted"
52
+ BRANCH_MERGED = f"{EVENT_NAMESPACE}.branch.merged"
53
+ BRANCH_REBASED = f"{EVENT_NAMESPACE}.branch.rebased"
54
+
55
+ SCHEMA_UPDATED = f"{EVENT_NAMESPACE}.schema.updated"
56
+
57
+ NODE_CREATED = f"{EVENT_NAMESPACE}.node.created"
58
+ NODE_UPDATED = f"{EVENT_NAMESPACE}.node.updated"
59
+ NODE_DELETED = f"{EVENT_NAMESPACE}.node.deleted"
60
+
61
+ GROUP_MEMBER_ADDED = f"{EVENT_NAMESPACE}.group.member_added"
62
+ GROUP_MEMBER_REMOVED = f"{EVENT_NAMESPACE}.group.member_removed"
63
+
64
+ REPOSITORY_UPDATE_COMMIT = f"{EVENT_NAMESPACE}.repository.update_commit"
65
+
66
+ ARTIFACT_CREATED = f"{EVENT_NAMESPACE}.artifact.created"
67
+ ARTIFACT_UPDATED = f"{EVENT_NAMESPACE}.artifact.updated"
68
+
69
+ VALIDATOR_STARTED = f"{EVENT_NAMESPACE}.validator.started"
70
+ VALIDATOR_PASSED = f"{EVENT_NAMESPACE}.validator.passed"
71
+ VALIDATOR_FAILED = f"{EVENT_NAMESPACE}.validator.failed"
72
+
46
73
 
47
74
  class PermissionLevel(enum.Flag):
48
75
  READ = 1
@@ -60,6 +87,7 @@ class GlobalPermissions(InfrahubStringEnum):
60
87
  MANAGE_ACCOUNTS = "manage_accounts"
61
88
  MANAGE_PERMISSIONS = "manage_permissions"
62
89
  MANAGE_REPOSITORIES = "manage_repositories"
90
+ OVERRIDE_CONTEXT = "override_context"
63
91
 
64
92
 
65
93
  class PermissionAction(InfrahubStringEnum):
@@ -11,6 +11,7 @@ ARTIFACTVALIDATOR = "CoreArtifactValidator"
11
11
  BASEPERMISSION = "CoreBasePermission"
12
12
  CHANGECOMMENT = "CoreChangeComment"
13
13
  CHANGETHREAD = "CoreChangeThread"
14
+ CHECK = "CoreCheck"
14
15
  CHECKDEFINITION = "CoreCheckDefinition"
15
16
  COMMENT = "CoreComment"
16
17
  CUSTOMWEBHOOK = "CoreCustomWebhook"
@@ -41,6 +42,7 @@ NODE = "CoreNode"
41
42
  NUMBERPOOL = "CoreNumberPool"
42
43
  LINEAGEOWNER = "LineageOwner"
43
44
  LINEAGESOURCE = "LineageSource"
45
+ OBJECTCOMPONENTTEMPLATE = "CoreObjectComponentTemplate"
44
46
  OBJECTPERMISSION = "CoreObjectPermission"
45
47
  OBJECTTEMPLATE = "CoreObjectTemplate"
46
48
  OBJECTTHREAD = "CoreObjectThread"
@@ -1,5 +1,7 @@
1
1
  from enum import Enum, Flag, auto
2
2
 
3
+ PARENT_CHILD_IDENTIFIER = "parent__child"
4
+
3
5
 
4
6
  class FlagProperty(Enum):
5
7
  IS_VISIBLE = "is_visible"
@@ -1,8 +1,8 @@
1
- from typing import TYPE_CHECKING, Optional
1
+ from typing import TYPE_CHECKING
2
2
 
3
3
  from infrahub.core.branch import Branch
4
4
  from infrahub.core.node import Node
5
- from infrahub.core.node.constraints.interface import NodeConstraintInterface
5
+ from infrahub.core.node.constraints.grouped_uniqueness import NodeGroupedUniquenessConstraint
6
6
  from infrahub.core.relationship.constraints.interface import RelationshipManagerConstraintInterface
7
7
  from infrahub.database import InfrahubDatabase
8
8
 
@@ -15,21 +15,18 @@ class NodeConstraintRunner:
15
15
  self,
16
16
  db: InfrahubDatabase,
17
17
  branch: Branch,
18
- node_constraints: list[NodeConstraintInterface],
18
+ uniqueness_constraint: NodeGroupedUniquenessConstraint,
19
19
  relationship_manager_constraints: list[RelationshipManagerConstraintInterface],
20
20
  ) -> None:
21
21
  self.db = db
22
22
  self.branch = branch
23
- self.node_constraints = node_constraints
23
+ self.uniqueness_constraint = uniqueness_constraint
24
24
  self.relationship_manager_constraints = relationship_manager_constraints
25
25
 
26
- async def check(self, node: Node, field_filters: Optional[list[str]] = None) -> None:
26
+ async def check(self, node: Node, field_filters: list[str] | None = None) -> None:
27
27
  async with self.db.start_session() as db:
28
28
  await node.resolve_relationships(db=db)
29
29
 
30
- for node_constraint in self.node_constraints:
31
- await node_constraint.check(node, filters=field_filters)
32
-
33
30
  for relationship_name in node.get_schema().relationship_names:
34
31
  if field_filters and relationship_name not in field_filters:
35
32
  continue
@@ -37,3 +34,7 @@ class NodeConstraintRunner:
37
34
  await relationship_manager.fetch_relationship_ids(db=db, force_refresh=True)
38
35
  for relationship_constraint in self.relationship_manager_constraints:
39
36
  await relationship_constraint.check(relm=relationship_manager, node_schema=node.get_schema())
37
+
38
+ # If HFID constraint is the only constraint violated, all other constraints need to have ran before,
39
+ # as it means there is an existing node that we might want to update in the case of an upsert
40
+ await self.uniqueness_constraint.check(node, filters=field_filters)