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,104 @@
1
+ #
2
+ # Copyright (c) 2023 Airbyte, Inc., all rights reserved.
3
+ #
4
+
5
+ from __future__ import ( # Used to evaluate type hints at runtime, a NameError: name 'ConfigObserver' is not defined is thrown otherwise
6
+ annotations,
7
+ )
8
+
9
+ import time
10
+ from copy import copy
11
+ from typing import Any, List, MutableMapping
12
+
13
+ import orjson
14
+
15
+ from airbyte_cdk.models import (
16
+ AirbyteControlConnectorConfigMessage,
17
+ AirbyteControlMessage,
18
+ AirbyteMessage,
19
+ AirbyteMessageSerializer,
20
+ OrchestratorType,
21
+ Type,
22
+ )
23
+
24
+
25
+ class ObservedDict(dict): # type: ignore # disallow_any_generics is set to True, and dict is equivalent to dict[Any]
26
+ def __init__(
27
+ self,
28
+ non_observed_mapping: MutableMapping[Any, Any],
29
+ observer: ConfigObserver,
30
+ update_on_unchanged_value: bool = True,
31
+ ) -> None:
32
+ non_observed_mapping = copy(non_observed_mapping)
33
+ self.observer = observer
34
+ self.update_on_unchanged_value = update_on_unchanged_value
35
+ for item, value in non_observed_mapping.items():
36
+ # Observe nested dicts
37
+ if isinstance(value, MutableMapping):
38
+ non_observed_mapping[item] = ObservedDict(value, observer)
39
+
40
+ # Observe nested list of dicts
41
+ if isinstance(value, List):
42
+ for i, sub_value in enumerate(value):
43
+ if isinstance(sub_value, MutableMapping):
44
+ value[i] = ObservedDict(sub_value, observer)
45
+ super().__init__(non_observed_mapping)
46
+
47
+ def __setitem__(self, item: Any, value: Any) -> None:
48
+ """Override dict.__setitem__ by:
49
+ 1. Observing the new value if it is a dict
50
+ 2. Call observer update if the new value is different from the previous one
51
+ """
52
+ previous_value = self.get(item)
53
+ if isinstance(value, MutableMapping):
54
+ value = ObservedDict(value, self.observer)
55
+ if isinstance(value, List):
56
+ for i, sub_value in enumerate(value):
57
+ if isinstance(sub_value, MutableMapping):
58
+ value[i] = ObservedDict(sub_value, self.observer)
59
+ super(ObservedDict, self).__setitem__(item, value)
60
+ if self.update_on_unchanged_value or value != previous_value:
61
+ self.observer.update()
62
+
63
+
64
+ class ConfigObserver:
65
+ """This class is made to track mutations on ObservedDict config.
66
+ When update is called a CONNECTOR_CONFIG control message is emitted on stdout.
67
+ """
68
+
69
+ def set_config(self, config: ObservedDict) -> None:
70
+ self.config = config
71
+
72
+ def update(self) -> None:
73
+ emit_configuration_as_airbyte_control_message(self.config)
74
+
75
+
76
+ def observe_connector_config(
77
+ non_observed_connector_config: MutableMapping[str, Any],
78
+ ) -> ObservedDict:
79
+ if isinstance(non_observed_connector_config, ObservedDict):
80
+ raise ValueError("This connector configuration is already observed")
81
+ connector_config_observer = ConfigObserver()
82
+ observed_connector_config = ObservedDict(
83
+ non_observed_connector_config, connector_config_observer
84
+ )
85
+ connector_config_observer.set_config(observed_connector_config)
86
+ return observed_connector_config
87
+
88
+
89
+ def emit_configuration_as_airbyte_control_message(config: MutableMapping[str, Any]) -> None:
90
+ """
91
+ WARNING: deprecated - emit_configuration_as_airbyte_control_message is being deprecated in favor of the MessageRepository mechanism.
92
+ See the airbyte_cdk.sources.message package
93
+ """
94
+ airbyte_message = create_connector_config_control_message(config)
95
+ print(orjson.dumps(AirbyteMessageSerializer.dump(airbyte_message)).decode())
96
+
97
+
98
+ def create_connector_config_control_message(config: MutableMapping[str, Any]) -> AirbyteMessage:
99
+ control_message = AirbyteControlMessage(
100
+ type=OrchestratorType.CONNECTOR_CONFIG,
101
+ emitted_at=time.time() * 1000,
102
+ connectorConfig=AirbyteControlConnectorConfigMessage(config=config),
103
+ )
104
+ return AirbyteMessage(type=Type.CONTROL, control=control_message)
@@ -0,0 +1,123 @@
1
+ #
2
+ # Copyright (c) 2023 Airbyte, Inc., all rights reserved.
3
+ #
4
+
5
+
6
+ import json
7
+ import logging
8
+ import os
9
+ import pkgutil
10
+ from abc import ABC, abstractmethod
11
+ from typing import Any, Generic, Mapping, Optional, Protocol, TypeVar
12
+
13
+ import yaml
14
+
15
+ from airbyte_cdk.models import (
16
+ AirbyteConnectionStatus,
17
+ ConnectorSpecification,
18
+ ConnectorSpecificationSerializer,
19
+ )
20
+
21
+
22
+ def load_optional_package_file(package: str, filename: str) -> Optional[bytes]:
23
+ """Gets a resource from a package, returning None if it does not exist"""
24
+ try:
25
+ return pkgutil.get_data(package, filename)
26
+ except FileNotFoundError:
27
+ return None
28
+
29
+
30
+ TConfig = TypeVar("TConfig", bound=Mapping[str, Any])
31
+
32
+
33
+ class BaseConnector(ABC, Generic[TConfig]):
34
+ # configure whether the `check_config_against_spec_or_exit()` needs to be called
35
+ check_config_against_spec: bool = True
36
+
37
+ @abstractmethod
38
+ def configure(self, config: Mapping[str, Any], temp_dir: str) -> TConfig:
39
+ """
40
+ Persist config in temporary directory to run the Source job
41
+ """
42
+
43
+ @staticmethod
44
+ def read_config(config_path: str) -> Mapping[str, Any]:
45
+ config = BaseConnector._read_json_file(config_path)
46
+ if isinstance(config, Mapping):
47
+ return config
48
+ else:
49
+ raise ValueError(
50
+ f"The content of {config_path} is not an object and therefore is not a valid config. Please ensure the file represent a config."
51
+ )
52
+
53
+ @staticmethod
54
+ def _read_json_file(file_path: str) -> Any:
55
+ with open(file_path, "r") as file:
56
+ contents = file.read()
57
+
58
+ try:
59
+ return json.loads(contents)
60
+ except json.JSONDecodeError as error:
61
+ raise ValueError(
62
+ f"Could not read json file {file_path}: {error}. Please ensure that it is a valid JSON."
63
+ )
64
+
65
+ @staticmethod
66
+ def write_config(config: TConfig, config_path: str) -> None:
67
+ with open(config_path, "w") as fh:
68
+ fh.write(json.dumps(config))
69
+
70
+ def spec(self, logger: logging.Logger) -> ConnectorSpecification:
71
+ """
72
+ Returns the spec for this integration. The spec is a JSON-Schema object describing the required configurations (e.g: username and password)
73
+ required to run this integration. By default, this will be loaded from a "spec.yaml" or a "spec.json" in the package root.
74
+ """
75
+
76
+ package = self.__class__.__module__.split(".")[0]
77
+
78
+ yaml_spec = load_optional_package_file(package, "spec.yaml")
79
+ json_spec = load_optional_package_file(package, "spec.json")
80
+
81
+ if yaml_spec and json_spec:
82
+ raise RuntimeError(
83
+ "Found multiple spec files in the package. Only one of spec.yaml or spec.json should be provided."
84
+ )
85
+
86
+ if yaml_spec:
87
+ spec_obj = yaml.load(yaml_spec, Loader=yaml.SafeLoader)
88
+ elif json_spec:
89
+ try:
90
+ spec_obj = json.loads(json_spec)
91
+ except json.JSONDecodeError as error:
92
+ raise ValueError(
93
+ f"Could not read json spec file: {error}. Please ensure that it is a valid JSON."
94
+ )
95
+ else:
96
+ raise FileNotFoundError("Unable to find spec.yaml or spec.json in the package.")
97
+
98
+ return ConnectorSpecificationSerializer.load(spec_obj)
99
+
100
+ @abstractmethod
101
+ def check(self, logger: logging.Logger, config: TConfig) -> AirbyteConnectionStatus:
102
+ """
103
+ Tests if the input configuration can be used to successfully connect to the integration e.g: if a provided Stripe API token can be used to connect
104
+ to the Stripe API.
105
+ """
106
+
107
+
108
+ class _WriteConfigProtocol(Protocol):
109
+ @staticmethod
110
+ def write_config(config: Mapping[str, Any], config_path: str) -> None: ...
111
+
112
+
113
+ class DefaultConnectorMixin:
114
+ # can be overridden to change an input config
115
+ def configure(
116
+ self: _WriteConfigProtocol, config: Mapping[str, Any], temp_dir: str
117
+ ) -> Mapping[str, Any]:
118
+ config_path = os.path.join(temp_dir, "config.json")
119
+ self.write_config(config, config_path)
120
+ return config
121
+
122
+
123
+ class Connector(DefaultConnectorMixin, BaseConnector[Mapping[str, Any]], ABC): ...
@@ -0,0 +1,53 @@
1
+ # Connector Builder Backend
2
+
3
+ This is the backend for requests from the
4
+ [Connector Builder](https://docs.airbyte.com/connector-development/connector-builder-ui/overview/).
5
+
6
+ ## Local development
7
+
8
+ ### Locally running the Connector Builder backend
9
+
10
+ ```bash
11
+ python main.py read --config path/to/config --catalog path/to/catalog
12
+ ```
13
+
14
+ Note:
15
+
16
+ - Requires the keys `__injected_declarative_manifest` and `__command` in its config, where
17
+ `__injected_declarative_manifest` is a JSON manifest and `__command` is one of the commands
18
+ handled by the ConnectorBuilderHandler (`stream_read` or `resolve_manifest`), i.e.
19
+
20
+ ```json
21
+ {
22
+ "config": <normal config>,
23
+ "__injected_declarative_manifest": {...},
24
+ "__command": <"resolve_manifest" | "test_read">
25
+ }
26
+ ```
27
+
28
+ \*See
29
+ [ConnectionSpecification](https://docs.airbyte.com/understanding-airbyte/airbyte-protocol/#actor-specification)
30
+ for details on the `"config"` key if needed.
31
+
32
+ - When the `__command` is `resolve_manifest`, the argument to `catalog` should be an empty string.
33
+ - The config can optionally contain an object under the `__test_read_config` key which can define
34
+ custom test read limits with `max_records`, `max_slices`, and `max_pages_per_slice` properties.
35
+ All custom limits are optional; a default will be used for any limit that is not provided.
36
+
37
+ ### Locally running the docker image
38
+
39
+ #### Build
40
+
41
+ First, make sure you build the latest Docker image:
42
+
43
+ ```bash
44
+ docker build -t airbyte/source-declarative-manifest:dev .
45
+ ```
46
+
47
+ #### Run
48
+
49
+ Then run any of the connector commands as follows:
50
+
51
+ ```bash
52
+ docker run --rm -v $(pwd)/secrets:/secrets airbyte/source-declarative-manifest:dev read --config /secrets/config.json
53
+ ```
@@ -0,0 +1,3 @@
1
+ #
2
+ # Copyright (c) 2023 Airbyte, Inc., all rights reserved.
3
+ #
@@ -0,0 +1,121 @@
1
+ #
2
+ # Copyright (c) 2023 Airbyte, Inc., all rights reserved.
3
+ #
4
+
5
+
6
+ from dataclasses import asdict, dataclass, field
7
+ from typing import Any, List, Mapping
8
+
9
+ from airbyte_cdk.connector_builder.test_reader import TestReader
10
+ from airbyte_cdk.models import (
11
+ AirbyteMessage,
12
+ AirbyteRecordMessage,
13
+ AirbyteStateMessage,
14
+ ConfiguredAirbyteCatalog,
15
+ Type,
16
+ )
17
+ from airbyte_cdk.models import Type as MessageType
18
+ from airbyte_cdk.sources.declarative.declarative_source import DeclarativeSource
19
+ from airbyte_cdk.sources.declarative.manifest_declarative_source import ManifestDeclarativeSource
20
+ from airbyte_cdk.sources.declarative.parsers.model_to_component_factory import (
21
+ ModelToComponentFactory,
22
+ )
23
+ from airbyte_cdk.utils.airbyte_secrets_utils import filter_secrets
24
+ from airbyte_cdk.utils.datetime_helpers import ab_datetime_now
25
+ from airbyte_cdk.utils.traced_exception import AirbyteTracedException
26
+
27
+ DEFAULT_MAXIMUM_NUMBER_OF_PAGES_PER_SLICE = 5
28
+ DEFAULT_MAXIMUM_NUMBER_OF_SLICES = 5
29
+ DEFAULT_MAXIMUM_RECORDS = 100
30
+
31
+ MAX_PAGES_PER_SLICE_KEY = "max_pages_per_slice"
32
+ MAX_SLICES_KEY = "max_slices"
33
+ MAX_RECORDS_KEY = "max_records"
34
+
35
+
36
+ @dataclass
37
+ class TestReadLimits:
38
+ max_records: int = field(default=DEFAULT_MAXIMUM_RECORDS)
39
+ max_pages_per_slice: int = field(default=DEFAULT_MAXIMUM_NUMBER_OF_PAGES_PER_SLICE)
40
+ max_slices: int = field(default=DEFAULT_MAXIMUM_NUMBER_OF_SLICES)
41
+
42
+
43
+ def get_limits(config: Mapping[str, Any]) -> TestReadLimits:
44
+ command_config = config.get("__test_read_config", {})
45
+ max_pages_per_slice = (
46
+ command_config.get(MAX_PAGES_PER_SLICE_KEY) or DEFAULT_MAXIMUM_NUMBER_OF_PAGES_PER_SLICE
47
+ )
48
+ max_slices = command_config.get(MAX_SLICES_KEY) or DEFAULT_MAXIMUM_NUMBER_OF_SLICES
49
+ max_records = command_config.get(MAX_RECORDS_KEY) or DEFAULT_MAXIMUM_RECORDS
50
+ return TestReadLimits(max_records, max_pages_per_slice, max_slices)
51
+
52
+
53
+ def create_source(config: Mapping[str, Any], limits: TestReadLimits) -> ManifestDeclarativeSource:
54
+ manifest = config["__injected_declarative_manifest"]
55
+ return ManifestDeclarativeSource(
56
+ config=config,
57
+ emit_connector_builder_messages=True,
58
+ source_config=manifest,
59
+ component_factory=ModelToComponentFactory(
60
+ emit_connector_builder_messages=True,
61
+ limit_pages_fetched_per_slice=limits.max_pages_per_slice,
62
+ limit_slices_fetched=limits.max_slices,
63
+ disable_retries=True,
64
+ disable_cache=True,
65
+ ),
66
+ )
67
+
68
+
69
+ def read_stream(
70
+ source: DeclarativeSource,
71
+ config: Mapping[str, Any],
72
+ configured_catalog: ConfiguredAirbyteCatalog,
73
+ state: List[AirbyteStateMessage],
74
+ limits: TestReadLimits,
75
+ ) -> AirbyteMessage:
76
+ try:
77
+ test_read_handler = TestReader(
78
+ limits.max_pages_per_slice, limits.max_slices, limits.max_records
79
+ )
80
+ # The connector builder only supports a single stream
81
+ stream_name = configured_catalog.streams[0].stream.name
82
+
83
+ stream_read = test_read_handler.run_test_read(
84
+ source, config, configured_catalog, state, limits.max_records
85
+ )
86
+
87
+ return AirbyteMessage(
88
+ type=MessageType.RECORD,
89
+ record=AirbyteRecordMessage(
90
+ data=asdict(stream_read), stream=stream_name, emitted_at=_emitted_at()
91
+ ),
92
+ )
93
+ except Exception as exc:
94
+ error = AirbyteTracedException.from_exception(
95
+ exc,
96
+ message=filter_secrets(
97
+ f"Error reading stream with config={config} and catalog={configured_catalog}: {str(exc)}"
98
+ ),
99
+ )
100
+ return error.as_airbyte_message()
101
+
102
+
103
+ def resolve_manifest(source: ManifestDeclarativeSource) -> AirbyteMessage:
104
+ try:
105
+ return AirbyteMessage(
106
+ type=Type.RECORD,
107
+ record=AirbyteRecordMessage(
108
+ data={"manifest": source.resolved_manifest},
109
+ emitted_at=_emitted_at(),
110
+ stream="resolve_manifest",
111
+ ),
112
+ )
113
+ except Exception as exc:
114
+ error = AirbyteTracedException.from_exception(
115
+ exc, message=f"Error resolving manifest: {str(exc)}"
116
+ )
117
+ return error.as_airbyte_message()
118
+
119
+
120
+ def _emitted_at() -> int:
121
+ return ab_datetime_now().to_epoch_millis()
@@ -0,0 +1,107 @@
1
+ #
2
+ # Copyright (c) 2023 Airbyte, Inc., all rights reserved.
3
+ #
4
+
5
+
6
+ import sys
7
+ from typing import Any, List, Mapping, Optional, Tuple
8
+
9
+ import orjson
10
+
11
+ from airbyte_cdk.connector import BaseConnector
12
+ from airbyte_cdk.connector_builder.connector_builder_handler import (
13
+ TestReadLimits,
14
+ create_source,
15
+ get_limits,
16
+ read_stream,
17
+ resolve_manifest,
18
+ )
19
+ from airbyte_cdk.entrypoint import AirbyteEntrypoint
20
+ from airbyte_cdk.models import (
21
+ AirbyteMessage,
22
+ AirbyteMessageSerializer,
23
+ AirbyteStateMessage,
24
+ ConfiguredAirbyteCatalog,
25
+ ConfiguredAirbyteCatalogSerializer,
26
+ )
27
+ from airbyte_cdk.sources.declarative.manifest_declarative_source import ManifestDeclarativeSource
28
+ from airbyte_cdk.sources.source import Source
29
+ from airbyte_cdk.utils.traced_exception import AirbyteTracedException
30
+
31
+
32
+ def get_config_and_catalog_from_args(
33
+ args: List[str],
34
+ ) -> Tuple[str, Mapping[str, Any], Optional[ConfiguredAirbyteCatalog], Any]:
35
+ # TODO: Add functionality for the `debug` logger.
36
+ # Currently, no one `debug` level log will be displayed during `read` a stream for a connector created through `connector-builder`.
37
+ parsed_args = AirbyteEntrypoint.parse_args(args)
38
+ config_path, catalog_path, state_path = (
39
+ parsed_args.config,
40
+ parsed_args.catalog,
41
+ parsed_args.state,
42
+ )
43
+ if parsed_args.command != "read":
44
+ raise ValueError("Only read commands are allowed for Connector Builder requests.")
45
+
46
+ config = BaseConnector.read_config(config_path)
47
+
48
+ if "__command" not in config:
49
+ raise ValueError(
50
+ f"Invalid config: `__command` should be provided at the root of the config but config only has keys {list(config.keys())}"
51
+ )
52
+
53
+ command = config["__command"]
54
+ if command == "test_read":
55
+ catalog = ConfiguredAirbyteCatalogSerializer.load(BaseConnector.read_config(catalog_path))
56
+ state = Source.read_state(state_path)
57
+ else:
58
+ catalog = None
59
+ state = []
60
+
61
+ if "__injected_declarative_manifest" not in config:
62
+ raise ValueError(
63
+ f"Invalid config: `__injected_declarative_manifest` should be provided at the root of the config but config only has keys {list(config.keys())}"
64
+ )
65
+
66
+ return command, config, catalog, state
67
+
68
+
69
+ def handle_connector_builder_request(
70
+ source: ManifestDeclarativeSource,
71
+ command: str,
72
+ config: Mapping[str, Any],
73
+ catalog: Optional[ConfiguredAirbyteCatalog],
74
+ state: List[AirbyteStateMessage],
75
+ limits: TestReadLimits,
76
+ ) -> AirbyteMessage:
77
+ if command == "resolve_manifest":
78
+ return resolve_manifest(source)
79
+ elif command == "test_read":
80
+ assert (
81
+ catalog is not None
82
+ ), "`test_read` requires a valid `ConfiguredAirbyteCatalog`, got None."
83
+ return read_stream(source, config, catalog, state, limits)
84
+ else:
85
+ raise ValueError(f"Unrecognized command {command}.")
86
+
87
+
88
+ def handle_request(args: List[str]) -> str:
89
+ command, config, catalog, state = get_config_and_catalog_from_args(args)
90
+ limits = get_limits(config)
91
+ source = create_source(config, limits)
92
+ return orjson.dumps(
93
+ AirbyteMessageSerializer.dump(
94
+ handle_connector_builder_request(source, command, config, catalog, state, limits)
95
+ )
96
+ ).decode() # type: ignore[no-any-return] # Serializer.dump() always returns AirbyteMessage
97
+
98
+
99
+ if __name__ == "__main__":
100
+ try:
101
+ print(handle_request(sys.argv[1:]))
102
+ except Exception as exc:
103
+ error = AirbyteTracedException.from_exception(
104
+ exc, message=f"Error handling request: {str(exc)}"
105
+ )
106
+ m = error.as_airbyte_message()
107
+ print(orjson.dumps(AirbyteMessageSerializer.dump(m)).decode())
@@ -0,0 +1,73 @@
1
+ #
2
+ # Copyright (c) 2023 Airbyte, Inc., all rights reserved.
3
+ #
4
+
5
+ from dataclasses import dataclass
6
+ from typing import Any, Dict, List, Optional
7
+
8
+
9
+ @dataclass
10
+ class HttpResponse:
11
+ status: int
12
+ body: Optional[str] = None
13
+ headers: Optional[Dict[str, Any]] = None
14
+
15
+
16
+ @dataclass
17
+ class HttpRequest:
18
+ url: str
19
+ headers: Optional[Dict[str, Any]]
20
+ http_method: str
21
+ body: Optional[str] = None
22
+
23
+
24
+ @dataclass
25
+ class LogMessage:
26
+ message: str
27
+ level: str
28
+ internal_message: Optional[str] = None
29
+ stacktrace: Optional[str] = None
30
+
31
+
32
+ @dataclass
33
+ class AuxiliaryRequest:
34
+ title: str
35
+ type: str
36
+ description: str
37
+ request: HttpRequest
38
+ response: HttpResponse
39
+
40
+
41
+ @dataclass
42
+ class StreamReadPages:
43
+ records: List[object]
44
+ request: Optional[HttpRequest] = None
45
+ response: Optional[HttpResponse] = None
46
+
47
+
48
+ @dataclass
49
+ class StreamReadSlices:
50
+ pages: List[StreamReadPages]
51
+ slice_descriptor: Optional[Dict[str, Any]]
52
+ state: Optional[List[Dict[str, Any]]] = None
53
+ auxiliary_requests: Optional[List[AuxiliaryRequest]] = None
54
+
55
+
56
+ @dataclass
57
+ class StreamRead(object):
58
+ logs: List[LogMessage]
59
+ slices: List[StreamReadSlices]
60
+ test_read_limit_reached: bool
61
+ auxiliary_requests: List[AuxiliaryRequest]
62
+ inferred_schema: Optional[Dict[str, Any]]
63
+ inferred_datetime_formats: Optional[Dict[str, str]]
64
+ latest_config_update: Optional[Dict[str, Any]]
65
+
66
+
67
+ @dataclass
68
+ class StreamReadRequestBody:
69
+ manifest: Dict[str, Any]
70
+ stream: str
71
+ config: Dict[str, Any]
72
+ state: Optional[Dict[str, Any]]
73
+ record_limit: Optional[int]
@@ -0,0 +1,7 @@
1
+ #
2
+ # Copyright (c) 2023 Airbyte, Inc., all rights reserved.
3
+ #
4
+
5
+ from .reader import TestReader
6
+
7
+ __all__ = ("TestReader",)