airbyte-cdk 6.43.0.dev0__py3-none-any.whl → 6.44.0__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/connector_builder/connector_builder_handler.py +22 -8
- airbyte_cdk/connector_builder/main.py +3 -3
- airbyte_cdk/sources/declarative/checks/__init__.py +5 -2
- airbyte_cdk/sources/declarative/checks/check_stream.py +113 -11
- airbyte_cdk/sources/declarative/declarative_component_schema.yaml +23 -129
- airbyte_cdk/sources/declarative/manifest_declarative_source.py +6 -0
- airbyte_cdk/sources/declarative/models/declarative_component_schema.py +21 -87
- airbyte_cdk/sources/declarative/parsers/manifest_component_transformer.py +16 -4
- airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py +44 -192
- airbyte_cdk/sources/declarative/requesters/request_options/interpolated_request_options_provider.py +2 -25
- airbyte_cdk/sources/declarative/retrievers/simple_retriever.py +31 -100
- airbyte_cdk/sources/declarative/transformations/add_fields.py +3 -1
- {airbyte_cdk-6.43.0.dev0.dist-info → airbyte_cdk-6.44.0.dist-info}/METADATA +1 -1
- {airbyte_cdk-6.43.0.dev0.dist-info → airbyte_cdk-6.44.0.dist-info}/RECORD +18 -23
- airbyte_cdk/sources/declarative/requesters/query_properties/__init__.py +0 -14
- airbyte_cdk/sources/declarative/requesters/query_properties/group_by_key.py +0 -24
- airbyte_cdk/sources/declarative/requesters/query_properties/properties_from_endpoint.py +0 -40
- airbyte_cdk/sources/declarative/requesters/query_properties/property_chunking.py +0 -65
- airbyte_cdk/sources/declarative/requesters/query_properties/query_properties.py +0 -48
- {airbyte_cdk-6.43.0.dev0.dist-info → airbyte_cdk-6.44.0.dist-info}/LICENSE.txt +0 -0
- {airbyte_cdk-6.43.0.dev0.dist-info → airbyte_cdk-6.44.0.dist-info}/LICENSE_SHORT +0 -0
- {airbyte_cdk-6.43.0.dev0.dist-info → airbyte_cdk-6.44.0.dist-info}/WHEEL +0 -0
- {airbyte_cdk-6.43.0.dev0.dist-info → airbyte_cdk-6.44.0.dist-info}/entry_points.txt +0 -0
@@ -4,7 +4,7 @@
|
|
4
4
|
|
5
5
|
import copy
|
6
6
|
import typing
|
7
|
-
from typing import Any, Mapping
|
7
|
+
from typing import Any, Mapping, Optional
|
8
8
|
|
9
9
|
PARAMETERS_STR = "$parameters"
|
10
10
|
|
@@ -94,6 +94,7 @@ class ManifestComponentTransformer:
|
|
94
94
|
parent_field_identifier: str,
|
95
95
|
declarative_component: Mapping[str, Any],
|
96
96
|
parent_parameters: Mapping[str, Any],
|
97
|
+
use_parent_parameters: Optional[bool] = None,
|
97
98
|
) -> Mapping[str, Any]:
|
98
99
|
"""
|
99
100
|
Recursively transforms the specified declarative component and subcomponents to propagate parameters and insert the
|
@@ -103,6 +104,7 @@ class ManifestComponentTransformer:
|
|
103
104
|
:param declarative_component: The current component that is having type and parameters added
|
104
105
|
:param parent_field_identifier: The name of the field of the current component coming from the parent component
|
105
106
|
:param parent_parameters: The parameters set on parent components defined before the current component
|
107
|
+
:param use_parent_parameters: If set, parent parameters will be used as the source of truth when key names are the same
|
106
108
|
:return: A deep copy of the transformed component with types and parameters persisted to it
|
107
109
|
"""
|
108
110
|
propagated_component = dict(copy.deepcopy(declarative_component))
|
@@ -130,7 +132,11 @@ class ManifestComponentTransformer:
|
|
130
132
|
# level take precedence
|
131
133
|
current_parameters = dict(copy.deepcopy(parent_parameters))
|
132
134
|
component_parameters = propagated_component.pop(PARAMETERS_STR, {})
|
133
|
-
current_parameters =
|
135
|
+
current_parameters = (
|
136
|
+
{**component_parameters, **current_parameters}
|
137
|
+
if use_parent_parameters
|
138
|
+
else {**current_parameters, **component_parameters}
|
139
|
+
)
|
134
140
|
|
135
141
|
# Parameters should be applied to the current component fields with the existing field taking precedence over parameters if
|
136
142
|
# both exist
|
@@ -145,7 +151,10 @@ class ManifestComponentTransformer:
|
|
145
151
|
excluded_parameter = current_parameters.pop(field_name, None)
|
146
152
|
parent_type_field_identifier = f"{propagated_component.get('type')}.{field_name}"
|
147
153
|
propagated_component[field_name] = self.propagate_types_and_parameters(
|
148
|
-
parent_type_field_identifier,
|
154
|
+
parent_type_field_identifier,
|
155
|
+
field_value,
|
156
|
+
current_parameters,
|
157
|
+
use_parent_parameters=use_parent_parameters,
|
149
158
|
)
|
150
159
|
if excluded_parameter:
|
151
160
|
current_parameters[field_name] = excluded_parameter
|
@@ -158,7 +167,10 @@ class ManifestComponentTransformer:
|
|
158
167
|
f"{propagated_component.get('type')}.{field_name}"
|
159
168
|
)
|
160
169
|
field_value[i] = self.propagate_types_and_parameters(
|
161
|
-
parent_type_field_identifier,
|
170
|
+
parent_type_field_identifier,
|
171
|
+
element,
|
172
|
+
current_parameters,
|
173
|
+
use_parent_parameters=use_parent_parameters,
|
162
174
|
)
|
163
175
|
if excluded_parameter:
|
164
176
|
current_parameters[field_name] = excluded_parameter
|
@@ -1,5 +1,5 @@
|
|
1
1
|
#
|
2
|
-
# Copyright (c)
|
2
|
+
# Copyright (c) 2023 Airbyte, Inc., all rights reserved.
|
3
3
|
#
|
4
4
|
|
5
5
|
from __future__ import annotations
|
@@ -54,7 +54,11 @@ from airbyte_cdk.sources.declarative.auth.token_provider import (
|
|
54
54
|
SessionTokenProvider,
|
55
55
|
TokenProvider,
|
56
56
|
)
|
57
|
-
from airbyte_cdk.sources.declarative.checks import
|
57
|
+
from airbyte_cdk.sources.declarative.checks import (
|
58
|
+
CheckDynamicStream,
|
59
|
+
CheckStream,
|
60
|
+
DynamicStreamCheckConfig,
|
61
|
+
)
|
58
62
|
from airbyte_cdk.sources.declarative.concurrency_level import ConcurrencyLevel
|
59
63
|
from airbyte_cdk.sources.declarative.datetime.min_max_datetime import MinMaxDatetime
|
60
64
|
from airbyte_cdk.sources.declarative.declarative_stream import DeclarativeStream
|
@@ -218,6 +222,9 @@ from airbyte_cdk.sources.declarative.models.declarative_component_schema import
|
|
218
222
|
from airbyte_cdk.sources.declarative.models.declarative_component_schema import (
|
219
223
|
DynamicSchemaLoader as DynamicSchemaLoaderModel,
|
220
224
|
)
|
225
|
+
from airbyte_cdk.sources.declarative.models.declarative_component_schema import (
|
226
|
+
DynamicStreamCheckConfig as DynamicStreamCheckConfigModel,
|
227
|
+
)
|
221
228
|
from airbyte_cdk.sources.declarative.models.declarative_component_schema import (
|
222
229
|
ExponentialBackoffStrategy as ExponentialBackoffStrategyModel,
|
223
230
|
)
|
@@ -227,9 +234,6 @@ from airbyte_cdk.sources.declarative.models.declarative_component_schema import
|
|
227
234
|
from airbyte_cdk.sources.declarative.models.declarative_component_schema import (
|
228
235
|
FlattenFields as FlattenFieldsModel,
|
229
236
|
)
|
230
|
-
from airbyte_cdk.sources.declarative.models.declarative_component_schema import (
|
231
|
-
GroupByKeyMergeStrategy as GroupByKeyMergeStrategyModel,
|
232
|
-
)
|
233
237
|
from airbyte_cdk.sources.declarative.models.declarative_component_schema import (
|
234
238
|
GroupingPartitionRouter as GroupingPartitionRouterModel,
|
235
239
|
)
|
@@ -320,18 +324,6 @@ from airbyte_cdk.sources.declarative.models.declarative_component_schema import
|
|
320
324
|
from airbyte_cdk.sources.declarative.models.declarative_component_schema import (
|
321
325
|
ParentStreamConfig as ParentStreamConfigModel,
|
322
326
|
)
|
323
|
-
from airbyte_cdk.sources.declarative.models.declarative_component_schema import (
|
324
|
-
PropertiesFromEndpoint as PropertiesFromEndpointModel,
|
325
|
-
)
|
326
|
-
from airbyte_cdk.sources.declarative.models.declarative_component_schema import (
|
327
|
-
PropertyChunking as PropertyChunkingModel,
|
328
|
-
)
|
329
|
-
from airbyte_cdk.sources.declarative.models.declarative_component_schema import (
|
330
|
-
PropertyLimitType as PropertyLimitTypeModel,
|
331
|
-
)
|
332
|
-
from airbyte_cdk.sources.declarative.models.declarative_component_schema import (
|
333
|
-
QueryProperties as QueryPropertiesModel,
|
334
|
-
)
|
335
327
|
from airbyte_cdk.sources.declarative.models.declarative_component_schema import (
|
336
328
|
Rate as RateModel,
|
337
329
|
)
|
@@ -440,15 +432,6 @@ from airbyte_cdk.sources.declarative.requesters.paginators.strategies import (
|
|
440
432
|
PageIncrement,
|
441
433
|
StopConditionPaginationStrategyDecorator,
|
442
434
|
)
|
443
|
-
from airbyte_cdk.sources.declarative.requesters.query_properties import (
|
444
|
-
GroupByKey,
|
445
|
-
PropertiesFromEndpoint,
|
446
|
-
PropertyChunking,
|
447
|
-
QueryProperties,
|
448
|
-
)
|
449
|
-
from airbyte_cdk.sources.declarative.requesters.query_properties.property_chunking import (
|
450
|
-
PropertyLimitType,
|
451
|
-
)
|
452
435
|
from airbyte_cdk.sources.declarative.requesters.request_option import RequestOptionType
|
453
436
|
from airbyte_cdk.sources.declarative.requesters.request_options import (
|
454
437
|
DatetimeBasedRequestOptionsProvider,
|
@@ -583,6 +566,7 @@ class ModelToComponentFactory:
|
|
583
566
|
BasicHttpAuthenticatorModel: self.create_basic_http_authenticator,
|
584
567
|
BearerAuthenticatorModel: self.create_bearer_authenticator,
|
585
568
|
CheckStreamModel: self.create_check_stream,
|
569
|
+
DynamicStreamCheckConfigModel: self.create_dynamic_stream_check_config,
|
586
570
|
CheckDynamicStreamModel: self.create_check_dynamic_stream,
|
587
571
|
CompositeErrorHandlerModel: self.create_composite_error_handler,
|
588
572
|
ConcurrencyLevelModel: self.create_concurrency_level,
|
@@ -612,7 +596,6 @@ class ModelToComponentFactory:
|
|
612
596
|
ResponseToFileExtractorModel: self.create_response_to_file_extractor,
|
613
597
|
ExponentialBackoffStrategyModel: self.create_exponential_backoff_strategy,
|
614
598
|
SessionTokenAuthenticatorModel: self.create_session_token_authenticator,
|
615
|
-
GroupByKeyMergeStrategyModel: self.create_group_by_key,
|
616
599
|
HttpRequesterModel: self.create_http_requester,
|
617
600
|
HttpResponseFilterModel: self.create_http_response_filter,
|
618
601
|
InlineSchemaLoaderModel: self.create_inline_schema_loader,
|
@@ -642,9 +625,6 @@ class ModelToComponentFactory:
|
|
642
625
|
OffsetIncrementModel: self.create_offset_increment,
|
643
626
|
PageIncrementModel: self.create_page_increment,
|
644
627
|
ParentStreamConfigModel: self.create_parent_stream_config,
|
645
|
-
PropertiesFromEndpointModel: self.create_properties_from_endpoint,
|
646
|
-
PropertyChunkingModel: self.create_property_chunking,
|
647
|
-
QueryPropertiesModel: self.create_query_properties,
|
648
628
|
RecordFilterModel: self.create_record_filter,
|
649
629
|
RecordSelectorModel: self.create_record_selector,
|
650
630
|
RemoveFieldsModel: self.create_remove_fields,
|
@@ -964,8 +944,36 @@ class ModelToComponentFactory:
|
|
964
944
|
)
|
965
945
|
|
966
946
|
@staticmethod
|
967
|
-
def
|
968
|
-
|
947
|
+
def create_dynamic_stream_check_config(
|
948
|
+
model: DynamicStreamCheckConfigModel, config: Config, **kwargs: Any
|
949
|
+
) -> DynamicStreamCheckConfig:
|
950
|
+
return DynamicStreamCheckConfig(
|
951
|
+
dynamic_stream_name=model.dynamic_stream_name,
|
952
|
+
stream_count=model.stream_count or 0,
|
953
|
+
)
|
954
|
+
|
955
|
+
def create_check_stream(
|
956
|
+
self, model: CheckStreamModel, config: Config, **kwargs: Any
|
957
|
+
) -> CheckStream:
|
958
|
+
if model.dynamic_streams_check_configs is None and model.stream_names is None:
|
959
|
+
raise ValueError(
|
960
|
+
"Expected either stream_names or dynamic_streams_check_configs to be set for CheckStream"
|
961
|
+
)
|
962
|
+
|
963
|
+
dynamic_streams_check_configs = (
|
964
|
+
[
|
965
|
+
self._create_component_from_model(model=dynamic_stream_check_config, config=config)
|
966
|
+
for dynamic_stream_check_config in model.dynamic_streams_check_configs
|
967
|
+
]
|
968
|
+
if model.dynamic_streams_check_configs
|
969
|
+
else []
|
970
|
+
)
|
971
|
+
|
972
|
+
return CheckStream(
|
973
|
+
stream_names=model.stream_names or [],
|
974
|
+
dynamic_streams_check_configs=dynamic_streams_check_configs,
|
975
|
+
parameters={},
|
976
|
+
)
|
969
977
|
|
970
978
|
@staticmethod
|
971
979
|
def create_check_dynamic_stream(
|
@@ -2075,8 +2083,8 @@ class ModelToComponentFactory:
|
|
2075
2083
|
parameters=model.parameters or {},
|
2076
2084
|
)
|
2077
2085
|
|
2078
|
-
@staticmethod
|
2079
2086
|
def create_response_to_file_extractor(
|
2087
|
+
self,
|
2080
2088
|
model: ResponseToFileExtractorModel,
|
2081
2089
|
**kwargs: Any,
|
2082
2090
|
) -> ResponseToFileExtractor:
|
@@ -2090,16 +2098,11 @@ class ModelToComponentFactory:
|
|
2090
2098
|
factor=model.factor or 5, parameters=model.parameters or {}, config=config
|
2091
2099
|
)
|
2092
2100
|
|
2093
|
-
@staticmethod
|
2094
|
-
def create_group_by_key(model: GroupByKeyMergeStrategyModel, config: Config) -> GroupByKey:
|
2095
|
-
return GroupByKey(model.key, config=config, parameters=model.parameters or {})
|
2096
|
-
|
2097
2101
|
def create_http_requester(
|
2098
2102
|
self,
|
2099
2103
|
model: HttpRequesterModel,
|
2100
2104
|
config: Config,
|
2101
2105
|
decoder: Decoder = JsonDecoder(parameters={}),
|
2102
|
-
query_properties_key: Optional[str] = None,
|
2103
2106
|
*,
|
2104
2107
|
name: str,
|
2105
2108
|
) -> HttpRequester:
|
@@ -2132,7 +2135,6 @@ class ModelToComponentFactory:
|
|
2132
2135
|
request_body_json=model.request_body_json,
|
2133
2136
|
request_headers=model.request_headers,
|
2134
2137
|
request_parameters=model.request_parameters,
|
2135
|
-
query_properties_key=query_properties_key,
|
2136
2138
|
config=config,
|
2137
2139
|
parameters=model.parameters or {},
|
2138
2140
|
)
|
@@ -2600,79 +2602,6 @@ class ModelToComponentFactory:
|
|
2600
2602
|
lazy_read_pointer=model_lazy_read_pointer,
|
2601
2603
|
)
|
2602
2604
|
|
2603
|
-
def create_properties_from_endpoint(
|
2604
|
-
self, model: PropertiesFromEndpointModel, config: Config, **kwargs: Any
|
2605
|
-
) -> PropertiesFromEndpoint:
|
2606
|
-
name = "property_retriever"
|
2607
|
-
retriever = self._create_component_from_model(
|
2608
|
-
model=model.retriever,
|
2609
|
-
config=config,
|
2610
|
-
name=name,
|
2611
|
-
primary_key=None,
|
2612
|
-
stream_slicer=None,
|
2613
|
-
transformations=[],
|
2614
|
-
)
|
2615
|
-
return PropertiesFromEndpoint(
|
2616
|
-
property_field_path=model.property_field_path,
|
2617
|
-
retriever=retriever,
|
2618
|
-
config=config,
|
2619
|
-
parameters=model.parameters or {},
|
2620
|
-
)
|
2621
|
-
|
2622
|
-
def create_property_chunking(
|
2623
|
-
self, model: PropertyChunkingModel, config: Config, **kwargs: Any
|
2624
|
-
) -> PropertyChunking:
|
2625
|
-
record_merge_strategy = (
|
2626
|
-
self._create_component_from_model(
|
2627
|
-
model=model.record_merge_strategy, config=config, **kwargs
|
2628
|
-
)
|
2629
|
-
if model.record_merge_strategy
|
2630
|
-
else None
|
2631
|
-
)
|
2632
|
-
|
2633
|
-
property_limit_type: PropertyLimitType
|
2634
|
-
match model.property_limit_type:
|
2635
|
-
case PropertyLimitTypeModel.property_count:
|
2636
|
-
property_limit_type = PropertyLimitType.property_count
|
2637
|
-
case PropertyLimitTypeModel.characters:
|
2638
|
-
property_limit_type = PropertyLimitType.characters
|
2639
|
-
case _:
|
2640
|
-
raise ValueError(f"Invalid PropertyLimitType {property_limit_type}")
|
2641
|
-
|
2642
|
-
return PropertyChunking(
|
2643
|
-
property_limit_type=property_limit_type,
|
2644
|
-
property_limit=model.property_limit,
|
2645
|
-
record_merge_strategy=record_merge_strategy,
|
2646
|
-
config=config,
|
2647
|
-
parameters=model.parameters or {},
|
2648
|
-
)
|
2649
|
-
|
2650
|
-
def create_query_properties(
|
2651
|
-
self, model: QueryPropertiesModel, config: Config, **kwargs: Any
|
2652
|
-
) -> QueryProperties:
|
2653
|
-
if isinstance(model.property_list, list):
|
2654
|
-
property_list = model.property_list
|
2655
|
-
else:
|
2656
|
-
property_list = self._create_component_from_model(
|
2657
|
-
model=model.property_list, config=config, **kwargs
|
2658
|
-
)
|
2659
|
-
|
2660
|
-
property_chunking = (
|
2661
|
-
self._create_component_from_model(
|
2662
|
-
model=model.property_chunking, config=config, **kwargs
|
2663
|
-
)
|
2664
|
-
if model.property_chunking
|
2665
|
-
else None
|
2666
|
-
)
|
2667
|
-
|
2668
|
-
return QueryProperties(
|
2669
|
-
property_list=property_list,
|
2670
|
-
always_include_properties=model.always_include_properties,
|
2671
|
-
property_chunking=property_chunking,
|
2672
|
-
config=config,
|
2673
|
-
parameters=model.parameters or {},
|
2674
|
-
)
|
2675
|
-
|
2676
2605
|
@staticmethod
|
2677
2606
|
def create_record_filter(
|
2678
2607
|
model: RecordFilterModel, config: Config, **kwargs: Any
|
@@ -2825,6 +2754,9 @@ class ModelToComponentFactory:
|
|
2825
2754
|
if model.decoder
|
2826
2755
|
else JsonDecoder(parameters={})
|
2827
2756
|
)
|
2757
|
+
requester = self._create_component_from_model(
|
2758
|
+
model=model.requester, decoder=decoder, config=config, name=name
|
2759
|
+
)
|
2828
2760
|
record_selector = self._create_component_from_model(
|
2829
2761
|
model=model.record_selector,
|
2830
2762
|
name=name,
|
@@ -2833,53 +2765,6 @@ class ModelToComponentFactory:
|
|
2833
2765
|
transformations=transformations,
|
2834
2766
|
client_side_incremental_sync=client_side_incremental_sync,
|
2835
2767
|
)
|
2836
|
-
|
2837
|
-
query_properties: Optional[QueryProperties] = None
|
2838
|
-
query_properties_key: Optional[str] = None
|
2839
|
-
if (
|
2840
|
-
hasattr(model.requester, "request_parameters")
|
2841
|
-
and model.requester.request_parameters
|
2842
|
-
and isinstance(model.requester.request_parameters, Mapping)
|
2843
|
-
):
|
2844
|
-
query_properties_definitions = []
|
2845
|
-
for key, request_parameter in model.requester.request_parameters.items():
|
2846
|
-
if (
|
2847
|
-
isinstance(request_parameter, Mapping)
|
2848
|
-
and request_parameter.get("type") == "QueryProperties"
|
2849
|
-
):
|
2850
|
-
query_properties_key = key
|
2851
|
-
query_properties_definitions.append(request_parameter)
|
2852
|
-
elif not isinstance(request_parameter, str):
|
2853
|
-
raise ValueError(
|
2854
|
-
f"Each element of request_parameters should be of type str or QueryProperties, but received {request_parameter.get('type')}"
|
2855
|
-
)
|
2856
|
-
|
2857
|
-
if len(query_properties_definitions) > 1:
|
2858
|
-
raise ValueError(
|
2859
|
-
f"request_parameters should only define one QueryProperties field, but found {len(query_properties_definitions)}"
|
2860
|
-
)
|
2861
|
-
|
2862
|
-
if len(query_properties_definitions) == 1:
|
2863
|
-
query_properties = self.create_component(
|
2864
|
-
model_type=QueryPropertiesModel,
|
2865
|
-
component_definition=query_properties_definitions[0],
|
2866
|
-
config=config,
|
2867
|
-
)
|
2868
|
-
|
2869
|
-
# Removes QueryProperties components from the interpolated mappings because it will be resolved in
|
2870
|
-
# the provider from the slice directly instead of through jinja interpolation
|
2871
|
-
if isinstance(model.requester.request_parameters, Mapping):
|
2872
|
-
model.requester.request_parameters = self._remove_query_properties(
|
2873
|
-
model.requester.request_parameters
|
2874
|
-
)
|
2875
|
-
|
2876
|
-
requester = self._create_component_from_model(
|
2877
|
-
model=model.requester,
|
2878
|
-
decoder=decoder,
|
2879
|
-
query_properties_key=query_properties_key,
|
2880
|
-
config=config,
|
2881
|
-
name=name,
|
2882
|
-
)
|
2883
2768
|
url_base = (
|
2884
2769
|
model.requester.url_base
|
2885
2770
|
if hasattr(model.requester, "url_base")
|
@@ -2985,42 +2870,9 @@ class ModelToComponentFactory:
|
|
2985
2870
|
cursor=cursor,
|
2986
2871
|
config=config,
|
2987
2872
|
ignore_stream_slicer_parameters_on_paginated_requests=ignore_stream_slicer_parameters_on_paginated_requests,
|
2988
|
-
additional_query_properties=query_properties,
|
2989
2873
|
parameters=model.parameters or {},
|
2990
2874
|
)
|
2991
2875
|
|
2992
|
-
@staticmethod
|
2993
|
-
def _remove_query_properties(
|
2994
|
-
request_parameters: Mapping[str, Union[Any, str]],
|
2995
|
-
) -> Mapping[str, Union[Any, str]]:
|
2996
|
-
return {
|
2997
|
-
parameter_field: request_parameter
|
2998
|
-
for parameter_field, request_parameter in request_parameters.items()
|
2999
|
-
if not isinstance(request_parameter, Mapping)
|
3000
|
-
or not request_parameter.get("type") == "QueryProperties"
|
3001
|
-
}
|
3002
|
-
|
3003
|
-
@staticmethod
|
3004
|
-
def _translate_query_properties_to_interpolated_strings(
|
3005
|
-
request_parameters: Mapping[str, Union[Any, str]],
|
3006
|
-
) -> Mapping[str, Union[Any, str]]:
|
3007
|
-
# todo blai: remove this since unused
|
3008
|
-
new_request_parameters = dict()
|
3009
|
-
for key, request_parameter in request_parameters.items():
|
3010
|
-
if (
|
3011
|
-
isinstance(request_parameter, Mapping)
|
3012
|
-
and request_parameter.get("type") == "QueryProperties"
|
3013
|
-
):
|
3014
|
-
# This may seem like this could be combined into the above conditional, but this is separated
|
3015
|
-
# so that we do not add the properties into the new request_parameters mapping
|
3016
|
-
if request_parameter.get("inject_into"):
|
3017
|
-
new_request_parameters[key] = (
|
3018
|
-
"{{ stream_partition.extra_fields['query_properties'] }}"
|
3019
|
-
)
|
3020
|
-
else:
|
3021
|
-
new_request_parameters[key] = request_parameter
|
3022
|
-
return new_request_parameters
|
3023
|
-
|
3024
2876
|
def create_state_delegating_stream(
|
3025
2877
|
self,
|
3026
2878
|
model: StateDelegatingStreamModel,
|
airbyte_cdk/sources/declarative/requesters/request_options/interpolated_request_options_provider.py
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
#
|
2
|
-
# Copyright (c)
|
2
|
+
# Copyright (c) 2023 Airbyte, Inc., all rights reserved.
|
3
3
|
#
|
4
4
|
|
5
5
|
from dataclasses import InitVar, dataclass, field
|
6
|
-
from typing import Any,
|
6
|
+
from typing import Any, Mapping, MutableMapping, Optional, Union
|
7
7
|
|
8
8
|
from airbyte_cdk.sources.declarative.interpolation.interpolated_nested_mapping import NestedMapping
|
9
9
|
from airbyte_cdk.sources.declarative.requesters.request_options.interpolated_nested_request_input_provider import (
|
@@ -40,7 +40,6 @@ class InterpolatedRequestOptionsProvider(RequestOptionsProvider):
|
|
40
40
|
request_headers: Optional[RequestInput] = None
|
41
41
|
request_body_data: Optional[RequestInput] = None
|
42
42
|
request_body_json: Optional[NestedMapping] = None
|
43
|
-
query_properties_key: Optional[str] = None
|
44
43
|
|
45
44
|
def __post_init__(self, parameters: Mapping[str, Any]) -> None:
|
46
45
|
if self.request_parameters is None:
|
@@ -84,28 +83,6 @@ class InterpolatedRequestOptionsProvider(RequestOptionsProvider):
|
|
84
83
|
valid_value_types=ValidRequestTypes,
|
85
84
|
)
|
86
85
|
if isinstance(interpolated_value, dict):
|
87
|
-
if self.query_properties_key:
|
88
|
-
if not stream_slice:
|
89
|
-
raise ValueError(
|
90
|
-
"stream_slice should not be None if query properties in requests is enabled. Please contact Airbyte Support"
|
91
|
-
)
|
92
|
-
elif (
|
93
|
-
"query_properties" not in stream_slice.extra_fields
|
94
|
-
or stream_slice.extra_fields.get("query_properties") is None
|
95
|
-
):
|
96
|
-
raise ValueError(
|
97
|
-
"QueryProperties component is defined but stream_partition does not contain query_properties. Please contact Airbyte Support"
|
98
|
-
)
|
99
|
-
elif not isinstance(stream_slice.extra_fields.get("query_properties"), List):
|
100
|
-
raise ValueError(
|
101
|
-
"QueryProperties component is defined but stream_slice.extra_fields.query_properties is not a List. Please contact Airbyte Support"
|
102
|
-
)
|
103
|
-
interpolated_value = {
|
104
|
-
**interpolated_value,
|
105
|
-
self.query_properties_key: ",".join(
|
106
|
-
stream_slice.extra_fields.get("query_properties") # type: ignore # Earlier type checks validate query_properties type
|
107
|
-
),
|
108
|
-
}
|
109
86
|
return interpolated_value
|
110
87
|
return {}
|
111
88
|
|
@@ -1,9 +1,8 @@
|
|
1
1
|
#
|
2
|
-
# Copyright (c)
|
2
|
+
# Copyright (c) 2023 Airbyte, Inc., all rights reserved.
|
3
3
|
#
|
4
4
|
|
5
5
|
import json
|
6
|
-
from collections import defaultdict
|
7
6
|
from dataclasses import InitVar, dataclass, field
|
8
7
|
from functools import partial
|
9
8
|
from itertools import islice
|
@@ -13,7 +12,6 @@ from typing import (
|
|
13
12
|
Iterable,
|
14
13
|
List,
|
15
14
|
Mapping,
|
16
|
-
MutableMapping,
|
17
15
|
Optional,
|
18
16
|
Set,
|
19
17
|
Tuple,
|
@@ -33,7 +31,6 @@ from airbyte_cdk.sources.declarative.partition_routers.single_partition_router i
|
|
33
31
|
)
|
34
32
|
from airbyte_cdk.sources.declarative.requesters.paginators.no_pagination import NoPagination
|
35
33
|
from airbyte_cdk.sources.declarative.requesters.paginators.paginator import Paginator
|
36
|
-
from airbyte_cdk.sources.declarative.requesters.query_properties import QueryProperties
|
37
34
|
from airbyte_cdk.sources.declarative.requesters.request_options import (
|
38
35
|
DefaultRequestOptionsProvider,
|
39
36
|
RequestOptionsProvider,
|
@@ -91,7 +88,6 @@ class SimpleRetriever(Retriever):
|
|
91
88
|
)
|
92
89
|
cursor: Optional[DeclarativeCursor] = None
|
93
90
|
ignore_stream_slicer_parameters_on_paginated_requests: bool = False
|
94
|
-
additional_query_properties: Optional[QueryProperties] = None
|
95
91
|
|
96
92
|
def __post_init__(self, parameters: Mapping[str, Any]) -> None:
|
97
93
|
self._paginator = self.paginator or NoPagination(parameters=parameters)
|
@@ -449,108 +445,43 @@ class SimpleRetriever(Retriever):
|
|
449
445
|
:param stream_slice: The stream slice to read data for
|
450
446
|
:return: The records read from the API source
|
451
447
|
"""
|
452
|
-
|
453
|
-
if self.additional_query_properties:
|
454
|
-
property_chunks = list(
|
455
|
-
self.additional_query_properties.get_request_property_chunks(
|
456
|
-
stream_slice=stream_slice
|
457
|
-
)
|
458
|
-
)
|
459
|
-
has_multiple_chunks = self.additional_query_properties.has_multiple_chunks(
|
460
|
-
stream_slice=stream_slice
|
461
|
-
)
|
462
|
-
else:
|
463
|
-
property_chunks = [[""]]
|
464
|
-
has_multiple_chunks = False
|
465
|
-
merged_records: MutableMapping[str, Any] = defaultdict(dict)
|
466
448
|
_slice = stream_slice or StreamSlice(partition={}, cursor_slice={}) # None-check
|
449
|
+
|
467
450
|
most_recent_record_from_slice = None
|
451
|
+
record_generator = partial(
|
452
|
+
self._parse_records,
|
453
|
+
stream_slice=stream_slice,
|
454
|
+
stream_state=self.state or {},
|
455
|
+
records_schema=records_schema,
|
456
|
+
)
|
468
457
|
|
469
|
-
if self.
|
470
|
-
|
471
|
-
_slice = StreamSlice(
|
472
|
-
partition=_slice.partition or {},
|
473
|
-
cursor_slice=_slice.cursor_slice or {},
|
474
|
-
extra_fields={"query_properties": properties},
|
475
|
-
) # None-check
|
476
|
-
|
477
|
-
record_generator = partial(
|
478
|
-
self._parse_records,
|
479
|
-
stream_slice=_slice,
|
480
|
-
stream_state=self.state or {},
|
481
|
-
records_schema=records_schema,
|
482
|
-
)
|
458
|
+
if self.cursor and isinstance(self.cursor, ResumableFullRefreshCursor):
|
459
|
+
stream_state = self.state
|
483
460
|
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
# Latest record read, not necessarily within slice boundaries.
|
490
|
-
# TODO Remove once all custom components implement `observe` method.
|
491
|
-
# https://github.com/airbytehq/airbyte-internal-issues/issues/6955
|
492
|
-
most_recent_record_from_slice = self._get_most_recent_record(
|
493
|
-
most_recent_record_from_slice, current_record, _slice
|
494
|
-
)
|
495
|
-
|
496
|
-
# Record merging should only be done if there are multiple property chunks. Otherwise,
|
497
|
-
# yielding immediately is more efficient so records can be emitted immediately
|
498
|
-
if (
|
499
|
-
has_multiple_chunks
|
500
|
-
and self.additional_query_properties.property_chunking
|
501
|
-
and current_record
|
502
|
-
):
|
503
|
-
merge_key = (
|
504
|
-
self.additional_query_properties.property_chunking.get_merge_key(
|
505
|
-
current_record
|
506
|
-
)
|
507
|
-
)
|
508
|
-
merged_records[merge_key].update(current_record)
|
509
|
-
else:
|
510
|
-
yield stream_data
|
511
|
-
if self.cursor:
|
512
|
-
self.cursor.close_slice(_slice, most_recent_record_from_slice)
|
461
|
+
# Before syncing the RFR stream, we check if the job's prior attempt was successful and don't need to
|
462
|
+
# fetch more records. The platform deletes stream state for full refresh streams before starting a
|
463
|
+
# new job, so we don't need to worry about this value existing for the initial attempt
|
464
|
+
if stream_state.get(FULL_REFRESH_SYNC_COMPLETE_KEY):
|
465
|
+
return
|
513
466
|
|
514
|
-
|
515
|
-
yield from merged_records.values()
|
467
|
+
yield from self._read_single_page(record_generator, stream_state, _slice)
|
516
468
|
else:
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
# Before syncing the RFR stream, we check if the job's prior attempt was successful and don't need to
|
531
|
-
# fetch more records. The platform deletes stream state for full refresh streams before starting a
|
532
|
-
# new job, so we don't need to worry about this value existing for the initial attempt
|
533
|
-
if stream_state.get(FULL_REFRESH_SYNC_COMPLETE_KEY):
|
534
|
-
return
|
469
|
+
for stream_data in self._read_pages(record_generator, self.state, _slice):
|
470
|
+
current_record = self._extract_record(stream_data, _slice)
|
471
|
+
if self.cursor and current_record:
|
472
|
+
self.cursor.observe(_slice, current_record)
|
473
|
+
|
474
|
+
# Latest record read, not necessarily within slice boundaries.
|
475
|
+
# TODO Remove once all custom components implement `observe` method.
|
476
|
+
# https://github.com/airbytehq/airbyte-internal-issues/issues/6955
|
477
|
+
most_recent_record_from_slice = self._get_most_recent_record(
|
478
|
+
most_recent_record_from_slice, current_record, _slice
|
479
|
+
)
|
480
|
+
yield stream_data
|
535
481
|
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
current_record = self._extract_record(stream_data, _slice)
|
540
|
-
if self.cursor and current_record:
|
541
|
-
self.cursor.observe(_slice, current_record)
|
542
|
-
|
543
|
-
# Latest record read, not necessarily within slice boundaries.
|
544
|
-
# TODO Remove once all custom components implement `observe` method.
|
545
|
-
# https://github.com/airbytehq/airbyte-internal-issues/issues/6955
|
546
|
-
most_recent_record_from_slice = self._get_most_recent_record(
|
547
|
-
most_recent_record_from_slice, current_record, _slice
|
548
|
-
)
|
549
|
-
yield stream_data
|
550
|
-
|
551
|
-
if self.cursor:
|
552
|
-
self.cursor.close_slice(_slice, most_recent_record_from_slice)
|
553
|
-
return
|
482
|
+
if self.cursor:
|
483
|
+
self.cursor.close_slice(_slice, most_recent_record_from_slice)
|
484
|
+
return
|
554
485
|
|
555
486
|
def _get_most_recent_record(
|
556
487
|
self,
|
@@ -139,7 +139,9 @@ class AddFields(RecordTransformation):
|
|
139
139
|
valid_types = (parsed_field.value_type,) if parsed_field.value_type else None
|
140
140
|
value = parsed_field.value.eval(config, valid_types=valid_types, **kwargs)
|
141
141
|
is_empty_condition = not self.condition
|
142
|
-
if is_empty_condition or self._filter_interpolator.eval(
|
142
|
+
if is_empty_condition or self._filter_interpolator.eval(
|
143
|
+
config, value=value, path=parsed_field.path, **kwargs
|
144
|
+
):
|
143
145
|
dpath.new(record, parsed_field.path, value)
|
144
146
|
|
145
147
|
def __eq__(self, other: Any) -> bool:
|