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,122 @@
1
+ # Copyright (c) 2024 Airbyte, Inc., all rights reserved.
2
+
3
+ from dataclasses import InitVar, dataclass
4
+ from typing import Any, Iterable, Mapping, Optional
5
+
6
+ from airbyte_cdk.sources.declarative.incremental import DeclarativeCursor
7
+ from airbyte_cdk.sources.declarative.types import Record, StreamSlice, StreamState
8
+ from airbyte_cdk.sources.streams.checkpoint.checkpoint_reader import FULL_REFRESH_COMPLETE_STATE
9
+
10
+
11
+ @dataclass
12
+ class ResumableFullRefreshCursor(DeclarativeCursor):
13
+ parameters: InitVar[Mapping[str, Any]]
14
+
15
+ def __post_init__(self, parameters: Mapping[str, Any]) -> None:
16
+ self._cursor: StreamState = {}
17
+
18
+ def get_stream_state(self) -> StreamState:
19
+ return self._cursor
20
+
21
+ def set_initial_state(self, stream_state: StreamState) -> None:
22
+ self._cursor = stream_state
23
+
24
+ def observe(self, stream_slice: StreamSlice, record: Record) -> None:
25
+ """
26
+ Resumable full refresh manages state using a page number so it does not need to update state by observing incoming records.
27
+ """
28
+ pass
29
+
30
+ def close_slice(self, stream_slice: StreamSlice, *args: Any) -> None:
31
+ # The ResumableFullRefreshCursor doesn't support nested streams yet so receiving a partition is unexpected
32
+ if stream_slice.partition:
33
+ raise ValueError(
34
+ f"Stream slice {stream_slice} should not have a partition. Got {stream_slice.partition}."
35
+ )
36
+ self._cursor = stream_slice.cursor_slice
37
+
38
+ def should_be_synced(self, record: Record) -> bool:
39
+ """
40
+ Unlike date-based cursors which filter out records outside slice boundaries, resumable full refresh records exist within pages
41
+ that don't have filterable bounds. We should always return them.
42
+ """
43
+ return True
44
+
45
+ def is_greater_than_or_equal(self, first: Record, second: Record) -> bool:
46
+ """
47
+ RFR record don't have ordering to be compared between one another.
48
+ """
49
+ return False
50
+
51
+ def select_state(self, stream_slice: Optional[StreamSlice] = None) -> Optional[StreamState]:
52
+ # A top-level RFR cursor only manages the state of a single partition
53
+ return self._cursor
54
+
55
+ def stream_slices(self) -> Iterable[StreamSlice]:
56
+ """
57
+ Resumable full refresh cursors only return a single slice and can't perform partitioning because iteration is done per-page
58
+ along an unbounded set.
59
+ """
60
+ yield from [StreamSlice(cursor_slice=self._cursor, partition={})]
61
+
62
+ # This is an interesting pattern that might not seem obvious at first glance. This cursor itself has no functional need to
63
+ # inject any request values into the outbound response because the up-to-date pagination state is already loaded and
64
+ # maintained by the paginator component
65
+ def get_request_params(
66
+ self,
67
+ *,
68
+ stream_state: Optional[StreamState] = None,
69
+ stream_slice: Optional[StreamSlice] = None,
70
+ next_page_token: Optional[Mapping[str, Any]] = None,
71
+ ) -> Mapping[str, Any]:
72
+ return {}
73
+
74
+ def get_request_headers(
75
+ self,
76
+ *,
77
+ stream_state: Optional[StreamState] = None,
78
+ stream_slice: Optional[StreamSlice] = None,
79
+ next_page_token: Optional[Mapping[str, Any]] = None,
80
+ ) -> Mapping[str, Any]:
81
+ return {}
82
+
83
+ def get_request_body_data(
84
+ self,
85
+ *,
86
+ stream_state: Optional[StreamState] = None,
87
+ stream_slice: Optional[StreamSlice] = None,
88
+ next_page_token: Optional[Mapping[str, Any]] = None,
89
+ ) -> Mapping[str, Any]:
90
+ return {}
91
+
92
+ def get_request_body_json(
93
+ self,
94
+ *,
95
+ stream_state: Optional[StreamState] = None,
96
+ stream_slice: Optional[StreamSlice] = None,
97
+ next_page_token: Optional[Mapping[str, Any]] = None,
98
+ ) -> Mapping[str, Any]:
99
+ return {}
100
+
101
+
102
+ @dataclass
103
+ class ChildPartitionResumableFullRefreshCursor(ResumableFullRefreshCursor):
104
+ """
105
+ The Sub-stream Resumable Cursor for Full-Refresh substreams.
106
+ Follows the parent type `ResumableFullRefreshCursor` with a small override,
107
+ to provide the ability to close the substream's slice once it has finished processing.
108
+
109
+ Check the `close_slice` method overide for more info about the actual behaviour of this cursor.
110
+ """
111
+
112
+ def close_slice(self, stream_slice: StreamSlice, *args: Any) -> None:
113
+ """
114
+ Once the current slice has finished syncing:
115
+ - paginator returns None
116
+ - no more slices to process
117
+
118
+ we assume that the records are processed and emitted already,
119
+ thus we have to set the cursor to ` __ab_full_refresh_sync_complete: true `,
120
+ otherwise there is a risk of Inf. Loop processing the same slice.
121
+ """
122
+ self._cursor = FULL_REFRESH_COMPLETE_STATE
@@ -0,0 +1,9 @@
1
+ #
2
+ # Copyright (c) 2025 Airbyte, Inc., all rights reserved.
3
+ #
4
+
5
+ from airbyte_cdk.sources.declarative.interpolation.interpolated_boolean import InterpolatedBoolean
6
+ from airbyte_cdk.sources.declarative.interpolation.interpolated_mapping import InterpolatedMapping
7
+ from airbyte_cdk.sources.declarative.interpolation.interpolated_string import InterpolatedString
8
+
9
+ __all__ = ["InterpolatedBoolean", "InterpolatedMapping", "InterpolatedString"]
@@ -0,0 +1,139 @@
1
+ #
2
+ # Copyright (c) 2025 Airbyte, Inc., all rights reserved.
3
+ #
4
+
5
+ import base64
6
+ import hashlib
7
+ import json
8
+ import re
9
+ from typing import Any, Optional
10
+
11
+
12
+ def hash(value: Any, hash_type: str = "md5", salt: Optional[str] = None) -> str:
13
+ """
14
+ Implementation of a custom Jinja2 hash filter
15
+ Hash type defaults to 'md5' if one is not specified.
16
+
17
+ If you are using this has function for GDPR compliance, then
18
+ you should probably also pass in a salt as discussed in:
19
+ https://security.stackexchange.com/questions/202022/hashing-email-addresses-for-gdpr-compliance
20
+
21
+ This can be used in a low code connector definition under the AddFields transformation.
22
+ For example:
23
+
24
+ rates_stream:
25
+ $ref: "#/definitions/base_stream"
26
+ $parameters:
27
+ name: "rates"
28
+ primary_key: "date"
29
+ path: "/exchangerates_data/latest"
30
+ transformations:
31
+ - type: AddFields
32
+ fields:
33
+ - path: ["some_new_path"]
34
+ value: "{{ record['rates']['CAD'] | hash('md5', 'mysalt') }}"
35
+
36
+
37
+
38
+ :param value: value to be hashed
39
+ :param hash_type: valid hash type
40
+ :param salt: a salt that will be combined with the value to ensure that the hash created for a given value on this system
41
+ is different from the hash created for that value on other systems.
42
+ :return: computed hash as a hexadecimal string
43
+ """
44
+ hash_func = getattr(hashlib, hash_type, None)
45
+
46
+ if hash_func:
47
+ hash_obj = hash_func()
48
+ hash_obj.update(str(value).encode("utf-8"))
49
+ if salt:
50
+ hash_obj.update(str(salt).encode("utf-8"))
51
+ computed_hash: str = hash_obj.hexdigest()
52
+ else:
53
+ raise AttributeError("No hashing function named {hname}".format(hname=hash_type))
54
+
55
+ return computed_hash
56
+
57
+
58
+ def base64encode(value: str) -> str:
59
+ """
60
+ Implementation of a custom Jinja2 base64encode filter
61
+
62
+ For example:
63
+
64
+ OAuthAuthenticator:
65
+ $ref: "#/definitions/OAuthAuthenticator"
66
+ $parameters:
67
+ name: "client_id"
68
+ value: "{{ config['client_id'] | base64encode }}"
69
+
70
+ :param value: value to be encoded in base64
71
+ :return: base64 encoded string
72
+ """
73
+
74
+ return base64.b64encode(value.encode("utf-8")).decode()
75
+
76
+
77
+ def base64decode(value: str) -> str:
78
+ """
79
+ Implementation of a custom Jinja2 base64decode filter
80
+
81
+ For example:
82
+
83
+ OAuthAuthenticator:
84
+ $ref: "#/definitions/OAuthAuthenticator"
85
+ $parameters:
86
+ name: "client_id"
87
+ value: "{{ config['client_id'] | base64decode }}"
88
+
89
+ :param value: value to be decoded from base64
90
+ :return: base64 decoded string
91
+ """
92
+
93
+ return base64.b64decode(value.encode("utf-8")).decode()
94
+
95
+
96
+ def base64binascii_decode(value: str) -> str:
97
+ """
98
+ Implementation of a custom Jinja2 filter to decode base64 strings using ASCII encoding
99
+
100
+ For example:
101
+
102
+ OAuthAuthenticator:
103
+ $ref: "#/definitions/OAuthAuthenticator"
104
+ $parameters:
105
+ name: "client_id"
106
+ value: "{{ config['client_id'] | base64binascii_decode }}"
107
+
108
+ :param value: value to be decoded from base64 using ascii
109
+ :return: base64 decoded string ascii
110
+ """
111
+ return base64.standard_b64encode(value.encode("ascii")).decode("ascii")
112
+
113
+
114
+ def string(value: Any) -> str:
115
+ """
116
+ Converts the input value to a string.
117
+ If the value is already a string, it is returned as is.
118
+ Otherwise, the value is interpreted as a json object and wrapped in triple-quotes so it's evalued as a string by the JinjaInterpolation
119
+ :param value: the value to convert to a string
120
+ :return: string representation of the input value
121
+ """
122
+ if isinstance(value, str):
123
+ return value
124
+ ret = f'"""{json.dumps(value)}"""'
125
+ return ret
126
+
127
+
128
+ def regex_search(value: str, regex: str) -> str:
129
+ """
130
+ Match a regular expression against a string and return the first match group if it exists.
131
+ """
132
+ match = re.search(regex, value)
133
+ if match and len(match.groups()) > 0:
134
+ return match.group(1)
135
+ return ""
136
+
137
+
138
+ _filters_list = [hash, base64encode, base64decode, base64binascii_decode, string, regex_search]
139
+ filters = {f.__name__: f for f in _filters_list}
@@ -0,0 +1,66 @@
1
+ #
2
+ # Copyright (c) 2025 Airbyte, Inc., all rights reserved.
3
+ #
4
+
5
+ from dataclasses import InitVar, dataclass
6
+ from typing import Any, Final, List, Mapping
7
+
8
+ from airbyte_cdk.sources.declarative.interpolation.jinja import JinjaInterpolation
9
+ from airbyte_cdk.sources.types import Config
10
+
11
+ FALSE_VALUES: Final[List[Any]] = [
12
+ "False",
13
+ "false",
14
+ "{}",
15
+ "[]",
16
+ "()",
17
+ "",
18
+ "0",
19
+ "0.0",
20
+ {},
21
+ False,
22
+ [],
23
+ (),
24
+ set(),
25
+ ]
26
+
27
+
28
+ @dataclass
29
+ class InterpolatedBoolean:
30
+ f"""
31
+ Wrapper around a string to be evaluated to a boolean value.
32
+ The string will be evaluated as False if it interpolates to a value in {FALSE_VALUES}
33
+
34
+ Attributes:
35
+ condition (str): The string representing the condition to evaluate to a boolean
36
+ """
37
+ condition: str
38
+ parameters: InitVar[Mapping[str, Any]]
39
+
40
+ def __post_init__(self, parameters: Mapping[str, Any]) -> None:
41
+ self._default = "False"
42
+ self._interpolation = JinjaInterpolation()
43
+ self._parameters = parameters
44
+
45
+ def eval(self, config: Config, **additional_parameters: Any) -> bool:
46
+ """
47
+ Interpolates the predicate condition string using the config and other optional arguments passed as parameter.
48
+
49
+ :param config: The user-provided configuration as specified by the source's spec
50
+ :param additional_parameters: Optional parameters used for interpolation
51
+ :return: The interpolated string
52
+ """
53
+ if isinstance(self.condition, bool):
54
+ return self.condition
55
+ else:
56
+ evaluated = self._interpolation.eval(
57
+ self.condition,
58
+ config,
59
+ self._default,
60
+ parameters=self._parameters,
61
+ **additional_parameters,
62
+ )
63
+ if evaluated in FALSE_VALUES:
64
+ return False
65
+ # The presence of a value is generally regarded as truthy, so we treat it as such
66
+ return True
@@ -0,0 +1,56 @@
1
+ #
2
+ # Copyright (c) 2025 Airbyte, Inc., all rights reserved.
3
+ #
4
+
5
+
6
+ from dataclasses import InitVar, dataclass
7
+ from typing import Any, Dict, Mapping, Optional
8
+
9
+ from airbyte_cdk.sources.declarative.interpolation.jinja import JinjaInterpolation
10
+ from airbyte_cdk.sources.types import Config
11
+
12
+
13
+ @dataclass
14
+ class InterpolatedMapping:
15
+ """
16
+ Wrapper around a Mapping[str, str] where both the keys and values are to be interpolated.
17
+
18
+ Attributes:
19
+ mapping (Mapping[str, str]): to be evaluated
20
+ """
21
+
22
+ mapping: Mapping[str, str]
23
+ parameters: InitVar[Mapping[str, Any]]
24
+
25
+ def __post_init__(self, parameters: Optional[Mapping[str, Any]]) -> None:
26
+ self._interpolation = JinjaInterpolation()
27
+ self._parameters = parameters
28
+
29
+ def eval(self, config: Config, **additional_parameters: Any) -> Dict[str, Any]:
30
+ """
31
+ Wrapper around a Mapping[str, str] that allows for both keys and values to be interpolated.
32
+
33
+ :param config: The user-provided configuration as specified by the source's spec
34
+ :param additional_parameters: Optional parameters used for interpolation
35
+ :return: The interpolated mapping
36
+ """
37
+ valid_key_types = additional_parameters.pop("valid_key_types", (str,))
38
+ valid_value_types = additional_parameters.pop("valid_value_types", None)
39
+ return {
40
+ self._interpolation.eval(
41
+ name,
42
+ config,
43
+ valid_types=valid_key_types,
44
+ parameters=self._parameters,
45
+ **additional_parameters,
46
+ ): self._eval(value, config, valid_types=valid_value_types, **additional_parameters)
47
+ for name, value in self.mapping.items()
48
+ }
49
+
50
+ def _eval(self, value: str, config: Config, **kwargs: Any) -> Any:
51
+ # The values in self._mapping can be of Any type
52
+ # We only want to interpolate them if they are strings
53
+ if isinstance(value, str):
54
+ return self._interpolation.eval(value, config, parameters=self._parameters, **kwargs)
55
+ else:
56
+ return value
@@ -0,0 +1,52 @@
1
+ #
2
+ # Copyright (c) 2025 Airbyte, Inc., all rights reserved.
3
+ #
4
+
5
+
6
+ from dataclasses import InitVar, dataclass
7
+ from typing import Any, Mapping, Optional, Union
8
+
9
+ from airbyte_cdk.sources.declarative.interpolation.jinja import JinjaInterpolation
10
+ from airbyte_cdk.sources.types import Config
11
+
12
+ NestedMappingEntry = Union[
13
+ dict[str, "NestedMapping"], list["NestedMapping"], str, int, float, bool, None
14
+ ]
15
+ NestedMapping = Union[dict[str, NestedMappingEntry], str]
16
+
17
+
18
+ @dataclass
19
+ class InterpolatedNestedMapping:
20
+ """
21
+ Wrapper around a nested dict which can contain lists and primitive values where both the keys and values are interpolated recursively.
22
+
23
+ Attributes:
24
+ mapping (NestedMapping): to be evaluated
25
+ """
26
+
27
+ mapping: NestedMapping
28
+ parameters: InitVar[Mapping[str, Any]]
29
+
30
+ def __post_init__(self, parameters: Optional[Mapping[str, Any]]) -> None:
31
+ self._interpolation = JinjaInterpolation()
32
+ self._parameters = parameters
33
+
34
+ def eval(self, config: Config, **additional_parameters: Any) -> Any:
35
+ return self._eval(self.mapping, config, **additional_parameters)
36
+
37
+ def _eval(
38
+ self, value: Union[NestedMapping, NestedMappingEntry], config: Config, **kwargs: Any
39
+ ) -> Any:
40
+ # Recursively interpolate dictionaries and lists
41
+ if isinstance(value, str):
42
+ return self._interpolation.eval(value, config, parameters=self._parameters, **kwargs)
43
+ elif isinstance(value, dict):
44
+ interpolated_dict = {
45
+ self._eval(k, config, **kwargs): self._eval(v, config, **kwargs)
46
+ for k, v in value.items()
47
+ }
48
+ return {k: v for k, v in interpolated_dict.items() if v is not None}
49
+ elif isinstance(value, list):
50
+ return [self._eval(v, config, **kwargs) for v in value]
51
+ else:
52
+ return value
@@ -0,0 +1,79 @@
1
+ #
2
+ # Copyright (c) 2025 Airbyte, Inc., all rights reserved.
3
+ #
4
+
5
+ from dataclasses import InitVar, dataclass
6
+ from typing import Any, Mapping, Optional, Union
7
+
8
+ from airbyte_cdk.sources.declarative.interpolation.jinja import JinjaInterpolation
9
+ from airbyte_cdk.sources.types import Config
10
+
11
+
12
+ @dataclass
13
+ class InterpolatedString:
14
+ """
15
+ Wrapper around a raw string to be interpolated with the Jinja2 templating engine
16
+
17
+ Attributes:
18
+ string (str): The string to evalute
19
+ default (Optional[str]): The default value to return if the evaluation returns an empty string
20
+ parameters (Mapping[str, Any]): Additional runtime parameters to be used for string interpolation
21
+ """
22
+
23
+ string: str
24
+ parameters: InitVar[Mapping[str, Any]]
25
+ default: Optional[str] = None
26
+
27
+ def __post_init__(self, parameters: Mapping[str, Any]) -> None:
28
+ self.default = self.default or self.string
29
+ self._interpolation = JinjaInterpolation()
30
+ self._parameters = parameters
31
+ # indicates whether passed string is just a plain string, not Jinja template
32
+ # This allows for optimization, but we do not know it yet at this stage
33
+ self._is_plain_string = None
34
+
35
+ def eval(self, config: Config, **kwargs: Any) -> Any:
36
+ """
37
+ Interpolates the input string using the config and other optional arguments passed as parameter.
38
+
39
+ :param config: The user-provided configuration as specified by the source's spec
40
+ :param kwargs: Optional parameters used for interpolation
41
+ :return: The interpolated string
42
+ """
43
+ if self._is_plain_string:
44
+ return self.string
45
+ if self._is_plain_string is None:
46
+ # Let's check whether output from evaluation is the same as input.
47
+ # This indicates occurrence of a plain string, not a template and we can skip Jinja in subsequent runs.
48
+ evaluated = self._interpolation.eval(
49
+ self.string, config, self.default, parameters=self._parameters, **kwargs
50
+ )
51
+ self._is_plain_string = self.string == evaluated
52
+ return evaluated
53
+ return self._interpolation.eval(
54
+ self.string, config, self.default, parameters=self._parameters, **kwargs
55
+ )
56
+
57
+ def __eq__(self, other: Any) -> bool:
58
+ if not isinstance(other, InterpolatedString):
59
+ return False
60
+ return self.string == other.string and self.default == other.default
61
+
62
+ @classmethod
63
+ def create(
64
+ cls,
65
+ string_or_interpolated: Union["InterpolatedString", str],
66
+ *,
67
+ parameters: Mapping[str, Any],
68
+ ) -> "InterpolatedString":
69
+ """
70
+ Helper function to obtain an InterpolatedString from either a raw string or an InterpolatedString.
71
+
72
+ :param string_or_interpolated: either a raw string or an InterpolatedString.
73
+ :param parameters: parameters propagated from parent component
74
+ :return: InterpolatedString representing the input string.
75
+ """
76
+ if isinstance(string_or_interpolated, str):
77
+ return InterpolatedString(string=string_or_interpolated, parameters=parameters)
78
+ else:
79
+ return string_or_interpolated
@@ -0,0 +1,34 @@
1
+ #
2
+ # Copyright (c) 2025 Airbyte, Inc., all rights reserved.
3
+ #
4
+
5
+
6
+ from abc import ABC, abstractmethod
7
+ from typing import Any, Optional
8
+
9
+ from airbyte_cdk.sources.types import Config
10
+
11
+
12
+ class Interpolation(ABC):
13
+ """
14
+ Strategy for evaluating the interpolated value of a string at runtime using Jinja.
15
+ """
16
+
17
+ @abstractmethod
18
+ def eval(
19
+ self,
20
+ input_str: str,
21
+ config: Config,
22
+ default: Optional[str] = None,
23
+ **additional_options: Any,
24
+ ) -> Any:
25
+ """
26
+ Interpolates the input string using the config, and additional options passed as parameter.
27
+
28
+ :param input_str: The string to interpolate
29
+ :param config: The user-provided configuration as specified by the source's spec
30
+ :param default: Default value to return if the evaluation returns an empty string
31
+ :param additional_options: Optional parameters used for interpolation
32
+ :return: The interpolated string
33
+ """
34
+ pass