infrahub-server 1.3.0a0__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 (68) hide show
  1. infrahub/core/attribute.py +3 -3
  2. infrahub/core/constants/__init__.py +5 -0
  3. infrahub/core/constants/infrahubkind.py +2 -0
  4. infrahub/core/migrations/query/attribute_rename.py +2 -4
  5. infrahub/core/migrations/query/delete_element_in_schema.py +16 -11
  6. infrahub/core/migrations/query/node_duplicate.py +16 -15
  7. infrahub/core/migrations/query/relationship_duplicate.py +16 -11
  8. infrahub/core/migrations/schema/node_attribute_remove.py +1 -2
  9. infrahub/core/migrations/schema/node_remove.py +16 -13
  10. infrahub/core/node/__init__.py +72 -14
  11. infrahub/core/node/resource_manager/ip_address_pool.py +6 -2
  12. infrahub/core/node/resource_manager/ip_prefix_pool.py +6 -2
  13. infrahub/core/node/resource_manager/number_pool.py +31 -5
  14. infrahub/core/node/standard.py +6 -1
  15. infrahub/core/protocols.py +9 -0
  16. infrahub/core/query/relationship.py +2 -4
  17. infrahub/core/schema/attribute_parameters.py +129 -5
  18. infrahub/core/schema/attribute_schema.py +38 -10
  19. infrahub/core/schema/definitions/core/__init__.py +16 -2
  20. infrahub/core/schema/definitions/core/group.py +45 -0
  21. infrahub/core/schema/definitions/core/resource_pool.py +20 -0
  22. infrahub/core/schema/definitions/internal.py +16 -3
  23. infrahub/core/schema/generated/attribute_schema.py +12 -5
  24. infrahub/core/schema/manager.py +3 -0
  25. infrahub/core/schema/schema_branch.py +55 -0
  26. infrahub/core/validators/__init__.py +8 -0
  27. infrahub/core/validators/attribute/choices.py +0 -1
  28. infrahub/core/validators/attribute/enum.py +0 -1
  29. infrahub/core/validators/attribute/kind.py +0 -1
  30. infrahub/core/validators/attribute/length.py +0 -1
  31. infrahub/core/validators/attribute/min_max.py +118 -0
  32. infrahub/core/validators/attribute/number_pool.py +106 -0
  33. infrahub/core/validators/attribute/optional.py +0 -2
  34. infrahub/core/validators/attribute/regex.py +0 -1
  35. infrahub/core/validators/enum.py +5 -0
  36. infrahub/database/__init__.py +15 -3
  37. infrahub/git/base.py +5 -3
  38. infrahub/git/integrator.py +102 -3
  39. infrahub/graphql/mutations/resource_manager.py +62 -6
  40. infrahub/graphql/queries/resource_manager.py +7 -1
  41. infrahub/graphql/queries/task.py +10 -0
  42. infrahub/graphql/types/task_log.py +3 -2
  43. infrahub/menu/menu.py +3 -3
  44. infrahub/pools/number.py +5 -3
  45. infrahub/task_manager/task.py +44 -4
  46. infrahub/types.py +6 -0
  47. infrahub_sdk/client.py +43 -10
  48. infrahub_sdk/node/__init__.py +39 -0
  49. infrahub_sdk/node/attribute.py +122 -0
  50. infrahub_sdk/node/constants.py +21 -0
  51. infrahub_sdk/{node.py → node/node.py} +50 -749
  52. infrahub_sdk/node/parsers.py +15 -0
  53. infrahub_sdk/node/property.py +24 -0
  54. infrahub_sdk/node/related_node.py +266 -0
  55. infrahub_sdk/node/relationship.py +302 -0
  56. infrahub_sdk/protocols.py +112 -0
  57. infrahub_sdk/protocols_base.py +34 -2
  58. infrahub_sdk/query_groups.py +13 -2
  59. infrahub_sdk/schema/main.py +1 -0
  60. infrahub_sdk/schema/repository.py +16 -0
  61. infrahub_sdk/spec/object.py +1 -1
  62. infrahub_sdk/store.py +1 -1
  63. infrahub_sdk/testing/schemas/car_person.py +1 -0
  64. {infrahub_server-1.3.0a0.dist-info → infrahub_server-1.3.0b1.dist-info}/METADATA +3 -3
  65. {infrahub_server-1.3.0a0.dist-info → infrahub_server-1.3.0b1.dist-info}/RECORD +68 -59
  66. {infrahub_server-1.3.0a0.dist-info → infrahub_server-1.3.0b1.dist-info}/WHEEL +1 -1
  67. {infrahub_server-1.3.0a0.dist-info → infrahub_server-1.3.0b1.dist-info}/LICENSE.txt +0 -0
  68. {infrahub_server-1.3.0a0.dist-info → infrahub_server-1.3.0b1.dist-info}/entry_points.txt +0 -0
@@ -2,22 +2,44 @@ from __future__ import annotations
2
2
 
3
3
  from typing import TYPE_CHECKING
4
4
 
5
+ from infrahub.core import registry
5
6
  from infrahub.core.query.resource_manager import NumberPoolGetReserved, NumberPoolGetUsed, NumberPoolSetReserved
7
+ from infrahub.core.schema.attribute_parameters import NumberAttributeParameters
6
8
  from infrahub.exceptions import PoolExhaustedError
7
9
 
8
10
  from .. import Node
9
11
 
10
12
  if TYPE_CHECKING:
13
+ from infrahub.core.attribute import BaseAttribute
11
14
  from infrahub.core.branch import Branch
12
15
  from infrahub.database import InfrahubDatabase
13
16
 
14
17
 
15
18
  class CoreNumberPool(Node):
19
+ def get_attribute_nb_excluded_values(self) -> int:
20
+ """
21
+ Returns the number of excluded values for the attribute of the number pool.
22
+ """
23
+
24
+ pool_node = registry.schema.get(name=self.node.value) # type: ignore [attr-defined]
25
+ attribute = [attribute for attribute in pool_node.attributes if attribute.name == self.node_attribute.value][0] # type: ignore [attr-defined]
26
+ if not isinstance(attribute.parameters, NumberAttributeParameters):
27
+ return 0
28
+
29
+ sum_excluded_values = 0
30
+ excluded_ranges = attribute.parameters.get_excluded_ranges()
31
+ for start_range, end_range in excluded_ranges:
32
+ sum_excluded_values += end_range - start_range + 1
33
+
34
+ res = len(attribute.parameters.get_excluded_single_values()) + sum_excluded_values
35
+ return res
36
+
16
37
  async def get_resource(
17
38
  self,
18
39
  db: InfrahubDatabase,
19
40
  branch: Branch,
20
41
  node: Node,
42
+ attribute: BaseAttribute,
21
43
  identifier: str | None = None,
22
44
  ) -> int:
23
45
  identifier = identifier or node.get_id()
@@ -31,23 +53,24 @@ class CoreNumberPool(Node):
31
53
  return reservation
32
54
 
33
55
  # If we have not returned a value we need to find one if avaiable
34
- number = await self.get_next(db=db, branch=branch)
56
+ number = await self.get_next(db=db, branch=branch, attribute=attribute)
35
57
 
36
58
  query_set = await NumberPoolSetReserved.init(
37
59
  db=db, pool_id=self.get_id(), identifier=identifier, reserved=number
38
60
  )
39
61
  await query_set.execute(db=db)
40
-
41
62
  return number
42
63
 
43
- async def get_next(self, db: InfrahubDatabase, branch: Branch) -> int:
64
+ async def get_next(self, db: InfrahubDatabase, branch: Branch, attribute: BaseAttribute) -> int:
44
65
  query = await NumberPoolGetUsed.init(db=db, branch=branch, pool=self, branch_agnostic=True)
45
66
  await query.execute(db=db)
46
67
  taken = [result.get_as_optional_type("av.value", return_type=int) for result in query.results]
68
+ parameters = attribute.schema.parameters
47
69
  next_number = find_next_free(
48
70
  start=self.start_range.value, # type: ignore[attr-defined]
49
71
  end=self.end_range.value, # type: ignore[attr-defined]
50
72
  taken=taken,
73
+ parameters=parameters if isinstance(parameters, NumberAttributeParameters) else None,
51
74
  )
52
75
  if next_number is None:
53
76
  raise PoolExhaustedError("There are no more values available in this pool.")
@@ -55,12 +78,15 @@ class CoreNumberPool(Node):
55
78
  return next_number
56
79
 
57
80
 
58
- def find_next_free(start: int, end: int, taken: list[int | None]) -> int | None:
81
+ def find_next_free(
82
+ start: int, end: int, taken: list[int | None], parameters: NumberAttributeParameters | None
83
+ ) -> int | None:
59
84
  used_numbers = [number for number in taken if number is not None]
60
85
  used_set = set(used_numbers)
61
86
 
62
87
  for num in range(start, end + 1):
63
88
  if num not in used_set:
64
- return num
89
+ if parameters is None or parameters.is_valid_value(num):
90
+ return num
65
91
 
66
92
  return None
@@ -210,7 +210,12 @@ class StandardNode(BaseModel):
210
210
 
211
211
  @classmethod
212
212
  async def get_list(
213
- cls, db: InfrahubDatabase, limit: int = 1000, ids: list[str] | None = None, name: str | None = None, **kwargs
213
+ cls,
214
+ db: InfrahubDatabase,
215
+ limit: int = 1000,
216
+ ids: list[str] | None = None,
217
+ name: str | None = None,
218
+ **kwargs: dict[str, Any],
214
219
  ) -> list[Self]:
215
220
  query: Query = await StandardNodeGetListQuery.init(
216
221
  db=db, node_class=cls, ids=ids, node_name=name, limit=limit, **kwargs
@@ -227,6 +227,10 @@ class CoreWebhook(CoreNode):
227
227
  validate_certificates: BooleanOptional
228
228
 
229
229
 
230
+ class CoreWeightedPoolResource(CoreNode):
231
+ allocation_weight: IntegerOptional
232
+
233
+
230
234
  class LineageOwner(CoreNode):
231
235
  pass
232
236
 
@@ -493,6 +497,11 @@ class CoreRepository(LineageOwner, LineageSource, CoreGenericRepository, CoreTas
493
497
  commit: StringOptional
494
498
 
495
499
 
500
+ class CoreRepositoryGroup(CoreGroup):
501
+ content: Dropdown
502
+ repository: RelationshipManager
503
+
504
+
496
505
  class CoreRepositoryValidator(CoreValidator):
497
506
  repository: RelationshipManager
498
507
 
@@ -217,8 +217,7 @@ class RelationshipQuery(Query):
217
217
  )
218
218
  source_query_match = """
219
219
  MATCH (s:Node { uuid: $source_id })
220
- CALL {
221
- WITH s
220
+ CALL (s) {
222
221
  MATCH (s)-[r:IS_PART_OF]->(:Root)
223
222
  WHERE %(source_filter)s
224
223
  RETURN r.status = "active" AS s_is_active
@@ -246,8 +245,7 @@ class RelationshipQuery(Query):
246
245
  )
247
246
  destination_query_match = """
248
247
  MATCH (d:Node { uuid: $destination_id })
249
- CALL {
250
- WITH d
248
+ CALL (d) {
251
249
  MATCH (d)-[r:IS_PART_OF]->(:Root)
252
250
  WHERE %(destination_filter)s
253
251
  RETURN r.status = "active" AS d_is_active
@@ -1,21 +1,27 @@
1
1
  from __future__ import annotations
2
2
 
3
- from pydantic import Field
3
+ import sys
4
+ from typing import Self
4
5
 
6
+ from pydantic import ConfigDict, Field, model_validator
7
+
8
+ from infrahub import config
5
9
  from infrahub.core.constants.schema import UpdateSupport
6
10
  from infrahub.core.models import HashableModel
7
11
 
8
12
 
9
13
  def get_attribute_parameters_class_for_kind(kind: str) -> type[AttributeParameters]:
10
- return {
14
+ param_classes: dict[str, type[AttributeParameters]] = {
15
+ "NumberPool": NumberPoolParameters,
11
16
  "Text": TextAttributeParameters,
12
17
  "TextArea": TextAttributeParameters,
13
- }.get(kind, AttributeParameters)
18
+ "Number": NumberAttributeParameters,
19
+ }
20
+ return param_classes.get(kind, AttributeParameters)
14
21
 
15
22
 
16
23
  class AttributeParameters(HashableModel):
17
- class Config:
18
- extra = "forbid"
24
+ model_config = ConfigDict(extra="forbid")
19
25
 
20
26
 
21
27
  class TextAttributeParameters(AttributeParameters):
@@ -34,3 +40,121 @@ class TextAttributeParameters(AttributeParameters):
34
40
  description="Set a maximum number of characters allowed.",
35
41
  json_schema_extra={"update": UpdateSupport.VALIDATE_CONSTRAINT.value},
36
42
  )
43
+
44
+ @model_validator(mode="after")
45
+ def validate_min_max(self) -> Self:
46
+ if (
47
+ config.SETTINGS.initialized
48
+ and config.SETTINGS.main.schema_strict_mode
49
+ and self.min_length is not None
50
+ and self.max_length is not None
51
+ ):
52
+ if self.min_length > self.max_length:
53
+ raise ValueError(
54
+ "`max_length` can't be less than `min_length` when the schema is configured with strict mode"
55
+ )
56
+
57
+ return self
58
+
59
+
60
+ class NumberAttributeParameters(AttributeParameters):
61
+ min_value: int | None = Field(
62
+ default=None,
63
+ description="Set a minimum value allowed.",
64
+ json_schema_extra={"update": UpdateSupport.VALIDATE_CONSTRAINT.value},
65
+ )
66
+ max_value: int | None = Field(
67
+ default=None,
68
+ description="Set a maximum value allowed.",
69
+ json_schema_extra={"update": UpdateSupport.VALIDATE_CONSTRAINT.value},
70
+ )
71
+ excluded_values: str | None = Field(
72
+ default=None,
73
+ description="List of values or range of values not allowed for the attribute, format is: '100,150-200,280,300-400'",
74
+ pattern=r"^(\d+(?:-\d+)?)(?:,\d+(?:-\d+)?)*$",
75
+ json_schema_extra={"update": UpdateSupport.VALIDATE_CONSTRAINT.value},
76
+ )
77
+
78
+ @model_validator(mode="after")
79
+ def validate_ranges(self) -> Self:
80
+ ranges = self.get_excluded_ranges()
81
+ for i, (start_range_1, end_range_1) in enumerate(ranges):
82
+ if start_range_1 > end_range_1:
83
+ raise ValueError("`start_range` can't be less than `end_range`")
84
+
85
+ # Check for overlapping ranges
86
+ for start_range_2, end_range_2 in ranges[i + 1 :]:
87
+ if not (end_range_1 < start_range_2 or start_range_1 > end_range_2):
88
+ raise ValueError("Excluded ranges cannot overlap")
89
+
90
+ return self
91
+
92
+ @model_validator(mode="after")
93
+ def validate_min_max(self) -> Self:
94
+ if (
95
+ config.SETTINGS.initialized
96
+ and config.SETTINGS.main.schema_strict_mode
97
+ and self.min_value is not None
98
+ and self.max_value is not None
99
+ ):
100
+ if self.min_value > self.max_value:
101
+ raise ValueError(
102
+ "`max_value` can't be less than `min_value` when the schema is configured with strict mode"
103
+ )
104
+
105
+ return self
106
+
107
+ def get_excluded_single_values(self) -> list[int]:
108
+ if not self.excluded_values:
109
+ return []
110
+
111
+ results = [int(value) for value in self.excluded_values.split(",") if "-" not in value]
112
+ return results
113
+
114
+ def get_excluded_ranges(self) -> list[tuple[int, int]]:
115
+ if not self.excluded_values:
116
+ return []
117
+
118
+ ranges = []
119
+ for value in self.excluded_values.split(","):
120
+ if "-" in value:
121
+ start, end = map(int, value.split("-"))
122
+ ranges.append((start, end))
123
+
124
+ return ranges
125
+
126
+ def is_valid_value(self, value: int) -> bool:
127
+ if self.min_value is not None and value < self.min_value:
128
+ return False
129
+ if self.max_value is not None and value > self.max_value:
130
+ return False
131
+ if value in self.get_excluded_single_values():
132
+ return False
133
+ for start, end in self.get_excluded_ranges():
134
+ if start <= value <= end:
135
+ return False
136
+ return True
137
+
138
+
139
+ class NumberPoolParameters(AttributeParameters):
140
+ end_range: int = Field(
141
+ default=sys.maxsize,
142
+ description="End range for numbers for the associated NumberPool",
143
+ json_schema_extra={"update": UpdateSupport.VALIDATE_CONSTRAINT.value},
144
+ )
145
+ start_range: int = Field(
146
+ default=1,
147
+ description="Start range for numbers for the associated NumberPool",
148
+ json_schema_extra={"update": UpdateSupport.VALIDATE_CONSTRAINT.value},
149
+ )
150
+ number_pool_id: str | None = Field(
151
+ default=None,
152
+ description="The ID of the numberpool associated with this attribute",
153
+ json_schema_extra={"update": UpdateSupport.NOT_SUPPORTED.value},
154
+ )
155
+
156
+ @model_validator(mode="after")
157
+ def validate_ranges(self) -> Self:
158
+ if self.start_range > self.end_range:
159
+ raise ValueError("`start_range` can't be less than `end_range`")
160
+ return self
@@ -12,7 +12,13 @@ from infrahub.core.enums import generate_python_enum
12
12
  from infrahub.core.query.attribute import default_attribute_query_filter
13
13
  from infrahub.types import ATTRIBUTE_KIND_LABELS, ATTRIBUTE_TYPES
14
14
 
15
- from .attribute_parameters import AttributeParameters, TextAttributeParameters, get_attribute_parameters_class_for_kind
15
+ from .attribute_parameters import (
16
+ AttributeParameters,
17
+ NumberAttributeParameters,
18
+ NumberPoolParameters,
19
+ TextAttributeParameters,
20
+ get_attribute_parameters_class_for_kind,
21
+ )
16
22
  from .generated.attribute_schema import GeneratedAttributeSchema
17
23
 
18
24
  if TYPE_CHECKING:
@@ -25,8 +31,10 @@ if TYPE_CHECKING:
25
31
 
26
32
  def get_attribute_schema_class_for_kind(kind: str) -> type[AttributeSchema]:
27
33
  attribute_schema_class_by_kind: dict[str, type[AttributeSchema]] = {
34
+ "NumberPool": NumberPoolSchema,
28
35
  "Text": TextAttributeSchema,
29
36
  "TextArea": TextAttributeSchema,
37
+ "Number": NumberAttributeSchema,
30
38
  }
31
39
  return attribute_schema_class_by_kind.get(kind, AttributeSchema)
32
40
 
@@ -93,6 +101,16 @@ class AttributeSchema(GeneratedAttributeSchema):
93
101
  return expected_parameters_class(**value.model_dump())
94
102
  return value
95
103
 
104
+ @model_validator(mode="after")
105
+ def validate_parameters(self) -> Self:
106
+ if isinstance(self.parameters, NumberPoolParameters) and not self.kind == "NumberPool":
107
+ raise ValueError(f"NumberPoolParameters can't be used as parameters for {self.kind}")
108
+
109
+ if isinstance(self.parameters, TextAttributeParameters) and self.kind not in ["Text", "TextArea"]:
110
+ raise ValueError(f"TextAttributeParameters can't be used as parameters for {self.kind}")
111
+
112
+ return self
113
+
96
114
  def get_class(self) -> type[BaseAttribute]:
97
115
  return ATTRIBUTE_TYPES[self.kind].get_infrahub_class()
98
116
 
@@ -185,6 +203,14 @@ class AttributeSchema(GeneratedAttributeSchema):
185
203
  )
186
204
 
187
205
 
206
+ class NumberPoolSchema(AttributeSchema):
207
+ parameters: NumberPoolParameters = Field(
208
+ default_factory=NumberPoolParameters,
209
+ description="Extra parameters specific to NumberPool attributes",
210
+ json_schema_extra={"update": UpdateSupport.VALIDATE_CONSTRAINT.value},
211
+ )
212
+
213
+
188
214
  class TextAttributeSchema(AttributeSchema):
189
215
  parameters: TextAttributeParameters = Field(
190
216
  default_factory=TextAttributeParameters,
@@ -195,19 +221,13 @@ class TextAttributeSchema(AttributeSchema):
195
221
  @model_validator(mode="after")
196
222
  def reconcile_parameters(self) -> Self:
197
223
  if self.regex != self.parameters.regex:
198
- final_regex = self.parameters.regex or self.regex
199
- if not final_regex: # falsy parameters.regex override falsy regex
200
- final_regex = self.parameters.regex
224
+ final_regex = self.parameters.regex if self.parameters.regex is not None else self.regex
201
225
  self.regex = self.parameters.regex = final_regex
202
226
  if self.min_length != self.parameters.min_length:
203
- final_min_length = self.parameters.min_length or self.min_length
204
- if not final_min_length: # falsy parameters.min_length override falsy min_length
205
- final_min_length = self.parameters.min_length
227
+ final_min_length = self.parameters.min_length if self.parameters.min_length is not None else self.min_length
206
228
  self.min_length = self.parameters.min_length = final_min_length
207
229
  if self.max_length != self.parameters.max_length:
208
- final_max_length = self.parameters.max_length or self.max_length
209
- if not final_max_length: # falsy parameters.max_length override falsy max_length
210
- final_max_length = self.parameters.max_length
230
+ final_max_length = self.parameters.max_length if self.parameters.max_length is not None else self.max_length
211
231
  self.max_length = self.parameters.max_length = final_max_length
212
232
  return self
213
233
 
@@ -219,3 +239,11 @@ class TextAttributeSchema(AttributeSchema):
219
239
 
220
240
  def get_max_length(self) -> int | None:
221
241
  return self.parameters.max_length
242
+
243
+
244
+ class NumberAttributeSchema(AttributeSchema):
245
+ parameters: NumberAttributeParameters = Field(
246
+ default_factory=NumberAttributeParameters,
247
+ description="Extra parameters specific to number attributes",
248
+ json_schema_extra={"update": UpdateSupport.VALIDATE_CONSTRAINT.value},
249
+ )
@@ -28,7 +28,13 @@ from .check import core_check_definition
28
28
  from .core import core_node, core_task_target
29
29
  from .generator import core_generator_definition, core_generator_instance
30
30
  from .graphql_query import core_graphql_query
31
- from .group import core_generator_group, core_graphql_query_group, core_group, core_standard_group
31
+ from .group import (
32
+ core_generator_group,
33
+ core_graphql_query_group,
34
+ core_group,
35
+ core_repository_group,
36
+ core_standard_group,
37
+ )
32
38
  from .ipam import builtin_ip_address, builtin_ip_prefix, builtin_ipam, core_ipam_namespace
33
39
  from .lineage import lineage_owner, lineage_source
34
40
  from .menu import generic_menu_item, menu_item
@@ -68,7 +74,13 @@ from .propose_change_validator import (
68
74
  core_user_validator,
69
75
  )
70
76
  from .repository import core_generic_repository, core_read_only_repository, core_repository
71
- from .resource_pool import core_ip_address_pool, core_ip_prefix_pool, core_number_pool, core_resource_pool
77
+ from .resource_pool import (
78
+ core_ip_address_pool,
79
+ core_ip_prefix_pool,
80
+ core_number_pool,
81
+ core_resource_pool,
82
+ core_weighted_pool_resource,
83
+ )
72
84
  from .template import core_object_component_template, core_object_template
73
85
  from .transform import core_transform, core_transform_jinja2, core_transform_python
74
86
  from .webhook import core_custom_webhook, core_standard_webhook, core_webhook
@@ -96,6 +108,7 @@ core_models_mixed: dict[str, list] = {
96
108
  builtin_ip_prefix,
97
109
  builtin_ip_address,
98
110
  core_resource_pool,
111
+ core_weighted_pool_resource,
99
112
  core_generic_account,
100
113
  core_base_permission,
101
114
  core_credential,
@@ -109,6 +122,7 @@ core_models_mixed: dict[str, list] = {
109
122
  core_standard_group,
110
123
  core_generator_group,
111
124
  core_graphql_query_group,
125
+ core_repository_group,
112
126
  builtin_tag,
113
127
  core_account,
114
128
  core_account_token,
@@ -1,9 +1,11 @@
1
1
  from infrahub.core.constants import (
2
2
  BranchSupportType,
3
3
  InfrahubKind,
4
+ RepositoryObjects,
4
5
  )
5
6
  from infrahub.core.constants import RelationshipCardinality as Cardinality
6
7
  from infrahub.core.constants import RelationshipKind as RelKind
8
+ from infrahub.core.schema.dropdown import DropdownChoice
7
9
 
8
10
  from ...attribute_schema import AttributeSchema as Attr
9
11
  from ...generic_schema import GenericSchema
@@ -80,6 +82,7 @@ core_generator_group = NodeSchema(
80
82
  generate_profile=False,
81
83
  )
82
84
 
85
+
83
86
  core_graphql_query_group = NodeSchema(
84
87
  name="GraphQLQueryGroup",
85
88
  namespace="Core",
@@ -106,3 +109,45 @@ core_graphql_query_group = NodeSchema(
106
109
  ),
107
110
  ],
108
111
  )
112
+
113
+
114
+ core_repository_group = NodeSchema(
115
+ name="RepositoryGroup",
116
+ namespace="Core",
117
+ description="Group of nodes associated with a given repository.",
118
+ include_in_menu=False,
119
+ icon="mdi:account-group",
120
+ label="Repository Group",
121
+ default_filter="name__value",
122
+ order_by=["name__value"],
123
+ display_labels=["name__value"],
124
+ branch=BranchSupportType.LOCAL,
125
+ inherit_from=[InfrahubKind.GENERICGROUP],
126
+ generate_profile=False,
127
+ attributes=[
128
+ Attr(
129
+ name="content",
130
+ kind="Dropdown",
131
+ description="Type of data to load, can be either `object` or `menu`",
132
+ choices=[
133
+ DropdownChoice(
134
+ name=RepositoryObjects.OBJECT.value,
135
+ label="Objects",
136
+ ),
137
+ DropdownChoice(
138
+ name=RepositoryObjects.MENU.value,
139
+ label="Menus",
140
+ ),
141
+ ],
142
+ optional=False,
143
+ ),
144
+ ],
145
+ relationships=[
146
+ Rel(
147
+ name="repository",
148
+ peer=InfrahubKind.GENERICREPOSITORY,
149
+ optional=False,
150
+ cardinality=Cardinality.ONE,
151
+ ),
152
+ ],
153
+ )
@@ -32,6 +32,26 @@ core_resource_pool = GenericSchema(
32
32
  ],
33
33
  )
34
34
 
35
+ core_weighted_pool_resource = GenericSchema(
36
+ name="WeightedPoolResource",
37
+ namespace="Core",
38
+ label="Weighted Pool Resource",
39
+ description="Resource to be used in a pool, its weight is used to determine its priority on allocation.",
40
+ include_in_menu=False,
41
+ branch=BranchSupportType.AWARE,
42
+ generate_profile=False,
43
+ attributes=[
44
+ Attr(
45
+ name="allocation_weight",
46
+ label="Weight",
47
+ description="Weight determines allocation priority, resources with higher values are selected first.",
48
+ kind="Number",
49
+ optional=True,
50
+ order_weight=10000,
51
+ )
52
+ ],
53
+ )
54
+
35
55
  core_ip_prefix_pool = NodeSchema(
36
56
  name="IPPrefixPool",
37
57
  namespace="Core",
@@ -31,7 +31,12 @@ from infrahub.core.constants import (
31
31
  RelationshipKind,
32
32
  UpdateSupport,
33
33
  )
34
- from infrahub.core.schema.attribute_parameters import AttributeParameters
34
+ from infrahub.core.schema.attribute_parameters import (
35
+ AttributeParameters,
36
+ NumberAttributeParameters,
37
+ NumberPoolParameters,
38
+ TextAttributeParameters,
39
+ )
35
40
  from infrahub.core.schema.attribute_schema import AttributeSchema
36
41
  from infrahub.core.schema.computed_attribute import ComputedAttribute
37
42
  from infrahub.core.schema.dropdown import DropdownChoice
@@ -48,7 +53,7 @@ class SchemaAttribute(BaseModel):
48
53
  kind: str
49
54
  description: str
50
55
  extra: ExtraField
51
- internal_kind: type[Any] | GenericAlias | None = None
56
+ internal_kind: type[Any] | GenericAlias | list[type[Any]] | None = None
52
57
  regex: str | None = None
53
58
  unique: bool | None = None
54
59
  optional: bool | None = None
@@ -94,6 +99,9 @@ class SchemaAttribute(BaseModel):
94
99
  if isinstance(self.internal_kind, GenericAlias):
95
100
  return str(self.internal_kind)
96
101
 
102
+ if isinstance(self.internal_kind, list):
103
+ return " | ".join([internal_kind.__name__ for internal_kind in self.internal_kind])
104
+
97
105
  if self.internal_kind and self.kind == "List":
98
106
  return f"list[{self.internal_kind.__name__}]"
99
107
 
@@ -621,7 +629,12 @@ attribute_schema = SchemaNode(
621
629
  SchemaAttribute(
622
630
  name="parameters",
623
631
  kind="JSON",
624
- internal_kind=AttributeParameters,
632
+ internal_kind=[
633
+ AttributeParameters,
634
+ TextAttributeParameters,
635
+ NumberAttributeParameters,
636
+ NumberPoolParameters,
637
+ ],
625
638
  optional=True,
626
639
  description="Extra parameters specific to this kind of attribute",
627
640
  extra={"update": UpdateSupport.VALIDATE_CONSTRAINT},
@@ -8,7 +8,12 @@ from pydantic import Field
8
8
 
9
9
  from infrahub.core.constants import AllowOverrideType, BranchSupportType, HashableModelState
10
10
  from infrahub.core.models import HashableModel
11
- from infrahub.core.schema.attribute_parameters import AttributeParameters # noqa: TC001
11
+ from infrahub.core.schema.attribute_parameters import (
12
+ AttributeParameters, # noqa: TC001
13
+ NumberAttributeParameters, # noqa: TC001
14
+ NumberPoolParameters, # noqa: TC001
15
+ TextAttributeParameters, # noqa: TC001
16
+ )
12
17
  from infrahub.core.schema.computed_attribute import ComputedAttribute # noqa: TC001
13
18
  from infrahub.core.schema.dropdown import DropdownChoice # noqa: TC001
14
19
 
@@ -113,10 +118,12 @@ class GeneratedAttributeSchema(HashableModel):
113
118
  description="Type of allowed override for the attribute.",
114
119
  json_schema_extra={"update": "allowed"},
115
120
  )
116
- parameters: AttributeParameters = Field(
117
- default_factory=AttributeParameters,
118
- description="Extra parameters specific to this kind of attribute",
119
- json_schema_extra={"update": "validate_constraint"},
121
+ parameters: AttributeParameters | TextAttributeParameters | NumberAttributeParameters | NumberPoolParameters = (
122
+ Field(
123
+ default_factory=AttributeParameters,
124
+ description="Extra parameters specific to this kind of attribute",
125
+ json_schema_extra={"update": "validate_constraint"},
126
+ )
120
127
  )
121
128
  deprecation: str | None = Field(
122
129
  default=None,
@@ -764,3 +764,6 @@ class SchemaManager(NodeManager):
764
764
  del self._cache[hash_key]
765
765
 
766
766
  return removed_branches
767
+
768
+ def get_branches(self) -> list[str]:
769
+ return list(self._branches.keys())