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
@@ -0,0 +1,82 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, Any, Sequence
4
+
5
+ from infrahub.core.migrations.shared import GraphMigration, MigrationResult
6
+ from infrahub.log import get_logger
7
+
8
+ from ...query import Query, QueryType
9
+
10
+ if TYPE_CHECKING:
11
+ from infrahub.database import InfrahubDatabase
12
+
13
+ log = get_logger()
14
+
15
+
16
+ class DeletePosthumousEdges(Query):
17
+ name = "delete_posthumous_edges_query"
18
+ type = QueryType.WRITE
19
+ insert_return = False
20
+
21
+ async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None: # noqa: ARG002
22
+ query = """
23
+ // ------------
24
+ // find deleted nodes
25
+ // ------------
26
+ MATCH (n:Node)-[e:IS_PART_OF]->(:Root)
27
+ WHERE e.status = "deleted" OR e.to IS NOT NULL
28
+ WITH DISTINCT n, e.branch AS delete_branch, e.branch_level AS delete_branch_level, CASE
29
+ WHEN e.status = "deleted" THEN e.from
30
+ ELSE e.to
31
+ END AS delete_time
32
+ // ------------
33
+ // find the edges added to the deleted node after the delete time
34
+ // ------------
35
+ MATCH (n)-[added_e]-(peer)
36
+ WHERE added_e.from > delete_time
37
+ AND type(added_e) <> "IS_PART_OF"
38
+ // if the node was deleted on a branch (delete_branch_level > 1), and then updated on main/global (added_e.branch_level = 1), we can ignore it
39
+ AND added_e.branch_level >= delete_branch_level
40
+ AND (added_e.branch = delete_branch OR delete_branch_level = 1)
41
+ WITH DISTINCT n, delete_branch, delete_time, added_e, peer
42
+ // ------------
43
+ // get the branched_from for the branch on which the node was deleted
44
+ // ------------
45
+ CALL (added_e) {
46
+ MATCH (b:Branch {name: added_e.branch})
47
+ RETURN b.branched_from AS added_e_branched_from
48
+ }
49
+ // ------------
50
+ // account for the following situations, given that the edge update time is after the node delete time
51
+ // - deleted on main/global, updated on branch
52
+ // - illegal if the delete is before branch.branched_from
53
+ // - deleted on branch, updated on branch
54
+ // - illegal
55
+ // ------------
56
+ WITH n, delete_branch, delete_time, added_e, peer
57
+ WHERE delete_branch = added_e.branch
58
+ OR delete_time < added_e_branched_from
59
+ DELETE added_e
60
+ // --------------
61
+ // the peer _should_ only be an Attribute, but I want to make sure we don't
62
+ // inadvertently delete Root or an AttributeValue or a Boolean
63
+ // --------------
64
+ WITH peer
65
+ WHERE "Attribute" IN labels(peer)
66
+ DETACH DELETE peer
67
+ """
68
+ self.add_to_query(query)
69
+
70
+
71
+ class Migration030(GraphMigration):
72
+ """
73
+ Edges could have been added to Nodes after the Node was deleted, so we need to hard-delete those illegal edges
74
+ """
75
+
76
+ name: str = "030_delete_illegal_edges"
77
+ minimum_version: int = 29
78
+ queries: Sequence[type[Query]] = [DeletePosthumousEdges]
79
+
80
+ async def validate_migration(self, db: InfrahubDatabase) -> MigrationResult: # noqa: ARG002
81
+ result = MigrationResult()
82
+ return result
@@ -61,30 +61,33 @@ class AttributeAddQuery(Query):
61
61
  MERGE (is_visible_value:Boolean { value: $is_visible_default })
62
62
  WITH av, is_protected_value, is_visible_value
63
63
  MATCH p = (n:%(node_kind)s)
64
- CALL {
65
- WITH n
66
- MATCH (root:Root)<-[r1:IS_PART_OF]-(n)
67
- OPTIONAL MATCH (n)-[r2:HAS_ATTRIBUTE]-(:Attribute { name: $attr_name })
68
- WHERE all(r in [r1, r2] WHERE (%(branch_filter)s))
69
- RETURN n as n1, r1 as r11, r2 as r12
70
- ORDER BY r2.branch_level DESC, r2.from ASC, r1.branch_level DESC, r1.from ASC
64
+ CALL (n) {
65
+ MATCH (:Root)<-[r:IS_PART_OF]-(n)
66
+ WHERE %(branch_filter)s
67
+ WITH n, r AS is_part_of_e
68
+ OPTIONAL MATCH (n)-[r:HAS_ATTRIBUTE]-(:Attribute { name: $attr_name })
69
+ WHERE %(branch_filter)s
70
+ WITH is_part_of_e, r AS has_attr_e
71
+ RETURN is_part_of_e, has_attr_e
72
+ ORDER BY has_attr_e.branch_level DESC, has_attr_e.from ASC, is_part_of_e.branch_level DESC, is_part_of_e.from ASC
71
73
  LIMIT 1
72
74
  }
73
- WITH n1 as n, r11 as r1, r12 as r2, av, is_protected_value, is_visible_value
74
- WHERE r1.status = "active" AND (r2 IS NULL OR r2.status = "deleted")
75
+ WITH n, is_part_of_e, has_attr_e, av, is_protected_value, is_visible_value
76
+ WHERE is_part_of_e.status = "active" AND (has_attr_e IS NULL OR has_attr_e.status = "deleted")
75
77
  CREATE (a:Attribute { name: $attr_name, branch_support: $branch_support })
76
78
  CREATE (n)-[:HAS_ATTRIBUTE $rel_props ]->(a)
77
79
  CREATE (a)-[:HAS_VALUE $rel_props ]->(av)
78
80
  CREATE (a)-[:IS_PROTECTED $rel_props]->(is_protected_value)
79
81
  CREATE (a)-[:IS_VISIBLE $rel_props]->(is_visible_value)
80
82
  %(uuid_generation)s
81
- FOREACH (i in CASE WHEN r2.status = "deleted" THEN [1] ELSE [] END |
82
- SET r2.to = $current_time
83
+ FOREACH (i in CASE WHEN has_attr_e.status = "deleted" THEN [1] ELSE [] END |
84
+ SET has_attr_e.to = $current_time
83
85
  )
84
86
  """ % {
85
87
  "branch_filter": branch_filter,
86
88
  "node_kind": self.node_kind,
87
89
  "uuid_generation": db.render_uuid_generation(node_label="a", node_attr="uuid"),
88
90
  }
91
+
89
92
  self.add_to_query(query)
90
93
  self.return_labels = ["n.uuid", "a.uuid"]
@@ -38,7 +38,7 @@ class AttributeRenameQuery(Query):
38
38
  def render_match(self) -> str:
39
39
  query = """
40
40
  // Find all the active nodes
41
- CALL {
41
+ CALL () {
42
42
  MATCH (node:%(node_kind)s)
43
43
  WHERE exists((node)-[:HAS_ATTRIBUTE]-(:Attribute { name: $prev_attr.name }))
44
44
  RETURN node
@@ -55,7 +55,6 @@ class AttributeRenameQuery(Query):
55
55
  @staticmethod
56
56
  def _render_sub_query_per_rel_type_update_active(rel_type: str, rel_def: FieldInfo) -> str:
57
57
  subquery = [
58
- "WITH peer_node, rb, active_attr",
59
58
  "WITH peer_node, rb, active_attr",
60
59
  f'WHERE type(rb) = "{rel_type}"',
61
60
  ]
@@ -72,7 +71,6 @@ class AttributeRenameQuery(Query):
72
71
  @staticmethod
73
72
  def _render_sub_query_per_rel_type_create_new(rel_type: str, rel_def: FieldInfo) -> str:
74
73
  subquery = [
75
- "WITH peer_node, rb, active_attr, new_attr",
76
74
  "WITH peer_node, rb, active_attr, new_attr",
77
75
  f'WHERE type(rb) = "{rel_type}"',
78
76
  ]
@@ -126,8 +124,7 @@ class AttributeRenameQuery(Query):
126
124
 
127
125
  add_uuid = db.render_uuid_generation(node_label="new_attr", node_attr="uuid")
128
126
  query = """
129
- CALL {
130
- WITH node
127
+ CALL (node) {
131
128
  MATCH (root:Root)<-[r:IS_PART_OF]-(node)
132
129
  WHERE %(branch_filter)s
133
130
  RETURN node as n1, r as r1
@@ -137,8 +134,7 @@ class AttributeRenameQuery(Query):
137
134
  WITH n1 as active_node, r1 as rb
138
135
  WHERE rb.status = "active"
139
136
  // Find all the attributes that need to be updated
140
- CALL {
141
- WITH active_node
137
+ CALL (active_node) {
142
138
  MATCH (active_node)-[r:HAS_ATTRIBUTE]-(attr:Attribute { name: $prev_attr.name })
143
139
  WHERE %(branch_filter)s
144
140
  RETURN active_node as n1, r as r1, attr as attr1
@@ -151,8 +147,7 @@ class AttributeRenameQuery(Query):
151
147
  %(add_uuid)s
152
148
  WITH active_attr, new_attr
153
149
  MATCH (active_attr)-[]-(peer)
154
- CALL {
155
- WITH active_attr, peer
150
+ CALL (active_attr, peer) {
156
151
  MATCH (active_attr)-[r]-(peer)
157
152
  WHERE %(branch_filter)s
158
153
  RETURN active_attr as a1, r as r1, peer as p1
@@ -161,7 +156,7 @@ class AttributeRenameQuery(Query):
161
156
  }
162
157
  WITH a1 as active_attr, r1 as rb, p1 as peer_node, new_attr
163
158
  WHERE rb.status = "active"
164
- CALL {
159
+ CALL (peer_node, rb, active_attr, new_attr){
165
160
  %(sub_query_create_all)s
166
161
  }
167
162
  WITH p2 as peer_node, rb, new_attr, active_attr
@@ -170,7 +165,7 @@ class AttributeRenameQuery(Query):
170
165
 
171
166
  if not (self.branch.is_default or self.branch.is_global):
172
167
  query = """
173
- CALL {
168
+ CALL (peer_node, rb, active_attr) {
174
169
  %(sub_query_update_all)s
175
170
  }
176
171
  WITH p2 as peer_node, rb, new_attr
@@ -55,7 +55,6 @@ class DeleteElementInSchemaQuery(Query):
55
55
  @staticmethod
56
56
  def _render_sub_query_per_rel_type(rel_name: str, rel_type: str, direction: GraphRelDirection) -> str:
57
57
  subquery = [
58
- f"WITH peer_node, {rel_name}, element_to_delete",
59
58
  f"WITH peer_node, {rel_name}, element_to_delete",
60
59
  f'WHERE type({rel_name}) = "{rel_type}"',
61
60
  ]
@@ -67,28 +66,32 @@ class DeleteElementInSchemaQuery(Query):
67
66
  return "\n".join(subquery)
68
67
 
69
68
  @classmethod
70
- def _render_sub_query_out(cls) -> str:
69
+ def _render_sub_query_out(cls) -> tuple[str, str]:
70
+ rel_name = "rel_outband"
71
+ sub_query_out_args = f"peer_node, {rel_name}, element_to_delete"
71
72
  sub_queries_out = [
72
73
  cls._render_sub_query_per_rel_type(
73
- rel_name="rel_outband", rel_type=rel_type, direction=GraphRelDirection.OUTBOUND
74
+ rel_name=rel_name, rel_type=rel_type, direction=GraphRelDirection.OUTBOUND
74
75
  )
75
76
  for rel_type, rel_def in GraphNodeRelationships.model_fields.items()
76
77
  if rel_def.default.direction in [GraphRelDirection.OUTBOUND, GraphRelDirection.EITHER]
77
78
  ]
78
79
  sub_query_out = "\nUNION\n".join(sub_queries_out)
79
- return sub_query_out
80
+ return sub_query_out, sub_query_out_args
80
81
 
81
82
  @classmethod
82
- def _render_sub_query_in(cls) -> str:
83
+ def _render_sub_query_in(cls) -> tuple[str, str]:
84
+ rel_name = "rel_inband"
85
+ sub_query_in_args = f"peer_node, {rel_name}, element_to_delete"
83
86
  sub_queries_in = [
84
87
  cls._render_sub_query_per_rel_type(
85
- rel_name="rel_inband", rel_type=rel_type, direction=GraphRelDirection.INBOUND
88
+ rel_name=rel_name, rel_type=rel_type, direction=GraphRelDirection.INBOUND
86
89
  )
87
90
  for rel_type, rel_def in GraphNodeRelationships.model_fields.items()
88
91
  if rel_def.default.direction in [GraphRelDirection.INBOUND, GraphRelDirection.EITHER]
89
92
  ]
90
93
  sub_query_in = "\nUNION\n".join(sub_queries_in)
91
- return sub_query_in
94
+ return sub_query_in, sub_query_in_args
92
95
 
93
96
  async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None: # noqa: ARG002
94
97
  branch_filter, branch_params = self.branch.get_query_filter_path(at=self.at.to_string())
@@ -108,16 +111,15 @@ class DeleteElementInSchemaQuery(Query):
108
111
  "from": self.at.to_string(),
109
112
  }
110
113
 
111
- sub_query_out = self._render_sub_query_out()
112
- sub_query_in = self._render_sub_query_in()
114
+ sub_query_out, sub_query_out_args = self._render_sub_query_out()
115
+ sub_query_in, sub_query_in_args = self._render_sub_query_in()
113
116
 
114
117
  self.add_to_query(self.render_match())
115
118
  self.add_to_query(self.render_where())
116
119
 
117
120
  # ruff: noqa: E501
118
121
  query = """
119
- CALL {
120
- WITH attr_node
122
+ CALL (attr_node) {
121
123
  MATCH (root:Root)<-[r:IS_PART_OF]-(attr_node)
122
124
  WHERE %(branch_filter)s
123
125
  RETURN attr_node as n1, r as r1
@@ -130,8 +132,7 @@ class DeleteElementInSchemaQuery(Query):
130
132
 
131
133
  // Process Outbound Relationship
132
134
  MATCH (element_to_delete)-[]->(peer)
133
- CALL {
134
- WITH element_to_delete, peer
135
+ CALL (element_to_delete, peer) {
135
136
  MATCH (element_to_delete)-[r]->(peer)
136
137
  WHERE %(branch_filter)s
137
138
  RETURN element_to_delete as n1, r as rel_outband1, peer as p1
@@ -140,7 +141,7 @@ class DeleteElementInSchemaQuery(Query):
140
141
  }
141
142
  WITH n1 as element_to_delete, rel_outband1 as rel_outband, p1 as peer_node
142
143
  WHERE rel_outband.status = "active"
143
- CALL {
144
+ CALL (%(sub_query_out_args)s) {
144
145
  %(sub_query_out)s
145
146
  }
146
147
  WITH p2 as peer_node, rel_outband, element_to_delete
@@ -150,8 +151,7 @@ class DeleteElementInSchemaQuery(Query):
150
151
  WITH DISTINCT(element_to_delete) AS element_to_delete
151
152
  // Process Inbound Relationship
152
153
  MATCH (element_to_delete)<-[]-(peer)
153
- CALL {
154
- WITH element_to_delete, peer
154
+ CALL (element_to_delete, peer) {
155
155
  MATCH (element_to_delete)<-[r]-(peer)
156
156
  WHERE %(branch_filter)s
157
157
  RETURN element_to_delete as n1, r as rel_inband1, peer as p1
@@ -160,7 +160,7 @@ class DeleteElementInSchemaQuery(Query):
160
160
  }
161
161
  WITH n1 as element_to_delete, rel_inband1 as rel_inband, p1 as peer_node
162
162
  WHERE rel_inband.status = "active"
163
- CALL {
163
+ CALL (%(sub_query_in_args)s) {
164
164
  %(sub_query_in)s
165
165
  }
166
166
  WITH p2 as peer_node, rel_inband, element_to_delete
@@ -172,5 +172,7 @@ class DeleteElementInSchemaQuery(Query):
172
172
  "branch_filter": branch_filter,
173
173
  "sub_query_out": sub_query_out,
174
174
  "sub_query_in": sub_query_in,
175
+ "sub_query_out_args": sub_query_out_args,
176
+ "sub_query_in_args": sub_query_in_args,
175
177
  }
176
178
  self.add_to_query(query)
@@ -47,7 +47,6 @@ class NodeDuplicateQuery(Query):
47
47
  @staticmethod
48
48
  def _render_sub_query_per_rel_type(rel_name: str, rel_type: str, rel_dir: GraphRelDirection) -> str:
49
49
  subquery = [
50
- f"WITH peer_node, {rel_name}, active_node, new_node",
51
50
  f"WITH peer_node, {rel_name}, active_node, new_node",
52
51
  f'WHERE type({rel_name}) = "{rel_type}"',
53
52
  ]
@@ -81,28 +80,28 @@ class NodeDuplicateQuery(Query):
81
80
  return "\n".join(subquery)
82
81
 
83
82
  @classmethod
84
- def _render_sub_query_out(cls) -> str:
83
+ def _render_sub_query_out(cls) -> tuple[str, str]:
84
+ rel_name = "rel_outband"
85
+ sub_query_out_args = f"peer_node, {rel_name}, active_node, new_node"
85
86
  sub_queries_out = [
86
- cls._render_sub_query_per_rel_type(
87
- rel_name="rel_outband", rel_type=rel_type, rel_dir=GraphRelDirection.OUTBOUND
88
- )
87
+ cls._render_sub_query_per_rel_type(rel_name=rel_name, rel_type=rel_type, rel_dir=GraphRelDirection.OUTBOUND)
89
88
  for rel_type, field_info in GraphNodeRelationships.model_fields.items()
90
89
  if field_info.default.direction in (GraphRelDirection.OUTBOUND, GraphRelDirection.EITHER)
91
90
  ]
92
91
  sub_query_out = "\nUNION\n".join(sub_queries_out)
93
- return sub_query_out
92
+ return sub_query_out, sub_query_out_args
94
93
 
95
94
  @classmethod
96
- def _render_sub_query_in(cls) -> str:
95
+ def _render_sub_query_in(cls) -> tuple[str, str]:
96
+ rel_name = "rel_inband"
97
+ sub_query_in_args = f"peer_node, {rel_name}, active_node, new_node"
97
98
  sub_queries_in = [
98
- cls._render_sub_query_per_rel_type(
99
- rel_name="rel_inband", rel_type=rel_type, rel_dir=GraphRelDirection.INBOUND
100
- )
99
+ cls._render_sub_query_per_rel_type(rel_name=rel_name, rel_type=rel_type, rel_dir=GraphRelDirection.INBOUND)
101
100
  for rel_type, field_info in GraphNodeRelationships.model_fields.items()
102
101
  if field_info.default.direction in (GraphRelDirection.INBOUND, GraphRelDirection.EITHER)
103
102
  ]
104
103
  sub_query_in = "\nUNION\n".join(sub_queries_in)
105
- return sub_query_in
104
+ return sub_query_in, sub_query_in_args
106
105
 
107
106
  async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None: # noqa: ARG002
108
107
  branch_filter, branch_params = self.branch.get_query_filter_path(at=self.at.to_string())
@@ -126,15 +125,14 @@ class NodeDuplicateQuery(Query):
126
125
  "from": self.at.to_string(),
127
126
  }
128
127
 
129
- sub_query_out = self._render_sub_query_out()
130
- sub_query_in = self._render_sub_query_in()
128
+ sub_query_out, sub_query_out_args = self._render_sub_query_out()
129
+ sub_query_in, sub_query_in_args = self._render_sub_query_in()
131
130
 
132
131
  self.add_to_query(self.render_match())
133
132
 
134
133
  # ruff: noqa: E501
135
134
  query = """
136
- CALL {
137
- WITH node
135
+ CALL (node) {
138
136
  MATCH (root:Root)<-[r:IS_PART_OF]-(node)
139
137
  WHERE %(branch_filter)s
140
138
  RETURN node as n1, r as r1
@@ -147,8 +145,7 @@ class NodeDuplicateQuery(Query):
147
145
  WITH active_node, new_node
148
146
  // Process Outbound Relationship
149
147
  MATCH (active_node)-[]->(peer)
150
- CALL {
151
- WITH active_node, peer
148
+ CALL (active_node, peer) {
152
149
  MATCH (active_node)-[r]->(peer)
153
150
  WHERE %(branch_filter)s
154
151
  RETURN active_node as n1, r as rel_outband1, peer as p1
@@ -157,7 +154,7 @@ class NodeDuplicateQuery(Query):
157
154
  }
158
155
  WITH n1 as active_node, rel_outband1 as rel_outband, p1 as peer_node, new_node
159
156
  WHERE rel_outband.status = "active" AND rel_outband.to IS NULL
160
- CALL {
157
+ CALL (%(sub_query_out_args)s) {
161
158
  %(sub_query_out)s
162
159
  }
163
160
  WITH p2 as peer_node, rel_outband, active_node, new_node
@@ -167,8 +164,7 @@ class NodeDuplicateQuery(Query):
167
164
  WITH DISTINCT active_node, new_node
168
165
  // Process Inbound Relationship
169
166
  MATCH (active_node)<-[]-(peer)
170
- CALL {
171
- WITH active_node, peer
167
+ CALL (active_node, peer) {
172
168
  MATCH (active_node)<-[r]-(peer)
173
169
  WHERE %(branch_filter)s
174
170
  RETURN active_node as n1, r as rel_inband1, peer as p1
@@ -177,7 +173,7 @@ class NodeDuplicateQuery(Query):
177
173
  }
178
174
  WITH n1 as active_node, rel_inband1 as rel_inband, p1 as peer_node, new_node
179
175
  WHERE rel_inband.status = "active" AND rel_inband.to IS NULL
180
- CALL {
176
+ CALL (%(sub_query_in_args)s) {
181
177
  %(sub_query_in)s
182
178
  }
183
179
  WITH p2 as peer_node, rel_inband, active_node, new_node
@@ -191,5 +187,7 @@ class NodeDuplicateQuery(Query):
191
187
  "labels": ":".join(self.new_node.labels),
192
188
  "sub_query_out": sub_query_out,
193
189
  "sub_query_in": sub_query_in,
190
+ "sub_query_out_args": sub_query_out_args,
191
+ "sub_query_in_args": sub_query_in_args,
194
192
  }
195
193
  self.add_to_query(query)
@@ -47,7 +47,6 @@ class RelationshipDuplicateQuery(Query):
47
47
  @staticmethod
48
48
  def _render_sub_query_per_rel_type(rel_name: str, rel_type: str, direction: GraphRelDirection) -> str:
49
49
  subquery = [
50
- f"WITH peer_node, {rel_name}, active_rel, new_rel",
51
50
  f"WITH peer_node, {rel_name}, active_rel, new_rel",
52
51
  f'WHERE type({rel_name}) = "{rel_type}"',
53
52
  ]
@@ -61,28 +60,32 @@ class RelationshipDuplicateQuery(Query):
61
60
  return "\n".join(subquery)
62
61
 
63
62
  @classmethod
64
- def _render_sub_query_out(cls) -> str:
63
+ def _render_sub_query_out(cls) -> tuple[str, str]:
64
+ rel_name = "rel_outband"
65
+ sub_query_out_args = f"peer_node, {rel_name}, active_rel, new_rel"
65
66
  sub_queries_out = [
66
67
  cls._render_sub_query_per_rel_type(
67
- rel_name="rel_outband", rel_type=rel_type, direction=GraphRelDirection.OUTBOUND
68
+ rel_name=rel_name, rel_type=rel_type, direction=GraphRelDirection.OUTBOUND
68
69
  )
69
70
  for rel_type, rel_def in GraphRelationshipRelationships.model_fields.items()
70
71
  if rel_def.default.direction in [GraphRelDirection.OUTBOUND, GraphRelDirection.EITHER]
71
72
  ]
72
73
  sub_query_out = "\nUNION\n".join(sub_queries_out)
73
- return sub_query_out
74
+ return sub_query_out, sub_query_out_args
74
75
 
75
76
  @classmethod
76
- def _render_sub_query_in(cls) -> str:
77
+ def _render_sub_query_in(cls) -> tuple[str, str]:
78
+ rel_name = "rel_inband"
79
+ sub_query_in_args = f"peer_node, {rel_name}, active_rel, new_rel"
77
80
  sub_queries_in = [
78
81
  cls._render_sub_query_per_rel_type(
79
- rel_name="rel_inband", rel_type=rel_type, direction=GraphRelDirection.INBOUND
82
+ rel_name=rel_name, rel_type=rel_type, direction=GraphRelDirection.INBOUND
80
83
  )
81
84
  for rel_type, rel_def in GraphRelationshipRelationships.model_fields.items()
82
85
  if rel_def.default.direction in [GraphRelDirection.INBOUND, GraphRelDirection.EITHER]
83
86
  ]
84
87
  sub_query_in = "\nUNION\n".join(sub_queries_in)
85
- return sub_query_in
88
+ return sub_query_in, sub_query_in_args
86
89
 
87
90
  async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None: # noqa: ARG002
88
91
  branch_filter, branch_params = self.branch.get_query_filter_path(at=self.at.to_string())
@@ -109,15 +112,13 @@ class RelationshipDuplicateQuery(Query):
109
112
  "from": self.at.to_string(),
110
113
  }
111
114
 
112
- sub_query_out = self._render_sub_query_out()
113
- sub_query_in = self._render_sub_query_in()
115
+ sub_query_out, sub_query_out_args = self._render_sub_query_out()
116
+ sub_query_in, sub_query_in_args = self._render_sub_query_in()
114
117
 
115
118
  self.add_to_query(self.render_match())
116
119
 
117
- # ruff: noqa: E501
118
120
  query = """
119
- CALL {
120
- WITH source, rel, destination
121
+ CALL (source, rel, destination) {
121
122
  MATCH path = (source)-[r1:IS_RELATED]-(rel)-[r2:IS_RELATED]-(destination)
122
123
  WHERE all(r IN relationships(path) WHERE %(branch_filter)s)
123
124
  RETURN rel as rel1, r1 as r11, r2 as r12
@@ -130,8 +131,7 @@ class RelationshipDuplicateQuery(Query):
130
131
  WITH DISTINCT(active_rel) as active_rel, new_rel
131
132
  // Process Inbound Relationship
132
133
  MATCH (active_rel)<-[]-(peer)
133
- CALL {
134
- WITH active_rel, peer
134
+ CALL (active_rel, peer) {
135
135
  MATCH (active_rel)<-[r]-(peer)
136
136
  WHERE %(branch_filter)s
137
137
  RETURN active_rel as n1, r as rel_inband1, peer as p1
@@ -140,7 +140,7 @@ class RelationshipDuplicateQuery(Query):
140
140
  }
141
141
  WITH n1 as active_rel, rel_inband1 as rel_inband, p1 as peer_node, new_rel
142
142
  WHERE rel_inband.status = "active"
143
- CALL {
143
+ CALL (%(sub_query_in_args)s) {
144
144
  %(sub_query_in)s
145
145
  }
146
146
  WITH p2 as peer_node, rel_inband, active_rel, new_rel
@@ -150,8 +150,7 @@ class RelationshipDuplicateQuery(Query):
150
150
  WITH DISTINCT(active_rel) as active_rel, new_rel
151
151
  // Process Outbound Relationship
152
152
  MATCH (active_rel)-[]->(peer)
153
- CALL {
154
- WITH active_rel, peer
153
+ CALL (active_rel, peer) {
155
154
  MATCH (active_rel)-[r]->(peer)
156
155
  WHERE %(branch_filter)s
157
156
  RETURN active_rel as n1, r as rel_outband1, peer as p1
@@ -160,7 +159,7 @@ class RelationshipDuplicateQuery(Query):
160
159
  }
161
160
  WITH n1 as active_rel, rel_outband1 as rel_outband, p1 as peer_node, new_rel
162
161
  WHERE rel_outband.status = "active"
163
- CALL {
162
+ CALL (%(sub_query_out_args)s) {
164
163
  %(sub_query_out)s
165
164
  }
166
165
  WITH p2 as peer_node, rel_outband, active_rel, new_rel
@@ -172,5 +171,7 @@ class RelationshipDuplicateQuery(Query):
172
171
  "branch_filter": branch_filter,
173
172
  "sub_query_out": sub_query_out,
174
173
  "sub_query_in": sub_query_in,
174
+ "sub_query_in_args": sub_query_in_args,
175
+ "sub_query_out_args": sub_query_out_args,
175
176
  }
176
177
  self.add_to_query(query)
@@ -50,7 +50,6 @@ class NodeAttributeRemoveMigrationQuery01(AttributeMigrationQuery):
50
50
 
51
51
  def render_sub_query_per_rel_type(rel_type: str, rel_def: FieldInfo) -> str:
52
52
  subquery = [
53
- "WITH peer_node, rb, active_attr",
54
53
  "WITH peer_node, rb, active_attr",
55
54
  f'WHERE type(rb) = "{rel_type}"',
56
55
  ]
@@ -75,8 +74,7 @@ class NodeAttributeRemoveMigrationQuery01(AttributeMigrationQuery):
75
74
  MATCH (node:%(node_kind)s)
76
75
  WHERE (size($kinds_to_ignore) = 0 OR NOT any(l IN labels(node) WHERE l IN $kinds_to_ignore))
77
76
  AND exists((node)-[:HAS_ATTRIBUTE]-(:Attribute { name: $attr_name }))
78
- CALL {
79
- WITH node
77
+ CALL (node) {
80
78
  MATCH (root:Root)<-[r:IS_PART_OF]-(node)
81
79
  WHERE %(branch_filter)s
82
80
  RETURN node as n1, r as r1
@@ -86,8 +84,7 @@ class NodeAttributeRemoveMigrationQuery01(AttributeMigrationQuery):
86
84
  WITH n1 as active_node, r1 as rb
87
85
  WHERE rb.status = "active"
88
86
  // Find all the attributes that need to be updated
89
- CALL {
90
- WITH active_node
87
+ CALL (active_node) {
91
88
  MATCH (active_node)-[r:HAS_ATTRIBUTE]-(attr:Attribute { name: $attr_name })
92
89
  WHERE %(branch_filter)s
93
90
  RETURN active_node as n1, r as r1, attr as attr1
@@ -98,8 +95,7 @@ class NodeAttributeRemoveMigrationQuery01(AttributeMigrationQuery):
98
95
  WHERE rb.status = "active"
99
96
  WITH active_attr
100
97
  MATCH (active_attr)-[]-(peer)
101
- CALL {
102
- WITH active_attr, peer
98
+ CALL (active_attr, peer) {
103
99
  MATCH (active_attr)-[r]-(peer)
104
100
  WHERE %(branch_filter)s
105
101
  RETURN active_attr as a1, r as r1, peer as p1
@@ -108,7 +104,7 @@ class NodeAttributeRemoveMigrationQuery01(AttributeMigrationQuery):
108
104
  }
109
105
  WITH a1 as active_attr, r1 as rb, p1 as peer_node
110
106
  WHERE rb.status = "active"
111
- CALL {
107
+ CALL (peer_node, rb, active_attr) {
112
108
  %(sub_query_all)s
113
109
  }
114
110
  WITH p2 as peer_node, rb, active_attr