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,10 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright (c) 2023 Airbyte, Inc., all rights reserved.
|
|
3
|
+
#
|
|
4
|
+
|
|
5
|
+
# Initialize Streams Package
|
|
6
|
+
from .exceptions import UserDefinedBackoffException
|
|
7
|
+
from .http import HttpStream, HttpSubStream
|
|
8
|
+
from .http_client import HttpClient
|
|
9
|
+
|
|
10
|
+
__all__ = ["HttpClient", "HttpStream", "HttpSubStream", "UserDefinedBackoffException"]
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright (c) 2023 Airbyte, Inc., all rights reserved.
|
|
3
|
+
#
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
import typing
|
|
7
|
+
from typing import Optional, Tuple
|
|
8
|
+
|
|
9
|
+
from airbyte_cdk.sources.streams import Stream
|
|
10
|
+
from airbyte_cdk.sources.streams.availability_strategy import AvailabilityStrategy
|
|
11
|
+
from airbyte_cdk.utils.traced_exception import AirbyteTracedException
|
|
12
|
+
|
|
13
|
+
if typing.TYPE_CHECKING:
|
|
14
|
+
from airbyte_cdk.sources import Source
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class HttpAvailabilityStrategy(AvailabilityStrategy):
|
|
18
|
+
def check_availability(
|
|
19
|
+
self, stream: Stream, logger: logging.Logger, source: Optional["Source"] = None
|
|
20
|
+
) -> Tuple[bool, Optional[str]]:
|
|
21
|
+
"""
|
|
22
|
+
Check stream availability by attempting to read the first record of the
|
|
23
|
+
stream.
|
|
24
|
+
|
|
25
|
+
:param stream: stream
|
|
26
|
+
:param logger: source logger
|
|
27
|
+
:param source: (optional) source
|
|
28
|
+
:return: A tuple of (boolean, str). If boolean is true, then the stream
|
|
29
|
+
is available, and no str is required. Otherwise, the stream is unavailable
|
|
30
|
+
for some reason and the str should describe what went wrong and how to
|
|
31
|
+
resolve the unavailability, if possible.
|
|
32
|
+
"""
|
|
33
|
+
reason: Optional[str]
|
|
34
|
+
try:
|
|
35
|
+
# Some streams need a stream slice to read records (e.g. if they have a SubstreamPartitionRouter)
|
|
36
|
+
# Streams that don't need a stream slice will return `None` as their first stream slice.
|
|
37
|
+
stream_slice = self.get_first_stream_slice(stream)
|
|
38
|
+
except StopIteration:
|
|
39
|
+
# If stream_slices has no `next()` item (Note - this is different from stream_slices returning [None]!)
|
|
40
|
+
# This can happen when a substream's `stream_slices` method does a `for record in parent_records: yield <something>`
|
|
41
|
+
# without accounting for the case in which the parent stream is empty.
|
|
42
|
+
reason = f"Cannot attempt to connect to stream {stream.name} - no stream slices were found, likely because the parent stream is empty."
|
|
43
|
+
return False, reason
|
|
44
|
+
except AirbyteTracedException as error:
|
|
45
|
+
return False, error.message
|
|
46
|
+
|
|
47
|
+
try:
|
|
48
|
+
self.get_first_record_for_slice(stream, stream_slice)
|
|
49
|
+
return True, None
|
|
50
|
+
except StopIteration:
|
|
51
|
+
logger.info(f"Successfully connected to stream {stream.name}, but got 0 records.")
|
|
52
|
+
return True, None
|
|
53
|
+
except AirbyteTracedException as error:
|
|
54
|
+
return False, error.message
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright (c) 2023 Airbyte, Inc., all rights reserved.
|
|
3
|
+
#
|
|
4
|
+
|
|
5
|
+
from .backoff_strategy import BackoffStrategy
|
|
6
|
+
from .default_backoff_strategy import DefaultBackoffStrategy
|
|
7
|
+
from .error_handler import ErrorHandler
|
|
8
|
+
from .error_message_parser import ErrorMessageParser
|
|
9
|
+
from .http_status_error_handler import HttpStatusErrorHandler
|
|
10
|
+
from .json_error_message_parser import JsonErrorMessageParser
|
|
11
|
+
from .response_models import ErrorResolution, ResponseAction
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
"BackoffStrategy",
|
|
15
|
+
"DefaultBackoffStrategy",
|
|
16
|
+
"ErrorHandler",
|
|
17
|
+
"ErrorMessageParser",
|
|
18
|
+
"HttpStatusErrorHandler",
|
|
19
|
+
"JsonErrorMessageParser",
|
|
20
|
+
"ResponseAction",
|
|
21
|
+
"ErrorResolution",
|
|
22
|
+
]
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright (c) 2023 Airbyte, Inc., all rights reserved.
|
|
3
|
+
#
|
|
4
|
+
|
|
5
|
+
from abc import ABC, abstractmethod
|
|
6
|
+
from typing import Optional, Union
|
|
7
|
+
|
|
8
|
+
import requests
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class BackoffStrategy(ABC):
|
|
12
|
+
@abstractmethod
|
|
13
|
+
def backoff_time(
|
|
14
|
+
self,
|
|
15
|
+
response_or_exception: Optional[Union[requests.Response, requests.RequestException]],
|
|
16
|
+
attempt_count: int,
|
|
17
|
+
) -> Optional[float]:
|
|
18
|
+
"""
|
|
19
|
+
Override this method to dynamically determine backoff time e.g: by reading the X-Retry-After header.
|
|
20
|
+
|
|
21
|
+
This method is called only if should_backoff() returns True for the input request.
|
|
22
|
+
|
|
23
|
+
:param response_or_exception: The response or exception that caused the backoff.
|
|
24
|
+
:param attempt_count: The number of attempts already performed for this request.
|
|
25
|
+
:return how long to backoff in seconds. The return value may be a floating point number for subsecond precision. Returning None defers backoff
|
|
26
|
+
to the default backoff behavior (e.g using an exponential algorithm).
|
|
27
|
+
"""
|
|
28
|
+
pass
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Copyright (c) 2024 Airbyte, Inc., all rights reserved.
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
from typing import Optional, Union
|
|
5
|
+
|
|
6
|
+
import requests
|
|
7
|
+
|
|
8
|
+
from .backoff_strategy import BackoffStrategy
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class DefaultBackoffStrategy(BackoffStrategy):
|
|
12
|
+
def backoff_time(
|
|
13
|
+
self,
|
|
14
|
+
response_or_exception: Optional[Union[requests.Response, requests.RequestException]],
|
|
15
|
+
attempt_count: int,
|
|
16
|
+
) -> Optional[float]:
|
|
17
|
+
return None
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright (c) 2024 Airbyte, Inc., all rights reserved.
|
|
3
|
+
#
|
|
4
|
+
|
|
5
|
+
from typing import Mapping, Type, Union
|
|
6
|
+
|
|
7
|
+
from requests.exceptions import InvalidSchema, InvalidURL, RequestException
|
|
8
|
+
|
|
9
|
+
from airbyte_cdk.models import FailureType
|
|
10
|
+
from airbyte_cdk.sources.streams.http.error_handlers.response_models import (
|
|
11
|
+
ErrorResolution,
|
|
12
|
+
ResponseAction,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
DEFAULT_ERROR_MAPPING: Mapping[Union[int, str, Type[Exception]], ErrorResolution] = {
|
|
16
|
+
InvalidSchema: ErrorResolution(
|
|
17
|
+
response_action=ResponseAction.FAIL,
|
|
18
|
+
failure_type=FailureType.config_error,
|
|
19
|
+
error_message="Invalid Protocol Schema: The endpoint that data is being requested from is using an invalid or insecure. Exception: requests.exceptions.InvalidSchema",
|
|
20
|
+
),
|
|
21
|
+
InvalidURL: ErrorResolution(
|
|
22
|
+
response_action=ResponseAction.RETRY,
|
|
23
|
+
failure_type=FailureType.transient_error,
|
|
24
|
+
error_message="Invalid URL specified or DNS error occurred: The endpoint that data is being requested from is not a valid URL. Exception: requests.exceptions.InvalidURL",
|
|
25
|
+
),
|
|
26
|
+
RequestException: ErrorResolution(
|
|
27
|
+
response_action=ResponseAction.RETRY,
|
|
28
|
+
failure_type=FailureType.transient_error,
|
|
29
|
+
error_message="An exception occurred when making the request. Exception: requests.exceptions.RequestException",
|
|
30
|
+
),
|
|
31
|
+
400: ErrorResolution(
|
|
32
|
+
response_action=ResponseAction.FAIL,
|
|
33
|
+
failure_type=FailureType.system_error,
|
|
34
|
+
error_message="Bad request. Please check your request parameters.",
|
|
35
|
+
),
|
|
36
|
+
401: ErrorResolution(
|
|
37
|
+
response_action=ResponseAction.FAIL,
|
|
38
|
+
failure_type=FailureType.config_error,
|
|
39
|
+
error_message="Unauthorized. Please ensure you are authenticated correctly.",
|
|
40
|
+
),
|
|
41
|
+
403: ErrorResolution(
|
|
42
|
+
response_action=ResponseAction.FAIL,
|
|
43
|
+
failure_type=FailureType.config_error,
|
|
44
|
+
error_message="Forbidden. You don't have permission to access this resource.",
|
|
45
|
+
),
|
|
46
|
+
404: ErrorResolution(
|
|
47
|
+
response_action=ResponseAction.FAIL,
|
|
48
|
+
failure_type=FailureType.system_error,
|
|
49
|
+
error_message="Not found. The requested resource was not found on the server.",
|
|
50
|
+
),
|
|
51
|
+
405: ErrorResolution(
|
|
52
|
+
response_action=ResponseAction.FAIL,
|
|
53
|
+
failure_type=FailureType.system_error,
|
|
54
|
+
error_message="Method not allowed. Please check your request method.",
|
|
55
|
+
),
|
|
56
|
+
408: ErrorResolution(
|
|
57
|
+
response_action=ResponseAction.RETRY,
|
|
58
|
+
failure_type=FailureType.transient_error,
|
|
59
|
+
error_message="Request timeout.",
|
|
60
|
+
),
|
|
61
|
+
429: ErrorResolution(
|
|
62
|
+
response_action=ResponseAction.RATE_LIMITED,
|
|
63
|
+
failure_type=FailureType.transient_error,
|
|
64
|
+
error_message="Too many requests.",
|
|
65
|
+
),
|
|
66
|
+
500: ErrorResolution(
|
|
67
|
+
response_action=ResponseAction.RETRY,
|
|
68
|
+
failure_type=FailureType.transient_error,
|
|
69
|
+
error_message="Internal server error.",
|
|
70
|
+
),
|
|
71
|
+
502: ErrorResolution(
|
|
72
|
+
response_action=ResponseAction.RETRY,
|
|
73
|
+
failure_type=FailureType.transient_error,
|
|
74
|
+
error_message="Bad gateway.",
|
|
75
|
+
),
|
|
76
|
+
503: ErrorResolution(
|
|
77
|
+
response_action=ResponseAction.RETRY,
|
|
78
|
+
failure_type=FailureType.transient_error,
|
|
79
|
+
error_message="Service unavailable.",
|
|
80
|
+
),
|
|
81
|
+
504: ErrorResolution(
|
|
82
|
+
response_action=ResponseAction.RETRY,
|
|
83
|
+
failure_type=FailureType.transient_error,
|
|
84
|
+
error_message="Gateway timeout.",
|
|
85
|
+
),
|
|
86
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Copyright (c) 2024 Airbyte, Inc., all rights reserved.
|
|
2
|
+
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
from typing import Optional, Union
|
|
5
|
+
|
|
6
|
+
import requests
|
|
7
|
+
|
|
8
|
+
from .response_models import ErrorResolution
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ErrorHandler(ABC):
|
|
12
|
+
"""
|
|
13
|
+
Abstract base class to determine how to handle a failed HTTP request.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
@property
|
|
17
|
+
@abstractmethod
|
|
18
|
+
def max_retries(self) -> Optional[int]:
|
|
19
|
+
"""
|
|
20
|
+
The maximum number of retries to attempt before giving up.
|
|
21
|
+
"""
|
|
22
|
+
pass
|
|
23
|
+
|
|
24
|
+
@property
|
|
25
|
+
@abstractmethod
|
|
26
|
+
def max_time(self) -> Optional[int]:
|
|
27
|
+
"""
|
|
28
|
+
The maximum amount of time in seconds to retry before giving up.
|
|
29
|
+
"""
|
|
30
|
+
pass
|
|
31
|
+
|
|
32
|
+
@abstractmethod
|
|
33
|
+
def interpret_response(
|
|
34
|
+
self, response: Optional[Union[requests.Response, Exception]]
|
|
35
|
+
) -> ErrorResolution:
|
|
36
|
+
"""
|
|
37
|
+
Interpret the response or exception and return the corresponding response action, failure type, and error message.
|
|
38
|
+
|
|
39
|
+
:param response: The HTTP response object or exception raised during the request.
|
|
40
|
+
:return: A tuple containing the response action, failure type, and error message.
|
|
41
|
+
"""
|
|
42
|
+
pass
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright (c) 2023 Airbyte, Inc., all rights reserved.
|
|
3
|
+
#
|
|
4
|
+
|
|
5
|
+
from abc import ABC, abstractmethod
|
|
6
|
+
from typing import Optional
|
|
7
|
+
|
|
8
|
+
import requests
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ErrorMessageParser(ABC):
|
|
12
|
+
@abstractmethod
|
|
13
|
+
def parse_response_error_message(self, response: requests.Response) -> Optional[str]:
|
|
14
|
+
"""
|
|
15
|
+
Parse error message from response.
|
|
16
|
+
:param response: response received for the request
|
|
17
|
+
:return: error message
|
|
18
|
+
"""
|
|
19
|
+
pass
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright (c) 2024 Airbyte, Inc., all rights reserved.
|
|
3
|
+
#
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
from datetime import timedelta
|
|
7
|
+
from typing import Mapping, Optional, Union
|
|
8
|
+
|
|
9
|
+
import requests
|
|
10
|
+
|
|
11
|
+
from airbyte_cdk.models import FailureType
|
|
12
|
+
from airbyte_cdk.sources.streams.http.error_handlers.default_error_mapping import (
|
|
13
|
+
DEFAULT_ERROR_MAPPING,
|
|
14
|
+
)
|
|
15
|
+
from airbyte_cdk.sources.streams.http.error_handlers.error_handler import ErrorHandler
|
|
16
|
+
from airbyte_cdk.sources.streams.http.error_handlers.response_models import (
|
|
17
|
+
ErrorResolution,
|
|
18
|
+
ResponseAction,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class HttpStatusErrorHandler(ErrorHandler):
|
|
23
|
+
def __init__(
|
|
24
|
+
self,
|
|
25
|
+
logger: logging.Logger,
|
|
26
|
+
error_mapping: Optional[Mapping[Union[int, str, type[Exception]], ErrorResolution]] = None,
|
|
27
|
+
max_retries: int = 5,
|
|
28
|
+
max_time: timedelta = timedelta(seconds=600),
|
|
29
|
+
) -> None:
|
|
30
|
+
"""
|
|
31
|
+
Initialize the HttpStatusErrorHandler.
|
|
32
|
+
|
|
33
|
+
:param error_mapping: Custom error mappings to extend or override the default mappings.
|
|
34
|
+
"""
|
|
35
|
+
self._logger = logger
|
|
36
|
+
self._error_mapping = error_mapping or DEFAULT_ERROR_MAPPING
|
|
37
|
+
self._max_retries = max_retries
|
|
38
|
+
self._max_time = int(max_time.total_seconds())
|
|
39
|
+
|
|
40
|
+
@property
|
|
41
|
+
def max_retries(self) -> Optional[int]:
|
|
42
|
+
return self._max_retries
|
|
43
|
+
|
|
44
|
+
@property
|
|
45
|
+
def max_time(self) -> Optional[int]:
|
|
46
|
+
return self._max_time
|
|
47
|
+
|
|
48
|
+
def interpret_response(
|
|
49
|
+
self, response_or_exception: Optional[Union[requests.Response, Exception]] = None
|
|
50
|
+
) -> ErrorResolution:
|
|
51
|
+
"""
|
|
52
|
+
Interpret the response and return the corresponding response action, failure type, and error message.
|
|
53
|
+
|
|
54
|
+
:param response: The HTTP response object.
|
|
55
|
+
:return: A tuple containing the response action, failure type, and error message.
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
if isinstance(response_or_exception, Exception):
|
|
59
|
+
mapped_error: Optional[ErrorResolution] = self._error_mapping.get(
|
|
60
|
+
response_or_exception.__class__
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
if mapped_error is not None:
|
|
64
|
+
return mapped_error
|
|
65
|
+
else:
|
|
66
|
+
self._logger.error(
|
|
67
|
+
f"Unexpected exception in error handler: {response_or_exception}"
|
|
68
|
+
)
|
|
69
|
+
return ErrorResolution(
|
|
70
|
+
response_action=ResponseAction.RETRY,
|
|
71
|
+
failure_type=FailureType.system_error,
|
|
72
|
+
error_message=f"Unexpected exception in error handler: {response_or_exception}",
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
elif isinstance(response_or_exception, requests.Response):
|
|
76
|
+
if response_or_exception.status_code is None:
|
|
77
|
+
self._logger.error("Response does not include an HTTP status code.")
|
|
78
|
+
return ErrorResolution(
|
|
79
|
+
response_action=ResponseAction.RETRY,
|
|
80
|
+
failure_type=FailureType.transient_error,
|
|
81
|
+
error_message="Response does not include an HTTP status code.",
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
if response_or_exception.ok:
|
|
85
|
+
return ErrorResolution(
|
|
86
|
+
response_action=ResponseAction.SUCCESS,
|
|
87
|
+
failure_type=None,
|
|
88
|
+
error_message=None,
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
error_key = response_or_exception.status_code
|
|
92
|
+
|
|
93
|
+
mapped_error = self._error_mapping.get(error_key)
|
|
94
|
+
|
|
95
|
+
if mapped_error is not None:
|
|
96
|
+
return mapped_error
|
|
97
|
+
else:
|
|
98
|
+
self._logger.warning(f"Unexpected HTTP Status Code in error handler: '{error_key}'")
|
|
99
|
+
return ErrorResolution(
|
|
100
|
+
response_action=ResponseAction.RETRY,
|
|
101
|
+
failure_type=FailureType.system_error,
|
|
102
|
+
error_message=f"Unexpected HTTP Status Code in error handler: {error_key}",
|
|
103
|
+
)
|
|
104
|
+
else:
|
|
105
|
+
self._logger.error(f"Received unexpected response type: {type(response_or_exception)}")
|
|
106
|
+
return ErrorResolution(
|
|
107
|
+
response_action=ResponseAction.FAIL,
|
|
108
|
+
failure_type=FailureType.system_error,
|
|
109
|
+
error_message=f"Received unexpected response type: {type(response_or_exception)}",
|
|
110
|
+
)
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright (c) 2023 Airbyte, Inc., all rights reserved.
|
|
3
|
+
#
|
|
4
|
+
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
import requests
|
|
8
|
+
|
|
9
|
+
from airbyte_cdk.sources.streams.http.error_handlers import ErrorMessageParser
|
|
10
|
+
from airbyte_cdk.sources.utils.types import JsonType
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class JsonErrorMessageParser(ErrorMessageParser):
|
|
14
|
+
def _try_get_error(self, value: Optional[JsonType]) -> Optional[str]:
|
|
15
|
+
if isinstance(value, str):
|
|
16
|
+
return value
|
|
17
|
+
elif isinstance(value, list):
|
|
18
|
+
errors_in_value = [self._try_get_error(v) for v in value]
|
|
19
|
+
return ", ".join(v for v in errors_in_value if v is not None)
|
|
20
|
+
elif isinstance(value, dict):
|
|
21
|
+
new_value = (
|
|
22
|
+
value.get("message")
|
|
23
|
+
or value.get("messages")
|
|
24
|
+
or value.get("error")
|
|
25
|
+
or value.get("errors")
|
|
26
|
+
or value.get("failures")
|
|
27
|
+
or value.get("failure")
|
|
28
|
+
or value.get("detail")
|
|
29
|
+
or value.get("err")
|
|
30
|
+
or value.get("error_message")
|
|
31
|
+
or value.get("msg")
|
|
32
|
+
or value.get("reason")
|
|
33
|
+
or value.get("status_message")
|
|
34
|
+
)
|
|
35
|
+
return self._try_get_error(new_value)
|
|
36
|
+
return None
|
|
37
|
+
|
|
38
|
+
def parse_response_error_message(self, response: requests.Response) -> Optional[str]:
|
|
39
|
+
"""
|
|
40
|
+
Parses the raw response object from a failed request into a user-friendly error message.
|
|
41
|
+
|
|
42
|
+
:param response:
|
|
43
|
+
:return: A user-friendly message that indicates the cause of the error
|
|
44
|
+
"""
|
|
45
|
+
try:
|
|
46
|
+
body = response.json()
|
|
47
|
+
return self._try_get_error(body)
|
|
48
|
+
except requests.exceptions.JSONDecodeError:
|
|
49
|
+
try:
|
|
50
|
+
return response.content.decode("utf-8")
|
|
51
|
+
except Exception:
|
|
52
|
+
return None
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Copyright (c) 2024 Airbyte, Inc., all rights reserved.
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from enum import Enum
|
|
5
|
+
from typing import Optional, Union
|
|
6
|
+
|
|
7
|
+
import requests
|
|
8
|
+
from requests import HTTPError
|
|
9
|
+
|
|
10
|
+
from airbyte_cdk.models import FailureType
|
|
11
|
+
from airbyte_cdk.utils.airbyte_secrets_utils import filter_secrets
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ResponseAction(Enum):
|
|
15
|
+
SUCCESS = "SUCCESS"
|
|
16
|
+
RETRY = "RETRY"
|
|
17
|
+
FAIL = "FAIL"
|
|
18
|
+
IGNORE = "IGNORE"
|
|
19
|
+
RATE_LIMITED = "RATE_LIMITED"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass
|
|
23
|
+
class ErrorResolution:
|
|
24
|
+
response_action: Optional[ResponseAction] = None
|
|
25
|
+
failure_type: Optional[FailureType] = None
|
|
26
|
+
error_message: Optional[str] = None
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _format_exception_error_message(exception: Exception) -> str:
|
|
30
|
+
return f"{type(exception).__name__}: {str(exception)}"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _format_response_error_message(response: requests.Response) -> str:
|
|
34
|
+
try:
|
|
35
|
+
response.raise_for_status()
|
|
36
|
+
except HTTPError as exception:
|
|
37
|
+
return filter_secrets(
|
|
38
|
+
f"Response was not ok: `{str(exception)}`. Response content is: {response.text}"
|
|
39
|
+
)
|
|
40
|
+
# We purposefully do not add the response.content because the response is "ok" so there might be sensitive information in the payload.
|
|
41
|
+
# Feel free the
|
|
42
|
+
return f"Unexpected response with HTTP status {response.status_code}"
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def create_fallback_error_resolution(
|
|
46
|
+
response_or_exception: Optional[Union[requests.Response, Exception]],
|
|
47
|
+
) -> ErrorResolution:
|
|
48
|
+
if response_or_exception is None:
|
|
49
|
+
# We do not expect this case to happen but if it does, it would be good to understand the cause and improve the error message
|
|
50
|
+
error_message = "Error handler did not receive a valid response or exception. This is unexpected please contact Airbyte Support"
|
|
51
|
+
elif isinstance(response_or_exception, Exception):
|
|
52
|
+
error_message = _format_exception_error_message(response_or_exception)
|
|
53
|
+
else:
|
|
54
|
+
error_message = _format_response_error_message(response_or_exception)
|
|
55
|
+
|
|
56
|
+
return ErrorResolution(
|
|
57
|
+
response_action=ResponseAction.RETRY,
|
|
58
|
+
failure_type=FailureType.system_error,
|
|
59
|
+
error_message=error_message,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
SUCCESS_RESOLUTION = ErrorResolution(
|
|
64
|
+
response_action=ResponseAction.SUCCESS, failure_type=None, error_message=None
|
|
65
|
+
)
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright (c) 2023 Airbyte, Inc., all rights reserved.
|
|
3
|
+
#
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
from typing import Optional, Union
|
|
7
|
+
|
|
8
|
+
import requests
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class BaseBackoffException(requests.exceptions.HTTPError):
|
|
12
|
+
def __init__(
|
|
13
|
+
self,
|
|
14
|
+
request: requests.PreparedRequest,
|
|
15
|
+
response: Optional[Union[requests.Response, Exception]],
|
|
16
|
+
error_message: str = "",
|
|
17
|
+
):
|
|
18
|
+
if isinstance(response, requests.Response):
|
|
19
|
+
error_message = (
|
|
20
|
+
error_message
|
|
21
|
+
or f"Request URL: {request.url}, Response Code: {response.status_code}, Response Text: {response.text}"
|
|
22
|
+
)
|
|
23
|
+
super().__init__(error_message, request=request, response=response)
|
|
24
|
+
else:
|
|
25
|
+
error_message = error_message or f"Request URL: {request.url}, Exception: {response}"
|
|
26
|
+
super().__init__(error_message, request=request, response=None)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class RequestBodyException(Exception):
|
|
30
|
+
"""
|
|
31
|
+
Raised when there are issues in configuring a request body
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class UserDefinedBackoffException(BaseBackoffException):
|
|
36
|
+
"""
|
|
37
|
+
An exception that exposes how long it attempted to backoff
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
def __init__(
|
|
41
|
+
self,
|
|
42
|
+
backoff: Union[int, float],
|
|
43
|
+
request: requests.PreparedRequest,
|
|
44
|
+
response: Optional[Union[requests.Response, Exception]],
|
|
45
|
+
error_message: str = "",
|
|
46
|
+
):
|
|
47
|
+
"""
|
|
48
|
+
:param backoff: how long to backoff in seconds
|
|
49
|
+
:param request: the request that triggered this backoff exception
|
|
50
|
+
:param response: the response that triggered the backoff exception
|
|
51
|
+
"""
|
|
52
|
+
self.backoff = backoff
|
|
53
|
+
super().__init__(request=request, response=response, error_message=error_message)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class DefaultBackoffException(BaseBackoffException):
|
|
57
|
+
pass
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class RateLimitBackoffException(BaseBackoffException):
|
|
61
|
+
pass
|