airbyte-cdk 0.0.0.dev0__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.
Files changed (368) hide show
  1. airbyte_cdk/__init__.py +358 -0
  2. airbyte_cdk/cli/__init__.py +1 -0
  3. airbyte_cdk/cli/source_declarative_manifest/__init__.py +5 -0
  4. airbyte_cdk/cli/source_declarative_manifest/_run.py +236 -0
  5. airbyte_cdk/cli/source_declarative_manifest/spec.json +17 -0
  6. airbyte_cdk/config_observation.py +104 -0
  7. airbyte_cdk/connector.py +123 -0
  8. airbyte_cdk/connector_builder/README.md +53 -0
  9. airbyte_cdk/connector_builder/__init__.py +3 -0
  10. airbyte_cdk/connector_builder/connector_builder_handler.py +121 -0
  11. airbyte_cdk/connector_builder/main.py +107 -0
  12. airbyte_cdk/connector_builder/models.py +73 -0
  13. airbyte_cdk/connector_builder/test_reader/__init__.py +7 -0
  14. airbyte_cdk/connector_builder/test_reader/helpers.py +689 -0
  15. airbyte_cdk/connector_builder/test_reader/message_grouper.py +173 -0
  16. airbyte_cdk/connector_builder/test_reader/reader.py +441 -0
  17. airbyte_cdk/connector_builder/test_reader/types.py +83 -0
  18. airbyte_cdk/destinations/__init__.py +8 -0
  19. airbyte_cdk/destinations/destination.py +154 -0
  20. airbyte_cdk/destinations/vector_db_based/README.md +37 -0
  21. airbyte_cdk/destinations/vector_db_based/__init__.py +38 -0
  22. airbyte_cdk/destinations/vector_db_based/config.py +298 -0
  23. airbyte_cdk/destinations/vector_db_based/document_processor.py +223 -0
  24. airbyte_cdk/destinations/vector_db_based/embedder.py +303 -0
  25. airbyte_cdk/destinations/vector_db_based/indexer.py +78 -0
  26. airbyte_cdk/destinations/vector_db_based/test_utils.py +63 -0
  27. airbyte_cdk/destinations/vector_db_based/utils.py +35 -0
  28. airbyte_cdk/destinations/vector_db_based/writer.py +104 -0
  29. airbyte_cdk/entrypoint.py +414 -0
  30. airbyte_cdk/exception_handler.py +56 -0
  31. airbyte_cdk/logger.py +109 -0
  32. airbyte_cdk/models/__init__.py +72 -0
  33. airbyte_cdk/models/airbyte_protocol.py +88 -0
  34. airbyte_cdk/models/airbyte_protocol_serializers.py +44 -0
  35. airbyte_cdk/models/well_known_types.py +5 -0
  36. airbyte_cdk/py.typed +0 -0
  37. airbyte_cdk/sources/__init__.py +26 -0
  38. airbyte_cdk/sources/abstract_source.py +326 -0
  39. airbyte_cdk/sources/concurrent_source/__init__.py +8 -0
  40. airbyte_cdk/sources/concurrent_source/concurrent_read_processor.py +255 -0
  41. airbyte_cdk/sources/concurrent_source/concurrent_source.py +165 -0
  42. airbyte_cdk/sources/concurrent_source/concurrent_source_adapter.py +147 -0
  43. airbyte_cdk/sources/concurrent_source/partition_generation_completed_sentinel.py +24 -0
  44. airbyte_cdk/sources/concurrent_source/stream_thread_exception.py +25 -0
  45. airbyte_cdk/sources/concurrent_source/thread_pool_manager.py +115 -0
  46. airbyte_cdk/sources/config.py +27 -0
  47. airbyte_cdk/sources/connector_state_manager.py +161 -0
  48. airbyte_cdk/sources/declarative/__init__.py +3 -0
  49. airbyte_cdk/sources/declarative/async_job/__init__.py +0 -0
  50. airbyte_cdk/sources/declarative/async_job/job.py +52 -0
  51. airbyte_cdk/sources/declarative/async_job/job_orchestrator.py +525 -0
  52. airbyte_cdk/sources/declarative/async_job/job_tracker.py +79 -0
  53. airbyte_cdk/sources/declarative/async_job/repository.py +35 -0
  54. airbyte_cdk/sources/declarative/async_job/status.py +24 -0
  55. airbyte_cdk/sources/declarative/async_job/timer.py +39 -0
  56. airbyte_cdk/sources/declarative/auth/__init__.py +8 -0
  57. airbyte_cdk/sources/declarative/auth/declarative_authenticator.py +42 -0
  58. airbyte_cdk/sources/declarative/auth/jwt.py +197 -0
  59. airbyte_cdk/sources/declarative/auth/oauth.py +293 -0
  60. airbyte_cdk/sources/declarative/auth/selective_authenticator.py +45 -0
  61. airbyte_cdk/sources/declarative/auth/token.py +267 -0
  62. airbyte_cdk/sources/declarative/auth/token_provider.py +82 -0
  63. airbyte_cdk/sources/declarative/checks/__init__.py +24 -0
  64. airbyte_cdk/sources/declarative/checks/check_dynamic_stream.py +61 -0
  65. airbyte_cdk/sources/declarative/checks/check_stream.py +56 -0
  66. airbyte_cdk/sources/declarative/checks/connection_checker.py +35 -0
  67. airbyte_cdk/sources/declarative/concurrency_level/__init__.py +7 -0
  68. airbyte_cdk/sources/declarative/concurrency_level/concurrency_level.py +50 -0
  69. airbyte_cdk/sources/declarative/concurrent_declarative_source.py +526 -0
  70. airbyte_cdk/sources/declarative/datetime/__init__.py +3 -0
  71. airbyte_cdk/sources/declarative/datetime/datetime_parser.py +65 -0
  72. airbyte_cdk/sources/declarative/datetime/min_max_datetime.py +118 -0
  73. airbyte_cdk/sources/declarative/declarative_component_schema.yaml +3975 -0
  74. airbyte_cdk/sources/declarative/declarative_source.py +36 -0
  75. airbyte_cdk/sources/declarative/declarative_stream.py +241 -0
  76. airbyte_cdk/sources/declarative/decoders/__init__.py +33 -0
  77. airbyte_cdk/sources/declarative/decoders/composite_raw_decoder.py +218 -0
  78. airbyte_cdk/sources/declarative/decoders/decoder.py +32 -0
  79. airbyte_cdk/sources/declarative/decoders/decoder_parser.py +30 -0
  80. airbyte_cdk/sources/declarative/decoders/json_decoder.py +65 -0
  81. airbyte_cdk/sources/declarative/decoders/noop_decoder.py +21 -0
  82. airbyte_cdk/sources/declarative/decoders/pagination_decoder_decorator.py +39 -0
  83. airbyte_cdk/sources/declarative/decoders/xml_decoder.py +98 -0
  84. airbyte_cdk/sources/declarative/decoders/zipfile_decoder.py +56 -0
  85. airbyte_cdk/sources/declarative/exceptions.py +9 -0
  86. airbyte_cdk/sources/declarative/extractors/__init__.py +21 -0
  87. airbyte_cdk/sources/declarative/extractors/dpath_extractor.py +86 -0
  88. airbyte_cdk/sources/declarative/extractors/http_selector.py +37 -0
  89. airbyte_cdk/sources/declarative/extractors/record_extractor.py +27 -0
  90. airbyte_cdk/sources/declarative/extractors/record_filter.py +91 -0
  91. airbyte_cdk/sources/declarative/extractors/record_selector.py +170 -0
  92. airbyte_cdk/sources/declarative/extractors/response_to_file_extractor.py +176 -0
  93. airbyte_cdk/sources/declarative/extractors/type_transformer.py +55 -0
  94. airbyte_cdk/sources/declarative/incremental/__init__.py +37 -0
  95. airbyte_cdk/sources/declarative/incremental/concurrent_partition_cursor.py +497 -0
  96. airbyte_cdk/sources/declarative/incremental/datetime_based_cursor.py +459 -0
  97. airbyte_cdk/sources/declarative/incremental/declarative_cursor.py +13 -0
  98. airbyte_cdk/sources/declarative/incremental/global_substream_cursor.py +357 -0
  99. airbyte_cdk/sources/declarative/incremental/per_partition_cursor.py +380 -0
  100. airbyte_cdk/sources/declarative/incremental/per_partition_with_global.py +200 -0
  101. airbyte_cdk/sources/declarative/incremental/resumable_full_refresh_cursor.py +122 -0
  102. airbyte_cdk/sources/declarative/interpolation/__init__.py +9 -0
  103. airbyte_cdk/sources/declarative/interpolation/filters.py +139 -0
  104. airbyte_cdk/sources/declarative/interpolation/interpolated_boolean.py +66 -0
  105. airbyte_cdk/sources/declarative/interpolation/interpolated_mapping.py +56 -0
  106. airbyte_cdk/sources/declarative/interpolation/interpolated_nested_mapping.py +52 -0
  107. airbyte_cdk/sources/declarative/interpolation/interpolated_string.py +79 -0
  108. airbyte_cdk/sources/declarative/interpolation/interpolation.py +34 -0
  109. airbyte_cdk/sources/declarative/interpolation/jinja.py +161 -0
  110. airbyte_cdk/sources/declarative/interpolation/macros.py +191 -0
  111. airbyte_cdk/sources/declarative/manifest_declarative_source.py +421 -0
  112. airbyte_cdk/sources/declarative/migrations/__init__.py +0 -0
  113. airbyte_cdk/sources/declarative/migrations/legacy_to_per_partition_state_migration.py +98 -0
  114. airbyte_cdk/sources/declarative/migrations/state_migration.py +24 -0
  115. airbyte_cdk/sources/declarative/models/__init__.py +2 -0
  116. airbyte_cdk/sources/declarative/models/declarative_component_schema.py +2503 -0
  117. airbyte_cdk/sources/declarative/parsers/__init__.py +3 -0
  118. airbyte_cdk/sources/declarative/parsers/custom_code_compiler.py +157 -0
  119. airbyte_cdk/sources/declarative/parsers/custom_exceptions.py +21 -0
  120. airbyte_cdk/sources/declarative/parsers/manifest_component_transformer.py +172 -0
  121. airbyte_cdk/sources/declarative/parsers/manifest_reference_resolver.py +213 -0
  122. airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py +3407 -0
  123. airbyte_cdk/sources/declarative/partition_routers/__init__.py +29 -0
  124. airbyte_cdk/sources/declarative/partition_routers/async_job_partition_router.py +65 -0
  125. airbyte_cdk/sources/declarative/partition_routers/cartesian_product_stream_slicer.py +176 -0
  126. airbyte_cdk/sources/declarative/partition_routers/list_partition_router.py +121 -0
  127. airbyte_cdk/sources/declarative/partition_routers/partition_router.py +62 -0
  128. airbyte_cdk/sources/declarative/partition_routers/single_partition_router.py +63 -0
  129. airbyte_cdk/sources/declarative/partition_routers/substream_partition_router.py +437 -0
  130. airbyte_cdk/sources/declarative/requesters/README.md +56 -0
  131. airbyte_cdk/sources/declarative/requesters/__init__.py +9 -0
  132. airbyte_cdk/sources/declarative/requesters/error_handlers/__init__.py +25 -0
  133. airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/__init__.py +23 -0
  134. airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/constant_backoff_strategy.py +45 -0
  135. airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/exponential_backoff_strategy.py +45 -0
  136. airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/header_helper.py +41 -0
  137. airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/wait_time_from_header_backoff_strategy.py +70 -0
  138. airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/wait_until_time_from_header_backoff_strategy.py +77 -0
  139. airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategy.py +17 -0
  140. airbyte_cdk/sources/declarative/requesters/error_handlers/composite_error_handler.py +101 -0
  141. airbyte_cdk/sources/declarative/requesters/error_handlers/default_error_handler.py +147 -0
  142. airbyte_cdk/sources/declarative/requesters/error_handlers/default_http_response_filter.py +40 -0
  143. airbyte_cdk/sources/declarative/requesters/error_handlers/error_handler.py +17 -0
  144. airbyte_cdk/sources/declarative/requesters/error_handlers/http_response_filter.py +179 -0
  145. airbyte_cdk/sources/declarative/requesters/http_job_repository.py +350 -0
  146. airbyte_cdk/sources/declarative/requesters/http_requester.py +433 -0
  147. airbyte_cdk/sources/declarative/requesters/paginators/__init__.py +21 -0
  148. airbyte_cdk/sources/declarative/requesters/paginators/default_paginator.py +327 -0
  149. airbyte_cdk/sources/declarative/requesters/paginators/no_pagination.py +76 -0
  150. airbyte_cdk/sources/declarative/requesters/paginators/paginator.py +65 -0
  151. airbyte_cdk/sources/declarative/requesters/paginators/strategies/__init__.py +25 -0
  152. airbyte_cdk/sources/declarative/requesters/paginators/strategies/cursor_pagination_strategy.py +98 -0
  153. airbyte_cdk/sources/declarative/requesters/paginators/strategies/offset_increment.py +102 -0
  154. airbyte_cdk/sources/declarative/requesters/paginators/strategies/page_increment.py +71 -0
  155. airbyte_cdk/sources/declarative/requesters/paginators/strategies/pagination_strategy.py +48 -0
  156. airbyte_cdk/sources/declarative/requesters/paginators/strategies/stop_condition.py +66 -0
  157. airbyte_cdk/sources/declarative/requesters/request_option.py +117 -0
  158. airbyte_cdk/sources/declarative/requesters/request_options/__init__.py +23 -0
  159. airbyte_cdk/sources/declarative/requesters/request_options/datetime_based_request_options_provider.py +92 -0
  160. airbyte_cdk/sources/declarative/requesters/request_options/default_request_options_provider.py +60 -0
  161. airbyte_cdk/sources/declarative/requesters/request_options/interpolated_nested_request_input_provider.py +59 -0
  162. airbyte_cdk/sources/declarative/requesters/request_options/interpolated_request_input_provider.py +68 -0
  163. airbyte_cdk/sources/declarative/requesters/request_options/interpolated_request_options_provider.py +119 -0
  164. airbyte_cdk/sources/declarative/requesters/request_options/request_options_provider.py +79 -0
  165. airbyte_cdk/sources/declarative/requesters/request_path.py +15 -0
  166. airbyte_cdk/sources/declarative/requesters/requester.py +144 -0
  167. airbyte_cdk/sources/declarative/resolvers/__init__.py +41 -0
  168. airbyte_cdk/sources/declarative/resolvers/components_resolver.py +55 -0
  169. airbyte_cdk/sources/declarative/resolvers/config_components_resolver.py +136 -0
  170. airbyte_cdk/sources/declarative/resolvers/http_components_resolver.py +112 -0
  171. airbyte_cdk/sources/declarative/retrievers/__init__.py +19 -0
  172. airbyte_cdk/sources/declarative/retrievers/async_retriever.py +124 -0
  173. airbyte_cdk/sources/declarative/retrievers/file_uploader.py +89 -0
  174. airbyte_cdk/sources/declarative/retrievers/retriever.py +54 -0
  175. airbyte_cdk/sources/declarative/retrievers/simple_retriever.py +702 -0
  176. airbyte_cdk/sources/declarative/schema/__init__.py +25 -0
  177. airbyte_cdk/sources/declarative/schema/default_schema_loader.py +47 -0
  178. airbyte_cdk/sources/declarative/schema/dynamic_schema_loader.py +285 -0
  179. airbyte_cdk/sources/declarative/schema/inline_schema_loader.py +19 -0
  180. airbyte_cdk/sources/declarative/schema/json_file_schema_loader.py +92 -0
  181. airbyte_cdk/sources/declarative/schema/schema_loader.py +17 -0
  182. airbyte_cdk/sources/declarative/spec/__init__.py +7 -0
  183. airbyte_cdk/sources/declarative/spec/spec.py +48 -0
  184. airbyte_cdk/sources/declarative/stream_slicers/__init__.py +7 -0
  185. airbyte_cdk/sources/declarative/stream_slicers/declarative_partition_generator.py +93 -0
  186. airbyte_cdk/sources/declarative/stream_slicers/stream_slicer.py +25 -0
  187. airbyte_cdk/sources/declarative/transformations/__init__.py +17 -0
  188. airbyte_cdk/sources/declarative/transformations/add_fields.py +146 -0
  189. airbyte_cdk/sources/declarative/transformations/dpath_flatten_fields.py +61 -0
  190. airbyte_cdk/sources/declarative/transformations/flatten_fields.py +52 -0
  191. airbyte_cdk/sources/declarative/transformations/keys_replace_transformation.py +61 -0
  192. airbyte_cdk/sources/declarative/transformations/keys_to_lower_transformation.py +22 -0
  193. airbyte_cdk/sources/declarative/transformations/keys_to_snake_transformation.py +68 -0
  194. airbyte_cdk/sources/declarative/transformations/remove_fields.py +75 -0
  195. airbyte_cdk/sources/declarative/transformations/transformation.py +37 -0
  196. airbyte_cdk/sources/declarative/types.py +25 -0
  197. airbyte_cdk/sources/declarative/yaml_declarative_source.py +67 -0
  198. airbyte_cdk/sources/file_based/README.md +152 -0
  199. airbyte_cdk/sources/file_based/__init__.py +24 -0
  200. airbyte_cdk/sources/file_based/availability_strategy/__init__.py +11 -0
  201. airbyte_cdk/sources/file_based/availability_strategy/abstract_file_based_availability_strategy.py +73 -0
  202. airbyte_cdk/sources/file_based/availability_strategy/default_file_based_availability_strategy.py +149 -0
  203. airbyte_cdk/sources/file_based/config/__init__.py +0 -0
  204. airbyte_cdk/sources/file_based/config/abstract_file_based_spec.py +153 -0
  205. airbyte_cdk/sources/file_based/config/avro_format.py +25 -0
  206. airbyte_cdk/sources/file_based/config/csv_format.py +210 -0
  207. airbyte_cdk/sources/file_based/config/excel_format.py +18 -0
  208. airbyte_cdk/sources/file_based/config/file_based_stream_config.py +99 -0
  209. airbyte_cdk/sources/file_based/config/jsonl_format.py +18 -0
  210. airbyte_cdk/sources/file_based/config/parquet_format.py +25 -0
  211. airbyte_cdk/sources/file_based/config/unstructured_format.py +102 -0
  212. airbyte_cdk/sources/file_based/config/validate_config_transfer_modes.py +81 -0
  213. airbyte_cdk/sources/file_based/discovery_policy/__init__.py +8 -0
  214. airbyte_cdk/sources/file_based/discovery_policy/abstract_discovery_policy.py +21 -0
  215. airbyte_cdk/sources/file_based/discovery_policy/default_discovery_policy.py +33 -0
  216. airbyte_cdk/sources/file_based/exceptions.py +159 -0
  217. airbyte_cdk/sources/file_based/file_based_source.py +466 -0
  218. airbyte_cdk/sources/file_based/file_based_stream_permissions_reader.py +123 -0
  219. airbyte_cdk/sources/file_based/file_based_stream_reader.py +209 -0
  220. airbyte_cdk/sources/file_based/file_record_data.py +22 -0
  221. airbyte_cdk/sources/file_based/file_types/__init__.py +37 -0
  222. airbyte_cdk/sources/file_based/file_types/avro_parser.py +233 -0
  223. airbyte_cdk/sources/file_based/file_types/csv_parser.py +527 -0
  224. airbyte_cdk/sources/file_based/file_types/excel_parser.py +196 -0
  225. airbyte_cdk/sources/file_based/file_types/file_transfer.py +30 -0
  226. airbyte_cdk/sources/file_based/file_types/file_type_parser.py +86 -0
  227. airbyte_cdk/sources/file_based/file_types/jsonl_parser.py +145 -0
  228. airbyte_cdk/sources/file_based/file_types/parquet_parser.py +275 -0
  229. airbyte_cdk/sources/file_based/file_types/unstructured_parser.py +480 -0
  230. airbyte_cdk/sources/file_based/remote_file.py +18 -0
  231. airbyte_cdk/sources/file_based/schema_helpers.py +281 -0
  232. airbyte_cdk/sources/file_based/schema_validation_policies/__init__.py +17 -0
  233. airbyte_cdk/sources/file_based/schema_validation_policies/abstract_schema_validation_policy.py +20 -0
  234. airbyte_cdk/sources/file_based/schema_validation_policies/default_schema_validation_policies.py +52 -0
  235. airbyte_cdk/sources/file_based/stream/__init__.py +13 -0
  236. airbyte_cdk/sources/file_based/stream/abstract_file_based_stream.py +197 -0
  237. airbyte_cdk/sources/file_based/stream/concurrent/__init__.py +0 -0
  238. airbyte_cdk/sources/file_based/stream/concurrent/adapters.py +343 -0
  239. airbyte_cdk/sources/file_based/stream/concurrent/cursor/__init__.py +9 -0
  240. airbyte_cdk/sources/file_based/stream/concurrent/cursor/abstract_concurrent_file_based_cursor.py +59 -0
  241. airbyte_cdk/sources/file_based/stream/concurrent/cursor/file_based_concurrent_cursor.py +313 -0
  242. airbyte_cdk/sources/file_based/stream/concurrent/cursor/file_based_final_state_cursor.py +83 -0
  243. airbyte_cdk/sources/file_based/stream/cursor/__init__.py +4 -0
  244. airbyte_cdk/sources/file_based/stream/cursor/abstract_file_based_cursor.py +66 -0
  245. airbyte_cdk/sources/file_based/stream/cursor/default_file_based_cursor.py +149 -0
  246. airbyte_cdk/sources/file_based/stream/default_file_based_stream.py +396 -0
  247. airbyte_cdk/sources/file_based/stream/identities_stream.py +49 -0
  248. airbyte_cdk/sources/file_based/stream/permissions_file_based_stream.py +92 -0
  249. airbyte_cdk/sources/file_based/types.py +10 -0
  250. airbyte_cdk/sources/http_config.py +10 -0
  251. airbyte_cdk/sources/http_logger.py +55 -0
  252. airbyte_cdk/sources/message/__init__.py +19 -0
  253. airbyte_cdk/sources/message/repository.py +137 -0
  254. airbyte_cdk/sources/source.py +95 -0
  255. airbyte_cdk/sources/specs/transfer_modes.py +26 -0
  256. airbyte_cdk/sources/streams/__init__.py +8 -0
  257. airbyte_cdk/sources/streams/availability_strategy.py +84 -0
  258. airbyte_cdk/sources/streams/call_rate.py +704 -0
  259. airbyte_cdk/sources/streams/checkpoint/__init__.py +26 -0
  260. airbyte_cdk/sources/streams/checkpoint/checkpoint_reader.py +335 -0
  261. airbyte_cdk/sources/streams/checkpoint/cursor.py +77 -0
  262. airbyte_cdk/sources/streams/checkpoint/per_partition_key_serializer.py +22 -0
  263. airbyte_cdk/sources/streams/checkpoint/resumable_full_refresh_cursor.py +51 -0
  264. airbyte_cdk/sources/streams/checkpoint/substream_resumable_full_refresh_cursor.py +110 -0
  265. airbyte_cdk/sources/streams/concurrent/README.md +7 -0
  266. airbyte_cdk/sources/streams/concurrent/__init__.py +3 -0
  267. airbyte_cdk/sources/streams/concurrent/abstract_stream.py +96 -0
  268. airbyte_cdk/sources/streams/concurrent/abstract_stream_facade.py +37 -0
  269. airbyte_cdk/sources/streams/concurrent/adapters.py +397 -0
  270. airbyte_cdk/sources/streams/concurrent/availability_strategy.py +94 -0
  271. airbyte_cdk/sources/streams/concurrent/clamping.py +99 -0
  272. airbyte_cdk/sources/streams/concurrent/cursor.py +481 -0
  273. airbyte_cdk/sources/streams/concurrent/cursor_types.py +32 -0
  274. airbyte_cdk/sources/streams/concurrent/default_stream.py +102 -0
  275. airbyte_cdk/sources/streams/concurrent/exceptions.py +18 -0
  276. airbyte_cdk/sources/streams/concurrent/helpers.py +42 -0
  277. airbyte_cdk/sources/streams/concurrent/partition_enqueuer.py +64 -0
  278. airbyte_cdk/sources/streams/concurrent/partition_reader.py +45 -0
  279. airbyte_cdk/sources/streams/concurrent/partitions/__init__.py +3 -0
  280. airbyte_cdk/sources/streams/concurrent/partitions/partition.py +48 -0
  281. airbyte_cdk/sources/streams/concurrent/partitions/partition_generator.py +18 -0
  282. airbyte_cdk/sources/streams/concurrent/partitions/stream_slicer.py +21 -0
  283. airbyte_cdk/sources/streams/concurrent/partitions/types.py +38 -0
  284. airbyte_cdk/sources/streams/concurrent/state_converters/__init__.py +0 -0
  285. airbyte_cdk/sources/streams/concurrent/state_converters/abstract_stream_state_converter.py +182 -0
  286. airbyte_cdk/sources/streams/concurrent/state_converters/datetime_stream_state_converter.py +223 -0
  287. airbyte_cdk/sources/streams/concurrent/state_converters/incrementing_count_stream_state_converter.py +92 -0
  288. airbyte_cdk/sources/streams/core.py +703 -0
  289. airbyte_cdk/sources/streams/http/__init__.py +10 -0
  290. airbyte_cdk/sources/streams/http/availability_strategy.py +54 -0
  291. airbyte_cdk/sources/streams/http/error_handlers/__init__.py +22 -0
  292. airbyte_cdk/sources/streams/http/error_handlers/backoff_strategy.py +28 -0
  293. airbyte_cdk/sources/streams/http/error_handlers/default_backoff_strategy.py +17 -0
  294. airbyte_cdk/sources/streams/http/error_handlers/default_error_mapping.py +86 -0
  295. airbyte_cdk/sources/streams/http/error_handlers/error_handler.py +42 -0
  296. airbyte_cdk/sources/streams/http/error_handlers/error_message_parser.py +19 -0
  297. airbyte_cdk/sources/streams/http/error_handlers/http_status_error_handler.py +110 -0
  298. airbyte_cdk/sources/streams/http/error_handlers/json_error_message_parser.py +52 -0
  299. airbyte_cdk/sources/streams/http/error_handlers/response_models.py +65 -0
  300. airbyte_cdk/sources/streams/http/exceptions.py +61 -0
  301. airbyte_cdk/sources/streams/http/http.py +673 -0
  302. airbyte_cdk/sources/streams/http/http_client.py +531 -0
  303. airbyte_cdk/sources/streams/http/rate_limiting.py +158 -0
  304. airbyte_cdk/sources/streams/http/requests_native_auth/__init__.py +14 -0
  305. airbyte_cdk/sources/streams/http/requests_native_auth/abstract_oauth.py +479 -0
  306. airbyte_cdk/sources/streams/http/requests_native_auth/abstract_token.py +34 -0
  307. airbyte_cdk/sources/streams/http/requests_native_auth/oauth.py +436 -0
  308. airbyte_cdk/sources/streams/http/requests_native_auth/token.py +83 -0
  309. airbyte_cdk/sources/streams/permissions/identities_stream.py +75 -0
  310. airbyte_cdk/sources/streams/utils/__init__.py +3 -0
  311. airbyte_cdk/sources/types.py +169 -0
  312. airbyte_cdk/sources/utils/__init__.py +7 -0
  313. airbyte_cdk/sources/utils/casing.py +12 -0
  314. airbyte_cdk/sources/utils/files_directory.py +15 -0
  315. airbyte_cdk/sources/utils/record_helper.py +53 -0
  316. airbyte_cdk/sources/utils/schema_helpers.py +230 -0
  317. airbyte_cdk/sources/utils/slice_logger.py +57 -0
  318. airbyte_cdk/sources/utils/transform.py +277 -0
  319. airbyte_cdk/sources/utils/types.py +7 -0
  320. airbyte_cdk/sql/__init__.py +0 -0
  321. airbyte_cdk/sql/_util/__init__.py +0 -0
  322. airbyte_cdk/sql/_util/hashing.py +34 -0
  323. airbyte_cdk/sql/_util/name_normalizers.py +92 -0
  324. airbyte_cdk/sql/constants.py +32 -0
  325. airbyte_cdk/sql/exceptions.py +235 -0
  326. airbyte_cdk/sql/secrets.py +123 -0
  327. airbyte_cdk/sql/shared/__init__.py +15 -0
  328. airbyte_cdk/sql/shared/catalog_providers.py +145 -0
  329. airbyte_cdk/sql/shared/sql_processor.py +786 -0
  330. airbyte_cdk/sql/types.py +160 -0
  331. airbyte_cdk/test/__init__.py +7 -0
  332. airbyte_cdk/test/catalog_builder.py +81 -0
  333. airbyte_cdk/test/entrypoint_wrapper.py +250 -0
  334. airbyte_cdk/test/mock_http/__init__.py +6 -0
  335. airbyte_cdk/test/mock_http/matcher.py +41 -0
  336. airbyte_cdk/test/mock_http/mocker.py +185 -0
  337. airbyte_cdk/test/mock_http/request.py +103 -0
  338. airbyte_cdk/test/mock_http/response.py +28 -0
  339. airbyte_cdk/test/mock_http/response_builder.py +237 -0
  340. airbyte_cdk/test/state_builder.py +33 -0
  341. airbyte_cdk/test/utils/__init__.py +1 -0
  342. airbyte_cdk/test/utils/data.py +24 -0
  343. airbyte_cdk/test/utils/http_mocking.py +16 -0
  344. airbyte_cdk/test/utils/manifest_only_fixtures.py +59 -0
  345. airbyte_cdk/test/utils/reading.py +26 -0
  346. airbyte_cdk/utils/__init__.py +10 -0
  347. airbyte_cdk/utils/airbyte_secrets_utils.py +80 -0
  348. airbyte_cdk/utils/analytics_message.py +25 -0
  349. airbyte_cdk/utils/constants.py +5 -0
  350. airbyte_cdk/utils/datetime_format_inferrer.py +94 -0
  351. airbyte_cdk/utils/datetime_helpers.py +499 -0
  352. airbyte_cdk/utils/event_timing.py +85 -0
  353. airbyte_cdk/utils/is_cloud_environment.py +18 -0
  354. airbyte_cdk/utils/mapping_helpers.py +162 -0
  355. airbyte_cdk/utils/message_utils.py +26 -0
  356. airbyte_cdk/utils/oneof_option_config.py +33 -0
  357. airbyte_cdk/utils/print_buffer.py +75 -0
  358. airbyte_cdk/utils/schema_inferrer.py +270 -0
  359. airbyte_cdk/utils/slice_hasher.py +37 -0
  360. airbyte_cdk/utils/spec_schema_transformations.py +26 -0
  361. airbyte_cdk/utils/stream_status_utils.py +43 -0
  362. airbyte_cdk/utils/traced_exception.py +145 -0
  363. airbyte_cdk-0.0.0.dev0.dist-info/LICENSE.txt +19 -0
  364. airbyte_cdk-0.0.0.dev0.dist-info/LICENSE_SHORT +1 -0
  365. airbyte_cdk-0.0.0.dev0.dist-info/METADATA +111 -0
  366. airbyte_cdk-0.0.0.dev0.dist-info/RECORD +368 -0
  367. airbyte_cdk-0.0.0.dev0.dist-info/WHEEL +4 -0
  368. airbyte_cdk-0.0.0.dev0.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,96 @@
1
+ #
2
+ # Copyright (c) 2023 Airbyte, Inc., all rights reserved.
3
+ #
4
+
5
+ from abc import ABC, abstractmethod
6
+ from typing import Any, Iterable, Mapping, Optional
7
+
8
+ from typing_extensions import deprecated
9
+
10
+ from airbyte_cdk.models import AirbyteStream
11
+ from airbyte_cdk.sources.source import ExperimentalClassWarning
12
+ from airbyte_cdk.sources.streams.concurrent.availability_strategy import StreamAvailability
13
+ from airbyte_cdk.sources.streams.concurrent.cursor import Cursor
14
+ from airbyte_cdk.sources.streams.concurrent.partitions.partition import Partition
15
+
16
+
17
+ @deprecated(
18
+ "This class is experimental. Use at your own risk.",
19
+ category=ExperimentalClassWarning,
20
+ )
21
+ class AbstractStream(ABC):
22
+ """
23
+ AbstractStream is an experimental interface for streams developed as part of the Concurrent CDK.
24
+ This interface is not yet stable and may change in the future. Use at your own risk.
25
+
26
+ Why create a new interface instead of adding concurrency capabilities the existing Stream?
27
+ We learnt a lot since the initial design of the Stream interface, and we wanted to take the opportunity to improve.
28
+
29
+ High level, the changes we are targeting are:
30
+ - Removing superfluous or leaky parameters from the methods' interfaces
31
+ - Using composition instead of inheritance to add new capabilities
32
+
33
+ To allow us to iterate fast while ensuring backwards compatibility, we are creating a new interface with a facade object that will bridge the old and the new interfaces.
34
+ Source connectors that wish to leverage concurrency need to implement this new interface. An example will be available shortly
35
+
36
+ Current restrictions on sources that implement this interface. Not all of these restrictions will be lifted in the future, but most will as we iterate on the design.
37
+ - Only full refresh is supported. This will be addressed in the future.
38
+ - The read method does not accept a cursor_field. Streams must be internally aware of the cursor field to use. User-defined cursor fields can be implemented by modifying the connector's main method to instantiate the streams with the configured cursor field.
39
+ - Streams cannot return user-friendly messages by overriding Stream.get_error_display_message. This will be addressed in the future.
40
+ - The Stream's behavior cannot depend on a namespace
41
+ - TypeTransformer is not supported. This will be addressed in the future.
42
+ - Nested cursor and primary keys are not supported
43
+ """
44
+
45
+ @abstractmethod
46
+ def generate_partitions(self) -> Iterable[Partition]:
47
+ """
48
+ Generates the partitions that will be read by this stream.
49
+ :return: An iterable of partitions.
50
+ """
51
+
52
+ @property
53
+ @abstractmethod
54
+ def name(self) -> str:
55
+ """
56
+ :return: The stream name
57
+ """
58
+
59
+ @property
60
+ @abstractmethod
61
+ def cursor_field(self) -> Optional[str]:
62
+ """
63
+ Override to return the default cursor field used by this stream e.g: an API entity might always use created_at as the cursor field.
64
+ :return: The name of the field used as a cursor. Nested cursor fields are not supported.
65
+ """
66
+
67
+ @abstractmethod
68
+ def check_availability(self) -> StreamAvailability:
69
+ """
70
+ :return: The stream's availability
71
+ """
72
+
73
+ @abstractmethod
74
+ def get_json_schema(self) -> Mapping[str, Any]:
75
+ """
76
+ :return: A dict of the JSON schema representing this stream.
77
+ """
78
+
79
+ @abstractmethod
80
+ def as_airbyte_stream(self) -> AirbyteStream:
81
+ """
82
+ :return: A dict of the JSON schema representing this stream.
83
+ """
84
+
85
+ @abstractmethod
86
+ def log_stream_sync_configuration(self) -> None:
87
+ """
88
+ Logs the stream's configuration for debugging purposes.
89
+ """
90
+
91
+ @property
92
+ @abstractmethod
93
+ def cursor(self) -> Cursor:
94
+ """
95
+ :return: The cursor associated with this stream.
96
+ """
@@ -0,0 +1,37 @@
1
+ # Copyright (c) 2023 Airbyte, Inc., all rights reserved.
2
+
3
+ from abc import ABC, abstractmethod
4
+ from typing import Generic, Optional, TypeVar
5
+
6
+ from airbyte_cdk.sources.streams.concurrent.exceptions import ExceptionWithDisplayMessage
7
+
8
+ StreamType = TypeVar("StreamType")
9
+
10
+
11
+ class AbstractStreamFacade(Generic[StreamType], ABC):
12
+ @abstractmethod
13
+ def get_underlying_stream(self) -> StreamType:
14
+ """
15
+ Return the underlying stream facade object.
16
+ """
17
+ ...
18
+
19
+ @property
20
+ def source_defined_cursor(self) -> bool:
21
+ # Streams must be aware of their cursor at instantiation time
22
+ return True
23
+
24
+ def get_error_display_message(self, exception: BaseException) -> Optional[str]:
25
+ """
26
+ Retrieves the user-friendly display message that corresponds to an exception.
27
+ This will be called when encountering an exception while reading records from the stream, and used to build the AirbyteTraceMessage.
28
+
29
+ A display message will be returned if the exception is an instance of ExceptionWithDisplayMessage.
30
+
31
+ :param exception: The exception that was raised
32
+ :return: A user-friendly message that indicates the cause of the error
33
+ """
34
+ if isinstance(exception, ExceptionWithDisplayMessage):
35
+ return exception.display_message
36
+ else:
37
+ return None
@@ -0,0 +1,397 @@
1
+ #
2
+ # Copyright (c) 2023 Airbyte, Inc., all rights reserved.
3
+ #
4
+
5
+ import copy
6
+ import json
7
+ import logging
8
+ from functools import lru_cache
9
+ from typing import Any, Iterable, List, Mapping, MutableMapping, Optional, Tuple, Union
10
+
11
+ from typing_extensions import deprecated
12
+
13
+ from airbyte_cdk.models import (
14
+ AirbyteLogMessage,
15
+ AirbyteMessage,
16
+ AirbyteStream,
17
+ ConfiguredAirbyteStream,
18
+ Level,
19
+ SyncMode,
20
+ Type,
21
+ )
22
+ from airbyte_cdk.sources import AbstractSource, Source
23
+ from airbyte_cdk.sources.connector_state_manager import ConnectorStateManager
24
+ from airbyte_cdk.sources.message import MessageRepository
25
+ from airbyte_cdk.sources.source import ExperimentalClassWarning
26
+ from airbyte_cdk.sources.streams import Stream
27
+ from airbyte_cdk.sources.streams.availability_strategy import AvailabilityStrategy
28
+ from airbyte_cdk.sources.streams.concurrent.abstract_stream_facade import AbstractStreamFacade
29
+ from airbyte_cdk.sources.streams.concurrent.availability_strategy import (
30
+ AbstractAvailabilityStrategy,
31
+ AlwaysAvailableAvailabilityStrategy,
32
+ )
33
+ from airbyte_cdk.sources.streams.concurrent.cursor import Cursor, FinalStateCursor
34
+ from airbyte_cdk.sources.streams.concurrent.default_stream import DefaultStream
35
+ from airbyte_cdk.sources.streams.concurrent.exceptions import ExceptionWithDisplayMessage
36
+ from airbyte_cdk.sources.streams.concurrent.helpers import (
37
+ get_cursor_field_from_stream,
38
+ get_primary_key_from_stream,
39
+ )
40
+ from airbyte_cdk.sources.streams.concurrent.partitions.partition import Partition
41
+ from airbyte_cdk.sources.streams.concurrent.partitions.partition_generator import PartitionGenerator
42
+ from airbyte_cdk.sources.streams.core import StreamData
43
+ from airbyte_cdk.sources.types import Record
44
+ from airbyte_cdk.sources.utils.schema_helpers import InternalConfig
45
+ from airbyte_cdk.sources.utils.slice_logger import SliceLogger
46
+ from airbyte_cdk.utils.slice_hasher import SliceHasher
47
+
48
+ """
49
+ This module contains adapters to help enabling concurrency on Stream objects without needing to migrate to AbstractStream
50
+ """
51
+
52
+
53
+ @deprecated(
54
+ "This class is experimental. Use at your own risk.",
55
+ category=ExperimentalClassWarning,
56
+ )
57
+ class StreamFacade(AbstractStreamFacade[DefaultStream], Stream):
58
+ """
59
+ The StreamFacade is a Stream that wraps an AbstractStream and exposes it as a Stream.
60
+
61
+ All methods either delegate to the wrapped AbstractStream or provide a default implementation.
62
+ The default implementations define restrictions imposed on Streams migrated to the new interface. For instance, only source-defined cursors are supported.
63
+ """
64
+
65
+ @classmethod
66
+ def create_from_stream(
67
+ cls,
68
+ stream: Stream,
69
+ source: AbstractSource,
70
+ logger: logging.Logger,
71
+ state: Optional[MutableMapping[str, Any]],
72
+ cursor: Cursor,
73
+ ) -> Stream:
74
+ """
75
+ Create a ConcurrentStream from a Stream object.
76
+ :param source: The source
77
+ :param stream: The stream
78
+ :param max_workers: The maximum number of worker thread to use
79
+ :return:
80
+ """
81
+ pk = get_primary_key_from_stream(stream.primary_key)
82
+ cursor_field = get_cursor_field_from_stream(stream)
83
+
84
+ if not source.message_repository:
85
+ raise ValueError(
86
+ "A message repository is required to emit non-record messages. Please set the message repository on the source."
87
+ )
88
+
89
+ message_repository = source.message_repository
90
+ return StreamFacade(
91
+ DefaultStream(
92
+ partition_generator=StreamPartitionGenerator(
93
+ stream,
94
+ message_repository,
95
+ SyncMode.full_refresh
96
+ if isinstance(cursor, FinalStateCursor)
97
+ else SyncMode.incremental,
98
+ [cursor_field] if cursor_field is not None else None,
99
+ state,
100
+ ),
101
+ name=stream.name,
102
+ namespace=stream.namespace,
103
+ json_schema=stream.get_json_schema(),
104
+ availability_strategy=AlwaysAvailableAvailabilityStrategy(),
105
+ primary_key=pk,
106
+ cursor_field=cursor_field,
107
+ logger=logger,
108
+ cursor=cursor,
109
+ ),
110
+ stream,
111
+ cursor,
112
+ slice_logger=source._slice_logger,
113
+ logger=logger,
114
+ )
115
+
116
+ @property
117
+ def state(self) -> MutableMapping[str, Any]:
118
+ raise NotImplementedError(
119
+ "This should not be called as part of the Concurrent CDK code. Please report the problem to Airbyte"
120
+ )
121
+
122
+ @state.setter
123
+ def state(self, value: Mapping[str, Any]) -> None:
124
+ if "state" in dir(self._legacy_stream):
125
+ self._legacy_stream.state = value # type: ignore # validating `state` is attribute of stream using `if` above
126
+
127
+ def __init__(
128
+ self,
129
+ stream: DefaultStream,
130
+ legacy_stream: Stream,
131
+ cursor: Cursor,
132
+ slice_logger: SliceLogger,
133
+ logger: logging.Logger,
134
+ ):
135
+ """
136
+ :param stream: The underlying AbstractStream
137
+ """
138
+ self._abstract_stream = stream
139
+ self._legacy_stream = legacy_stream
140
+ self._cursor = cursor
141
+ self._slice_logger = slice_logger
142
+ self._logger = logger
143
+
144
+ def read(
145
+ self,
146
+ configured_stream: ConfiguredAirbyteStream,
147
+ logger: logging.Logger,
148
+ slice_logger: SliceLogger,
149
+ stream_state: MutableMapping[str, Any],
150
+ state_manager: ConnectorStateManager,
151
+ internal_config: InternalConfig,
152
+ ) -> Iterable[StreamData]:
153
+ yield from self._read_records()
154
+
155
+ def read_records(
156
+ self,
157
+ sync_mode: SyncMode,
158
+ cursor_field: Optional[List[str]] = None,
159
+ stream_slice: Optional[Mapping[str, Any]] = None,
160
+ stream_state: Optional[Mapping[str, Any]] = None,
161
+ ) -> Iterable[StreamData]:
162
+ try:
163
+ yield from self._read_records()
164
+ except Exception as exc:
165
+ if hasattr(self._cursor, "state"):
166
+ state = str(self._cursor.state)
167
+ else:
168
+ # This shouldn't happen if the ConcurrentCursor was used
169
+ state = "unknown; no state attribute was available on the cursor"
170
+ yield AirbyteMessage(
171
+ type=Type.LOG,
172
+ log=AirbyteLogMessage(
173
+ level=Level.ERROR, message=f"Cursor State at time of exception: {state}"
174
+ ),
175
+ )
176
+ raise exc
177
+
178
+ def _read_records(self) -> Iterable[StreamData]:
179
+ for partition in self._abstract_stream.generate_partitions():
180
+ if self._slice_logger.should_log_slice_message(self._logger):
181
+ yield self._slice_logger.create_slice_log_message(partition.to_slice())
182
+ for record in partition.read():
183
+ yield record.data
184
+
185
+ @property
186
+ def name(self) -> str:
187
+ return self._abstract_stream.name
188
+
189
+ @property
190
+ def primary_key(self) -> Optional[Union[str, List[str], List[List[str]]]]:
191
+ # This method is not expected to be called directly. It is only implemented for backward compatibility with the old interface
192
+ return self.as_airbyte_stream().source_defined_primary_key # type: ignore # source_defined_primary_key is known to be an Optional[List[List[str]]]
193
+
194
+ @property
195
+ def cursor_field(self) -> Union[str, List[str]]:
196
+ if self._abstract_stream.cursor_field is None:
197
+ return []
198
+ else:
199
+ return self._abstract_stream.cursor_field
200
+
201
+ @property
202
+ def cursor(self) -> Optional[Cursor]: # type: ignore[override] # StreamFaced expects to use only airbyte_cdk.sources.streams.concurrent.cursor.Cursor
203
+ return self._cursor
204
+
205
+ @lru_cache(maxsize=None)
206
+ def get_json_schema(self) -> Mapping[str, Any]:
207
+ return self._abstract_stream.get_json_schema()
208
+
209
+ @property
210
+ def supports_incremental(self) -> bool:
211
+ return self._legacy_stream.supports_incremental
212
+
213
+ def check_availability(
214
+ self, logger: logging.Logger, source: Optional["Source"] = None
215
+ ) -> Tuple[bool, Optional[str]]:
216
+ """
217
+ Verifies the stream is available. Delegates to the underlying AbstractStream and ignores the parameters
218
+ :param logger: (ignored)
219
+ :param source: (ignored)
220
+ :return:
221
+ """
222
+ availability = self._abstract_stream.check_availability()
223
+ return availability.is_available(), availability.message()
224
+
225
+ def as_airbyte_stream(self) -> AirbyteStream:
226
+ return self._abstract_stream.as_airbyte_stream()
227
+
228
+ def log_stream_sync_configuration(self) -> None:
229
+ self._abstract_stream.log_stream_sync_configuration()
230
+
231
+ def get_underlying_stream(self) -> DefaultStream:
232
+ return self._abstract_stream
233
+
234
+
235
+ class SliceEncoder(json.JSONEncoder):
236
+ def default(self, obj: Any) -> Any:
237
+ if hasattr(obj, "__json_serializable__"):
238
+ return obj.__json_serializable__()
239
+
240
+ # Let the base class default method raise the TypeError
241
+ return super().default(obj)
242
+
243
+
244
+ class StreamPartition(Partition):
245
+ """
246
+ This class acts as an adapter between the new Partition interface and the Stream's stream_slice interface
247
+
248
+ StreamPartitions are instantiated from a Stream and a stream_slice.
249
+
250
+ This class can be used to help enable concurrency on existing connectors without having to rewrite everything as AbstractStream.
251
+ In the long-run, it would be preferable to update the connectors, but we don't have the tooling or need to justify the effort at this time.
252
+ """
253
+
254
+ def __init__(
255
+ self,
256
+ stream: Stream,
257
+ _slice: Optional[Mapping[str, Any]],
258
+ message_repository: MessageRepository,
259
+ sync_mode: SyncMode,
260
+ cursor_field: Optional[List[str]],
261
+ state: Optional[MutableMapping[str, Any]],
262
+ ):
263
+ """
264
+ :param stream: The stream to delegate to
265
+ :param _slice: The partition's stream_slice
266
+ :param message_repository: The message repository to use to emit non-record messages
267
+ """
268
+ self._stream = stream
269
+ self._slice = _slice
270
+ self._message_repository = message_repository
271
+ self._sync_mode = sync_mode
272
+ self._cursor_field = cursor_field
273
+ self._state = state
274
+ self._hash = SliceHasher.hash(self._stream.name, self._slice)
275
+
276
+ def read(self) -> Iterable[Record]:
277
+ """
278
+ Read messages from the stream.
279
+ If the StreamData is a Mapping or an AirbyteMessage of type RECORD, it will be converted to a Record.
280
+ Otherwise, the message will be emitted on the message repository.
281
+ """
282
+ try:
283
+ # using `stream_state=self._state` have a very different behavior than the current one as today the state is updated slice
284
+ # by slice incrementally. We don't have this guarantee with Concurrent CDK. For HttpStream, `stream_state` is passed to:
285
+ # * fetch_next_page
286
+ # * parse_response
287
+ # Both are not used for Stripe so we should be good for the first iteration of Concurrent CDK. However, Stripe still do
288
+ # `if not stream_state` to know if it calls the Event stream or not
289
+ for record_data in self._stream.read_records(
290
+ cursor_field=self._cursor_field,
291
+ sync_mode=SyncMode.full_refresh,
292
+ stream_slice=copy.deepcopy(self._slice),
293
+ stream_state=self._state,
294
+ ):
295
+ # Noting we'll also need to support FileTransferRecordMessage if we want to support file-based connectors in this facade
296
+ # For now, file-based connectors have their own stream facade
297
+ if isinstance(record_data, Mapping):
298
+ data_to_return = dict(record_data)
299
+ self._stream.transformer.transform(
300
+ data_to_return, self._stream.get_json_schema()
301
+ )
302
+ yield Record(
303
+ data=data_to_return,
304
+ stream_name=self.stream_name(),
305
+ associated_slice=self._slice, # type: ignore [arg-type]
306
+ )
307
+ elif isinstance(record_data, AirbyteMessage) and record_data.record is not None:
308
+ yield Record(
309
+ data=record_data.record.data or {},
310
+ stream_name=self.stream_name(),
311
+ associated_slice=self._slice, # type: ignore [arg-type]
312
+ )
313
+ else:
314
+ self._message_repository.emit_message(record_data)
315
+ except Exception as e:
316
+ display_message = self._stream.get_error_display_message(e)
317
+ if display_message:
318
+ raise ExceptionWithDisplayMessage(display_message) from e
319
+ else:
320
+ raise e
321
+
322
+ def to_slice(self) -> Optional[Mapping[str, Any]]:
323
+ return self._slice
324
+
325
+ def __hash__(self) -> int:
326
+ return self._hash
327
+
328
+ def stream_name(self) -> str:
329
+ return self._stream.name
330
+
331
+ def __repr__(self) -> str:
332
+ return f"StreamPartition({self._stream.name}, {self._slice})"
333
+
334
+
335
+ class StreamPartitionGenerator(PartitionGenerator):
336
+ """
337
+ This class acts as an adapter between the new PartitionGenerator and Stream.stream_slices
338
+
339
+ This class can be used to help enable concurrency on existing connectors without having to rewrite everything as AbstractStream.
340
+ In the long-run, it would be preferable to update the connectors, but we don't have the tooling or need to justify the effort at this time.
341
+ """
342
+
343
+ def __init__(
344
+ self,
345
+ stream: Stream,
346
+ message_repository: MessageRepository,
347
+ sync_mode: SyncMode,
348
+ cursor_field: Optional[List[str]],
349
+ state: Optional[MutableMapping[str, Any]],
350
+ ):
351
+ """
352
+ :param stream: The stream to delegate to
353
+ :param message_repository: The message repository to use to emit non-record messages
354
+ """
355
+ self.message_repository = message_repository
356
+ self._stream = stream
357
+ self._sync_mode = sync_mode
358
+ self._cursor_field = cursor_field
359
+ self._state = state
360
+
361
+ def generate(self) -> Iterable[Partition]:
362
+ for s in self._stream.stream_slices(
363
+ sync_mode=self._sync_mode, cursor_field=self._cursor_field, stream_state=self._state
364
+ ):
365
+ yield StreamPartition(
366
+ self._stream,
367
+ copy.deepcopy(s),
368
+ self.message_repository,
369
+ self._sync_mode,
370
+ self._cursor_field,
371
+ self._state,
372
+ )
373
+
374
+
375
+ @deprecated(
376
+ "Availability strategy has been soft deprecated. Do not use. Class is subject to removal",
377
+ category=ExperimentalClassWarning,
378
+ )
379
+ class AvailabilityStrategyFacade(AvailabilityStrategy):
380
+ def __init__(self, abstract_availability_strategy: AbstractAvailabilityStrategy):
381
+ self._abstract_availability_strategy = abstract_availability_strategy
382
+
383
+ def check_availability(
384
+ self, stream: Stream, logger: logging.Logger, source: Optional["Source"] = None
385
+ ) -> Tuple[bool, Optional[str]]:
386
+ """
387
+ Checks stream availability.
388
+
389
+ Important to note that the stream and source parameters are not used by the underlying AbstractAvailabilityStrategy.
390
+
391
+ :param stream: (unused)
392
+ :param logger: logger object to use
393
+ :param source: (unused)
394
+ :return: A tuple of (boolean, str). If boolean is true, then the stream
395
+ """
396
+ stream_availability = self._abstract_availability_strategy.check_availability(logger)
397
+ return stream_availability.is_available(), stream_availability.message()
@@ -0,0 +1,94 @@
1
+ #
2
+ # Copyright (c) 2023 Airbyte, Inc., all rights reserved.
3
+ #
4
+
5
+ import logging
6
+ from abc import ABC, abstractmethod
7
+ from typing import Optional
8
+
9
+ from typing_extensions import deprecated
10
+
11
+ from airbyte_cdk.sources.source import ExperimentalClassWarning
12
+
13
+
14
+ class StreamAvailability(ABC):
15
+ @abstractmethod
16
+ def is_available(self) -> bool:
17
+ """
18
+ :return: True if the stream is available. False if the stream is not
19
+ """
20
+
21
+ @abstractmethod
22
+ def message(self) -> Optional[str]:
23
+ """
24
+ :return: A message describing why the stream is not available. If the stream is available, this should return None.
25
+ """
26
+
27
+
28
+ class StreamAvailable(StreamAvailability):
29
+ def is_available(self) -> bool:
30
+ return True
31
+
32
+ def message(self) -> Optional[str]:
33
+ return None
34
+
35
+
36
+ class StreamUnavailable(StreamAvailability):
37
+ def __init__(self, message: str):
38
+ self._message = message
39
+
40
+ def is_available(self) -> bool:
41
+ return False
42
+
43
+ def message(self) -> Optional[str]:
44
+ return self._message
45
+
46
+
47
+ # Singleton instances of StreamAvailability to avoid the overhead of creating new dummy objects
48
+ STREAM_AVAILABLE = StreamAvailable()
49
+
50
+
51
+ @deprecated(
52
+ "This class is experimental. Use at your own risk.",
53
+ category=ExperimentalClassWarning,
54
+ )
55
+ class AbstractAvailabilityStrategy(ABC):
56
+ """
57
+ AbstractAvailabilityStrategy is an experimental interface developed as part of the Concurrent CDK.
58
+ This interface is not yet stable and may change in the future. Use at your own risk.
59
+
60
+ Why create a new interface instead of using the existing AvailabilityStrategy?
61
+ The existing AvailabilityStrategy is tightly coupled with Stream and Source, which yields to circular dependencies and makes it difficult to move away from the Stream interface to AbstractStream.
62
+ """
63
+
64
+ @abstractmethod
65
+ def check_availability(self, logger: logging.Logger) -> StreamAvailability:
66
+ """
67
+ Checks stream availability.
68
+
69
+ :param logger: logger object to use
70
+ :return: A StreamAvailability object describing the stream's availability
71
+ """
72
+
73
+
74
+ @deprecated(
75
+ "This class is experimental. Use at your own risk.",
76
+ category=ExperimentalClassWarning,
77
+ )
78
+ class AlwaysAvailableAvailabilityStrategy(AbstractAvailabilityStrategy):
79
+ """
80
+ An availability strategy that always indicates a stream is available.
81
+
82
+ This strategy is used to avoid breaking changes and serves as a soft
83
+ deprecation of the availability strategy, allowing a smoother transition
84
+ without disrupting existing functionality.
85
+ """
86
+
87
+ def check_availability(self, logger: logging.Logger) -> StreamAvailability:
88
+ """
89
+ Checks stream availability.
90
+
91
+ :param logger: logger object to use
92
+ :return: A StreamAvailability object describing the stream's availability
93
+ """
94
+ return StreamAvailable()