infrahub-server 1.1.7__py3-none-any.whl → 1.2.0b1__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} +5 -3
- 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/models.py +1 -1
- infrahub/computed_attribute/tasks.py +64 -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 +50 -21
- infrahub/core/branch/models.py +4 -4
- infrahub/core/branch/tasks.py +130 -125
- infrahub/core/changelog/__init__.py +0 -0
- infrahub/core/changelog/diff.py +283 -0
- infrahub/core/changelog/models.py +499 -0
- infrahub/core/constants/__init__.py +43 -2
- infrahub/core/constants/infrahubkind.py +1 -0
- infrahub/core/constants/schema.py +2 -0
- infrahub/core/diff/combiner.py +1 -1
- infrahub/core/diff/enricher/cardinality_one.py +6 -1
- infrahub/core/diff/enricher/hierarchy.py +22 -7
- infrahub/core/diff/enricher/labels.py +6 -1
- infrahub/core/diff/enricher/path_identifier.py +5 -1
- infrahub/core/diff/enricher/summary_counts.py +107 -0
- infrahub/core/diff/merger/merger.py +3 -1
- infrahub/core/diff/model/path.py +34 -11
- infrahub/core/diff/parent_node_adder.py +78 -0
- infrahub/core/diff/payload_builder.py +13 -2
- infrahub/core/diff/query/all_conflicts.py +1 -1
- infrahub/core/diff/query/artifact.py +1 -1
- infrahub/core/diff/query/delete_query.py +1 -1
- infrahub/core/diff/query/diff_get.py +1 -1
- infrahub/core/diff/query/diff_summary.py +1 -1
- infrahub/core/diff/query/field_specifiers.py +1 -1
- infrahub/core/diff/query/field_summary.py +1 -1
- infrahub/core/diff/query/filters.py +2 -2
- infrahub/core/diff/query/get_conflict_query.py +1 -1
- infrahub/core/diff/query/has_conflicts_query.py +1 -1
- infrahub/core/diff/query/merge.py +3 -3
- infrahub/core/diff/query/merge_tracking_id.py +1 -1
- infrahub/core/diff/query/roots_metadata.py +1 -1
- infrahub/core/diff/query/save.py +191 -185
- infrahub/core/diff/query/summary_counts_enricher.py +52 -5
- infrahub/core/diff/query/time_range_query.py +1 -1
- infrahub/core/diff/query/update_conflict_query.py +1 -1
- infrahub/core/diff/repository/deserializer.py +9 -4
- infrahub/core/diff/repository/repository.py +156 -38
- infrahub/core/diff/tasks.py +13 -12
- infrahub/core/enums.py +1 -1
- infrahub/core/graph/__init__.py +1 -1
- infrahub/core/graph/index.py +3 -0
- 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 +20 -15
- infrahub/core/merge.py +5 -2
- infrahub/core/migrations/graph/__init__.py +4 -0
- 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/graph/m019_restore_rels_to_time.py +256 -0
- infrahub/core/migrations/graph/m020_add_generate_template_attr.py +48 -0
- 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 +39 -19
- 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 +27 -13
- 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 +164 -45
- infrahub/core/node/base.py +1 -1
- infrahub/core/node/delete_validator.py +4 -4
- infrahub/core/node/ipam.py +7 -7
- 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 +6 -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 +4 -4
- infrahub/core/query/ipam.py +4 -4
- infrahub/core/query/node.py +11 -12
- infrahub/core/query/relationship.py +211 -25
- infrahub/core/query/resource_manager.py +10 -10
- 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 +76 -38
- infrahub/core/schema/__init__.py +6 -4
- infrahub/core/schema/attribute_schema.py +8 -0
- infrahub/core/schema/basenode_schema.py +13 -3
- infrahub/core/schema/definitions/core/__init__.py +153 -0
- infrahub/core/schema/definitions/core/account.py +168 -0
- infrahub/core/schema/definitions/core/artifact.py +127 -0
- infrahub/core/schema/definitions/core/builtin.py +21 -0
- infrahub/core/schema/definitions/core/check.py +60 -0
- infrahub/core/schema/definitions/core/generator.py +96 -0
- infrahub/core/schema/definitions/core/graphql_query.py +77 -0
- infrahub/core/schema/definitions/core/group.py +105 -0
- infrahub/core/schema/definitions/core/ipam.py +252 -0
- infrahub/core/schema/definitions/core/lineage.py +17 -0
- infrahub/core/schema/definitions/core/menu.py +46 -0
- infrahub/core/schema/definitions/core/permission.py +161 -0
- infrahub/core/schema/definitions/core/profile.py +29 -0
- infrahub/core/schema/definitions/core/propose_change.py +88 -0
- infrahub/core/schema/definitions/core/propose_change_comment.py +188 -0
- infrahub/core/schema/definitions/core/propose_change_validator.py +326 -0
- infrahub/core/schema/definitions/core/repository.py +280 -0
- infrahub/core/schema/definitions/core/resource_pool.py +180 -0
- infrahub/core/schema/definitions/core/template.py +12 -0
- infrahub/core/schema/definitions/core/transform.py +87 -0
- infrahub/core/schema/definitions/core/webhook.py +108 -0
- 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 +10 -2
- infrahub/core/schema/schema_branch.py +260 -16
- infrahub/core/schema/template_schema.py +36 -0
- infrahub/core/task/user_task.py +7 -5
- infrahub/core/timestamp.py +3 -3
- infrahub/core/utils.py +2 -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 +1 -1
- infrahub/database/__init__.py +3 -2
- infrahub/database/memgraph.py +1 -1
- infrahub/dependencies/builder/diff/combiner.py +1 -1
- infrahub/dependencies/builder/diff/conflicts_enricher.py +1 -1
- infrahub/dependencies/builder/diff/deserializer.py +4 -2
- infrahub/dependencies/builder/diff/enricher/hierarchy.py +3 -1
- infrahub/dependencies/builder/diff/enricher/summary_counts.py +8 -0
- infrahub/dependencies/builder/diff/parent_node_adder.py +8 -0
- infrahub/events/artifact_action.py +76 -0
- infrahub/events/branch_action.py +50 -21
- infrahub/events/group_action.py +117 -0
- infrahub/events/models.py +164 -51
- infrahub/events/node_action.py +70 -8
- infrahub/events/repository_action.py +8 -8
- infrahub/events/schema_action.py +21 -8
- infrahub/exceptions.py +9 -0
- infrahub/generators/models.py +1 -0
- infrahub/generators/tasks.py +34 -15
- infrahub/git/base.py +3 -5
- infrahub/git/constants.py +0 -1
- infrahub/git/integrator.py +60 -36
- infrahub/git/models.py +80 -1
- infrahub/git/repository.py +7 -8
- infrahub/git/tasks.py +432 -112
- 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/context.py +33 -0
- infrahub/graphql/enums.py +1 -1
- infrahub/graphql/initialization.py +5 -1
- infrahub/graphql/loaders/node.py +2 -2
- infrahub/graphql/manager.py +63 -63
- infrahub/graphql/mutations/account.py +20 -13
- infrahub/graphql/mutations/artifact_definition.py +16 -12
- infrahub/graphql/mutations/branch.py +86 -40
- infrahub/graphql/mutations/computed_attribute.py +24 -15
- infrahub/graphql/mutations/diff.py +33 -17
- infrahub/graphql/mutations/diff_conflict.py +14 -8
- infrahub/graphql/mutations/generator.py +83 -0
- infrahub/graphql/mutations/graphql_query.py +19 -11
- infrahub/graphql/mutations/ipam.py +25 -23
- infrahub/graphql/mutations/main.py +233 -45
- infrahub/graphql/mutations/menu.py +10 -10
- infrahub/graphql/mutations/proposed_change.py +36 -28
- infrahub/graphql/mutations/relationship.py +341 -130
- infrahub/graphql/mutations/repository.py +41 -35
- infrahub/graphql/mutations/resource_manager.py +26 -26
- infrahub/graphql/mutations/schema.py +66 -33
- infrahub/graphql/mutations/tasks.py +10 -7
- infrahub/graphql/parser.py +1 -1
- infrahub/graphql/permissions.py +3 -10
- infrahub/graphql/queries/account.py +22 -18
- infrahub/graphql/queries/branch.py +6 -4
- infrahub/graphql/queries/diff/tree.py +63 -52
- infrahub/graphql/queries/event.py +115 -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 +37 -25
- 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 +4 -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/context.py +12 -0
- infrahub/graphql/types/enums.py +2 -0
- infrahub/graphql/types/event.py +158 -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 +21 -21
- infrahub/menu/generator.py +0 -1
- infrahub/menu/menu.py +116 -138
- infrahub/menu/models.py +4 -4
- infrahub/message_bus/__init__.py +11 -13
- infrahub/message_bus/messages/__init__.py +0 -14
- infrahub/message_bus/messages/check_generator_run.py +1 -3
- infrahub/message_bus/messages/event_branch_merge.py +3 -0
- infrahub/message_bus/messages/proposed_change/request_proposedchange_refreshartifacts.py +6 -0
- infrahub/message_bus/messages/request_proposedchange_pipeline.py +2 -0
- infrahub/message_bus/messages/send_echo_request.py +1 -1
- infrahub/message_bus/operations/__init__.py +4 -13
- infrahub/message_bus/operations/check/__init__.py +2 -2
- infrahub/message_bus/operations/check/generator.py +1 -3
- infrahub/message_bus/operations/event/branch.py +7 -3
- infrahub/message_bus/operations/event/schema.py +1 -1
- infrahub/message_bus/operations/event/worker.py +0 -3
- 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 -4
- infrahub/message_bus/operations/requests/generator_definition.py +2 -4
- infrahub/message_bus/operations/requests/proposed_change.py +37 -20
- infrahub/message_bus/operations/send/echo.py +1 -1
- infrahub/message_bus/types.py +1 -1
- infrahub/permissions/globals.py +15 -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 +18 -1
- infrahub/proposed_change/tasks.py +195 -53
- infrahub/pytest_plugin.py +4 -4
- infrahub/server.py +13 -12
- infrahub/services/__init__.py +148 -63
- infrahub/services/adapters/cache/__init__.py +11 -11
- infrahub/services/adapters/cache/nats.py +42 -25
- infrahub/services/adapters/cache/redis.py +3 -11
- 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 +25 -8
- infrahub/services/adapters/message_bus/local.py +9 -7
- infrahub/services/adapters/message_bus/nats.py +14 -8
- infrahub/services/adapters/message_bus/rabbitmq.py +23 -10
- infrahub/services/adapters/workflow/__init__.py +11 -8
- infrahub/services/adapters/workflow/local.py +27 -6
- infrahub/services/adapters/workflow/worker.py +23 -7
- infrahub/services/component.py +43 -40
- infrahub/services/protocols.py +7 -7
- infrahub/services/scheduler.py +30 -29
- infrahub/storage.py +2 -4
- infrahub/task_manager/constants.py +1 -1
- infrahub/task_manager/event.py +261 -0
- infrahub/task_manager/models.py +147 -3
- infrahub/task_manager/task.py +1 -1
- infrahub/tasks/artifact.py +19 -18
- 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 +16 -0
- infrahub/trigger/constants.py +9 -0
- infrahub/trigger/models.py +105 -0
- infrahub/trigger/tasks.py +91 -0
- infrahub/types.py +1 -1
- infrahub/utils.py +1 -1
- infrahub/webhook/constants.py +0 -2
- infrahub/webhook/models.py +161 -40
- infrahub/webhook/tasks.py +123 -202
- infrahub/webhook/triggers.py +27 -0
- infrahub/workers/infrahub_async.py +36 -25
- infrahub/workers/utils.py +63 -0
- infrahub/workflows/catalogue.py +71 -52
- infrahub/workflows/initialization.py +14 -8
- infrahub/workflows/models.py +28 -4
- infrahub/workflows/utils.py +1 -1
- infrahub_sdk/batch.py +2 -2
- infrahub_sdk/client.py +8 -0
- infrahub_sdk/config.py +1 -1
- infrahub_sdk/ctl/branch.py +3 -2
- infrahub_sdk/ctl/check.py +4 -4
- infrahub_sdk/ctl/cli_commands.py +16 -11
- 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 +6 -21
- infrahub_sdk/ctl/validate.py +2 -1
- infrahub_sdk/data.py +1 -1
- infrahub_sdk/exceptions.py +12 -0
- infrahub_sdk/generator.py +3 -0
- infrahub_sdk/node.py +5 -8
- infrahub_sdk/protocols.py +20 -8
- infrahub_sdk/schema/__init__.py +14 -5
- infrahub_sdk/schema/main.py +7 -0
- infrahub_sdk/task/__init__.py +1 -0
- infrahub_sdk/task/constants.py +3 -0
- infrahub_sdk/task/exceptions.py +25 -0
- infrahub_sdk/task/manager.py +545 -0
- infrahub_sdk/task/models.py +74 -0
- infrahub_sdk/testing/docker.py +30 -0
- infrahub_sdk/timestamp.py +134 -33
- infrahub_sdk/transfer/exporter/json.py +1 -1
- infrahub_sdk/utils.py +39 -1
- infrahub_sdk/yaml.py +2 -3
- {infrahub_server-1.1.7.dist-info → infrahub_server-1.2.0b1.dist-info}/METADATA +7 -6
- {infrahub_server-1.1.7.dist-info → infrahub_server-1.2.0b1.dist-info}/RECORD +383 -339
- infrahub_testcontainers/container.py +2 -3
- infrahub_testcontainers/docker-compose.test.yml +2 -2
- infrahub/core/branch/constants.py +0 -2
- infrahub/core/schema/definitions/core.py +0 -2274
- infrahub/graphql/query.py +0 -52
- infrahub/message_bus/messages/check_repository_checkdefinition.py +0 -20
- infrahub/message_bus/messages/check_repository_mergeconflicts.py +0 -16
- infrahub/message_bus/messages/check_repository_usercheck.py +0 -26
- infrahub/message_bus/messages/request_artifactdefinition_check.py +0 -17
- infrahub/message_bus/messages/request_repository_checks.py +0 -12
- infrahub/message_bus/messages/request_repository_userchecks.py +0 -18
- infrahub/message_bus/operations/check/repository.py +0 -293
- infrahub/message_bus/operations/requests/artifact_definition.py +0 -148
- infrahub/message_bus/operations/requests/repository.py +0 -133
- 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.7.dist-info → infrahub_server-1.2.0b1.dist-info}/LICENSE.txt +0 -0
- {infrahub_server-1.1.7.dist-info → infrahub_server-1.2.0b1.dist-info}/WHEEL +0 -0
- {infrahub_server-1.1.7.dist-info → infrahub_server-1.2.0b1.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,499 @@
|
|
|
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
|
+
def set_value(self, value: Any) -> None:
|
|
79
|
+
if isinstance(value, str) and value == NULL_VALUE:
|
|
80
|
+
self.value = None
|
|
81
|
+
return
|
|
82
|
+
self.value = value
|
|
83
|
+
|
|
84
|
+
def set_value_previous(self, value: Any) -> None:
|
|
85
|
+
if isinstance(value, str) and value == NULL_VALUE:
|
|
86
|
+
self.value_previous = None
|
|
87
|
+
return
|
|
88
|
+
self.value_previous = value
|
|
89
|
+
|
|
90
|
+
@field_validator("value", "value_previous")
|
|
91
|
+
@classmethod
|
|
92
|
+
def convert_null_values(cls, value: Any) -> Any:
|
|
93
|
+
if isinstance(value, str) and value == NULL_VALUE:
|
|
94
|
+
return None
|
|
95
|
+
return value
|
|
96
|
+
|
|
97
|
+
@model_validator(mode="after")
|
|
98
|
+
def filter_sensitive(self) -> Self:
|
|
99
|
+
if self.kind in ["HashedPassword", "Password"]:
|
|
100
|
+
if self.value is not None:
|
|
101
|
+
self.value = "***"
|
|
102
|
+
if self.value_previous is not None:
|
|
103
|
+
self.value_previous = "***"
|
|
104
|
+
|
|
105
|
+
return self
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class RelationshipCardinalityOneChangelog(BaseModel):
|
|
109
|
+
name: str = Field(..., description="The name of the relationship")
|
|
110
|
+
peer_id_previous: str | None = Field(default=None, description="The previous peer of this relationship")
|
|
111
|
+
peer_kind_previous: str | None = Field(default=None, description="The node kind of the previous peer")
|
|
112
|
+
peer_id: str | None = Field(default=None, description="The current peer of this relationship")
|
|
113
|
+
peer_kind: str | None = Field(default=None, description="The node kind of the current peer")
|
|
114
|
+
properties: dict[str, PropertyChangelog] = Field(
|
|
115
|
+
default_factory=dict, description="Changes to properties of this relationship if any were made"
|
|
116
|
+
)
|
|
117
|
+
_parent: ChangelogRelatedNode | None = PrivateAttr(default=None)
|
|
118
|
+
|
|
119
|
+
@property
|
|
120
|
+
def parent(self) -> ChangelogRelatedNode | None:
|
|
121
|
+
return self._parent
|
|
122
|
+
|
|
123
|
+
@computed_field
|
|
124
|
+
def cardinality(self) -> str:
|
|
125
|
+
return "one"
|
|
126
|
+
|
|
127
|
+
@computed_field
|
|
128
|
+
def peer_status(self) -> DiffAction:
|
|
129
|
+
"""Indicate how the peer was changed during this update"""
|
|
130
|
+
if self.peer_id_previous == self.peer_id:
|
|
131
|
+
return DiffAction.UNCHANGED
|
|
132
|
+
if self.peer_id_previous and not self.peer_id:
|
|
133
|
+
return DiffAction.REMOVED
|
|
134
|
+
if self.peer_id and not self.peer_id_previous:
|
|
135
|
+
return DiffAction.ADDED
|
|
136
|
+
return DiffAction.UPDATED
|
|
137
|
+
|
|
138
|
+
def add_property(self, name: str, value_current: bool | str | None, value_previous: bool | str | None) -> None:
|
|
139
|
+
self.properties[name] = PropertyChangelog(name=name, value=value_current, value_previous=value_previous)
|
|
140
|
+
|
|
141
|
+
def set_parent(self, parent_id: str, parent_kind: str) -> None:
|
|
142
|
+
self._parent = ChangelogRelatedNode(node_id=parent_id, node_kind=parent_kind)
|
|
143
|
+
|
|
144
|
+
def set_parent_from_relationship(self, rel_kind: RelationshipKind) -> None:
|
|
145
|
+
if rel_kind == RelationshipKind.PARENT:
|
|
146
|
+
if (
|
|
147
|
+
self.peer_status in [DiffAction.ADDED, DiffAction.UNCHANGED, DiffAction.UPDATED]
|
|
148
|
+
and self.peer_id
|
|
149
|
+
and self.peer_kind
|
|
150
|
+
):
|
|
151
|
+
self._parent = ChangelogRelatedNode(node_id=self.peer_id, node_kind=self.peer_kind)
|
|
152
|
+
elif self.peer_id_previous and self.peer_kind_previous:
|
|
153
|
+
self._parent = ChangelogRelatedNode(node_id=self.peer_id_previous, node_kind=self.peer_kind_previous)
|
|
154
|
+
|
|
155
|
+
@property
|
|
156
|
+
def is_empty(self) -> bool:
|
|
157
|
+
return self.peer_status == DiffAction.UNCHANGED and not self.properties
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
class RelationshipPeerChangelog(BaseModel):
|
|
161
|
+
peer_id: str = Field(..., description="The ID of the peer")
|
|
162
|
+
peer_kind: str = Field(..., description="The node kind of the peer")
|
|
163
|
+
peer_status: DiffAction = Field(
|
|
164
|
+
..., description="Indicate how the relationship to this peer was changed in this update"
|
|
165
|
+
)
|
|
166
|
+
properties: dict[str, PropertyChangelog] = Field(
|
|
167
|
+
default_factory=dict, description="Changes to properties of this relationship if any were made"
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
def add_property(self, name: str, value_current: bool | str | None, value_previous: bool | str | None) -> None:
|
|
171
|
+
self.properties[name] = PropertyChangelog(name=name, value=value_current, value_previous=value_previous)
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
class RelationshipCardinalityManyChangelog(BaseModel):
|
|
175
|
+
name: str
|
|
176
|
+
peers: list[RelationshipPeerChangelog] = Field(default_factory=list)
|
|
177
|
+
|
|
178
|
+
@computed_field
|
|
179
|
+
def cardinality(self) -> str:
|
|
180
|
+
return "many"
|
|
181
|
+
|
|
182
|
+
def add_new_peer(self, relationship: Relationship) -> None:
|
|
183
|
+
properties: dict[str, PropertyChangelog] = {}
|
|
184
|
+
properties["is_protected"] = PropertyChangelog(
|
|
185
|
+
name="is_protected", value=relationship.is_protected, value_previous=None
|
|
186
|
+
)
|
|
187
|
+
properties["is_visible"] = PropertyChangelog(
|
|
188
|
+
name="is_visible", value=relationship.is_protected, value_previous=None
|
|
189
|
+
)
|
|
190
|
+
if owner := getattr(relationship, "owner_id", None):
|
|
191
|
+
properties["owner"] = PropertyChangelog(name="owner", value=owner, value_previous=None)
|
|
192
|
+
if source := getattr(relationship, "source_id", None):
|
|
193
|
+
properties["source"] = PropertyChangelog(name="source_id", value=source, value_previous=None)
|
|
194
|
+
|
|
195
|
+
self.peers.append(
|
|
196
|
+
RelationshipPeerChangelog(
|
|
197
|
+
peer_id=relationship.get_peer_id(),
|
|
198
|
+
peer_kind=relationship.get_peer_kind(),
|
|
199
|
+
peer_status=DiffAction.ADDED,
|
|
200
|
+
properties=properties,
|
|
201
|
+
)
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
def remove_peer(self, peer_id: str, peer_kind: str) -> None:
|
|
205
|
+
self.peers.append(
|
|
206
|
+
RelationshipPeerChangelog(
|
|
207
|
+
peer_id=peer_id,
|
|
208
|
+
peer_kind=peer_kind,
|
|
209
|
+
peer_status=DiffAction.REMOVED,
|
|
210
|
+
)
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
@property
|
|
214
|
+
def is_empty(self) -> bool:
|
|
215
|
+
return not self.peers
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
class ChangelogRelatedNode(BaseModel):
|
|
219
|
+
node_id: str
|
|
220
|
+
node_kind: str
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
class NodeChangelog(BaseModel):
|
|
224
|
+
"""Emitted when a node is updated"""
|
|
225
|
+
|
|
226
|
+
node_id: str
|
|
227
|
+
node_kind: str
|
|
228
|
+
display_label: str
|
|
229
|
+
|
|
230
|
+
attributes: dict[str, AttributeChangelog] = Field(default_factory=dict)
|
|
231
|
+
relationships: dict[str, RelationshipCardinalityOneChangelog | RelationshipCardinalityManyChangelog] = Field(
|
|
232
|
+
default_factory=dict
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
_parent: ChangelogRelatedNode | None = PrivateAttr(default=None)
|
|
236
|
+
|
|
237
|
+
@property
|
|
238
|
+
def parent(self) -> ChangelogRelatedNode | None:
|
|
239
|
+
return self._parent
|
|
240
|
+
|
|
241
|
+
@property
|
|
242
|
+
def updated_fields(self) -> list[str]:
|
|
243
|
+
"""Return a list of update fields i.e. attributes and relationships"""
|
|
244
|
+
return list(self.relationships.keys()) + list(self.attributes.keys())
|
|
245
|
+
|
|
246
|
+
@property
|
|
247
|
+
def has_changes(self) -> bool:
|
|
248
|
+
return len(self.updated_fields) > 0
|
|
249
|
+
|
|
250
|
+
@property
|
|
251
|
+
def root_node_id(self) -> str:
|
|
252
|
+
"""Return the top level node_id"""
|
|
253
|
+
if self.parent:
|
|
254
|
+
return self.parent.node_id
|
|
255
|
+
return self.node_id
|
|
256
|
+
|
|
257
|
+
def add_parent(self, parent: ChangelogRelatedNode) -> None:
|
|
258
|
+
self._parent = parent
|
|
259
|
+
|
|
260
|
+
def add_parent_from_relationship(self, parent: Relationship) -> None:
|
|
261
|
+
self._parent = ChangelogRelatedNode(node_id=parent.get_peer_id(), node_kind=parent.get_peer_kind())
|
|
262
|
+
|
|
263
|
+
def create_relationship(self, relationship: Relationship) -> None:
|
|
264
|
+
if relationship.schema.cardinality == RelationshipCardinality.ONE:
|
|
265
|
+
peer_id = relationship.get_peer_id()
|
|
266
|
+
peer_kind = relationship.get_peer_kind()
|
|
267
|
+
if relationship.schema.kind == RelationshipKind.PARENT:
|
|
268
|
+
self._parent = ChangelogRelatedNode(node_id=peer_id, node_kind=peer_kind)
|
|
269
|
+
changelog_relationship = RelationshipCardinalityOneChangelog(
|
|
270
|
+
name=relationship.schema.name,
|
|
271
|
+
peer_id=peer_id,
|
|
272
|
+
peer_kind=peer_kind,
|
|
273
|
+
)
|
|
274
|
+
if source_id := getattr(relationship, "source_id", None):
|
|
275
|
+
changelog_relationship.add_property(name="source", value_current=source_id, value_previous=None)
|
|
276
|
+
if owner_id := getattr(relationship, "owner_id", None):
|
|
277
|
+
changelog_relationship.add_property(name="owner", value_current=owner_id, value_previous=None)
|
|
278
|
+
changelog_relationship.add_property(
|
|
279
|
+
name="is_protected", value_current=relationship.is_protected, value_previous=None
|
|
280
|
+
)
|
|
281
|
+
changelog_relationship.add_property(
|
|
282
|
+
name="is_visible", value_current=relationship.is_visible, value_previous=None
|
|
283
|
+
)
|
|
284
|
+
self.relationships[changelog_relationship.name] = changelog_relationship
|
|
285
|
+
elif relationship.schema.cardinality == RelationshipCardinality.MANY:
|
|
286
|
+
if relationship.schema.name not in self.relationships:
|
|
287
|
+
self.relationships[relationship.schema.name] = RelationshipCardinalityManyChangelog(
|
|
288
|
+
name=relationship.schema.name
|
|
289
|
+
)
|
|
290
|
+
relationship_container = cast(
|
|
291
|
+
RelationshipCardinalityManyChangelog, self.relationships[relationship.schema.name]
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
relationship_container.add_new_peer(relationship=relationship)
|
|
295
|
+
|
|
296
|
+
def delete_relationship(self, relationship: Relationship) -> None:
|
|
297
|
+
if relationship.schema.cardinality == RelationshipCardinality.ONE:
|
|
298
|
+
peer_id = relationship.get_peer_id()
|
|
299
|
+
peer_kind = relationship.get_peer_kind()
|
|
300
|
+
changelog_relationship = RelationshipCardinalityOneChangelog(
|
|
301
|
+
name=relationship.schema.name,
|
|
302
|
+
peer_id_previous=peer_id,
|
|
303
|
+
peer_kind_previous=peer_kind,
|
|
304
|
+
)
|
|
305
|
+
self.relationships[changelog_relationship.name] = changelog_relationship
|
|
306
|
+
elif relationship.schema.cardinality == RelationshipCardinality.MANY:
|
|
307
|
+
if relationship.schema.name not in self.relationships:
|
|
308
|
+
self.relationships[relationship.schema.name] = RelationshipCardinalityManyChangelog(
|
|
309
|
+
name=relationship.schema.name
|
|
310
|
+
)
|
|
311
|
+
relationship_container = cast(
|
|
312
|
+
RelationshipCardinalityManyChangelog, self.relationships[relationship.schema.name]
|
|
313
|
+
)
|
|
314
|
+
relationship_container.remove_peer(
|
|
315
|
+
peer_id=relationship.get_peer_id(), peer_kind=relationship.get_peer_kind()
|
|
316
|
+
)
|
|
317
|
+
|
|
318
|
+
def add_attribute(self, attribute: AttributeChangelog) -> None:
|
|
319
|
+
if attribute.has_updates:
|
|
320
|
+
self.attributes[attribute.name] = attribute
|
|
321
|
+
|
|
322
|
+
def add_relationship(
|
|
323
|
+
self, relationship_changelog: RelationshipCardinalityOneChangelog | RelationshipCardinalityManyChangelog
|
|
324
|
+
) -> None:
|
|
325
|
+
if isinstance(relationship_changelog, RelationshipCardinalityOneChangelog) and relationship_changelog.parent:
|
|
326
|
+
self.add_parent(parent=relationship_changelog.parent)
|
|
327
|
+
if relationship_changelog.is_empty:
|
|
328
|
+
return
|
|
329
|
+
|
|
330
|
+
self.relationships[relationship_changelog.name] = relationship_changelog
|
|
331
|
+
|
|
332
|
+
def create_attribute(self, attribute: BaseAttribute) -> None:
|
|
333
|
+
changelog_attribute = AttributeChangelog(
|
|
334
|
+
name=attribute.name, value=attribute.value, value_previous=None, kind=attribute.schema.kind
|
|
335
|
+
)
|
|
336
|
+
if source_id := getattr(attribute, "source_id", None):
|
|
337
|
+
changelog_attribute.add_property(name="source", value_current=source_id, value_previous=None)
|
|
338
|
+
if owner_id := getattr(attribute, "owner_id", None):
|
|
339
|
+
changelog_attribute.add_property(name="owner", value_current=owner_id, value_previous=None)
|
|
340
|
+
changelog_attribute.add_property(name="is_protected", value_current=attribute.is_protected, value_previous=None)
|
|
341
|
+
changelog_attribute.add_property(name="is_visible", value_current=attribute.is_visible, value_previous=None)
|
|
342
|
+
self.attributes[changelog_attribute.name] = changelog_attribute
|
|
343
|
+
|
|
344
|
+
def get_related_nodes(self) -> list[ChangelogRelatedNode]:
|
|
345
|
+
related_nodes: dict[str, ChangelogRelatedNode] = {}
|
|
346
|
+
for relationship in self.relationships.values():
|
|
347
|
+
if isinstance(relationship, RelationshipCardinalityOneChangelog):
|
|
348
|
+
if relationship.peer_id and relationship.peer_kind:
|
|
349
|
+
related_nodes[relationship.peer_id] = ChangelogRelatedNode(
|
|
350
|
+
node_id=relationship.peer_id, node_kind=relationship.peer_kind
|
|
351
|
+
)
|
|
352
|
+
if relationship.peer_id_previous and relationship.peer_kind_previous:
|
|
353
|
+
related_nodes[relationship.peer_id_previous] = ChangelogRelatedNode(
|
|
354
|
+
node_id=relationship.peer_id_previous, node_kind=relationship.peer_kind_previous
|
|
355
|
+
)
|
|
356
|
+
elif isinstance(relationship, RelationshipCardinalityManyChangelog):
|
|
357
|
+
for peer in relationship.peers:
|
|
358
|
+
related_nodes[peer.peer_id] = ChangelogRelatedNode(node_id=peer.peer_id, node_kind=peer.peer_kind)
|
|
359
|
+
|
|
360
|
+
if self.parent:
|
|
361
|
+
related_nodes[self.parent.node_id] = self.parent
|
|
362
|
+
|
|
363
|
+
return list(related_nodes.values())
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
class ChangelogRelationshipMapper:
|
|
367
|
+
def __init__(self, schema: RelationshipSchema) -> None:
|
|
368
|
+
self.schema = schema
|
|
369
|
+
self._cardinality_one_relationship: RelationshipCardinalityOneChangelog | None = None
|
|
370
|
+
self._cardinality_many_relationship: RelationshipCardinalityManyChangelog | None = None
|
|
371
|
+
|
|
372
|
+
@property
|
|
373
|
+
def cardinality_one_relationship(self) -> RelationshipCardinalityOneChangelog:
|
|
374
|
+
if not self._cardinality_one_relationship:
|
|
375
|
+
self._cardinality_one_relationship = RelationshipCardinalityOneChangelog(name=self.schema.name)
|
|
376
|
+
|
|
377
|
+
return self._cardinality_one_relationship
|
|
378
|
+
|
|
379
|
+
@property
|
|
380
|
+
def cardinality_many_relationship(self) -> RelationshipCardinalityManyChangelog:
|
|
381
|
+
if not self._cardinality_many_relationship:
|
|
382
|
+
self._cardinality_many_relationship = RelationshipCardinalityManyChangelog(name=self.schema.name)
|
|
383
|
+
|
|
384
|
+
return self._cardinality_many_relationship
|
|
385
|
+
|
|
386
|
+
def remove_peer(self, peer_data: RelationshipPeerData) -> None:
|
|
387
|
+
if self.schema.cardinality == RelationshipCardinality.ONE:
|
|
388
|
+
self.cardinality_one_relationship.peer_id_previous = str(peer_data.peer_id)
|
|
389
|
+
self.cardinality_one_relationship.peer_kind_previous = peer_data.peer_kind
|
|
390
|
+
elif self.schema.cardinality == RelationshipCardinality.MANY:
|
|
391
|
+
self.cardinality_many_relationship.remove_peer(
|
|
392
|
+
peer_id=str(peer_data.peer_id), peer_kind=peer_data.peer_kind
|
|
393
|
+
)
|
|
394
|
+
|
|
395
|
+
def _set_cardinality_one_peer(self, relationship: Relationship) -> None:
|
|
396
|
+
self.cardinality_one_relationship.peer_id = relationship.peer_id
|
|
397
|
+
self.cardinality_one_relationship.peer_kind = relationship.get_peer_kind()
|
|
398
|
+
self.cardinality_one_relationship.set_parent_from_relationship(rel_kind=relationship.schema.kind)
|
|
399
|
+
|
|
400
|
+
def add_parent_from_relationship(self, relationship: Relationship) -> None:
|
|
401
|
+
if self.schema.cardinality == RelationshipCardinality.ONE:
|
|
402
|
+
self.cardinality_one_relationship.set_parent(
|
|
403
|
+
parent_id=relationship.get_peer_id(), parent_kind=relationship.get_peer_kind()
|
|
404
|
+
)
|
|
405
|
+
|
|
406
|
+
def add_peer_from_relationship(self, relationship: Relationship) -> None:
|
|
407
|
+
if self.schema.cardinality == RelationshipCardinality.ONE:
|
|
408
|
+
self._set_cardinality_one_peer(relationship=relationship)
|
|
409
|
+
elif self.schema.cardinality == RelationshipCardinality.MANY:
|
|
410
|
+
self.cardinality_many_relationship.add_new_peer(relationship=relationship)
|
|
411
|
+
|
|
412
|
+
def add_updated_relationship(
|
|
413
|
+
self, relationship: Relationship, old_data: RelationshipPeerData, properties_to_update: list[str]
|
|
414
|
+
) -> None:
|
|
415
|
+
if self.schema.cardinality == RelationshipCardinality.ONE:
|
|
416
|
+
self._set_cardinality_one_peer(relationship=relationship)
|
|
417
|
+
self.cardinality_one_relationship.peer_id_previous = self.cardinality_one_relationship.peer_id
|
|
418
|
+
self.cardinality_one_relationship.peer_kind_previous = self.cardinality_one_relationship.peer_kind
|
|
419
|
+
for property_to_update in properties_to_update:
|
|
420
|
+
previous_property = old_data.properties.get(property_to_update)
|
|
421
|
+
previous_value: str | bool | None = None
|
|
422
|
+
if previous_property:
|
|
423
|
+
if isinstance(previous_property.value, UUID):
|
|
424
|
+
previous_value = str(previous_property.value)
|
|
425
|
+
else:
|
|
426
|
+
previous_value = previous_property.value
|
|
427
|
+
property_name = (
|
|
428
|
+
property_to_update if property_to_update not in ["source", "owner"] else f"{property_to_update}_id"
|
|
429
|
+
)
|
|
430
|
+
self.cardinality_one_relationship.add_property(
|
|
431
|
+
name=property_to_update,
|
|
432
|
+
value_current=getattr(relationship, property_name),
|
|
433
|
+
value_previous=previous_value,
|
|
434
|
+
)
|
|
435
|
+
self.cardinality_one_relationship.set_parent_from_relationship(rel_kind=relationship.schema.kind)
|
|
436
|
+
|
|
437
|
+
def delete_relationship(self, peer_id: str, peer_kind: str, rel_schema: RelationshipSchema) -> None:
|
|
438
|
+
if self.schema.cardinality == RelationshipCardinality.ONE:
|
|
439
|
+
self.cardinality_one_relationship.peer_id_previous = peer_id
|
|
440
|
+
self.cardinality_one_relationship.peer_kind_previous = peer_kind
|
|
441
|
+
self.cardinality_one_relationship.set_parent_from_relationship(rel_kind=rel_schema.kind)
|
|
442
|
+
|
|
443
|
+
elif self.schema.cardinality == RelationshipCardinality.MANY:
|
|
444
|
+
self.cardinality_many_relationship.remove_peer(peer_id=peer_id, peer_kind=peer_kind)
|
|
445
|
+
|
|
446
|
+
@property
|
|
447
|
+
def changelog(self) -> RelationshipCardinalityOneChangelog | RelationshipCardinalityManyChangelog:
|
|
448
|
+
match self.schema.cardinality:
|
|
449
|
+
case RelationshipCardinality.ONE:
|
|
450
|
+
return self.cardinality_one_relationship
|
|
451
|
+
case RelationshipCardinality.MANY:
|
|
452
|
+
return self.cardinality_many_relationship
|
|
453
|
+
|
|
454
|
+
|
|
455
|
+
class RelationshipChangelogGetter:
|
|
456
|
+
def __init__(self, db: InfrahubDatabase, branch: Branch) -> None:
|
|
457
|
+
self._db = db
|
|
458
|
+
self._branch = branch
|
|
459
|
+
|
|
460
|
+
async def get_changelogs(self, primary_changelog: NodeChangelog) -> list[NodeChangelog]:
|
|
461
|
+
"""Return secondary changelogs based on this update
|
|
462
|
+
|
|
463
|
+
These will typically include updates to relationships on other nodes.
|
|
464
|
+
"""
|
|
465
|
+
schema_branch = self._db.schema.get_schema_branch(name=self._branch.name)
|
|
466
|
+
node_schema = schema_branch.get(name=primary_changelog.node_kind)
|
|
467
|
+
secondaries: list[NodeChangelog] = []
|
|
468
|
+
|
|
469
|
+
for relationship in primary_changelog.relationships.values():
|
|
470
|
+
rel_schema = node_schema.get_relationship(name=relationship.name)
|
|
471
|
+
if isinstance(relationship, RelationshipCardinalityOneChangelog):
|
|
472
|
+
# For now this code only looks at the scenario when a cardinality=one relationship
|
|
473
|
+
# is added to a node and it has a cardinality=many relationship coming back from
|
|
474
|
+
# another node, it will be expanded to include all variations.
|
|
475
|
+
if relationship.peer_status == DiffAction.ADDED:
|
|
476
|
+
peer_schema = schema_branch.get(name=str(relationship.peer_kind))
|
|
477
|
+
peer_relation = peer_schema.get_relationship_by_identifier(
|
|
478
|
+
id=str(rel_schema.identifier), raise_on_error=False
|
|
479
|
+
)
|
|
480
|
+
if peer_relation:
|
|
481
|
+
node_changelog = NodeChangelog(
|
|
482
|
+
node_id=str(relationship.peer_id),
|
|
483
|
+
node_kind=str(relationship.peer_kind),
|
|
484
|
+
display_label="n/a",
|
|
485
|
+
)
|
|
486
|
+
if peer_relation.cardinality == RelationshipCardinality.MANY:
|
|
487
|
+
node_changelog.relationships[peer_relation.name] = RelationshipCardinalityManyChangelog(
|
|
488
|
+
name=peer_relation.name,
|
|
489
|
+
peers=[
|
|
490
|
+
RelationshipPeerChangelog(
|
|
491
|
+
peer_id=primary_changelog.node_id,
|
|
492
|
+
peer_kind=primary_changelog.node_kind,
|
|
493
|
+
peer_status=DiffAction.ADDED,
|
|
494
|
+
)
|
|
495
|
+
],
|
|
496
|
+
)
|
|
497
|
+
secondaries.append(node_changelog)
|
|
498
|
+
|
|
499
|
+
return secondaries
|
|
@@ -43,6 +43,29 @@ RESERVED_ATTR_GEN_NAMES = ["type"]
|
|
|
43
43
|
|
|
44
44
|
NULL_VALUE = "NULL"
|
|
45
45
|
|
|
46
|
+
EVENT_NAMESPACE = "infrahub"
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class EventType(InfrahubStringEnum):
|
|
50
|
+
BRANCH_CREATED = f"{EVENT_NAMESPACE}.branch.created"
|
|
51
|
+
BRANCH_DELETED = f"{EVENT_NAMESPACE}.branch.deleted"
|
|
52
|
+
BRANCH_MERGED = f"{EVENT_NAMESPACE}.branch.merged"
|
|
53
|
+
BRANCH_REBASED = f"{EVENT_NAMESPACE}.branch.rebased"
|
|
54
|
+
|
|
55
|
+
SCHEMA_UPDATED = f"{EVENT_NAMESPACE}.schema.update"
|
|
56
|
+
|
|
57
|
+
NODE_CREATED = f"{EVENT_NAMESPACE}.node.created"
|
|
58
|
+
NODE_UPDATED = f"{EVENT_NAMESPACE}.node.updated"
|
|
59
|
+
NODE_DELETED = f"{EVENT_NAMESPACE}.node.deleted"
|
|
60
|
+
|
|
61
|
+
GROUP_MEMBER_ADDED = f"{EVENT_NAMESPACE}.group.member_added"
|
|
62
|
+
GROUP_MEMBER_REMOVED = f"{EVENT_NAMESPACE}.group.member_removed"
|
|
63
|
+
|
|
64
|
+
REPOSITORY_UPDATE_COMMIT = f"{EVENT_NAMESPACE}.repository.update_commit"
|
|
65
|
+
|
|
66
|
+
ARTIFACT_CREATED = f"{EVENT_NAMESPACE}.artifact.created"
|
|
67
|
+
ARTIFACT_UPDATED = f"{EVENT_NAMESPACE}.artifact.updated"
|
|
68
|
+
|
|
46
69
|
|
|
47
70
|
class PermissionLevel(enum.Flag):
|
|
48
71
|
READ = 1
|
|
@@ -60,6 +83,7 @@ class GlobalPermissions(InfrahubStringEnum):
|
|
|
60
83
|
MANAGE_ACCOUNTS = "manage_accounts"
|
|
61
84
|
MANAGE_PERMISSIONS = "manage_permissions"
|
|
62
85
|
MANAGE_REPOSITORIES = "manage_repositories"
|
|
86
|
+
OVERRIDE_CONTEXT = "override_context"
|
|
63
87
|
|
|
64
88
|
|
|
65
89
|
class PermissionAction(InfrahubStringEnum):
|
|
@@ -175,11 +199,23 @@ class GeneratorInstanceStatus(InfrahubStringEnum):
|
|
|
175
199
|
|
|
176
200
|
|
|
177
201
|
class MutationAction(InfrahubStringEnum):
|
|
178
|
-
|
|
179
|
-
|
|
202
|
+
CREATED = "created"
|
|
203
|
+
DELETED = "deleted"
|
|
180
204
|
UPDATED = "updated"
|
|
181
205
|
UNDEFINED = "undefined"
|
|
182
206
|
|
|
207
|
+
@classmethod
|
|
208
|
+
def from_diff_action(cls, diff_action: DiffAction) -> MutationAction:
|
|
209
|
+
match diff_action:
|
|
210
|
+
case DiffAction.ADDED:
|
|
211
|
+
return MutationAction.CREATED
|
|
212
|
+
case DiffAction.REMOVED:
|
|
213
|
+
return MutationAction.DELETED
|
|
214
|
+
case DiffAction.UPDATED:
|
|
215
|
+
return MutationAction.UPDATED
|
|
216
|
+
case DiffAction.UNCHANGED:
|
|
217
|
+
return MutationAction.UNDEFINED
|
|
218
|
+
|
|
183
219
|
|
|
184
220
|
class PathResourceType(InfrahubStringEnum):
|
|
185
221
|
SCHEMA = "schema"
|
|
@@ -225,6 +261,7 @@ class RelationshipKind(InfrahubStringEnum):
|
|
|
225
261
|
GROUP = "Group"
|
|
226
262
|
HIERARCHY = "Hierarchy"
|
|
227
263
|
PROFILE = "Profile"
|
|
264
|
+
TEMPLATE = "Template"
|
|
228
265
|
|
|
229
266
|
|
|
230
267
|
class RelationshipStatus(InfrahubStringEnum):
|
|
@@ -301,6 +338,7 @@ RESTRICTED_NAMESPACES: list[str] = [
|
|
|
301
338
|
"Lineage",
|
|
302
339
|
"Schema",
|
|
303
340
|
"Profile",
|
|
341
|
+
"Template",
|
|
304
342
|
]
|
|
305
343
|
|
|
306
344
|
NODE_NAME_REGEX = r"^[A-Z][a-zA-Z0-9]+$"
|
|
@@ -315,3 +353,6 @@ DEFAULT_KIND_MAX_LENGTH = 32
|
|
|
315
353
|
NAMESPACE_REGEX = r"^[A-Z][a-z0-9]+$"
|
|
316
354
|
NODE_KIND_REGEX = r"^[A-Z][a-zA-Z0-9]+$"
|
|
317
355
|
DEFAULT_REL_IDENTIFIER_LENGTH = 128
|
|
356
|
+
|
|
357
|
+
OBJECT_TEMPLATE_RELATIONSHIP_NAME = "object_template"
|
|
358
|
+
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
|
@@ -435,7 +435,7 @@ class DiffCombiner:
|
|
|
435
435
|
nodes=combined_nodes,
|
|
436
436
|
)
|
|
437
437
|
)
|
|
438
|
-
base_branch_diff, diff_branch_diff = combined_diffs
|
|
438
|
+
base_branch_diff, diff_branch_diff = combined_diffs
|
|
439
439
|
base_branch_diff.partner_uuid = diff_branch_diff.uuid
|
|
440
440
|
diff_branch_diff.partner_uuid = base_branch_diff.uuid
|
|
441
441
|
return EnrichedDiffs(
|
|
@@ -3,6 +3,7 @@ from typing import TYPE_CHECKING, Any
|
|
|
3
3
|
from infrahub.core.constants import NULL_VALUE, DiffAction, RelationshipCardinality
|
|
4
4
|
from infrahub.core.constants.database import DatabaseEdgeType
|
|
5
5
|
from infrahub.database import InfrahubDatabase
|
|
6
|
+
from infrahub.log import get_logger
|
|
6
7
|
|
|
7
8
|
from ..model.path import (
|
|
8
9
|
CalculatedDiffs,
|
|
@@ -16,6 +17,8 @@ from .interface import DiffEnricherInterface
|
|
|
16
17
|
if TYPE_CHECKING:
|
|
17
18
|
from infrahub.core.schema import MainSchemaTypes
|
|
18
19
|
|
|
20
|
+
log = get_logger()
|
|
21
|
+
|
|
19
22
|
|
|
20
23
|
class DiffCardinalityOneEnricher(DiffEnricherInterface):
|
|
21
24
|
"""Clean up diffs for cardinality=one relationships to make them cleaner and more intuitive
|
|
@@ -32,8 +35,9 @@ class DiffCardinalityOneEnricher(DiffEnricherInterface):
|
|
|
32
35
|
self.db = db
|
|
33
36
|
self._node_schema_map: dict[str, MainSchemaTypes] = {}
|
|
34
37
|
|
|
35
|
-
async def enrich(self, enriched_diff_root: EnrichedDiffRoot, calculated_diffs: CalculatedDiffs) -> None:
|
|
38
|
+
async def enrich(self, enriched_diff_root: EnrichedDiffRoot, calculated_diffs: CalculatedDiffs) -> None: # noqa: ARG002
|
|
36
39
|
self._node_schema_map = {}
|
|
40
|
+
log.info("Beginning cardinality-one diff enrichment...")
|
|
37
41
|
for diff_node in enriched_diff_root.nodes:
|
|
38
42
|
for relationship_group in diff_node.relationships:
|
|
39
43
|
if (
|
|
@@ -41,6 +45,7 @@ class DiffCardinalityOneEnricher(DiffEnricherInterface):
|
|
|
41
45
|
and len(relationship_group.relationships) > 0
|
|
42
46
|
):
|
|
43
47
|
self.consolidate_cardinality_one_diff_elements(diff_relationship=relationship_group)
|
|
48
|
+
log.info("Cardinality-one diff enrichment complete.")
|
|
44
49
|
|
|
45
50
|
def _determine_action(self, previous_value: Any, new_value: Any) -> DiffAction:
|
|
46
51
|
if previous_value == new_value:
|