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.
- infrahub/core/attribute.py +3 -3
- infrahub/core/constants/__init__.py +5 -0
- infrahub/core/constants/infrahubkind.py +2 -0
- infrahub/core/migrations/query/attribute_rename.py +2 -4
- infrahub/core/migrations/query/delete_element_in_schema.py +16 -11
- infrahub/core/migrations/query/node_duplicate.py +16 -15
- infrahub/core/migrations/query/relationship_duplicate.py +16 -11
- infrahub/core/migrations/schema/node_attribute_remove.py +1 -2
- infrahub/core/migrations/schema/node_remove.py +16 -13
- infrahub/core/node/__init__.py +72 -14
- infrahub/core/node/resource_manager/ip_address_pool.py +6 -2
- infrahub/core/node/resource_manager/ip_prefix_pool.py +6 -2
- infrahub/core/node/resource_manager/number_pool.py +31 -5
- infrahub/core/node/standard.py +6 -1
- infrahub/core/protocols.py +9 -0
- infrahub/core/query/relationship.py +2 -4
- infrahub/core/schema/attribute_parameters.py +129 -5
- infrahub/core/schema/attribute_schema.py +38 -10
- infrahub/core/schema/definitions/core/__init__.py +16 -2
- infrahub/core/schema/definitions/core/group.py +45 -0
- infrahub/core/schema/definitions/core/resource_pool.py +20 -0
- infrahub/core/schema/definitions/internal.py +16 -3
- infrahub/core/schema/generated/attribute_schema.py +12 -5
- infrahub/core/schema/manager.py +3 -0
- infrahub/core/schema/schema_branch.py +55 -0
- infrahub/core/validators/__init__.py +8 -0
- infrahub/core/validators/attribute/choices.py +0 -1
- infrahub/core/validators/attribute/enum.py +0 -1
- infrahub/core/validators/attribute/kind.py +0 -1
- infrahub/core/validators/attribute/length.py +0 -1
- infrahub/core/validators/attribute/min_max.py +118 -0
- infrahub/core/validators/attribute/number_pool.py +106 -0
- infrahub/core/validators/attribute/optional.py +0 -2
- infrahub/core/validators/attribute/regex.py +0 -1
- infrahub/core/validators/enum.py +5 -0
- infrahub/database/__init__.py +15 -3
- infrahub/git/base.py +5 -3
- infrahub/git/integrator.py +102 -3
- infrahub/graphql/mutations/resource_manager.py +62 -6
- infrahub/graphql/queries/resource_manager.py +7 -1
- infrahub/graphql/queries/task.py +10 -0
- infrahub/graphql/types/task_log.py +3 -2
- infrahub/menu/menu.py +3 -3
- infrahub/pools/number.py +5 -3
- infrahub/task_manager/task.py +44 -4
- infrahub/types.py +6 -0
- infrahub_sdk/client.py +43 -10
- infrahub_sdk/node/__init__.py +39 -0
- infrahub_sdk/node/attribute.py +122 -0
- infrahub_sdk/node/constants.py +21 -0
- infrahub_sdk/{node.py → node/node.py} +50 -749
- infrahub_sdk/node/parsers.py +15 -0
- infrahub_sdk/node/property.py +24 -0
- infrahub_sdk/node/related_node.py +266 -0
- infrahub_sdk/node/relationship.py +302 -0
- infrahub_sdk/protocols.py +112 -0
- infrahub_sdk/protocols_base.py +34 -2
- infrahub_sdk/query_groups.py +13 -2
- infrahub_sdk/schema/main.py +1 -0
- infrahub_sdk/schema/repository.py +16 -0
- infrahub_sdk/spec/object.py +1 -1
- infrahub_sdk/store.py +1 -1
- infrahub_sdk/testing/schemas/car_person.py +1 -0
- {infrahub_server-1.3.0a0.dist-info → infrahub_server-1.3.0b1.dist-info}/METADATA +3 -3
- {infrahub_server-1.3.0a0.dist-info → infrahub_server-1.3.0b1.dist-info}/RECORD +68 -59
- {infrahub_server-1.3.0a0.dist-info → infrahub_server-1.3.0b1.dist-info}/WHEEL +1 -1
- {infrahub_server-1.3.0a0.dist-info → infrahub_server-1.3.0b1.dist-info}/LICENSE.txt +0 -0
- {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(
|
|
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
|
-
|
|
89
|
+
if parameters is None or parameters.is_valid_value(num):
|
|
90
|
+
return num
|
|
65
91
|
|
|
66
92
|
return None
|
infrahub/core/node/standard.py
CHANGED
|
@@ -210,7 +210,12 @@ class StandardNode(BaseModel):
|
|
|
210
210
|
|
|
211
211
|
@classmethod
|
|
212
212
|
async def get_list(
|
|
213
|
-
cls,
|
|
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
|
infrahub/core/protocols.py
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
14
|
+
param_classes: dict[str, type[AttributeParameters]] = {
|
|
15
|
+
"NumberPool": NumberPoolParameters,
|
|
11
16
|
"Text": TextAttributeParameters,
|
|
12
17
|
"TextArea": TextAttributeParameters,
|
|
13
|
-
|
|
18
|
+
"Number": NumberAttributeParameters,
|
|
19
|
+
}
|
|
20
|
+
return param_classes.get(kind, AttributeParameters)
|
|
14
21
|
|
|
15
22
|
|
|
16
23
|
class AttributeParameters(HashableModel):
|
|
17
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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=
|
|
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
|
|
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 =
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
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,
|