infrahub-server 1.6.0b0__py3-none-any.whl → 1.6.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/oauth2.py +33 -6
- infrahub/api/oidc.py +36 -6
- infrahub/auth.py +11 -0
- infrahub/auth_pkce.py +41 -0
- infrahub/config.py +9 -3
- infrahub/core/branch/models.py +3 -2
- infrahub/core/changelog/models.py +2 -2
- infrahub/core/constants/__init__.py +1 -0
- infrahub/core/graph/__init__.py +1 -1
- infrahub/core/integrity/object_conflict/conflict_recorder.py +1 -1
- infrahub/core/manager.py +36 -31
- infrahub/core/migrations/graph/__init__.py +2 -0
- infrahub/core/migrations/graph/m047_backfill_or_null_display_label.py +606 -0
- infrahub/core/models.py +5 -6
- infrahub/core/node/__init__.py +16 -13
- infrahub/core/node/create.py +36 -8
- infrahub/core/node/proposed_change.py +5 -3
- infrahub/core/node/standard.py +1 -1
- infrahub/core/protocols.py +1 -7
- infrahub/core/query/attribute.py +1 -1
- infrahub/core/query/node.py +9 -5
- infrahub/core/relationship/model.py +21 -4
- infrahub/core/schema/generic_schema.py +1 -1
- infrahub/core/schema/manager.py +8 -3
- infrahub/core/schema/schema_branch.py +35 -16
- infrahub/core/validators/attribute/choices.py +2 -2
- infrahub/core/validators/determiner.py +3 -6
- infrahub/database/__init__.py +1 -1
- infrahub/git/base.py +2 -3
- infrahub/git/models.py +13 -0
- infrahub/git/tasks.py +23 -19
- infrahub/git/utils.py +16 -9
- infrahub/graphql/app.py +6 -6
- infrahub/graphql/loaders/peers.py +6 -0
- infrahub/graphql/mutations/action.py +15 -7
- infrahub/graphql/mutations/hfid.py +1 -1
- infrahub/graphql/mutations/profile.py +8 -1
- infrahub/graphql/mutations/repository.py +3 -3
- infrahub/graphql/mutations/schema.py +4 -4
- infrahub/graphql/mutations/webhook.py +2 -2
- infrahub/graphql/queries/resource_manager.py +2 -3
- infrahub/graphql/queries/search.py +2 -3
- infrahub/graphql/resolvers/ipam.py +20 -0
- infrahub/graphql/resolvers/many_relationship.py +12 -11
- infrahub/graphql/resolvers/resolver.py +6 -2
- infrahub/graphql/resolvers/single_relationship.py +1 -11
- infrahub/log.py +1 -1
- infrahub/message_bus/messages/__init__.py +0 -12
- infrahub/profiles/node_applier.py +9 -0
- infrahub/proposed_change/branch_diff.py +1 -1
- infrahub/proposed_change/tasks.py +1 -1
- infrahub/repositories/create_repository.py +3 -3
- infrahub/task_manager/models.py +1 -1
- infrahub/task_manager/task.py +5 -5
- infrahub/trigger/setup.py +6 -9
- infrahub/utils.py +18 -0
- infrahub/validators/tasks.py +1 -1
- infrahub/workers/infrahub_async.py +7 -6
- infrahub_sdk/client.py +113 -1
- infrahub_sdk/ctl/AGENTS.md +67 -0
- infrahub_sdk/ctl/branch.py +175 -1
- infrahub_sdk/ctl/check.py +3 -3
- infrahub_sdk/ctl/cli_commands.py +9 -9
- infrahub_sdk/ctl/generator.py +2 -2
- infrahub_sdk/ctl/graphql.py +1 -2
- infrahub_sdk/ctl/importer.py +1 -2
- infrahub_sdk/ctl/repository.py +6 -49
- infrahub_sdk/ctl/task.py +2 -4
- infrahub_sdk/ctl/utils.py +2 -2
- infrahub_sdk/ctl/validate.py +1 -2
- infrahub_sdk/diff.py +80 -3
- infrahub_sdk/graphql/constants.py +14 -1
- infrahub_sdk/graphql/renderers.py +5 -1
- infrahub_sdk/node/attribute.py +0 -1
- infrahub_sdk/node/constants.py +3 -1
- infrahub_sdk/node/node.py +303 -3
- infrahub_sdk/node/related_node.py +1 -2
- infrahub_sdk/node/relationship.py +1 -2
- infrahub_sdk/protocols_base.py +0 -1
- infrahub_sdk/pytest_plugin/AGENTS.md +67 -0
- infrahub_sdk/schema/__init__.py +0 -3
- infrahub_sdk/timestamp.py +7 -7
- {infrahub_server-1.6.0b0.dist-info → infrahub_server-1.6.1.dist-info}/METADATA +2 -3
- {infrahub_server-1.6.0b0.dist-info → infrahub_server-1.6.1.dist-info}/RECORD +88 -84
- {infrahub_server-1.6.0b0.dist-info → infrahub_server-1.6.1.dist-info}/WHEEL +1 -1
- infrahub_testcontainers/container.py +2 -2
- {infrahub_server-1.6.0b0.dist-info → infrahub_server-1.6.1.dist-info}/entry_points.txt +0 -0
- {infrahub_server-1.6.0b0.dist-info → infrahub_server-1.6.1.dist-info}/licenses/LICENSE.txt +0 -0
infrahub/core/node/__init__.py
CHANGED
|
@@ -170,7 +170,7 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
|
|
|
170
170
|
return self._human_friendly_id is not None
|
|
171
171
|
|
|
172
172
|
async def add_human_friendly_id(self, db: InfrahubDatabase) -> None:
|
|
173
|
-
if
|
|
173
|
+
if self._human_friendly_id:
|
|
174
174
|
return
|
|
175
175
|
|
|
176
176
|
self._human_friendly_id = HumanFriendlyIdentifier(
|
|
@@ -179,11 +179,8 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
|
|
|
179
179
|
await self._human_friendly_id.compute(db=db, node=self)
|
|
180
180
|
|
|
181
181
|
async def get_display_label(self, db: InfrahubDatabase) -> str:
|
|
182
|
-
if self._display_label:
|
|
183
|
-
|
|
184
|
-
return self._display_label._value
|
|
185
|
-
if self._display_label._value:
|
|
186
|
-
return self._display_label._value.value
|
|
182
|
+
if self._display_label and (value := self._display_label.get_value(node=self, at=self._at)):
|
|
183
|
+
return value
|
|
187
184
|
|
|
188
185
|
return await self.render_display_label(db=db)
|
|
189
186
|
|
|
@@ -191,7 +188,7 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
|
|
|
191
188
|
return self._display_label is not None
|
|
192
189
|
|
|
193
190
|
async def add_display_label(self, db: InfrahubDatabase) -> None:
|
|
194
|
-
if
|
|
191
|
+
if self._display_label:
|
|
195
192
|
return
|
|
196
193
|
|
|
197
194
|
self._display_label = DisplayLabel(node_schema=self._schema, template=self._schema.display_label)
|
|
@@ -467,15 +464,21 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
|
|
|
467
464
|
for attribute_name in template._attributes:
|
|
468
465
|
if attribute_name in list(fields) + [OBJECT_TEMPLATE_NAME_ATTR]:
|
|
469
466
|
continue
|
|
470
|
-
|
|
467
|
+
attr = getattr(template, attribute_name)
|
|
468
|
+
attr_value = attr.value
|
|
471
469
|
if attr_value is not None:
|
|
472
|
-
|
|
470
|
+
# Preserve is_from_profile flag when copying from template
|
|
471
|
+
field_data = {"value": attr_value, "source": attr.source_id or template.id}
|
|
472
|
+
if attr.is_from_profile:
|
|
473
|
+
field_data["is_from_profile"] = True
|
|
474
|
+
fields[attribute_name] = field_data
|
|
473
475
|
|
|
474
476
|
for relationship_name in template._relationships:
|
|
475
477
|
relationship_schema = template._schema.get_relationship(name=relationship_name)
|
|
476
478
|
if (
|
|
477
479
|
relationship_name in list(fields)
|
|
478
|
-
or relationship_schema.kind
|
|
480
|
+
or relationship_schema.kind
|
|
481
|
+
not in [RelationshipKind.ATTRIBUTE, RelationshipKind.GENERIC, RelationshipKind.PROFILE]
|
|
479
482
|
or relationship_name == OBJECT_TEMPLATE_RELATIONSHIP_NAME
|
|
480
483
|
):
|
|
481
484
|
continue
|
|
@@ -570,7 +573,7 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
|
|
|
570
573
|
self,
|
|
571
574
|
rel_schema.name,
|
|
572
575
|
await generator_method(
|
|
573
|
-
db=db, name=rel_schema.name, schema=rel_schema, data=fields.get(rel_schema.name
|
|
576
|
+
db=db, name=rel_schema.name, schema=rel_schema, data=fields.get(rel_schema.name)
|
|
574
577
|
),
|
|
575
578
|
)
|
|
576
579
|
except ValidationError as exc:
|
|
@@ -600,7 +603,7 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
|
|
|
600
603
|
self,
|
|
601
604
|
attr_schema.name,
|
|
602
605
|
await generator_method(
|
|
603
|
-
db=db, name=attr_schema.name, schema=attr_schema, data=fields.get(attr_schema.name
|
|
606
|
+
db=db, name=attr_schema.name, schema=attr_schema, data=fields.get(attr_schema.name)
|
|
604
607
|
),
|
|
605
608
|
)
|
|
606
609
|
if not self._existing:
|
|
@@ -1088,7 +1091,7 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
|
|
|
1088
1091
|
|
|
1089
1092
|
if key in self._relationships:
|
|
1090
1093
|
rel: RelationshipManager = getattr(self, key)
|
|
1091
|
-
changed |= await rel.update(db=db, data=value)
|
|
1094
|
+
changed |= await rel.update(db=db, data=value, process_delete=process_pools)
|
|
1092
1095
|
|
|
1093
1096
|
return changed
|
|
1094
1097
|
|
infrahub/core/node/create.py
CHANGED
|
@@ -58,18 +58,34 @@ async def extract_peer_data(
|
|
|
58
58
|
except ValueError:
|
|
59
59
|
pass
|
|
60
60
|
|
|
61
|
-
|
|
61
|
+
# If the template attribute comes from a profile, preserve the profile as the source
|
|
62
|
+
# Otherwise, use the template itself as the source
|
|
63
|
+
source_id = template_attr.source_id or template_peer.id
|
|
64
|
+
attr_data = {"value": template_attr.value, "source": source_id}
|
|
65
|
+
if template_attr.is_from_profile:
|
|
66
|
+
attr_data["is_from_profile"] = True
|
|
67
|
+
obj_peer_data[attr_name] = attr_data
|
|
62
68
|
|
|
63
69
|
for rel in template_peer.get_schema().relationship_names:
|
|
64
70
|
rel_manager: RelationshipManager = getattr(template_peer, rel)
|
|
65
|
-
|
|
66
|
-
|
|
71
|
+
if (
|
|
72
|
+
rel_manager.schema.kind
|
|
73
|
+
not in [
|
|
74
|
+
RelationshipKind.COMPONENT,
|
|
75
|
+
RelationshipKind.PARENT,
|
|
76
|
+
RelationshipKind.PROFILE,
|
|
77
|
+
RelationshipKind.ATTRIBUTE,
|
|
78
|
+
]
|
|
79
|
+
or rel_manager.schema.name not in obj_peer_schema.relationship_names
|
|
80
|
+
):
|
|
67
81
|
continue
|
|
68
82
|
|
|
69
83
|
peers_map = await rel_manager.get_peers(db=db)
|
|
70
|
-
if rel_manager.schema.kind in [
|
|
71
|
-
|
|
72
|
-
|
|
84
|
+
if rel_manager.schema.kind in [
|
|
85
|
+
RelationshipKind.COMPONENT,
|
|
86
|
+
RelationshipKind.PARENT,
|
|
87
|
+
RelationshipKind.PROFILE,
|
|
88
|
+
] and list(peers_map.keys()) == [current_template.id]:
|
|
73
89
|
obj_peer_data[rel] = {"id": parent_obj.id}
|
|
74
90
|
continue
|
|
75
91
|
|
|
@@ -80,7 +96,13 @@ async def extract_peer_data(
|
|
|
80
96
|
continue
|
|
81
97
|
rel_peer_ids.append({"id": peer_id})
|
|
82
98
|
|
|
83
|
-
|
|
99
|
+
# Only set the relationship data if there are actual peers to set
|
|
100
|
+
if rel_peer_ids:
|
|
101
|
+
obj_peer_data[rel] = rel_peer_ids
|
|
102
|
+
|
|
103
|
+
if rel_manager.schema.kind == RelationshipKind.PROFILE:
|
|
104
|
+
profiles = list(await rel_manager.get_peers(db=db))
|
|
105
|
+
obj_peer_data[rel] = profiles
|
|
84
106
|
|
|
85
107
|
return obj_peer_data
|
|
86
108
|
|
|
@@ -125,6 +147,12 @@ async def handle_template_relationships(
|
|
|
125
147
|
await constraint_runner.check(node=obj_peer, field_filters=list(obj_peer_data))
|
|
126
148
|
await obj_peer.save(db=db)
|
|
127
149
|
|
|
150
|
+
template_profile_ids = await get_profile_ids(db=db, obj=template_relationship_peer)
|
|
151
|
+
if template_profile_ids:
|
|
152
|
+
node_profiles_applier = NodeProfilesApplier(db=db, branch=branch)
|
|
153
|
+
await node_profiles_applier.apply_profiles(node=obj_peer)
|
|
154
|
+
await obj_peer.save(db=db)
|
|
155
|
+
|
|
128
156
|
await handle_template_relationships(
|
|
129
157
|
db=db,
|
|
130
158
|
branch=branch,
|
|
@@ -136,7 +164,7 @@ async def handle_template_relationships(
|
|
|
136
164
|
)
|
|
137
165
|
|
|
138
166
|
|
|
139
|
-
async def get_profile_ids(db: InfrahubDatabase, obj: Node) -> set[str]:
|
|
167
|
+
async def get_profile_ids(db: InfrahubDatabase, obj: Node | CoreObjectTemplate) -> set[str]:
|
|
140
168
|
if not hasattr(obj, "profiles"):
|
|
141
169
|
return set()
|
|
142
170
|
profile_rels = await obj.profiles.get_relationships(db=db)
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
from typing import cast
|
|
1
|
+
from typing import TYPE_CHECKING, cast
|
|
2
2
|
|
|
3
3
|
from infrahub.core.constants.infrahubkind import THREADCOMMENT
|
|
4
4
|
from infrahub.core.manager import NodeManager
|
|
5
5
|
from infrahub.core.node import Node
|
|
6
|
-
from infrahub.core.protocols import CoreProposedChange as CoreProposedChangeProtocol
|
|
7
6
|
from infrahub.database import InfrahubDatabase
|
|
8
7
|
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from infrahub.core.protocols import CoreProposedChange as CoreProposedChangeProtocol
|
|
10
|
+
|
|
9
11
|
|
|
10
12
|
class CoreProposedChange(Node):
|
|
11
13
|
async def to_graphql(
|
|
@@ -29,7 +31,7 @@ class CoreProposedChange(Node):
|
|
|
29
31
|
if fields:
|
|
30
32
|
if "total_comments" in fields:
|
|
31
33
|
total_comments = 0
|
|
32
|
-
proposed_change = cast(CoreProposedChangeProtocol, self)
|
|
34
|
+
proposed_change = cast("CoreProposedChangeProtocol", self)
|
|
33
35
|
change_comments = await proposed_change.comments.get_relationships(db=db)
|
|
34
36
|
total_comments += len(change_comments)
|
|
35
37
|
|
infrahub/core/node/standard.py
CHANGED
infrahub/core/protocols.py
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
from typing import TYPE_CHECKING
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
6
|
|
|
7
7
|
from infrahub.core.protocols_base import CoreNode
|
|
8
8
|
|
|
@@ -16,22 +16,16 @@ if TYPE_CHECKING:
|
|
|
16
16
|
DateTime,
|
|
17
17
|
DateTimeOptional,
|
|
18
18
|
Dropdown,
|
|
19
|
-
DropdownOptional,
|
|
20
19
|
HashedPassword,
|
|
21
|
-
HashedPasswordOptional,
|
|
22
20
|
Integer,
|
|
23
21
|
IntegerOptional,
|
|
24
22
|
IPHost,
|
|
25
|
-
IPHostOptional,
|
|
26
23
|
IPNetwork,
|
|
27
|
-
IPNetworkOptional,
|
|
28
24
|
JSONAttribute,
|
|
29
25
|
JSONAttributeOptional,
|
|
30
|
-
ListAttribute,
|
|
31
26
|
ListAttributeOptional,
|
|
32
27
|
String,
|
|
33
28
|
StringOptional,
|
|
34
|
-
URLOptional,
|
|
35
29
|
)
|
|
36
30
|
from infrahub.core.relationship import RelationshipManager
|
|
37
31
|
|
infrahub/core/query/attribute.py
CHANGED
|
@@ -373,7 +373,7 @@ async def default_attribute_query_filter(
|
|
|
373
373
|
if property_name not in [v.value for v in NodeProperty]:
|
|
374
374
|
raise ValueError(f"filter {filter_name}: {filter_value}, {property_name} is not a valid property")
|
|
375
375
|
|
|
376
|
-
if property_attr
|
|
376
|
+
if property_attr != "id":
|
|
377
377
|
raise ValueError(f"filter {filter_name}: {filter_value}, {property_attr} is supported")
|
|
378
378
|
|
|
379
379
|
clean_filter_name = f"{property_name}_{property_attr}"
|
infrahub/core/query/node.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import contextlib
|
|
3
4
|
from collections import defaultdict
|
|
4
5
|
from copy import copy
|
|
5
6
|
from dataclasses import dataclass
|
|
@@ -12,6 +13,7 @@ from infrahub.core import registry
|
|
|
12
13
|
from infrahub.core.constants import (
|
|
13
14
|
GLOBAL_BRANCH_NAME,
|
|
14
15
|
PROFILE_NODE_RELATIONSHIP_IDENTIFIER,
|
|
16
|
+
PROFILE_TEMPLATE_RELATIONSHIP_IDENTIFIER,
|
|
15
17
|
AttributeDBNodeType,
|
|
16
18
|
RelationshipDirection,
|
|
17
19
|
RelationshipHierarchyDirection,
|
|
@@ -623,7 +625,8 @@ class NodeListGetAttributeQuery(Query):
|
|
|
623
625
|
|
|
624
626
|
async def query_init(self, db: InfrahubDatabase, **kwargs) -> None: # noqa: ARG002
|
|
625
627
|
self.params["ids"] = self.ids
|
|
626
|
-
self.params["
|
|
628
|
+
self.params["profile_node_relationship_name"] = PROFILE_NODE_RELATIONSHIP_IDENTIFIER
|
|
629
|
+
self.params["profile_template_relationship_name"] = PROFILE_TEMPLATE_RELATIONSHIP_IDENTIFIER
|
|
627
630
|
|
|
628
631
|
branch_filter, branch_params = self.branch.get_query_filter_path(
|
|
629
632
|
at=self.at, branch_agnostic=self.branch_agnostic
|
|
@@ -632,7 +635,10 @@ class NodeListGetAttributeQuery(Query):
|
|
|
632
635
|
|
|
633
636
|
query = """
|
|
634
637
|
MATCH (n:Node) WHERE n.uuid IN $ids
|
|
635
|
-
WITH n,
|
|
638
|
+
WITH n, (
|
|
639
|
+
exists((n)-[:IS_RELATED]-(:Relationship {name: $profile_node_relationship_name})) OR
|
|
640
|
+
exists((n)-[:IS_RELATED]-(:Relationship {name: $profile_template_relationship_name}))
|
|
641
|
+
) AS might_use_profile
|
|
636
642
|
MATCH (n)-[:HAS_ATTRIBUTE]-(a:Attribute)
|
|
637
643
|
"""
|
|
638
644
|
if self.fields:
|
|
@@ -1134,10 +1140,8 @@ class NodeGetListQuery(Query):
|
|
|
1134
1140
|
self._variables_to_track.append(variable)
|
|
1135
1141
|
|
|
1136
1142
|
def _untrack_variable(self, variable: str) -> None:
|
|
1137
|
-
|
|
1143
|
+
with contextlib.suppress(ValueError):
|
|
1138
1144
|
self._variables_to_track.remove(variable)
|
|
1139
|
-
except ValueError:
|
|
1140
|
-
...
|
|
1141
1145
|
|
|
1142
1146
|
def _get_tracked_variables(self) -> list[str]:
|
|
1143
1147
|
return self._variables_to_track
|
|
@@ -912,6 +912,8 @@ class RelationshipManager:
|
|
|
912
912
|
db: InfrahubDatabase,
|
|
913
913
|
peer_type: type[PeerType],
|
|
914
914
|
branch_agnostic: bool = ...,
|
|
915
|
+
include_source: bool = ...,
|
|
916
|
+
include_owner: bool = ...,
|
|
915
917
|
) -> Mapping[str, PeerType]: ...
|
|
916
918
|
|
|
917
919
|
@overload
|
|
@@ -920,6 +922,8 @@ class RelationshipManager:
|
|
|
920
922
|
db: InfrahubDatabase,
|
|
921
923
|
peer_type: None = None,
|
|
922
924
|
branch_agnostic: bool = ...,
|
|
925
|
+
include_source: bool = ...,
|
|
926
|
+
include_owner: bool = ...,
|
|
923
927
|
) -> Mapping[str, Node]: ...
|
|
924
928
|
|
|
925
929
|
async def get_peers(
|
|
@@ -927,11 +931,18 @@ class RelationshipManager:
|
|
|
927
931
|
db: InfrahubDatabase,
|
|
928
932
|
peer_type: type[PeerType] | None = None, # noqa: ARG002
|
|
929
933
|
branch_agnostic: bool = False,
|
|
934
|
+
include_source: bool = False,
|
|
935
|
+
include_owner: bool = False,
|
|
930
936
|
) -> Mapping[str, Node | PeerType]:
|
|
931
937
|
rels = await self.get_relationships(db=db, branch_agnostic=branch_agnostic)
|
|
932
938
|
peer_ids = [rel.peer_id for rel in rels if rel.peer_id]
|
|
933
939
|
nodes = await registry.manager.get_many(
|
|
934
|
-
db=db,
|
|
940
|
+
db=db,
|
|
941
|
+
ids=peer_ids,
|
|
942
|
+
branch=self.branch,
|
|
943
|
+
branch_agnostic=branch_agnostic,
|
|
944
|
+
include_source=include_source,
|
|
945
|
+
include_owner=include_owner,
|
|
935
946
|
)
|
|
936
947
|
return nodes
|
|
937
948
|
|
|
@@ -1061,7 +1072,12 @@ class RelationshipManager:
|
|
|
1061
1072
|
|
|
1062
1073
|
return self._relationships.as_list()
|
|
1063
1074
|
|
|
1064
|
-
async def update(
|
|
1075
|
+
async def update(
|
|
1076
|
+
self,
|
|
1077
|
+
data: list[str | Node] | dict[str, Any] | str | Node | None,
|
|
1078
|
+
db: InfrahubDatabase,
|
|
1079
|
+
process_delete: bool = True,
|
|
1080
|
+
) -> bool:
|
|
1065
1081
|
"""Replace and Update the list of relationships with this one."""
|
|
1066
1082
|
if not isinstance(data, list):
|
|
1067
1083
|
list_data: Sequence[str | Node | dict[str, Any] | None] = [data]
|
|
@@ -1087,8 +1103,9 @@ class RelationshipManager:
|
|
|
1087
1103
|
|
|
1088
1104
|
if item is None:
|
|
1089
1105
|
if previous_relationships:
|
|
1090
|
-
|
|
1091
|
-
|
|
1106
|
+
if process_delete:
|
|
1107
|
+
for rel in previous_relationships.values():
|
|
1108
|
+
await rel.delete(db=db)
|
|
1092
1109
|
changed = True
|
|
1093
1110
|
continue
|
|
1094
1111
|
|
|
@@ -51,4 +51,4 @@ class GenericSchema(GeneratedGenericSchema):
|
|
|
51
51
|
def _get_field_names_for_diff(self) -> list[str]:
|
|
52
52
|
"""Exclude used_by from the diff for generic nodes"""
|
|
53
53
|
fields = super()._get_field_names_for_diff()
|
|
54
|
-
return [field for field in fields if field
|
|
54
|
+
return [field for field in fields if field != "used_by"]
|
infrahub/core/schema/manager.py
CHANGED
|
@@ -774,10 +774,15 @@ class SchemaManager(NodeManager):
|
|
|
774
774
|
"""Return non active branches that were purged."""
|
|
775
775
|
|
|
776
776
|
hashes_to_keep: set[str] = set()
|
|
777
|
+
branch_processed: set[str] = set()
|
|
777
778
|
for active_branch in active_branches:
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
779
|
+
branch_hash = self._branch_hash_by_name.get(active_branch)
|
|
780
|
+
if not branch_hash or branch_hash not in branch_processed:
|
|
781
|
+
if branch_hash:
|
|
782
|
+
branch_processed.add(branch_hash)
|
|
783
|
+
if branch := self._branches.get(active_branch):
|
|
784
|
+
nodes = branch.get_all(include_internal=True, duplicate=False)
|
|
785
|
+
hashes_to_keep.update([node.get_hash() for node in nodes.values()])
|
|
781
786
|
|
|
782
787
|
removed_branches: list[str] = []
|
|
783
788
|
for branch_name in list(self._branches.keys()):
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import contextlib
|
|
3
4
|
import copy
|
|
4
5
|
import hashlib
|
|
5
6
|
import keyword
|
|
@@ -19,6 +20,7 @@ from infrahub.core.constants import (
|
|
|
19
20
|
OBJECT_TEMPLATE_NAME_ATTR,
|
|
20
21
|
OBJECT_TEMPLATE_RELATIONSHIP_NAME,
|
|
21
22
|
PROFILE_NODE_RELATIONSHIP_IDENTIFIER,
|
|
23
|
+
PROFILE_TEMPLATE_RELATIONSHIP_IDENTIFIER,
|
|
22
24
|
RESERVED_ATTR_GEN_NAMES,
|
|
23
25
|
RESERVED_ATTR_REL_NAMES,
|
|
24
26
|
RESTRICTED_NAMESPACES,
|
|
@@ -72,6 +74,16 @@ from .schema_branch_hfid import HFIDs
|
|
|
72
74
|
log = get_logger()
|
|
73
75
|
|
|
74
76
|
|
|
77
|
+
profiles_rel_settings: dict[str, Any] = {
|
|
78
|
+
"name": "profiles",
|
|
79
|
+
"identifier": PROFILE_NODE_RELATIONSHIP_IDENTIFIER,
|
|
80
|
+
"peer": InfrahubKind.PROFILE,
|
|
81
|
+
"kind": RelationshipKind.PROFILE,
|
|
82
|
+
"cardinality": RelationshipCardinality.MANY,
|
|
83
|
+
"branch": BranchSupportType.AWARE,
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
|
|
75
87
|
class SchemaBranch:
|
|
76
88
|
def __init__(
|
|
77
89
|
self,
|
|
@@ -334,10 +346,8 @@ class SchemaBranch:
|
|
|
334
346
|
)
|
|
335
347
|
|
|
336
348
|
schema: MainSchemaTypes | None = None
|
|
337
|
-
|
|
349
|
+
with contextlib.suppress(KeyError):
|
|
338
350
|
schema = self._cache[key]
|
|
339
|
-
except KeyError:
|
|
340
|
-
pass
|
|
341
351
|
|
|
342
352
|
if not schema:
|
|
343
353
|
raise ValueError(f"Schema {name!r} on branch {self.name} has incorrect hash: {key!r}")
|
|
@@ -1116,7 +1126,7 @@ class SchemaBranch:
|
|
|
1116
1126
|
) from None
|
|
1117
1127
|
|
|
1118
1128
|
for rel in node.relationships:
|
|
1119
|
-
if rel.peer
|
|
1129
|
+
if rel.peer == InfrahubKind.GENERICGROUP:
|
|
1120
1130
|
continue
|
|
1121
1131
|
if not self.has(rel.peer) or self.get(rel.peer, duplicate=False).state == HashableModelState.ABSENT:
|
|
1122
1132
|
raise ValueError(
|
|
@@ -2163,10 +2173,8 @@ class SchemaBranch:
|
|
|
2163
2173
|
or not node.generate_profile
|
|
2164
2174
|
or node.state == HashableModelState.ABSENT
|
|
2165
2175
|
):
|
|
2166
|
-
|
|
2176
|
+
with contextlib.suppress(SchemaNotFoundError):
|
|
2167
2177
|
self.delete(name=self._get_profile_kind(node_kind=node.kind))
|
|
2168
|
-
except SchemaNotFoundError:
|
|
2169
|
-
...
|
|
2170
2178
|
continue
|
|
2171
2179
|
|
|
2172
2180
|
profile = self.generate_profile_from_node(node=node)
|
|
@@ -2212,15 +2220,6 @@ class SchemaBranch:
|
|
|
2212
2220
|
):
|
|
2213
2221
|
continue
|
|
2214
2222
|
|
|
2215
|
-
profiles_rel_settings: dict[str, Any] = {
|
|
2216
|
-
"name": "profiles",
|
|
2217
|
-
"identifier": PROFILE_NODE_RELATIONSHIP_IDENTIFIER,
|
|
2218
|
-
"peer": InfrahubKind.PROFILE,
|
|
2219
|
-
"kind": RelationshipKind.PROFILE,
|
|
2220
|
-
"cardinality": RelationshipCardinality.MANY,
|
|
2221
|
-
"branch": BranchSupportType.AWARE,
|
|
2222
|
-
}
|
|
2223
|
-
|
|
2224
2223
|
# Add relationship between node and profile
|
|
2225
2224
|
if "profiles" not in node.relationship_names:
|
|
2226
2225
|
node_schema = self.get(name=node_name, duplicate=True)
|
|
@@ -2285,6 +2284,18 @@ class SchemaBranch:
|
|
|
2285
2284
|
)
|
|
2286
2285
|
],
|
|
2287
2286
|
)
|
|
2287
|
+
if f"Template{node.kind}" in self.all_names:
|
|
2288
|
+
template = self.get(name=f"Template{node.kind}", duplicate=False)
|
|
2289
|
+
profile.relationships.append(
|
|
2290
|
+
RelationshipSchema(
|
|
2291
|
+
name="related_templates",
|
|
2292
|
+
identifier=PROFILE_TEMPLATE_RELATIONSHIP_IDENTIFIER,
|
|
2293
|
+
peer=template.kind,
|
|
2294
|
+
kind=RelationshipKind.PROFILE,
|
|
2295
|
+
cardinality=RelationshipCardinality.MANY,
|
|
2296
|
+
branch=BranchSupportType.AWARE,
|
|
2297
|
+
)
|
|
2298
|
+
)
|
|
2288
2299
|
|
|
2289
2300
|
for node_attr in node.attributes:
|
|
2290
2301
|
if not node_attr.support_profiles:
|
|
@@ -2415,6 +2426,14 @@ class SchemaBranch:
|
|
|
2415
2426
|
template_schema.human_friendly_id = [parent_hfid] + template_schema.human_friendly_id
|
|
2416
2427
|
template_schema.uniqueness_constraints[0].append(relationship.name)
|
|
2417
2428
|
|
|
2429
|
+
if getattr(node, "generate_profile", False):
|
|
2430
|
+
if "profiles" not in [r.name for r in template_schema.relationships]:
|
|
2431
|
+
settings = dict(profiles_rel_settings)
|
|
2432
|
+
settings["identifier"] = PROFILE_TEMPLATE_RELATIONSHIP_IDENTIFIER
|
|
2433
|
+
template_schema.relationships.append(RelationshipSchema(**settings))
|
|
2434
|
+
|
|
2435
|
+
self.set(name=template_schema.kind, schema=template_schema)
|
|
2436
|
+
|
|
2418
2437
|
def generate_object_template_from_node(
|
|
2419
2438
|
self, node: NodeSchema | GenericSchema, need_templates: set[NodeSchema | GenericSchema]
|
|
2420
2439
|
) -> TemplateSchema | GenericSchema:
|
|
@@ -4,13 +4,13 @@ from typing import TYPE_CHECKING, Any, cast
|
|
|
4
4
|
|
|
5
5
|
from infrahub.core.constants import NULL_VALUE, PathType
|
|
6
6
|
from infrahub.core.path import DataPath, GroupedDataPaths
|
|
7
|
-
from infrahub.core.schema.generic_schema import GenericSchema
|
|
8
7
|
|
|
9
8
|
from ..interface import ConstraintCheckerInterface
|
|
10
9
|
from ..shared import AttributeSchemaValidatorQuery
|
|
11
10
|
|
|
12
11
|
if TYPE_CHECKING:
|
|
13
12
|
from infrahub.core.branch import Branch
|
|
13
|
+
from infrahub.core.schema.generic_schema import GenericSchema
|
|
14
14
|
from infrahub.database import InfrahubDatabase
|
|
15
15
|
|
|
16
16
|
from ..model import SchemaConstraintValidatorRequest
|
|
@@ -106,7 +106,7 @@ class AttributeChoicesChecker(ConstraintCheckerInterface):
|
|
|
106
106
|
# skip inheriting schemas that override the attribute being checked
|
|
107
107
|
excluded_kinds: list[str] = []
|
|
108
108
|
if request.node_schema.is_generic_schema:
|
|
109
|
-
request.node_schema = cast(GenericSchema, request.node_schema)
|
|
109
|
+
request.node_schema = cast("GenericSchema", request.node_schema)
|
|
110
110
|
for inheriting_kind in request.node_schema.used_by:
|
|
111
111
|
inheriting_schema = request.schema_branch.get_node(name=inheriting_kind, duplicate=False)
|
|
112
112
|
inheriting_schema_attribute = inheriting_schema.get_attribute(name=request.schema_path.field_name)
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import contextlib
|
|
1
2
|
from typing import TYPE_CHECKING, Any
|
|
2
3
|
|
|
3
4
|
from infrahub.core.constants import RelationshipKind, SchemaPathType
|
|
@@ -84,14 +85,10 @@ class ConstraintValidatorDeterminer:
|
|
|
84
85
|
constraints: list[SchemaUpdateConstraintInfo] = []
|
|
85
86
|
schemas = list(self.schema_branch.get_all(duplicate=False).values())
|
|
86
87
|
# added here to check their uniqueness constraints
|
|
87
|
-
|
|
88
|
+
with contextlib.suppress(SchemaNotFoundError):
|
|
88
89
|
schemas.append(self.schema_branch.get_node(name="SchemaAttribute", duplicate=False))
|
|
89
|
-
|
|
90
|
-
pass
|
|
91
|
-
try:
|
|
90
|
+
with contextlib.suppress(SchemaNotFoundError):
|
|
92
91
|
schemas.append(self.schema_branch.get_node(name="SchemaRelationship", duplicate=False))
|
|
93
|
-
except SchemaNotFoundError:
|
|
94
|
-
pass
|
|
95
92
|
for schema in schemas:
|
|
96
93
|
constraints.extend(await self._get_property_constraints_for_one_schema(schema=schema))
|
|
97
94
|
return constraints
|
infrahub/database/__init__.py
CHANGED
|
@@ -356,7 +356,7 @@ class InfrahubDatabase:
|
|
|
356
356
|
type
|
|
357
357
|
and type == QueryType.READ
|
|
358
358
|
and runtime not in [Neo4jRuntime.DEFAULT, Neo4jRuntime.UNDEFINED]
|
|
359
|
-
and not (self.is_transaction and runtime
|
|
359
|
+
and not (self.is_transaction and runtime == Neo4jRuntime.PARALLEL)
|
|
360
360
|
):
|
|
361
361
|
query = f"CYPHER runtime = {runtime.value}\n" + query
|
|
362
362
|
else:
|
infrahub/git/base.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import contextlib
|
|
3
4
|
import shutil
|
|
4
5
|
from abc import ABC, abstractmethod
|
|
5
6
|
from pathlib import Path
|
|
@@ -748,10 +749,8 @@ class InfrahubRepositoryBase(BaseModel, ABC):
|
|
|
748
749
|
for short_name, branch_data in branches.items():
|
|
749
750
|
branch = None
|
|
750
751
|
|
|
751
|
-
|
|
752
|
+
with contextlib.suppress(BranchNotFoundError):
|
|
752
753
|
branch = registry.get_branch_from_registry(branch=short_name)
|
|
753
|
-
except BranchNotFoundError:
|
|
754
|
-
...
|
|
755
754
|
|
|
756
755
|
branch_exists_import_sync_condition = branch and (
|
|
757
756
|
branch.name not in {registry.default_branch, self.default_branch}
|
infrahub/git/models.py
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
from pydantic import BaseModel, ConfigDict, Field
|
|
2
2
|
|
|
3
3
|
from infrahub.context import InfrahubContext
|
|
4
|
+
from infrahub.core.node import Node
|
|
5
|
+
from infrahub.core.protocols import CoreReadOnlyRepository, CoreRepository
|
|
4
6
|
from infrahub.message_bus.types import ProposedChangeBranchDiff
|
|
5
7
|
|
|
6
8
|
|
|
@@ -201,11 +203,22 @@ class RepositoryBranchInfo(BaseModel):
|
|
|
201
203
|
|
|
202
204
|
|
|
203
205
|
class RepositoryData(BaseModel):
|
|
206
|
+
model_config = ConfigDict(arbitrary_types_allowed=True)
|
|
207
|
+
|
|
204
208
|
repository_id: str = Field(..., description="Id of the repository")
|
|
205
209
|
repository_name: str = Field(..., description="Name of the repository")
|
|
210
|
+
repository: CoreRepository | CoreReadOnlyRepository | Node = Field(
|
|
211
|
+
..., description="InfrahubNode representing a Repository"
|
|
212
|
+
)
|
|
206
213
|
branches: dict[str, str] = Field(
|
|
207
214
|
...,
|
|
208
215
|
description="Dictionary with the name of the branch as the key and the active commit id as the value",
|
|
209
216
|
)
|
|
210
217
|
|
|
211
218
|
branch_info: dict[str, RepositoryBranchInfo] = Field(default_factory=dict)
|
|
219
|
+
|
|
220
|
+
def get_staging_branch(self) -> str | None:
|
|
221
|
+
for branch, info in self.branch_info.items():
|
|
222
|
+
if info.internal_status == "staging":
|
|
223
|
+
return branch
|
|
224
|
+
return None
|