airbyte-cdk 0.72.0__py3-none-any.whl → 6.13.1.dev4106__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- airbyte_cdk/__init__.py +355 -6
- 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 +230 -0
- airbyte_cdk/cli/source_declarative_manifest/spec.json +17 -0
- airbyte_cdk/config_observation.py +29 -10
- airbyte_cdk/connector.py +24 -24
- airbyte_cdk/connector_builder/README.md +53 -0
- airbyte_cdk/connector_builder/connector_builder_handler.py +37 -11
- airbyte_cdk/connector_builder/main.py +45 -13
- airbyte_cdk/connector_builder/message_grouper.py +189 -50
- airbyte_cdk/connector_builder/models.py +3 -2
- airbyte_cdk/destinations/__init__.py +4 -3
- airbyte_cdk/destinations/destination.py +54 -20
- airbyte_cdk/destinations/vector_db_based/README.md +37 -0
- airbyte_cdk/destinations/vector_db_based/config.py +40 -17
- airbyte_cdk/destinations/vector_db_based/document_processor.py +56 -17
- airbyte_cdk/destinations/vector_db_based/embedder.py +57 -15
- airbyte_cdk/destinations/vector_db_based/test_utils.py +14 -4
- airbyte_cdk/destinations/vector_db_based/utils.py +8 -2
- airbyte_cdk/destinations/vector_db_based/writer.py +24 -5
- airbyte_cdk/entrypoint.py +153 -44
- airbyte_cdk/exception_handler.py +21 -3
- airbyte_cdk/logger.py +30 -44
- airbyte_cdk/models/__init__.py +13 -2
- airbyte_cdk/models/airbyte_protocol.py +86 -1
- airbyte_cdk/models/airbyte_protocol_serializers.py +44 -0
- airbyte_cdk/models/file_transfer_record_message.py +13 -0
- airbyte_cdk/models/well_known_types.py +1 -1
- airbyte_cdk/sources/__init__.py +5 -1
- airbyte_cdk/sources/abstract_source.py +125 -79
- airbyte_cdk/sources/concurrent_source/__init__.py +7 -2
- airbyte_cdk/sources/concurrent_source/concurrent_read_processor.py +102 -36
- airbyte_cdk/sources/concurrent_source/concurrent_source.py +29 -36
- airbyte_cdk/sources/concurrent_source/concurrent_source_adapter.py +94 -10
- airbyte_cdk/sources/concurrent_source/stream_thread_exception.py +25 -0
- airbyte_cdk/sources/concurrent_source/thread_pool_manager.py +20 -14
- airbyte_cdk/sources/config.py +3 -2
- airbyte_cdk/sources/connector_state_manager.py +49 -83
- airbyte_cdk/sources/declarative/async_job/job.py +52 -0
- airbyte_cdk/sources/declarative/async_job/job_orchestrator.py +497 -0
- airbyte_cdk/sources/declarative/async_job/job_tracker.py +75 -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 +2 -3
- airbyte_cdk/sources/declarative/auth/declarative_authenticator.py +3 -1
- airbyte_cdk/sources/declarative/auth/jwt.py +191 -0
- airbyte_cdk/sources/declarative/auth/oauth.py +60 -20
- airbyte_cdk/sources/declarative/auth/selective_authenticator.py +10 -2
- airbyte_cdk/sources/declarative/auth/token.py +28 -10
- airbyte_cdk/sources/declarative/auth/token_provider.py +9 -8
- airbyte_cdk/sources/declarative/checks/check_stream.py +16 -8
- airbyte_cdk/sources/declarative/checks/connection_checker.py +4 -2
- 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 +421 -0
- airbyte_cdk/sources/declarative/datetime/datetime_parser.py +4 -0
- airbyte_cdk/sources/declarative/datetime/min_max_datetime.py +26 -6
- airbyte_cdk/sources/declarative/declarative_component_schema.yaml +1213 -88
- airbyte_cdk/sources/declarative/declarative_source.py +5 -2
- airbyte_cdk/sources/declarative/declarative_stream.py +95 -9
- airbyte_cdk/sources/declarative/decoders/__init__.py +23 -2
- airbyte_cdk/sources/declarative/decoders/composite_raw_decoder.py +97 -0
- airbyte_cdk/sources/declarative/decoders/decoder.py +11 -4
- airbyte_cdk/sources/declarative/decoders/json_decoder.py +92 -5
- 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/extractors/__init__.py +12 -1
- airbyte_cdk/sources/declarative/extractors/dpath_extractor.py +29 -24
- airbyte_cdk/sources/declarative/extractors/http_selector.py +4 -5
- airbyte_cdk/sources/declarative/extractors/record_extractor.py +2 -3
- airbyte_cdk/sources/declarative/extractors/record_filter.py +65 -8
- airbyte_cdk/sources/declarative/extractors/record_selector.py +85 -26
- airbyte_cdk/sources/declarative/extractors/response_to_file_extractor.py +177 -0
- airbyte_cdk/sources/declarative/extractors/type_transformer.py +55 -0
- airbyte_cdk/sources/declarative/incremental/__init__.py +25 -3
- airbyte_cdk/sources/declarative/incremental/datetime_based_cursor.py +156 -48
- airbyte_cdk/sources/declarative/incremental/declarative_cursor.py +13 -0
- airbyte_cdk/sources/declarative/incremental/global_substream_cursor.py +350 -0
- airbyte_cdk/sources/declarative/incremental/per_partition_cursor.py +159 -74
- 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/filters.py +27 -1
- airbyte_cdk/sources/declarative/interpolation/interpolated_boolean.py +23 -5
- airbyte_cdk/sources/declarative/interpolation/interpolated_mapping.py +12 -8
- airbyte_cdk/sources/declarative/interpolation/interpolated_nested_mapping.py +13 -6
- airbyte_cdk/sources/declarative/interpolation/interpolated_string.py +21 -6
- airbyte_cdk/sources/declarative/interpolation/interpolation.py +9 -3
- airbyte_cdk/sources/declarative/interpolation/jinja.py +72 -37
- airbyte_cdk/sources/declarative/interpolation/macros.py +72 -17
- airbyte_cdk/sources/declarative/manifest_declarative_source.py +193 -52
- 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 +1 -1
- airbyte_cdk/sources/declarative/models/declarative_component_schema.py +1329 -595
- airbyte_cdk/sources/declarative/parsers/custom_exceptions.py +2 -2
- airbyte_cdk/sources/declarative/parsers/manifest_component_transformer.py +26 -4
- airbyte_cdk/sources/declarative/parsers/manifest_reference_resolver.py +26 -15
- airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py +1699 -226
- airbyte_cdk/sources/declarative/partition_routers/__init__.py +24 -4
- 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 +39 -9
- airbyte_cdk/sources/declarative/partition_routers/partition_router.py +62 -0
- airbyte_cdk/sources/declarative/partition_routers/single_partition_router.py +15 -3
- airbyte_cdk/sources/declarative/partition_routers/substream_partition_router.py +222 -39
- airbyte_cdk/sources/declarative/requesters/error_handlers/__init__.py +19 -5
- airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/__init__.py +3 -1
- airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/constant_backoff_strategy.py +19 -7
- airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/exponential_backoff_strategy.py +19 -7
- airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/header_helper.py +4 -2
- airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/wait_time_from_header_backoff_strategy.py +41 -9
- airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/wait_until_time_from_header_backoff_strategy.py +29 -14
- airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategy.py +5 -13
- airbyte_cdk/sources/declarative/requesters/error_handlers/composite_error_handler.py +32 -16
- airbyte_cdk/sources/declarative/requesters/error_handlers/default_error_handler.py +46 -56
- airbyte_cdk/sources/declarative/requesters/error_handlers/default_http_response_filter.py +40 -0
- airbyte_cdk/sources/declarative/requesters/error_handlers/error_handler.py +6 -32
- airbyte_cdk/sources/declarative/requesters/error_handlers/http_response_filter.py +119 -41
- airbyte_cdk/sources/declarative/requesters/http_job_repository.py +228 -0
- airbyte_cdk/sources/declarative/requesters/http_requester.py +98 -344
- airbyte_cdk/sources/declarative/requesters/paginators/__init__.py +14 -3
- airbyte_cdk/sources/declarative/requesters/paginators/default_paginator.py +105 -46
- airbyte_cdk/sources/declarative/requesters/paginators/no_pagination.py +14 -8
- airbyte_cdk/sources/declarative/requesters/paginators/paginator.py +19 -8
- airbyte_cdk/sources/declarative/requesters/paginators/strategies/__init__.py +9 -3
- airbyte_cdk/sources/declarative/requesters/paginators/strategies/cursor_pagination_strategy.py +53 -21
- airbyte_cdk/sources/declarative/requesters/paginators/strategies/offset_increment.py +42 -19
- airbyte_cdk/sources/declarative/requesters/paginators/strategies/page_increment.py +25 -12
- airbyte_cdk/sources/declarative/requesters/paginators/strategies/pagination_strategy.py +13 -10
- airbyte_cdk/sources/declarative/requesters/paginators/strategies/stop_condition.py +26 -13
- airbyte_cdk/sources/declarative/requesters/request_options/__init__.py +15 -2
- airbyte_cdk/sources/declarative/requesters/request_options/datetime_based_request_options_provider.py +91 -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 +31 -14
- airbyte_cdk/sources/declarative/requesters/request_options/interpolated_request_input_provider.py +27 -15
- airbyte_cdk/sources/declarative/requesters/request_options/interpolated_request_options_provider.py +63 -10
- airbyte_cdk/sources/declarative/requesters/request_options/request_options_provider.py +1 -1
- airbyte_cdk/sources/declarative/requesters/requester.py +9 -17
- 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 +6 -2
- airbyte_cdk/sources/declarative/retrievers/async_retriever.py +100 -0
- airbyte_cdk/sources/declarative/retrievers/retriever.py +1 -3
- airbyte_cdk/sources/declarative/retrievers/simple_retriever.py +228 -72
- airbyte_cdk/sources/declarative/schema/__init__.py +14 -1
- airbyte_cdk/sources/declarative/schema/default_schema_loader.py +5 -3
- airbyte_cdk/sources/declarative/schema/dynamic_schema_loader.py +236 -0
- airbyte_cdk/sources/declarative/schema/json_file_schema_loader.py +8 -8
- airbyte_cdk/sources/declarative/spec/spec.py +12 -5
- airbyte_cdk/sources/declarative/stream_slicers/__init__.py +1 -2
- airbyte_cdk/sources/declarative/stream_slicers/declarative_partition_generator.py +88 -0
- airbyte_cdk/sources/declarative/stream_slicers/stream_slicer.py +9 -14
- airbyte_cdk/sources/declarative/transformations/add_fields.py +19 -11
- 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 +13 -10
- airbyte_cdk/sources/declarative/transformations/transformation.py +5 -5
- airbyte_cdk/sources/declarative/types.py +19 -110
- airbyte_cdk/sources/declarative/yaml_declarative_source.py +31 -10
- airbyte_cdk/sources/embedded/base_integration.py +16 -5
- airbyte_cdk/sources/embedded/catalog.py +16 -4
- airbyte_cdk/sources/embedded/runner.py +19 -3
- airbyte_cdk/sources/embedded/tools.py +5 -2
- 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 +9 -2
- airbyte_cdk/sources/file_based/availability_strategy/abstract_file_based_availability_strategy.py +22 -6
- airbyte_cdk/sources/file_based/availability_strategy/default_file_based_availability_strategy.py +46 -10
- airbyte_cdk/sources/file_based/config/abstract_file_based_spec.py +58 -10
- airbyte_cdk/sources/file_based/config/avro_format.py +2 -1
- airbyte_cdk/sources/file_based/config/csv_format.py +29 -10
- airbyte_cdk/sources/file_based/config/excel_format.py +18 -0
- airbyte_cdk/sources/file_based/config/file_based_stream_config.py +16 -4
- airbyte_cdk/sources/file_based/config/jsonl_format.py +2 -1
- airbyte_cdk/sources/file_based/config/parquet_format.py +2 -1
- airbyte_cdk/sources/file_based/config/unstructured_format.py +13 -5
- airbyte_cdk/sources/file_based/discovery_policy/__init__.py +6 -2
- airbyte_cdk/sources/file_based/discovery_policy/abstract_discovery_policy.py +2 -4
- airbyte_cdk/sources/file_based/discovery_policy/default_discovery_policy.py +7 -2
- airbyte_cdk/sources/file_based/exceptions.py +52 -15
- airbyte_cdk/sources/file_based/file_based_source.py +163 -33
- airbyte_cdk/sources/file_based/file_based_stream_reader.py +83 -5
- airbyte_cdk/sources/file_based/file_types/__init__.py +14 -1
- airbyte_cdk/sources/file_based/file_types/avro_parser.py +75 -24
- airbyte_cdk/sources/file_based/file_types/csv_parser.py +116 -34
- airbyte_cdk/sources/file_based/file_types/excel_parser.py +196 -0
- airbyte_cdk/sources/file_based/file_types/file_transfer.py +37 -0
- airbyte_cdk/sources/file_based/file_types/file_type_parser.py +4 -1
- airbyte_cdk/sources/file_based/file_types/jsonl_parser.py +24 -8
- airbyte_cdk/sources/file_based/file_types/parquet_parser.py +60 -18
- airbyte_cdk/sources/file_based/file_types/unstructured_parser.py +145 -41
- airbyte_cdk/sources/file_based/remote_file.py +1 -1
- airbyte_cdk/sources/file_based/schema_helpers.py +38 -10
- airbyte_cdk/sources/file_based/schema_validation_policies/__init__.py +3 -1
- airbyte_cdk/sources/file_based/schema_validation_policies/abstract_schema_validation_policy.py +3 -1
- airbyte_cdk/sources/file_based/schema_validation_policies/default_schema_validation_policies.py +16 -5
- airbyte_cdk/sources/file_based/stream/abstract_file_based_stream.py +50 -13
- airbyte_cdk/sources/file_based/stream/concurrent/adapters.py +67 -27
- airbyte_cdk/sources/file_based/stream/concurrent/cursor/__init__.py +5 -1
- airbyte_cdk/sources/file_based/stream/concurrent/cursor/abstract_concurrent_file_based_cursor.py +14 -23
- airbyte_cdk/sources/file_based/stream/concurrent/cursor/file_based_concurrent_cursor.py +54 -18
- airbyte_cdk/sources/file_based/stream/concurrent/cursor/file_based_final_state_cursor.py +21 -9
- airbyte_cdk/sources/file_based/stream/cursor/abstract_file_based_cursor.py +3 -1
- airbyte_cdk/sources/file_based/stream/cursor/default_file_based_cursor.py +27 -10
- airbyte_cdk/sources/file_based/stream/default_file_based_stream.py +175 -45
- airbyte_cdk/sources/http_logger.py +8 -3
- airbyte_cdk/sources/message/__init__.py +7 -1
- airbyte_cdk/sources/message/repository.py +18 -4
- airbyte_cdk/sources/source.py +42 -38
- airbyte_cdk/sources/streams/__init__.py +2 -2
- airbyte_cdk/sources/streams/availability_strategy.py +54 -3
- airbyte_cdk/sources/streams/call_rate.py +64 -21
- airbyte_cdk/sources/streams/checkpoint/__init__.py +26 -0
- airbyte_cdk/sources/streams/checkpoint/checkpoint_reader.py +335 -0
- airbyte_cdk/sources/{declarative/incremental → streams/checkpoint}/cursor.py +17 -14
- 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/abstract_stream.py +7 -2
- airbyte_cdk/sources/streams/concurrent/adapters.py +84 -75
- airbyte_cdk/sources/streams/concurrent/availability_strategy.py +30 -2
- airbyte_cdk/sources/streams/concurrent/cursor.py +298 -42
- airbyte_cdk/sources/streams/concurrent/default_stream.py +12 -3
- airbyte_cdk/sources/streams/concurrent/exceptions.py +3 -0
- airbyte_cdk/sources/streams/concurrent/helpers.py +14 -3
- airbyte_cdk/sources/streams/concurrent/partition_enqueuer.py +12 -3
- airbyte_cdk/sources/streams/concurrent/partition_reader.py +10 -3
- airbyte_cdk/sources/streams/concurrent/partitions/partition.py +1 -16
- airbyte_cdk/sources/streams/concurrent/partitions/stream_slicer.py +21 -0
- airbyte_cdk/sources/streams/concurrent/partitions/types.py +15 -5
- airbyte_cdk/sources/streams/concurrent/state_converters/abstract_stream_state_converter.py +109 -17
- airbyte_cdk/sources/streams/concurrent/state_converters/datetime_stream_state_converter.py +90 -72
- airbyte_cdk/sources/streams/core.py +412 -87
- airbyte_cdk/sources/streams/http/__init__.py +2 -1
- airbyte_cdk/sources/streams/http/availability_strategy.py +12 -101
- 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 +27 -7
- airbyte_cdk/sources/streams/http/http.py +369 -246
- airbyte_cdk/sources/streams/http/http_client.py +531 -0
- airbyte_cdk/sources/streams/http/rate_limiting.py +76 -12
- airbyte_cdk/sources/streams/http/requests_native_auth/abstract_oauth.py +28 -9
- airbyte_cdk/sources/streams/http/requests_native_auth/abstract_token.py +2 -1
- airbyte_cdk/sources/streams/http/requests_native_auth/oauth.py +90 -35
- airbyte_cdk/sources/streams/http/requests_native_auth/token.py +13 -3
- airbyte_cdk/sources/types.py +154 -0
- airbyte_cdk/sources/utils/record_helper.py +36 -21
- airbyte_cdk/sources/utils/schema_helpers.py +13 -6
- airbyte_cdk/sources/utils/slice_logger.py +4 -1
- airbyte_cdk/sources/utils/transform.py +54 -20
- 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/catalog_builder.py +70 -18
- airbyte_cdk/test/entrypoint_wrapper.py +117 -42
- airbyte_cdk/test/mock_http/__init__.py +1 -1
- airbyte_cdk/test/mock_http/matcher.py +6 -0
- airbyte_cdk/test/mock_http/mocker.py +57 -10
- airbyte_cdk/test/mock_http/request.py +19 -3
- airbyte_cdk/test/mock_http/response.py +3 -1
- airbyte_cdk/test/mock_http/response_builder.py +32 -16
- airbyte_cdk/test/state_builder.py +18 -10
- 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 +60 -0
- airbyte_cdk/test/utils/reading.py +26 -0
- airbyte_cdk/utils/__init__.py +2 -1
- airbyte_cdk/utils/airbyte_secrets_utils.py +5 -3
- airbyte_cdk/utils/analytics_message.py +10 -2
- airbyte_cdk/utils/datetime_format_inferrer.py +4 -1
- airbyte_cdk/utils/event_timing.py +10 -10
- airbyte_cdk/utils/mapping_helpers.py +3 -1
- airbyte_cdk/utils/message_utils.py +20 -11
- airbyte_cdk/utils/print_buffer.py +75 -0
- airbyte_cdk/utils/schema_inferrer.py +198 -28
- airbyte_cdk/utils/slice_hasher.py +30 -0
- airbyte_cdk/utils/spec_schema_transformations.py +6 -3
- airbyte_cdk/utils/stream_status_utils.py +8 -1
- airbyte_cdk/utils/traced_exception.py +61 -21
- airbyte_cdk-6.13.1.dev4106.dist-info/METADATA +109 -0
- airbyte_cdk-6.13.1.dev4106.dist-info/RECORD +349 -0
- {airbyte_cdk-0.72.0.dist-info → airbyte_cdk-6.13.1.dev4106.dist-info}/WHEEL +1 -2
- airbyte_cdk-6.13.1.dev4106.dist-info/entry_points.txt +3 -0
- airbyte_cdk/sources/declarative/create_partial.py +0 -92
- airbyte_cdk/sources/declarative/parsers/class_types_registry.py +0 -102
- airbyte_cdk/sources/declarative/parsers/default_implementation_registry.py +0 -64
- airbyte_cdk/sources/declarative/requesters/error_handlers/response_action.py +0 -16
- airbyte_cdk/sources/declarative/requesters/error_handlers/response_status.py +0 -68
- airbyte_cdk/sources/declarative/stream_slicers/cartesian_product_stream_slicer.py +0 -114
- airbyte_cdk/sources/deprecated/base_source.py +0 -94
- airbyte_cdk/sources/deprecated/client.py +0 -99
- airbyte_cdk/sources/singer/__init__.py +0 -8
- airbyte_cdk/sources/singer/singer_helpers.py +0 -304
- airbyte_cdk/sources/singer/source.py +0 -186
- airbyte_cdk/sources/streams/concurrent/partitions/record.py +0 -23
- airbyte_cdk/sources/streams/http/auth/__init__.py +0 -17
- airbyte_cdk/sources/streams/http/auth/core.py +0 -29
- airbyte_cdk/sources/streams/http/auth/oauth.py +0 -113
- airbyte_cdk/sources/streams/http/auth/token.py +0 -47
- airbyte_cdk/sources/streams/utils/stream_helper.py +0 -40
- airbyte_cdk/sources/utils/catalog_helpers.py +0 -22
- airbyte_cdk/sources/utils/schema_models.py +0 -84
- airbyte_cdk-0.72.0.dist-info/METADATA +0 -243
- airbyte_cdk-0.72.0.dist-info/RECORD +0 -466
- airbyte_cdk-0.72.0.dist-info/top_level.txt +0 -3
- source_declarative_manifest/main.py +0 -29
- unit_tests/connector_builder/__init__.py +0 -3
- unit_tests/connector_builder/test_connector_builder_handler.py +0 -871
- unit_tests/connector_builder/test_message_grouper.py +0 -713
- unit_tests/connector_builder/utils.py +0 -27
- unit_tests/destinations/test_destination.py +0 -243
- unit_tests/singer/test_singer_helpers.py +0 -56
- unit_tests/singer/test_singer_source.py +0 -112
- unit_tests/sources/__init__.py +0 -0
- unit_tests/sources/concurrent_source/__init__.py +0 -3
- unit_tests/sources/concurrent_source/test_concurrent_source_adapter.py +0 -106
- unit_tests/sources/declarative/__init__.py +0 -3
- unit_tests/sources/declarative/auth/__init__.py +0 -3
- unit_tests/sources/declarative/auth/test_oauth.py +0 -331
- unit_tests/sources/declarative/auth/test_selective_authenticator.py +0 -39
- unit_tests/sources/declarative/auth/test_session_token_auth.py +0 -182
- unit_tests/sources/declarative/auth/test_token_auth.py +0 -200
- unit_tests/sources/declarative/auth/test_token_provider.py +0 -73
- unit_tests/sources/declarative/checks/__init__.py +0 -3
- unit_tests/sources/declarative/checks/test_check_stream.py +0 -146
- unit_tests/sources/declarative/decoders/__init__.py +0 -0
- unit_tests/sources/declarative/decoders/test_json_decoder.py +0 -16
- unit_tests/sources/declarative/external_component.py +0 -13
- unit_tests/sources/declarative/extractors/__init__.py +0 -3
- unit_tests/sources/declarative/extractors/test_dpath_extractor.py +0 -55
- unit_tests/sources/declarative/extractors/test_record_filter.py +0 -55
- unit_tests/sources/declarative/extractors/test_record_selector.py +0 -179
- unit_tests/sources/declarative/incremental/__init__.py +0 -0
- unit_tests/sources/declarative/incremental/test_datetime_based_cursor.py +0 -860
- unit_tests/sources/declarative/incremental/test_per_partition_cursor.py +0 -406
- unit_tests/sources/declarative/incremental/test_per_partition_cursor_integration.py +0 -332
- unit_tests/sources/declarative/interpolation/__init__.py +0 -3
- unit_tests/sources/declarative/interpolation/test_filters.py +0 -80
- unit_tests/sources/declarative/interpolation/test_interpolated_boolean.py +0 -40
- unit_tests/sources/declarative/interpolation/test_interpolated_mapping.py +0 -35
- unit_tests/sources/declarative/interpolation/test_interpolated_nested_mapping.py +0 -45
- unit_tests/sources/declarative/interpolation/test_interpolated_string.py +0 -25
- unit_tests/sources/declarative/interpolation/test_jinja.py +0 -240
- unit_tests/sources/declarative/interpolation/test_macros.py +0 -73
- unit_tests/sources/declarative/parsers/__init__.py +0 -3
- unit_tests/sources/declarative/parsers/test_manifest_component_transformer.py +0 -406
- unit_tests/sources/declarative/parsers/test_manifest_reference_resolver.py +0 -139
- unit_tests/sources/declarative/parsers/test_model_to_component_factory.py +0 -1841
- unit_tests/sources/declarative/parsers/testing_components.py +0 -36
- unit_tests/sources/declarative/partition_routers/__init__.py +0 -3
- unit_tests/sources/declarative/partition_routers/test_list_partition_router.py +0 -155
- unit_tests/sources/declarative/partition_routers/test_single_partition_router.py +0 -14
- unit_tests/sources/declarative/partition_routers/test_substream_partition_router.py +0 -404
- unit_tests/sources/declarative/requesters/__init__.py +0 -3
- unit_tests/sources/declarative/requesters/error_handlers/__init__.py +0 -3
- unit_tests/sources/declarative/requesters/error_handlers/backoff_strategies/__init__.py +0 -3
- unit_tests/sources/declarative/requesters/error_handlers/backoff_strategies/test_constant_backoff.py +0 -34
- unit_tests/sources/declarative/requesters/error_handlers/backoff_strategies/test_exponential_backoff.py +0 -36
- unit_tests/sources/declarative/requesters/error_handlers/backoff_strategies/test_header_helper.py +0 -38
- unit_tests/sources/declarative/requesters/error_handlers/backoff_strategies/test_wait_time_from_header.py +0 -35
- unit_tests/sources/declarative/requesters/error_handlers/backoff_strategies/test_wait_until_time_from_header.py +0 -64
- unit_tests/sources/declarative/requesters/error_handlers/test_composite_error_handler.py +0 -213
- unit_tests/sources/declarative/requesters/error_handlers/test_default_error_handler.py +0 -178
- unit_tests/sources/declarative/requesters/error_handlers/test_http_response_filter.py +0 -121
- unit_tests/sources/declarative/requesters/error_handlers/test_response_status.py +0 -44
- unit_tests/sources/declarative/requesters/paginators/__init__.py +0 -3
- unit_tests/sources/declarative/requesters/paginators/test_cursor_pagination_strategy.py +0 -64
- unit_tests/sources/declarative/requesters/paginators/test_default_paginator.py +0 -313
- unit_tests/sources/declarative/requesters/paginators/test_no_paginator.py +0 -12
- unit_tests/sources/declarative/requesters/paginators/test_offset_increment.py +0 -58
- unit_tests/sources/declarative/requesters/paginators/test_page_increment.py +0 -70
- unit_tests/sources/declarative/requesters/paginators/test_request_option.py +0 -43
- unit_tests/sources/declarative/requesters/paginators/test_stop_condition.py +0 -105
- unit_tests/sources/declarative/requesters/request_options/__init__.py +0 -3
- unit_tests/sources/declarative/requesters/request_options/test_interpolated_request_options_provider.py +0 -101
- unit_tests/sources/declarative/requesters/test_http_requester.py +0 -974
- unit_tests/sources/declarative/requesters/test_interpolated_request_input_provider.py +0 -32
- unit_tests/sources/declarative/retrievers/__init__.py +0 -3
- unit_tests/sources/declarative/retrievers/test_simple_retriever.py +0 -542
- unit_tests/sources/declarative/schema/__init__.py +0 -6
- unit_tests/sources/declarative/schema/source_test/SourceTest.py +0 -8
- unit_tests/sources/declarative/schema/source_test/__init__.py +0 -3
- unit_tests/sources/declarative/schema/test_default_schema_loader.py +0 -32
- unit_tests/sources/declarative/schema/test_inline_schema_loader.py +0 -19
- unit_tests/sources/declarative/schema/test_json_file_schema_loader.py +0 -26
- unit_tests/sources/declarative/states/__init__.py +0 -3
- unit_tests/sources/declarative/stream_slicers/__init__.py +0 -3
- unit_tests/sources/declarative/stream_slicers/test_cartesian_product_stream_slicer.py +0 -225
- unit_tests/sources/declarative/test_create_partial.py +0 -83
- unit_tests/sources/declarative/test_declarative_stream.py +0 -103
- unit_tests/sources/declarative/test_manifest_declarative_source.py +0 -1260
- unit_tests/sources/declarative/test_types.py +0 -39
- unit_tests/sources/declarative/test_yaml_declarative_source.py +0 -148
- unit_tests/sources/file_based/__init__.py +0 -0
- unit_tests/sources/file_based/availability_strategy/__init__.py +0 -0
- unit_tests/sources/file_based/availability_strategy/test_default_file_based_availability_strategy.py +0 -100
- unit_tests/sources/file_based/config/__init__.py +0 -0
- unit_tests/sources/file_based/config/test_abstract_file_based_spec.py +0 -28
- unit_tests/sources/file_based/config/test_csv_format.py +0 -34
- unit_tests/sources/file_based/config/test_file_based_stream_config.py +0 -84
- unit_tests/sources/file_based/discovery_policy/__init__.py +0 -0
- unit_tests/sources/file_based/discovery_policy/test_default_discovery_policy.py +0 -31
- unit_tests/sources/file_based/file_types/__init__.py +0 -0
- unit_tests/sources/file_based/file_types/test_avro_parser.py +0 -243
- unit_tests/sources/file_based/file_types/test_csv_parser.py +0 -546
- unit_tests/sources/file_based/file_types/test_jsonl_parser.py +0 -158
- unit_tests/sources/file_based/file_types/test_parquet_parser.py +0 -274
- unit_tests/sources/file_based/file_types/test_unstructured_parser.py +0 -593
- unit_tests/sources/file_based/helpers.py +0 -70
- unit_tests/sources/file_based/in_memory_files_source.py +0 -211
- unit_tests/sources/file_based/scenarios/__init__.py +0 -0
- unit_tests/sources/file_based/scenarios/avro_scenarios.py +0 -744
- unit_tests/sources/file_based/scenarios/check_scenarios.py +0 -220
- unit_tests/sources/file_based/scenarios/concurrent_incremental_scenarios.py +0 -2844
- unit_tests/sources/file_based/scenarios/csv_scenarios.py +0 -3105
- unit_tests/sources/file_based/scenarios/file_based_source_builder.py +0 -91
- unit_tests/sources/file_based/scenarios/incremental_scenarios.py +0 -1926
- unit_tests/sources/file_based/scenarios/jsonl_scenarios.py +0 -930
- unit_tests/sources/file_based/scenarios/parquet_scenarios.py +0 -754
- unit_tests/sources/file_based/scenarios/scenario_builder.py +0 -234
- unit_tests/sources/file_based/scenarios/unstructured_scenarios.py +0 -608
- unit_tests/sources/file_based/scenarios/user_input_schema_scenarios.py +0 -746
- unit_tests/sources/file_based/scenarios/validation_policy_scenarios.py +0 -726
- unit_tests/sources/file_based/stream/__init__.py +0 -0
- unit_tests/sources/file_based/stream/concurrent/__init__.py +0 -0
- unit_tests/sources/file_based/stream/concurrent/test_adapters.py +0 -362
- unit_tests/sources/file_based/stream/concurrent/test_file_based_concurrent_cursor.py +0 -458
- unit_tests/sources/file_based/stream/test_default_file_based_cursor.py +0 -310
- unit_tests/sources/file_based/stream/test_default_file_based_stream.py +0 -244
- unit_tests/sources/file_based/test_file_based_scenarios.py +0 -320
- unit_tests/sources/file_based/test_file_based_stream_reader.py +0 -272
- unit_tests/sources/file_based/test_scenarios.py +0 -253
- unit_tests/sources/file_based/test_schema_helpers.py +0 -346
- unit_tests/sources/fixtures/__init__.py +0 -3
- unit_tests/sources/fixtures/source_test_fixture.py +0 -153
- unit_tests/sources/message/__init__.py +0 -0
- unit_tests/sources/message/test_repository.py +0 -153
- unit_tests/sources/streams/__init__.py +0 -0
- unit_tests/sources/streams/concurrent/__init__.py +0 -3
- unit_tests/sources/streams/concurrent/scenarios/__init__.py +0 -3
- unit_tests/sources/streams/concurrent/scenarios/incremental_scenarios.py +0 -250
- unit_tests/sources/streams/concurrent/scenarios/stream_facade_builder.py +0 -140
- unit_tests/sources/streams/concurrent/scenarios/stream_facade_scenarios.py +0 -452
- unit_tests/sources/streams/concurrent/scenarios/test_concurrent_scenarios.py +0 -76
- unit_tests/sources/streams/concurrent/scenarios/thread_based_concurrent_stream_scenarios.py +0 -418
- unit_tests/sources/streams/concurrent/scenarios/thread_based_concurrent_stream_source_builder.py +0 -142
- unit_tests/sources/streams/concurrent/scenarios/utils.py +0 -55
- unit_tests/sources/streams/concurrent/test_adapters.py +0 -380
- unit_tests/sources/streams/concurrent/test_concurrent_read_processor.py +0 -684
- unit_tests/sources/streams/concurrent/test_cursor.py +0 -139
- unit_tests/sources/streams/concurrent/test_datetime_state_converter.py +0 -369
- unit_tests/sources/streams/concurrent/test_default_stream.py +0 -197
- unit_tests/sources/streams/concurrent/test_partition_enqueuer.py +0 -90
- unit_tests/sources/streams/concurrent/test_partition_reader.py +0 -67
- unit_tests/sources/streams/concurrent/test_thread_pool_manager.py +0 -106
- unit_tests/sources/streams/http/__init__.py +0 -0
- unit_tests/sources/streams/http/auth/__init__.py +0 -0
- unit_tests/sources/streams/http/auth/test_auth.py +0 -173
- unit_tests/sources/streams/http/requests_native_auth/__init__.py +0 -0
- unit_tests/sources/streams/http/requests_native_auth/test_requests_native_auth.py +0 -423
- unit_tests/sources/streams/http/test_availability_strategy.py +0 -180
- unit_tests/sources/streams/http/test_http.py +0 -635
- unit_tests/sources/streams/test_availability_strategy.py +0 -70
- unit_tests/sources/streams/test_call_rate.py +0 -300
- unit_tests/sources/streams/test_stream_read.py +0 -405
- unit_tests/sources/streams/test_streams_core.py +0 -184
- unit_tests/sources/test_abstract_source.py +0 -1442
- unit_tests/sources/test_concurrent_source.py +0 -112
- unit_tests/sources/test_config.py +0 -92
- unit_tests/sources/test_connector_state_manager.py +0 -482
- unit_tests/sources/test_http_logger.py +0 -252
- unit_tests/sources/test_integration_source.py +0 -86
- unit_tests/sources/test_source.py +0 -684
- unit_tests/sources/test_source_read.py +0 -460
- unit_tests/test/__init__.py +0 -0
- unit_tests/test/mock_http/__init__.py +0 -0
- unit_tests/test/mock_http/test_matcher.py +0 -53
- unit_tests/test/mock_http/test_mocker.py +0 -214
- unit_tests/test/mock_http/test_request.py +0 -117
- unit_tests/test/mock_http/test_response_builder.py +0 -177
- unit_tests/test/test_entrypoint_wrapper.py +0 -240
- unit_tests/utils/__init__.py +0 -0
- unit_tests/utils/test_datetime_format_inferrer.py +0 -60
- unit_tests/utils/test_mapping_helpers.py +0 -54
- unit_tests/utils/test_message_utils.py +0 -91
- unit_tests/utils/test_rate_limiting.py +0 -26
- unit_tests/utils/test_schema_inferrer.py +0 -202
- unit_tests/utils/test_secret_utils.py +0 -135
- unit_tests/utils/test_stream_status_utils.py +0 -61
- unit_tests/utils/test_traced_exception.py +0 -107
- /airbyte_cdk/sources/{deprecated → declarative/async_job}/__init__.py +0 -0
- {source_declarative_manifest → airbyte_cdk/sources/declarative/migrations}/__init__.py +0 -0
- {unit_tests/destinations → airbyte_cdk/sql}/__init__.py +0 -0
- {unit_tests/singer → airbyte_cdk/sql/_util}/__init__.py +0 -0
- {airbyte_cdk-0.72.0.dist-info → airbyte_cdk-6.13.1.dev4106.dist-info}/LICENSE.txt +0 -0
@@ -1,16 +1,23 @@
|
|
1
1
|
#
|
2
2
|
# Copyright (c) 2023 Airbyte, Inc., all rights reserved.
|
3
3
|
#
|
4
|
-
|
4
|
+
import copy
|
5
|
+
import logging
|
5
6
|
from dataclasses import InitVar, dataclass
|
6
7
|
from typing import TYPE_CHECKING, Any, Iterable, List, Mapping, Optional, Union
|
7
8
|
|
8
|
-
import dpath
|
9
|
-
|
9
|
+
import dpath
|
10
|
+
|
11
|
+
from airbyte_cdk.models import AirbyteMessage
|
12
|
+
from airbyte_cdk.models import Type as MessageType
|
10
13
|
from airbyte_cdk.sources.declarative.interpolation.interpolated_string import InterpolatedString
|
11
|
-
from airbyte_cdk.sources.declarative.
|
12
|
-
from airbyte_cdk.sources.declarative.
|
13
|
-
|
14
|
+
from airbyte_cdk.sources.declarative.partition_routers.partition_router import PartitionRouter
|
15
|
+
from airbyte_cdk.sources.declarative.requesters.request_option import (
|
16
|
+
RequestOption,
|
17
|
+
RequestOptionType,
|
18
|
+
)
|
19
|
+
from airbyte_cdk.sources.types import Config, Record, StreamSlice, StreamState
|
20
|
+
from airbyte_cdk.utils import AirbyteTracedException
|
14
21
|
|
15
22
|
if TYPE_CHECKING:
|
16
23
|
from airbyte_cdk.sources.declarative.declarative_stream import DeclarativeStream
|
@@ -24,7 +31,9 @@ class ParentStreamConfig:
|
|
24
31
|
stream: The stream to read records from
|
25
32
|
parent_key: The key of the parent stream's records that will be the stream slice key
|
26
33
|
partition_field: The partition key
|
34
|
+
extra_fields: Additional field paths to include in the stream slice
|
27
35
|
request_option: How to inject the slice value on an outgoing HTTP request
|
36
|
+
incremental_dependency (bool): Indicates if the parent stream should be read incrementally.
|
28
37
|
"""
|
29
38
|
|
30
39
|
stream: "DeclarativeStream" # Parent streams must be DeclarativeStream because we can't know which part of the stream slice is a partition for regular Stream
|
@@ -32,15 +41,27 @@ class ParentStreamConfig:
|
|
32
41
|
partition_field: Union[InterpolatedString, str]
|
33
42
|
config: Config
|
34
43
|
parameters: InitVar[Mapping[str, Any]]
|
44
|
+
extra_fields: Optional[Union[List[List[str]], List[List[InterpolatedString]]]] = (
|
45
|
+
None # List of field paths (arrays of strings)
|
46
|
+
)
|
35
47
|
request_option: Optional[RequestOption] = None
|
48
|
+
incremental_dependency: bool = False
|
36
49
|
|
37
50
|
def __post_init__(self, parameters: Mapping[str, Any]) -> None:
|
38
51
|
self.parent_key = InterpolatedString.create(self.parent_key, parameters=parameters)
|
39
|
-
self.partition_field = InterpolatedString.create(
|
52
|
+
self.partition_field = InterpolatedString.create(
|
53
|
+
self.partition_field, parameters=parameters
|
54
|
+
)
|
55
|
+
if self.extra_fields:
|
56
|
+
# Create InterpolatedString for each field path in extra_keys
|
57
|
+
self.extra_fields = [
|
58
|
+
[InterpolatedString.create(path, parameters=parameters) for path in key_path]
|
59
|
+
for key_path in self.extra_fields
|
60
|
+
]
|
40
61
|
|
41
62
|
|
42
63
|
@dataclass
|
43
|
-
class SubstreamPartitionRouter(
|
64
|
+
class SubstreamPartitionRouter(PartitionRouter):
|
44
65
|
"""
|
45
66
|
Partition router that iterates over the parent's stream records and emits slices
|
46
67
|
Will populate the state with `partition_field` and `parent_slice` so they can be accessed by other components
|
@@ -94,15 +115,26 @@ class SubstreamPartitionRouter(StreamSlicer):
|
|
94
115
|
# Pass the stream_slice from the argument, not the cursor because the cursor is updated after processing the response
|
95
116
|
return self._get_request_option(RequestOptionType.body_json, stream_slice)
|
96
117
|
|
97
|
-
def _get_request_option(
|
118
|
+
def _get_request_option(
|
119
|
+
self, option_type: RequestOptionType, stream_slice: Optional[StreamSlice]
|
120
|
+
) -> Mapping[str, Any]:
|
98
121
|
params = {}
|
99
122
|
if stream_slice:
|
100
123
|
for parent_config in self.parent_stream_configs:
|
101
|
-
if
|
124
|
+
if (
|
125
|
+
parent_config.request_option
|
126
|
+
and parent_config.request_option.inject_into == option_type
|
127
|
+
):
|
102
128
|
key = parent_config.partition_field.eval(self.config) # type: ignore # partition_field is always casted to an interpolated string
|
103
129
|
value = stream_slice.get(key)
|
104
130
|
if value:
|
105
|
-
params.update(
|
131
|
+
params.update(
|
132
|
+
{
|
133
|
+
parent_config.request_option.field_name.eval( # type: ignore [union-attr]
|
134
|
+
config=self.config
|
135
|
+
): value
|
136
|
+
}
|
137
|
+
)
|
106
138
|
return params
|
107
139
|
|
108
140
|
def stream_slices(self) -> Iterable[StreamSlice]:
|
@@ -127,32 +159,183 @@ class SubstreamPartitionRouter(StreamSlicer):
|
|
127
159
|
parent_stream = parent_stream_config.stream
|
128
160
|
parent_field = parent_stream_config.parent_key.eval(self.config) # type: ignore # parent_key is always casted to an interpolated string
|
129
161
|
partition_field = parent_stream_config.partition_field.eval(self.config) # type: ignore # partition_field is always casted to an interpolated string
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
partition_value = dpath.util.get(parent_record, parent_field)
|
149
|
-
except KeyError:
|
150
|
-
pass
|
162
|
+
extra_fields = None
|
163
|
+
if parent_stream_config.extra_fields:
|
164
|
+
extra_fields = [
|
165
|
+
[field_path_part.eval(self.config) for field_path_part in field_path] # type: ignore [union-attr]
|
166
|
+
for field_path in parent_stream_config.extra_fields
|
167
|
+
]
|
168
|
+
|
169
|
+
# read_stateless() assumes the parent is not concurrent. This is currently okay since the concurrent CDK does
|
170
|
+
# not support either substreams or RFR, but something that needs to be considered once we do
|
171
|
+
for parent_record in parent_stream.read_only_records():
|
172
|
+
parent_partition = None
|
173
|
+
# Skip non-records (eg AirbyteLogMessage)
|
174
|
+
if isinstance(parent_record, AirbyteMessage):
|
175
|
+
self.logger.warning(
|
176
|
+
f"Parent stream {parent_stream.name} returns records of type AirbyteMessage. This SubstreamPartitionRouter is not able to checkpoint incremental parent state."
|
177
|
+
)
|
178
|
+
if parent_record.type == MessageType.RECORD:
|
179
|
+
parent_record = parent_record.record.data # type: ignore[union-attr, assignment] # record is always a Record
|
151
180
|
else:
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
181
|
+
continue
|
182
|
+
elif isinstance(parent_record, Record):
|
183
|
+
parent_partition = (
|
184
|
+
parent_record.associated_slice.partition
|
185
|
+
if parent_record.associated_slice
|
186
|
+
else {}
|
187
|
+
)
|
188
|
+
parent_record = parent_record.data
|
189
|
+
elif not isinstance(parent_record, Mapping):
|
190
|
+
# The parent_record should only take the form of a Record, AirbyteMessage, or Mapping. Anything else is invalid
|
191
|
+
raise AirbyteTracedException(
|
192
|
+
message=f"Parent stream returned records as invalid type {type(parent_record)}"
|
193
|
+
)
|
194
|
+
try:
|
195
|
+
partition_value = dpath.get(
|
196
|
+
parent_record, # type: ignore [arg-type]
|
197
|
+
parent_field,
|
198
|
+
)
|
199
|
+
except KeyError:
|
200
|
+
continue
|
201
|
+
|
202
|
+
# Add extra fields
|
203
|
+
extracted_extra_fields = self._extract_extra_fields(parent_record, extra_fields)
|
204
|
+
|
205
|
+
yield StreamSlice(
|
206
|
+
partition={
|
207
|
+
partition_field: partition_value,
|
208
|
+
"parent_slice": parent_partition or {},
|
209
|
+
},
|
210
|
+
cursor_slice={},
|
211
|
+
extra_fields=extracted_extra_fields,
|
212
|
+
)
|
213
|
+
|
214
|
+
def _extract_extra_fields(
|
215
|
+
self,
|
216
|
+
parent_record: Mapping[str, Any] | AirbyteMessage,
|
217
|
+
extra_fields: Optional[List[List[str]]] = None,
|
218
|
+
) -> Mapping[str, Any]:
|
219
|
+
"""
|
220
|
+
Extracts additional fields specified by their paths from the parent record.
|
221
|
+
|
222
|
+
Args:
|
223
|
+
parent_record (Mapping[str, Any]): The record from the parent stream to extract fields from.
|
224
|
+
extra_fields (Optional[List[List[str]]]): A list of field paths (as lists of strings) to extract from the parent record.
|
225
|
+
|
226
|
+
Returns:
|
227
|
+
Mapping[str, Any]: A dictionary containing the extracted fields.
|
228
|
+
The keys are the joined field paths, and the values are the corresponding extracted values.
|
229
|
+
"""
|
230
|
+
extracted_extra_fields = {}
|
231
|
+
if extra_fields:
|
232
|
+
for extra_field_path in extra_fields:
|
233
|
+
try:
|
234
|
+
extra_field_value = dpath.get(
|
235
|
+
parent_record, # type: ignore [arg-type]
|
236
|
+
extra_field_path,
|
237
|
+
)
|
238
|
+
self.logger.debug(
|
239
|
+
f"Extracted extra_field_path: {extra_field_path} with value: {extra_field_value}"
|
240
|
+
)
|
241
|
+
except KeyError:
|
242
|
+
self.logger.debug(f"Failed to extract extra_field_path: {extra_field_path}")
|
243
|
+
extra_field_value = None
|
244
|
+
extracted_extra_fields[".".join(extra_field_path)] = extra_field_value
|
245
|
+
return extracted_extra_fields
|
246
|
+
|
247
|
+
def set_initial_state(self, stream_state: StreamState) -> None:
|
248
|
+
"""
|
249
|
+
Set the state of the parent streams.
|
250
|
+
|
251
|
+
If the `parent_state` key is missing from `stream_state`, migrate the child stream state to the parent stream's state format.
|
252
|
+
This migration applies only to parent streams with incremental dependencies.
|
253
|
+
|
254
|
+
Args:
|
255
|
+
stream_state (StreamState): The state of the streams to be set.
|
256
|
+
|
257
|
+
Example of state format:
|
258
|
+
{
|
259
|
+
"parent_state": {
|
260
|
+
"parent_stream_name1": {
|
261
|
+
"last_updated": "2023-05-27T00:00:00Z"
|
262
|
+
},
|
263
|
+
"parent_stream_name2": {
|
264
|
+
"last_updated": "2023-05-27T00:00:00Z"
|
265
|
+
}
|
266
|
+
}
|
267
|
+
}
|
268
|
+
|
269
|
+
Example of migrating to parent state format:
|
270
|
+
- Initial state:
|
271
|
+
{
|
272
|
+
"updated_at": "2023-05-27T00:00:00Z"
|
273
|
+
}
|
274
|
+
- After migration:
|
275
|
+
{
|
276
|
+
"updated_at": "2023-05-27T00:00:00Z",
|
277
|
+
"parent_state": {
|
278
|
+
"parent_stream_name": {
|
279
|
+
"parent_stream_cursor": "2023-05-27T00:00:00Z"
|
280
|
+
}
|
281
|
+
}
|
282
|
+
}
|
283
|
+
"""
|
284
|
+
if not stream_state:
|
285
|
+
return
|
286
|
+
|
287
|
+
parent_state = stream_state.get("parent_state", {})
|
288
|
+
|
289
|
+
# If `parent_state` doesn't exist and at least one parent stream has an incremental dependency,
|
290
|
+
# copy the child state to parent streams with incremental dependencies.
|
291
|
+
incremental_dependency = any(
|
292
|
+
[parent_config.incremental_dependency for parent_config in self.parent_stream_configs]
|
293
|
+
)
|
294
|
+
if not parent_state and not incremental_dependency:
|
295
|
+
return
|
296
|
+
|
297
|
+
if not parent_state and incremental_dependency:
|
298
|
+
# Attempt to retrieve child state
|
299
|
+
substream_state = list(stream_state.values())
|
300
|
+
substream_state = substream_state[0] if substream_state else {} # type: ignore [assignment] # Incorrect type for assignment
|
301
|
+
parent_state = {}
|
302
|
+
|
303
|
+
# Copy child state to parent streams with incremental dependencies
|
304
|
+
if substream_state:
|
305
|
+
for parent_config in self.parent_stream_configs:
|
306
|
+
if parent_config.incremental_dependency:
|
307
|
+
parent_state[parent_config.stream.name] = {
|
308
|
+
parent_config.stream.cursor_field: substream_state
|
309
|
+
}
|
310
|
+
|
311
|
+
# Set state for each parent stream with an incremental dependency
|
312
|
+
for parent_config in self.parent_stream_configs:
|
313
|
+
if parent_config.incremental_dependency:
|
314
|
+
parent_config.stream.state = parent_state.get(parent_config.stream.name, {})
|
315
|
+
|
316
|
+
def get_stream_state(self) -> Optional[Mapping[str, StreamState]]:
|
317
|
+
"""
|
318
|
+
Get the state of the parent streams.
|
319
|
+
|
320
|
+
Returns:
|
321
|
+
StreamState: The current state of the parent streams.
|
322
|
+
|
323
|
+
Example of state format:
|
324
|
+
{
|
325
|
+
"parent_stream_name1": {
|
326
|
+
"last_updated": "2023-05-27T00:00:00Z"
|
327
|
+
},
|
328
|
+
"parent_stream_name2": {
|
329
|
+
"last_updated": "2023-05-27T00:00:00Z"
|
330
|
+
}
|
331
|
+
}
|
332
|
+
"""
|
333
|
+
parent_state = {}
|
334
|
+
for parent_config in self.parent_stream_configs:
|
335
|
+
if parent_config.incremental_dependency:
|
336
|
+
parent_state[parent_config.stream.name] = copy.deepcopy(parent_config.stream.state)
|
337
|
+
return parent_state
|
338
|
+
|
339
|
+
@property
|
340
|
+
def logger(self) -> logging.Logger:
|
341
|
+
return logging.getLogger("airbyte.SubstreamPartitionRouter")
|
@@ -2,10 +2,24 @@
|
|
2
2
|
# Copyright (c) 2023 Airbyte, Inc., all rights reserved.
|
3
3
|
#
|
4
4
|
|
5
|
-
from airbyte_cdk.sources.declarative.requesters.error_handlers.backoff_strategy import
|
6
|
-
|
7
|
-
|
5
|
+
from airbyte_cdk.sources.declarative.requesters.error_handlers.backoff_strategy import (
|
6
|
+
BackoffStrategy,
|
7
|
+
)
|
8
|
+
from airbyte_cdk.sources.declarative.requesters.error_handlers.composite_error_handler import (
|
9
|
+
CompositeErrorHandler,
|
10
|
+
)
|
11
|
+
from airbyte_cdk.sources.declarative.requesters.error_handlers.default_error_handler import (
|
12
|
+
DefaultErrorHandler,
|
13
|
+
)
|
8
14
|
from airbyte_cdk.sources.declarative.requesters.error_handlers.error_handler import ErrorHandler
|
9
|
-
from airbyte_cdk.sources.declarative.requesters.error_handlers.http_response_filter import
|
15
|
+
from airbyte_cdk.sources.declarative.requesters.error_handlers.http_response_filter import (
|
16
|
+
HttpResponseFilter,
|
17
|
+
)
|
10
18
|
|
11
|
-
__all__ = [
|
19
|
+
__all__ = [
|
20
|
+
"BackoffStrategy",
|
21
|
+
"CompositeErrorHandler",
|
22
|
+
"DefaultErrorHandler",
|
23
|
+
"ErrorHandler",
|
24
|
+
"HttpResponseFilter",
|
25
|
+
]
|
@@ -2,7 +2,9 @@
|
|
2
2
|
# Copyright (c) 2023 Airbyte, Inc., all rights reserved.
|
3
3
|
#
|
4
4
|
|
5
|
-
from airbyte_cdk.sources.declarative.requesters.error_handlers.backoff_strategies.constant_backoff_strategy import
|
5
|
+
from airbyte_cdk.sources.declarative.requesters.error_handlers.backoff_strategies.constant_backoff_strategy import (
|
6
|
+
ConstantBackoffStrategy,
|
7
|
+
)
|
6
8
|
from airbyte_cdk.sources.declarative.requesters.error_handlers.backoff_strategies.exponential_backoff_strategy import (
|
7
9
|
ExponentialBackoffStrategy,
|
8
10
|
)
|
@@ -6,9 +6,10 @@ from dataclasses import InitVar, dataclass
|
|
6
6
|
from typing import Any, Mapping, Optional, Union
|
7
7
|
|
8
8
|
import requests
|
9
|
+
|
9
10
|
from airbyte_cdk.sources.declarative.interpolation.interpolated_string import InterpolatedString
|
10
|
-
from airbyte_cdk.sources.
|
11
|
-
from airbyte_cdk.sources.
|
11
|
+
from airbyte_cdk.sources.streams.http.error_handlers import BackoffStrategy
|
12
|
+
from airbyte_cdk.sources.types import Config
|
12
13
|
|
13
14
|
|
14
15
|
@dataclass
|
@@ -24,10 +25,21 @@ class ConstantBackoffStrategy(BackoffStrategy):
|
|
24
25
|
parameters: InitVar[Mapping[str, Any]]
|
25
26
|
config: Config
|
26
27
|
|
27
|
-
def __post_init__(self, parameters: Mapping[str, Any]):
|
28
|
+
def __post_init__(self, parameters: Mapping[str, Any]) -> None:
|
28
29
|
if not isinstance(self.backoff_time_in_seconds, InterpolatedString):
|
29
30
|
self.backoff_time_in_seconds = str(self.backoff_time_in_seconds)
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
31
|
+
if isinstance(self.backoff_time_in_seconds, float):
|
32
|
+
self.backoff_time_in_seconds = InterpolatedString.create(
|
33
|
+
str(self.backoff_time_in_seconds), parameters=parameters
|
34
|
+
)
|
35
|
+
else:
|
36
|
+
self.backoff_time_in_seconds = InterpolatedString.create(
|
37
|
+
self.backoff_time_in_seconds, parameters=parameters
|
38
|
+
)
|
39
|
+
|
40
|
+
def backoff_time(
|
41
|
+
self,
|
42
|
+
response_or_exception: Optional[Union[requests.Response, requests.RequestException]],
|
43
|
+
attempt_count: int,
|
44
|
+
) -> Optional[float]:
|
45
|
+
return self.backoff_time_in_seconds.eval(self.config) # type: ignore # backoff_time_in_seconds is always cast to an interpolated string
|
@@ -6,9 +6,10 @@ from dataclasses import InitVar, dataclass
|
|
6
6
|
from typing import Any, Mapping, Optional, Union
|
7
7
|
|
8
8
|
import requests
|
9
|
+
|
9
10
|
from airbyte_cdk.sources.declarative.interpolation.interpolated_string import InterpolatedString
|
10
|
-
from airbyte_cdk.sources.
|
11
|
-
from airbyte_cdk.sources.
|
11
|
+
from airbyte_cdk.sources.streams.http.error_handlers import BackoffStrategy
|
12
|
+
from airbyte_cdk.sources.types import Config
|
12
13
|
|
13
14
|
|
14
15
|
@dataclass
|
@@ -24,10 +25,21 @@ class ExponentialBackoffStrategy(BackoffStrategy):
|
|
24
25
|
config: Config
|
25
26
|
factor: Union[float, InterpolatedString, str] = 5
|
26
27
|
|
27
|
-
def __post_init__(self, parameters: Mapping[str, Any]):
|
28
|
+
def __post_init__(self, parameters: Mapping[str, Any]) -> None:
|
28
29
|
if not isinstance(self.factor, InterpolatedString):
|
29
30
|
self.factor = str(self.factor)
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
31
|
+
if isinstance(self.factor, float):
|
32
|
+
self._factor = InterpolatedString.create(str(self.factor), parameters=parameters)
|
33
|
+
else:
|
34
|
+
self._factor = InterpolatedString.create(self.factor, parameters=parameters)
|
35
|
+
|
36
|
+
@property
|
37
|
+
def _retry_factor(self) -> float:
|
38
|
+
return self._factor.eval(self.config) # type: ignore # factor is always cast to an interpolated string
|
39
|
+
|
40
|
+
def backoff_time(
|
41
|
+
self,
|
42
|
+
response_or_exception: Optional[Union[requests.Response, requests.RequestException]],
|
43
|
+
attempt_count: int,
|
44
|
+
) -> Optional[float]:
|
45
|
+
return self._retry_factor * 2**attempt_count # type: ignore # factor is always cast to an interpolated string
|
airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/header_helper.py
CHANGED
@@ -9,7 +9,9 @@ from typing import Optional
|
|
9
9
|
import requests
|
10
10
|
|
11
11
|
|
12
|
-
def get_numeric_value_from_header(
|
12
|
+
def get_numeric_value_from_header(
|
13
|
+
response: requests.Response, header: str, regex: Optional[Pattern[str]]
|
14
|
+
) -> Optional[float]:
|
13
15
|
"""
|
14
16
|
Extract a header value from the response as a float
|
15
17
|
:param response: response the extract header value from
|
@@ -27,7 +29,7 @@ def get_numeric_value_from_header(response: requests.Response, header: str, rege
|
|
27
29
|
header_value = match.group()
|
28
30
|
return _as_float(header_value)
|
29
31
|
elif isinstance(header_value, numbers.Number):
|
30
|
-
return float(header_value)
|
32
|
+
return float(header_value) # type: ignore[arg-type]
|
31
33
|
else:
|
32
34
|
return None
|
33
35
|
|
@@ -7,10 +7,17 @@ from dataclasses import InitVar, dataclass
|
|
7
7
|
from typing import Any, Mapping, Optional, Union
|
8
8
|
|
9
9
|
import requests
|
10
|
+
|
11
|
+
from airbyte_cdk.models import FailureType
|
10
12
|
from airbyte_cdk.sources.declarative.interpolation.interpolated_string import InterpolatedString
|
11
|
-
from airbyte_cdk.sources.declarative.requesters.error_handlers.backoff_strategies.header_helper import
|
12
|
-
|
13
|
-
|
13
|
+
from airbyte_cdk.sources.declarative.requesters.error_handlers.backoff_strategies.header_helper import (
|
14
|
+
get_numeric_value_from_header,
|
15
|
+
)
|
16
|
+
from airbyte_cdk.sources.declarative.requesters.error_handlers.backoff_strategy import (
|
17
|
+
BackoffStrategy,
|
18
|
+
)
|
19
|
+
from airbyte_cdk.sources.types import Config
|
20
|
+
from airbyte_cdk.utils import AirbyteTracedException
|
14
21
|
|
15
22
|
|
16
23
|
@dataclass
|
@@ -21,18 +28,43 @@ class WaitTimeFromHeaderBackoffStrategy(BackoffStrategy):
|
|
21
28
|
Attributes:
|
22
29
|
header (str): header to read wait time from
|
23
30
|
regex (Optional[str]): optional regex to apply on the header to extract its value
|
31
|
+
max_waiting_time_in_seconds: (Optional[float]): given the value extracted from the header is greater than this value, stop the stream
|
24
32
|
"""
|
25
33
|
|
26
34
|
header: Union[InterpolatedString, str]
|
27
35
|
parameters: InitVar[Mapping[str, Any]]
|
28
36
|
config: Config
|
29
|
-
regex: Optional[str] = None
|
37
|
+
regex: Optional[Union[InterpolatedString, str]] = None
|
38
|
+
max_waiting_time_in_seconds: Optional[float] = None
|
30
39
|
|
31
|
-
def __post_init__(self, parameters: Mapping[str, Any]):
|
32
|
-
self.regex =
|
40
|
+
def __post_init__(self, parameters: Mapping[str, Any]) -> None:
|
41
|
+
self.regex = (
|
42
|
+
InterpolatedString.create(self.regex, parameters=parameters) if self.regex else None
|
43
|
+
)
|
33
44
|
self.header = InterpolatedString.create(self.header, parameters=parameters)
|
34
45
|
|
35
|
-
def
|
36
|
-
|
37
|
-
|
46
|
+
def backoff_time(
|
47
|
+
self,
|
48
|
+
response_or_exception: Optional[Union[requests.Response, requests.RequestException]],
|
49
|
+
attempt_count: int,
|
50
|
+
) -> Optional[float]:
|
51
|
+
header = self.header.eval(config=self.config) # type: ignore # header is always cast to an interpolated stream
|
52
|
+
if self.regex:
|
53
|
+
evaled_regex = self.regex.eval(self.config) # type: ignore # header is always cast to an interpolated string
|
54
|
+
regex = re.compile(evaled_regex)
|
55
|
+
else:
|
56
|
+
regex = None
|
57
|
+
header_value = None
|
58
|
+
if isinstance(response_or_exception, requests.Response):
|
59
|
+
header_value = get_numeric_value_from_header(response_or_exception, header, regex)
|
60
|
+
if (
|
61
|
+
self.max_waiting_time_in_seconds
|
62
|
+
and header_value
|
63
|
+
and header_value >= self.max_waiting_time_in_seconds
|
64
|
+
):
|
65
|
+
raise AirbyteTracedException(
|
66
|
+
internal_message=f"Rate limit wait time {header_value} is greater than max waiting time of {self.max_waiting_time_in_seconds} seconds. Stopping the stream...",
|
67
|
+
message="The rate limit is greater than max waiting time has been reached.",
|
68
|
+
failure_type=FailureType.transient_error,
|
69
|
+
)
|
38
70
|
return header_value
|
@@ -9,10 +9,15 @@ from dataclasses import InitVar, dataclass
|
|
9
9
|
from typing import Any, Mapping, Optional, Union
|
10
10
|
|
11
11
|
import requests
|
12
|
+
|
12
13
|
from airbyte_cdk.sources.declarative.interpolation.interpolated_string import InterpolatedString
|
13
|
-
from airbyte_cdk.sources.declarative.requesters.error_handlers.backoff_strategies.header_helper import
|
14
|
-
|
15
|
-
|
14
|
+
from airbyte_cdk.sources.declarative.requesters.error_handlers.backoff_strategies.header_helper import (
|
15
|
+
get_numeric_value_from_header,
|
16
|
+
)
|
17
|
+
from airbyte_cdk.sources.declarative.requesters.error_handlers.backoff_strategy import (
|
18
|
+
BackoffStrategy,
|
19
|
+
)
|
20
|
+
from airbyte_cdk.sources.types import Config
|
16
21
|
|
17
22
|
|
18
23
|
@dataclass
|
@@ -33,30 +38,40 @@ class WaitUntilTimeFromHeaderBackoffStrategy(BackoffStrategy):
|
|
33
38
|
min_wait: Optional[Union[float, InterpolatedString, str]] = None
|
34
39
|
regex: Optional[Union[InterpolatedString, str]] = None
|
35
40
|
|
36
|
-
def __post_init__(self, parameters: Mapping[str, Any]):
|
41
|
+
def __post_init__(self, parameters: Mapping[str, Any]) -> None:
|
37
42
|
self.header = InterpolatedString.create(self.header, parameters=parameters)
|
38
|
-
self.regex =
|
43
|
+
self.regex = (
|
44
|
+
InterpolatedString.create(self.regex, parameters=parameters) if self.regex else None
|
45
|
+
)
|
39
46
|
if not isinstance(self.min_wait, InterpolatedString):
|
40
47
|
self.min_wait = InterpolatedString.create(str(self.min_wait), parameters=parameters)
|
41
48
|
|
42
|
-
def
|
49
|
+
def backoff_time(
|
50
|
+
self,
|
51
|
+
response_or_exception: Optional[Union[requests.Response, requests.RequestException]],
|
52
|
+
attempt_count: int,
|
53
|
+
) -> Optional[float]:
|
43
54
|
now = time.time()
|
44
|
-
header = self.header.eval(self.config)
|
55
|
+
header = self.header.eval(self.config) # type: ignore # header is always cast to an interpolated string
|
45
56
|
if self.regex:
|
46
|
-
evaled_regex = self.regex.eval(self.config)
|
57
|
+
evaled_regex = self.regex.eval(self.config) # type: ignore # header is always cast to an interpolated string
|
47
58
|
regex = re.compile(evaled_regex)
|
48
59
|
else:
|
49
60
|
regex = None
|
50
|
-
wait_until =
|
51
|
-
|
61
|
+
wait_until = None
|
62
|
+
if isinstance(response_or_exception, requests.Response):
|
63
|
+
wait_until = get_numeric_value_from_header(response_or_exception, header, regex)
|
64
|
+
min_wait = self.min_wait.eval(self.config) # type: ignore # header is always cast to an interpolated string
|
52
65
|
if wait_until is None or not wait_until:
|
53
|
-
return min_wait
|
54
|
-
if (isinstance(wait_until, str) and wait_until.isnumeric()) or isinstance(
|
66
|
+
return float(min_wait) if min_wait else None
|
67
|
+
if (isinstance(wait_until, str) and wait_until.isnumeric()) or isinstance(
|
68
|
+
wait_until, numbers.Number
|
69
|
+
):
|
55
70
|
wait_time = float(wait_until) - now
|
56
71
|
else:
|
57
|
-
return
|
72
|
+
return float(min_wait)
|
58
73
|
if min_wait:
|
59
|
-
return max(wait_time, min_wait)
|
74
|
+
return float(max(wait_time, min_wait))
|
60
75
|
elif wait_time < 0:
|
61
76
|
return None
|
62
77
|
return wait_time
|
@@ -2,24 +2,16 @@
|
|
2
2
|
# Copyright (c) 2023 Airbyte, Inc., all rights reserved.
|
3
3
|
#
|
4
4
|
|
5
|
-
from abc import
|
5
|
+
from abc import ABC
|
6
6
|
from dataclasses import dataclass
|
7
|
-
from typing import Optional
|
8
7
|
|
9
|
-
import
|
8
|
+
from airbyte_cdk.sources.streams.http.error_handlers import BackoffStrategy
|
10
9
|
|
11
10
|
|
12
11
|
@dataclass
|
13
|
-
class BackoffStrategy:
|
12
|
+
class DecalarativeBackoffStrategy(BackoffStrategy, ABC):
|
14
13
|
"""
|
14
|
+
This interface exists to retain backwards compatability with connectors that reference the declarative BackoffStrategy. As part of the effort to promote common interfaces to the Python CDK, this now extends the Python CDK backoff strategy interface.
|
15
|
+
|
15
16
|
Backoff strategy defining how long to wait before retrying a request that resulted in an error.
|
16
17
|
"""
|
17
|
-
|
18
|
-
@abstractmethod
|
19
|
-
def backoff(self, response: requests.Response, attempt_count: int) -> Optional[float]:
|
20
|
-
"""
|
21
|
-
Return time to wait before retrying the request.
|
22
|
-
:param response: response received for the request to retry
|
23
|
-
:param attempt_count: number of attempts to submit the request
|
24
|
-
:return: time to wait in seconds
|
25
|
-
"""
|