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.
Files changed (198) hide show
  1. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/PKG-INFO +1 -1
  2. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/fdc_shared_kernel.egg-info/PKG-INFO +1 -1
  3. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/pyproject.toml +1 -1
  4. fdc_shared_kernel-0.0.193/shared_kernel/data_warehouse_handlers/connections/fabric_sql_warehouse_connection.py +256 -0
  5. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/data_warehouse_handlers/fabric_sql_handler.py +0 -3
  6. fdc_shared_kernel-0.0.191/shared_kernel/data_warehouse_handlers/connections/fabric_sql_warehouse_connection.py +0 -183
  7. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/README.md +0 -0
  8. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/README_pypi.md +0 -0
  9. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/fdc_shared_kernel.egg-info/SOURCES.txt +0 -0
  10. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/fdc_shared_kernel.egg-info/dependency_links.txt +0 -0
  11. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/fdc_shared_kernel.egg-info/requires.txt +0 -0
  12. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/fdc_shared_kernel.egg-info/top_level.txt +0 -0
  13. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/requirements.txt +0 -0
  14. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/setup.cfg +0 -0
  15. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/__init__.py +0 -0
  16. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/agent_communication_channel/__init__.py +0 -0
  17. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/agent_communication_channel/agent_channel.py +0 -0
  18. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/agent_communication_channel/contexts.py +0 -0
  19. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/agent_communication_channel/event_channel.py +0 -0
  20. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/ai_audit_log/__init__.py +0 -0
  21. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/ai_chat_persistence/__init__.py +0 -0
  22. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/ai_chat_persistence/chat_persistence.py +0 -0
  23. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/async_task_executor/__init__.py +0 -0
  24. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/async_task_executor/async_task_executor.py +0 -0
  25. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/auth/__init__.py +0 -0
  26. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/auth/jwt_helper.py +0 -0
  27. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/auth/token_handler.py +0 -0
  28. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/auth/workbook_permission_handler.py +0 -0
  29. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/batch_job/__init__.py +0 -0
  30. {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
  31. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/batch_job/job_handlers/__init__.py +0 -0
  32. {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
  33. {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
  34. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/batch_job/job_handlers/job_handler_factory.py +0 -0
  35. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/batch_job/job_handlers/job_status_mapper.py +0 -0
  36. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/batch_job/job_storage_managers/__init__.py +0 -0
  37. {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
  38. {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
  39. {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
  40. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/config/__init__.py +0 -0
  41. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/constants/constants.py +0 -0
  42. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/data_warehouse_handlers/__init__.py +0 -0
  43. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/data_warehouse_handlers/connections/databricks_warehouse_connection.py +0 -0
  44. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/data_warehouse_handlers/connections/jdbc_warehouse_connection.py +0 -0
  45. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/data_warehouse_handlers/connections/mssql_warehouse_connection.py +0 -0
  46. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/data_warehouse_handlers/connections/mysql_warehouse_connection.py +0 -0
  47. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/data_warehouse_handlers/connections/postgresql_warehouse_connection.py +0 -0
  48. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/data_warehouse_handlers/connections/redshift_warehouse_connection.py +0 -0
  49. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/data_warehouse_handlers/connections/snowflake_warehouse_connection.py +0 -0
  50. {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
  51. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/data_warehouse_handlers/databricks_handler.py +0 -0
  52. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/data_warehouse_handlers/factory.py +0 -0
  53. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/data_warehouse_handlers/jdbc_driver_registry.py +0 -0
  54. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/data_warehouse_handlers/jdbc_handler.py +0 -0
  55. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/data_warehouse_handlers/mssql_handler.py +0 -0
  56. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/data_warehouse_handlers/mysql_handler.py +0 -0
  57. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/data_warehouse_handlers/odbc_handler.py +0 -0
  58. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/data_warehouse_handlers/postgresql_handler.py +0 -0
  59. {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
  60. {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
  61. {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
  62. {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
  63. {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
  64. {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
  65. {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
  66. {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
  67. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/data_warehouse_handlers/redshift_handler.py +0 -0
  68. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/data_warehouse_handlers/snowflake_handler.py +0 -0
  69. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/data_warehouse_handlers/utils.py +0 -0
  70. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/data_warehouse_handlers/warehouse_handlers_map.py +0 -0
  71. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/database/__init__.py +0 -0
  72. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/dataclasses/__init__.py +0 -0
  73. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/dataclasses/aws_batch_job_context.py +0 -0
  74. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/dataclasses/azure_container_app_job_context.py +0 -0
  75. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/dataclasses/event_executor.py +0 -0
  76. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/dataclasses/job_context.py +0 -0
  77. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/dataclasses/job_record_dataclasses.py +0 -0
  78. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/dataclasses/warehouse_configs.py +0 -0
  79. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/datatype_mappings/__init__.py +0 -0
  80. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/datatype_mappings/connectors_to_system/__init__.py +0 -0
  81. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/datatype_mappings/connectors_to_system/csv.py +0 -0
  82. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/datatype_mappings/connectors_to_system/databricks.py +0 -0
  83. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/datatype_mappings/connectors_to_system/db2.py +0 -0
  84. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/datatype_mappings/connectors_to_system/jdbc.py +0 -0
  85. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/datatype_mappings/connectors_to_system/jira.py +0 -0
  86. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/datatype_mappings/connectors_to_system/mssql.py +0 -0
  87. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/datatype_mappings/connectors_to_system/mysql.py +0 -0
  88. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/datatype_mappings/connectors_to_system/oracle.py +0 -0
  89. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/datatype_mappings/connectors_to_system/postgres.py +0 -0
  90. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/datatype_mappings/connectors_to_system/redshift.py +0 -0
  91. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/datatype_mappings/connectors_to_system/salesforce.py +0 -0
  92. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/datatype_mappings/connectors_to_system/snowflake.py +0 -0
  93. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/datatype_mappings/connectors_to_system/sqlalchemy.py +0 -0
  94. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/datatype_mappings/system_to_warehouse/__init__.py +0 -0
  95. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/datatype_mappings/system_to_warehouse/postgres.py +0 -0
  96. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/datatype_mappings/system_to_warehouse/redshift.py +0 -0
  97. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/enums/__init__.py +0 -0
  98. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/enums/async_task_executor.py +0 -0
  99. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/enums/connection_engine.py +0 -0
  100. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/enums/status_tracker.py +0 -0
  101. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/event_executor/__init__.py +0 -0
  102. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/event_executor/event_executor.py +0 -0
  103. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/event_executor/job_executor.py +0 -0
  104. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/event_executor/utils.py +0 -0
  105. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/exceptions/__init__.py +0 -0
  106. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/exceptions/batch_job_exceptions.py +0 -0
  107. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/exceptions/configuration_exceptions.py +0 -0
  108. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/exceptions/custom_exceptions.py +0 -0
  109. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/exceptions/data_validation_exceptions.py +0 -0
  110. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/exceptions/http_exceptions.py +0 -0
  111. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/exceptions/infrastructure_exceptions.py +0 -0
  112. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/exceptions/operational_exceptions.py +0 -0
  113. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/exceptions/security_exceptions.py +0 -0
  114. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/exceptions/user_intervention_exception.py +0 -0
  115. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/http/__init__.py +0 -0
  116. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/http/httpx_http_client.py +0 -0
  117. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/http/request_http_client.py +0 -0
  118. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/interfaces/__init__.py +0 -0
  119. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/interfaces/base_job_handler.py +0 -0
  120. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/interfaces/data_warehouse_handler.py +0 -0
  121. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/interfaces/databus.py +0 -0
  122. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/interfaces/encryption.py +0 -0
  123. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/interfaces/http.py +0 -0
  124. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/interfaces/keyvault.py +0 -0
  125. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/interfaces/query_executor.py +0 -0
  126. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/interfaces/storage_manager.py +0 -0
  127. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/interfaces/warehouse_connection.py +0 -0
  128. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/logger/__init__.py +0 -0
  129. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/messaging/__init__.py +0 -0
  130. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/messaging/aws_databus.py +0 -0
  131. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/messaging/azure_databus.py +0 -0
  132. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/messaging/http_databus.py +0 -0
  133. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/messaging/nats_databus.py +0 -0
  134. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/messaging/nats_publisher.py +0 -0
  135. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/messaging/nats_test.py +0 -0
  136. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/messaging/utils/aws_utility.py +0 -0
  137. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/messaging/utils/azure_utility.py +0 -0
  138. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/messaging/utils/event_messages.py +0 -0
  139. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/metrics/__init__.py +0 -0
  140. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/metrics/status_tracker.py +0 -0
  141. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/models/__init__.py +0 -0
  142. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/registries/__init__.py +0 -0
  143. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/registries/schema_onboarding_event_registry.py +0 -0
  144. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/registries/service_event_registry.py +0 -0
  145. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/schema_onboarding_tracker/__init__.py +0 -0
  146. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/schema_onboarding_tracker/base_tracker.py +0 -0
  147. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/schema_onboarding_tracker/enums.py +0 -0
  148. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/schema_onboarding_tracker/exceptions.py +0 -0
  149. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/schema_onboarding_tracker/run_manager.py +0 -0
  150. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/schema_onboarding_tracker/schema_onboarding_tracker.py +0 -0
  151. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/schema_onboarding_tracker/shared_helpers.py +0 -0
  152. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/schema_onboarding_tracker/task_determination.py +0 -0
  153. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/schema_onboarding_tracker/task_helpers.py +0 -0
  154. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/schema_onboarding_tracker/task_manager.py +0 -0
  155. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/schema_onboarding_tracker/template_manager.py +0 -0
  156. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/schema_onboarding_tracker/utility_manager.py +0 -0
  157. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/security/__init__.py +0 -0
  158. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/security/encryption/__init__.py +0 -0
  159. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/security/encryption/manager.py +0 -0
  160. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/security/encryption/providers.py +0 -0
  161. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/security/key_vault/__init__.py +0 -0
  162. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/security/key_vault/aws_secret_manager.py +0 -0
  163. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/security/key_vault/azure_keyvault.py +0 -0
  164. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/status_tracker/__init__.py +0 -0
  165. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/status_tracker/status_tracker.py +0 -0
  166. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/storage_handlers/__init__.py +0 -0
  167. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/storage_handlers/blob_file_handler.py +0 -0
  168. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/storage_handlers/s3_storage_handler.py +0 -0
  169. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/tests/__init__.py +0 -0
  170. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/tests/config/__init__.py +0 -0
  171. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/tests/config/test_config.py +0 -0
  172. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/tests/logger/__init__.py +0 -0
  173. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/tests/logger/test_logger.py +0 -0
  174. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/tests/messaging/__init__.py +0 -0
  175. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/tests/messaging/test_aws_databus.py +0 -0
  176. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/tests/messaging/test_event_executor.py +0 -0
  177. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/tests/messaging/test_nats_interface.py +0 -0
  178. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/tests/security/test_encryption.py +0 -0
  179. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/tests/utils/__init__.py +0 -0
  180. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/tests/utils/test_data_validators.py +0 -0
  181. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/tests/utils/test_date_format_utils.py +0 -0
  182. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/tests/utils/test_string_utils.py +0 -0
  183. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/traceability/__init__.py +0 -0
  184. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/traceability/traceability_handler.py +0 -0
  185. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/utils/__init__.py +0 -0
  186. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/utils/data_validators_utils.py +0 -0
  187. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/utils/date_format_utils.py +0 -0
  188. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/utils/date_utils.py +0 -0
  189. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/utils/string_utils.py +0 -0
  190. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/utils/template_renderer.py +0 -0
  191. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/utils/thread_debug_utils.py +0 -0
  192. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/utils/thread_local_storage.py +0 -0
  193. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/shared_kernel/utils/thread_log_traker.py +0 -0
  194. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/tests/__init__.py +0 -0
  195. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/tests/messaging/__init__.py +0 -0
  196. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/tests/messaging/utils/__init__.py +0 -0
  197. {fdc_shared_kernel-0.0.191 → fdc_shared_kernel-0.0.193}/tests/messaging/utils/test_aws_utility.py +0 -0
  198. {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.191
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.191
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
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "fdc_shared_kernel"
7
- version = "0.0.191"
7
+ version = "0.0.193"
8
8
  requires-python = ">=3.7"
9
9
  readme = "README_pypi.md"
10
10
  description = "Shared library for microservice"
@@ -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")