infrahub-server 1.5.5__py3-none-any.whl → 1.6.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/api/artifact.py +5 -3
- infrahub/auth.py +5 -6
- infrahub/cli/db.py +3 -3
- infrahub/cli/db_commands/clean_duplicate_schema_fields.py +2 -2
- infrahub/cli/dev.py +30 -0
- infrahub/config.py +62 -14
- infrahub/constants/database.py +5 -5
- infrahub/core/branch/models.py +24 -6
- infrahub/core/constants/__init__.py +1 -0
- infrahub/core/diff/model/diff.py +2 -2
- infrahub/core/graph/constraints.py +2 -2
- infrahub/core/manager.py +191 -60
- infrahub/core/merge.py +29 -2
- infrahub/core/migrations/graph/m041_deleted_dup_edges.py +2 -3
- infrahub/core/migrations/shared.py +2 -2
- infrahub/core/models.py +5 -6
- infrahub/core/node/__init__.py +12 -6
- infrahub/core/node/create.py +36 -8
- infrahub/core/node/ipam.py +4 -4
- infrahub/core/node/node_property_attribute.py +2 -2
- infrahub/core/node/standard.py +1 -1
- infrahub/core/query/attribute.py +1 -1
- infrahub/core/query/branch.py +11 -0
- infrahub/core/query/node.py +9 -5
- infrahub/core/query/standard_node.py +3 -0
- infrahub/core/relationship/model.py +15 -10
- infrahub/core/schema/__init__.py +3 -3
- infrahub/core/schema/generic_schema.py +1 -1
- infrahub/core/schema/schema_branch.py +35 -16
- infrahub/core/task/user_task.py +2 -2
- infrahub/core/validators/determiner.py +3 -6
- infrahub/core/validators/enum.py +2 -2
- infrahub/database/__init__.py +1 -1
- infrahub/dependencies/interface.py +2 -2
- infrahub/events/constants.py +2 -2
- infrahub/git/base.py +42 -1
- infrahub/git/models.py +2 -1
- infrahub/git/repository.py +5 -1
- infrahub/git/tasks.py +28 -1
- infrahub/git/utils.py +9 -0
- infrahub/graphql/analyzer.py +4 -4
- infrahub/graphql/loaders/peers.py +6 -0
- infrahub/graphql/mutations/computed_attribute.py +1 -1
- infrahub/graphql/mutations/convert_object_type.py +1 -1
- infrahub/graphql/mutations/display_label.py +1 -1
- infrahub/graphql/mutations/hfid.py +1 -1
- infrahub/graphql/mutations/ipam.py +1 -1
- infrahub/graphql/mutations/profile.py +9 -1
- infrahub/graphql/mutations/relationship.py +2 -2
- infrahub/graphql/mutations/resource_manager.py +1 -1
- infrahub/graphql/queries/__init__.py +2 -1
- infrahub/graphql/queries/branch.py +58 -3
- infrahub/graphql/queries/ipam.py +9 -4
- infrahub/graphql/queries/resource_manager.py +7 -11
- infrahub/graphql/queries/search.py +5 -6
- 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/graphql/schema.py +2 -0
- infrahub/graphql/types/__init__.py +3 -1
- infrahub/graphql/types/branch.py +98 -2
- infrahub/lock.py +6 -6
- infrahub/log.py +1 -1
- infrahub/message_bus/messages/__init__.py +0 -12
- infrahub/patch/constants.py +2 -2
- infrahub/profiles/node_applier.py +9 -0
- infrahub/proposed_change/tasks.py +1 -1
- infrahub/task_manager/task.py +4 -4
- infrahub/telemetry/constants.py +2 -2
- infrahub/trigger/models.py +2 -2
- infrahub/trigger/setup.py +6 -9
- infrahub/utils.py +19 -1
- infrahub/validators/tasks.py +1 -1
- infrahub/workers/infrahub_async.py +39 -1
- infrahub_sdk/async_typer.py +2 -1
- infrahub_sdk/batch.py +2 -2
- infrahub_sdk/client.py +121 -10
- infrahub_sdk/config.py +2 -2
- infrahub_sdk/ctl/branch.py +176 -2
- infrahub_sdk/ctl/check.py +3 -3
- infrahub_sdk/ctl/cli.py +2 -2
- infrahub_sdk/ctl/cli_commands.py +10 -9
- infrahub_sdk/ctl/generator.py +2 -2
- infrahub_sdk/ctl/graphql.py +3 -4
- infrahub_sdk/ctl/importer.py +2 -3
- infrahub_sdk/ctl/repository.py +5 -6
- infrahub_sdk/ctl/task.py +2 -4
- infrahub_sdk/ctl/utils.py +4 -4
- 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 +10 -10
- infrahub_sdk/node/constants.py +2 -3
- infrahub_sdk/node/node.py +54 -11
- infrahub_sdk/node/related_node.py +1 -2
- infrahub_sdk/node/relationship.py +1 -2
- infrahub_sdk/object_store.py +4 -4
- infrahub_sdk/operation.py +2 -2
- infrahub_sdk/protocols_base.py +0 -1
- infrahub_sdk/protocols_generator/generator.py +1 -1
- infrahub_sdk/pytest_plugin/items/jinja2_transform.py +1 -1
- infrahub_sdk/pytest_plugin/models.py +1 -1
- infrahub_sdk/pytest_plugin/plugin.py +1 -1
- infrahub_sdk/query_groups.py +2 -2
- infrahub_sdk/schema/__init__.py +10 -14
- infrahub_sdk/schema/main.py +2 -2
- infrahub_sdk/schema/repository.py +2 -2
- infrahub_sdk/spec/object.py +2 -2
- infrahub_sdk/spec/range_expansion.py +1 -1
- infrahub_sdk/template/__init__.py +2 -1
- infrahub_sdk/transfer/importer/json.py +3 -3
- infrahub_sdk/types.py +2 -2
- infrahub_sdk/utils.py +2 -2
- {infrahub_server-1.5.5.dist-info → infrahub_server-1.6.0.dist-info}/METADATA +58 -59
- {infrahub_server-1.5.5.dist-info → infrahub_server-1.6.0.dist-info}/RECORD +240 -246
- {infrahub_server-1.5.5.dist-info → infrahub_server-1.6.0.dist-info}/WHEEL +1 -1
- infrahub_server-1.6.0.dist-info/entry_points.txt +12 -0
- infrahub_testcontainers/container.py +2 -2
- infrahub_testcontainers/docker-compose-cluster.test.yml +1 -1
- infrahub_testcontainers/docker-compose.test.yml +1 -1
- infrahub/core/schema/generated/__init__.py +0 -0
- infrahub/core/schema/generated/attribute_schema.py +0 -133
- infrahub/core/schema/generated/base_node_schema.py +0 -111
- infrahub/core/schema/generated/genericnode_schema.py +0 -30
- infrahub/core/schema/generated/node_schema.py +0 -40
- infrahub/core/schema/generated/relationship_schema.py +0 -141
- infrahub_server-1.5.5.dist-info/entry_points.txt +0 -13
- {infrahub_server-1.5.5.dist-info → infrahub_server-1.6.0.dist-info/licenses}/LICENSE.txt +0 -0
infrahub/core/manager.py
CHANGED
|
@@ -5,7 +5,7 @@ from typing import TYPE_CHECKING, Any, Iterable, Literal, TypeVar, overload
|
|
|
5
5
|
|
|
6
6
|
from infrahub_sdk.utils import deep_merge_dict, is_valid_uuid
|
|
7
7
|
|
|
8
|
-
from infrahub.core.constants import RelationshipCardinality, RelationshipDirection
|
|
8
|
+
from infrahub.core.constants import InfrahubKind, RelationshipCardinality, RelationshipDirection
|
|
9
9
|
from infrahub.core.node import Node
|
|
10
10
|
from infrahub.core.node.delete_validator import NodeDeleteValidator
|
|
11
11
|
from infrahub.core.query.node import (
|
|
@@ -59,7 +59,7 @@ def identify_node_class(node: NodeToProcess) -> type[Node]:
|
|
|
59
59
|
return Node
|
|
60
60
|
|
|
61
61
|
|
|
62
|
-
def get_schema(
|
|
62
|
+
def get_schema[SchemaProtocol](
|
|
63
63
|
db: InfrahubDatabase,
|
|
64
64
|
branch: Branch,
|
|
65
65
|
node_schema: type[SchemaProtocol] | MainSchemaTypes | str,
|
|
@@ -188,17 +188,22 @@ class NodeManager:
|
|
|
188
188
|
await query.execute(db=db)
|
|
189
189
|
node_ids = query.get_node_ids()
|
|
190
190
|
|
|
191
|
-
|
|
192
|
-
|
|
191
|
+
if (
|
|
192
|
+
fields
|
|
193
|
+
and "identifier" in fields
|
|
194
|
+
and node_schema.kind
|
|
195
|
+
in [
|
|
196
|
+
InfrahubKind.BASEPERMISSION,
|
|
197
|
+
InfrahubKind.GLOBALPERMISSION,
|
|
198
|
+
InfrahubKind.OBJECTPERMISSION,
|
|
199
|
+
]
|
|
200
|
+
):
|
|
201
|
+
# This is a workaround to ensure we are querying the right fields for permissions
|
|
202
|
+
# The identifier for permissions needs the same fields as the display label
|
|
193
203
|
schema_branch = db.schema.get_schema_branch(name=branch.name)
|
|
194
204
|
display_label_fields = schema_branch.generate_fields_for_display_label(name=node_schema.kind)
|
|
195
205
|
if display_label_fields:
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
if fields and "hfid" in fields and node_schema.human_friendly_id:
|
|
199
|
-
hfid_fields = node_schema.generate_fields_for_hfid()
|
|
200
|
-
if hfid_fields:
|
|
201
|
-
fields = deep_merge_dict(dicta=fields, dictb=hfid_fields)
|
|
206
|
+
deep_merge_dict(dicta=fields, dictb=display_label_fields)
|
|
202
207
|
|
|
203
208
|
response = await cls.get_many(
|
|
204
209
|
ids=node_ids,
|
|
@@ -300,6 +305,8 @@ class NodeManager:
|
|
|
300
305
|
branch: Branch | str | None = None,
|
|
301
306
|
branch_agnostic: bool = False,
|
|
302
307
|
fetch_peers: bool = False,
|
|
308
|
+
include_source: bool = False,
|
|
309
|
+
include_owner: bool = False,
|
|
303
310
|
) -> list[Relationship]:
|
|
304
311
|
branch = await registry.get_branch(branch=branch, db=db)
|
|
305
312
|
at = Timestamp(at)
|
|
@@ -324,24 +331,31 @@ class NodeManager:
|
|
|
324
331
|
if not peers_info:
|
|
325
332
|
return []
|
|
326
333
|
|
|
327
|
-
|
|
328
|
-
|
|
334
|
+
if fields and "identifier" in fields:
|
|
335
|
+
# This is a workaround to ensure we are querying the right fields for permissions
|
|
336
|
+
# The identifier for permissions needs the same fields as the display label
|
|
329
337
|
peer_schema = schema.get_peer_schema(db=db, branch=branch)
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
fields = deep_merge_dict(dicta=fields, dictb=hfid_fields)
|
|
338
|
+
if peer_schema.kind in [
|
|
339
|
+
InfrahubKind.BASEPERMISSION,
|
|
340
|
+
InfrahubKind.GLOBALPERMISSION,
|
|
341
|
+
InfrahubKind.OBJECTPERMISSION,
|
|
342
|
+
]:
|
|
343
|
+
schema_branch = db.schema.get_schema_branch(name=branch.name)
|
|
344
|
+
display_label_fields = schema_branch.generate_fields_for_display_label(name=peer_schema.kind)
|
|
345
|
+
if display_label_fields:
|
|
346
|
+
deep_merge_dict(dicta=fields, dictb=display_label_fields)
|
|
340
347
|
|
|
341
348
|
if fetch_peers:
|
|
342
349
|
peer_ids = [peer.peer_id for peer in peers_info]
|
|
343
350
|
peer_nodes = await cls.get_many(
|
|
344
|
-
db=db,
|
|
351
|
+
db=db,
|
|
352
|
+
ids=peer_ids,
|
|
353
|
+
fields=fields,
|
|
354
|
+
at=at,
|
|
355
|
+
branch=branch,
|
|
356
|
+
branch_agnostic=branch_agnostic,
|
|
357
|
+
include_source=include_source,
|
|
358
|
+
include_owner=include_owner,
|
|
345
359
|
)
|
|
346
360
|
|
|
347
361
|
results = []
|
|
@@ -420,15 +434,6 @@ class NodeManager:
|
|
|
420
434
|
if not peers_ids:
|
|
421
435
|
return {}
|
|
422
436
|
|
|
423
|
-
hierarchy_schema = node_schema.get_hierarchy_schema(db=db, branch=branch)
|
|
424
|
-
|
|
425
|
-
# if display_label has been requested we need to ensure we are querying the right fields
|
|
426
|
-
if fields and "display_label" in fields:
|
|
427
|
-
schema_branch = db.schema.get_schema_branch(name=branch.name)
|
|
428
|
-
display_label_fields = schema_branch.generate_fields_for_display_label(name=hierarchy_schema.kind)
|
|
429
|
-
if display_label_fields:
|
|
430
|
-
fields = deep_merge_dict(dicta=fields, dictb=display_label_fields)
|
|
431
|
-
|
|
432
437
|
return await cls.get_many(
|
|
433
438
|
db=db, ids=peers_ids, fields=fields, at=at, branch=branch, include_owner=True, include_source=True
|
|
434
439
|
)
|
|
@@ -455,7 +460,7 @@ class NodeManager:
|
|
|
455
460
|
branch: Branch | str | None = ...,
|
|
456
461
|
id: str | None = ...,
|
|
457
462
|
hfid: list[str] | None = ...,
|
|
458
|
-
) ->
|
|
463
|
+
) -> Node: ...
|
|
459
464
|
|
|
460
465
|
@classmethod
|
|
461
466
|
async def find_object(
|
|
@@ -466,10 +471,7 @@ class NodeManager:
|
|
|
466
471
|
branch: Branch | str | None = None,
|
|
467
472
|
id: str | None = None,
|
|
468
473
|
hfid: list[str] | None = None,
|
|
469
|
-
) ->
|
|
470
|
-
if not id and not hfid:
|
|
471
|
-
raise ProcessingError(message="either id or hfid must be provided.")
|
|
472
|
-
|
|
474
|
+
) -> Node | SchemaProtocol:
|
|
473
475
|
if id and is_valid_uuid(id):
|
|
474
476
|
return await cls.get_one(
|
|
475
477
|
db=db,
|
|
@@ -494,16 +496,19 @@ class NodeManager:
|
|
|
494
496
|
raise_on_error=True,
|
|
495
497
|
)
|
|
496
498
|
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
499
|
+
if id:
|
|
500
|
+
return await cls.get_one_by_default_filter(
|
|
501
|
+
db=db,
|
|
502
|
+
kind=kind,
|
|
503
|
+
id=id,
|
|
504
|
+
branch=branch,
|
|
505
|
+
at=at,
|
|
506
|
+
include_owner=True,
|
|
507
|
+
include_source=True,
|
|
508
|
+
raise_on_error=True,
|
|
509
|
+
)
|
|
510
|
+
|
|
511
|
+
raise ProcessingError(message="either id or hfid must be provided.")
|
|
507
512
|
|
|
508
513
|
@overload
|
|
509
514
|
@classmethod
|
|
@@ -557,7 +562,43 @@ class NodeManager:
|
|
|
557
562
|
prefetch_relationships: bool = ...,
|
|
558
563
|
account=...,
|
|
559
564
|
branch_agnostic: bool = ...,
|
|
560
|
-
) -> SchemaProtocol: ...
|
|
565
|
+
) -> SchemaProtocol | None: ...
|
|
566
|
+
|
|
567
|
+
@overload
|
|
568
|
+
@classmethod
|
|
569
|
+
async def get_one_by_default_filter(
|
|
570
|
+
cls,
|
|
571
|
+
db: InfrahubDatabase,
|
|
572
|
+
id: str,
|
|
573
|
+
kind: str,
|
|
574
|
+
raise_on_error: Literal[False] = ...,
|
|
575
|
+
fields: dict | None = ...,
|
|
576
|
+
at: Timestamp | str | None = ...,
|
|
577
|
+
branch: Branch | str | None = ...,
|
|
578
|
+
include_source: bool = ...,
|
|
579
|
+
include_owner: bool = ...,
|
|
580
|
+
prefetch_relationships: bool = ...,
|
|
581
|
+
account=...,
|
|
582
|
+
branch_agnostic: bool = ...,
|
|
583
|
+
) -> Node | None: ...
|
|
584
|
+
|
|
585
|
+
@overload
|
|
586
|
+
@classmethod
|
|
587
|
+
async def get_one_by_default_filter(
|
|
588
|
+
cls,
|
|
589
|
+
db: InfrahubDatabase,
|
|
590
|
+
id: str,
|
|
591
|
+
kind: str,
|
|
592
|
+
raise_on_error: Literal[True] = ...,
|
|
593
|
+
fields: dict | None = ...,
|
|
594
|
+
at: Timestamp | str | None = ...,
|
|
595
|
+
branch: Branch | str | None = ...,
|
|
596
|
+
include_source: bool = ...,
|
|
597
|
+
include_owner: bool = ...,
|
|
598
|
+
prefetch_relationships: bool = ...,
|
|
599
|
+
account=...,
|
|
600
|
+
branch_agnostic: bool = ...,
|
|
601
|
+
) -> Node: ...
|
|
561
602
|
|
|
562
603
|
@overload
|
|
563
604
|
@classmethod
|
|
@@ -575,7 +616,7 @@ class NodeManager:
|
|
|
575
616
|
prefetch_relationships: bool = ...,
|
|
576
617
|
account=...,
|
|
577
618
|
branch_agnostic: bool = ...,
|
|
578
|
-
) ->
|
|
619
|
+
) -> Node | None: ...
|
|
579
620
|
|
|
580
621
|
@classmethod
|
|
581
622
|
async def get_one_by_default_filter(
|
|
@@ -592,7 +633,7 @@ class NodeManager:
|
|
|
592
633
|
prefetch_relationships: bool = False,
|
|
593
634
|
account=None,
|
|
594
635
|
branch_agnostic: bool = False,
|
|
595
|
-
) ->
|
|
636
|
+
) -> Node | SchemaProtocol | None:
|
|
596
637
|
branch = await registry.get_branch(branch=branch, db=db)
|
|
597
638
|
at = Timestamp(at)
|
|
598
639
|
|
|
@@ -688,7 +729,7 @@ class NodeManager:
|
|
|
688
729
|
prefetch_relationships: bool = ...,
|
|
689
730
|
account=...,
|
|
690
731
|
branch_agnostic: bool = ...,
|
|
691
|
-
) -> SchemaProtocol: ...
|
|
732
|
+
) -> SchemaProtocol | None: ...
|
|
692
733
|
|
|
693
734
|
@overload
|
|
694
735
|
@classmethod
|
|
@@ -706,7 +747,25 @@ class NodeManager:
|
|
|
706
747
|
prefetch_relationships: bool = ...,
|
|
707
748
|
account=...,
|
|
708
749
|
branch_agnostic: bool = ...,
|
|
709
|
-
) ->
|
|
750
|
+
) -> Node: ...
|
|
751
|
+
|
|
752
|
+
@overload
|
|
753
|
+
@classmethod
|
|
754
|
+
async def get_one_by_hfid(
|
|
755
|
+
cls,
|
|
756
|
+
db: InfrahubDatabase,
|
|
757
|
+
hfid: list[str],
|
|
758
|
+
kind: str,
|
|
759
|
+
raise_on_error: Literal[False],
|
|
760
|
+
fields: dict | None = ...,
|
|
761
|
+
at: Timestamp | str | None = ...,
|
|
762
|
+
branch: Branch | str | None = ...,
|
|
763
|
+
include_source: bool = ...,
|
|
764
|
+
include_owner: bool = ...,
|
|
765
|
+
prefetch_relationships: bool = ...,
|
|
766
|
+
account=...,
|
|
767
|
+
branch_agnostic: bool = ...,
|
|
768
|
+
) -> Node | None: ...
|
|
710
769
|
|
|
711
770
|
@overload
|
|
712
771
|
@classmethod
|
|
@@ -724,7 +783,7 @@ class NodeManager:
|
|
|
724
783
|
prefetch_relationships: bool = ...,
|
|
725
784
|
account=...,
|
|
726
785
|
branch_agnostic: bool = ...,
|
|
727
|
-
) ->
|
|
786
|
+
) -> Node | None: ...
|
|
728
787
|
|
|
729
788
|
@classmethod
|
|
730
789
|
async def get_one_by_hfid(
|
|
@@ -741,7 +800,7 @@ class NodeManager:
|
|
|
741
800
|
prefetch_relationships: bool = False,
|
|
742
801
|
account=None,
|
|
743
802
|
branch_agnostic: bool = False,
|
|
744
|
-
) ->
|
|
803
|
+
) -> Node | SchemaProtocol | None:
|
|
745
804
|
branch = await registry.get_branch(branch=branch, db=db)
|
|
746
805
|
at = Timestamp(at)
|
|
747
806
|
|
|
@@ -770,14 +829,14 @@ class NodeManager:
|
|
|
770
829
|
for key, item in zip(node_schema.human_friendly_id, hfid, strict=False):
|
|
771
830
|
path = node_schema.parse_schema_path(path=key, schema=registry.schema.get_schema_branch(name=branch.name))
|
|
772
831
|
|
|
773
|
-
if path.is_type_relationship:
|
|
832
|
+
if path.is_type_relationship and path.related_schema:
|
|
774
833
|
rel_schema = path.related_schema
|
|
775
834
|
# Keep the relationship attribute path and parse it
|
|
776
835
|
path = rel_schema.parse_schema_path(
|
|
777
836
|
path=key.split("__", maxsplit=1)[1], schema=registry.schema.get_schema_branch(name=branch.name)
|
|
778
837
|
)
|
|
779
838
|
|
|
780
|
-
filters[key] = path.
|
|
839
|
+
filters[key] = path.active_attribute_schema.get_class().deserialize_from_string(item)
|
|
781
840
|
|
|
782
841
|
items = await NodeManager.query(
|
|
783
842
|
db=db,
|
|
@@ -948,6 +1007,42 @@ class NodeManager:
|
|
|
948
1007
|
branch_agnostic: bool = ...,
|
|
949
1008
|
) -> SchemaProtocol: ...
|
|
950
1009
|
|
|
1010
|
+
@overload
|
|
1011
|
+
@classmethod
|
|
1012
|
+
async def get_one(
|
|
1013
|
+
cls,
|
|
1014
|
+
id: str,
|
|
1015
|
+
db: InfrahubDatabase,
|
|
1016
|
+
kind: str,
|
|
1017
|
+
raise_on_error: Literal[True] = ...,
|
|
1018
|
+
fields: dict | None = ...,
|
|
1019
|
+
at: Timestamp | str | None = ...,
|
|
1020
|
+
branch: Branch | str | None = ...,
|
|
1021
|
+
include_source: bool = ...,
|
|
1022
|
+
include_owner: bool = ...,
|
|
1023
|
+
prefetch_relationships: bool = ...,
|
|
1024
|
+
account=...,
|
|
1025
|
+
branch_agnostic: bool = ...,
|
|
1026
|
+
) -> Node: ...
|
|
1027
|
+
|
|
1028
|
+
@overload
|
|
1029
|
+
@classmethod
|
|
1030
|
+
async def get_one(
|
|
1031
|
+
cls,
|
|
1032
|
+
id: str,
|
|
1033
|
+
db: InfrahubDatabase,
|
|
1034
|
+
kind: str,
|
|
1035
|
+
raise_on_error: Literal[False] = ...,
|
|
1036
|
+
fields: dict | None = ...,
|
|
1037
|
+
at: Timestamp | str | None = ...,
|
|
1038
|
+
branch: Branch | str | None = ...,
|
|
1039
|
+
include_source: bool = ...,
|
|
1040
|
+
include_owner: bool = ...,
|
|
1041
|
+
prefetch_relationships: bool = ...,
|
|
1042
|
+
account=...,
|
|
1043
|
+
branch_agnostic: bool = ...,
|
|
1044
|
+
) -> Node | None: ...
|
|
1045
|
+
|
|
951
1046
|
@overload
|
|
952
1047
|
@classmethod
|
|
953
1048
|
async def get_one(
|
|
@@ -964,7 +1059,43 @@ class NodeManager:
|
|
|
964
1059
|
prefetch_relationships: bool = ...,
|
|
965
1060
|
account=...,
|
|
966
1061
|
branch_agnostic: bool = ...,
|
|
967
|
-
) ->
|
|
1062
|
+
) -> Node | None: ...
|
|
1063
|
+
|
|
1064
|
+
@overload
|
|
1065
|
+
@classmethod
|
|
1066
|
+
async def get_one(
|
|
1067
|
+
cls,
|
|
1068
|
+
id: str,
|
|
1069
|
+
db: InfrahubDatabase,
|
|
1070
|
+
kind: None = ...,
|
|
1071
|
+
raise_on_error: Literal[True] = ...,
|
|
1072
|
+
fields: dict | None = ...,
|
|
1073
|
+
at: Timestamp | str | None = ...,
|
|
1074
|
+
branch: Branch | str | None = ...,
|
|
1075
|
+
include_source: bool = ...,
|
|
1076
|
+
include_owner: bool = ...,
|
|
1077
|
+
prefetch_relationships: bool = ...,
|
|
1078
|
+
account=...,
|
|
1079
|
+
branch_agnostic: bool = ...,
|
|
1080
|
+
) -> Node: ...
|
|
1081
|
+
|
|
1082
|
+
@overload
|
|
1083
|
+
@classmethod
|
|
1084
|
+
async def get_one(
|
|
1085
|
+
cls,
|
|
1086
|
+
id: str,
|
|
1087
|
+
db: InfrahubDatabase,
|
|
1088
|
+
kind: None = ...,
|
|
1089
|
+
raise_on_error: Literal[False] = ...,
|
|
1090
|
+
fields: dict | None = ...,
|
|
1091
|
+
at: Timestamp | str | None = ...,
|
|
1092
|
+
branch: Branch | str | None = ...,
|
|
1093
|
+
include_source: bool = ...,
|
|
1094
|
+
include_owner: bool = ...,
|
|
1095
|
+
prefetch_relationships: bool = ...,
|
|
1096
|
+
account=...,
|
|
1097
|
+
branch_agnostic: bool = ...,
|
|
1098
|
+
) -> Node | None: ...
|
|
968
1099
|
|
|
969
1100
|
@overload
|
|
970
1101
|
@classmethod
|
|
@@ -982,7 +1113,7 @@ class NodeManager:
|
|
|
982
1113
|
prefetch_relationships: bool = ...,
|
|
983
1114
|
account=...,
|
|
984
1115
|
branch_agnostic: bool = ...,
|
|
985
|
-
) ->
|
|
1116
|
+
) -> Node | None: ...
|
|
986
1117
|
|
|
987
1118
|
@classmethod
|
|
988
1119
|
async def get_one(
|
|
@@ -999,7 +1130,7 @@ class NodeManager:
|
|
|
999
1130
|
prefetch_relationships: bool = False,
|
|
1000
1131
|
account=None,
|
|
1001
1132
|
branch_agnostic: bool = False,
|
|
1002
|
-
) ->
|
|
1133
|
+
) -> Node | SchemaProtocol | None:
|
|
1003
1134
|
"""Return one node based on its ID."""
|
|
1004
1135
|
branch = await registry.get_branch(branch=branch, db=db)
|
|
1005
1136
|
|
|
@@ -1264,7 +1395,7 @@ class NodeManager:
|
|
|
1264
1395
|
db: InfrahubDatabase,
|
|
1265
1396
|
nodes: list[Node],
|
|
1266
1397
|
branch: Branch | str | None = None,
|
|
1267
|
-
at: Timestamp |
|
|
1398
|
+
at: Timestamp | None = None,
|
|
1268
1399
|
cascade_delete: bool = True,
|
|
1269
1400
|
) -> list[Node]:
|
|
1270
1401
|
"""Returns list of deleted nodes because of cascading deletes"""
|
infrahub/core/merge.py
CHANGED
|
@@ -2,11 +2,11 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from typing import TYPE_CHECKING
|
|
4
4
|
|
|
5
|
-
from infrahub.core.constants import RepositoryInternalStatus
|
|
5
|
+
from infrahub.core.constants import InfrahubKind, RepositoryInternalStatus
|
|
6
6
|
from infrahub.core.diff.model.path import BranchTrackingId
|
|
7
7
|
from infrahub.core.manager import NodeManager
|
|
8
8
|
from infrahub.core.models import SchemaUpdateValidationResult
|
|
9
|
-
from infrahub.core.protocols import CoreRepository
|
|
9
|
+
from infrahub.core.protocols import CoreReadOnlyRepository, CoreRepository
|
|
10
10
|
from infrahub.core.registry import registry
|
|
11
11
|
from infrahub.core.timestamp import Timestamp
|
|
12
12
|
from infrahub.exceptions import MergeFailedError, ValidationError
|
|
@@ -223,6 +223,32 @@ class BranchMerger:
|
|
|
223
223
|
await self.diff_merger.rollback(at=self._merge_at)
|
|
224
224
|
|
|
225
225
|
async def merge_repositories(self) -> None:
|
|
226
|
+
await self.merge_core_read_only_repositories()
|
|
227
|
+
await self.merge_core_repositories()
|
|
228
|
+
|
|
229
|
+
async def merge_core_read_only_repositories(self) -> None:
|
|
230
|
+
repos_in_main_list = await NodeManager.query(schema=CoreReadOnlyRepository, db=self.db)
|
|
231
|
+
repos_in_main = {repo.id: repo for repo in repos_in_main_list}
|
|
232
|
+
|
|
233
|
+
repos_in_branch_list = await NodeManager.query(
|
|
234
|
+
schema=CoreReadOnlyRepository, db=self.db, branch=self.source_branch
|
|
235
|
+
)
|
|
236
|
+
for repo in repos_in_branch_list:
|
|
237
|
+
if repo.id not in repos_in_main:
|
|
238
|
+
continue
|
|
239
|
+
|
|
240
|
+
model = GitRepositoryMerge(
|
|
241
|
+
repository_id=repo.id,
|
|
242
|
+
repository_name=repo.name.value,
|
|
243
|
+
source_branch=self.source_branch.name,
|
|
244
|
+
destination_branch=self.destination_branch.name,
|
|
245
|
+
destination_branch_id=str(self.destination_branch.get_uuid()),
|
|
246
|
+
internal_status=repo.internal_status.value,
|
|
247
|
+
repository_kind=InfrahubKind.READONLYREPOSITORY,
|
|
248
|
+
)
|
|
249
|
+
await self.workflow.submit_workflow(workflow=GIT_REPOSITORIES_MERGE, parameters={"model": model})
|
|
250
|
+
|
|
251
|
+
async def merge_core_repositories(self) -> None:
|
|
226
252
|
# Collect all Repositories in Main because we'll need the commit in Main for each one.
|
|
227
253
|
repos_in_main_list = await NodeManager.query(schema=CoreRepository, db=self.db)
|
|
228
254
|
repos_in_main = {repo.id: repo for repo in repos_in_main_list}
|
|
@@ -245,5 +271,6 @@ class BranchMerger:
|
|
|
245
271
|
destination_branch=self.destination_branch.name,
|
|
246
272
|
destination_branch_id=str(self.destination_branch.get_uuid()),
|
|
247
273
|
default_branch=repo.default_branch.value,
|
|
274
|
+
repository_kind=InfrahubKind.REPOSITORY,
|
|
248
275
|
)
|
|
249
276
|
await self.workflow.submit_workflow(workflow=GIT_REPOSITORIES_MERGE, parameters={"model": model})
|
|
@@ -87,15 +87,14 @@ CALL (node_uuid) {
|
|
|
87
87
|
// ------------
|
|
88
88
|
MATCH (n:Node {uuid: node_uuid})-[:IS_RELATED]-(rel:Relationship)
|
|
89
89
|
WITH DISTINCT rel
|
|
90
|
-
MATCH (rel)-[e]
|
|
90
|
+
MATCH (rel)-[e]->(peer)
|
|
91
91
|
WITH
|
|
92
|
-
elementId(rel) AS rel_element_id,
|
|
93
92
|
type(e) AS e_type,
|
|
94
93
|
e.branch AS e_branch,
|
|
95
94
|
e.from AS e_from,
|
|
96
95
|
e.to AS e_to,
|
|
97
96
|
e.status AS e_status,
|
|
98
|
-
|
|
97
|
+
e.peer AS e_peer,
|
|
99
98
|
CASE
|
|
100
99
|
WHEN startNode(e) = rel THEN "out" ELSE "in"
|
|
101
100
|
END AS direction,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from typing import TYPE_CHECKING, Any, Sequence
|
|
3
|
+
from typing import TYPE_CHECKING, Any, Sequence
|
|
4
4
|
|
|
5
5
|
from pydantic import BaseModel, ConfigDict, Field
|
|
6
6
|
from rich.console import Console
|
|
@@ -261,4 +261,4 @@ class MigrationRequiringRebase(BaseModel):
|
|
|
261
261
|
raise NotImplementedError()
|
|
262
262
|
|
|
263
263
|
|
|
264
|
-
MigrationTypes
|
|
264
|
+
type MigrationTypes = GraphMigration | InternalSchemaMigration | ArbitraryMigration | MigrationRequiringRebase
|
infrahub/core/models.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import contextlib
|
|
3
4
|
import hashlib
|
|
4
5
|
from typing import TYPE_CHECKING, Any
|
|
5
6
|
|
|
@@ -359,7 +360,7 @@ class SchemaUpdateValidationResult(BaseModel):
|
|
|
359
360
|
|
|
360
361
|
def validate_migrations(self, migration_map: dict[str, Any]) -> None:
|
|
361
362
|
for migration in self.migrations:
|
|
362
|
-
if migration_map.get(migration.migration_name
|
|
363
|
+
if migration_map.get(migration.migration_name) is None:
|
|
363
364
|
self.errors.append(
|
|
364
365
|
SchemaUpdateValidationError(
|
|
365
366
|
path=migration.path,
|
|
@@ -370,7 +371,7 @@ class SchemaUpdateValidationResult(BaseModel):
|
|
|
370
371
|
|
|
371
372
|
def validate_constraints(self, validator_map: dict[str, Any]) -> None:
|
|
372
373
|
for constraint in self.constraints:
|
|
373
|
-
if validator_map.get(constraint.constraint_name
|
|
374
|
+
if validator_map.get(constraint.constraint_name) is None:
|
|
374
375
|
self.errors.append(
|
|
375
376
|
SchemaUpdateValidationError(
|
|
376
377
|
path=constraint.path,
|
|
@@ -578,11 +579,9 @@ class HashableModel(BaseModel):
|
|
|
578
579
|
|
|
579
580
|
for field_name in other.model_fields.keys():
|
|
580
581
|
if not hasattr(self, field_name):
|
|
581
|
-
|
|
582
|
-
setattr(self, field_name, getattr(other, field_name))
|
|
583
|
-
except ValueError:
|
|
582
|
+
with contextlib.suppress(ValueError):
|
|
584
583
|
# handles the case where self and other are different types and other has fields that self does not
|
|
585
|
-
|
|
584
|
+
setattr(self, field_name, getattr(other, field_name))
|
|
586
585
|
continue
|
|
587
586
|
|
|
588
587
|
attr_other = getattr(other, field_name)
|
infrahub/core/node/__init__.py
CHANGED
|
@@ -467,15 +467,21 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
|
|
|
467
467
|
for attribute_name in template._attributes:
|
|
468
468
|
if attribute_name in list(fields) + [OBJECT_TEMPLATE_NAME_ATTR]:
|
|
469
469
|
continue
|
|
470
|
-
|
|
470
|
+
attr = getattr(template, attribute_name)
|
|
471
|
+
attr_value = attr.value
|
|
471
472
|
if attr_value is not None:
|
|
472
|
-
|
|
473
|
+
# Preserve is_from_profile flag when copying from template
|
|
474
|
+
field_data = {"value": attr_value, "source": attr.source_id or template.id}
|
|
475
|
+
if attr.is_from_profile:
|
|
476
|
+
field_data["is_from_profile"] = True
|
|
477
|
+
fields[attribute_name] = field_data
|
|
473
478
|
|
|
474
479
|
for relationship_name in template._relationships:
|
|
475
480
|
relationship_schema = template._schema.get_relationship(name=relationship_name)
|
|
476
481
|
if (
|
|
477
482
|
relationship_name in list(fields)
|
|
478
|
-
or relationship_schema.kind
|
|
483
|
+
or relationship_schema.kind
|
|
484
|
+
not in [RelationshipKind.ATTRIBUTE, RelationshipKind.GENERIC, RelationshipKind.PROFILE]
|
|
479
485
|
or relationship_name == OBJECT_TEMPLATE_RELATIONSHIP_NAME
|
|
480
486
|
):
|
|
481
487
|
continue
|
|
@@ -570,7 +576,7 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
|
|
|
570
576
|
self,
|
|
571
577
|
rel_schema.name,
|
|
572
578
|
await generator_method(
|
|
573
|
-
db=db, name=rel_schema.name, schema=rel_schema, data=fields.get(rel_schema.name
|
|
579
|
+
db=db, name=rel_schema.name, schema=rel_schema, data=fields.get(rel_schema.name)
|
|
574
580
|
),
|
|
575
581
|
)
|
|
576
582
|
except ValidationError as exc:
|
|
@@ -600,7 +606,7 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
|
|
|
600
606
|
self,
|
|
601
607
|
attr_schema.name,
|
|
602
608
|
await generator_method(
|
|
603
|
-
db=db, name=attr_schema.name, schema=attr_schema, data=fields.get(attr_schema.name
|
|
609
|
+
db=db, name=attr_schema.name, schema=attr_schema, data=fields.get(attr_schema.name)
|
|
604
610
|
),
|
|
605
611
|
)
|
|
606
612
|
if not self._existing:
|
|
@@ -1088,7 +1094,7 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
|
|
|
1088
1094
|
|
|
1089
1095
|
if key in self._relationships:
|
|
1090
1096
|
rel: RelationshipManager = getattr(self, key)
|
|
1091
|
-
changed |= await rel.update(db=db, data=value
|
|
1097
|
+
changed |= await rel.update(db=db, data=value)
|
|
1092
1098
|
|
|
1093
1099
|
return changed
|
|
1094
1100
|
|
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)
|