infrahub-server 1.2.11__py3-none-any.whl → 1.3.0__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 (211) hide show
  1. infrahub/actions/constants.py +130 -0
  2. infrahub/actions/gather.py +114 -0
  3. infrahub/actions/models.py +243 -0
  4. infrahub/actions/parsers.py +104 -0
  5. infrahub/actions/schema.py +393 -0
  6. infrahub/actions/tasks.py +119 -0
  7. infrahub/actions/triggers.py +21 -0
  8. infrahub/branch/__init__.py +0 -0
  9. infrahub/branch/tasks.py +29 -0
  10. infrahub/branch/triggers.py +22 -0
  11. infrahub/cli/db.py +3 -4
  12. infrahub/computed_attribute/gather.py +3 -1
  13. infrahub/computed_attribute/tasks.py +23 -29
  14. infrahub/core/account.py +24 -47
  15. infrahub/core/attribute.py +13 -15
  16. infrahub/core/constants/__init__.py +10 -0
  17. infrahub/core/constants/database.py +1 -0
  18. infrahub/core/constants/infrahubkind.py +9 -0
  19. infrahub/core/constraint/node/runner.py +3 -1
  20. infrahub/core/convert_object_type/__init__.py +0 -0
  21. infrahub/core/convert_object_type/conversion.py +124 -0
  22. infrahub/core/convert_object_type/schema_mapping.py +56 -0
  23. infrahub/core/diff/coordinator.py +8 -1
  24. infrahub/core/diff/query/all_conflicts.py +1 -5
  25. infrahub/core/diff/query/artifact.py +10 -20
  26. infrahub/core/diff/query/delete_query.py +8 -4
  27. infrahub/core/diff/query/diff_get.py +3 -6
  28. infrahub/core/diff/query/field_specifiers.py +1 -1
  29. infrahub/core/diff/query/field_summary.py +2 -4
  30. infrahub/core/diff/query/merge.py +72 -125
  31. infrahub/core/diff/query/save.py +83 -68
  32. infrahub/core/diff/query/summary_counts_enricher.py +34 -54
  33. infrahub/core/diff/query/time_range_query.py +0 -1
  34. infrahub/core/diff/repository/repository.py +4 -0
  35. infrahub/core/graph/__init__.py +1 -1
  36. infrahub/core/manager.py +14 -11
  37. infrahub/core/migrations/graph/__init__.py +6 -0
  38. infrahub/core/migrations/graph/m003_relationship_parent_optional.py +1 -2
  39. infrahub/core/migrations/graph/m012_convert_account_generic.py +1 -1
  40. infrahub/core/migrations/graph/m013_convert_git_password_credential.py +2 -6
  41. infrahub/core/migrations/graph/m015_diff_format_update.py +1 -2
  42. infrahub/core/migrations/graph/m016_diff_delete_bug_fix.py +1 -2
  43. infrahub/core/migrations/graph/m019_restore_rels_to_time.py +11 -22
  44. infrahub/core/migrations/graph/m020_duplicate_edges.py +3 -6
  45. infrahub/core/migrations/graph/m021_missing_hierarchy_merge.py +1 -2
  46. infrahub/core/migrations/graph/m023_deduplicate_cardinality_one_relationships.py +2 -2
  47. infrahub/core/migrations/graph/m024_missing_hierarchy_backfill.py +1 -2
  48. infrahub/core/migrations/graph/m028_delete_diffs.py +1 -2
  49. infrahub/core/migrations/graph/m029_duplicates_cleanup.py +662 -0
  50. infrahub/core/migrations/graph/m030_illegal_edges.py +82 -0
  51. infrahub/core/migrations/query/attribute_add.py +14 -11
  52. infrahub/core/migrations/query/attribute_rename.py +6 -11
  53. infrahub/core/migrations/query/delete_element_in_schema.py +19 -17
  54. infrahub/core/migrations/query/node_duplicate.py +19 -21
  55. infrahub/core/migrations/query/relationship_duplicate.py +19 -18
  56. infrahub/core/migrations/schema/node_attribute_remove.py +4 -8
  57. infrahub/core/migrations/schema/node_remove.py +19 -20
  58. infrahub/core/models.py +29 -2
  59. infrahub/core/node/__init__.py +131 -28
  60. infrahub/core/node/base.py +1 -1
  61. infrahub/core/node/create.py +211 -0
  62. infrahub/core/node/resource_manager/number_pool.py +31 -5
  63. infrahub/core/node/standard.py +6 -1
  64. infrahub/core/path.py +15 -1
  65. infrahub/core/protocols.py +57 -0
  66. infrahub/core/protocols_base.py +3 -0
  67. infrahub/core/query/__init__.py +2 -2
  68. infrahub/core/query/delete.py +3 -3
  69. infrahub/core/query/diff.py +19 -32
  70. infrahub/core/query/ipam.py +10 -20
  71. infrahub/core/query/node.py +29 -47
  72. infrahub/core/query/relationship.py +55 -34
  73. infrahub/core/query/resource_manager.py +1 -2
  74. infrahub/core/query/standard_node.py +19 -5
  75. infrahub/core/query/subquery.py +2 -4
  76. infrahub/core/relationship/constraints/count.py +10 -9
  77. infrahub/core/relationship/constraints/interface.py +2 -1
  78. infrahub/core/relationship/constraints/peer_kind.py +2 -1
  79. infrahub/core/relationship/constraints/peer_parent.py +56 -0
  80. infrahub/core/relationship/constraints/peer_relatives.py +72 -0
  81. infrahub/core/relationship/constraints/profiles_kind.py +1 -1
  82. infrahub/core/relationship/model.py +4 -1
  83. infrahub/core/schema/__init__.py +2 -1
  84. infrahub/core/schema/attribute_parameters.py +160 -0
  85. infrahub/core/schema/attribute_schema.py +130 -7
  86. infrahub/core/schema/basenode_schema.py +27 -3
  87. infrahub/core/schema/definitions/core/__init__.py +29 -1
  88. infrahub/core/schema/definitions/core/group.py +45 -0
  89. infrahub/core/schema/definitions/core/resource_pool.py +9 -0
  90. infrahub/core/schema/definitions/internal.py +43 -5
  91. infrahub/core/schema/generated/attribute_schema.py +16 -3
  92. infrahub/core/schema/generated/relationship_schema.py +11 -1
  93. infrahub/core/schema/manager.py +7 -2
  94. infrahub/core/schema/schema_branch.py +109 -12
  95. infrahub/core/validators/__init__.py +15 -2
  96. infrahub/core/validators/attribute/choices.py +1 -3
  97. infrahub/core/validators/attribute/enum.py +1 -3
  98. infrahub/core/validators/attribute/kind.py +1 -3
  99. infrahub/core/validators/attribute/length.py +13 -7
  100. infrahub/core/validators/attribute/min_max.py +118 -0
  101. infrahub/core/validators/attribute/number_pool.py +106 -0
  102. infrahub/core/validators/attribute/optional.py +1 -4
  103. infrahub/core/validators/attribute/regex.py +5 -6
  104. infrahub/core/validators/attribute/unique.py +1 -3
  105. infrahub/core/validators/determiner.py +18 -2
  106. infrahub/core/validators/enum.py +12 -0
  107. infrahub/core/validators/node/hierarchy.py +3 -6
  108. infrahub/core/validators/query.py +1 -3
  109. infrahub/core/validators/relationship/count.py +6 -12
  110. infrahub/core/validators/relationship/optional.py +2 -4
  111. infrahub/core/validators/relationship/peer.py +177 -12
  112. infrahub/core/validators/tasks.py +1 -1
  113. infrahub/core/validators/uniqueness/query.py +5 -9
  114. infrahub/database/__init__.py +12 -4
  115. infrahub/database/validation.py +100 -0
  116. infrahub/dependencies/builder/constraint/grouped/node_runner.py +4 -0
  117. infrahub/dependencies/builder/constraint/relationship_manager/peer_parent.py +8 -0
  118. infrahub/dependencies/builder/constraint/relationship_manager/peer_relatives.py +8 -0
  119. infrahub/dependencies/builder/constraint/schema/aggregated.py +2 -0
  120. infrahub/dependencies/builder/constraint/schema/relationship_peer.py +8 -0
  121. infrahub/dependencies/builder/diff/deserializer.py +1 -1
  122. infrahub/dependencies/registry.py +4 -0
  123. infrahub/events/group_action.py +1 -0
  124. infrahub/events/models.py +1 -1
  125. infrahub/git/base.py +5 -3
  126. infrahub/git/integrator.py +96 -5
  127. infrahub/git/tasks.py +1 -0
  128. infrahub/graphql/analyzer.py +139 -18
  129. infrahub/graphql/manager.py +4 -0
  130. infrahub/graphql/mutations/action.py +164 -0
  131. infrahub/graphql/mutations/convert_object_type.py +71 -0
  132. infrahub/graphql/mutations/main.py +25 -176
  133. infrahub/graphql/mutations/proposed_change.py +20 -17
  134. infrahub/graphql/mutations/relationship.py +32 -0
  135. infrahub/graphql/mutations/resource_manager.py +63 -7
  136. infrahub/graphql/queries/convert_object_type_mapping.py +34 -0
  137. infrahub/graphql/queries/resource_manager.py +7 -1
  138. infrahub/graphql/resolvers/many_relationship.py +1 -1
  139. infrahub/graphql/resolvers/resolver.py +2 -2
  140. infrahub/graphql/resolvers/single_relationship.py +1 -1
  141. infrahub/graphql/schema.py +6 -0
  142. infrahub/menu/menu.py +34 -2
  143. infrahub/message_bus/messages/__init__.py +0 -10
  144. infrahub/message_bus/operations/__init__.py +0 -8
  145. infrahub/message_bus/operations/refresh/registry.py +4 -7
  146. infrahub/patch/queries/delete_duplicated_edges.py +45 -39
  147. infrahub/pools/models.py +14 -0
  148. infrahub/pools/number.py +5 -3
  149. infrahub/pools/registration.py +22 -0
  150. infrahub/pools/tasks.py +126 -0
  151. infrahub/prefect_server/models.py +1 -19
  152. infrahub/proposed_change/models.py +68 -3
  153. infrahub/proposed_change/tasks.py +911 -34
  154. infrahub/schema/__init__.py +0 -0
  155. infrahub/schema/tasks.py +27 -0
  156. infrahub/schema/triggers.py +23 -0
  157. infrahub/task_manager/models.py +10 -6
  158. infrahub/trigger/catalogue.py +6 -0
  159. infrahub/trigger/models.py +23 -6
  160. infrahub/trigger/setup.py +26 -2
  161. infrahub/trigger/tasks.py +4 -2
  162. infrahub/types.py +6 -0
  163. infrahub/webhook/tasks.py +6 -9
  164. infrahub/workflows/catalogue.py +103 -1
  165. infrahub_sdk/client.py +43 -10
  166. infrahub_sdk/ctl/generator.py +4 -4
  167. infrahub_sdk/ctl/repository.py +1 -1
  168. infrahub_sdk/node/__init__.py +39 -0
  169. infrahub_sdk/node/attribute.py +122 -0
  170. infrahub_sdk/node/constants.py +21 -0
  171. infrahub_sdk/{node.py → node/node.py} +158 -803
  172. infrahub_sdk/node/parsers.py +15 -0
  173. infrahub_sdk/node/property.py +24 -0
  174. infrahub_sdk/node/related_node.py +266 -0
  175. infrahub_sdk/node/relationship.py +302 -0
  176. infrahub_sdk/protocols.py +112 -0
  177. infrahub_sdk/protocols_base.py +34 -2
  178. infrahub_sdk/pytest_plugin/items/python_transform.py +2 -1
  179. infrahub_sdk/query_groups.py +17 -5
  180. infrahub_sdk/schema/main.py +1 -0
  181. infrahub_sdk/schema/repository.py +16 -0
  182. infrahub_sdk/spec/object.py +1 -1
  183. infrahub_sdk/store.py +1 -1
  184. infrahub_sdk/testing/schemas/car_person.py +1 -0
  185. infrahub_sdk/utils.py +7 -20
  186. infrahub_sdk/yaml.py +6 -5
  187. {infrahub_server-1.2.11.dist-info → infrahub_server-1.3.0.dist-info}/METADATA +5 -5
  188. {infrahub_server-1.2.11.dist-info → infrahub_server-1.3.0.dist-info}/RECORD +197 -168
  189. {infrahub_server-1.2.11.dist-info → infrahub_server-1.3.0.dist-info}/WHEEL +1 -1
  190. infrahub_testcontainers/container.py +239 -65
  191. infrahub_testcontainers/docker-compose-cluster.test.yml +321 -0
  192. infrahub_testcontainers/docker-compose.test.yml +2 -1
  193. infrahub_testcontainers/helpers.py +23 -3
  194. infrahub_testcontainers/plugin.py +9 -0
  195. infrahub/message_bus/messages/check_generator_run.py +0 -26
  196. infrahub/message_bus/messages/finalize_validator_execution.py +0 -15
  197. infrahub/message_bus/messages/proposed_change/base_with_diff.py +0 -16
  198. infrahub/message_bus/messages/proposed_change/request_proposedchange_refreshartifacts.py +0 -11
  199. infrahub/message_bus/messages/request_generatordefinition_check.py +0 -20
  200. infrahub/message_bus/messages/request_proposedchange_pipeline.py +0 -23
  201. infrahub/message_bus/operations/check/__init__.py +0 -3
  202. infrahub/message_bus/operations/check/generator.py +0 -156
  203. infrahub/message_bus/operations/finalize/__init__.py +0 -3
  204. infrahub/message_bus/operations/finalize/validator.py +0 -133
  205. infrahub/message_bus/operations/requests/__init__.py +0 -9
  206. infrahub/message_bus/operations/requests/generator_definition.py +0 -140
  207. infrahub/message_bus/operations/requests/proposed_change.py +0 -629
  208. infrahub/patch/queries/consolidate_duplicated_nodes.py +0 -109
  209. /infrahub/{message_bus/messages/proposed_change → actions}/__init__.py +0 -0
  210. {infrahub_server-1.2.11.dist-info → infrahub_server-1.3.0.dist-info}/LICENSE.txt +0 -0
  211. {infrahub_server-1.2.11.dist-info → infrahub_server-1.3.0.dist-info}/entry_points.txt +0 -0
@@ -207,12 +207,11 @@ class NodeCreateAllQuery(NodeQuery):
207
207
  attrs_query = """
208
208
  WITH distinct n
209
209
  UNWIND $attrs AS attr
210
- CALL {
211
- WITH n, attr
210
+ CALL (n, attr) {
212
211
  CREATE (a:Attribute { uuid: attr.uuid, name: attr.name, branch_support: attr.branch_support })
213
212
  CREATE (n)-[:HAS_ATTRIBUTE { branch: attr.branch, branch_level: attr.branch_level, status: attr.status, from: $at }]->(a)
214
213
  MERGE (av:AttributeValue { value: attr.content.value, is_default: attr.content.is_default })
215
- WITH n, attr, av, a
214
+ WITH av, a
216
215
  LIMIT 1
217
216
  CREATE (a)-[:HAS_VALUE { branch: attr.branch, branch_level: attr.branch_level, status: attr.status, from: $at }]->(av)
218
217
  MERGE (ip:Boolean { value: attr.is_protected })
@@ -232,13 +231,12 @@ class NodeCreateAllQuery(NodeQuery):
232
231
  attrs_iphost_query = """
233
232
  WITH distinct n
234
233
  UNWIND $attrs_iphost AS attr_iphost
235
- CALL {
236
- WITH n, attr_iphost
234
+ CALL (n, attr_iphost) {
237
235
  WITH n, attr_iphost AS attr
238
236
  CREATE (a:Attribute { uuid: attr.uuid, name: attr.name, branch_support: attr.branch_support })
239
237
  CREATE (n)-[:HAS_ATTRIBUTE { branch: attr.branch, branch_level: attr.branch_level, status: attr.status, from: $at }]->(a)
240
238
  MERGE (av:AttributeValue:AttributeIPHost { %(iphost_prop)s })
241
- WITH n, attr, av, a
239
+ WITH attr, av, a
242
240
  LIMIT 1
243
241
  CREATE (a)-[:HAS_VALUE { branch: attr.branch, branch_level: attr.branch_level, status: attr.status, from: $at }]->(av)
244
242
  MERGE (ip:Boolean { value: attr.is_protected })
@@ -259,13 +257,12 @@ class NodeCreateAllQuery(NodeQuery):
259
257
  attrs_ipnetwork_query = """
260
258
  WITH distinct n
261
259
  UNWIND $attrs_ipnetwork AS attr_ipnetwork
262
- CALL {
263
- WITH n, attr_ipnetwork
260
+ CALL (n, attr_ipnetwork) {
264
261
  WITH n, attr_ipnetwork AS attr
265
262
  CREATE (a:Attribute { uuid: attr.uuid, name: attr.name, branch_support: attr.branch_support })
266
263
  CREATE (n)-[:HAS_ATTRIBUTE { branch: attr.branch, branch_level: attr.branch_level, status: attr.status, from: $at }]->(a)
267
264
  MERGE (av:AttributeValue:AttributeIPNetwork { %(ipnetwork_prop)s })
268
- WITH n, attr, av, a
265
+ WITH attr, av, a
269
266
  LIMIT 1
270
267
  CREATE (a)-[:HAS_VALUE { branch: attr.branch, branch_level: attr.branch_level, status: attr.status, from: $at }]->(av)
271
268
  MERGE (ip:Boolean { value: attr.is_protected })
@@ -286,8 +283,7 @@ class NodeCreateAllQuery(NodeQuery):
286
283
  rels_bidir_query = """
287
284
  WITH distinct n
288
285
  UNWIND $rels_bidir AS rel
289
- CALL {
290
- WITH n, rel
286
+ CALL (n, rel) {
291
287
  MERGE (d:Node { uuid: rel.destination_id })
292
288
  CREATE (rl:Relationship { uuid: rel.uuid, name: rel.name, branch_support: rel.branch_support })
293
289
  CREATE (n)-[:IS_RELATED %(rel_prop)s ]->(rl)
@@ -310,8 +306,7 @@ class NodeCreateAllQuery(NodeQuery):
310
306
  rels_out_query = """
311
307
  WITH distinct n
312
308
  UNWIND $rels_out AS rel_out
313
- CALL {
314
- WITH n, rel_out
309
+ CALL (n, rel_out) {
315
310
  WITH n, rel_out as rel
316
311
  MERGE (d:Node { uuid: rel.destination_id })
317
312
  CREATE (rl:Relationship { uuid: rel.uuid, name: rel.name, branch_support: rel.branch_support })
@@ -335,8 +330,7 @@ class NodeCreateAllQuery(NodeQuery):
335
330
  rels_in_query = """
336
331
  WITH distinct n
337
332
  UNWIND $rels_in AS rel_in
338
- CALL {
339
- WITH n, rel_in
333
+ CALL (n, rel_in) {
340
334
  WITH n, rel_in AS rel
341
335
  MERGE (d:Node { uuid: rel.destination_id })
342
336
  CREATE (rl:Relationship { uuid: rel.uuid, name: rel.name, branch_support: rel.branch_support })
@@ -424,8 +418,7 @@ class NodeDeleteQuery(NodeQuery):
424
418
  node_filter, node_filter_params = self.branch.get_query_filter_path(at=self.at, variable_name="r")
425
419
  node_query_match = """
426
420
  MATCH (n:Node { uuid: $uuid })
427
- CALL {
428
- WITH n
421
+ CALL (n) {
429
422
  MATCH (n)-[r:IS_PART_OF]->(:Root)
430
423
  WHERE %(node_filter)s
431
424
  RETURN r.status = "active" AS is_active
@@ -520,8 +513,7 @@ class NodeListGetAttributeQuery(Query):
520
513
  self.add_to_query(query)
521
514
 
522
515
  query = """
523
- CALL {
524
- WITH n, a
516
+ CALL (n, a) {
525
517
  MATCH (n)-[r:HAS_ATTRIBUTE]-(a:Attribute)
526
518
  WHERE %(branch_filter)s
527
519
  RETURN n as n1, r as r1, a as a1
@@ -533,8 +525,7 @@ class NodeListGetAttributeQuery(Query):
533
525
  WITH n, r1, a
534
526
  MATCH (a)-[r:HAS_VALUE]-(av:AttributeValue)
535
527
  WHERE %(branch_filter)s
536
- CALL {
537
- WITH a, av
528
+ CALL (a, av) {
538
529
  MATCH (a)-[r:HAS_VALUE]-(av:AttributeValue)
539
530
  WHERE %(branch_filter)s
540
531
  RETURN a as a1, r as r2, av as av1
@@ -698,14 +689,12 @@ class NodeListGetRelationshipsQuery(Query):
698
689
 
699
690
  query = """
700
691
  MATCH (n:Node) WHERE n.uuid IN $ids
701
- CALL {
702
- WITH n
692
+ CALL (n) {
703
693
  MATCH (n)<-[:IS_RELATED]-(rel:Relationship)<-[:IS_RELATED]-(peer)
704
694
  WHERE ($inbound_identifiers IS NULL OR rel.name in $inbound_identifiers)
705
695
  AND n.uuid <> peer.uuid
706
696
  WITH DISTINCT n, rel, peer
707
- CALL {
708
- WITH n, rel, peer
697
+ CALL (n, rel, peer) {
709
698
  MATCH (n)<-[r:IS_RELATED]-(rel)
710
699
  WHERE (%(filters)s)
711
700
  WITH n, rel, peer, r
@@ -729,8 +718,7 @@ class NodeListGetRelationshipsQuery(Query):
729
718
  WHERE ($outbound_identifiers IS NULL OR rel.name in $outbound_identifiers)
730
719
  AND n.uuid <> peer.uuid
731
720
  WITH DISTINCT n, rel, peer
732
- CALL {
733
- WITH n, rel, peer
721
+ CALL (n, rel, peer) {
734
722
  MATCH (n)-[r:IS_RELATED]->(rel)
735
723
  WHERE (%(filters)s)
736
724
  WITH n, rel, peer, r
@@ -754,8 +742,7 @@ class NodeListGetRelationshipsQuery(Query):
754
742
  WHERE ($bidirectional_identifiers IS NULL OR rel.name in $bidirectional_identifiers)
755
743
  AND n.uuid <> peer.uuid
756
744
  WITH DISTINCT n, rel, peer
757
- CALL {
758
- WITH n, rel, peer
745
+ CALL (n, rel, peer) {
759
746
  MATCH (n)-[r:IS_RELATED]->(rel)
760
747
  WHERE (%(filters)s)
761
748
  WITH n, rel, peer, r
@@ -849,8 +836,7 @@ class NodeListGetInfoQuery(Query):
849
836
  query = """
850
837
  MATCH p = (root:Root)<-[:IS_PART_OF]-(n:Node)
851
838
  WHERE n.uuid IN $ids
852
- CALL {
853
- WITH root, n
839
+ CALL (root, n) {
854
840
  MATCH (root:Root)<-[r:IS_PART_OF]-(n:Node)
855
841
  WHERE %(branch_filter)s
856
842
  RETURN n as n1, r as r1
@@ -1046,8 +1032,7 @@ class NodeGetListQuery(Query):
1046
1032
  if not self.branch.is_default:
1047
1033
  topquery = """
1048
1034
  MATCH (n:%(node_kind)s)
1049
- CALL {
1050
- WITH n
1035
+ CALL (n) {
1051
1036
  MATCH (root:Root)<-[r:IS_PART_OF]-(n)
1052
1037
  WHERE %(branch_filter)s
1053
1038
  RETURN r
@@ -1152,7 +1137,7 @@ class NodeGetListQuery(Query):
1152
1137
  )
1153
1138
 
1154
1139
  filter_params.update(subquery_params)
1155
- filter_query.append("CALL {")
1140
+ filter_query.append("CALL (n) {")
1156
1141
  filter_query.append(subquery)
1157
1142
  filter_query.append("}")
1158
1143
  filter_query.append(f"WITH {with_str}")
@@ -1201,7 +1186,7 @@ class NodeGetListQuery(Query):
1201
1186
  with_str = ", ".join(self._get_tracked_variables())
1202
1187
 
1203
1188
  sort_params.update(subquery_params)
1204
- sort_query.append("CALL {")
1189
+ sort_query.append("CALL (n) {")
1205
1190
  sort_query.append(subquery)
1206
1191
  sort_query.append("}")
1207
1192
  sort_query.append(f"WITH {with_str}")
@@ -1215,8 +1200,7 @@ class NodeGetListQuery(Query):
1215
1200
  froms_str = db.render_list_comprehension(items="relationships(profile_path)", item_name="from")
1216
1201
  profiles_per_node_query = (
1217
1202
  """
1218
- CALL {
1219
- WITH n
1203
+ CALL (n) {
1220
1204
  OPTIONAL MATCH profile_path = (n)-[:IS_RELATED]->(profile_r:Relationship)<-[:IS_RELATED]-(maybe_profile_n:Node)-[:IS_PART_OF]->(:Root)
1221
1205
  WHERE profile_r.name = "node__profile"
1222
1206
  AND all(r in relationships(profile_path) WHERE %(branch_filter)s)
@@ -1234,8 +1218,7 @@ class NodeGetListQuery(Query):
1234
1218
  WITH %(with_str)s, CASE
1235
1219
  WHEN ordered_is_actives[0] = True THEN maybe_profile_n ELSE NULL
1236
1220
  END AS profile_n
1237
- CALL {
1238
- WITH profile_n
1221
+ CALL (profile_n) {
1239
1222
  OPTIONAL MATCH profile_priority_path = (profile_n)-[pr1:HAS_ATTRIBUTE]->(a:Attribute)-[pr2:HAS_VALUE]->(av:AttributeValue)
1240
1223
  WHERE a.name = "profile_priority"
1241
1224
  AND all(r in relationships(profile_priority_path) WHERE %(branch_filter)s and r.status = "active")
@@ -1276,7 +1259,7 @@ class NodeGetListQuery(Query):
1276
1259
  self._track_variable(profile_attr.profile_value_query_variable)
1277
1260
  with_str = ", ".join(self._get_tracked_variables())
1278
1261
 
1279
- attributes_queries.append("CALL {")
1262
+ attributes_queries.append("CALL (profile_n) {")
1280
1263
  attributes_queries.append(subquery)
1281
1264
  attributes_queries.append("}")
1282
1265
  attributes_queries.append(f"WITH {with_str}")
@@ -1457,17 +1440,16 @@ class NodeGetHierarchyQuery(Query):
1457
1440
  MATCH path = (n:Node { uuid: $uuid } )%(filter)s(peer:Node)
1458
1441
  WHERE $hierarchy IN LABELS(peer) and all(r IN relationships(path) WHERE (%(branch_filter)s))
1459
1442
  WITH n, collect(last(nodes(path))) AS peers_with_duplicates
1460
- CALL {
1461
- WITH peers_with_duplicates
1443
+ CALL (peers_with_duplicates) {
1462
1444
  UNWIND peers_with_duplicates AS pwd
1463
1445
  RETURN DISTINCT pwd AS peer
1464
1446
  }
1447
+
1465
1448
  """ % {"filter": filter_str, "branch_filter": branch_filter}
1466
1449
 
1467
1450
  if not self.branch.is_default:
1468
1451
  query += """
1469
- CALL {
1470
- WITH n, peer
1452
+ CALL (n, peer) {
1471
1453
  MATCH path = (n)%(filter)s(peer)
1472
1454
  WHERE all(r IN relationships(path) WHERE (%(branch_filter)s))
1473
1455
  WITH %(with_clause)s
@@ -1487,7 +1469,7 @@ class NodeGetHierarchyQuery(Query):
1487
1469
 
1488
1470
  clean_filters = extract_field_filters(field_name=self.direction.value, filters=self.filters)
1489
1471
 
1490
- if clean_filters and "id" in clean_filters or "ids" in clean_filters:
1472
+ if (clean_filters and "id" in clean_filters) or "ids" in clean_filters:
1491
1473
  where_clause.append("peer.uuid IN $peer_ids")
1492
1474
  self.params["peer_ids"] = clean_filters.get("ids", [])
1493
1475
  if clean_filters.get("id", None):
@@ -1532,7 +1514,7 @@ class NodeGetHierarchyQuery(Query):
1532
1514
  [f"{subquery_result_name} as {label}" if label == "peer" else label for label in self.return_labels]
1533
1515
  )
1534
1516
 
1535
- self.add_subquery(subquery=subquery, with_clause=with_str)
1517
+ self.add_subquery(subquery=subquery, node_alias="peer", with_clause=with_str)
1536
1518
 
1537
1519
  # ----------------------------------------------------------------------------
1538
1520
  # ORDER Results
@@ -1560,7 +1542,7 @@ class NodeGetHierarchyQuery(Query):
1560
1542
  self.order_by.append(subquery_result_name)
1561
1543
  self.params.update(subquery_params)
1562
1544
 
1563
- self.add_subquery(subquery=subquery)
1545
+ self.add_subquery(subquery=subquery, node_alias="peer")
1564
1546
 
1565
1547
  order_cnt += 1
1566
1548
  else:
@@ -217,8 +217,7 @@ class RelationshipQuery(Query):
217
217
  )
218
218
  source_query_match = """
219
219
  MATCH (s:Node { uuid: $source_id })
220
- CALL {
221
- WITH s
220
+ CALL (s) {
222
221
  MATCH (s)-[r:IS_PART_OF]->(:Root)
223
222
  WHERE %(source_filter)s
224
223
  RETURN r.status = "active" AS s_is_active
@@ -246,8 +245,7 @@ class RelationshipQuery(Query):
246
245
  )
247
246
  destination_query_match = """
248
247
  MATCH (d:Node { uuid: $destination_id })
249
- CALL {
250
- WITH d
248
+ CALL (d) {
251
249
  MATCH (d)-[r:IS_PART_OF]->(:Root)
252
250
  WHERE %(destination_filter)s
253
251
  RETURN r.status = "active" AS d_is_active
@@ -524,8 +522,7 @@ class RelationshipDeleteQuery(RelationshipQuery):
524
522
  CREATE (s)%(r1)s(rl)
525
523
  CREATE (rl)%(r2)s(d)
526
524
  WITH rl
527
- CALL {
528
- WITH rl
525
+ CALL (rl) {
529
526
  MATCH (rl)-[edge:IS_VISIBLE]->(visible)
530
527
  WHERE %(rel_filter)s AND edge.status = "active"
531
528
  WITH rl, edge, visible
@@ -536,8 +533,7 @@ class RelationshipDeleteQuery(RelationshipQuery):
536
533
  WHERE edge.branch = $branch
537
534
  SET edge.to = $at
538
535
  }
539
- CALL {
540
- WITH rl
536
+ CALL (rl) {
541
537
  MATCH (rl)-[edge:IS_PROTECTED]->(protected)
542
538
  WHERE %(rel_filter)s AND edge.status = "active"
543
539
  WITH rl, edge, protected
@@ -548,8 +544,7 @@ class RelationshipDeleteQuery(RelationshipQuery):
548
544
  WHERE edge.branch = $branch
549
545
  SET edge.to = $at
550
546
  }
551
- CALL {
552
- WITH rl
547
+ CALL (rl) {
553
548
  MATCH (rl)-[edge:HAS_OWNER]->(owner_node)
554
549
  WHERE %(rel_filter)s AND edge.status = "active"
555
550
  WITH rl, edge, owner_node
@@ -560,8 +555,7 @@ class RelationshipDeleteQuery(RelationshipQuery):
560
555
  WHERE edge.branch = $branch
561
556
  SET edge.to = $at
562
557
  }
563
- CALL {
564
- WITH rl
558
+ CALL (rl) {
565
559
  MATCH (rl)-[edge:HAS_SOURCE]->(source_node)
566
560
  WHERE %(rel_filter)s AND edge.status = "active"
567
561
  WITH rl, edge, source_node
@@ -656,8 +650,7 @@ class RelationshipGetPeerQuery(Query):
656
650
  MATCH (source_node:Node)%(arrow_left_start)s[:IS_RELATED]%(arrow_left_end)s(rl:Relationship { name: $rel_identifier })
657
651
  WHERE source_node.uuid IN $source_ids
658
652
  WITH DISTINCT source_node, rl
659
- CALL {
660
- WITH rl, source_node
653
+ CALL (rl, source_node) {
661
654
  MATCH path = (source_node)%(path)s(peer:Node)
662
655
  WHERE
663
656
  $source_kind IN LABELS(source_node) AND
@@ -683,7 +676,7 @@ class RelationshipGetPeerQuery(Query):
683
676
  where_clause = ['all(r IN rels WHERE r.status = "active")']
684
677
  clean_filters = extract_field_filters(field_name=self.schema.name, filters=self.filters)
685
678
 
686
- if clean_filters and "id" in clean_filters or "ids" in clean_filters:
679
+ if (clean_filters and "id" in clean_filters) or "ids" in clean_filters:
687
680
  where_clause.append("peer.uuid IN $peer_ids")
688
681
  self.params["peer_ids"] = clean_filters.get("ids", [])
689
682
  if clean_filters.get("id", None):
@@ -726,22 +719,19 @@ class RelationshipGetPeerQuery(Query):
726
719
  with_str = ", ".join(
727
720
  [f"{subquery_result_name} as {label}" if label == "peer" else label for label in self.return_labels]
728
721
  )
729
- self.add_subquery(subquery=subquery, with_clause=with_str)
730
-
722
+ self.add_subquery(subquery=subquery, node_alias="peer", with_clause=with_str)
731
723
  # ----------------------------------------------------------------------------
732
724
  # QUERY Properties
733
725
  # ----------------------------------------------------------------------------
734
726
  query = """
735
- CALL {
736
- WITH rl
727
+ CALL (rl) {
737
728
  MATCH (rl)-[r:IS_VISIBLE]-(is_visible)
738
729
  WHERE %(branch_filter)s
739
730
  RETURN r AS rel_is_visible, is_visible
740
731
  ORDER BY r.branch_level DESC, r.from DESC, r.status ASC
741
732
  LIMIT 1
742
733
  }
743
- CALL {
744
- WITH rl
734
+ CALL (rl) {
745
735
  MATCH (rl)-[r:IS_PROTECTED]-(is_protected)
746
736
  WHERE %(branch_filter)s
747
737
  RETURN r AS rel_is_protected, is_protected
@@ -758,8 +748,7 @@ class RelationshipGetPeerQuery(Query):
758
748
  # We must query them one by one otherwise the second one won't return
759
749
  for node_prop in ["source", "owner"]:
760
750
  query = """
761
- CALL {
762
- WITH rl
751
+ CALL (rl) {
763
752
  OPTIONAL MATCH (rl)-[r:HAS_%(node_prop_type)s]-(%(node_prop)s)
764
753
  WHERE %(branch_filter)s
765
754
  RETURN r AS rel_%(node_prop)s, %(node_prop)s
@@ -800,7 +789,7 @@ class RelationshipGetPeerQuery(Query):
800
789
  self.order_by.append(subquery_result_name)
801
790
  self.params.update(subquery_params)
802
791
 
803
- self.add_subquery(subquery=subquery)
792
+ self.add_subquery(subquery=subquery, node_alias="peer")
804
793
 
805
794
  order_cnt += 1
806
795
 
@@ -939,8 +928,7 @@ class RelationshipGetByIdentifierQuery(Query):
939
928
  query = """
940
929
  MATCH (rl:Relationship)
941
930
  WHERE rl.name IN $identifiers
942
- CALL {
943
- WITH rl
931
+ CALL (rl) {
944
932
  MATCH (src:Node)-[r1:IS_RELATED]-(rl:Relationship)-[r2:IS_RELATED]-(dst:Node)
945
933
  WHERE (size($full_identifiers) = 0 OR [src.kind, rl.name, dst.kind] in $full_identifiers)
946
934
  AND NOT src.namespace IN $excluded_namespaces
@@ -1003,8 +991,7 @@ class RelationshipCountPerNodeQuery(Query):
1003
991
  query = """
1004
992
  MATCH (peer_node:Node)%(path)s(rl:Relationship { name: $rel_identifier })
1005
993
  WHERE peer_node.uuid IN $peer_ids AND %(branch_filter)s
1006
- CALL {
1007
- WITH rl
994
+ CALL (rl) {
1008
995
  MATCH path = (peer_node:Node)%(path)s(rl)
1009
996
  WHERE peer_node.uuid IN $peer_ids AND %(branch_filter)s
1010
997
  RETURN peer_node as peer, r as r1
@@ -1048,7 +1035,7 @@ class RelationshipDeleteAllQuery(Query):
1048
1035
  self.node_id = node_id
1049
1036
  super().__init__(**kwargs)
1050
1037
 
1051
- async def query_init(self, db: InfrahubDatabase, **kwargs) -> None: # noqa: ARG002
1038
+ async def query_init(self, db: InfrahubDatabase, **kwargs) -> None:
1052
1039
  self.params["source_id"] = kwargs["node_id"]
1053
1040
  self.params["branch"] = self.branch.name
1054
1041
 
@@ -1082,8 +1069,7 @@ class RelationshipDeleteAllQuery(Query):
1082
1069
  for arrow_left, arrow_right in (("<-", "-"), ("-", "->")):
1083
1070
  for edge_type in edge_types:
1084
1071
  sub_query = """
1085
- CALL {
1086
- WITH rl
1072
+ CALL (rl) {
1087
1073
  MATCH (rl)%(arrow_left)s[active_edge:%(edge_type)s]%(arrow_right)s(n)
1088
1074
  WHERE %(active_rel_filter)s AND active_edge.status ="active"
1089
1075
  CREATE (rl)%(arrow_left)s[deleted_edge:%(edge_type)s $rel_prop]%(arrow_right)s(n)
@@ -1103,8 +1089,7 @@ class RelationshipDeleteAllQuery(Query):
1103
1089
 
1104
1090
  # We only want to return uuid/kind of `Node` connected through `IS_RELATED` edges.
1105
1091
  query += """
1106
- CALL {
1107
- WITH rl
1092
+ CALL (rl) {
1108
1093
  MATCH (rl)-[active_edge:IS_RELATED]->(n)
1109
1094
  WHERE %(active_rel_filter)s
1110
1095
  WITH rl, active_edge, n
@@ -1123,7 +1108,6 @@ class RelationshipDeleteAllQuery(Query):
1123
1108
  "outbound" as rel_direction
1124
1109
 
1125
1110
  UNION
1126
-
1127
1111
  WITH rl
1128
1112
  MATCH (rl)<-[active_edge:IS_RELATED]-(n)
1129
1113
  WHERE %(active_rel_filter)s
@@ -1193,3 +1177,40 @@ class RelationshipDeleteAllQuery(Query):
1193
1177
  changelog_mapper.delete_relationship(peer_id=peer_uuid, peer_kind=kind, rel_schema=deleted_rel_schema)
1194
1178
 
1195
1179
  return [changelog_mapper.changelog for changelog_mapper in rel_identifier_to_changelog_mapper.values()]
1180
+
1181
+
1182
+ class GetAllPeersIds(Query):
1183
+ """
1184
+ Return all peers ids connected to input node. Some peers can be excluded using `exclude_identifiers`.
1185
+ """
1186
+
1187
+ name = "get_peers_ids"
1188
+ type: QueryType = QueryType.READ
1189
+ insert_return = False
1190
+
1191
+ def __init__(self, node_id: str, exclude_identifiers: list[str], **kwargs):
1192
+ self.node_id = node_id
1193
+ self.exclude_identifiers = exclude_identifiers
1194
+ super().__init__(**kwargs)
1195
+
1196
+ async def query_init(self, db: InfrahubDatabase, **kwargs) -> None: # noqa: ARG002
1197
+ self.params["source_id"] = kwargs["node_id"]
1198
+ self.params["branch"] = self.branch.name
1199
+ self.params["exclude_identifiers"] = self.exclude_identifiers
1200
+
1201
+ active_rel_filter, rel_params = self.branch.get_query_filter_path(
1202
+ at=self.at, variable_name="e1", branch_agnostic=self.branch_agnostic
1203
+ )
1204
+ self.params.update(rel_params)
1205
+
1206
+ query = """
1207
+ MATCH (node:Node { uuid: $source_id })-[e1:IS_RELATED]-(rl:Relationship)-[e2:IS_RELATED]-(peer:Node)
1208
+ WHERE %(active_rel_filter)s AND peer.uuid <> node.uuid AND NOT (rl.name IN $exclude_identifiers)
1209
+ WITH DISTINCT(peer.uuid) as uuid
1210
+ RETURN uuid
1211
+ """ % {"active_rel_filter": active_rel_filter}
1212
+
1213
+ self.add_to_query(query)
1214
+
1215
+ def get_peers_uuids(self) -> list[str]:
1216
+ return [row.data["uuid"] for row in self.results] # type: ignore
@@ -220,8 +220,7 @@ class NumberPoolGetUsed(Query):
220
220
 
221
221
  query = """
222
222
  MATCH (pool:%(number_pool)s { uuid: $pool_id })
223
- CALL {
224
- WITH pool
223
+ CALL (pool) {
225
224
  MATCH (pool)-[res:IS_RESERVED]->(av:AttributeValue)<-[hv:HAS_VALUE]-(attr:Attribute)
226
225
  WHERE
227
226
  attr.name = $attribute_name
@@ -3,28 +3,42 @@ from __future__ import annotations
3
3
  from typing import TYPE_CHECKING, Any
4
4
 
5
5
  from infrahub.core.query import Query, QueryType
6
+ from infrahub.exceptions import InitializationError
6
7
 
7
8
  if TYPE_CHECKING:
9
+ from uuid import UUID
10
+
8
11
  from infrahub.core.node.standard import StandardNode
9
12
  from infrahub.database import InfrahubDatabase
10
13
 
11
14
 
12
15
  class StandardNodeQuery(Query):
13
16
  def __init__(
14
- self, node: StandardNode = None, node_id: str | None = None, node_db_id: int | None = None, **kwargs: Any
15
- ):
16
- self.node = node
17
+ self,
18
+ node: StandardNode | None = None,
19
+ node_id: UUID | None = None,
20
+ node_db_id: str | None = None,
21
+ **kwargs: Any,
22
+ ) -> None:
23
+ self._node = node
17
24
  self.node_id = node_id
18
25
  self.node_db_id = node_db_id
19
26
 
20
- if not self.node_id and self.node:
27
+ if not self.node_id and self._node:
21
28
  self.node_id = self.node.uuid
22
29
 
23
- if not self.node_db_id and self.node:
30
+ if not self.node_db_id and self._node:
24
31
  self.node_db_id = self.node.id
25
32
 
26
33
  super().__init__(**kwargs)
27
34
 
35
+ @property
36
+ def node(self) -> StandardNode:
37
+ if self._node:
38
+ return self._node
39
+
40
+ raise InitializationError("The query is not initialized with a node")
41
+
28
42
 
29
43
  class RootNodeCreateQuery(StandardNodeQuery):
30
44
  name = "standard_node_create"
@@ -61,7 +61,7 @@ async def build_subquery_filter(
61
61
  where_str = " AND ".join(field_where)
62
62
  branch_level_str = "reduce(br_lvl = 0, r in relationships(path) | br_lvl + r.branch_level)"
63
63
  froms_str = db.render_list_comprehension(items="relationships(path)", item_name="from")
64
- to_return = f"{node_alias} AS {prefix}"
64
+ to_return = f"{prefix}"
65
65
  with_extra = ""
66
66
  final_with_extra = ""
67
67
  is_isnull = filter_name == "isnull"
@@ -82,7 +82,6 @@ async def build_subquery_filter(
82
82
  elif field is not None and field.is_attribute:
83
83
  is_active_filter = "(latest_node_details[2]).value = 'NULL'"
84
84
  query = f"""
85
- WITH {node_alias}
86
85
  {match} path = {filter_str}
87
86
  WHERE {where_str}
88
87
  WITH
@@ -94,7 +93,7 @@ async def build_subquery_filter(
94
93
  ORDER BY branch_level DESC, froms[-1] DESC, froms[-2] DESC
95
94
  WITH head(collect([is_active, {node_alias}{with_extra}])) AS latest_node_details
96
95
  WHERE {is_active_filter}
97
- WITH latest_node_details[1] AS {node_alias}{final_with_extra}
96
+ WITH latest_node_details[1] AS {prefix}{final_with_extra}
98
97
  RETURN {to_return}
99
98
  """
100
99
  return query, params, prefix
@@ -174,7 +173,6 @@ async def build_subquery_order(
174
173
  to_return_str_parts.append(f"CASE WHEN is_active = TRUE THEN {expression} ELSE NULL END AS {alias}")
175
174
  to_return_str = ", ".join(to_return_str_parts)
176
175
  query = f"""
177
- WITH {node_alias}
178
176
  OPTIONAL MATCH path = {filter_str}
179
177
  WHERE {where_str}
180
178
  WITH {with_str_to_alias}
@@ -3,6 +3,7 @@ from dataclasses import dataclass
3
3
  from infrahub.core import registry
4
4
  from infrahub.core.branch import Branch
5
5
  from infrahub.core.constants import RelationshipCardinality, RelationshipDirection
6
+ from infrahub.core.node import Node
6
7
  from infrahub.core.query.relationship import RelationshipCountPerNodeQuery
7
8
  from infrahub.core.schema import MainSchemaTypes
8
9
  from infrahub.database import InfrahubDatabase
@@ -25,7 +26,7 @@ class RelationshipCountConstraint(RelationshipManagerConstraintInterface):
25
26
  self.db = db
26
27
  self.branch = branch
27
28
 
28
- async def check(self, relm: RelationshipManager, node_schema: MainSchemaTypes) -> None: # noqa: ARG002
29
+ async def check(self, relm: RelationshipManager, node_schema: MainSchemaTypes, node: Node) -> None: # noqa: ARG002
29
30
  branch = await registry.get_branch(db=self.db) if not self.branch else self.branch
30
31
 
31
32
  # NOTE adding resolve here because we need to retrieve the real ID
@@ -63,7 +64,7 @@ class RelationshipCountConstraint(RelationshipManagerConstraintInterface):
63
64
 
64
65
  query = await RelationshipCountPerNodeQuery.init(
65
66
  db=self.db,
66
- node_ids=[node.uuid for node in nodes_to_validate],
67
+ node_ids=[n.uuid for n in nodes_to_validate],
67
68
  identifier=relm.schema.identifier,
68
69
  direction=relm.schema.direction.neighbor_direction,
69
70
  branch=branch,
@@ -74,14 +75,14 @@ class RelationshipCountConstraint(RelationshipManagerConstraintInterface):
74
75
  # Need to adjust the number based on what we will add / remove
75
76
  # +1 for max_count
76
77
  # -1 for min_count
77
- for node in nodes_to_validate:
78
- if node.max_count and count_per_peer[node.uuid] + 1 > node.max_count:
78
+ for node_to_validate in nodes_to_validate:
79
+ if node_to_validate.max_count and count_per_peer[node_to_validate.uuid] + 1 > node_to_validate.max_count:
79
80
  raise ValidationError(
80
- f"Node {node.uuid} has {count_per_peer[node.uuid] + 1} peers "
81
- f"for {relm.schema.identifier}, maximum of {node.max_count} allowed",
81
+ f"Node {node_to_validate.uuid} has {count_per_peer[node_to_validate.uuid] + 1} peers "
82
+ f"for {relm.schema.identifier}, maximum of {node_to_validate.max_count} allowed",
82
83
  )
83
- if node.min_count and count_per_peer[node.uuid] - 1 < node.min_count:
84
+ if node_to_validate.min_count and count_per_peer[node_to_validate.uuid] - 1 < node_to_validate.min_count:
84
85
  raise ValidationError(
85
- f"Node {node.uuid} has {count_per_peer[node.uuid] - 1} peers "
86
- f"for {relm.schema.identifier}, no fewer than {node.min_count} allowed",
86
+ f"Node {node_to_validate.uuid} has {count_per_peer[node_to_validate.uuid] - 1} peers "
87
+ f"for {relm.schema.identifier}, no fewer than {node_to_validate.min_count} allowed",
87
88
  )
@@ -1,5 +1,6 @@
1
1
  from abc import ABC, abstractmethod
2
2
 
3
+ from infrahub.core.node import Node
3
4
  from infrahub.core.schema import MainSchemaTypes
4
5
 
5
6
  from ..model import RelationshipManager
@@ -7,4 +8,4 @@ from ..model import RelationshipManager
7
8
 
8
9
  class RelationshipManagerConstraintInterface(ABC):
9
10
  @abstractmethod
10
- async def check(self, relm: RelationshipManager, node_schema: MainSchemaTypes) -> None: ...
11
+ async def check(self, relm: RelationshipManager, node_schema: MainSchemaTypes, node: Node) -> None: ...
@@ -3,6 +3,7 @@ from dataclasses import dataclass
3
3
  from infrahub.core import registry
4
4
  from infrahub.core.branch import Branch
5
5
  from infrahub.core.constants import RelationshipCardinality
6
+ from infrahub.core.node import Node
6
7
  from infrahub.core.query.node import NodeListGetInfoQuery
7
8
  from infrahub.core.schema import MainSchemaTypes
8
9
  from infrahub.core.schema.generic_schema import GenericSchema
@@ -26,7 +27,7 @@ class RelationshipPeerKindConstraint(RelationshipManagerConstraintInterface):
26
27
  self.db = db
27
28
  self.branch = branch
28
29
 
29
- async def check(self, relm: RelationshipManager, node_schema: MainSchemaTypes) -> None: # noqa: ARG002
30
+ async def check(self, relm: RelationshipManager, node_schema: MainSchemaTypes, node: Node) -> None: # noqa: ARG002
30
31
  branch = await registry.get_branch(db=self.db) if not self.branch else self.branch
31
32
  peer_schema = registry.schema.get(name=relm.schema.peer, branch=branch, duplicate=False)
32
33
  if isinstance(peer_schema, GenericSchema):