infrahub-server 1.1.6__py3-none-any.whl → 1.2.0rc0__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 +16 -4
- infrahub/api/dependencies.py +8 -0
- infrahub/api/oauth2.py +0 -1
- infrahub/api/oidc.py +0 -1
- infrahub/api/query.py +18 -7
- infrahub/api/schema.py +32 -6
- infrahub/api/transformation.py +12 -5
- infrahub/{message_bus/messages/check_artifact_create.py → artifacts/models.py} +2 -4
- infrahub/{message_bus/operations/check/artifact.py → artifacts/tasks.py} +26 -25
- infrahub/cli/__init__.py +0 -2
- infrahub/cli/db.py +6 -7
- infrahub/cli/events.py +8 -3
- infrahub/cli/git_agent.py +9 -7
- infrahub/cli/tasks.py +4 -6
- infrahub/computed_attribute/tasks.py +63 -17
- infrahub/computed_attribute/triggers.py +90 -0
- infrahub/config.py +1 -1
- infrahub/context.py +39 -0
- infrahub/core/account.py +5 -8
- infrahub/core/attribute.py +53 -21
- infrahub/core/branch/models.py +4 -4
- infrahub/core/branch/tasks.py +89 -130
- infrahub/core/changelog/__init__.py +0 -0
- infrahub/core/changelog/diff.py +232 -0
- infrahub/core/changelog/models.py +488 -0
- infrahub/core/constants/__init__.py +19 -2
- infrahub/core/constants/infrahubkind.py +1 -0
- infrahub/core/diff/combiner.py +12 -8
- infrahub/core/diff/coordinator.py +49 -70
- infrahub/core/diff/data_check_synchronizer.py +86 -7
- infrahub/core/diff/enricher/aggregated.py +3 -3
- infrahub/core/diff/enricher/cardinality_one.py +2 -7
- infrahub/core/diff/enricher/hierarchy.py +5 -3
- infrahub/core/diff/enricher/labels.py +14 -4
- infrahub/core/diff/enricher/path_identifier.py +3 -9
- infrahub/core/diff/enricher/summary_counts.py +3 -1
- infrahub/core/diff/merger/merger.py +8 -4
- infrahub/core/diff/model/path.py +47 -29
- infrahub/core/diff/query/all_conflicts.py +6 -3
- infrahub/core/diff/query/artifact.py +1 -1
- infrahub/core/diff/query/delete_query.py +1 -1
- infrahub/core/diff/query/diff_get.py +3 -2
- infrahub/core/diff/query/diff_summary.py +1 -1
- infrahub/core/diff/query/field_specifiers.py +3 -1
- infrahub/core/diff/query/field_summary.py +3 -2
- infrahub/core/diff/query/filters.py +12 -1
- infrahub/core/diff/query/get_conflict_query.py +1 -1
- infrahub/core/diff/query/has_conflicts_query.py +6 -3
- infrahub/core/diff/query/merge.py +3 -3
- infrahub/core/diff/query/{drop_tracking_id.py → merge_tracking_id.py} +4 -4
- infrahub/core/diff/query/roots_metadata.py +9 -2
- infrahub/core/diff/query/save.py +151 -66
- infrahub/core/diff/query/summary_counts_enricher.py +220 -0
- infrahub/core/diff/query/time_range_query.py +3 -2
- infrahub/core/diff/query/update_conflict_query.py +1 -1
- infrahub/core/diff/query_parser.py +49 -24
- infrahub/core/diff/repository/deserializer.py +24 -25
- infrahub/core/diff/repository/repository.py +76 -20
- infrahub/core/diff/tasks.py +9 -8
- infrahub/core/enums.py +1 -1
- infrahub/core/integrity/object_conflict/conflict_recorder.py +1 -1
- infrahub/core/ipam/reconciler.py +1 -1
- infrahub/core/ipam/tasks.py +2 -3
- infrahub/core/manager.py +18 -13
- infrahub/core/merge.py +5 -2
- infrahub/core/migrations/graph/m001_add_version_to_graph.py +1 -1
- infrahub/core/migrations/graph/m002_attribute_is_default.py +2 -2
- infrahub/core/migrations/graph/m003_relationship_parent_optional.py +2 -2
- infrahub/core/migrations/graph/m004_add_attr_documentation.py +1 -1
- infrahub/core/migrations/graph/m005_add_rel_read_only.py +1 -1
- infrahub/core/migrations/graph/m006_add_rel_on_delete.py +1 -1
- infrahub/core/migrations/graph/m007_add_rel_allow_override.py +1 -1
- infrahub/core/migrations/graph/m008_add_human_friendly_id.py +1 -1
- infrahub/core/migrations/graph/m009_add_generate_profile_attr.py +1 -1
- infrahub/core/migrations/graph/m010_add_generate_profile_attr_generic.py +1 -1
- infrahub/core/migrations/graph/m011_remove_profile_relationship_schema.py +2 -2
- infrahub/core/migrations/graph/m012_convert_account_generic.py +12 -23
- infrahub/core/migrations/graph/m013_convert_git_password_credential.py +7 -11
- infrahub/core/migrations/graph/m014_remove_index_attr_value.py +2 -2
- 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/m017_add_core_profile.py +1 -1
- infrahub/core/migrations/graph/m018_uniqueness_nulls.py +2 -2
- infrahub/core/migrations/query/attribute_add.py +1 -1
- infrahub/core/migrations/query/attribute_rename.py +1 -1
- infrahub/core/migrations/query/delete_element_in_schema.py +1 -1
- infrahub/core/migrations/query/node_duplicate.py +1 -1
- infrahub/core/migrations/query/relationship_duplicate.py +1 -1
- infrahub/core/migrations/query/schema_attribute_update.py +1 -1
- infrahub/core/migrations/schema/node_attribute_remove.py +1 -1
- infrahub/core/migrations/schema/node_remove.py +1 -1
- infrahub/core/migrations/schema/tasks.py +5 -5
- infrahub/core/migrations/shared.py +4 -4
- infrahub/core/models.py +7 -8
- infrahub/core/node/__init__.py +161 -40
- infrahub/core/node/base.py +1 -1
- infrahub/core/node/constraints/grouped_uniqueness.py +9 -2
- infrahub/core/node/delete_validator.py +4 -4
- infrahub/core/node/ipam.py +13 -8
- infrahub/core/node/permissions.py +4 -0
- infrahub/core/node/resource_manager/ip_prefix_pool.py +8 -5
- infrahub/core/node/standard.py +3 -5
- infrahub/core/property.py +1 -1
- infrahub/core/protocols.py +4 -0
- infrahub/core/protocols_base.py +4 -2
- infrahub/core/query/__init__.py +2 -5
- infrahub/core/query/attribute.py +9 -9
- infrahub/core/query/branch.py +5 -5
- infrahub/core/query/delete.py +1 -1
- infrahub/core/query/diff.py +45 -7
- infrahub/core/query/ipam.py +4 -4
- infrahub/core/query/node.py +19 -14
- infrahub/core/query/relationship.py +10 -11
- infrahub/core/query/resource_manager.py +13 -11
- infrahub/core/query/standard_node.py +6 -6
- infrahub/core/query/task.py +3 -3
- infrahub/core/query/task_log.py +1 -1
- infrahub/core/query/utils.py +5 -5
- infrahub/core/registry.py +0 -2
- infrahub/core/relationship/constraints/count.py +1 -1
- infrahub/core/relationship/constraints/peer_kind.py +1 -1
- infrahub/core/relationship/model.py +66 -26
- infrahub/core/schema/__init__.py +6 -4
- infrahub/core/schema/basenode_schema.py +1 -3
- infrahub/core/schema/definitions/core.py +14 -2
- infrahub/core/schema/definitions/internal.py +16 -0
- infrahub/core/schema/generated/genericnode_schema.py +5 -0
- infrahub/core/schema/generated/node_schema.py +5 -0
- infrahub/core/schema/generic_schema.py +5 -1
- infrahub/core/schema/manager.py +45 -42
- infrahub/core/schema/node_schema.py +4 -0
- infrahub/core/schema/profile_schema.py +4 -0
- infrahub/core/schema/relationship_schema.py +2 -2
- infrahub/core/schema/schema_branch.py +248 -14
- infrahub/core/schema/template_schema.py +36 -0
- infrahub/core/task/user_task.py +7 -5
- infrahub/core/timestamp.py +1 -1
- infrahub/core/utils.py +3 -2
- infrahub/core/validators/attribute/choices.py +1 -1
- infrahub/core/validators/attribute/enum.py +1 -1
- infrahub/core/validators/attribute/kind.py +1 -1
- infrahub/core/validators/attribute/length.py +1 -1
- infrahub/core/validators/attribute/optional.py +1 -1
- infrahub/core/validators/attribute/regex.py +1 -1
- infrahub/core/validators/attribute/unique.py +1 -1
- infrahub/core/validators/checks_runner.py +37 -0
- infrahub/core/validators/node/generate_profile.py +1 -1
- infrahub/core/validators/node/hierarchy.py +1 -1
- infrahub/core/validators/query.py +1 -1
- infrahub/core/validators/relationship/count.py +1 -1
- infrahub/core/validators/relationship/optional.py +1 -1
- infrahub/core/validators/relationship/peer.py +1 -1
- infrahub/core/validators/tasks.py +8 -6
- infrahub/core/validators/uniqueness/query.py +20 -17
- infrahub/database/__init__.py +15 -2
- infrahub/database/memgraph.py +1 -1
- infrahub/dependencies/builder/constraint/grouped/node_runner.py +0 -2
- infrahub/dependencies/builder/diff/combiner.py +1 -1
- infrahub/dependencies/builder/diff/conflicts_enricher.py +1 -1
- infrahub/dependencies/builder/diff/coordinator.py +0 -2
- infrahub/dependencies/builder/diff/deserializer.py +1 -1
- infrahub/dependencies/builder/diff/enricher/summary_counts.py +1 -1
- infrahub/events/branch_action.py +47 -21
- infrahub/events/group_action.py +73 -0
- infrahub/events/models.py +159 -51
- infrahub/events/node_action.py +74 -8
- infrahub/events/repository_action.py +8 -8
- infrahub/events/schema_action.py +21 -8
- infrahub/generators/tasks.py +12 -13
- infrahub/git/base.py +3 -5
- infrahub/git/constants.py +0 -1
- infrahub/git/integrator.py +36 -35
- infrahub/git/repository.py +7 -8
- infrahub/git/tasks.py +43 -107
- infrahub/git_credential/helper.py +2 -3
- infrahub/graphql/analyzer.py +572 -11
- infrahub/graphql/app.py +34 -26
- infrahub/graphql/auth/query_permission_checker/anonymous_checker.py +5 -5
- infrahub/graphql/auth/query_permission_checker/default_branch_checker.py +4 -4
- infrahub/graphql/auth/query_permission_checker/merge_operation_checker.py +4 -4
- infrahub/graphql/auth/query_permission_checker/object_permission_checker.py +28 -35
- infrahub/graphql/auth/query_permission_checker/super_admin_checker.py +5 -5
- infrahub/graphql/enums.py +1 -1
- infrahub/graphql/initialization.py +5 -1
- infrahub/graphql/loaders/node.py +2 -2
- infrahub/graphql/manager.py +59 -54
- infrahub/graphql/mutations/account.py +20 -13
- infrahub/graphql/mutations/artifact_definition.py +16 -12
- infrahub/graphql/mutations/branch.py +61 -40
- infrahub/graphql/mutations/computed_attribute.py +19 -13
- infrahub/graphql/mutations/diff.py +37 -9
- infrahub/graphql/mutations/diff_conflict.py +9 -8
- infrahub/graphql/mutations/graphql_query.py +19 -11
- infrahub/graphql/mutations/ipam.py +21 -19
- infrahub/graphql/mutations/main.py +197 -44
- infrahub/graphql/mutations/menu.py +8 -8
- infrahub/graphql/mutations/proposed_change.py +36 -28
- infrahub/graphql/mutations/relationship.py +302 -105
- infrahub/graphql/mutations/repository.py +41 -35
- infrahub/graphql/mutations/resource_manager.py +26 -26
- infrahub/graphql/mutations/schema.py +51 -33
- infrahub/graphql/mutations/tasks.py +16 -10
- infrahub/graphql/parser.py +1 -1
- infrahub/graphql/permissions.py +6 -4
- infrahub/graphql/queries/account.py +22 -18
- infrahub/graphql/queries/branch.py +6 -4
- infrahub/graphql/queries/diff/tree.py +48 -42
- infrahub/graphql/queries/event.py +112 -0
- infrahub/graphql/queries/internal.py +3 -3
- infrahub/graphql/queries/ipam.py +23 -18
- infrahub/graphql/queries/relationship.py +11 -10
- infrahub/graphql/queries/resource_manager.py +43 -27
- infrahub/graphql/queries/search.py +9 -8
- infrahub/graphql/queries/status.py +12 -9
- infrahub/graphql/queries/task.py +11 -9
- infrahub/graphql/resolvers/resolver.py +69 -43
- infrahub/graphql/resolvers/single_relationship.py +16 -10
- infrahub/graphql/schema.py +2 -0
- infrahub/graphql/subscription/__init__.py +1 -1
- infrahub/graphql/subscription/events.py +1 -1
- infrahub/graphql/subscription/graphql_query.py +8 -8
- infrahub/graphql/types/branch.py +2 -2
- infrahub/graphql/types/common.py +6 -1
- infrahub/graphql/types/enums.py +2 -0
- infrahub/graphql/types/event.py +100 -0
- infrahub/graphql/types/interface.py +2 -2
- infrahub/graphql/types/node.py +3 -3
- infrahub/graphql/types/permission.py +2 -2
- infrahub/graphql/types/relationship.py +3 -3
- infrahub/graphql/types/standard_node.py +9 -11
- infrahub/graphql/utils.py +28 -182
- infrahub/groups/tasks.py +2 -3
- infrahub/lock.py +1 -1
- infrahub/menu/constants.py +1 -0
- infrahub/menu/generator.py +14 -3
- infrahub/menu/menu.py +116 -127
- infrahub/menu/models.py +4 -4
- infrahub/message_bus/messages/__init__.py +0 -4
- infrahub/message_bus/messages/event_branch_merge.py +3 -0
- infrahub/message_bus/messages/request_proposedchange_pipeline.py +2 -0
- infrahub/message_bus/operations/__init__.py +3 -5
- infrahub/message_bus/operations/check/__init__.py +2 -2
- infrahub/message_bus/operations/check/generator.py +1 -3
- infrahub/message_bus/operations/check/repository.py +1 -1
- infrahub/message_bus/operations/event/branch.py +7 -3
- infrahub/message_bus/operations/event/schema.py +1 -1
- infrahub/message_bus/operations/finalize/validator.py +1 -1
- infrahub/message_bus/operations/git/file.py +2 -2
- infrahub/message_bus/operations/git/repository.py +1 -1
- infrahub/message_bus/operations/requests/__init__.py +0 -2
- infrahub/message_bus/operations/requests/generator_definition.py +1 -1
- infrahub/message_bus/operations/requests/proposed_change.py +26 -11
- infrahub/message_bus/operations/requests/repository.py +2 -2
- infrahub/message_bus/operations/send/echo.py +1 -1
- infrahub/message_bus/types.py +1 -1
- infrahub/permissions/__init__.py +2 -1
- infrahub/permissions/types.py +26 -0
- infrahub/pools/prefix.py +29 -165
- infrahub/prefect_server/__init__.py +0 -0
- infrahub/prefect_server/app.py +18 -0
- infrahub/prefect_server/database.py +20 -0
- infrahub/prefect_server/events.py +28 -0
- infrahub/prefect_server/models.py +46 -0
- infrahub/proposed_change/models.py +15 -1
- infrahub/proposed_change/tasks.py +173 -35
- infrahub/pytest_plugin.py +4 -4
- infrahub/server.py +12 -11
- infrahub/services/__init__.py +147 -62
- infrahub/services/adapters/cache/__init__.py +7 -5
- infrahub/services/adapters/cache/nats.py +40 -22
- infrahub/services/adapters/cache/redis.py +0 -4
- infrahub/services/adapters/event/__init__.py +10 -18
- infrahub/services/adapters/http/__init__.py +0 -5
- infrahub/services/adapters/http/httpx.py +22 -15
- infrahub/services/adapters/message_bus/__init__.py +23 -6
- infrahub/services/adapters/message_bus/local.py +8 -6
- infrahub/services/adapters/message_bus/nats.py +12 -6
- infrahub/services/adapters/message_bus/rabbitmq.py +22 -9
- infrahub/services/adapters/workflow/__init__.py +11 -8
- infrahub/services/adapters/workflow/local.py +28 -7
- infrahub/services/adapters/workflow/worker.py +23 -7
- infrahub/services/component.py +38 -35
- infrahub/services/scheduler.py +32 -29
- infrahub/storage.py +2 -4
- infrahub/task_manager/constants.py +1 -1
- infrahub/task_manager/event.py +182 -0
- infrahub/task_manager/models.py +125 -1
- infrahub/task_manager/task.py +1 -1
- infrahub/tasks/artifact.py +14 -16
- infrahub/tasks/registry.py +1 -1
- infrahub/tasks/telemetry.py +13 -14
- infrahub/transformations/tasks.py +3 -5
- infrahub/trigger/__init__.py +0 -0
- infrahub/trigger/catalogue.py +15 -0
- infrahub/trigger/constants.py +9 -0
- infrahub/trigger/models.py +69 -0
- infrahub/trigger/tasks.py +85 -0
- infrahub/types.py +1 -1
- infrahub/utils.py +1 -1
- infrahub/webhook/constants.py +0 -2
- infrahub/webhook/models.py +8 -2
- infrahub/webhook/tasks.py +20 -73
- infrahub/webhook/triggers.py +20 -0
- infrahub/workers/infrahub_async.py +36 -25
- infrahub/workers/utils.py +63 -0
- infrahub/workflows/catalogue.py +13 -37
- infrahub/workflows/initialization.py +6 -8
- infrahub/workflows/models.py +3 -5
- infrahub/workflows/utils.py +1 -1
- infrahub_sdk/ctl/check.py +3 -3
- infrahub_sdk/ctl/cli_commands.py +11 -10
- infrahub_sdk/ctl/exceptions.py +0 -6
- infrahub_sdk/ctl/exporter.py +1 -1
- infrahub_sdk/ctl/generator.py +5 -5
- infrahub_sdk/ctl/importer.py +3 -2
- infrahub_sdk/ctl/menu.py +1 -1
- infrahub_sdk/ctl/object.py +1 -1
- infrahub_sdk/ctl/repository.py +23 -15
- infrahub_sdk/ctl/schema.py +2 -2
- infrahub_sdk/ctl/utils.py +4 -3
- infrahub_sdk/ctl/validate.py +2 -1
- infrahub_sdk/exceptions.py +6 -0
- infrahub_sdk/generator.py +3 -0
- infrahub_sdk/node.py +2 -2
- infrahub_sdk/schema/__init__.py +14 -2
- infrahub_sdk/schema/main.py +7 -0
- infrahub_sdk/utils.py +11 -1
- infrahub_sdk/yaml.py +2 -3
- {infrahub_server-1.1.6.dist-info → infrahub_server-1.2.0rc0.dist-info}/METADATA +46 -12
- {infrahub_server-1.1.6.dist-info → infrahub_server-1.2.0rc0.dist-info}/RECORD +338 -321
- infrahub_testcontainers/container.py +14 -6
- infrahub_testcontainers/docker-compose.test.yml +24 -5
- infrahub_testcontainers/haproxy.cfg +43 -0
- infrahub_testcontainers/helpers.py +85 -1
- infrahub/core/branch/constants.py +0 -2
- infrahub/graphql/query.py +0 -52
- infrahub/message_bus/messages/request_artifactdefinition_check.py +0 -17
- infrahub/message_bus/operations/requests/artifact_definition.py +0 -148
- infrahub/schema/constants.py +0 -1
- infrahub/schema/tasks.py +0 -76
- infrahub/services/adapters/database/__init__.py +0 -9
- infrahub_sdk/ctl/_file.py +0 -13
- /infrahub/{schema → artifacts}/__init__.py +0 -0
- {infrahub_server-1.1.6.dist-info → infrahub_server-1.2.0rc0.dist-info}/LICENSE.txt +0 -0
- {infrahub_server-1.1.6.dist-info → infrahub_server-1.2.0rc0.dist-info}/WHEEL +0 -0
- {infrahub_server-1.1.6.dist-info → infrahub_server-1.2.0rc0.dist-info}/entry_points.txt +0 -0
|
@@ -10,7 +10,7 @@ from prefect.logging import get_run_logger
|
|
|
10
10
|
from infrahub.core.branch import Branch # noqa: TC001
|
|
11
11
|
from infrahub.core.migrations import MIGRATION_MAP
|
|
12
12
|
from infrahub.core.path import SchemaPath # noqa: TC001
|
|
13
|
-
from infrahub.services import
|
|
13
|
+
from infrahub.services import InfrahubServices # noqa: TC001 needed for prefect flow
|
|
14
14
|
from infrahub.workflows.utils import add_branch_tag
|
|
15
15
|
|
|
16
16
|
from .models import SchemaApplyMigrationData, SchemaMigrationPathResponseData
|
|
@@ -20,7 +20,7 @@ if TYPE_CHECKING:
|
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
@flow(name="schema_apply_migrations", flow_run_name="Apply schema migrations", persist_result=True)
|
|
23
|
-
async def schema_apply_migrations(message: SchemaApplyMigrationData) -> list[str]:
|
|
23
|
+
async def schema_apply_migrations(message: SchemaApplyMigrationData, service: InfrahubServices) -> list[str]:
|
|
24
24
|
await add_branch_tag(branch_name=message.branch.name)
|
|
25
25
|
log = get_run_logger()
|
|
26
26
|
|
|
@@ -34,7 +34,6 @@ async def schema_apply_migrations(message: SchemaApplyMigrationData) -> list[str
|
|
|
34
34
|
log.info(f"Preparing migration for {migration.migration_name!r} ({migration.routing_key})")
|
|
35
35
|
|
|
36
36
|
new_node_schema: Optional[MainSchemaTypes] = None
|
|
37
|
-
previous_node_schema: Optional[MainSchemaTypes] = None
|
|
38
37
|
|
|
39
38
|
if message.new_schema.has(name=migration.path.schema_kind):
|
|
40
39
|
new_node_schema = message.new_schema.get(name=migration.path.schema_kind)
|
|
@@ -56,6 +55,7 @@ async def schema_apply_migrations(message: SchemaApplyMigrationData) -> list[str
|
|
|
56
55
|
new_node_schema=new_node_schema,
|
|
57
56
|
previous_node_schema=previous_node_schema,
|
|
58
57
|
schema_path=migration.path,
|
|
58
|
+
service=service,
|
|
59
59
|
)
|
|
60
60
|
|
|
61
61
|
async for _, result in batch.execute():
|
|
@@ -64,7 +64,7 @@ async def schema_apply_migrations(message: SchemaApplyMigrationData) -> list[str
|
|
|
64
64
|
return error_messages
|
|
65
65
|
|
|
66
66
|
|
|
67
|
-
@task(
|
|
67
|
+
@task( # type: ignore[arg-type]
|
|
68
68
|
name="schema-path-migrate",
|
|
69
69
|
task_run_name="Migrate Schema Path {migration_name} on {branch.name}",
|
|
70
70
|
description="Apply a given migration to the database",
|
|
@@ -75,10 +75,10 @@ async def schema_path_migrate(
|
|
|
75
75
|
branch: Branch,
|
|
76
76
|
migration_name: str,
|
|
77
77
|
schema_path: SchemaPath,
|
|
78
|
+
service: InfrahubServices,
|
|
78
79
|
new_node_schema: MainSchemaTypes | None = None,
|
|
79
80
|
previous_node_schema: MainSchemaTypes | None = None,
|
|
80
81
|
) -> SchemaMigrationPathResponseData:
|
|
81
|
-
service = services.service
|
|
82
82
|
log = get_run_logger()
|
|
83
83
|
|
|
84
84
|
async with service.database.start_session() as db:
|
|
@@ -58,7 +58,7 @@ class SchemaMigration(BaseModel):
|
|
|
58
58
|
query = await migration_query.init(db=ts, branch=branch, at=at, migration=self)
|
|
59
59
|
await query.execute(db=ts)
|
|
60
60
|
result.nbr_migrations_executed += query.get_nbr_migrations_executed()
|
|
61
|
-
except Exception as exc:
|
|
61
|
+
except Exception as exc:
|
|
62
62
|
result.errors.append(str(exc))
|
|
63
63
|
return result
|
|
64
64
|
|
|
@@ -126,7 +126,7 @@ class GraphMigration(BaseModel):
|
|
|
126
126
|
try:
|
|
127
127
|
query = await migration_query.init(db=ts)
|
|
128
128
|
await query.execute(db=ts)
|
|
129
|
-
except Exception as exc:
|
|
129
|
+
except Exception as exc:
|
|
130
130
|
result.errors.append(str(exc))
|
|
131
131
|
return result
|
|
132
132
|
|
|
@@ -141,7 +141,7 @@ class InternalSchemaMigration(BaseModel):
|
|
|
141
141
|
|
|
142
142
|
@staticmethod
|
|
143
143
|
def get_internal_schema() -> SchemaBranch:
|
|
144
|
-
from infrahub.core.schema.schema_branch import SchemaBranch
|
|
144
|
+
from infrahub.core.schema.schema_branch import SchemaBranch
|
|
145
145
|
|
|
146
146
|
# load the internal schema from
|
|
147
147
|
schema = SchemaRoot(**internal_schema)
|
|
@@ -167,7 +167,7 @@ class InternalSchemaMigration(BaseModel):
|
|
|
167
167
|
try:
|
|
168
168
|
execution_result = await migration.execute(db=db, branch=default_branch)
|
|
169
169
|
result.errors.extend(execution_result.errors)
|
|
170
|
-
except Exception as exc:
|
|
170
|
+
except Exception as exc:
|
|
171
171
|
result.errors.append(str(exc))
|
|
172
172
|
return result
|
|
173
173
|
|
infrahub/core/models.py
CHANGED
|
@@ -105,7 +105,6 @@ class SchemaDiff(BaseModel):
|
|
|
105
105
|
|
|
106
106
|
indent_str = " " * indentation
|
|
107
107
|
|
|
108
|
-
# pylint: disable=too-many-nested-blocks
|
|
109
108
|
for node_action, node_info in data.items():
|
|
110
109
|
for node_name, elements in node_info.items():
|
|
111
110
|
print(f"{str(node_name).ljust(column_size)} | {str(node_action).title()}")
|
|
@@ -152,7 +151,7 @@ class SchemaUpdateConstraintInfo(BaseModel):
|
|
|
152
151
|
return "schema.validator.path"
|
|
153
152
|
|
|
154
153
|
def __hash__(self) -> int:
|
|
155
|
-
return hash((type(self),) + tuple(
|
|
154
|
+
return hash((type(self),) + tuple(self.constraint_name + self.path.get_path()))
|
|
156
155
|
|
|
157
156
|
|
|
158
157
|
class SchemaUpdateValidationResult(BaseModel):
|
|
@@ -170,7 +169,7 @@ class SchemaUpdateValidationResult(BaseModel):
|
|
|
170
169
|
return obj
|
|
171
170
|
|
|
172
171
|
def process_diff(self, schema: SchemaBranch) -> None:
|
|
173
|
-
for schema_name
|
|
172
|
+
for schema_name in self.diff.removed.keys():
|
|
174
173
|
self.migrations.append(
|
|
175
174
|
SchemaUpdateMigrationInfo(
|
|
176
175
|
path=SchemaPath( # type: ignore[call-arg]
|
|
@@ -331,7 +330,7 @@ class SchemaUpdateValidationResult(BaseModel):
|
|
|
331
330
|
|
|
332
331
|
def add_validator_for_migration(self, validator_map: dict[str, Any]) -> None:
|
|
333
332
|
for migration in self.migrations:
|
|
334
|
-
if validator_map.get(migration.migration_name
|
|
333
|
+
if validator_map.get(migration.migration_name):
|
|
335
334
|
self.constraints.append(
|
|
336
335
|
SchemaUpdateConstraintInfo(
|
|
337
336
|
path=migration.path,
|
|
@@ -384,7 +383,7 @@ class HashableModel(BaseModel):
|
|
|
384
383
|
md5hash.update(item)
|
|
385
384
|
|
|
386
385
|
if display_values:
|
|
387
|
-
from rich import print as rprint
|
|
386
|
+
from rich import print as rprint
|
|
388
387
|
|
|
389
388
|
rprint(tuple(values))
|
|
390
389
|
|
|
@@ -494,8 +493,8 @@ class HashableModel(BaseModel):
|
|
|
494
493
|
raise ValueError(f"Unable to merge the list for {field_name}, some items have the same _sorting_id")
|
|
495
494
|
|
|
496
495
|
shared_ids = intersection(list(local_sub_items.keys()), list(other_sub_items.keys()))
|
|
497
|
-
local_only_ids = set(
|
|
498
|
-
other_only_ids = set(
|
|
496
|
+
local_only_ids = set(local_sub_items.keys()) - set(shared_ids)
|
|
497
|
+
other_only_ids = set(other_sub_items.keys()) - set(shared_ids)
|
|
499
498
|
|
|
500
499
|
new_list = [value for key, value in local_sub_items.items() if key in local_only_ids]
|
|
501
500
|
new_list.extend(
|
|
@@ -536,7 +535,7 @@ class HashableModel(BaseModel):
|
|
|
536
535
|
if attr_other is None or attr_local == attr_other:
|
|
537
536
|
continue
|
|
538
537
|
|
|
539
|
-
if attr_local is None or isinstance(attr_other,
|
|
538
|
+
if attr_local is None or isinstance(attr_other, int | str | bool | float):
|
|
540
539
|
setattr(self, field_name, attr_other)
|
|
541
540
|
continue
|
|
542
541
|
|
infrahub/core/node/__init__.py
CHANGED
|
@@ -1,17 +1,26 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from enum import Enum
|
|
4
|
-
from typing import TYPE_CHECKING, Any, Optional, TypeVar, Union, overload
|
|
4
|
+
from typing import TYPE_CHECKING, Any, Optional, Sequence, TypeVar, Union, overload
|
|
5
5
|
|
|
6
6
|
from infrahub_sdk.utils import is_valid_uuid
|
|
7
7
|
from infrahub_sdk.uuidt import UUIDT
|
|
8
8
|
|
|
9
9
|
from infrahub.core import registry
|
|
10
|
-
from infrahub.core.
|
|
10
|
+
from infrahub.core.changelog.models import NodeChangelog
|
|
11
|
+
from infrahub.core.constants import (
|
|
12
|
+
OBJECT_TEMPLATE_NAME_ATTR,
|
|
13
|
+
OBJECT_TEMPLATE_RELATIONSHIP_NAME,
|
|
14
|
+
BranchSupportType,
|
|
15
|
+
ComputedAttributeKind,
|
|
16
|
+
InfrahubKind,
|
|
17
|
+
RelationshipCardinality,
|
|
18
|
+
RelationshipKind,
|
|
19
|
+
)
|
|
11
20
|
from infrahub.core.constants.schema import SchemaElementPathType
|
|
12
|
-
from infrahub.core.protocols import CoreNumberPool
|
|
21
|
+
from infrahub.core.protocols import CoreNumberPool, CoreObjectTemplate
|
|
13
22
|
from infrahub.core.query.node import NodeCheckIDQuery, NodeCreateAllQuery, NodeDeleteQuery, NodeGetListQuery
|
|
14
|
-
from infrahub.core.schema import AttributeSchema, NodeSchema, ProfileSchema, RelationshipSchema
|
|
23
|
+
from infrahub.core.schema import AttributeSchema, NodeSchema, ProfileSchema, RelationshipSchema, TemplateSchema
|
|
15
24
|
from infrahub.core.timestamp import Timestamp
|
|
16
25
|
from infrahub.exceptions import InitializationError, NodeNotFoundError, PoolExhaustedError, ValidationError
|
|
17
26
|
from infrahub.support.macro import MacroDefinition
|
|
@@ -42,21 +51,17 @@ SchemaProtocol = TypeVar("SchemaProtocol")
|
|
|
42
51
|
# -
|
|
43
52
|
# ---------------------------------------------------------------------------------------
|
|
44
53
|
|
|
45
|
-
# pylint: disable=redefined-builtin,too-many-branches
|
|
46
|
-
|
|
47
54
|
|
|
48
55
|
class Node(BaseNode, metaclass=BaseNodeMeta):
|
|
49
56
|
@classmethod
|
|
50
|
-
def __init_subclass_with_meta__(
|
|
51
|
-
cls, _meta=None, default_filter=None, **options
|
|
52
|
-
) -> None:
|
|
57
|
+
def __init_subclass_with_meta__(cls, _meta=None, default_filter=None, **options) -> None:
|
|
53
58
|
if not _meta:
|
|
54
59
|
_meta = BaseNodeOptions(cls)
|
|
55
60
|
|
|
56
61
|
_meta.default_filter = default_filter
|
|
57
62
|
super().__init_subclass_with_meta__(_meta=_meta, **options)
|
|
58
63
|
|
|
59
|
-
def get_schema(self) -> Union[NodeSchema, ProfileSchema]:
|
|
64
|
+
def get_schema(self) -> Union[NodeSchema, ProfileSchema, TemplateSchema]:
|
|
60
65
|
return self._schema
|
|
61
66
|
|
|
62
67
|
def get_kind(self) -> str:
|
|
@@ -130,7 +135,7 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
|
|
|
130
135
|
labels.append(InfrahubKind.NODE)
|
|
131
136
|
return labels
|
|
132
137
|
|
|
133
|
-
if isinstance(self._schema, ProfileSchema):
|
|
138
|
+
if isinstance(self._schema, ProfileSchema | TemplateSchema):
|
|
134
139
|
labels = [self.get_kind()] + self._schema.inherit_from
|
|
135
140
|
return labels
|
|
136
141
|
|
|
@@ -153,8 +158,8 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
|
|
|
153
158
|
|
|
154
159
|
return f"{self.get_kind()}(ID: {str(self.id)})"
|
|
155
160
|
|
|
156
|
-
def __init__(self, schema: Union[NodeSchema, ProfileSchema], branch: Branch, at: Timestamp):
|
|
157
|
-
self._schema: Union[NodeSchema, ProfileSchema] = schema
|
|
161
|
+
def __init__(self, schema: Union[NodeSchema, ProfileSchema, TemplateSchema], branch: Branch, at: Timestamp):
|
|
162
|
+
self._schema: Union[NodeSchema, ProfileSchema, TemplateSchema] = schema
|
|
158
163
|
self._branch: Branch = branch
|
|
159
164
|
self._at: Timestamp = at
|
|
160
165
|
self._existing: bool = False
|
|
@@ -171,12 +176,20 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
|
|
|
171
176
|
# Lists of attributes and relationships names
|
|
172
177
|
self._attributes: list[str] = []
|
|
173
178
|
self._relationships: list[str] = []
|
|
179
|
+
self._node_changelog: NodeChangelog | None = None
|
|
180
|
+
|
|
181
|
+
@property
|
|
182
|
+
def node_changelog(self) -> NodeChangelog:
|
|
183
|
+
if self._node_changelog:
|
|
184
|
+
return self._node_changelog
|
|
185
|
+
|
|
186
|
+
raise InitializationError("The node has not been saved so no changelog exists")
|
|
174
187
|
|
|
175
188
|
@overload
|
|
176
189
|
@classmethod
|
|
177
190
|
async def init(
|
|
178
191
|
cls,
|
|
179
|
-
schema: Union[NodeSchema, ProfileSchema, str],
|
|
192
|
+
schema: Union[NodeSchema, ProfileSchema, TemplateSchema, str],
|
|
180
193
|
db: InfrahubDatabase,
|
|
181
194
|
branch: Optional[Union[Branch, str]] = ...,
|
|
182
195
|
at: Optional[Union[Timestamp, str]] = ...,
|
|
@@ -195,7 +208,7 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
|
|
|
195
208
|
@classmethod
|
|
196
209
|
async def init(
|
|
197
210
|
cls,
|
|
198
|
-
schema: Union[NodeSchema, ProfileSchema, str, type[SchemaProtocol]],
|
|
211
|
+
schema: Union[NodeSchema, ProfileSchema, TemplateSchema, str, type[SchemaProtocol]],
|
|
199
212
|
db: InfrahubDatabase,
|
|
200
213
|
branch: Optional[Union[Branch, str]] = None,
|
|
201
214
|
at: Optional[Union[Timestamp, str]] = None,
|
|
@@ -204,15 +217,17 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
|
|
|
204
217
|
|
|
205
218
|
branch = await registry.get_branch(branch=branch, db=db)
|
|
206
219
|
|
|
207
|
-
if isinstance(schema,
|
|
220
|
+
if isinstance(schema, NodeSchema | ProfileSchema | TemplateSchema):
|
|
208
221
|
attrs["schema"] = schema
|
|
209
222
|
elif isinstance(schema, str):
|
|
210
223
|
# TODO need to raise a proper exception for this, right now it will raise a generic ValueError
|
|
211
224
|
attrs["schema"] = db.schema.get(name=schema, branch=branch)
|
|
212
|
-
elif hasattr(schema, "_is_runtime_protocol") and
|
|
225
|
+
elif hasattr(schema, "_is_runtime_protocol") and schema._is_runtime_protocol:
|
|
213
226
|
attrs["schema"] = db.schema.get(name=schema.__name__, branch=branch)
|
|
214
227
|
else:
|
|
215
|
-
raise ValueError(
|
|
228
|
+
raise ValueError(
|
|
229
|
+
f"Invalid schema provided {type(schema)}, expected NodeSchema, ProfileSchema or TemplateSchema"
|
|
230
|
+
)
|
|
216
231
|
|
|
217
232
|
attrs["branch"] = branch
|
|
218
233
|
attrs["at"] = Timestamp(at)
|
|
@@ -261,6 +276,56 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
|
|
|
261
276
|
)
|
|
262
277
|
)
|
|
263
278
|
|
|
279
|
+
async def handle_object_template(self, fields: dict, db: InfrahubDatabase, errors: list) -> None:
|
|
280
|
+
"""Fill the `fields` parameters with values from an object template if one is in use."""
|
|
281
|
+
object_template_field = fields.get(OBJECT_TEMPLATE_RELATIONSHIP_NAME)
|
|
282
|
+
if not object_template_field:
|
|
283
|
+
return
|
|
284
|
+
|
|
285
|
+
try:
|
|
286
|
+
template: CoreObjectTemplate = await registry.manager.find_object(
|
|
287
|
+
db=db,
|
|
288
|
+
kind=self._schema.get_relationship(name=OBJECT_TEMPLATE_RELATIONSHIP_NAME).peer,
|
|
289
|
+
id=object_template_field.get("id"),
|
|
290
|
+
hfid=object_template_field.get("hfid"),
|
|
291
|
+
branch=self.get_branch_based_on_support_type(),
|
|
292
|
+
)
|
|
293
|
+
except NodeNotFoundError:
|
|
294
|
+
errors.append(
|
|
295
|
+
ValidationError(
|
|
296
|
+
{
|
|
297
|
+
f"{OBJECT_TEMPLATE_RELATIONSHIP_NAME}": (
|
|
298
|
+
"Unable to find the object template in the database "
|
|
299
|
+
f"'{object_template_field.get('id') or object_template_field.get('hfid')}'"
|
|
300
|
+
)
|
|
301
|
+
}
|
|
302
|
+
)
|
|
303
|
+
)
|
|
304
|
+
return
|
|
305
|
+
|
|
306
|
+
# Handle attributes, copy values from template
|
|
307
|
+
# Relationships handling in performed in GraphQL mutation to create nodes for relationships
|
|
308
|
+
for attribute_name in template._attributes:
|
|
309
|
+
if attribute_name in list(fields) + [OBJECT_TEMPLATE_NAME_ATTR]:
|
|
310
|
+
continue
|
|
311
|
+
fields[attribute_name] = {"value": getattr(template, attribute_name).value}
|
|
312
|
+
|
|
313
|
+
for relationship_name in template._relationships:
|
|
314
|
+
relationship_schema = template._schema.get_relationship(name=relationship_name)
|
|
315
|
+
if (
|
|
316
|
+
relationship_name in list(fields)
|
|
317
|
+
or relationship_schema.kind != RelationshipKind.ATTRIBUTE
|
|
318
|
+
or relationship_name == OBJECT_TEMPLATE_RELATIONSHIP_NAME
|
|
319
|
+
):
|
|
320
|
+
continue
|
|
321
|
+
|
|
322
|
+
relationship: RelationshipManager = getattr(template, relationship_name)
|
|
323
|
+
if relationship_schema.cardinality == RelationshipCardinality.ONE:
|
|
324
|
+
if relationship_peer := await relationship.get_peer(db=db):
|
|
325
|
+
fields[relationship_name] = {"id": relationship_peer.id}
|
|
326
|
+
elif relationship_peers := await relationship.get_peers(db=db):
|
|
327
|
+
fields[relationship_name] = [{"id": peer_id} for peer_id in relationship_peers]
|
|
328
|
+
|
|
264
329
|
async def _process_fields(self, fields: dict, db: InfrahubDatabase) -> None:
|
|
265
330
|
errors = []
|
|
266
331
|
|
|
@@ -279,6 +344,9 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
|
|
|
279
344
|
if field_name not in self._schema.valid_input_names:
|
|
280
345
|
errors.append(ValidationError({field_name: f"{field_name} is not a valid input for {self.get_kind()}"}))
|
|
281
346
|
|
|
347
|
+
# Backfill fields with the ones from the template if there's one
|
|
348
|
+
await self.handle_object_template(fields=fields, db=db, errors=errors)
|
|
349
|
+
|
|
282
350
|
# If the object is new, we need to ensure that all mandatory attributes and relationships have been provided
|
|
283
351
|
if not self._existing:
|
|
284
352
|
for mandatory_attr in self._schema.mandatory_attribute_names:
|
|
@@ -440,7 +508,7 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
|
|
|
440
508
|
|
|
441
509
|
async def _generate_relationship_default(
|
|
442
510
|
self,
|
|
443
|
-
name: str, #
|
|
511
|
+
name: str, # noqa: ARG002
|
|
444
512
|
schema: RelationshipSchema,
|
|
445
513
|
data: Any,
|
|
446
514
|
db: InfrahubDatabase,
|
|
@@ -461,7 +529,7 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
|
|
|
461
529
|
name: str,
|
|
462
530
|
schema: AttributeSchema,
|
|
463
531
|
data: Any,
|
|
464
|
-
db: InfrahubDatabase, #
|
|
532
|
+
db: InfrahubDatabase, # noqa: ARG002
|
|
465
533
|
) -> BaseAttribute:
|
|
466
534
|
attr_class = ATTRIBUTE_TYPES[schema.kind].get_infrahub_class()
|
|
467
535
|
attr = attr_class(
|
|
@@ -476,10 +544,9 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
|
|
|
476
544
|
)
|
|
477
545
|
return attr
|
|
478
546
|
|
|
479
|
-
async def process_label(self, db: Optional[InfrahubDatabase] = None) -> None: #
|
|
547
|
+
async def process_label(self, db: Optional[InfrahubDatabase] = None) -> None: # noqa: ARG002
|
|
480
548
|
# If there label and name are both defined for this node
|
|
481
549
|
# if label is not define, we'll automatically populate it with a human friendy vesion of name
|
|
482
|
-
# pylint: disable=no-member
|
|
483
550
|
if not self._existing and hasattr(self, "label") and hasattr(self, "name"):
|
|
484
551
|
if self.label.value is None and self.name.value:
|
|
485
552
|
self.label.value = " ".join([word.title() for word in self.name.value.split("_")])
|
|
@@ -526,7 +593,7 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
|
|
|
526
593
|
await self._process_fields(db=db, fields=kwargs)
|
|
527
594
|
return self
|
|
528
595
|
|
|
529
|
-
async def _create(self, db: InfrahubDatabase, at:
|
|
596
|
+
async def _create(self, db: InfrahubDatabase, at: Timestamp | None = None) -> NodeChangelog:
|
|
530
597
|
create_at = Timestamp(at)
|
|
531
598
|
|
|
532
599
|
query = await NodeCreateAllQuery.init(db=db, node=self, at=create_at)
|
|
@@ -538,44 +605,63 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
|
|
|
538
605
|
self._existing = True
|
|
539
606
|
|
|
540
607
|
new_ids = query.get_ids()
|
|
608
|
+
node_changelog = NodeChangelog(node_id=self.get_id(), node_kind=self.get_kind(), display_label="")
|
|
541
609
|
|
|
542
610
|
# Go over the list of Attribute and assign the new IDs one by one
|
|
543
611
|
for name in self._attributes:
|
|
544
612
|
attr: BaseAttribute = getattr(self, name)
|
|
545
613
|
attr.id, attr.db_id = new_ids[name]
|
|
546
614
|
attr.at = create_at
|
|
615
|
+
node_changelog.create_attribute(attribute=attr)
|
|
547
616
|
|
|
548
617
|
# Go over the list of relationships and assign the new IDs one by one
|
|
549
618
|
for name in self._relationships:
|
|
550
619
|
relm: RelationshipManager = getattr(self, name)
|
|
551
620
|
for rel in relm._relationships:
|
|
552
621
|
identifier = f"{rel.schema.identifier}::{rel.peer_id}"
|
|
622
|
+
|
|
553
623
|
rel.id, rel.db_id = new_ids[identifier]
|
|
554
624
|
|
|
625
|
+
node_changelog.create_relationship(relationship=rel)
|
|
626
|
+
|
|
627
|
+
node_changelog.display_label = await self.render_display_label(db=db)
|
|
628
|
+
return node_changelog
|
|
629
|
+
|
|
555
630
|
async def _update(
|
|
556
631
|
self, db: InfrahubDatabase, at: Optional[Timestamp] = None, fields: list[str] | None = None
|
|
557
|
-
) ->
|
|
632
|
+
) -> NodeChangelog:
|
|
558
633
|
"""Update the node in the database if needed."""
|
|
559
634
|
|
|
560
635
|
update_at = Timestamp(at)
|
|
636
|
+
node_changelog = NodeChangelog(node_id=self.get_id(), node_kind=self.get_kind(), display_label="")
|
|
561
637
|
|
|
562
638
|
# Go over the list of Attribute and update them one by one
|
|
563
639
|
for name in self._attributes:
|
|
564
|
-
if fields and name in fields:
|
|
565
|
-
attr: BaseAttribute = getattr(self, name)
|
|
566
|
-
await attr.save(at=update_at, db=db)
|
|
567
|
-
else:
|
|
640
|
+
if (fields and name in fields) or not fields:
|
|
568
641
|
attr: BaseAttribute = getattr(self, name)
|
|
569
|
-
await attr.save(at=update_at, db=db)
|
|
642
|
+
updated_attribute = await attr.save(at=update_at, db=db)
|
|
643
|
+
if updated_attribute:
|
|
644
|
+
node_changelog.add_attribute(attribute=updated_attribute)
|
|
570
645
|
|
|
571
646
|
# Go over the list of relationships and update them one by one
|
|
647
|
+
processed_relationships: list[str] = []
|
|
572
648
|
for name in self._relationships:
|
|
573
|
-
if fields and name in fields:
|
|
649
|
+
if (fields and name in fields) or not fields:
|
|
650
|
+
processed_relationships.append(name)
|
|
574
651
|
rel: RelationshipManager = getattr(self, name)
|
|
575
|
-
await rel.save(at=update_at, db=db)
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
652
|
+
updated_relationship = await rel.save(at=update_at, db=db)
|
|
653
|
+
node_changelog.add_relationship(relationship=updated_relationship)
|
|
654
|
+
|
|
655
|
+
if len(processed_relationships) != len(self._relationships):
|
|
656
|
+
# Analyze if the node has a parent and add it to the changelog if missing
|
|
657
|
+
if parent_relationship := self._get_parent_relationship_name():
|
|
658
|
+
if parent_relationship not in processed_relationships:
|
|
659
|
+
rel: RelationshipManager = getattr(self, parent_relationship)
|
|
660
|
+
if parent := await rel.get_parent(db=db):
|
|
661
|
+
node_changelog.add_parent_from_relationship(parent=parent)
|
|
662
|
+
|
|
663
|
+
node_changelog.display_label = await self.render_display_label(db=db)
|
|
664
|
+
return node_changelog
|
|
579
665
|
|
|
580
666
|
async def save(self, db: InfrahubDatabase, at: Optional[Timestamp] = None, fields: list[str] | None = None) -> Self:
|
|
581
667
|
"""Create or Update the Node in the database."""
|
|
@@ -583,10 +669,10 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
|
|
|
583
669
|
save_at = Timestamp(at)
|
|
584
670
|
|
|
585
671
|
if self._existing:
|
|
586
|
-
await self._update(at=save_at, db=db, fields=fields)
|
|
672
|
+
self._node_changelog = await self._update(at=save_at, db=db, fields=fields)
|
|
587
673
|
return self
|
|
588
674
|
|
|
589
|
-
await self._create(at=save_at, db=db)
|
|
675
|
+
self._node_changelog = await self._create(at=save_at, db=db)
|
|
590
676
|
return self
|
|
591
677
|
|
|
592
678
|
async def delete(self, db: InfrahubDatabase, at: Optional[Timestamp] = None) -> None:
|
|
@@ -594,15 +680,21 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
|
|
|
594
680
|
|
|
595
681
|
delete_at = Timestamp(at)
|
|
596
682
|
|
|
683
|
+
node_changelog = NodeChangelog(
|
|
684
|
+
node_id=self.get_id(), node_kind=self.get_kind(), display_label=await self.render_display_label(db=db)
|
|
685
|
+
)
|
|
597
686
|
# Go over the list of Attribute and update them one by one
|
|
598
687
|
for name in self._attributes:
|
|
599
688
|
attr: BaseAttribute = getattr(self, name)
|
|
600
|
-
await attr.delete(at=delete_at, db=db)
|
|
689
|
+
deleted_attribute = await attr.delete(at=delete_at, db=db)
|
|
690
|
+
if deleted_attribute:
|
|
691
|
+
node_changelog.add_attribute(attribute=deleted_attribute)
|
|
601
692
|
|
|
602
693
|
# Go over the list of relationships and update them one by one
|
|
603
694
|
for name in self._relationships:
|
|
604
695
|
rel: RelationshipManager = getattr(self, name)
|
|
605
|
-
await rel.delete(at=delete_at, db=db)
|
|
696
|
+
updated_relationship = await rel.delete(at=delete_at, db=db)
|
|
697
|
+
node_changelog.add_relationship(relationship=updated_relationship)
|
|
606
698
|
|
|
607
699
|
# Need to check if there are some unidirectional relationship as well
|
|
608
700
|
# For example, if we delete a tag, we must check the permissions and update all the relationships pointing at it
|
|
@@ -625,6 +717,7 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
|
|
|
625
717
|
|
|
626
718
|
query = await NodeDeleteQuery.init(db=db, node=self, at=delete_at)
|
|
627
719
|
await query.execute(db=db)
|
|
720
|
+
self._node_changelog = node_changelog
|
|
628
721
|
|
|
629
722
|
async def to_graphql(
|
|
630
723
|
self,
|
|
@@ -633,6 +726,7 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
|
|
|
633
726
|
related_node_ids: set | None = None,
|
|
634
727
|
filter_sensitive: bool = False,
|
|
635
728
|
permissions: dict | None = None,
|
|
729
|
+
include_properties: bool = True,
|
|
636
730
|
) -> dict:
|
|
637
731
|
"""Generate GraphQL Payload for all attributes
|
|
638
732
|
|
|
@@ -686,10 +780,14 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
|
|
|
686
780
|
related_node_ids=related_node_ids,
|
|
687
781
|
filter_sensitive=filter_sensitive,
|
|
688
782
|
permissions=permissions,
|
|
783
|
+
include_properties=include_properties,
|
|
689
784
|
)
|
|
690
785
|
else:
|
|
691
786
|
response[field_name] = await field.to_graphql(
|
|
692
|
-
db=db,
|
|
787
|
+
db=db,
|
|
788
|
+
filter_sensitive=filter_sensitive,
|
|
789
|
+
permissions=permissions,
|
|
790
|
+
include_properties=include_properties,
|
|
693
791
|
)
|
|
694
792
|
|
|
695
793
|
for relationship_schema in self.get_schema().relationships:
|
|
@@ -731,7 +829,7 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
|
|
|
731
829
|
|
|
732
830
|
return changed
|
|
733
831
|
|
|
734
|
-
async def render_display_label(self, db: Optional[InfrahubDatabase] = None) -> str: #
|
|
832
|
+
async def render_display_label(self, db: Optional[InfrahubDatabase] = None) -> str: # noqa: ARG002
|
|
735
833
|
if not self._schema.display_labels:
|
|
736
834
|
return repr(self)
|
|
737
835
|
|
|
@@ -757,3 +855,26 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
|
|
|
757
855
|
if not display_label.strip():
|
|
758
856
|
return repr(self)
|
|
759
857
|
return display_label.strip()
|
|
858
|
+
|
|
859
|
+
def _get_parent_relationship_name(self) -> str | None:
|
|
860
|
+
"""Return the name of the parent relationship is one is present"""
|
|
861
|
+
for relationship in self._schema.relationships:
|
|
862
|
+
if relationship.kind == RelationshipKind.PARENT:
|
|
863
|
+
return relationship.name
|
|
864
|
+
|
|
865
|
+
async def get_object_template(self, db: InfrahubDatabase) -> Node | None:
|
|
866
|
+
object_template: RelationshipManager = getattr(self, OBJECT_TEMPLATE_RELATIONSHIP_NAME, None)
|
|
867
|
+
return await object_template.get_peer(db=db) if object_template is not None else None
|
|
868
|
+
|
|
869
|
+
def get_relationships(
|
|
870
|
+
self, kind: RelationshipKind, exclude: Sequence[str] | None = None
|
|
871
|
+
) -> list[RelationshipSchema]:
|
|
872
|
+
"""Return relationships of a given kind with the possiblity to exclude some of them by name."""
|
|
873
|
+
if exclude is None:
|
|
874
|
+
exclude = []
|
|
875
|
+
|
|
876
|
+
return [
|
|
877
|
+
relationship
|
|
878
|
+
for relationship in self.get_schema().relationships
|
|
879
|
+
if relationship.name not in exclude and relationship.kind == kind
|
|
880
|
+
]
|
infrahub/core/node/base.py
CHANGED
|
@@ -21,7 +21,7 @@ class BaseOptions:
|
|
|
21
21
|
if not self._frozen:
|
|
22
22
|
super().__setattr__(name, value)
|
|
23
23
|
else:
|
|
24
|
-
raise Exception(f"Can't modify frozen Options {self}")
|
|
24
|
+
raise Exception(f"Can't modify frozen Options {self}")
|
|
25
25
|
|
|
26
26
|
def __repr__(self):
|
|
27
27
|
return f"<{self.__class__.__name__} name={repr(self.name)}>"
|
|
@@ -35,7 +35,7 @@ class NodeGroupedUniquenessConstraint(NodeConstraintInterface):
|
|
|
35
35
|
self.branch = branch
|
|
36
36
|
self.schema_branch = registry.schema.get_schema_branch(branch.name)
|
|
37
37
|
|
|
38
|
-
def _build_query_request(
|
|
38
|
+
async def _build_query_request(
|
|
39
39
|
self,
|
|
40
40
|
updated_node: Node,
|
|
41
41
|
node_schema: MainSchemaTypes,
|
|
@@ -51,9 +51,16 @@ class NodeGroupedUniquenessConstraint(NodeConstraintInterface):
|
|
|
51
51
|
if attribute_path.related_schema and attribute_path.relationship_schema:
|
|
52
52
|
if filters and attribute_path.relationship_schema.name in filters:
|
|
53
53
|
include_in_query = True
|
|
54
|
+
|
|
55
|
+
relationship_manager: RelationshipManager = getattr(
|
|
56
|
+
updated_node, attribute_path.relationship_schema.name
|
|
57
|
+
)
|
|
58
|
+
related_node = await relationship_manager.get_peer(db=self.db)
|
|
59
|
+
related_node_id = related_node.get_id() if related_node else None
|
|
54
60
|
query_relationship_paths.add(
|
|
55
61
|
QueryRelationshipAttributePath(
|
|
56
62
|
identifier=attribute_path.relationship_schema.get_identifier(),
|
|
63
|
+
value=related_node_id,
|
|
57
64
|
)
|
|
58
65
|
)
|
|
59
66
|
continue
|
|
@@ -158,7 +165,7 @@ class NodeGroupedUniquenessConstraint(NodeConstraintInterface):
|
|
|
158
165
|
) -> None:
|
|
159
166
|
schema_branch = self.db.schema.get_schema_branch(name=self.branch.name)
|
|
160
167
|
path_groups = node_schema.get_unique_constraint_schema_attribute_paths(schema_branch=schema_branch)
|
|
161
|
-
query_request = self._build_query_request(
|
|
168
|
+
query_request = await self._build_query_request(
|
|
162
169
|
updated_node=node, node_schema=node_schema, path_groups=path_groups, filters=filters
|
|
163
170
|
)
|
|
164
171
|
if not query_request:
|
|
@@ -11,7 +11,7 @@ from infrahub.core.query.relationship import (
|
|
|
11
11
|
RelationshipGetByIdentifierQuery,
|
|
12
12
|
RelationshipPeersData,
|
|
13
13
|
)
|
|
14
|
-
from infrahub.core.schema import MainSchemaTypes, NodeSchema, ProfileSchema
|
|
14
|
+
from infrahub.core.schema import MainSchemaTypes, NodeSchema, ProfileSchema, TemplateSchema
|
|
15
15
|
from infrahub.core.timestamp import Timestamp
|
|
16
16
|
from infrahub.database import InfrahubDatabase
|
|
17
17
|
from infrahub.exceptions import ValidationError
|
|
@@ -28,7 +28,7 @@ class NodeDeleteIndex:
|
|
|
28
28
|
# {node_schema: {DeleteRelationshipType: {relationship_identifier: peer_node_schema}}}
|
|
29
29
|
self._dependency_graph: dict[str, dict[DeleteRelationshipType, dict[str, set[str]]]] = {}
|
|
30
30
|
|
|
31
|
-
def index(self, start_schemas: Iterable[NodeSchema | ProfileSchema]) -> None:
|
|
31
|
+
def index(self, start_schemas: Iterable[NodeSchema | ProfileSchema | TemplateSchema]) -> None:
|
|
32
32
|
self._index_cascading_deletes(start_schemas=start_schemas)
|
|
33
33
|
self._index_dependent_schema(start_schemas=start_schemas)
|
|
34
34
|
|
|
@@ -50,7 +50,7 @@ class NodeDeleteIndex:
|
|
|
50
50
|
self._dependency_graph[kind][relationship_type] = defaultdict(set)
|
|
51
51
|
self._dependency_graph[kind][relationship_type][relationship_identifier].update(peer_kinds)
|
|
52
52
|
|
|
53
|
-
def _index_cascading_deletes(self, start_schemas: Iterable[NodeSchema | ProfileSchema]) -> None:
|
|
53
|
+
def _index_cascading_deletes(self, start_schemas: Iterable[NodeSchema | ProfileSchema | TemplateSchema]) -> None:
|
|
54
54
|
kinds_to_check: set[str] = {schema.kind for schema in start_schemas}
|
|
55
55
|
while True:
|
|
56
56
|
try:
|
|
@@ -72,7 +72,7 @@ class NodeDeleteIndex:
|
|
|
72
72
|
if peer_kind not in self._dependency_graph:
|
|
73
73
|
kinds_to_check.add(peer_kind)
|
|
74
74
|
|
|
75
|
-
def _index_dependent_schema(self, start_schemas: Iterable[NodeSchema | ProfileSchema]) -> None:
|
|
75
|
+
def _index_dependent_schema(self, start_schemas: Iterable[NodeSchema | ProfileSchema | TemplateSchema]) -> None:
|
|
76
76
|
start_schema_kinds: set[str] = set()
|
|
77
77
|
for start_schema in start_schemas:
|
|
78
78
|
start_schema_kinds.add(start_schema.kind)
|