infrahub-server 1.2.0b1__py3-none-any.whl → 1.2.1__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 (297) hide show
  1. infrahub/api/dependencies.py +6 -6
  2. infrahub/api/diff/validation_models.py +7 -7
  3. infrahub/api/schema.py +1 -1
  4. infrahub/artifacts/models.py +1 -3
  5. infrahub/artifacts/tasks.py +1 -3
  6. infrahub/cli/__init__.py +13 -9
  7. infrahub/cli/constants.py +3 -0
  8. infrahub/cli/db.py +165 -183
  9. infrahub/cli/upgrade.py +146 -0
  10. infrahub/computed_attribute/gather.py +185 -0
  11. infrahub/computed_attribute/models.py +239 -11
  12. infrahub/computed_attribute/tasks.py +77 -442
  13. infrahub/computed_attribute/triggers.py +11 -45
  14. infrahub/config.py +43 -32
  15. infrahub/context.py +14 -0
  16. infrahub/core/account.py +4 -4
  17. infrahub/core/attribute.py +57 -57
  18. infrahub/core/branch/tasks.py +12 -9
  19. infrahub/core/changelog/diff.py +16 -8
  20. infrahub/core/changelog/models.py +189 -26
  21. infrahub/core/constants/__init__.py +5 -1
  22. infrahub/core/constants/infrahubkind.py +2 -0
  23. infrahub/core/constraint/node/runner.py +9 -8
  24. infrahub/core/diff/branch_differ.py +10 -10
  25. infrahub/core/diff/ipam_diff_parser.py +4 -5
  26. infrahub/core/diff/model/diff.py +27 -27
  27. infrahub/core/diff/model/path.py +3 -3
  28. infrahub/core/diff/query/merge.py +20 -17
  29. infrahub/core/diff/query_parser.py +4 -4
  30. infrahub/core/graph/__init__.py +1 -1
  31. infrahub/core/initialization.py +1 -10
  32. infrahub/core/ipam/constants.py +3 -4
  33. infrahub/core/ipam/reconciler.py +12 -12
  34. infrahub/core/ipam/utilization.py +10 -13
  35. infrahub/core/manager.py +34 -34
  36. infrahub/core/merge.py +7 -7
  37. infrahub/core/migrations/__init__.py +2 -3
  38. infrahub/core/migrations/graph/__init__.py +9 -4
  39. infrahub/core/migrations/graph/m017_add_core_profile.py +1 -5
  40. infrahub/core/migrations/graph/m018_uniqueness_nulls.py +4 -4
  41. infrahub/core/migrations/graph/m020_duplicate_edges.py +160 -0
  42. infrahub/core/migrations/graph/m021_missing_hierarchy_merge.py +51 -0
  43. infrahub/core/migrations/graph/{m020_add_generate_template_attr.py → m022_add_generate_template_attr.py} +3 -3
  44. infrahub/core/migrations/graph/m023_deduplicate_cardinality_one_relationships.py +96 -0
  45. infrahub/core/migrations/query/attribute_add.py +2 -2
  46. infrahub/core/migrations/query/node_duplicate.py +18 -21
  47. infrahub/core/migrations/query/schema_attribute_update.py +2 -2
  48. infrahub/core/migrations/schema/models.py +19 -4
  49. infrahub/core/migrations/schema/tasks.py +2 -2
  50. infrahub/core/migrations/shared.py +16 -16
  51. infrahub/core/models.py +15 -6
  52. infrahub/core/node/__init__.py +29 -28
  53. infrahub/core/node/base.py +2 -4
  54. infrahub/core/node/constraints/attribute_uniqueness.py +2 -2
  55. infrahub/core/node/constraints/grouped_uniqueness.py +99 -47
  56. infrahub/core/node/constraints/interface.py +1 -2
  57. infrahub/core/node/delete_validator.py +3 -5
  58. infrahub/core/node/ipam.py +4 -4
  59. infrahub/core/node/permissions.py +7 -7
  60. infrahub/core/node/resource_manager/ip_address_pool.py +6 -6
  61. infrahub/core/node/resource_manager/ip_prefix_pool.py +6 -6
  62. infrahub/core/node/resource_manager/number_pool.py +3 -3
  63. infrahub/core/path.py +12 -12
  64. infrahub/core/property.py +11 -11
  65. infrahub/core/protocols.py +5 -0
  66. infrahub/core/protocols_base.py +21 -21
  67. infrahub/core/query/__init__.py +33 -33
  68. infrahub/core/query/attribute.py +6 -4
  69. infrahub/core/query/diff.py +3 -3
  70. infrahub/core/query/node.py +82 -32
  71. infrahub/core/query/relationship.py +24 -24
  72. infrahub/core/query/resource_manager.py +2 -0
  73. infrahub/core/query/standard_node.py +3 -3
  74. infrahub/core/query/subquery.py +9 -9
  75. infrahub/core/registry.py +13 -15
  76. infrahub/core/relationship/constraints/count.py +3 -4
  77. infrahub/core/relationship/constraints/peer_kind.py +3 -4
  78. infrahub/core/relationship/constraints/profiles_kind.py +2 -2
  79. infrahub/core/relationship/model.py +40 -46
  80. infrahub/core/schema/attribute_schema.py +9 -9
  81. infrahub/core/schema/basenode_schema.py +93 -44
  82. infrahub/core/schema/computed_attribute.py +3 -3
  83. infrahub/core/schema/definitions/core/__init__.py +13 -19
  84. infrahub/core/schema/definitions/core/account.py +151 -148
  85. infrahub/core/schema/definitions/core/artifact.py +122 -113
  86. infrahub/core/schema/definitions/core/builtin.py +19 -16
  87. infrahub/core/schema/definitions/core/check.py +61 -53
  88. infrahub/core/schema/definitions/core/core.py +17 -0
  89. infrahub/core/schema/definitions/core/generator.py +89 -85
  90. infrahub/core/schema/definitions/core/graphql_query.py +72 -70
  91. infrahub/core/schema/definitions/core/group.py +96 -93
  92. infrahub/core/schema/definitions/core/ipam.py +176 -235
  93. infrahub/core/schema/definitions/core/lineage.py +18 -16
  94. infrahub/core/schema/definitions/core/menu.py +42 -40
  95. infrahub/core/schema/definitions/core/permission.py +144 -142
  96. infrahub/core/schema/definitions/core/profile.py +16 -27
  97. infrahub/core/schema/definitions/core/propose_change.py +88 -79
  98. infrahub/core/schema/definitions/core/propose_change_comment.py +170 -165
  99. infrahub/core/schema/definitions/core/propose_change_validator.py +290 -288
  100. infrahub/core/schema/definitions/core/repository.py +231 -225
  101. infrahub/core/schema/definitions/core/resource_pool.py +156 -166
  102. infrahub/core/schema/definitions/core/template.py +27 -12
  103. infrahub/core/schema/definitions/core/transform.py +85 -76
  104. infrahub/core/schema/definitions/core/webhook.py +127 -101
  105. infrahub/core/schema/definitions/internal.py +16 -16
  106. infrahub/core/schema/dropdown.py +3 -4
  107. infrahub/core/schema/generated/attribute_schema.py +15 -18
  108. infrahub/core/schema/generated/base_node_schema.py +12 -14
  109. infrahub/core/schema/generated/node_schema.py +3 -5
  110. infrahub/core/schema/generated/relationship_schema.py +9 -11
  111. infrahub/core/schema/generic_schema.py +2 -2
  112. infrahub/core/schema/manager.py +20 -9
  113. infrahub/core/schema/node_schema.py +4 -2
  114. infrahub/core/schema/relationship_schema.py +7 -7
  115. infrahub/core/schema/schema_branch.py +276 -138
  116. infrahub/core/schema/schema_branch_computed.py +41 -4
  117. infrahub/core/task/task.py +3 -3
  118. infrahub/core/task/user_task.py +15 -15
  119. infrahub/core/utils.py +20 -18
  120. infrahub/core/validators/__init__.py +1 -3
  121. infrahub/core/validators/aggregated_checker.py +2 -2
  122. infrahub/core/validators/attribute/choices.py +2 -2
  123. infrahub/core/validators/attribute/enum.py +2 -2
  124. infrahub/core/validators/attribute/kind.py +2 -2
  125. infrahub/core/validators/attribute/length.py +2 -2
  126. infrahub/core/validators/attribute/optional.py +2 -2
  127. infrahub/core/validators/attribute/regex.py +2 -2
  128. infrahub/core/validators/attribute/unique.py +2 -2
  129. infrahub/core/validators/checks_runner.py +25 -2
  130. infrahub/core/validators/determiner.py +1 -3
  131. infrahub/core/validators/interface.py +6 -2
  132. infrahub/core/validators/model.py +22 -3
  133. infrahub/core/validators/models/validate_migration.py +17 -4
  134. infrahub/core/validators/node/attribute.py +2 -2
  135. infrahub/core/validators/node/generate_profile.py +2 -2
  136. infrahub/core/validators/node/hierarchy.py +3 -5
  137. infrahub/core/validators/node/inherit_from.py +27 -5
  138. infrahub/core/validators/node/relationship.py +2 -2
  139. infrahub/core/validators/relationship/count.py +4 -4
  140. infrahub/core/validators/relationship/optional.py +2 -2
  141. infrahub/core/validators/relationship/peer.py +2 -2
  142. infrahub/core/validators/shared.py +2 -2
  143. infrahub/core/validators/tasks.py +8 -0
  144. infrahub/core/validators/uniqueness/checker.py +22 -21
  145. infrahub/core/validators/uniqueness/index.py +2 -2
  146. infrahub/core/validators/uniqueness/model.py +11 -11
  147. infrahub/database/__init__.py +26 -22
  148. infrahub/database/metrics.py +7 -1
  149. infrahub/dependencies/builder/constraint/grouped/node_runner.py +1 -3
  150. infrahub/dependencies/component/registry.py +2 -2
  151. infrahub/events/__init__.py +25 -2
  152. infrahub/events/artifact_action.py +13 -25
  153. infrahub/events/branch_action.py +26 -18
  154. infrahub/events/generator.py +71 -0
  155. infrahub/events/group_action.py +10 -24
  156. infrahub/events/models.py +10 -16
  157. infrahub/events/node_action.py +87 -32
  158. infrahub/events/repository_action.py +5 -18
  159. infrahub/events/schema_action.py +4 -9
  160. infrahub/events/utils.py +16 -0
  161. infrahub/events/validator_action.py +55 -0
  162. infrahub/exceptions.py +23 -24
  163. infrahub/generators/models.py +1 -3
  164. infrahub/git/base.py +7 -7
  165. infrahub/git/integrator.py +26 -25
  166. infrahub/git/models.py +22 -9
  167. infrahub/git/repository.py +3 -3
  168. infrahub/git/tasks.py +67 -49
  169. infrahub/git/utils.py +48 -0
  170. infrahub/git/worktree.py +1 -2
  171. infrahub/git_credential/askpass.py +1 -2
  172. infrahub/graphql/analyzer.py +12 -0
  173. infrahub/graphql/app.py +13 -15
  174. infrahub/graphql/context.py +6 -0
  175. infrahub/graphql/initialization.py +3 -0
  176. infrahub/graphql/loaders/node.py +2 -12
  177. infrahub/graphql/loaders/peers.py +77 -0
  178. infrahub/graphql/loaders/shared.py +13 -0
  179. infrahub/graphql/manager.py +13 -10
  180. infrahub/graphql/mutations/artifact_definition.py +5 -5
  181. infrahub/graphql/mutations/computed_attribute.py +4 -5
  182. infrahub/graphql/mutations/graphql_query.py +5 -5
  183. infrahub/graphql/mutations/ipam.py +50 -70
  184. infrahub/graphql/mutations/main.py +164 -141
  185. infrahub/graphql/mutations/menu.py +5 -5
  186. infrahub/graphql/mutations/models.py +2 -4
  187. infrahub/graphql/mutations/node_getter/by_default_filter.py +10 -10
  188. infrahub/graphql/mutations/node_getter/by_hfid.py +1 -3
  189. infrahub/graphql/mutations/node_getter/by_id.py +1 -3
  190. infrahub/graphql/mutations/node_getter/interface.py +1 -2
  191. infrahub/graphql/mutations/proposed_change.py +7 -7
  192. infrahub/graphql/mutations/relationship.py +67 -35
  193. infrahub/graphql/mutations/repository.py +8 -8
  194. infrahub/graphql/mutations/resource_manager.py +3 -3
  195. infrahub/graphql/mutations/schema.py +4 -4
  196. infrahub/graphql/mutations/webhook.py +137 -0
  197. infrahub/graphql/parser.py +4 -4
  198. infrahub/graphql/queries/diff/tree.py +4 -4
  199. infrahub/graphql/queries/ipam.py +2 -2
  200. infrahub/graphql/queries/relationship.py +2 -2
  201. infrahub/graphql/queries/search.py +2 -2
  202. infrahub/graphql/resolvers/many_relationship.py +264 -0
  203. infrahub/graphql/resolvers/resolver.py +13 -110
  204. infrahub/graphql/subscription/graphql_query.py +2 -0
  205. infrahub/graphql/types/event.py +20 -11
  206. infrahub/graphql/types/node.py +2 -2
  207. infrahub/graphql/utils.py +2 -2
  208. infrahub/groups/ancestors.py +29 -0
  209. infrahub/groups/parsers.py +107 -0
  210. infrahub/menu/generator.py +7 -7
  211. infrahub/menu/menu.py +0 -10
  212. infrahub/menu/models.py +117 -16
  213. infrahub/menu/repository.py +111 -0
  214. infrahub/menu/utils.py +5 -8
  215. infrahub/message_bus/messages/__init__.py +1 -11
  216. infrahub/message_bus/messages/check_generator_run.py +2 -0
  217. infrahub/message_bus/messages/finalize_validator_execution.py +3 -0
  218. infrahub/message_bus/messages/request_generatordefinition_check.py +2 -0
  219. infrahub/message_bus/operations/__init__.py +0 -2
  220. infrahub/message_bus/operations/check/generator.py +1 -0
  221. infrahub/message_bus/operations/event/__init__.py +2 -2
  222. infrahub/message_bus/operations/finalize/validator.py +51 -1
  223. infrahub/message_bus/operations/requests/generator_definition.py +19 -19
  224. infrahub/message_bus/operations/requests/proposed_change.py +3 -1
  225. infrahub/pools/number.py +2 -4
  226. infrahub/proposed_change/tasks.py +37 -28
  227. infrahub/pytest_plugin.py +13 -10
  228. infrahub/server.py +1 -2
  229. infrahub/services/adapters/event/__init__.py +1 -1
  230. infrahub/task_manager/event.py +23 -9
  231. infrahub/tasks/artifact.py +2 -4
  232. infrahub/telemetry/__init__.py +0 -0
  233. infrahub/telemetry/constants.py +9 -0
  234. infrahub/telemetry/database.py +86 -0
  235. infrahub/telemetry/models.py +65 -0
  236. infrahub/telemetry/task_manager.py +77 -0
  237. infrahub/{tasks/telemetry.py → telemetry/tasks.py} +49 -56
  238. infrahub/telemetry/utils.py +11 -0
  239. infrahub/trace.py +4 -4
  240. infrahub/transformations/tasks.py +2 -2
  241. infrahub/trigger/catalogue.py +2 -5
  242. infrahub/trigger/constants.py +0 -8
  243. infrahub/trigger/models.py +14 -1
  244. infrahub/trigger/setup.py +90 -0
  245. infrahub/trigger/tasks.py +35 -90
  246. infrahub/utils.py +11 -1
  247. infrahub/validators/__init__.py +0 -0
  248. infrahub/validators/events.py +42 -0
  249. infrahub/validators/tasks.py +41 -0
  250. infrahub/webhook/gather.py +17 -0
  251. infrahub/webhook/models.py +22 -5
  252. infrahub/webhook/tasks.py +44 -19
  253. infrahub/webhook/triggers.py +22 -5
  254. infrahub/workers/infrahub_async.py +2 -2
  255. infrahub/workers/utils.py +2 -2
  256. infrahub/workflows/catalogue.py +28 -20
  257. infrahub/workflows/initialization.py +1 -3
  258. infrahub/workflows/models.py +1 -1
  259. infrahub/workflows/utils.py +10 -1
  260. infrahub_sdk/client.py +27 -8
  261. infrahub_sdk/config.py +3 -0
  262. infrahub_sdk/context.py +13 -0
  263. infrahub_sdk/exceptions.py +6 -0
  264. infrahub_sdk/generator.py +4 -1
  265. infrahub_sdk/graphql.py +45 -13
  266. infrahub_sdk/node.py +69 -20
  267. infrahub_sdk/protocols_base.py +32 -11
  268. infrahub_sdk/query_groups.py +6 -35
  269. infrahub_sdk/schema/__init__.py +55 -26
  270. infrahub_sdk/schema/main.py +8 -0
  271. infrahub_sdk/task/__init__.py +10 -0
  272. infrahub_sdk/task/manager.py +12 -6
  273. infrahub_sdk/testing/schemas/animal.py +9 -0
  274. infrahub_sdk/timestamp.py +12 -4
  275. {infrahub_server-1.2.0b1.dist-info → infrahub_server-1.2.1.dist-info}/METADATA +3 -2
  276. {infrahub_server-1.2.0b1.dist-info → infrahub_server-1.2.1.dist-info}/RECORD +289 -260
  277. {infrahub_server-1.2.0b1.dist-info → infrahub_server-1.2.1.dist-info}/entry_points.txt +1 -0
  278. infrahub_testcontainers/constants.py +2 -0
  279. infrahub_testcontainers/container.py +157 -12
  280. infrahub_testcontainers/docker-compose.test.yml +31 -6
  281. infrahub_testcontainers/helpers.py +18 -73
  282. infrahub_testcontainers/host.py +41 -0
  283. infrahub_testcontainers/measurements.py +93 -0
  284. infrahub_testcontainers/models.py +38 -0
  285. infrahub_testcontainers/performance_test.py +166 -0
  286. infrahub_testcontainers/plugin.py +136 -0
  287. infrahub_testcontainers/prometheus.yml +30 -0
  288. infrahub/message_bus/messages/event_branch_create.py +0 -11
  289. infrahub/message_bus/messages/event_branch_delete.py +0 -11
  290. infrahub/message_bus/messages/event_branch_rebased.py +0 -9
  291. infrahub/message_bus/messages/event_node_mutated.py +0 -15
  292. infrahub/message_bus/messages/event_schema_update.py +0 -9
  293. infrahub/message_bus/operations/event/node.py +0 -20
  294. infrahub/message_bus/operations/event/schema.py +0 -17
  295. infrahub/webhook/constants.py +0 -1
  296. {infrahub_server-1.2.0b1.dist-info → infrahub_server-1.2.1.dist-info}/LICENSE.txt +0 -0
  297. {infrahub_server-1.2.0b1.dist-info → infrahub_server-1.2.1.dist-info}/WHEEL +0 -0
@@ -42,6 +42,17 @@ class ComputedAttributeTarget(BaseModel):
42
42
  return hash((self.kind, self.attribute, tuple(self.filter_keys)))
43
43
 
44
44
 
45
+ class ComputedAttributeTriggerNode(BaseModel):
46
+ kind: str
47
+ attributes: list[str] = Field(default_factory=list)
48
+ relationships: list[str] = Field(default_factory=list)
49
+ targets_self: bool = Field(default=False)
50
+
51
+ @property
52
+ def fields(self) -> list[str]:
53
+ return self.attributes + self.relationships
54
+
55
+
45
56
  class RegisteredNodeComputedAttribute(BaseModel):
46
57
  local_fields: dict[str, list[ComputedAttributeTarget]] = Field(
47
58
  default_factory=dict,
@@ -129,17 +140,17 @@ class ComputedAttributes:
129
140
  if schema_path.active_attribute_schema.name not in trigger_node.local_fields:
130
141
  trigger_node.local_fields[schema_path.active_attribute_schema.name] = []
131
142
 
132
- trigger_node.local_fields[schema_path.active_attribute_schema.name].append(source_attribute)
143
+ trigger_node.local_fields[schema_path.active_attribute_schema.name].append(deepcopy(source_attribute))
133
144
  elif schema_path.is_type_relationship:
134
145
  if schema_path.active_attribute_schema.name not in trigger_node.local_fields:
135
146
  trigger_node.local_fields[schema_path.active_attribute_schema.name] = []
136
147
 
137
- trigger_node.local_fields[schema_path.active_attribute_schema.name].append(source_attribute)
148
+ trigger_node.local_fields[schema_path.active_attribute_schema.name].append(deepcopy(source_attribute))
138
149
 
139
150
  if schema_path.active_relationship_schema.name not in trigger_node.relationships:
140
151
  trigger_node.relationships[schema_path.active_relationship_schema.name] = []
141
152
 
142
- trigger_node.relationships[schema_path.active_relationship_schema.name].append(source_attribute)
153
+ trigger_node.relationships[schema_path.active_relationship_schema.name].append(deepcopy(source_attribute))
143
154
 
144
155
  if source_attribute.kind not in self._computed_jinja2_attribute_map:
145
156
  self._computed_jinja2_attribute_map[source_attribute.kind] = RegisteredNodeComputedAttribute()
@@ -153,7 +164,7 @@ class ComputedAttributes:
153
164
  ] = []
154
165
  self._computed_jinja2_attribute_map[source_attribute.kind].local_fields[
155
166
  schema_path.active_relationship_schema.name
156
- ].append(source_attribute)
167
+ ].append(deepcopy(source_attribute))
157
168
 
158
169
  def get_impacted_jinja2_targets(self, kind: str, updates: list[str] | None = None) -> list[ComputedAttributeTarget]:
159
170
  if mapping := self._computed_jinja2_attribute_map.get(kind):
@@ -172,3 +183,29 @@ class ComputedAttributes:
172
183
  mapping[local_field].add(node)
173
184
 
174
185
  return {key: list(value) for key, value in mapping.items()}
186
+
187
+ def get_jinja2_trigger_nodes(self) -> dict[ComputedAttributeTarget, list[ComputedAttributeTriggerNode]]:
188
+ working_map: dict[ComputedAttributeTarget, dict[str, ComputedAttributeTriggerNode]] = {}
189
+ for node_kind, registered_computed_attribute in self._computed_jinja2_attribute_map.items():
190
+ for local_field, computed_attribute_targets in registered_computed_attribute.local_fields.items():
191
+ for computed_attribute_target in computed_attribute_targets:
192
+ if computed_attribute_target not in working_map:
193
+ working_map[computed_attribute_target] = {}
194
+ if node_kind not in working_map[computed_attribute_target]:
195
+ working_map[computed_attribute_target][node_kind] = ComputedAttributeTriggerNode(kind=node_kind)
196
+ working_map[computed_attribute_target][node_kind].attributes.append(local_field)
197
+ if computed_attribute_target.kind == node_kind:
198
+ working_map[computed_attribute_target][node_kind].targets_self = True
199
+ for relationship, computed_attribute_targets in registered_computed_attribute.relationships.items():
200
+ for computed_attribute_target in computed_attribute_targets:
201
+ if computed_attribute_target not in working_map:
202
+ working_map[computed_attribute_target] = {}
203
+ if node_kind not in working_map[computed_attribute_target]:
204
+ working_map[computed_attribute_target][node_kind] = ComputedAttributeTriggerNode(kind=node_kind)
205
+ working_map[computed_attribute_target][node_kind].relationships.append(relationship)
206
+ if computed_attribute_target.kind == node_kind:
207
+ working_map[computed_attribute_target][node_kind].targets_self = True
208
+
209
+ return {
210
+ computed_attribute_target: list(nodes.values()) for computed_attribute_target, nodes in working_map.items()
211
+ }
@@ -1,4 +1,4 @@
1
- from typing import Any, Optional
1
+ from typing import Any
2
2
 
3
3
  from pydantic import ConfigDict, Field
4
4
 
@@ -19,10 +19,10 @@ class Task(StandardNode):
19
19
 
20
20
  title: str
21
21
  conclusion: TaskConclusion
22
- account_id: Optional[str] = Field(default=None, description="The ID of the account that created this task")
22
+ account_id: str | None = Field(default=None, description="The ID of the account that created this task")
23
23
  created_at: str = Field(default_factory=current_timestamp, description="The time when this task was created")
24
24
  updated_at: str = Field(default_factory=current_timestamp, description="The time when this task was last updated")
25
- related_node: Optional[CoreNode] = Field(default=None, description="The Infrahub node that this object refers to")
25
+ related_node: CoreNode | None = Field(default=None, description="The Infrahub node that this object refers to")
26
26
 
27
27
  _exclude_attrs: list[str] = ["id", "uuid", "account_id", "_query", "related_node"]
28
28
  _query: type[StandardNodeQuery] = TaskNodeCreateQuery
@@ -1,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import TYPE_CHECKING, Any, Optional, Union
3
+ from typing import TYPE_CHECKING, Any
4
4
 
5
5
  from typing_extensions import Self
6
6
 
@@ -27,9 +27,9 @@ class UserTask:
27
27
  self,
28
28
  title: str,
29
29
  db: InfrahubDatabase,
30
- account: Optional[CoreGenericAccount] = None,
31
- account_id: Optional[str] = None,
32
- logger: Optional[Union[BoundLogger, InfrahubLogger]] = None,
30
+ account: CoreGenericAccount | None = None,
31
+ account_id: str | None = None,
32
+ logger: BoundLogger | InfrahubLogger | None = None,
33
33
  ) -> None:
34
34
  if not account and not account_id:
35
35
  raise ValueError("Either account or account_id must be provided to initialize UserTask")
@@ -41,8 +41,8 @@ class UserTask:
41
41
  self.account_id = account.id
42
42
 
43
43
  self.title = title
44
- self._task: Optional[Task]
45
- self.log: Union[BoundLogger, InfrahubLogger] = logger or get_logger()
44
+ self._task: Task | None
45
+ self.log: BoundLogger | InfrahubLogger = logger or get_logger()
46
46
  self.db = db
47
47
 
48
48
  @property
@@ -67,7 +67,7 @@ class UserTask:
67
67
  if self._account:
68
68
  return False
69
69
 
70
- account: Optional[CoreGenericAccount] = await registry.manager.get_one(id=self.account_id, db=self.db)
70
+ account: CoreGenericAccount | None = await registry.manager.get_one(id=self.account_id, db=self.db)
71
71
  if not account:
72
72
  raise ValueError(f"Unable to find the account associated with {self.account_id}")
73
73
  self._account = account
@@ -80,7 +80,7 @@ class UserTask:
80
80
 
81
81
  @classmethod
82
82
  def from_graphql_context(
83
- cls, title: str, graphql_context: GraphqlContext, logger: Optional[Union[BoundLogger, InfrahubLogger]] = None
83
+ cls, title: str, graphql_context: GraphqlContext, logger: BoundLogger | InfrahubLogger | None = None
84
84
  ) -> Self:
85
85
  if not graphql_context.db or not graphql_context.account_session:
86
86
  raise ValueError("db and account_session must be provided to initialize a GraphQLTaskReport")
@@ -97,9 +97,9 @@ class UserTask:
97
97
 
98
98
  async def __aexit__(
99
99
  self,
100
- exc_type: Optional[type[BaseException]],
101
- exc_value: Optional[BaseException],
102
- traceback: Optional[TracebackType],
100
+ exc_type: type[BaseException] | None,
101
+ exc_value: BaseException | None,
102
+ traceback: TracebackType | None,
103
103
  ) -> None:
104
104
  if exc_type:
105
105
  await self.error(message=str(exc_value))
@@ -109,22 +109,22 @@ class UserTask:
109
109
  await self.task.save(db=self.db)
110
110
 
111
111
  async def add_log(
112
- self, message: str, severity: Severity = Severity.INFO, db: Optional[InfrahubDatabase] = None
112
+ self, message: str, severity: Severity = Severity.INFO, db: InfrahubDatabase | None = None
113
113
  ) -> None:
114
114
  tlog = TaskLog(message=message, severity=severity, task_id=self.task_id)
115
115
  await tlog.save(db=db or self.db)
116
116
 
117
- async def info(self, message: str, db: Optional[InfrahubDatabase] = None, **kwargs: Any) -> None:
117
+ async def info(self, message: str, db: InfrahubDatabase | None = None, **kwargs: Any) -> None:
118
118
  if self.log:
119
119
  self.log.info(message, **kwargs)
120
120
  await self.add_log(message=message, severity=Severity.INFO, db=db)
121
121
 
122
- async def warning(self, message: str, db: Optional[InfrahubDatabase] = None, **kwargs: Any) -> None:
122
+ async def warning(self, message: str, db: InfrahubDatabase | None = None, **kwargs: Any) -> None:
123
123
  if self.log:
124
124
  self.log.warning(message, **kwargs)
125
125
  await self.add_log(message=message, severity=Severity.WARNING, db=db)
126
126
 
127
- async def error(self, message: str, db: Optional[InfrahubDatabase] = None, **kwargs: Any) -> None:
127
+ async def error(self, message: str, db: InfrahubDatabase | None = None, **kwargs: Any) -> None:
128
128
  if self.log:
129
129
  self.log.error(message, **kwargs)
130
130
  await self.add_log(message=message, severity=Severity.ERROR, db=db)
infrahub/core/utils.py CHANGED
@@ -3,7 +3,7 @@ from __future__ import annotations
3
3
  import ipaddress
4
4
  import re
5
5
  from inspect import isclass
6
- from typing import TYPE_CHECKING, Any, Optional, Union
6
+ from typing import TYPE_CHECKING, Any
7
7
 
8
8
  from infrahub.core.constants import RelationshipStatus
9
9
  from infrahub.core.models import NodeKind
@@ -23,10 +23,10 @@ async def add_relationship(
23
23
  dst_node_id: str,
24
24
  rel_type: str,
25
25
  db: InfrahubDatabase,
26
- branch_name: Optional[str] = None,
27
- branch_level: Optional[int] = None,
28
- at: Optional[Timestamp] = None,
29
- status=RelationshipStatus.ACTIVE,
26
+ branch_name: str | None = None,
27
+ branch_level: int | None = None,
28
+ at: Timestamp | None = None,
29
+ status: RelationshipStatus = RelationshipStatus.ACTIVE,
30
30
  ) -> Record | None:
31
31
  create_rel_query = """
32
32
  MATCH (s) WHERE %(id_func)s(s) = $src_node_id
@@ -62,7 +62,9 @@ async def delete_all_relationships_for_branch(branch_name: str, db: InfrahubData
62
62
  await db.execute_query(query=query, params=params, name="delete_all_relationships_for_branch")
63
63
 
64
64
 
65
- async def update_relationships_to(ids: list[str], db: InfrahubDatabase, to: Timestamp = None) -> list[Record] | None:
65
+ async def update_relationships_to(
66
+ ids: list[str], db: InfrahubDatabase, to: Timestamp | None = None
67
+ ) -> list[Record] | None:
66
68
  """Update the "to" field on one or multiple relationships."""
67
69
  if not ids:
68
70
  return None
@@ -86,9 +88,9 @@ async def get_paths_between_nodes(
86
88
  db: InfrahubDatabase,
87
89
  source_id: str,
88
90
  destination_id: str,
89
- relationships: Optional[list[str]] = None,
90
- max_length: Optional[int] = None,
91
- print_query=False,
91
+ relationships: list[str] | None = None,
92
+ max_length: int | None = None,
93
+ print_query: bool = False,
92
94
  ) -> list[Record]:
93
95
  """Return all paths between 2 nodes."""
94
96
 
@@ -115,7 +117,7 @@ async def get_paths_between_nodes(
115
117
  return await db.execute_query(query=query, params=params, name="get_paths_between_nodes", type=QueryType.READ)
116
118
 
117
119
 
118
- async def count_relationships(db: InfrahubDatabase, label: Optional[str] = None) -> int:
120
+ async def count_relationships(db: InfrahubDatabase, label: str | None = None) -> int:
119
121
  """Return the total number of relationships in the database."""
120
122
 
121
123
  label_str = f":{label}" if label else ""
@@ -141,7 +143,7 @@ async def get_nodes(db: InfrahubDatabase, label: str) -> list[Neo4jNode]:
141
143
  return [result[0] for result in results]
142
144
 
143
145
 
144
- async def count_nodes(db: InfrahubDatabase, label: Optional[str] = None) -> int:
146
+ async def count_nodes(db: InfrahubDatabase, label: str | None = None) -> int:
145
147
  """Return the total number of nodes of a given label in the database."""
146
148
 
147
149
  label_str = f":{label}" if label else ""
@@ -183,7 +185,7 @@ def parse_node_kind(kind: str) -> NodeKind:
183
185
 
184
186
 
185
187
  def convert_ip_to_binary_str(
186
- obj: Union[ipaddress.IPv6Network, ipaddress.IPv4Network, ipaddress.IPv4Interface, ipaddress.IPv6Interface],
188
+ obj: ipaddress.IPv6Network | ipaddress.IPv4Network | ipaddress.IPv4Interface | ipaddress.IPv6Interface,
187
189
  ) -> str:
188
190
  if isinstance(obj, ipaddress.IPv6Network | ipaddress.IPv4Network):
189
191
  prefix_bin = f"{int(obj.network_address):b}"
@@ -230,26 +232,26 @@ class _NewClass:
230
232
  _all_vars = set(dir(_OldClass) + dir(_NewClass))
231
233
 
232
234
 
233
- def props(x) -> dict[str, Any]:
235
+ def props(x: Any) -> dict[str, Any]:
234
236
  return {key: vars(x).get(key, getattr(x, key)) for key in dir(x) if key not in _all_vars}
235
237
 
236
238
 
237
239
  class SubclassWithMeta_Meta(type):
238
240
  _meta = None
239
241
 
240
- def __str__(cls):
242
+ def __str__(cls) -> str:
241
243
  if cls._meta:
242
244
  return cls._meta.name
243
245
  return cls.__name__
244
246
 
245
- def __repr__(cls):
247
+ def __repr__(cls) -> str:
246
248
  return f"<{cls.__name__} meta={repr(cls._meta)}>"
247
249
 
248
250
 
249
251
  class SubclassWithMeta(metaclass=SubclassWithMeta_Meta):
250
252
  """This class improves __init_subclass__ to receive automatically the options from meta"""
251
253
 
252
- def __init_subclass__(cls, **meta_options):
254
+ def __init_subclass__(cls, **meta_options: dict[str, Any]) -> None:
253
255
  """This method just terminates the super() chain"""
254
256
  _Meta = getattr(cls, "Meta", None)
255
257
  _meta_props = {}
@@ -266,7 +268,7 @@ class SubclassWithMeta(metaclass=SubclassWithMeta_Meta):
266
268
  abstract = options.pop("abstract", False)
267
269
  if abstract:
268
270
  assert not options, (
269
- "Abstract types can only contain the abstract attribute. " f"Received: abstract, {', '.join(options)}"
271
+ f"Abstract types can only contain the abstract attribute. Received: abstract, {', '.join(options)}"
270
272
  )
271
273
  else:
272
274
  super_class = super(cls, cls)
@@ -274,5 +276,5 @@ class SubclassWithMeta(metaclass=SubclassWithMeta_Meta):
274
276
  super_class.__init_subclass_with_meta__(**options)
275
277
 
276
278
  @classmethod
277
- def __init_subclass_with_meta__(cls, **meta_options) -> None:
279
+ def __init_subclass_with_meta__(cls, **meta_options: dict[str, Any]) -> None:
278
280
  """This method just terminates the super() chain"""
@@ -1,5 +1,3 @@
1
- from typing import Optional
2
-
3
1
  from .attribute.choices import AttributeChoicesChecker
4
2
  from .attribute.enum import AttributeEnumChecker
5
3
  from .attribute.kind import AttributeKindChecker
@@ -18,7 +16,7 @@ from .relationship.optional import RelationshipOptionalChecker
18
16
  from .relationship.peer import RelationshipPeerChecker
19
17
  from .uniqueness.checker import UniquenessChecker
20
18
 
21
- CONSTRAINT_VALIDATOR_MAP: dict[str, Optional[type[ConstraintCheckerInterface]]] = {
19
+ CONSTRAINT_VALIDATOR_MAP: dict[str, type[ConstraintCheckerInterface] | None] = {
22
20
  "attribute.regex.update": AttributeRegexChecker,
23
21
  "attribute.enum.update": AttributeEnumChecker,
24
22
  "attribute.kind.update": AttributeKindChecker,
@@ -1,7 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from itertools import chain
4
- from typing import TYPE_CHECKING, Optional
4
+ from typing import TYPE_CHECKING
5
5
 
6
6
  from infrahub.core import registry
7
7
  from infrahub.exceptions import ValidationError
@@ -19,7 +19,7 @@ if TYPE_CHECKING:
19
19
 
20
20
  class AggregatedConstraintChecker:
21
21
  def __init__(
22
- self, constraints: list[ConstraintCheckerInterface], db: InfrahubDatabase, branch: Optional[Branch] = None
22
+ self, constraints: list[ConstraintCheckerInterface], db: InfrahubDatabase, branch: Branch | None = None
23
23
  ):
24
24
  self.constraints = constraints
25
25
  self.db = db
@@ -1,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import TYPE_CHECKING, Any, Optional
3
+ from typing import TYPE_CHECKING, Any
4
4
 
5
5
  from infrahub.core.constants import NULL_VALUE, PathType
6
6
  from infrahub.core.path import DataPath, GroupedDataPaths
@@ -75,7 +75,7 @@ class AttributeChoicesUpdateValidatorQuery(AttributeSchemaValidatorQuery):
75
75
  class AttributeChoicesChecker(ConstraintCheckerInterface):
76
76
  query_classes = [AttributeChoicesUpdateValidatorQuery]
77
77
 
78
- def __init__(self, db: InfrahubDatabase, branch: Optional[Branch] = None):
78
+ def __init__(self, db: InfrahubDatabase, branch: Branch | None = None):
79
79
  self.db = db
80
80
  self.branch = branch
81
81
 
@@ -1,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import TYPE_CHECKING, Any, Optional
3
+ from typing import TYPE_CHECKING, Any
4
4
 
5
5
  from infrahub.core.constants import NULL_VALUE, PathType
6
6
  from infrahub.core.path import DataPath, GroupedDataPaths
@@ -74,7 +74,7 @@ class AttributeEnumUpdateValidatorQuery(AttributeSchemaValidatorQuery):
74
74
  class AttributeEnumChecker(ConstraintCheckerInterface):
75
75
  query_classes = [AttributeEnumUpdateValidatorQuery]
76
76
 
77
- def __init__(self, db: InfrahubDatabase, branch: Optional[Branch] = None):
77
+ def __init__(self, db: InfrahubDatabase, branch: Branch | None = None):
78
78
  self.db = db
79
79
  self.branch = branch
80
80
 
@@ -1,7 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from dataclasses import dataclass
4
- from typing import TYPE_CHECKING, Any, Optional
4
+ from typing import TYPE_CHECKING, Any
5
5
 
6
6
  from infrahub.core.constants import NULL_VALUE, PathType
7
7
  from infrahub.core.path import DataPath, GroupedDataPaths
@@ -87,7 +87,7 @@ class AttributeKindUpdateValidatorQuery(AttributeSchemaValidatorQuery):
87
87
  class AttributeKindChecker(ConstraintCheckerInterface):
88
88
  query_classes = [AttributeKindUpdateValidatorQuery]
89
89
 
90
- def __init__(self, db: InfrahubDatabase, branch: Optional[Branch] = None):
90
+ def __init__(self, db: InfrahubDatabase, branch: Branch | None = None):
91
91
  self.db = db
92
92
  self.branch = branch
93
93
 
@@ -1,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import TYPE_CHECKING, Any, Optional
3
+ from typing import TYPE_CHECKING, Any
4
4
 
5
5
  from infrahub.core.constants import PathType
6
6
  from infrahub.core.path import DataPath, GroupedDataPaths
@@ -70,7 +70,7 @@ class AttributeLengthUpdateValidatorQuery(AttributeSchemaValidatorQuery):
70
70
  class AttributeLengthChecker(ConstraintCheckerInterface):
71
71
  query_classes = [AttributeLengthUpdateValidatorQuery]
72
72
 
73
- def __init__(self, db: InfrahubDatabase, branch: Optional[Branch] = None):
73
+ def __init__(self, db: InfrahubDatabase, branch: Branch | None = None):
74
74
  self.db = db
75
75
  self.branch = branch
76
76
 
@@ -1,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import TYPE_CHECKING, Any, Optional
3
+ from typing import TYPE_CHECKING, Any
4
4
 
5
5
  from infrahub.core.constants import NULL_VALUE, PathType
6
6
  from infrahub.core.path import DataPath, GroupedDataPaths
@@ -66,7 +66,7 @@ class AttributeOptionalUpdateValidatorQuery(AttributeSchemaValidatorQuery):
66
66
  class AttributeOptionalChecker(ConstraintCheckerInterface):
67
67
  query_classes = [AttributeOptionalUpdateValidatorQuery]
68
68
 
69
- def __init__(self, db: InfrahubDatabase, branch: Optional[Branch] = None):
69
+ def __init__(self, db: InfrahubDatabase, branch: Branch | None = None):
70
70
  self.db = db
71
71
  self.branch = branch
72
72
 
@@ -1,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import TYPE_CHECKING, Any, Optional
3
+ from typing import TYPE_CHECKING, Any
4
4
 
5
5
  from infrahub.core.constants import NULL_VALUE, PathType
6
6
  from infrahub.core.path import DataPath, GroupedDataPaths
@@ -70,7 +70,7 @@ class AttributeRegexUpdateValidatorQuery(AttributeSchemaValidatorQuery):
70
70
  class AttributeRegexChecker(ConstraintCheckerInterface):
71
71
  query_classes = [AttributeRegexUpdateValidatorQuery]
72
72
 
73
- def __init__(self, db: InfrahubDatabase, branch: Optional[Branch] = None):
73
+ def __init__(self, db: InfrahubDatabase, branch: Branch | None = None):
74
74
  self.db = db
75
75
  self.branch = branch
76
76
 
@@ -1,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import TYPE_CHECKING, Any, Optional
3
+ from typing import TYPE_CHECKING, Any
4
4
 
5
5
  from infrahub.core.constants import PathType
6
6
  from infrahub.core.path import DataPath, GroupedDataPaths
@@ -91,7 +91,7 @@ class AttributeUniqueUpdateValidatorQuery(AttributeSchemaValidatorQuery):
91
91
  class AttributeUniquenessChecker(ConstraintCheckerInterface):
92
92
  query_classes = [AttributeUniqueUpdateValidatorQuery]
93
93
 
94
- def __init__(self, db: InfrahubDatabase, branch: Optional[Branch] = None) -> None:
94
+ def __init__(self, db: InfrahubDatabase, branch: Branch | None = None) -> None:
95
95
  self.db = db
96
96
  self.branch = branch
97
97
 
@@ -1,14 +1,21 @@
1
1
  import asyncio
2
2
  from typing import Any, Coroutine
3
3
 
4
- from infrahub_sdk.node import InfrahubNode
4
+ from infrahub_sdk.protocols import CoreValidator
5
5
 
6
+ from infrahub.context import InfrahubContext
6
7
  from infrahub.core.constants import ValidatorConclusion, ValidatorState
7
8
  from infrahub.core.timestamp import Timestamp
9
+ from infrahub.services import InfrahubServices
10
+ from infrahub.validators.events import send_failed_validator, send_passed_validator
8
11
 
9
12
 
10
13
  async def run_checks_and_update_validator(
11
- checks: list[Coroutine[Any, None, ValidatorConclusion]], validator: InfrahubNode
14
+ checks: list[Coroutine[Any, None, ValidatorConclusion]],
15
+ validator: CoreValidator,
16
+ context: InfrahubContext,
17
+ service: InfrahubServices,
18
+ proposed_change_id: str,
12
19
  ) -> None:
13
20
  """
14
21
  Execute a list of checks coroutines, and set validator fields accordingly.
@@ -22,11 +29,17 @@ async def run_checks_and_update_validator(
22
29
  validator.completed_at.value = ""
23
30
  await validator.save()
24
31
 
32
+ failed_early = False
33
+
25
34
  for earliest_task in asyncio.as_completed(checks):
26
35
  result = await earliest_task
27
36
  if validator.conclusion.value != ValidatorConclusion.FAILURE.value and result == ValidatorConclusion.FAILURE:
28
37
  validator.conclusion.value = ValidatorConclusion.FAILURE.value
38
+ failed_early = True
29
39
  await validator.save()
40
+ await send_failed_validator(
41
+ service=service, validator=validator, proposed_change_id=proposed_change_id, context=context
42
+ )
30
43
  # Continue to iterate to wait for the end of all checks
31
44
 
32
45
  validator.state.value = ValidatorState.COMPLETED.value
@@ -35,3 +48,13 @@ async def run_checks_and_update_validator(
35
48
  validator.conclusion.value = ValidatorConclusion.SUCCESS.value
36
49
 
37
50
  await validator.save()
51
+
52
+ if not failed_early:
53
+ if validator.conclusion.value == ValidatorConclusion.SUCCESS.value:
54
+ await send_passed_validator(
55
+ service=service, validator=validator, proposed_change_id=proposed_change_id, context=context
56
+ )
57
+ else:
58
+ await send_failed_validator(
59
+ service=service, validator=validator, proposed_change_id=proposed_change_id, context=context
60
+ )
@@ -1,5 +1,3 @@
1
- from typing import Union
2
-
3
1
  from infrahub.core.constants import RelationshipKind, SchemaPathType
4
2
  from infrahub.core.constants.schema import UpdateSupport
5
3
  from infrahub.core.diff.model.path import NodeDiffFieldSummary
@@ -132,7 +130,7 @@ class ConstraintValidatorDeterminer:
132
130
  return constraints
133
131
 
134
132
  async def _get_constraints_for_one_field(
135
- self, schema: MainSchemaTypes, field: Union[AttributeSchema, RelationshipSchema]
133
+ self, schema: MainSchemaTypes, field: AttributeSchema | RelationshipSchema
136
134
  ) -> list[SchemaUpdateConstraintInfo]:
137
135
  constraints: list[SchemaUpdateConstraintInfo] = []
138
136
  for prop_name, prop_field_info in field.model_fields.items():
@@ -1,8 +1,12 @@
1
+ from __future__ import annotations
2
+
1
3
  from abc import ABC, abstractmethod
4
+ from typing import TYPE_CHECKING
2
5
 
3
- from infrahub.core.path import GroupedDataPaths
6
+ if TYPE_CHECKING:
7
+ from infrahub.core.path import GroupedDataPaths
4
8
 
5
- from .model import SchemaConstraintValidatorRequest
9
+ from .model import SchemaConstraintValidatorRequest
6
10
 
7
11
 
8
12
  class ConstraintCheckerInterface(ABC):
@@ -1,17 +1,36 @@
1
- from typing import Union
1
+ from typing import Any
2
2
 
3
- from pydantic import BaseModel, Field
3
+ from pydantic import BaseModel, ConfigDict, Field, field_validator, model_serializer
4
4
 
5
5
  from infrahub.core.branch import Branch
6
6
  from infrahub.core.path import SchemaPath
7
7
  from infrahub.core.schema import GenericSchema, NodeSchema
8
+ from infrahub.core.schema.schema_branch import SchemaBranch
8
9
 
9
10
 
10
11
  class SchemaConstraintValidatorRequest(BaseModel):
12
+ model_config = ConfigDict(arbitrary_types_allowed=True)
13
+
11
14
  branch: Branch = Field(..., description="The name of the branch to target")
12
15
  constraint_name: str = Field(..., description="The name of the constraint to validate")
13
- node_schema: Union[NodeSchema, GenericSchema] = Field(..., description="Schema of Node or Generic to validate")
16
+ node_schema: NodeSchema | GenericSchema = Field(..., description="Schema of Node or Generic to validate")
14
17
  schema_path: SchemaPath = Field(..., description="SchemaPath to the element of the schema to validate")
18
+ schema_branch: SchemaBranch = Field(..., description="SchemaBranch of the element to validate")
19
+
20
+ @model_serializer()
21
+ def serialize_model(self) -> dict[str, Any]:
22
+ return {
23
+ "branch": self.branch.model_dump(),
24
+ "constraint_name": self.constraint_name,
25
+ "node_schema": self.node_schema.model_dump(),
26
+ "schema_path": self.schema_path.model_dump(),
27
+ "schema_branch": self.schema_branch.to_dict_schema_object(),
28
+ }
29
+
30
+ @field_validator("schema_branch", mode="before")
31
+ @classmethod
32
+ def validate_schema_branch(cls, value: Any) -> SchemaBranch:
33
+ return SchemaBranch.validate(data=value)
15
34
 
16
35
 
17
36
  class SchemaViolation(BaseModel):
@@ -1,4 +1,6 @@
1
- from pydantic import BaseModel, ConfigDict, Field
1
+ from typing import Any
2
+
3
+ from pydantic import BaseModel, ConfigDict, Field, field_validator, model_serializer
2
4
 
3
5
  from infrahub.core.branch import Branch
4
6
  from infrahub.core.models import SchemaUpdateConstraintInfo
@@ -9,13 +11,24 @@ from infrahub.message_bus import InfrahubResponseData
9
11
 
10
12
 
11
13
  class SchemaValidateMigrationData(BaseModel):
12
- model_config = ConfigDict(
13
- arbitrary_types_allowed=True, json_encoders={SchemaBranch: SchemaBranch.to_dict_schema_object}
14
- )
14
+ model_config = ConfigDict(arbitrary_types_allowed=True)
15
15
  branch: Branch
16
16
  schema_branch: SchemaBranch
17
17
  constraints: list[SchemaUpdateConstraintInfo]
18
18
 
19
+ @model_serializer()
20
+ def serialize_model(self) -> dict[str, Any]:
21
+ return {
22
+ "branch": self.branch.model_dump(),
23
+ "schema_branch": self.schema_branch.to_dict_schema_object(),
24
+ "constraints": [constraint.model_dump() for constraint in self.constraints],
25
+ }
26
+
27
+ @field_validator("schema_branch", mode="before")
28
+ @classmethod
29
+ def validate_schema_branch(cls, value: Any) -> SchemaBranch:
30
+ return SchemaBranch.validate(data=value)
31
+
19
32
 
20
33
  class SchemaValidatorPathResponseData(InfrahubResponseData):
21
34
  violations: list[SchemaViolation] = Field(default_factory=list)
@@ -1,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import TYPE_CHECKING, Optional
3
+ from typing import TYPE_CHECKING
4
4
 
5
5
  from ..interface import ConstraintCheckerInterface
6
6
  from ..query import NodeNotPresentValidatorQuery
@@ -16,7 +16,7 @@ if TYPE_CHECKING:
16
16
  class NodeAttributeAddChecker(ConstraintCheckerInterface):
17
17
  query_classes = [NodeNotPresentValidatorQuery]
18
18
 
19
- def __init__(self, db: InfrahubDatabase, branch: Optional[Branch] = None):
19
+ def __init__(self, db: InfrahubDatabase, branch: Branch | None = None):
20
20
  self.db = db
21
21
  self.branch = branch
22
22