infrahub-server 1.2.11__py3-none-any.whl → 1.3.0b1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (147) hide show
  1. infrahub/actions/constants.py +86 -0
  2. infrahub/actions/gather.py +114 -0
  3. infrahub/actions/models.py +241 -0
  4. infrahub/actions/parsers.py +104 -0
  5. infrahub/actions/schema.py +382 -0
  6. infrahub/actions/tasks.py +126 -0
  7. infrahub/actions/triggers.py +21 -0
  8. infrahub/cli/db.py +1 -2
  9. infrahub/core/account.py +24 -47
  10. infrahub/core/attribute.py +13 -15
  11. infrahub/core/constants/__init__.py +5 -0
  12. infrahub/core/constants/infrahubkind.py +9 -0
  13. infrahub/core/convert_object_type/__init__.py +0 -0
  14. infrahub/core/convert_object_type/conversion.py +122 -0
  15. infrahub/core/convert_object_type/schema_mapping.py +56 -0
  16. infrahub/core/diff/query/all_conflicts.py +1 -5
  17. infrahub/core/diff/query/artifact.py +10 -20
  18. infrahub/core/diff/query/diff_get.py +3 -6
  19. infrahub/core/diff/query/field_summary.py +2 -4
  20. infrahub/core/diff/query/merge.py +70 -123
  21. infrahub/core/diff/query/save.py +20 -32
  22. infrahub/core/diff/query/summary_counts_enricher.py +34 -54
  23. infrahub/core/manager.py +14 -11
  24. infrahub/core/migrations/graph/m003_relationship_parent_optional.py +1 -2
  25. infrahub/core/migrations/graph/m013_convert_git_password_credential.py +2 -4
  26. infrahub/core/migrations/graph/m019_restore_rels_to_time.py +11 -22
  27. infrahub/core/migrations/graph/m020_duplicate_edges.py +3 -6
  28. infrahub/core/migrations/graph/m021_missing_hierarchy_merge.py +1 -2
  29. infrahub/core/migrations/graph/m024_missing_hierarchy_backfill.py +1 -2
  30. infrahub/core/migrations/query/attribute_add.py +1 -2
  31. infrahub/core/migrations/query/attribute_rename.py +5 -10
  32. infrahub/core/migrations/query/delete_element_in_schema.py +19 -17
  33. infrahub/core/migrations/query/node_duplicate.py +19 -21
  34. infrahub/core/migrations/query/relationship_duplicate.py +19 -17
  35. infrahub/core/migrations/schema/node_attribute_remove.py +4 -8
  36. infrahub/core/migrations/schema/node_remove.py +19 -19
  37. infrahub/core/models.py +29 -2
  38. infrahub/core/node/__init__.py +90 -18
  39. infrahub/core/node/create.py +211 -0
  40. infrahub/core/node/resource_manager/number_pool.py +31 -5
  41. infrahub/core/node/standard.py +6 -1
  42. infrahub/core/protocols.py +56 -0
  43. infrahub/core/protocols_base.py +3 -0
  44. infrahub/core/query/__init__.py +2 -2
  45. infrahub/core/query/diff.py +19 -32
  46. infrahub/core/query/ipam.py +10 -20
  47. infrahub/core/query/node.py +28 -46
  48. infrahub/core/query/relationship.py +53 -32
  49. infrahub/core/query/resource_manager.py +1 -2
  50. infrahub/core/query/subquery.py +2 -4
  51. infrahub/core/relationship/model.py +3 -0
  52. infrahub/core/schema/__init__.py +2 -1
  53. infrahub/core/schema/attribute_parameters.py +160 -0
  54. infrahub/core/schema/attribute_schema.py +111 -8
  55. infrahub/core/schema/basenode_schema.py +25 -1
  56. infrahub/core/schema/definitions/core/__init__.py +29 -1
  57. infrahub/core/schema/definitions/core/group.py +45 -0
  58. infrahub/core/schema/definitions/internal.py +27 -4
  59. infrahub/core/schema/generated/attribute_schema.py +16 -3
  60. infrahub/core/schema/manager.py +3 -0
  61. infrahub/core/schema/schema_branch.py +67 -7
  62. infrahub/core/validators/__init__.py +13 -1
  63. infrahub/core/validators/attribute/choices.py +1 -3
  64. infrahub/core/validators/attribute/enum.py +1 -3
  65. infrahub/core/validators/attribute/kind.py +1 -3
  66. infrahub/core/validators/attribute/length.py +13 -7
  67. infrahub/core/validators/attribute/min_max.py +118 -0
  68. infrahub/core/validators/attribute/number_pool.py +106 -0
  69. infrahub/core/validators/attribute/optional.py +1 -4
  70. infrahub/core/validators/attribute/regex.py +5 -6
  71. infrahub/core/validators/attribute/unique.py +1 -3
  72. infrahub/core/validators/determiner.py +18 -2
  73. infrahub/core/validators/enum.py +12 -0
  74. infrahub/core/validators/node/hierarchy.py +3 -6
  75. infrahub/core/validators/query.py +1 -3
  76. infrahub/core/validators/relationship/count.py +6 -12
  77. infrahub/core/validators/relationship/optional.py +2 -4
  78. infrahub/core/validators/relationship/peer.py +3 -8
  79. infrahub/core/validators/uniqueness/query.py +5 -9
  80. infrahub/database/__init__.py +11 -2
  81. infrahub/events/group_action.py +1 -0
  82. infrahub/git/base.py +5 -3
  83. infrahub/git/integrator.py +102 -3
  84. infrahub/graphql/analyzer.py +139 -18
  85. infrahub/graphql/manager.py +4 -0
  86. infrahub/graphql/mutations/action.py +164 -0
  87. infrahub/graphql/mutations/convert_object_type.py +62 -0
  88. infrahub/graphql/mutations/main.py +24 -175
  89. infrahub/graphql/mutations/proposed_change.py +20 -17
  90. infrahub/graphql/mutations/resource_manager.py +62 -6
  91. infrahub/graphql/queries/convert_object_type_mapping.py +36 -0
  92. infrahub/graphql/queries/resource_manager.py +7 -1
  93. infrahub/graphql/schema.py +6 -0
  94. infrahub/menu/menu.py +31 -0
  95. infrahub/message_bus/messages/__init__.py +0 -10
  96. infrahub/message_bus/operations/__init__.py +0 -8
  97. infrahub/patch/queries/consolidate_duplicated_nodes.py +3 -6
  98. infrahub/patch/queries/delete_duplicated_edges.py +5 -10
  99. infrahub/pools/number.py +5 -3
  100. infrahub/prefect_server/models.py +1 -19
  101. infrahub/proposed_change/models.py +68 -3
  102. infrahub/proposed_change/tasks.py +907 -30
  103. infrahub/task_manager/models.py +10 -6
  104. infrahub/trigger/catalogue.py +2 -0
  105. infrahub/trigger/models.py +18 -2
  106. infrahub/trigger/tasks.py +3 -1
  107. infrahub/types.py +6 -0
  108. infrahub/workflows/catalogue.py +76 -0
  109. infrahub_sdk/client.py +43 -10
  110. infrahub_sdk/node/__init__.py +39 -0
  111. infrahub_sdk/node/attribute.py +122 -0
  112. infrahub_sdk/node/constants.py +21 -0
  113. infrahub_sdk/{node.py → node/node.py} +50 -749
  114. infrahub_sdk/node/parsers.py +15 -0
  115. infrahub_sdk/node/property.py +24 -0
  116. infrahub_sdk/node/related_node.py +266 -0
  117. infrahub_sdk/node/relationship.py +302 -0
  118. infrahub_sdk/protocols.py +112 -0
  119. infrahub_sdk/protocols_base.py +34 -2
  120. infrahub_sdk/query_groups.py +13 -2
  121. infrahub_sdk/schema/main.py +1 -0
  122. infrahub_sdk/schema/repository.py +16 -0
  123. infrahub_sdk/spec/object.py +1 -1
  124. infrahub_sdk/store.py +1 -1
  125. infrahub_sdk/testing/schemas/car_person.py +1 -0
  126. {infrahub_server-1.2.11.dist-info → infrahub_server-1.3.0b1.dist-info}/METADATA +4 -4
  127. {infrahub_server-1.2.11.dist-info → infrahub_server-1.3.0b1.dist-info}/RECORD +134 -122
  128. {infrahub_server-1.2.11.dist-info → infrahub_server-1.3.0b1.dist-info}/WHEEL +1 -1
  129. infrahub_testcontainers/container.py +0 -1
  130. infrahub_testcontainers/docker-compose.test.yml +1 -1
  131. infrahub_testcontainers/helpers.py +8 -2
  132. infrahub/message_bus/messages/check_generator_run.py +0 -26
  133. infrahub/message_bus/messages/finalize_validator_execution.py +0 -15
  134. infrahub/message_bus/messages/proposed_change/base_with_diff.py +0 -16
  135. infrahub/message_bus/messages/proposed_change/request_proposedchange_refreshartifacts.py +0 -11
  136. infrahub/message_bus/messages/request_generatordefinition_check.py +0 -20
  137. infrahub/message_bus/messages/request_proposedchange_pipeline.py +0 -23
  138. infrahub/message_bus/operations/check/__init__.py +0 -3
  139. infrahub/message_bus/operations/check/generator.py +0 -156
  140. infrahub/message_bus/operations/finalize/__init__.py +0 -3
  141. infrahub/message_bus/operations/finalize/validator.py +0 -133
  142. infrahub/message_bus/operations/requests/__init__.py +0 -9
  143. infrahub/message_bus/operations/requests/generator_definition.py +0 -140
  144. infrahub/message_bus/operations/requests/proposed_change.py +0 -629
  145. /infrahub/{message_bus/messages/proposed_change → actions}/__init__.py +0 -0
  146. {infrahub_server-1.2.11.dist-info → infrahub_server-1.3.0b1.dist-info}/LICENSE.txt +0 -0
  147. {infrahub_server-1.2.11.dist-info → infrahub_server-1.3.0b1.dist-info}/entry_points.txt +0 -0
@@ -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,14 @@ 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
120
  # ruff: noqa: E501
118
121
  query = """
119
- CALL {
120
- WITH source, rel, destination
122
+ CALL (source, rel, destination) {
121
123
  MATCH path = (source)-[r1:IS_RELATED]-(rel)-[r2:IS_RELATED]-(destination)
122
124
  WHERE all(r IN relationships(path) WHERE %(branch_filter)s)
123
125
  RETURN rel as rel1, r1 as r11, r2 as r12
@@ -130,8 +132,7 @@ class RelationshipDuplicateQuery(Query):
130
132
  WITH DISTINCT(active_rel) as active_rel, new_rel
131
133
  // Process Inbound Relationship
132
134
  MATCH (active_rel)<-[]-(peer)
133
- CALL {
134
- WITH active_rel, peer
135
+ CALL (active_rel, peer) {
135
136
  MATCH (active_rel)<-[r]-(peer)
136
137
  WHERE %(branch_filter)s
137
138
  RETURN active_rel as n1, r as rel_inband1, peer as p1
@@ -140,7 +141,7 @@ class RelationshipDuplicateQuery(Query):
140
141
  }
141
142
  WITH n1 as active_rel, rel_inband1 as rel_inband, p1 as peer_node, new_rel
142
143
  WHERE rel_inband.status = "active"
143
- CALL {
144
+ CALL (%(sub_query_in_args)s) {
144
145
  %(sub_query_in)s
145
146
  }
146
147
  WITH p2 as peer_node, rel_inband, active_rel, new_rel
@@ -150,8 +151,7 @@ class RelationshipDuplicateQuery(Query):
150
151
  WITH DISTINCT(active_rel) as active_rel, new_rel
151
152
  // Process Outbound Relationship
152
153
  MATCH (active_rel)-[]->(peer)
153
- CALL {
154
- WITH active_rel, peer
154
+ CALL (active_rel, peer) {
155
155
  MATCH (active_rel)-[r]->(peer)
156
156
  WHERE %(branch_filter)s
157
157
  RETURN active_rel as n1, r as rel_outband1, peer as p1
@@ -160,7 +160,7 @@ class RelationshipDuplicateQuery(Query):
160
160
  }
161
161
  WITH n1 as active_rel, rel_outband1 as rel_outband, p1 as peer_node, new_rel
162
162
  WHERE rel_outband.status = "active"
163
- CALL {
163
+ CALL (%(sub_query_out_args)s) {
164
164
  %(sub_query_out)s
165
165
  }
166
166
  WITH p2 as peer_node, rel_outband, active_rel, new_rel
@@ -172,5 +172,7 @@ class RelationshipDuplicateQuery(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_in_args": sub_query_in_args,
176
+ "sub_query_out_args": sub_query_out_args,
175
177
  }
176
178
  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
@@ -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
  ]
@@ -63,8 +62,7 @@ class NodeRemoveMigrationBaseQuery(MigrationQuery):
63
62
  query = """
64
63
  // Find all the active nodes
65
64
  MATCH (node:%(node_kind)s)
66
- CALL {
67
- WITH node
65
+ CALL (node) {
68
66
  MATCH (root:Root)<-[r:IS_PART_OF]-(node)
69
67
  WHERE %(branch_filter)s
70
68
  RETURN node as n1, r as r1
@@ -91,13 +89,12 @@ class NodeRemoveMigrationQueryIn(NodeRemoveMigrationBaseQuery):
91
89
  insert_return: bool = False
92
90
 
93
91
  def render_node_remove_query(self, branch_filter: str) -> str:
94
- sub_query = self.render_sub_query_in()
92
+ sub_query, sub_query_args = self.render_sub_query_in()
95
93
  query = """
96
94
  // Process Inbound Relationship
97
95
  WITH active_node
98
96
  MATCH (active_node)<-[]-(peer)
99
- CALL {
100
- WITH active_node, peer
97
+ CALL (active_node, peer) {
101
98
  MATCH (active_node)-[r]->(peer)
102
99
  WHERE %(branch_filter)s
103
100
  RETURN active_node as n1, r as rel_inband1, peer as p1
@@ -106,27 +103,29 @@ class NodeRemoveMigrationQueryIn(NodeRemoveMigrationBaseQuery):
106
103
  }
107
104
  WITH n1 as active_node, rel_inband1 as rel_inband, p1 as peer_node
108
105
  WHERE rel_inband.status = "active"
109
- CALL {
106
+ CALL (%(sub_query_args)s) {
110
107
  %(sub_query)s
111
108
  }
112
109
  WITH p2 as peer_node, rel_inband, active_node
113
110
  FOREACH (i in CASE WHEN rel_inband.branch IN ["-global-", $branch] THEN [1] ELSE [] END |
114
111
  SET rel_inband.to = $current_time
115
112
  )
116
- """ % {"sub_query": sub_query, "branch_filter": branch_filter}
113
+ """ % {"sub_query": sub_query, "sub_query_args": sub_query_args, "branch_filter": branch_filter}
117
114
  return query
118
115
 
119
- def render_sub_query_in(self) -> str:
116
+ def render_sub_query_in(self) -> tuple[str, str]:
117
+ rel_name = "rel_inband"
118
+ sub_query_in_args = f"peer_node, {rel_name}, active_node"
120
119
  sub_queries_in = [
121
120
  self.render_sub_query_per_rel_type(
122
- rel_name="rel_inband",
121
+ rel_name=rel_name,
123
122
  rel_type=rel_type,
124
123
  rel_def=rel_def,
125
124
  )
126
125
  for rel_type, rel_def in GraphNodeRelationships.model_fields.items()
127
126
  ]
128
127
  sub_query_in = "\nUNION\n".join(sub_queries_in)
129
- return sub_query_in
128
+ return sub_query_in, sub_query_in_args
130
129
 
131
130
  def get_nbr_migrations_executed(self) -> int:
132
131
  return 0
@@ -137,13 +136,12 @@ class NodeRemoveMigrationQueryOut(NodeRemoveMigrationBaseQuery):
137
136
  insert_return: bool = False
138
137
 
139
138
  def render_node_remove_query(self, branch_filter: str) -> str:
140
- sub_query = self.render_sub_query_out()
139
+ sub_query, sub_query_args = self.render_sub_query_out()
141
140
  query = """
142
141
  // Process Outbound Relationship
143
142
  WITH active_node
144
143
  MATCH (active_node)-[]->(peer)
145
- CALL {
146
- WITH active_node, peer
144
+ CALL (active_node, peer) {
147
145
  MATCH (active_node)-[r]->(peer)
148
146
  WHERE %(branch_filter)s
149
147
  RETURN active_node as n1, r as rel_outband1, peer as p1
@@ -152,27 +150,29 @@ class NodeRemoveMigrationQueryOut(NodeRemoveMigrationBaseQuery):
152
150
  }
153
151
  WITH n1 as active_node, rel_outband1 as rel_outband, p1 as peer_node
154
152
  WHERE rel_outband.status = "active"
155
- CALL {
153
+ CALL (%(sub_query_args)s) {
156
154
  %(sub_query)s
157
155
  }
158
156
  FOREACH (i in CASE WHEN rel_outband.branch IN ["-global-", $branch] THEN [1] ELSE [] END |
159
157
  SET rel_outband.to = $current_time
160
158
  )
161
- """ % {"sub_query": sub_query, "branch_filter": branch_filter}
159
+ """ % {"sub_query": sub_query, "sub_query_args": sub_query_args, "branch_filter": branch_filter}
162
160
 
163
161
  return query
164
162
 
165
- def render_sub_query_out(self) -> str:
163
+ def render_sub_query_out(self) -> tuple[str, str]:
164
+ rel_name = "rel_outband"
165
+ sub_query_out_args = f"peer_node, {rel_name}, active_node"
166
166
  sub_queries_out = [
167
167
  self.render_sub_query_per_rel_type(
168
- rel_name="rel_outband",
168
+ rel_name=rel_name,
169
169
  rel_type=rel_type,
170
170
  rel_def=rel_def,
171
171
  )
172
172
  for rel_type, rel_def in GraphNodeRelationships.model_fields.items()
173
173
  ]
174
174
  sub_query_out = "\nUNION\n".join(sub_queries_out)
175
- return sub_query_out
175
+ return sub_query_out, sub_query_out_args
176
176
 
177
177
  def get_nbr_migrations_executed(self) -> int:
178
178
  return self.num_of_results
infrahub/core/models.py CHANGED
@@ -20,6 +20,7 @@ if TYPE_CHECKING:
20
20
  from infrahub.core.schema.schema_branch import SchemaBranch
21
21
 
22
22
  GENERIC_ATTRIBUTES_TO_IGNORE = ["namespace", "name", "branch"]
23
+ PROPERTY_NAMES_TO_IGNORE = ["regex", "min_length", "max_length"]
23
24
 
24
25
 
25
26
  class NodeKind(BaseModel):
@@ -252,11 +253,37 @@ class SchemaUpdateValidationResult(BaseModel):
252
253
  if not sub_field_diff:
253
254
  raise ValueError("sub_field_diff must be defined, unexpected situation")
254
255
 
255
- for prop_name in sub_field_diff.changed:
256
+ for prop_name, prop_diff in sub_field_diff.changed.items():
257
+ if prop_name in PROPERTY_NAMES_TO_IGNORE:
258
+ continue
259
+
256
260
  field_info = field.model_fields[prop_name]
257
261
  field_update = str(field_info.json_schema_extra.get("update")) # type: ignore[union-attr]
258
262
 
259
- schema_path = SchemaPath( # type: ignore[call-arg]
263
+ if isinstance(prop_diff, HashableModelDiff):
264
+ for param_field_name in prop_diff.changed:
265
+ # override field_update if this field has its own json_schema_extra.update
266
+ try:
267
+ prop_field = getattr(field, prop_name)
268
+ param_field_info = prop_field.model_fields[param_field_name]
269
+ param_field_update = str(param_field_info.json_schema_extra.get("update"))
270
+ except (AttributeError, KeyError):
271
+ param_field_update = None
272
+
273
+ schema_path = SchemaPath(
274
+ schema_kind=schema.kind,
275
+ path_type=path_type,
276
+ field_name=field_name,
277
+ property_name=f"{prop_name}.{param_field_name}",
278
+ )
279
+
280
+ self._process_field(
281
+ schema_path=schema_path,
282
+ field_update=param_field_update or field_update,
283
+ )
284
+ continue
285
+
286
+ schema_path = SchemaPath(
260
287
  schema_kind=schema.kind,
261
288
  path_type=path_type,
262
289
  field_name=field_name,