infrahub-server 1.3.0a0__py3-none-any.whl → 1.3.0b2__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 (123) hide show
  1. infrahub/actions/tasks.py +4 -11
  2. infrahub/branch/__init__.py +0 -0
  3. infrahub/branch/tasks.py +29 -0
  4. infrahub/branch/triggers.py +22 -0
  5. infrahub/cli/db.py +2 -2
  6. infrahub/computed_attribute/gather.py +3 -1
  7. infrahub/computed_attribute/tasks.py +23 -29
  8. infrahub/core/attribute.py +3 -3
  9. infrahub/core/constants/__init__.py +10 -0
  10. infrahub/core/constants/database.py +1 -0
  11. infrahub/core/constants/infrahubkind.py +2 -0
  12. infrahub/core/convert_object_type/conversion.py +1 -1
  13. infrahub/core/diff/query/save.py +67 -40
  14. infrahub/core/diff/query/time_range_query.py +0 -1
  15. infrahub/core/graph/__init__.py +1 -1
  16. infrahub/core/migrations/graph/__init__.py +6 -0
  17. infrahub/core/migrations/graph/m013_convert_git_password_credential.py +0 -2
  18. infrahub/core/migrations/graph/m029_duplicates_cleanup.py +662 -0
  19. infrahub/core/migrations/graph/m030_illegal_edges.py +82 -0
  20. infrahub/core/migrations/query/attribute_add.py +13 -9
  21. infrahub/core/migrations/query/attribute_rename.py +2 -4
  22. infrahub/core/migrations/query/delete_element_in_schema.py +16 -11
  23. infrahub/core/migrations/query/node_duplicate.py +16 -15
  24. infrahub/core/migrations/query/relationship_duplicate.py +16 -12
  25. infrahub/core/migrations/schema/node_attribute_remove.py +1 -2
  26. infrahub/core/migrations/schema/node_remove.py +16 -14
  27. infrahub/core/node/__init__.py +74 -14
  28. infrahub/core/node/base.py +1 -1
  29. infrahub/core/node/resource_manager/ip_address_pool.py +6 -2
  30. infrahub/core/node/resource_manager/ip_prefix_pool.py +6 -2
  31. infrahub/core/node/resource_manager/number_pool.py +31 -5
  32. infrahub/core/node/standard.py +6 -1
  33. infrahub/core/path.py +1 -1
  34. infrahub/core/protocols.py +10 -0
  35. infrahub/core/query/node.py +1 -1
  36. infrahub/core/query/relationship.py +4 -6
  37. infrahub/core/query/standard_node.py +19 -5
  38. infrahub/core/relationship/constraints/peer_relatives.py +72 -0
  39. infrahub/core/relationship/model.py +1 -1
  40. infrahub/core/schema/attribute_parameters.py +129 -5
  41. infrahub/core/schema/attribute_schema.py +62 -14
  42. infrahub/core/schema/basenode_schema.py +2 -2
  43. infrahub/core/schema/definitions/core/__init__.py +16 -2
  44. infrahub/core/schema/definitions/core/group.py +45 -0
  45. infrahub/core/schema/definitions/core/resource_pool.py +29 -0
  46. infrahub/core/schema/definitions/internal.py +25 -4
  47. infrahub/core/schema/generated/attribute_schema.py +12 -5
  48. infrahub/core/schema/generated/relationship_schema.py +6 -1
  49. infrahub/core/schema/manager.py +7 -2
  50. infrahub/core/schema/schema_branch.py +69 -5
  51. infrahub/core/validators/__init__.py +8 -0
  52. infrahub/core/validators/attribute/choices.py +0 -1
  53. infrahub/core/validators/attribute/enum.py +0 -1
  54. infrahub/core/validators/attribute/kind.py +0 -1
  55. infrahub/core/validators/attribute/length.py +0 -1
  56. infrahub/core/validators/attribute/min_max.py +118 -0
  57. infrahub/core/validators/attribute/number_pool.py +106 -0
  58. infrahub/core/validators/attribute/optional.py +0 -2
  59. infrahub/core/validators/attribute/regex.py +0 -1
  60. infrahub/core/validators/enum.py +5 -0
  61. infrahub/core/validators/tasks.py +1 -1
  62. infrahub/database/__init__.py +16 -4
  63. infrahub/database/validation.py +100 -0
  64. infrahub/dependencies/builder/constraint/grouped/node_runner.py +2 -0
  65. infrahub/dependencies/builder/constraint/relationship_manager/peer_relatives.py +8 -0
  66. infrahub/dependencies/builder/diff/deserializer.py +1 -1
  67. infrahub/dependencies/registry.py +2 -0
  68. infrahub/events/models.py +1 -1
  69. infrahub/git/base.py +5 -3
  70. infrahub/git/integrator.py +102 -3
  71. infrahub/graphql/mutations/main.py +1 -1
  72. infrahub/graphql/mutations/resource_manager.py +54 -6
  73. infrahub/graphql/queries/resource_manager.py +7 -1
  74. infrahub/graphql/queries/task.py +10 -0
  75. infrahub/graphql/resolvers/many_relationship.py +1 -1
  76. infrahub/graphql/resolvers/resolver.py +2 -2
  77. infrahub/graphql/resolvers/single_relationship.py +1 -1
  78. infrahub/graphql/types/task_log.py +3 -2
  79. infrahub/menu/menu.py +8 -7
  80. infrahub/message_bus/operations/refresh/registry.py +3 -3
  81. infrahub/patch/queries/delete_duplicated_edges.py +40 -29
  82. infrahub/pools/number.py +5 -3
  83. infrahub/pools/registration.py +22 -0
  84. infrahub/pools/tasks.py +56 -0
  85. infrahub/schema/__init__.py +0 -0
  86. infrahub/schema/tasks.py +27 -0
  87. infrahub/schema/triggers.py +23 -0
  88. infrahub/task_manager/task.py +44 -4
  89. infrahub/trigger/catalogue.py +4 -0
  90. infrahub/trigger/models.py +5 -4
  91. infrahub/trigger/setup.py +26 -2
  92. infrahub/trigger/tasks.py +1 -1
  93. infrahub/types.py +6 -0
  94. infrahub/webhook/tasks.py +6 -9
  95. infrahub/workflows/catalogue.py +27 -1
  96. infrahub_sdk/client.py +43 -10
  97. infrahub_sdk/node/__init__.py +39 -0
  98. infrahub_sdk/node/attribute.py +122 -0
  99. infrahub_sdk/node/constants.py +21 -0
  100. infrahub_sdk/{node.py → node/node.py} +50 -749
  101. infrahub_sdk/node/parsers.py +15 -0
  102. infrahub_sdk/node/property.py +24 -0
  103. infrahub_sdk/node/related_node.py +266 -0
  104. infrahub_sdk/node/relationship.py +302 -0
  105. infrahub_sdk/protocols.py +112 -0
  106. infrahub_sdk/protocols_base.py +34 -2
  107. infrahub_sdk/query_groups.py +13 -2
  108. infrahub_sdk/schema/main.py +1 -0
  109. infrahub_sdk/schema/repository.py +16 -0
  110. infrahub_sdk/spec/object.py +1 -1
  111. infrahub_sdk/store.py +1 -1
  112. infrahub_sdk/testing/schemas/car_person.py +1 -0
  113. {infrahub_server-1.3.0a0.dist-info → infrahub_server-1.3.0b2.dist-info}/METADATA +3 -3
  114. {infrahub_server-1.3.0a0.dist-info → infrahub_server-1.3.0b2.dist-info}/RECORD +122 -100
  115. {infrahub_server-1.3.0a0.dist-info → infrahub_server-1.3.0b2.dist-info}/WHEEL +1 -1
  116. infrahub_testcontainers/container.py +239 -64
  117. infrahub_testcontainers/docker-compose-cluster.test.yml +321 -0
  118. infrahub_testcontainers/docker-compose.test.yml +1 -0
  119. infrahub_testcontainers/helpers.py +15 -1
  120. infrahub_testcontainers/plugin.py +9 -0
  121. infrahub/patch/queries/consolidate_duplicated_nodes.py +0 -106
  122. {infrahub_server-1.3.0a0.dist-info → infrahub_server-1.3.0b2.dist-info}/LICENSE.txt +0 -0
  123. {infrahub_server-1.3.0a0.dist-info → infrahub_server-1.3.0b2.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
@@ -62,28 +62,32 @@ class AttributeAddQuery(Query):
62
62
  WITH av, is_protected_value, is_visible_value
63
63
  MATCH p = (n:%(node_kind)s)
64
64
  CALL (n) {
65
- MATCH (root:Root)<-[r1:IS_PART_OF]-(n)
66
- OPTIONAL MATCH (n)-[r2:HAS_ATTRIBUTE]-(:Attribute { name: $attr_name })
67
- WHERE all(r in [r1, r2] WHERE (%(branch_filter)s))
68
- RETURN n as n1, r1 as r11, r2 as r12
69
- ORDER BY r2.branch_level DESC, r2.from ASC, r1.branch_level DESC, r1.from ASC
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
70
73
  LIMIT 1
71
74
  }
72
- WITH n1 as n, r11 as r1, r12 as r2, av, is_protected_value, is_visible_value
73
- 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")
74
77
  CREATE (a:Attribute { name: $attr_name, branch_support: $branch_support })
75
78
  CREATE (n)-[:HAS_ATTRIBUTE $rel_props ]->(a)
76
79
  CREATE (a)-[:HAS_VALUE $rel_props ]->(av)
77
80
  CREATE (a)-[:IS_PROTECTED $rel_props]->(is_protected_value)
78
81
  CREATE (a)-[:IS_VISIBLE $rel_props]->(is_visible_value)
79
82
  %(uuid_generation)s
80
- FOREACH (i in CASE WHEN r2.status = "deleted" THEN [1] ELSE [] END |
81
- 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
82
85
  )
83
86
  """ % {
84
87
  "branch_filter": branch_filter,
85
88
  "node_kind": self.node_kind,
86
89
  "uuid_generation": db.render_uuid_generation(node_label="a", node_attr="uuid"),
87
90
  }
91
+
88
92
  self.add_to_query(query)
89
93
  self.return_labels = ["n.uuid", "a.uuid"]
@@ -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
  ]
@@ -158,7 +156,7 @@ class AttributeRenameQuery(Query):
158
156
  }
159
157
  WITH a1 as active_attr, r1 as rb, p1 as peer_node, new_attr
160
158
  WHERE rb.status = "active"
161
- CALL {
159
+ CALL (peer_node, rb, active_attr, new_attr){
162
160
  %(sub_query_create_all)s
163
161
  }
164
162
  WITH p2 as peer_node, rb, new_attr, active_attr
@@ -167,7 +165,7 @@ class AttributeRenameQuery(Query):
167
165
 
168
166
  if not (self.branch.is_default or self.branch.is_global):
169
167
  query = """
170
- CALL {
168
+ CALL (peer_node, rb, active_attr) {
171
169
  %(sub_query_update_all)s
172
170
  }
173
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,8 +111,8 @@ 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())
@@ -138,7 +141,7 @@ class DeleteElementInSchemaQuery(Query):
138
141
  }
139
142
  WITH n1 as element_to_delete, rel_outband1 as rel_outband, p1 as peer_node
140
143
  WHERE rel_outband.status = "active"
141
- CALL {
144
+ CALL (%(sub_query_out_args)s) {
142
145
  %(sub_query_out)s
143
146
  }
144
147
  WITH p2 as peer_node, rel_outband, element_to_delete
@@ -157,7 +160,7 @@ class DeleteElementInSchemaQuery(Query):
157
160
  }
158
161
  WITH n1 as element_to_delete, rel_inband1 as rel_inband, p1 as peer_node
159
162
  WHERE rel_inband.status = "active"
160
- CALL {
163
+ CALL (%(sub_query_in_args)s) {
161
164
  %(sub_query_in)s
162
165
  }
163
166
  WITH p2 as peer_node, rel_inband, element_to_delete
@@ -169,5 +172,7 @@ class DeleteElementInSchemaQuery(Query):
169
172
  "branch_filter": branch_filter,
170
173
  "sub_query_out": sub_query_out,
171
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,
172
177
  }
173
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,8 +125,8 @@ 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
 
@@ -155,7 +154,7 @@ class NodeDuplicateQuery(Query):
155
154
  }
156
155
  WITH n1 as active_node, rel_outband1 as rel_outband, p1 as peer_node, new_node
157
156
  WHERE rel_outband.status = "active" AND rel_outband.to IS NULL
158
- CALL {
157
+ CALL (%(sub_query_out_args)s) {
159
158
  %(sub_query_out)s
160
159
  }
161
160
  WITH p2 as peer_node, rel_outband, active_node, new_node
@@ -174,7 +173,7 @@ class NodeDuplicateQuery(Query):
174
173
  }
175
174
  WITH n1 as active_node, rel_inband1 as rel_inband, p1 as peer_node, new_node
176
175
  WHERE rel_inband.status = "active" AND rel_inband.to IS NULL
177
- CALL {
176
+ CALL (%(sub_query_in_args)s) {
178
177
  %(sub_query_in)s
179
178
  }
180
179
  WITH p2 as peer_node, rel_inband, active_node, new_node
@@ -188,5 +187,7 @@ class NodeDuplicateQuery(Query):
188
187
  "labels": ":".join(self.new_node.labels),
189
188
  "sub_query_out": sub_query_out,
190
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,
191
192
  }
192
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,12 +112,11 @@ 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
121
  CALL (source, rel, destination) {
120
122
  MATCH path = (source)-[r1:IS_RELATED]-(rel)-[r2:IS_RELATED]-(destination)
@@ -138,7 +140,7 @@ class RelationshipDuplicateQuery(Query):
138
140
  }
139
141
  WITH n1 as active_rel, rel_inband1 as rel_inband, p1 as peer_node, new_rel
140
142
  WHERE rel_inband.status = "active"
141
- CALL {
143
+ CALL (%(sub_query_in_args)s) {
142
144
  %(sub_query_in)s
143
145
  }
144
146
  WITH p2 as peer_node, rel_inband, active_rel, new_rel
@@ -157,7 +159,7 @@ class RelationshipDuplicateQuery(Query):
157
159
  }
158
160
  WITH n1 as active_rel, rel_outband1 as rel_outband, p1 as peer_node, new_rel
159
161
  WHERE rel_outband.status = "active"
160
- CALL {
162
+ CALL (%(sub_query_out_args)s) {
161
163
  %(sub_query_out)s
162
164
  }
163
165
  WITH p2 as peer_node, rel_outband, active_rel, new_rel
@@ -169,5 +171,7 @@ class RelationshipDuplicateQuery(Query):
169
171
  "branch_filter": branch_filter,
170
172
  "sub_query_out": sub_query_out,
171
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,
172
176
  }
173
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
  ]
@@ -105,7 +104,7 @@ class NodeAttributeRemoveMigrationQuery01(AttributeMigrationQuery):
105
104
  }
106
105
  WITH a1 as active_attr, r1 as rb, p1 as peer_node
107
106
  WHERE rb.status = "active"
108
- CALL {
107
+ CALL (peer_node, rb, active_attr) {
109
108
  %(sub_query_all)s
110
109
  }
111
110
  WITH p2 as peer_node, rb, active_attr
@@ -21,7 +21,6 @@ class NodeRemoveMigrationBaseQuery(MigrationQuery):
21
21
  rel_def: FieldInfo,
22
22
  ) -> str:
23
23
  subquery = [
24
- f"WITH peer_node, {rel_name}, active_node",
25
24
  f"WITH peer_node, {rel_name}, active_node",
26
25
  f'WHERE type({rel_name}) = "{rel_type}"',
27
26
  ]
@@ -59,7 +58,6 @@ class NodeRemoveMigrationBaseQuery(MigrationQuery):
59
58
 
60
59
  node_remove_query = self.render_node_remove_query(branch_filter=branch_filter)
61
60
 
62
- # ruff: noqa: E501
63
61
  query = """
64
62
  // Find all the active nodes
65
63
  MATCH (node:%(node_kind)s)
@@ -90,7 +88,7 @@ class NodeRemoveMigrationQueryIn(NodeRemoveMigrationBaseQuery):
90
88
  insert_return: bool = False
91
89
 
92
90
  def render_node_remove_query(self, branch_filter: str) -> str:
93
- sub_query = self.render_sub_query_in()
91
+ sub_query, sub_query_args = self.render_sub_query_in()
94
92
  query = """
95
93
  // Process Inbound Relationship
96
94
  WITH active_node
@@ -104,27 +102,29 @@ class NodeRemoveMigrationQueryIn(NodeRemoveMigrationBaseQuery):
104
102
  }
105
103
  WITH n1 as active_node, rel_inband1 as rel_inband, p1 as peer_node
106
104
  WHERE rel_inband.status = "active"
107
- CALL {
105
+ CALL (%(sub_query_args)s) {
108
106
  %(sub_query)s
109
107
  }
110
108
  WITH p2 as peer_node, rel_inband, active_node
111
109
  FOREACH (i in CASE WHEN rel_inband.branch IN ["-global-", $branch] THEN [1] ELSE [] END |
112
110
  SET rel_inband.to = $current_time
113
111
  )
114
- """ % {"sub_query": sub_query, "branch_filter": branch_filter}
112
+ """ % {"sub_query": sub_query, "sub_query_args": sub_query_args, "branch_filter": branch_filter}
115
113
  return query
116
114
 
117
- def render_sub_query_in(self) -> str:
115
+ def render_sub_query_in(self) -> tuple[str, str]:
116
+ rel_name = "rel_inband"
117
+ sub_query_in_args = f"peer_node, {rel_name}, active_node"
118
118
  sub_queries_in = [
119
119
  self.render_sub_query_per_rel_type(
120
- rel_name="rel_inband",
120
+ rel_name=rel_name,
121
121
  rel_type=rel_type,
122
122
  rel_def=rel_def,
123
123
  )
124
124
  for rel_type, rel_def in GraphNodeRelationships.model_fields.items()
125
125
  ]
126
126
  sub_query_in = "\nUNION\n".join(sub_queries_in)
127
- return sub_query_in
127
+ return sub_query_in, sub_query_in_args
128
128
 
129
129
  def get_nbr_migrations_executed(self) -> int:
130
130
  return 0
@@ -135,7 +135,7 @@ class NodeRemoveMigrationQueryOut(NodeRemoveMigrationBaseQuery):
135
135
  insert_return: bool = False
136
136
 
137
137
  def render_node_remove_query(self, branch_filter: str) -> str:
138
- sub_query = self.render_sub_query_out()
138
+ sub_query, sub_query_args = self.render_sub_query_out()
139
139
  query = """
140
140
  // Process Outbound Relationship
141
141
  WITH active_node
@@ -149,27 +149,29 @@ class NodeRemoveMigrationQueryOut(NodeRemoveMigrationBaseQuery):
149
149
  }
150
150
  WITH n1 as active_node, rel_outband1 as rel_outband, p1 as peer_node
151
151
  WHERE rel_outband.status = "active"
152
- CALL {
152
+ CALL (%(sub_query_args)s) {
153
153
  %(sub_query)s
154
154
  }
155
155
  FOREACH (i in CASE WHEN rel_outband.branch IN ["-global-", $branch] THEN [1] ELSE [] END |
156
156
  SET rel_outband.to = $current_time
157
157
  )
158
- """ % {"sub_query": sub_query, "branch_filter": branch_filter}
158
+ """ % {"sub_query": sub_query, "sub_query_args": sub_query_args, "branch_filter": branch_filter}
159
159
 
160
160
  return query
161
161
 
162
- def render_sub_query_out(self) -> str:
162
+ def render_sub_query_out(self) -> tuple[str, str]:
163
+ rel_name = "rel_outband"
164
+ sub_query_out_args = f"peer_node, {rel_name}, active_node"
163
165
  sub_queries_out = [
164
166
  self.render_sub_query_per_rel_type(
165
- rel_name="rel_outband",
167
+ rel_name=rel_name,
166
168
  rel_type=rel_type,
167
169
  rel_def=rel_def,
168
170
  )
169
171
  for rel_type, rel_def in GraphNodeRelationships.model_fields.items()
170
172
  ]
171
173
  sub_query_out = "\nUNION\n".join(sub_queries_out)
172
- return sub_query_out
174
+ return sub_query_out, sub_query_out_args
173
175
 
174
176
  def get_nbr_migrations_executed(self) -> int:
175
177
  return self.num_of_results