infrahub-server 1.2.0rc0__py3-none-any.whl → 1.2.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- infrahub/api/dependencies.py +6 -6
- infrahub/api/diff/validation_models.py +7 -7
- infrahub/api/schema.py +1 -1
- infrahub/artifacts/models.py +5 -3
- infrahub/artifacts/tasks.py +3 -5
- infrahub/cli/__init__.py +13 -9
- infrahub/cli/constants.py +3 -0
- infrahub/cli/db.py +165 -183
- infrahub/cli/upgrade.py +146 -0
- infrahub/computed_attribute/gather.py +185 -0
- infrahub/computed_attribute/models.py +240 -12
- infrahub/computed_attribute/tasks.py +77 -441
- infrahub/computed_attribute/triggers.py +13 -47
- infrahub/config.py +43 -32
- infrahub/context.py +14 -0
- infrahub/core/account.py +4 -4
- infrahub/core/attribute.py +58 -58
- infrahub/core/branch/tasks.py +74 -22
- infrahub/core/changelog/diff.py +95 -36
- infrahub/core/changelog/models.py +217 -43
- infrahub/core/constants/__init__.py +28 -0
- infrahub/core/constants/infrahubkind.py +2 -0
- infrahub/core/constants/schema.py +2 -0
- infrahub/core/constraint/node/runner.py +9 -8
- infrahub/core/diff/branch_differ.py +10 -10
- infrahub/core/diff/enricher/cardinality_one.py +5 -0
- infrahub/core/diff/enricher/hierarchy.py +17 -4
- infrahub/core/diff/enricher/labels.py +5 -0
- infrahub/core/diff/enricher/path_identifier.py +4 -0
- infrahub/core/diff/ipam_diff_parser.py +4 -5
- infrahub/core/diff/model/diff.py +27 -27
- infrahub/core/diff/model/path.py +32 -9
- infrahub/core/diff/parent_node_adder.py +78 -0
- infrahub/core/diff/payload_builder.py +13 -2
- infrahub/core/diff/query/filters.py +2 -2
- infrahub/core/diff/query/merge.py +20 -17
- infrahub/core/diff/query/save.py +188 -182
- infrahub/core/diff/query/summary_counts_enricher.py +51 -4
- infrahub/core/diff/query_parser.py +4 -4
- infrahub/core/diff/repository/deserializer.py +8 -3
- infrahub/core/diff/repository/repository.py +156 -38
- infrahub/core/diff/tasks.py +4 -4
- infrahub/core/graph/__init__.py +1 -1
- infrahub/core/graph/index.py +3 -0
- infrahub/core/initialization.py +1 -10
- infrahub/core/ipam/constants.py +3 -4
- infrahub/core/ipam/reconciler.py +12 -12
- infrahub/core/ipam/utilization.py +10 -13
- infrahub/core/manager.py +36 -36
- infrahub/core/merge.py +7 -7
- infrahub/core/migrations/__init__.py +2 -3
- infrahub/core/migrations/graph/__init__.py +12 -3
- infrahub/core/migrations/graph/m017_add_core_profile.py +1 -5
- infrahub/core/migrations/graph/m018_uniqueness_nulls.py +4 -4
- infrahub/core/migrations/graph/m019_restore_rels_to_time.py +256 -0
- infrahub/core/migrations/graph/m020_duplicate_edges.py +160 -0
- infrahub/core/migrations/graph/m021_missing_hierarchy_merge.py +51 -0
- infrahub/core/migrations/graph/m022_add_generate_template_attr.py +48 -0
- infrahub/core/migrations/graph/m023_deduplicate_cardinality_one_relationships.py +96 -0
- infrahub/core/migrations/query/attribute_add.py +2 -2
- infrahub/core/migrations/query/node_duplicate.py +43 -26
- infrahub/core/migrations/query/schema_attribute_update.py +2 -2
- infrahub/core/migrations/schema/models.py +19 -4
- infrahub/core/migrations/schema/node_remove.py +26 -12
- infrahub/core/migrations/schema/tasks.py +2 -2
- infrahub/core/migrations/shared.py +16 -16
- infrahub/core/models.py +15 -6
- infrahub/core/node/__init__.py +43 -39
- infrahub/core/node/base.py +2 -4
- infrahub/core/node/constraints/attribute_uniqueness.py +2 -2
- infrahub/core/node/constraints/grouped_uniqueness.py +99 -47
- infrahub/core/node/constraints/interface.py +1 -2
- infrahub/core/node/delete_validator.py +3 -5
- infrahub/core/node/ipam.py +4 -4
- infrahub/core/node/permissions.py +7 -7
- infrahub/core/node/resource_manager/ip_address_pool.py +6 -6
- infrahub/core/node/resource_manager/ip_prefix_pool.py +6 -6
- infrahub/core/node/resource_manager/number_pool.py +3 -3
- infrahub/core/path.py +12 -12
- infrahub/core/property.py +11 -11
- infrahub/core/protocols.py +7 -0
- infrahub/core/protocols_base.py +21 -21
- infrahub/core/query/__init__.py +33 -33
- infrahub/core/query/attribute.py +6 -4
- infrahub/core/query/diff.py +3 -3
- infrahub/core/query/node.py +82 -32
- infrahub/core/query/relationship.py +228 -40
- infrahub/core/query/resource_manager.py +2 -0
- infrahub/core/query/standard_node.py +3 -3
- infrahub/core/query/subquery.py +9 -9
- infrahub/core/registry.py +13 -15
- infrahub/core/relationship/constraints/count.py +3 -4
- infrahub/core/relationship/constraints/peer_kind.py +3 -4
- infrahub/core/relationship/constraints/profiles_kind.py +2 -2
- infrahub/core/relationship/model.py +51 -59
- infrahub/core/schema/attribute_schema.py +16 -8
- infrahub/core/schema/basenode_schema.py +105 -44
- infrahub/core/schema/computed_attribute.py +3 -3
- infrahub/core/schema/definitions/core/__init__.py +147 -0
- infrahub/core/schema/definitions/core/account.py +171 -0
- infrahub/core/schema/definitions/core/artifact.py +136 -0
- infrahub/core/schema/definitions/core/builtin.py +24 -0
- infrahub/core/schema/definitions/core/check.py +68 -0
- infrahub/core/schema/definitions/core/core.py +17 -0
- infrahub/core/schema/definitions/core/generator.py +100 -0
- infrahub/core/schema/definitions/core/graphql_query.py +79 -0
- infrahub/core/schema/definitions/core/group.py +108 -0
- infrahub/core/schema/definitions/core/ipam.py +193 -0
- infrahub/core/schema/definitions/core/lineage.py +19 -0
- infrahub/core/schema/definitions/core/menu.py +48 -0
- infrahub/core/schema/definitions/core/permission.py +163 -0
- infrahub/core/schema/definitions/core/profile.py +18 -0
- infrahub/core/schema/definitions/core/propose_change.py +97 -0
- infrahub/core/schema/definitions/core/propose_change_comment.py +193 -0
- infrahub/core/schema/definitions/core/propose_change_validator.py +328 -0
- infrahub/core/schema/definitions/core/repository.py +286 -0
- infrahub/core/schema/definitions/core/resource_pool.py +170 -0
- infrahub/core/schema/definitions/core/template.py +27 -0
- infrahub/core/schema/definitions/core/transform.py +96 -0
- infrahub/core/schema/definitions/core/webhook.py +134 -0
- infrahub/core/schema/definitions/internal.py +16 -16
- infrahub/core/schema/dropdown.py +3 -4
- infrahub/core/schema/generated/attribute_schema.py +15 -18
- infrahub/core/schema/generated/base_node_schema.py +12 -14
- infrahub/core/schema/generated/node_schema.py +3 -5
- infrahub/core/schema/generated/relationship_schema.py +9 -11
- infrahub/core/schema/generic_schema.py +2 -2
- infrahub/core/schema/manager.py +20 -9
- infrahub/core/schema/node_schema.py +4 -2
- infrahub/core/schema/relationship_schema.py +14 -6
- infrahub/core/schema/schema_branch.py +292 -144
- infrahub/core/schema/schema_branch_computed.py +41 -4
- infrahub/core/task/task.py +3 -3
- infrahub/core/task/user_task.py +15 -15
- infrahub/core/timestamp.py +3 -3
- infrahub/core/utils.py +20 -18
- infrahub/core/validators/__init__.py +1 -3
- infrahub/core/validators/aggregated_checker.py +2 -2
- infrahub/core/validators/attribute/choices.py +2 -2
- infrahub/core/validators/attribute/enum.py +2 -2
- infrahub/core/validators/attribute/kind.py +2 -2
- infrahub/core/validators/attribute/length.py +2 -2
- infrahub/core/validators/attribute/optional.py +2 -2
- infrahub/core/validators/attribute/regex.py +2 -2
- infrahub/core/validators/attribute/unique.py +2 -2
- infrahub/core/validators/checks_runner.py +25 -2
- infrahub/core/validators/determiner.py +1 -3
- infrahub/core/validators/interface.py +6 -2
- infrahub/core/validators/model.py +22 -3
- infrahub/core/validators/models/validate_migration.py +17 -4
- infrahub/core/validators/node/attribute.py +2 -2
- infrahub/core/validators/node/generate_profile.py +2 -2
- infrahub/core/validators/node/hierarchy.py +3 -5
- infrahub/core/validators/node/inherit_from.py +27 -5
- infrahub/core/validators/node/relationship.py +2 -2
- infrahub/core/validators/relationship/count.py +4 -4
- infrahub/core/validators/relationship/optional.py +2 -2
- infrahub/core/validators/relationship/peer.py +2 -2
- infrahub/core/validators/shared.py +2 -2
- infrahub/core/validators/tasks.py +8 -0
- infrahub/core/validators/uniqueness/checker.py +22 -21
- infrahub/core/validators/uniqueness/index.py +2 -2
- infrahub/core/validators/uniqueness/model.py +11 -11
- infrahub/database/__init__.py +27 -22
- infrahub/database/metrics.py +7 -1
- infrahub/dependencies/builder/constraint/grouped/node_runner.py +1 -3
- infrahub/dependencies/builder/diff/deserializer.py +3 -1
- infrahub/dependencies/builder/diff/enricher/hierarchy.py +3 -1
- infrahub/dependencies/builder/diff/parent_node_adder.py +8 -0
- infrahub/dependencies/component/registry.py +2 -2
- infrahub/events/__init__.py +25 -2
- infrahub/events/artifact_action.py +64 -0
- infrahub/events/branch_action.py +33 -22
- infrahub/events/generator.py +71 -0
- infrahub/events/group_action.py +51 -21
- infrahub/events/models.py +18 -19
- infrahub/events/node_action.py +88 -37
- infrahub/events/repository_action.py +5 -18
- infrahub/events/schema_action.py +4 -9
- infrahub/events/utils.py +16 -0
- infrahub/events/validator_action.py +55 -0
- infrahub/exceptions.py +32 -24
- infrahub/generators/models.py +2 -3
- infrahub/generators/tasks.py +24 -4
- infrahub/git/base.py +7 -7
- infrahub/git/integrator.py +48 -24
- infrahub/git/models.py +101 -9
- infrahub/git/repository.py +3 -3
- infrahub/git/tasks.py +408 -6
- infrahub/git/utils.py +48 -0
- infrahub/git/worktree.py +1 -2
- infrahub/git_credential/askpass.py +1 -2
- infrahub/graphql/analyzer.py +12 -0
- infrahub/graphql/app.py +13 -15
- infrahub/graphql/context.py +39 -0
- infrahub/graphql/initialization.py +3 -0
- infrahub/graphql/loaders/node.py +2 -12
- infrahub/graphql/loaders/peers.py +77 -0
- infrahub/graphql/loaders/shared.py +13 -0
- infrahub/graphql/manager.py +17 -19
- infrahub/graphql/mutations/artifact_definition.py +5 -5
- infrahub/graphql/mutations/branch.py +26 -1
- infrahub/graphql/mutations/computed_attribute.py +9 -5
- infrahub/graphql/mutations/diff.py +23 -11
- infrahub/graphql/mutations/diff_conflict.py +5 -0
- infrahub/graphql/mutations/generator.py +83 -0
- infrahub/graphql/mutations/graphql_query.py +5 -5
- infrahub/graphql/mutations/ipam.py +54 -74
- infrahub/graphql/mutations/main.py +195 -132
- infrahub/graphql/mutations/menu.py +7 -7
- infrahub/graphql/mutations/models.py +2 -4
- infrahub/graphql/mutations/node_getter/by_default_filter.py +10 -10
- infrahub/graphql/mutations/node_getter/by_hfid.py +1 -3
- infrahub/graphql/mutations/node_getter/by_id.py +1 -3
- infrahub/graphql/mutations/node_getter/interface.py +1 -2
- infrahub/graphql/mutations/proposed_change.py +7 -7
- infrahub/graphql/mutations/relationship.py +93 -19
- infrahub/graphql/mutations/repository.py +8 -8
- infrahub/graphql/mutations/resource_manager.py +3 -3
- infrahub/graphql/mutations/schema.py +19 -4
- infrahub/graphql/mutations/webhook.py +137 -0
- infrahub/graphql/parser.py +4 -4
- infrahub/graphql/permissions.py +1 -10
- infrahub/graphql/queries/diff/tree.py +19 -14
- infrahub/graphql/queries/event.py +5 -2
- infrahub/graphql/queries/ipam.py +2 -2
- infrahub/graphql/queries/relationship.py +2 -2
- infrahub/graphql/queries/search.py +2 -2
- infrahub/graphql/resolvers/many_relationship.py +264 -0
- infrahub/graphql/resolvers/resolver.py +13 -110
- infrahub/graphql/schema.py +2 -0
- infrahub/graphql/subscription/graphql_query.py +2 -0
- infrahub/graphql/types/context.py +12 -0
- infrahub/graphql/types/event.py +84 -17
- infrahub/graphql/types/node.py +2 -2
- infrahub/graphql/utils.py +2 -2
- infrahub/groups/ancestors.py +29 -0
- infrahub/groups/parsers.py +107 -0
- infrahub/lock.py +20 -20
- infrahub/menu/constants.py +0 -1
- infrahub/menu/generator.py +9 -21
- infrahub/menu/menu.py +17 -38
- infrahub/menu/models.py +117 -16
- infrahub/menu/repository.py +111 -0
- infrahub/menu/utils.py +5 -8
- infrahub/message_bus/__init__.py +11 -13
- infrahub/message_bus/messages/__init__.py +1 -21
- infrahub/message_bus/messages/check_generator_run.py +3 -3
- infrahub/message_bus/messages/finalize_validator_execution.py +3 -0
- infrahub/message_bus/messages/proposed_change/request_proposedchange_refreshartifacts.py +6 -0
- infrahub/message_bus/messages/request_generatordefinition_check.py +2 -0
- infrahub/message_bus/messages/send_echo_request.py +1 -1
- infrahub/message_bus/operations/__init__.py +1 -10
- infrahub/message_bus/operations/check/__init__.py +2 -2
- infrahub/message_bus/operations/check/generator.py +1 -0
- infrahub/message_bus/operations/event/__init__.py +2 -2
- infrahub/message_bus/operations/event/worker.py +0 -3
- infrahub/message_bus/operations/finalize/validator.py +51 -1
- infrahub/message_bus/operations/requests/__init__.py +0 -2
- infrahub/message_bus/operations/requests/generator_definition.py +21 -23
- infrahub/message_bus/operations/requests/proposed_change.py +14 -10
- infrahub/permissions/globals.py +15 -0
- infrahub/pools/number.py +2 -4
- infrahub/proposed_change/models.py +3 -0
- infrahub/proposed_change/tasks.py +58 -45
- infrahub/pytest_plugin.py +13 -10
- infrahub/server.py +2 -3
- infrahub/services/__init__.py +2 -2
- infrahub/services/adapters/cache/__init__.py +4 -6
- infrahub/services/adapters/cache/nats.py +4 -5
- infrahub/services/adapters/cache/redis.py +3 -7
- infrahub/services/adapters/event/__init__.py +1 -1
- infrahub/services/adapters/message_bus/__init__.py +3 -3
- infrahub/services/adapters/message_bus/local.py +2 -2
- infrahub/services/adapters/message_bus/nats.py +4 -4
- infrahub/services/adapters/message_bus/rabbitmq.py +4 -4
- infrahub/services/adapters/workflow/local.py +2 -2
- infrahub/services/component.py +5 -5
- infrahub/services/protocols.py +7 -7
- infrahub/services/scheduler.py +1 -3
- infrahub/task_manager/event.py +102 -9
- infrahub/task_manager/models.py +27 -7
- infrahub/tasks/artifact.py +7 -6
- infrahub/telemetry/__init__.py +0 -0
- infrahub/telemetry/constants.py +9 -0
- infrahub/telemetry/database.py +86 -0
- infrahub/telemetry/models.py +65 -0
- infrahub/telemetry/task_manager.py +77 -0
- infrahub/{tasks/telemetry.py → telemetry/tasks.py} +49 -56
- infrahub/telemetry/utils.py +11 -0
- infrahub/trace.py +4 -4
- infrahub/transformations/tasks.py +2 -2
- infrahub/trigger/catalogue.py +4 -6
- infrahub/trigger/constants.py +0 -8
- infrahub/trigger/models.py +54 -5
- infrahub/trigger/setup.py +90 -0
- infrahub/trigger/tasks.py +35 -84
- infrahub/utils.py +11 -1
- infrahub/validators/__init__.py +0 -0
- infrahub/validators/events.py +42 -0
- infrahub/validators/tasks.py +41 -0
- infrahub/webhook/gather.py +17 -0
- infrahub/webhook/models.py +176 -44
- infrahub/webhook/tasks.py +154 -155
- infrahub/webhook/triggers.py +31 -7
- infrahub/workers/infrahub_async.py +2 -2
- infrahub/workers/utils.py +2 -2
- infrahub/workflows/catalogue.py +86 -35
- infrahub/workflows/initialization.py +8 -2
- infrahub/workflows/models.py +27 -1
- infrahub/workflows/utils.py +10 -1
- infrahub_sdk/client.py +35 -8
- infrahub_sdk/config.py +3 -0
- infrahub_sdk/context.py +13 -0
- infrahub_sdk/ctl/branch.py +3 -2
- infrahub_sdk/ctl/cli_commands.py +5 -1
- infrahub_sdk/ctl/utils.py +0 -16
- infrahub_sdk/exceptions.py +12 -0
- infrahub_sdk/generator.py +4 -1
- infrahub_sdk/graphql.py +45 -13
- infrahub_sdk/node.py +71 -22
- infrahub_sdk/protocols.py +21 -8
- infrahub_sdk/protocols_base.py +32 -11
- infrahub_sdk/query_groups.py +6 -35
- infrahub_sdk/schema/__init__.py +55 -26
- infrahub_sdk/schema/main.py +8 -0
- infrahub_sdk/task/__init__.py +11 -0
- infrahub_sdk/task/constants.py +3 -0
- infrahub_sdk/task/exceptions.py +25 -0
- infrahub_sdk/task/manager.py +551 -0
- infrahub_sdk/task/models.py +74 -0
- infrahub_sdk/testing/schemas/animal.py +9 -0
- infrahub_sdk/timestamp.py +142 -33
- infrahub_sdk/utils.py +29 -1
- {infrahub_server-1.2.0rc0.dist-info → infrahub_server-1.2.1.dist-info}/METADATA +8 -6
- {infrahub_server-1.2.0rc0.dist-info → infrahub_server-1.2.1.dist-info}/RECORD +349 -293
- {infrahub_server-1.2.0rc0.dist-info → infrahub_server-1.2.1.dist-info}/entry_points.txt +1 -0
- infrahub_testcontainers/constants.py +2 -0
- infrahub_testcontainers/container.py +157 -12
- infrahub_testcontainers/docker-compose.test.yml +31 -6
- infrahub_testcontainers/helpers.py +18 -73
- infrahub_testcontainers/host.py +41 -0
- infrahub_testcontainers/measurements.py +93 -0
- infrahub_testcontainers/models.py +38 -0
- infrahub_testcontainers/performance_test.py +166 -0
- infrahub_testcontainers/plugin.py +136 -0
- infrahub_testcontainers/prometheus.yml +30 -0
- infrahub/core/schema/definitions/core.py +0 -2286
- infrahub/message_bus/messages/check_repository_checkdefinition.py +0 -20
- infrahub/message_bus/messages/check_repository_mergeconflicts.py +0 -16
- infrahub/message_bus/messages/check_repository_usercheck.py +0 -26
- infrahub/message_bus/messages/event_branch_create.py +0 -11
- infrahub/message_bus/messages/event_branch_delete.py +0 -11
- infrahub/message_bus/messages/event_branch_rebased.py +0 -9
- infrahub/message_bus/messages/event_node_mutated.py +0 -15
- infrahub/message_bus/messages/event_schema_update.py +0 -9
- infrahub/message_bus/messages/request_repository_checks.py +0 -12
- infrahub/message_bus/messages/request_repository_userchecks.py +0 -18
- infrahub/message_bus/operations/check/repository.py +0 -293
- infrahub/message_bus/operations/event/node.py +0 -20
- infrahub/message_bus/operations/event/schema.py +0 -17
- infrahub/message_bus/operations/requests/repository.py +0 -133
- infrahub/webhook/constants.py +0 -1
- {infrahub_server-1.2.0rc0.dist-info → infrahub_server-1.2.1.dist-info}/LICENSE.txt +0 -0
- {infrahub_server-1.2.0rc0.dist-info → infrahub_server-1.2.1.dist-info}/WHEEL +0 -0
infrahub/lock.py
CHANGED
|
@@ -5,7 +5,7 @@ import time
|
|
|
5
5
|
import uuid
|
|
6
6
|
from asyncio import Lock as LocalLock
|
|
7
7
|
from asyncio import sleep
|
|
8
|
-
from typing import TYPE_CHECKING
|
|
8
|
+
from typing import TYPE_CHECKING
|
|
9
9
|
|
|
10
10
|
import redis.asyncio as redis
|
|
11
11
|
from prometheus_client import Histogram
|
|
@@ -47,7 +47,7 @@ GLOBAL_GRAPH_LOCK = "global.graph"
|
|
|
47
47
|
class InfrahubMultiLock:
|
|
48
48
|
"""Context manager to allow multiple locks to be reserved together"""
|
|
49
49
|
|
|
50
|
-
def __init__(self, lock_registry: InfrahubLockRegistry, locks:
|
|
50
|
+
def __init__(self, lock_registry: InfrahubLockRegistry, locks: list[str] | None = None) -> None:
|
|
51
51
|
self.registry = lock_registry
|
|
52
52
|
self.locks = locks or []
|
|
53
53
|
|
|
@@ -56,9 +56,9 @@ class InfrahubMultiLock:
|
|
|
56
56
|
|
|
57
57
|
async def __aexit__(
|
|
58
58
|
self,
|
|
59
|
-
exc_type:
|
|
60
|
-
exc_value:
|
|
61
|
-
traceback:
|
|
59
|
+
exc_type: type[BaseException] | None,
|
|
60
|
+
exc_value: BaseException | None,
|
|
61
|
+
traceback: TracebackType | None,
|
|
62
62
|
):
|
|
63
63
|
await self.release()
|
|
64
64
|
|
|
@@ -84,9 +84,9 @@ class NATSLock:
|
|
|
84
84
|
|
|
85
85
|
async def __aexit__(
|
|
86
86
|
self,
|
|
87
|
-
exc_type:
|
|
88
|
-
exc_value:
|
|
89
|
-
traceback:
|
|
87
|
+
exc_type: type[BaseException] | None,
|
|
88
|
+
exc_value: BaseException | None,
|
|
89
|
+
traceback: TracebackType | None,
|
|
90
90
|
):
|
|
91
91
|
await self.release()
|
|
92
92
|
|
|
@@ -117,18 +117,18 @@ class InfrahubLock:
|
|
|
117
117
|
def __init__(
|
|
118
118
|
self,
|
|
119
119
|
name: str,
|
|
120
|
-
connection:
|
|
121
|
-
local:
|
|
120
|
+
connection: redis.Redis | InfrahubServices | None = None,
|
|
121
|
+
local: bool | None = None,
|
|
122
122
|
in_multi: bool = False,
|
|
123
123
|
) -> None:
|
|
124
124
|
self.use_local: bool = local
|
|
125
125
|
self.local: LocalLock = None
|
|
126
126
|
self.remote: GlobalLock = None
|
|
127
127
|
self.name: str = name
|
|
128
|
-
self.connection:
|
|
128
|
+
self.connection: redis.Redis | None = connection
|
|
129
129
|
self.in_multi: bool = in_multi
|
|
130
130
|
self.lock_type: str = "multi" if self.in_multi else "individual"
|
|
131
|
-
self.acquire_time:
|
|
131
|
+
self.acquire_time: int | None = None
|
|
132
132
|
self.event = asyncio.Event()
|
|
133
133
|
|
|
134
134
|
if not self.connection or (self.use_local is None and name.startswith("local.")):
|
|
@@ -146,9 +146,9 @@ class InfrahubLock:
|
|
|
146
146
|
|
|
147
147
|
async def __aexit__(
|
|
148
148
|
self,
|
|
149
|
-
exc_type:
|
|
150
|
-
exc_value:
|
|
151
|
-
traceback:
|
|
149
|
+
exc_type: type[BaseException] | None,
|
|
150
|
+
exc_value: BaseException | None,
|
|
151
|
+
traceback: TracebackType | None,
|
|
152
152
|
):
|
|
153
153
|
await self.release()
|
|
154
154
|
|
|
@@ -179,7 +179,7 @@ class InfrahubLock:
|
|
|
179
179
|
|
|
180
180
|
class InfrahubLockRegistry:
|
|
181
181
|
def __init__(
|
|
182
|
-
self, token:
|
|
182
|
+
self, token: str | None = None, local_only: bool = False, service: InfrahubServices | None = None
|
|
183
183
|
) -> None:
|
|
184
184
|
if config.SETTINGS.cache.enable and not local_only:
|
|
185
185
|
if config.SETTINGS.cache.driver == config.CacheDriver.Redis:
|
|
@@ -201,7 +201,7 @@ class InfrahubLockRegistry:
|
|
|
201
201
|
self.locks: dict[str, InfrahubLock] = {}
|
|
202
202
|
|
|
203
203
|
@classmethod
|
|
204
|
-
def _generate_name(cls, name: str, namespace:
|
|
204
|
+
def _generate_name(cls, name: str, namespace: str | None = None, local: bool | None = None) -> str:
|
|
205
205
|
if namespace is None and local is None:
|
|
206
206
|
return name
|
|
207
207
|
|
|
@@ -221,7 +221,7 @@ class InfrahubLockRegistry:
|
|
|
221
221
|
self,
|
|
222
222
|
name: str,
|
|
223
223
|
namespace: str | None,
|
|
224
|
-
local:
|
|
224
|
+
local: bool | None = None,
|
|
225
225
|
) -> InfrahubLock | None:
|
|
226
226
|
lock_name = self._generate_name(name=name, namespace=namespace, local=local)
|
|
227
227
|
if lock_name not in self.locks:
|
|
@@ -229,7 +229,7 @@ class InfrahubLockRegistry:
|
|
|
229
229
|
return self.locks[lock_name]
|
|
230
230
|
|
|
231
231
|
def get(
|
|
232
|
-
self, name: str, namespace:
|
|
232
|
+
self, name: str, namespace: str | None = None, local: bool | None = None, in_multi: bool = False
|
|
233
233
|
) -> InfrahubLock:
|
|
234
234
|
lock_name = self._generate_name(name=name, namespace=namespace, local=local)
|
|
235
235
|
if lock_name not in self.locks:
|
|
@@ -252,7 +252,7 @@ class InfrahubLockRegistry:
|
|
|
252
252
|
return InfrahubMultiLock(lock_registry=self, locks=[LOCAL_SCHEMA_LOCK, GLOBAL_GRAPH_LOCK, GLOBAL_SCHEMA_LOCK])
|
|
253
253
|
|
|
254
254
|
|
|
255
|
-
def initialize_lock(local_only: bool = False, service:
|
|
255
|
+
def initialize_lock(local_only: bool = False, service: InfrahubServices | None = None) -> None:
|
|
256
256
|
global registry
|
|
257
257
|
registry = InfrahubLockRegistry(local_only=local_only, service=service)
|
|
258
258
|
|
infrahub/menu/constants.py
CHANGED
infrahub/menu/generator.py
CHANGED
|
@@ -3,11 +3,10 @@ from __future__ import annotations
|
|
|
3
3
|
from typing import TYPE_CHECKING
|
|
4
4
|
|
|
5
5
|
from infrahub.core import registry
|
|
6
|
-
from infrahub.core.constants import InfrahubKind
|
|
7
6
|
from infrahub.core.protocols import CoreMenuItem
|
|
8
7
|
from infrahub.log import get_logger
|
|
9
8
|
|
|
10
|
-
from .constants import FULL_DEFAULT_MENU
|
|
9
|
+
from .constants import FULL_DEFAULT_MENU
|
|
11
10
|
from .models import MenuDict, MenuItemDict
|
|
12
11
|
|
|
13
12
|
if TYPE_CHECKING:
|
|
@@ -71,7 +70,7 @@ async def generate_menu(db: InfrahubDatabase, branch: Branch, menu_items: list[C
|
|
|
71
70
|
menu_item = structure.find_item(name=parent_full_name)
|
|
72
71
|
if menu_item:
|
|
73
72
|
child_item = MenuItemDict.from_node(obj=item)
|
|
74
|
-
menu_item.children[child_item.identifier] = child_item
|
|
73
|
+
menu_item.children[str(child_item.identifier)] = child_item
|
|
75
74
|
else:
|
|
76
75
|
log.warning(
|
|
77
76
|
"new_menu_request: unable to find the parent menu item",
|
|
@@ -82,11 +81,6 @@ async def generate_menu(db: InfrahubDatabase, branch: Branch, menu_items: list[C
|
|
|
82
81
|
|
|
83
82
|
items_to_add = {schema.kind: False for schema in full_schema.values() if schema.include_in_menu is True}
|
|
84
83
|
|
|
85
|
-
# Object templates should go into a dedicated menu item
|
|
86
|
-
object_templates_menu = MenuItemDict(
|
|
87
|
-
identifier=InfrahubKind.OBJECTTEMPLATE, label=TEMPLATE_MENU.title(), icon="mdi:pencil-ruler", order_weight=20000
|
|
88
|
-
)
|
|
89
|
-
|
|
90
84
|
nbr_remaining_items_last_round = len(items_to_add.values())
|
|
91
85
|
nbr_remaining_items = len([value for value in items_to_add.values() if value is False])
|
|
92
86
|
while not all(items_to_add.values()):
|
|
@@ -96,23 +90,20 @@ async def generate_menu(db: InfrahubDatabase, branch: Branch, menu_items: list[C
|
|
|
96
90
|
|
|
97
91
|
schema = full_schema[item_name]
|
|
98
92
|
menu_item = MenuItemDict.from_schema(model=schema)
|
|
99
|
-
already_in_schema = bool(structure.find_item(name=menu_item.identifier))
|
|
93
|
+
already_in_schema = bool(structure.find_item(name=str(menu_item.identifier)))
|
|
100
94
|
if already_in_schema:
|
|
101
95
|
items_to_add[item_name] = True
|
|
102
96
|
continue
|
|
103
97
|
|
|
104
|
-
if schema.
|
|
105
|
-
object_templates_menu.children[menu_item.identifier] = menu_item
|
|
106
|
-
items_to_add[item_name] = True
|
|
107
|
-
elif not schema.menu_placement:
|
|
98
|
+
if not schema.menu_placement:
|
|
108
99
|
first_element = MenuItemDict.from_schema(model=schema)
|
|
109
|
-
first_element.
|
|
100
|
+
first_element.name = f"{first_element.name}Sub"
|
|
110
101
|
first_element.order_weight = 1
|
|
111
|
-
menu_item.children[first_element.identifier] = first_element
|
|
112
|
-
structure.data[menu_item.identifier] = menu_item
|
|
102
|
+
menu_item.children[str(first_element.identifier)] = first_element
|
|
103
|
+
structure.data[str(menu_item.identifier)] = menu_item
|
|
113
104
|
items_to_add[item_name] = True
|
|
114
105
|
elif menu_placement := structure.find_item(name=schema.menu_placement):
|
|
115
|
-
menu_placement.children[menu_item.identifier] = menu_item
|
|
106
|
+
menu_placement.children[str(menu_item.identifier)] = menu_item
|
|
116
107
|
items_to_add[item_name] = True
|
|
117
108
|
continue
|
|
118
109
|
|
|
@@ -121,9 +112,6 @@ async def generate_menu(db: InfrahubDatabase, branch: Branch, menu_items: list[C
|
|
|
121
112
|
break
|
|
122
113
|
nbr_remaining_items_last_round = nbr_remaining_items
|
|
123
114
|
|
|
124
|
-
if object_templates_menu.children:
|
|
125
|
-
structure.data[object_templates_menu.identifier] = object_templates_menu
|
|
126
|
-
|
|
127
115
|
# ----------------------------------------------------------------------------
|
|
128
116
|
# Assign the remaining items for which we couldn't find the menu_placement to the default menu
|
|
129
117
|
# ----------------------------------------------------------------------------
|
|
@@ -142,7 +130,7 @@ async def generate_menu(db: InfrahubDatabase, branch: Branch, menu_items: list[C
|
|
|
142
130
|
item=schema.kind,
|
|
143
131
|
menu_placement=schema.menu_placement,
|
|
144
132
|
)
|
|
145
|
-
default_menu.children[menu_item.identifier] = menu_item
|
|
133
|
+
default_menu.children[str(menu_item.identifier)] = menu_item
|
|
146
134
|
items_to_add[item_name] = True
|
|
147
135
|
|
|
148
136
|
return structure
|
infrahub/menu/menu.py
CHANGED
|
@@ -135,6 +135,16 @@ default_menu = [
|
|
|
135
135
|
section=MenuSection.INTERNAL,
|
|
136
136
|
order_weight=1500,
|
|
137
137
|
),
|
|
138
|
+
MenuItemDefinition(
|
|
139
|
+
namespace="Builtin",
|
|
140
|
+
name="Templates",
|
|
141
|
+
label="Templates",
|
|
142
|
+
kind=InfrahubKind.OBJECTTEMPLATE,
|
|
143
|
+
icon=_extract_node_icon(infrahub_schema.get(InfrahubKind.OBJECTTEMPLATE)),
|
|
144
|
+
protected=True,
|
|
145
|
+
section=MenuSection.INTERNAL,
|
|
146
|
+
order_weight=2000,
|
|
147
|
+
),
|
|
138
148
|
MenuItemDefinition(
|
|
139
149
|
namespace="Builtin",
|
|
140
150
|
name="ResourceManager",
|
|
@@ -143,7 +153,7 @@ default_menu = [
|
|
|
143
153
|
icon=_extract_node_icon(infrahub_schema.get(InfrahubKind.RESOURCEPOOL)),
|
|
144
154
|
protected=True,
|
|
145
155
|
section=MenuSection.INTERNAL,
|
|
146
|
-
order_weight=
|
|
156
|
+
order_weight=2500,
|
|
147
157
|
),
|
|
148
158
|
MenuItemDefinition(
|
|
149
159
|
namespace="Builtin",
|
|
@@ -153,7 +163,7 @@ default_menu = [
|
|
|
153
163
|
icon=_extract_node_icon(infrahub_schema.get(InfrahubKind.ARTIFACT)),
|
|
154
164
|
protected=True,
|
|
155
165
|
section=MenuSection.INTERNAL,
|
|
156
|
-
order_weight=
|
|
166
|
+
order_weight=3000,
|
|
157
167
|
),
|
|
158
168
|
MenuItemDefinition(
|
|
159
169
|
namespace="Builtin",
|
|
@@ -175,16 +185,6 @@ default_menu = [
|
|
|
175
185
|
section=MenuSection.INTERNAL,
|
|
176
186
|
order_weight=3500,
|
|
177
187
|
),
|
|
178
|
-
MenuItemDefinition(
|
|
179
|
-
namespace="Builtin",
|
|
180
|
-
name="ActivityLogs",
|
|
181
|
-
label="Activity Logs",
|
|
182
|
-
path="/activities",
|
|
183
|
-
icon="mdi:format-list-bulleted",
|
|
184
|
-
protected=True,
|
|
185
|
-
section=MenuSection.INTERNAL,
|
|
186
|
-
order_weight=4000,
|
|
187
|
-
),
|
|
188
188
|
],
|
|
189
189
|
),
|
|
190
190
|
MenuItemDefinition(
|
|
@@ -261,32 +261,11 @@ default_menu = [
|
|
|
261
261
|
namespace="Builtin",
|
|
262
262
|
name="Webhooks",
|
|
263
263
|
label="Webhooks",
|
|
264
|
-
icon=_extract_node_icon(infrahub_schema.get(InfrahubKind.
|
|
264
|
+
icon=_extract_node_icon(infrahub_schema.get(InfrahubKind.WEBHOOK)),
|
|
265
|
+
kind=InfrahubKind.WEBHOOK,
|
|
265
266
|
protected=True,
|
|
266
267
|
section=MenuSection.INTERNAL,
|
|
267
268
|
order_weight=3000,
|
|
268
|
-
children=[
|
|
269
|
-
MenuItemDefinition(
|
|
270
|
-
namespace="Builtin",
|
|
271
|
-
name="WebhookStandard",
|
|
272
|
-
label="Webhooks",
|
|
273
|
-
kind=InfrahubKind.STANDARDWEBHOOK,
|
|
274
|
-
icon=_extract_node_icon(infrahub_schema.get(InfrahubKind.STANDARDWEBHOOK)),
|
|
275
|
-
protected=True,
|
|
276
|
-
section=MenuSection.INTERNAL,
|
|
277
|
-
order_weight=1000,
|
|
278
|
-
),
|
|
279
|
-
MenuItemDefinition(
|
|
280
|
-
namespace="Builtin",
|
|
281
|
-
name="WebhookCustom",
|
|
282
|
-
label="Custom Webhooks",
|
|
283
|
-
kind=InfrahubKind.CUSTOMWEBHOOK,
|
|
284
|
-
icon=_extract_node_icon(infrahub_schema.get(InfrahubKind.CUSTOMWEBHOOK)),
|
|
285
|
-
protected=True,
|
|
286
|
-
section=MenuSection.INTERNAL,
|
|
287
|
-
order_weight=2000,
|
|
288
|
-
),
|
|
289
|
-
],
|
|
290
269
|
),
|
|
291
270
|
MenuItemDefinition(
|
|
292
271
|
namespace="Builtin",
|
|
@@ -331,9 +310,9 @@ default_menu = [
|
|
|
331
310
|
),
|
|
332
311
|
MenuItemDefinition(
|
|
333
312
|
namespace="Builtin",
|
|
334
|
-
name="
|
|
335
|
-
label="
|
|
336
|
-
path="/
|
|
313
|
+
name="Activities",
|
|
314
|
+
label="Activities",
|
|
315
|
+
path="/activities",
|
|
337
316
|
icon="mdi:timeline-text",
|
|
338
317
|
protected=True,
|
|
339
318
|
section=MenuSection.INTERNAL,
|
infrahub/menu/models.py
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from dataclasses import dataclass, field
|
|
4
|
-
from typing import TYPE_CHECKING
|
|
4
|
+
from typing import TYPE_CHECKING, Any
|
|
5
5
|
|
|
6
|
-
from pydantic import BaseModel, Field
|
|
6
|
+
from pydantic import BaseModel, Field, computed_field
|
|
7
7
|
from typing_extensions import Self
|
|
8
8
|
|
|
9
9
|
from infrahub.core.account import GlobalPermission
|
|
@@ -35,21 +35,26 @@ def _get_full_name_schema(node: MainSchemaTypes) -> str:
|
|
|
35
35
|
class MenuDict:
|
|
36
36
|
data: dict[str, MenuItemDict] = field(default_factory=dict)
|
|
37
37
|
|
|
38
|
+
def get_item_location(self, name: str) -> list[str]:
|
|
39
|
+
location, _ = self._find_child_item(name=name, children=self.data)
|
|
40
|
+
return location
|
|
41
|
+
|
|
38
42
|
def find_item(self, name: str) -> MenuItemDict | None:
|
|
39
|
-
|
|
43
|
+
_, item = self._find_child_item(name=name, children=self.data)
|
|
44
|
+
return item
|
|
40
45
|
|
|
41
46
|
@classmethod
|
|
42
|
-
def _find_child_item(cls, name: str, children: dict[str, MenuItemDict]) -> MenuItemDict | None:
|
|
47
|
+
def _find_child_item(cls, name: str, children: dict[str, MenuItemDict]) -> tuple[list[str], MenuItemDict | None]:
|
|
43
48
|
if name in children.keys():
|
|
44
|
-
return children[name]
|
|
49
|
+
return [], children[name]
|
|
45
50
|
|
|
46
51
|
for child in children.values():
|
|
47
52
|
if not child.children:
|
|
48
53
|
continue
|
|
49
|
-
found = cls._find_child_item(name=name, children=child.children)
|
|
54
|
+
position, found = cls._find_child_item(name=name, children=child.children)
|
|
50
55
|
if found:
|
|
51
|
-
return found
|
|
52
|
-
return None
|
|
56
|
+
return [str(child.identifier)] + position, found
|
|
57
|
+
return [], None
|
|
53
58
|
|
|
54
59
|
def to_rest(self) -> Menu:
|
|
55
60
|
data: dict[str, list[MenuItemList]] = {}
|
|
@@ -62,10 +67,15 @@ class MenuDict:
|
|
|
62
67
|
|
|
63
68
|
return Menu(sections=data)
|
|
64
69
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
70
|
+
@classmethod
|
|
71
|
+
def from_definition_list(cls, definitions: list[MenuItemDefinition]) -> Self:
|
|
72
|
+
menu = cls()
|
|
73
|
+
for definition in definitions:
|
|
74
|
+
menu.data[definition.full_name] = MenuItemDict.from_definition(definition=definition)
|
|
75
|
+
return menu
|
|
76
|
+
|
|
77
|
+
def get_all_identifiers(self) -> set[str]:
|
|
78
|
+
return {identifier for item in self.data.values() for identifier in item.get_all_identifiers()}
|
|
69
79
|
|
|
70
80
|
|
|
71
81
|
@dataclass
|
|
@@ -74,7 +84,11 @@ class Menu:
|
|
|
74
84
|
|
|
75
85
|
|
|
76
86
|
class MenuItem(BaseModel):
|
|
77
|
-
|
|
87
|
+
id: str | None = None
|
|
88
|
+
namespace: str = Field(..., description="Namespace of the menu item")
|
|
89
|
+
name: str = Field(..., description="Name of the menu item")
|
|
90
|
+
description: str = Field(default="", description="Description of the menu item")
|
|
91
|
+
protected: bool = Field(default=False, description="Whether the menu item is protected")
|
|
78
92
|
label: str = Field(..., description="Title of the menu item")
|
|
79
93
|
path: str = Field(default="", description="URL endpoint if applicable")
|
|
80
94
|
icon: str = Field(default="", description="The icon to show for the current view")
|
|
@@ -83,10 +97,27 @@ class MenuItem(BaseModel):
|
|
|
83
97
|
section: MenuSection = MenuSection.OBJECT
|
|
84
98
|
permissions: list[str] = Field(default_factory=list)
|
|
85
99
|
|
|
100
|
+
@computed_field
|
|
101
|
+
def identifier(self) -> str:
|
|
102
|
+
return f"{self.namespace}{self.name}"
|
|
103
|
+
|
|
104
|
+
def get_path(self) -> str | None:
|
|
105
|
+
if self.path:
|
|
106
|
+
return self.path
|
|
107
|
+
|
|
108
|
+
if self.kind:
|
|
109
|
+
return f"/objects/{self.kind}"
|
|
110
|
+
|
|
111
|
+
return None
|
|
112
|
+
|
|
86
113
|
@classmethod
|
|
87
114
|
def from_node(cls, obj: CoreMenuItem) -> Self:
|
|
88
115
|
return cls(
|
|
89
|
-
|
|
116
|
+
id=obj.get_id(),
|
|
117
|
+
name=obj.name.value,
|
|
118
|
+
namespace=obj.namespace.value,
|
|
119
|
+
protected=obj.protected.value,
|
|
120
|
+
description=obj.description.value or "",
|
|
90
121
|
label=obj.label.value or "",
|
|
91
122
|
icon=obj.icon.value or "",
|
|
92
123
|
order_weight=obj.order_weight.value,
|
|
@@ -96,10 +127,30 @@ class MenuItem(BaseModel):
|
|
|
96
127
|
permissions=obj.required_permissions.value or [],
|
|
97
128
|
)
|
|
98
129
|
|
|
130
|
+
async def to_node(self, db: InfrahubDatabase, parent: CoreMenuItem | None = None) -> CoreMenuItem:
|
|
131
|
+
obj = await Node.init(db=db, schema=CoreMenuItem)
|
|
132
|
+
await obj.new(
|
|
133
|
+
db=db,
|
|
134
|
+
namespace=self.namespace,
|
|
135
|
+
name=self.name,
|
|
136
|
+
label=self.label,
|
|
137
|
+
kind=self.kind,
|
|
138
|
+
path=self.get_path(),
|
|
139
|
+
description=self.description or None,
|
|
140
|
+
icon=self.icon or None,
|
|
141
|
+
protected=self.protected,
|
|
142
|
+
section=self.section.value,
|
|
143
|
+
order_weight=self.order_weight,
|
|
144
|
+
parent=parent.id if parent else None,
|
|
145
|
+
required_permissions=self.permissions,
|
|
146
|
+
)
|
|
147
|
+
return obj
|
|
148
|
+
|
|
99
149
|
@classmethod
|
|
100
150
|
def from_schema(cls, model: NodeSchema | GenericSchema | ProfileSchema | TemplateSchema) -> Self:
|
|
101
151
|
return cls(
|
|
102
|
-
|
|
152
|
+
name=model.name,
|
|
153
|
+
namespace=model.namespace,
|
|
103
154
|
label=model.label or model.kind,
|
|
104
155
|
path=f"/objects/{model.kind}",
|
|
105
156
|
icon=model.icon or "",
|
|
@@ -111,8 +162,14 @@ class MenuItemDict(MenuItem):
|
|
|
111
162
|
hidden: bool = False
|
|
112
163
|
children: dict[str, MenuItemDict] = Field(default_factory=dict, description="Child objects")
|
|
113
164
|
|
|
165
|
+
def get_all_identifiers(self) -> set[str]:
|
|
166
|
+
identifiers: set[str] = {str(self.identifier)}
|
|
167
|
+
for child in self.children.values():
|
|
168
|
+
identifiers.update(child.get_all_identifiers())
|
|
169
|
+
return identifiers
|
|
170
|
+
|
|
114
171
|
def to_list(self) -> MenuItemList:
|
|
115
|
-
data = self.model_dump(exclude={"children"})
|
|
172
|
+
data = self.model_dump(exclude={"children", "id"})
|
|
116
173
|
unsorted_children = [child.to_list() for child in self.children.values() if child.hidden is False]
|
|
117
174
|
data["children"] = sorted(unsorted_children, key=lambda d: d.order_weight)
|
|
118
175
|
return MenuItemList(**data)
|
|
@@ -125,6 +182,35 @@ class MenuItemDict(MenuItem):
|
|
|
125
182
|
permissions.append(GlobalPermission.from_string(input=permission))
|
|
126
183
|
return permissions
|
|
127
184
|
|
|
185
|
+
def diff_attributes(self, other: Self) -> dict[str, Any]:
|
|
186
|
+
other_attributes = other.model_dump(exclude={"children"})
|
|
187
|
+
self_attributes = self.model_dump(exclude={"children"})
|
|
188
|
+
return {
|
|
189
|
+
key: value
|
|
190
|
+
for key, value in other_attributes.items()
|
|
191
|
+
if value != self_attributes[key] and key not in ["id", "children"]
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
@classmethod
|
|
195
|
+
def from_definition(cls, definition: MenuItemDefinition) -> Self:
|
|
196
|
+
menu_item = cls(
|
|
197
|
+
name=definition.name,
|
|
198
|
+
namespace=definition.namespace,
|
|
199
|
+
label=definition.label,
|
|
200
|
+
path=definition.get_path() or "",
|
|
201
|
+
icon=definition.icon,
|
|
202
|
+
kind=definition.kind,
|
|
203
|
+
protected=definition.protected,
|
|
204
|
+
section=definition.section,
|
|
205
|
+
permissions=definition.permissions,
|
|
206
|
+
order_weight=definition.order_weight,
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
for child in definition.children:
|
|
210
|
+
menu_item.children[child.full_name] = MenuItemDict.from_definition(definition=child)
|
|
211
|
+
|
|
212
|
+
return menu_item
|
|
213
|
+
|
|
128
214
|
|
|
129
215
|
class MenuItemList(MenuItem):
|
|
130
216
|
children: list[MenuItemList] = Field(default_factory=list, description="Child objects")
|
|
@@ -162,6 +248,21 @@ class MenuItemDefinition(BaseModel):
|
|
|
162
248
|
)
|
|
163
249
|
return obj
|
|
164
250
|
|
|
251
|
+
@classmethod
|
|
252
|
+
async def from_node(cls, node: CoreMenuItem) -> Self:
|
|
253
|
+
return cls(
|
|
254
|
+
namespace=node.namespace.value,
|
|
255
|
+
name=node.name.value,
|
|
256
|
+
label=node.label.value or "",
|
|
257
|
+
description=node.description.value or "",
|
|
258
|
+
icon=node.icon.value or "",
|
|
259
|
+
protected=node.protected.value,
|
|
260
|
+
path=node.path.value or "",
|
|
261
|
+
kind=node.kind.value or "",
|
|
262
|
+
section=node.section.value,
|
|
263
|
+
order_weight=node.order_weight.value,
|
|
264
|
+
)
|
|
265
|
+
|
|
165
266
|
def get_path(self) -> str | None:
|
|
166
267
|
if self.path:
|
|
167
268
|
return self.path
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
from infrahub.core.manager import NodeManager
|
|
2
|
+
from infrahub.core.protocols import CoreMenuItem
|
|
3
|
+
from infrahub.database import InfrahubDatabase
|
|
4
|
+
|
|
5
|
+
from .models import MenuDict, MenuItemDefinition, MenuItemDict
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class MenuRepository:
|
|
9
|
+
def __init__(self, db: InfrahubDatabase):
|
|
10
|
+
self.db = db
|
|
11
|
+
|
|
12
|
+
async def get_menu(self, nodes: dict[str, CoreMenuItem] | None = None) -> MenuDict:
|
|
13
|
+
menu_nodes = nodes or await self.get_menu_db()
|
|
14
|
+
return await self._convert_menu_from_db(nodes=menu_nodes)
|
|
15
|
+
|
|
16
|
+
async def _convert_menu_from_db(self, nodes: dict[str, CoreMenuItem]) -> MenuDict:
|
|
17
|
+
menu = MenuDict()
|
|
18
|
+
menu_by_ids = {menu_node.get_id(): MenuItemDict.from_node(menu_node) for menu_node in nodes.values()}
|
|
19
|
+
|
|
20
|
+
async def add_children(menu_item: MenuItemDict, menu_node: CoreMenuItem) -> MenuItemDict:
|
|
21
|
+
children = await menu_node.children.get_peers(db=self.db, peer_type=CoreMenuItem)
|
|
22
|
+
for child_id, child_node in children.items():
|
|
23
|
+
child_menu_item = menu_by_ids[child_id]
|
|
24
|
+
child = await add_children(child_menu_item, child_node)
|
|
25
|
+
menu_item.children[str(child.identifier)] = child
|
|
26
|
+
return menu_item
|
|
27
|
+
|
|
28
|
+
for menu_node in nodes.values():
|
|
29
|
+
menu_item = menu_by_ids[menu_node.get_id()]
|
|
30
|
+
parent = await menu_node.parent.get_peer(db=self.db, peer_type=CoreMenuItem)
|
|
31
|
+
if parent:
|
|
32
|
+
continue
|
|
33
|
+
|
|
34
|
+
children = await menu_node.children.get_peers(db=self.db, peer_type=CoreMenuItem)
|
|
35
|
+
for child_id, child_node in children.items():
|
|
36
|
+
child_menu_item = menu_by_ids[child_id]
|
|
37
|
+
child = await add_children(child_menu_item, child_node)
|
|
38
|
+
menu_item.children[str(child.identifier)] = child
|
|
39
|
+
|
|
40
|
+
menu.data[str(menu_item.identifier)] = menu_item
|
|
41
|
+
|
|
42
|
+
return menu
|
|
43
|
+
|
|
44
|
+
async def get_menu_db(self) -> dict[str, CoreMenuItem]:
|
|
45
|
+
menu_nodes = await NodeManager.query(
|
|
46
|
+
schema=CoreMenuItem,
|
|
47
|
+
filters={"namespace__value": "Builtin"},
|
|
48
|
+
prefetch_relationships=True,
|
|
49
|
+
db=self.db,
|
|
50
|
+
)
|
|
51
|
+
return {node.get_id(): node for node in menu_nodes}
|
|
52
|
+
|
|
53
|
+
async def create_menu(self, menu: list[MenuItemDefinition]) -> None:
|
|
54
|
+
for item in menu:
|
|
55
|
+
obj = await item.to_node(db=self.db)
|
|
56
|
+
await obj.save(db=self.db)
|
|
57
|
+
if item.children:
|
|
58
|
+
await self.create_menu_children(parent=obj, children=item.children)
|
|
59
|
+
|
|
60
|
+
async def create_menu_children(self, parent: CoreMenuItem, children: list[MenuItemDefinition]) -> None:
|
|
61
|
+
for child in children:
|
|
62
|
+
obj = await child.to_node(db=self.db, parent=parent)
|
|
63
|
+
await obj.save(db=self.db)
|
|
64
|
+
if child.children:
|
|
65
|
+
await self.create_menu_children(parent=obj, children=child.children)
|
|
66
|
+
|
|
67
|
+
async def update_menu(
|
|
68
|
+
self, existing_menu: MenuDict, new_menu: MenuDict, menu_nodes: dict[str, CoreMenuItem]
|
|
69
|
+
) -> None:
|
|
70
|
+
async def process_menu_item(menu_item: MenuItemDict, parent: CoreMenuItem | None) -> None:
|
|
71
|
+
existing_item = existing_menu.find_item(name=str(menu_item.identifier))
|
|
72
|
+
if existing_item and existing_item.id:
|
|
73
|
+
node = menu_nodes[existing_item.id]
|
|
74
|
+
await self.update_menu_item(
|
|
75
|
+
node=node, existing_menu_item=existing_item, new_menu_item=menu_item, parent=parent
|
|
76
|
+
)
|
|
77
|
+
else:
|
|
78
|
+
node = await self.create_menu_item(new_menu_item=menu_item, parent=parent)
|
|
79
|
+
|
|
80
|
+
for child_item in menu_item.children.values():
|
|
81
|
+
await process_menu_item(menu_item=child_item, parent=node)
|
|
82
|
+
|
|
83
|
+
for top_level_item in new_menu.data.values():
|
|
84
|
+
await process_menu_item(menu_item=top_level_item, parent=None)
|
|
85
|
+
|
|
86
|
+
# Delete items that are not in the new menu
|
|
87
|
+
menu_to_delete = existing_menu.get_all_identifiers() - new_menu.get_all_identifiers()
|
|
88
|
+
for item_to_delete in menu_to_delete:
|
|
89
|
+
existing_item = existing_menu.find_item(name=item_to_delete)
|
|
90
|
+
if existing_item and existing_item.id:
|
|
91
|
+
node = menu_nodes[existing_item.id]
|
|
92
|
+
await node.delete(db=self.db)
|
|
93
|
+
|
|
94
|
+
async def update_menu_item(
|
|
95
|
+
self,
|
|
96
|
+
node: CoreMenuItem,
|
|
97
|
+
existing_menu_item: MenuItemDict,
|
|
98
|
+
new_menu_item: MenuItemDict,
|
|
99
|
+
parent: CoreMenuItem | None,
|
|
100
|
+
) -> None:
|
|
101
|
+
attrs_to_update = existing_menu_item.diff_attributes(new_menu_item)
|
|
102
|
+
for attr_name, value in attrs_to_update.items():
|
|
103
|
+
attr = getattr(node, attr_name)
|
|
104
|
+
attr.value = value
|
|
105
|
+
await node.parent.update(data=parent, db=self.db) # type: ignore[arg-type]
|
|
106
|
+
await node.save(db=self.db)
|
|
107
|
+
|
|
108
|
+
async def create_menu_item(self, new_menu_item: MenuItemDict, parent: CoreMenuItem | None) -> CoreMenuItem:
|
|
109
|
+
obj = await new_menu_item.to_node(db=self.db, parent=parent)
|
|
110
|
+
await obj.save(db=self.db)
|
|
111
|
+
return obj
|
infrahub/menu/utils.py
CHANGED
|
@@ -1,12 +1,9 @@
|
|
|
1
|
-
from infrahub.core.protocols import CoreMenuItem
|
|
2
1
|
from infrahub.database import InfrahubDatabase
|
|
3
2
|
|
|
4
|
-
from .
|
|
3
|
+
from .menu import default_menu
|
|
4
|
+
from .repository import MenuRepository
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
async def
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
await obj.save(db=db)
|
|
11
|
-
if child.children:
|
|
12
|
-
await create_menu_children(db=db, parent=obj, children=child.children)
|
|
7
|
+
async def create_default_menu(db: InfrahubDatabase) -> None:
|
|
8
|
+
repository = MenuRepository(db=db)
|
|
9
|
+
await repository.create_menu(menu=default_menu)
|