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
|
@@ -0,0 +1,488 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING, Any, Self, cast
|
|
4
|
+
from uuid import UUID
|
|
5
|
+
|
|
6
|
+
from pydantic import BaseModel, Field, PrivateAttr, computed_field, field_validator, model_validator
|
|
7
|
+
|
|
8
|
+
from infrahub.core.constants import NULL_VALUE, DiffAction, RelationshipCardinality, RelationshipKind
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from infrahub.core.attribute import BaseAttribute
|
|
12
|
+
from infrahub.core.branch import Branch
|
|
13
|
+
from infrahub.core.manager import RelationshipSchema
|
|
14
|
+
from infrahub.core.query.relationship import RelationshipPeerData
|
|
15
|
+
from infrahub.core.relationship.model import Relationship
|
|
16
|
+
from infrahub.database import InfrahubDatabase
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class PropertyChangelog(BaseModel):
|
|
20
|
+
name: str = Field(..., description="The name of the property")
|
|
21
|
+
value: str | bool | None = Field(..., description="The updated or current value of the property")
|
|
22
|
+
value_previous: str | bool | None = Field(
|
|
23
|
+
...,
|
|
24
|
+
description="The previous value of the property, a `null` value indicates that the property didn't previously have a value",
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
@computed_field
|
|
28
|
+
def value_type(self) -> str:
|
|
29
|
+
"""The value_type of the property, used to help external systems"""
|
|
30
|
+
if isinstance(self.value, str):
|
|
31
|
+
return "Text"
|
|
32
|
+
|
|
33
|
+
return "Boolean"
|
|
34
|
+
|
|
35
|
+
@computed_field
|
|
36
|
+
def value_update_status(self) -> DiffAction:
|
|
37
|
+
"""Indicate how the value was changed during this update"""
|
|
38
|
+
if self.value == self.value_previous:
|
|
39
|
+
return DiffAction.UNCHANGED
|
|
40
|
+
if self.value_previous is not None and self.value is None:
|
|
41
|
+
return DiffAction.REMOVED
|
|
42
|
+
if self.value_previous is None and self.value is not None:
|
|
43
|
+
return DiffAction.ADDED
|
|
44
|
+
|
|
45
|
+
return DiffAction.UPDATED
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class AttributeChangelog(BaseModel):
|
|
49
|
+
name: str = Field(..., description="The name of the attribute")
|
|
50
|
+
value: Any = Field(default=None, description="The current value of the attribute")
|
|
51
|
+
value_previous: Any = Field(default=None, description="The previous value of the attribute")
|
|
52
|
+
properties: dict[str, PropertyChangelog] = Field(
|
|
53
|
+
default_factory=dict, description="The properties that were updated during this update"
|
|
54
|
+
)
|
|
55
|
+
kind: str = Field(..., description="The attribute kind")
|
|
56
|
+
|
|
57
|
+
@computed_field
|
|
58
|
+
def value_update_status(self) -> DiffAction:
|
|
59
|
+
"""Indicate how the peer was changed during this update"""
|
|
60
|
+
if self.value == self.value_previous:
|
|
61
|
+
return DiffAction.UNCHANGED
|
|
62
|
+
if self.value_previous is not None and self.value is None:
|
|
63
|
+
return DiffAction.REMOVED
|
|
64
|
+
if self.value_previous is None and self.value is not None:
|
|
65
|
+
return DiffAction.ADDED
|
|
66
|
+
|
|
67
|
+
return DiffAction.UPDATED
|
|
68
|
+
|
|
69
|
+
def add_property(self, name: str, value_current: bool | str | None, value_previous: bool | str | None) -> None:
|
|
70
|
+
self.properties[name] = PropertyChangelog(name=name, value=value_current, value_previous=value_previous)
|
|
71
|
+
|
|
72
|
+
@property
|
|
73
|
+
def has_updates(self) -> bool:
|
|
74
|
+
if self.value_update_status != DiffAction.UNCHANGED or self.properties:
|
|
75
|
+
return True
|
|
76
|
+
return False
|
|
77
|
+
|
|
78
|
+
@field_validator("value", "value_previous")
|
|
79
|
+
@classmethod
|
|
80
|
+
def convert_null_values(cls, value: Any) -> Any:
|
|
81
|
+
if isinstance(value, str) and value == NULL_VALUE:
|
|
82
|
+
return None
|
|
83
|
+
return value
|
|
84
|
+
|
|
85
|
+
@model_validator(mode="after")
|
|
86
|
+
def filter_sensitive(self) -> Self:
|
|
87
|
+
if self.kind in ["HashedPassword", "Password"]:
|
|
88
|
+
if self.value is not None:
|
|
89
|
+
self.value = "***"
|
|
90
|
+
if self.value_previous is not None:
|
|
91
|
+
self.value_previous = "***"
|
|
92
|
+
|
|
93
|
+
return self
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class RelationshipCardinalityOneChangelog(BaseModel):
|
|
97
|
+
name: str = Field(..., description="The name of the relationship")
|
|
98
|
+
peer_id_previous: str | None = Field(default=None, description="The previous peer of this relationship")
|
|
99
|
+
peer_kind_previous: str | None = Field(default=None, description="The node kind of the previous peer")
|
|
100
|
+
peer_id: str | None = Field(default=None, description="The current peer of this relationship")
|
|
101
|
+
peer_kind: str | None = Field(default=None, description="The node kind of the current peer")
|
|
102
|
+
properties: dict[str, PropertyChangelog] = Field(
|
|
103
|
+
default_factory=dict, description="Changes to properties of this relationship if any were made"
|
|
104
|
+
)
|
|
105
|
+
_parent: ChangelogRelatedNode | None = PrivateAttr(default=None)
|
|
106
|
+
|
|
107
|
+
@property
|
|
108
|
+
def parent(self) -> ChangelogRelatedNode | None:
|
|
109
|
+
return self._parent
|
|
110
|
+
|
|
111
|
+
@computed_field
|
|
112
|
+
def cardinality(self) -> str:
|
|
113
|
+
return "one"
|
|
114
|
+
|
|
115
|
+
@computed_field
|
|
116
|
+
def peer_status(self) -> DiffAction:
|
|
117
|
+
"""Indicate how the peer was changed during this update"""
|
|
118
|
+
if self.peer_id_previous == self.peer_id:
|
|
119
|
+
return DiffAction.UNCHANGED
|
|
120
|
+
if self.peer_id_previous and not self.peer_id:
|
|
121
|
+
return DiffAction.REMOVED
|
|
122
|
+
if self.peer_id and not self.peer_id_previous:
|
|
123
|
+
return DiffAction.ADDED
|
|
124
|
+
return DiffAction.UPDATED
|
|
125
|
+
|
|
126
|
+
def add_property(self, name: str, value_current: bool | str | None, value_previous: bool | str | None) -> None:
|
|
127
|
+
self.properties[name] = PropertyChangelog(name=name, value=value_current, value_previous=value_previous)
|
|
128
|
+
|
|
129
|
+
def set_parent(self, parent_id: str, parent_kind: str) -> None:
|
|
130
|
+
self._parent = ChangelogRelatedNode(node_id=parent_id, node_kind=parent_kind)
|
|
131
|
+
|
|
132
|
+
def set_parent_from_relationship(self, relationship: Relationship) -> None:
|
|
133
|
+
if relationship.schema.kind == RelationshipKind.PARENT:
|
|
134
|
+
if (
|
|
135
|
+
self.peer_status in [DiffAction.ADDED, DiffAction.UNCHANGED, DiffAction.UPDATED]
|
|
136
|
+
and self.peer_id
|
|
137
|
+
and self.peer_kind
|
|
138
|
+
):
|
|
139
|
+
self._parent = ChangelogRelatedNode(node_id=self.peer_id, node_kind=self.peer_kind)
|
|
140
|
+
elif self.peer_id_previous and self.peer_kind_previous:
|
|
141
|
+
self._parent = ChangelogRelatedNode(node_id=self.peer_id_previous, node_kind=self.peer_kind_previous)
|
|
142
|
+
|
|
143
|
+
@property
|
|
144
|
+
def is_empty(self) -> bool:
|
|
145
|
+
return self.peer_status == DiffAction.UNCHANGED and not self.properties
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
class RelationshipPeerChangelog(BaseModel):
|
|
149
|
+
peer_id: str = Field(..., description="The ID of the peer")
|
|
150
|
+
peer_kind: str = Field(..., description="The node kind of the peer")
|
|
151
|
+
peer_status: DiffAction = Field(
|
|
152
|
+
..., description="Indicate how the relationship to this peer was changed in this update"
|
|
153
|
+
)
|
|
154
|
+
properties: dict[str, PropertyChangelog] = Field(
|
|
155
|
+
default_factory=dict, description="Changes to properties of this relationship if any were made"
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
def add_property(self, name: str, value_current: bool | str | None, value_previous: bool | str | None) -> None:
|
|
159
|
+
self.properties[name] = PropertyChangelog(name=name, value=value_current, value_previous=value_previous)
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
class RelationshipCardinalityManyChangelog(BaseModel):
|
|
163
|
+
name: str
|
|
164
|
+
peers: list[RelationshipPeerChangelog] = Field(default_factory=list)
|
|
165
|
+
|
|
166
|
+
@computed_field
|
|
167
|
+
def cardinality(self) -> str:
|
|
168
|
+
return "many"
|
|
169
|
+
|
|
170
|
+
def add_new_peer(self, relationship: Relationship) -> None:
|
|
171
|
+
properties: dict[str, PropertyChangelog] = {}
|
|
172
|
+
properties["is_protected"] = PropertyChangelog(
|
|
173
|
+
name="is_protected", value=relationship.is_protected, value_previous=None
|
|
174
|
+
)
|
|
175
|
+
properties["is_visible"] = PropertyChangelog(
|
|
176
|
+
name="is_visible", value=relationship.is_protected, value_previous=None
|
|
177
|
+
)
|
|
178
|
+
if owner := getattr(relationship, "owner_id", None):
|
|
179
|
+
properties["owner"] = PropertyChangelog(name="owner", value=owner, value_previous=None)
|
|
180
|
+
if source := getattr(relationship, "source_id", None):
|
|
181
|
+
properties["source"] = PropertyChangelog(name="source_id", value=source, value_previous=None)
|
|
182
|
+
|
|
183
|
+
self.peers.append(
|
|
184
|
+
RelationshipPeerChangelog(
|
|
185
|
+
peer_id=relationship.get_peer_id(),
|
|
186
|
+
peer_kind=relationship.get_peer_kind(),
|
|
187
|
+
peer_status=DiffAction.ADDED,
|
|
188
|
+
properties=properties,
|
|
189
|
+
)
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
def remove_peer(self, peer_id: str, peer_kind: str) -> None:
|
|
193
|
+
self.peers.append(
|
|
194
|
+
RelationshipPeerChangelog(
|
|
195
|
+
peer_id=peer_id,
|
|
196
|
+
peer_kind=peer_kind,
|
|
197
|
+
peer_status=DiffAction.REMOVED,
|
|
198
|
+
)
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
@property
|
|
202
|
+
def is_empty(self) -> bool:
|
|
203
|
+
return not self.peers
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
class ChangelogRelatedNode(BaseModel):
|
|
207
|
+
node_id: str
|
|
208
|
+
node_kind: str
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
class NodeChangelog(BaseModel):
|
|
212
|
+
"""Emitted when a node is updated"""
|
|
213
|
+
|
|
214
|
+
node_id: str
|
|
215
|
+
node_kind: str
|
|
216
|
+
display_label: str
|
|
217
|
+
|
|
218
|
+
attributes: dict[str, AttributeChangelog] = Field(default_factory=dict)
|
|
219
|
+
relationships: dict[str, RelationshipCardinalityOneChangelog | RelationshipCardinalityManyChangelog] = Field(
|
|
220
|
+
default_factory=dict
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
_parent: ChangelogRelatedNode | None = PrivateAttr(default=None)
|
|
224
|
+
|
|
225
|
+
@property
|
|
226
|
+
def parent(self) -> ChangelogRelatedNode | None:
|
|
227
|
+
return self._parent
|
|
228
|
+
|
|
229
|
+
@property
|
|
230
|
+
def updated_fields(self) -> list[str]:
|
|
231
|
+
"""Return a list of update fields i.e. attributes and relationships"""
|
|
232
|
+
return list(self.relationships.keys()) + list(self.attributes.keys())
|
|
233
|
+
|
|
234
|
+
@property
|
|
235
|
+
def has_changes(self) -> bool:
|
|
236
|
+
return len(self.updated_fields) > 0
|
|
237
|
+
|
|
238
|
+
@property
|
|
239
|
+
def root_node_id(self) -> str:
|
|
240
|
+
"""Return the top level node_id"""
|
|
241
|
+
if self.parent:
|
|
242
|
+
return self.parent.node_id
|
|
243
|
+
return self.node_id
|
|
244
|
+
|
|
245
|
+
def add_parent(self, parent: ChangelogRelatedNode) -> None:
|
|
246
|
+
self._parent = parent
|
|
247
|
+
|
|
248
|
+
def add_parent_from_relationship(self, parent: Relationship) -> None:
|
|
249
|
+
self._parent = ChangelogRelatedNode(node_id=parent.get_peer_id(), node_kind=parent.get_peer_kind())
|
|
250
|
+
|
|
251
|
+
def create_relationship(self, relationship: Relationship) -> None:
|
|
252
|
+
if relationship.schema.cardinality == RelationshipCardinality.ONE:
|
|
253
|
+
peer_id = relationship.get_peer_id()
|
|
254
|
+
peer_kind = relationship.get_peer_kind()
|
|
255
|
+
if relationship.schema.kind == RelationshipKind.PARENT:
|
|
256
|
+
self._parent = ChangelogRelatedNode(node_id=peer_id, node_kind=peer_kind)
|
|
257
|
+
changelog_relationship = RelationshipCardinalityOneChangelog(
|
|
258
|
+
name=relationship.schema.name,
|
|
259
|
+
peer_id=peer_id,
|
|
260
|
+
peer_kind=peer_kind,
|
|
261
|
+
)
|
|
262
|
+
if source_id := getattr(relationship, "source_id", None):
|
|
263
|
+
changelog_relationship.add_property(name="source", value_current=source_id, value_previous=None)
|
|
264
|
+
if owner_id := getattr(relationship, "owner_id", None):
|
|
265
|
+
changelog_relationship.add_property(name="owner", value_current=owner_id, value_previous=None)
|
|
266
|
+
changelog_relationship.add_property(
|
|
267
|
+
name="is_protected", value_current=relationship.is_protected, value_previous=None
|
|
268
|
+
)
|
|
269
|
+
changelog_relationship.add_property(
|
|
270
|
+
name="is_visible", value_current=relationship.is_visible, value_previous=None
|
|
271
|
+
)
|
|
272
|
+
self.relationships[changelog_relationship.name] = changelog_relationship
|
|
273
|
+
elif relationship.schema.cardinality == RelationshipCardinality.MANY:
|
|
274
|
+
if relationship.schema.name not in self.relationships:
|
|
275
|
+
self.relationships[relationship.schema.name] = RelationshipCardinalityManyChangelog(
|
|
276
|
+
name=relationship.schema.name
|
|
277
|
+
)
|
|
278
|
+
relationship_container = cast(
|
|
279
|
+
RelationshipCardinalityManyChangelog, self.relationships[relationship.schema.name]
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
relationship_container.add_new_peer(relationship=relationship)
|
|
283
|
+
|
|
284
|
+
def delete_relationship(self, relationship: Relationship) -> None:
|
|
285
|
+
if relationship.schema.cardinality == RelationshipCardinality.ONE:
|
|
286
|
+
peer_id = relationship.get_peer_id()
|
|
287
|
+
peer_kind = relationship.get_peer_kind()
|
|
288
|
+
changelog_relationship = RelationshipCardinalityOneChangelog(
|
|
289
|
+
name=relationship.schema.name,
|
|
290
|
+
peer_id_previous=peer_id,
|
|
291
|
+
peer_kind_previous=peer_kind,
|
|
292
|
+
)
|
|
293
|
+
self.relationships[changelog_relationship.name] = changelog_relationship
|
|
294
|
+
elif relationship.schema.cardinality == RelationshipCardinality.MANY:
|
|
295
|
+
if relationship.schema.name not in self.relationships:
|
|
296
|
+
self.relationships[relationship.schema.name] = RelationshipCardinalityManyChangelog(
|
|
297
|
+
name=relationship.schema.name
|
|
298
|
+
)
|
|
299
|
+
relationship_container = cast(
|
|
300
|
+
RelationshipCardinalityManyChangelog, self.relationships[relationship.schema.name]
|
|
301
|
+
)
|
|
302
|
+
relationship_container.remove_peer(
|
|
303
|
+
peer_id=relationship.get_peer_id(), peer_kind=relationship.get_peer_kind()
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
def add_attribute(self, attribute: AttributeChangelog) -> None:
|
|
307
|
+
self.attributes[attribute.name] = attribute
|
|
308
|
+
|
|
309
|
+
def add_relationship(
|
|
310
|
+
self, relationship: RelationshipCardinalityOneChangelog | RelationshipCardinalityManyChangelog
|
|
311
|
+
) -> None:
|
|
312
|
+
if isinstance(relationship, RelationshipCardinalityOneChangelog) and relationship.parent:
|
|
313
|
+
self.add_parent(parent=relationship.parent)
|
|
314
|
+
if relationship.is_empty:
|
|
315
|
+
return
|
|
316
|
+
|
|
317
|
+
self.relationships[relationship.name] = relationship
|
|
318
|
+
|
|
319
|
+
def create_attribute(self, attribute: BaseAttribute) -> None:
|
|
320
|
+
changelog_attribute = AttributeChangelog(
|
|
321
|
+
name=attribute.name, value=attribute.value, value_previous=None, kind=attribute.schema.kind
|
|
322
|
+
)
|
|
323
|
+
if source_id := getattr(attribute, "source_id", None):
|
|
324
|
+
changelog_attribute.add_property(name="source", value_current=source_id, value_previous=None)
|
|
325
|
+
if owner_id := getattr(attribute, "owner_id", None):
|
|
326
|
+
changelog_attribute.add_property(name="owner", value_current=owner_id, value_previous=None)
|
|
327
|
+
changelog_attribute.add_property(name="is_protected", value_current=attribute.is_protected, value_previous=None)
|
|
328
|
+
changelog_attribute.add_property(name="is_visible", value_current=attribute.is_visible, value_previous=None)
|
|
329
|
+
self.attributes[changelog_attribute.name] = changelog_attribute
|
|
330
|
+
|
|
331
|
+
def get_related_nodes(self) -> list[ChangelogRelatedNode]:
|
|
332
|
+
related_nodes: dict[str, ChangelogRelatedNode] = {}
|
|
333
|
+
for relationship in self.relationships.values():
|
|
334
|
+
if isinstance(relationship, RelationshipCardinalityOneChangelog):
|
|
335
|
+
if relationship.peer_id and relationship.peer_kind:
|
|
336
|
+
related_nodes[relationship.peer_id] = ChangelogRelatedNode(
|
|
337
|
+
node_id=relationship.peer_id, node_kind=relationship.peer_kind
|
|
338
|
+
)
|
|
339
|
+
if relationship.peer_id_previous and relationship.peer_kind_previous:
|
|
340
|
+
related_nodes[relationship.peer_id_previous] = ChangelogRelatedNode(
|
|
341
|
+
node_id=relationship.peer_id_previous, node_kind=relationship.peer_kind_previous
|
|
342
|
+
)
|
|
343
|
+
elif isinstance(relationship, RelationshipCardinalityManyChangelog):
|
|
344
|
+
for peer in relationship.peers:
|
|
345
|
+
related_nodes[peer.peer_id] = ChangelogRelatedNode(node_id=peer.peer_id, node_kind=peer.peer_kind)
|
|
346
|
+
|
|
347
|
+
if self.parent:
|
|
348
|
+
related_nodes[self.parent.node_id] = self.parent
|
|
349
|
+
|
|
350
|
+
return list(related_nodes.values())
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
class ChangelogRelationshipMapper:
|
|
354
|
+
def __init__(self, schema: RelationshipSchema) -> None:
|
|
355
|
+
self.schema = schema
|
|
356
|
+
self._cardinality_one_relationship: RelationshipCardinalityOneChangelog | None = None
|
|
357
|
+
self._cardinality_many_relationship: RelationshipCardinalityManyChangelog | None = None
|
|
358
|
+
|
|
359
|
+
@property
|
|
360
|
+
def cardinality_one_relationship(self) -> RelationshipCardinalityOneChangelog:
|
|
361
|
+
if not self._cardinality_one_relationship:
|
|
362
|
+
self._cardinality_one_relationship = RelationshipCardinalityOneChangelog(name=self.schema.name)
|
|
363
|
+
|
|
364
|
+
return self._cardinality_one_relationship
|
|
365
|
+
|
|
366
|
+
@property
|
|
367
|
+
def cardinality_many_relationship(self) -> RelationshipCardinalityManyChangelog:
|
|
368
|
+
if not self._cardinality_many_relationship:
|
|
369
|
+
self._cardinality_many_relationship = RelationshipCardinalityManyChangelog(name=self.schema.name)
|
|
370
|
+
|
|
371
|
+
return self._cardinality_many_relationship
|
|
372
|
+
|
|
373
|
+
def remove_peer(self, peer_data: RelationshipPeerData) -> None:
|
|
374
|
+
if self.schema.cardinality == RelationshipCardinality.ONE:
|
|
375
|
+
self.cardinality_one_relationship.peer_id_previous = str(peer_data.peer_id)
|
|
376
|
+
self.cardinality_one_relationship.peer_kind_previous = peer_data.peer_kind
|
|
377
|
+
elif self.schema.cardinality == RelationshipCardinality.MANY:
|
|
378
|
+
self.cardinality_many_relationship.remove_peer(
|
|
379
|
+
peer_id=str(peer_data.peer_id), peer_kind=peer_data.peer_kind
|
|
380
|
+
)
|
|
381
|
+
|
|
382
|
+
def _set_cardinality_one_peer(self, relationship: Relationship) -> None:
|
|
383
|
+
self.cardinality_one_relationship.peer_id = relationship.peer_id
|
|
384
|
+
self.cardinality_one_relationship.peer_kind = relationship.get_peer_kind()
|
|
385
|
+
self.cardinality_one_relationship.set_parent_from_relationship(relationship=relationship)
|
|
386
|
+
|
|
387
|
+
def add_parent_from_relationship(self, relationship: Relationship) -> None:
|
|
388
|
+
if self.schema.cardinality == RelationshipCardinality.ONE:
|
|
389
|
+
self.cardinality_one_relationship.set_parent(
|
|
390
|
+
parent_id=relationship.get_peer_id(), parent_kind=relationship.get_peer_kind()
|
|
391
|
+
)
|
|
392
|
+
|
|
393
|
+
def add_peer_from_relationship(self, relationship: Relationship) -> None:
|
|
394
|
+
if self.schema.cardinality == RelationshipCardinality.ONE:
|
|
395
|
+
self._set_cardinality_one_peer(relationship=relationship)
|
|
396
|
+
elif self.schema.cardinality == RelationshipCardinality.MANY:
|
|
397
|
+
self.cardinality_many_relationship.add_new_peer(relationship=relationship)
|
|
398
|
+
|
|
399
|
+
def add_updated_relationship(
|
|
400
|
+
self, relationship: Relationship, old_data: RelationshipPeerData, properties_to_update: list[str]
|
|
401
|
+
) -> None:
|
|
402
|
+
if self.schema.cardinality == RelationshipCardinality.ONE:
|
|
403
|
+
self._set_cardinality_one_peer(relationship=relationship)
|
|
404
|
+
self.cardinality_one_relationship.peer_id_previous = self.cardinality_one_relationship.peer_id
|
|
405
|
+
self.cardinality_one_relationship.peer_kind_previous = self.cardinality_one_relationship.peer_kind
|
|
406
|
+
for property_to_update in properties_to_update:
|
|
407
|
+
previous_property = old_data.properties.get(property_to_update)
|
|
408
|
+
previous_value: str | bool | None = None
|
|
409
|
+
if previous_property:
|
|
410
|
+
if isinstance(previous_property.value, UUID):
|
|
411
|
+
previous_value = str(previous_property.value)
|
|
412
|
+
else:
|
|
413
|
+
previous_value = previous_property.value
|
|
414
|
+
property_name = (
|
|
415
|
+
property_to_update if property_to_update not in ["source", "owner"] else f"{property_to_update}_id"
|
|
416
|
+
)
|
|
417
|
+
self.cardinality_one_relationship.add_property(
|
|
418
|
+
name=property_to_update,
|
|
419
|
+
value_current=getattr(relationship, property_name),
|
|
420
|
+
value_previous=previous_value,
|
|
421
|
+
)
|
|
422
|
+
self.cardinality_one_relationship.set_parent_from_relationship(relationship=relationship)
|
|
423
|
+
|
|
424
|
+
def delete_relationship(self, relationship: Relationship) -> None:
|
|
425
|
+
if self.schema.cardinality == RelationshipCardinality.ONE:
|
|
426
|
+
self.cardinality_one_relationship.peer_id_previous = relationship.get_peer_id()
|
|
427
|
+
self.cardinality_one_relationship.peer_kind_previous = relationship.get_peer_kind()
|
|
428
|
+
self.cardinality_one_relationship.set_parent_from_relationship(relationship=relationship)
|
|
429
|
+
|
|
430
|
+
elif self.schema.cardinality == RelationshipCardinality.MANY:
|
|
431
|
+
self.cardinality_many_relationship.remove_peer(
|
|
432
|
+
peer_id=relationship.get_peer_id(), peer_kind=relationship.get_peer_kind()
|
|
433
|
+
)
|
|
434
|
+
|
|
435
|
+
@property
|
|
436
|
+
def changelog(self) -> RelationshipCardinalityOneChangelog | RelationshipCardinalityManyChangelog:
|
|
437
|
+
match self.schema.cardinality:
|
|
438
|
+
case RelationshipCardinality.ONE:
|
|
439
|
+
return self.cardinality_one_relationship
|
|
440
|
+
case RelationshipCardinality.MANY:
|
|
441
|
+
return self.cardinality_many_relationship
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
class RelationshipChangelogGetter:
|
|
445
|
+
def __init__(self, db: InfrahubDatabase, branch: Branch) -> None:
|
|
446
|
+
self._db = db
|
|
447
|
+
self._branch = branch
|
|
448
|
+
|
|
449
|
+
async def get_changelogs(self, primary_changelog: NodeChangelog) -> list[NodeChangelog]:
|
|
450
|
+
"""Return secondary changelogs based on this update
|
|
451
|
+
|
|
452
|
+
These will typically include updates to relationships on other nodes.
|
|
453
|
+
"""
|
|
454
|
+
schema_branch = self._db.schema.get_schema_branch(name=self._branch.name)
|
|
455
|
+
node_schema = schema_branch.get(name=primary_changelog.node_kind)
|
|
456
|
+
secondaries: list[NodeChangelog] = []
|
|
457
|
+
|
|
458
|
+
for relationship in primary_changelog.relationships.values():
|
|
459
|
+
rel_schema = node_schema.get_relationship(name=relationship.name)
|
|
460
|
+
if isinstance(relationship, RelationshipCardinalityOneChangelog):
|
|
461
|
+
# For now this code only looks at the scenario when a cardinality=one relationship
|
|
462
|
+
# is added to a node and it has a cardinality=many relationship coming back from
|
|
463
|
+
# another node, it will be expanded to include all variations.
|
|
464
|
+
if relationship.peer_status == DiffAction.ADDED:
|
|
465
|
+
peer_schema = schema_branch.get(name=str(relationship.peer_kind))
|
|
466
|
+
peer_relation = peer_schema.get_relationship_by_identifier(
|
|
467
|
+
id=str(rel_schema.identifier), raise_on_error=False
|
|
468
|
+
)
|
|
469
|
+
if peer_relation:
|
|
470
|
+
node_changelog = NodeChangelog(
|
|
471
|
+
node_id=str(relationship.peer_id),
|
|
472
|
+
node_kind=str(relationship.peer_kind),
|
|
473
|
+
display_label="n/a",
|
|
474
|
+
)
|
|
475
|
+
if peer_relation.cardinality == RelationshipCardinality.MANY:
|
|
476
|
+
node_changelog.relationships[peer_relation.name] = RelationshipCardinalityManyChangelog(
|
|
477
|
+
name=peer_relation.name,
|
|
478
|
+
peers=[
|
|
479
|
+
RelationshipPeerChangelog(
|
|
480
|
+
peer_id=primary_changelog.node_id,
|
|
481
|
+
peer_kind=primary_changelog.node_kind,
|
|
482
|
+
peer_status=DiffAction.ADDED,
|
|
483
|
+
)
|
|
484
|
+
],
|
|
485
|
+
)
|
|
486
|
+
secondaries.append(node_changelog)
|
|
487
|
+
|
|
488
|
+
return secondaries
|
|
@@ -175,11 +175,23 @@ class GeneratorInstanceStatus(InfrahubStringEnum):
|
|
|
175
175
|
|
|
176
176
|
|
|
177
177
|
class MutationAction(InfrahubStringEnum):
|
|
178
|
-
|
|
179
|
-
|
|
178
|
+
CREATED = "created"
|
|
179
|
+
DELETED = "deleted"
|
|
180
180
|
UPDATED = "updated"
|
|
181
181
|
UNDEFINED = "undefined"
|
|
182
182
|
|
|
183
|
+
@classmethod
|
|
184
|
+
def from_diff_action(cls, diff_action: DiffAction) -> MutationAction:
|
|
185
|
+
match diff_action:
|
|
186
|
+
case DiffAction.ADDED:
|
|
187
|
+
return MutationAction.CREATED
|
|
188
|
+
case DiffAction.REMOVED:
|
|
189
|
+
return MutationAction.DELETED
|
|
190
|
+
case DiffAction.UPDATED:
|
|
191
|
+
return MutationAction.UPDATED
|
|
192
|
+
case DiffAction.UNCHANGED:
|
|
193
|
+
return MutationAction.UNDEFINED
|
|
194
|
+
|
|
183
195
|
|
|
184
196
|
class PathResourceType(InfrahubStringEnum):
|
|
185
197
|
SCHEMA = "schema"
|
|
@@ -225,6 +237,7 @@ class RelationshipKind(InfrahubStringEnum):
|
|
|
225
237
|
GROUP = "Group"
|
|
226
238
|
HIERARCHY = "Hierarchy"
|
|
227
239
|
PROFILE = "Profile"
|
|
240
|
+
TEMPLATE = "Template"
|
|
228
241
|
|
|
229
242
|
|
|
230
243
|
class RelationshipStatus(InfrahubStringEnum):
|
|
@@ -301,6 +314,7 @@ RESTRICTED_NAMESPACES: list[str] = [
|
|
|
301
314
|
"Lineage",
|
|
302
315
|
"Schema",
|
|
303
316
|
"Profile",
|
|
317
|
+
"Template",
|
|
304
318
|
]
|
|
305
319
|
|
|
306
320
|
NODE_NAME_REGEX = r"^[A-Z][a-zA-Z0-9]+$"
|
|
@@ -315,3 +329,6 @@ DEFAULT_KIND_MAX_LENGTH = 32
|
|
|
315
329
|
NAMESPACE_REGEX = r"^[A-Z][a-z0-9]+$"
|
|
316
330
|
NODE_KIND_REGEX = r"^[A-Z][a-zA-Z0-9]+$"
|
|
317
331
|
DEFAULT_REL_IDENTIFIER_LENGTH = 128
|
|
332
|
+
|
|
333
|
+
OBJECT_TEMPLATE_RELATIONSHIP_NAME = "object_template"
|
|
334
|
+
OBJECT_TEMPLATE_NAME_ATTR = "template_name"
|
|
@@ -42,6 +42,7 @@ NUMBERPOOL = "CoreNumberPool"
|
|
|
42
42
|
LINEAGEOWNER = "LineageOwner"
|
|
43
43
|
LINEAGESOURCE = "LineageSource"
|
|
44
44
|
OBJECTPERMISSION = "CoreObjectPermission"
|
|
45
|
+
OBJECTTEMPLATE = "CoreObjectTemplate"
|
|
45
46
|
OBJECTTHREAD = "CoreObjectThread"
|
|
46
47
|
PASSWORDCREDENTIAL = "CorePasswordCredential"
|
|
47
48
|
PROFILE = "CoreProfile"
|
infrahub/core/diff/combiner.py
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
from copy import deepcopy
|
|
2
2
|
from dataclasses import dataclass, field, replace
|
|
3
3
|
from typing import Iterable
|
|
4
|
-
from uuid import uuid4
|
|
5
4
|
|
|
6
5
|
from infrahub.core.constants import NULL_VALUE, DiffAction, RelationshipCardinality
|
|
7
6
|
from infrahub.core.constants.database import DatabaseEdgeType
|
|
@@ -342,6 +341,8 @@ class DiffCombiner:
|
|
|
342
341
|
|
|
343
342
|
def _copy_node_without_parents(self, node: EnrichedDiffNode) -> EnrichedDiffNode:
|
|
344
343
|
rels_without_parents = {replace(r, nodes=set()) for r in node.relationships}
|
|
344
|
+
for rel in rels_without_parents:
|
|
345
|
+
rel.reset_summaries()
|
|
345
346
|
node_without_parents = replace(node, relationships=rels_without_parents)
|
|
346
347
|
return deepcopy(node_without_parents)
|
|
347
348
|
|
|
@@ -351,15 +352,11 @@ class DiffCombiner:
|
|
|
351
352
|
if node_pair.earlier is None:
|
|
352
353
|
if node_pair.later is not None:
|
|
353
354
|
copied = self._copy_node_without_parents(node_pair.later)
|
|
354
|
-
for rel in copied.relationships:
|
|
355
|
-
rel.reset_summaries()
|
|
356
355
|
combined_nodes.add(copied)
|
|
357
356
|
continue
|
|
358
357
|
if node_pair.later is None:
|
|
359
358
|
if node_pair.earlier is not None:
|
|
360
359
|
copied = self._copy_node_without_parents(node_pair.earlier)
|
|
361
|
-
for rel in copied.relationships:
|
|
362
|
-
rel.reset_summaries()
|
|
363
360
|
combined_nodes.add(copied)
|
|
364
361
|
continue
|
|
365
362
|
combined_attributes = self._combine_attributes(
|
|
@@ -420,18 +417,25 @@ class DiffCombiner:
|
|
|
420
417
|
filtered_node_pairs = self._filter_nodes_to_keep(earlier_diff=earlier, later_diff=later)
|
|
421
418
|
combined_nodes = self._combine_nodes(node_pairs=filtered_node_pairs)
|
|
422
419
|
self._link_child_nodes(nodes=combined_nodes)
|
|
420
|
+
if earlier.exists_on_database:
|
|
421
|
+
diff_uuid = earlier.uuid
|
|
422
|
+
partner_uuid = earlier.partner_uuid
|
|
423
|
+
else:
|
|
424
|
+
diff_uuid = later.uuid
|
|
425
|
+
partner_uuid = later.partner_uuid
|
|
423
426
|
combined_diffs.append(
|
|
424
427
|
EnrichedDiffRoot(
|
|
425
|
-
uuid=
|
|
426
|
-
partner_uuid=
|
|
428
|
+
uuid=diff_uuid,
|
|
429
|
+
partner_uuid=partner_uuid,
|
|
427
430
|
base_branch_name=later.base_branch_name,
|
|
428
431
|
diff_branch_name=later.diff_branch_name,
|
|
429
432
|
from_time=earlier.from_time,
|
|
430
433
|
to_time=later.to_time,
|
|
434
|
+
tracking_id=later.tracking_id,
|
|
431
435
|
nodes=combined_nodes,
|
|
432
436
|
)
|
|
433
437
|
)
|
|
434
|
-
base_branch_diff, diff_branch_diff = combined_diffs
|
|
438
|
+
base_branch_diff, diff_branch_diff = combined_diffs
|
|
435
439
|
base_branch_diff.partner_uuid = diff_branch_diff.uuid
|
|
436
440
|
diff_branch_diff.partner_uuid = base_branch_diff.uuid
|
|
437
441
|
return EnrichedDiffs(
|