airbyte-cdk 6.33.0.dev0__py3-none-any.whl → 6.33.1.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/sources/declarative/auth/token.py +3 -8
- airbyte_cdk/sources/declarative/concurrent_declarative_source.py +13 -2
- airbyte_cdk/sources/declarative/declarative_component_schema.yaml +18 -8
- airbyte_cdk/sources/declarative/incremental/datetime_based_cursor.py +7 -6
- airbyte_cdk/sources/declarative/models/declarative_component_schema.py +14 -8
- airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py +40 -36
- airbyte_cdk/sources/declarative/partition_routers/list_partition_router.py +4 -2
- airbyte_cdk/sources/declarative/partition_routers/substream_partition_router.py +26 -18
- airbyte_cdk/sources/declarative/requesters/http_requester.py +5 -1
- airbyte_cdk/sources/declarative/requesters/paginators/default_paginator.py +6 -5
- airbyte_cdk/sources/declarative/requesters/request_option.py +83 -4
- airbyte_cdk/sources/declarative/requesters/request_options/datetime_based_request_options_provider.py +7 -6
- airbyte_cdk/sources/declarative/retrievers/simple_retriever.py +4 -1
- airbyte_cdk/sources/streams/call_rate.py +63 -0
- airbyte_cdk/utils/mapping_helpers.py +86 -27
- {airbyte_cdk-6.33.0.dev0.dist-info → airbyte_cdk-6.33.1.dev0.dist-info}/METADATA +1 -1
- {airbyte_cdk-6.33.0.dev0.dist-info → airbyte_cdk-6.33.1.dev0.dist-info}/RECORD +21 -21
- {airbyte_cdk-6.33.0.dev0.dist-info → airbyte_cdk-6.33.1.dev0.dist-info}/LICENSE.txt +0 -0
- {airbyte_cdk-6.33.0.dev0.dist-info → airbyte_cdk-6.33.1.dev0.dist-info}/LICENSE_SHORT +0 -0
- {airbyte_cdk-6.33.0.dev0.dist-info → airbyte_cdk-6.33.1.dev0.dist-info}/WHEEL +0 -0
- {airbyte_cdk-6.33.0.dev0.dist-info → airbyte_cdk-6.33.1.dev0.dist-info}/entry_points.txt +0 -0
@@ -5,7 +5,7 @@
|
|
5
5
|
import base64
|
6
6
|
import logging
|
7
7
|
from dataclasses import InitVar, dataclass
|
8
|
-
from typing import Any, Mapping, Union
|
8
|
+
from typing import Any, Mapping, MutableMapping, Union
|
9
9
|
|
10
10
|
import requests
|
11
11
|
from cachetools import TTLCache, cached
|
@@ -45,11 +45,6 @@ class ApiKeyAuthenticator(DeclarativeAuthenticator):
|
|
45
45
|
config: Config
|
46
46
|
parameters: InitVar[Mapping[str, Any]]
|
47
47
|
|
48
|
-
def __post_init__(self, parameters: Mapping[str, Any]) -> None:
|
49
|
-
self._field_name = InterpolatedString.create(
|
50
|
-
self.request_option.field_name, parameters=parameters
|
51
|
-
)
|
52
|
-
|
53
48
|
@property
|
54
49
|
def auth_header(self) -> str:
|
55
50
|
options = self._get_request_options(RequestOptionType.header)
|
@@ -60,9 +55,9 @@ class ApiKeyAuthenticator(DeclarativeAuthenticator):
|
|
60
55
|
return self.token_provider.get_token()
|
61
56
|
|
62
57
|
def _get_request_options(self, option_type: RequestOptionType) -> Mapping[str, Any]:
|
63
|
-
options = {}
|
58
|
+
options: MutableMapping[str, Any] = {}
|
64
59
|
if self.request_option.inject_into == option_type:
|
65
|
-
|
60
|
+
self.request_option.inject_into_request(options, self.token, self.config)
|
66
61
|
return options
|
67
62
|
|
68
63
|
def get_request_params(self) -> Mapping[str, Any]:
|
@@ -475,10 +475,21 @@ class ConcurrentDeclarativeSource(ManifestDeclarativeSource, Generic[TState]):
|
|
475
475
|
# Also a temporary hack. In the legacy Stream implementation, as part of the read,
|
476
476
|
# set_initial_state() is called to instantiate incoming state on the cursor. Although we no
|
477
477
|
# longer rely on the legacy low-code cursor for concurrent checkpointing, low-code components
|
478
|
-
# like StopConditionPaginationStrategyDecorator
|
479
|
-
#
|
478
|
+
# like StopConditionPaginationStrategyDecorator still rely on a DatetimeBasedCursor that is
|
479
|
+
# properly initialized with state.
|
480
480
|
if retriever.cursor:
|
481
481
|
retriever.cursor.set_initial_state(stream_state=stream_state)
|
482
|
+
|
483
|
+
# Similar to above, the ClientSideIncrementalRecordFilterDecorator cursor is a separate instance
|
484
|
+
# from the one initialized on the SimpleRetriever, so it also must also have state initialized
|
485
|
+
# for semi-incremental streams using is_client_side_incremental to filter properly
|
486
|
+
if isinstance(retriever.record_selector, RecordSelector) and isinstance(
|
487
|
+
retriever.record_selector.record_filter, ClientSideIncrementalRecordFilterDecorator
|
488
|
+
):
|
489
|
+
retriever.record_selector.record_filter._cursor.set_initial_state(
|
490
|
+
stream_state=stream_state
|
491
|
+
) # type: ignore # After non-concurrent cursors are deprecated we can remove these cursor workarounds
|
492
|
+
|
482
493
|
# We zero it out here, but since this is a cursor reference, the state is still properly
|
483
494
|
# instantiated for the other components that reference it
|
484
495
|
retriever.cursor = None
|
@@ -1478,7 +1478,7 @@ definitions:
|
|
1478
1478
|
description: List of matchers that define which requests this policy applies to.
|
1479
1479
|
type: array
|
1480
1480
|
items:
|
1481
|
-
"$ref": "#/definitions/
|
1481
|
+
"$ref": "#/definitions/HttpRequestRegexMatcher"
|
1482
1482
|
additionalProperties: true
|
1483
1483
|
MovingWindowCallRatePolicy:
|
1484
1484
|
title: Moving Window Call Rate Policy
|
@@ -1503,7 +1503,7 @@ definitions:
|
|
1503
1503
|
description: List of matchers that define which requests this policy applies to.
|
1504
1504
|
type: array
|
1505
1505
|
items:
|
1506
|
-
"$ref": "#/definitions/
|
1506
|
+
"$ref": "#/definitions/HttpRequestRegexMatcher"
|
1507
1507
|
additionalProperties: true
|
1508
1508
|
UnlimitedCallRatePolicy:
|
1509
1509
|
title: Unlimited Call Rate Policy
|
@@ -1521,7 +1521,7 @@ definitions:
|
|
1521
1521
|
description: List of matchers that define which requests this policy applies to.
|
1522
1522
|
type: array
|
1523
1523
|
items:
|
1524
|
-
"$ref": "#/definitions/
|
1524
|
+
"$ref": "#/definitions/HttpRequestRegexMatcher"
|
1525
1525
|
additionalProperties: true
|
1526
1526
|
Rate:
|
1527
1527
|
title: Rate
|
@@ -1541,7 +1541,7 @@ definitions:
|
|
1541
1541
|
type: string
|
1542
1542
|
format: duration
|
1543
1543
|
additionalProperties: true
|
1544
|
-
|
1544
|
+
HttpRequestRegexMatcher:
|
1545
1545
|
title: HTTP Request Matcher
|
1546
1546
|
description: >
|
1547
1547
|
Matches HTTP requests based on method, base URL, URL path pattern, query parameters, and headers.
|
@@ -3054,25 +3054,35 @@ definitions:
|
|
3054
3054
|
enum: [RequestPath]
|
3055
3055
|
RequestOption:
|
3056
3056
|
title: Request Option
|
3057
|
-
description: Specifies the key field and where in the request a component's value should be injected.
|
3057
|
+
description: Specifies the key field or path and where in the request a component's value should be injected.
|
3058
3058
|
type: object
|
3059
3059
|
required:
|
3060
3060
|
- type
|
3061
|
-
- field_name
|
3062
3061
|
- inject_into
|
3063
3062
|
properties:
|
3064
3063
|
type:
|
3065
3064
|
type: string
|
3066
3065
|
enum: [RequestOption]
|
3067
3066
|
field_name:
|
3068
|
-
title:
|
3069
|
-
description: Configures which key should be used in the location that the descriptor is being injected into
|
3067
|
+
title: Field Name
|
3068
|
+
description: Configures which key should be used in the location that the descriptor is being injected into. We hope to eventually deprecate this field in favor of `field_path` for all request_options, but must currently maintain it for backwards compatibility in the Builder.
|
3070
3069
|
type: string
|
3071
3070
|
examples:
|
3072
3071
|
- segment_id
|
3073
3072
|
interpolation_context:
|
3074
3073
|
- config
|
3075
3074
|
- parameters
|
3075
|
+
field_path:
|
3076
|
+
title: Field Path
|
3077
|
+
description: Configures a path to be used for nested structures in JSON body requests (e.g. GraphQL queries)
|
3078
|
+
type: array
|
3079
|
+
items:
|
3080
|
+
type: string
|
3081
|
+
examples:
|
3082
|
+
- ["data", "viewer", "id"]
|
3083
|
+
interpolation_context:
|
3084
|
+
- config
|
3085
|
+
- parameters
|
3076
3086
|
inject_into:
|
3077
3087
|
title: Inject Into
|
3078
3088
|
description: Configures where the descriptor should be set on the HTTP requests. Note that request parameters that are already encoded in the URL path will not be duplicated.
|
@@ -365,14 +365,15 @@ class DatetimeBasedCursor(DeclarativeCursor):
|
|
365
365
|
options: MutableMapping[str, Any] = {}
|
366
366
|
if not stream_slice:
|
367
367
|
return options
|
368
|
+
|
368
369
|
if self.start_time_option and self.start_time_option.inject_into == option_type:
|
369
|
-
|
370
|
-
|
371
|
-
|
370
|
+
start_time_value = stream_slice.get(self._partition_field_start.eval(self.config))
|
371
|
+
self.start_time_option.inject_into_request(options, start_time_value, self.config)
|
372
|
+
|
372
373
|
if self.end_time_option and self.end_time_option.inject_into == option_type:
|
373
|
-
|
374
|
-
|
375
|
-
|
374
|
+
end_time_value = stream_slice.get(self._partition_field_end.eval(self.config))
|
375
|
+
self.end_time_option.inject_into_request(options, end_time_value, self.config)
|
376
|
+
|
376
377
|
return options
|
377
378
|
|
378
379
|
def should_be_synced(self, record: Record) -> bool:
|
@@ -657,7 +657,7 @@ class Rate(BaseModel):
|
|
657
657
|
)
|
658
658
|
|
659
659
|
|
660
|
-
class
|
660
|
+
class HttpRequestRegexMatcher(BaseModel):
|
661
661
|
class Config:
|
662
662
|
extra = Extra.allow
|
663
663
|
|
@@ -1240,11 +1240,17 @@ class InjectInto(Enum):
|
|
1240
1240
|
|
1241
1241
|
class RequestOption(BaseModel):
|
1242
1242
|
type: Literal["RequestOption"]
|
1243
|
-
field_name: str = Field(
|
1244
|
-
|
1245
|
-
description="Configures which key should be used in the location that the descriptor is being injected into",
|
1243
|
+
field_name: Optional[str] = Field(
|
1244
|
+
None,
|
1245
|
+
description="Configures which key should be used in the location that the descriptor is being injected into. We hope to eventually deprecate this field in favor of `field_path` for all request_options, but must currently maintain it for backwards compatibility in the Builder.",
|
1246
1246
|
examples=["segment_id"],
|
1247
|
-
title="
|
1247
|
+
title="Field Name",
|
1248
|
+
)
|
1249
|
+
field_path: Optional[List[str]] = Field(
|
1250
|
+
None,
|
1251
|
+
description="Configures a path to be used for nested structures in JSON body requests (e.g. GraphQL queries)",
|
1252
|
+
examples=[["data", "viewer", "id"]],
|
1253
|
+
title="Field Path",
|
1248
1254
|
)
|
1249
1255
|
inject_into: InjectInto = Field(
|
1250
1256
|
...,
|
@@ -1636,7 +1642,7 @@ class FixedWindowCallRatePolicy(BaseModel):
|
|
1636
1642
|
description="The maximum number of calls allowed within the period.",
|
1637
1643
|
title="Call Limit",
|
1638
1644
|
)
|
1639
|
-
matchers: List[
|
1645
|
+
matchers: List[HttpRequestRegexMatcher] = Field(
|
1640
1646
|
...,
|
1641
1647
|
description="List of matchers that define which requests this policy applies to.",
|
1642
1648
|
title="Matchers",
|
@@ -1653,7 +1659,7 @@ class MovingWindowCallRatePolicy(BaseModel):
|
|
1653
1659
|
description="List of rates that define the call limits for different time intervals.",
|
1654
1660
|
title="Rates",
|
1655
1661
|
)
|
1656
|
-
matchers: List[
|
1662
|
+
matchers: List[HttpRequestRegexMatcher] = Field(
|
1657
1663
|
...,
|
1658
1664
|
description="List of matchers that define which requests this policy applies to.",
|
1659
1665
|
title="Matchers",
|
@@ -1665,7 +1671,7 @@ class UnlimitedCallRatePolicy(BaseModel):
|
|
1665
1671
|
extra = Extra.allow
|
1666
1672
|
|
1667
1673
|
type: Literal["UnlimitedCallRatePolicy"]
|
1668
|
-
matchers: List[
|
1674
|
+
matchers: List[HttpRequestRegexMatcher] = Field(
|
1669
1675
|
...,
|
1670
1676
|
description="List of matchers that define which requests this policy applies to.",
|
1671
1677
|
title="Matchers",
|
@@ -251,7 +251,7 @@ from airbyte_cdk.sources.declarative.models.declarative_component_schema import
|
|
251
251
|
HttpRequester as HttpRequesterModel,
|
252
252
|
)
|
253
253
|
from airbyte_cdk.sources.declarative.models.declarative_component_schema import (
|
254
|
-
|
254
|
+
HttpRequestRegexMatcher as HttpRequestRegexMatcherModel,
|
255
255
|
)
|
256
256
|
from airbyte_cdk.sources.declarative.models.declarative_component_schema import (
|
257
257
|
HttpResponseFilter as HttpResponseFilterModel,
|
@@ -494,7 +494,7 @@ from airbyte_cdk.sources.streams.call_rate import (
|
|
494
494
|
APIBudget,
|
495
495
|
FixedWindowCallRatePolicy,
|
496
496
|
HttpAPIBudget,
|
497
|
-
|
497
|
+
HttpRequestRegexMatcher,
|
498
498
|
MovingWindowCallRatePolicy,
|
499
499
|
Rate,
|
500
500
|
UnlimitedCallRatePolicy,
|
@@ -644,7 +644,7 @@ class ModelToComponentFactory:
|
|
644
644
|
MovingWindowCallRatePolicyModel: self.create_moving_window_call_rate_policy,
|
645
645
|
UnlimitedCallRatePolicyModel: self.create_unlimited_call_rate_policy,
|
646
646
|
RateModel: self.create_rate,
|
647
|
-
|
647
|
+
HttpRequestRegexMatcherModel: self.create_http_request_matcher,
|
648
648
|
}
|
649
649
|
|
650
650
|
# Needed for the case where we need to perform a second parse on the fields of a custom component
|
@@ -771,8 +771,8 @@ class ModelToComponentFactory:
|
|
771
771
|
}
|
772
772
|
return names_to_types[value_type]
|
773
773
|
|
774
|
-
@staticmethod
|
775
774
|
def create_api_key_authenticator(
|
775
|
+
self,
|
776
776
|
model: ApiKeyAuthenticatorModel,
|
777
777
|
config: Config,
|
778
778
|
token_provider: Optional[TokenProvider] = None,
|
@@ -794,10 +794,8 @@ class ModelToComponentFactory:
|
|
794
794
|
)
|
795
795
|
|
796
796
|
request_option = (
|
797
|
-
|
798
|
-
inject_into=
|
799
|
-
field_name=model.inject_into.field_name,
|
800
|
-
parameters=model.parameters or {},
|
797
|
+
self._create_component_from_model(
|
798
|
+
model.inject_into, config, parameters=model.parameters or {}
|
801
799
|
)
|
802
800
|
if model.inject_into
|
803
801
|
else RequestOption(
|
@@ -806,6 +804,7 @@ class ModelToComponentFactory:
|
|
806
804
|
parameters=model.parameters or {},
|
807
805
|
)
|
808
806
|
)
|
807
|
+
|
809
808
|
return ApiKeyAuthenticator(
|
810
809
|
token_provider=(
|
811
810
|
token_provider
|
@@ -887,7 +886,7 @@ class ModelToComponentFactory:
|
|
887
886
|
token_provider=token_provider,
|
888
887
|
)
|
889
888
|
else:
|
890
|
-
return
|
889
|
+
return self.create_api_key_authenticator(
|
891
890
|
ApiKeyAuthenticatorModel(
|
892
891
|
type="ApiKeyAuthenticator",
|
893
892
|
api_token="",
|
@@ -1527,19 +1526,15 @@ class ModelToComponentFactory:
|
|
1527
1526
|
)
|
1528
1527
|
|
1529
1528
|
end_time_option = (
|
1530
|
-
|
1531
|
-
|
1532
|
-
field_name=model.end_time_option.field_name,
|
1533
|
-
parameters=model.parameters or {},
|
1529
|
+
self._create_component_from_model(
|
1530
|
+
model.end_time_option, config, parameters=model.parameters or {}
|
1534
1531
|
)
|
1535
1532
|
if model.end_time_option
|
1536
1533
|
else None
|
1537
1534
|
)
|
1538
1535
|
start_time_option = (
|
1539
|
-
|
1540
|
-
|
1541
|
-
field_name=model.start_time_option.field_name,
|
1542
|
-
parameters=model.parameters or {},
|
1536
|
+
self._create_component_from_model(
|
1537
|
+
model.start_time_option, config, parameters=model.parameters or {}
|
1543
1538
|
)
|
1544
1539
|
if model.start_time_option
|
1545
1540
|
else None
|
@@ -1610,19 +1605,15 @@ class ModelToComponentFactory:
|
|
1610
1605
|
cursor_model = model.incremental_sync
|
1611
1606
|
|
1612
1607
|
end_time_option = (
|
1613
|
-
|
1614
|
-
|
1615
|
-
field_name=cursor_model.end_time_option.field_name,
|
1616
|
-
parameters=cursor_model.parameters or {},
|
1608
|
+
self._create_component_from_model(
|
1609
|
+
cursor_model.end_time_option, config, parameters=cursor_model.parameters or {}
|
1617
1610
|
)
|
1618
1611
|
if cursor_model.end_time_option
|
1619
1612
|
else None
|
1620
1613
|
)
|
1621
1614
|
start_time_option = (
|
1622
|
-
|
1623
|
-
|
1624
|
-
field_name=cursor_model.start_time_option.field_name,
|
1625
|
-
parameters=cursor_model.parameters or {},
|
1615
|
+
self._create_component_from_model(
|
1616
|
+
cursor_model.start_time_option, config, parameters=cursor_model.parameters or {}
|
1626
1617
|
)
|
1627
1618
|
if cursor_model.start_time_option
|
1628
1619
|
else None
|
@@ -2191,16 +2182,11 @@ class ModelToComponentFactory:
|
|
2191
2182
|
additional_jwt_payload=model.additional_jwt_payload,
|
2192
2183
|
)
|
2193
2184
|
|
2194
|
-
@staticmethod
|
2195
2185
|
def create_list_partition_router(
|
2196
|
-
model: ListPartitionRouterModel, config: Config, **kwargs: Any
|
2186
|
+
self, model: ListPartitionRouterModel, config: Config, **kwargs: Any
|
2197
2187
|
) -> ListPartitionRouter:
|
2198
2188
|
request_option = (
|
2199
|
-
|
2200
|
-
inject_into=RequestOptionType(model.request_option.inject_into.value),
|
2201
|
-
field_name=model.request_option.field_name,
|
2202
|
-
parameters=model.parameters or {},
|
2203
|
-
)
|
2189
|
+
self._create_component_from_model(model.request_option, config)
|
2204
2190
|
if model.request_option
|
2205
2191
|
else None
|
2206
2192
|
)
|
@@ -2396,7 +2382,25 @@ class ModelToComponentFactory:
|
|
2396
2382
|
model: RequestOptionModel, config: Config, **kwargs: Any
|
2397
2383
|
) -> RequestOption:
|
2398
2384
|
inject_into = RequestOptionType(model.inject_into.value)
|
2399
|
-
|
2385
|
+
field_path: Optional[List[Union[InterpolatedString, str]]] = (
|
2386
|
+
[
|
2387
|
+
InterpolatedString.create(segment, parameters=kwargs.get("parameters", {}))
|
2388
|
+
for segment in model.field_path
|
2389
|
+
]
|
2390
|
+
if model.field_path
|
2391
|
+
else None
|
2392
|
+
)
|
2393
|
+
field_name = (
|
2394
|
+
InterpolatedString.create(model.field_name, parameters=kwargs.get("parameters", {}))
|
2395
|
+
if model.field_name
|
2396
|
+
else None
|
2397
|
+
)
|
2398
|
+
return RequestOption(
|
2399
|
+
field_name=field_name,
|
2400
|
+
field_path=field_path,
|
2401
|
+
inject_into=inject_into,
|
2402
|
+
parameters=kwargs.get("parameters", {}),
|
2403
|
+
)
|
2400
2404
|
|
2401
2405
|
def create_record_selector(
|
2402
2406
|
self,
|
@@ -3036,9 +3040,9 @@ class ModelToComponentFactory:
|
|
3036
3040
|
)
|
3037
3041
|
|
3038
3042
|
def create_http_request_matcher(
|
3039
|
-
self, model:
|
3040
|
-
) ->
|
3041
|
-
return
|
3043
|
+
self, model: HttpRequestRegexMatcherModel, config: Config, **kwargs: Any
|
3044
|
+
) -> HttpRequestRegexMatcher:
|
3045
|
+
return HttpRequestRegexMatcher(
|
3042
3046
|
method=model.method,
|
3043
3047
|
url_base=model.url_base,
|
3044
3048
|
url_path_pattern=model.url_path_pattern,
|
@@ -3,7 +3,7 @@
|
|
3
3
|
#
|
4
4
|
|
5
5
|
from dataclasses import InitVar, dataclass
|
6
|
-
from typing import Any, Iterable, List, Mapping, Optional, Union
|
6
|
+
from typing import Any, Iterable, List, Mapping, MutableMapping, Optional, Union
|
7
7
|
|
8
8
|
from airbyte_cdk.sources.declarative.interpolation.interpolated_string import InterpolatedString
|
9
9
|
from airbyte_cdk.sources.declarative.partition_routers.partition_router import PartitionRouter
|
@@ -100,7 +100,9 @@ class ListPartitionRouter(PartitionRouter):
|
|
100
100
|
):
|
101
101
|
slice_value = stream_slice.get(self._cursor_field.eval(self.config))
|
102
102
|
if slice_value:
|
103
|
-
|
103
|
+
options: MutableMapping[str, Any] = {}
|
104
|
+
self.request_option.inject_into_request(options, slice_value, self.config)
|
105
|
+
return options
|
104
106
|
else:
|
105
107
|
return {}
|
106
108
|
else:
|
@@ -4,7 +4,7 @@
|
|
4
4
|
import copy
|
5
5
|
import logging
|
6
6
|
from dataclasses import InitVar, dataclass
|
7
|
-
from typing import TYPE_CHECKING, Any, Iterable, List, Mapping, Optional, Union
|
7
|
+
from typing import TYPE_CHECKING, Any, Iterable, List, Mapping, MutableMapping, Optional, Union
|
8
8
|
|
9
9
|
import dpath
|
10
10
|
|
@@ -118,7 +118,7 @@ class SubstreamPartitionRouter(PartitionRouter):
|
|
118
118
|
def _get_request_option(
|
119
119
|
self, option_type: RequestOptionType, stream_slice: Optional[StreamSlice]
|
120
120
|
) -> Mapping[str, Any]:
|
121
|
-
params = {}
|
121
|
+
params: MutableMapping[str, Any] = {}
|
122
122
|
if stream_slice:
|
123
123
|
for parent_config in self.parent_stream_configs:
|
124
124
|
if (
|
@@ -128,13 +128,7 @@ class SubstreamPartitionRouter(PartitionRouter):
|
|
128
128
|
key = parent_config.partition_field.eval(self.config) # type: ignore # partition_field is always casted to an interpolated string
|
129
129
|
value = stream_slice.get(key)
|
130
130
|
if value:
|
131
|
-
params.
|
132
|
-
{
|
133
|
-
parent_config.request_option.field_name.eval( # type: ignore [union-attr]
|
134
|
-
config=self.config
|
135
|
-
): value
|
136
|
-
}
|
137
|
-
)
|
131
|
+
parent_config.request_option.inject_into_request(params, value, self.config)
|
138
132
|
return params
|
139
133
|
|
140
134
|
def stream_slices(self) -> Iterable[StreamSlice]:
|
@@ -305,23 +299,33 @@ class SubstreamPartitionRouter(PartitionRouter):
|
|
305
299
|
|
306
300
|
def _migrate_child_state_to_parent_state(self, stream_state: StreamState) -> StreamState:
|
307
301
|
"""
|
308
|
-
Migrate the child stream state
|
302
|
+
Migrate the child or global stream state into the parent stream's state format.
|
303
|
+
|
304
|
+
This method converts the child stream state—or, if present, the global state—into a format that is
|
305
|
+
compatible with parent streams that use incremental synchronization. The migration occurs only for
|
306
|
+
parent streams with incremental dependencies. It filters out per-partition states and retains only the
|
307
|
+
global state in the form {cursor_field: cursor_value}.
|
309
308
|
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
309
|
+
The method supports multiple input formats:
|
310
|
+
- A simple global state, e.g.:
|
311
|
+
{"updated_at": "2023-05-27T00:00:00Z"}
|
312
|
+
- A state object that contains a "state" key (which is assumed to hold the global state), e.g.:
|
313
|
+
{"state": {"updated_at": "2023-05-27T00:00:00Z"}, ...}
|
314
|
+
In this case, the migration uses the first value from the "state" dictionary.
|
315
|
+
- Any per-partition state formats or other non-simple structures are ignored during migration.
|
314
316
|
|
315
317
|
Args:
|
316
318
|
stream_state (StreamState): The state to migrate. Expected formats include:
|
317
319
|
- {"updated_at": "2023-05-27T00:00:00Z"}
|
318
|
-
- {"
|
320
|
+
- {"state": {"updated_at": "2023-05-27T00:00:00Z"}, ...}
|
321
|
+
(In this format, only the first global state value is used, and per-partition states are ignored.)
|
319
322
|
|
320
323
|
Returns:
|
321
324
|
StreamState: A migrated state for parent streams in the format:
|
322
325
|
{
|
323
326
|
"parent_stream_name": {"parent_stream_cursor": "2023-05-27T00:00:00Z"}
|
324
327
|
}
|
328
|
+
where each parent stream with an incremental dependency is assigned its corresponding cursor value.
|
325
329
|
|
326
330
|
Example:
|
327
331
|
Input: {"updated_at": "2023-05-27T00:00:00Z"}
|
@@ -332,11 +336,15 @@ class SubstreamPartitionRouter(PartitionRouter):
|
|
332
336
|
substream_state_values = list(stream_state.values())
|
333
337
|
substream_state = substream_state_values[0] if substream_state_values else {}
|
334
338
|
|
335
|
-
# Ignore per-partition states or invalid formats
|
339
|
+
# Ignore per-partition states or invalid formats.
|
336
340
|
if isinstance(substream_state, (list, dict)) or len(substream_state_values) != 1:
|
337
|
-
|
341
|
+
# If a global state is present under the key "state", use its first value.
|
342
|
+
if "state" in stream_state and isinstance(stream_state["state"], dict):
|
343
|
+
substream_state = list(stream_state["state"].values())[0]
|
344
|
+
else:
|
345
|
+
return {}
|
338
346
|
|
339
|
-
#
|
347
|
+
# Build the parent state for all parent streams with incremental dependencies.
|
340
348
|
parent_state = {}
|
341
349
|
if substream_state:
|
342
350
|
for parent_config in self.parent_stream_configs:
|
@@ -202,6 +202,9 @@ class HttpRequester(Requester):
|
|
202
202
|
Raise a ValueError if there's a key collision
|
203
203
|
Returned merged mapping otherwise
|
204
204
|
"""
|
205
|
+
|
206
|
+
is_body_json = requester_method.__name__ == "get_request_body_json"
|
207
|
+
|
205
208
|
return combine_mappings(
|
206
209
|
[
|
207
210
|
requester_method(
|
@@ -211,7 +214,8 @@ class HttpRequester(Requester):
|
|
211
214
|
),
|
212
215
|
auth_options_method(),
|
213
216
|
extra_options,
|
214
|
-
]
|
217
|
+
],
|
218
|
+
allow_same_value_merge=is_body_json,
|
215
219
|
)
|
216
220
|
|
217
221
|
def _request_headers(
|
@@ -187,7 +187,7 @@ class DefaultPaginator(Paginator):
|
|
187
187
|
def _get_request_options(
|
188
188
|
self, option_type: RequestOptionType, next_page_token: Optional[Mapping[str, Any]]
|
189
189
|
) -> MutableMapping[str, Any]:
|
190
|
-
options = {}
|
190
|
+
options: MutableMapping[str, Any] = {}
|
191
191
|
|
192
192
|
token = next_page_token.get("next_page_token") if next_page_token else None
|
193
193
|
if (
|
@@ -196,15 +196,16 @@ class DefaultPaginator(Paginator):
|
|
196
196
|
and isinstance(self.page_token_option, RequestOption)
|
197
197
|
and self.page_token_option.inject_into == option_type
|
198
198
|
):
|
199
|
-
|
199
|
+
self.page_token_option.inject_into_request(options, token, self.config)
|
200
|
+
|
200
201
|
if (
|
201
202
|
self.page_size_option
|
202
203
|
and self.pagination_strategy.get_page_size()
|
203
204
|
and self.page_size_option.inject_into == option_type
|
204
205
|
):
|
205
|
-
|
206
|
-
|
207
|
-
|
206
|
+
page_size = self.pagination_strategy.get_page_size()
|
207
|
+
self.page_size_option.inject_into_request(options, page_size, self.config)
|
208
|
+
|
208
209
|
return options
|
209
210
|
|
210
211
|
|
@@ -4,9 +4,10 @@
|
|
4
4
|
|
5
5
|
from dataclasses import InitVar, dataclass
|
6
6
|
from enum import Enum
|
7
|
-
from typing import Any, Mapping, Union
|
7
|
+
from typing import Any, List, Literal, Mapping, MutableMapping, Optional, Union
|
8
8
|
|
9
9
|
from airbyte_cdk.sources.declarative.interpolation.interpolated_string import InterpolatedString
|
10
|
+
from airbyte_cdk.sources.types import Config
|
10
11
|
|
11
12
|
|
12
13
|
class RequestOptionType(Enum):
|
@@ -26,13 +27,91 @@ class RequestOption:
|
|
26
27
|
Describes an option to set on a request
|
27
28
|
|
28
29
|
Attributes:
|
29
|
-
field_name (str): Describes the name of the parameter to inject
|
30
|
+
field_name (str): Describes the name of the parameter to inject. Mutually exclusive with field_path.
|
31
|
+
field_path (list(str)): Describes the path to a nested field as a list of field names.
|
32
|
+
Only valid for body_json injection type, and mutually exclusive with field_name.
|
30
33
|
inject_into (RequestOptionType): Describes where in the HTTP request to inject the parameter
|
31
34
|
"""
|
32
35
|
|
33
|
-
field_name: Union[InterpolatedString, str]
|
34
36
|
inject_into: RequestOptionType
|
35
37
|
parameters: InitVar[Mapping[str, Any]]
|
38
|
+
field_name: Optional[Union[InterpolatedString, str]] = None
|
39
|
+
field_path: Optional[List[Union[InterpolatedString, str]]] = None
|
36
40
|
|
37
41
|
def __post_init__(self, parameters: Mapping[str, Any]) -> None:
|
38
|
-
|
42
|
+
# Validate inputs. We should expect either field_name or field_path, but not both
|
43
|
+
if self.field_name is None and self.field_path is None:
|
44
|
+
raise ValueError("RequestOption requires either a field_name or field_path")
|
45
|
+
|
46
|
+
if self.field_name is not None and self.field_path is not None:
|
47
|
+
raise ValueError(
|
48
|
+
"Only one of field_name or field_path can be provided to RequestOption"
|
49
|
+
)
|
50
|
+
|
51
|
+
# Nested field injection is only supported for body JSON injection
|
52
|
+
if self.field_path is not None and self.inject_into != RequestOptionType.body_json:
|
53
|
+
raise ValueError(
|
54
|
+
"Nested field injection is only supported for body JSON injection. Please use a top-level field_name for other injection types."
|
55
|
+
)
|
56
|
+
|
57
|
+
# Convert field_name and field_path into InterpolatedString objects if they are strings
|
58
|
+
if self.field_name is not None:
|
59
|
+
self.field_name = InterpolatedString.create(self.field_name, parameters=parameters)
|
60
|
+
elif self.field_path is not None:
|
61
|
+
self.field_path = [
|
62
|
+
InterpolatedString.create(segment, parameters=parameters)
|
63
|
+
for segment in self.field_path
|
64
|
+
]
|
65
|
+
|
66
|
+
@property
|
67
|
+
def _is_field_path(self) -> bool:
|
68
|
+
"""Returns whether this option is a field path (ie, a nested field)"""
|
69
|
+
return self.field_path is not None
|
70
|
+
|
71
|
+
def inject_into_request(
|
72
|
+
self,
|
73
|
+
target: MutableMapping[str, Any],
|
74
|
+
value: Any,
|
75
|
+
config: Config,
|
76
|
+
) -> None:
|
77
|
+
"""
|
78
|
+
Inject a request option value into a target request structure using either field_name or field_path.
|
79
|
+
For non-body-json injection, only top-level field names are supported.
|
80
|
+
For body-json injection, both field names and nested field paths are supported.
|
81
|
+
|
82
|
+
Args:
|
83
|
+
target: The request structure to inject the value into
|
84
|
+
value: The value to inject
|
85
|
+
config: The config object to use for interpolation
|
86
|
+
"""
|
87
|
+
if self._is_field_path:
|
88
|
+
if self.inject_into != RequestOptionType.body_json:
|
89
|
+
raise ValueError(
|
90
|
+
"Nested field injection is only supported for body JSON injection. Please use a top-level field_name for other injection types."
|
91
|
+
)
|
92
|
+
|
93
|
+
assert self.field_path is not None # for type checker
|
94
|
+
current = target
|
95
|
+
# Convert path segments into strings, evaluating any interpolated segments
|
96
|
+
# Example: ["data", "{{ config[user_type] }}", "id"] -> ["data", "admin", "id"]
|
97
|
+
*path_parts, final_key = [
|
98
|
+
str(
|
99
|
+
segment.eval(config=config)
|
100
|
+
if isinstance(segment, InterpolatedString)
|
101
|
+
else segment
|
102
|
+
)
|
103
|
+
for segment in self.field_path
|
104
|
+
]
|
105
|
+
|
106
|
+
# Build a nested dictionary structure and set the final value at the deepest level
|
107
|
+
for part in path_parts:
|
108
|
+
current = current.setdefault(part, {})
|
109
|
+
current[final_key] = value
|
110
|
+
else:
|
111
|
+
# For non-nested fields, evaluate the field name if it's an interpolated string
|
112
|
+
key = (
|
113
|
+
self.field_name.eval(config=config)
|
114
|
+
if isinstance(self.field_name, InterpolatedString)
|
115
|
+
else self.field_name
|
116
|
+
)
|
117
|
+
target[str(key)] = value
|
@@ -80,12 +80,13 @@ class DatetimeBasedRequestOptionsProvider(RequestOptionsProvider):
|
|
80
80
|
options: MutableMapping[str, Any] = {}
|
81
81
|
if not stream_slice:
|
82
82
|
return options
|
83
|
+
|
83
84
|
if self.start_time_option and self.start_time_option.inject_into == option_type:
|
84
|
-
|
85
|
-
|
86
|
-
|
85
|
+
start_time_value = stream_slice.get(self._partition_field_start.eval(self.config))
|
86
|
+
self.start_time_option.inject_into_request(options, start_time_value, self.config)
|
87
|
+
|
87
88
|
if self.end_time_option and self.end_time_option.inject_into == option_type:
|
88
|
-
|
89
|
-
|
90
|
-
|
89
|
+
end_time_value = stream_slice.get(self._partition_field_end.eval(self.config))
|
90
|
+
self.end_time_option.inject_into_request(options, end_time_value, self.config)
|
91
|
+
|
91
92
|
return options
|
@@ -128,6 +128,9 @@ class SimpleRetriever(Retriever):
|
|
128
128
|
Returned merged mapping otherwise
|
129
129
|
"""
|
130
130
|
# FIXME we should eventually remove the usage of stream_state as part of the interpolation
|
131
|
+
|
132
|
+
is_body_json = paginator_method.__name__ == "get_request_body_json"
|
133
|
+
|
131
134
|
mappings = [
|
132
135
|
paginator_method(
|
133
136
|
stream_state=stream_state,
|
@@ -143,7 +146,7 @@ class SimpleRetriever(Retriever):
|
|
143
146
|
next_page_token=next_page_token,
|
144
147
|
)
|
145
148
|
)
|
146
|
-
return combine_mappings(mappings)
|
149
|
+
return combine_mappings(mappings, allow_same_value_merge=is_body_json)
|
147
150
|
|
148
151
|
def _request_headers(
|
149
152
|
self,
|
@@ -100,6 +100,69 @@ class RequestMatcher(abc.ABC):
|
|
100
100
|
|
101
101
|
|
102
102
|
class HttpRequestMatcher(RequestMatcher):
|
103
|
+
"""Simple implementation of RequestMatcher for http requests case"""
|
104
|
+
|
105
|
+
def __init__(
|
106
|
+
self,
|
107
|
+
method: Optional[str] = None,
|
108
|
+
url: Optional[str] = None,
|
109
|
+
params: Optional[Mapping[str, Any]] = None,
|
110
|
+
headers: Optional[Mapping[str, Any]] = None,
|
111
|
+
):
|
112
|
+
"""Constructor
|
113
|
+
|
114
|
+
:param method:
|
115
|
+
:param url:
|
116
|
+
:param params:
|
117
|
+
:param headers:
|
118
|
+
"""
|
119
|
+
self._method = method
|
120
|
+
self._url = url
|
121
|
+
self._params = {str(k): str(v) for k, v in (params or {}).items()}
|
122
|
+
self._headers = {str(k): str(v) for k, v in (headers or {}).items()}
|
123
|
+
|
124
|
+
@staticmethod
|
125
|
+
def _match_dict(obj: Mapping[str, Any], pattern: Mapping[str, Any]) -> bool:
|
126
|
+
"""Check that all elements from pattern dict present and have the same values in obj dict
|
127
|
+
|
128
|
+
:param obj:
|
129
|
+
:param pattern:
|
130
|
+
:return:
|
131
|
+
"""
|
132
|
+
return pattern.items() <= obj.items()
|
133
|
+
|
134
|
+
def __call__(self, request: Any) -> bool:
|
135
|
+
"""
|
136
|
+
|
137
|
+
:param request:
|
138
|
+
:return: True if matches the provided request object, False - otherwise
|
139
|
+
"""
|
140
|
+
if isinstance(request, requests.Request):
|
141
|
+
prepared_request = request.prepare()
|
142
|
+
elif isinstance(request, requests.PreparedRequest):
|
143
|
+
prepared_request = request
|
144
|
+
else:
|
145
|
+
return False
|
146
|
+
|
147
|
+
if self._method is not None:
|
148
|
+
if prepared_request.method != self._method:
|
149
|
+
return False
|
150
|
+
if self._url is not None and prepared_request.url is not None:
|
151
|
+
url_without_params = prepared_request.url.split("?")[0]
|
152
|
+
if url_without_params != self._url:
|
153
|
+
return False
|
154
|
+
if self._params is not None:
|
155
|
+
parsed_url = parse.urlsplit(prepared_request.url)
|
156
|
+
params = dict(parse.parse_qsl(str(parsed_url.query)))
|
157
|
+
if not self._match_dict(params, self._params):
|
158
|
+
return False
|
159
|
+
if self._headers is not None:
|
160
|
+
if not self._match_dict(prepared_request.headers, self._headers):
|
161
|
+
return False
|
162
|
+
return True
|
163
|
+
|
164
|
+
|
165
|
+
class HttpRequestRegexMatcher(RequestMatcher):
|
103
166
|
"""
|
104
167
|
Extended RequestMatcher for HTTP requests that supports matching on:
|
105
168
|
- HTTP method (case-insensitive)
|
@@ -3,43 +3,102 @@
|
|
3
3
|
#
|
4
4
|
|
5
5
|
|
6
|
-
|
6
|
+
import copy
|
7
|
+
from typing import Any, Dict, List, Mapping, Optional, Union
|
8
|
+
|
9
|
+
|
10
|
+
def _merge_mappings(
|
11
|
+
target: Dict[str, Any],
|
12
|
+
source: Mapping[str, Any],
|
13
|
+
path: Optional[List[str]] = None,
|
14
|
+
allow_same_value_merge: bool = False,
|
15
|
+
) -> None:
|
16
|
+
"""
|
17
|
+
Recursively merge two dictionaries, raising an error if there are any conflicts.
|
18
|
+
For body_json requests (allow_same_value_merge=True), a conflict occurs only when the same path has different values.
|
19
|
+
For other request types (allow_same_value_merge=False), any duplicate key is a conflict, regardless of value.
|
20
|
+
|
21
|
+
Args:
|
22
|
+
target: The dictionary to merge into
|
23
|
+
source: The dictionary to merge from
|
24
|
+
path: The current path in the nested structure (for error messages)
|
25
|
+
allow_same_value_merge: Whether to allow merging the same value into the same key. Set to false by default, should only be true for body_json injections
|
26
|
+
"""
|
27
|
+
path = path or []
|
28
|
+
for key, source_value in source.items():
|
29
|
+
current_path = path + [str(key)]
|
30
|
+
|
31
|
+
if key in target:
|
32
|
+
target_value = target[key]
|
33
|
+
if isinstance(target_value, dict) and isinstance(source_value, dict):
|
34
|
+
# Only body_json supports nested_structures
|
35
|
+
if not allow_same_value_merge:
|
36
|
+
raise ValueError(f"Duplicate keys found: {'.'.join(current_path)}")
|
37
|
+
# If both are dictionaries, recursively merge them
|
38
|
+
_merge_mappings(target_value, source_value, current_path, allow_same_value_merge)
|
39
|
+
|
40
|
+
elif not allow_same_value_merge or target_value != source_value:
|
41
|
+
# If same key has different values, that's a conflict
|
42
|
+
raise ValueError(f"Duplicate keys found: {'.'.join(current_path)}")
|
43
|
+
else:
|
44
|
+
# No conflict, just copy the value (using deepcopy for nested structures)
|
45
|
+
target[key] = copy.deepcopy(source_value)
|
7
46
|
|
8
47
|
|
9
48
|
def combine_mappings(
|
10
49
|
mappings: List[Optional[Union[Mapping[str, Any], str]]],
|
50
|
+
allow_same_value_merge: bool = False,
|
11
51
|
) -> Union[Mapping[str, Any], str]:
|
12
52
|
"""
|
13
|
-
Combine multiple mappings into a single mapping.
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
53
|
+
Combine multiple mappings into a single mapping.
|
54
|
+
|
55
|
+
For body_json requests (allow_same_value_merge=True):
|
56
|
+
- Supports nested structures (e.g., {"data": {"user": {"id": 1}}})
|
57
|
+
- Allows duplicate keys if their values match
|
58
|
+
- Raises error if same path has different values
|
59
|
+
|
60
|
+
For other request types (allow_same_value_merge=False):
|
61
|
+
- Only supports flat structures
|
62
|
+
- Any duplicate key raises an error, regardless of value
|
63
|
+
|
64
|
+
Args:
|
65
|
+
mappings: List of mappings to combine
|
66
|
+
allow_same_value_merge: Whether to allow duplicate keys with matching values.
|
67
|
+
Should only be True for body_json requests.
|
68
|
+
|
69
|
+
Returns:
|
70
|
+
A single mapping combining all inputs, or a string if there is exactly one
|
71
|
+
string mapping and no other non-empty mappings.
|
72
|
+
|
73
|
+
Raises:
|
74
|
+
ValueError: If there are:
|
75
|
+
- Multiple string mappings
|
76
|
+
- Both a string mapping and non-empty dictionary mappings
|
77
|
+
- Conflicting keys/paths based on allow_same_value_merge setting
|
18
78
|
"""
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
all_keys.append(keys)
|
25
|
-
|
26
|
-
string_options = sum(isinstance(mapping, str) for mapping in mappings)
|
27
|
-
# If more than one mapping is a string, raise a ValueError
|
79
|
+
if not mappings:
|
80
|
+
return {}
|
81
|
+
|
82
|
+
# Count how many string options we have, ignoring None values
|
83
|
+
string_options = sum(isinstance(mapping, str) for mapping in mappings if mapping is not None)
|
28
84
|
if string_options > 1:
|
29
85
|
raise ValueError("Cannot combine multiple string options")
|
30
86
|
|
31
|
-
|
32
|
-
|
87
|
+
# Filter out None values and empty mappings
|
88
|
+
non_empty_mappings = [
|
89
|
+
m for m in mappings if m is not None and not (isinstance(m, Mapping) and not m)
|
90
|
+
]
|
33
91
|
|
34
|
-
# If
|
35
|
-
|
36
|
-
if
|
37
|
-
|
92
|
+
# If there is only one string option and no other non-empty mappings, return it
|
93
|
+
if string_options == 1:
|
94
|
+
if len(non_empty_mappings) > 1:
|
95
|
+
raise ValueError("Cannot combine multiple options if one is a string")
|
96
|
+
return next(m for m in non_empty_mappings if isinstance(m, str))
|
38
97
|
|
39
|
-
#
|
40
|
-
|
41
|
-
|
42
|
-
|
98
|
+
# Start with an empty result and merge each mapping into it
|
99
|
+
result: Dict[str, Any] = {}
|
100
|
+
for mapping in non_empty_mappings:
|
101
|
+
if mapping and isinstance(mapping, Mapping):
|
102
|
+
_merge_mappings(result, mapping, allow_same_value_merge=allow_same_value_merge)
|
43
103
|
|
44
|
-
|
45
|
-
return {key: value for mapping in mappings if mapping for key, value in mapping.items()} # type: ignore # mapping can't be string here
|
104
|
+
return result
|
@@ -55,7 +55,7 @@ airbyte_cdk/sources/declarative/auth/declarative_authenticator.py,sha256=nf-OmRU
|
|
55
55
|
airbyte_cdk/sources/declarative/auth/jwt.py,sha256=SICqNsN2Cn_EgKadIgWuZpQxuMHyzrMZD_2-Uwy10rY,8539
|
56
56
|
airbyte_cdk/sources/declarative/auth/oauth.py,sha256=fibXa-dqtM54jIUscWbz7DEA5uY6F2o1LfARjEeGRy0,13926
|
57
57
|
airbyte_cdk/sources/declarative/auth/selective_authenticator.py,sha256=qGwC6YsCldr1bIeKG6Qo-A9a5cTdHw-vcOn3OtQrS4c,1540
|
58
|
-
airbyte_cdk/sources/declarative/auth/token.py,sha256=
|
58
|
+
airbyte_cdk/sources/declarative/auth/token.py,sha256=2EnE78EhBOY9hbeZnQJ9AuFaM-G7dccU-oKo_LThRQk,11070
|
59
59
|
airbyte_cdk/sources/declarative/auth/token_provider.py,sha256=9CuSsmOoHkvlc4k-oZ3Jx5luAgfTMm1I_5HOZxw7wMU,3075
|
60
60
|
airbyte_cdk/sources/declarative/checks/__init__.py,sha256=nsVV5Bo0E_tBNd8A4Xdsdb-75PpcLo5RQu2RQ_Gv-ME,806
|
61
61
|
airbyte_cdk/sources/declarative/checks/check_dynamic_stream.py,sha256=HUktywjI8pqOeED08UGqponUSwxs2TOAECTowlWlrRE,2138
|
@@ -63,11 +63,11 @@ airbyte_cdk/sources/declarative/checks/check_stream.py,sha256=dAA-UhmMj0WLXCkRQr
|
|
63
63
|
airbyte_cdk/sources/declarative/checks/connection_checker.py,sha256=MBRJo6WJlZQHpIfOGaNOkkHUmgUl_4wDM6VPo41z5Ss,1383
|
64
64
|
airbyte_cdk/sources/declarative/concurrency_level/__init__.py,sha256=5XUqrmlstYlMM0j6crktlKQwALek0uiz2D3WdM46MyA,191
|
65
65
|
airbyte_cdk/sources/declarative/concurrency_level/concurrency_level.py,sha256=YIwCTCpOr_QSNW4ltQK0yUGWInI8PKNY216HOOegYLk,2101
|
66
|
-
airbyte_cdk/sources/declarative/concurrent_declarative_source.py,sha256=
|
66
|
+
airbyte_cdk/sources/declarative/concurrent_declarative_source.py,sha256=7uqf_zQd2T08AYdMDJ80Zt0W1QHqZd-dvltXC-3g8W4,28136
|
67
67
|
airbyte_cdk/sources/declarative/datetime/__init__.py,sha256=l9LG7Qm6e5r_qgqfVKnx3mXYtg1I9MmMjomVIPfU4XA,177
|
68
68
|
airbyte_cdk/sources/declarative/datetime/datetime_parser.py,sha256=SX9JjdesN1edN2WVUVMzU_ptqp2QB1OnsnjZ4mwcX7w,2579
|
69
69
|
airbyte_cdk/sources/declarative/datetime/min_max_datetime.py,sha256=0BHBtDNQZfvwM45-tY5pNlTcKAFSGGNxemoi0Jic-0E,5785
|
70
|
-
airbyte_cdk/sources/declarative/declarative_component_schema.yaml,sha256=
|
70
|
+
airbyte_cdk/sources/declarative/declarative_component_schema.yaml,sha256=_pL5eEPf9N7U5kyzfB6YJG2xUHAPNTrJSApLYlQS9vw,147556
|
71
71
|
airbyte_cdk/sources/declarative/declarative_source.py,sha256=nF7wBqFd3AQmEKAm4CnIo29CJoQL562cJGSCeL8U8bA,1531
|
72
72
|
airbyte_cdk/sources/declarative/declarative_stream.py,sha256=venZjfpvtqr3oFSuvMBWtn4h9ayLhD4L65ACuXCDZ64,10445
|
73
73
|
airbyte_cdk/sources/declarative/decoders/__init__.py,sha256=KSpQetKGqPCv-38QgcVJ5kzM5nzbFldTSsYDCS3Xf0Y,1035
|
@@ -89,7 +89,7 @@ airbyte_cdk/sources/declarative/extractors/response_to_file_extractor.py,sha256=
|
|
89
89
|
airbyte_cdk/sources/declarative/extractors/type_transformer.py,sha256=d6Y2Rfg8pMVEEnHllfVksWZdNVOU55yk34O03dP9muY,1626
|
90
90
|
airbyte_cdk/sources/declarative/incremental/__init__.py,sha256=U1oZKtBaEC6IACmvziY9Wzg7Z8EgF4ZuR7NwvjlB_Sk,1255
|
91
91
|
airbyte_cdk/sources/declarative/incremental/concurrent_partition_cursor.py,sha256=5dbO47TFmC5Oz8TZ8DKXwXeZElz70xy2v2HJlZr5qVs,17751
|
92
|
-
airbyte_cdk/sources/declarative/incremental/datetime_based_cursor.py,sha256=
|
92
|
+
airbyte_cdk/sources/declarative/incremental/datetime_based_cursor.py,sha256=5Bl_2EeA4as0e3J23Yxp8Q8BXzh0nJ2NcGSgj3V0h2o,21954
|
93
93
|
airbyte_cdk/sources/declarative/incremental/declarative_cursor.py,sha256=5Bhw9VRPyIuCaD0wmmq_L3DZsa-rJgtKSEUzSd8YYD0,536
|
94
94
|
airbyte_cdk/sources/declarative/incremental/global_substream_cursor.py,sha256=9HO-QbL9akvjq2NP7l498RwLA4iQZlBMQW1tZbt34I8,15943
|
95
95
|
airbyte_cdk/sources/declarative/incremental/per_partition_cursor.py,sha256=9IAJTCiRUXvhFFz-IhZtYh_KfAjLHqthsYf2jErQRls,17728
|
@@ -109,20 +109,20 @@ airbyte_cdk/sources/declarative/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW
|
|
109
109
|
airbyte_cdk/sources/declarative/migrations/legacy_to_per_partition_state_migration.py,sha256=iemy3fKLczcU0-Aor7tx5jcT6DRedKMqyK7kCOp01hg,3924
|
110
110
|
airbyte_cdk/sources/declarative/migrations/state_migration.py,sha256=KWPjealMLKSMtajXgkdGgKg7EmTLR-CqqD7UIh0-eDU,794
|
111
111
|
airbyte_cdk/sources/declarative/models/__init__.py,sha256=nUFxNCiKeYRVXuZEKA7GD-lTHxsiKcQ8FitZjKhPIvE,100
|
112
|
-
airbyte_cdk/sources/declarative/models/declarative_component_schema.py,sha256=
|
112
|
+
airbyte_cdk/sources/declarative/models/declarative_component_schema.py,sha256=4C2CQ3IkDI39X0Pkf5j-X5pfKUVTrfwjy6wAJoaI6cE,104148
|
113
113
|
airbyte_cdk/sources/declarative/parsers/__init__.py,sha256=ZnqYNxHsKCgO38IwB34RQyRMXTs4GTvlRi3ImKnIioo,61
|
114
114
|
airbyte_cdk/sources/declarative/parsers/custom_code_compiler.py,sha256=958MMX6_ZOJUlDDdNr9Krosgi2bCKGx2Z765M2Woz18,5505
|
115
115
|
airbyte_cdk/sources/declarative/parsers/custom_exceptions.py,sha256=Rir9_z3Kcd5Es0-LChrzk-0qubAsiK_RSEnLmK2OXm8,553
|
116
116
|
airbyte_cdk/sources/declarative/parsers/manifest_component_transformer.py,sha256=CXwTfD3wSQq3okcqwigpprbHhSURUokh4GK2OmOyKC8,9132
|
117
117
|
airbyte_cdk/sources/declarative/parsers/manifest_reference_resolver.py,sha256=IWUOdF03o-aQn0Occo1BJCxU0Pz-QILk5L67nzw2thw,6803
|
118
|
-
airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py,sha256=
|
118
|
+
airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py,sha256=5werqhabiFoYHEiHfn6v0cnzrX0t2jwI3kGa_u0xjn4,134157
|
119
119
|
airbyte_cdk/sources/declarative/partition_routers/__init__.py,sha256=HJ-Syp3p7RpyR_OK0X_a2kSyISfu3W-PKrRI16iY0a8,957
|
120
120
|
airbyte_cdk/sources/declarative/partition_routers/async_job_partition_router.py,sha256=VelO7zKqKtzMJ35jyFeg0ypJLQC0plqqIBNXoBW1G2E,3001
|
121
121
|
airbyte_cdk/sources/declarative/partition_routers/cartesian_product_stream_slicer.py,sha256=c5cuVFM6NFkuQqG8Z5IwkBuwDrvXZN1CunUOM_L0ezg,6892
|
122
|
-
airbyte_cdk/sources/declarative/partition_routers/list_partition_router.py,sha256=
|
122
|
+
airbyte_cdk/sources/declarative/partition_routers/list_partition_router.py,sha256=tmGGpMoOBmaMfhVZq53AEWxoHm2lmNVi6hA2_IVEnAA,4882
|
123
123
|
airbyte_cdk/sources/declarative/partition_routers/partition_router.py,sha256=YyEIzdmLd1FjbVP3QbQ2VFCLW_P-OGbVh6VpZShp54k,2218
|
124
124
|
airbyte_cdk/sources/declarative/partition_routers/single_partition_router.py,sha256=SKzKjSyfccq4dxGIh-J6ejrgkCHzaiTIazmbmeQiRD4,1942
|
125
|
-
airbyte_cdk/sources/declarative/partition_routers/substream_partition_router.py,sha256=
|
125
|
+
airbyte_cdk/sources/declarative/partition_routers/substream_partition_router.py,sha256=LlWj-Ofs-xfjlqmDzH8OYpyblP2Pb8bPDdR9g1UZyt0,17693
|
126
126
|
airbyte_cdk/sources/declarative/requesters/README.md,sha256=eL1I4iLkxaw7hJi9S9d18_XcRl-R8lUSjqBVJJzvXmg,2656
|
127
127
|
airbyte_cdk/sources/declarative/requesters/__init__.py,sha256=d7a3OoHbqaJDyyPli3nqqJ2yAW_SLX6XDaBAKOwvpxw,364
|
128
128
|
airbyte_cdk/sources/declarative/requesters/error_handlers/__init__.py,sha256=SkEDcJxlT1683rNx93K9whoS0OyUukkuOfToGtgpF58,776
|
@@ -139,9 +139,9 @@ airbyte_cdk/sources/declarative/requesters/error_handlers/default_http_response_
|
|
139
139
|
airbyte_cdk/sources/declarative/requesters/error_handlers/error_handler.py,sha256=Tan66odx8VHzfdyyXMQkXz2pJYksllGqvxmpoajgcK4,669
|
140
140
|
airbyte_cdk/sources/declarative/requesters/error_handlers/http_response_filter.py,sha256=E-fQbt4ShfxZVoqfnmOx69C6FUPWZz8BIqI3DN9Kcjs,7935
|
141
141
|
airbyte_cdk/sources/declarative/requesters/http_job_repository.py,sha256=3GtOefPH08evlSUxaILkiKLTHbIspFY4qd5B3ZqNE60,10063
|
142
|
-
airbyte_cdk/sources/declarative/requesters/http_requester.py,sha256=
|
142
|
+
airbyte_cdk/sources/declarative/requesters/http_requester.py,sha256=Ek5hS60-CYjvEaFD-bI7qA-bPgbOPb9hTbMBU4n5zNs,14994
|
143
143
|
airbyte_cdk/sources/declarative/requesters/paginators/__init__.py,sha256=uArbKs9JKNCt7t9tZoeWwjDpyI1HoPp29FNW0JzvaEM,644
|
144
|
-
airbyte_cdk/sources/declarative/requesters/paginators/default_paginator.py,sha256=
|
144
|
+
airbyte_cdk/sources/declarative/requesters/paginators/default_paginator.py,sha256=dSm_pKGOZjzvg-X_Vif-MjrnlUG23fCa69bocq8dVIs,11693
|
145
145
|
airbyte_cdk/sources/declarative/requesters/paginators/no_pagination.py,sha256=j6j9QRPaTbKQ2N661RFVKthhkWiodEp6ut0tKeEd0Ng,2019
|
146
146
|
airbyte_cdk/sources/declarative/requesters/paginators/paginator.py,sha256=OlN-y0PEOMzlUNUh3pzonoTpIJpGwkP4ibFengvpLVU,2230
|
147
147
|
airbyte_cdk/sources/declarative/requesters/paginators/strategies/__init__.py,sha256=2gly8fuZpDNwtu1Qg6oE2jBLGqQRdzSLJdnpk_iDV6I,767
|
@@ -150,9 +150,9 @@ airbyte_cdk/sources/declarative/requesters/paginators/strategies/offset_incremen
|
|
150
150
|
airbyte_cdk/sources/declarative/requesters/paginators/strategies/page_increment.py,sha256=Z2i6a-oKMmOTxHxsTVSnyaShkJ3u8xZw1xIJdx2yxss,2731
|
151
151
|
airbyte_cdk/sources/declarative/requesters/paginators/strategies/pagination_strategy.py,sha256=ZBshGQNr5Bb_V8dqnWRISqdXFcjm1CKIXnlfbRhNl8g,1308
|
152
152
|
airbyte_cdk/sources/declarative/requesters/paginators/strategies/stop_condition.py,sha256=LoKXdUbSgHEtSwtA8DFrnX6SpQbRVVwreY8NguTKTcI,2229
|
153
|
-
airbyte_cdk/sources/declarative/requesters/request_option.py,sha256=
|
153
|
+
airbyte_cdk/sources/declarative/requesters/request_option.py,sha256=Bl0gxGWudmwT3FXBozTN00WYle2jd6ry_S1YylCnwqM,4825
|
154
154
|
airbyte_cdk/sources/declarative/requesters/request_options/__init__.py,sha256=WCwpKqM4wKqy-DHJaCHbKAlFqRVOqMi9K5qonxIfi_Y,809
|
155
|
-
airbyte_cdk/sources/declarative/requesters/request_options/datetime_based_request_options_provider.py,sha256=
|
155
|
+
airbyte_cdk/sources/declarative/requesters/request_options/datetime_based_request_options_provider.py,sha256=31nG6_0igidJFQon37-WeQkTpG3g2A5ZmlluI3ilZdE,3632
|
156
156
|
airbyte_cdk/sources/declarative/requesters/request_options/default_request_options_provider.py,sha256=SRROdPJZ5kuqHLOlkh115pWP9nDGfDxRYPgH9oD3hPo,1798
|
157
157
|
airbyte_cdk/sources/declarative/requesters/request_options/interpolated_nested_request_input_provider.py,sha256=UW4cAtzkQ261AyLI1cmCL2WLdI3ZDYGUTmrqKB9W3u8,2422
|
158
158
|
airbyte_cdk/sources/declarative/requesters/request_options/interpolated_request_input_provider.py,sha256=Vr2-qa8iHC0vJ4cCtPl7lAUlhrnl4lUuPLMSFrzxMIg,3024
|
@@ -167,7 +167,7 @@ airbyte_cdk/sources/declarative/resolvers/http_components_resolver.py,sha256=Aio
|
|
167
167
|
airbyte_cdk/sources/declarative/retrievers/__init__.py,sha256=ix9m1dkR69DcXCXUKC5RK_ZZM7ojTLBQ4IkWQTfmfCk,456
|
168
168
|
airbyte_cdk/sources/declarative/retrievers/async_retriever.py,sha256=2oQn_vo7uJKp4pdMnsF5CG5Iwc9rkPeEOLoAm_9bcus,3222
|
169
169
|
airbyte_cdk/sources/declarative/retrievers/retriever.py,sha256=XPLs593Xv8c5cKMc37XzUAYmzlXd1a7eSsspM-CMuWA,1696
|
170
|
-
airbyte_cdk/sources/declarative/retrievers/simple_retriever.py,sha256=
|
170
|
+
airbyte_cdk/sources/declarative/retrievers/simple_retriever.py,sha256=uvsBqSUimi85YfSjPuOUoAlewwtvaYwgsLg2EDcswLE,24665
|
171
171
|
airbyte_cdk/sources/declarative/schema/__init__.py,sha256=xU45UvM5O4c1PSM13UHpCdh5hpW3HXy9vRRGEiAC1rg,795
|
172
172
|
airbyte_cdk/sources/declarative/schema/default_schema_loader.py,sha256=KTACrIE23a83wsm3Rd9Eb4K6-20lrGqYxTHNp9yxsso,1820
|
173
173
|
airbyte_cdk/sources/declarative/schema/dynamic_schema_loader.py,sha256=J8Q_iJYhcSQLWyt0bTZCbDAGpxt9G8FCc6Q9jtGsNzw,10703
|
@@ -249,7 +249,7 @@ airbyte_cdk/sources/message/repository.py,sha256=SG7avgti_-dj8FcRHTTrhgLLGJbElv1
|
|
249
249
|
airbyte_cdk/sources/source.py,sha256=KIBBH5VLEb8BZ8B9aROlfaI6OLoJqKDPMJ10jkAR7nk,3611
|
250
250
|
airbyte_cdk/sources/streams/__init__.py,sha256=8fzTKpRTnSx5PggXgQPKJzHNZUV2BCA40N-dI6JM1xI,256
|
251
251
|
airbyte_cdk/sources/streams/availability_strategy.py,sha256=_RU4JITrxMEN36g1RDHMu0iSw0I_3yWGfo5N8_YRvOg,3247
|
252
|
-
airbyte_cdk/sources/streams/call_rate.py,sha256=
|
252
|
+
airbyte_cdk/sources/streams/call_rate.py,sha256=WHMJ0-8xf0w_DiI0RkhPA8KEaPzaojNb23AEnKfcuS4,23939
|
253
253
|
airbyte_cdk/sources/streams/checkpoint/__init__.py,sha256=3oy7Hd4ivVWTZlN6dKAf4Fv_G7U5iZrvhO9hT871UIo,712
|
254
254
|
airbyte_cdk/sources/streams/checkpoint/checkpoint_reader.py,sha256=6HMT2NI-FQuaW0nt95NcyWrt5rZN4gF-Arx0sxdgbv4,15221
|
255
255
|
airbyte_cdk/sources/streams/checkpoint/cursor.py,sha256=3e-3c-54k8U7Awno7DMmAD9ndbnl9OM48EnbEgeDUO0,3499
|
@@ -342,7 +342,7 @@ airbyte_cdk/utils/datetime_format_inferrer.py,sha256=Ne2cpk7Tx3eZDEW2Q3O7jnNOY9g
|
|
342
342
|
airbyte_cdk/utils/datetime_helpers.py,sha256=8mqzZ67Or2PBp7tLtrhh6XFv4wFzYsjCL_DOQJRaftI,17751
|
343
343
|
airbyte_cdk/utils/event_timing.py,sha256=aiuFmPU80buLlNdKq4fDTEqqhEIelHPF6AalFGwY8as,2557
|
344
344
|
airbyte_cdk/utils/is_cloud_environment.py,sha256=DayV32Irh-SdnJ0MnjvstwCJ66_l5oEsd8l85rZtHoc,574
|
345
|
-
airbyte_cdk/utils/mapping_helpers.py,sha256=
|
345
|
+
airbyte_cdk/utils/mapping_helpers.py,sha256=4EOyUzNAGkq-M0QF5rPeBfT4v_eV7qBrEaAtsTH1k8Y,4309
|
346
346
|
airbyte_cdk/utils/message_utils.py,sha256=OTzbkwN7AdMDA3iKYq1LKwfPFxpyEDfdgEF9BED3dkU,1366
|
347
347
|
airbyte_cdk/utils/oneof_option_config.py,sha256=N8EmWdYdwt0FM7fuShh6H8nj_r4KEL9tb2DJJtwsPow,1180
|
348
348
|
airbyte_cdk/utils/print_buffer.py,sha256=PhMOi0C4Z91kWKrSvCQXcp8qRh1uCimpIdvrg6voZIA,2810
|
@@ -351,9 +351,9 @@ airbyte_cdk/utils/slice_hasher.py,sha256=EDxgROHDbfG-QKQb59m7h_7crN1tRiawdf5uU7G
|
|
351
351
|
airbyte_cdk/utils/spec_schema_transformations.py,sha256=-5HTuNsnDBAhj-oLeQXwpTGA0HdcjFOf2zTEMUTTg_Y,816
|
352
352
|
airbyte_cdk/utils/stream_status_utils.py,sha256=ZmBoiy5HVbUEHAMrUONxZvxnvfV9CesmQJLDTAIWnWw,1171
|
353
353
|
airbyte_cdk/utils/traced_exception.py,sha256=C8uIBuCL_E4WnBAOPSxBicD06JAldoN9fGsQDp463OY,6292
|
354
|
-
airbyte_cdk-6.33.
|
355
|
-
airbyte_cdk-6.33.
|
356
|
-
airbyte_cdk-6.33.
|
357
|
-
airbyte_cdk-6.33.
|
358
|
-
airbyte_cdk-6.33.
|
359
|
-
airbyte_cdk-6.33.
|
354
|
+
airbyte_cdk-6.33.1.dev0.dist-info/LICENSE.txt,sha256=Wfe61S4BaGPj404v8lrAbvhjYR68SHlkzeYrg3_bbuM,1051
|
355
|
+
airbyte_cdk-6.33.1.dev0.dist-info/LICENSE_SHORT,sha256=aqF6D1NcESmpn-cqsxBtszTEnHKnlsp8L4x9wAh3Nxg,55
|
356
|
+
airbyte_cdk-6.33.1.dev0.dist-info/METADATA,sha256=mQGuCOSysZqTcr0t3XkeCBinHgcB5qnbdctfA9INpaY,6015
|
357
|
+
airbyte_cdk-6.33.1.dev0.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
358
|
+
airbyte_cdk-6.33.1.dev0.dist-info/entry_points.txt,sha256=fj-e3PAQvsxsQzyyq8UkG1k8spunWnD4BAH2AwlR6NM,95
|
359
|
+
airbyte_cdk-6.33.1.dev0.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|