infrahub-server 1.1.1__py3-none-any.whl → 1.1.3__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/__init__.py +13 -5
- infrahub/api/artifact.py +9 -15
- infrahub/api/auth.py +7 -1
- infrahub/api/dependencies.py +15 -2
- infrahub/api/diff/diff.py +13 -7
- infrahub/api/file.py +5 -10
- infrahub/api/internal.py +19 -6
- infrahub/api/menu.py +8 -6
- infrahub/api/oauth2.py +25 -10
- infrahub/api/oidc.py +26 -10
- infrahub/api/query.py +2 -2
- infrahub/api/schema.py +48 -59
- infrahub/api/storage.py +8 -8
- infrahub/api/transformation.py +6 -5
- infrahub/auth.py +1 -26
- infrahub/cli/__init__.py +1 -1
- infrahub/cli/context.py +5 -8
- infrahub/cli/db.py +6 -6
- infrahub/cli/git_agent.py +1 -1
- infrahub/computed_attribute/models.py +1 -1
- infrahub/computed_attribute/tasks.py +1 -1
- infrahub/config.py +5 -5
- infrahub/core/account.py +2 -10
- infrahub/core/attribute.py +22 -0
- infrahub/core/branch/models.py +1 -1
- infrahub/core/branch/tasks.py +4 -3
- infrahub/core/diff/calculator.py +14 -0
- infrahub/core/diff/combiner.py +6 -2
- infrahub/core/diff/conflicts_enricher.py +2 -2
- infrahub/core/diff/coordinator.py +296 -87
- infrahub/core/diff/data_check_synchronizer.py +33 -4
- infrahub/core/diff/enricher/cardinality_one.py +3 -3
- infrahub/core/diff/enricher/hierarchy.py +4 -1
- infrahub/core/diff/merger/merger.py +11 -1
- infrahub/core/diff/merger/serializer.py +5 -29
- infrahub/core/diff/model/path.py +88 -4
- infrahub/core/diff/query/field_specifiers.py +35 -0
- infrahub/core/diff/query/roots_metadata.py +48 -0
- infrahub/core/diff/query/save.py +1 -0
- infrahub/core/diff/query_parser.py +27 -11
- infrahub/core/diff/repository/deserializer.py +7 -3
- infrahub/core/diff/repository/repository.py +100 -9
- infrahub/core/diff/tasks.py +1 -1
- infrahub/core/graph/__init__.py +1 -1
- infrahub/core/integrity/object_conflict/conflict_recorder.py +6 -1
- infrahub/core/ipam/utilization.py +6 -1
- infrahub/core/manager.py +8 -0
- infrahub/core/merge.py +6 -1
- infrahub/core/migrations/graph/__init__.py +2 -0
- infrahub/core/migrations/graph/m014_remove_index_attr_value.py +1 -1
- infrahub/core/migrations/graph/m015_diff_format_update.py +1 -1
- infrahub/core/migrations/graph/m016_diff_delete_bug_fix.py +1 -1
- infrahub/core/migrations/graph/m018_uniqueness_nulls.py +101 -0
- infrahub/core/migrations/query/attribute_add.py +5 -5
- infrahub/core/migrations/schema/tasks.py +2 -2
- infrahub/core/migrations/shared.py +3 -3
- infrahub/core/node/__init__.py +8 -2
- infrahub/core/node/constraints/grouped_uniqueness.py +9 -2
- infrahub/core/query/__init__.py +5 -2
- infrahub/core/query/diff.py +32 -19
- infrahub/core/query/ipam.py +30 -22
- infrahub/core/query/node.py +91 -40
- infrahub/core/schema/generated/attribute_schema.py +2 -2
- infrahub/core/schema/generated/base_node_schema.py +2 -2
- infrahub/core/schema/generated/relationship_schema.py +1 -1
- infrahub/core/schema/schema_branch_computed.py +1 -1
- infrahub/core/task/task_log.py +1 -1
- infrahub/core/validators/attribute/kind.py +1 -1
- infrahub/core/validators/interface.py +1 -2
- infrahub/core/validators/models/violation.py +1 -14
- infrahub/core/validators/shared.py +2 -2
- infrahub/core/validators/tasks.py +7 -4
- infrahub/core/validators/uniqueness/index.py +2 -4
- infrahub/database/index.py +1 -1
- infrahub/dependencies/builder/constraint/schema/aggregated.py +2 -0
- infrahub/dependencies/builder/constraint/schema/attribute_kind.py +8 -0
- infrahub/dependencies/builder/diff/data_check_synchronizer.py +2 -0
- infrahub/git/base.py +3 -3
- infrahub/git/integrator.py +1 -1
- infrahub/graphql/api/endpoints.py +12 -3
- infrahub/graphql/app.py +2 -2
- infrahub/graphql/auth/query_permission_checker/default_branch_checker.py +2 -17
- infrahub/graphql/auth/query_permission_checker/merge_operation_checker.py +1 -12
- infrahub/graphql/auth/query_permission_checker/object_permission_checker.py +6 -40
- infrahub/graphql/auth/query_permission_checker/super_admin_checker.py +5 -8
- infrahub/graphql/enums.py +2 -2
- infrahub/graphql/initialization.py +27 -8
- infrahub/graphql/manager.py +9 -3
- infrahub/graphql/models.py +6 -0
- infrahub/graphql/mutations/account.py +14 -10
- infrahub/graphql/mutations/computed_attribute.py +11 -22
- infrahub/graphql/mutations/diff.py +2 -0
- infrahub/graphql/mutations/main.py +5 -16
- infrahub/graphql/mutations/proposed_change.py +11 -20
- infrahub/graphql/mutations/resource_manager.py +6 -3
- infrahub/graphql/mutations/schema.py +8 -7
- infrahub/graphql/mutations/tasks.py +1 -1
- infrahub/graphql/permissions.py +3 -4
- infrahub/graphql/queries/account.py +2 -11
- infrahub/graphql/queries/resource_manager.py +21 -10
- infrahub/graphql/query.py +3 -1
- infrahub/graphql/resolvers/resolver.py +5 -1
- infrahub/graphql/types/task.py +14 -2
- infrahub/menu/generator.py +6 -18
- infrahub/message_bus/messages/event_node_mutated.py +2 -2
- infrahub/message_bus/operations/check/repository.py +2 -4
- infrahub/message_bus/operations/event/branch.py +2 -4
- infrahub/message_bus/operations/requests/proposed_change.py +1 -1
- infrahub/message_bus/operations/requests/repository.py +3 -5
- infrahub/message_bus/types.py +1 -1
- infrahub/permissions/__init__.py +12 -3
- infrahub/permissions/backend.py +2 -17
- infrahub/permissions/constants.py +12 -8
- infrahub/permissions/local_backend.py +5 -102
- infrahub/permissions/manager.py +135 -0
- infrahub/permissions/report.py +14 -25
- infrahub/permissions/types.py +6 -0
- infrahub/proposed_change/tasks.py +1 -1
- infrahub/task_manager/models.py +34 -5
- infrahub/task_manager/task.py +14 -6
- infrahub/visuals.py +1 -3
- infrahub_sdk/client.py +204 -43
- infrahub_sdk/ctl/cli_commands.py +106 -6
- infrahub_sdk/data.py +3 -2
- infrahub_sdk/graphql.py +5 -0
- infrahub_sdk/node.py +21 -2
- infrahub_sdk/queries.py +69 -0
- infrahub_sdk/schema/main.py +1 -0
- infrahub_sdk/testing/schemas/animal.py +1 -0
- infrahub_sdk/types.py +6 -0
- infrahub_sdk/utils.py +17 -0
- {infrahub_server-1.1.1.dist-info → infrahub_server-1.1.3.dist-info}/METADATA +1 -1
- {infrahub_server-1.1.1.dist-info → infrahub_server-1.1.3.dist-info}/RECORD +136 -131
- infrahub/core/diff/query/empty_roots.py +0 -33
- {infrahub_server-1.1.1.dist-info → infrahub_server-1.1.3.dist-info}/LICENSE.txt +0 -0
- {infrahub_server-1.1.1.dist-info → infrahub_server-1.1.3.dist-info}/WHEEL +0 -0
- {infrahub_server-1.1.1.dist-info → infrahub_server-1.1.3.dist-info}/entry_points.txt +0 -0
infrahub_sdk/client.py
CHANGED
|
@@ -46,13 +46,13 @@ from .node import (
|
|
|
46
46
|
)
|
|
47
47
|
from .object_store import ObjectStore, ObjectStoreSync
|
|
48
48
|
from .protocols_base import CoreNode, CoreNodeSync
|
|
49
|
-
from .queries import get_commit_update_mutation
|
|
49
|
+
from .queries import QUERY_USER, get_commit_update_mutation
|
|
50
50
|
from .query_groups import InfrahubGroupContext, InfrahubGroupContextSync
|
|
51
51
|
from .schema import InfrahubSchema, InfrahubSchemaSync, NodeSchemaAPI
|
|
52
52
|
from .store import NodeStore, NodeStoreSync
|
|
53
53
|
from .timestamp import Timestamp
|
|
54
|
-
from .types import AsyncRequester, HTTPMethod, SyncRequester
|
|
55
|
-
from .utils import decode_json, is_valid_uuid
|
|
54
|
+
from .types import AsyncRequester, HTTPMethod, Order, SyncRequester
|
|
55
|
+
from .utils import decode_json, get_user_permissions, is_valid_uuid
|
|
56
56
|
|
|
57
57
|
if TYPE_CHECKING:
|
|
58
58
|
from types import TracebackType
|
|
@@ -272,6 +272,22 @@ class InfrahubClient(BaseClient):
|
|
|
272
272
|
self._request_method: AsyncRequester = self.config.requester or self._default_request_method
|
|
273
273
|
self.group_context = InfrahubGroupContext(self)
|
|
274
274
|
|
|
275
|
+
async def get_version(self) -> str:
|
|
276
|
+
"""Return the Infrahub version."""
|
|
277
|
+
response = await self.execute_graphql(query="query { InfrahubInfo { version }}")
|
|
278
|
+
version = response.get("InfrahubInfo", {}).get("version", "")
|
|
279
|
+
return version
|
|
280
|
+
|
|
281
|
+
async def get_user(self) -> dict:
|
|
282
|
+
"""Return user information"""
|
|
283
|
+
user_info = await self.execute_graphql(query=QUERY_USER)
|
|
284
|
+
return user_info
|
|
285
|
+
|
|
286
|
+
async def get_user_permissions(self) -> dict:
|
|
287
|
+
"""Return user permissions"""
|
|
288
|
+
user_info = await self.get_user()
|
|
289
|
+
return get_user_permissions(user_info["AccountProfile"]["member_of_groups"]["edges"])
|
|
290
|
+
|
|
275
291
|
@overload
|
|
276
292
|
async def create(
|
|
277
293
|
self,
|
|
@@ -525,6 +541,25 @@ class InfrahubClient(BaseClient):
|
|
|
525
541
|
|
|
526
542
|
return ProcessRelationsNode(nodes=nodes, related_nodes=related_nodes)
|
|
527
543
|
|
|
544
|
+
async def count(
|
|
545
|
+
self,
|
|
546
|
+
kind: str | type[SchemaType],
|
|
547
|
+
at: Timestamp | None = None,
|
|
548
|
+
branch: str | None = None,
|
|
549
|
+
timeout: int | None = None,
|
|
550
|
+
) -> int:
|
|
551
|
+
"""Return the number of nodes of a given kind."""
|
|
552
|
+
schema = await self.schema.get(kind=kind, branch=branch)
|
|
553
|
+
|
|
554
|
+
branch = branch or self.default_branch
|
|
555
|
+
if at:
|
|
556
|
+
at = Timestamp(at)
|
|
557
|
+
|
|
558
|
+
response = await self.execute_graphql(
|
|
559
|
+
query=Query(query={schema.kind: {"count": None}}).render(), branch_name=branch, at=at, timeout=timeout
|
|
560
|
+
)
|
|
561
|
+
return int(response.get(schema.kind, {}).get("count", 0))
|
|
562
|
+
|
|
528
563
|
@overload
|
|
529
564
|
async def all(
|
|
530
565
|
self,
|
|
@@ -540,6 +575,8 @@ class InfrahubClient(BaseClient):
|
|
|
540
575
|
fragment: bool = ...,
|
|
541
576
|
prefetch_relationships: bool = ...,
|
|
542
577
|
property: bool = ...,
|
|
578
|
+
parallel: bool = ...,
|
|
579
|
+
order: Order | None = ...,
|
|
543
580
|
) -> list[SchemaType]: ...
|
|
544
581
|
|
|
545
582
|
@overload
|
|
@@ -557,6 +594,8 @@ class InfrahubClient(BaseClient):
|
|
|
557
594
|
fragment: bool = ...,
|
|
558
595
|
prefetch_relationships: bool = ...,
|
|
559
596
|
property: bool = ...,
|
|
597
|
+
parallel: bool = ...,
|
|
598
|
+
order: Order | None = ...,
|
|
560
599
|
) -> list[InfrahubNode]: ...
|
|
561
600
|
|
|
562
601
|
async def all(
|
|
@@ -573,6 +612,8 @@ class InfrahubClient(BaseClient):
|
|
|
573
612
|
fragment: bool = False,
|
|
574
613
|
prefetch_relationships: bool = False,
|
|
575
614
|
property: bool = False,
|
|
615
|
+
parallel: bool = False,
|
|
616
|
+
order: Order | None = None,
|
|
576
617
|
) -> list[InfrahubNode] | list[SchemaType]:
|
|
577
618
|
"""Retrieve all nodes of a given kind
|
|
578
619
|
|
|
@@ -588,6 +629,8 @@ class InfrahubClient(BaseClient):
|
|
|
588
629
|
exclude (list[str], optional): List of attributes or relationships to exclude from the query.
|
|
589
630
|
fragment (bool, optional): Flag to use GraphQL fragments for generic schemas.
|
|
590
631
|
prefetch_relationships (bool, optional): Flag to indicate whether to prefetch related node data.
|
|
632
|
+
parallel (bool, optional): Whether to use parallel processing for the query.
|
|
633
|
+
order (Order, optional): Ordering related options. Setting `disable=True` enhances performances.
|
|
591
634
|
|
|
592
635
|
Returns:
|
|
593
636
|
list[InfrahubNode]: List of Nodes
|
|
@@ -605,6 +648,8 @@ class InfrahubClient(BaseClient):
|
|
|
605
648
|
fragment=fragment,
|
|
606
649
|
prefetch_relationships=prefetch_relationships,
|
|
607
650
|
property=property,
|
|
651
|
+
parallel=parallel,
|
|
652
|
+
order=order,
|
|
608
653
|
)
|
|
609
654
|
|
|
610
655
|
@overload
|
|
@@ -623,6 +668,8 @@ class InfrahubClient(BaseClient):
|
|
|
623
668
|
prefetch_relationships: bool = ...,
|
|
624
669
|
partial_match: bool = ...,
|
|
625
670
|
property: bool = ...,
|
|
671
|
+
parallel: bool = ...,
|
|
672
|
+
order: Order | None = ...,
|
|
626
673
|
**kwargs: Any,
|
|
627
674
|
) -> list[SchemaType]: ...
|
|
628
675
|
|
|
@@ -642,6 +689,8 @@ class InfrahubClient(BaseClient):
|
|
|
642
689
|
prefetch_relationships: bool = ...,
|
|
643
690
|
partial_match: bool = ...,
|
|
644
691
|
property: bool = ...,
|
|
692
|
+
parallel: bool = ...,
|
|
693
|
+
order: Order | None = ...,
|
|
645
694
|
**kwargs: Any,
|
|
646
695
|
) -> list[InfrahubNode]: ...
|
|
647
696
|
|
|
@@ -660,6 +709,8 @@ class InfrahubClient(BaseClient):
|
|
|
660
709
|
prefetch_relationships: bool = False,
|
|
661
710
|
partial_match: bool = False,
|
|
662
711
|
property: bool = False,
|
|
712
|
+
parallel: bool = False,
|
|
713
|
+
order: Order | None = None,
|
|
663
714
|
**kwargs: Any,
|
|
664
715
|
) -> list[InfrahubNode] | list[SchemaType]:
|
|
665
716
|
"""Retrieve nodes of a given kind based on provided filters.
|
|
@@ -677,32 +728,27 @@ class InfrahubClient(BaseClient):
|
|
|
677
728
|
fragment (bool, optional): Flag to use GraphQL fragments for generic schemas.
|
|
678
729
|
prefetch_relationships (bool, optional): Flag to indicate whether to prefetch related node data.
|
|
679
730
|
partial_match (bool, optional): Allow partial match of filter criteria for the query.
|
|
731
|
+
parallel (bool, optional): Whether to use parallel processing for the query.
|
|
732
|
+
order (Order, optional): Ordering related options. Setting `disable=True` enhances performances.
|
|
680
733
|
**kwargs (Any): Additional filter criteria for the query.
|
|
681
734
|
|
|
682
735
|
Returns:
|
|
683
736
|
list[InfrahubNodeSync]: List of Nodes that match the given filters.
|
|
684
737
|
"""
|
|
685
|
-
schema = await self.schema.get(kind=kind, branch=branch)
|
|
686
|
-
|
|
687
738
|
branch = branch or self.default_branch
|
|
739
|
+
schema = await self.schema.get(kind=kind, branch=branch)
|
|
688
740
|
if at:
|
|
689
741
|
at = Timestamp(at)
|
|
690
742
|
|
|
691
743
|
node = InfrahubNode(client=self, schema=schema, branch=branch)
|
|
692
744
|
filters = kwargs
|
|
745
|
+
pagination_size = self.pagination_size
|
|
693
746
|
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
has_remaining_items = True
|
|
698
|
-
page_number = 1
|
|
699
|
-
|
|
700
|
-
while has_remaining_items:
|
|
701
|
-
page_offset = (page_number - 1) * self.pagination_size
|
|
702
|
-
|
|
747
|
+
async def process_page(page_offset: int, page_number: int) -> tuple[dict, ProcessRelationsNode]:
|
|
748
|
+
"""Process a single page of results."""
|
|
703
749
|
query_data = await InfrahubNode(client=self, schema=schema, branch=branch).generate_query_data(
|
|
704
750
|
offset=offset or page_offset,
|
|
705
|
-
limit=limit or
|
|
751
|
+
limit=limit or pagination_size,
|
|
706
752
|
filters=filters,
|
|
707
753
|
include=include,
|
|
708
754
|
exclude=exclude,
|
|
@@ -710,6 +756,7 @@ class InfrahubClient(BaseClient):
|
|
|
710
756
|
prefetch_relationships=prefetch_relationships,
|
|
711
757
|
partial_match=partial_match,
|
|
712
758
|
property=property,
|
|
759
|
+
order=order,
|
|
713
760
|
)
|
|
714
761
|
query = Query(query=query_data)
|
|
715
762
|
response = await self.execute_graphql(
|
|
@@ -727,14 +774,48 @@ class InfrahubClient(BaseClient):
|
|
|
727
774
|
prefetch_relationships=prefetch_relationships,
|
|
728
775
|
timeout=timeout,
|
|
729
776
|
)
|
|
730
|
-
|
|
731
|
-
|
|
777
|
+
return response, process_result
|
|
778
|
+
|
|
779
|
+
async def process_batch() -> tuple[list[InfrahubNode], list[InfrahubNode]]:
|
|
780
|
+
"""Process queries in parallel mode."""
|
|
781
|
+
nodes = []
|
|
782
|
+
related_nodes = []
|
|
783
|
+
batch_process = await self.create_batch()
|
|
784
|
+
count = await self.count(kind=schema.kind)
|
|
785
|
+
total_pages = (count + pagination_size - 1) // pagination_size
|
|
786
|
+
|
|
787
|
+
for page_number in range(1, total_pages + 1):
|
|
788
|
+
page_offset = (page_number - 1) * pagination_size
|
|
789
|
+
batch_process.add(task=process_page, node=node, page_offset=page_offset, page_number=page_number)
|
|
732
790
|
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
791
|
+
async for _, response in batch_process.execute():
|
|
792
|
+
nodes.extend(response[1]["nodes"])
|
|
793
|
+
related_nodes.extend(response[1]["related_nodes"])
|
|
736
794
|
|
|
737
|
-
|
|
795
|
+
return nodes, related_nodes
|
|
796
|
+
|
|
797
|
+
async def process_non_batch() -> tuple[list[InfrahubNode], list[InfrahubNode]]:
|
|
798
|
+
"""Process queries without parallel mode."""
|
|
799
|
+
nodes = []
|
|
800
|
+
related_nodes = []
|
|
801
|
+
has_remaining_items = True
|
|
802
|
+
page_number = 1
|
|
803
|
+
|
|
804
|
+
while has_remaining_items:
|
|
805
|
+
page_offset = (page_number - 1) * pagination_size
|
|
806
|
+
response, process_result = await process_page(page_offset, page_number)
|
|
807
|
+
|
|
808
|
+
nodes.extend(process_result["nodes"])
|
|
809
|
+
related_nodes.extend(process_result["related_nodes"])
|
|
810
|
+
remaining_items = response[schema.kind].get("count", 0) - (page_offset + pagination_size)
|
|
811
|
+
if remaining_items < 0 or offset is not None or limit is not None:
|
|
812
|
+
has_remaining_items = False
|
|
813
|
+
page_number += 1
|
|
814
|
+
|
|
815
|
+
return nodes, related_nodes
|
|
816
|
+
|
|
817
|
+
# Select parallel or non-parallel processing
|
|
818
|
+
nodes, related_nodes = await (process_batch() if parallel else process_non_batch())
|
|
738
819
|
|
|
739
820
|
if populate_store:
|
|
740
821
|
for node in nodes:
|
|
@@ -744,7 +825,6 @@ class InfrahubClient(BaseClient):
|
|
|
744
825
|
for node in related_nodes:
|
|
745
826
|
if node.id:
|
|
746
827
|
self.store.set(key=node.id, node=node)
|
|
747
|
-
|
|
748
828
|
return nodes
|
|
749
829
|
|
|
750
830
|
def clone(self) -> InfrahubClient:
|
|
@@ -1425,6 +1505,22 @@ class InfrahubClientSync(BaseClient):
|
|
|
1425
1505
|
self._request_method: SyncRequester = self.config.sync_requester or self._default_request_method
|
|
1426
1506
|
self.group_context = InfrahubGroupContextSync(self)
|
|
1427
1507
|
|
|
1508
|
+
def get_version(self) -> str:
|
|
1509
|
+
"""Return the Infrahub version."""
|
|
1510
|
+
response = self.execute_graphql(query="query { InfrahubInfo { version }}")
|
|
1511
|
+
version = response.get("InfrahubInfo", {}).get("version", "")
|
|
1512
|
+
return version
|
|
1513
|
+
|
|
1514
|
+
def get_user(self) -> dict:
|
|
1515
|
+
"""Return user information"""
|
|
1516
|
+
user_info = self.execute_graphql(query=QUERY_USER)
|
|
1517
|
+
return user_info
|
|
1518
|
+
|
|
1519
|
+
def get_user_permissions(self) -> dict:
|
|
1520
|
+
"""Return user permissions"""
|
|
1521
|
+
user_info = self.get_user()
|
|
1522
|
+
return get_user_permissions(user_info["AccountProfile"]["member_of_groups"]["edges"])
|
|
1523
|
+
|
|
1428
1524
|
@overload
|
|
1429
1525
|
def create(
|
|
1430
1526
|
self,
|
|
@@ -1549,6 +1645,25 @@ class InfrahubClientSync(BaseClient):
|
|
|
1549
1645
|
|
|
1550
1646
|
# TODO add a special method to execute mutation that will check if the method returned OK
|
|
1551
1647
|
|
|
1648
|
+
def count(
|
|
1649
|
+
self,
|
|
1650
|
+
kind: str | type[SchemaType],
|
|
1651
|
+
at: Timestamp | None = None,
|
|
1652
|
+
branch: str | None = None,
|
|
1653
|
+
timeout: int | None = None,
|
|
1654
|
+
) -> int:
|
|
1655
|
+
"""Return the number of nodes of a given kind."""
|
|
1656
|
+
schema = self.schema.get(kind=kind, branch=branch)
|
|
1657
|
+
|
|
1658
|
+
branch = branch or self.default_branch
|
|
1659
|
+
if at:
|
|
1660
|
+
at = Timestamp(at)
|
|
1661
|
+
|
|
1662
|
+
response = self.execute_graphql(
|
|
1663
|
+
query=Query(query={schema.kind: {"count": None}}).render(), branch_name=branch, at=at, timeout=timeout
|
|
1664
|
+
)
|
|
1665
|
+
return int(response.get(schema.kind, {}).get("count", 0))
|
|
1666
|
+
|
|
1552
1667
|
@overload
|
|
1553
1668
|
def all(
|
|
1554
1669
|
self,
|
|
@@ -1564,6 +1679,8 @@ class InfrahubClientSync(BaseClient):
|
|
|
1564
1679
|
fragment: bool = ...,
|
|
1565
1680
|
prefetch_relationships: bool = ...,
|
|
1566
1681
|
property: bool = ...,
|
|
1682
|
+
parallel: bool = ...,
|
|
1683
|
+
order: Order | None = ...,
|
|
1567
1684
|
) -> list[SchemaTypeSync]: ...
|
|
1568
1685
|
|
|
1569
1686
|
@overload
|
|
@@ -1581,6 +1698,8 @@ class InfrahubClientSync(BaseClient):
|
|
|
1581
1698
|
fragment: bool = ...,
|
|
1582
1699
|
prefetch_relationships: bool = ...,
|
|
1583
1700
|
property: bool = ...,
|
|
1701
|
+
parallel: bool = ...,
|
|
1702
|
+
order: Order | None = ...,
|
|
1584
1703
|
) -> list[InfrahubNodeSync]: ...
|
|
1585
1704
|
|
|
1586
1705
|
def all(
|
|
@@ -1597,6 +1716,8 @@ class InfrahubClientSync(BaseClient):
|
|
|
1597
1716
|
fragment: bool = False,
|
|
1598
1717
|
prefetch_relationships: bool = False,
|
|
1599
1718
|
property: bool = False,
|
|
1719
|
+
parallel: bool = False,
|
|
1720
|
+
order: Order | None = None,
|
|
1600
1721
|
) -> list[InfrahubNodeSync] | list[SchemaTypeSync]:
|
|
1601
1722
|
"""Retrieve all nodes of a given kind
|
|
1602
1723
|
|
|
@@ -1612,6 +1733,8 @@ class InfrahubClientSync(BaseClient):
|
|
|
1612
1733
|
exclude (list[str], optional): List of attributes or relationships to exclude from the query.
|
|
1613
1734
|
fragment (bool, optional): Flag to use GraphQL fragments for generic schemas.
|
|
1614
1735
|
prefetch_relationships (bool, optional): Flag to indicate whether to prefetch related node data.
|
|
1736
|
+
parallel (bool, optional): Whether to use parallel processing for the query.
|
|
1737
|
+
order (Order, optional): Ordering related options. Setting `disable=True` enhances performances.
|
|
1615
1738
|
|
|
1616
1739
|
Returns:
|
|
1617
1740
|
list[InfrahubNodeSync]: List of Nodes
|
|
@@ -1629,6 +1752,8 @@ class InfrahubClientSync(BaseClient):
|
|
|
1629
1752
|
fragment=fragment,
|
|
1630
1753
|
prefetch_relationships=prefetch_relationships,
|
|
1631
1754
|
property=property,
|
|
1755
|
+
parallel=parallel,
|
|
1756
|
+
order=order,
|
|
1632
1757
|
)
|
|
1633
1758
|
|
|
1634
1759
|
def _process_nodes_and_relationships(
|
|
@@ -1682,6 +1807,8 @@ class InfrahubClientSync(BaseClient):
|
|
|
1682
1807
|
prefetch_relationships: bool = ...,
|
|
1683
1808
|
partial_match: bool = ...,
|
|
1684
1809
|
property: bool = ...,
|
|
1810
|
+
parallel: bool = ...,
|
|
1811
|
+
order: Order | None = ...,
|
|
1685
1812
|
**kwargs: Any,
|
|
1686
1813
|
) -> list[SchemaTypeSync]: ...
|
|
1687
1814
|
|
|
@@ -1701,6 +1828,8 @@ class InfrahubClientSync(BaseClient):
|
|
|
1701
1828
|
prefetch_relationships: bool = ...,
|
|
1702
1829
|
partial_match: bool = ...,
|
|
1703
1830
|
property: bool = ...,
|
|
1831
|
+
parallel: bool = ...,
|
|
1832
|
+
order: Order | None = ...,
|
|
1704
1833
|
**kwargs: Any,
|
|
1705
1834
|
) -> list[InfrahubNodeSync]: ...
|
|
1706
1835
|
|
|
@@ -1719,6 +1848,8 @@ class InfrahubClientSync(BaseClient):
|
|
|
1719
1848
|
prefetch_relationships: bool = False,
|
|
1720
1849
|
partial_match: bool = False,
|
|
1721
1850
|
property: bool = False,
|
|
1851
|
+
parallel: bool = False,
|
|
1852
|
+
order: Order | None = None,
|
|
1722
1853
|
**kwargs: Any,
|
|
1723
1854
|
) -> list[InfrahubNodeSync] | list[SchemaTypeSync]:
|
|
1724
1855
|
"""Retrieve nodes of a given kind based on provided filters.
|
|
@@ -1736,32 +1867,26 @@ class InfrahubClientSync(BaseClient):
|
|
|
1736
1867
|
fragment (bool, optional): Flag to use GraphQL fragments for generic schemas.
|
|
1737
1868
|
prefetch_relationships (bool, optional): Flag to indicate whether to prefetch related node data.
|
|
1738
1869
|
partial_match (bool, optional): Allow partial match of filter criteria for the query.
|
|
1870
|
+
parallel (bool, optional): Whether to use parallel processing for the query.
|
|
1871
|
+
order (Order, optional): Ordering related options. Setting `disable=True` enhances performances.
|
|
1739
1872
|
**kwargs (Any): Additional filter criteria for the query.
|
|
1740
1873
|
|
|
1741
1874
|
Returns:
|
|
1742
1875
|
list[InfrahubNodeSync]: List of Nodes that match the given filters.
|
|
1743
1876
|
"""
|
|
1744
|
-
schema = self.schema.get(kind=kind, branch=branch)
|
|
1745
|
-
|
|
1746
1877
|
branch = branch or self.default_branch
|
|
1878
|
+
schema = self.schema.get(kind=kind, branch=branch)
|
|
1879
|
+
node = InfrahubNodeSync(client=self, schema=schema, branch=branch)
|
|
1747
1880
|
if at:
|
|
1748
1881
|
at = Timestamp(at)
|
|
1749
|
-
|
|
1750
|
-
node = InfrahubNodeSync(client=self, schema=schema, branch=branch)
|
|
1751
1882
|
filters = kwargs
|
|
1883
|
+
pagination_size = self.pagination_size
|
|
1752
1884
|
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
has_remaining_items = True
|
|
1757
|
-
page_number = 1
|
|
1758
|
-
|
|
1759
|
-
while has_remaining_items:
|
|
1760
|
-
page_offset = (page_number - 1) * self.pagination_size
|
|
1761
|
-
|
|
1885
|
+
def process_page(page_offset: int, page_number: int) -> tuple[dict, ProcessRelationsNodeSync]:
|
|
1886
|
+
"""Process a single page of results."""
|
|
1762
1887
|
query_data = InfrahubNodeSync(client=self, schema=schema, branch=branch).generate_query_data(
|
|
1763
1888
|
offset=offset or page_offset,
|
|
1764
|
-
limit=limit or
|
|
1889
|
+
limit=limit or pagination_size,
|
|
1765
1890
|
filters=filters,
|
|
1766
1891
|
include=include,
|
|
1767
1892
|
exclude=exclude,
|
|
@@ -1769,6 +1894,7 @@ class InfrahubClientSync(BaseClient):
|
|
|
1769
1894
|
prefetch_relationships=prefetch_relationships,
|
|
1770
1895
|
partial_match=partial_match,
|
|
1771
1896
|
property=property,
|
|
1897
|
+
order=order,
|
|
1772
1898
|
)
|
|
1773
1899
|
query = Query(query=query_data)
|
|
1774
1900
|
response = self.execute_graphql(
|
|
@@ -1786,14 +1912,50 @@ class InfrahubClientSync(BaseClient):
|
|
|
1786
1912
|
prefetch_relationships=prefetch_relationships,
|
|
1787
1913
|
timeout=timeout,
|
|
1788
1914
|
)
|
|
1789
|
-
|
|
1790
|
-
|
|
1915
|
+
return response, process_result
|
|
1916
|
+
|
|
1917
|
+
def process_batch() -> tuple[list[InfrahubNodeSync], list[InfrahubNodeSync]]:
|
|
1918
|
+
"""Process queries in parallel mode."""
|
|
1919
|
+
nodes = []
|
|
1920
|
+
related_nodes = []
|
|
1921
|
+
batch_process = self.create_batch()
|
|
1922
|
+
|
|
1923
|
+
count = self.count(kind=schema.kind)
|
|
1924
|
+
total_pages = (count + pagination_size - 1) // pagination_size
|
|
1791
1925
|
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1926
|
+
for page_number in range(1, total_pages + 1):
|
|
1927
|
+
page_offset = (page_number - 1) * pagination_size
|
|
1928
|
+
batch_process.add(task=process_page, node=node, page_offset=page_offset, page_number=page_number)
|
|
1795
1929
|
|
|
1796
|
-
|
|
1930
|
+
for _, response in batch_process.execute():
|
|
1931
|
+
nodes.extend(response[1]["nodes"])
|
|
1932
|
+
related_nodes.extend(response[1]["related_nodes"])
|
|
1933
|
+
|
|
1934
|
+
return nodes, related_nodes
|
|
1935
|
+
|
|
1936
|
+
def process_non_batch() -> tuple[list[InfrahubNodeSync], list[InfrahubNodeSync]]:
|
|
1937
|
+
"""Process queries without parallel mode."""
|
|
1938
|
+
nodes = []
|
|
1939
|
+
related_nodes = []
|
|
1940
|
+
has_remaining_items = True
|
|
1941
|
+
page_number = 1
|
|
1942
|
+
|
|
1943
|
+
while has_remaining_items:
|
|
1944
|
+
page_offset = (page_number - 1) * pagination_size
|
|
1945
|
+
response, process_result = process_page(page_offset, page_number)
|
|
1946
|
+
|
|
1947
|
+
nodes.extend(process_result["nodes"])
|
|
1948
|
+
related_nodes.extend(process_result["related_nodes"])
|
|
1949
|
+
|
|
1950
|
+
remaining_items = response[schema.kind].get("count", 0) - (page_offset + pagination_size)
|
|
1951
|
+
if remaining_items < 0 or offset is not None or limit is not None:
|
|
1952
|
+
has_remaining_items = False
|
|
1953
|
+
page_number += 1
|
|
1954
|
+
|
|
1955
|
+
return nodes, related_nodes
|
|
1956
|
+
|
|
1957
|
+
# Select parallel or non-parallel processing
|
|
1958
|
+
nodes, related_nodes = process_batch() if parallel else process_non_batch()
|
|
1797
1959
|
|
|
1798
1960
|
if populate_store:
|
|
1799
1961
|
for node in nodes:
|
|
@@ -1803,7 +1965,6 @@ class InfrahubClientSync(BaseClient):
|
|
|
1803
1965
|
for node in related_nodes:
|
|
1804
1966
|
if node.id:
|
|
1805
1967
|
self.store.set(key=node.id, node=node)
|
|
1806
|
-
|
|
1807
1968
|
return nodes
|
|
1808
1969
|
|
|
1809
1970
|
@overload
|
infrahub_sdk/ctl/cli_commands.py
CHANGED
|
@@ -4,6 +4,7 @@ import asyncio
|
|
|
4
4
|
import functools
|
|
5
5
|
import importlib
|
|
6
6
|
import logging
|
|
7
|
+
import platform
|
|
7
8
|
import sys
|
|
8
9
|
from pathlib import Path
|
|
9
10
|
from typing import TYPE_CHECKING, Any, Callable
|
|
@@ -12,7 +13,11 @@ import jinja2
|
|
|
12
13
|
import typer
|
|
13
14
|
import ujson
|
|
14
15
|
from rich.console import Console
|
|
16
|
+
from rich.layout import Layout
|
|
15
17
|
from rich.logging import RichHandler
|
|
18
|
+
from rich.panel import Panel
|
|
19
|
+
from rich.pretty import Pretty
|
|
20
|
+
from rich.table import Table
|
|
16
21
|
from rich.traceback import Traceback
|
|
17
22
|
|
|
18
23
|
from .. import __version__ as sdk_version
|
|
@@ -392,11 +397,106 @@ def protocols(
|
|
|
392
397
|
|
|
393
398
|
@app.command(name="version")
|
|
394
399
|
@catch_exception(console=console)
|
|
395
|
-
def version(
|
|
396
|
-
"""Display the version of
|
|
400
|
+
def version() -> None:
|
|
401
|
+
"""Display the version of Python and the version of the Python SDK in use."""
|
|
397
402
|
|
|
398
|
-
|
|
399
|
-
response = client.execute_graphql(query="query { InfrahubInfo { version }}")
|
|
403
|
+
console.print(f"Python: {platform.python_version()}\nPython SDK: v{sdk_version}")
|
|
400
404
|
|
|
401
|
-
|
|
402
|
-
|
|
405
|
+
|
|
406
|
+
@app.command(name="info")
|
|
407
|
+
@catch_exception(console=console)
|
|
408
|
+
def info(detail: bool = typer.Option(False, help="Display detailed information."), _: str = CONFIG_PARAM) -> None: # noqa: PLR0915
|
|
409
|
+
"""Display the status of the Python SDK."""
|
|
410
|
+
|
|
411
|
+
info: dict[str, Any] = {
|
|
412
|
+
"error": None,
|
|
413
|
+
"status": ":x:",
|
|
414
|
+
"infrahub_version": "N/A",
|
|
415
|
+
"user_info": {},
|
|
416
|
+
"groups": {},
|
|
417
|
+
}
|
|
418
|
+
try:
|
|
419
|
+
client = initialize_client_sync()
|
|
420
|
+
info["infrahub_version"] = client.get_version()
|
|
421
|
+
info["user_info"] = client.get_user()
|
|
422
|
+
info["status"] = ":white_heavy_check_mark:"
|
|
423
|
+
info["groups"] = client.get_user_permissions()
|
|
424
|
+
except Exception as e:
|
|
425
|
+
info["error"] = f"{e!s} ({e.__class__.__name__})"
|
|
426
|
+
|
|
427
|
+
if detail:
|
|
428
|
+
layout = Layout()
|
|
429
|
+
|
|
430
|
+
# Layout structure
|
|
431
|
+
new_console = Console(height=45)
|
|
432
|
+
layout = Layout()
|
|
433
|
+
layout.split_column(
|
|
434
|
+
Layout(name="body", ratio=1),
|
|
435
|
+
)
|
|
436
|
+
layout["body"].split_row(
|
|
437
|
+
Layout(name="left"),
|
|
438
|
+
Layout(name="right"),
|
|
439
|
+
)
|
|
440
|
+
|
|
441
|
+
layout["left"].split_column(
|
|
442
|
+
Layout(name="connection_status", size=7),
|
|
443
|
+
Layout(name="client_info", ratio=1),
|
|
444
|
+
)
|
|
445
|
+
|
|
446
|
+
layout["right"].split_column(
|
|
447
|
+
Layout(name="version_info", size=7),
|
|
448
|
+
Layout(name="infrahub_info", ratio=1),
|
|
449
|
+
)
|
|
450
|
+
|
|
451
|
+
# Connection status panel
|
|
452
|
+
connection_status = Table(show_header=False, box=None)
|
|
453
|
+
connection_status.add_row("Server Address:", client.config.address)
|
|
454
|
+
connection_status.add_row("Status:", info["status"])
|
|
455
|
+
if info["error"]:
|
|
456
|
+
connection_status.add_row("Error Reason:", info["error"])
|
|
457
|
+
layout["connection_status"].update(Panel(connection_status, title="Connection Status"))
|
|
458
|
+
|
|
459
|
+
# Version information panel
|
|
460
|
+
version_info = Table(show_header=False, box=None)
|
|
461
|
+
version_info.add_row("Python Version:", platform.python_version())
|
|
462
|
+
version_info.add_row("Infrahub Version", info["infrahub_version"])
|
|
463
|
+
version_info.add_row("Infrahub SDK:", sdk_version)
|
|
464
|
+
layout["version_info"].update(Panel(version_info, title="Version Information"))
|
|
465
|
+
|
|
466
|
+
# SDK client configuration panel
|
|
467
|
+
pretty_model = Pretty(client.config.model_dump(), expand_all=True)
|
|
468
|
+
layout["client_info"].update(Panel(pretty_model, title="Client Info"))
|
|
469
|
+
|
|
470
|
+
# Infrahub information planel
|
|
471
|
+
infrahub_info = Table(show_header=False, box=None)
|
|
472
|
+
if info["user_info"]:
|
|
473
|
+
infrahub_info.add_row("User:", info["user_info"]["AccountProfile"]["display_label"])
|
|
474
|
+
infrahub_info.add_row("Description:", info["user_info"]["AccountProfile"]["description"]["value"])
|
|
475
|
+
infrahub_info.add_row("Status:", info["user_info"]["AccountProfile"]["status"]["label"])
|
|
476
|
+
infrahub_info.add_row(
|
|
477
|
+
"Number of Groups:", str(info["user_info"]["AccountProfile"]["member_of_groups"]["count"])
|
|
478
|
+
)
|
|
479
|
+
|
|
480
|
+
if groups := info["groups"]:
|
|
481
|
+
infrahub_info.add_row("Groups:", "")
|
|
482
|
+
for group, roles in groups.items():
|
|
483
|
+
infrahub_info.add_row("", group, ", ".join(roles))
|
|
484
|
+
|
|
485
|
+
layout["infrahub_info"].update(Panel(infrahub_info, title="Infrahub Info"))
|
|
486
|
+
|
|
487
|
+
new_console.print(layout)
|
|
488
|
+
else:
|
|
489
|
+
# Simple output
|
|
490
|
+
table = Table(show_header=False, box=None)
|
|
491
|
+
table.add_row("Address:", client.config.address)
|
|
492
|
+
table.add_row("Connection Status:", info["status"])
|
|
493
|
+
if info["error"]:
|
|
494
|
+
table.add_row("Connection Error:", info["error"])
|
|
495
|
+
|
|
496
|
+
table.add_row("Python Version:", platform.python_version())
|
|
497
|
+
table.add_row("SDK Version:", sdk_version)
|
|
498
|
+
table.add_row("Infrahub Version:", info["infrahub_version"])
|
|
499
|
+
if account := info["user_info"].get("AccountProfile"):
|
|
500
|
+
table.add_row("User:", account["display_label"])
|
|
501
|
+
|
|
502
|
+
console.print(table)
|
infrahub_sdk/data.py
CHANGED
|
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from pydantic import BaseModel, ConfigDict, Field
|
|
4
4
|
|
|
5
|
-
from .node import InfrahubNode # noqa:
|
|
5
|
+
from .node import InfrahubNode # noqa: TC001
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
class RepositoryBranchInfo(BaseModel):
|
|
@@ -13,7 +13,8 @@ class RepositoryData(BaseModel):
|
|
|
13
13
|
model_config = ConfigDict(arbitrary_types_allowed=True)
|
|
14
14
|
repository: InfrahubNode = Field(..., description="InfrahubNode representing a Repository")
|
|
15
15
|
branches: dict[str, str] = Field(
|
|
16
|
-
...,
|
|
16
|
+
...,
|
|
17
|
+
description="Dictionary with the name of the branch as the key and the active commit id as the value",
|
|
17
18
|
)
|
|
18
19
|
|
|
19
20
|
branch_info: dict[str, RepositoryBranchInfo] = Field(default_factory=dict)
|
infrahub_sdk/graphql.py
CHANGED
|
@@ -2,6 +2,8 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from typing import Any
|
|
4
4
|
|
|
5
|
+
from pydantic import BaseModel
|
|
6
|
+
|
|
5
7
|
VARIABLE_TYPE_MAPPING = ((str, "String!"), (int, "Int!"), (float, "Float!"), (bool, "Boolean!"))
|
|
6
8
|
|
|
7
9
|
|
|
@@ -15,6 +17,9 @@ def convert_to_graphql_as_string(value: str | bool | list) -> str:
|
|
|
15
17
|
if isinstance(value, list):
|
|
16
18
|
values_as_string = [convert_to_graphql_as_string(item) for item in value]
|
|
17
19
|
return "[" + ", ".join(values_as_string) + "]"
|
|
20
|
+
if isinstance(value, BaseModel):
|
|
21
|
+
data = value.model_dump()
|
|
22
|
+
return "{ " + ", ".join(f"{key}: {convert_to_graphql_as_string(val)}" for key, val in data.items()) + " }"
|
|
18
23
|
|
|
19
24
|
return str(value)
|
|
20
25
|
|