airbyte-cdk 6.8.1rc9__py3-none-any.whl → 6.8.1rc10__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/cli/source_declarative_manifest/_run.py +11 -5
- airbyte_cdk/config_observation.py +1 -1
- airbyte_cdk/connector_builder/main.py +1 -1
- airbyte_cdk/connector_builder/message_grouper.py +10 -10
- airbyte_cdk/destinations/destination.py +1 -1
- airbyte_cdk/destinations/vector_db_based/embedder.py +2 -2
- airbyte_cdk/destinations/vector_db_based/writer.py +12 -4
- airbyte_cdk/entrypoint.py +7 -6
- airbyte_cdk/logger.py +2 -2
- airbyte_cdk/sources/abstract_source.py +1 -1
- airbyte_cdk/sources/config.py +1 -1
- airbyte_cdk/sources/connector_state_manager.py +9 -4
- airbyte_cdk/sources/declarative/auth/oauth.py +1 -1
- airbyte_cdk/sources/declarative/auth/selective_authenticator.py +6 -1
- airbyte_cdk/sources/declarative/concurrent_declarative_source.py +28 -42
- airbyte_cdk/sources/declarative/datetime/min_max_datetime.py +10 -4
- airbyte_cdk/sources/declarative/declarative_component_schema.yaml +116 -19
- airbyte_cdk/sources/declarative/decoders/noop_decoder.py +4 -1
- airbyte_cdk/sources/declarative/incremental/datetime_based_cursor.py +8 -6
- airbyte_cdk/sources/declarative/interpolation/jinja.py +35 -36
- airbyte_cdk/sources/declarative/interpolation/macros.py +1 -1
- airbyte_cdk/sources/declarative/manifest_declarative_source.py +53 -2
- airbyte_cdk/sources/declarative/models/declarative_component_schema.py +95 -2
- airbyte_cdk/sources/declarative/parsers/manifest_component_transformer.py +6 -0
- airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py +100 -27
- airbyte_cdk/sources/declarative/partition_routers/__init__.py +2 -1
- airbyte_cdk/sources/declarative/partition_routers/substream_partition_router.py +13 -7
- airbyte_cdk/sources/declarative/requesters/error_handlers/default_error_handler.py +1 -1
- airbyte_cdk/sources/declarative/requesters/error_handlers/http_response_filter.py +8 -6
- airbyte_cdk/sources/declarative/requesters/paginators/default_paginator.py +1 -1
- airbyte_cdk/sources/declarative/requesters/request_options/datetime_based_request_options_provider.py +2 -2
- airbyte_cdk/sources/declarative/requesters/request_options/interpolated_request_options_provider.py +1 -1
- airbyte_cdk/sources/declarative/resolvers/__init__.py +13 -0
- airbyte_cdk/sources/declarative/resolvers/components_resolver.py +55 -0
- airbyte_cdk/sources/declarative/resolvers/http_components_resolver.py +106 -0
- airbyte_cdk/sources/declarative/retrievers/async_retriever.py +5 -2
- airbyte_cdk/sources/declarative/spec/spec.py +1 -1
- airbyte_cdk/sources/embedded/base_integration.py +3 -2
- airbyte_cdk/sources/file_based/availability_strategy/abstract_file_based_availability_strategy.py +12 -4
- airbyte_cdk/sources/file_based/availability_strategy/default_file_based_availability_strategy.py +18 -7
- airbyte_cdk/sources/file_based/file_types/avro_parser.py +14 -11
- airbyte_cdk/sources/file_based/file_types/csv_parser.py +3 -3
- airbyte_cdk/sources/file_based/file_types/excel_parser.py +11 -5
- airbyte_cdk/sources/file_based/file_types/jsonl_parser.py +1 -1
- airbyte_cdk/sources/file_based/stream/abstract_file_based_stream.py +2 -2
- airbyte_cdk/sources/file_based/stream/concurrent/adapters.py +6 -3
- airbyte_cdk/sources/file_based/stream/cursor/default_file_based_cursor.py +1 -1
- airbyte_cdk/sources/http_logger.py +3 -3
- airbyte_cdk/sources/streams/concurrent/abstract_stream.py +5 -2
- airbyte_cdk/sources/streams/concurrent/adapters.py +6 -3
- airbyte_cdk/sources/streams/concurrent/availability_strategy.py +9 -3
- airbyte_cdk/sources/streams/concurrent/cursor.py +1 -1
- airbyte_cdk/sources/streams/concurrent/state_converters/datetime_stream_state_converter.py +2 -2
- airbyte_cdk/sources/streams/core.py +17 -14
- airbyte_cdk/sources/streams/http/http.py +19 -19
- airbyte_cdk/sources/streams/http/http_client.py +4 -48
- airbyte_cdk/sources/streams/http/requests_native_auth/abstract_token.py +2 -1
- airbyte_cdk/sources/streams/http/requests_native_auth/oauth.py +62 -33
- airbyte_cdk/sources/utils/record_helper.py +1 -1
- airbyte_cdk/sources/utils/schema_helpers.py +1 -1
- airbyte_cdk/sources/utils/transform.py +34 -15
- airbyte_cdk/test/entrypoint_wrapper.py +11 -6
- airbyte_cdk/test/mock_http/response_builder.py +1 -1
- airbyte_cdk/utils/airbyte_secrets_utils.py +1 -1
- airbyte_cdk/utils/event_timing.py +10 -10
- airbyte_cdk/utils/message_utils.py +4 -3
- airbyte_cdk/utils/spec_schema_transformations.py +3 -2
- airbyte_cdk/utils/traced_exception.py +14 -12
- airbyte_cdk-6.8.1rc10.dist-info/METADATA +111 -0
- {airbyte_cdk-6.8.1rc9.dist-info → airbyte_cdk-6.8.1rc10.dist-info}/RECORD +73 -70
- airbyte_cdk-6.8.1rc9.dist-info/METADATA +0 -307
- {airbyte_cdk-6.8.1rc9.dist-info → airbyte_cdk-6.8.1rc10.dist-info}/LICENSE.txt +0 -0
- {airbyte_cdk-6.8.1rc9.dist-info → airbyte_cdk-6.8.1rc10.dist-info}/WHEEL +0 -0
- {airbyte_cdk-6.8.1rc9.dist-info → airbyte_cdk-6.8.1rc10.dist-info}/entry_points.txt +0 -0
@@ -119,6 +119,9 @@ from airbyte_cdk.sources.declarative.models.declarative_component_schema import
|
|
119
119
|
from airbyte_cdk.sources.declarative.models.declarative_component_schema import (
|
120
120
|
CheckStream as CheckStreamModel,
|
121
121
|
)
|
122
|
+
from airbyte_cdk.sources.declarative.models.declarative_component_schema import (
|
123
|
+
ComponentMappingDefinition as ComponentMappingDefinitionModel,
|
124
|
+
)
|
122
125
|
from airbyte_cdk.sources.declarative.models.declarative_component_schema import (
|
123
126
|
CompositeErrorHandler as CompositeErrorHandlerModel,
|
124
127
|
)
|
@@ -191,6 +194,9 @@ from airbyte_cdk.sources.declarative.models.declarative_component_schema import
|
|
191
194
|
from airbyte_cdk.sources.declarative.models.declarative_component_schema import (
|
192
195
|
GzipJsonDecoder as GzipJsonDecoderModel,
|
193
196
|
)
|
197
|
+
from airbyte_cdk.sources.declarative.models.declarative_component_schema import (
|
198
|
+
HttpComponentsResolver as HttpComponentsResolverModel,
|
199
|
+
)
|
194
200
|
from airbyte_cdk.sources.declarative.models.declarative_component_schema import (
|
195
201
|
HttpRequester as HttpRequesterModel,
|
196
202
|
)
|
@@ -298,6 +304,7 @@ from airbyte_cdk.sources.declarative.models.declarative_component_schema import
|
|
298
304
|
from airbyte_cdk.sources.declarative.partition_routers import (
|
299
305
|
CartesianProductStreamSlicer,
|
300
306
|
ListPartitionRouter,
|
307
|
+
PartitionRouter,
|
301
308
|
SinglePartitionRouter,
|
302
309
|
SubstreamPartitionRouter,
|
303
310
|
)
|
@@ -338,6 +345,10 @@ from airbyte_cdk.sources.declarative.requesters.request_options import (
|
|
338
345
|
)
|
339
346
|
from airbyte_cdk.sources.declarative.requesters.request_path import RequestPath
|
340
347
|
from airbyte_cdk.sources.declarative.requesters.requester import HttpMethod
|
348
|
+
from airbyte_cdk.sources.declarative.resolvers import (
|
349
|
+
ComponentMappingDefinition,
|
350
|
+
HttpComponentsResolver,
|
351
|
+
)
|
341
352
|
from airbyte_cdk.sources.declarative.retrievers import (
|
342
353
|
AsyncRetriever,
|
343
354
|
SimpleRetriever,
|
@@ -396,7 +407,7 @@ class ModelToComponentFactory:
|
|
396
407
|
self._disable_retries = disable_retries
|
397
408
|
self._disable_cache = disable_cache
|
398
409
|
self._disable_resumable_full_refresh = disable_resumable_full_refresh
|
399
|
-
self._message_repository = message_repository or InMemoryMessageRepository(
|
410
|
+
self._message_repository = message_repository or InMemoryMessageRepository(
|
400
411
|
self._evaluate_log_level(emit_connector_builder_messages)
|
401
412
|
)
|
402
413
|
|
@@ -467,6 +478,8 @@ class ModelToComponentFactory:
|
|
467
478
|
WaitTimeFromHeaderModel: self.create_wait_time_from_header,
|
468
479
|
WaitUntilTimeFromHeaderModel: self.create_wait_until_time_from_header,
|
469
480
|
AsyncRetrieverModel: self.create_async_retriever,
|
481
|
+
HttpComponentsResolverModel: self.create_http_components_resolver,
|
482
|
+
ComponentMappingDefinitionModel: self.create_components_mapping_definition,
|
470
483
|
}
|
471
484
|
|
472
485
|
# Needed for the case where we need to perform a second parse on the fields of a custom component
|
@@ -644,7 +657,7 @@ class ModelToComponentFactory:
|
|
644
657
|
declarative_stream.incremental_sync, # type: ignore # was already checked. Migration can be applied only to incremental streams.
|
645
658
|
config,
|
646
659
|
declarative_stream.parameters, # type: ignore # different type is expected here Mapping[str, Any], got Dict[str, Any]
|
647
|
-
)
|
660
|
+
)
|
648
661
|
|
649
662
|
def create_session_token_authenticator(
|
650
663
|
self, model: SessionTokenAuthenticatorModel, config: Config, name: str, **kwargs: Any
|
@@ -674,7 +687,7 @@ class ModelToComponentFactory:
|
|
674
687
|
return ModelToComponentFactory.create_bearer_authenticator(
|
675
688
|
BearerAuthenticatorModel(type="BearerAuthenticator", api_token=""), # type: ignore # $parameters has a default value
|
676
689
|
config,
|
677
|
-
token_provider=token_provider,
|
690
|
+
token_provider=token_provider,
|
678
691
|
)
|
679
692
|
else:
|
680
693
|
return ModelToComponentFactory.create_api_key_authenticator(
|
@@ -821,7 +834,6 @@ class ModelToComponentFactory:
|
|
821
834
|
input_datetime_formats=datetime_based_cursor_model.cursor_datetime_formats,
|
822
835
|
is_sequential_state=True,
|
823
836
|
cursor_granularity=cursor_granularity,
|
824
|
-
# type: ignore # Having issues w/ inspection for GapType and CursorValueType as shown in existing tests. Confirmed functionality is working in practice
|
825
837
|
)
|
826
838
|
|
827
839
|
start_date_runtime_value: Union[InterpolatedString, str, MinMaxDatetime]
|
@@ -894,7 +906,7 @@ class ModelToComponentFactory:
|
|
894
906
|
stream_name=stream_name,
|
895
907
|
stream_namespace=stream_namespace,
|
896
908
|
stream_state=stream_state,
|
897
|
-
message_repository=self._message_repository,
|
909
|
+
message_repository=self._message_repository,
|
898
910
|
connector_state_manager=state_manager,
|
899
911
|
connector_state_converter=connector_state_converter,
|
900
912
|
cursor_field=cursor_field,
|
@@ -1282,19 +1294,20 @@ class ModelToComponentFactory:
|
|
1282
1294
|
parameters=model.parameters or {},
|
1283
1295
|
)
|
1284
1296
|
|
1285
|
-
def
|
1286
|
-
self,
|
1287
|
-
|
1288
|
-
|
1297
|
+
def _build_stream_slicer_from_partition_router(
|
1298
|
+
self,
|
1299
|
+
model: Union[AsyncRetrieverModel, CustomRetrieverModel, SimpleRetrieverModel],
|
1300
|
+
config: Config,
|
1301
|
+
) -> Optional[PartitionRouter]:
|
1289
1302
|
if (
|
1290
|
-
hasattr(model
|
1291
|
-
and isinstance(model
|
1292
|
-
and model.
|
1303
|
+
hasattr(model, "partition_router")
|
1304
|
+
and isinstance(model, SimpleRetrieverModel)
|
1305
|
+
and model.partition_router
|
1293
1306
|
):
|
1294
|
-
stream_slicer_model = model.
|
1307
|
+
stream_slicer_model = model.partition_router
|
1295
1308
|
|
1296
1309
|
if isinstance(stream_slicer_model, list):
|
1297
|
-
|
1310
|
+
return CartesianProductStreamSlicer(
|
1298
1311
|
[
|
1299
1312
|
self._create_component_from_model(model=slicer, config=config)
|
1300
1313
|
for slicer in stream_slicer_model
|
@@ -1302,9 +1315,24 @@ class ModelToComponentFactory:
|
|
1302
1315
|
parameters={},
|
1303
1316
|
)
|
1304
1317
|
else:
|
1305
|
-
|
1306
|
-
|
1307
|
-
|
1318
|
+
return self._create_component_from_model(model=stream_slicer_model, config=config) # type: ignore[no-any-return]
|
1319
|
+
# Will be created PartitionRouter as stream_slicer_model is model.partition_router
|
1320
|
+
return None
|
1321
|
+
|
1322
|
+
def _build_resumable_cursor_from_paginator(
|
1323
|
+
self,
|
1324
|
+
model: Union[AsyncRetrieverModel, CustomRetrieverModel, SimpleRetrieverModel],
|
1325
|
+
stream_slicer: Optional[StreamSlicer],
|
1326
|
+
) -> Optional[StreamSlicer]:
|
1327
|
+
if hasattr(model, "paginator") and model.paginator and not stream_slicer:
|
1328
|
+
# For the regular Full-Refresh streams, we use the high level `ResumableFullRefreshCursor`
|
1329
|
+
return ResumableFullRefreshCursor(parameters={})
|
1330
|
+
return None
|
1331
|
+
|
1332
|
+
def _merge_stream_slicers(
|
1333
|
+
self, model: DeclarativeStreamModel, config: Config
|
1334
|
+
) -> Optional[StreamSlicer]:
|
1335
|
+
stream_slicer = self._build_stream_slicer_from_partition_router(model.retriever, config)
|
1308
1336
|
|
1309
1337
|
if model.incremental_sync and stream_slicer:
|
1310
1338
|
incremental_sync_model = model.incremental_sync
|
@@ -1347,15 +1375,7 @@ class ModelToComponentFactory:
|
|
1347
1375
|
),
|
1348
1376
|
partition_router=stream_slicer,
|
1349
1377
|
)
|
1350
|
-
|
1351
|
-
hasattr(model.retriever, "paginator")
|
1352
|
-
and model.retriever.paginator
|
1353
|
-
and not stream_slicer
|
1354
|
-
):
|
1355
|
-
# For the regular Full-Refresh streams, we use the high level `ResumableFullRefreshCursor`
|
1356
|
-
return ResumableFullRefreshCursor(parameters={})
|
1357
|
-
else:
|
1358
|
-
return None
|
1378
|
+
return self._build_resumable_cursor_from_paginator(model.retriever, stream_slicer)
|
1359
1379
|
|
1360
1380
|
def create_default_error_handler(
|
1361
1381
|
self, model: DefaultErrorHandlerModel, config: Config, **kwargs: Any
|
@@ -1705,7 +1725,7 @@ class ModelToComponentFactory:
|
|
1705
1725
|
refresh_token=model.refresh_token,
|
1706
1726
|
scopes=model.scopes,
|
1707
1727
|
token_expiry_date=model.token_expiry_date,
|
1708
|
-
token_expiry_date_format=model.token_expiry_date_format,
|
1728
|
+
token_expiry_date_format=model.token_expiry_date_format,
|
1709
1729
|
token_expiry_is_time_of_expiration=bool(model.token_expiry_date_format),
|
1710
1730
|
token_refresh_endpoint=model.token_refresh_endpoint,
|
1711
1731
|
config=config,
|
@@ -2219,3 +2239,56 @@ class ModelToComponentFactory:
|
|
2219
2239
|
|
2220
2240
|
def _evaluate_log_level(self, emit_connector_builder_messages: bool) -> Level:
|
2221
2241
|
return Level.DEBUG if emit_connector_builder_messages else Level.INFO
|
2242
|
+
|
2243
|
+
@staticmethod
|
2244
|
+
def create_components_mapping_definition(
|
2245
|
+
model: ComponentMappingDefinitionModel, config: Config, **kwargs: Any
|
2246
|
+
) -> ComponentMappingDefinition:
|
2247
|
+
interpolated_value = InterpolatedString.create(
|
2248
|
+
model.value, parameters=model.parameters or {}
|
2249
|
+
)
|
2250
|
+
field_path = [
|
2251
|
+
InterpolatedString.create(path, parameters=model.parameters or {})
|
2252
|
+
for path in model.field_path
|
2253
|
+
]
|
2254
|
+
return ComponentMappingDefinition(
|
2255
|
+
field_path=field_path, # type: ignore[arg-type] # field_path can be str and InterpolatedString
|
2256
|
+
value=interpolated_value,
|
2257
|
+
value_type=ModelToComponentFactory._json_schema_type_name_to_type(model.value_type),
|
2258
|
+
parameters=model.parameters or {},
|
2259
|
+
)
|
2260
|
+
|
2261
|
+
def create_http_components_resolver(
|
2262
|
+
self, model: HttpComponentsResolverModel, config: Config
|
2263
|
+
) -> Any:
|
2264
|
+
stream_slicer = self._build_stream_slicer_from_partition_router(model.retriever, config)
|
2265
|
+
combined_slicers = self._build_resumable_cursor_from_paginator(
|
2266
|
+
model.retriever, stream_slicer
|
2267
|
+
)
|
2268
|
+
|
2269
|
+
retriever = self._create_component_from_model(
|
2270
|
+
model=model.retriever,
|
2271
|
+
config=config,
|
2272
|
+
name="",
|
2273
|
+
primary_key=None,
|
2274
|
+
stream_slicer=combined_slicers,
|
2275
|
+
transformations=[],
|
2276
|
+
)
|
2277
|
+
|
2278
|
+
components_mapping = [
|
2279
|
+
self._create_component_from_model(
|
2280
|
+
model=components_mapping_definition_model,
|
2281
|
+
value_type=ModelToComponentFactory._json_schema_type_name_to_type(
|
2282
|
+
components_mapping_definition_model.value_type
|
2283
|
+
),
|
2284
|
+
config=config,
|
2285
|
+
)
|
2286
|
+
for components_mapping_definition_model in model.components_mapping
|
2287
|
+
]
|
2288
|
+
|
2289
|
+
return HttpComponentsResolver(
|
2290
|
+
retriever=retriever,
|
2291
|
+
config=config,
|
2292
|
+
components_mapping=components_mapping,
|
2293
|
+
parameters=model.parameters or {},
|
2294
|
+
)
|
@@ -6,5 +6,6 @@ from airbyte_cdk.sources.declarative.partition_routers.cartesian_product_stream_
|
|
6
6
|
from airbyte_cdk.sources.declarative.partition_routers.list_partition_router import ListPartitionRouter
|
7
7
|
from airbyte_cdk.sources.declarative.partition_routers.single_partition_router import SinglePartitionRouter
|
8
8
|
from airbyte_cdk.sources.declarative.partition_routers.substream_partition_router import SubstreamPartitionRouter
|
9
|
+
from airbyte_cdk.sources.declarative.partition_routers.partition_router import PartitionRouter
|
9
10
|
|
10
|
-
__all__ = ["CartesianProductStreamSlicer", "ListPartitionRouter", "SinglePartitionRouter", "SubstreamPartitionRouter"]
|
11
|
+
__all__ = ["CartesianProductStreamSlicer", "ListPartitionRouter", "SinglePartitionRouter", "SubstreamPartitionRouter", "PartitionRouter"]
|
@@ -130,11 +130,11 @@ class SubstreamPartitionRouter(PartitionRouter):
|
|
130
130
|
if value:
|
131
131
|
params.update(
|
132
132
|
{
|
133
|
-
parent_config.request_option.field_name.eval(
|
133
|
+
parent_config.request_option.field_name.eval( # type: ignore [union-attr]
|
134
134
|
config=self.config
|
135
135
|
): value
|
136
136
|
}
|
137
|
-
)
|
137
|
+
)
|
138
138
|
return params
|
139
139
|
|
140
140
|
def stream_slices(self) -> Iterable[StreamSlice]:
|
@@ -162,9 +162,9 @@ class SubstreamPartitionRouter(PartitionRouter):
|
|
162
162
|
extra_fields = None
|
163
163
|
if parent_stream_config.extra_fields:
|
164
164
|
extra_fields = [
|
165
|
-
[field_path_part.eval(self.config) for field_path_part in field_path]
|
165
|
+
[field_path_part.eval(self.config) for field_path_part in field_path] # type: ignore [union-attr]
|
166
166
|
for field_path in parent_stream_config.extra_fields
|
167
|
-
]
|
167
|
+
]
|
168
168
|
|
169
169
|
# read_stateless() assumes the parent is not concurrent. This is currently okay since the concurrent CDK does
|
170
170
|
# not support either substreams or RFR, but something that needs to be considered once we do
|
@@ -192,7 +192,10 @@ class SubstreamPartitionRouter(PartitionRouter):
|
|
192
192
|
message=f"Parent stream returned records as invalid type {type(parent_record)}"
|
193
193
|
)
|
194
194
|
try:
|
195
|
-
partition_value = dpath.get(
|
195
|
+
partition_value = dpath.get(
|
196
|
+
parent_record, # type: ignore [arg-type]
|
197
|
+
parent_field,
|
198
|
+
)
|
196
199
|
except KeyError:
|
197
200
|
continue
|
198
201
|
|
@@ -228,7 +231,10 @@ class SubstreamPartitionRouter(PartitionRouter):
|
|
228
231
|
if extra_fields:
|
229
232
|
for extra_field_path in extra_fields:
|
230
233
|
try:
|
231
|
-
extra_field_value = dpath.get(
|
234
|
+
extra_field_value = dpath.get(
|
235
|
+
parent_record, # type: ignore [arg-type]
|
236
|
+
extra_field_path,
|
237
|
+
)
|
232
238
|
self.logger.debug(
|
233
239
|
f"Extracted extra_field_path: {extra_field_path} with value: {extra_field_value}"
|
234
240
|
)
|
@@ -291,7 +297,7 @@ class SubstreamPartitionRouter(PartitionRouter):
|
|
291
297
|
if not parent_state and incremental_dependency:
|
292
298
|
# Attempt to retrieve child state
|
293
299
|
substream_state = list(stream_state.values())
|
294
|
-
substream_state = substream_state[0] if substream_state else {}
|
300
|
+
substream_state = substream_state[0] if substream_state else {} # type: ignore [assignment] # Incorrect type for assignment
|
295
301
|
parent_state = {}
|
296
302
|
|
297
303
|
# Copy child state to parent streams with incremental dependencies
|
@@ -141,7 +141,7 @@ class DefaultErrorHandler(ErrorHandler):
|
|
141
141
|
for backoff_strategy in self.backoff_strategies:
|
142
142
|
backoff = backoff_strategy.backoff_time(
|
143
143
|
response_or_exception=response_or_exception, attempt_count=attempt_count
|
144
|
-
)
|
144
|
+
)
|
145
145
|
if backoff:
|
146
146
|
return backoff
|
147
147
|
return backoff
|
@@ -151,21 +151,23 @@ class HttpResponseFilter:
|
|
151
151
|
:param response: The HTTP response which can be used during interpolation
|
152
152
|
:return: The evaluated error message string to be emitted
|
153
153
|
"""
|
154
|
-
return self.error_message.eval(
|
154
|
+
return self.error_message.eval( # type: ignore [no-any-return, union-attr]
|
155
155
|
self.config, response=self._safe_response_json(response), headers=response.headers
|
156
|
-
)
|
156
|
+
)
|
157
157
|
|
158
158
|
def _response_matches_predicate(self, response: requests.Response) -> bool:
|
159
159
|
return (
|
160
160
|
bool(
|
161
|
-
self.predicate.condition
|
162
|
-
and self.predicate.eval(
|
163
|
-
None,
|
161
|
+
self.predicate.condition # type: ignore [union-attr]
|
162
|
+
and self.predicate.eval( # type: ignore [union-attr]
|
163
|
+
None, # type: ignore [arg-type]
|
164
|
+
response=self._safe_response_json(response),
|
165
|
+
headers=response.headers,
|
164
166
|
)
|
165
167
|
)
|
166
168
|
if self.predicate
|
167
169
|
else False
|
168
|
-
)
|
170
|
+
)
|
169
171
|
|
170
172
|
def _response_contains_error_message(self, response: requests.Response) -> bool:
|
171
173
|
if not self.error_message_contains:
|
@@ -194,7 +194,7 @@ class DefaultPaginator(Paginator):
|
|
194
194
|
and self.pagination_strategy.get_page_size()
|
195
195
|
and self.page_size_option.inject_into == option_type
|
196
196
|
):
|
197
|
-
options[self.page_size_option.field_name.eval(config=self.config)] = (
|
197
|
+
options[self.page_size_option.field_name.eval(config=self.config)] = ( # type: ignore [union-attr]
|
198
198
|
self.pagination_strategy.get_page_size()
|
199
199
|
) # type: ignore # field_name is always cast to an interpolated string
|
200
200
|
return options
|
@@ -85,7 +85,7 @@ class DatetimeBasedRequestOptionsProvider(RequestOptionsProvider):
|
|
85
85
|
self._partition_field_start.eval(self.config)
|
86
86
|
)
|
87
87
|
if self.end_time_option and self.end_time_option.inject_into == option_type:
|
88
|
-
options[self.end_time_option.field_name.eval(config=self.config)] = stream_slice.get(
|
88
|
+
options[self.end_time_option.field_name.eval(config=self.config)] = stream_slice.get( # type: ignore [union-attr]
|
89
89
|
self._partition_field_end.eval(self.config)
|
90
|
-
)
|
90
|
+
)
|
91
91
|
return options
|
airbyte_cdk/sources/declarative/requesters/request_options/interpolated_request_options_provider.py
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
from dataclasses import InitVar, dataclass, field
|
6
6
|
from typing import Any, Mapping, MutableMapping, Optional, Union
|
7
7
|
|
8
|
-
from
|
8
|
+
from typing_extensions import deprecated
|
9
9
|
|
10
10
|
from airbyte_cdk.sources.declarative.interpolation.interpolated_nested_mapping import NestedMapping
|
11
11
|
from airbyte_cdk.sources.declarative.requesters.request_options.interpolated_nested_request_input_provider import (
|
@@ -0,0 +1,13 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2024 Airbyte, Inc., all rights reserved.
|
3
|
+
#
|
4
|
+
|
5
|
+
from airbyte_cdk.sources.declarative.resolvers.components_resolver import ComponentsResolver, ComponentMappingDefinition, ResolvedComponentMappingDefinition
|
6
|
+
from airbyte_cdk.sources.declarative.resolvers.http_components_resolver import HttpComponentsResolver
|
7
|
+
from airbyte_cdk.sources.declarative.models import HttpComponentsResolver as HttpComponentsResolverModel
|
8
|
+
|
9
|
+
COMPONENTS_RESOLVER_TYPE_MAPPING = {
|
10
|
+
"HttpComponentsResolver": HttpComponentsResolverModel
|
11
|
+
}
|
12
|
+
|
13
|
+
__all__ = ["ComponentsResolver", "HttpComponentsResolver", "ComponentMappingDefinition", "ResolvedComponentMappingDefinition", "COMPONENTS_RESOLVER_TYPE_MAPPING"]
|
@@ -0,0 +1,55 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2024 Airbyte, Inc., all rights reserved.
|
3
|
+
#
|
4
|
+
|
5
|
+
from abc import ABC, abstractmethod
|
6
|
+
from dataclasses import InitVar, dataclass
|
7
|
+
from typing import Any, Dict, Iterable, List, Mapping, Optional, Type, Union
|
8
|
+
|
9
|
+
from typing_extensions import deprecated
|
10
|
+
|
11
|
+
from airbyte_cdk.sources.declarative.interpolation import InterpolatedString
|
12
|
+
from airbyte_cdk.sources.source import ExperimentalClassWarning
|
13
|
+
|
14
|
+
|
15
|
+
@dataclass(frozen=True)
|
16
|
+
class ComponentMappingDefinition:
|
17
|
+
"""Defines the configuration for mapping a component in a stream. This class specifies
|
18
|
+
what field in the stream template should be updated with value, supporting dynamic interpolation
|
19
|
+
and type enforcement."""
|
20
|
+
|
21
|
+
field_path: List["InterpolatedString"]
|
22
|
+
value: Union["InterpolatedString", str]
|
23
|
+
value_type: Optional[Type[Any]]
|
24
|
+
parameters: InitVar[Mapping[str, Any]]
|
25
|
+
|
26
|
+
|
27
|
+
@dataclass(frozen=True)
|
28
|
+
class ResolvedComponentMappingDefinition:
|
29
|
+
"""Defines resolved configuration for mapping a component in a stream. This class specifies
|
30
|
+
what field in the stream template should be updated with value, supporting dynamic interpolation
|
31
|
+
and type enforcement."""
|
32
|
+
|
33
|
+
field_path: List["InterpolatedString"]
|
34
|
+
value: "InterpolatedString"
|
35
|
+
value_type: Optional[Type[Any]]
|
36
|
+
parameters: InitVar[Mapping[str, Any]]
|
37
|
+
|
38
|
+
|
39
|
+
@deprecated("This class is experimental. Use at your own risk.", category=ExperimentalClassWarning)
|
40
|
+
@dataclass
|
41
|
+
class ComponentsResolver(ABC):
|
42
|
+
"""
|
43
|
+
Abstract base class for resolving components in a stream template.
|
44
|
+
"""
|
45
|
+
|
46
|
+
@abstractmethod
|
47
|
+
def resolve_components(
|
48
|
+
self, stream_template_config: Dict[str, Any]
|
49
|
+
) -> Iterable[Dict[str, Any]]:
|
50
|
+
"""
|
51
|
+
Maps and populates values into a stream template configuration.
|
52
|
+
:param stream_template_config: The stream template with placeholders for components.
|
53
|
+
:yields: The resolved stream config with populated values.
|
54
|
+
"""
|
55
|
+
pass
|
@@ -0,0 +1,106 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2024 Airbyte, Inc., all rights reserved.
|
3
|
+
#
|
4
|
+
|
5
|
+
from copy import deepcopy
|
6
|
+
from dataclasses import InitVar, dataclass, field
|
7
|
+
from typing import Any, Dict, Iterable, List, Mapping
|
8
|
+
|
9
|
+
import dpath
|
10
|
+
from typing_extensions import deprecated
|
11
|
+
|
12
|
+
from airbyte_cdk.sources.declarative.interpolation import InterpolatedString
|
13
|
+
from airbyte_cdk.sources.declarative.resolvers.components_resolver import (
|
14
|
+
ComponentMappingDefinition,
|
15
|
+
ComponentsResolver,
|
16
|
+
ResolvedComponentMappingDefinition,
|
17
|
+
)
|
18
|
+
from airbyte_cdk.sources.declarative.retrievers.retriever import Retriever
|
19
|
+
from airbyte_cdk.sources.source import ExperimentalClassWarning
|
20
|
+
from airbyte_cdk.sources.types import Config
|
21
|
+
|
22
|
+
|
23
|
+
@deprecated("This class is experimental. Use at your own risk.", category=ExperimentalClassWarning)
|
24
|
+
@dataclass
|
25
|
+
class HttpComponentsResolver(ComponentsResolver):
|
26
|
+
"""
|
27
|
+
Resolves and populates stream templates with components fetched via an HTTP retriever.
|
28
|
+
|
29
|
+
Attributes:
|
30
|
+
retriever (Retriever): The retriever used to fetch data from an API.
|
31
|
+
config (Config): Configuration object for the resolver.
|
32
|
+
components_mapping (List[ComponentMappingDefinition]): List of mappings to resolve.
|
33
|
+
parameters (InitVar[Mapping[str, Any]]): Additional parameters for interpolation.
|
34
|
+
"""
|
35
|
+
|
36
|
+
retriever: Retriever
|
37
|
+
config: Config
|
38
|
+
components_mapping: List[ComponentMappingDefinition]
|
39
|
+
parameters: InitVar[Mapping[str, Any]]
|
40
|
+
_resolved_components: List[ResolvedComponentMappingDefinition] = field(
|
41
|
+
init=False, repr=False, default_factory=list
|
42
|
+
)
|
43
|
+
|
44
|
+
def __post_init__(self, parameters: Mapping[str, Any]) -> None:
|
45
|
+
"""
|
46
|
+
Initializes and parses component mappings, converting them to resolved definitions.
|
47
|
+
|
48
|
+
Args:
|
49
|
+
parameters (Mapping[str, Any]): Parameters for interpolation.
|
50
|
+
"""
|
51
|
+
for component_mapping in self.components_mapping:
|
52
|
+
if isinstance(component_mapping.value, (str, InterpolatedString)):
|
53
|
+
interpolated_value = (
|
54
|
+
InterpolatedString.create(component_mapping.value, parameters=parameters)
|
55
|
+
if isinstance(component_mapping.value, str)
|
56
|
+
else component_mapping.value
|
57
|
+
)
|
58
|
+
|
59
|
+
field_path = [
|
60
|
+
InterpolatedString.create(path, parameters=parameters)
|
61
|
+
for path in component_mapping.field_path
|
62
|
+
]
|
63
|
+
|
64
|
+
self._resolved_components.append(
|
65
|
+
ResolvedComponentMappingDefinition(
|
66
|
+
field_path=field_path,
|
67
|
+
value=interpolated_value,
|
68
|
+
value_type=component_mapping.value_type,
|
69
|
+
parameters=parameters,
|
70
|
+
)
|
71
|
+
)
|
72
|
+
else:
|
73
|
+
raise ValueError(
|
74
|
+
f"Expected a string or InterpolatedString for value in mapping: {component_mapping}"
|
75
|
+
)
|
76
|
+
|
77
|
+
def resolve_components(
|
78
|
+
self, stream_template_config: Dict[str, Any]
|
79
|
+
) -> Iterable[Dict[str, Any]]:
|
80
|
+
"""
|
81
|
+
Resolves components in the stream template configuration by populating values.
|
82
|
+
|
83
|
+
Args:
|
84
|
+
stream_template_config (Dict[str, Any]): Stream template to populate.
|
85
|
+
|
86
|
+
Yields:
|
87
|
+
Dict[str, Any]: Updated configurations with resolved components.
|
88
|
+
"""
|
89
|
+
kwargs = {"stream_template_config": stream_template_config}
|
90
|
+
|
91
|
+
for components_values in self.retriever.read_records({}):
|
92
|
+
updated_config = deepcopy(stream_template_config)
|
93
|
+
kwargs["components_values"] = components_values # type: ignore[assignment] # component_values will always be of type Mapping[str, Any]
|
94
|
+
|
95
|
+
for resolved_component in self._resolved_components:
|
96
|
+
valid_types = (
|
97
|
+
(resolved_component.value_type,) if resolved_component.value_type else None
|
98
|
+
)
|
99
|
+
value = resolved_component.value.eval(
|
100
|
+
self.config, valid_types=valid_types, **kwargs
|
101
|
+
)
|
102
|
+
|
103
|
+
path = [path.eval(self.config, **kwargs) for path in resolved_component.field_path]
|
104
|
+
dpath.set(updated_config, path, value)
|
105
|
+
|
106
|
+
yield updated_config
|
@@ -4,7 +4,7 @@
|
|
4
4
|
from dataclasses import InitVar, dataclass, field
|
5
5
|
from typing import Any, Callable, Iterable, Mapping, Optional
|
6
6
|
|
7
|
-
from
|
7
|
+
from typing_extensions import deprecated
|
8
8
|
|
9
9
|
from airbyte_cdk.models import FailureType
|
10
10
|
from airbyte_cdk.sources.declarative.async_job.job_orchestrator import (
|
@@ -21,7 +21,10 @@ from airbyte_cdk.sources.types import Config, StreamSlice, StreamState
|
|
21
21
|
from airbyte_cdk.utils.traced_exception import AirbyteTracedException
|
22
22
|
|
23
23
|
|
24
|
-
@deprecated(
|
24
|
+
@deprecated(
|
25
|
+
"This class is experimental. Use at your own risk.",
|
26
|
+
category=ExperimentalClassWarning,
|
27
|
+
)
|
25
28
|
@dataclass
|
26
29
|
class AsyncRetriever(Retriever):
|
27
30
|
config: Config
|
@@ -52,8 +52,9 @@ class BaseEmbeddedIntegration(ABC, Generic[TConfig, TOutput]):
|
|
52
52
|
for message in self.source.read(self.config, configured_catalog, state):
|
53
53
|
if message.type == Type.RECORD:
|
54
54
|
output = self._handle_record(
|
55
|
-
message.record,
|
56
|
-
|
55
|
+
message.record,
|
56
|
+
get_defined_id(stream, message.record.data), # type: ignore[union-attr, arg-type]
|
57
|
+
)
|
57
58
|
if output:
|
58
59
|
yield output
|
59
60
|
elif message.type is Type.STATE and message.state:
|
airbyte_cdk/sources/file_based/availability_strategy/abstract_file_based_availability_strategy.py
CHANGED
@@ -2,6 +2,8 @@
|
|
2
2
|
# Copyright (c) 2023 Airbyte, Inc., all rights reserved.
|
3
3
|
#
|
4
4
|
|
5
|
+
from __future__ import annotations
|
6
|
+
|
5
7
|
import logging
|
6
8
|
from abc import abstractmethod
|
7
9
|
from typing import TYPE_CHECKING, Optional, Tuple
|
@@ -22,8 +24,11 @@ if TYPE_CHECKING:
|
|
22
24
|
|
23
25
|
class AbstractFileBasedAvailabilityStrategy(AvailabilityStrategy):
|
24
26
|
@abstractmethod
|
25
|
-
def check_availability(
|
26
|
-
self,
|
27
|
+
def check_availability( # type: ignore[override] # Signature doesn't match base class
|
28
|
+
self,
|
29
|
+
stream: Stream,
|
30
|
+
logger: logging.Logger,
|
31
|
+
_: Optional[Source],
|
27
32
|
) -> Tuple[bool, Optional[str]]:
|
28
33
|
"""
|
29
34
|
Perform a connection check for the stream.
|
@@ -34,7 +39,10 @@ class AbstractFileBasedAvailabilityStrategy(AvailabilityStrategy):
|
|
34
39
|
|
35
40
|
@abstractmethod
|
36
41
|
def check_availability_and_parsability(
|
37
|
-
self,
|
42
|
+
self,
|
43
|
+
stream: AbstractFileBasedStream,
|
44
|
+
logger: logging.Logger,
|
45
|
+
_: Optional[Source],
|
38
46
|
) -> Tuple[bool, Optional[str]]:
|
39
47
|
"""
|
40
48
|
Performs a connection check for the stream, as well as additional checks that
|
@@ -46,7 +54,7 @@ class AbstractFileBasedAvailabilityStrategy(AvailabilityStrategy):
|
|
46
54
|
|
47
55
|
|
48
56
|
class AbstractFileBasedAvailabilityStrategyWrapper(AbstractAvailabilityStrategy):
|
49
|
-
def __init__(self, stream:
|
57
|
+
def __init__(self, stream: AbstractFileBasedStream) -> None:
|
50
58
|
self.stream = stream
|
51
59
|
|
52
60
|
def check_availability(self, logger: logging.Logger) -> StreamAvailability:
|