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,267 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright (c) 2023 Airbyte, Inc., all rights reserved.
|
|
3
|
+
#
|
|
4
|
+
|
|
5
|
+
import base64
|
|
6
|
+
import logging
|
|
7
|
+
from dataclasses import InitVar, dataclass
|
|
8
|
+
from typing import Any, Mapping, MutableMapping, Union
|
|
9
|
+
|
|
10
|
+
import requests
|
|
11
|
+
from cachetools import TTLCache, cached
|
|
12
|
+
|
|
13
|
+
from airbyte_cdk.sources.declarative.auth.declarative_authenticator import DeclarativeAuthenticator
|
|
14
|
+
from airbyte_cdk.sources.declarative.auth.token_provider import TokenProvider
|
|
15
|
+
from airbyte_cdk.sources.declarative.interpolation.interpolated_string import InterpolatedString
|
|
16
|
+
from airbyte_cdk.sources.declarative.requesters.request_option import (
|
|
17
|
+
RequestOption,
|
|
18
|
+
RequestOptionType,
|
|
19
|
+
)
|
|
20
|
+
from airbyte_cdk.sources.types import Config
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass
|
|
24
|
+
class ApiKeyAuthenticator(DeclarativeAuthenticator):
|
|
25
|
+
"""
|
|
26
|
+
ApiKeyAuth sets a request header on the HTTP requests sent.
|
|
27
|
+
|
|
28
|
+
The header is of the form:
|
|
29
|
+
`"<header>": "<token>"`
|
|
30
|
+
|
|
31
|
+
For example,
|
|
32
|
+
`ApiKeyAuthenticator("Authorization", "Bearer hello")`
|
|
33
|
+
will result in the following header set on the HTTP request
|
|
34
|
+
`"Authorization": "Bearer hello"`
|
|
35
|
+
|
|
36
|
+
Attributes:
|
|
37
|
+
request_option (RequestOption): request option how to inject the token into the request
|
|
38
|
+
token_provider (TokenProvider): Provider of the token
|
|
39
|
+
config (Config): The user-provided configuration as specified by the source's spec
|
|
40
|
+
parameters (Mapping[str, Any]): Additional runtime parameters to be used for string interpolation
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
request_option: RequestOption
|
|
44
|
+
token_provider: TokenProvider
|
|
45
|
+
config: Config
|
|
46
|
+
parameters: InitVar[Mapping[str, Any]]
|
|
47
|
+
|
|
48
|
+
@property
|
|
49
|
+
def auth_header(self) -> str:
|
|
50
|
+
options = self._get_request_options(RequestOptionType.header)
|
|
51
|
+
return next(iter(options.keys()), "")
|
|
52
|
+
|
|
53
|
+
@property
|
|
54
|
+
def token(self) -> str:
|
|
55
|
+
return self.token_provider.get_token()
|
|
56
|
+
|
|
57
|
+
def _get_request_options(self, option_type: RequestOptionType) -> Mapping[str, Any]:
|
|
58
|
+
options: MutableMapping[str, Any] = {}
|
|
59
|
+
if self.request_option.inject_into == option_type:
|
|
60
|
+
self.request_option.inject_into_request(options, self.token, self.config)
|
|
61
|
+
return options
|
|
62
|
+
|
|
63
|
+
def get_request_params(self) -> Mapping[str, Any]:
|
|
64
|
+
return self._get_request_options(RequestOptionType.request_parameter)
|
|
65
|
+
|
|
66
|
+
def get_request_body_data(self) -> Union[Mapping[str, Any], str]:
|
|
67
|
+
return self._get_request_options(RequestOptionType.body_data)
|
|
68
|
+
|
|
69
|
+
def get_request_body_json(self) -> Mapping[str, Any]:
|
|
70
|
+
return self._get_request_options(RequestOptionType.body_json)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@dataclass
|
|
74
|
+
class BearerAuthenticator(DeclarativeAuthenticator):
|
|
75
|
+
"""
|
|
76
|
+
Authenticator that sets the Authorization header on the HTTP requests sent.
|
|
77
|
+
|
|
78
|
+
The header is of the form:
|
|
79
|
+
`"Authorization": "Bearer <token>"`
|
|
80
|
+
|
|
81
|
+
Attributes:
|
|
82
|
+
token_provider (TokenProvider): Provider of the token
|
|
83
|
+
config (Config): The user-provided configuration as specified by the source's spec
|
|
84
|
+
parameters (Mapping[str, Any]): Additional runtime parameters to be used for string interpolation
|
|
85
|
+
"""
|
|
86
|
+
|
|
87
|
+
token_provider: TokenProvider
|
|
88
|
+
config: Config
|
|
89
|
+
parameters: InitVar[Mapping[str, Any]]
|
|
90
|
+
|
|
91
|
+
@property
|
|
92
|
+
def auth_header(self) -> str:
|
|
93
|
+
return "Authorization"
|
|
94
|
+
|
|
95
|
+
@property
|
|
96
|
+
def token(self) -> str:
|
|
97
|
+
return f"Bearer {self.token_provider.get_token()}"
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
@dataclass
|
|
101
|
+
class BasicHttpAuthenticator(DeclarativeAuthenticator):
|
|
102
|
+
"""
|
|
103
|
+
Builds auth based off the basic authentication scheme as defined by RFC 7617, which transmits credentials as USER ID/password pairs, encoded using base64
|
|
104
|
+
https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication#basic_authentication_scheme
|
|
105
|
+
|
|
106
|
+
The header is of the form
|
|
107
|
+
`"Authorization": "Basic <encoded_credentials>"`
|
|
108
|
+
|
|
109
|
+
Attributes:
|
|
110
|
+
username (Union[InterpolatedString, str]): The username
|
|
111
|
+
config (Config): The user-provided configuration as specified by the source's spec
|
|
112
|
+
password (Union[InterpolatedString, str]): The password
|
|
113
|
+
parameters (Mapping[str, Any]): Additional runtime parameters to be used for string interpolation
|
|
114
|
+
"""
|
|
115
|
+
|
|
116
|
+
username: Union[InterpolatedString, str]
|
|
117
|
+
config: Config
|
|
118
|
+
parameters: InitVar[Mapping[str, Any]]
|
|
119
|
+
password: Union[InterpolatedString, str] = ""
|
|
120
|
+
|
|
121
|
+
def __post_init__(self, parameters: Mapping[str, Any]) -> None:
|
|
122
|
+
self._username = InterpolatedString.create(self.username, parameters=parameters)
|
|
123
|
+
self._password = InterpolatedString.create(self.password, parameters=parameters)
|
|
124
|
+
|
|
125
|
+
@property
|
|
126
|
+
def auth_header(self) -> str:
|
|
127
|
+
return "Authorization"
|
|
128
|
+
|
|
129
|
+
@property
|
|
130
|
+
def token(self) -> str:
|
|
131
|
+
auth_string = (
|
|
132
|
+
f"{self._username.eval(self.config)}:{self._password.eval(self.config)}".encode("utf8")
|
|
133
|
+
)
|
|
134
|
+
b64_encoded = base64.b64encode(auth_string).decode("utf8")
|
|
135
|
+
return f"Basic {b64_encoded}"
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
"""
|
|
139
|
+
maxsize - The maximum size of the cache
|
|
140
|
+
ttl - time-to-live value in seconds
|
|
141
|
+
docs https://cachetools.readthedocs.io/en/latest/
|
|
142
|
+
maxsize=1000 - when the cache is full, in this case more than 1000,
|
|
143
|
+
i.e. by adding another item the cache would exceed its maximum size, the cache must choose which item(s) to discard
|
|
144
|
+
ttl=86400 means that cached token will live for 86400 seconds (one day)
|
|
145
|
+
"""
|
|
146
|
+
cacheSessionTokenAuthenticator: TTLCache[str, str] = TTLCache(maxsize=1000, ttl=86400)
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
@cached(cacheSessionTokenAuthenticator)
|
|
150
|
+
def get_new_session_token(api_url: str, username: str, password: str, response_key: str) -> str:
|
|
151
|
+
"""
|
|
152
|
+
This method retrieves session token from api by username and password for SessionTokenAuthenticator.
|
|
153
|
+
It's cashed to avoid a multiple calling by sync and updating session token every stream sync.
|
|
154
|
+
Args:
|
|
155
|
+
api_url: api url for getting new session token
|
|
156
|
+
username: username for auth
|
|
157
|
+
password: password for auth
|
|
158
|
+
response_key: field name in response to retrieve a session token
|
|
159
|
+
|
|
160
|
+
Returns:
|
|
161
|
+
session token
|
|
162
|
+
"""
|
|
163
|
+
response = requests.post(
|
|
164
|
+
f"{api_url}",
|
|
165
|
+
headers={"Content-Type": "application/json"},
|
|
166
|
+
json={"username": username, "password": password},
|
|
167
|
+
)
|
|
168
|
+
response.raise_for_status()
|
|
169
|
+
if not response.ok:
|
|
170
|
+
raise ConnectionError(
|
|
171
|
+
f"Failed to retrieve new session token, response code {response.status_code} because {response.reason}"
|
|
172
|
+
)
|
|
173
|
+
return str(response.json()[response_key])
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
@dataclass
|
|
177
|
+
class LegacySessionTokenAuthenticator(DeclarativeAuthenticator):
|
|
178
|
+
"""
|
|
179
|
+
Builds auth based on session tokens.
|
|
180
|
+
A session token is a random value generated by a server to identify
|
|
181
|
+
a specific user for the duration of one interaction session.
|
|
182
|
+
|
|
183
|
+
The header is of the form
|
|
184
|
+
`"Specific Header": "Session Token Value"`
|
|
185
|
+
|
|
186
|
+
Attributes:
|
|
187
|
+
api_url (Union[InterpolatedString, str]): Base api url of source
|
|
188
|
+
username (Union[InterpolatedString, str]): The username
|
|
189
|
+
config (Config): The user-provided configuration as specified by the source's spec
|
|
190
|
+
password (Union[InterpolatedString, str]): The password
|
|
191
|
+
header (Union[InterpolatedString, str]): Specific header of source for providing session token
|
|
192
|
+
parameters (Mapping[str, Any]): Additional runtime parameters to be used for string interpolation
|
|
193
|
+
session_token (Union[InterpolatedString, str]): Session token generated by user
|
|
194
|
+
session_token_response_key (Union[InterpolatedString, str]): Key for retrieving session token from api response
|
|
195
|
+
login_url (Union[InterpolatedString, str]): Url fot getting a specific session token
|
|
196
|
+
validate_session_url (Union[InterpolatedString, str]): Url to validate passed session token
|
|
197
|
+
"""
|
|
198
|
+
|
|
199
|
+
api_url: Union[InterpolatedString, str]
|
|
200
|
+
header: Union[InterpolatedString, str]
|
|
201
|
+
session_token: Union[InterpolatedString, str]
|
|
202
|
+
session_token_response_key: Union[InterpolatedString, str]
|
|
203
|
+
username: Union[InterpolatedString, str]
|
|
204
|
+
config: Config
|
|
205
|
+
parameters: InitVar[Mapping[str, Any]]
|
|
206
|
+
login_url: Union[InterpolatedString, str]
|
|
207
|
+
validate_session_url: Union[InterpolatedString, str]
|
|
208
|
+
password: Union[InterpolatedString, str] = ""
|
|
209
|
+
|
|
210
|
+
def __post_init__(self, parameters: Mapping[str, Any]) -> None:
|
|
211
|
+
self._username = InterpolatedString.create(self.username, parameters=parameters)
|
|
212
|
+
self._password = InterpolatedString.create(self.password, parameters=parameters)
|
|
213
|
+
self._api_url = InterpolatedString.create(self.api_url, parameters=parameters)
|
|
214
|
+
self._header = InterpolatedString.create(self.header, parameters=parameters)
|
|
215
|
+
self._session_token = InterpolatedString.create(self.session_token, parameters=parameters)
|
|
216
|
+
self._session_token_response_key = InterpolatedString.create(
|
|
217
|
+
self.session_token_response_key, parameters=parameters
|
|
218
|
+
)
|
|
219
|
+
self._login_url = InterpolatedString.create(self.login_url, parameters=parameters)
|
|
220
|
+
self._validate_session_url = InterpolatedString.create(
|
|
221
|
+
self.validate_session_url, parameters=parameters
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
self.logger = logging.getLogger("airbyte")
|
|
225
|
+
|
|
226
|
+
@property
|
|
227
|
+
def auth_header(self) -> str:
|
|
228
|
+
return str(self._header.eval(self.config))
|
|
229
|
+
|
|
230
|
+
@property
|
|
231
|
+
def token(self) -> str:
|
|
232
|
+
if self._session_token.eval(self.config):
|
|
233
|
+
if self.is_valid_session_token():
|
|
234
|
+
return str(self._session_token.eval(self.config))
|
|
235
|
+
if self._password.eval(self.config) and self._username.eval(self.config):
|
|
236
|
+
username = self._username.eval(self.config)
|
|
237
|
+
password = self._password.eval(self.config)
|
|
238
|
+
session_token_response_key = self._session_token_response_key.eval(self.config)
|
|
239
|
+
api_url = f"{self._api_url.eval(self.config)}{self._login_url.eval(self.config)}"
|
|
240
|
+
|
|
241
|
+
self.logger.info("Using generated session token by username and password")
|
|
242
|
+
return get_new_session_token(api_url, username, password, session_token_response_key)
|
|
243
|
+
|
|
244
|
+
raise ConnectionError(
|
|
245
|
+
"Invalid credentials: session token is not valid or provide username and password"
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
def is_valid_session_token(self) -> bool:
|
|
249
|
+
try:
|
|
250
|
+
response = requests.get(
|
|
251
|
+
f"{self._api_url.eval(self.config)}{self._validate_session_url.eval(self.config)}",
|
|
252
|
+
headers={self.auth_header: self._session_token.eval(self.config)},
|
|
253
|
+
)
|
|
254
|
+
response.raise_for_status()
|
|
255
|
+
except requests.exceptions.HTTPError as e:
|
|
256
|
+
if e.response.status_code == requests.codes["unauthorized"]:
|
|
257
|
+
self.logger.info(f"Unable to connect by session token from config due to {str(e)}")
|
|
258
|
+
return False
|
|
259
|
+
else:
|
|
260
|
+
raise ConnectionError(f"Error while validating session token: {e}")
|
|
261
|
+
if response.ok:
|
|
262
|
+
self.logger.info("Connection check for source is successful.")
|
|
263
|
+
return True
|
|
264
|
+
else:
|
|
265
|
+
raise ConnectionError(
|
|
266
|
+
f"Failed to retrieve new session token, response code {response.status_code} because {response.reason}"
|
|
267
|
+
)
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright (c) 2023 Airbyte, Inc., all rights reserved.
|
|
3
|
+
#
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
import datetime
|
|
7
|
+
from abc import abstractmethod
|
|
8
|
+
from dataclasses import InitVar, dataclass, field
|
|
9
|
+
from typing import Any, List, Mapping, Optional, Union
|
|
10
|
+
|
|
11
|
+
import dpath
|
|
12
|
+
from isodate import Duration
|
|
13
|
+
|
|
14
|
+
from airbyte_cdk.sources.declarative.decoders.decoder import Decoder
|
|
15
|
+
from airbyte_cdk.sources.declarative.decoders.json_decoder import JsonDecoder
|
|
16
|
+
from airbyte_cdk.sources.declarative.exceptions import ReadException
|
|
17
|
+
from airbyte_cdk.sources.declarative.interpolation.interpolated_string import InterpolatedString
|
|
18
|
+
from airbyte_cdk.sources.declarative.requesters.requester import Requester
|
|
19
|
+
from airbyte_cdk.sources.http_logger import format_http_message
|
|
20
|
+
from airbyte_cdk.sources.message import MessageRepository, NoopMessageRepository
|
|
21
|
+
from airbyte_cdk.sources.types import Config
|
|
22
|
+
from airbyte_cdk.utils.datetime_helpers import AirbyteDateTime, ab_datetime_now
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class TokenProvider:
|
|
26
|
+
@abstractmethod
|
|
27
|
+
def get_token(self) -> str:
|
|
28
|
+
pass
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@dataclass
|
|
32
|
+
class SessionTokenProvider(TokenProvider):
|
|
33
|
+
login_requester: Requester
|
|
34
|
+
session_token_path: List[str]
|
|
35
|
+
expiration_duration: Optional[Union[datetime.timedelta, Duration]]
|
|
36
|
+
parameters: InitVar[Mapping[str, Any]]
|
|
37
|
+
message_repository: MessageRepository = NoopMessageRepository()
|
|
38
|
+
decoder: Decoder = field(default_factory=lambda: JsonDecoder(parameters={}))
|
|
39
|
+
|
|
40
|
+
_next_expiration_time: Optional[AirbyteDateTime] = None
|
|
41
|
+
_token: Optional[str] = None
|
|
42
|
+
|
|
43
|
+
def get_token(self) -> str:
|
|
44
|
+
self._refresh_if_necessary()
|
|
45
|
+
if self._token is None:
|
|
46
|
+
raise ReadException("Failed to get session token, token is None")
|
|
47
|
+
return self._token
|
|
48
|
+
|
|
49
|
+
def _refresh_if_necessary(self) -> None:
|
|
50
|
+
if self._next_expiration_time is None or self._next_expiration_time < ab_datetime_now():
|
|
51
|
+
self._refresh()
|
|
52
|
+
|
|
53
|
+
def _refresh(self) -> None:
|
|
54
|
+
response = self.login_requester.send_request(
|
|
55
|
+
log_formatter=lambda response: format_http_message(
|
|
56
|
+
response,
|
|
57
|
+
"Login request",
|
|
58
|
+
"Obtains session token",
|
|
59
|
+
None,
|
|
60
|
+
is_auxiliary=True,
|
|
61
|
+
type="AUTH",
|
|
62
|
+
),
|
|
63
|
+
)
|
|
64
|
+
if response is None:
|
|
65
|
+
raise ReadException("Failed to get session token, response got ignored by requester")
|
|
66
|
+
session_token = dpath.get(next(self.decoder.decode(response)), self.session_token_path)
|
|
67
|
+
if self.expiration_duration is not None:
|
|
68
|
+
self._next_expiration_time = ab_datetime_now() + self.expiration_duration
|
|
69
|
+
self._token = session_token # type: ignore # Returned decoded response will be Mapping and therefore session_token will be str or None
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@dataclass
|
|
73
|
+
class InterpolatedStringTokenProvider(TokenProvider):
|
|
74
|
+
config: Config
|
|
75
|
+
api_token: Union[InterpolatedString, str]
|
|
76
|
+
parameters: Mapping[str, Any]
|
|
77
|
+
|
|
78
|
+
def __post_init__(self) -> None:
|
|
79
|
+
self._token = InterpolatedString.create(self.api_token, parameters=self.parameters)
|
|
80
|
+
|
|
81
|
+
def get_token(self) -> str:
|
|
82
|
+
return str(self._token.eval(self.config))
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright (c) 2025 Airbyte, Inc., all rights reserved.
|
|
3
|
+
#
|
|
4
|
+
|
|
5
|
+
from typing import Mapping
|
|
6
|
+
|
|
7
|
+
from pydantic.v1 import BaseModel
|
|
8
|
+
|
|
9
|
+
from airbyte_cdk.sources.declarative.checks.check_dynamic_stream import CheckDynamicStream
|
|
10
|
+
from airbyte_cdk.sources.declarative.checks.check_stream import CheckStream
|
|
11
|
+
from airbyte_cdk.sources.declarative.checks.connection_checker import ConnectionChecker
|
|
12
|
+
from airbyte_cdk.sources.declarative.models import (
|
|
13
|
+
CheckDynamicStream as CheckDynamicStreamModel,
|
|
14
|
+
)
|
|
15
|
+
from airbyte_cdk.sources.declarative.models import (
|
|
16
|
+
CheckStream as CheckStreamModel,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
COMPONENTS_CHECKER_TYPE_MAPPING: Mapping[str, type[BaseModel]] = {
|
|
20
|
+
"CheckStream": CheckStreamModel,
|
|
21
|
+
"CheckDynamicStream": CheckDynamicStreamModel,
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
__all__ = ["CheckStream", "CheckDynamicStream", "ConnectionChecker"]
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright (c) 2025 Airbyte, Inc., all rights reserved.
|
|
3
|
+
#
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
import traceback
|
|
7
|
+
from dataclasses import InitVar, dataclass
|
|
8
|
+
from typing import Any, List, Mapping, Tuple
|
|
9
|
+
|
|
10
|
+
from airbyte_cdk import AbstractSource
|
|
11
|
+
from airbyte_cdk.sources.declarative.checks.connection_checker import ConnectionChecker
|
|
12
|
+
from airbyte_cdk.sources.streams.http.availability_strategy import HttpAvailabilityStrategy
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class CheckDynamicStream(ConnectionChecker):
|
|
17
|
+
"""
|
|
18
|
+
Checks the connections by checking availability of one or many dynamic streams
|
|
19
|
+
|
|
20
|
+
Attributes:
|
|
21
|
+
stream_count (int): numbers of streams to check
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
# TODO: Add field stream_names to check_connection for static streams
|
|
25
|
+
# https://github.com/airbytehq/airbyte-python-cdk/pull/293#discussion_r1934933483
|
|
26
|
+
|
|
27
|
+
stream_count: int
|
|
28
|
+
parameters: InitVar[Mapping[str, Any]]
|
|
29
|
+
use_check_availability: bool = True
|
|
30
|
+
|
|
31
|
+
def __post_init__(self, parameters: Mapping[str, Any]) -> None:
|
|
32
|
+
self._parameters = parameters
|
|
33
|
+
|
|
34
|
+
def check_connection(
|
|
35
|
+
self, source: AbstractSource, logger: logging.Logger, config: Mapping[str, Any]
|
|
36
|
+
) -> Tuple[bool, Any]:
|
|
37
|
+
streams = source.streams(config=config)
|
|
38
|
+
|
|
39
|
+
if len(streams) == 0:
|
|
40
|
+
return False, f"No streams to connect to from source {source}"
|
|
41
|
+
if not self.use_check_availability:
|
|
42
|
+
return True, None
|
|
43
|
+
|
|
44
|
+
availability_strategy = HttpAvailabilityStrategy()
|
|
45
|
+
|
|
46
|
+
try:
|
|
47
|
+
for stream in streams[: min(self.stream_count, len(streams))]:
|
|
48
|
+
stream_is_available, reason = availability_strategy.check_availability(
|
|
49
|
+
stream, logger
|
|
50
|
+
)
|
|
51
|
+
if not stream_is_available:
|
|
52
|
+
logger.warning(f"Stream {stream.name} is not available: {reason}")
|
|
53
|
+
return False, reason
|
|
54
|
+
except Exception as error:
|
|
55
|
+
error_message = (
|
|
56
|
+
f"Encountered an error trying to connect to stream {stream.name}. Error: {error}"
|
|
57
|
+
)
|
|
58
|
+
logger.error(error_message, exc_info=True)
|
|
59
|
+
return False, error_message
|
|
60
|
+
|
|
61
|
+
return True, None
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright (c) 2023 Airbyte, Inc., all rights reserved.
|
|
3
|
+
#
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
import traceback
|
|
7
|
+
from dataclasses import InitVar, dataclass
|
|
8
|
+
from typing import Any, List, Mapping, Tuple
|
|
9
|
+
|
|
10
|
+
from airbyte_cdk import AbstractSource
|
|
11
|
+
from airbyte_cdk.sources.declarative.checks.connection_checker import ConnectionChecker
|
|
12
|
+
from airbyte_cdk.sources.streams.http.availability_strategy import HttpAvailabilityStrategy
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class CheckStream(ConnectionChecker):
|
|
17
|
+
"""
|
|
18
|
+
Checks the connections by checking availability of one or many streams selected by the developer
|
|
19
|
+
|
|
20
|
+
Attributes:
|
|
21
|
+
stream_name (List[str]): names of streams to check
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
stream_names: List[str]
|
|
25
|
+
parameters: InitVar[Mapping[str, Any]]
|
|
26
|
+
|
|
27
|
+
def __post_init__(self, parameters: Mapping[str, Any]) -> None:
|
|
28
|
+
self._parameters = parameters
|
|
29
|
+
|
|
30
|
+
def check_connection(
|
|
31
|
+
self, source: AbstractSource, logger: logging.Logger, config: Mapping[str, Any]
|
|
32
|
+
) -> Tuple[bool, Any]:
|
|
33
|
+
streams = source.streams(config=config)
|
|
34
|
+
stream_name_to_stream = {s.name: s for s in streams}
|
|
35
|
+
if len(streams) == 0:
|
|
36
|
+
return False, f"No streams to connect to from source {source}"
|
|
37
|
+
for stream_name in self.stream_names:
|
|
38
|
+
if stream_name not in stream_name_to_stream.keys():
|
|
39
|
+
raise ValueError(
|
|
40
|
+
f"{stream_name} is not part of the catalog. Expected one of {stream_name_to_stream.keys()}."
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
stream = stream_name_to_stream[stream_name]
|
|
44
|
+
availability_strategy = HttpAvailabilityStrategy()
|
|
45
|
+
try:
|
|
46
|
+
stream_is_available, reason = availability_strategy.check_availability(
|
|
47
|
+
stream, logger
|
|
48
|
+
)
|
|
49
|
+
if not stream_is_available:
|
|
50
|
+
return False, reason
|
|
51
|
+
except Exception as error:
|
|
52
|
+
logger.error(
|
|
53
|
+
f"Encountered an error trying to connect to stream {stream_name}. Error: \n {traceback.format_exc()}"
|
|
54
|
+
)
|
|
55
|
+
return False, f"Unable to connect to stream {stream_name} - {error}"
|
|
56
|
+
return True, None
|
|
@@ -0,0 +1,35 @@
|
|
|
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 Any, Mapping, Tuple
|
|
8
|
+
|
|
9
|
+
from airbyte_cdk import AbstractSource
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ConnectionChecker(ABC):
|
|
13
|
+
"""
|
|
14
|
+
Abstract base class for checking a connection
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
@abstractmethod
|
|
18
|
+
def check_connection(
|
|
19
|
+
self, source: AbstractSource, logger: logging.Logger, config: Mapping[str, Any]
|
|
20
|
+
) -> Tuple[bool, Any]:
|
|
21
|
+
"""
|
|
22
|
+
Tests if the input configuration can be used to successfully connect to the integration e.g: if a provided Stripe API token can be used to connect
|
|
23
|
+
to the Stripe API.
|
|
24
|
+
|
|
25
|
+
:param source: source
|
|
26
|
+
:param logger: source logger
|
|
27
|
+
:param config: The user-provided configuration as specified by the source's spec.
|
|
28
|
+
This usually contains information required to check connection e.g. tokens, secrets and keys etc.
|
|
29
|
+
:return: A tuple of (boolean, error). If boolean is true, then the connection check is successful
|
|
30
|
+
and we can connect to the underlying data source using the provided configuration.
|
|
31
|
+
Otherwise, the input config cannot be used to connect to the underlying data source,
|
|
32
|
+
and the "error" object should describe what went wrong.
|
|
33
|
+
The error object will be cast to string to display the problem to the user.
|
|
34
|
+
"""
|
|
35
|
+
pass
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright (c) 2024 Airbyte, Inc., all rights reserved.
|
|
3
|
+
#
|
|
4
|
+
|
|
5
|
+
from dataclasses import InitVar, dataclass
|
|
6
|
+
from typing import Any, Mapping, Optional, Union
|
|
7
|
+
|
|
8
|
+
from airbyte_cdk.sources.declarative.interpolation import InterpolatedString
|
|
9
|
+
from airbyte_cdk.sources.types import Config
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass
|
|
13
|
+
class ConcurrencyLevel:
|
|
14
|
+
"""
|
|
15
|
+
Returns the number of worker threads that should be used when syncing concurrent streams in parallel
|
|
16
|
+
|
|
17
|
+
Attributes:
|
|
18
|
+
default_concurrency (Union[int, str]): The hardcoded integer or interpolation of how many worker threads to use during a sync
|
|
19
|
+
max_concurrency (Optional[int]): The maximum number of worker threads to use when the default_concurrency is exceeded
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
default_concurrency: Union[int, str]
|
|
23
|
+
max_concurrency: Optional[int]
|
|
24
|
+
config: Config
|
|
25
|
+
parameters: InitVar[Mapping[str, Any]]
|
|
26
|
+
|
|
27
|
+
def __post_init__(self, parameters: Mapping[str, Any]) -> None:
|
|
28
|
+
if isinstance(self.default_concurrency, int):
|
|
29
|
+
self._default_concurrency: Union[int, InterpolatedString] = self.default_concurrency
|
|
30
|
+
elif "config" in self.default_concurrency and not self.max_concurrency:
|
|
31
|
+
raise ValueError(
|
|
32
|
+
"ConcurrencyLevel requires that max_concurrency be defined if the default_concurrency can be used-specified"
|
|
33
|
+
)
|
|
34
|
+
else:
|
|
35
|
+
self._default_concurrency = InterpolatedString.create(
|
|
36
|
+
self.default_concurrency, parameters=parameters
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
def get_concurrency_level(self) -> int:
|
|
40
|
+
if isinstance(self._default_concurrency, InterpolatedString):
|
|
41
|
+
evaluated_default_concurrency = self._default_concurrency.eval(config=self.config)
|
|
42
|
+
if not isinstance(evaluated_default_concurrency, int):
|
|
43
|
+
raise ValueError("default_concurrency did not evaluate to an integer")
|
|
44
|
+
return (
|
|
45
|
+
min(evaluated_default_concurrency, self.max_concurrency)
|
|
46
|
+
if self.max_concurrency
|
|
47
|
+
else evaluated_default_concurrency
|
|
48
|
+
)
|
|
49
|
+
else:
|
|
50
|
+
return self._default_concurrency
|