infrahub-server 1.6.3__py3-none-any.whl → 1.7.0b0__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/schema.py +3 -1
- infrahub/artifacts/tasks.py +1 -0
- infrahub/auth.py +2 -2
- infrahub/cli/db.py +6 -6
- 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 +5 -8
- 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/data_check_synchronizer.py +3 -2
- infrahub/core/diff/enricher/cardinality_one.py +1 -1
- infrahub/core/diff/merger/merger.py +27 -1
- 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/graph/__init__.py +1 -1
- infrahub/core/graph/constraints.py +2 -2
- infrahub/core/graph/schema.py +2 -12
- infrahub/core/manager.py +132 -126
- 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/m013_convert_git_password_credential.py +3 -8
- 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/m049_remove_is_visible_relationship.py +38 -0
- infrahub/core/migrations/graph/m050_backfill_vertex_metadata.py +168 -0
- 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/schema/attribute_kind_update.py +25 -7
- infrahub/core/migrations/schema/attribute_supports_profile.py +3 -1
- infrahub/core/migrations/schema/models.py +3 -0
- infrahub/core/migrations/schema/node_attribute_add.py +4 -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 +156 -57
- infrahub/core/node/create.py +7 -3
- infrahub/core/node/standard.py +100 -14
- infrahub/core/property.py +0 -1
- infrahub/core/protocols_base.py +6 -2
- infrahub/core/query/__init__.py +6 -7
- infrahub/core/query/attribute.py +161 -46
- infrahub/core/query/branch.py +57 -69
- infrahub/core/query/diff.py +4 -4
- infrahub/core/query/node.py +618 -180
- infrahub/core/query/relationship.py +449 -300
- infrahub/core/query/standard_node.py +25 -5
- infrahub/core/query/utils.py +2 -4
- infrahub/core/relationship/constraints/profiles_removal.py +168 -0
- infrahub/core/relationship/model.py +293 -139
- infrahub/core/schema/attribute_parameters.py +1 -28
- infrahub/core/schema/attribute_schema.py +17 -11
- infrahub/core/schema/manager.py +63 -43
- infrahub/core/schema/relationship_schema.py +6 -2
- infrahub/core/schema/schema_branch.py +48 -76
- infrahub/core/task/task.py +4 -2
- infrahub/core/utils.py +0 -22
- infrahub/core/validators/attribute/kind.py +2 -5
- infrahub/core/validators/determiner.py +3 -3
- infrahub/database/__init__.py +3 -3
- 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/graphql/app.py +2 -2
- infrahub/graphql/constants.py +3 -0
- infrahub/graphql/context.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 +158 -18
- infrahub/graphql/metadata.py +91 -0
- infrahub/graphql/models.py +33 -3
- infrahub/graphql/mutations/account.py +5 -5
- 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/queries/branch.py +62 -6
- infrahub/graphql/queries/diff/tree.py +5 -5
- 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 +16 -12
- 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 +4 -3
- infrahub/hfid/tasks.py +12 -3
- 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 +153 -12
- infrahub/profiles/queries/get_profile_data.py +143 -31
- infrahub/profiles/tasks.py +79 -27
- infrahub/profiles/triggers.py +22 -0
- infrahub/proposed_change/tasks.py +4 -1
- 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/models.py +1 -1
- infrahub/webhook/tasks.py +1 -1
- infrahub/workers/dependencies.py +9 -3
- infrahub/workers/infrahub_async.py +13 -4
- infrahub/workflows/catalogue.py +19 -0
- infrahub_sdk/node/constants.py +1 -0
- infrahub_sdk/node/related_node.py +13 -4
- infrahub_sdk/node/relationship.py +8 -0
- {infrahub_server-1.6.3.dist-info → infrahub_server-1.7.0b0.dist-info}/METADATA +17 -16
- {infrahub_server-1.6.3.dist-info → infrahub_server-1.7.0b0.dist-info}/RECORD +161 -143
- infrahub_testcontainers/container.py +3 -3
- infrahub_testcontainers/docker-compose-cluster.test.yml +7 -7
- infrahub_testcontainers/docker-compose.test.yml +13 -5
- {infrahub_server-1.6.3.dist-info → infrahub_server-1.7.0b0.dist-info}/WHEEL +0 -0
- {infrahub_server-1.6.3.dist-info → infrahub_server-1.7.0b0.dist-info}/entry_points.txt +0 -0
- {infrahub_server-1.6.3.dist-info → infrahub_server-1.7.0b0.dist-info}/licenses/LICENSE.txt +0 -0
infrahub/core/branch/models.py
CHANGED
|
@@ -6,17 +6,15 @@ from typing import TYPE_CHECKING, Any, Optional, Self, Union, cast
|
|
|
6
6
|
from pydantic import Field, field_validator
|
|
7
7
|
|
|
8
8
|
from infrahub.core.branch.enums import BranchStatus
|
|
9
|
-
from infrahub.core.constants import GLOBAL_BRANCH_NAME
|
|
9
|
+
from infrahub.core.constants import GLOBAL_BRANCH_NAME, SYSTEM_USER_ID
|
|
10
10
|
from infrahub.core.graph import GRAPH_VERSION
|
|
11
11
|
from infrahub.core.models import SchemaBranchHash # noqa: TC001
|
|
12
|
-
from infrahub.core.node.standard import StandardNode
|
|
12
|
+
from infrahub.core.node.standard import StandardNode, StandardNodeOrdering
|
|
13
13
|
from infrahub.core.query import Query, QueryType
|
|
14
14
|
from infrahub.core.query.branch import (
|
|
15
15
|
BranchNodeGetListQuery,
|
|
16
16
|
DeleteBranchRelationshipsQuery,
|
|
17
|
-
|
|
18
|
-
RebaseBranchDeleteRelationshipQuery,
|
|
19
|
-
RebaseBranchUpdateRelationshipQuery,
|
|
17
|
+
RebaseBranchQuery,
|
|
20
18
|
)
|
|
21
19
|
from infrahub.core.registry import registry
|
|
22
20
|
from infrahub.core.timestamp import Timestamp
|
|
@@ -37,7 +35,6 @@ class Branch(StandardNode):
|
|
|
37
35
|
origin_branch: str = "main"
|
|
38
36
|
branched_from: Optional[str] = Field(default=None, validate_default=True)
|
|
39
37
|
hierarchy_level: int = 2
|
|
40
|
-
created_at: Optional[str] = Field(default=None, validate_default=True)
|
|
41
38
|
is_default: bool = False
|
|
42
39
|
is_global: bool = False
|
|
43
40
|
is_protected: bool = False
|
|
@@ -93,11 +90,6 @@ class Branch(StandardNode):
|
|
|
93
90
|
raise RuntimeError(f"branched_from not set for branch {self.name}")
|
|
94
91
|
return self.branched_from
|
|
95
92
|
|
|
96
|
-
@field_validator("created_at", mode="before")
|
|
97
|
-
@classmethod
|
|
98
|
-
def set_created_at(cls, value: str) -> str:
|
|
99
|
-
return Timestamp(value).to_string()
|
|
100
|
-
|
|
101
93
|
def get_created_at(self) -> str:
|
|
102
94
|
if not self.created_at:
|
|
103
95
|
raise RuntimeError(f"created_at not set for branch {self.name}")
|
|
@@ -162,10 +154,18 @@ class Branch(StandardNode):
|
|
|
162
154
|
limit: int = 1000,
|
|
163
155
|
ids: list[str] | None = None,
|
|
164
156
|
name: str | None = None,
|
|
157
|
+
node_ordering: StandardNodeOrdering | None = None,
|
|
165
158
|
**kwargs: Any,
|
|
166
159
|
) -> list[Self]:
|
|
160
|
+
node_ordering = node_ordering or StandardNodeOrdering()
|
|
167
161
|
query: Query = await BranchNodeGetListQuery.init(
|
|
168
|
-
db=db,
|
|
162
|
+
db=db,
|
|
163
|
+
node_class=cls,
|
|
164
|
+
ids=ids,
|
|
165
|
+
node_name=name,
|
|
166
|
+
limit=limit,
|
|
167
|
+
node_ordering=node_ordering,
|
|
168
|
+
**kwargs,
|
|
169
169
|
)
|
|
170
170
|
await query.execute(db=db)
|
|
171
171
|
|
|
@@ -178,10 +178,18 @@ class Branch(StandardNode):
|
|
|
178
178
|
limit: int = 1000,
|
|
179
179
|
ids: list[str] | None = None,
|
|
180
180
|
name: str | None = None,
|
|
181
|
+
partial_match: bool = False,
|
|
181
182
|
**kwargs: Any,
|
|
182
183
|
) -> int:
|
|
183
184
|
query: Query = await BranchNodeGetListQuery.init(
|
|
184
|
-
db=db,
|
|
185
|
+
db=db,
|
|
186
|
+
node_class=cls,
|
|
187
|
+
ids=ids,
|
|
188
|
+
node_name=name,
|
|
189
|
+
limit=limit,
|
|
190
|
+
exclude_global=True,
|
|
191
|
+
partial_match=partial_match,
|
|
192
|
+
**kwargs,
|
|
185
193
|
)
|
|
186
194
|
return await query.count(db=db)
|
|
187
195
|
|
|
@@ -208,7 +216,7 @@ class Branch(StandardNode):
|
|
|
208
216
|
|
|
209
217
|
return [default_branch, self.name]
|
|
210
218
|
|
|
211
|
-
def get_branches_and_times_to_query(self, at: Optional[
|
|
219
|
+
def get_branches_and_times_to_query(self, at: Optional[Timestamp] = None) -> dict[frozenset, str]:
|
|
212
220
|
"""Return all the names of the branches that are constituing this branch with the associated times excluding the global branch"""
|
|
213
221
|
|
|
214
222
|
at = Timestamp(at)
|
|
@@ -229,7 +237,7 @@ class Branch(StandardNode):
|
|
|
229
237
|
|
|
230
238
|
def get_branches_and_times_to_query_global(
|
|
231
239
|
self,
|
|
232
|
-
at: Optional[
|
|
240
|
+
at: Optional[Timestamp] = None,
|
|
233
241
|
is_isolated: bool = True,
|
|
234
242
|
) -> dict[frozenset, str]:
|
|
235
243
|
"""Return all the names of the branches that are constituting this branch with the associated times."""
|
|
@@ -281,9 +289,9 @@ class Branch(StandardNode):
|
|
|
281
289
|
|
|
282
290
|
return start, end
|
|
283
291
|
|
|
284
|
-
async def create(self, db: InfrahubDatabase) -> bool:
|
|
292
|
+
async def create(self, db: InfrahubDatabase, user_id: str = SYSTEM_USER_ID) -> bool:
|
|
285
293
|
self.graph_version = GRAPH_VERSION
|
|
286
|
-
return await super().create(db=db)
|
|
294
|
+
return await super().create(db=db, user_id=user_id)
|
|
287
295
|
|
|
288
296
|
async def delete(self, db: InfrahubDatabase) -> None:
|
|
289
297
|
if self.is_default:
|
|
@@ -299,7 +307,7 @@ class Branch(StandardNode):
|
|
|
299
307
|
await super().delete(db=db)
|
|
300
308
|
|
|
301
309
|
def get_query_filter_relationships(
|
|
302
|
-
self, rel_labels: list, at: Optional[
|
|
310
|
+
self, rel_labels: list, at: Optional[Timestamp] = None, include_outside_parentheses: bool = False
|
|
303
311
|
) -> tuple[list, dict]:
|
|
304
312
|
"""
|
|
305
313
|
Generate a CYPHER Query filter based on a list of relationships to query a part of the graph at a specific time and on a specific branch.
|
|
@@ -362,7 +370,7 @@ class Branch(StandardNode):
|
|
|
362
370
|
params[f"{pp}time1"] = at_str
|
|
363
371
|
return filter_str, params
|
|
364
372
|
|
|
365
|
-
branches_times = self.get_branches_and_times_to_query_global(at=
|
|
373
|
+
branches_times = self.get_branches_and_times_to_query_global(at=at, is_isolated=is_isolated)
|
|
366
374
|
|
|
367
375
|
for idx, (branch_name, time_to_query) in enumerate(branches_times.items()):
|
|
368
376
|
params[f"{pp}branch{idx}"] = list(branch_name)
|
|
@@ -387,8 +395,8 @@ class Branch(StandardNode):
|
|
|
387
395
|
def get_query_filter_relationships_range(
|
|
388
396
|
self,
|
|
389
397
|
rel_labels: list,
|
|
390
|
-
start_time:
|
|
391
|
-
end_time:
|
|
398
|
+
start_time: Timestamp,
|
|
399
|
+
end_time: Timestamp,
|
|
392
400
|
include_outside_parentheses: bool = False,
|
|
393
401
|
include_global: bool = False,
|
|
394
402
|
) -> tuple[list, dict]:
|
|
@@ -468,9 +476,7 @@ class Branch(StandardNode):
|
|
|
468
476
|
|
|
469
477
|
return filters, params
|
|
470
478
|
|
|
471
|
-
def get_query_filter_range(
|
|
472
|
-
self, rel_label: list, start_time: Union[Timestamp, str], end_time: Union[Timestamp, str]
|
|
473
|
-
) -> tuple[list, dict]:
|
|
479
|
+
def get_query_filter_range(self, rel_label: list, start_time: Timestamp, end_time: Timestamp) -> tuple[list, dict]:
|
|
474
480
|
"""
|
|
475
481
|
Generate a CYPHER Query filter to query a range of values in the graph between start_time and end_time."""
|
|
476
482
|
|
|
@@ -496,67 +502,34 @@ class Branch(StandardNode):
|
|
|
496
502
|
|
|
497
503
|
return filters, params
|
|
498
504
|
|
|
499
|
-
async def rebase(self, db: InfrahubDatabase,
|
|
505
|
+
async def rebase(self, db: InfrahubDatabase, user_id: str = SYSTEM_USER_ID) -> None:
|
|
500
506
|
"""Rebase the current Branch with its origin branch"""
|
|
501
507
|
|
|
502
|
-
at = Timestamp(
|
|
503
|
-
|
|
504
|
-
# Find all relationships with the name of the branch
|
|
505
|
-
# Delete all relationship that have a to date defined in the past
|
|
506
|
-
# Update the from time on all other relationships
|
|
507
|
-
# If conflict is set, ignore the one with Drop
|
|
508
|
+
at = Timestamp()
|
|
508
509
|
|
|
509
510
|
await self.rebase_graph(db=db, at=at)
|
|
510
511
|
|
|
511
|
-
# FIXME, we must ensure that there is no conflict before rebasing a branch
|
|
512
|
-
# Otherwise we could endup with a complicated situation
|
|
513
512
|
self.branched_from = at.to_string()
|
|
514
513
|
self.status = BranchStatus.OPEN
|
|
515
|
-
await self.save(db=db)
|
|
514
|
+
await self.save(db=db, user_id=user_id)
|
|
516
515
|
|
|
517
516
|
# Update the branch in the registry after the rebase
|
|
518
517
|
registry.branch[self.name] = self
|
|
519
518
|
|
|
520
|
-
async def rebase_graph(self, db: InfrahubDatabase, at:
|
|
521
|
-
|
|
519
|
+
async def rebase_graph(self, db: InfrahubDatabase, at: Timestamp) -> None:
|
|
520
|
+
"""Rebase all relationships on this branch to a new point in time.
|
|
522
521
|
|
|
523
|
-
|
|
524
|
-
|
|
522
|
+
This method updates the graph to reflect the state of the branch as if it had been created
|
|
523
|
+
at the specified timestamp. Relationships are processed as follows:
|
|
525
524
|
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
element_id = result.get("r").element_id
|
|
525
|
+
- Relationships with no `to` timestamp and `from` <= at: Updated to start from `at`
|
|
526
|
+
- Relationships with `to` < at: Deleted (ended before rebase point)
|
|
527
|
+
- Relationships with `to` >= at: Updated to start from `at`
|
|
530
528
|
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
time_to_str = result.get("r").get("to", None)
|
|
537
|
-
time_from_str = result.get("r").get("from")
|
|
538
|
-
time_from = Timestamp(time_from_str)
|
|
539
|
-
|
|
540
|
-
if not time_to_str and time_from_str and time_from <= at:
|
|
541
|
-
rels_to_update.append(element_id)
|
|
542
|
-
continue
|
|
543
|
-
|
|
544
|
-
if not time_to_str and time_from_str and time_from > at:
|
|
545
|
-
rels_to_delete.append(element_id)
|
|
546
|
-
continue
|
|
547
|
-
|
|
548
|
-
time_to = Timestamp(time_to_str)
|
|
549
|
-
if time_to < at:
|
|
550
|
-
rels_to_delete.append(element_id)
|
|
551
|
-
continue
|
|
552
|
-
|
|
553
|
-
rels_to_update.append(element_id)
|
|
554
|
-
|
|
555
|
-
update_query = await RebaseBranchUpdateRelationshipQuery.init(db=db, ids=rels_to_update, at=at)
|
|
556
|
-
await update_query.execute(db=db)
|
|
557
|
-
|
|
558
|
-
delete_query = await RebaseBranchDeleteRelationshipQuery.init(db=db, ids=rels_to_delete, at=at)
|
|
559
|
-
await delete_query.execute(db=db)
|
|
529
|
+
Orphaned nodes (nodes with no remaining relationships) are also cleaned up.
|
|
530
|
+
"""
|
|
531
|
+
query = await RebaseBranchQuery.init(db=db, branch=self, at=at)
|
|
532
|
+
await query.execute(db=db)
|
|
560
533
|
|
|
561
534
|
|
|
562
535
|
registry.branch_object = Branch
|
infrahub/core/branch/tasks.py
CHANGED
|
@@ -170,7 +170,7 @@ async def rebase_branch(branch: str, context: InfrahubContext, send_events: bool
|
|
|
170
170
|
migrations = []
|
|
171
171
|
async with lock.registry.global_graph_lock():
|
|
172
172
|
async with db.start_transaction() as dbt:
|
|
173
|
-
await obj.rebase(db=dbt)
|
|
173
|
+
await obj.rebase(db=dbt, user_id=context.account.account_id)
|
|
174
174
|
log.info("Branch successfully rebased")
|
|
175
175
|
|
|
176
176
|
if obj.has_schema_changes:
|
|
@@ -187,7 +187,7 @@ async def rebase_branch(branch: str, context: InfrahubContext, send_events: bool
|
|
|
187
187
|
)
|
|
188
188
|
registry.schema.set_schema_branch(name=obj.name, schema=updated_schema)
|
|
189
189
|
obj.update_schema_hash()
|
|
190
|
-
await obj.save(db=db)
|
|
190
|
+
await obj.save(db=db, user_id=context.account.account_id)
|
|
191
191
|
|
|
192
192
|
# Execute the migrations
|
|
193
193
|
migrations = await merger.calculate_migrations(target_schema=updated_schema)
|
|
@@ -198,6 +198,7 @@ async def rebase_branch(branch: str, context: InfrahubContext, send_events: bool
|
|
|
198
198
|
new_schema=candidate_schema,
|
|
199
199
|
previous_schema=schema_in_main_before,
|
|
200
200
|
migrations=migrations,
|
|
201
|
+
user_id=context.account.account_id,
|
|
201
202
|
)
|
|
202
203
|
)
|
|
203
204
|
for error in errors:
|
|
@@ -302,6 +303,7 @@ async def merge_branch(branch: str, context: InfrahubContext, proposed_change_id
|
|
|
302
303
|
new_schema=merger.destination_schema,
|
|
303
304
|
previous_schema=merger.initial_source_schema,
|
|
304
305
|
migrations=merger.migrations,
|
|
306
|
+
user_id=context.account.account_id,
|
|
305
307
|
)
|
|
306
308
|
)
|
|
307
309
|
for error in errors:
|
|
@@ -431,7 +433,7 @@ async def create_branch(model: BranchCreateModel, context: InfrahubContext) -> N
|
|
|
431
433
|
new_schema = origin_schema.duplicate(name=obj.name)
|
|
432
434
|
registry.schema.set_schema_branch(name=obj.name, schema=new_schema)
|
|
433
435
|
obj.update_schema_hash()
|
|
434
|
-
await obj.save(db=db)
|
|
436
|
+
await obj.save(db=db, user_id=context.account.account_id)
|
|
435
437
|
|
|
436
438
|
# Add Branch to registry
|
|
437
439
|
registry.branch[obj.name] = obj
|
infrahub/core/changelog/diff.py
CHANGED
|
@@ -116,13 +116,6 @@ class DiffChangelogCollector:
|
|
|
116
116
|
value_current=self._convert_string_boolean_value(value=attr_property.new_value),
|
|
117
117
|
value_previous=self._convert_string_boolean_value(value=attr_property.previous_value),
|
|
118
118
|
)
|
|
119
|
-
case DatabaseEdgeType.IS_VISIBLE:
|
|
120
|
-
if _keep_branch_update(diff_property=attr_property):
|
|
121
|
-
changelog_attribute.add_property(
|
|
122
|
-
name="is_visible",
|
|
123
|
-
value_current=self._convert_string_boolean_value(value=attr_property.new_value),
|
|
124
|
-
value_previous=self._convert_string_boolean_value(value=attr_property.previous_value),
|
|
125
|
-
)
|
|
126
119
|
case DatabaseEdgeType.HAS_SOURCE:
|
|
127
120
|
if _keep_branch_update(diff_property=attr_property):
|
|
128
121
|
changelog_attribute.add_property(
|
|
@@ -176,12 +169,6 @@ class DiffChangelogCollector:
|
|
|
176
169
|
value_current=self._convert_string_boolean_value(value=rel_prop.new_value),
|
|
177
170
|
value_previous=self._convert_string_boolean_value(value=rel_prop.previous_value),
|
|
178
171
|
)
|
|
179
|
-
case DatabaseEdgeType.IS_VISIBLE:
|
|
180
|
-
changelog_rel.add_property(
|
|
181
|
-
name="is_visible",
|
|
182
|
-
value_current=self._convert_string_boolean_value(value=rel_prop.new_value),
|
|
183
|
-
value_previous=self._convert_string_boolean_value(value=rel_prop.previous_value),
|
|
184
|
-
)
|
|
185
172
|
case DatabaseEdgeType.HAS_OWNER:
|
|
186
173
|
changelog_rel.add_property(
|
|
187
174
|
name="owner",
|
|
@@ -198,7 +185,7 @@ class DiffChangelogCollector:
|
|
|
198
185
|
node.add_relationship(relationship_changelog=changelog_rel)
|
|
199
186
|
|
|
200
187
|
def _convert_string_boolean_value(self, value: str | None) -> bool | None:
|
|
201
|
-
"""Convert string based boolean for is_protected
|
|
188
|
+
"""Convert string based boolean for is_protected."""
|
|
202
189
|
if value is not None:
|
|
203
190
|
return str_to_bool(value)
|
|
204
191
|
|
|
@@ -218,12 +205,6 @@ class DiffChangelogCollector:
|
|
|
218
205
|
)
|
|
219
206
|
for peer_prop in peer.properties:
|
|
220
207
|
match peer_prop.property_type:
|
|
221
|
-
case DatabaseEdgeType.IS_VISIBLE:
|
|
222
|
-
peer_log.add_property(
|
|
223
|
-
name="is_visible",
|
|
224
|
-
value_current=self._convert_string_boolean_value(value=peer_prop.new_value),
|
|
225
|
-
value_previous=self._convert_string_boolean_value(value=peer_prop.previous_value),
|
|
226
|
-
)
|
|
227
208
|
case DatabaseEdgeType.IS_PROTECTED:
|
|
228
209
|
peer_log.add_property(
|
|
229
210
|
name="is_protected",
|
|
@@ -186,9 +186,6 @@ class RelationshipCardinalityManyChangelog(BaseModel):
|
|
|
186
186
|
properties["is_protected"] = PropertyChangelog(
|
|
187
187
|
name="is_protected", value=relationship.is_protected, value_previous=None
|
|
188
188
|
)
|
|
189
|
-
properties["is_visible"] = PropertyChangelog(
|
|
190
|
-
name="is_visible", value=relationship.is_protected, value_previous=None
|
|
191
|
-
)
|
|
192
189
|
if owner := getattr(relationship, "owner_id", None):
|
|
193
190
|
properties["owner"] = PropertyChangelog(name="owner", value=owner, value_previous=None)
|
|
194
191
|
if source := getattr(relationship, "source_id", None):
|
|
@@ -280,9 +277,6 @@ class NodeChangelog(BaseModel):
|
|
|
280
277
|
changelog_relationship.add_property(
|
|
281
278
|
name="is_protected", value_current=relationship.is_protected, value_previous=None
|
|
282
279
|
)
|
|
283
|
-
changelog_relationship.add_property(
|
|
284
|
-
name="is_visible", value_current=relationship.is_visible, value_previous=None
|
|
285
|
-
)
|
|
286
280
|
self.relationships[changelog_relationship.name] = changelog_relationship
|
|
287
281
|
elif relationship.schema.cardinality == RelationshipCardinality.MANY:
|
|
288
282
|
if relationship.schema.name not in self.relationships:
|
|
@@ -340,7 +334,6 @@ class NodeChangelog(BaseModel):
|
|
|
340
334
|
if owner_id := getattr(attribute, "owner_id", None):
|
|
341
335
|
changelog_attribute.add_property(name="owner", value_current=owner_id, value_previous=None)
|
|
342
336
|
changelog_attribute.add_property(name="is_protected", value_current=attribute.is_protected, value_previous=None)
|
|
343
|
-
changelog_attribute.add_property(name="is_visible", value_current=attribute.is_visible, value_previous=None)
|
|
344
337
|
self.attributes[changelog_attribute.name] = changelog_attribute
|
|
345
338
|
|
|
346
339
|
def get_related_nodes(self) -> list[ChangelogRelatedNode]:
|
|
@@ -46,6 +46,8 @@ NULL_VALUE = "NULL"
|
|
|
46
46
|
|
|
47
47
|
EVENT_NAMESPACE = "infrahub"
|
|
48
48
|
|
|
49
|
+
SYSTEM_USER_ID = "__system__"
|
|
50
|
+
|
|
49
51
|
|
|
50
52
|
class EventType(InfrahubStringEnum):
|
|
51
53
|
BRANCH_CREATED = f"{EVENT_NAMESPACE}.branch.created"
|
|
@@ -359,6 +361,21 @@ class AttributeDBNodeType(Flag):
|
|
|
359
361
|
IPNETWORK = DEFAULT | INDEX_ONLY | IPNETWORK_ONLY
|
|
360
362
|
|
|
361
363
|
|
|
364
|
+
class MetadataOptions(Flag):
|
|
365
|
+
NONE = 0
|
|
366
|
+
SOURCE = auto()
|
|
367
|
+
OWNER = auto()
|
|
368
|
+
LINKED_NODES = SOURCE | OWNER
|
|
369
|
+
IS_PROTECTED = auto()
|
|
370
|
+
CREATED_BY = auto()
|
|
371
|
+
CREATED_AT = auto()
|
|
372
|
+
UPDATED_BY = auto()
|
|
373
|
+
UPDATED_AT = auto()
|
|
374
|
+
TIMESTAMPS = CREATED_AT | UPDATED_AT
|
|
375
|
+
USERS = CREATED_BY | UPDATED_BY
|
|
376
|
+
USER_TIMESTAMPS = TIMESTAMPS | USERS
|
|
377
|
+
|
|
378
|
+
|
|
362
379
|
RESTRICTED_NAMESPACES: list[str] = [
|
|
363
380
|
"Account",
|
|
364
381
|
"Branch",
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from infrahub import lock
|
|
2
2
|
from infrahub.core.branch import Branch
|
|
3
|
-
from infrahub.core.constants.infrahubkind import REPOSITORYVALIDATOR, USERVALIDATOR
|
|
4
3
|
from infrahub.core.convert_object_type.object_conversion import (
|
|
5
4
|
ConversionFieldInput,
|
|
6
5
|
convert_object_type,
|
|
@@ -8,7 +7,7 @@ from infrahub.core.convert_object_type.object_conversion import (
|
|
|
8
7
|
)
|
|
9
8
|
from infrahub.core.manager import NodeManager
|
|
10
9
|
from infrahub.core.node import Node
|
|
11
|
-
from infrahub.core.protocols import CoreReadOnlyRepository, CoreRepository
|
|
10
|
+
from infrahub.core.protocols import CoreReadOnlyRepository, CoreRepository, CoreRepositoryValidator, CoreUserValidator
|
|
12
11
|
from infrahub.core.schema import NodeSchema
|
|
13
12
|
from infrahub.core.timestamp import Timestamp
|
|
14
13
|
from infrahub.database import InfrahubDatabase
|
|
@@ -35,11 +34,11 @@ async def convert_repository_type(
|
|
|
35
34
|
|
|
36
35
|
# Fetch validators before deleting the repository otherwise validator-repository would no longer exist
|
|
37
36
|
user_validators = await NodeManager.query(
|
|
38
|
-
db=dbt, schema=
|
|
37
|
+
db=dbt, schema=CoreUserValidator, prefetch_relationships=True, filters={"repository__id": repository.id}
|
|
39
38
|
)
|
|
40
39
|
repository_validators = await NodeManager.query(
|
|
41
40
|
db=dbt,
|
|
42
|
-
schema=
|
|
41
|
+
schema=CoreRepositoryValidator,
|
|
43
42
|
prefetch_relationships=True,
|
|
44
43
|
filters={"repository__id": repository.id},
|
|
45
44
|
)
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
from enum import Enum
|
|
2
2
|
|
|
3
|
-
from infrahub.core.constants import BranchConflictKeep
|
|
3
|
+
from infrahub.core.constants import BranchConflictKeep
|
|
4
4
|
from infrahub.core.diff.query.filters import EnrichedDiffQueryFilters
|
|
5
5
|
from infrahub.core.integrity.object_conflict.conflict_recorder import ObjectConflictValidatorRecorder
|
|
6
6
|
from infrahub.core.manager import NodeManager
|
|
7
7
|
from infrahub.core.node import Node
|
|
8
|
+
from infrahub.core.protocols import CoreProposedChange
|
|
8
9
|
from infrahub.database import InfrahubDatabase
|
|
9
10
|
from infrahub.exceptions import SchemaNotFoundError
|
|
10
11
|
from infrahub.proposed_change.constants import ProposedChangeState
|
|
@@ -52,7 +53,7 @@ class DiffDataCheckSynchronizer:
|
|
|
52
53
|
try:
|
|
53
54
|
proposed_changes = await NodeManager.query(
|
|
54
55
|
db=self.db,
|
|
55
|
-
schema=
|
|
56
|
+
schema=CoreProposedChange,
|
|
56
57
|
filters={"source_branch": enriched_diff.diff_branch_name, "state": ProposedChangeState.OPEN},
|
|
57
58
|
)
|
|
58
59
|
except SchemaNotFoundError:
|
|
@@ -28,7 +28,7 @@ class DiffCardinalityOneEnricher(DiffEnricherInterface):
|
|
|
28
28
|
- the peer_id property of the element will be the latest non-null peer ID for this element
|
|
29
29
|
- the element MUST have an EnrichedDiffProperty of property_type=IS_RELATED that correctly records
|
|
30
30
|
the previous and new values of the peer ID for this element
|
|
31
|
-
- changes to properties (
|
|
31
|
+
- changes to properties (IS_PROTECTED, etc) of a cardinality=one relationship are consolidated as well
|
|
32
32
|
"""
|
|
33
33
|
|
|
34
34
|
def __init__(self, db: InfrahubDatabase):
|
|
@@ -6,6 +6,7 @@ from infrahub.core import registry
|
|
|
6
6
|
from infrahub.core.constants import DiffAction
|
|
7
7
|
from infrahub.core.diff.model.path import BranchTrackingId
|
|
8
8
|
from infrahub.core.diff.query.merge import (
|
|
9
|
+
DiffMergeMetadataQuery,
|
|
9
10
|
DiffMergeMigratedKindsQuery,
|
|
10
11
|
DiffMergePropertiesQuery,
|
|
11
12
|
DiffMergeQuery,
|
|
@@ -26,6 +27,8 @@ log = get_logger()
|
|
|
26
27
|
|
|
27
28
|
|
|
28
29
|
class DiffMerger:
|
|
30
|
+
metadata_batch_size = 500
|
|
31
|
+
|
|
29
32
|
def __init__(
|
|
30
33
|
self,
|
|
31
34
|
db: InfrahubDatabase,
|
|
@@ -39,6 +42,7 @@ class DiffMerger:
|
|
|
39
42
|
self.db = db
|
|
40
43
|
self.diff_repository = diff_repository
|
|
41
44
|
self.serializer = serializer
|
|
45
|
+
self._affected_node_uuids: list[str] = []
|
|
42
46
|
|
|
43
47
|
async def merge_graph(self, at: Timestamp) -> EnrichedDiffRoot:
|
|
44
48
|
tracking_id = BranchTrackingId(name=self.source_branch.name)
|
|
@@ -69,6 +73,7 @@ class DiffMerger:
|
|
|
69
73
|
# make sure that we use the ADDED db_id if it exists
|
|
70
74
|
# it will not if a node was migrated and then deleted
|
|
71
75
|
migrated_kinds_id_map[n.uuid] = n.identifier.db_id
|
|
76
|
+
|
|
72
77
|
async for node_diff_dicts, property_diff_dicts in self.serializer.serialize_diff(diff=enriched_diff):
|
|
73
78
|
if node_diff_dicts:
|
|
74
79
|
log.info(f"Merging batch of nodes #{batch_num}")
|
|
@@ -105,13 +110,34 @@ class DiffMerger:
|
|
|
105
110
|
)
|
|
106
111
|
await migrated_merge_query.execute(db=self.db)
|
|
107
112
|
|
|
113
|
+
affected_node_uuids = [n.uuid for n in enriched_diff.nodes]
|
|
114
|
+
self._affected_node_uuids = affected_node_uuids
|
|
115
|
+
if affected_node_uuids:
|
|
116
|
+
for i in range(0, len(affected_node_uuids), self.metadata_batch_size):
|
|
117
|
+
batch_uuids = affected_node_uuids[i : i + self.metadata_batch_size]
|
|
118
|
+
log.info(f"Updating metadata for batch {i // self.metadata_batch_size + 1} ({len(batch_uuids)} nodes)")
|
|
119
|
+
metadata_query = await DiffMergeMetadataQuery.init(
|
|
120
|
+
db=self.db,
|
|
121
|
+
branch=self.source_branch,
|
|
122
|
+
at=at,
|
|
123
|
+
target_branch=self.destination_branch,
|
|
124
|
+
node_uuids=batch_uuids,
|
|
125
|
+
)
|
|
126
|
+
await metadata_query.execute(db=self.db)
|
|
127
|
+
|
|
108
128
|
self.source_branch.branched_from = at.to_string()
|
|
109
129
|
await self.source_branch.save(db=self.db)
|
|
110
130
|
registry.branch[self.source_branch.name] = self.source_branch
|
|
111
131
|
return enriched_diff
|
|
112
132
|
|
|
113
133
|
async def rollback(self, at: Timestamp) -> None:
|
|
134
|
+
if not self._affected_node_uuids:
|
|
135
|
+
return
|
|
114
136
|
rollback_query = await DiffMergeRollbackQuery.init(
|
|
115
|
-
db=self.db,
|
|
137
|
+
db=self.db,
|
|
138
|
+
branch=self.source_branch,
|
|
139
|
+
target_branch=self.destination_branch,
|
|
140
|
+
at=at,
|
|
141
|
+
node_uuids=self._affected_node_uuids,
|
|
116
142
|
)
|
|
117
143
|
await rollback_query.execute(db=self.db)
|
|
@@ -93,9 +93,7 @@ class DiffMergeSerializer:
|
|
|
93
93
|
if property_type in (DatabaseEdgeType.HAS_OWNER, DatabaseEdgeType.HAS_SOURCE, DatabaseEdgeType.IS_RELATED):
|
|
94
94
|
return raw_value
|
|
95
95
|
# these are boolean
|
|
96
|
-
if
|
|
97
|
-
raw_value, str
|
|
98
|
-
):
|
|
96
|
+
if property_type == DatabaseEdgeType.IS_PROTECTED and isinstance(raw_value, str):
|
|
99
97
|
return raw_value.lower() == "true"
|
|
100
98
|
# this must be HAS_VALUE
|
|
101
99
|
if raw_value in (None, NULL_VALUE):
|
|
@@ -249,13 +247,8 @@ class DiffMergeSerializer:
|
|
|
249
247
|
return attr_dict, attr_prop_dict
|
|
250
248
|
|
|
251
249
|
def _get_default_property_merge_dicts(self, action: DiffAction) -> dict[DatabaseEdgeType, PropertyMergeDict]:
|
|
252
|
-
# start with default values for
|
|
250
|
+
# start with default values for IS_PROTECTED b/c we always want to update them during a merge
|
|
253
251
|
return {
|
|
254
|
-
DatabaseEdgeType.IS_VISIBLE: PropertyMergeDict(
|
|
255
|
-
property_type=DatabaseEdgeType.IS_VISIBLE.value,
|
|
256
|
-
action=self._to_action_str(action),
|
|
257
|
-
value=None,
|
|
258
|
-
),
|
|
259
252
|
DatabaseEdgeType.IS_PROTECTED: PropertyMergeDict(
|
|
260
253
|
property_type=DatabaseEdgeType.IS_PROTECTED.value,
|
|
261
254
|
action=self._to_action_str(action),
|
|
@@ -335,7 +328,7 @@ class DiffMergeSerializer:
|
|
|
335
328
|
# handled above
|
|
336
329
|
continue
|
|
337
330
|
python_value_type: type = str
|
|
338
|
-
if property_diff.property_type
|
|
331
|
+
if property_diff.property_type is DatabaseEdgeType.IS_PROTECTED:
|
|
339
332
|
python_value_type = bool
|
|
340
333
|
actions_and_values = self._get_property_actions_and_values(
|
|
341
334
|
property_diff=property_diff, python_value_type=python_value_type
|
infrahub/core/diff/model/diff.py
CHANGED
|
@@ -26,7 +26,7 @@ class BaseDiffElement(BaseModel):
|
|
|
26
26
|
"""
|
|
27
27
|
resp: dict[str, Any] = {}
|
|
28
28
|
for key, value in self:
|
|
29
|
-
field_info = self.model_fields[key]
|
|
29
|
+
field_info = self.__class__.model_fields[key]
|
|
30
30
|
if isinstance(value, BaseModel):
|
|
31
31
|
resp[key] = value.to_graphql() # type: ignore[attr-defined]
|
|
32
32
|
elif isinstance(value, dict):
|