infrahub-server 1.5.0b1__py3-none-any.whl → 1.5.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.
- infrahub/api/dependencies.py +4 -13
- infrahub/api/internal.py +2 -0
- infrahub/api/oauth2.py +13 -19
- infrahub/api/oidc.py +15 -21
- infrahub/api/schema.py +24 -3
- infrahub/api/transformation.py +22 -20
- infrahub/artifacts/models.py +2 -1
- infrahub/auth.py +137 -3
- infrahub/cli/__init__.py +2 -0
- infrahub/cli/db.py +158 -155
- infrahub/cli/dev.py +118 -0
- infrahub/cli/tasks.py +46 -0
- infrahub/cli/upgrade.py +56 -9
- infrahub/computed_attribute/tasks.py +20 -8
- infrahub/core/attribute.py +10 -2
- infrahub/core/branch/enums.py +1 -1
- infrahub/core/branch/models.py +7 -3
- infrahub/core/branch/tasks.py +68 -7
- infrahub/core/constants/__init__.py +3 -0
- infrahub/core/diff/calculator.py +2 -2
- infrahub/core/diff/query/artifact.py +1 -0
- infrahub/core/diff/query/delete_query.py +9 -5
- infrahub/core/diff/query/field_summary.py +1 -0
- infrahub/core/diff/query/merge.py +39 -23
- infrahub/core/graph/__init__.py +1 -1
- infrahub/core/initialization.py +5 -2
- infrahub/core/migrations/__init__.py +3 -0
- infrahub/core/migrations/exceptions.py +4 -0
- infrahub/core/migrations/graph/__init__.py +12 -13
- infrahub/core/migrations/graph/load_schema_branch.py +21 -0
- infrahub/core/migrations/graph/m013_convert_git_password_credential.py +1 -1
- infrahub/core/migrations/graph/m037_index_attr_vals.py +11 -30
- infrahub/core/migrations/graph/m039_ipam_reconcile.py +9 -7
- infrahub/core/migrations/graph/m040_duplicated_attributes.py +81 -0
- infrahub/core/migrations/graph/m041_deleted_dup_edges.py +149 -0
- infrahub/core/migrations/graph/m042_profile_attrs_in_db.py +147 -0
- infrahub/core/migrations/graph/m043_create_hfid_display_label_in_db.py +164 -0
- infrahub/core/migrations/graph/m044_backfill_hfid_display_label_in_db.py +864 -0
- infrahub/core/migrations/query/__init__.py +7 -8
- infrahub/core/migrations/query/attribute_add.py +8 -6
- infrahub/core/migrations/query/attribute_remove.py +134 -0
- infrahub/core/migrations/runner.py +54 -0
- infrahub/core/migrations/schema/attribute_kind_update.py +9 -3
- infrahub/core/migrations/schema/attribute_supports_profile.py +90 -0
- infrahub/core/migrations/schema/node_attribute_add.py +30 -2
- infrahub/core/migrations/schema/node_attribute_remove.py +13 -109
- infrahub/core/migrations/schema/node_kind_update.py +2 -1
- infrahub/core/migrations/schema/node_remove.py +2 -1
- infrahub/core/migrations/schema/placeholder_dummy.py +3 -2
- infrahub/core/migrations/shared.py +62 -14
- infrahub/core/models.py +2 -2
- infrahub/core/node/__init__.py +42 -12
- infrahub/core/node/create.py +46 -63
- infrahub/core/node/lock_utils.py +70 -44
- infrahub/core/node/resource_manager/ip_address_pool.py +2 -1
- infrahub/core/node/resource_manager/ip_prefix_pool.py +2 -1
- infrahub/core/node/resource_manager/number_pool.py +2 -1
- infrahub/core/query/attribute.py +55 -0
- infrahub/core/query/diff.py +61 -16
- infrahub/core/query/ipam.py +16 -4
- infrahub/core/query/node.py +51 -43
- infrahub/core/query/relationship.py +1 -0
- infrahub/core/relationship/model.py +10 -5
- infrahub/core/schema/__init__.py +56 -0
- infrahub/core/schema/attribute_schema.py +4 -0
- infrahub/core/schema/definitions/core/check.py +1 -1
- infrahub/core/schema/definitions/core/transform.py +1 -1
- infrahub/core/schema/definitions/internal.py +2 -2
- infrahub/core/schema/generated/attribute_schema.py +2 -2
- infrahub/core/schema/manager.py +22 -1
- infrahub/core/schema/schema_branch.py +180 -22
- infrahub/core/schema/schema_branch_display.py +12 -0
- infrahub/core/schema/schema_branch_hfid.py +6 -0
- infrahub/core/validators/uniqueness/checker.py +2 -1
- infrahub/database/__init__.py +0 -13
- infrahub/database/graph.py +21 -0
- infrahub/display_labels/tasks.py +13 -7
- infrahub/events/branch_action.py +27 -1
- infrahub/generators/tasks.py +3 -7
- infrahub/git/base.py +4 -1
- infrahub/git/integrator.py +1 -1
- infrahub/git/models.py +2 -1
- infrahub/git/repository.py +22 -5
- infrahub/git/tasks.py +66 -10
- infrahub/git/utils.py +123 -1
- infrahub/graphql/analyzer.py +9 -0
- infrahub/graphql/api/endpoints.py +14 -4
- infrahub/graphql/manager.py +4 -9
- infrahub/graphql/mutations/branch.py +5 -0
- infrahub/graphql/mutations/convert_object_type.py +11 -1
- infrahub/graphql/mutations/display_label.py +17 -10
- infrahub/graphql/mutations/hfid.py +17 -10
- infrahub/graphql/mutations/ipam.py +54 -35
- infrahub/graphql/mutations/main.py +27 -28
- infrahub/graphql/mutations/proposed_change.py +6 -0
- infrahub/graphql/schema_sort.py +170 -0
- infrahub/graphql/types/branch.py +4 -1
- infrahub/graphql/types/enums.py +3 -0
- infrahub/hfid/tasks.py +13 -7
- infrahub/lock.py +52 -12
- infrahub/message_bus/types.py +3 -1
- infrahub/permissions/constants.py +2 -0
- infrahub/profiles/queries/get_profile_data.py +4 -5
- infrahub/proposed_change/tasks.py +66 -23
- infrahub/server.py +6 -2
- infrahub/services/__init__.py +2 -2
- infrahub/services/adapters/http/__init__.py +5 -0
- infrahub/services/adapters/workflow/worker.py +14 -3
- infrahub/task_manager/event.py +5 -0
- infrahub/task_manager/models.py +7 -0
- infrahub/task_manager/task.py +73 -0
- infrahub/trigger/setup.py +13 -4
- infrahub/trigger/tasks.py +3 -0
- infrahub/workers/dependencies.py +10 -1
- infrahub/workers/infrahub_async.py +10 -2
- infrahub/workflows/catalogue.py +8 -0
- infrahub/workflows/initialization.py +5 -0
- infrahub/workflows/utils.py +2 -1
- infrahub_sdk/analyzer.py +1 -1
- infrahub_sdk/batch.py +2 -2
- infrahub_sdk/branch.py +14 -2
- infrahub_sdk/checks.py +1 -1
- infrahub_sdk/client.py +15 -14
- infrahub_sdk/config.py +29 -2
- infrahub_sdk/ctl/branch.py +3 -0
- infrahub_sdk/ctl/cli_commands.py +2 -0
- infrahub_sdk/ctl/exceptions.py +1 -1
- infrahub_sdk/ctl/schema.py +22 -7
- infrahub_sdk/ctl/task.py +110 -0
- infrahub_sdk/exceptions.py +18 -18
- infrahub_sdk/graphql/query.py +2 -2
- infrahub_sdk/node/attribute.py +1 -1
- infrahub_sdk/node/property.py +1 -1
- infrahub_sdk/node/related_node.py +3 -3
- infrahub_sdk/node/relationship.py +4 -6
- infrahub_sdk/object_store.py +2 -2
- infrahub_sdk/operation.py +1 -1
- infrahub_sdk/protocols_generator/generator.py +1 -1
- infrahub_sdk/pytest_plugin/exceptions.py +9 -9
- infrahub_sdk/pytest_plugin/items/base.py +1 -1
- infrahub_sdk/pytest_plugin/items/check.py +1 -1
- infrahub_sdk/pytest_plugin/items/python_transform.py +1 -1
- infrahub_sdk/repository.py +1 -1
- infrahub_sdk/schema/__init__.py +33 -5
- infrahub_sdk/spec/models.py +7 -0
- infrahub_sdk/spec/object.py +41 -102
- infrahub_sdk/spec/processors/__init__.py +0 -0
- infrahub_sdk/spec/processors/data_processor.py +10 -0
- infrahub_sdk/spec/processors/factory.py +34 -0
- infrahub_sdk/spec/processors/range_expand_processor.py +56 -0
- infrahub_sdk/task/exceptions.py +4 -4
- infrahub_sdk/task/manager.py +2 -2
- infrahub_sdk/task/models.py +6 -4
- infrahub_sdk/timestamp.py +1 -1
- infrahub_sdk/transfer/exporter/json.py +1 -1
- infrahub_sdk/transfer/importer/json.py +1 -1
- infrahub_sdk/transforms.py +1 -1
- {infrahub_server-1.5.0b1.dist-info → infrahub_server-1.5.1.dist-info}/METADATA +4 -2
- {infrahub_server-1.5.0b1.dist-info → infrahub_server-1.5.1.dist-info}/RECORD +168 -152
- infrahub_testcontainers/container.py +144 -6
- infrahub_testcontainers/docker-compose-cluster.test.yml +5 -0
- infrahub_testcontainers/docker-compose.test.yml +5 -0
- infrahub_testcontainers/helpers.py +19 -4
- infrahub_testcontainers/models.py +8 -6
- infrahub_testcontainers/performance_test.py +6 -4
- infrahub/core/migrations/graph/m040_profile_attrs_in_db.py +0 -166
- infrahub/core/migrations/graph/m041_create_hfid_display_label_in_db.py +0 -97
- infrahub/core/migrations/graph/m042_backfill_hfid_display_label_in_db.py +0 -86
- {infrahub_server-1.5.0b1.dist-info → infrahub_server-1.5.1.dist-info}/LICENSE.txt +0 -0
- {infrahub_server-1.5.0b1.dist-info → infrahub_server-1.5.1.dist-info}/WHEEL +0 -0
- {infrahub_server-1.5.0b1.dist-info → infrahub_server-1.5.1.dist-info}/entry_points.txt +0 -0
|
@@ -17,7 +17,7 @@ if TYPE_CHECKING:
|
|
|
17
17
|
class RelatedNodeBase:
|
|
18
18
|
"""Base class for representing a related node in a relationship."""
|
|
19
19
|
|
|
20
|
-
def __init__(self, branch: str, schema: RelationshipSchemaAPI, data: Any | dict, name: str | None = None):
|
|
20
|
+
def __init__(self, branch: str, schema: RelationshipSchemaAPI, data: Any | dict, name: str | None = None) -> None:
|
|
21
21
|
"""
|
|
22
22
|
Args:
|
|
23
23
|
branch (str): The branch where the related node resides.
|
|
@@ -189,7 +189,7 @@ class RelatedNode(RelatedNodeBase):
|
|
|
189
189
|
schema: RelationshipSchemaAPI,
|
|
190
190
|
data: Any | dict,
|
|
191
191
|
name: str | None = None,
|
|
192
|
-
):
|
|
192
|
+
) -> None:
|
|
193
193
|
"""
|
|
194
194
|
Args:
|
|
195
195
|
client (InfrahubClient): The client used to interact with the backend asynchronously.
|
|
@@ -236,7 +236,7 @@ class RelatedNodeSync(RelatedNodeBase):
|
|
|
236
236
|
schema: RelationshipSchemaAPI,
|
|
237
237
|
data: Any | dict,
|
|
238
238
|
name: str | None = None,
|
|
239
|
-
):
|
|
239
|
+
) -> None:
|
|
240
240
|
"""
|
|
241
241
|
Args:
|
|
242
242
|
client (InfrahubClientSync): The client used to interact with the backend synchronously.
|
|
@@ -4,7 +4,6 @@ from collections import defaultdict
|
|
|
4
4
|
from collections.abc import Iterable
|
|
5
5
|
from typing import TYPE_CHECKING, Any
|
|
6
6
|
|
|
7
|
-
from ..batch import InfrahubBatch
|
|
8
7
|
from ..exceptions import (
|
|
9
8
|
Error,
|
|
10
9
|
UninitializedError,
|
|
@@ -22,7 +21,7 @@ if TYPE_CHECKING:
|
|
|
22
21
|
class RelationshipManagerBase:
|
|
23
22
|
"""Base class for RelationshipManager and RelationshipManagerSync"""
|
|
24
23
|
|
|
25
|
-
def __init__(self, name: str, branch: str, schema: RelationshipSchemaAPI):
|
|
24
|
+
def __init__(self, name: str, branch: str, schema: RelationshipSchemaAPI) -> None:
|
|
26
25
|
"""
|
|
27
26
|
Args:
|
|
28
27
|
name (str): The name of the relationship.
|
|
@@ -108,7 +107,7 @@ class RelationshipManager(RelationshipManagerBase):
|
|
|
108
107
|
branch: str,
|
|
109
108
|
schema: RelationshipSchemaAPI,
|
|
110
109
|
data: Any | dict,
|
|
111
|
-
):
|
|
110
|
+
) -> None:
|
|
112
111
|
"""
|
|
113
112
|
Args:
|
|
114
113
|
name (str): The name of the relationship.
|
|
@@ -166,7 +165,7 @@ class RelationshipManager(RelationshipManagerBase):
|
|
|
166
165
|
raise Error("Unable to fetch the peer, id and/or typename are not defined")
|
|
167
166
|
ids_per_kind_map[peer.typename].append(peer.id)
|
|
168
167
|
|
|
169
|
-
batch =
|
|
168
|
+
batch = await self.client.create_batch()
|
|
170
169
|
for kind, ids in ids_per_kind_map.items():
|
|
171
170
|
batch.add(
|
|
172
171
|
task=self.client.filters,
|
|
@@ -231,7 +230,7 @@ class RelationshipManagerSync(RelationshipManagerBase):
|
|
|
231
230
|
branch: str,
|
|
232
231
|
schema: RelationshipSchemaAPI,
|
|
233
232
|
data: Any | dict,
|
|
234
|
-
):
|
|
233
|
+
) -> None:
|
|
235
234
|
"""
|
|
236
235
|
Args:
|
|
237
236
|
name (str): The name of the relationship.
|
|
@@ -289,7 +288,6 @@ class RelationshipManagerSync(RelationshipManagerBase):
|
|
|
289
288
|
raise Error("Unable to fetch the peer, id and/or typename are not defined")
|
|
290
289
|
ids_per_kind_map[peer.typename].append(peer.id)
|
|
291
290
|
|
|
292
|
-
# Unlike Async, no need to create a new batch from scratch because we are not using a semaphore
|
|
293
291
|
batch = self.client.create_batch()
|
|
294
292
|
for kind, ids in ids_per_kind_map.items():
|
|
295
293
|
batch.add(
|
infrahub_sdk/object_store.py
CHANGED
|
@@ -16,7 +16,7 @@ class ObjectStoreBase:
|
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
class ObjectStore(ObjectStoreBase):
|
|
19
|
-
def __init__(self, client: InfrahubClient):
|
|
19
|
+
def __init__(self, client: InfrahubClient) -> None:
|
|
20
20
|
self.client = client
|
|
21
21
|
|
|
22
22
|
async def get(self, identifier: str, tracker: str | None = None) -> str:
|
|
@@ -64,7 +64,7 @@ class ObjectStore(ObjectStoreBase):
|
|
|
64
64
|
|
|
65
65
|
|
|
66
66
|
class ObjectStoreSync(ObjectStoreBase):
|
|
67
|
-
def __init__(self, client: InfrahubClientSync):
|
|
67
|
+
def __init__(self, client: InfrahubClientSync) -> None:
|
|
68
68
|
self.client = client
|
|
69
69
|
|
|
70
70
|
def get(self, identifier: str, tracker: str | None = None) -> str:
|
infrahub_sdk/operation.py
CHANGED
|
@@ -34,7 +34,7 @@ def move_to_end_of_list(lst: list, item: str) -> list:
|
|
|
34
34
|
|
|
35
35
|
|
|
36
36
|
class CodeGenerator:
|
|
37
|
-
def __init__(self, schema: dict[str, MainSchemaTypesAll]):
|
|
37
|
+
def __init__(self, schema: dict[str, MainSchemaTypesAll]) -> None:
|
|
38
38
|
self.generics: dict[str, GenericSchemaAPI | GenericSchema] = {}
|
|
39
39
|
self.nodes: dict[str, NodeSchemaAPI | NodeSchema] = {}
|
|
40
40
|
self.profiles: dict[str, ProfileSchemaAPI] = {}
|
|
@@ -7,37 +7,37 @@ class Error(Exception):
|
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
class InvalidResourceConfigError(Error):
|
|
10
|
-
def __init__(self, resource_name: str):
|
|
10
|
+
def __init__(self, resource_name: str) -> None:
|
|
11
11
|
super().__init__(f"Improperly configured resource with name '{resource_name}'.")
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
class DirectoryNotFoundError(Error):
|
|
15
|
-
def __init__(self, name: str, message: str = ""):
|
|
15
|
+
def __init__(self, name: str, message: str = "") -> None:
|
|
16
16
|
self.message = message or f"Unable to find directory {name!r}."
|
|
17
17
|
super().__init__(self.message)
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
class FileNotValidError(Error):
|
|
21
|
-
def __init__(self, name: str, message: str = ""):
|
|
21
|
+
def __init__(self, name: str, message: str = "") -> None:
|
|
22
22
|
self.message = message or f"Unable to access file {name!r}."
|
|
23
23
|
super().__init__(self.message)
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
class OutputMatchError(Error):
|
|
27
|
-
def __init__(self, name: str, message: str = "", differences: str = ""):
|
|
27
|
+
def __init__(self, name: str, message: str = "", differences: str = "") -> None:
|
|
28
28
|
self.message = message or f"Rendered output does not match expected output for {name!r}."
|
|
29
29
|
self.differences = differences
|
|
30
30
|
super().__init__(self.message)
|
|
31
31
|
|
|
32
32
|
|
|
33
33
|
class Jinja2TransformError(Error):
|
|
34
|
-
def __init__(self, name: str, message: str = ""):
|
|
34
|
+
def __init__(self, name: str, message: str = "") -> None:
|
|
35
35
|
self.message = message or f"Unexpected error happened while processing {name!r}."
|
|
36
36
|
super().__init__(self.message)
|
|
37
37
|
|
|
38
38
|
|
|
39
39
|
class Jinja2TransformUndefinedError(Error):
|
|
40
|
-
def __init__(self, name: str, rtb: Traceback, errors: list[tuple[Frame, Syntax]], message: str = ""):
|
|
40
|
+
def __init__(self, name: str, rtb: Traceback, errors: list[tuple[Frame, Syntax]], message: str = "") -> None:
|
|
41
41
|
self.rtb = rtb
|
|
42
42
|
self.errors = errors
|
|
43
43
|
self.message = message or f"Unable to render Jinja2 transform {name!r}."
|
|
@@ -45,18 +45,18 @@ class Jinja2TransformUndefinedError(Error):
|
|
|
45
45
|
|
|
46
46
|
|
|
47
47
|
class CheckDefinitionError(Error):
|
|
48
|
-
def __init__(self, name: str, message: str = ""):
|
|
48
|
+
def __init__(self, name: str, message: str = "") -> None:
|
|
49
49
|
self.message = message or f"Check {name!r} is not properly defined."
|
|
50
50
|
super().__init__(self.message)
|
|
51
51
|
|
|
52
52
|
|
|
53
53
|
class CheckResultError(Error):
|
|
54
|
-
def __init__(self, name: str, message: str = ""):
|
|
54
|
+
def __init__(self, name: str, message: str = "") -> None:
|
|
55
55
|
self.message = message or f"Unexpected result for check {name!r}."
|
|
56
56
|
super().__init__(self.message)
|
|
57
57
|
|
|
58
58
|
|
|
59
59
|
class PythonTransformDefinitionError(Error):
|
|
60
|
-
def __init__(self, name: str, message: str = ""):
|
|
60
|
+
def __init__(self, name: str, message: str = "") -> None:
|
|
61
61
|
self.message = message or f"Python transform {name!r} is not properly defined."
|
|
62
62
|
super().__init__(self.message)
|
|
@@ -25,7 +25,7 @@ class InfrahubItem(pytest.Item):
|
|
|
25
25
|
resource_config: InfrahubRepositoryConfigElement,
|
|
26
26
|
test: InfrahubTest,
|
|
27
27
|
**kwargs: dict[str, Any],
|
|
28
|
-
):
|
|
28
|
+
) -> None:
|
|
29
29
|
super().__init__(*args, **kwargs) # type: ignore[arg-type]
|
|
30
30
|
self.resource_name: str = resource_name
|
|
31
31
|
self.resource_config: InfrahubRepositoryConfigElement = resource_config
|
|
@@ -27,7 +27,7 @@ class InfrahubCheckItem(InfrahubItem):
|
|
|
27
27
|
resource_config: InfrahubRepositoryConfigElement,
|
|
28
28
|
test: InfrahubTest,
|
|
29
29
|
**kwargs: dict[str, Any],
|
|
30
|
-
):
|
|
30
|
+
) -> None:
|
|
31
31
|
super().__init__(*args, resource_name=resource_name, resource_config=resource_config, test=test, **kwargs)
|
|
32
32
|
|
|
33
33
|
self.check_instance: InfrahubCheck
|
|
@@ -28,7 +28,7 @@ class InfrahubPythonTransformItem(InfrahubItem):
|
|
|
28
28
|
resource_config: InfrahubRepositoryConfigElement,
|
|
29
29
|
test: InfrahubTest,
|
|
30
30
|
**kwargs: dict[str, Any],
|
|
31
|
-
):
|
|
31
|
+
) -> None:
|
|
32
32
|
super().__init__(*args, resource_name=resource_name, resource_config=resource_config, test=test, **kwargs)
|
|
33
33
|
|
|
34
34
|
self.transform_instance: InfrahubTransform
|
infrahub_sdk/repository.py
CHANGED
|
@@ -7,7 +7,7 @@ from dulwich.repo import Repo
|
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
class GitRepoManager:
|
|
10
|
-
def __init__(self, root_directory: str, branch: str = "main"):
|
|
10
|
+
def __init__(self, root_directory: str, branch: str = "main") -> None:
|
|
11
11
|
self.root_directory = root_directory
|
|
12
12
|
self.branch = branch
|
|
13
13
|
self.git: Repo = self.initialize_repo()
|
infrahub_sdk/schema/__init__.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
|
+
import inspect
|
|
4
5
|
import json
|
|
5
6
|
import warnings
|
|
6
7
|
from collections.abc import MutableMapping
|
|
@@ -90,11 +91,31 @@ MainSchemaTypesAll: TypeAlias = Union[
|
|
|
90
91
|
]
|
|
91
92
|
|
|
92
93
|
|
|
94
|
+
class SchemaWarningType(Enum):
|
|
95
|
+
DEPRECATION = "deprecation"
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class SchemaWarningKind(BaseModel):
|
|
99
|
+
kind: str = Field(..., description="The kind impacted by the warning")
|
|
100
|
+
field: str | None = Field(default=None, description="The attribute or relationship impacted by the warning")
|
|
101
|
+
|
|
102
|
+
@property
|
|
103
|
+
def display(self) -> str:
|
|
104
|
+
suffix = f".{self.field}" if self.field else ""
|
|
105
|
+
return f"{self.kind}{suffix}"
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class SchemaWarning(BaseModel):
|
|
109
|
+
type: SchemaWarningType = Field(..., description="The type of warning")
|
|
110
|
+
kinds: list[SchemaWarningKind] = Field(default_factory=list, description="The kinds impacted by the warning")
|
|
111
|
+
message: str = Field(..., description="The message that describes the warning")
|
|
112
|
+
|
|
113
|
+
|
|
93
114
|
class InfrahubSchemaBase:
|
|
94
115
|
client: InfrahubClient | InfrahubClientSync
|
|
95
116
|
cache: dict[str, BranchSchema]
|
|
96
117
|
|
|
97
|
-
def __init__(self, client: InfrahubClient | InfrahubClientSync):
|
|
118
|
+
def __init__(self, client: InfrahubClient | InfrahubClientSync) -> None:
|
|
98
119
|
self.client = client
|
|
99
120
|
self.cache = {}
|
|
100
121
|
|
|
@@ -169,7 +190,9 @@ class InfrahubSchemaBase:
|
|
|
169
190
|
def _validate_load_schema_response(response: httpx.Response) -> SchemaLoadResponse:
|
|
170
191
|
if response.status_code == httpx.codes.OK:
|
|
171
192
|
status = response.json()
|
|
172
|
-
return SchemaLoadResponse(
|
|
193
|
+
return SchemaLoadResponse(
|
|
194
|
+
hash=status["hash"], previous_hash=status["previous_hash"], warnings=status.get("warnings") or []
|
|
195
|
+
)
|
|
173
196
|
|
|
174
197
|
if response.status_code in [
|
|
175
198
|
httpx.codes.BAD_REQUEST,
|
|
@@ -185,12 +208,16 @@ class InfrahubSchemaBase:
|
|
|
185
208
|
|
|
186
209
|
@staticmethod
|
|
187
210
|
def _get_schema_name(schema: type[SchemaType | SchemaTypeSync] | str) -> str:
|
|
188
|
-
if hasattr(schema, "_is_runtime_protocol") and schema._is_runtime_protocol: # type: ignore[union-attr]
|
|
189
|
-
return schema.__name__ # type: ignore[union-attr]
|
|
190
|
-
|
|
191
211
|
if isinstance(schema, str):
|
|
192
212
|
return schema
|
|
193
213
|
|
|
214
|
+
if hasattr(schema, "_is_runtime_protocol") and getattr(schema, "_is_runtime_protocol", None):
|
|
215
|
+
if inspect.iscoroutinefunction(schema.save):
|
|
216
|
+
return schema.__name__
|
|
217
|
+
if schema.__name__[-4:] == "Sync":
|
|
218
|
+
return schema.__name__[:-4]
|
|
219
|
+
return schema.__name__
|
|
220
|
+
|
|
194
221
|
raise ValueError("schema must be a protocol or a string")
|
|
195
222
|
|
|
196
223
|
@staticmethod
|
|
@@ -802,6 +829,7 @@ class SchemaLoadResponse(BaseModel):
|
|
|
802
829
|
hash: str = Field(default="", description="The new hash for the entire schema")
|
|
803
830
|
previous_hash: str = Field(default="", description="The previous hash for the entire schema")
|
|
804
831
|
errors: dict = Field(default_factory=dict, description="Errors reported by the server")
|
|
832
|
+
warnings: list[SchemaWarning] = Field(default_factory=list, description="Warnings reported by the server")
|
|
805
833
|
|
|
806
834
|
@property
|
|
807
835
|
def schema_updated(self) -> bool:
|
infrahub_sdk/spec/object.py
CHANGED
|
@@ -1,17 +1,15 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
import copy
|
|
4
|
-
import re
|
|
5
|
-
from abc import ABC, abstractmethod
|
|
6
3
|
from enum import Enum
|
|
7
|
-
from typing import TYPE_CHECKING, Any
|
|
4
|
+
from typing import TYPE_CHECKING, Any
|
|
8
5
|
|
|
9
6
|
from pydantic import BaseModel, Field
|
|
10
7
|
|
|
11
8
|
from ..exceptions import ObjectValidationError, ValidationError
|
|
12
9
|
from ..schema import GenericSchemaAPI, RelationshipKind, RelationshipSchema
|
|
13
10
|
from ..yaml import InfrahubFile, InfrahubFileKind
|
|
14
|
-
from .
|
|
11
|
+
from .models import InfrahubObjectParameters
|
|
12
|
+
from .processors.factory import DataProcessorFactory
|
|
15
13
|
|
|
16
14
|
if TYPE_CHECKING:
|
|
17
15
|
from ..client import InfrahubClient
|
|
@@ -46,11 +44,6 @@ class RelationshipDataFormat(str, Enum):
|
|
|
46
44
|
MANY_REF = "many_ref_list"
|
|
47
45
|
|
|
48
46
|
|
|
49
|
-
class ObjectStrategy(str, Enum):
|
|
50
|
-
NORMAL = "normal"
|
|
51
|
-
RANGE_EXPAND = "range_expand"
|
|
52
|
-
|
|
53
|
-
|
|
54
47
|
class RelationshipInfo(BaseModel):
|
|
55
48
|
name: str
|
|
56
49
|
rel_schema: RelationshipSchema
|
|
@@ -173,97 +166,21 @@ async def get_relationship_info(
|
|
|
173
166
|
return info
|
|
174
167
|
|
|
175
168
|
|
|
176
|
-
def expand_data_with_ranges(data: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
|
177
|
-
"""Expand any item in data with range pattern in any value. Supports multiple fields, requires equal expansion length."""
|
|
178
|
-
range_pattern = re.compile(MATCH_PATTERN)
|
|
179
|
-
expanded = []
|
|
180
|
-
for item in data:
|
|
181
|
-
# Find all fields to expand
|
|
182
|
-
expand_fields = {}
|
|
183
|
-
for key, value in item.items():
|
|
184
|
-
if isinstance(value, str) and range_pattern.search(value):
|
|
185
|
-
try:
|
|
186
|
-
expand_fields[key] = range_expansion(value)
|
|
187
|
-
except Exception:
|
|
188
|
-
# If expansion fails, treat as no expansion
|
|
189
|
-
expand_fields[key] = [value]
|
|
190
|
-
if not expand_fields:
|
|
191
|
-
expanded.append(item)
|
|
192
|
-
continue
|
|
193
|
-
# Check all expanded lists have the same length
|
|
194
|
-
lengths = [len(v) for v in expand_fields.values()]
|
|
195
|
-
if len(set(lengths)) > 1:
|
|
196
|
-
raise ValidationError(f"Range expansion mismatch: fields expanded to different lengths: {lengths}")
|
|
197
|
-
n = lengths[0]
|
|
198
|
-
# Zip expanded values and produce new items
|
|
199
|
-
for i in range(n):
|
|
200
|
-
new_item = copy.deepcopy(item)
|
|
201
|
-
for key, values in expand_fields.items():
|
|
202
|
-
new_item[key] = values[i]
|
|
203
|
-
expanded.append(new_item)
|
|
204
|
-
return expanded
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
class DataProcessor(ABC):
|
|
208
|
-
"""Abstract base class for data processing strategies"""
|
|
209
|
-
|
|
210
|
-
@abstractmethod
|
|
211
|
-
def process_data(self, data: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
|
212
|
-
"""Process the data according to the strategy"""
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
class SingleDataProcessor(DataProcessor):
|
|
216
|
-
"""Process data without any expansion"""
|
|
217
|
-
|
|
218
|
-
def process_data(self, data: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
|
219
|
-
return data
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
class RangeExpandDataProcessor(DataProcessor):
|
|
223
|
-
"""Process data with range expansion"""
|
|
224
|
-
|
|
225
|
-
def process_data(self, data: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
|
226
|
-
return expand_data_with_ranges(data)
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
class DataProcessorFactory:
|
|
230
|
-
"""Factory to create appropriate data processor based on strategy"""
|
|
231
|
-
|
|
232
|
-
_processors: ClassVar[dict[ObjectStrategy, type[DataProcessor]]] = {
|
|
233
|
-
ObjectStrategy.NORMAL: SingleDataProcessor,
|
|
234
|
-
ObjectStrategy.RANGE_EXPAND: RangeExpandDataProcessor,
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
@classmethod
|
|
238
|
-
def get_processor(cls, strategy: ObjectStrategy) -> DataProcessor:
|
|
239
|
-
processor_class = cls._processors.get(strategy)
|
|
240
|
-
if not processor_class:
|
|
241
|
-
raise ValueError(
|
|
242
|
-
f"Unknown strategy: {strategy} - no processor found. Valid strategies are: {list(cls._processors.keys())}"
|
|
243
|
-
)
|
|
244
|
-
return processor_class()
|
|
245
|
-
|
|
246
|
-
@classmethod
|
|
247
|
-
def register_processor(cls, strategy: ObjectStrategy, processor_class: type[DataProcessor]) -> None:
|
|
248
|
-
"""Register a new processor for a strategy - useful for future extensions"""
|
|
249
|
-
cls._processors[strategy] = processor_class
|
|
250
|
-
|
|
251
|
-
|
|
252
169
|
class InfrahubObjectFileData(BaseModel):
|
|
253
170
|
kind: str
|
|
254
|
-
|
|
171
|
+
parameters: InfrahubObjectParameters = Field(default_factory=InfrahubObjectParameters)
|
|
255
172
|
data: list[dict[str, Any]] = Field(default_factory=list)
|
|
256
173
|
|
|
257
|
-
def _get_processed_data(self, data: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
|
174
|
+
async def _get_processed_data(self, data: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
|
258
175
|
"""Get data processed according to the strategy"""
|
|
259
|
-
|
|
260
|
-
return
|
|
176
|
+
|
|
177
|
+
return await DataProcessorFactory.process_data(kind=self.kind, parameters=self.parameters, data=data)
|
|
261
178
|
|
|
262
179
|
async def validate_format(self, client: InfrahubClient, branch: str | None = None) -> list[ObjectValidationError]:
|
|
263
180
|
errors: list[ObjectValidationError] = []
|
|
264
181
|
schema = await client.schema.get(kind=self.kind, branch=branch)
|
|
265
182
|
|
|
266
|
-
processed_data = self._get_processed_data(data=self.data)
|
|
183
|
+
processed_data = await self._get_processed_data(data=self.data)
|
|
267
184
|
self.data = processed_data
|
|
268
185
|
|
|
269
186
|
for idx, item in enumerate(processed_data):
|
|
@@ -275,14 +192,14 @@ class InfrahubObjectFileData(BaseModel):
|
|
|
275
192
|
data=item,
|
|
276
193
|
branch=branch,
|
|
277
194
|
default_schema_kind=self.kind,
|
|
278
|
-
|
|
195
|
+
parameters=self.parameters,
|
|
279
196
|
)
|
|
280
197
|
)
|
|
281
198
|
return errors
|
|
282
199
|
|
|
283
200
|
async def process(self, client: InfrahubClient, branch: str | None = None) -> None:
|
|
284
201
|
schema = await client.schema.get(kind=self.kind, branch=branch)
|
|
285
|
-
processed_data = self._get_processed_data(data=self.data)
|
|
202
|
+
processed_data = await self._get_processed_data(data=self.data)
|
|
286
203
|
|
|
287
204
|
for idx, item in enumerate(processed_data):
|
|
288
205
|
await self.create_node(
|
|
@@ -292,6 +209,7 @@ class InfrahubObjectFileData(BaseModel):
|
|
|
292
209
|
position=[idx + 1],
|
|
293
210
|
branch=branch,
|
|
294
211
|
default_schema_kind=self.kind,
|
|
212
|
+
parameters=self.parameters,
|
|
295
213
|
)
|
|
296
214
|
|
|
297
215
|
@classmethod
|
|
@@ -304,8 +222,9 @@ class InfrahubObjectFileData(BaseModel):
|
|
|
304
222
|
context: dict | None = None,
|
|
305
223
|
branch: str | None = None,
|
|
306
224
|
default_schema_kind: str | None = None,
|
|
307
|
-
|
|
225
|
+
parameters: InfrahubObjectParameters | None = None,
|
|
308
226
|
) -> list[ObjectValidationError]:
|
|
227
|
+
parameters = parameters or InfrahubObjectParameters()
|
|
309
228
|
errors: list[ObjectValidationError] = []
|
|
310
229
|
context = context.copy() if context else {}
|
|
311
230
|
|
|
@@ -354,7 +273,7 @@ class InfrahubObjectFileData(BaseModel):
|
|
|
354
273
|
context=context,
|
|
355
274
|
branch=branch,
|
|
356
275
|
default_schema_kind=default_schema_kind,
|
|
357
|
-
|
|
276
|
+
parameters=parameters,
|
|
358
277
|
)
|
|
359
278
|
)
|
|
360
279
|
|
|
@@ -370,8 +289,9 @@ class InfrahubObjectFileData(BaseModel):
|
|
|
370
289
|
context: dict | None = None,
|
|
371
290
|
branch: str | None = None,
|
|
372
291
|
default_schema_kind: str | None = None,
|
|
373
|
-
|
|
292
|
+
parameters: InfrahubObjectParameters | None = None,
|
|
374
293
|
) -> list[ObjectValidationError]:
|
|
294
|
+
parameters = parameters or InfrahubObjectParameters()
|
|
375
295
|
context = context.copy() if context else {}
|
|
376
296
|
errors: list[ObjectValidationError] = []
|
|
377
297
|
|
|
@@ -399,6 +319,7 @@ class InfrahubObjectFileData(BaseModel):
|
|
|
399
319
|
context=context,
|
|
400
320
|
branch=branch,
|
|
401
321
|
default_schema_kind=default_schema_kind,
|
|
322
|
+
parameters=parameters,
|
|
402
323
|
)
|
|
403
324
|
)
|
|
404
325
|
return errors
|
|
@@ -412,11 +333,11 @@ class InfrahubObjectFileData(BaseModel):
|
|
|
412
333
|
rel_info.find_matching_relationship(peer_schema=peer_schema)
|
|
413
334
|
context.update(rel_info.get_context(value="placeholder"))
|
|
414
335
|
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
336
|
+
processed_data = await DataProcessorFactory.process_data(
|
|
337
|
+
kind=peer_kind, data=data["data"], parameters=parameters
|
|
338
|
+
)
|
|
418
339
|
|
|
419
|
-
for idx, peer_data in enumerate(
|
|
340
|
+
for idx, peer_data in enumerate(processed_data):
|
|
420
341
|
context["list_index"] = idx
|
|
421
342
|
errors.extend(
|
|
422
343
|
await cls.validate_object(
|
|
@@ -427,7 +348,7 @@ class InfrahubObjectFileData(BaseModel):
|
|
|
427
348
|
context=context,
|
|
428
349
|
branch=branch,
|
|
429
350
|
default_schema_kind=default_schema_kind,
|
|
430
|
-
|
|
351
|
+
parameters=parameters,
|
|
431
352
|
)
|
|
432
353
|
)
|
|
433
354
|
return errors
|
|
@@ -452,6 +373,7 @@ class InfrahubObjectFileData(BaseModel):
|
|
|
452
373
|
context=context,
|
|
453
374
|
branch=branch,
|
|
454
375
|
default_schema_kind=default_schema_kind,
|
|
376
|
+
parameters=parameters,
|
|
455
377
|
)
|
|
456
378
|
)
|
|
457
379
|
return errors
|
|
@@ -478,7 +400,9 @@ class InfrahubObjectFileData(BaseModel):
|
|
|
478
400
|
context: dict | None = None,
|
|
479
401
|
branch: str | None = None,
|
|
480
402
|
default_schema_kind: str | None = None,
|
|
403
|
+
parameters: InfrahubObjectParameters | None = None,
|
|
481
404
|
) -> InfrahubNode:
|
|
405
|
+
parameters = parameters or InfrahubObjectParameters()
|
|
482
406
|
context = context.copy() if context else {}
|
|
483
407
|
|
|
484
408
|
errors = await cls.validate_object(
|
|
@@ -489,6 +413,7 @@ class InfrahubObjectFileData(BaseModel):
|
|
|
489
413
|
context=context,
|
|
490
414
|
branch=branch,
|
|
491
415
|
default_schema_kind=default_schema_kind,
|
|
416
|
+
parameters=parameters,
|
|
492
417
|
)
|
|
493
418
|
if errors:
|
|
494
419
|
messages = [str(error) for error in errors]
|
|
@@ -545,6 +470,9 @@ class InfrahubObjectFileData(BaseModel):
|
|
|
545
470
|
data=value,
|
|
546
471
|
branch=branch,
|
|
547
472
|
default_schema_kind=default_schema_kind,
|
|
473
|
+
parameters=InfrahubObjectParameters(**value.get("parameters"))
|
|
474
|
+
if "parameters" in value
|
|
475
|
+
else None,
|
|
548
476
|
)
|
|
549
477
|
clean_data[key] = nodes
|
|
550
478
|
|
|
@@ -583,6 +511,9 @@ class InfrahubObjectFileData(BaseModel):
|
|
|
583
511
|
context=context,
|
|
584
512
|
branch=branch,
|
|
585
513
|
default_schema_kind=default_schema_kind,
|
|
514
|
+
parameters=InfrahubObjectParameters(**data[rel].get("parameters"))
|
|
515
|
+
if "parameters" in data[rel]
|
|
516
|
+
else None,
|
|
586
517
|
)
|
|
587
518
|
|
|
588
519
|
return node
|
|
@@ -598,7 +529,9 @@ class InfrahubObjectFileData(BaseModel):
|
|
|
598
529
|
context: dict | None = None,
|
|
599
530
|
branch: str | None = None,
|
|
600
531
|
default_schema_kind: str | None = None,
|
|
532
|
+
parameters: InfrahubObjectParameters | None = None,
|
|
601
533
|
) -> list[InfrahubNode]:
|
|
534
|
+
parameters = parameters or InfrahubObjectParameters()
|
|
602
535
|
nodes: list[InfrahubNode] = []
|
|
603
536
|
context = context.copy() if context else {}
|
|
604
537
|
|
|
@@ -618,6 +551,7 @@ class InfrahubObjectFileData(BaseModel):
|
|
|
618
551
|
context=context,
|
|
619
552
|
branch=branch,
|
|
620
553
|
default_schema_kind=default_schema_kind,
|
|
554
|
+
parameters=parameters,
|
|
621
555
|
)
|
|
622
556
|
return [new_node]
|
|
623
557
|
|
|
@@ -631,7 +565,10 @@ class InfrahubObjectFileData(BaseModel):
|
|
|
631
565
|
rel_info.find_matching_relationship(peer_schema=peer_schema)
|
|
632
566
|
context.update(rel_info.get_context(value=parent_node.id))
|
|
633
567
|
|
|
634
|
-
expanded_data =
|
|
568
|
+
expanded_data = await DataProcessorFactory.process_data(
|
|
569
|
+
kind=peer_kind, data=data["data"], parameters=parameters
|
|
570
|
+
)
|
|
571
|
+
|
|
635
572
|
for idx, peer_data in enumerate(expanded_data):
|
|
636
573
|
context["list_index"] = idx
|
|
637
574
|
if isinstance(peer_data, dict):
|
|
@@ -643,6 +580,7 @@ class InfrahubObjectFileData(BaseModel):
|
|
|
643
580
|
context=context,
|
|
644
581
|
branch=branch,
|
|
645
582
|
default_schema_kind=default_schema_kind,
|
|
583
|
+
parameters=parameters,
|
|
646
584
|
)
|
|
647
585
|
nodes.append(node)
|
|
648
586
|
return nodes
|
|
@@ -668,6 +606,7 @@ class InfrahubObjectFileData(BaseModel):
|
|
|
668
606
|
context=context,
|
|
669
607
|
branch=branch,
|
|
670
608
|
default_schema_kind=default_schema_kind,
|
|
609
|
+
parameters=parameters,
|
|
671
610
|
)
|
|
672
611
|
nodes.append(node)
|
|
673
612
|
|
|
File without changes
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class DataProcessor(ABC):
|
|
6
|
+
"""Abstract base class for data processing strategies"""
|
|
7
|
+
|
|
8
|
+
@abstractmethod
|
|
9
|
+
async def process_data(self, data: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
|
10
|
+
"""Process the data according to the strategy"""
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from collections.abc import Sequence
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
from ..models import InfrahubObjectParameters
|
|
5
|
+
from .data_processor import DataProcessor
|
|
6
|
+
from .range_expand_processor import RangeExpandDataProcessor
|
|
7
|
+
|
|
8
|
+
PROCESSOR_PER_KIND: dict[str, DataProcessor] = {}
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class DataProcessorFactory:
|
|
12
|
+
"""Factory to create appropriate data processor based on strategy"""
|
|
13
|
+
|
|
14
|
+
@classmethod
|
|
15
|
+
def get_processors(cls, kind: str, parameters: InfrahubObjectParameters) -> Sequence[DataProcessor]:
|
|
16
|
+
processors: list[DataProcessor] = []
|
|
17
|
+
if parameters.expand_range:
|
|
18
|
+
processors.append(RangeExpandDataProcessor())
|
|
19
|
+
if kind in PROCESSOR_PER_KIND:
|
|
20
|
+
processors.append(PROCESSOR_PER_KIND[kind])
|
|
21
|
+
|
|
22
|
+
return processors
|
|
23
|
+
|
|
24
|
+
@classmethod
|
|
25
|
+
async def process_data(
|
|
26
|
+
cls,
|
|
27
|
+
kind: str,
|
|
28
|
+
data: list[dict[str, Any]],
|
|
29
|
+
parameters: InfrahubObjectParameters,
|
|
30
|
+
) -> list[dict[str, Any]]:
|
|
31
|
+
processors = cls.get_processors(kind=kind, parameters=parameters)
|
|
32
|
+
for processor in processors:
|
|
33
|
+
data = await processor.process_data(data=data)
|
|
34
|
+
return data
|