infrahub-server 1.5.0b0__py3-none-any.whl → 1.5.0b2__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/actions/tasks.py +8 -0
- infrahub/api/diff/diff.py +1 -1
- 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/artifacts/models.py +2 -1
- infrahub/auth.py +137 -3
- infrahub/cli/__init__.py +2 -0
- infrahub/cli/db.py +103 -98
- infrahub/cli/db_commands/clean_duplicate_schema_fields.py +212 -0
- infrahub/cli/dev.py +118 -0
- infrahub/cli/tasks.py +46 -0
- infrahub/cli/upgrade.py +30 -3
- infrahub/computed_attribute/tasks.py +20 -8
- infrahub/core/attribute.py +13 -5
- infrahub/core/branch/enums.py +1 -1
- infrahub/core/branch/models.py +7 -3
- infrahub/core/branch/tasks.py +70 -8
- infrahub/core/changelog/models.py +4 -12
- infrahub/core/constants/__init__.py +3 -0
- infrahub/core/constants/infrahubkind.py +1 -0
- infrahub/core/diff/model/path.py +4 -0
- infrahub/core/diff/payload_builder.py +1 -1
- infrahub/core/diff/query/artifact.py +1 -0
- infrahub/core/diff/query/field_summary.py +1 -0
- infrahub/core/graph/__init__.py +1 -1
- infrahub/core/initialization.py +5 -2
- infrahub/core/ipam/utilization.py +1 -1
- infrahub/core/manager.py +6 -3
- infrahub/core/migrations/__init__.py +3 -0
- infrahub/core/migrations/exceptions.py +4 -0
- infrahub/core/migrations/graph/__init__.py +12 -11
- 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/m040_duplicated_attributes.py +81 -0
- infrahub/core/migrations/graph/m041_profile_attrs_in_db.py +145 -0
- infrahub/core/migrations/graph/m042_create_hfid_display_label_in_db.py +164 -0
- infrahub/core/migrations/graph/m043_backfill_hfid_display_label_in_db.py +866 -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 +35 -4
- 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 +52 -19
- infrahub/core/node/__init__.py +158 -51
- infrahub/core/node/constraints/attribute_uniqueness.py +3 -1
- infrahub/core/node/create.py +46 -63
- infrahub/core/node/lock_utils.py +70 -44
- infrahub/core/node/node_property_attribute.py +230 -0
- 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/node/standard.py +1 -1
- infrahub/core/protocols.py +7 -1
- infrahub/core/query/attribute.py +55 -0
- infrahub/core/query/ipam.py +1 -0
- infrahub/core/query/node.py +23 -4
- infrahub/core/query/relationship.py +1 -0
- infrahub/core/registry.py +2 -2
- infrahub/core/relationship/constraints/count.py +1 -1
- infrahub/core/relationship/model.py +1 -1
- infrahub/core/schema/__init__.py +56 -0
- infrahub/core/schema/attribute_schema.py +4 -0
- infrahub/core/schema/basenode_schema.py +42 -2
- infrahub/core/schema/definitions/core/__init__.py +2 -0
- infrahub/core/schema/definitions/core/generator.py +2 -0
- infrahub/core/schema/definitions/core/group.py +16 -2
- infrahub/core/schema/definitions/internal.py +16 -3
- infrahub/core/schema/generated/attribute_schema.py +2 -2
- infrahub/core/schema/generated/base_node_schema.py +6 -1
- infrahub/core/schema/manager.py +22 -1
- infrahub/core/schema/node_schema.py +5 -2
- infrahub/core/schema/schema_branch.py +300 -8
- infrahub/core/schema/schema_branch_display.py +123 -0
- infrahub/core/schema/schema_branch_hfid.py +114 -0
- infrahub/core/validators/aggregated_checker.py +1 -1
- infrahub/core/validators/determiner.py +12 -1
- infrahub/core/validators/relationship/peer.py +1 -1
- infrahub/core/validators/tasks.py +1 -1
- infrahub/database/graph.py +21 -0
- infrahub/display_labels/__init__.py +0 -0
- infrahub/display_labels/gather.py +48 -0
- infrahub/display_labels/models.py +240 -0
- infrahub/display_labels/tasks.py +192 -0
- infrahub/display_labels/triggers.py +22 -0
- infrahub/events/branch_action.py +27 -1
- infrahub/events/group_action.py +1 -1
- infrahub/events/node_action.py +1 -1
- infrahub/generators/constants.py +7 -0
- infrahub/generators/models.py +7 -0
- infrahub/generators/tasks.py +34 -22
- infrahub/git/base.py +4 -1
- infrahub/git/integrator.py +23 -15
- 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 +1 -1
- infrahub/graphql/api/endpoints.py +14 -4
- infrahub/graphql/manager.py +4 -9
- infrahub/graphql/mutations/convert_object_type.py +11 -1
- infrahub/graphql/mutations/display_label.py +118 -0
- infrahub/graphql/mutations/generator.py +25 -7
- infrahub/graphql/mutations/hfid.py +125 -0
- infrahub/graphql/mutations/ipam.py +54 -35
- infrahub/graphql/mutations/main.py +27 -28
- infrahub/graphql/mutations/relationship.py +2 -2
- infrahub/graphql/mutations/resource_manager.py +2 -2
- infrahub/graphql/mutations/schema.py +5 -5
- infrahub/graphql/queries/resource_manager.py +1 -1
- infrahub/graphql/resolvers/resolver.py +2 -0
- infrahub/graphql/schema.py +4 -0
- infrahub/graphql/schema_sort.py +170 -0
- infrahub/graphql/types/branch.py +4 -1
- infrahub/graphql/types/enums.py +3 -0
- infrahub/groups/tasks.py +1 -1
- infrahub/hfid/__init__.py +0 -0
- infrahub/hfid/gather.py +48 -0
- infrahub/hfid/models.py +240 -0
- infrahub/hfid/tasks.py +191 -0
- infrahub/hfid/triggers.py +22 -0
- infrahub/lock.py +67 -16
- infrahub/message_bus/types.py +2 -1
- infrahub/middleware.py +26 -1
- infrahub/permissions/constants.py +2 -0
- infrahub/proposed_change/tasks.py +35 -17
- infrahub/server.py +21 -4
- infrahub/services/__init__.py +8 -5
- 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/catalogue.py +4 -0
- infrahub/trigger/models.py +2 -0
- infrahub/trigger/setup.py +13 -4
- infrahub/trigger/tasks.py +6 -0
- infrahub/workers/dependencies.py +10 -1
- infrahub/workers/infrahub_async.py +10 -2
- infrahub/workflows/catalogue.py +80 -0
- infrahub/workflows/initialization.py +21 -0
- infrahub/workflows/utils.py +2 -1
- infrahub_sdk/checks.py +1 -1
- infrahub_sdk/client.py +13 -10
- infrahub_sdk/config.py +29 -2
- infrahub_sdk/ctl/cli_commands.py +2 -0
- infrahub_sdk/ctl/generator.py +4 -0
- infrahub_sdk/ctl/graphql.py +184 -0
- infrahub_sdk/ctl/schema.py +28 -9
- infrahub_sdk/generator.py +7 -1
- infrahub_sdk/graphql/__init__.py +12 -0
- infrahub_sdk/graphql/constants.py +1 -0
- infrahub_sdk/graphql/plugin.py +85 -0
- infrahub_sdk/graphql/query.py +77 -0
- infrahub_sdk/{graphql.py → graphql/renderers.py} +81 -73
- infrahub_sdk/graphql/utils.py +40 -0
- infrahub_sdk/protocols.py +14 -0
- infrahub_sdk/schema/__init__.py +70 -4
- infrahub_sdk/schema/repository.py +8 -0
- infrahub_sdk/spec/models.py +7 -0
- infrahub_sdk/spec/object.py +53 -44
- 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/spec/range_expansion.py +1 -1
- infrahub_sdk/transforms.py +1 -1
- {infrahub_server-1.5.0b0.dist-info → infrahub_server-1.5.0b2.dist-info}/METADATA +7 -4
- {infrahub_server-1.5.0b0.dist-info → infrahub_server-1.5.0b2.dist-info}/RECORD +182 -143
- infrahub_testcontainers/container.py +115 -3
- infrahub_testcontainers/docker-compose-cluster.test.yml +6 -1
- infrahub_testcontainers/docker-compose.test.yml +6 -1
- infrahub/core/migrations/graph/m040_profile_attrs_in_db.py +0 -166
- {infrahub_server-1.5.0b0.dist-info → infrahub_server-1.5.0b2.dist-info}/LICENSE.txt +0 -0
- {infrahub_server-1.5.0b0.dist-info → infrahub_server-1.5.0b2.dist-info}/WHEEL +0 -0
- {infrahub_server-1.5.0b0.dist-info → infrahub_server-1.5.0b2.dist-info}/entry_points.txt +0 -0
infrahub/core/schema/__init__.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import uuid
|
|
4
|
+
from enum import Enum
|
|
4
5
|
from typing import Any, TypeAlias
|
|
5
6
|
|
|
6
7
|
from infrahub_sdk.utils import deep_merge_dict
|
|
@@ -44,6 +45,21 @@ class SchemaExtension(HashableModel):
|
|
|
44
45
|
nodes: list[NodeExtensionSchema] = Field(default_factory=list)
|
|
45
46
|
|
|
46
47
|
|
|
48
|
+
class SchemaWarningType(Enum):
|
|
49
|
+
DEPRECATION = "deprecation"
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class SchemaWarningKind(BaseModel):
|
|
53
|
+
kind: str = Field(..., description="The kind impacted by the warning")
|
|
54
|
+
field: str | None = Field(default=None, description="The attribute or relationship impacted by the warning")
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class SchemaWarning(BaseModel):
|
|
58
|
+
type: SchemaWarningType = Field(..., description="The type of warning")
|
|
59
|
+
kinds: list[SchemaWarningKind] = Field(default_factory=list, description="The kinds impacted by the warning")
|
|
60
|
+
message: str = Field(..., description="The message that describes the warning")
|
|
61
|
+
|
|
62
|
+
|
|
47
63
|
class SchemaRoot(BaseModel):
|
|
48
64
|
model_config = ConfigDict(extra="forbid")
|
|
49
65
|
|
|
@@ -80,6 +96,46 @@ class SchemaRoot(BaseModel):
|
|
|
80
96
|
|
|
81
97
|
return errors
|
|
82
98
|
|
|
99
|
+
def gather_warnings(self) -> list[SchemaWarning]:
|
|
100
|
+
models = self.nodes + self.generics
|
|
101
|
+
warnings: list[SchemaWarning] = []
|
|
102
|
+
for model in models:
|
|
103
|
+
if model.display_labels is not None:
|
|
104
|
+
warnings.append(
|
|
105
|
+
SchemaWarning(
|
|
106
|
+
type=SchemaWarningType.DEPRECATION,
|
|
107
|
+
kinds=[SchemaWarningKind(kind=model.kind)],
|
|
108
|
+
message="display_labels are deprecated, use display_label instead",
|
|
109
|
+
)
|
|
110
|
+
)
|
|
111
|
+
if model.default_filter is not None:
|
|
112
|
+
warnings.append(
|
|
113
|
+
SchemaWarning(
|
|
114
|
+
type=SchemaWarningType.DEPRECATION,
|
|
115
|
+
kinds=[SchemaWarningKind(kind=model.kind)],
|
|
116
|
+
message="default_filter is deprecated",
|
|
117
|
+
)
|
|
118
|
+
)
|
|
119
|
+
for attribute in model.attributes:
|
|
120
|
+
if attribute.max_length is not None:
|
|
121
|
+
warnings.append(
|
|
122
|
+
SchemaWarning(
|
|
123
|
+
type=SchemaWarningType.DEPRECATION,
|
|
124
|
+
kinds=[SchemaWarningKind(kind=model.kind, field=attribute.name)],
|
|
125
|
+
message="Use of 'max_length' on attributes is deprecated, use parameters instead",
|
|
126
|
+
)
|
|
127
|
+
)
|
|
128
|
+
if attribute.min_length is not None:
|
|
129
|
+
warnings.append(
|
|
130
|
+
SchemaWarning(
|
|
131
|
+
type=SchemaWarningType.DEPRECATION,
|
|
132
|
+
kinds=[SchemaWarningKind(kind=model.kind, field=attribute.name)],
|
|
133
|
+
message="Use of 'min_length' on attributes is deprecated, use parameters instead",
|
|
134
|
+
)
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
return warnings
|
|
138
|
+
|
|
83
139
|
def generate_uuid(self) -> None:
|
|
84
140
|
"""Generate UUID for all nodes, attributes & relationships
|
|
85
141
|
Mainly useful during unit tests."""
|
|
@@ -68,6 +68,10 @@ class AttributeSchema(GeneratedAttributeSchema):
|
|
|
68
68
|
def is_deprecated(self) -> bool:
|
|
69
69
|
return bool(self.deprecation)
|
|
70
70
|
|
|
71
|
+
@property
|
|
72
|
+
def support_profiles(self) -> bool:
|
|
73
|
+
return self.read_only is False and self.optional is True
|
|
74
|
+
|
|
71
75
|
def get_id(self) -> str:
|
|
72
76
|
if self.id is None:
|
|
73
77
|
raise InitializationError("The attribute schema has not been saved yet and doesn't have an id")
|
|
@@ -9,7 +9,7 @@ from enum import Enum
|
|
|
9
9
|
from typing import TYPE_CHECKING, Any, Callable, Iterable, Literal, overload
|
|
10
10
|
|
|
11
11
|
from infrahub_sdk.utils import compare_lists, intersection
|
|
12
|
-
from pydantic import field_validator
|
|
12
|
+
from pydantic import ConfigDict, field_validator
|
|
13
13
|
|
|
14
14
|
from infrahub.core.constants import HashableModelState, RelationshipCardinality, RelationshipKind
|
|
15
15
|
from infrahub.core.models import HashableModel, HashableModelDiff
|
|
@@ -19,6 +19,7 @@ from .generated.base_node_schema import GeneratedBaseNodeSchema
|
|
|
19
19
|
from .relationship_schema import RelationshipSchema
|
|
20
20
|
|
|
21
21
|
if TYPE_CHECKING:
|
|
22
|
+
from pydantic.config import JsonDict
|
|
22
23
|
from typing_extensions import Self
|
|
23
24
|
|
|
24
25
|
from infrahub.core.schema import GenericSchema, NodeSchema
|
|
@@ -26,6 +27,7 @@ if TYPE_CHECKING:
|
|
|
26
27
|
|
|
27
28
|
|
|
28
29
|
NODE_METADATA_ATTRIBUTES = ["_source", "_owner"]
|
|
30
|
+
NODE_PROPERTY_ATTRIBUTES = ["display_label", "human_friendly_id"]
|
|
29
31
|
INHERITED = "INHERITED"
|
|
30
32
|
|
|
31
33
|
OPTIONAL_TEXT_FIELDS = [
|
|
@@ -39,10 +41,43 @@ OPTIONAL_TEXT_FIELDS = [
|
|
|
39
41
|
]
|
|
40
42
|
|
|
41
43
|
|
|
44
|
+
def _json_schema_extra(schema: JsonDict) -> None:
|
|
45
|
+
"""
|
|
46
|
+
Mutate the generated JSON Schema in place to:
|
|
47
|
+
- allow `null` for `display_labels`
|
|
48
|
+
- mark the non-null branch as deprecated
|
|
49
|
+
"""
|
|
50
|
+
props = schema.get("properties")
|
|
51
|
+
if not isinstance(props, dict):
|
|
52
|
+
return
|
|
53
|
+
dl = props.get("display_labels")
|
|
54
|
+
if not isinstance(dl, dict):
|
|
55
|
+
return
|
|
56
|
+
|
|
57
|
+
if "anyOf" in dl:
|
|
58
|
+
dl["anyOf"] = [
|
|
59
|
+
{
|
|
60
|
+
"type": "array",
|
|
61
|
+
"items": {
|
|
62
|
+
"type": "string",
|
|
63
|
+
"deprecationMessage": "display_labels are deprecated use display_label instead",
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
{"type": "null"},
|
|
67
|
+
]
|
|
68
|
+
|
|
69
|
+
|
|
42
70
|
class BaseNodeSchema(GeneratedBaseNodeSchema):
|
|
43
71
|
_exclude_from_hash: list[str] = ["attributes", "relationships"]
|
|
44
72
|
_sort_by: list[str] = ["namespace", "name"]
|
|
45
73
|
|
|
74
|
+
model_config = ConfigDict(extra="forbid", json_schema_extra=_json_schema_extra)
|
|
75
|
+
|
|
76
|
+
@property
|
|
77
|
+
def is_schema_node(self) -> bool:
|
|
78
|
+
"""Tell if this node represent a part of the schema. Not to confuse this with `is_node_schema`."""
|
|
79
|
+
return self.namespace == "Schema"
|
|
80
|
+
|
|
46
81
|
@property
|
|
47
82
|
def is_node_schema(self) -> bool:
|
|
48
83
|
return False
|
|
@@ -240,6 +275,11 @@ class BaseNodeSchema(GeneratedBaseNodeSchema):
|
|
|
240
275
|
return None
|
|
241
276
|
|
|
242
277
|
def get_attribute(self, name: str) -> AttributeSchema:
|
|
278
|
+
if name == "human_friendly_id":
|
|
279
|
+
return AttributeSchema(name="human_friendly_id", kind="List", optional=True, branch=self.branch)
|
|
280
|
+
if name == "display_label":
|
|
281
|
+
return AttributeSchema(name="display_label", kind="Text", optional=True, branch=self.branch)
|
|
282
|
+
|
|
243
283
|
for item in self.attributes:
|
|
244
284
|
if item.name == name:
|
|
245
285
|
return item
|
|
@@ -329,7 +369,7 @@ class BaseNodeSchema(GeneratedBaseNodeSchema):
|
|
|
329
369
|
|
|
330
370
|
@property
|
|
331
371
|
def valid_input_names(self) -> list[str]:
|
|
332
|
-
return self.attribute_names + self.relationship_names + NODE_METADATA_ATTRIBUTES
|
|
372
|
+
return self.attribute_names + self.relationship_names + NODE_METADATA_ATTRIBUTES + NODE_PROPERTY_ATTRIBUTES
|
|
333
373
|
|
|
334
374
|
@property
|
|
335
375
|
def valid_local_names(self) -> list[str]:
|
|
@@ -29,6 +29,7 @@ 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
31
|
from .group import (
|
|
32
|
+
core_generator_aware_group,
|
|
32
33
|
core_generator_group,
|
|
33
34
|
core_graphql_query_group,
|
|
34
35
|
core_group,
|
|
@@ -128,6 +129,7 @@ core_models_mixed: dict[str, list] = {
|
|
|
128
129
|
core_group_action,
|
|
129
130
|
core_standard_group,
|
|
130
131
|
core_generator_group,
|
|
132
|
+
core_generator_aware_group,
|
|
131
133
|
core_graphql_query_group,
|
|
132
134
|
core_repository_group,
|
|
133
135
|
builtin_tag,
|
|
@@ -33,6 +33,8 @@ core_generator_definition = NodeSchema(
|
|
|
33
33
|
Attr(name="file_path", kind="Text"),
|
|
34
34
|
Attr(name="class_name", kind="Text"),
|
|
35
35
|
Attr(name="convert_query_response", kind="Boolean", optional=True, default_value=False),
|
|
36
|
+
Attr(name="execute_in_proposed_change", kind="Boolean", optional=True, default_value=True),
|
|
37
|
+
Attr(name="execute_after_merge", kind="Boolean", optional=True, default_value=True),
|
|
36
38
|
],
|
|
37
39
|
relationships=[
|
|
38
40
|
Rel(
|
|
@@ -70,10 +70,10 @@ core_standard_group = NodeSchema(
|
|
|
70
70
|
core_generator_group = NodeSchema(
|
|
71
71
|
name="GeneratorGroup",
|
|
72
72
|
namespace="Core",
|
|
73
|
-
description="Group of nodes that are created by a generator.",
|
|
73
|
+
description="Group of nodes that are created by a generator. (local)",
|
|
74
74
|
include_in_menu=False,
|
|
75
75
|
icon="mdi:state-machine",
|
|
76
|
-
label="Generator Group",
|
|
76
|
+
label="Generator Group (local)",
|
|
77
77
|
default_filter="name__value",
|
|
78
78
|
order_by=["name__value"],
|
|
79
79
|
display_labels=["name__value"],
|
|
@@ -82,6 +82,20 @@ core_generator_group = NodeSchema(
|
|
|
82
82
|
generate_profile=False,
|
|
83
83
|
)
|
|
84
84
|
|
|
85
|
+
core_generator_aware_group = NodeSchema(
|
|
86
|
+
name="GeneratorAwareGroup",
|
|
87
|
+
namespace="Core",
|
|
88
|
+
description="Group of nodes that are created by a generator. (Aware)",
|
|
89
|
+
include_in_menu=False,
|
|
90
|
+
icon="mdi:state-machine",
|
|
91
|
+
label="Generator Group (aware)",
|
|
92
|
+
default_filter="name__value",
|
|
93
|
+
order_by=["name__value"],
|
|
94
|
+
display_labels=["name__value"],
|
|
95
|
+
branch=BranchSupportType.AWARE,
|
|
96
|
+
inherit_from=[InfrahubKind.GENERICGROUP],
|
|
97
|
+
generate_profile=False,
|
|
98
|
+
)
|
|
85
99
|
|
|
86
100
|
core_graphql_query_group = NodeSchema(
|
|
87
101
|
name="GraphQLQueryGroup",
|
|
@@ -179,7 +179,9 @@ class SchemaNode(BaseModel):
|
|
|
179
179
|
default_filter: str | None = None
|
|
180
180
|
attributes: list[SchemaAttribute]
|
|
181
181
|
relationships: list[SchemaRelationship]
|
|
182
|
+
display_label: str | None = None
|
|
182
183
|
display_labels: list[str]
|
|
184
|
+
uniqueness_constraints: list[list[str]] | None = None
|
|
183
185
|
|
|
184
186
|
def to_dict(self) -> dict[str, Any]:
|
|
185
187
|
return {
|
|
@@ -194,7 +196,9 @@ class SchemaNode(BaseModel):
|
|
|
194
196
|
if attribute.name not in ["id", "attributes", "relationships"]
|
|
195
197
|
],
|
|
196
198
|
"relationships": [relationship.to_dict() for relationship in self.relationships],
|
|
199
|
+
"display_label": self.display_label,
|
|
197
200
|
"display_labels": self.display_labels,
|
|
201
|
+
"uniqueness_constraints": self.uniqueness_constraints,
|
|
198
202
|
}
|
|
199
203
|
|
|
200
204
|
def without_duplicates(self, other: SchemaNode) -> SchemaNode:
|
|
@@ -292,11 +296,18 @@ base_node_schema = SchemaNode(
|
|
|
292
296
|
optional=True,
|
|
293
297
|
extra={"update": UpdateSupport.ALLOWED},
|
|
294
298
|
),
|
|
299
|
+
SchemaAttribute(
|
|
300
|
+
name="display_label",
|
|
301
|
+
kind="Text",
|
|
302
|
+
description="Attribute or Jinja2 template to use to generate the display label",
|
|
303
|
+
optional=True,
|
|
304
|
+
extra={"update": UpdateSupport.ALLOWED},
|
|
305
|
+
),
|
|
295
306
|
SchemaAttribute(
|
|
296
307
|
name="display_labels",
|
|
297
308
|
kind="List",
|
|
298
309
|
internal_kind=str,
|
|
299
|
-
description="List of attributes to use to generate the display label",
|
|
310
|
+
description="List of attributes to use to generate the display label (deprecated)",
|
|
300
311
|
optional=True,
|
|
301
312
|
extra={"update": UpdateSupport.ALLOWED},
|
|
302
313
|
),
|
|
@@ -465,6 +476,7 @@ attribute_schema = SchemaNode(
|
|
|
465
476
|
include_in_menu=False,
|
|
466
477
|
default_filter=None,
|
|
467
478
|
display_labels=["name__value"],
|
|
479
|
+
uniqueness_constraints=[["name__value", "node"]],
|
|
468
480
|
attributes=[
|
|
469
481
|
SchemaAttribute(
|
|
470
482
|
name="id",
|
|
@@ -556,7 +568,7 @@ attribute_schema = SchemaNode(
|
|
|
556
568
|
"Mainly relevant for internal object.",
|
|
557
569
|
default_value=False,
|
|
558
570
|
optional=True,
|
|
559
|
-
extra={"update": UpdateSupport.
|
|
571
|
+
extra={"update": UpdateSupport.MIGRATION_REQUIRED},
|
|
560
572
|
),
|
|
561
573
|
SchemaAttribute(
|
|
562
574
|
name="unique",
|
|
@@ -573,7 +585,7 @@ attribute_schema = SchemaNode(
|
|
|
573
585
|
default_value=False,
|
|
574
586
|
override_default_value=False,
|
|
575
587
|
optional=True,
|
|
576
|
-
extra={"update": UpdateSupport.
|
|
588
|
+
extra={"update": UpdateSupport.MIGRATION_REQUIRED},
|
|
577
589
|
),
|
|
578
590
|
SchemaAttribute(
|
|
579
591
|
name="branch",
|
|
@@ -669,6 +681,7 @@ relationship_schema = SchemaNode(
|
|
|
669
681
|
include_in_menu=False,
|
|
670
682
|
default_filter=None,
|
|
671
683
|
display_labels=["name__value"],
|
|
684
|
+
uniqueness_constraints=[["name__value", "node"]],
|
|
672
685
|
attributes=[
|
|
673
686
|
SchemaAttribute(
|
|
674
687
|
name="id",
|
|
@@ -78,7 +78,7 @@ class GeneratedAttributeSchema(HashableModel):
|
|
|
78
78
|
read_only: bool = Field(
|
|
79
79
|
default=False,
|
|
80
80
|
description="Set the attribute as Read-Only, users won't be able to change its value. Mainly relevant for internal object.",
|
|
81
|
-
json_schema_extra={"update": "
|
|
81
|
+
json_schema_extra={"update": "migration_required"},
|
|
82
82
|
)
|
|
83
83
|
unique: bool = Field(
|
|
84
84
|
default=False,
|
|
@@ -88,7 +88,7 @@ class GeneratedAttributeSchema(HashableModel):
|
|
|
88
88
|
optional: bool = Field(
|
|
89
89
|
default=False,
|
|
90
90
|
description="Indicate if this attribute is mandatory or optional.",
|
|
91
|
-
json_schema_extra={"update": "
|
|
91
|
+
json_schema_extra={"update": "migration_required"},
|
|
92
92
|
)
|
|
93
93
|
branch: BranchSupportType | None = Field(
|
|
94
94
|
default=None,
|
|
@@ -58,9 +58,14 @@ class GeneratedBaseNodeSchema(HashableModel):
|
|
|
58
58
|
description="Human friendly and unique identifier for the object.",
|
|
59
59
|
json_schema_extra={"update": "allowed"},
|
|
60
60
|
)
|
|
61
|
+
display_label: str | None = Field(
|
|
62
|
+
default=None,
|
|
63
|
+
description="Attribute or Jinja2 template to use to generate the display label",
|
|
64
|
+
json_schema_extra={"update": "allowed"},
|
|
65
|
+
)
|
|
61
66
|
display_labels: list[str] | None = Field(
|
|
62
67
|
default=None,
|
|
63
|
-
description="List of attributes to use to generate the display label",
|
|
68
|
+
description="List of attributes to use to generate the display label (deprecated)",
|
|
64
69
|
json_schema_extra={"update": "allowed"},
|
|
65
70
|
)
|
|
66
71
|
include_in_menu: bool | None = Field(
|
infrahub/core/schema/manager.py
CHANGED
|
@@ -2,6 +2,9 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from typing import TYPE_CHECKING, Any
|
|
4
4
|
|
|
5
|
+
from cachetools import LRUCache
|
|
6
|
+
from infrahub_sdk.schema import BranchSchema as SDKBranchSchema
|
|
7
|
+
|
|
5
8
|
from infrahub import lock
|
|
6
9
|
from infrahub.core.manager import NodeManager
|
|
7
10
|
from infrahub.core.models import (
|
|
@@ -40,6 +43,8 @@ class SchemaManager(NodeManager):
|
|
|
40
43
|
def __init__(self) -> None:
|
|
41
44
|
self._cache: dict[int, Any] = {}
|
|
42
45
|
self._branches: dict[str, SchemaBranch] = {}
|
|
46
|
+
self._branch_hash_by_name: dict[str, str] = {}
|
|
47
|
+
self._sdk_branches: LRUCache[str, SDKBranchSchema] = LRUCache(maxsize=10)
|
|
43
48
|
|
|
44
49
|
def _get_from_cache(self, key: int) -> Any:
|
|
45
50
|
return self._cache[key]
|
|
@@ -140,12 +145,26 @@ class SchemaManager(NodeManager):
|
|
|
140
145
|
if name in self._branches:
|
|
141
146
|
return self._branches[name]
|
|
142
147
|
|
|
143
|
-
self.
|
|
148
|
+
self.set_schema_branch(name, schema=SchemaBranch(cache=self._cache, name=name))
|
|
144
149
|
return self._branches[name]
|
|
145
150
|
|
|
151
|
+
def get_sdk_schema_branch(self, name: str) -> SDKBranchSchema:
|
|
152
|
+
schema_hash = self._branch_hash_by_name[name]
|
|
153
|
+
branch_schema = self._sdk_branches.get(schema_hash)
|
|
154
|
+
if not branch_schema:
|
|
155
|
+
self._sdk_branches[schema_hash] = SDKBranchSchema.from_api_response(
|
|
156
|
+
data=self._branches[name].to_dict_api_schema_object()
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
return self._sdk_branches[schema_hash]
|
|
160
|
+
|
|
146
161
|
def set_schema_branch(self, name: str, schema: SchemaBranch) -> None:
|
|
147
162
|
schema.name = name
|
|
148
163
|
self._branches[name] = schema
|
|
164
|
+
self._branch_hash_by_name[name] = schema.get_hash()
|
|
165
|
+
|
|
166
|
+
def has_schema_branch(self, name: str) -> bool:
|
|
167
|
+
return name in self._branches
|
|
149
168
|
|
|
150
169
|
def process_schema_branch(self, name: str) -> None:
|
|
151
170
|
schema_branch = self.get_schema_branch(name=name)
|
|
@@ -764,6 +783,8 @@ class SchemaManager(NodeManager):
|
|
|
764
783
|
for branch_name in list(self._branches.keys()):
|
|
765
784
|
if branch_name not in active_branches:
|
|
766
785
|
del self._branches[branch_name]
|
|
786
|
+
if branch_name in self._branch_hash_by_name:
|
|
787
|
+
del self._branch_hash_by_name[branch_name]
|
|
767
788
|
removed_branches.append(branch_name)
|
|
768
789
|
|
|
769
790
|
for hash_key in list(self._cache.keys()):
|
|
@@ -90,6 +90,7 @@ class NodeSchema(GeneratedNodeSchema):
|
|
|
90
90
|
|
|
91
91
|
properties_to_inherit = [
|
|
92
92
|
"human_friendly_id",
|
|
93
|
+
"display_label",
|
|
93
94
|
"display_labels",
|
|
94
95
|
"default_filter",
|
|
95
96
|
"menu_placement",
|
|
@@ -129,10 +130,12 @@ class NodeSchema(GeneratedNodeSchema):
|
|
|
129
130
|
item_idx = existing_inherited_relationships[relationship.name]
|
|
130
131
|
self.relationships[item_idx].update_from_generic(other=new_relationship)
|
|
131
132
|
|
|
132
|
-
def get_hierarchy_schema(
|
|
133
|
+
def get_hierarchy_schema(
|
|
134
|
+
self, db: InfrahubDatabase, branch: Branch | str | None = None, duplicate: bool = False
|
|
135
|
+
) -> GenericSchema:
|
|
133
136
|
if not self.hierarchy:
|
|
134
137
|
raise ValueError("The node is not part of a hierarchy")
|
|
135
|
-
schema = db.schema.get(name=self.hierarchy, branch=branch)
|
|
138
|
+
schema = db.schema.get(name=self.hierarchy, branch=branch, duplicate=duplicate)
|
|
136
139
|
if not isinstance(schema, GenericSchema):
|
|
137
140
|
raise TypeError
|
|
138
141
|
return schema
|