omnibase_infra 0.2.6__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.
- omnibase_infra/__init__.py +101 -0
- omnibase_infra/adapters/adapter_onex_tool_execution.py +451 -0
- omnibase_infra/capabilities/__init__.py +15 -0
- omnibase_infra/capabilities/capability_inference_rules.py +211 -0
- omnibase_infra/capabilities/contract_capability_extractor.py +221 -0
- omnibase_infra/capabilities/intent_type_extractor.py +160 -0
- omnibase_infra/cli/__init__.py +1 -0
- omnibase_infra/cli/commands.py +216 -0
- omnibase_infra/clients/__init__.py +0 -0
- omnibase_infra/configs/widget_mapping.yaml +176 -0
- omnibase_infra/constants_topic_patterns.py +26 -0
- omnibase_infra/contracts/handlers/filesystem/handler_contract.yaml +264 -0
- omnibase_infra/contracts/handlers/mcp/handler_contract.yaml +141 -0
- omnibase_infra/decorators/__init__.py +29 -0
- omnibase_infra/decorators/allow_any.py +109 -0
- omnibase_infra/dlq/__init__.py +90 -0
- omnibase_infra/dlq/constants_dlq.py +57 -0
- omnibase_infra/dlq/models/__init__.py +26 -0
- omnibase_infra/dlq/models/enum_replay_status.py +37 -0
- omnibase_infra/dlq/models/model_dlq_replay_record.py +135 -0
- omnibase_infra/dlq/models/model_dlq_tracking_config.py +184 -0
- omnibase_infra/dlq/service_dlq_tracking.py +611 -0
- omnibase_infra/enums/__init__.py +132 -0
- omnibase_infra/enums/enum_any_type_violation.py +104 -0
- omnibase_infra/enums/enum_backend_type.py +27 -0
- omnibase_infra/enums/enum_capture_outcome.py +42 -0
- omnibase_infra/enums/enum_capture_state.py +88 -0
- omnibase_infra/enums/enum_chain_violation_type.py +119 -0
- omnibase_infra/enums/enum_circuit_state.py +51 -0
- omnibase_infra/enums/enum_confirmation_event_type.py +27 -0
- omnibase_infra/enums/enum_consumer_group_purpose.py +92 -0
- omnibase_infra/enums/enum_contract_type.py +84 -0
- omnibase_infra/enums/enum_dedupe_strategy.py +46 -0
- omnibase_infra/enums/enum_dispatch_status.py +191 -0
- omnibase_infra/enums/enum_environment.py +46 -0
- omnibase_infra/enums/enum_execution_shape_violation.py +103 -0
- omnibase_infra/enums/enum_handler_error_type.py +111 -0
- omnibase_infra/enums/enum_handler_loader_error.py +178 -0
- omnibase_infra/enums/enum_handler_source_mode.py +86 -0
- omnibase_infra/enums/enum_handler_source_type.py +87 -0
- omnibase_infra/enums/enum_handler_type.py +77 -0
- omnibase_infra/enums/enum_handler_type_category.py +61 -0
- omnibase_infra/enums/enum_infra_transport_type.py +73 -0
- omnibase_infra/enums/enum_introspection_reason.py +154 -0
- omnibase_infra/enums/enum_kafka_acks.py +99 -0
- omnibase_infra/enums/enum_message_category.py +213 -0
- omnibase_infra/enums/enum_node_archetype.py +74 -0
- omnibase_infra/enums/enum_node_output_type.py +185 -0
- omnibase_infra/enums/enum_non_retryable_error_category.py +224 -0
- omnibase_infra/enums/enum_policy_type.py +32 -0
- omnibase_infra/enums/enum_registration_state.py +261 -0
- omnibase_infra/enums/enum_registration_status.py +33 -0
- omnibase_infra/enums/enum_registry_response_status.py +28 -0
- omnibase_infra/enums/enum_response_status.py +26 -0
- omnibase_infra/enums/enum_retry_error_category.py +98 -0
- omnibase_infra/enums/enum_security_rule_id.py +103 -0
- omnibase_infra/enums/enum_selection_strategy.py +91 -0
- omnibase_infra/enums/enum_topic_standard.py +42 -0
- omnibase_infra/enums/enum_validation_severity.py +78 -0
- omnibase_infra/errors/__init__.py +160 -0
- omnibase_infra/errors/error_architecture_violation.py +152 -0
- omnibase_infra/errors/error_binding_resolution.py +128 -0
- omnibase_infra/errors/error_chain_propagation.py +188 -0
- omnibase_infra/errors/error_compute_registry.py +95 -0
- omnibase_infra/errors/error_consul.py +132 -0
- omnibase_infra/errors/error_container_wiring.py +243 -0
- omnibase_infra/errors/error_event_bus_registry.py +105 -0
- omnibase_infra/errors/error_infra.py +610 -0
- omnibase_infra/errors/error_message_type_registry.py +101 -0
- omnibase_infra/errors/error_policy_registry.py +115 -0
- omnibase_infra/errors/error_vault.py +123 -0
- omnibase_infra/event_bus/__init__.py +72 -0
- omnibase_infra/event_bus/configs/kafka_event_bus_config.yaml +84 -0
- omnibase_infra/event_bus/event_bus_inmemory.py +797 -0
- omnibase_infra/event_bus/event_bus_kafka.py +1716 -0
- omnibase_infra/event_bus/mixin_kafka_broadcast.py +180 -0
- omnibase_infra/event_bus/mixin_kafka_dlq.py +771 -0
- omnibase_infra/event_bus/models/__init__.py +29 -0
- omnibase_infra/event_bus/models/config/__init__.py +20 -0
- omnibase_infra/event_bus/models/config/model_kafka_event_bus_config.py +693 -0
- omnibase_infra/event_bus/models/model_dlq_event.py +206 -0
- omnibase_infra/event_bus/models/model_dlq_metrics.py +304 -0
- omnibase_infra/event_bus/models/model_event_headers.py +115 -0
- omnibase_infra/event_bus/models/model_event_message.py +60 -0
- omnibase_infra/event_bus/testing/__init__.py +26 -0
- omnibase_infra/event_bus/testing/adapter_protocol_event_publisher_inmemory.py +418 -0
- omnibase_infra/event_bus/testing/model_publisher_metrics.py +64 -0
- omnibase_infra/event_bus/topic_constants.py +376 -0
- omnibase_infra/handlers/__init__.py +82 -0
- omnibase_infra/handlers/filesystem/__init__.py +48 -0
- omnibase_infra/handlers/filesystem/enum_file_system_operation.py +35 -0
- omnibase_infra/handlers/filesystem/model_file_system_request.py +298 -0
- omnibase_infra/handlers/filesystem/model_file_system_result.py +166 -0
- omnibase_infra/handlers/handler_consul.py +795 -0
- omnibase_infra/handlers/handler_db.py +1046 -0
- omnibase_infra/handlers/handler_filesystem.py +1478 -0
- omnibase_infra/handlers/handler_graph.py +2015 -0
- omnibase_infra/handlers/handler_http.py +926 -0
- omnibase_infra/handlers/handler_intent.py +387 -0
- omnibase_infra/handlers/handler_manifest_persistence.contract.yaml +184 -0
- omnibase_infra/handlers/handler_manifest_persistence.py +1539 -0
- omnibase_infra/handlers/handler_mcp.py +1430 -0
- omnibase_infra/handlers/handler_qdrant.py +1076 -0
- omnibase_infra/handlers/handler_vault.py +428 -0
- omnibase_infra/handlers/mcp/__init__.py +19 -0
- omnibase_infra/handlers/mcp/adapter_onex_to_mcp.py +446 -0
- omnibase_infra/handlers/mcp/protocols.py +178 -0
- omnibase_infra/handlers/mcp/transport_streamable_http.py +352 -0
- omnibase_infra/handlers/mixins/__init__.py +47 -0
- omnibase_infra/handlers/mixins/mixin_consul_initialization.py +349 -0
- omnibase_infra/handlers/mixins/mixin_consul_kv.py +338 -0
- omnibase_infra/handlers/mixins/mixin_consul_service.py +542 -0
- omnibase_infra/handlers/mixins/mixin_consul_topic_index.py +585 -0
- omnibase_infra/handlers/mixins/mixin_vault_initialization.py +338 -0
- omnibase_infra/handlers/mixins/mixin_vault_retry.py +412 -0
- omnibase_infra/handlers/mixins/mixin_vault_secrets.py +450 -0
- omnibase_infra/handlers/mixins/mixin_vault_token.py +365 -0
- omnibase_infra/handlers/models/__init__.py +286 -0
- omnibase_infra/handlers/models/consul/__init__.py +81 -0
- omnibase_infra/handlers/models/consul/enum_consul_operation_type.py +57 -0
- omnibase_infra/handlers/models/consul/model_consul_deregister_payload.py +51 -0
- omnibase_infra/handlers/models/consul/model_consul_handler_config.py +153 -0
- omnibase_infra/handlers/models/consul/model_consul_handler_payload.py +89 -0
- omnibase_infra/handlers/models/consul/model_consul_kv_get_found_payload.py +55 -0
- omnibase_infra/handlers/models/consul/model_consul_kv_get_not_found_payload.py +49 -0
- omnibase_infra/handlers/models/consul/model_consul_kv_get_recurse_payload.py +50 -0
- omnibase_infra/handlers/models/consul/model_consul_kv_item.py +33 -0
- omnibase_infra/handlers/models/consul/model_consul_kv_put_payload.py +41 -0
- omnibase_infra/handlers/models/consul/model_consul_register_payload.py +53 -0
- omnibase_infra/handlers/models/consul/model_consul_retry_config.py +66 -0
- omnibase_infra/handlers/models/consul/model_payload_consul.py +66 -0
- omnibase_infra/handlers/models/consul/registry_payload_consul.py +214 -0
- omnibase_infra/handlers/models/graph/__init__.py +35 -0
- omnibase_infra/handlers/models/graph/enum_graph_operation_type.py +20 -0
- omnibase_infra/handlers/models/graph/model_graph_execute_payload.py +38 -0
- omnibase_infra/handlers/models/graph/model_graph_handler_config.py +54 -0
- omnibase_infra/handlers/models/graph/model_graph_handler_payload.py +44 -0
- omnibase_infra/handlers/models/graph/model_graph_query_payload.py +40 -0
- omnibase_infra/handlers/models/graph/model_graph_record.py +22 -0
- omnibase_infra/handlers/models/http/__init__.py +50 -0
- omnibase_infra/handlers/models/http/enum_http_operation_type.py +29 -0
- omnibase_infra/handlers/models/http/model_http_body_content.py +45 -0
- omnibase_infra/handlers/models/http/model_http_get_payload.py +88 -0
- omnibase_infra/handlers/models/http/model_http_handler_payload.py +90 -0
- omnibase_infra/handlers/models/http/model_http_post_payload.py +88 -0
- omnibase_infra/handlers/models/http/model_payload_http.py +66 -0
- omnibase_infra/handlers/models/http/registry_payload_http.py +212 -0
- omnibase_infra/handlers/models/mcp/__init__.py +23 -0
- omnibase_infra/handlers/models/mcp/enum_mcp_operation_type.py +24 -0
- omnibase_infra/handlers/models/mcp/model_mcp_handler_config.py +40 -0
- omnibase_infra/handlers/models/mcp/model_mcp_tool_call.py +32 -0
- omnibase_infra/handlers/models/mcp/model_mcp_tool_result.py +45 -0
- omnibase_infra/handlers/models/model_consul_handler_response.py +96 -0
- omnibase_infra/handlers/models/model_db_describe_response.py +83 -0
- omnibase_infra/handlers/models/model_db_query_payload.py +95 -0
- omnibase_infra/handlers/models/model_db_query_response.py +60 -0
- omnibase_infra/handlers/models/model_filesystem_config.py +98 -0
- omnibase_infra/handlers/models/model_filesystem_delete_payload.py +54 -0
- omnibase_infra/handlers/models/model_filesystem_delete_result.py +77 -0
- omnibase_infra/handlers/models/model_filesystem_directory_entry.py +75 -0
- omnibase_infra/handlers/models/model_filesystem_ensure_directory_payload.py +54 -0
- omnibase_infra/handlers/models/model_filesystem_ensure_directory_result.py +60 -0
- omnibase_infra/handlers/models/model_filesystem_list_directory_payload.py +60 -0
- omnibase_infra/handlers/models/model_filesystem_list_directory_result.py +68 -0
- omnibase_infra/handlers/models/model_filesystem_read_payload.py +62 -0
- omnibase_infra/handlers/models/model_filesystem_read_result.py +61 -0
- omnibase_infra/handlers/models/model_filesystem_write_payload.py +70 -0
- omnibase_infra/handlers/models/model_filesystem_write_result.py +55 -0
- omnibase_infra/handlers/models/model_graph_handler_response.py +98 -0
- omnibase_infra/handlers/models/model_handler_response.py +103 -0
- omnibase_infra/handlers/models/model_http_handler_response.py +101 -0
- omnibase_infra/handlers/models/model_manifest_metadata.py +75 -0
- omnibase_infra/handlers/models/model_manifest_persistence_config.py +62 -0
- omnibase_infra/handlers/models/model_manifest_query_payload.py +90 -0
- omnibase_infra/handlers/models/model_manifest_query_result.py +97 -0
- omnibase_infra/handlers/models/model_manifest_retrieve_payload.py +44 -0
- omnibase_infra/handlers/models/model_manifest_retrieve_result.py +98 -0
- omnibase_infra/handlers/models/model_manifest_store_payload.py +47 -0
- omnibase_infra/handlers/models/model_manifest_store_result.py +67 -0
- omnibase_infra/handlers/models/model_operation_context.py +187 -0
- omnibase_infra/handlers/models/model_qdrant_handler_response.py +98 -0
- omnibase_infra/handlers/models/model_retry_state.py +162 -0
- omnibase_infra/handlers/models/model_vault_handler_response.py +98 -0
- omnibase_infra/handlers/models/qdrant/__init__.py +44 -0
- omnibase_infra/handlers/models/qdrant/enum_qdrant_operation_type.py +26 -0
- omnibase_infra/handlers/models/qdrant/model_qdrant_collection_payload.py +42 -0
- omnibase_infra/handlers/models/qdrant/model_qdrant_delete_payload.py +36 -0
- omnibase_infra/handlers/models/qdrant/model_qdrant_handler_config.py +42 -0
- omnibase_infra/handlers/models/qdrant/model_qdrant_handler_payload.py +54 -0
- omnibase_infra/handlers/models/qdrant/model_qdrant_search_payload.py +42 -0
- omnibase_infra/handlers/models/qdrant/model_qdrant_search_result.py +30 -0
- omnibase_infra/handlers/models/qdrant/model_qdrant_upsert_payload.py +36 -0
- omnibase_infra/handlers/models/vault/__init__.py +69 -0
- omnibase_infra/handlers/models/vault/enum_vault_operation_type.py +35 -0
- omnibase_infra/handlers/models/vault/model_payload_vault.py +66 -0
- omnibase_infra/handlers/models/vault/model_vault_delete_payload.py +57 -0
- omnibase_infra/handlers/models/vault/model_vault_handler_config.py +148 -0
- omnibase_infra/handlers/models/vault/model_vault_handler_payload.py +101 -0
- omnibase_infra/handlers/models/vault/model_vault_list_payload.py +58 -0
- omnibase_infra/handlers/models/vault/model_vault_renew_token_payload.py +67 -0
- omnibase_infra/handlers/models/vault/model_vault_retry_config.py +66 -0
- omnibase_infra/handlers/models/vault/model_vault_secret_payload.py +106 -0
- omnibase_infra/handlers/models/vault/model_vault_write_payload.py +66 -0
- omnibase_infra/handlers/models/vault/registry_payload_vault.py +213 -0
- omnibase_infra/handlers/registration_storage/__init__.py +43 -0
- omnibase_infra/handlers/registration_storage/handler_registration_storage_mock.py +392 -0
- omnibase_infra/handlers/registration_storage/handler_registration_storage_postgres.py +922 -0
- omnibase_infra/handlers/registration_storage/models/__init__.py +23 -0
- omnibase_infra/handlers/registration_storage/models/model_delete_registration_request.py +58 -0
- omnibase_infra/handlers/registration_storage/models/model_update_registration_request.py +73 -0
- omnibase_infra/handlers/registration_storage/protocol_registration_persistence.py +191 -0
- omnibase_infra/handlers/service_discovery/__init__.py +43 -0
- omnibase_infra/handlers/service_discovery/handler_service_discovery_consul.py +1051 -0
- omnibase_infra/handlers/service_discovery/handler_service_discovery_mock.py +258 -0
- omnibase_infra/handlers/service_discovery/models/__init__.py +22 -0
- omnibase_infra/handlers/service_discovery/models/model_discovery_result.py +64 -0
- omnibase_infra/handlers/service_discovery/models/model_registration_result.py +138 -0
- omnibase_infra/handlers/service_discovery/models/model_service_info.py +109 -0
- omnibase_infra/handlers/service_discovery/protocol_discovery_operations.py +170 -0
- omnibase_infra/idempotency/__init__.py +94 -0
- omnibase_infra/idempotency/models/__init__.py +43 -0
- omnibase_infra/idempotency/models/model_idempotency_check_result.py +85 -0
- omnibase_infra/idempotency/models/model_idempotency_guard_config.py +130 -0
- omnibase_infra/idempotency/models/model_idempotency_record.py +86 -0
- omnibase_infra/idempotency/models/model_idempotency_store_health_check_result.py +81 -0
- omnibase_infra/idempotency/models/model_idempotency_store_metrics.py +140 -0
- omnibase_infra/idempotency/models/model_postgres_idempotency_store_config.py +299 -0
- omnibase_infra/idempotency/protocol_idempotency_store.py +184 -0
- omnibase_infra/idempotency/store_inmemory.py +265 -0
- omnibase_infra/idempotency/store_postgres.py +923 -0
- omnibase_infra/infrastructure/__init__.py +0 -0
- omnibase_infra/migrations/001_create_event_ledger.sql +166 -0
- omnibase_infra/migrations/001_drop_event_ledger.sql +18 -0
- omnibase_infra/mixins/__init__.py +71 -0
- omnibase_infra/mixins/mixin_async_circuit_breaker.py +656 -0
- omnibase_infra/mixins/mixin_dict_like_accessors.py +146 -0
- omnibase_infra/mixins/mixin_envelope_extraction.py +119 -0
- omnibase_infra/mixins/mixin_node_introspection.py +2670 -0
- omnibase_infra/mixins/mixin_retry_execution.py +386 -0
- omnibase_infra/mixins/protocol_circuit_breaker_aware.py +133 -0
- omnibase_infra/models/__init__.py +144 -0
- omnibase_infra/models/bindings/__init__.py +59 -0
- omnibase_infra/models/bindings/constants.py +144 -0
- omnibase_infra/models/bindings/model_binding_resolution_result.py +103 -0
- omnibase_infra/models/bindings/model_operation_binding.py +44 -0
- omnibase_infra/models/bindings/model_operation_bindings_subcontract.py +152 -0
- omnibase_infra/models/bindings/model_parsed_binding.py +52 -0
- omnibase_infra/models/corpus/__init__.py +17 -0
- omnibase_infra/models/corpus/model_capture_config.py +133 -0
- omnibase_infra/models/corpus/model_capture_result.py +86 -0
- omnibase_infra/models/discovery/__init__.py +42 -0
- omnibase_infra/models/discovery/model_dependency_spec.py +319 -0
- omnibase_infra/models/discovery/model_discovered_capabilities.py +50 -0
- omnibase_infra/models/discovery/model_introspection_config.py +330 -0
- omnibase_infra/models/discovery/model_introspection_performance_metrics.py +169 -0
- omnibase_infra/models/discovery/model_introspection_task_config.py +116 -0
- omnibase_infra/models/dispatch/__init__.py +155 -0
- omnibase_infra/models/dispatch/model_debug_trace_snapshot.py +114 -0
- omnibase_infra/models/dispatch/model_dispatch_context.py +439 -0
- omnibase_infra/models/dispatch/model_dispatch_error.py +336 -0
- omnibase_infra/models/dispatch/model_dispatch_log_context.py +400 -0
- omnibase_infra/models/dispatch/model_dispatch_metadata.py +228 -0
- omnibase_infra/models/dispatch/model_dispatch_metrics.py +496 -0
- omnibase_infra/models/dispatch/model_dispatch_outcome.py +317 -0
- omnibase_infra/models/dispatch/model_dispatch_outputs.py +231 -0
- omnibase_infra/models/dispatch/model_dispatch_result.py +436 -0
- omnibase_infra/models/dispatch/model_dispatch_route.py +279 -0
- omnibase_infra/models/dispatch/model_dispatcher_metrics.py +275 -0
- omnibase_infra/models/dispatch/model_dispatcher_registration.py +352 -0
- omnibase_infra/models/dispatch/model_materialized_dispatch.py +141 -0
- omnibase_infra/models/dispatch/model_parsed_topic.py +135 -0
- omnibase_infra/models/dispatch/model_topic_parser.py +725 -0
- omnibase_infra/models/dispatch/model_tracing_context.py +285 -0
- omnibase_infra/models/errors/__init__.py +45 -0
- omnibase_infra/models/errors/model_handler_validation_error.py +594 -0
- omnibase_infra/models/errors/model_infra_error_context.py +99 -0
- omnibase_infra/models/errors/model_message_type_registry_error_context.py +71 -0
- omnibase_infra/models/errors/model_timeout_error_context.py +110 -0
- omnibase_infra/models/handlers/__init__.py +80 -0
- omnibase_infra/models/handlers/model_bootstrap_handler_descriptor.py +162 -0
- omnibase_infra/models/handlers/model_contract_discovery_result.py +82 -0
- omnibase_infra/models/handlers/model_handler_descriptor.py +200 -0
- omnibase_infra/models/handlers/model_handler_identifier.py +215 -0
- omnibase_infra/models/handlers/model_handler_source_config.py +220 -0
- omnibase_infra/models/health/__init__.py +9 -0
- omnibase_infra/models/health/model_health_check_result.py +40 -0
- omnibase_infra/models/lifecycle/__init__.py +39 -0
- omnibase_infra/models/logging/__init__.py +51 -0
- omnibase_infra/models/logging/model_log_context.py +756 -0
- omnibase_infra/models/mcp/__init__.py +15 -0
- omnibase_infra/models/mcp/model_mcp_contract_config.py +80 -0
- omnibase_infra/models/mcp/model_mcp_server_config.py +67 -0
- omnibase_infra/models/mcp/model_mcp_tool_definition.py +73 -0
- omnibase_infra/models/mcp/model_mcp_tool_parameter.py +35 -0
- omnibase_infra/models/model_node_identity.py +126 -0
- omnibase_infra/models/model_retry_error_classification.py +78 -0
- omnibase_infra/models/projection/__init__.py +43 -0
- omnibase_infra/models/projection/model_capability_fields.py +112 -0
- omnibase_infra/models/projection/model_registration_projection.py +434 -0
- omnibase_infra/models/projection/model_registration_snapshot.py +322 -0
- omnibase_infra/models/projection/model_sequence_info.py +182 -0
- omnibase_infra/models/projection/model_snapshot_topic_config.py +591 -0
- omnibase_infra/models/projectors/__init__.py +41 -0
- omnibase_infra/models/projectors/model_projector_column.py +289 -0
- omnibase_infra/models/projectors/model_projector_discovery_result.py +65 -0
- omnibase_infra/models/projectors/model_projector_index.py +270 -0
- omnibase_infra/models/projectors/model_projector_schema.py +415 -0
- omnibase_infra/models/projectors/model_projector_validation_error.py +63 -0
- omnibase_infra/models/projectors/util_sql_identifiers.py +115 -0
- omnibase_infra/models/registration/__init__.py +68 -0
- omnibase_infra/models/registration/commands/__init__.py +15 -0
- omnibase_infra/models/registration/commands/model_node_registration_acked.py +108 -0
- omnibase_infra/models/registration/events/__init__.py +56 -0
- omnibase_infra/models/registration/events/model_node_became_active.py +103 -0
- omnibase_infra/models/registration/events/model_node_liveness_expired.py +103 -0
- omnibase_infra/models/registration/events/model_node_registration_accepted.py +98 -0
- omnibase_infra/models/registration/events/model_node_registration_ack_received.py +98 -0
- omnibase_infra/models/registration/events/model_node_registration_ack_timed_out.py +112 -0
- omnibase_infra/models/registration/events/model_node_registration_initiated.py +107 -0
- omnibase_infra/models/registration/events/model_node_registration_rejected.py +104 -0
- omnibase_infra/models/registration/model_event_bus_topic_entry.py +59 -0
- omnibase_infra/models/registration/model_introspection_metrics.py +253 -0
- omnibase_infra/models/registration/model_node_capabilities.py +190 -0
- omnibase_infra/models/registration/model_node_event_bus_config.py +99 -0
- omnibase_infra/models/registration/model_node_heartbeat_event.py +126 -0
- omnibase_infra/models/registration/model_node_introspection_event.py +195 -0
- omnibase_infra/models/registration/model_node_metadata.py +79 -0
- omnibase_infra/models/registration/model_node_registration.py +162 -0
- omnibase_infra/models/registration/model_node_registration_record.py +162 -0
- omnibase_infra/models/registry/__init__.py +29 -0
- omnibase_infra/models/registry/model_domain_constraint.py +202 -0
- omnibase_infra/models/registry/model_message_type_entry.py +271 -0
- omnibase_infra/models/resilience/__init__.py +9 -0
- omnibase_infra/models/resilience/model_circuit_breaker_config.py +227 -0
- omnibase_infra/models/routing/__init__.py +25 -0
- omnibase_infra/models/routing/model_routing_entry.py +52 -0
- omnibase_infra/models/routing/model_routing_subcontract.py +70 -0
- omnibase_infra/models/runtime/__init__.py +49 -0
- omnibase_infra/models/runtime/model_contract_security_config.py +41 -0
- omnibase_infra/models/runtime/model_discovery_error.py +81 -0
- omnibase_infra/models/runtime/model_discovery_result.py +162 -0
- omnibase_infra/models/runtime/model_discovery_warning.py +74 -0
- omnibase_infra/models/runtime/model_failed_plugin_load.py +63 -0
- omnibase_infra/models/runtime/model_handler_contract.py +296 -0
- omnibase_infra/models/runtime/model_loaded_handler.py +129 -0
- omnibase_infra/models/runtime/model_plugin_load_context.py +93 -0
- omnibase_infra/models/runtime/model_plugin_load_summary.py +124 -0
- omnibase_infra/models/security/__init__.py +50 -0
- omnibase_infra/models/security/classification_levels.py +99 -0
- omnibase_infra/models/security/model_environment_policy.py +145 -0
- omnibase_infra/models/security/model_handler_security_policy.py +107 -0
- omnibase_infra/models/security/model_security_error.py +81 -0
- omnibase_infra/models/security/model_security_validation_result.py +328 -0
- omnibase_infra/models/security/model_security_warning.py +67 -0
- omnibase_infra/models/snapshot/__init__.py +27 -0
- omnibase_infra/models/snapshot/model_field_change.py +65 -0
- omnibase_infra/models/snapshot/model_snapshot.py +270 -0
- omnibase_infra/models/snapshot/model_snapshot_diff.py +203 -0
- omnibase_infra/models/snapshot/model_subject_ref.py +81 -0
- omnibase_infra/models/types/__init__.py +71 -0
- omnibase_infra/models/validation/__init__.py +89 -0
- omnibase_infra/models/validation/model_any_type_validation_result.py +118 -0
- omnibase_infra/models/validation/model_any_type_violation.py +141 -0
- omnibase_infra/models/validation/model_category_match_result.py +345 -0
- omnibase_infra/models/validation/model_chain_violation.py +166 -0
- omnibase_infra/models/validation/model_coverage_metrics.py +316 -0
- omnibase_infra/models/validation/model_execution_shape_rule.py +159 -0
- omnibase_infra/models/validation/model_execution_shape_validation.py +208 -0
- omnibase_infra/models/validation/model_execution_shape_validation_result.py +294 -0
- omnibase_infra/models/validation/model_execution_shape_violation.py +122 -0
- omnibase_infra/models/validation/model_localhandler_validation_result.py +139 -0
- omnibase_infra/models/validation/model_localhandler_violation.py +100 -0
- omnibase_infra/models/validation/model_output_validation_params.py +74 -0
- omnibase_infra/models/validation/model_validate_and_raise_params.py +84 -0
- omnibase_infra/models/validation/model_validation_error_params.py +84 -0
- omnibase_infra/models/validation/model_validation_outcome.py +287 -0
- omnibase_infra/nodes/__init__.py +57 -0
- omnibase_infra/nodes/architecture_validator/__init__.py +79 -0
- omnibase_infra/nodes/architecture_validator/contract.yaml +252 -0
- omnibase_infra/nodes/architecture_validator/contract_architecture_validator.yaml +203 -0
- omnibase_infra/nodes/architecture_validator/mixins/__init__.py +16 -0
- omnibase_infra/nodes/architecture_validator/mixins/mixin_file_path_rule.py +92 -0
- omnibase_infra/nodes/architecture_validator/models/__init__.py +36 -0
- omnibase_infra/nodes/architecture_validator/models/model_architecture_validation_request.py +56 -0
- omnibase_infra/nodes/architecture_validator/models/model_architecture_validation_result.py +311 -0
- omnibase_infra/nodes/architecture_validator/models/model_architecture_violation.py +163 -0
- omnibase_infra/nodes/architecture_validator/models/model_rule_check_result.py +265 -0
- omnibase_infra/nodes/architecture_validator/models/model_validation_request.py +105 -0
- omnibase_infra/nodes/architecture_validator/models/model_validation_result.py +314 -0
- omnibase_infra/nodes/architecture_validator/node.py +262 -0
- omnibase_infra/nodes/architecture_validator/node_architecture_validator.py +383 -0
- omnibase_infra/nodes/architecture_validator/protocols/__init__.py +9 -0
- omnibase_infra/nodes/architecture_validator/protocols/protocol_architecture_rule.py +225 -0
- omnibase_infra/nodes/architecture_validator/registry/__init__.py +28 -0
- omnibase_infra/nodes/architecture_validator/registry/registry_infra_architecture_validator.py +106 -0
- omnibase_infra/nodes/architecture_validator/validators/__init__.py +104 -0
- omnibase_infra/nodes/architecture_validator/validators/validator_no_direct_dispatch.py +422 -0
- omnibase_infra/nodes/architecture_validator/validators/validator_no_handler_publishing.py +481 -0
- omnibase_infra/nodes/architecture_validator/validators/validator_no_orchestrator_fsm.py +491 -0
- omnibase_infra/nodes/contract_registry_reducer/__init__.py +29 -0
- omnibase_infra/nodes/contract_registry_reducer/contract.yaml +255 -0
- omnibase_infra/nodes/contract_registry_reducer/models/__init__.py +38 -0
- omnibase_infra/nodes/contract_registry_reducer/models/model_contract_registry_state.py +266 -0
- omnibase_infra/nodes/contract_registry_reducer/models/model_payload_cleanup_topic_references.py +55 -0
- omnibase_infra/nodes/contract_registry_reducer/models/model_payload_deactivate_contract.py +58 -0
- omnibase_infra/nodes/contract_registry_reducer/models/model_payload_mark_stale.py +49 -0
- omnibase_infra/nodes/contract_registry_reducer/models/model_payload_update_heartbeat.py +71 -0
- omnibase_infra/nodes/contract_registry_reducer/models/model_payload_update_topic.py +66 -0
- omnibase_infra/nodes/contract_registry_reducer/models/model_payload_upsert_contract.py +92 -0
- omnibase_infra/nodes/contract_registry_reducer/node.py +121 -0
- omnibase_infra/nodes/contract_registry_reducer/reducer.py +784 -0
- omnibase_infra/nodes/contract_registry_reducer/registry/__init__.py +9 -0
- omnibase_infra/nodes/contract_registry_reducer/registry/registry_infra_contract_registry_reducer.py +101 -0
- omnibase_infra/nodes/effects/README.md +358 -0
- omnibase_infra/nodes/effects/__init__.py +26 -0
- omnibase_infra/nodes/effects/contract.yaml +167 -0
- omnibase_infra/nodes/effects/models/__init__.py +32 -0
- omnibase_infra/nodes/effects/models/model_backend_result.py +190 -0
- omnibase_infra/nodes/effects/models/model_effect_idempotency_config.py +92 -0
- omnibase_infra/nodes/effects/models/model_registry_request.py +132 -0
- omnibase_infra/nodes/effects/models/model_registry_response.py +263 -0
- omnibase_infra/nodes/effects/protocol_consul_client.py +89 -0
- omnibase_infra/nodes/effects/protocol_effect_idempotency_store.py +143 -0
- omnibase_infra/nodes/effects/protocol_postgres_adapter.py +96 -0
- omnibase_infra/nodes/effects/registry_effect.py +525 -0
- omnibase_infra/nodes/effects/store_effect_idempotency_inmemory.py +425 -0
- omnibase_infra/nodes/handlers/consul/contract.yaml +85 -0
- omnibase_infra/nodes/handlers/db/contract.yaml +72 -0
- omnibase_infra/nodes/handlers/graph/contract.yaml +127 -0
- omnibase_infra/nodes/handlers/http/contract.yaml +74 -0
- omnibase_infra/nodes/handlers/intent/contract.yaml +66 -0
- omnibase_infra/nodes/handlers/mcp/contract.yaml +69 -0
- omnibase_infra/nodes/handlers/vault/contract.yaml +91 -0
- omnibase_infra/nodes/node_intent_storage_effect/__init__.py +50 -0
- omnibase_infra/nodes/node_intent_storage_effect/contract.yaml +194 -0
- omnibase_infra/nodes/node_intent_storage_effect/models/__init__.py +24 -0
- omnibase_infra/nodes/node_intent_storage_effect/models/model_intent_storage_input.py +141 -0
- omnibase_infra/nodes/node_intent_storage_effect/models/model_intent_storage_output.py +130 -0
- omnibase_infra/nodes/node_intent_storage_effect/node.py +94 -0
- omnibase_infra/nodes/node_intent_storage_effect/registry/__init__.py +35 -0
- omnibase_infra/nodes/node_intent_storage_effect/registry/registry_infra_intent_storage.py +294 -0
- omnibase_infra/nodes/node_ledger_projection_compute/__init__.py +50 -0
- omnibase_infra/nodes/node_ledger_projection_compute/contract.yaml +104 -0
- omnibase_infra/nodes/node_ledger_projection_compute/node.py +284 -0
- omnibase_infra/nodes/node_ledger_projection_compute/registry/__init__.py +29 -0
- omnibase_infra/nodes/node_ledger_projection_compute/registry/registry_infra_ledger_projection.py +118 -0
- omnibase_infra/nodes/node_ledger_write_effect/__init__.py +82 -0
- omnibase_infra/nodes/node_ledger_write_effect/contract.yaml +200 -0
- omnibase_infra/nodes/node_ledger_write_effect/handlers/__init__.py +22 -0
- omnibase_infra/nodes/node_ledger_write_effect/handlers/handler_ledger_append.py +372 -0
- omnibase_infra/nodes/node_ledger_write_effect/handlers/handler_ledger_query.py +597 -0
- omnibase_infra/nodes/node_ledger_write_effect/models/__init__.py +31 -0
- omnibase_infra/nodes/node_ledger_write_effect/models/model_ledger_append_result.py +54 -0
- omnibase_infra/nodes/node_ledger_write_effect/models/model_ledger_entry.py +92 -0
- omnibase_infra/nodes/node_ledger_write_effect/models/model_ledger_query.py +53 -0
- omnibase_infra/nodes/node_ledger_write_effect/models/model_ledger_query_result.py +41 -0
- omnibase_infra/nodes/node_ledger_write_effect/node.py +89 -0
- omnibase_infra/nodes/node_ledger_write_effect/protocols/__init__.py +13 -0
- omnibase_infra/nodes/node_ledger_write_effect/protocols/protocol_ledger_persistence.py +127 -0
- omnibase_infra/nodes/node_ledger_write_effect/registry/__init__.py +9 -0
- omnibase_infra/nodes/node_ledger_write_effect/registry/registry_infra_ledger_write.py +121 -0
- omnibase_infra/nodes/node_registration_orchestrator/README.md +542 -0
- omnibase_infra/nodes/node_registration_orchestrator/__init__.py +120 -0
- omnibase_infra/nodes/node_registration_orchestrator/contract.yaml +482 -0
- omnibase_infra/nodes/node_registration_orchestrator/dispatchers/__init__.py +53 -0
- omnibase_infra/nodes/node_registration_orchestrator/dispatchers/dispatcher_node_introspected.py +376 -0
- omnibase_infra/nodes/node_registration_orchestrator/dispatchers/dispatcher_node_registration_acked.py +376 -0
- omnibase_infra/nodes/node_registration_orchestrator/dispatchers/dispatcher_runtime_tick.py +373 -0
- omnibase_infra/nodes/node_registration_orchestrator/handlers/__init__.py +62 -0
- omnibase_infra/nodes/node_registration_orchestrator/handlers/handler_node_heartbeat.py +376 -0
- omnibase_infra/nodes/node_registration_orchestrator/handlers/handler_node_introspected.py +694 -0
- omnibase_infra/nodes/node_registration_orchestrator/handlers/handler_node_registration_acked.py +458 -0
- omnibase_infra/nodes/node_registration_orchestrator/handlers/handler_runtime_tick.py +364 -0
- omnibase_infra/nodes/node_registration_orchestrator/introspection_event_router.py +544 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/__init__.py +75 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_consul_intent_payload.py +194 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_consul_registration_intent.py +67 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_intent_execution_result.py +50 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_node_liveness_expired.py +107 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_orchestrator_config.py +67 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_orchestrator_input.py +41 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_orchestrator_output.py +166 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_postgres_intent_payload.py +235 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_postgres_upsert_intent.py +68 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_reducer_execution_result.py +384 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_reducer_state.py +60 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_registration_intent.py +177 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_registry_intent.py +247 -0
- omnibase_infra/nodes/node_registration_orchestrator/node.py +195 -0
- omnibase_infra/nodes/node_registration_orchestrator/plugin.py +909 -0
- omnibase_infra/nodes/node_registration_orchestrator/protocols.py +439 -0
- omnibase_infra/nodes/node_registration_orchestrator/registry/__init__.py +41 -0
- omnibase_infra/nodes/node_registration_orchestrator/registry/registry_infra_node_registration_orchestrator.py +528 -0
- omnibase_infra/nodes/node_registration_orchestrator/timeout_coordinator.py +393 -0
- omnibase_infra/nodes/node_registration_orchestrator/wiring.py +743 -0
- omnibase_infra/nodes/node_registration_reducer/__init__.py +15 -0
- omnibase_infra/nodes/node_registration_reducer/contract.yaml +301 -0
- omnibase_infra/nodes/node_registration_reducer/models/__init__.py +38 -0
- omnibase_infra/nodes/node_registration_reducer/models/model_validation_result.py +113 -0
- omnibase_infra/nodes/node_registration_reducer/node.py +139 -0
- omnibase_infra/nodes/node_registration_reducer/registry/__init__.py +9 -0
- omnibase_infra/nodes/node_registration_reducer/registry/registry_infra_node_registration_reducer.py +79 -0
- omnibase_infra/nodes/node_registration_storage_effect/__init__.py +41 -0
- omnibase_infra/nodes/node_registration_storage_effect/contract.yaml +220 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/__init__.py +44 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/model_delete_result.py +132 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/model_registration_record.py +199 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/model_registration_update.py +155 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/model_storage_health_check_details.py +123 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/model_storage_health_check_result.py +117 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/model_storage_query.py +100 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/model_storage_result.py +136 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/model_upsert_result.py +127 -0
- omnibase_infra/nodes/node_registration_storage_effect/node.py +112 -0
- omnibase_infra/nodes/node_registration_storage_effect/protocols/__init__.py +22 -0
- omnibase_infra/nodes/node_registration_storage_effect/protocols/protocol_registration_persistence.py +333 -0
- omnibase_infra/nodes/node_registration_storage_effect/registry/__init__.py +23 -0
- omnibase_infra/nodes/node_registration_storage_effect/registry/registry_infra_registration_storage.py +215 -0
- omnibase_infra/nodes/node_registry_effect/__init__.py +85 -0
- omnibase_infra/nodes/node_registry_effect/contract.yaml +677 -0
- omnibase_infra/nodes/node_registry_effect/handlers/__init__.py +70 -0
- omnibase_infra/nodes/node_registry_effect/handlers/handler_consul_deregister.py +211 -0
- omnibase_infra/nodes/node_registry_effect/handlers/handler_consul_register.py +212 -0
- omnibase_infra/nodes/node_registry_effect/handlers/handler_partial_retry.py +417 -0
- omnibase_infra/nodes/node_registry_effect/handlers/handler_postgres_deactivate.py +215 -0
- omnibase_infra/nodes/node_registry_effect/handlers/handler_postgres_upsert.py +208 -0
- omnibase_infra/nodes/node_registry_effect/models/__init__.py +43 -0
- omnibase_infra/nodes/node_registry_effect/models/model_partial_retry_request.py +92 -0
- omnibase_infra/nodes/node_registry_effect/node.py +165 -0
- omnibase_infra/nodes/node_registry_effect/registry/__init__.py +27 -0
- omnibase_infra/nodes/node_registry_effect/registry/registry_infra_registry_effect.py +196 -0
- omnibase_infra/nodes/node_service_discovery_effect/__init__.py +111 -0
- omnibase_infra/nodes/node_service_discovery_effect/contract.yaml +246 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/__init__.py +67 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/enum_health_status.py +72 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/enum_service_discovery_operation.py +58 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_discovery_query.py +99 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_discovery_result.py +98 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_health_check_config.py +121 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_query_metadata.py +63 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_registration_result.py +130 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_service_discovery_health_check_details.py +111 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_service_discovery_health_check_result.py +119 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_service_info.py +106 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_service_registration.py +121 -0
- omnibase_infra/nodes/node_service_discovery_effect/node.py +111 -0
- omnibase_infra/nodes/node_service_discovery_effect/protocols/__init__.py +14 -0
- omnibase_infra/nodes/node_service_discovery_effect/protocols/protocol_discovery_operations.py +279 -0
- omnibase_infra/nodes/node_service_discovery_effect/registry/__init__.py +13 -0
- omnibase_infra/nodes/node_service_discovery_effect/registry/registry_infra_service_discovery.py +222 -0
- omnibase_infra/nodes/reducers/__init__.py +30 -0
- omnibase_infra/nodes/reducers/models/__init__.py +37 -0
- omnibase_infra/nodes/reducers/models/model_payload_consul_register.py +87 -0
- omnibase_infra/nodes/reducers/models/model_payload_ledger_append.py +133 -0
- omnibase_infra/nodes/reducers/models/model_payload_postgres_upsert_registration.py +60 -0
- omnibase_infra/nodes/reducers/models/model_registration_confirmation.py +166 -0
- omnibase_infra/nodes/reducers/models/model_registration_state.py +433 -0
- omnibase_infra/nodes/reducers/registration_reducer.py +1138 -0
- omnibase_infra/observability/__init__.py +143 -0
- omnibase_infra/observability/constants_metrics.py +91 -0
- omnibase_infra/observability/factory_observability_sink.py +525 -0
- omnibase_infra/observability/handlers/__init__.py +118 -0
- omnibase_infra/observability/handlers/handler_logging_structured.py +967 -0
- omnibase_infra/observability/handlers/handler_metrics_prometheus.py +1120 -0
- omnibase_infra/observability/handlers/model_logging_handler_config.py +71 -0
- omnibase_infra/observability/handlers/model_logging_handler_response.py +77 -0
- omnibase_infra/observability/handlers/model_metrics_handler_config.py +172 -0
- omnibase_infra/observability/handlers/model_metrics_handler_payload.py +135 -0
- omnibase_infra/observability/handlers/model_metrics_handler_response.py +101 -0
- omnibase_infra/observability/hooks/__init__.py +74 -0
- omnibase_infra/observability/hooks/hook_observability.py +1223 -0
- omnibase_infra/observability/models/__init__.py +30 -0
- omnibase_infra/observability/models/enum_required_log_context_key.py +77 -0
- omnibase_infra/observability/models/model_buffered_log_entry.py +117 -0
- omnibase_infra/observability/models/model_logging_sink_config.py +73 -0
- omnibase_infra/observability/models/model_metrics_sink_config.py +156 -0
- omnibase_infra/observability/sinks/__init__.py +69 -0
- omnibase_infra/observability/sinks/sink_logging_structured.py +809 -0
- omnibase_infra/observability/sinks/sink_metrics_prometheus.py +710 -0
- omnibase_infra/plugins/__init__.py +27 -0
- omnibase_infra/plugins/examples/__init__.py +28 -0
- omnibase_infra/plugins/examples/plugin_json_normalizer.py +271 -0
- omnibase_infra/plugins/examples/plugin_json_normalizer_error_handling.py +210 -0
- omnibase_infra/plugins/models/__init__.py +21 -0
- omnibase_infra/plugins/models/model_plugin_context.py +76 -0
- omnibase_infra/plugins/models/model_plugin_input_data.py +58 -0
- omnibase_infra/plugins/models/model_plugin_output_data.py +62 -0
- omnibase_infra/plugins/plugin_compute_base.py +449 -0
- omnibase_infra/projectors/__init__.py +30 -0
- omnibase_infra/projectors/contracts/__init__.py +63 -0
- omnibase_infra/projectors/contracts/registration_projector.yaml +370 -0
- omnibase_infra/projectors/projection_reader_registration.py +1559 -0
- omnibase_infra/projectors/snapshot_publisher_registration.py +1329 -0
- omnibase_infra/protocols/__init__.py +104 -0
- omnibase_infra/protocols/protocol_capability_projection.py +253 -0
- omnibase_infra/protocols/protocol_capability_query.py +251 -0
- omnibase_infra/protocols/protocol_container_aware.py +200 -0
- omnibase_infra/protocols/protocol_dispatch_engine.py +152 -0
- omnibase_infra/protocols/protocol_event_bus_like.py +127 -0
- omnibase_infra/protocols/protocol_event_projector.py +96 -0
- omnibase_infra/protocols/protocol_idempotency_store.py +142 -0
- omnibase_infra/protocols/protocol_message_dispatcher.py +247 -0
- omnibase_infra/protocols/protocol_message_type_registry.py +306 -0
- omnibase_infra/protocols/protocol_plugin_compute.py +368 -0
- omnibase_infra/protocols/protocol_projector_schema_validator.py +82 -0
- omnibase_infra/protocols/protocol_registry_metrics.py +215 -0
- omnibase_infra/protocols/protocol_snapshot_publisher.py +396 -0
- omnibase_infra/protocols/protocol_snapshot_store.py +567 -0
- omnibase_infra/runtime/__init__.py +445 -0
- omnibase_infra/runtime/binding_config_resolver.py +2771 -0
- omnibase_infra/runtime/binding_resolver.py +753 -0
- omnibase_infra/runtime/chain_aware_dispatch.py +467 -0
- omnibase_infra/runtime/constants_notification.py +75 -0
- omnibase_infra/runtime/constants_security.py +70 -0
- omnibase_infra/runtime/contract_handler_discovery.py +587 -0
- omnibase_infra/runtime/contract_loaders/__init__.py +51 -0
- omnibase_infra/runtime/contract_loaders/handler_routing_loader.py +464 -0
- omnibase_infra/runtime/contract_loaders/operation_bindings_loader.py +789 -0
- omnibase_infra/runtime/dispatch_context_enforcer.py +427 -0
- omnibase_infra/runtime/emit_daemon/__init__.py +97 -0
- omnibase_infra/runtime/emit_daemon/cli.py +844 -0
- omnibase_infra/runtime/emit_daemon/client.py +811 -0
- omnibase_infra/runtime/emit_daemon/config.py +535 -0
- omnibase_infra/runtime/emit_daemon/daemon.py +812 -0
- omnibase_infra/runtime/emit_daemon/event_registry.py +477 -0
- omnibase_infra/runtime/emit_daemon/model_daemon_request.py +139 -0
- omnibase_infra/runtime/emit_daemon/model_daemon_response.py +191 -0
- omnibase_infra/runtime/emit_daemon/queue.py +618 -0
- omnibase_infra/runtime/enums/__init__.py +18 -0
- omnibase_infra/runtime/enums/enum_config_ref_scheme.py +33 -0
- omnibase_infra/runtime/enums/enum_scheduler_status.py +170 -0
- omnibase_infra/runtime/envelope_validator.py +179 -0
- omnibase_infra/runtime/event_bus_subcontract_wiring.py +466 -0
- omnibase_infra/runtime/handler_bootstrap_source.py +507 -0
- omnibase_infra/runtime/handler_contract_config_loader.py +603 -0
- omnibase_infra/runtime/handler_contract_source.py +750 -0
- omnibase_infra/runtime/handler_identity.py +81 -0
- omnibase_infra/runtime/handler_plugin_loader.py +2046 -0
- omnibase_infra/runtime/handler_registry.py +329 -0
- omnibase_infra/runtime/handler_source_resolver.py +367 -0
- omnibase_infra/runtime/invocation_security_enforcer.py +427 -0
- omnibase_infra/runtime/kafka_contract_source.py +984 -0
- omnibase_infra/runtime/kernel.py +40 -0
- omnibase_infra/runtime/mixin_policy_validation.py +522 -0
- omnibase_infra/runtime/mixin_semver_cache.py +402 -0
- omnibase_infra/runtime/mixins/__init__.py +24 -0
- omnibase_infra/runtime/mixins/mixin_projector_notification_publishing.py +566 -0
- omnibase_infra/runtime/mixins/mixin_projector_sql_operations.py +778 -0
- omnibase_infra/runtime/models/__init__.py +229 -0
- omnibase_infra/runtime/models/model_batch_lifecycle_result.py +217 -0
- omnibase_infra/runtime/models/model_binding_config.py +168 -0
- omnibase_infra/runtime/models/model_binding_config_cache_stats.py +135 -0
- omnibase_infra/runtime/models/model_binding_config_resolver_config.py +329 -0
- omnibase_infra/runtime/models/model_cached_secret.py +138 -0
- omnibase_infra/runtime/models/model_compute_key.py +138 -0
- omnibase_infra/runtime/models/model_compute_registration.py +97 -0
- omnibase_infra/runtime/models/model_config_cache_entry.py +61 -0
- omnibase_infra/runtime/models/model_config_ref.py +331 -0
- omnibase_infra/runtime/models/model_config_ref_parse_result.py +125 -0
- omnibase_infra/runtime/models/model_contract_load_result.py +224 -0
- omnibase_infra/runtime/models/model_domain_plugin_config.py +92 -0
- omnibase_infra/runtime/models/model_domain_plugin_result.py +270 -0
- omnibase_infra/runtime/models/model_duplicate_response.py +54 -0
- omnibase_infra/runtime/models/model_enabled_protocols_config.py +61 -0
- omnibase_infra/runtime/models/model_event_bus_config.py +54 -0
- omnibase_infra/runtime/models/model_failed_component.py +55 -0
- omnibase_infra/runtime/models/model_health_check_response.py +168 -0
- omnibase_infra/runtime/models/model_health_check_result.py +229 -0
- omnibase_infra/runtime/models/model_lifecycle_result.py +245 -0
- omnibase_infra/runtime/models/model_logging_config.py +42 -0
- omnibase_infra/runtime/models/model_optional_correlation_id.py +167 -0
- omnibase_infra/runtime/models/model_optional_string.py +94 -0
- omnibase_infra/runtime/models/model_optional_uuid.py +110 -0
- omnibase_infra/runtime/models/model_policy_context.py +100 -0
- omnibase_infra/runtime/models/model_policy_key.py +138 -0
- omnibase_infra/runtime/models/model_policy_registration.py +139 -0
- omnibase_infra/runtime/models/model_policy_result.py +103 -0
- omnibase_infra/runtime/models/model_policy_type_filter.py +157 -0
- omnibase_infra/runtime/models/model_projector_notification_config.py +171 -0
- omnibase_infra/runtime/models/model_projector_plugin_loader_config.py +47 -0
- omnibase_infra/runtime/models/model_protocol_registration_config.py +65 -0
- omnibase_infra/runtime/models/model_retry_policy.py +105 -0
- omnibase_infra/runtime/models/model_runtime_config.py +150 -0
- omnibase_infra/runtime/models/model_runtime_contract_config.py +268 -0
- omnibase_infra/runtime/models/model_runtime_scheduler_config.py +625 -0
- omnibase_infra/runtime/models/model_runtime_scheduler_metrics.py +233 -0
- omnibase_infra/runtime/models/model_runtime_tick.py +193 -0
- omnibase_infra/runtime/models/model_secret_cache_stats.py +82 -0
- omnibase_infra/runtime/models/model_secret_mapping.py +63 -0
- omnibase_infra/runtime/models/model_secret_resolver_config.py +107 -0
- omnibase_infra/runtime/models/model_secret_resolver_metrics.py +111 -0
- omnibase_infra/runtime/models/model_secret_source_info.py +72 -0
- omnibase_infra/runtime/models/model_secret_source_spec.py +66 -0
- omnibase_infra/runtime/models/model_security_config.py +109 -0
- omnibase_infra/runtime/models/model_shutdown_batch_result.py +75 -0
- omnibase_infra/runtime/models/model_shutdown_config.py +94 -0
- omnibase_infra/runtime/models/model_transition_notification_outbox_config.py +112 -0
- omnibase_infra/runtime/models/model_transition_notification_outbox_metrics.py +140 -0
- omnibase_infra/runtime/models/model_transition_notification_publisher_metrics.py +357 -0
- omnibase_infra/runtime/projector_plugin_loader.py +1462 -0
- omnibase_infra/runtime/projector_schema_manager.py +565 -0
- omnibase_infra/runtime/projector_shell.py +1330 -0
- omnibase_infra/runtime/protocol_contract_descriptor.py +92 -0
- omnibase_infra/runtime/protocol_contract_source.py +92 -0
- omnibase_infra/runtime/protocol_domain_plugin.py +474 -0
- omnibase_infra/runtime/protocol_handler_discovery.py +221 -0
- omnibase_infra/runtime/protocol_handler_plugin_loader.py +327 -0
- omnibase_infra/runtime/protocol_lifecycle_executor.py +435 -0
- omnibase_infra/runtime/protocol_policy.py +366 -0
- omnibase_infra/runtime/protocols/__init__.py +37 -0
- omnibase_infra/runtime/protocols/protocol_runtime_scheduler.py +468 -0
- omnibase_infra/runtime/publisher_topic_scoped.py +294 -0
- omnibase_infra/runtime/registry/__init__.py +93 -0
- omnibase_infra/runtime/registry/mixin_message_type_query.py +326 -0
- omnibase_infra/runtime/registry/mixin_message_type_registration.py +354 -0
- omnibase_infra/runtime/registry/registry_event_bus_binding.py +268 -0
- omnibase_infra/runtime/registry/registry_message_type.py +542 -0
- omnibase_infra/runtime/registry/registry_protocol_binding.py +445 -0
- omnibase_infra/runtime/registry_compute.py +1143 -0
- omnibase_infra/runtime/registry_contract_source.py +693 -0
- omnibase_infra/runtime/registry_dispatcher.py +678 -0
- omnibase_infra/runtime/registry_policy.py +1185 -0
- omnibase_infra/runtime/runtime_contract_config_loader.py +406 -0
- omnibase_infra/runtime/runtime_scheduler.py +1070 -0
- omnibase_infra/runtime/secret_resolver.py +2112 -0
- omnibase_infra/runtime/security_metadata_validator.py +776 -0
- omnibase_infra/runtime/service_kernel.py +1651 -0
- omnibase_infra/runtime/service_message_dispatch_engine.py +2350 -0
- omnibase_infra/runtime/service_runtime_host_process.py +3493 -0
- omnibase_infra/runtime/transition_notification_outbox.py +1190 -0
- omnibase_infra/runtime/transition_notification_publisher.py +765 -0
- omnibase_infra/runtime/util_container_wiring.py +1124 -0
- omnibase_infra/runtime/util_validation.py +314 -0
- omnibase_infra/runtime/util_version.py +98 -0
- omnibase_infra/runtime/util_wiring.py +723 -0
- omnibase_infra/schemas/schema_registration_projection.sql +320 -0
- omnibase_infra/schemas/schema_transition_notification_outbox.sql +245 -0
- omnibase_infra/services/__init__.py +89 -0
- omnibase_infra/services/corpus_capture.py +684 -0
- omnibase_infra/services/mcp/__init__.py +31 -0
- omnibase_infra/services/mcp/mcp_server_lifecycle.py +449 -0
- omnibase_infra/services/mcp/service_mcp_tool_discovery.py +411 -0
- omnibase_infra/services/mcp/service_mcp_tool_registry.py +329 -0
- omnibase_infra/services/mcp/service_mcp_tool_sync.py +565 -0
- omnibase_infra/services/registry_api/__init__.py +40 -0
- omnibase_infra/services/registry_api/main.py +261 -0
- omnibase_infra/services/registry_api/models/__init__.py +66 -0
- omnibase_infra/services/registry_api/models/model_capability_widget_mapping.py +38 -0
- omnibase_infra/services/registry_api/models/model_pagination_info.py +48 -0
- omnibase_infra/services/registry_api/models/model_registry_discovery_response.py +73 -0
- omnibase_infra/services/registry_api/models/model_registry_health_response.py +49 -0
- omnibase_infra/services/registry_api/models/model_registry_instance_view.py +88 -0
- omnibase_infra/services/registry_api/models/model_registry_node_view.py +88 -0
- omnibase_infra/services/registry_api/models/model_registry_summary.py +60 -0
- omnibase_infra/services/registry_api/models/model_response_list_instances.py +43 -0
- omnibase_infra/services/registry_api/models/model_response_list_nodes.py +51 -0
- omnibase_infra/services/registry_api/models/model_warning.py +49 -0
- omnibase_infra/services/registry_api/models/model_widget_defaults.py +28 -0
- omnibase_infra/services/registry_api/models/model_widget_mapping.py +51 -0
- omnibase_infra/services/registry_api/routes.py +371 -0
- omnibase_infra/services/registry_api/service.py +837 -0
- omnibase_infra/services/service_capability_query.py +945 -0
- omnibase_infra/services/service_health.py +898 -0
- omnibase_infra/services/service_node_selector.py +530 -0
- omnibase_infra/services/service_timeout_emitter.py +699 -0
- omnibase_infra/services/service_timeout_scanner.py +394 -0
- omnibase_infra/services/session/__init__.py +56 -0
- omnibase_infra/services/session/config_consumer.py +137 -0
- omnibase_infra/services/session/config_store.py +139 -0
- omnibase_infra/services/session/consumer.py +1007 -0
- omnibase_infra/services/session/protocol_session_aggregator.py +117 -0
- omnibase_infra/services/session/store.py +997 -0
- omnibase_infra/services/snapshot/__init__.py +31 -0
- omnibase_infra/services/snapshot/service_snapshot.py +647 -0
- omnibase_infra/services/snapshot/store_inmemory.py +637 -0
- omnibase_infra/services/snapshot/store_postgres.py +1279 -0
- omnibase_infra/shared/__init__.py +8 -0
- omnibase_infra/testing/__init__.py +10 -0
- omnibase_infra/testing/utils.py +23 -0
- omnibase_infra/topics/__init__.py +45 -0
- omnibase_infra/topics/platform_topic_suffixes.py +140 -0
- omnibase_infra/topics/util_topic_composition.py +95 -0
- omnibase_infra/types/__init__.py +48 -0
- omnibase_infra/types/type_cache_info.py +49 -0
- omnibase_infra/types/type_dsn.py +173 -0
- omnibase_infra/types/type_infra_aliases.py +60 -0
- omnibase_infra/types/typed_dict/__init__.py +29 -0
- omnibase_infra/types/typed_dict/typed_dict_envelope_build_params.py +115 -0
- omnibase_infra/types/typed_dict/typed_dict_introspection_cache.py +128 -0
- omnibase_infra/types/typed_dict/typed_dict_performance_metrics_cache.py +140 -0
- omnibase_infra/types/typed_dict_capabilities.py +64 -0
- omnibase_infra/utils/__init__.py +117 -0
- omnibase_infra/utils/correlation.py +208 -0
- omnibase_infra/utils/util_atomic_file.py +261 -0
- omnibase_infra/utils/util_consumer_group.py +232 -0
- omnibase_infra/utils/util_datetime.py +372 -0
- omnibase_infra/utils/util_db_transaction.py +239 -0
- omnibase_infra/utils/util_dsn_validation.py +333 -0
- omnibase_infra/utils/util_env_parsing.py +264 -0
- omnibase_infra/utils/util_error_sanitization.py +457 -0
- omnibase_infra/utils/util_pydantic_validators.py +477 -0
- omnibase_infra/utils/util_retry_optimistic.py +281 -0
- omnibase_infra/utils/util_semver.py +233 -0
- omnibase_infra/validation/__init__.py +307 -0
- omnibase_infra/validation/contracts/security.validation.yaml +114 -0
- omnibase_infra/validation/enums/__init__.py +11 -0
- omnibase_infra/validation/enums/enum_contract_violation_severity.py +13 -0
- omnibase_infra/validation/infra_validators.py +1514 -0
- omnibase_infra/validation/linter_contract.py +907 -0
- omnibase_infra/validation/mixin_any_type_classification.py +120 -0
- omnibase_infra/validation/mixin_any_type_exemption.py +580 -0
- omnibase_infra/validation/mixin_any_type_reporting.py +106 -0
- omnibase_infra/validation/mixin_execution_shape_violation_checks.py +596 -0
- omnibase_infra/validation/mixin_node_archetype_detection.py +254 -0
- omnibase_infra/validation/models/__init__.py +15 -0
- omnibase_infra/validation/models/model_contract_lint_result.py +101 -0
- omnibase_infra/validation/models/model_contract_violation.py +41 -0
- omnibase_infra/validation/service_validation_aggregator.py +395 -0
- omnibase_infra/validation/validation_exemptions.yaml +2033 -0
- omnibase_infra/validation/validator_any_type.py +715 -0
- omnibase_infra/validation/validator_chain_propagation.py +839 -0
- omnibase_infra/validation/validator_execution_shape.py +465 -0
- omnibase_infra/validation/validator_localhandler.py +261 -0
- omnibase_infra/validation/validator_registration_security.py +410 -0
- omnibase_infra/validation/validator_routing_coverage.py +1020 -0
- omnibase_infra/validation/validator_runtime_shape.py +915 -0
- omnibase_infra/validation/validator_security.py +513 -0
- omnibase_infra/validation/validator_topic_category.py +1152 -0
- omnibase_infra-0.2.6.dist-info/METADATA +197 -0
- omnibase_infra-0.2.6.dist-info/RECORD +833 -0
- omnibase_infra-0.2.6.dist-info/WHEEL +4 -0
- omnibase_infra-0.2.6.dist-info/entry_points.txt +5 -0
- omnibase_infra-0.2.6.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,778 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2025 OmniNode Team
|
|
3
|
+
"""SQL Operation Mixin for Projector Implementations.
|
|
4
|
+
|
|
5
|
+
Provides SQL generation and execution methods for projector shells. This mixin
|
|
6
|
+
extracts database-specific operations from ProjectorShell to keep the main
|
|
7
|
+
class focused on projection logic and under the method count limit.
|
|
8
|
+
|
|
9
|
+
Features:
|
|
10
|
+
- INSERT, UPSERT, APPEND operations
|
|
11
|
+
- Parameterized SQL for injection protection
|
|
12
|
+
- Configurable query timeouts
|
|
13
|
+
- Row count parsing from asyncpg results
|
|
14
|
+
|
|
15
|
+
See Also:
|
|
16
|
+
- ProjectorShell: Main projector class that uses this mixin
|
|
17
|
+
- ModelProjectorContract: Contract model defining projection behavior
|
|
18
|
+
|
|
19
|
+
Related Tickets:
|
|
20
|
+
- OMN-1169: ProjectorShell contract-driven projections
|
|
21
|
+
|
|
22
|
+
.. versionadded:: 0.7.0
|
|
23
|
+
Extracted from ProjectorShell as part of OMN-1169 class decomposition.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
from __future__ import annotations
|
|
27
|
+
|
|
28
|
+
import logging
|
|
29
|
+
from datetime import datetime
|
|
30
|
+
from typing import TYPE_CHECKING, Protocol
|
|
31
|
+
from uuid import UUID
|
|
32
|
+
|
|
33
|
+
import asyncpg
|
|
34
|
+
|
|
35
|
+
from omnibase_infra.enums import EnumInfraTransportType
|
|
36
|
+
from omnibase_infra.errors import ModelInfraErrorContext, ProtocolConfigurationError
|
|
37
|
+
from omnibase_infra.models.projectors.util_sql_identifiers import quote_identifier
|
|
38
|
+
from omnibase_infra.utils.util_datetime import ensure_timezone_aware
|
|
39
|
+
|
|
40
|
+
if TYPE_CHECKING:
|
|
41
|
+
from omnibase_core.models.projectors import (
|
|
42
|
+
ModelProjectorContract,
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
logger = logging.getLogger(__name__)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class ProtocolProjectorContext(Protocol):
|
|
49
|
+
"""Protocol for projector context required by SQL operations mixin.
|
|
50
|
+
|
|
51
|
+
This protocol defines the minimum interface that a projector must
|
|
52
|
+
implement to use MixinProjectorSqlOperations.
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
@property
|
|
56
|
+
def _contract(self) -> ModelProjectorContract:
|
|
57
|
+
"""The projector contract defining projection behavior."""
|
|
58
|
+
...
|
|
59
|
+
|
|
60
|
+
@property
|
|
61
|
+
def _pool(self) -> asyncpg.Pool:
|
|
62
|
+
"""The asyncpg connection pool for database operations."""
|
|
63
|
+
...
|
|
64
|
+
|
|
65
|
+
@property
|
|
66
|
+
def _query_timeout(self) -> float:
|
|
67
|
+
"""Query timeout in seconds."""
|
|
68
|
+
...
|
|
69
|
+
|
|
70
|
+
@property
|
|
71
|
+
def projector_id(self) -> str:
|
|
72
|
+
"""Unique identifier for the projector."""
|
|
73
|
+
...
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class MixinProjectorSqlOperations:
|
|
77
|
+
"""SQL operation mixin for projector implementations.
|
|
78
|
+
|
|
79
|
+
Provides INSERT, UPSERT, and APPEND database operations with:
|
|
80
|
+
- Parameterized SQL for injection protection
|
|
81
|
+
- Empty SET clause handling for upsert edge cases
|
|
82
|
+
- Configurable query timeouts
|
|
83
|
+
- Row count parsing
|
|
84
|
+
|
|
85
|
+
This mixin expects the implementing class to provide:
|
|
86
|
+
- ``_contract``: ModelProjectorContract instance
|
|
87
|
+
- ``_pool``: asyncpg.Pool for database connections
|
|
88
|
+
- ``_query_timeout``: float timeout in seconds
|
|
89
|
+
- ``projector_id``: str identifier for logging
|
|
90
|
+
|
|
91
|
+
Example:
|
|
92
|
+
>>> class ProjectorShell(MixinProjectorSqlOperations):
|
|
93
|
+
... def __init__(self, contract, pool, timeout):
|
|
94
|
+
... self._contract = contract
|
|
95
|
+
... self._pool = pool
|
|
96
|
+
... self._query_timeout = timeout
|
|
97
|
+
...
|
|
98
|
+
... @property
|
|
99
|
+
... def projector_id(self):
|
|
100
|
+
... return str(self._contract.projector_id)
|
|
101
|
+
|
|
102
|
+
Note:
|
|
103
|
+
This mixin expects the implementing class to provide the attributes
|
|
104
|
+
documented in ProtocolProjectorContext. The ``projector_id`` attribute
|
|
105
|
+
is not declared here as it may be implemented as a property.
|
|
106
|
+
"""
|
|
107
|
+
|
|
108
|
+
# Type hints for expected attributes from implementing class
|
|
109
|
+
_contract: ModelProjectorContract
|
|
110
|
+
_pool: asyncpg.Pool
|
|
111
|
+
_query_timeout: float
|
|
112
|
+
|
|
113
|
+
@property
|
|
114
|
+
def projector_id(self) -> str:
|
|
115
|
+
"""Unique identifier for the projector (expected from implementing class)."""
|
|
116
|
+
raise NotImplementedError("projector_id must be implemented by subclass")
|
|
117
|
+
|
|
118
|
+
def normalize_value(
|
|
119
|
+
self,
|
|
120
|
+
value: object,
|
|
121
|
+
column_name: str | None = None,
|
|
122
|
+
) -> object:
|
|
123
|
+
"""Normalize a value before SQL persistence.
|
|
124
|
+
|
|
125
|
+
Performs value normalization to ensure data consistency and prevent
|
|
126
|
+
common issues when persisting to PostgreSQL:
|
|
127
|
+
|
|
128
|
+
- **Datetime validation**: Ensures datetime values are timezone-aware.
|
|
129
|
+
Naive datetimes (without tzinfo) are automatically converted to UTC
|
|
130
|
+
with a warning log. This prevents subtle bugs when storing in
|
|
131
|
+
TIMESTAMPTZ columns.
|
|
132
|
+
|
|
133
|
+
- **Pass-through for other types**: Non-datetime values are returned
|
|
134
|
+
unchanged.
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
value: The value to normalize. Can be any type.
|
|
138
|
+
column_name: Optional column name for context in warning messages.
|
|
139
|
+
Helps identify the source of naive datetimes in logs.
|
|
140
|
+
|
|
141
|
+
Returns:
|
|
142
|
+
The normalized value. For datetimes, returns a timezone-aware
|
|
143
|
+
datetime. For other types, returns the original value unchanged.
|
|
144
|
+
|
|
145
|
+
Example:
|
|
146
|
+
>>> from datetime import datetime, UTC
|
|
147
|
+
>>> mixin = MixinProjectorSqlOperations()
|
|
148
|
+
>>>
|
|
149
|
+
>>> # Aware datetime passes through
|
|
150
|
+
>>> aware_dt = datetime.now(UTC)
|
|
151
|
+
>>> mixin.normalize_value(aware_dt, "updated_at") == aware_dt
|
|
152
|
+
True
|
|
153
|
+
>>>
|
|
154
|
+
>>> # Naive datetime is converted to UTC (with warning log)
|
|
155
|
+
>>> naive_dt = datetime(2025, 1, 15, 12, 0, 0)
|
|
156
|
+
>>> result = mixin.normalize_value(naive_dt, "created_at")
|
|
157
|
+
>>> result.tzinfo is not None
|
|
158
|
+
True
|
|
159
|
+
>>>
|
|
160
|
+
>>> # Non-datetime values pass through unchanged
|
|
161
|
+
>>> mixin.normalize_value("test", "name")
|
|
162
|
+
'test'
|
|
163
|
+
>>> mixin.normalize_value(123, "count")
|
|
164
|
+
123
|
|
165
|
+
|
|
166
|
+
Warning:
|
|
167
|
+
Naive datetimes are automatically converted to UTC to prevent
|
|
168
|
+
database errors, but this may mask timezone bugs in your code.
|
|
169
|
+
The warning log helps identify these issues. Prefer using
|
|
170
|
+
``datetime.now(UTC)`` explicitly in your code.
|
|
171
|
+
|
|
172
|
+
Related:
|
|
173
|
+
- ensure_timezone_aware: The underlying datetime validation utility
|
|
174
|
+
- OMN-1170: Declarative contract projections
|
|
175
|
+
- PR #146: Datetime validation improvements
|
|
176
|
+
"""
|
|
177
|
+
# Handle datetime timezone validation
|
|
178
|
+
if isinstance(value, datetime):
|
|
179
|
+
return ensure_timezone_aware(
|
|
180
|
+
value,
|
|
181
|
+
assume_utc=True,
|
|
182
|
+
warn_on_naive=True,
|
|
183
|
+
context=column_name,
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
# Pass through other types unchanged
|
|
187
|
+
return value
|
|
188
|
+
|
|
189
|
+
def _normalize_values(
|
|
190
|
+
self,
|
|
191
|
+
values: dict[str, object],
|
|
192
|
+
) -> dict[str, object]:
|
|
193
|
+
"""Normalize all values in a dictionary before SQL persistence.
|
|
194
|
+
|
|
195
|
+
Applies normalize_value() to each value in the dictionary, using the
|
|
196
|
+
key as the column_name context for any warnings.
|
|
197
|
+
|
|
198
|
+
Args:
|
|
199
|
+
values: Dictionary of column names to values.
|
|
200
|
+
|
|
201
|
+
Returns:
|
|
202
|
+
New dictionary with all values normalized.
|
|
203
|
+
|
|
204
|
+
Example:
|
|
205
|
+
>>> from datetime import datetime, UTC
|
|
206
|
+
>>> mixin = MixinProjectorSqlOperations()
|
|
207
|
+
>>> values = {
|
|
208
|
+
... "name": "test",
|
|
209
|
+
... "created_at": datetime(2025, 1, 15), # Naive - will be converted
|
|
210
|
+
... "updated_at": datetime.now(UTC), # Aware - passes through
|
|
211
|
+
... }
|
|
212
|
+
>>> normalized = mixin._normalize_values(values)
|
|
213
|
+
>>> normalized["created_at"].tzinfo is not None # Now timezone-aware
|
|
214
|
+
True
|
|
215
|
+
"""
|
|
216
|
+
return {
|
|
217
|
+
column: self.normalize_value(value, column_name=column)
|
|
218
|
+
for column, value in values.items()
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
async def _upsert(
|
|
222
|
+
self,
|
|
223
|
+
values: dict[str, object],
|
|
224
|
+
correlation_id: UUID,
|
|
225
|
+
event_type: str | None = None,
|
|
226
|
+
) -> int:
|
|
227
|
+
"""Execute upsert (INSERT ON CONFLICT DO UPDATE).
|
|
228
|
+
|
|
229
|
+
Uses the contract's upsert_key for conflict detection. Supports both
|
|
230
|
+
single-column keys (str) and composite keys (list[str]). When all columns
|
|
231
|
+
are part of the upsert key (i.e., no updatable columns), uses
|
|
232
|
+
DO NOTHING to avoid generating invalid SQL with empty SET clause.
|
|
233
|
+
|
|
234
|
+
Args:
|
|
235
|
+
values: Column name to value mapping.
|
|
236
|
+
correlation_id: Correlation ID for tracing.
|
|
237
|
+
event_type: The event type being projected (for logging context).
|
|
238
|
+
|
|
239
|
+
Returns:
|
|
240
|
+
Number of rows affected.
|
|
241
|
+
"""
|
|
242
|
+
# Normalize values (datetime timezone validation, etc.)
|
|
243
|
+
values = self._normalize_values(values)
|
|
244
|
+
|
|
245
|
+
schema = self._contract.projection_schema
|
|
246
|
+
behavior = self._contract.behavior
|
|
247
|
+
table_quoted = quote_identifier(schema.table)
|
|
248
|
+
|
|
249
|
+
# Normalize upsert_key to list for uniform handling
|
|
250
|
+
# schema.primary_key is a str field in omnibase_core.ModelProjectorSchema
|
|
251
|
+
upsert_key = behavior.upsert_key or schema.primary_key
|
|
252
|
+
upsert_key_list = [upsert_key] if isinstance(upsert_key, str) else upsert_key
|
|
253
|
+
upsert_key_set = set(upsert_key_list)
|
|
254
|
+
|
|
255
|
+
# Build quoted upsert key column list for ON CONFLICT clause
|
|
256
|
+
upsert_key_quoted = ", ".join(quote_identifier(col) for col in upsert_key_list)
|
|
257
|
+
|
|
258
|
+
# Build column lists
|
|
259
|
+
columns = list(values.keys())
|
|
260
|
+
if not columns:
|
|
261
|
+
self._log_empty_columns_skip("upsert", event_type, correlation_id)
|
|
262
|
+
return 0
|
|
263
|
+
|
|
264
|
+
# Build parameterized INSERT ... ON CONFLICT DO UPDATE
|
|
265
|
+
column_list = ", ".join(quote_identifier(col) for col in columns)
|
|
266
|
+
param_list = ", ".join(f"${i + 1}" for i in range(len(columns)))
|
|
267
|
+
# Exclude all upsert key columns from updatable columns
|
|
268
|
+
updatable_columns = [col for col in columns if col not in upsert_key_set]
|
|
269
|
+
|
|
270
|
+
# S608: Safe - identifiers quoted via quote_identifier(), not user input
|
|
271
|
+
if updatable_columns:
|
|
272
|
+
# Normal case: columns to update on conflict
|
|
273
|
+
update_list = ", ".join(
|
|
274
|
+
f"{quote_identifier(col)} = EXCLUDED.{quote_identifier(col)}"
|
|
275
|
+
for col in updatable_columns
|
|
276
|
+
)
|
|
277
|
+
sql = f"""
|
|
278
|
+
INSERT INTO {table_quoted} ({column_list})
|
|
279
|
+
VALUES ({param_list})
|
|
280
|
+
ON CONFLICT ({upsert_key_quoted}) DO UPDATE SET {update_list}
|
|
281
|
+
""" # noqa: S608
|
|
282
|
+
else:
|
|
283
|
+
# Edge case: all columns are part of primary key - no columns to update
|
|
284
|
+
# Use DO NOTHING to avoid invalid SQL with empty SET clause
|
|
285
|
+
sql = f"""
|
|
286
|
+
INSERT INTO {table_quoted} ({column_list})
|
|
287
|
+
VALUES ({param_list})
|
|
288
|
+
ON CONFLICT ({upsert_key_quoted}) DO NOTHING
|
|
289
|
+
""" # noqa: S608
|
|
290
|
+
|
|
291
|
+
params = list(values.values())
|
|
292
|
+
|
|
293
|
+
async with self._pool.acquire() as conn:
|
|
294
|
+
result = await conn.execute(sql, *params, timeout=self._query_timeout)
|
|
295
|
+
|
|
296
|
+
# Parse row count from result (e.g., "INSERT 0 1" -> 1)
|
|
297
|
+
return self._parse_row_count(result)
|
|
298
|
+
|
|
299
|
+
async def _insert(
|
|
300
|
+
self,
|
|
301
|
+
values: dict[str, object],
|
|
302
|
+
correlation_id: UUID,
|
|
303
|
+
event_type: str | None = None,
|
|
304
|
+
) -> int:
|
|
305
|
+
"""Execute INSERT (fail on conflict).
|
|
306
|
+
|
|
307
|
+
Args:
|
|
308
|
+
values: Column name to value mapping.
|
|
309
|
+
correlation_id: Correlation ID for tracing.
|
|
310
|
+
event_type: The event type being projected (for logging context).
|
|
311
|
+
|
|
312
|
+
Returns:
|
|
313
|
+
Number of rows affected.
|
|
314
|
+
|
|
315
|
+
Raises:
|
|
316
|
+
asyncpg.UniqueViolationError: On conflict (handled by caller
|
|
317
|
+
based on projection mode).
|
|
318
|
+
"""
|
|
319
|
+
# Normalize values (datetime timezone validation, etc.)
|
|
320
|
+
values = self._normalize_values(values)
|
|
321
|
+
|
|
322
|
+
schema = self._contract.projection_schema
|
|
323
|
+
table_quoted = quote_identifier(schema.table)
|
|
324
|
+
|
|
325
|
+
columns = list(values.keys())
|
|
326
|
+
if not columns:
|
|
327
|
+
self._log_empty_columns_skip("insert", event_type, correlation_id)
|
|
328
|
+
return 0
|
|
329
|
+
|
|
330
|
+
column_list = ", ".join(quote_identifier(col) for col in columns)
|
|
331
|
+
param_list = ", ".join(f"${i + 1}" for i in range(len(columns)))
|
|
332
|
+
|
|
333
|
+
# S608: Safe - identifiers quoted via quote_identifier(), not user input
|
|
334
|
+
sql = f"INSERT INTO {table_quoted} ({column_list}) VALUES ({param_list})" # noqa: S608
|
|
335
|
+
|
|
336
|
+
params = list(values.values())
|
|
337
|
+
|
|
338
|
+
async with self._pool.acquire() as conn:
|
|
339
|
+
result = await conn.execute(sql, *params, timeout=self._query_timeout)
|
|
340
|
+
|
|
341
|
+
return self._parse_row_count(result)
|
|
342
|
+
|
|
343
|
+
async def _append(
|
|
344
|
+
self,
|
|
345
|
+
values: dict[str, object],
|
|
346
|
+
correlation_id: UUID,
|
|
347
|
+
event_type: str | None = None,
|
|
348
|
+
) -> int:
|
|
349
|
+
"""Execute INSERT (always append, event-log style).
|
|
350
|
+
|
|
351
|
+
Similar to insert, but semantically indicates this is an
|
|
352
|
+
append-only projection where conflicts are unexpected.
|
|
353
|
+
|
|
354
|
+
Args:
|
|
355
|
+
values: Column name to value mapping.
|
|
356
|
+
correlation_id: Correlation ID for tracing.
|
|
357
|
+
event_type: The event type being projected (for logging context).
|
|
358
|
+
|
|
359
|
+
Returns:
|
|
360
|
+
Number of rows affected.
|
|
361
|
+
"""
|
|
362
|
+
# Implementation is same as insert - semantic difference only
|
|
363
|
+
return await self._insert(values, correlation_id, event_type)
|
|
364
|
+
|
|
365
|
+
def _log_empty_columns_skip(
|
|
366
|
+
self,
|
|
367
|
+
operation: str,
|
|
368
|
+
event_type: str | None,
|
|
369
|
+
correlation_id: UUID,
|
|
370
|
+
) -> None:
|
|
371
|
+
"""Log appropriate message when skipping due to empty columns.
|
|
372
|
+
|
|
373
|
+
Differentiates between expected skips (on_event filtering) and
|
|
374
|
+
unexpected skips (empty value extraction) to help identify
|
|
375
|
+
configuration errors.
|
|
376
|
+
|
|
377
|
+
Args:
|
|
378
|
+
operation: The SQL operation being attempted (upsert/insert).
|
|
379
|
+
event_type: The event type being projected.
|
|
380
|
+
correlation_id: Correlation ID for tracing.
|
|
381
|
+
"""
|
|
382
|
+
extra = {
|
|
383
|
+
"projector_id": self.projector_id,
|
|
384
|
+
"operation": operation,
|
|
385
|
+
"correlation_id": str(correlation_id),
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
if event_type is None:
|
|
389
|
+
# No event type context - use warning as we can't determine reason
|
|
390
|
+
logger.warning(
|
|
391
|
+
"No columns to %s - missing event type context",
|
|
392
|
+
operation,
|
|
393
|
+
extra=extra,
|
|
394
|
+
)
|
|
395
|
+
return
|
|
396
|
+
|
|
397
|
+
extra["event_type"] = event_type
|
|
398
|
+
schema = self._contract.projection_schema
|
|
399
|
+
|
|
400
|
+
# Count columns that SHOULD have values for this event type
|
|
401
|
+
# (no on_event filter OR matching on_event filter)
|
|
402
|
+
expected_columns = [
|
|
403
|
+
col
|
|
404
|
+
for col in schema.columns
|
|
405
|
+
if col.on_event is None or col.on_event == event_type
|
|
406
|
+
]
|
|
407
|
+
|
|
408
|
+
if not expected_columns:
|
|
409
|
+
# All columns have on_event filters that don't match this event type
|
|
410
|
+
# This is expected behavior when using event-specific column filtering
|
|
411
|
+
logger.info(
|
|
412
|
+
"Skipping projection for event type '%s' - no columns match on_event filters",
|
|
413
|
+
event_type,
|
|
414
|
+
extra=extra,
|
|
415
|
+
)
|
|
416
|
+
else:
|
|
417
|
+
# Columns should have values but all were extracted as empty
|
|
418
|
+
# This likely indicates a configuration error (wrong source paths)
|
|
419
|
+
logger.warning(
|
|
420
|
+
"Skipping projection - all column values extracted as empty. "
|
|
421
|
+
"Check source paths in contract for event type '%s'",
|
|
422
|
+
event_type,
|
|
423
|
+
extra=extra,
|
|
424
|
+
)
|
|
425
|
+
|
|
426
|
+
def _parse_row_count(self, result: str) -> int:
|
|
427
|
+
"""Parse row count from asyncpg execute result.
|
|
428
|
+
|
|
429
|
+
Args:
|
|
430
|
+
result: Result string from conn.execute (e.g., "INSERT 0 1").
|
|
431
|
+
|
|
432
|
+
Returns:
|
|
433
|
+
Number of rows affected.
|
|
434
|
+
"""
|
|
435
|
+
# asyncpg returns strings like "INSERT 0 1", "UPDATE 3", etc.
|
|
436
|
+
# The last number is the row count
|
|
437
|
+
parts = result.split()
|
|
438
|
+
if parts and parts[-1].isdigit():
|
|
439
|
+
return int(parts[-1])
|
|
440
|
+
return 0
|
|
441
|
+
|
|
442
|
+
async def _partial_upsert(
|
|
443
|
+
self,
|
|
444
|
+
aggregate_id: UUID,
|
|
445
|
+
values: dict[str, object],
|
|
446
|
+
correlation_id: UUID,
|
|
447
|
+
conflict_columns: list[str] | None = None,
|
|
448
|
+
) -> bool:
|
|
449
|
+
"""Execute a partial UPSERT (INSERT ON CONFLICT DO UPDATE) on specific columns.
|
|
450
|
+
|
|
451
|
+
Inserts a new row if no row exists with the given conflict key(s), or updates
|
|
452
|
+
only the specified columns if a row already exists. Supports both single and
|
|
453
|
+
composite conflict keys for flexible schema support.
|
|
454
|
+
|
|
455
|
+
This method is designed for state transition operations where:
|
|
456
|
+
- A new entity may be created if it doesn't exist
|
|
457
|
+
- An existing entity should be updated with new state
|
|
458
|
+
|
|
459
|
+
Unlike partial_update() which only does UPDATE, partial_upsert() handles
|
|
460
|
+
both INSERT and UPDATE cases atomically.
|
|
461
|
+
|
|
462
|
+
Args:
|
|
463
|
+
aggregate_id: The primary aggregate identifier (for logging).
|
|
464
|
+
values: Dictionary mapping column names to their values.
|
|
465
|
+
MUST include all conflict columns specified.
|
|
466
|
+
Column names are validated and quoted for SQL safety.
|
|
467
|
+
Values are passed as parameterized query arguments.
|
|
468
|
+
correlation_id: Correlation ID for distributed tracing.
|
|
469
|
+
conflict_columns: Optional list of column names for ON CONFLICT clause.
|
|
470
|
+
If not provided, defaults to the contract's primary_key.
|
|
471
|
+
Use this for composite unique constraints (e.g., ["entity_id", "domain"]).
|
|
472
|
+
|
|
473
|
+
Returns:
|
|
474
|
+
True if a row was inserted or updated successfully.
|
|
475
|
+
False only if the upsert produced no rows (edge case).
|
|
476
|
+
|
|
477
|
+
Raises:
|
|
478
|
+
ProtocolConfigurationError: If values dict is empty or missing required
|
|
479
|
+
conflict columns.
|
|
480
|
+
|
|
481
|
+
Note:
|
|
482
|
+
The values dict MUST include all conflict columns. This method
|
|
483
|
+
is for cases where you want to upsert a subset of columns while
|
|
484
|
+
ensuring the row exists.
|
|
485
|
+
|
|
486
|
+
Example:
|
|
487
|
+
>>> # Upsert with composite conflict key
|
|
488
|
+
>>> result = await projector.partial_upsert(
|
|
489
|
+
... aggregate_id=node_id,
|
|
490
|
+
... values={
|
|
491
|
+
... "entity_id": node_id,
|
|
492
|
+
... "domain": "registration",
|
|
493
|
+
... "current_state": "pending_registration",
|
|
494
|
+
... "node_type": "effect",
|
|
495
|
+
... "updated_at": datetime.now(UTC),
|
|
496
|
+
... },
|
|
497
|
+
... correlation_id=correlation_id,
|
|
498
|
+
... conflict_columns=["entity_id", "domain"],
|
|
499
|
+
... )
|
|
500
|
+
"""
|
|
501
|
+
if not values:
|
|
502
|
+
context = ModelInfraErrorContext(
|
|
503
|
+
transport_type=EnumInfraTransportType.RUNTIME,
|
|
504
|
+
operation="partial_upsert",
|
|
505
|
+
correlation_id=correlation_id,
|
|
506
|
+
)
|
|
507
|
+
raise ProtocolConfigurationError(
|
|
508
|
+
"values dict cannot be empty for partial_upsert",
|
|
509
|
+
context=context,
|
|
510
|
+
)
|
|
511
|
+
|
|
512
|
+
# Normalize values (datetime timezone validation, etc.)
|
|
513
|
+
values = self._normalize_values(values)
|
|
514
|
+
|
|
515
|
+
schema = self._contract.projection_schema
|
|
516
|
+
# Normalize primary_key to list (handles both str and list[str] from omnibase_core)
|
|
517
|
+
pk = schema.primary_key
|
|
518
|
+
pk_list: list[str] = pk if isinstance(pk, list) else [pk]
|
|
519
|
+
|
|
520
|
+
# Determine conflict columns (single or composite)
|
|
521
|
+
conflict_cols = conflict_columns if conflict_columns else pk_list
|
|
522
|
+
|
|
523
|
+
# Verify all conflict columns are in values
|
|
524
|
+
for col in conflict_cols:
|
|
525
|
+
if col not in values:
|
|
526
|
+
context = ModelInfraErrorContext(
|
|
527
|
+
transport_type=EnumInfraTransportType.RUNTIME,
|
|
528
|
+
operation="partial_upsert",
|
|
529
|
+
correlation_id=correlation_id,
|
|
530
|
+
)
|
|
531
|
+
raise ProtocolConfigurationError(
|
|
532
|
+
f"values dict must include conflict column '{col}' for partial_upsert",
|
|
533
|
+
context=context,
|
|
534
|
+
)
|
|
535
|
+
|
|
536
|
+
table_quoted = quote_identifier(schema.table)
|
|
537
|
+
|
|
538
|
+
# Build conflict clause (handles both single and composite keys)
|
|
539
|
+
conflict_list = ", ".join(quote_identifier(col) for col in conflict_cols)
|
|
540
|
+
|
|
541
|
+
# Build column lists
|
|
542
|
+
columns = list(values.keys())
|
|
543
|
+
column_list = ", ".join(quote_identifier(col) for col in columns)
|
|
544
|
+
param_list = ", ".join(f"${i + 1}" for i in range(len(columns)))
|
|
545
|
+
|
|
546
|
+
# Build UPDATE SET clause (exclude conflict columns from update)
|
|
547
|
+
updatable_columns = [col for col in columns if col not in conflict_cols]
|
|
548
|
+
|
|
549
|
+
# First conflict column for RETURNING clause
|
|
550
|
+
first_conflict_quoted = quote_identifier(conflict_cols[0])
|
|
551
|
+
|
|
552
|
+
if updatable_columns:
|
|
553
|
+
update_list = ", ".join(
|
|
554
|
+
f"{quote_identifier(col)} = EXCLUDED.{quote_identifier(col)}"
|
|
555
|
+
for col in updatable_columns
|
|
556
|
+
)
|
|
557
|
+
# S608: Safe - identifiers quoted via quote_identifier(), not user input
|
|
558
|
+
sql = f"""
|
|
559
|
+
INSERT INTO {table_quoted} ({column_list})
|
|
560
|
+
VALUES ({param_list})
|
|
561
|
+
ON CONFLICT ({conflict_list}) DO UPDATE SET {update_list}
|
|
562
|
+
RETURNING {first_conflict_quoted}
|
|
563
|
+
""" # noqa: S608
|
|
564
|
+
else:
|
|
565
|
+
# Edge case: only conflict columns provided - DO NOTHING on conflict
|
|
566
|
+
# NOTE: RETURNING with DO NOTHING returns NO ROWS when a conflict occurs
|
|
567
|
+
# (i.e., row already exists). This is expected PostgreSQL behavior.
|
|
568
|
+
# We handle this at the call site by returning True when result is None,
|
|
569
|
+
# since for upsert semantics "row exists" is success.
|
|
570
|
+
sql = f"""
|
|
571
|
+
INSERT INTO {table_quoted} ({column_list})
|
|
572
|
+
VALUES ({param_list})
|
|
573
|
+
ON CONFLICT ({conflict_list}) DO NOTHING
|
|
574
|
+
RETURNING {first_conflict_quoted}
|
|
575
|
+
""" # noqa: S608
|
|
576
|
+
|
|
577
|
+
params = list(values.values())
|
|
578
|
+
|
|
579
|
+
logger.debug(
|
|
580
|
+
"Executing partial upsert",
|
|
581
|
+
extra={
|
|
582
|
+
"projector_id": self.projector_id,
|
|
583
|
+
"aggregate_id": str(aggregate_id),
|
|
584
|
+
"columns": columns,
|
|
585
|
+
"conflict_columns": conflict_cols,
|
|
586
|
+
"correlation_id": str(correlation_id),
|
|
587
|
+
},
|
|
588
|
+
)
|
|
589
|
+
|
|
590
|
+
async with self._pool.acquire() as conn:
|
|
591
|
+
result = await conn.fetchrow(sql, *params, timeout=self._query_timeout)
|
|
592
|
+
|
|
593
|
+
if result:
|
|
594
|
+
logger.debug(
|
|
595
|
+
"Partial upsert completed",
|
|
596
|
+
extra={
|
|
597
|
+
"projector_id": self.projector_id,
|
|
598
|
+
"aggregate_id": str(aggregate_id),
|
|
599
|
+
"correlation_id": str(correlation_id),
|
|
600
|
+
},
|
|
601
|
+
)
|
|
602
|
+
return True
|
|
603
|
+
|
|
604
|
+
# DO NOTHING case - row already exists with same values
|
|
605
|
+
logger.debug(
|
|
606
|
+
"Partial upsert returned no result (DO NOTHING case)",
|
|
607
|
+
extra={
|
|
608
|
+
"projector_id": self.projector_id,
|
|
609
|
+
"aggregate_id": str(aggregate_id),
|
|
610
|
+
"correlation_id": str(correlation_id),
|
|
611
|
+
},
|
|
612
|
+
)
|
|
613
|
+
return True # Row exists, which is success for upsert semantics
|
|
614
|
+
|
|
615
|
+
async def _partial_update(
|
|
616
|
+
self,
|
|
617
|
+
aggregate_id: UUID,
|
|
618
|
+
updates: dict[str, object],
|
|
619
|
+
correlation_id: UUID,
|
|
620
|
+
) -> bool:
|
|
621
|
+
"""Execute a partial UPDATE on specific columns.
|
|
622
|
+
|
|
623
|
+
Updates only the specified columns for the row matching the aggregate_id.
|
|
624
|
+
Uses the contract's primary_key for the WHERE clause.
|
|
625
|
+
|
|
626
|
+
This method is designed for lightweight operations like:
|
|
627
|
+
- Updating heartbeat timestamps
|
|
628
|
+
- Setting timeout marker columns
|
|
629
|
+
- Updating single fields without full row replacement
|
|
630
|
+
|
|
631
|
+
Unlike project() which performs full event-driven projection, partial_update()
|
|
632
|
+
directly updates specified columns without event type filtering or value
|
|
633
|
+
extraction from event envelopes.
|
|
634
|
+
|
|
635
|
+
Args:
|
|
636
|
+
aggregate_id: The primary key value identifying the row to update.
|
|
637
|
+
updates: Dictionary mapping column names to their new values.
|
|
638
|
+
Column names are validated and quoted for SQL safety.
|
|
639
|
+
Values are passed as parameterized query arguments.
|
|
640
|
+
correlation_id: Correlation ID for distributed tracing.
|
|
641
|
+
|
|
642
|
+
Returns:
|
|
643
|
+
True if a row was updated (found and modified).
|
|
644
|
+
False if no row was found matching the aggregate_id.
|
|
645
|
+
|
|
646
|
+
Raises:
|
|
647
|
+
ProtocolConfigurationError: If updates dict is empty, or if the
|
|
648
|
+
schema has a composite primary key. Composite PK schemas
|
|
649
|
+
cannot use this method because using only part of the key
|
|
650
|
+
could cause unintended multi-row updates.
|
|
651
|
+
|
|
652
|
+
Note:
|
|
653
|
+
This method does NOT check whether column names exist in the contract
|
|
654
|
+
schema - it trusts the caller to provide valid column names. This
|
|
655
|
+
enables updating columns that may not be in the projection schema
|
|
656
|
+
(e.g., internal tracking columns like updated_at).
|
|
657
|
+
|
|
658
|
+
**Composite Primary Key Restriction**: Schemas with composite primary
|
|
659
|
+
keys (e.g., ``["entity_id", "domain"]``) are explicitly rejected to
|
|
660
|
+
prevent accidental multi-row updates. For composite key schemas, use
|
|
661
|
+
``_partial_upsert()`` with explicit ``conflict_columns`` parameter,
|
|
662
|
+
or build a custom UPDATE query with a full WHERE clause.
|
|
663
|
+
|
|
664
|
+
Example:
|
|
665
|
+
>>> # Update heartbeat tracking fields
|
|
666
|
+
>>> updated = await projector.partial_update(
|
|
667
|
+
... aggregate_id=node_id,
|
|
668
|
+
... updates={
|
|
669
|
+
... "last_heartbeat_at": datetime.now(UTC),
|
|
670
|
+
... "liveness_deadline": datetime.now(UTC) + timedelta(seconds=90),
|
|
671
|
+
... "updated_at": datetime.now(UTC),
|
|
672
|
+
... },
|
|
673
|
+
... correlation_id=correlation_id,
|
|
674
|
+
... )
|
|
675
|
+
>>> if not updated:
|
|
676
|
+
... logger.warning("Entity not found for heartbeat update")
|
|
677
|
+
"""
|
|
678
|
+
if not updates:
|
|
679
|
+
context = ModelInfraErrorContext(
|
|
680
|
+
transport_type=EnumInfraTransportType.RUNTIME,
|
|
681
|
+
operation="partial_update",
|
|
682
|
+
correlation_id=correlation_id,
|
|
683
|
+
)
|
|
684
|
+
raise ProtocolConfigurationError(
|
|
685
|
+
"updates dict cannot be empty for partial_update",
|
|
686
|
+
context=context,
|
|
687
|
+
)
|
|
688
|
+
|
|
689
|
+
# Normalize values (datetime timezone validation, etc.)
|
|
690
|
+
updates = self._normalize_values(updates)
|
|
691
|
+
|
|
692
|
+
schema = self._contract.projection_schema
|
|
693
|
+
table_quoted = quote_identifier(schema.table)
|
|
694
|
+
|
|
695
|
+
# Validate primary key is single-column to prevent multi-row updates
|
|
696
|
+
pk = schema.primary_key
|
|
697
|
+
if isinstance(pk, list) and len(pk) > 1:
|
|
698
|
+
context = ModelInfraErrorContext(
|
|
699
|
+
transport_type=EnumInfraTransportType.RUNTIME,
|
|
700
|
+
operation="partial_update",
|
|
701
|
+
correlation_id=correlation_id,
|
|
702
|
+
)
|
|
703
|
+
raise ProtocolConfigurationError(
|
|
704
|
+
f"partial_update does not support composite primary keys. "
|
|
705
|
+
f"Schema '{schema.table}' has composite PK: {pk}. "
|
|
706
|
+
f"Use _partial_upsert() with explicit conflict_columns parameter instead, "
|
|
707
|
+
f"or build a custom UPDATE query with full WHERE clause.",
|
|
708
|
+
context=context,
|
|
709
|
+
)
|
|
710
|
+
|
|
711
|
+
pk_col = pk[0] if isinstance(pk, list) else pk
|
|
712
|
+
pk_quoted = quote_identifier(pk_col)
|
|
713
|
+
|
|
714
|
+
# Build SET clause with parameterized placeholders
|
|
715
|
+
# Column names are quoted for SQL safety; values use $1, $2, etc.
|
|
716
|
+
columns = list(updates.keys())
|
|
717
|
+
set_clauses = []
|
|
718
|
+
for i, col in enumerate(columns):
|
|
719
|
+
col_quoted = quote_identifier(col)
|
|
720
|
+
set_clauses.append(f"{col_quoted} = ${i + 1}")
|
|
721
|
+
|
|
722
|
+
set_clause = ", ".join(set_clauses)
|
|
723
|
+
|
|
724
|
+
# Primary key is the last parameter
|
|
725
|
+
pk_param_index = len(columns) + 1
|
|
726
|
+
|
|
727
|
+
# Build UPDATE query
|
|
728
|
+
# S608: Safe - identifiers quoted via quote_identifier(), values parameterized
|
|
729
|
+
sql = f"""
|
|
730
|
+
UPDATE {table_quoted}
|
|
731
|
+
SET {set_clause}
|
|
732
|
+
WHERE {pk_quoted} = ${pk_param_index}
|
|
733
|
+
""" # noqa: S608
|
|
734
|
+
|
|
735
|
+
# Build parameter list: values first, then aggregate_id
|
|
736
|
+
params = list(updates.values()) + [aggregate_id]
|
|
737
|
+
|
|
738
|
+
logger.debug(
|
|
739
|
+
"Executing partial update",
|
|
740
|
+
extra={
|
|
741
|
+
"projector_id": self.projector_id,
|
|
742
|
+
"aggregate_id": str(aggregate_id),
|
|
743
|
+
"columns": columns,
|
|
744
|
+
"correlation_id": str(correlation_id),
|
|
745
|
+
},
|
|
746
|
+
)
|
|
747
|
+
|
|
748
|
+
async with self._pool.acquire() as conn:
|
|
749
|
+
result = await conn.execute(sql, *params, timeout=self._query_timeout)
|
|
750
|
+
|
|
751
|
+
rows_affected = self._parse_row_count(result)
|
|
752
|
+
|
|
753
|
+
if rows_affected > 0:
|
|
754
|
+
logger.debug(
|
|
755
|
+
"Partial update completed",
|
|
756
|
+
extra={
|
|
757
|
+
"projector_id": self.projector_id,
|
|
758
|
+
"aggregate_id": str(aggregate_id),
|
|
759
|
+
"rows_affected": rows_affected,
|
|
760
|
+
"correlation_id": str(correlation_id),
|
|
761
|
+
},
|
|
762
|
+
)
|
|
763
|
+
return True
|
|
764
|
+
|
|
765
|
+
logger.debug(
|
|
766
|
+
"Partial update found no matching row",
|
|
767
|
+
extra={
|
|
768
|
+
"projector_id": self.projector_id,
|
|
769
|
+
"aggregate_id": str(aggregate_id),
|
|
770
|
+
"correlation_id": str(correlation_id),
|
|
771
|
+
},
|
|
772
|
+
)
|
|
773
|
+
return False
|
|
774
|
+
|
|
775
|
+
|
|
776
|
+
__all__ = [
|
|
777
|
+
"MixinProjectorSqlOperations",
|
|
778
|
+
]
|