fdc-shared-kernel 0.0.191__tar.gz → 0.0.193__tar.gz
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.
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/PKG-INFO +1 -1
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/fdc_shared_kernel.egg-info/PKG-INFO +1 -1
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/pyproject.toml +1 -1
- fdc_shared_kernel-0.0.193/shared_kernel/data_warehouse_handlers/connections/fabric_sql_warehouse_connection.py +256 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/data_warehouse_handlers/fabric_sql_handler.py +0 -3
- fdc_shared_kernel-0.0.191/shared_kernel/data_warehouse_handlers/connections/fabric_sql_warehouse_connection.py +0 -183
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/README.md +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/README_pypi.md +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/fdc_shared_kernel.egg-info/SOURCES.txt +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/fdc_shared_kernel.egg-info/dependency_links.txt +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/fdc_shared_kernel.egg-info/requires.txt +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/fdc_shared_kernel.egg-info/top_level.txt +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/requirements.txt +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/setup.cfg +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/__init__.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/agent_communication_channel/__init__.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/agent_communication_channel/agent_channel.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/agent_communication_channel/contexts.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/agent_communication_channel/event_channel.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/ai_audit_log/__init__.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/ai_chat_persistence/__init__.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/ai_chat_persistence/chat_persistence.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/async_task_executor/__init__.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/async_task_executor/async_task_executor.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/auth/__init__.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/auth/jwt_helper.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/auth/token_handler.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/auth/workbook_permission_handler.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/batch_job/__init__.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/batch_job/batch_job_status_tracker/container_job_status_tracker.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/batch_job/job_handlers/__init__.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/batch_job/job_handlers/aws_batch_job_handler.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/batch_job/job_handlers/azure_apps_job_handler.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/batch_job/job_handlers/job_handler_factory.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/batch_job/job_handlers/job_status_mapper.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/batch_job/job_storage_managers/__init__.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/batch_job/job_storage_managers/azure_blob_storage_manager.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/batch_job/job_storage_managers/job_storage_manager_factory.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/batch_job/job_storage_managers/s3_job_storage_manager.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/config/__init__.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/constants/constants.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/data_warehouse_handlers/__init__.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/data_warehouse_handlers/connections/databricks_warehouse_connection.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/data_warehouse_handlers/connections/jdbc_warehouse_connection.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/data_warehouse_handlers/connections/mssql_warehouse_connection.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/data_warehouse_handlers/connections/mysql_warehouse_connection.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/data_warehouse_handlers/connections/postgresql_warehouse_connection.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/data_warehouse_handlers/connections/redshift_warehouse_connection.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/data_warehouse_handlers/connections/snowflake_warehouse_connection.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/data_warehouse_handlers/custom_query_builders/databricks_query.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/data_warehouse_handlers/databricks_handler.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/data_warehouse_handlers/factory.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/data_warehouse_handlers/jdbc_driver_registry.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/data_warehouse_handlers/jdbc_handler.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/data_warehouse_handlers/mssql_handler.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/data_warehouse_handlers/mysql_handler.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/data_warehouse_handlers/odbc_handler.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/data_warehouse_handlers/postgresql_handler.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/data_warehouse_handlers/query_executors/databricks_query_executor.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/data_warehouse_handlers/query_executors/fabric_sql_query_executor.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/data_warehouse_handlers/query_executors/jdbc_query_executor.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/data_warehouse_handlers/query_executors/mssql_query_executor.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/data_warehouse_handlers/query_executors/mysql_query_executor.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/data_warehouse_handlers/query_executors/postgresql_query_executor.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/data_warehouse_handlers/query_executors/redshift_query_executor.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/data_warehouse_handlers/query_executors/snowflake_query_executor.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/data_warehouse_handlers/redshift_handler.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/data_warehouse_handlers/snowflake_handler.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/data_warehouse_handlers/utils.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/data_warehouse_handlers/warehouse_handlers_map.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/database/__init__.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/dataclasses/__init__.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/dataclasses/aws_batch_job_context.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/dataclasses/azure_container_app_job_context.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/dataclasses/event_executor.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/dataclasses/job_context.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/dataclasses/job_record_dataclasses.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/dataclasses/warehouse_configs.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/datatype_mappings/__init__.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/datatype_mappings/connectors_to_system/__init__.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/datatype_mappings/connectors_to_system/csv.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/datatype_mappings/connectors_to_system/databricks.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/datatype_mappings/connectors_to_system/db2.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/datatype_mappings/connectors_to_system/jdbc.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/datatype_mappings/connectors_to_system/jira.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/datatype_mappings/connectors_to_system/mssql.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/datatype_mappings/connectors_to_system/mysql.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/datatype_mappings/connectors_to_system/oracle.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/datatype_mappings/connectors_to_system/postgres.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/datatype_mappings/connectors_to_system/redshift.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/datatype_mappings/connectors_to_system/salesforce.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/datatype_mappings/connectors_to_system/snowflake.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/datatype_mappings/connectors_to_system/sqlalchemy.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/datatype_mappings/system_to_warehouse/__init__.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/datatype_mappings/system_to_warehouse/postgres.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/datatype_mappings/system_to_warehouse/redshift.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/enums/__init__.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/enums/async_task_executor.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/enums/connection_engine.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/enums/status_tracker.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/event_executor/__init__.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/event_executor/event_executor.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/event_executor/job_executor.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/event_executor/utils.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/exceptions/__init__.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/exceptions/batch_job_exceptions.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/exceptions/configuration_exceptions.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/exceptions/custom_exceptions.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/exceptions/data_validation_exceptions.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/exceptions/http_exceptions.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/exceptions/infrastructure_exceptions.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/exceptions/operational_exceptions.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/exceptions/security_exceptions.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/exceptions/user_intervention_exception.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/http/__init__.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/http/httpx_http_client.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/http/request_http_client.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/interfaces/__init__.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/interfaces/base_job_handler.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/interfaces/data_warehouse_handler.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/interfaces/databus.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/interfaces/encryption.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/interfaces/http.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/interfaces/keyvault.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/interfaces/query_executor.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/interfaces/storage_manager.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/interfaces/warehouse_connection.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/logger/__init__.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/messaging/__init__.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/messaging/aws_databus.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/messaging/azure_databus.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/messaging/http_databus.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/messaging/nats_databus.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/messaging/nats_publisher.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/messaging/nats_test.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/messaging/utils/aws_utility.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/messaging/utils/azure_utility.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/messaging/utils/event_messages.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/metrics/__init__.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/metrics/status_tracker.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/models/__init__.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/registries/__init__.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/registries/schema_onboarding_event_registry.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/registries/service_event_registry.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/schema_onboarding_tracker/__init__.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/schema_onboarding_tracker/base_tracker.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/schema_onboarding_tracker/enums.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/schema_onboarding_tracker/exceptions.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/schema_onboarding_tracker/run_manager.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/schema_onboarding_tracker/schema_onboarding_tracker.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/schema_onboarding_tracker/shared_helpers.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/schema_onboarding_tracker/task_determination.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/schema_onboarding_tracker/task_helpers.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/schema_onboarding_tracker/task_manager.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/schema_onboarding_tracker/template_manager.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/schema_onboarding_tracker/utility_manager.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/security/__init__.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/security/encryption/__init__.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/security/encryption/manager.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/security/encryption/providers.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/security/key_vault/__init__.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/security/key_vault/aws_secret_manager.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/security/key_vault/azure_keyvault.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/status_tracker/__init__.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/status_tracker/status_tracker.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/storage_handlers/__init__.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/storage_handlers/blob_file_handler.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/storage_handlers/s3_storage_handler.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/tests/__init__.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/tests/config/__init__.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/tests/config/test_config.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/tests/logger/__init__.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/tests/logger/test_logger.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/tests/messaging/__init__.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/tests/messaging/test_aws_databus.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/tests/messaging/test_event_executor.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/tests/messaging/test_nats_interface.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/tests/security/test_encryption.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/tests/utils/__init__.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/tests/utils/test_data_validators.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/tests/utils/test_date_format_utils.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/tests/utils/test_string_utils.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/traceability/__init__.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/traceability/traceability_handler.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/utils/__init__.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/utils/data_validators_utils.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/utils/date_format_utils.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/utils/date_utils.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/utils/string_utils.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/utils/template_renderer.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/utils/thread_debug_utils.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/utils/thread_local_storage.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/utils/thread_log_traker.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/tests/__init__.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/tests/messaging/__init__.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/tests/messaging/utils/__init__.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/tests/messaging/utils/test_aws_utility.py +0 -0
- {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/tests/test_job_executor.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fdc_shared_kernel
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.193
|
|
4
4
|
Summary: Shared library for microservice
|
|
5
5
|
Author-email: Shikhil S <shikhil.s@dbizsolution.com>, Ahammed Akdham N <ahammedakdham.n@dbizsolution.com>
|
|
6
6
|
Classifier: Programming Language :: Python :: 3
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fdc_shared_kernel
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.193
|
|
4
4
|
Summary: Shared library for microservice
|
|
5
5
|
Author-email: Shikhil S <shikhil.s@dbizsolution.com>, Ahammed Akdham N <ahammedakdham.n@dbizsolution.com>
|
|
6
6
|
Classifier: Programming Language :: Python :: 3
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import hashlib
|
|
2
|
+
import re
|
|
3
|
+
import struct
|
|
4
|
+
import time
|
|
5
|
+
import threading
|
|
6
|
+
|
|
7
|
+
from contextlib import contextmanager
|
|
8
|
+
from typing import Dict
|
|
9
|
+
|
|
10
|
+
from azure.identity import ClientSecretCredential
|
|
11
|
+
from sqlalchemy import create_engine, text
|
|
12
|
+
from sqlalchemy.engine import Engine
|
|
13
|
+
from sqlalchemy.exc import InterfaceError, OperationalError, ProgrammingError
|
|
14
|
+
|
|
15
|
+
from shared_kernel.dataclasses.warehouse_configs import FabricSQLConfig
|
|
16
|
+
from shared_kernel.exceptions.http_exceptions import BadRequest
|
|
17
|
+
from shared_kernel.interfaces.warehouse_connection import DataWarehouseConnection
|
|
18
|
+
from shared_kernel.data_warehouse_handlers.utils import extract_sql_server_error_message
|
|
19
|
+
from shared_kernel.logger import Logger
|
|
20
|
+
from shared_kernel.config import Config
|
|
21
|
+
|
|
22
|
+
config = Config()
|
|
23
|
+
logger = Logger(config.get("APP_NAME"))
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class FabricSQLWarehouseConnection(DataWarehouseConnection):
|
|
27
|
+
|
|
28
|
+
_engines: Dict[str, Engine] = {}
|
|
29
|
+
_tokens: Dict[str, dict] = {}
|
|
30
|
+
_lock = threading.Lock()
|
|
31
|
+
|
|
32
|
+
def __init__(
|
|
33
|
+
self,
|
|
34
|
+
source_config: FabricSQLConfig,
|
|
35
|
+
set_default_schema: bool = True,
|
|
36
|
+
):
|
|
37
|
+
if source_config is None:
|
|
38
|
+
raise ValueError("Missing required parameter: source_config (FabricSQLConfig)")
|
|
39
|
+
|
|
40
|
+
self._fabric_config = source_config
|
|
41
|
+
self.org_schema = source_config.schema if source_config.schema else None
|
|
42
|
+
self.set_default_schema = set_default_schema
|
|
43
|
+
|
|
44
|
+
pagination = getattr(source_config, "pagination", None) or {}
|
|
45
|
+
self.page = pagination.get("page") if isinstance(pagination, dict) else None
|
|
46
|
+
self.limit = pagination.get("limit") if isinstance(pagination, dict) else None
|
|
47
|
+
self.search = pagination.get("search") if isinstance(pagination, dict) else None
|
|
48
|
+
|
|
49
|
+
def _engine_key(self, cfg: FabricSQLConfig) -> str:
|
|
50
|
+
# All five credential fields are part of the key so that any change to any of them
|
|
51
|
+
# forces a new engine + fresh token request (no stale-cache bypass).
|
|
52
|
+
# Password is SHA-256 hashed so the raw secret is never stored in a dict key.
|
|
53
|
+
pwd_hash = hashlib.sha256(cfg.password.encode()).hexdigest()
|
|
54
|
+
return f"{cfg.host}|{cfg.port}|{cfg.database}|{cfg.username}|{cfg.tenant_id}|{pwd_hash}"
|
|
55
|
+
|
|
56
|
+
def _get_fabric_connection_params(self, cfg: FabricSQLConfig) -> str:
|
|
57
|
+
"""
|
|
58
|
+
Builds the base connection parameters for Microsoft Fabric SQL.
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
str: Connection string
|
|
62
|
+
"""
|
|
63
|
+
import urllib.parse
|
|
64
|
+
# Build ODBC params. We omit UID and Password fields entirely from the connection string.
|
|
65
|
+
# Authentication is handled solely by injecting the access token into the connection handle.
|
|
66
|
+
odbc_params = urllib.parse.quote_plus(
|
|
67
|
+
f"DRIVER={{ODBC Driver 18 for SQL Server}};"
|
|
68
|
+
f"SERVER={cfg.host},{int(cfg.port)};"
|
|
69
|
+
f"DATABASE={cfg.database};"
|
|
70
|
+
"Encrypt=yes;"
|
|
71
|
+
"TrustServerCertificate=no;"
|
|
72
|
+
)
|
|
73
|
+
# We use odbc_connect query param to prevent SQLAlchemy dialect from auto-injecting
|
|
74
|
+
# conflicting credentials/parameters (avoiding error FA005).
|
|
75
|
+
connection_string = f"mssql+pyodbc:///?odbc_connect={odbc_params}"
|
|
76
|
+
return connection_string
|
|
77
|
+
|
|
78
|
+
def _get_engine(self) -> Engine:
|
|
79
|
+
cfg = self._fabric_config
|
|
80
|
+
key = self._engine_key(cfg)
|
|
81
|
+
|
|
82
|
+
if key not in self._engines:
|
|
83
|
+
with self.__class__._lock: # thread-safe
|
|
84
|
+
if key not in self._engines: # recheck
|
|
85
|
+
from sqlalchemy import event
|
|
86
|
+
|
|
87
|
+
connection_string = self._get_fabric_connection_params(cfg)
|
|
88
|
+
|
|
89
|
+
engine = create_engine(
|
|
90
|
+
connection_string,
|
|
91
|
+
pool_size=10,
|
|
92
|
+
max_overflow=5,
|
|
93
|
+
pool_timeout=30,
|
|
94
|
+
pool_recycle=1800,
|
|
95
|
+
pool_pre_ping=True,
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
# Ensure a thread-safe token cache structure exists for this database key.
|
|
99
|
+
# Each unique database configuration gets its own token cache, expiration tracker, and lock.
|
|
100
|
+
if key not in self._tokens:
|
|
101
|
+
self._tokens[key] = {
|
|
102
|
+
"token": None, # Stores the packed binary token structure
|
|
103
|
+
"expires_at": 0, # Unix timestamp indicating token expiration time
|
|
104
|
+
"lock": threading.Lock() # Lock to synchronize concurrent token refreshes
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
# Register an event listener on the engine to intercept connection establishment.
|
|
108
|
+
# Every time SQLAlchemy checks out or opens a new raw DBAPI connection from the pool,
|
|
109
|
+
# this listener executes to inject a valid, fresh Entra ID access token.
|
|
110
|
+
@event.listens_for(engine, "do_connect")
|
|
111
|
+
def provide_token(dialect, conn_rec, cargs, cparams):
|
|
112
|
+
now = time.time()
|
|
113
|
+
token_info = self._tokens[key]
|
|
114
|
+
|
|
115
|
+
# Check if the token is missing or near expiration (within 5 minutes / 300 seconds).
|
|
116
|
+
# This buffer ensures we don't send a token that is about to expire.
|
|
117
|
+
if token_info["token"] is None or now >= token_info["expires_at"] - 300:
|
|
118
|
+
# Acquire the database-specific lock to serialize token generation.
|
|
119
|
+
# If 10 threads try to connect concurrently when a token expires,
|
|
120
|
+
# only the first thread executes the token fetch; others block here.
|
|
121
|
+
with token_info["lock"]:
|
|
122
|
+
# Double-checked locking pattern: once the lock is acquired,
|
|
123
|
+
# re-verify if another thread updated the token while we were waiting.
|
|
124
|
+
if token_info["token"] is None or now >= token_info["expires_at"] - 300:
|
|
125
|
+
tenant_id = getattr(cfg, "tenant_id", None)
|
|
126
|
+
if not tenant_id:
|
|
127
|
+
raise BadRequest(
|
|
128
|
+
"tenant_id is required in FabricSQLConfig for authenticating "
|
|
129
|
+
"to Microsoft Fabric via Active Directory Service Principal."
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
# Validate that tenant_id looks like a UUID or a domain name.
|
|
133
|
+
# A truncated/malformed UUID (e.g. 'a815263-...' instead of '1a815263-...')
|
|
134
|
+
# causes a cryptic MSAL authority error — catch it here with a clear message.
|
|
135
|
+
_TENANT_UUID = re.compile(
|
|
136
|
+
r"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"
|
|
137
|
+
)
|
|
138
|
+
_TENANT_DOMAIN = re.compile(r"^[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$")
|
|
139
|
+
if not (_TENANT_UUID.match(tenant_id) or _TENANT_DOMAIN.match(tenant_id)):
|
|
140
|
+
raise BadRequest(
|
|
141
|
+
f"Invalid tenant_id '{tenant_id}': must be a full UUID "
|
|
142
|
+
f"(e.g. '1a815263-e4f7-4552-b141-ce31ce582f69') or a domain "
|
|
143
|
+
f"(e.g. 'contoso.onmicrosoft.com'). "
|
|
144
|
+
f"Check the stored tenant_id — a common mistake is a truncated UUID "
|
|
145
|
+
f"(missing leading characters)."
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
# --- Secure diagnostic logging (never logs the full secret) ---
|
|
149
|
+
_secret = cfg.password or ""
|
|
150
|
+
_masked = (
|
|
151
|
+
f"{_secret[:3]}...{_secret[-3:]}" if len(_secret) > 6 else "***"
|
|
152
|
+
)
|
|
153
|
+
logger.info(
|
|
154
|
+
f"Fetching Entra ID token | tenant_id={tenant_id} "
|
|
155
|
+
f"| client_id={cfg.username} "
|
|
156
|
+
f"| secret_len={len(_secret)} "
|
|
157
|
+
f"| secret_preview={_masked}"
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
try:
|
|
161
|
+
cred = ClientSecretCredential(
|
|
162
|
+
tenant_id=tenant_id,
|
|
163
|
+
client_id=cfg.username,
|
|
164
|
+
client_secret=cfg.password
|
|
165
|
+
)
|
|
166
|
+
token_obj = cred.get_token("https://database.windows.net/.default")
|
|
167
|
+
logger.info(f"Entra ID token acquired via tenant_id={tenant_id}")
|
|
168
|
+
except Exception as auth_err:
|
|
169
|
+
raise BadRequest(
|
|
170
|
+
f"Authentication failed for tenant_id: {auth_err}. "
|
|
171
|
+
"Verify that tenant_id, client_id (user), and client_secret (password) "
|
|
172
|
+
"are all correct and the App Registration has not expired."
|
|
173
|
+
) from auth_err
|
|
174
|
+
|
|
175
|
+
# Encode the OAuth token to UTF-16-LE bytes.
|
|
176
|
+
# Pack the payload into the binary structure required by ODBC Driver 18:
|
|
177
|
+
# [4 bytes representing the length of the token string] followed by [the token string bytes].
|
|
178
|
+
token_bytes = token_obj.token.encode("utf-16-le")
|
|
179
|
+
token_info["token"] = struct.pack(f"<I{len(token_bytes)}s", len(token_bytes), token_bytes)
|
|
180
|
+
token_info["expires_at"] = token_obj.expires_on
|
|
181
|
+
logger.info("Successfully fetched and cached fresh Entra ID access token.")
|
|
182
|
+
|
|
183
|
+
# Inject the packed binary token structure into pyodbc's attrs_before connection attribute list.
|
|
184
|
+
# Attribute ID 1256 maps to SQL_COPT_SS_ACCESS_TOKEN.
|
|
185
|
+
cparams["attrs_before"] = {1256: token_info["token"]}
|
|
186
|
+
|
|
187
|
+
self._engines[key] = engine
|
|
188
|
+
logger.info(
|
|
189
|
+
f"Created new Fabric SQL engine for {cfg.host}:{cfg.port}/{cfg.database} with dynamic token provider"
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
return self._engines[key]
|
|
193
|
+
|
|
194
|
+
@contextmanager
|
|
195
|
+
def get_connection(self, retries: int = 3, delay: int = 5, query_timeout: int = 30):
|
|
196
|
+
"""Context manager for database connections with retry mechanism"""
|
|
197
|
+
engine = self._get_engine()
|
|
198
|
+
last_exception = None
|
|
199
|
+
|
|
200
|
+
for attempt in range(retries):
|
|
201
|
+
conn = None
|
|
202
|
+
try:
|
|
203
|
+
conn = engine.connect()
|
|
204
|
+
yield conn
|
|
205
|
+
return
|
|
206
|
+
|
|
207
|
+
except (OperationalError, InterfaceError, ProgrammingError) as e:
|
|
208
|
+
last_exception = e
|
|
209
|
+
if conn is not None:
|
|
210
|
+
conn.close()
|
|
211
|
+
conn = None
|
|
212
|
+
|
|
213
|
+
error_message = extract_sql_server_error_message(str(e))
|
|
214
|
+
logger.warning(
|
|
215
|
+
f"Fabric SQL connection failed (attempt {attempt + 1}/{retries}): {error_message}"
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
if attempt < retries - 1:
|
|
219
|
+
time.sleep(delay)
|
|
220
|
+
delay += 5
|
|
221
|
+
continue
|
|
222
|
+
|
|
223
|
+
break
|
|
224
|
+
|
|
225
|
+
finally:
|
|
226
|
+
if conn is not None:
|
|
227
|
+
conn.close()
|
|
228
|
+
|
|
229
|
+
raise BadRequest(
|
|
230
|
+
f"Failed to connect to Fabric SQL after {retries} attempts"
|
|
231
|
+
) from last_exception
|
|
232
|
+
|
|
233
|
+
def close_connection(self):
|
|
234
|
+
"""Dispose all pooled engines managed by this class."""
|
|
235
|
+
for engine in self._engines.values():
|
|
236
|
+
engine.dispose()
|
|
237
|
+
self._engines.clear()
|
|
238
|
+
self._tokens.clear()
|
|
239
|
+
logger.info("All Fabric SQL engine pools disposed.")
|
|
240
|
+
|
|
241
|
+
def is_valid_connection(self) -> bool:
|
|
242
|
+
"""Pool health is managed by pool_pre_ping; no manual check needed."""
|
|
243
|
+
return True
|
|
244
|
+
|
|
245
|
+
def test_connection(self) -> bool:
|
|
246
|
+
"""Verify the connection is reachable. Raises on failure."""
|
|
247
|
+
try:
|
|
248
|
+
with self.get_connection() as conn:
|
|
249
|
+
conn.execute(text("SELECT 1"))
|
|
250
|
+
return True
|
|
251
|
+
except OperationalError as e:
|
|
252
|
+
if "authentication failed" in str(e).lower():
|
|
253
|
+
raise ValueError("Invalid username or password")
|
|
254
|
+
raise ConnectionError(f"Database error: {str(e)}")
|
|
255
|
+
except InterfaceError:
|
|
256
|
+
raise ConnectionError("Cannot connect to database server")
|
|
@@ -52,9 +52,6 @@ class FabricSQLWarehouseHandler(WarehouseHandler):
|
|
|
52
52
|
connection = self._build_connection()
|
|
53
53
|
return FabricSQLQueryExecutor(warehouse_connection=connection)
|
|
54
54
|
|
|
55
|
-
def get_constants_section(self) -> str:
|
|
56
|
-
return "mssql"
|
|
57
|
-
|
|
58
55
|
def get_query_object(self) -> MSSQLQuery:
|
|
59
56
|
return MSSQLQuery
|
|
60
57
|
|
|
@@ -1,183 +0,0 @@
|
|
|
1
|
-
import hashlib
|
|
2
|
-
import time
|
|
3
|
-
import threading
|
|
4
|
-
|
|
5
|
-
from contextlib import contextmanager
|
|
6
|
-
from typing import Dict
|
|
7
|
-
|
|
8
|
-
from sqlalchemy import create_engine, text
|
|
9
|
-
from sqlalchemy.engine import Engine
|
|
10
|
-
from sqlalchemy.exc import InterfaceError, OperationalError, ProgrammingError
|
|
11
|
-
|
|
12
|
-
from shared_kernel.dataclasses.warehouse_configs import FabricSQLConfig
|
|
13
|
-
from shared_kernel.exceptions.http_exceptions import BadRequest
|
|
14
|
-
from shared_kernel.interfaces.warehouse_connection import DataWarehouseConnection
|
|
15
|
-
from shared_kernel.data_warehouse_handlers.utils import extract_sql_server_error_message
|
|
16
|
-
from shared_kernel.logger import Logger
|
|
17
|
-
from shared_kernel.config import Config
|
|
18
|
-
|
|
19
|
-
config = Config()
|
|
20
|
-
logger = Logger(config.get("APP_NAME"))
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
class FabricSQLWarehouseConnection(DataWarehouseConnection):
|
|
24
|
-
|
|
25
|
-
_engines: Dict[str, Engine] = {}
|
|
26
|
-
_lock = threading.Lock()
|
|
27
|
-
|
|
28
|
-
def __init__(
|
|
29
|
-
self,
|
|
30
|
-
source_config: FabricSQLConfig,
|
|
31
|
-
set_default_schema: bool = True,
|
|
32
|
-
):
|
|
33
|
-
if source_config is None:
|
|
34
|
-
raise ValueError("Missing required parameter: source_config (FabricSQLConfig)")
|
|
35
|
-
|
|
36
|
-
self._fabric_config = source_config
|
|
37
|
-
self.org_schema = source_config.schema if source_config.schema else None
|
|
38
|
-
self.set_default_schema = set_default_schema
|
|
39
|
-
|
|
40
|
-
pagination = getattr(source_config, "pagination", None) or {}
|
|
41
|
-
self.page = pagination.get("page") if isinstance(pagination, dict) else None
|
|
42
|
-
self.limit = pagination.get("limit") if isinstance(pagination, dict) else None
|
|
43
|
-
self.search = pagination.get("search") if isinstance(pagination, dict) else None
|
|
44
|
-
|
|
45
|
-
def _engine_key(self, cfg: FabricSQLConfig) -> str:
|
|
46
|
-
pwd_hash = hashlib.sha256(cfg.password.encode()).hexdigest()
|
|
47
|
-
return f"{cfg.host}|{cfg.port}|{cfg.database}|{cfg.username}|{pwd_hash}"
|
|
48
|
-
|
|
49
|
-
def _get_fabric_connection_params(self, cfg: FabricSQLConfig) -> tuple:
|
|
50
|
-
"""
|
|
51
|
-
Builds the token authentication and connection parameters for Microsoft Fabric SQL.
|
|
52
|
-
|
|
53
|
-
Returns:
|
|
54
|
-
tuple: (connection_string, connect_args)
|
|
55
|
-
"""
|
|
56
|
-
logger.info("Using Fabric SQL connector (pyodbc native) with access token authentication")
|
|
57
|
-
tenant_id = getattr(cfg, "tenant_id", None)
|
|
58
|
-
if not tenant_id:
|
|
59
|
-
raise ValueError(
|
|
60
|
-
"tenant_id is required in FabricSQLConfig for authenticating to Microsoft Fabric via Active Directory Service Principal."
|
|
61
|
-
)
|
|
62
|
-
|
|
63
|
-
from azure.identity import ClientSecretCredential
|
|
64
|
-
import struct
|
|
65
|
-
|
|
66
|
-
# Get OAuth2 token for Azure SQL / Fabric from Microsoft Entra ID
|
|
67
|
-
cred = ClientSecretCredential(
|
|
68
|
-
tenant_id=tenant_id,
|
|
69
|
-
client_id=cfg.username,
|
|
70
|
-
client_secret=cfg.password
|
|
71
|
-
)
|
|
72
|
-
token_obj = cred.get_token("https://database.windows.net/.default")
|
|
73
|
-
|
|
74
|
-
# Encode token as UTF-16-LE and pack into structure expected by the SQL Server ODBC driver.
|
|
75
|
-
# The binary layout expects [4 bytes for length of token payload] followed by the [raw token bytes].
|
|
76
|
-
token_bytes = token_obj.token.encode("utf-16-le")
|
|
77
|
-
token_struct = struct.pack(f"<I{len(token_bytes)}s", len(token_bytes), token_bytes)
|
|
78
|
-
|
|
79
|
-
import urllib.parse
|
|
80
|
-
# Build ODBC params. We omit UID and Password fields entirely from the connection string.
|
|
81
|
-
# Authentication is handled solely by injecting the access token into the connection handle.
|
|
82
|
-
odbc_params = urllib.parse.quote_plus(
|
|
83
|
-
f"DRIVER={{ODBC Driver 18 for SQL Server}};"
|
|
84
|
-
f"SERVER={cfg.host},{int(cfg.port)};"
|
|
85
|
-
f"DATABASE={cfg.database};"
|
|
86
|
-
"Encrypt=yes;"
|
|
87
|
-
"TrustServerCertificate=no;"
|
|
88
|
-
)
|
|
89
|
-
# We use odbc_connect query param to prevent SQLAlchemy dialect from auto-injecting
|
|
90
|
-
# conflicting credentials/parameters (avoiding error FA005).
|
|
91
|
-
connection_string = f"mssql+pyodbc:///?odbc_connect={odbc_params}"
|
|
92
|
-
|
|
93
|
-
# Inject the token using ODBC attribute SQL_COPT_SS_ACCESS_TOKEN (attribute ID 1256)
|
|
94
|
-
# before the connection is established.
|
|
95
|
-
connect_args = {"attrs_before": {1256: token_struct}}
|
|
96
|
-
return connection_string, connect_args
|
|
97
|
-
|
|
98
|
-
def _get_engine(self) -> Engine:
|
|
99
|
-
cfg = self._fabric_config
|
|
100
|
-
key = self._engine_key(cfg)
|
|
101
|
-
|
|
102
|
-
if key not in self._engines:
|
|
103
|
-
with self.__class__._lock: # thread-safe
|
|
104
|
-
if key not in self._engines: # recheck
|
|
105
|
-
connection_string, connect_args = self._get_fabric_connection_params(cfg)
|
|
106
|
-
|
|
107
|
-
self._engines[key] = create_engine(
|
|
108
|
-
connection_string,
|
|
109
|
-
connect_args=connect_args,
|
|
110
|
-
pool_size=10,
|
|
111
|
-
max_overflow=5,
|
|
112
|
-
pool_timeout=30,
|
|
113
|
-
pool_recycle=1800,
|
|
114
|
-
pool_pre_ping=True,
|
|
115
|
-
)
|
|
116
|
-
logger.info(
|
|
117
|
-
f"Created new Fabric SQL engine for {cfg.host}:{cfg.port}/{cfg.database}"
|
|
118
|
-
)
|
|
119
|
-
|
|
120
|
-
return self._engines[key]
|
|
121
|
-
|
|
122
|
-
@contextmanager
|
|
123
|
-
def get_connection(self, retries: int = 3, delay: int = 5, query_timeout: int = 30):
|
|
124
|
-
"""Context manager for database connections with retry mechanism"""
|
|
125
|
-
engine = self._get_engine()
|
|
126
|
-
last_exception = None
|
|
127
|
-
|
|
128
|
-
for attempt in range(retries):
|
|
129
|
-
conn = None
|
|
130
|
-
try:
|
|
131
|
-
conn = engine.connect()
|
|
132
|
-
yield conn
|
|
133
|
-
return
|
|
134
|
-
|
|
135
|
-
except (OperationalError, InterfaceError, ProgrammingError) as e:
|
|
136
|
-
last_exception = e
|
|
137
|
-
if conn is not None:
|
|
138
|
-
conn.close()
|
|
139
|
-
conn = None
|
|
140
|
-
|
|
141
|
-
error_message = extract_sql_server_error_message(str(e))
|
|
142
|
-
logger.warning(
|
|
143
|
-
f"Fabric SQL connection failed (attempt {attempt + 1}/{retries}): {error_message}"
|
|
144
|
-
)
|
|
145
|
-
|
|
146
|
-
if attempt < retries - 1:
|
|
147
|
-
time.sleep(delay)
|
|
148
|
-
delay += 5
|
|
149
|
-
continue
|
|
150
|
-
|
|
151
|
-
break
|
|
152
|
-
|
|
153
|
-
finally:
|
|
154
|
-
if conn is not None:
|
|
155
|
-
conn.close()
|
|
156
|
-
|
|
157
|
-
raise BadRequest(
|
|
158
|
-
f"Failed to connect to Fabric SQL after {retries} attempts"
|
|
159
|
-
) from last_exception
|
|
160
|
-
|
|
161
|
-
def close_connection(self):
|
|
162
|
-
"""Dispose all pooled engines managed by this class."""
|
|
163
|
-
for engine in self._engines.values():
|
|
164
|
-
engine.dispose()
|
|
165
|
-
self._engines.clear()
|
|
166
|
-
logger.info("All Fabric SQL engine pools disposed.")
|
|
167
|
-
|
|
168
|
-
def is_valid_connection(self) -> bool:
|
|
169
|
-
"""Pool health is managed by pool_pre_ping; no manual check needed."""
|
|
170
|
-
return True
|
|
171
|
-
|
|
172
|
-
def test_connection(self) -> bool:
|
|
173
|
-
"""Verify the connection is reachable. Raises on failure."""
|
|
174
|
-
try:
|
|
175
|
-
with self.get_connection() as conn:
|
|
176
|
-
conn.execute(text("SELECT 1"))
|
|
177
|
-
return True
|
|
178
|
-
except OperationalError as e:
|
|
179
|
-
if "authentication failed" in str(e).lower():
|
|
180
|
-
raise ValueError("Invalid username or password")
|
|
181
|
-
raise ConnectionError(f"Database error: {str(e)}")
|
|
182
|
-
except InterfaceError:
|
|
183
|
-
raise ConnectionError("Cannot connect to database server")
|
|
File without changes
|
|
File without changes
|
{fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/fdc_shared_kernel.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
{fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/fdc_shared_kernel.egg-info/requires.txt
RENAMED
|
File without changes
|
{fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/fdc_shared_kernel.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/ai_audit_log/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/constants/constants.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|