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