infrahub-server 1.6.3__py3-none-any.whl → 1.7.0__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 +4 -2
- infrahub/api/exceptions.py +2 -2
- infrahub/api/schema.py +3 -1
- infrahub/artifacts/tasks.py +1 -0
- infrahub/auth.py +2 -2
- infrahub/cli/db.py +54 -28
- infrahub/computed_attribute/gather.py +3 -4
- infrahub/computed_attribute/tasks.py +23 -6
- infrahub/config.py +8 -0
- infrahub/constants/enums.py +12 -0
- infrahub/core/account.py +4 -4
- infrahub/core/attribute.py +106 -108
- infrahub/core/branch/models.py +44 -71
- infrahub/core/branch/tasks.py +5 -3
- infrahub/core/changelog/diff.py +1 -20
- infrahub/core/changelog/models.py +0 -7
- infrahub/core/constants/__init__.py +17 -0
- infrahub/core/constants/database.py +0 -1
- infrahub/core/constants/schema.py +0 -1
- infrahub/core/convert_object_type/repository_conversion.py +3 -4
- infrahub/core/diff/branch_differ.py +1 -1
- infrahub/core/diff/conflict_transferer.py +1 -1
- infrahub/core/diff/data_check_synchronizer.py +4 -3
- infrahub/core/diff/enricher/cardinality_one.py +2 -2
- infrahub/core/diff/enricher/hierarchy.py +1 -1
- infrahub/core/diff/enricher/labels.py +1 -1
- infrahub/core/diff/merger/merger.py +28 -2
- infrahub/core/diff/merger/serializer.py +3 -10
- infrahub/core/diff/model/diff.py +1 -1
- infrahub/core/diff/query/merge.py +376 -135
- infrahub/core/diff/repository/repository.py +3 -1
- infrahub/core/graph/__init__.py +1 -1
- infrahub/core/graph/constraints.py +3 -3
- infrahub/core/graph/schema.py +2 -12
- infrahub/core/ipam/reconciler.py +8 -6
- infrahub/core/ipam/utilization.py +8 -15
- infrahub/core/manager.py +133 -152
- infrahub/core/merge.py +1 -1
- infrahub/core/metadata/__init__.py +0 -0
- infrahub/core/metadata/interface.py +37 -0
- infrahub/core/metadata/model.py +31 -0
- infrahub/core/metadata/query/__init__.py +0 -0
- infrahub/core/metadata/query/node_metadata.py +301 -0
- infrahub/core/migrations/graph/__init__.py +4 -0
- infrahub/core/migrations/graph/m012_convert_account_generic.py +12 -12
- infrahub/core/migrations/graph/m013_convert_git_password_credential.py +7 -12
- infrahub/core/migrations/graph/m017_add_core_profile.py +5 -2
- infrahub/core/migrations/graph/m018_uniqueness_nulls.py +2 -1
- infrahub/core/migrations/graph/m019_restore_rels_to_time.py +0 -10
- infrahub/core/migrations/graph/m020_duplicate_edges.py +0 -8
- infrahub/core/migrations/graph/m025_uniqueness_nulls.py +2 -1
- infrahub/core/migrations/graph/m026_0000_prefix_fix.py +2 -1
- infrahub/core/migrations/graph/m029_duplicates_cleanup.py +0 -1
- infrahub/core/migrations/graph/m031_check_number_attributes.py +2 -2
- infrahub/core/migrations/graph/m038_redo_0000_prefix_fix.py +2 -1
- infrahub/core/migrations/graph/m041_deleted_dup_edges.py +1 -1
- infrahub/core/migrations/graph/m049_remove_is_visible_relationship.py +53 -0
- infrahub/core/migrations/graph/m050_backfill_vertex_metadata.py +168 -0
- infrahub/core/migrations/query/__init__.py +2 -2
- infrahub/core/migrations/query/attribute_add.py +17 -6
- infrahub/core/migrations/query/attribute_remove.py +19 -5
- infrahub/core/migrations/query/attribute_rename.py +21 -5
- infrahub/core/migrations/query/node_duplicate.py +19 -4
- infrahub/core/migrations/query/schema_attribute_update.py +1 -1
- infrahub/core/migrations/schema/attribute_kind_update.py +21 -2
- infrahub/core/migrations/schema/attribute_name_update.py +1 -1
- infrahub/core/migrations/schema/attribute_supports_profile.py +5 -3
- infrahub/core/migrations/schema/models.py +3 -0
- infrahub/core/migrations/schema/node_attribute_add.py +5 -2
- infrahub/core/migrations/schema/node_attribute_remove.py +1 -1
- infrahub/core/migrations/schema/node_kind_update.py +1 -1
- infrahub/core/migrations/schema/node_remove.py +24 -2
- infrahub/core/migrations/schema/tasks.py +4 -1
- infrahub/core/migrations/shared.py +13 -6
- infrahub/core/models.py +6 -6
- infrahub/core/node/__init__.py +157 -58
- infrahub/core/node/base.py +9 -5
- infrahub/core/node/create.py +7 -3
- infrahub/core/node/delete_validator.py +1 -1
- infrahub/core/node/standard.py +100 -14
- infrahub/core/order.py +30 -0
- infrahub/core/property.py +0 -1
- infrahub/core/protocols.py +1 -0
- infrahub/core/protocols_base.py +10 -2
- infrahub/core/query/__init__.py +5 -3
- infrahub/core/query/attribute.py +164 -49
- infrahub/core/query/branch.py +58 -70
- infrahub/core/query/delete.py +1 -1
- infrahub/core/query/diff.py +7 -7
- infrahub/core/query/ipam.py +104 -43
- infrahub/core/query/node.py +1072 -281
- infrahub/core/query/relationship.py +531 -325
- infrahub/core/query/resource_manager.py +107 -18
- infrahub/core/query/standard_node.py +25 -5
- infrahub/core/query/utils.py +2 -4
- infrahub/core/relationship/constraints/count.py +1 -1
- infrahub/core/relationship/constraints/peer_kind.py +1 -1
- infrahub/core/relationship/constraints/peer_parent.py +1 -1
- infrahub/core/relationship/constraints/peer_relatives.py +1 -1
- infrahub/core/relationship/constraints/profiles_kind.py +1 -1
- infrahub/core/relationship/constraints/profiles_removal.py +168 -0
- infrahub/core/relationship/model.py +293 -139
- infrahub/core/schema/attribute_schema.py +2 -2
- infrahub/core/schema/basenode_schema.py +3 -0
- infrahub/core/schema/definitions/core/__init__.py +8 -2
- infrahub/core/schema/definitions/core/account.py +10 -10
- infrahub/core/schema/definitions/core/artifact.py +14 -8
- infrahub/core/schema/definitions/core/check.py +10 -4
- infrahub/core/schema/definitions/core/generator.py +26 -6
- infrahub/core/schema/definitions/core/graphql_query.py +1 -1
- infrahub/core/schema/definitions/core/group.py +9 -2
- infrahub/core/schema/definitions/core/ipam.py +80 -10
- infrahub/core/schema/definitions/core/menu.py +41 -7
- infrahub/core/schema/definitions/core/permission.py +16 -2
- infrahub/core/schema/definitions/core/profile.py +16 -2
- infrahub/core/schema/definitions/core/propose_change.py +24 -4
- infrahub/core/schema/definitions/core/propose_change_comment.py +23 -11
- infrahub/core/schema/definitions/core/propose_change_validator.py +50 -21
- infrahub/core/schema/definitions/core/repository.py +10 -0
- infrahub/core/schema/definitions/core/resource_pool.py +8 -1
- infrahub/core/schema/definitions/core/template.py +19 -2
- infrahub/core/schema/definitions/core/transform.py +11 -5
- infrahub/core/schema/definitions/core/webhook.py +27 -9
- infrahub/core/schema/manager.py +63 -43
- infrahub/core/schema/relationship_schema.py +6 -2
- infrahub/core/schema/schema_branch.py +48 -10
- infrahub/core/task/task.py +4 -2
- infrahub/core/utils.py +3 -25
- infrahub/core/validators/aggregated_checker.py +1 -1
- infrahub/core/validators/attribute/choices.py +1 -1
- infrahub/core/validators/attribute/enum.py +1 -1
- infrahub/core/validators/attribute/kind.py +1 -1
- infrahub/core/validators/attribute/length.py +1 -1
- infrahub/core/validators/attribute/min_max.py +1 -1
- infrahub/core/validators/attribute/number_pool.py +1 -1
- infrahub/core/validators/attribute/optional.py +1 -1
- infrahub/core/validators/attribute/regex.py +1 -1
- infrahub/core/validators/determiner.py +3 -3
- infrahub/core/validators/node/attribute.py +1 -1
- infrahub/core/validators/node/relationship.py +1 -1
- infrahub/core/validators/relationship/peer.py +1 -1
- infrahub/database/__init__.py +4 -4
- infrahub/dependencies/builder/constraint/grouped/node_runner.py +2 -0
- infrahub/dependencies/builder/constraint/relationship_manager/profiles_removal.py +8 -0
- infrahub/dependencies/registry.py +2 -0
- infrahub/display_labels/tasks.py +12 -3
- infrahub/git/integrator.py +18 -18
- infrahub/git/tasks.py +1 -1
- infrahub/git/utils.py +1 -1
- infrahub/graphql/constants.py +3 -0
- infrahub/graphql/context.py +1 -1
- infrahub/graphql/field_extractor.py +1 -1
- infrahub/graphql/initialization.py +11 -0
- infrahub/graphql/loaders/account.py +134 -0
- infrahub/graphql/loaders/node.py +5 -12
- infrahub/graphql/loaders/peers.py +5 -7
- infrahub/graphql/manager.py +175 -21
- infrahub/graphql/metadata.py +91 -0
- infrahub/graphql/mutations/account.py +6 -6
- infrahub/graphql/mutations/attribute.py +0 -2
- infrahub/graphql/mutations/branch.py +9 -5
- infrahub/graphql/mutations/computed_attribute.py +1 -1
- infrahub/graphql/mutations/display_label.py +1 -1
- infrahub/graphql/mutations/hfid.py +1 -1
- infrahub/graphql/mutations/ipam.py +4 -6
- infrahub/graphql/mutations/main.py +9 -4
- infrahub/graphql/mutations/profile.py +16 -22
- infrahub/graphql/mutations/proposed_change.py +4 -4
- infrahub/graphql/mutations/relationship.py +40 -10
- infrahub/graphql/mutations/repository.py +14 -12
- infrahub/graphql/mutations/schema.py +2 -2
- infrahub/graphql/order.py +14 -0
- infrahub/graphql/queries/branch.py +62 -6
- infrahub/graphql/queries/resource_manager.py +25 -24
- infrahub/graphql/resolvers/account_metadata.py +84 -0
- infrahub/graphql/resolvers/ipam.py +6 -8
- infrahub/graphql/resolvers/many_relationship.py +77 -35
- infrahub/graphql/resolvers/resolver.py +59 -14
- infrahub/graphql/resolvers/single_relationship.py +87 -23
- infrahub/graphql/subscription/graphql_query.py +2 -0
- infrahub/graphql/types/__init__.py +0 -1
- infrahub/graphql/types/attribute.py +10 -5
- infrahub/graphql/types/branch.py +40 -53
- infrahub/graphql/types/enums.py +3 -0
- infrahub/graphql/types/metadata.py +28 -0
- infrahub/graphql/types/node.py +22 -2
- infrahub/graphql/types/relationship.py +10 -2
- infrahub/graphql/types/standard_node.py +12 -7
- infrahub/hfid/tasks.py +12 -3
- infrahub/lock.py +7 -0
- infrahub/menu/repository.py +1 -1
- infrahub/patch/queries/base.py +1 -1
- infrahub/pools/number.py +1 -8
- infrahub/profiles/gather.py +56 -0
- infrahub/profiles/mandatory_fields_checker.py +116 -0
- infrahub/profiles/models.py +66 -0
- infrahub/profiles/node_applier.py +154 -13
- infrahub/profiles/queries/get_profile_data.py +143 -31
- infrahub/profiles/tasks.py +79 -27
- infrahub/profiles/triggers.py +22 -0
- infrahub/proposed_change/action_checker.py +1 -1
- infrahub/proposed_change/tasks.py +4 -1
- infrahub/services/__init__.py +1 -1
- infrahub/services/adapters/cache/nats.py +1 -1
- infrahub/services/adapters/cache/redis.py +7 -0
- infrahub/tasks/artifact.py +1 -0
- infrahub/transformations/tasks.py +2 -2
- infrahub/trigger/catalogue.py +2 -0
- infrahub/trigger/models.py +1 -0
- infrahub/trigger/setup.py +3 -3
- infrahub/trigger/tasks.py +3 -0
- infrahub/validators/tasks.py +1 -0
- infrahub/webhook/gather.py +1 -1
- infrahub/webhook/models.py +1 -1
- infrahub/webhook/tasks.py +23 -7
- infrahub/workers/dependencies.py +9 -3
- infrahub/workers/infrahub_async.py +13 -4
- infrahub/workflows/catalogue.py +19 -0
- infrahub_sdk/analyzer.py +2 -2
- infrahub_sdk/branch.py +12 -39
- infrahub_sdk/checks.py +4 -4
- infrahub_sdk/client.py +36 -0
- infrahub_sdk/ctl/cli_commands.py +2 -1
- infrahub_sdk/ctl/graphql.py +15 -4
- infrahub_sdk/ctl/utils.py +2 -2
- infrahub_sdk/enums.py +6 -0
- infrahub_sdk/graphql/renderers.py +21 -0
- infrahub_sdk/graphql/utils.py +85 -0
- infrahub_sdk/node/attribute.py +12 -2
- infrahub_sdk/node/constants.py +12 -0
- infrahub_sdk/node/metadata.py +69 -0
- infrahub_sdk/node/node.py +65 -14
- infrahub_sdk/node/property.py +3 -0
- infrahub_sdk/node/related_node.py +37 -5
- infrahub_sdk/node/relationship.py +18 -1
- infrahub_sdk/operation.py +2 -2
- infrahub_sdk/schema/repository.py +1 -2
- infrahub_sdk/transforms.py +2 -2
- infrahub_sdk/types.py +18 -2
- {infrahub_server-1.6.3.dist-info → infrahub_server-1.7.0.dist-info}/METADATA +17 -16
- {infrahub_server-1.6.3.dist-info → infrahub_server-1.7.0.dist-info}/RECORD +249 -228
- infrahub_testcontainers/container.py +3 -3
- infrahub_testcontainers/docker-compose-cluster.test.yml +7 -7
- infrahub_testcontainers/docker-compose.test.yml +13 -5
- infrahub_testcontainers/models.py +3 -3
- infrahub_testcontainers/performance_test.py +1 -1
- infrahub/graphql/models.py +0 -6
- {infrahub_server-1.6.3.dist-info → infrahub_server-1.7.0.dist-info}/WHEEL +0 -0
- {infrahub_server-1.6.3.dist-info → infrahub_server-1.7.0.dist-info}/entry_points.txt +0 -0
- {infrahub_server-1.6.3.dist-info → infrahub_server-1.7.0.dist-info}/licenses/LICENSE.txt +0 -0
|
@@ -22,7 +22,9 @@ from pydantic import BaseModel, Field
|
|
|
22
22
|
|
|
23
23
|
from infrahub.core import registry
|
|
24
24
|
from infrahub.core.changelog.models import ChangelogRelationshipMapper
|
|
25
|
-
from infrahub.core.constants import BranchSupportType, InfrahubKind, RelationshipKind
|
|
25
|
+
from infrahub.core.constants import SYSTEM_USER_ID, BranchSupportType, InfrahubKind, MetadataOptions, RelationshipKind
|
|
26
|
+
from infrahub.core.metadata.interface import MetadataInterface
|
|
27
|
+
from infrahub.core.metadata.model import MetadataInfo, MetadataQueryOptions
|
|
26
28
|
from infrahub.core.property import (
|
|
27
29
|
FlagPropertyMixin,
|
|
28
30
|
NodePropertyData,
|
|
@@ -30,16 +32,13 @@ from infrahub.core.property import (
|
|
|
30
32
|
)
|
|
31
33
|
from infrahub.core.query.relationship import (
|
|
32
34
|
RelationshipCreateQuery,
|
|
33
|
-
RelationshipDataDeleteQuery,
|
|
34
35
|
RelationshipDeleteQuery,
|
|
35
36
|
RelationshipGetPeerQuery,
|
|
36
|
-
RelationshipGetQuery,
|
|
37
37
|
RelationshipPeerData,
|
|
38
38
|
RelationshipUpdatePropertyQuery,
|
|
39
39
|
)
|
|
40
40
|
from infrahub.core.timestamp import Timestamp
|
|
41
|
-
from infrahub.
|
|
42
|
-
from infrahub.exceptions import Error, NodeNotFoundError, ValidationError
|
|
41
|
+
from infrahub.exceptions import NodeNotFoundError, ValidationError
|
|
43
42
|
|
|
44
43
|
if TYPE_CHECKING:
|
|
45
44
|
from uuid import UUID
|
|
@@ -71,7 +70,6 @@ class RelationshipCreateData(BaseModel):
|
|
|
71
70
|
direction: str
|
|
72
71
|
status: str
|
|
73
72
|
is_protected: bool
|
|
74
|
-
is_visible: bool
|
|
75
73
|
hierarchical: str | None = None
|
|
76
74
|
source_prop: list[NodePropertyData] = Field(default_factory=list)
|
|
77
75
|
owner_prop: list[NodePropertyData] = Field(default_factory=list)
|
|
@@ -85,16 +83,37 @@ class RelationshipUpdateDetails:
|
|
|
85
83
|
peer_ids_present_database_only: list[str]
|
|
86
84
|
|
|
87
85
|
|
|
88
|
-
|
|
86
|
+
@dataclass
|
|
87
|
+
class PeerWithRelationshipMetadata:
|
|
88
|
+
peer: Node | str
|
|
89
|
+
created_at: Timestamp | None = None
|
|
90
|
+
created_by: str | None = None
|
|
91
|
+
updated_at: Timestamp | None = None
|
|
92
|
+
updated_by: str | None = None
|
|
93
|
+
owner_id: str | None = None
|
|
94
|
+
source_id: str | None = None
|
|
95
|
+
is_protected: bool | None = None
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _use_global_branch(schema: MainSchemaTypes) -> bool:
|
|
99
|
+
# only use the global branch alone if the schema is branch agnostic and it is a node schema
|
|
100
|
+
# if the schema is a generic, then its used_by schemas could be branch-aware
|
|
101
|
+
return bool(schema.branch is BranchSupportType.AGNOSTIC and schema.is_node_schema)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
class Relationship(FlagPropertyMixin, NodePropertyMixin, MetadataInterface):
|
|
89
105
|
rel_type: str = "IS_RELATED"
|
|
90
106
|
|
|
91
107
|
def __init__(
|
|
92
108
|
self,
|
|
93
109
|
schema: RelationshipSchema,
|
|
94
110
|
branch: Branch,
|
|
111
|
+
source_kind: str,
|
|
95
112
|
at: Timestamp | None = None,
|
|
96
113
|
node: Node | None = None,
|
|
97
114
|
node_id: str | None = None,
|
|
115
|
+
is_from_profile: bool = False,
|
|
116
|
+
profile_id: UUID | None = None,
|
|
98
117
|
**kwargs: Any,
|
|
99
118
|
) -> None:
|
|
100
119
|
if not node and not node_id:
|
|
@@ -104,6 +123,7 @@ class Relationship(FlagPropertyMixin, NodePropertyMixin):
|
|
|
104
123
|
self.name = schema.name
|
|
105
124
|
|
|
106
125
|
self.branch = branch
|
|
126
|
+
self.source_kind = source_kind
|
|
107
127
|
self.at = Timestamp(at)
|
|
108
128
|
|
|
109
129
|
self._node = node
|
|
@@ -111,14 +131,21 @@ class Relationship(FlagPropertyMixin, NodePropertyMixin):
|
|
|
111
131
|
|
|
112
132
|
self.id: UUID | None = None
|
|
113
133
|
self.db_id: str | None = None
|
|
114
|
-
self.
|
|
134
|
+
self.is_from_profile: bool = is_from_profile
|
|
135
|
+
self.profile_id: UUID | None = profile_id
|
|
115
136
|
|
|
116
137
|
self._peer: Node | str | None = None
|
|
117
138
|
self.peer_id: str | None = None
|
|
118
139
|
self.peer_hfid: list[str] | None = None
|
|
119
|
-
self.data: dict | RelationshipPeerData | str | None = None
|
|
140
|
+
self.data: dict | RelationshipPeerData | str | Node | None = None
|
|
120
141
|
|
|
121
142
|
self.from_pool: dict[str, Any] | None = None
|
|
143
|
+
self._metadata = MetadataInfo(
|
|
144
|
+
created_at=kwargs.get("created_at"),
|
|
145
|
+
created_by=kwargs.get("created_by"),
|
|
146
|
+
updated_at=kwargs.get("updated_at"),
|
|
147
|
+
updated_by=kwargs.get("updated_by"),
|
|
148
|
+
)
|
|
122
149
|
|
|
123
150
|
self._init_node_property_mixin(kwargs=kwargs)
|
|
124
151
|
self._init_flag_property_mixin(kwargs=kwargs)
|
|
@@ -166,44 +193,102 @@ class Relationship(FlagPropertyMixin, NodePropertyMixin):
|
|
|
166
193
|
return registry.get_global_branch()
|
|
167
194
|
return self.branch
|
|
168
195
|
|
|
169
|
-
def
|
|
170
|
-
self.
|
|
196
|
+
def _set_created_at(self, value: Timestamp | None) -> None:
|
|
197
|
+
self._metadata.created_at = value
|
|
171
198
|
|
|
172
|
-
|
|
173
|
-
|
|
199
|
+
def _set_created_by(self, value: str | None) -> None:
|
|
200
|
+
self._metadata.created_by = value
|
|
174
201
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
if not self.db_id and data.rel_node_db_id:
|
|
178
|
-
self.db_id = data.rel_node_db_id
|
|
202
|
+
def _set_updated_at(self, value: Timestamp | None) -> None:
|
|
203
|
+
self._metadata.updated_at = value
|
|
179
204
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
if hasattr(self, "_flag_properties") and prop_name in self._flag_properties:
|
|
183
|
-
setattr(self, prop_name, prop.value)
|
|
184
|
-
elif hasattr(self, "_node_properties") and prop_name in self._node_properties:
|
|
185
|
-
setattr(self, prop_name, prop.value)
|
|
205
|
+
def _set_updated_by(self, value: str | None) -> None:
|
|
206
|
+
self._metadata.updated_by = value
|
|
186
207
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
self.peer_hfid = value
|
|
193
|
-
elif key.startswith(PREFIX_PROPERTY) and key.replace(PREFIX_PROPERTY, "") in self._flag_properties:
|
|
194
|
-
setattr(self, key.replace(PREFIX_PROPERTY, ""), value)
|
|
195
|
-
elif key.startswith(PREFIX_PROPERTY) and key.replace(PREFIX_PROPERTY, "") in self._node_properties:
|
|
196
|
-
setattr(self, key.replace(PREFIX_PROPERTY, ""), value)
|
|
197
|
-
elif key == "from_pool":
|
|
198
|
-
self.from_pool = value
|
|
208
|
+
def _get_created_at(self) -> Timestamp | None:
|
|
209
|
+
return self._metadata.created_at
|
|
210
|
+
|
|
211
|
+
def _get_created_by(self) -> str | None:
|
|
212
|
+
return self._metadata.created_by
|
|
199
213
|
|
|
214
|
+
def _get_updated_at(self) -> Timestamp | None:
|
|
215
|
+
return self._metadata.updated_at
|
|
216
|
+
|
|
217
|
+
def _get_updated_by(self) -> str | None:
|
|
218
|
+
return self._metadata.updated_by
|
|
219
|
+
|
|
220
|
+
def _process_relationship_peer_data(self, data: RelationshipPeerData) -> None:
|
|
221
|
+
self.set_peer(value=str(data.peer_id))
|
|
222
|
+
|
|
223
|
+
if not self.id and data.rel_node_id:
|
|
224
|
+
self.id = data.rel_node_id
|
|
225
|
+
if not self.db_id and data.rel_node_db_id:
|
|
226
|
+
self.db_id = data.rel_node_db_id
|
|
227
|
+
|
|
228
|
+
# Extract the properties
|
|
229
|
+
for prop_name, prop in data.properties.items():
|
|
230
|
+
if hasattr(self, "_flag_properties") and prop_name in self._flag_properties:
|
|
231
|
+
setattr(self, prop_name, prop.value)
|
|
232
|
+
elif hasattr(self, "_node_properties") and prop_name in self._node_properties:
|
|
233
|
+
setattr(self, prop_name, prop.value)
|
|
234
|
+
self._set_created_at(data.created_at)
|
|
235
|
+
self._set_created_by(data.created_by)
|
|
236
|
+
self._set_updated_at(data.updated_at)
|
|
237
|
+
self._set_updated_by(data.updated_by)
|
|
238
|
+
|
|
239
|
+
def _process_dict_data(self, data: dict) -> None:
|
|
240
|
+
for key, value in data.items():
|
|
241
|
+
if key in ["peer", "id"]:
|
|
242
|
+
self.set_peer(value=value)
|
|
243
|
+
elif key == "hfid" and self.peer_id is None:
|
|
244
|
+
self.peer_hfid = value
|
|
245
|
+
elif key.startswith(PREFIX_PROPERTY) and key.replace(PREFIX_PROPERTY, "") in self._flag_properties:
|
|
246
|
+
setattr(self, key.replace(PREFIX_PROPERTY, ""), value)
|
|
247
|
+
elif key.startswith(PREFIX_PROPERTY) and key.replace(PREFIX_PROPERTY, "") in self._node_properties:
|
|
248
|
+
setattr(self, key.replace(PREFIX_PROPERTY, ""), value)
|
|
249
|
+
elif key == "from_pool":
|
|
250
|
+
self.from_pool = value
|
|
251
|
+
elif key == "created_at" and value:
|
|
252
|
+
self._set_created_at(Timestamp(value))
|
|
253
|
+
elif key == "created_by" and value:
|
|
254
|
+
self._set_created_by(value)
|
|
255
|
+
elif key == "updated_at" and value:
|
|
256
|
+
self._set_updated_at(Timestamp(value))
|
|
257
|
+
elif key == "updated_by" and value:
|
|
258
|
+
self._set_updated_by(value)
|
|
259
|
+
|
|
260
|
+
def _process_peer_with_relationship_metadata(self, data: PeerWithRelationshipMetadata) -> None:
|
|
261
|
+
self.set_peer(value=data.peer)
|
|
262
|
+
self._set_created_at(data.created_at)
|
|
263
|
+
self._set_created_by(data.created_by)
|
|
264
|
+
self._set_updated_at(data.updated_at)
|
|
265
|
+
self._set_updated_by(data.updated_by)
|
|
266
|
+
if data.is_protected is not None:
|
|
267
|
+
self.is_protected = data.is_protected
|
|
268
|
+
if data.owner_id is not None:
|
|
269
|
+
self.set_owner(value=data.owner_id)
|
|
270
|
+
if data.source_id is not None:
|
|
271
|
+
self.set_source(value=data.source_id)
|
|
272
|
+
|
|
273
|
+
def _process_data(self, data: dict | RelationshipPeerData | str | Node | PeerWithRelationshipMetadata) -> None:
|
|
274
|
+
if isinstance(data, PeerWithRelationshipMetadata):
|
|
275
|
+
self.data = data.peer
|
|
276
|
+
else:
|
|
277
|
+
self.data = data
|
|
278
|
+
|
|
279
|
+
if isinstance(data, RelationshipPeerData):
|
|
280
|
+
self._process_relationship_peer_data(data=data)
|
|
281
|
+
elif isinstance(data, dict):
|
|
282
|
+
self._process_dict_data(data=data)
|
|
283
|
+
elif isinstance(data, PeerWithRelationshipMetadata):
|
|
284
|
+
self._process_peer_with_relationship_metadata(data=data)
|
|
200
285
|
else:
|
|
201
286
|
self.set_peer(value=data)
|
|
202
287
|
|
|
203
288
|
async def new(
|
|
204
289
|
self,
|
|
205
290
|
db: InfrahubDatabase, # noqa: ARG002
|
|
206
|
-
data: dict | RelationshipPeerData | Any = None,
|
|
291
|
+
data: dict | RelationshipPeerData | PeerWithRelationshipMetadata | Any = None,
|
|
207
292
|
**kwargs: Any, # noqa: ARG002
|
|
208
293
|
) -> Relationship:
|
|
209
294
|
self._process_data(data=data)
|
|
@@ -215,19 +300,11 @@ class Relationship(FlagPropertyMixin, NodePropertyMixin):
|
|
|
215
300
|
db: InfrahubDatabase, # noqa: ARG002
|
|
216
301
|
id: UUID | None = None,
|
|
217
302
|
db_id: str | None = None,
|
|
218
|
-
updated_at: Timestamp | str | None = None,
|
|
219
303
|
data: dict | RelationshipPeerData | Any = None,
|
|
220
304
|
) -> Self:
|
|
221
|
-
hash_before = hash(self)
|
|
222
|
-
|
|
223
305
|
self.id = id or self.id
|
|
224
306
|
self.db_id = db_id or self.db_id
|
|
225
|
-
|
|
226
307
|
self._process_data(data=data)
|
|
227
|
-
|
|
228
|
-
if updated_at and hash(self) != hash_before:
|
|
229
|
-
self.updated_at = Timestamp(updated_at)
|
|
230
|
-
|
|
231
308
|
return self
|
|
232
309
|
|
|
233
310
|
def get_kind(self) -> str:
|
|
@@ -245,8 +322,7 @@ class Relationship(FlagPropertyMixin, NodePropertyMixin):
|
|
|
245
322
|
kind=self.schema.kind.value,
|
|
246
323
|
branch=self.branch,
|
|
247
324
|
at=self.at,
|
|
248
|
-
|
|
249
|
-
include_source=True,
|
|
325
|
+
include_metadata=MetadataOptions.LINKED_NODES,
|
|
250
326
|
)
|
|
251
327
|
self._node = node
|
|
252
328
|
self._node_id = self._node.id
|
|
@@ -290,8 +366,7 @@ class Relationship(FlagPropertyMixin, NodePropertyMixin):
|
|
|
290
366
|
raise_on_error=True,
|
|
291
367
|
at=self.at,
|
|
292
368
|
branch=self.branch,
|
|
293
|
-
|
|
294
|
-
include_owner=True,
|
|
369
|
+
include_metadata=MetadataOptions.LINKED_NODES,
|
|
295
370
|
prefetch_relationships=False,
|
|
296
371
|
branch_agnostic=self.schema.branch is BranchSupportType.AGNOSTIC,
|
|
297
372
|
)
|
|
@@ -302,8 +377,7 @@ class Relationship(FlagPropertyMixin, NodePropertyMixin):
|
|
|
302
377
|
kind=self.schema.peer,
|
|
303
378
|
branch=self.branch,
|
|
304
379
|
at=self.at,
|
|
305
|
-
|
|
306
|
-
include_source=True,
|
|
380
|
+
include_metadata=MetadataOptions.LINKED_NODES,
|
|
307
381
|
branch_agnostic=self.schema.branch is BranchSupportType.AGNOSTIC,
|
|
308
382
|
)
|
|
309
383
|
except NodeNotFoundError:
|
|
@@ -332,15 +406,19 @@ class Relationship(FlagPropertyMixin, NodePropertyMixin):
|
|
|
332
406
|
memory_value = getattr(self, f"{property_name}_id", None)
|
|
333
407
|
database_prop = data.properties.get(property_name)
|
|
334
408
|
database_value = database_prop.value if database_prop else None
|
|
335
|
-
if memory_value != database_value:
|
|
409
|
+
if memory_value != database_value or self.is_clear(property_name):
|
|
336
410
|
different_properties.append(property_name)
|
|
337
411
|
|
|
338
412
|
return different_properties
|
|
339
413
|
|
|
340
|
-
async def _create(self, db: InfrahubDatabase, at: Timestamp | None = None) -> None:
|
|
414
|
+
async def _create(self, db: InfrahubDatabase, user_id: str, at: Timestamp | None = None) -> None:
|
|
341
415
|
"""Add a relationship with another object by creating a new relationship node."""
|
|
342
416
|
|
|
343
417
|
create_at = Timestamp(at)
|
|
418
|
+
self._set_created_by(value=user_id)
|
|
419
|
+
self._set_created_at(value=create_at)
|
|
420
|
+
self._set_updated_by(value=user_id)
|
|
421
|
+
self._set_updated_at(value=create_at)
|
|
344
422
|
|
|
345
423
|
# Assuming nothing is present in the database yet
|
|
346
424
|
# Create a new Relationship node and attach each object to it
|
|
@@ -349,7 +427,7 @@ class Relationship(FlagPropertyMixin, NodePropertyMixin):
|
|
|
349
427
|
branch = self.get_branch_based_on_support_type()
|
|
350
428
|
|
|
351
429
|
query = await RelationshipCreateQuery.init(
|
|
352
|
-
db=db, source=node, destination=peer, rel=self, branch=branch, at=create_at
|
|
430
|
+
db=db, source=node, destination=peer, rel=self, branch=branch, at=create_at, user_id=user_id
|
|
353
431
|
)
|
|
354
432
|
await query.execute(db=db)
|
|
355
433
|
result = query.get_result()
|
|
@@ -364,6 +442,7 @@ class Relationship(FlagPropertyMixin, NodePropertyMixin):
|
|
|
364
442
|
db: InfrahubDatabase,
|
|
365
443
|
properties_to_update: list[str],
|
|
366
444
|
data: RelationshipPeerData,
|
|
445
|
+
user_id: str = SYSTEM_USER_ID,
|
|
367
446
|
at: Timestamp | None = None,
|
|
368
447
|
) -> None:
|
|
369
448
|
"""Update the properties of an existing relationship."""
|
|
@@ -371,16 +450,6 @@ class Relationship(FlagPropertyMixin, NodePropertyMixin):
|
|
|
371
450
|
update_at = Timestamp(at)
|
|
372
451
|
branch = self.get_branch_based_on_support_type()
|
|
373
452
|
|
|
374
|
-
rel_ids_to_update = []
|
|
375
|
-
for prop_name, prop in data.properties.items():
|
|
376
|
-
if prop_name in properties_to_update and prop.rel.branch == self.branch.name:
|
|
377
|
-
rel_ids_to_update.append(prop.rel.db_id)
|
|
378
|
-
|
|
379
|
-
if rel_ids_to_update:
|
|
380
|
-
await update_relationships_to(rel_ids_to_update, to=update_at, db=db)
|
|
381
|
-
|
|
382
|
-
node = await self.get_node(db=db)
|
|
383
|
-
|
|
384
453
|
flag_properties_to_update = {}
|
|
385
454
|
for prop_name in self._flag_properties:
|
|
386
455
|
if prop_name not in properties_to_update:
|
|
@@ -393,8 +462,10 @@ class Relationship(FlagPropertyMixin, NodePropertyMixin):
|
|
|
393
462
|
for prop_name in self._node_properties:
|
|
394
463
|
if prop_name not in properties_to_update:
|
|
395
464
|
continue
|
|
396
|
-
|
|
397
|
-
|
|
465
|
+
value = getattr(self, f"{prop_name}_id")
|
|
466
|
+
needs_clear = self.is_clear(prop_name)
|
|
467
|
+
if value or needs_clear:
|
|
468
|
+
node_properties_to_update[prop_name] = getattr(self, f"{prop_name}_id")
|
|
398
469
|
|
|
399
470
|
if not flag_properties_to_update and not node_properties_to_update:
|
|
400
471
|
return
|
|
@@ -402,46 +473,48 @@ class Relationship(FlagPropertyMixin, NodePropertyMixin):
|
|
|
402
473
|
query = await RelationshipUpdatePropertyQuery.init(
|
|
403
474
|
db=db,
|
|
404
475
|
branch=branch,
|
|
405
|
-
|
|
476
|
+
source_id=self.node_id,
|
|
406
477
|
rel=self,
|
|
478
|
+
user_id=user_id,
|
|
407
479
|
at=update_at,
|
|
408
480
|
flag_properties_to_update=flag_properties_to_update,
|
|
409
481
|
node_properties_to_update=node_properties_to_update,
|
|
410
|
-
|
|
482
|
+
rel_id=data.rel_node_id,
|
|
411
483
|
)
|
|
412
484
|
await query.execute(db=db)
|
|
413
485
|
|
|
414
|
-
async def delete(self, db: InfrahubDatabase, at: Timestamp | None = None) -> None:
|
|
486
|
+
async def delete(self, db: InfrahubDatabase, user_id: str = SYSTEM_USER_ID, at: Timestamp | None = None) -> None:
|
|
415
487
|
delete_at = Timestamp(at)
|
|
416
488
|
|
|
417
|
-
|
|
418
|
-
|
|
489
|
+
if self.peer_id:
|
|
490
|
+
peer_id = self.peer_id
|
|
491
|
+
else:
|
|
492
|
+
peer = await self.get_peer(db=db)
|
|
493
|
+
peer_id = peer.id
|
|
419
494
|
|
|
420
495
|
branch = self.get_branch_based_on_support_type()
|
|
421
496
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
if
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
if get_query.is_already_deleted():
|
|
434
|
-
return
|
|
435
|
-
|
|
436
|
-
# when we remove a relationship we need to :
|
|
437
|
-
# - Update the existing relationship if we are on the same branch
|
|
438
|
-
# - Create a new rel of type DELETED in the right branch
|
|
439
|
-
|
|
440
|
-
if rel_ids_to_update:
|
|
441
|
-
await update_relationships_to(rel_ids_to_update, to=delete_at, db=db)
|
|
497
|
+
source_schema = db.schema.get(name=self.source_kind, branch=self.branch, duplicate=False)
|
|
498
|
+
if _use_global_branch(schema=source_schema):
|
|
499
|
+
source_branch = registry.get_global_branch()
|
|
500
|
+
else:
|
|
501
|
+
source_branch = self.branch
|
|
502
|
+
destination_schema = db.schema.get(name=self.schema.peer, branch=self.branch, duplicate=False)
|
|
503
|
+
if _use_global_branch(schema=destination_schema):
|
|
504
|
+
destination_branch = registry.get_global_branch()
|
|
505
|
+
else:
|
|
506
|
+
destination_branch = self.branch
|
|
442
507
|
|
|
443
508
|
delete_query = await RelationshipDeleteQuery.init(
|
|
444
|
-
db=db,
|
|
509
|
+
db=db,
|
|
510
|
+
rel=self,
|
|
511
|
+
source_id=self.node_id,
|
|
512
|
+
source_branch=source_branch,
|
|
513
|
+
destination_id=peer_id,
|
|
514
|
+
destination_branch=destination_branch,
|
|
515
|
+
branch=branch,
|
|
516
|
+
user_id=user_id,
|
|
517
|
+
at=delete_at,
|
|
445
518
|
)
|
|
446
519
|
await delete_query.execute(db=db)
|
|
447
520
|
|
|
@@ -504,13 +577,13 @@ class Relationship(FlagPropertyMixin, NodePropertyMixin):
|
|
|
504
577
|
self.set_peer(value=assigned_peer)
|
|
505
578
|
self.set_source(value=pool.id)
|
|
506
579
|
|
|
507
|
-
async def save(self, db: InfrahubDatabase, at: Timestamp | None = None) -> Self:
|
|
580
|
+
async def save(self, db: InfrahubDatabase, at: Timestamp | None = None, user_id: str = SYSTEM_USER_ID) -> Self:
|
|
508
581
|
"""Create or Update the Relationship in the database."""
|
|
509
582
|
|
|
510
583
|
save_at = Timestamp(at)
|
|
511
584
|
|
|
512
585
|
if not self.id:
|
|
513
|
-
await self._create(at=save_at, db=db)
|
|
586
|
+
await self._create(user_id=user_id, at=save_at, db=db)
|
|
514
587
|
return self
|
|
515
588
|
|
|
516
589
|
return self
|
|
@@ -536,9 +609,6 @@ class Relationship(FlagPropertyMixin, NodePropertyMixin):
|
|
|
536
609
|
response = await peer.to_graphql(fields=peer_fields, db=db, related_node_ids=related_node_ids)
|
|
537
610
|
|
|
538
611
|
for field_name in rel_fields.keys():
|
|
539
|
-
if field_name == "updated_at" and self.updated_at:
|
|
540
|
-
response[f"{PREFIX_PROPERTY}{field_name}"] = await self.updated_at.to_graphql(db=db)
|
|
541
|
-
|
|
542
612
|
if field_name in self._node_properties:
|
|
543
613
|
node_prop_getter = getattr(self, f"get_{field_name}")
|
|
544
614
|
node_prop = await node_prop_getter(db=db)
|
|
@@ -550,6 +620,16 @@ class Relationship(FlagPropertyMixin, NodePropertyMixin):
|
|
|
550
620
|
)
|
|
551
621
|
if field_name in self._flag_properties:
|
|
552
622
|
response[f"{PREFIX_PROPERTY}{field_name}"] = getattr(self, field_name)
|
|
623
|
+
if field_name == "created_at":
|
|
624
|
+
created_at = self._get_created_at()
|
|
625
|
+
response[f"{PREFIX_PROPERTY}{field_name}"] = await created_at.to_graphql() if created_at else None
|
|
626
|
+
elif field_name == "created_by":
|
|
627
|
+
response[f"{PREFIX_PROPERTY}{field_name}"] = self._get_created_by()
|
|
628
|
+
elif field_name in ["_updated_at", "updated_at"]:
|
|
629
|
+
updated_at = self._get_updated_at()
|
|
630
|
+
response[f"{PREFIX_PROPERTY}{field_name}"] = await updated_at.to_graphql() if updated_at else None
|
|
631
|
+
elif field_name == "updated_by":
|
|
632
|
+
response[f"{PREFIX_PROPERTY}{field_name}"] = self._get_updated_by()
|
|
553
633
|
|
|
554
634
|
if fields and "__typename" in fields:
|
|
555
635
|
response["__typename"] = f"Related{peer.get_kind()}"
|
|
@@ -576,7 +656,6 @@ class Relationship(FlagPropertyMixin, NodePropertyMixin):
|
|
|
576
656
|
branch_support=self.schema.branch.value if self.schema.branch else None,
|
|
577
657
|
hierarchical=self.schema.hierarchical,
|
|
578
658
|
is_protected=self.is_protected,
|
|
579
|
-
is_visible=self.is_visible,
|
|
580
659
|
)
|
|
581
660
|
if hasattr(self, "source_id") and self.source_id:
|
|
582
661
|
data.source_prop.append(NodePropertyData(name="source", peer_id=str(self.source_id)))
|
|
@@ -715,6 +794,10 @@ class RelationshipValidatorList:
|
|
|
715
794
|
self._relationships_count -= 1
|
|
716
795
|
return result
|
|
717
796
|
|
|
797
|
+
def replace(self, rel_to_insert: Relationship, rel_to_remove: Relationship) -> None:
|
|
798
|
+
self._relationships.remove(rel_to_remove)
|
|
799
|
+
self._relationships.append(rel_to_insert)
|
|
800
|
+
|
|
718
801
|
def remove(self, value: Relationship) -> None:
|
|
719
802
|
if self.min_count and self._relationships_count - 1 < self.min_count:
|
|
720
803
|
self._raise_too_few()
|
|
@@ -748,12 +831,15 @@ class RelationshipValidatorList:
|
|
|
748
831
|
|
|
749
832
|
|
|
750
833
|
class RelationshipManager:
|
|
751
|
-
def __init__(
|
|
834
|
+
def __init__(
|
|
835
|
+
self, schema: RelationshipSchema, branch: Branch, at: Timestamp, node: Node, is_from_profile: bool = False
|
|
836
|
+
) -> None:
|
|
752
837
|
self.schema: RelationshipSchema = schema
|
|
753
838
|
self.name: str = schema.name
|
|
754
839
|
self.node: Node = node
|
|
755
840
|
self.branch: Branch = branch
|
|
756
841
|
self.at = at
|
|
842
|
+
self.is_from_profile = is_from_profile
|
|
757
843
|
|
|
758
844
|
# TODO Ideally this information should come from the Schema
|
|
759
845
|
self.rel_class = Relationship
|
|
@@ -804,7 +890,9 @@ class RelationshipManager:
|
|
|
804
890
|
if not isinstance(item, rm.rel_class | str | dict) and not hasattr(item, "_schema"):
|
|
805
891
|
raise ValidationError({rm.name: f"Invalid data provided to form a relationship {item}"})
|
|
806
892
|
|
|
807
|
-
rel = rm.rel_class(
|
|
893
|
+
rel = rm.rel_class(
|
|
894
|
+
schema=rm.schema, branch=rm.branch, source_kind=rm.node.get_kind(), at=rm.at, node=rm.node
|
|
895
|
+
)
|
|
808
896
|
await rel.new(db=db, data=item)
|
|
809
897
|
|
|
810
898
|
rm._relationships.append(rel)
|
|
@@ -912,8 +1000,7 @@ class RelationshipManager:
|
|
|
912
1000
|
db: InfrahubDatabase,
|
|
913
1001
|
peer_type: type[PeerType],
|
|
914
1002
|
branch_agnostic: bool = ...,
|
|
915
|
-
|
|
916
|
-
include_owner: bool = ...,
|
|
1003
|
+
include_metadata: MetadataQueryOptions | MetadataOptions = MetadataOptions.NONE,
|
|
917
1004
|
) -> Mapping[str, PeerType]: ...
|
|
918
1005
|
|
|
919
1006
|
@overload
|
|
@@ -922,8 +1009,7 @@ class RelationshipManager:
|
|
|
922
1009
|
db: InfrahubDatabase,
|
|
923
1010
|
peer_type: None = None,
|
|
924
1011
|
branch_agnostic: bool = ...,
|
|
925
|
-
|
|
926
|
-
include_owner: bool = ...,
|
|
1012
|
+
include_metadata: MetadataQueryOptions | MetadataOptions = MetadataOptions.NONE,
|
|
927
1013
|
) -> Mapping[str, Node]: ...
|
|
928
1014
|
|
|
929
1015
|
async def get_peers(
|
|
@@ -931,8 +1017,7 @@ class RelationshipManager:
|
|
|
931
1017
|
db: InfrahubDatabase,
|
|
932
1018
|
peer_type: type[PeerType] | None = None, # noqa: ARG002
|
|
933
1019
|
branch_agnostic: bool = False,
|
|
934
|
-
|
|
935
|
-
include_owner: bool = False,
|
|
1020
|
+
include_metadata: MetadataQueryOptions | MetadataOptions = MetadataOptions.NONE,
|
|
936
1021
|
) -> Mapping[str, Node | PeerType]:
|
|
937
1022
|
rels = await self.get_relationships(db=db, branch_agnostic=branch_agnostic)
|
|
938
1023
|
peer_ids = [rel.peer_id for rel in rels if rel.peer_id]
|
|
@@ -941,8 +1026,7 @@ class RelationshipManager:
|
|
|
941
1026
|
ids=peer_ids,
|
|
942
1027
|
branch=self.branch,
|
|
943
1028
|
branch_agnostic=branch_agnostic,
|
|
944
|
-
|
|
945
|
-
include_owner=include_owner,
|
|
1029
|
+
include_metadata=include_metadata,
|
|
946
1030
|
)
|
|
947
1031
|
return nodes
|
|
948
1032
|
|
|
@@ -966,8 +1050,11 @@ class RelationshipManager:
|
|
|
966
1050
|
db=db,
|
|
967
1051
|
source=self.node,
|
|
968
1052
|
at=at or self.at,
|
|
969
|
-
rel=self.rel_class(
|
|
1053
|
+
rel=self.rel_class(
|
|
1054
|
+
schema=self.schema, branch=self.branch, source_kind=self.node.get_kind(), node=self.node
|
|
1055
|
+
),
|
|
970
1056
|
branch_agnostic=branch_agnostic,
|
|
1057
|
+
include_metadata=MetadataOptions.IS_PROTECTED | MetadataOptions.LINKED_NODES,
|
|
971
1058
|
)
|
|
972
1059
|
await query.execute(db=db)
|
|
973
1060
|
return list(query.get_peers())
|
|
@@ -991,7 +1078,9 @@ class RelationshipManager:
|
|
|
991
1078
|
|
|
992
1079
|
peers = await self.get_db_peers(db=db, at=at, branch_agnostic=branch_agnostic)
|
|
993
1080
|
|
|
994
|
-
|
|
1081
|
+
self.is_from_profile = bool(peers) and all(peer.is_from_profile for peer in peers)
|
|
1082
|
+
|
|
1083
|
+
peers_database = {str(peer.peer_id): peer for peer in peers}
|
|
995
1084
|
peer_ids = list(peers_database.keys())
|
|
996
1085
|
|
|
997
1086
|
# Calculate which peer should be added or removed
|
|
@@ -1025,8 +1114,11 @@ class RelationshipManager:
|
|
|
1025
1114
|
Relationship(
|
|
1026
1115
|
schema=self.schema,
|
|
1027
1116
|
branch=self.branch,
|
|
1117
|
+
source_kind=self.node.get_kind(),
|
|
1028
1118
|
at=at or self.at,
|
|
1029
1119
|
node=self.node,
|
|
1120
|
+
is_from_profile=details.peers_database[peer_id].is_from_profile,
|
|
1121
|
+
profile_id=details.peers_database[peer_id].profile_id,
|
|
1030
1122
|
).load(db=db, data=details.peers_database[peer_id])
|
|
1031
1123
|
)
|
|
1032
1124
|
|
|
@@ -1072,15 +1164,27 @@ class RelationshipManager:
|
|
|
1072
1164
|
|
|
1073
1165
|
return self._relationships.as_list()
|
|
1074
1166
|
|
|
1167
|
+
async def get_relationship(self, db: InfrahubDatabase, peer_id: str) -> Relationship | None:
|
|
1168
|
+
for rel in await self.get_relationships(db=db):
|
|
1169
|
+
if rel.peer_id == peer_id:
|
|
1170
|
+
return rel
|
|
1171
|
+
return None
|
|
1172
|
+
|
|
1075
1173
|
async def update(
|
|
1076
1174
|
self,
|
|
1077
|
-
data: list[str | Node
|
|
1175
|
+
data: list[str | Node | dict[str, Any] | PeerWithRelationshipMetadata]
|
|
1176
|
+
| dict[str, Any]
|
|
1177
|
+
| str
|
|
1178
|
+
| Node
|
|
1179
|
+
| PeerWithRelationshipMetadata
|
|
1180
|
+
| None,
|
|
1078
1181
|
db: InfrahubDatabase,
|
|
1079
1182
|
process_delete: bool = True,
|
|
1183
|
+
user_id: str = SYSTEM_USER_ID,
|
|
1080
1184
|
) -> bool:
|
|
1081
1185
|
"""Replace and Update the list of relationships with this one."""
|
|
1082
1186
|
if not isinstance(data, list):
|
|
1083
|
-
list_data: Sequence[str | Node | dict[str, Any] | None] = [data]
|
|
1187
|
+
list_data: Sequence[str | Node | dict[str, Any] | PeerWithRelationshipMetadata | None] = [data]
|
|
1084
1188
|
else:
|
|
1085
1189
|
list_data = data
|
|
1086
1190
|
|
|
@@ -1092,7 +1196,9 @@ class RelationshipManager:
|
|
|
1092
1196
|
changed = False
|
|
1093
1197
|
|
|
1094
1198
|
for item in list_data:
|
|
1095
|
-
if not isinstance(
|
|
1199
|
+
if not isinstance(
|
|
1200
|
+
item, self.rel_class | str | dict | type(None) | PeerWithRelationshipMetadata
|
|
1201
|
+
) and not hasattr(item, "_schema"):
|
|
1096
1202
|
raise ValidationError({self.name: f"Invalid data provided to form a relationship {item}"})
|
|
1097
1203
|
|
|
1098
1204
|
if hasattr(item, "_schema"):
|
|
@@ -1105,7 +1211,7 @@ class RelationshipManager:
|
|
|
1105
1211
|
if previous_relationships:
|
|
1106
1212
|
if process_delete:
|
|
1107
1213
|
for rel in previous_relationships.values():
|
|
1108
|
-
await rel.delete(db=db)
|
|
1214
|
+
await rel.delete(db=db, user_id=user_id)
|
|
1109
1215
|
changed = True
|
|
1110
1216
|
continue
|
|
1111
1217
|
|
|
@@ -1124,9 +1230,9 @@ class RelationshipManager:
|
|
|
1124
1230
|
|
|
1125
1231
|
# If the item is not present in the previous list of relationship, we create a new one.
|
|
1126
1232
|
self._relationships.append(
|
|
1127
|
-
await self.rel_class(
|
|
1128
|
-
|
|
1129
|
-
)
|
|
1233
|
+
await self.rel_class(
|
|
1234
|
+
schema=self.schema, branch=self.branch, source_kind=self.node.get_kind(), at=self.at, node=self.node
|
|
1235
|
+
).new(db=db, data=item)
|
|
1130
1236
|
)
|
|
1131
1237
|
changed = True
|
|
1132
1238
|
|
|
@@ -1157,9 +1263,9 @@ class RelationshipManager:
|
|
|
1157
1263
|
|
|
1158
1264
|
# If the item ID is not present in the previous set of relationships, create a new one
|
|
1159
1265
|
self._relationships.append(
|
|
1160
|
-
await self.rel_class(
|
|
1161
|
-
|
|
1162
|
-
)
|
|
1266
|
+
await self.rel_class(
|
|
1267
|
+
schema=self.schema, branch=self.branch, source_kind=self.node.get_kind(), at=self.at, node=self.node
|
|
1268
|
+
).new(db=db, data=data)
|
|
1163
1269
|
)
|
|
1164
1270
|
|
|
1165
1271
|
return True
|
|
@@ -1188,42 +1294,47 @@ class RelationshipManager:
|
|
|
1188
1294
|
self,
|
|
1189
1295
|
db: InfrahubDatabase,
|
|
1190
1296
|
peer_data: RelationshipPeerData,
|
|
1297
|
+
user_id: str,
|
|
1191
1298
|
at: Timestamp | None = None,
|
|
1192
1299
|
) -> None:
|
|
1193
1300
|
remove_at = Timestamp(at)
|
|
1194
1301
|
branch = self.get_branch_based_on_support_type()
|
|
1195
1302
|
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1303
|
+
source_schema = db.schema.get(name=self.node.get_kind(), branch=self.branch, duplicate=False)
|
|
1304
|
+
if _use_global_branch(schema=source_schema):
|
|
1305
|
+
source_branch = registry.get_global_branch()
|
|
1306
|
+
else:
|
|
1307
|
+
source_branch = self.branch
|
|
1308
|
+
destination_schema = db.schema.get(name=self.schema.peer, branch=self.branch, duplicate=False)
|
|
1309
|
+
if _use_global_branch(schema=destination_schema):
|
|
1310
|
+
destination_branch = registry.get_global_branch()
|
|
1311
|
+
else:
|
|
1312
|
+
destination_branch = self.branch
|
|
1206
1313
|
|
|
1207
|
-
|
|
1314
|
+
delete_query = await RelationshipDeleteQuery.init(
|
|
1208
1315
|
db=db,
|
|
1209
|
-
|
|
1316
|
+
rel_id=peer_data.rel_node_id,
|
|
1210
1317
|
schema=self.schema,
|
|
1211
|
-
|
|
1212
|
-
|
|
1318
|
+
source_id=peer_data.source_id,
|
|
1319
|
+
destination_id=peer_data.peer_id,
|
|
1320
|
+
source_branch=source_branch,
|
|
1321
|
+
destination_branch=destination_branch,
|
|
1213
1322
|
branch=branch,
|
|
1323
|
+
user_id=user_id,
|
|
1214
1324
|
at=remove_at,
|
|
1215
1325
|
)
|
|
1216
|
-
await
|
|
1326
|
+
await delete_query.execute(db=db)
|
|
1217
1327
|
|
|
1218
1328
|
async def save(
|
|
1219
|
-
self, db: InfrahubDatabase, at: Timestamp | None = None
|
|
1329
|
+
self, db: InfrahubDatabase, user_id: str = SYSTEM_USER_ID, at: Timestamp | None = None
|
|
1220
1330
|
) -> RelationshipCardinalityManyChangelog | RelationshipCardinalityOneChangelog:
|
|
1221
1331
|
"""Create or Update the Relationship in the database."""
|
|
1222
1332
|
|
|
1223
1333
|
await self.resolve(db=db)
|
|
1334
|
+
branch_agnostic = self.schema.branch is BranchSupportType.AGNOSTIC
|
|
1224
1335
|
|
|
1225
1336
|
save_at = Timestamp(at)
|
|
1226
|
-
details = await self.fetch_relationship_ids(db=db, force_refresh=True)
|
|
1337
|
+
details = await self.fetch_relationship_ids(db=db, branch_agnostic=branch_agnostic, force_refresh=True)
|
|
1227
1338
|
relationship_mapper = ChangelogRelationshipMapper(schema=self.schema)
|
|
1228
1339
|
|
|
1229
1340
|
# If we have previously fetched the relationships from the database
|
|
@@ -1231,13 +1342,13 @@ class RelationshipManager:
|
|
|
1231
1342
|
if self.has_fetched_relationships:
|
|
1232
1343
|
for peer_id in details.peer_ids_present_database_only:
|
|
1233
1344
|
relationship_mapper.remove_peer(peer_data=details.peers_database[peer_id])
|
|
1234
|
-
await self.remove_in_db(peer_data=details.peers_database[peer_id],
|
|
1345
|
+
await self.remove_in_db(db=db, peer_data=details.peers_database[peer_id], user_id=user_id, at=save_at)
|
|
1235
1346
|
|
|
1236
1347
|
# Create the new relationship that are not present in the database
|
|
1237
1348
|
# and Compare the existing one
|
|
1238
|
-
for rel in await self.get_relationships(db=db):
|
|
1349
|
+
for rel in await self.get_relationships(db=db, branch_agnostic=branch_agnostic):
|
|
1239
1350
|
if rel.peer_id in details.peer_ids_present_local_only:
|
|
1240
|
-
await rel.save(
|
|
1351
|
+
await rel.save(db=db, user_id=user_id, at=save_at)
|
|
1241
1352
|
|
|
1242
1353
|
relationship_mapper.add_peer_from_relationship(relationship=rel)
|
|
1243
1354
|
|
|
@@ -1247,6 +1358,7 @@ class RelationshipManager:
|
|
|
1247
1358
|
):
|
|
1248
1359
|
await rel.update(
|
|
1249
1360
|
at=save_at,
|
|
1361
|
+
user_id=user_id,
|
|
1250
1362
|
properties_to_update=properties_not_matching,
|
|
1251
1363
|
data=details.peers_database[rel.peer_id],
|
|
1252
1364
|
db=db,
|
|
@@ -1302,3 +1414,45 @@ class RelationshipManager:
|
|
|
1302
1414
|
|
|
1303
1415
|
if self.name == "children" and not schema.children: # type: ignore[union-attr]
|
|
1304
1416
|
raise ValidationError({self.name: f"Not supported to assign some children for {schema.kind}"})
|
|
1417
|
+
|
|
1418
|
+
async def update_relationships(
|
|
1419
|
+
self,
|
|
1420
|
+
db: InfrahubDatabase,
|
|
1421
|
+
relationships_to_remove: list[Relationship],
|
|
1422
|
+
relationships_to_insert: list[Relationship],
|
|
1423
|
+
) -> bool:
|
|
1424
|
+
is_changed = False
|
|
1425
|
+
len_rels_to_remove = len(relationships_to_remove)
|
|
1426
|
+
len_rels_to_insert = len(relationships_to_insert)
|
|
1427
|
+
|
|
1428
|
+
if len_rels_to_remove and not len_rels_to_insert:
|
|
1429
|
+
for rel_to_remove in relationships_to_remove:
|
|
1430
|
+
if rel_to_remove.peer_id:
|
|
1431
|
+
await self.remove_locally(peer_id=rel_to_remove.peer_id, db=db)
|
|
1432
|
+
is_changed = True
|
|
1433
|
+
elif len_rels_to_insert and not len_rels_to_remove:
|
|
1434
|
+
for rel_to_insert in relationships_to_insert:
|
|
1435
|
+
self._relationships.append(rel_to_insert)
|
|
1436
|
+
is_changed = True
|
|
1437
|
+
elif len_rels_to_remove == len_rels_to_insert:
|
|
1438
|
+
for rel_to_remove, rel_to_insert in zip(relationships_to_remove, relationships_to_insert, strict=True):
|
|
1439
|
+
self._relationships.replace(rel_to_insert=rel_to_insert, rel_to_remove=rel_to_remove)
|
|
1440
|
+
is_changed = True
|
|
1441
|
+
elif len_rels_to_remove > len_rels_to_insert:
|
|
1442
|
+
for rel_to_remove, rel_to_insert in zip(
|
|
1443
|
+
relationships_to_remove[:len_rels_to_insert], relationships_to_insert, strict=True
|
|
1444
|
+
):
|
|
1445
|
+
self._relationships.replace(rel_to_insert=rel_to_insert, rel_to_remove=rel_to_remove)
|
|
1446
|
+
for rel_to_remove in relationships_to_remove[len_rels_to_insert:]:
|
|
1447
|
+
if rel_to_remove.peer_id:
|
|
1448
|
+
await self.remove_locally(peer_id=rel_to_remove.peer_id, db=db)
|
|
1449
|
+
is_changed = True
|
|
1450
|
+
elif len_rels_to_insert > len_rels_to_remove:
|
|
1451
|
+
for rel_to_insert, rel_to_remove in zip(
|
|
1452
|
+
relationships_to_insert[:len_rels_to_remove], relationships_to_remove, strict=True
|
|
1453
|
+
):
|
|
1454
|
+
self._relationships.replace(rel_to_insert=rel_to_insert, rel_to_remove=rel_to_remove)
|
|
1455
|
+
for rel_to_insert in relationships_to_insert[len_rels_to_remove:]:
|
|
1456
|
+
self._relationships.append(rel_to_insert)
|
|
1457
|
+
is_changed = True
|
|
1458
|
+
return is_changed
|