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
@@ -5,7 +5,7 @@ import keyword
5
5
  import os
6
6
  from dataclasses import asdict, dataclass
7
7
  from enum import Enum
8
- from typing import TYPE_CHECKING, Any, Callable, Iterable, Literal, Optional, Union, overload
8
+ from typing import TYPE_CHECKING, Any, Callable, Iterable, Literal, overload
9
9
 
10
10
  from infrahub_sdk.utils import compare_lists, intersection
11
11
  from pydantic import field_validator
@@ -25,6 +25,7 @@ if TYPE_CHECKING:
25
25
 
26
26
 
27
27
  NODE_METADATA_ATTRIBUTES = ["_source", "_owner"]
28
+ INHERITED = "INHERITED"
28
29
 
29
30
 
30
31
  class BaseNodeSchema(GeneratedBaseNodeSchema):
@@ -120,7 +121,7 @@ class BaseNodeSchema(GeneratedBaseNodeSchema):
120
121
  other: Self,
121
122
  get_func: Callable,
122
123
  get_map_func: Callable,
123
- obj_type: type[Union[AttributeSchema, RelationshipSchema]],
124
+ obj_type: type[AttributeSchema | RelationshipSchema],
124
125
  ) -> HashableModelDiff:
125
126
  """The goal of this function is to reduce the amount of code duplicated between Attribute and Relationship to calculate a diff
126
127
  The logic is the same for both, except that the functions we are using to access these objects are differents
@@ -138,8 +139,8 @@ class BaseNodeSchema(GeneratedBaseNodeSchema):
138
139
  reversed_map_other = dict(map(reversed, other_map.items()))
139
140
 
140
141
  # Identify which elements are using the same id on both sides
141
- clean_local_ids = [id for id in local_map.values() if id is not None]
142
- clean_other_ids = [id for id in other_map.values() if id is not None]
142
+ clean_local_ids = [id for id in local_map.values() if id is not None and id != INHERITED]
143
+ clean_other_ids = [id for id in other_map.values() if id is not None and id != INHERITED]
143
144
  shared_ids = intersection(list1=clean_local_ids, list2=clean_other_ids)
144
145
 
145
146
  # Identify which elements are present on both side based on the name
@@ -176,16 +177,14 @@ class BaseNodeSchema(GeneratedBaseNodeSchema):
176
177
  return elements_diff
177
178
 
178
179
  @overload
179
- def get_field(
180
- self, name: str, raise_on_error: Literal[True] = True
181
- ) -> Union[AttributeSchema, RelationshipSchema]: ...
180
+ def get_field(self, name: str, raise_on_error: Literal[True] = True) -> AttributeSchema | RelationshipSchema: ...
182
181
 
183
182
  @overload
184
183
  def get_field(
185
184
  self, name: str, raise_on_error: Literal[False] = False
186
- ) -> Optional[Union[AttributeSchema, RelationshipSchema]]: ...
185
+ ) -> AttributeSchema | RelationshipSchema | None: ...
187
186
 
188
- def get_field(self, name: str, raise_on_error: bool = True) -> Optional[Union[AttributeSchema, RelationshipSchema]]:
187
+ def get_field(self, name: str, raise_on_error: bool = True) -> AttributeSchema | RelationshipSchema | None:
189
188
  if field := self.get_attribute_or_none(name=name):
190
189
  return field
191
190
 
@@ -204,7 +203,7 @@ class BaseNodeSchema(GeneratedBaseNodeSchema):
204
203
 
205
204
  raise ValueError(f"Unable to find the attribute {name}")
206
205
 
207
- def get_attribute_or_none(self, name: str) -> Optional[AttributeSchema]:
206
+ def get_attribute_or_none(self, name: str) -> AttributeSchema | None:
208
207
  for item in self.attributes:
209
208
  if item.name == name:
210
209
  return item
@@ -230,7 +229,7 @@ class BaseNodeSchema(GeneratedBaseNodeSchema):
230
229
 
231
230
  raise ValueError(f"Unable to find the relationship with the ID: {id}")
232
231
 
233
- def get_relationship_or_none(self, name: str) -> Optional[RelationshipSchema]:
232
+ def get_relationship_or_none(self, name: str) -> RelationshipSchema | None:
234
233
  for item in self.relationships:
235
234
  if item.name == name:
236
235
  return item
@@ -242,9 +241,9 @@ class BaseNodeSchema(GeneratedBaseNodeSchema):
242
241
  @overload
243
242
  def get_relationship_by_identifier(
244
243
  self, id: str, raise_on_error: Literal[False] = False
245
- ) -> Optional[RelationshipSchema]: ...
244
+ ) -> RelationshipSchema | None: ...
246
245
 
247
- def get_relationship_by_identifier(self, id: str, raise_on_error: bool = True) -> Optional[RelationshipSchema]:
246
+ def get_relationship_by_identifier(self, id: str, raise_on_error: bool = True) -> RelationshipSchema | None:
248
247
  for item in self.relationships:
249
248
  if item.identifier == id:
250
249
  return item
@@ -269,13 +268,13 @@ class BaseNodeSchema(GeneratedBaseNodeSchema):
269
268
  def get_attributes_name_id_map(self) -> dict[str, str]:
270
269
  name_id_map = {}
271
270
  for attr in self.attributes:
272
- name_id_map[attr.name] = attr.id
271
+ name_id_map[attr.name] = INHERITED if attr.inherited else attr.id
273
272
  return name_id_map
274
273
 
275
274
  def get_relationship_name_id_map(self) -> dict[str, str]:
276
275
  name_id_map = {}
277
276
  for rel in self.relationships:
278
- name_id_map[rel.name] = rel.id
277
+ name_id_map[rel.name] = INHERITED if rel.inherited else rel.id
279
278
  return name_id_map
280
279
 
281
280
  @property
@@ -343,7 +342,7 @@ class BaseNodeSchema(GeneratedBaseNodeSchema):
343
342
  fields[subpaths[0]] = cls.convert_path_to_graphql_fields(path=subpaths[1])
344
343
  return fields
345
344
 
346
- def generate_fields_for_display_label(self) -> Optional[dict]:
345
+ def generate_fields_for_display_label(self) -> dict | None:
347
346
  """Generate a dictionary containing the list of fields that are required
348
347
  to generate the display_label.
349
348
 
@@ -353,12 +352,12 @@ class BaseNodeSchema(GeneratedBaseNodeSchema):
353
352
  if not self.display_labels:
354
353
  return None
355
354
 
356
- fields: dict[str, Union[str, None, dict[str, None]]] = {}
355
+ fields: dict[str, str | None | dict[str, None]] = {}
357
356
  for item in self.display_labels:
358
357
  fields.update(self.convert_path_to_graphql_fields(path=item))
359
358
  return fields
360
359
 
361
- def generate_fields_for_hfid(self) -> Optional[dict]:
360
+ def generate_fields_for_hfid(self) -> dict | None:
362
361
  """Generate a dictionary containing the list of fields that are required
363
362
  to generate the hfid.
364
363
 
@@ -368,7 +367,7 @@ class BaseNodeSchema(GeneratedBaseNodeSchema):
368
367
  if not self.human_friendly_id:
369
368
  return None
370
369
 
371
- fields: dict[str, Union[str, None, dict[str, None]]] = {}
370
+ fields: dict[str, str | None | dict[str, None]] = {}
372
371
  for item in self.human_friendly_id:
373
372
  fields.update(self.convert_path_to_graphql_fields(path=item))
374
373
  return fields
@@ -381,11 +380,11 @@ class BaseNodeSchema(GeneratedBaseNodeSchema):
381
380
 
382
381
  return value
383
382
 
384
- def parse_schema_path(self, path: str, schema: Optional[SchemaBranch] = None) -> SchemaAttributePath:
383
+ def parse_schema_path(self, path: str, schema: SchemaBranch | None = None) -> SchemaAttributePath:
385
384
  schema_path = SchemaAttributePath()
386
- relationship_piece: Optional[str] = None
387
- attribute_piece: Optional[str] = None
388
- property_piece: Optional[str] = None
385
+ relationship_piece: str | None = None
386
+ attribute_piece: str | None = None
387
+ property_piece: str | None = None
389
388
 
390
389
  path_parts = path.split("__")
391
390
  if path_parts[0] in self.relationship_names:
@@ -434,33 +433,79 @@ class BaseNodeSchema(GeneratedBaseNodeSchema):
434
433
  def get_unique_constraint_schema_attribute_paths(
435
434
  self,
436
435
  schema_branch: SchemaBranch,
437
- include_unique_attributes: bool = False,
438
- ) -> list[list[SchemaAttributePath]]:
439
- constraint_paths_groups = []
440
- if include_unique_attributes:
441
- for attribute_schema in self.unique_attributes:
442
- constraint_paths_groups.append(
443
- [SchemaAttributePath(attribute_schema=attribute_schema, attribute_property_name="value")]
444
- )
445
-
446
- if not self.uniqueness_constraints:
447
- return constraint_paths_groups
436
+ ) -> list[SchemaUniquenessConstraintPath]:
437
+ if self.uniqueness_constraints is None:
438
+ return []
439
+
440
+ uniqueness_constraint_paths = []
448
441
 
449
442
  for uniqueness_path_group in self.uniqueness_constraints:
450
- constraint_paths_group = []
451
- for uniqueness_path_part in uniqueness_path_group:
452
- constraint_paths_group.append(self.parse_schema_path(path=uniqueness_path_part, schema=schema_branch))
453
- if constraint_paths_group not in constraint_paths_groups:
454
- constraint_paths_groups.append(constraint_paths_group)
455
- return constraint_paths_groups
443
+ attributes_paths = [
444
+ self.parse_schema_path(path=uniqueness_path_part, schema=schema_branch)
445
+ for uniqueness_path_part in uniqueness_path_group
446
+ ]
447
+ uniqueness_constraint_type = self.get_uniqueness_constraint_type(
448
+ uniqueness_constraint=set(uniqueness_path_group), schema_branch=schema_branch
449
+ )
450
+ uniqueness_constraint_path = SchemaUniquenessConstraintPath(
451
+ attributes_paths=attributes_paths, typ=uniqueness_constraint_type
452
+ )
453
+ uniqueness_constraint_paths.append(uniqueness_constraint_path)
454
+
455
+ return uniqueness_constraint_paths
456
+
457
+ def convert_hfid_to_uniqueness_constraint(self, schema_branch: SchemaBranch) -> list[str] | None:
458
+ if self.human_friendly_id is None:
459
+ return None
460
+
461
+ uniqueness_constraint = []
462
+ for item in self.human_friendly_id:
463
+ schema_attribute_path = self.parse_schema_path(path=item, schema=schema_branch)
464
+ if schema_attribute_path.is_type_attribute:
465
+ uniqueness_constraint.append(item)
466
+ elif schema_attribute_path.is_type_relationship:
467
+ uniqueness_constraint.append(schema_attribute_path.relationship_schema.name)
468
+ return uniqueness_constraint
469
+
470
+ def get_uniqueness_constraint_type(
471
+ self, uniqueness_constraint: set[str], schema_branch: SchemaBranch
472
+ ) -> UniquenessConstraintType:
473
+ hfid = self.convert_hfid_to_uniqueness_constraint(schema_branch=schema_branch)
474
+ if hfid is None:
475
+ return UniquenessConstraintType.STANDARD
476
+ hfid_set = set(hfid)
477
+ if uniqueness_constraint == hfid_set:
478
+ return UniquenessConstraintType.HFID
479
+ if uniqueness_constraint <= hfid_set:
480
+ return UniquenessConstraintType.SUBSET_OF_HFID
481
+ return UniquenessConstraintType.STANDARD
482
+
483
+
484
+ @dataclass
485
+ class SchemaUniquenessConstraintPath:
486
+ attributes_paths: list[SchemaAttributePath]
487
+ typ: UniquenessConstraintType
488
+
489
+
490
+ class UniquenessConstraintType(Enum):
491
+ HFID = "HFID"
492
+ SUBSET_OF_HFID = "SUBSET_OF_HFID"
493
+ STANDARD = "STANDARD"
494
+
495
+
496
+ @dataclass
497
+ class UniquenessConstraintViolation:
498
+ nodes_ids: set[str]
499
+ fields: list[str]
500
+ typ: UniquenessConstraintType
456
501
 
457
502
 
458
503
  @dataclass
459
504
  class SchemaAttributePath:
460
- relationship_schema: Optional[RelationshipSchema] = None
461
- related_schema: Optional[Union[NodeSchema, GenericSchema]] = None
462
- attribute_schema: Optional[AttributeSchema] = None
463
- attribute_property_name: Optional[str] = None
505
+ relationship_schema: RelationshipSchema | None = None
506
+ related_schema: NodeSchema | GenericSchema | None = None
507
+ attribute_schema: AttributeSchema | None = None
508
+ attribute_property_name: str | None = None
464
509
 
465
510
  @property
466
511
  def is_type_attribute(self) -> bool:
@@ -492,6 +537,10 @@ class SchemaAttributePath:
492
537
  return self.attribute_property_name
493
538
  raise AttributePathParsingError("An attribute_property_name was expected but not found")
494
539
 
540
+ @property
541
+ def attribute_path_as_str(self) -> str:
542
+ return self.active_attribute_schema.name + "__" + self.active_attribute_property_name
543
+
495
544
 
496
545
  @dataclass
497
546
  class SchemaAttributePathValue(SchemaAttributePath):
@@ -1,4 +1,4 @@
1
- from typing import Any, Optional
1
+ from typing import Any
2
2
 
3
3
  from pydantic import ConfigDict, Field, model_serializer
4
4
 
@@ -8,10 +8,10 @@ from infrahub.core.models import HashableModel
8
8
 
9
9
  class ComputedAttribute(HashableModel):
10
10
  kind: ComputedAttributeKind
11
- jinja2_template: Optional[str] = Field(
11
+ jinja2_template: str | None = Field(
12
12
  default=None, description="The Jinja2 template in string format, required when assignment_type=jinja2"
13
13
  )
14
- transform: Optional[str] = Field(
14
+ transform: str | None = Field(
15
15
  default=None, description="The Python Transform name or ID, required when assignment_type=transform"
16
16
  )
17
17
 
@@ -1,5 +1,7 @@
1
1
  from typing import Any
2
2
 
3
+ from ...generic_schema import GenericSchema
4
+ from ...node_schema import NodeSchema
3
5
  from .account import (
4
6
  core_account,
5
7
  core_account_token,
@@ -11,6 +13,7 @@ from .account import (
11
13
  from .artifact import core_artifact, core_artifact_definition, core_artifact_target
12
14
  from .builtin import builtin_tag
13
15
  from .check import core_check_definition
16
+ from .core import core_node, core_task_target
14
17
  from .generator import core_generator_definition, core_generator_instance
15
18
  from .graphql_query import core_graphql_query
16
19
  from .group import core_generator_group, core_graphql_query_group, core_group, core_standard_group
@@ -54,27 +57,11 @@ from .propose_change_validator import (
54
57
  )
55
58
  from .repository import core_generic_repository, core_read_only_repository, core_repository
56
59
  from .resource_pool import core_ip_address_pool, core_ip_prefix_pool, core_number_pool, core_resource_pool
57
- from .template import core_object_template
60
+ from .template import core_object_component_template, core_object_template
58
61
  from .transform import core_transform, core_transform_jinja2, core_transform_python
59
62
  from .webhook import core_custom_webhook, core_standard_webhook, core_webhook
60
63
 
61
- core_node = {
62
- "name": "Node",
63
- "namespace": "Core",
64
- "include_in_menu": False,
65
- "description": "Base Node in Infrahub.",
66
- "label": "Node",
67
- }
68
-
69
- core_task_target = {
70
- "name": "TaskTarget",
71
- "include_in_menu": False,
72
- "namespace": "Core",
73
- "description": "Extend a node to be associated with tasks",
74
- "label": "Task Target",
75
- }
76
-
77
- core_models: dict[str, Any] = {
64
+ core_models_mixed: dict[str, list] = {
78
65
  "generics": [
79
66
  core_node,
80
67
  lineage_owner,
@@ -98,6 +85,7 @@ core_models: dict[str, Any] = {
98
85
  core_base_permission,
99
86
  core_credential,
100
87
  core_object_template,
88
+ core_object_component_template,
101
89
  generic_menu_item,
102
90
  ],
103
91
  "nodes": [
@@ -117,7 +105,7 @@ core_models: dict[str, Any] = {
117
105
  core_object_thread,
118
106
  core_change_comment,
119
107
  core_thread_comment,
120
- core_repository.to_dict(),
108
+ core_repository,
121
109
  core_read_only_repository,
122
110
  core_transform_jinja2,
123
111
  core_data_check,
@@ -151,3 +139,9 @@ core_models: dict[str, Any] = {
151
139
  core_account_group,
152
140
  ],
153
141
  }
142
+
143
+
144
+ core_models: dict[str, Any] = {
145
+ "generics": [item.to_dict() if isinstance(item, GenericSchema) else item for item in core_models_mixed["generics"]],
146
+ "nodes": [item.to_dict() if isinstance(item, NodeSchema) else item for item in core_models_mixed["nodes"]],
147
+ }