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