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
@@ -58,7 +58,7 @@ class SchemaMigration(BaseModel):
58
58
  query = await migration_query.init(db=ts, branch=branch, at=at, migration=self)
59
59
  await query.execute(db=ts)
60
60
  result.nbr_migrations_executed += query.get_nbr_migrations_executed()
61
- except Exception as exc: # pylint: disable=broad-exception-caught
61
+ except Exception as exc:
62
62
  result.errors.append(str(exc))
63
63
  return result
64
64
 
@@ -126,7 +126,7 @@ class GraphMigration(BaseModel):
126
126
  try:
127
127
  query = await migration_query.init(db=ts)
128
128
  await query.execute(db=ts)
129
- except Exception as exc: # pylint: disable=broad-exception-caught
129
+ except Exception as exc:
130
130
  result.errors.append(str(exc))
131
131
  return result
132
132
 
@@ -141,7 +141,7 @@ class InternalSchemaMigration(BaseModel):
141
141
 
142
142
  @staticmethod
143
143
  def get_internal_schema() -> SchemaBranch:
144
- from infrahub.core.schema.schema_branch import SchemaBranch # pylint: disable=import-outside-toplevel
144
+ from infrahub.core.schema.schema_branch import SchemaBranch
145
145
 
146
146
  # load the internal schema from
147
147
  schema = SchemaRoot(**internal_schema)
@@ -167,7 +167,7 @@ class InternalSchemaMigration(BaseModel):
167
167
  try:
168
168
  execution_result = await migration.execute(db=db, branch=default_branch)
169
169
  result.errors.extend(execution_result.errors)
170
- except Exception as exc: # pylint: disable=broad-exception-caught
170
+ except Exception as exc:
171
171
  result.errors.append(str(exc))
172
172
  return result
173
173
 
infrahub/core/models.py CHANGED
@@ -105,7 +105,6 @@ class SchemaDiff(BaseModel):
105
105
 
106
106
  indent_str = " " * indentation
107
107
 
108
- # pylint: disable=too-many-nested-blocks
109
108
  for node_action, node_info in data.items():
110
109
  for node_name, elements in node_info.items():
111
110
  print(f"{str(node_name).ljust(column_size)} | {str(node_action).title()}")
@@ -152,7 +151,7 @@ class SchemaUpdateConstraintInfo(BaseModel):
152
151
  return "schema.validator.path"
153
152
 
154
153
  def __hash__(self) -> int:
155
- return hash((type(self),) + tuple([self.constraint_name + self.path.get_path()]))
154
+ return hash((type(self),) + tuple(self.constraint_name + self.path.get_path()))
156
155
 
157
156
 
158
157
  class SchemaUpdateValidationResult(BaseModel):
@@ -170,7 +169,7 @@ class SchemaUpdateValidationResult(BaseModel):
170
169
  return obj
171
170
 
172
171
  def process_diff(self, schema: SchemaBranch) -> None:
173
- for schema_name, schema_diff in self.diff.removed.items():
172
+ for schema_name in self.diff.removed.keys():
174
173
  self.migrations.append(
175
174
  SchemaUpdateMigrationInfo(
176
175
  path=SchemaPath( # type: ignore[call-arg]
@@ -331,7 +330,7 @@ class SchemaUpdateValidationResult(BaseModel):
331
330
 
332
331
  def add_validator_for_migration(self, validator_map: dict[str, Any]) -> None:
333
332
  for migration in self.migrations:
334
- if validator_map.get(migration.migration_name, None):
333
+ if validator_map.get(migration.migration_name):
335
334
  self.constraints.append(
336
335
  SchemaUpdateConstraintInfo(
337
336
  path=migration.path,
@@ -384,7 +383,7 @@ class HashableModel(BaseModel):
384
383
  md5hash.update(item)
385
384
 
386
385
  if display_values:
387
- from rich import print as rprint # pylint: disable=import-outside-toplevel
386
+ from rich import print as rprint
388
387
 
389
388
  rprint(tuple(values))
390
389
 
@@ -494,8 +493,8 @@ class HashableModel(BaseModel):
494
493
  raise ValueError(f"Unable to merge the list for {field_name}, some items have the same _sorting_id")
495
494
 
496
495
  shared_ids = intersection(list(local_sub_items.keys()), list(other_sub_items.keys()))
497
- local_only_ids = set(list(local_sub_items.keys())) - set(shared_ids)
498
- other_only_ids = set(list(other_sub_items.keys())) - set(shared_ids)
496
+ local_only_ids = set(local_sub_items.keys()) - set(shared_ids)
497
+ other_only_ids = set(other_sub_items.keys()) - set(shared_ids)
499
498
 
500
499
  new_list = [value for key, value in local_sub_items.items() if key in local_only_ids]
501
500
  new_list.extend(
@@ -536,7 +535,7 @@ class HashableModel(BaseModel):
536
535
  if attr_other is None or attr_local == attr_other:
537
536
  continue
538
537
 
539
- if attr_local is None or isinstance(attr_other, (int, str, bool, float)):
538
+ if attr_local is None or isinstance(attr_other, int | str | bool | float):
540
539
  setattr(self, field_name, attr_other)
541
540
  continue
542
541
 
@@ -1,17 +1,27 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from enum import Enum
4
- from typing import TYPE_CHECKING, Any, Optional, TypeVar, Union, overload
4
+ from typing import TYPE_CHECKING, Any, Optional, Sequence, TypeVar, Union, overload
5
5
 
6
6
  from infrahub_sdk.utils import is_valid_uuid
7
7
  from infrahub_sdk.uuidt import UUIDT
8
8
 
9
9
  from infrahub.core import registry
10
- from infrahub.core.constants import BranchSupportType, ComputedAttributeKind, InfrahubKind, RelationshipCardinality
10
+ from infrahub.core.changelog.models import NodeChangelog
11
+ from infrahub.core.constants import (
12
+ GLOBAL_BRANCH_NAME,
13
+ OBJECT_TEMPLATE_NAME_ATTR,
14
+ OBJECT_TEMPLATE_RELATIONSHIP_NAME,
15
+ BranchSupportType,
16
+ ComputedAttributeKind,
17
+ InfrahubKind,
18
+ RelationshipCardinality,
19
+ RelationshipKind,
20
+ )
11
21
  from infrahub.core.constants.schema import SchemaElementPathType
12
- from infrahub.core.protocols import CoreNumberPool
22
+ from infrahub.core.protocols import CoreNumberPool, CoreObjectTemplate
13
23
  from infrahub.core.query.node import NodeCheckIDQuery, NodeCreateAllQuery, NodeDeleteQuery, NodeGetListQuery
14
- from infrahub.core.schema import AttributeSchema, NodeSchema, ProfileSchema, RelationshipSchema
24
+ from infrahub.core.schema import AttributeSchema, NodeSchema, ProfileSchema, RelationshipSchema, TemplateSchema
15
25
  from infrahub.core.timestamp import Timestamp
16
26
  from infrahub.exceptions import InitializationError, NodeNotFoundError, PoolExhaustedError, ValidationError
17
27
  from infrahub.support.macro import MacroDefinition
@@ -19,6 +29,7 @@ from infrahub.types import ATTRIBUTE_TYPES
19
29
 
20
30
  from ...graphql.constants import KIND_GRAPHQL_FIELD_NAME
21
31
  from ...graphql.models import OrderModel
32
+ from ..query.relationship import RelationshipDeleteAllQuery
22
33
  from ..relationship import RelationshipManager
23
34
  from ..utils import update_relationships_to
24
35
  from .base import BaseNode, BaseNodeMeta, BaseNodeOptions
@@ -42,21 +53,17 @@ SchemaProtocol = TypeVar("SchemaProtocol")
42
53
  # -
43
54
  # ---------------------------------------------------------------------------------------
44
55
 
45
- # pylint: disable=redefined-builtin,too-many-branches
46
-
47
56
 
48
57
  class Node(BaseNode, metaclass=BaseNodeMeta):
49
58
  @classmethod
50
- def __init_subclass_with_meta__( # pylint: disable=arguments-differ
51
- cls, _meta=None, default_filter=None, **options
52
- ) -> None:
59
+ def __init_subclass_with_meta__(cls, _meta=None, default_filter=None, **options) -> None:
53
60
  if not _meta:
54
61
  _meta = BaseNodeOptions(cls)
55
62
 
56
63
  _meta.default_filter = default_filter
57
64
  super().__init_subclass_with_meta__(_meta=_meta, **options)
58
65
 
59
- def get_schema(self) -> Union[NodeSchema, ProfileSchema]:
66
+ def get_schema(self) -> Union[NodeSchema, ProfileSchema, TemplateSchema]:
60
67
  return self._schema
61
68
 
62
69
  def get_kind(self) -> str:
@@ -130,7 +137,7 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
130
137
  labels.append(InfrahubKind.NODE)
131
138
  return labels
132
139
 
133
- if isinstance(self._schema, ProfileSchema):
140
+ if isinstance(self._schema, ProfileSchema | TemplateSchema):
134
141
  labels = [self.get_kind()] + self._schema.inherit_from
135
142
  return labels
136
143
 
@@ -153,8 +160,8 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
153
160
 
154
161
  return f"{self.get_kind()}(ID: {str(self.id)})"
155
162
 
156
- def __init__(self, schema: Union[NodeSchema, ProfileSchema], branch: Branch, at: Timestamp):
157
- self._schema: Union[NodeSchema, ProfileSchema] = schema
163
+ def __init__(self, schema: Union[NodeSchema, ProfileSchema, TemplateSchema], branch: Branch, at: Timestamp):
164
+ self._schema: Union[NodeSchema, ProfileSchema, TemplateSchema] = schema
158
165
  self._branch: Branch = branch
159
166
  self._at: Timestamp = at
160
167
  self._existing: bool = False
@@ -171,12 +178,20 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
171
178
  # Lists of attributes and relationships names
172
179
  self._attributes: list[str] = []
173
180
  self._relationships: list[str] = []
181
+ self._node_changelog: NodeChangelog | None = None
182
+
183
+ @property
184
+ def node_changelog(self) -> NodeChangelog:
185
+ if self._node_changelog:
186
+ return self._node_changelog
187
+
188
+ raise InitializationError("The node has not been saved so no changelog exists")
174
189
 
175
190
  @overload
176
191
  @classmethod
177
192
  async def init(
178
193
  cls,
179
- schema: Union[NodeSchema, ProfileSchema, str],
194
+ schema: Union[NodeSchema, ProfileSchema, TemplateSchema, str],
180
195
  db: InfrahubDatabase,
181
196
  branch: Optional[Union[Branch, str]] = ...,
182
197
  at: Optional[Union[Timestamp, str]] = ...,
@@ -195,7 +210,7 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
195
210
  @classmethod
196
211
  async def init(
197
212
  cls,
198
- schema: Union[NodeSchema, ProfileSchema, str, type[SchemaProtocol]],
213
+ schema: Union[NodeSchema, ProfileSchema, TemplateSchema, str, type[SchemaProtocol]],
199
214
  db: InfrahubDatabase,
200
215
  branch: Optional[Union[Branch, str]] = None,
201
216
  at: Optional[Union[Timestamp, str]] = None,
@@ -204,15 +219,17 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
204
219
 
205
220
  branch = await registry.get_branch(branch=branch, db=db)
206
221
 
207
- if isinstance(schema, (NodeSchema, ProfileSchema)):
222
+ if isinstance(schema, NodeSchema | ProfileSchema | TemplateSchema):
208
223
  attrs["schema"] = schema
209
224
  elif isinstance(schema, str):
210
225
  # TODO need to raise a proper exception for this, right now it will raise a generic ValueError
211
226
  attrs["schema"] = db.schema.get(name=schema, branch=branch)
212
- elif hasattr(schema, "_is_runtime_protocol") and getattr(schema, "_is_runtime_protocol"):
227
+ elif hasattr(schema, "_is_runtime_protocol") and schema._is_runtime_protocol:
213
228
  attrs["schema"] = db.schema.get(name=schema.__name__, branch=branch)
214
229
  else:
215
- raise ValueError(f"Invalid schema provided {type(schema)}, expected NodeSchema or ProfileSchema")
230
+ raise ValueError(
231
+ f"Invalid schema provided {type(schema)}, expected NodeSchema, ProfileSchema or TemplateSchema"
232
+ )
216
233
 
217
234
  attrs["branch"] = branch
218
235
  attrs["at"] = Timestamp(at)
@@ -261,6 +278,56 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
261
278
  )
262
279
  )
263
280
 
281
+ async def handle_object_template(self, fields: dict, db: InfrahubDatabase, errors: list) -> None:
282
+ """Fill the `fields` parameters with values from an object template if one is in use."""
283
+ object_template_field = fields.get(OBJECT_TEMPLATE_RELATIONSHIP_NAME)
284
+ if not object_template_field:
285
+ return
286
+
287
+ try:
288
+ template: CoreObjectTemplate = await registry.manager.find_object(
289
+ db=db,
290
+ kind=self._schema.get_relationship(name=OBJECT_TEMPLATE_RELATIONSHIP_NAME).peer,
291
+ id=object_template_field.get("id"),
292
+ hfid=object_template_field.get("hfid"),
293
+ branch=self.get_branch_based_on_support_type(),
294
+ )
295
+ except NodeNotFoundError:
296
+ errors.append(
297
+ ValidationError(
298
+ {
299
+ f"{OBJECT_TEMPLATE_RELATIONSHIP_NAME}": (
300
+ "Unable to find the object template in the database "
301
+ f"'{object_template_field.get('id') or object_template_field.get('hfid')}'"
302
+ )
303
+ }
304
+ )
305
+ )
306
+ return
307
+
308
+ # Handle attributes, copy values from template
309
+ # Relationships handling in performed in GraphQL mutation to create nodes for relationships
310
+ for attribute_name in template._attributes:
311
+ if attribute_name in list(fields) + [OBJECT_TEMPLATE_NAME_ATTR]:
312
+ continue
313
+ fields[attribute_name] = {"value": getattr(template, attribute_name).value, "source": template.id}
314
+
315
+ for relationship_name in template._relationships:
316
+ relationship_schema = template._schema.get_relationship(name=relationship_name)
317
+ if (
318
+ relationship_name in list(fields)
319
+ or relationship_schema.kind not in [RelationshipKind.ATTRIBUTE, RelationshipKind.GENERIC]
320
+ or relationship_name == OBJECT_TEMPLATE_RELATIONSHIP_NAME
321
+ ):
322
+ continue
323
+
324
+ relationship: RelationshipManager = getattr(template, relationship_name)
325
+ if relationship_schema.cardinality == RelationshipCardinality.ONE:
326
+ if relationship_peer := await relationship.get_peer(db=db):
327
+ fields[relationship_name] = {"id": relationship_peer.id}
328
+ elif relationship_peers := await relationship.get_peers(db=db):
329
+ fields[relationship_name] = [{"id": peer_id} for peer_id in relationship_peers]
330
+
264
331
  async def _process_fields(self, fields: dict, db: InfrahubDatabase) -> None:
265
332
  errors = []
266
333
 
@@ -279,6 +346,9 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
279
346
  if field_name not in self._schema.valid_input_names:
280
347
  errors.append(ValidationError({field_name: f"{field_name} is not a valid input for {self.get_kind()}"}))
281
348
 
349
+ # Backfill fields with the ones from the template if there's one
350
+ await self.handle_object_template(fields=fields, db=db, errors=errors)
351
+
282
352
  # If the object is new, we need to ensure that all mandatory attributes and relationships have been provided
283
353
  if not self._existing:
284
354
  for mandatory_attr in self._schema.mandatory_attribute_names:
@@ -440,7 +510,7 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
440
510
 
441
511
  async def _generate_relationship_default(
442
512
  self,
443
- name: str, # pylint: disable=unused-argument
513
+ name: str, # noqa: ARG002
444
514
  schema: RelationshipSchema,
445
515
  data: Any,
446
516
  db: InfrahubDatabase,
@@ -461,7 +531,7 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
461
531
  name: str,
462
532
  schema: AttributeSchema,
463
533
  data: Any,
464
- db: InfrahubDatabase, # pylint: disable=unused-argument
534
+ db: InfrahubDatabase, # noqa: ARG002
465
535
  ) -> BaseAttribute:
466
536
  attr_class = ATTRIBUTE_TYPES[schema.kind].get_infrahub_class()
467
537
  attr = attr_class(
@@ -476,10 +546,9 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
476
546
  )
477
547
  return attr
478
548
 
479
- async def process_label(self, db: Optional[InfrahubDatabase] = None) -> None: # pylint: disable=unused-argument
549
+ async def process_label(self, db: Optional[InfrahubDatabase] = None) -> None: # noqa: ARG002
480
550
  # If there label and name are both defined for this node
481
551
  # if label is not define, we'll automatically populate it with a human friendy vesion of name
482
- # pylint: disable=no-member
483
552
  if not self._existing and hasattr(self, "label") and hasattr(self, "name"):
484
553
  if self.label.value is None and self.name.value:
485
554
  self.label.value = " ".join([word.title() for word in self.name.value.split("_")])
@@ -526,7 +595,7 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
526
595
  await self._process_fields(db=db, fields=kwargs)
527
596
  return self
528
597
 
529
- async def _create(self, db: InfrahubDatabase, at: Optional[Timestamp] = None) -> None:
598
+ async def _create(self, db: InfrahubDatabase, at: Timestamp | None = None) -> NodeChangelog:
530
599
  create_at = Timestamp(at)
531
600
 
532
601
  query = await NodeCreateAllQuery.init(db=db, node=self, at=create_at)
@@ -538,44 +607,63 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
538
607
  self._existing = True
539
608
 
540
609
  new_ids = query.get_ids()
610
+ node_changelog = NodeChangelog(node_id=self.get_id(), node_kind=self.get_kind(), display_label="")
541
611
 
542
612
  # Go over the list of Attribute and assign the new IDs one by one
543
613
  for name in self._attributes:
544
614
  attr: BaseAttribute = getattr(self, name)
545
615
  attr.id, attr.db_id = new_ids[name]
546
616
  attr.at = create_at
617
+ node_changelog.create_attribute(attribute=attr)
547
618
 
548
619
  # Go over the list of relationships and assign the new IDs one by one
549
620
  for name in self._relationships:
550
621
  relm: RelationshipManager = getattr(self, name)
551
622
  for rel in relm._relationships:
552
623
  identifier = f"{rel.schema.identifier}::{rel.peer_id}"
624
+
553
625
  rel.id, rel.db_id = new_ids[identifier]
554
626
 
627
+ node_changelog.create_relationship(relationship=rel)
628
+
629
+ node_changelog.display_label = await self.render_display_label(db=db)
630
+ return node_changelog
631
+
555
632
  async def _update(
556
633
  self, db: InfrahubDatabase, at: Optional[Timestamp] = None, fields: list[str] | None = None
557
- ) -> None:
634
+ ) -> NodeChangelog:
558
635
  """Update the node in the database if needed."""
559
636
 
560
637
  update_at = Timestamp(at)
638
+ node_changelog = NodeChangelog(node_id=self.get_id(), node_kind=self.get_kind(), display_label="")
561
639
 
562
640
  # Go over the list of Attribute and update them one by one
563
641
  for name in self._attributes:
564
- if fields and name in fields:
642
+ if (fields and name in fields) or not fields:
565
643
  attr: BaseAttribute = getattr(self, name)
566
- await attr.save(at=update_at, db=db)
567
- else:
568
- attr: BaseAttribute = getattr(self, name)
569
- await attr.save(at=update_at, db=db)
644
+ updated_attribute = await attr.save(at=update_at, db=db)
645
+ if updated_attribute:
646
+ node_changelog.add_attribute(attribute=updated_attribute)
570
647
 
571
648
  # Go over the list of relationships and update them one by one
649
+ processed_relationships: list[str] = []
572
650
  for name in self._relationships:
573
- if fields and name in fields:
651
+ if (fields and name in fields) or not fields:
652
+ processed_relationships.append(name)
574
653
  rel: RelationshipManager = getattr(self, name)
575
- await rel.save(at=update_at, db=db)
576
- else:
577
- attr: BaseAttribute = getattr(self, name)
578
- await attr.save(at=update_at, db=db)
654
+ updated_relationship = await rel.save(at=update_at, db=db)
655
+ node_changelog.add_relationship(relationship_changelog=updated_relationship)
656
+
657
+ if len(processed_relationships) != len(self._relationships):
658
+ # Analyze if the node has a parent and add it to the changelog if missing
659
+ if parent_relationship := self._get_parent_relationship_name():
660
+ if parent_relationship not in processed_relationships:
661
+ rel: RelationshipManager = getattr(self, parent_relationship)
662
+ if parent := await rel.get_parent(db=db):
663
+ node_changelog.add_parent_from_relationship(parent=parent)
664
+
665
+ node_changelog.display_label = await self.render_display_label(db=db)
666
+ return node_changelog
579
667
 
580
668
  async def save(self, db: InfrahubDatabase, at: Optional[Timestamp] = None, fields: list[str] | None = None) -> Self:
581
669
  """Create or Update the Node in the database."""
@@ -583,10 +671,10 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
583
671
  save_at = Timestamp(at)
584
672
 
585
673
  if self._existing:
586
- await self._update(at=save_at, db=db, fields=fields)
674
+ self._node_changelog = await self._update(at=save_at, db=db, fields=fields)
587
675
  return self
588
676
 
589
- await self._create(at=save_at, db=db)
677
+ self._node_changelog = await self._create(at=save_at, db=db)
590
678
  return self
591
679
 
592
680
  async def delete(self, db: InfrahubDatabase, at: Optional[Timestamp] = None) -> None:
@@ -594,20 +682,27 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
594
682
 
595
683
  delete_at = Timestamp(at)
596
684
 
685
+ node_changelog = NodeChangelog(
686
+ node_id=self.get_id(), node_kind=self.get_kind(), display_label=await self.render_display_label(db=db)
687
+ )
597
688
  # Go over the list of Attribute and update them one by one
598
689
  for name in self._attributes:
599
690
  attr: BaseAttribute = getattr(self, name)
600
- await attr.delete(at=delete_at, db=db)
601
-
602
- # Go over the list of relationships and update them one by one
603
- for name in self._relationships:
604
- rel: RelationshipManager = getattr(self, name)
605
- await rel.delete(at=delete_at, db=db)
691
+ deleted_attribute = await attr.delete(at=delete_at, db=db)
692
+ if deleted_attribute:
693
+ node_changelog.add_attribute(attribute=deleted_attribute)
606
694
 
607
- # Need to check if there are some unidirectional relationship as well
608
- # For example, if we delete a tag, we must check the permissions and update all the relationships pointing at it
609
695
  branch = self.get_branch_based_on_support_type()
610
696
 
697
+ delete_query = await RelationshipDeleteAllQuery.init(
698
+ db=db, node_id=self.get_id(), branch=branch, at=delete_at, branch_agnostic=branch.name == GLOBAL_BRANCH_NAME
699
+ )
700
+ await delete_query.execute(db=db)
701
+
702
+ deleted_relationships_changelogs = delete_query.get_deleted_relationships_changelog(self._schema)
703
+ for relationship_changelog in deleted_relationships_changelogs:
704
+ node_changelog.add_relationship(relationship_changelog=relationship_changelog)
705
+
611
706
  # Update the relationship to the branch itself
612
707
  query = await NodeGetListQuery.init(
613
708
  db=db,
@@ -625,6 +720,7 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
625
720
 
626
721
  query = await NodeDeleteQuery.init(db=db, node=self, at=delete_at)
627
722
  await query.execute(db=db)
723
+ self._node_changelog = node_changelog
628
724
 
629
725
  async def to_graphql(
630
726
  self,
@@ -633,6 +729,7 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
633
729
  related_node_ids: set | None = None,
634
730
  filter_sensitive: bool = False,
635
731
  permissions: dict | None = None,
732
+ include_properties: bool = True,
636
733
  ) -> dict:
637
734
  """Generate GraphQL Payload for all attributes
638
735
 
@@ -686,10 +783,14 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
686
783
  related_node_ids=related_node_ids,
687
784
  filter_sensitive=filter_sensitive,
688
785
  permissions=permissions,
786
+ include_properties=include_properties,
689
787
  )
690
788
  else:
691
789
  response[field_name] = await field.to_graphql(
692
- db=db, filter_sensitive=filter_sensitive, permissions=permissions
790
+ db=db,
791
+ filter_sensitive=filter_sensitive,
792
+ permissions=permissions,
793
+ include_properties=include_properties,
693
794
  )
694
795
 
695
796
  for relationship_schema in self.get_schema().relationships:
@@ -731,7 +832,7 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
731
832
 
732
833
  return changed
733
834
 
734
- async def render_display_label(self, db: Optional[InfrahubDatabase] = None) -> str: # pylint: disable=unused-argument
835
+ async def render_display_label(self, db: Optional[InfrahubDatabase] = None) -> str: # noqa: ARG002
735
836
  if not self._schema.display_labels:
736
837
  return repr(self)
737
838
 
@@ -757,3 +858,26 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
757
858
  if not display_label.strip():
758
859
  return repr(self)
759
860
  return display_label.strip()
861
+
862
+ def _get_parent_relationship_name(self) -> str | None:
863
+ """Return the name of the parent relationship is one is present"""
864
+ for relationship in self._schema.relationships:
865
+ if relationship.kind == RelationshipKind.PARENT:
866
+ return relationship.name
867
+
868
+ async def get_object_template(self, db: InfrahubDatabase) -> Node | None:
869
+ object_template: RelationshipManager = getattr(self, OBJECT_TEMPLATE_RELATIONSHIP_NAME, None)
870
+ return await object_template.get_peer(db=db) if object_template is not None else None
871
+
872
+ def get_relationships(
873
+ self, kind: RelationshipKind, exclude: Sequence[str] | None = None
874
+ ) -> list[RelationshipSchema]:
875
+ """Return relationships of a given kind with the possiblity to exclude some of them by name."""
876
+ if exclude is None:
877
+ exclude = []
878
+
879
+ return [
880
+ relationship
881
+ for relationship in self.get_schema().relationships
882
+ if relationship.name not in exclude and relationship.kind == kind
883
+ ]
@@ -21,7 +21,7 @@ class BaseOptions:
21
21
  if not self._frozen:
22
22
  super().__setattr__(name, value)
23
23
  else:
24
- raise Exception(f"Can't modify frozen Options {self}") # pylint: disable=broad-exception-raised
24
+ raise Exception(f"Can't modify frozen Options {self}")
25
25
 
26
26
  def __repr__(self):
27
27
  return f"<{self.__class__.__name__} name={repr(self.name)}>"
@@ -35,7 +35,7 @@ class NodeGroupedUniquenessConstraint(NodeConstraintInterface):
35
35
  self.branch = branch
36
36
  self.schema_branch = registry.schema.get_schema_branch(branch.name)
37
37
 
38
- def _build_query_request(
38
+ async def _build_query_request(
39
39
  self,
40
40
  updated_node: Node,
41
41
  node_schema: MainSchemaTypes,
@@ -51,9 +51,16 @@ class NodeGroupedUniquenessConstraint(NodeConstraintInterface):
51
51
  if attribute_path.related_schema and attribute_path.relationship_schema:
52
52
  if filters and attribute_path.relationship_schema.name in filters:
53
53
  include_in_query = True
54
+
55
+ relationship_manager: RelationshipManager = getattr(
56
+ updated_node, attribute_path.relationship_schema.name
57
+ )
58
+ related_node = await relationship_manager.get_peer(db=self.db)
59
+ related_node_id = related_node.get_id() if related_node else None
54
60
  query_relationship_paths.add(
55
61
  QueryRelationshipAttributePath(
56
62
  identifier=attribute_path.relationship_schema.get_identifier(),
63
+ value=related_node_id,
57
64
  )
58
65
  )
59
66
  continue
@@ -158,7 +165,7 @@ class NodeGroupedUniquenessConstraint(NodeConstraintInterface):
158
165
  ) -> None:
159
166
  schema_branch = self.db.schema.get_schema_branch(name=self.branch.name)
160
167
  path_groups = node_schema.get_unique_constraint_schema_attribute_paths(schema_branch=schema_branch)
161
- query_request = self._build_query_request(
168
+ query_request = await self._build_query_request(
162
169
  updated_node=node, node_schema=node_schema, path_groups=path_groups, filters=filters
163
170
  )
164
171
  if not query_request:
@@ -11,7 +11,7 @@ from infrahub.core.query.relationship import (
11
11
  RelationshipGetByIdentifierQuery,
12
12
  RelationshipPeersData,
13
13
  )
14
- from infrahub.core.schema import MainSchemaTypes, NodeSchema, ProfileSchema
14
+ from infrahub.core.schema import MainSchemaTypes, NodeSchema, ProfileSchema, TemplateSchema
15
15
  from infrahub.core.timestamp import Timestamp
16
16
  from infrahub.database import InfrahubDatabase
17
17
  from infrahub.exceptions import ValidationError
@@ -28,7 +28,7 @@ class NodeDeleteIndex:
28
28
  # {node_schema: {DeleteRelationshipType: {relationship_identifier: peer_node_schema}}}
29
29
  self._dependency_graph: dict[str, dict[DeleteRelationshipType, dict[str, set[str]]]] = {}
30
30
 
31
- def index(self, start_schemas: Iterable[NodeSchema | ProfileSchema]) -> None:
31
+ def index(self, start_schemas: Iterable[NodeSchema | ProfileSchema | TemplateSchema]) -> None:
32
32
  self._index_cascading_deletes(start_schemas=start_schemas)
33
33
  self._index_dependent_schema(start_schemas=start_schemas)
34
34
 
@@ -50,7 +50,7 @@ class NodeDeleteIndex:
50
50
  self._dependency_graph[kind][relationship_type] = defaultdict(set)
51
51
  self._dependency_graph[kind][relationship_type][relationship_identifier].update(peer_kinds)
52
52
 
53
- def _index_cascading_deletes(self, start_schemas: Iterable[NodeSchema | ProfileSchema]) -> None:
53
+ def _index_cascading_deletes(self, start_schemas: Iterable[NodeSchema | ProfileSchema | TemplateSchema]) -> None:
54
54
  kinds_to_check: set[str] = {schema.kind for schema in start_schemas}
55
55
  while True:
56
56
  try:
@@ -72,7 +72,7 @@ class NodeDeleteIndex:
72
72
  if peer_kind not in self._dependency_graph:
73
73
  kinds_to_check.add(peer_kind)
74
74
 
75
- def _index_dependent_schema(self, start_schemas: Iterable[NodeSchema | ProfileSchema]) -> None:
75
+ def _index_dependent_schema(self, start_schemas: Iterable[NodeSchema | ProfileSchema | TemplateSchema]) -> None:
76
76
  start_schema_kinds: set[str] = set()
77
77
  for start_schema in start_schemas:
78
78
  start_schema_kinds.add(start_schema.kind)
@@ -19,10 +19,15 @@ class BuiltinIPPrefix(Node):
19
19
  fields: Optional[dict] = None,
20
20
  related_node_ids: Optional[set] = None,
21
21
  filter_sensitive: bool = False,
22
- permissions: Optional[dict] = None,
22
+ permissions: Optional[dict] = None, # noqa: ARG002
23
+ include_properties: bool = True,
23
24
  ) -> dict:
24
25
  response = await super().to_graphql(
25
- db, fields=fields, related_node_ids=related_node_ids, filter_sensitive=filter_sensitive
26
+ db,
27
+ fields=fields,
28
+ related_node_ids=related_node_ids,
29
+ filter_sensitive=filter_sensitive,
30
+ include_properties=include_properties,
26
31
  )
27
32
 
28
33
  if fields:
@@ -35,8 +40,8 @@ class BuiltinIPPrefix(Node):
35
40
  retrieved = await NodeManager.get_one(
36
41
  db=db, branch=self._branch, id=self.id, fields={"member_type": None, "prefix": None}
37
42
  )
38
- self.member_type = retrieved.member_type # type: ignore[union-attr] # pylint: disable=attribute-defined-outside-init
39
- self.prefix = retrieved.prefix # type: ignore[union-attr] # pylint: disable=attribute-defined-outside-init
43
+ self.member_type = retrieved.member_type # type: ignore[union-attr]
44
+ self.prefix = retrieved.prefix # type: ignore[union-attr]
40
45
  utilization_getter = PrefixUtilizationGetter(db=db, ip_prefixes=[self])
41
46
  utilization = await utilization_getter.get_use_percentage(
42
47
  ip_prefixes=[self], branch_names=[self._branch.name]
@@ -46,12 +51,12 @@ class BuiltinIPPrefix(Node):
46
51
  return response
47
52
 
48
53
  async def get_resource_weight(self, db: InfrahubDatabase) -> int:
49
- member_type = self.member_type.value # type: ignore[has-type] # pylint: disable=access-member-before-definition
50
- prefixlen = self.prefix.prefixlen # type: ignore[has-type] # pylint: disable=access-member-before-definition
54
+ member_type = self.member_type.value # type: ignore[has-type]
55
+ prefixlen = self.prefix.prefixlen # type: ignore[has-type]
51
56
  if member_type is None or prefixlen is None:
52
57
  retrieved = await NodeManager.get_one(
53
58
  db=db, branch=self._branch, id=self.id, fields={"member_type": None, "prefix": None}
54
59
  )
55
- self.member_type = retrieved.member_type # type: ignore[union-attr] # pylint: disable=attribute-defined-outside-init
56
- self.prefix = retrieved.prefix # type: ignore[union-attr] # pylint: disable=attribute-defined-outside-init
60
+ self.member_type = retrieved.member_type # type: ignore[union-attr]
61
+ self.prefix = retrieved.prefix # type: ignore[union-attr]
57
62
  return get_prefix_space(self)