infrahub-server 1.1.6__py3-none-any.whl → 1.2.0rc0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (346) hide show
  1. infrahub/api/artifact.py +16 -4
  2. infrahub/api/dependencies.py +8 -0
  3. infrahub/api/oauth2.py +0 -1
  4. infrahub/api/oidc.py +0 -1
  5. infrahub/api/query.py +18 -7
  6. infrahub/api/schema.py +32 -6
  7. infrahub/api/transformation.py +12 -5
  8. infrahub/{message_bus/messages/check_artifact_create.py → artifacts/models.py} +2 -4
  9. infrahub/{message_bus/operations/check/artifact.py → artifacts/tasks.py} +26 -25
  10. infrahub/cli/__init__.py +0 -2
  11. infrahub/cli/db.py +6 -7
  12. infrahub/cli/events.py +8 -3
  13. infrahub/cli/git_agent.py +9 -7
  14. infrahub/cli/tasks.py +4 -6
  15. infrahub/computed_attribute/tasks.py +63 -17
  16. infrahub/computed_attribute/triggers.py +90 -0
  17. infrahub/config.py +1 -1
  18. infrahub/context.py +39 -0
  19. infrahub/core/account.py +5 -8
  20. infrahub/core/attribute.py +53 -21
  21. infrahub/core/branch/models.py +4 -4
  22. infrahub/core/branch/tasks.py +89 -130
  23. infrahub/core/changelog/__init__.py +0 -0
  24. infrahub/core/changelog/diff.py +232 -0
  25. infrahub/core/changelog/models.py +488 -0
  26. infrahub/core/constants/__init__.py +19 -2
  27. infrahub/core/constants/infrahubkind.py +1 -0
  28. infrahub/core/diff/combiner.py +12 -8
  29. infrahub/core/diff/coordinator.py +49 -70
  30. infrahub/core/diff/data_check_synchronizer.py +86 -7
  31. infrahub/core/diff/enricher/aggregated.py +3 -3
  32. infrahub/core/diff/enricher/cardinality_one.py +2 -7
  33. infrahub/core/diff/enricher/hierarchy.py +5 -3
  34. infrahub/core/diff/enricher/labels.py +14 -4
  35. infrahub/core/diff/enricher/path_identifier.py +3 -9
  36. infrahub/core/diff/enricher/summary_counts.py +3 -1
  37. infrahub/core/diff/merger/merger.py +8 -4
  38. infrahub/core/diff/model/path.py +47 -29
  39. infrahub/core/diff/query/all_conflicts.py +6 -3
  40. infrahub/core/diff/query/artifact.py +1 -1
  41. infrahub/core/diff/query/delete_query.py +1 -1
  42. infrahub/core/diff/query/diff_get.py +3 -2
  43. infrahub/core/diff/query/diff_summary.py +1 -1
  44. infrahub/core/diff/query/field_specifiers.py +3 -1
  45. infrahub/core/diff/query/field_summary.py +3 -2
  46. infrahub/core/diff/query/filters.py +12 -1
  47. infrahub/core/diff/query/get_conflict_query.py +1 -1
  48. infrahub/core/diff/query/has_conflicts_query.py +6 -3
  49. infrahub/core/diff/query/merge.py +3 -3
  50. infrahub/core/diff/query/{drop_tracking_id.py → merge_tracking_id.py} +4 -4
  51. infrahub/core/diff/query/roots_metadata.py +9 -2
  52. infrahub/core/diff/query/save.py +151 -66
  53. infrahub/core/diff/query/summary_counts_enricher.py +220 -0
  54. infrahub/core/diff/query/time_range_query.py +3 -2
  55. infrahub/core/diff/query/update_conflict_query.py +1 -1
  56. infrahub/core/diff/query_parser.py +49 -24
  57. infrahub/core/diff/repository/deserializer.py +24 -25
  58. infrahub/core/diff/repository/repository.py +76 -20
  59. infrahub/core/diff/tasks.py +9 -8
  60. infrahub/core/enums.py +1 -1
  61. infrahub/core/integrity/object_conflict/conflict_recorder.py +1 -1
  62. infrahub/core/ipam/reconciler.py +1 -1
  63. infrahub/core/ipam/tasks.py +2 -3
  64. infrahub/core/manager.py +18 -13
  65. infrahub/core/merge.py +5 -2
  66. infrahub/core/migrations/graph/m001_add_version_to_graph.py +1 -1
  67. infrahub/core/migrations/graph/m002_attribute_is_default.py +2 -2
  68. infrahub/core/migrations/graph/m003_relationship_parent_optional.py +2 -2
  69. infrahub/core/migrations/graph/m004_add_attr_documentation.py +1 -1
  70. infrahub/core/migrations/graph/m005_add_rel_read_only.py +1 -1
  71. infrahub/core/migrations/graph/m006_add_rel_on_delete.py +1 -1
  72. infrahub/core/migrations/graph/m007_add_rel_allow_override.py +1 -1
  73. infrahub/core/migrations/graph/m008_add_human_friendly_id.py +1 -1
  74. infrahub/core/migrations/graph/m009_add_generate_profile_attr.py +1 -1
  75. infrahub/core/migrations/graph/m010_add_generate_profile_attr_generic.py +1 -1
  76. infrahub/core/migrations/graph/m011_remove_profile_relationship_schema.py +2 -2
  77. infrahub/core/migrations/graph/m012_convert_account_generic.py +12 -23
  78. infrahub/core/migrations/graph/m013_convert_git_password_credential.py +7 -11
  79. infrahub/core/migrations/graph/m014_remove_index_attr_value.py +2 -2
  80. infrahub/core/migrations/graph/m015_diff_format_update.py +1 -1
  81. infrahub/core/migrations/graph/m016_diff_delete_bug_fix.py +1 -1
  82. infrahub/core/migrations/graph/m017_add_core_profile.py +1 -1
  83. infrahub/core/migrations/graph/m018_uniqueness_nulls.py +2 -2
  84. infrahub/core/migrations/query/attribute_add.py +1 -1
  85. infrahub/core/migrations/query/attribute_rename.py +1 -1
  86. infrahub/core/migrations/query/delete_element_in_schema.py +1 -1
  87. infrahub/core/migrations/query/node_duplicate.py +1 -1
  88. infrahub/core/migrations/query/relationship_duplicate.py +1 -1
  89. infrahub/core/migrations/query/schema_attribute_update.py +1 -1
  90. infrahub/core/migrations/schema/node_attribute_remove.py +1 -1
  91. infrahub/core/migrations/schema/node_remove.py +1 -1
  92. infrahub/core/migrations/schema/tasks.py +5 -5
  93. infrahub/core/migrations/shared.py +4 -4
  94. infrahub/core/models.py +7 -8
  95. infrahub/core/node/__init__.py +161 -40
  96. infrahub/core/node/base.py +1 -1
  97. infrahub/core/node/constraints/grouped_uniqueness.py +9 -2
  98. infrahub/core/node/delete_validator.py +4 -4
  99. infrahub/core/node/ipam.py +13 -8
  100. infrahub/core/node/permissions.py +4 -0
  101. infrahub/core/node/resource_manager/ip_prefix_pool.py +8 -5
  102. infrahub/core/node/standard.py +3 -5
  103. infrahub/core/property.py +1 -1
  104. infrahub/core/protocols.py +4 -0
  105. infrahub/core/protocols_base.py +4 -2
  106. infrahub/core/query/__init__.py +2 -5
  107. infrahub/core/query/attribute.py +9 -9
  108. infrahub/core/query/branch.py +5 -5
  109. infrahub/core/query/delete.py +1 -1
  110. infrahub/core/query/diff.py +45 -7
  111. infrahub/core/query/ipam.py +4 -4
  112. infrahub/core/query/node.py +19 -14
  113. infrahub/core/query/relationship.py +10 -11
  114. infrahub/core/query/resource_manager.py +13 -11
  115. infrahub/core/query/standard_node.py +6 -6
  116. infrahub/core/query/task.py +3 -3
  117. infrahub/core/query/task_log.py +1 -1
  118. infrahub/core/query/utils.py +5 -5
  119. infrahub/core/registry.py +0 -2
  120. infrahub/core/relationship/constraints/count.py +1 -1
  121. infrahub/core/relationship/constraints/peer_kind.py +1 -1
  122. infrahub/core/relationship/model.py +66 -26
  123. infrahub/core/schema/__init__.py +6 -4
  124. infrahub/core/schema/basenode_schema.py +1 -3
  125. infrahub/core/schema/definitions/core.py +14 -2
  126. infrahub/core/schema/definitions/internal.py +16 -0
  127. infrahub/core/schema/generated/genericnode_schema.py +5 -0
  128. infrahub/core/schema/generated/node_schema.py +5 -0
  129. infrahub/core/schema/generic_schema.py +5 -1
  130. infrahub/core/schema/manager.py +45 -42
  131. infrahub/core/schema/node_schema.py +4 -0
  132. infrahub/core/schema/profile_schema.py +4 -0
  133. infrahub/core/schema/relationship_schema.py +2 -2
  134. infrahub/core/schema/schema_branch.py +248 -14
  135. infrahub/core/schema/template_schema.py +36 -0
  136. infrahub/core/task/user_task.py +7 -5
  137. infrahub/core/timestamp.py +1 -1
  138. infrahub/core/utils.py +3 -2
  139. infrahub/core/validators/attribute/choices.py +1 -1
  140. infrahub/core/validators/attribute/enum.py +1 -1
  141. infrahub/core/validators/attribute/kind.py +1 -1
  142. infrahub/core/validators/attribute/length.py +1 -1
  143. infrahub/core/validators/attribute/optional.py +1 -1
  144. infrahub/core/validators/attribute/regex.py +1 -1
  145. infrahub/core/validators/attribute/unique.py +1 -1
  146. infrahub/core/validators/checks_runner.py +37 -0
  147. infrahub/core/validators/node/generate_profile.py +1 -1
  148. infrahub/core/validators/node/hierarchy.py +1 -1
  149. infrahub/core/validators/query.py +1 -1
  150. infrahub/core/validators/relationship/count.py +1 -1
  151. infrahub/core/validators/relationship/optional.py +1 -1
  152. infrahub/core/validators/relationship/peer.py +1 -1
  153. infrahub/core/validators/tasks.py +8 -6
  154. infrahub/core/validators/uniqueness/query.py +20 -17
  155. infrahub/database/__init__.py +15 -2
  156. infrahub/database/memgraph.py +1 -1
  157. infrahub/dependencies/builder/constraint/grouped/node_runner.py +0 -2
  158. infrahub/dependencies/builder/diff/combiner.py +1 -1
  159. infrahub/dependencies/builder/diff/conflicts_enricher.py +1 -1
  160. infrahub/dependencies/builder/diff/coordinator.py +0 -2
  161. infrahub/dependencies/builder/diff/deserializer.py +1 -1
  162. infrahub/dependencies/builder/diff/enricher/summary_counts.py +1 -1
  163. infrahub/events/branch_action.py +47 -21
  164. infrahub/events/group_action.py +73 -0
  165. infrahub/events/models.py +159 -51
  166. infrahub/events/node_action.py +74 -8
  167. infrahub/events/repository_action.py +8 -8
  168. infrahub/events/schema_action.py +21 -8
  169. infrahub/generators/tasks.py +12 -13
  170. infrahub/git/base.py +3 -5
  171. infrahub/git/constants.py +0 -1
  172. infrahub/git/integrator.py +36 -35
  173. infrahub/git/repository.py +7 -8
  174. infrahub/git/tasks.py +43 -107
  175. infrahub/git_credential/helper.py +2 -3
  176. infrahub/graphql/analyzer.py +572 -11
  177. infrahub/graphql/app.py +34 -26
  178. infrahub/graphql/auth/query_permission_checker/anonymous_checker.py +5 -5
  179. infrahub/graphql/auth/query_permission_checker/default_branch_checker.py +4 -4
  180. infrahub/graphql/auth/query_permission_checker/merge_operation_checker.py +4 -4
  181. infrahub/graphql/auth/query_permission_checker/object_permission_checker.py +28 -35
  182. infrahub/graphql/auth/query_permission_checker/super_admin_checker.py +5 -5
  183. infrahub/graphql/enums.py +1 -1
  184. infrahub/graphql/initialization.py +5 -1
  185. infrahub/graphql/loaders/node.py +2 -2
  186. infrahub/graphql/manager.py +59 -54
  187. infrahub/graphql/mutations/account.py +20 -13
  188. infrahub/graphql/mutations/artifact_definition.py +16 -12
  189. infrahub/graphql/mutations/branch.py +61 -40
  190. infrahub/graphql/mutations/computed_attribute.py +19 -13
  191. infrahub/graphql/mutations/diff.py +37 -9
  192. infrahub/graphql/mutations/diff_conflict.py +9 -8
  193. infrahub/graphql/mutations/graphql_query.py +19 -11
  194. infrahub/graphql/mutations/ipam.py +21 -19
  195. infrahub/graphql/mutations/main.py +197 -44
  196. infrahub/graphql/mutations/menu.py +8 -8
  197. infrahub/graphql/mutations/proposed_change.py +36 -28
  198. infrahub/graphql/mutations/relationship.py +302 -105
  199. infrahub/graphql/mutations/repository.py +41 -35
  200. infrahub/graphql/mutations/resource_manager.py +26 -26
  201. infrahub/graphql/mutations/schema.py +51 -33
  202. infrahub/graphql/mutations/tasks.py +16 -10
  203. infrahub/graphql/parser.py +1 -1
  204. infrahub/graphql/permissions.py +6 -4
  205. infrahub/graphql/queries/account.py +22 -18
  206. infrahub/graphql/queries/branch.py +6 -4
  207. infrahub/graphql/queries/diff/tree.py +48 -42
  208. infrahub/graphql/queries/event.py +112 -0
  209. infrahub/graphql/queries/internal.py +3 -3
  210. infrahub/graphql/queries/ipam.py +23 -18
  211. infrahub/graphql/queries/relationship.py +11 -10
  212. infrahub/graphql/queries/resource_manager.py +43 -27
  213. infrahub/graphql/queries/search.py +9 -8
  214. infrahub/graphql/queries/status.py +12 -9
  215. infrahub/graphql/queries/task.py +11 -9
  216. infrahub/graphql/resolvers/resolver.py +69 -43
  217. infrahub/graphql/resolvers/single_relationship.py +16 -10
  218. infrahub/graphql/schema.py +2 -0
  219. infrahub/graphql/subscription/__init__.py +1 -1
  220. infrahub/graphql/subscription/events.py +1 -1
  221. infrahub/graphql/subscription/graphql_query.py +8 -8
  222. infrahub/graphql/types/branch.py +2 -2
  223. infrahub/graphql/types/common.py +6 -1
  224. infrahub/graphql/types/enums.py +2 -0
  225. infrahub/graphql/types/event.py +100 -0
  226. infrahub/graphql/types/interface.py +2 -2
  227. infrahub/graphql/types/node.py +3 -3
  228. infrahub/graphql/types/permission.py +2 -2
  229. infrahub/graphql/types/relationship.py +3 -3
  230. infrahub/graphql/types/standard_node.py +9 -11
  231. infrahub/graphql/utils.py +28 -182
  232. infrahub/groups/tasks.py +2 -3
  233. infrahub/lock.py +1 -1
  234. infrahub/menu/constants.py +1 -0
  235. infrahub/menu/generator.py +14 -3
  236. infrahub/menu/menu.py +116 -127
  237. infrahub/menu/models.py +4 -4
  238. infrahub/message_bus/messages/__init__.py +0 -4
  239. infrahub/message_bus/messages/event_branch_merge.py +3 -0
  240. infrahub/message_bus/messages/request_proposedchange_pipeline.py +2 -0
  241. infrahub/message_bus/operations/__init__.py +3 -5
  242. infrahub/message_bus/operations/check/__init__.py +2 -2
  243. infrahub/message_bus/operations/check/generator.py +1 -3
  244. infrahub/message_bus/operations/check/repository.py +1 -1
  245. infrahub/message_bus/operations/event/branch.py +7 -3
  246. infrahub/message_bus/operations/event/schema.py +1 -1
  247. infrahub/message_bus/operations/finalize/validator.py +1 -1
  248. infrahub/message_bus/operations/git/file.py +2 -2
  249. infrahub/message_bus/operations/git/repository.py +1 -1
  250. infrahub/message_bus/operations/requests/__init__.py +0 -2
  251. infrahub/message_bus/operations/requests/generator_definition.py +1 -1
  252. infrahub/message_bus/operations/requests/proposed_change.py +26 -11
  253. infrahub/message_bus/operations/requests/repository.py +2 -2
  254. infrahub/message_bus/operations/send/echo.py +1 -1
  255. infrahub/message_bus/types.py +1 -1
  256. infrahub/permissions/__init__.py +2 -1
  257. infrahub/permissions/types.py +26 -0
  258. infrahub/pools/prefix.py +29 -165
  259. infrahub/prefect_server/__init__.py +0 -0
  260. infrahub/prefect_server/app.py +18 -0
  261. infrahub/prefect_server/database.py +20 -0
  262. infrahub/prefect_server/events.py +28 -0
  263. infrahub/prefect_server/models.py +46 -0
  264. infrahub/proposed_change/models.py +15 -1
  265. infrahub/proposed_change/tasks.py +173 -35
  266. infrahub/pytest_plugin.py +4 -4
  267. infrahub/server.py +12 -11
  268. infrahub/services/__init__.py +147 -62
  269. infrahub/services/adapters/cache/__init__.py +7 -5
  270. infrahub/services/adapters/cache/nats.py +40 -22
  271. infrahub/services/adapters/cache/redis.py +0 -4
  272. infrahub/services/adapters/event/__init__.py +10 -18
  273. infrahub/services/adapters/http/__init__.py +0 -5
  274. infrahub/services/adapters/http/httpx.py +22 -15
  275. infrahub/services/adapters/message_bus/__init__.py +23 -6
  276. infrahub/services/adapters/message_bus/local.py +8 -6
  277. infrahub/services/adapters/message_bus/nats.py +12 -6
  278. infrahub/services/adapters/message_bus/rabbitmq.py +22 -9
  279. infrahub/services/adapters/workflow/__init__.py +11 -8
  280. infrahub/services/adapters/workflow/local.py +28 -7
  281. infrahub/services/adapters/workflow/worker.py +23 -7
  282. infrahub/services/component.py +38 -35
  283. infrahub/services/scheduler.py +32 -29
  284. infrahub/storage.py +2 -4
  285. infrahub/task_manager/constants.py +1 -1
  286. infrahub/task_manager/event.py +182 -0
  287. infrahub/task_manager/models.py +125 -1
  288. infrahub/task_manager/task.py +1 -1
  289. infrahub/tasks/artifact.py +14 -16
  290. infrahub/tasks/registry.py +1 -1
  291. infrahub/tasks/telemetry.py +13 -14
  292. infrahub/transformations/tasks.py +3 -5
  293. infrahub/trigger/__init__.py +0 -0
  294. infrahub/trigger/catalogue.py +15 -0
  295. infrahub/trigger/constants.py +9 -0
  296. infrahub/trigger/models.py +69 -0
  297. infrahub/trigger/tasks.py +85 -0
  298. infrahub/types.py +1 -1
  299. infrahub/utils.py +1 -1
  300. infrahub/webhook/constants.py +0 -2
  301. infrahub/webhook/models.py +8 -2
  302. infrahub/webhook/tasks.py +20 -73
  303. infrahub/webhook/triggers.py +20 -0
  304. infrahub/workers/infrahub_async.py +36 -25
  305. infrahub/workers/utils.py +63 -0
  306. infrahub/workflows/catalogue.py +13 -37
  307. infrahub/workflows/initialization.py +6 -8
  308. infrahub/workflows/models.py +3 -5
  309. infrahub/workflows/utils.py +1 -1
  310. infrahub_sdk/ctl/check.py +3 -3
  311. infrahub_sdk/ctl/cli_commands.py +11 -10
  312. infrahub_sdk/ctl/exceptions.py +0 -6
  313. infrahub_sdk/ctl/exporter.py +1 -1
  314. infrahub_sdk/ctl/generator.py +5 -5
  315. infrahub_sdk/ctl/importer.py +3 -2
  316. infrahub_sdk/ctl/menu.py +1 -1
  317. infrahub_sdk/ctl/object.py +1 -1
  318. infrahub_sdk/ctl/repository.py +23 -15
  319. infrahub_sdk/ctl/schema.py +2 -2
  320. infrahub_sdk/ctl/utils.py +4 -3
  321. infrahub_sdk/ctl/validate.py +2 -1
  322. infrahub_sdk/exceptions.py +6 -0
  323. infrahub_sdk/generator.py +3 -0
  324. infrahub_sdk/node.py +2 -2
  325. infrahub_sdk/schema/__init__.py +14 -2
  326. infrahub_sdk/schema/main.py +7 -0
  327. infrahub_sdk/utils.py +11 -1
  328. infrahub_sdk/yaml.py +2 -3
  329. {infrahub_server-1.1.6.dist-info → infrahub_server-1.2.0rc0.dist-info}/METADATA +46 -12
  330. {infrahub_server-1.1.6.dist-info → infrahub_server-1.2.0rc0.dist-info}/RECORD +338 -321
  331. infrahub_testcontainers/container.py +14 -6
  332. infrahub_testcontainers/docker-compose.test.yml +24 -5
  333. infrahub_testcontainers/haproxy.cfg +43 -0
  334. infrahub_testcontainers/helpers.py +85 -1
  335. infrahub/core/branch/constants.py +0 -2
  336. infrahub/graphql/query.py +0 -52
  337. infrahub/message_bus/messages/request_artifactdefinition_check.py +0 -17
  338. infrahub/message_bus/operations/requests/artifact_definition.py +0 -148
  339. infrahub/schema/constants.py +0 -1
  340. infrahub/schema/tasks.py +0 -76
  341. infrahub/services/adapters/database/__init__.py +0 -9
  342. infrahub_sdk/ctl/_file.py +0 -13
  343. /infrahub/{schema → artifacts}/__init__.py +0 -0
  344. {infrahub_server-1.1.6.dist-info → infrahub_server-1.2.0rc0.dist-info}/LICENSE.txt +0 -0
  345. {infrahub_server-1.1.6.dist-info → infrahub_server-1.2.0rc0.dist-info}/WHEEL +0 -0
  346. {infrahub_server-1.1.6.dist-info → infrahub_server-1.2.0rc0.dist-info}/entry_points.txt +0 -0
@@ -24,8 +24,6 @@ if TYPE_CHECKING:
24
24
  from infrahub.core.schema import RelationshipSchema
25
25
  from infrahub.database import InfrahubDatabase
26
26
 
27
- # pylint: disable=redefined-builtin
28
-
29
27
 
30
28
  @dataclass
31
29
  class RelData:
@@ -204,7 +202,7 @@ class RelationshipCreateQuery(RelationshipQuery):
204
202
 
205
203
  super().__init__(destination=destination, destination_id=destination_id, **kwargs)
206
204
 
207
- async def query_init(self, db: InfrahubDatabase, **kwargs) -> None:
205
+ async def query_init(self, db: InfrahubDatabase, **kwargs) -> None: # noqa: ARG002
208
206
  self.params["source_id"] = self.source_id
209
207
  self.params["destination_id"] = self.destination_id
210
208
  self.params["name"] = self.schema.identifier
@@ -289,7 +287,7 @@ class RelationshipUpdatePropertyQuery(RelationshipQuery):
289
287
 
290
288
  super().__init__(**kwargs)
291
289
 
292
- async def query_init(self, db: InfrahubDatabase, **kwargs) -> None:
290
+ async def query_init(self, db: InfrahubDatabase, **kwargs) -> None: # noqa: ARG002
293
291
  self.params["rel_node_id"] = self.data.rel_node_id
294
292
  self.params["branch"] = self.branch.name
295
293
  self.params["branch_level"] = self.branch.hierarchy_level
@@ -367,7 +365,7 @@ class RelationshipDataDeleteQuery(RelationshipQuery):
367
365
  self.data = data
368
366
  super().__init__(**kwargs)
369
367
 
370
- async def query_init(self, db: InfrahubDatabase, **kwargs) -> None:
368
+ async def query_init(self, db: InfrahubDatabase, **kwargs) -> None: # noqa: ARG002
371
369
  self.params["source_id"] = self.source_id
372
370
  self.params["destination_id"] = self.data.peer_id
373
371
  self.params["rel_node_id"] = self.data.rel_node_id
@@ -431,7 +429,7 @@ class RelationshipDeleteQuery(RelationshipQuery):
431
429
  if inspect.isclass(self.rel):
432
430
  raise TypeError("An instance of Relationship must be provided to RelationshipDeleteQuery")
433
431
 
434
- async def query_init(self, db: InfrahubDatabase, **kwargs) -> None:
432
+ async def query_init(self, db: InfrahubDatabase, **kwargs) -> None: # noqa: ARG002
435
433
  rel_filter, rel_params = self.branch.get_query_filter_path(at=self.at, variable_name="edge")
436
434
  self.params["source_id"] = self.source_id
437
435
  self.params["destination_id"] = self.destination_id
@@ -558,7 +556,7 @@ class RelationshipGetPeerQuery(Query):
558
556
 
559
557
  super().__init__(**kwargs)
560
558
 
561
- async def query_init(self, db: InfrahubDatabase, **kwargs) -> None: # pylint: disable=too-many-statements
559
+ async def query_init(self, db: InfrahubDatabase, **kwargs) -> None: # noqa: ARG002
562
560
  branch_filter, branch_params = self.branch.get_query_filter_path(
563
561
  at=self.at, branch_agnostic=self.branch_agnostic
564
562
  )
@@ -765,7 +763,7 @@ class RelationshipGetQuery(RelationshipQuery):
765
763
 
766
764
  type: QueryType = QueryType.READ
767
765
 
768
- async def query_init(self, db: InfrahubDatabase, **kwargs) -> None:
766
+ async def query_init(self, db: InfrahubDatabase, **kwargs) -> None: # noqa: ARG002
769
767
  self.params["source_id"] = self.source_id
770
768
  self.params["destination_id"] = self.destination_id
771
769
  self.params["name"] = self.schema.identifier
@@ -826,7 +824,7 @@ class RelationshipGetByIdentifierQuery(Query):
826
824
 
827
825
  super().__init__(**kwargs)
828
826
 
829
- async def query_init(self, db: InfrahubDatabase, **kwargs) -> None:
827
+ async def query_init(self, db: InfrahubDatabase, **kwargs) -> None: # noqa: ARG002
830
828
  self.params["identifiers"] = self.identifiers
831
829
  self.params["full_identifiers"] = [
832
830
  [full_id.source_kind, full_id.identifier, full_id.destination_kind] for full_id in self.full_identifiers
@@ -891,7 +889,7 @@ class RelationshipCountPerNodeQuery(Query):
891
889
 
892
890
  super().__init__(**kwargs)
893
891
 
894
- async def query_init(self, db: InfrahubDatabase, **kwargs) -> None:
892
+ async def query_init(self, db: InfrahubDatabase, **kwargs) -> None: # noqa: ARG002
895
893
  branch_filter, branch_params = self.branch.get_query_filter_path(at=self.at.to_string())
896
894
  self.params.update(branch_params)
897
895
 
@@ -905,7 +903,8 @@ class RelationshipCountPerNodeQuery(Query):
905
903
  path = "<-[r:IS_RELATED]-"
906
904
 
907
905
  query = """
908
- MATCH (rl:Relationship { name: $rel_identifier })
906
+ MATCH (peer_node:Node)%(path)s(rl:Relationship { name: $rel_identifier })
907
+ WHERE peer_node.uuid IN $peer_ids AND %(branch_filter)s
909
908
  CALL {
910
909
  WITH rl
911
910
  MATCH path = (peer_node:Node)%(path)s(rl)
@@ -26,7 +26,7 @@ class IPAddressPoolGetIdentifiers(Query):
26
26
 
27
27
  super().__init__(**kwargs) # type: ignore[arg-type]
28
28
 
29
- async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None:
29
+ async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None: # noqa: ARG002
30
30
  self.params["pool_id"] = self.pool_id
31
31
  self.params["addresses"] = self.addresses
32
32
 
@@ -53,7 +53,7 @@ class IPAddressPoolGetReserved(Query):
53
53
 
54
54
  super().__init__(**kwargs) # type: ignore[arg-type]
55
55
 
56
- async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None:
56
+ async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None: # noqa: ARG002
57
57
  self.params["pool_id"] = self.pool_id
58
58
  self.params["identifier"] = self.identifier
59
59
 
@@ -82,7 +82,7 @@ class IPAddressPoolSetReserved(Query):
82
82
 
83
83
  super().__init__(**kwargs) # type: ignore[arg-type]
84
84
 
85
- async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None:
85
+ async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None: # noqa: ARG002
86
86
  self.params["pool_id"] = self.pool_id
87
87
  self.params["address_id"] = self.address_id
88
88
  self.params["identifier"] = self.identifier
@@ -119,10 +119,11 @@ class NumberPoolGetAllocated(Query):
119
119
 
120
120
  super().__init__(**kwargs) # type: ignore[arg-type]
121
121
 
122
- async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None:
122
+ async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None: # noqa: ARG002
123
123
  self.params["node_attribute"] = self.pool.node_attribute.value
124
124
  self.params["start_range"] = self.pool.start_range.value
125
125
  self.params["end_range"] = self.pool.end_range.value
126
+ self.params["pool_id"] = self.pool.get_id()
126
127
 
127
128
  branch_filter, branch_params = self.branch.get_query_filter_path(
128
129
  at=self.at.to_string(), branch_agnostic=self.branch_agnostic
@@ -133,7 +134,8 @@ class NumberPoolGetAllocated(Query):
133
134
  MATCH (n:%(node)s)-[ha:HAS_ATTRIBUTE]-(a:Attribute {name: $node_attribute})-[hv:HAS_VALUE]-(av:AttributeValue)
134
135
  MATCH (a)-[hs:HAS_SOURCE]-(pool:%(number_pool_kind)s)
135
136
  WHERE
136
- av.value >= $start_range and av.value <= $end_range
137
+ pool.uuid = $pool_id
138
+ AND av.value >= $start_range and av.value <= $end_range
137
139
  AND all(r in [ha, hv, hs] WHERE (%(branch_filter)s))
138
140
  AND ha.status = "active"
139
141
  AND hv.status = "active"
@@ -164,7 +166,7 @@ class NumberPoolGetReserved(Query):
164
166
 
165
167
  super().__init__(**kwargs) # type: ignore[arg-type]
166
168
 
167
- async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None:
169
+ async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None: # noqa: ARG002
168
170
  self.params["pool_id"] = self.pool_id
169
171
  self.params["identifier"] = self.identifier
170
172
 
@@ -204,7 +206,7 @@ class NumberPoolGetUsed(Query):
204
206
 
205
207
  super().__init__(**kwargs) # type: ignore[arg-type]
206
208
 
207
- async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None:
209
+ async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None: # noqa: ARG002
208
210
  self.params["pool_id"] = self.pool.get_id()
209
211
  self.params["start_range"] = self.pool.start_range.value
210
212
  self.params["end_range"] = self.pool.end_range.value
@@ -257,7 +259,7 @@ class NumberPoolSetReserved(Query):
257
259
 
258
260
  super().__init__(**kwargs) # type: ignore[arg-type]
259
261
 
260
- async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None:
262
+ async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None: # noqa: ARG002
261
263
  self.params["pool_id"] = self.pool_id
262
264
  self.params["reserved"] = self.reserved
263
265
  self.params["identifier"] = self.identifier
@@ -296,7 +298,7 @@ class PrefixPoolGetIdentifiers(Query):
296
298
 
297
299
  super().__init__(**kwargs) # type: ignore[arg-type]
298
300
 
299
- async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None:
301
+ async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None: # noqa: ARG002
300
302
  self.params["pool_id"] = self.pool_id
301
303
  self.params["prefixes"] = self.prefixes
302
304
 
@@ -323,7 +325,7 @@ class PrefixPoolGetReserved(Query):
323
325
 
324
326
  super().__init__(**kwargs) # type: ignore[arg-type]
325
327
 
326
- async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None:
328
+ async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None: # noqa: ARG002
327
329
  self.params["pool_id"] = self.pool_id
328
330
  self.params["identifier"] = self.identifier
329
331
 
@@ -352,7 +354,7 @@ class PrefixPoolSetReserved(Query):
352
354
 
353
355
  super().__init__(**kwargs) # type: ignore[arg-type]
354
356
 
355
- async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None:
357
+ async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None: # noqa: ARG002
356
358
  self.params["pool_id"] = self.pool_id
357
359
  self.params["prefix_id"] = self.prefix_id
358
360
  self.params["identifier"] = self.identifier
@@ -30,7 +30,7 @@ class RootNodeCreateQuery(StandardNodeQuery):
30
30
  name = "standard_node_create"
31
31
  type = QueryType.WRITE
32
32
 
33
- async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None:
33
+ async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None: # noqa: ARG002
34
34
  node_type = self.node.get_type()
35
35
  self.params["node_prop"] = self.node.to_db()
36
36
 
@@ -46,7 +46,7 @@ class StandardNodeCreateQuery(StandardNodeQuery):
46
46
  name = "standard_node_create"
47
47
  type = QueryType.WRITE
48
48
 
49
- async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None:
49
+ async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None: # noqa: ARG002
50
50
  node_type = self.node.get_type()
51
51
  self.params["node_prop"] = self.node.to_db()
52
52
 
@@ -63,7 +63,7 @@ class StandardNodeUpdateQuery(StandardNodeQuery):
63
63
  name = "standard_node_update"
64
64
  type = QueryType.WRITE
65
65
 
66
- async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None:
66
+ async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None: # noqa: ARG002
67
67
  self.node.get_type()
68
68
  self.params["node_prop"] = self.node.to_db()
69
69
  self.params["node_prop"]["uuid"] = str(self.node.uuid)
@@ -83,7 +83,7 @@ class StandardNodeDeleteQuery(StandardNodeQuery):
83
83
  insert_return = False
84
84
  type = QueryType.WRITE
85
85
 
86
- async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None:
86
+ async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None: # noqa: ARG002
87
87
  query = """
88
88
  MATCH (n:%s { uuid: $uuid })
89
89
  DETACH DELETE (n)
@@ -103,7 +103,7 @@ class StandardNodeGetItemQuery(Query):
103
103
 
104
104
  super().__init__(**kwargs)
105
105
 
106
- async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None:
106
+ async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None: # noqa: ARG002
107
107
  query = """
108
108
  MATCH (n:%(node_type)s)
109
109
  WHERE %(id_func)s(n) = $node_id OR n.uuid = $node_id
@@ -128,7 +128,7 @@ class StandardNodeGetListQuery(Query):
128
128
 
129
129
  super().__init__(**kwargs)
130
130
 
131
- async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None:
131
+ async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None: # noqa: ARG002
132
132
  filters = []
133
133
  if self.ids:
134
134
  filters.append("n.uuid in $ids_value")
@@ -18,7 +18,7 @@ class TaskNodeCreateQuery(StandardNodeQuery):
18
18
 
19
19
  type: QueryType = QueryType.WRITE
20
20
 
21
- async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None:
21
+ async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None: # noqa: ARG002
22
22
  node_type = self.node.get_type()
23
23
  self.params["node_prop"] = self.node.to_db()
24
24
  self.params["related_id"] = self.node.related.get_id()
@@ -56,7 +56,7 @@ class TaskNodeQuery(StandardNodeQuery):
56
56
  self.params["related_nodes"] = self.related_nodes
57
57
  self.params["ids"] = self.ids
58
58
 
59
- async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None:
59
+ async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None: # noqa: ARG002
60
60
  self.add_to_query(query="MATCH (n:Task)-[:IMPACTS]->(rn:Node)")
61
61
  if self.ids:
62
62
  self.add_to_query(query="WHERE n.uuid in $ids")
@@ -75,7 +75,7 @@ class TaskNodeQueryWithLogs(TaskNodeQuery):
75
75
 
76
76
  type: QueryType = QueryType.READ
77
77
 
78
- async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None:
78
+ async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None: # noqa: ARG002
79
79
  self.add_to_query(query="MATCH (n:Task)-[:IMPACTS]->(rn:Node)")
80
80
  if self.ids:
81
81
  self.add_to_query(query="WHERE n.uuid in $ids")
@@ -17,7 +17,7 @@ class TaskLogNodeCreateQuery(StandardNodeQuery):
17
17
 
18
18
  type: QueryType = QueryType.WRITE
19
19
 
20
- async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None:
20
+ async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None: # noqa: ARG002
21
21
  node_type = self.node.get_type()
22
22
  self.params["node_prop"] = self.node.to_db()
23
23
  self.params["task_id"] = self.node.task_id
@@ -1,8 +1,8 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import TYPE_CHECKING, Optional, Union
3
+ from typing import TYPE_CHECKING
4
4
 
5
- from infrahub.core.schema import NodeSchema, ProfileSchema
5
+ from infrahub.core.schema import NodeSchema, ProfileSchema, TemplateSchema
6
6
 
7
7
  if TYPE_CHECKING:
8
8
  from neo4j.graph import Node as Neo4jNode
@@ -12,12 +12,12 @@ if TYPE_CHECKING:
12
12
 
13
13
 
14
14
  def find_node_schema(
15
- db: InfrahubDatabase, node: Neo4jNode, branch: Union[Branch, str], duplicate: bool = False
16
- ) -> Optional[Union[NodeSchema, ProfileSchema]]:
15
+ db: InfrahubDatabase, node: Neo4jNode, branch: Branch | str, duplicate: bool = False
16
+ ) -> NodeSchema | ProfileSchema | TemplateSchema | None:
17
17
  for label in node.labels:
18
18
  if db.schema.has(name=label, branch=branch):
19
19
  schema = db.schema.get(name=label, branch=branch, duplicate=duplicate)
20
- if isinstance(schema, (NodeSchema, ProfileSchema)):
20
+ if isinstance(schema, NodeSchema | ProfileSchema | TemplateSchema):
21
21
  return schema
22
22
 
23
23
  return None
infrahub/core/registry.py CHANGED
@@ -23,8 +23,6 @@ if TYPE_CHECKING:
23
23
  from infrahub.storage import InfrahubObjectStorage
24
24
  from infrahub.types import InfrahubDataType
25
25
 
26
- # pylint: disable=too-many-public-methods
27
-
28
26
 
29
27
  @dataclass
30
28
  class Registry:
@@ -26,7 +26,7 @@ class RelationshipCountConstraint(RelationshipManagerConstraintInterface):
26
26
  self.db = db
27
27
  self.branch = branch
28
28
 
29
- async def check(self, relm: RelationshipManager, node_schema: MainSchemaTypes) -> None:
29
+ async def check(self, relm: RelationshipManager, node_schema: MainSchemaTypes) -> None: # noqa: ARG002
30
30
  branch = await registry.get_branch(db=self.db) if not self.branch else self.branch
31
31
 
32
32
  # NOTE adding resolve here because we need to retrieve the real ID
@@ -27,7 +27,7 @@ class RelationshipPeerKindConstraint(RelationshipManagerConstraintInterface):
27
27
  self.db = db
28
28
  self.branch = branch
29
29
 
30
- async def check(self, relm: RelationshipManager, node_schema: MainSchemaTypes) -> None:
30
+ async def check(self, relm: RelationshipManager, node_schema: MainSchemaTypes) -> None: # noqa: ARG002
31
31
  branch = await registry.get_branch(db=self.db) if not self.branch else self.branch
32
32
  peer_schema = registry.schema.get(name=relm.schema.peer, branch=branch, duplicate=False)
33
33
  if isinstance(peer_schema, GenericSchema):
@@ -23,7 +23,8 @@ from infrahub_sdk.uuidt import UUIDT
23
23
  from pydantic import BaseModel, Field
24
24
 
25
25
  from infrahub.core import registry
26
- from infrahub.core.constants import BranchSupportType, InfrahubKind
26
+ from infrahub.core.changelog.models import ChangelogRelationshipMapper
27
+ from infrahub.core.constants import BranchSupportType, InfrahubKind, RelationshipKind
27
28
  from infrahub.core.property import (
28
29
  FlagPropertyMixin,
29
30
  NodePropertyData,
@@ -48,11 +49,11 @@ if TYPE_CHECKING:
48
49
  from typing_extensions import Self
49
50
 
50
51
  from infrahub.core.branch import Branch
52
+ from infrahub.core.changelog.models import RelationshipCardinalityManyChangelog, RelationshipCardinalityOneChangelog
51
53
  from infrahub.core.node import Node
52
54
  from infrahub.core.schema import MainSchemaTypes, RelationshipSchema
53
55
  from infrahub.database import InfrahubDatabase
54
56
 
55
- # pylint: disable=redefined-builtin,too-many-lines
56
57
 
57
58
  PeerType = TypeVar("PeerType")
58
59
 
@@ -140,6 +141,12 @@ class Relationship(FlagPropertyMixin, NodePropertyMixin):
140
141
 
141
142
  return self.peer_id
142
143
 
144
+ def get_peer_kind(self) -> str:
145
+ if not self._peer or isinstance(self._peer, str):
146
+ return self.schema.peer
147
+
148
+ return self._peer.get_kind()
149
+
143
150
  @property
144
151
  def node_id(self) -> str:
145
152
  if self._node_id:
@@ -159,7 +166,7 @@ class Relationship(FlagPropertyMixin, NodePropertyMixin):
159
166
  return registry.get_global_branch()
160
167
  return self.branch
161
168
 
162
- async def _process_data(self, data: Union[dict, RelationshipPeerData, str]) -> None: # pylint: disable=too-many-branches
169
+ async def _process_data(self, data: Union[dict, RelationshipPeerData, str]) -> None:
163
170
  self.data = data
164
171
 
165
172
  if isinstance(data, RelationshipPeerData):
@@ -193,16 +200,19 @@ class Relationship(FlagPropertyMixin, NodePropertyMixin):
193
200
  else:
194
201
  await self.set_peer(value=data)
195
202
 
196
- async def new( # pylint: disable=unused-argument
197
- self, db: InfrahubDatabase, data: Union[dict, RelationshipPeerData, Any] = None, **kwargs: Any
203
+ async def new(
204
+ self,
205
+ db: InfrahubDatabase, # noqa: ARG002
206
+ data: Union[dict, RelationshipPeerData, Any] = None,
207
+ **kwargs: Any, # noqa: ARG002
198
208
  ) -> Relationship:
199
209
  await self._process_data(data=data)
200
210
 
201
211
  return self
202
212
 
203
- async def load( # pylint: disable=unused-argument
213
+ async def load(
204
214
  self,
205
- db: InfrahubDatabase,
215
+ db: InfrahubDatabase, # noqa: ARG002
206
216
  id: Optional[UUID] = None,
207
217
  db_id: Optional[str] = None,
208
218
  updated_at: Optional[Union[Timestamp, str]] = None,
@@ -253,9 +263,9 @@ class Relationship(FlagPropertyMixin, NodePropertyMixin):
253
263
  async def get_peer(self, db: InfrahubDatabase, peer_type: type[PeerType]) -> PeerType: ...
254
264
 
255
265
  @overload
256
- async def get_peer(self, db: InfrahubDatabase, peer_type: Literal[None] = ...) -> Node: ...
266
+ async def get_peer(self, db: InfrahubDatabase, peer_type: None = ...) -> Node: ...
257
267
 
258
- async def get_peer(self, db: InfrahubDatabase, peer_type: type[PeerType] | None = None) -> Any: # pylint: disable=unused-argument
268
+ async def get_peer(self, db: InfrahubDatabase, peer_type: type[PeerType] | None = None) -> Any: # noqa: ARG002
259
269
  """Return the peer of the relationship."""
260
270
  if self._peer is None:
261
271
  await self._get_peer(db=db)
@@ -521,8 +531,6 @@ class Relationship(FlagPropertyMixin, NodePropertyMixin):
521
531
  return response
522
532
 
523
533
  async def get_create_data(self, db: InfrahubDatabase) -> RelationshipCreateData:
524
- # pylint: disable=no-member
525
-
526
534
  branch = self.get_branch_based_on_support_type()
527
535
 
528
536
  await self.resolve(db=db)
@@ -749,7 +757,7 @@ class RelationshipManager:
749
757
  await rm._validate_hierarchy()
750
758
 
751
759
  for item in data:
752
- if not isinstance(item, (rm.rel_class, str, dict)) and not hasattr(item, "_schema"):
760
+ if not isinstance(item, rm.rel_class | str | dict) and not hasattr(item, "_schema"):
753
761
  raise ValidationError({rm.name: f"Invalid data provided to form a relationship {item}"})
754
762
 
755
763
  rel = rm.rel_class(schema=rm.schema, branch=rm.branch, at=rm.at, node=rm.node)
@@ -813,7 +821,7 @@ class RelationshipManager:
813
821
  async def get_peer(
814
822
  self,
815
823
  db: InfrahubDatabase,
816
- peer_type: Literal[None] = ...,
824
+ peer_type: None = ...,
817
825
  raise_on_error: Literal[False] = ...,
818
826
  ) -> Node | None: ...
819
827
 
@@ -821,7 +829,7 @@ class RelationshipManager:
821
829
  async def get_peer(
822
830
  self,
823
831
  db: InfrahubDatabase,
824
- peer_type: Literal[None] = ...,
832
+ peer_type: None = ...,
825
833
  raise_on_error: Literal[True] = ...,
826
834
  ) -> Node: ...
827
835
 
@@ -829,14 +837,14 @@ class RelationshipManager:
829
837
  async def get_peer(
830
838
  self,
831
839
  db: InfrahubDatabase,
832
- peer_type: Literal[None] = ...,
840
+ peer_type: None = ...,
833
841
  raise_on_error: bool = ...,
834
842
  ) -> Node: ...
835
843
 
836
844
  async def get_peer(
837
845
  self,
838
846
  db: InfrahubDatabase,
839
- peer_type: type[PeerType] | None = None, # pylint: disable=unused-argument
847
+ peer_type: type[PeerType] | None = None, # noqa: ARG002
840
848
  raise_on_error: bool = False,
841
849
  ) -> Node | PeerType | None:
842
850
  if self.schema.cardinality == "many":
@@ -863,14 +871,14 @@ class RelationshipManager:
863
871
  async def get_peers(
864
872
  self,
865
873
  db: InfrahubDatabase,
866
- peer_type: Literal[None] = None,
874
+ peer_type: None = None,
867
875
  branch_agnostic: bool = ...,
868
876
  ) -> Mapping[str, Node]: ...
869
877
 
870
878
  async def get_peers(
871
879
  self,
872
880
  db: InfrahubDatabase,
873
- peer_type: type[PeerType] | None = None, # pylint: disable=unused-argument
881
+ peer_type: type[PeerType] | None = None, # noqa: ARG002
874
882
  branch_agnostic: bool = False,
875
883
  ) -> Mapping[str, Node | PeerType]:
876
884
  rels = await self.get_relationships(db=db, branch_agnostic=branch_agnostic)
@@ -971,6 +979,19 @@ class RelationshipManager:
971
979
 
972
980
  return rels
973
981
 
982
+ async def get_parent(
983
+ self, db: InfrahubDatabase, branch_agnostic: bool = False, force_refresh: bool = False
984
+ ) -> Relationship | None:
985
+ if self.schema.kind == RelationshipKind.PARENT:
986
+ for relationship in await self.get_relationships(
987
+ db=db, branch_agnostic=branch_agnostic, force_refresh=force_refresh
988
+ ):
989
+ # As parent relationships requires cardinality=one there will always only be one relationship
990
+ # here even though it's within a loop
991
+ return relationship
992
+
993
+ return None
994
+
974
995
  async def get_relationships(
975
996
  self, db: InfrahubDatabase, branch_agnostic: bool = False, force_refresh: bool = False
976
997
  ) -> list[Relationship]:
@@ -985,7 +1006,7 @@ class RelationshipManager:
985
1006
 
986
1007
  return self._relationships.as_list()
987
1008
 
988
- async def update( # pylint: disable=too-many-branches
1009
+ async def update(
989
1010
  self, data: Union[list[Union[str, Node]], dict[str, Any], str, Node, None], db: InfrahubDatabase
990
1011
  ) -> bool:
991
1012
  """Replace and Update the list of relationships with this one."""
@@ -1002,7 +1023,7 @@ class RelationshipManager:
1002
1023
  changed = False
1003
1024
 
1004
1025
  for item in list_data:
1005
- if not isinstance(item, (self.rel_class, str, dict, type(None))) and not hasattr(item, "_schema"):
1026
+ if not isinstance(item, self.rel_class | str | dict | type(None)) and not hasattr(item, "_schema"):
1006
1027
  raise ValidationError({self.name: f"Invalid data provided to form a relationship {item}"})
1007
1028
 
1008
1029
  if hasattr(item, "_schema"):
@@ -1040,7 +1061,7 @@ class RelationshipManager:
1040
1061
  changed = True
1041
1062
 
1042
1063
  # Check if some relationship got removed by checking if the previous list of relationship is a subset of the current list of not
1043
- if set(list(previous_relationships.keys())) <= {rel.peer_id for rel in await self.get_relationships(db=db)}:
1064
+ if set(previous_relationships.keys()) <= {rel.peer_id for rel in await self.get_relationships(db=db)}:
1044
1065
  changed = True
1045
1066
 
1046
1067
  if changed:
@@ -1050,7 +1071,7 @@ class RelationshipManager:
1050
1071
 
1051
1072
  async def add(self, data: Union[dict[str, Any], Node], db: InfrahubDatabase) -> bool:
1052
1073
  """Add a new relationship to the list of existing ones, avoid duplication."""
1053
- if not isinstance(data, (self.rel_class, dict)) and not hasattr(data, "_schema"):
1074
+ if not isinstance(data, self.rel_class | dict) and not hasattr(data, "_schema"):
1054
1075
  raise ValidationError({self.name: f"Invalid data provided to form a relationship {data}"})
1055
1076
 
1056
1077
  await self._validate_hierarchy()
@@ -1130,18 +1151,22 @@ class RelationshipManager:
1130
1151
  )
1131
1152
  await query.execute(db=db)
1132
1153
 
1133
- async def save(self, db: InfrahubDatabase, at: Optional[Timestamp] = None) -> Self:
1154
+ async def save(
1155
+ self, db: InfrahubDatabase, at: Optional[Timestamp] = None
1156
+ ) -> RelationshipCardinalityManyChangelog | RelationshipCardinalityOneChangelog:
1134
1157
  """Create or Update the Relationship in the database."""
1135
1158
 
1136
1159
  await self.resolve(db=db)
1137
1160
 
1138
1161
  save_at = Timestamp(at)
1139
1162
  details = await self.fetch_relationship_ids(db=db, force_refresh=True)
1163
+ relationship_mapper = ChangelogRelationshipMapper(schema=self.schema)
1140
1164
 
1141
1165
  # If we have previously fetched the relationships from the database
1142
1166
  # Update the one in the database that shouldn't be here.
1143
1167
  if self.has_fetched_relationships:
1144
1168
  for peer_id in details.peer_ids_present_database_only:
1169
+ relationship_mapper.remove_peer(peer_data=details.peers_database[peer_id])
1145
1170
  await self.remove_in_db(peer_data=details.peers_database[peer_id], at=save_at, db=db)
1146
1171
 
1147
1172
  # Create the new relationship that are not present in the database
@@ -1150,6 +1175,8 @@ class RelationshipManager:
1150
1175
  if rel.peer_id in details.peer_ids_present_local_only:
1151
1176
  await rel.save(at=save_at, db=db)
1152
1177
 
1178
+ relationship_mapper.add_peer_from_relationship(relationship=rel)
1179
+
1153
1180
  elif rel.peer_id in details.peer_ids_present_both:
1154
1181
  if properties_not_matching := rel.compare_properties_with_data(
1155
1182
  data=details.peers_database[rel.peer_id]
@@ -1160,19 +1187,32 @@ class RelationshipManager:
1160
1187
  data=details.peers_database[rel.peer_id],
1161
1188
  db=db,
1162
1189
  )
1190
+ relationship_mapper.add_updated_relationship(
1191
+ relationship=rel,
1192
+ old_data=details.peers_database[rel.peer_id],
1193
+ properties_to_update=properties_not_matching,
1194
+ )
1195
+ elif rel.schema.kind == RelationshipKind.PARENT:
1196
+ relationship_mapper.add_parent_from_relationship(relationship=rel)
1163
1197
 
1164
- return self
1198
+ return relationship_mapper.changelog
1165
1199
 
1166
- async def delete(self, db: InfrahubDatabase, at: Optional[Timestamp] = None) -> None:
1200
+ async def delete(
1201
+ self, db: InfrahubDatabase, at: Optional[Timestamp] = None
1202
+ ) -> RelationshipCardinalityManyChangelog | RelationshipCardinalityOneChangelog:
1167
1203
  """Delete all the relationships."""
1168
1204
 
1169
1205
  delete_at = Timestamp(at)
1206
+ relationship_mapper = ChangelogRelationshipMapper(schema=self.schema)
1170
1207
 
1171
1208
  await self._fetch_relationships(at=delete_at, db=db, force_refresh=True)
1172
1209
 
1173
1210
  for rel in await self.get_relationships(db=db):
1211
+ relationship_mapper.delete_relationship(relationship=rel)
1174
1212
  await rel.delete(at=delete_at, db=db)
1175
1213
 
1214
+ return relationship_mapper.changelog
1215
+
1176
1216
  async def to_graphql(
1177
1217
  self, db: InfrahubDatabase, fields: Optional[dict] = None, related_node_ids: Optional[set] = None
1178
1218
  ) -> Union[dict, None]:
@@ -1188,7 +1228,7 @@ class RelationshipManager:
1188
1228
 
1189
1229
  async def _validate_hierarchy(self) -> None:
1190
1230
  schema = self.node.get_schema()
1191
- if schema.is_profile_schema or not schema.hierarchy: # type: ignore[union-attr]
1231
+ if schema.is_profile_schema or schema.is_template_schema or not schema.hierarchy: # type: ignore[union-attr]
1192
1232
  return
1193
1233
 
1194
1234
  if self.name == "parent" and not schema.parent: # type: ignore[union-attr]
@@ -1,7 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import uuid
4
- from typing import Any, Optional, TypeAlias, Union
4
+ from typing import Any, TypeAlias
5
5
 
6
6
  from infrahub_sdk.utils import deep_merge_dict
7
7
  from pydantic import BaseModel, ConfigDict, Field
@@ -19,8 +19,9 @@ from .generic_schema import GenericSchema
19
19
  from .node_schema import NodeSchema
20
20
  from .profile_schema import ProfileSchema
21
21
  from .relationship_schema import RelationshipSchema
22
+ from .template_schema import TemplateSchema
22
23
 
23
- MainSchemaTypes: TypeAlias = Union[NodeSchema, GenericSchema, ProfileSchema]
24
+ MainSchemaTypes: TypeAlias = NodeSchema | GenericSchema | ProfileSchema | TemplateSchema
24
25
 
25
26
 
26
27
  # -----------------------------------------------------
@@ -44,7 +45,7 @@ class SchemaExtension(HashableModel):
44
45
 
45
46
  class SchemaRoot(BaseModel):
46
47
  model_config = ConfigDict(extra="forbid")
47
- version: Optional[str] = Field(default=None)
48
+ version: str | None = Field(default=None)
48
49
  generics: list[GenericSchema] = Field(default_factory=list)
49
50
  nodes: list[NodeSchema] = Field(default_factory=list)
50
51
  extensions: SchemaExtension = SchemaExtension()
@@ -59,7 +60,7 @@ class SchemaRoot(BaseModel):
59
60
 
60
61
  return True
61
62
 
62
- def get(self, name: str) -> Union[NodeSchema, GenericSchema]:
63
+ def get(self, name: str) -> NodeSchema | GenericSchema:
63
64
  """Check if a schema exist locally as a node or as a generic."""
64
65
 
65
66
  for item in self.nodes + self.generics:
@@ -107,6 +108,7 @@ __all__ = [
107
108
  "SchemaAttributePath",
108
109
  "SchemaAttributePathValue",
109
110
  "SchemaRoot",
111
+ "TemplateSchema",
110
112
  "core_models",
111
113
  "internal_schema",
112
114
  ]