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,122 @@
|
|
|
1
|
+
# Copyright (c) 2024 Airbyte, Inc., all rights reserved.
|
|
2
|
+
|
|
3
|
+
from dataclasses import InitVar, dataclass
|
|
4
|
+
from typing import Any, Iterable, Mapping, Optional
|
|
5
|
+
|
|
6
|
+
from airbyte_cdk.sources.declarative.incremental import DeclarativeCursor
|
|
7
|
+
from airbyte_cdk.sources.declarative.types import Record, StreamSlice, StreamState
|
|
8
|
+
from airbyte_cdk.sources.streams.checkpoint.checkpoint_reader import FULL_REFRESH_COMPLETE_STATE
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass
|
|
12
|
+
class ResumableFullRefreshCursor(DeclarativeCursor):
|
|
13
|
+
parameters: InitVar[Mapping[str, Any]]
|
|
14
|
+
|
|
15
|
+
def __post_init__(self, parameters: Mapping[str, Any]) -> None:
|
|
16
|
+
self._cursor: StreamState = {}
|
|
17
|
+
|
|
18
|
+
def get_stream_state(self) -> StreamState:
|
|
19
|
+
return self._cursor
|
|
20
|
+
|
|
21
|
+
def set_initial_state(self, stream_state: StreamState) -> None:
|
|
22
|
+
self._cursor = stream_state
|
|
23
|
+
|
|
24
|
+
def observe(self, stream_slice: StreamSlice, record: Record) -> None:
|
|
25
|
+
"""
|
|
26
|
+
Resumable full refresh manages state using a page number so it does not need to update state by observing incoming records.
|
|
27
|
+
"""
|
|
28
|
+
pass
|
|
29
|
+
|
|
30
|
+
def close_slice(self, stream_slice: StreamSlice, *args: Any) -> None:
|
|
31
|
+
# The ResumableFullRefreshCursor doesn't support nested streams yet so receiving a partition is unexpected
|
|
32
|
+
if stream_slice.partition:
|
|
33
|
+
raise ValueError(
|
|
34
|
+
f"Stream slice {stream_slice} should not have a partition. Got {stream_slice.partition}."
|
|
35
|
+
)
|
|
36
|
+
self._cursor = stream_slice.cursor_slice
|
|
37
|
+
|
|
38
|
+
def should_be_synced(self, record: Record) -> bool:
|
|
39
|
+
"""
|
|
40
|
+
Unlike date-based cursors which filter out records outside slice boundaries, resumable full refresh records exist within pages
|
|
41
|
+
that don't have filterable bounds. We should always return them.
|
|
42
|
+
"""
|
|
43
|
+
return True
|
|
44
|
+
|
|
45
|
+
def is_greater_than_or_equal(self, first: Record, second: Record) -> bool:
|
|
46
|
+
"""
|
|
47
|
+
RFR record don't have ordering to be compared between one another.
|
|
48
|
+
"""
|
|
49
|
+
return False
|
|
50
|
+
|
|
51
|
+
def select_state(self, stream_slice: Optional[StreamSlice] = None) -> Optional[StreamState]:
|
|
52
|
+
# A top-level RFR cursor only manages the state of a single partition
|
|
53
|
+
return self._cursor
|
|
54
|
+
|
|
55
|
+
def stream_slices(self) -> Iterable[StreamSlice]:
|
|
56
|
+
"""
|
|
57
|
+
Resumable full refresh cursors only return a single slice and can't perform partitioning because iteration is done per-page
|
|
58
|
+
along an unbounded set.
|
|
59
|
+
"""
|
|
60
|
+
yield from [StreamSlice(cursor_slice=self._cursor, partition={})]
|
|
61
|
+
|
|
62
|
+
# This is an interesting pattern that might not seem obvious at first glance. This cursor itself has no functional need to
|
|
63
|
+
# inject any request values into the outbound response because the up-to-date pagination state is already loaded and
|
|
64
|
+
# maintained by the paginator component
|
|
65
|
+
def get_request_params(
|
|
66
|
+
self,
|
|
67
|
+
*,
|
|
68
|
+
stream_state: Optional[StreamState] = None,
|
|
69
|
+
stream_slice: Optional[StreamSlice] = None,
|
|
70
|
+
next_page_token: Optional[Mapping[str, Any]] = None,
|
|
71
|
+
) -> Mapping[str, Any]:
|
|
72
|
+
return {}
|
|
73
|
+
|
|
74
|
+
def get_request_headers(
|
|
75
|
+
self,
|
|
76
|
+
*,
|
|
77
|
+
stream_state: Optional[StreamState] = None,
|
|
78
|
+
stream_slice: Optional[StreamSlice] = None,
|
|
79
|
+
next_page_token: Optional[Mapping[str, Any]] = None,
|
|
80
|
+
) -> Mapping[str, Any]:
|
|
81
|
+
return {}
|
|
82
|
+
|
|
83
|
+
def get_request_body_data(
|
|
84
|
+
self,
|
|
85
|
+
*,
|
|
86
|
+
stream_state: Optional[StreamState] = None,
|
|
87
|
+
stream_slice: Optional[StreamSlice] = None,
|
|
88
|
+
next_page_token: Optional[Mapping[str, Any]] = None,
|
|
89
|
+
) -> Mapping[str, Any]:
|
|
90
|
+
return {}
|
|
91
|
+
|
|
92
|
+
def get_request_body_json(
|
|
93
|
+
self,
|
|
94
|
+
*,
|
|
95
|
+
stream_state: Optional[StreamState] = None,
|
|
96
|
+
stream_slice: Optional[StreamSlice] = None,
|
|
97
|
+
next_page_token: Optional[Mapping[str, Any]] = None,
|
|
98
|
+
) -> Mapping[str, Any]:
|
|
99
|
+
return {}
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
@dataclass
|
|
103
|
+
class ChildPartitionResumableFullRefreshCursor(ResumableFullRefreshCursor):
|
|
104
|
+
"""
|
|
105
|
+
The Sub-stream Resumable Cursor for Full-Refresh substreams.
|
|
106
|
+
Follows the parent type `ResumableFullRefreshCursor` with a small override,
|
|
107
|
+
to provide the ability to close the substream's slice once it has finished processing.
|
|
108
|
+
|
|
109
|
+
Check the `close_slice` method overide for more info about the actual behaviour of this cursor.
|
|
110
|
+
"""
|
|
111
|
+
|
|
112
|
+
def close_slice(self, stream_slice: StreamSlice, *args: Any) -> None:
|
|
113
|
+
"""
|
|
114
|
+
Once the current slice has finished syncing:
|
|
115
|
+
- paginator returns None
|
|
116
|
+
- no more slices to process
|
|
117
|
+
|
|
118
|
+
we assume that the records are processed and emitted already,
|
|
119
|
+
thus we have to set the cursor to ` __ab_full_refresh_sync_complete: true `,
|
|
120
|
+
otherwise there is a risk of Inf. Loop processing the same slice.
|
|
121
|
+
"""
|
|
122
|
+
self._cursor = FULL_REFRESH_COMPLETE_STATE
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright (c) 2025 Airbyte, Inc., all rights reserved.
|
|
3
|
+
#
|
|
4
|
+
|
|
5
|
+
from airbyte_cdk.sources.declarative.interpolation.interpolated_boolean import InterpolatedBoolean
|
|
6
|
+
from airbyte_cdk.sources.declarative.interpolation.interpolated_mapping import InterpolatedMapping
|
|
7
|
+
from airbyte_cdk.sources.declarative.interpolation.interpolated_string import InterpolatedString
|
|
8
|
+
|
|
9
|
+
__all__ = ["InterpolatedBoolean", "InterpolatedMapping", "InterpolatedString"]
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright (c) 2025 Airbyte, Inc., all rights reserved.
|
|
3
|
+
#
|
|
4
|
+
|
|
5
|
+
import base64
|
|
6
|
+
import hashlib
|
|
7
|
+
import json
|
|
8
|
+
import re
|
|
9
|
+
from typing import Any, Optional
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def hash(value: Any, hash_type: str = "md5", salt: Optional[str] = None) -> str:
|
|
13
|
+
"""
|
|
14
|
+
Implementation of a custom Jinja2 hash filter
|
|
15
|
+
Hash type defaults to 'md5' if one is not specified.
|
|
16
|
+
|
|
17
|
+
If you are using this has function for GDPR compliance, then
|
|
18
|
+
you should probably also pass in a salt as discussed in:
|
|
19
|
+
https://security.stackexchange.com/questions/202022/hashing-email-addresses-for-gdpr-compliance
|
|
20
|
+
|
|
21
|
+
This can be used in a low code connector definition under the AddFields transformation.
|
|
22
|
+
For example:
|
|
23
|
+
|
|
24
|
+
rates_stream:
|
|
25
|
+
$ref: "#/definitions/base_stream"
|
|
26
|
+
$parameters:
|
|
27
|
+
name: "rates"
|
|
28
|
+
primary_key: "date"
|
|
29
|
+
path: "/exchangerates_data/latest"
|
|
30
|
+
transformations:
|
|
31
|
+
- type: AddFields
|
|
32
|
+
fields:
|
|
33
|
+
- path: ["some_new_path"]
|
|
34
|
+
value: "{{ record['rates']['CAD'] | hash('md5', 'mysalt') }}"
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
:param value: value to be hashed
|
|
39
|
+
:param hash_type: valid hash type
|
|
40
|
+
:param salt: a salt that will be combined with the value to ensure that the hash created for a given value on this system
|
|
41
|
+
is different from the hash created for that value on other systems.
|
|
42
|
+
:return: computed hash as a hexadecimal string
|
|
43
|
+
"""
|
|
44
|
+
hash_func = getattr(hashlib, hash_type, None)
|
|
45
|
+
|
|
46
|
+
if hash_func:
|
|
47
|
+
hash_obj = hash_func()
|
|
48
|
+
hash_obj.update(str(value).encode("utf-8"))
|
|
49
|
+
if salt:
|
|
50
|
+
hash_obj.update(str(salt).encode("utf-8"))
|
|
51
|
+
computed_hash: str = hash_obj.hexdigest()
|
|
52
|
+
else:
|
|
53
|
+
raise AttributeError("No hashing function named {hname}".format(hname=hash_type))
|
|
54
|
+
|
|
55
|
+
return computed_hash
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def base64encode(value: str) -> str:
|
|
59
|
+
"""
|
|
60
|
+
Implementation of a custom Jinja2 base64encode filter
|
|
61
|
+
|
|
62
|
+
For example:
|
|
63
|
+
|
|
64
|
+
OAuthAuthenticator:
|
|
65
|
+
$ref: "#/definitions/OAuthAuthenticator"
|
|
66
|
+
$parameters:
|
|
67
|
+
name: "client_id"
|
|
68
|
+
value: "{{ config['client_id'] | base64encode }}"
|
|
69
|
+
|
|
70
|
+
:param value: value to be encoded in base64
|
|
71
|
+
:return: base64 encoded string
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
return base64.b64encode(value.encode("utf-8")).decode()
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def base64decode(value: str) -> str:
|
|
78
|
+
"""
|
|
79
|
+
Implementation of a custom Jinja2 base64decode filter
|
|
80
|
+
|
|
81
|
+
For example:
|
|
82
|
+
|
|
83
|
+
OAuthAuthenticator:
|
|
84
|
+
$ref: "#/definitions/OAuthAuthenticator"
|
|
85
|
+
$parameters:
|
|
86
|
+
name: "client_id"
|
|
87
|
+
value: "{{ config['client_id'] | base64decode }}"
|
|
88
|
+
|
|
89
|
+
:param value: value to be decoded from base64
|
|
90
|
+
:return: base64 decoded string
|
|
91
|
+
"""
|
|
92
|
+
|
|
93
|
+
return base64.b64decode(value.encode("utf-8")).decode()
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def base64binascii_decode(value: str) -> str:
|
|
97
|
+
"""
|
|
98
|
+
Implementation of a custom Jinja2 filter to decode base64 strings using ASCII encoding
|
|
99
|
+
|
|
100
|
+
For example:
|
|
101
|
+
|
|
102
|
+
OAuthAuthenticator:
|
|
103
|
+
$ref: "#/definitions/OAuthAuthenticator"
|
|
104
|
+
$parameters:
|
|
105
|
+
name: "client_id"
|
|
106
|
+
value: "{{ config['client_id'] | base64binascii_decode }}"
|
|
107
|
+
|
|
108
|
+
:param value: value to be decoded from base64 using ascii
|
|
109
|
+
:return: base64 decoded string ascii
|
|
110
|
+
"""
|
|
111
|
+
return base64.standard_b64encode(value.encode("ascii")).decode("ascii")
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def string(value: Any) -> str:
|
|
115
|
+
"""
|
|
116
|
+
Converts the input value to a string.
|
|
117
|
+
If the value is already a string, it is returned as is.
|
|
118
|
+
Otherwise, the value is interpreted as a json object and wrapped in triple-quotes so it's evalued as a string by the JinjaInterpolation
|
|
119
|
+
:param value: the value to convert to a string
|
|
120
|
+
:return: string representation of the input value
|
|
121
|
+
"""
|
|
122
|
+
if isinstance(value, str):
|
|
123
|
+
return value
|
|
124
|
+
ret = f'"""{json.dumps(value)}"""'
|
|
125
|
+
return ret
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def regex_search(value: str, regex: str) -> str:
|
|
129
|
+
"""
|
|
130
|
+
Match a regular expression against a string and return the first match group if it exists.
|
|
131
|
+
"""
|
|
132
|
+
match = re.search(regex, value)
|
|
133
|
+
if match and len(match.groups()) > 0:
|
|
134
|
+
return match.group(1)
|
|
135
|
+
return ""
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
_filters_list = [hash, base64encode, base64decode, base64binascii_decode, string, regex_search]
|
|
139
|
+
filters = {f.__name__: f for f in _filters_list}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright (c) 2025 Airbyte, Inc., all rights reserved.
|
|
3
|
+
#
|
|
4
|
+
|
|
5
|
+
from dataclasses import InitVar, dataclass
|
|
6
|
+
from typing import Any, Final, List, Mapping
|
|
7
|
+
|
|
8
|
+
from airbyte_cdk.sources.declarative.interpolation.jinja import JinjaInterpolation
|
|
9
|
+
from airbyte_cdk.sources.types import Config
|
|
10
|
+
|
|
11
|
+
FALSE_VALUES: Final[List[Any]] = [
|
|
12
|
+
"False",
|
|
13
|
+
"false",
|
|
14
|
+
"{}",
|
|
15
|
+
"[]",
|
|
16
|
+
"()",
|
|
17
|
+
"",
|
|
18
|
+
"0",
|
|
19
|
+
"0.0",
|
|
20
|
+
{},
|
|
21
|
+
False,
|
|
22
|
+
[],
|
|
23
|
+
(),
|
|
24
|
+
set(),
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass
|
|
29
|
+
class InterpolatedBoolean:
|
|
30
|
+
f"""
|
|
31
|
+
Wrapper around a string to be evaluated to a boolean value.
|
|
32
|
+
The string will be evaluated as False if it interpolates to a value in {FALSE_VALUES}
|
|
33
|
+
|
|
34
|
+
Attributes:
|
|
35
|
+
condition (str): The string representing the condition to evaluate to a boolean
|
|
36
|
+
"""
|
|
37
|
+
condition: str
|
|
38
|
+
parameters: InitVar[Mapping[str, Any]]
|
|
39
|
+
|
|
40
|
+
def __post_init__(self, parameters: Mapping[str, Any]) -> None:
|
|
41
|
+
self._default = "False"
|
|
42
|
+
self._interpolation = JinjaInterpolation()
|
|
43
|
+
self._parameters = parameters
|
|
44
|
+
|
|
45
|
+
def eval(self, config: Config, **additional_parameters: Any) -> bool:
|
|
46
|
+
"""
|
|
47
|
+
Interpolates the predicate condition string using the config and other optional arguments passed as parameter.
|
|
48
|
+
|
|
49
|
+
:param config: The user-provided configuration as specified by the source's spec
|
|
50
|
+
:param additional_parameters: Optional parameters used for interpolation
|
|
51
|
+
:return: The interpolated string
|
|
52
|
+
"""
|
|
53
|
+
if isinstance(self.condition, bool):
|
|
54
|
+
return self.condition
|
|
55
|
+
else:
|
|
56
|
+
evaluated = self._interpolation.eval(
|
|
57
|
+
self.condition,
|
|
58
|
+
config,
|
|
59
|
+
self._default,
|
|
60
|
+
parameters=self._parameters,
|
|
61
|
+
**additional_parameters,
|
|
62
|
+
)
|
|
63
|
+
if evaluated in FALSE_VALUES:
|
|
64
|
+
return False
|
|
65
|
+
# The presence of a value is generally regarded as truthy, so we treat it as such
|
|
66
|
+
return True
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright (c) 2025 Airbyte, Inc., all rights reserved.
|
|
3
|
+
#
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
from dataclasses import InitVar, dataclass
|
|
7
|
+
from typing import Any, Dict, Mapping, Optional
|
|
8
|
+
|
|
9
|
+
from airbyte_cdk.sources.declarative.interpolation.jinja import JinjaInterpolation
|
|
10
|
+
from airbyte_cdk.sources.types import Config
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass
|
|
14
|
+
class InterpolatedMapping:
|
|
15
|
+
"""
|
|
16
|
+
Wrapper around a Mapping[str, str] where both the keys and values are to be interpolated.
|
|
17
|
+
|
|
18
|
+
Attributes:
|
|
19
|
+
mapping (Mapping[str, str]): to be evaluated
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
mapping: Mapping[str, str]
|
|
23
|
+
parameters: InitVar[Mapping[str, Any]]
|
|
24
|
+
|
|
25
|
+
def __post_init__(self, parameters: Optional[Mapping[str, Any]]) -> None:
|
|
26
|
+
self._interpolation = JinjaInterpolation()
|
|
27
|
+
self._parameters = parameters
|
|
28
|
+
|
|
29
|
+
def eval(self, config: Config, **additional_parameters: Any) -> Dict[str, Any]:
|
|
30
|
+
"""
|
|
31
|
+
Wrapper around a Mapping[str, str] that allows for both keys and values to be interpolated.
|
|
32
|
+
|
|
33
|
+
:param config: The user-provided configuration as specified by the source's spec
|
|
34
|
+
:param additional_parameters: Optional parameters used for interpolation
|
|
35
|
+
:return: The interpolated mapping
|
|
36
|
+
"""
|
|
37
|
+
valid_key_types = additional_parameters.pop("valid_key_types", (str,))
|
|
38
|
+
valid_value_types = additional_parameters.pop("valid_value_types", None)
|
|
39
|
+
return {
|
|
40
|
+
self._interpolation.eval(
|
|
41
|
+
name,
|
|
42
|
+
config,
|
|
43
|
+
valid_types=valid_key_types,
|
|
44
|
+
parameters=self._parameters,
|
|
45
|
+
**additional_parameters,
|
|
46
|
+
): self._eval(value, config, valid_types=valid_value_types, **additional_parameters)
|
|
47
|
+
for name, value in self.mapping.items()
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
def _eval(self, value: str, config: Config, **kwargs: Any) -> Any:
|
|
51
|
+
# The values in self._mapping can be of Any type
|
|
52
|
+
# We only want to interpolate them if they are strings
|
|
53
|
+
if isinstance(value, str):
|
|
54
|
+
return self._interpolation.eval(value, config, parameters=self._parameters, **kwargs)
|
|
55
|
+
else:
|
|
56
|
+
return value
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright (c) 2025 Airbyte, Inc., all rights reserved.
|
|
3
|
+
#
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
from dataclasses import InitVar, dataclass
|
|
7
|
+
from typing import Any, Mapping, Optional, Union
|
|
8
|
+
|
|
9
|
+
from airbyte_cdk.sources.declarative.interpolation.jinja import JinjaInterpolation
|
|
10
|
+
from airbyte_cdk.sources.types import Config
|
|
11
|
+
|
|
12
|
+
NestedMappingEntry = Union[
|
|
13
|
+
dict[str, "NestedMapping"], list["NestedMapping"], str, int, float, bool, None
|
|
14
|
+
]
|
|
15
|
+
NestedMapping = Union[dict[str, NestedMappingEntry], str]
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass
|
|
19
|
+
class InterpolatedNestedMapping:
|
|
20
|
+
"""
|
|
21
|
+
Wrapper around a nested dict which can contain lists and primitive values where both the keys and values are interpolated recursively.
|
|
22
|
+
|
|
23
|
+
Attributes:
|
|
24
|
+
mapping (NestedMapping): to be evaluated
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
mapping: NestedMapping
|
|
28
|
+
parameters: InitVar[Mapping[str, Any]]
|
|
29
|
+
|
|
30
|
+
def __post_init__(self, parameters: Optional[Mapping[str, Any]]) -> None:
|
|
31
|
+
self._interpolation = JinjaInterpolation()
|
|
32
|
+
self._parameters = parameters
|
|
33
|
+
|
|
34
|
+
def eval(self, config: Config, **additional_parameters: Any) -> Any:
|
|
35
|
+
return self._eval(self.mapping, config, **additional_parameters)
|
|
36
|
+
|
|
37
|
+
def _eval(
|
|
38
|
+
self, value: Union[NestedMapping, NestedMappingEntry], config: Config, **kwargs: Any
|
|
39
|
+
) -> Any:
|
|
40
|
+
# Recursively interpolate dictionaries and lists
|
|
41
|
+
if isinstance(value, str):
|
|
42
|
+
return self._interpolation.eval(value, config, parameters=self._parameters, **kwargs)
|
|
43
|
+
elif isinstance(value, dict):
|
|
44
|
+
interpolated_dict = {
|
|
45
|
+
self._eval(k, config, **kwargs): self._eval(v, config, **kwargs)
|
|
46
|
+
for k, v in value.items()
|
|
47
|
+
}
|
|
48
|
+
return {k: v for k, v in interpolated_dict.items() if v is not None}
|
|
49
|
+
elif isinstance(value, list):
|
|
50
|
+
return [self._eval(v, config, **kwargs) for v in value]
|
|
51
|
+
else:
|
|
52
|
+
return value
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright (c) 2025 Airbyte, Inc., all rights reserved.
|
|
3
|
+
#
|
|
4
|
+
|
|
5
|
+
from dataclasses import InitVar, dataclass
|
|
6
|
+
from typing import Any, Mapping, Optional, Union
|
|
7
|
+
|
|
8
|
+
from airbyte_cdk.sources.declarative.interpolation.jinja import JinjaInterpolation
|
|
9
|
+
from airbyte_cdk.sources.types import Config
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass
|
|
13
|
+
class InterpolatedString:
|
|
14
|
+
"""
|
|
15
|
+
Wrapper around a raw string to be interpolated with the Jinja2 templating engine
|
|
16
|
+
|
|
17
|
+
Attributes:
|
|
18
|
+
string (str): The string to evalute
|
|
19
|
+
default (Optional[str]): The default value to return if the evaluation returns an empty string
|
|
20
|
+
parameters (Mapping[str, Any]): Additional runtime parameters to be used for string interpolation
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
string: str
|
|
24
|
+
parameters: InitVar[Mapping[str, Any]]
|
|
25
|
+
default: Optional[str] = None
|
|
26
|
+
|
|
27
|
+
def __post_init__(self, parameters: Mapping[str, Any]) -> None:
|
|
28
|
+
self.default = self.default or self.string
|
|
29
|
+
self._interpolation = JinjaInterpolation()
|
|
30
|
+
self._parameters = parameters
|
|
31
|
+
# indicates whether passed string is just a plain string, not Jinja template
|
|
32
|
+
# This allows for optimization, but we do not know it yet at this stage
|
|
33
|
+
self._is_plain_string = None
|
|
34
|
+
|
|
35
|
+
def eval(self, config: Config, **kwargs: Any) -> Any:
|
|
36
|
+
"""
|
|
37
|
+
Interpolates the input string using the config and other optional arguments passed as parameter.
|
|
38
|
+
|
|
39
|
+
:param config: The user-provided configuration as specified by the source's spec
|
|
40
|
+
:param kwargs: Optional parameters used for interpolation
|
|
41
|
+
:return: The interpolated string
|
|
42
|
+
"""
|
|
43
|
+
if self._is_plain_string:
|
|
44
|
+
return self.string
|
|
45
|
+
if self._is_plain_string is None:
|
|
46
|
+
# Let's check whether output from evaluation is the same as input.
|
|
47
|
+
# This indicates occurrence of a plain string, not a template and we can skip Jinja in subsequent runs.
|
|
48
|
+
evaluated = self._interpolation.eval(
|
|
49
|
+
self.string, config, self.default, parameters=self._parameters, **kwargs
|
|
50
|
+
)
|
|
51
|
+
self._is_plain_string = self.string == evaluated
|
|
52
|
+
return evaluated
|
|
53
|
+
return self._interpolation.eval(
|
|
54
|
+
self.string, config, self.default, parameters=self._parameters, **kwargs
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
def __eq__(self, other: Any) -> bool:
|
|
58
|
+
if not isinstance(other, InterpolatedString):
|
|
59
|
+
return False
|
|
60
|
+
return self.string == other.string and self.default == other.default
|
|
61
|
+
|
|
62
|
+
@classmethod
|
|
63
|
+
def create(
|
|
64
|
+
cls,
|
|
65
|
+
string_or_interpolated: Union["InterpolatedString", str],
|
|
66
|
+
*,
|
|
67
|
+
parameters: Mapping[str, Any],
|
|
68
|
+
) -> "InterpolatedString":
|
|
69
|
+
"""
|
|
70
|
+
Helper function to obtain an InterpolatedString from either a raw string or an InterpolatedString.
|
|
71
|
+
|
|
72
|
+
:param string_or_interpolated: either a raw string or an InterpolatedString.
|
|
73
|
+
:param parameters: parameters propagated from parent component
|
|
74
|
+
:return: InterpolatedString representing the input string.
|
|
75
|
+
"""
|
|
76
|
+
if isinstance(string_or_interpolated, str):
|
|
77
|
+
return InterpolatedString(string=string_or_interpolated, parameters=parameters)
|
|
78
|
+
else:
|
|
79
|
+
return string_or_interpolated
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright (c) 2025 Airbyte, Inc., all rights reserved.
|
|
3
|
+
#
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
from abc import ABC, abstractmethod
|
|
7
|
+
from typing import Any, Optional
|
|
8
|
+
|
|
9
|
+
from airbyte_cdk.sources.types import Config
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Interpolation(ABC):
|
|
13
|
+
"""
|
|
14
|
+
Strategy for evaluating the interpolated value of a string at runtime using Jinja.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
@abstractmethod
|
|
18
|
+
def eval(
|
|
19
|
+
self,
|
|
20
|
+
input_str: str,
|
|
21
|
+
config: Config,
|
|
22
|
+
default: Optional[str] = None,
|
|
23
|
+
**additional_options: Any,
|
|
24
|
+
) -> Any:
|
|
25
|
+
"""
|
|
26
|
+
Interpolates the input string using the config, and additional options passed as parameter.
|
|
27
|
+
|
|
28
|
+
:param input_str: The string to interpolate
|
|
29
|
+
:param config: The user-provided configuration as specified by the source's spec
|
|
30
|
+
:param default: Default value to return if the evaluation returns an empty string
|
|
31
|
+
:param additional_options: Optional parameters used for interpolation
|
|
32
|
+
:return: The interpolated string
|
|
33
|
+
"""
|
|
34
|
+
pass
|