airbyte-cdk 6.23.0.dev1__py3-none-any.whl → 6.26.0.dev4103__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/oauth.py +68 -11
- airbyte_cdk/sources/declarative/declarative_component_schema.yaml +9 -2
- airbyte_cdk/sources/declarative/incremental/per_partition_cursor.py +35 -18
- airbyte_cdk/sources/declarative/models/declarative_component_schema.py +14 -4
- airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py +15 -3
- airbyte_cdk/sources/declarative/partition_routers/substream_partition_router.py +6 -2
- airbyte_cdk/sources/declarative/requesters/error_handlers/composite_error_handler.py +22 -0
- airbyte_cdk/sources/file_based/config/abstract_file_based_spec.py +15 -0
- airbyte_cdk/sources/file_based/config/identities_based_stream_config.py +8 -0
- airbyte_cdk/sources/file_based/config/permissions.py +34 -0
- airbyte_cdk/sources/file_based/file_based_source.py +65 -1
- airbyte_cdk/sources/file_based/file_based_stream_reader.py +33 -0
- airbyte_cdk/sources/file_based/schema_helpers.py +25 -0
- airbyte_cdk/sources/file_based/stream/__init__.py +2 -1
- airbyte_cdk/sources/file_based/stream/default_file_based_stream.py +29 -0
- airbyte_cdk/sources/file_based/stream/identities_stream.py +99 -0
- airbyte_cdk/sources/http_logger.py +1 -1
- airbyte_cdk/sources/streams/http/requests_native_auth/oauth.py +20 -20
- {airbyte_cdk-6.23.0.dev1.dist-info → airbyte_cdk-6.26.0.dev4103.dist-info}/METADATA +1 -1
- {airbyte_cdk-6.23.0.dev1.dist-info → airbyte_cdk-6.26.0.dev4103.dist-info}/RECORD +23 -20
- {airbyte_cdk-6.23.0.dev1.dist-info → airbyte_cdk-6.26.0.dev4103.dist-info}/LICENSE.txt +0 -0
- {airbyte_cdk-6.23.0.dev1.dist-info → airbyte_cdk-6.26.0.dev4103.dist-info}/WHEEL +0 -0
- {airbyte_cdk-6.23.0.dev1.dist-info → airbyte_cdk-6.26.0.dev4103.dist-info}/entry_points.txt +0 -0
@@ -3,11 +3,12 @@
|
|
3
3
|
#
|
4
4
|
|
5
5
|
from dataclasses import InitVar, dataclass, field
|
6
|
-
from typing import Any, List, Mapping, Optional, Union
|
6
|
+
from typing import Any, List, Mapping, MutableMapping, Optional, Union
|
7
7
|
|
8
8
|
import pendulum
|
9
9
|
|
10
10
|
from airbyte_cdk.sources.declarative.auth.declarative_authenticator import DeclarativeAuthenticator
|
11
|
+
from airbyte_cdk.sources.declarative.interpolation.interpolated_boolean import InterpolatedBoolean
|
11
12
|
from airbyte_cdk.sources.declarative.interpolation.interpolated_mapping import InterpolatedMapping
|
12
13
|
from airbyte_cdk.sources.declarative.interpolation.interpolated_string import InterpolatedString
|
13
14
|
from airbyte_cdk.sources.message import MessageRepository, NoopMessageRepository
|
@@ -44,10 +45,10 @@ class DeclarativeOauth2Authenticator(AbstractOauth2Authenticator, DeclarativeAut
|
|
44
45
|
message_repository (MessageRepository): the message repository used to emit logs on HTTP requests
|
45
46
|
"""
|
46
47
|
|
47
|
-
client_id: Union[InterpolatedString, str]
|
48
|
-
client_secret: Union[InterpolatedString, str]
|
49
48
|
config: Mapping[str, Any]
|
50
49
|
parameters: InitVar[Mapping[str, Any]]
|
50
|
+
client_id: Optional[Union[InterpolatedString, str]] = None
|
51
|
+
client_secret: Optional[Union[InterpolatedString, str]] = None
|
51
52
|
token_refresh_endpoint: Optional[Union[InterpolatedString, str]] = None
|
52
53
|
refresh_token: Optional[Union[InterpolatedString, str]] = None
|
53
54
|
scopes: Optional[List[str]] = None
|
@@ -66,6 +67,8 @@ class DeclarativeOauth2Authenticator(AbstractOauth2Authenticator, DeclarativeAut
|
|
66
67
|
grant_type_name: Union[InterpolatedString, str] = "grant_type"
|
67
68
|
grant_type: Union[InterpolatedString, str] = "refresh_token"
|
68
69
|
message_repository: MessageRepository = NoopMessageRepository()
|
70
|
+
profile_assertion: Optional[DeclarativeAuthenticator] = None
|
71
|
+
use_profile_assertion: Optional[Union[InterpolatedBoolean, str, bool]] = False
|
69
72
|
|
70
73
|
def __post_init__(self, parameters: Mapping[str, Any]) -> None:
|
71
74
|
super().__init__()
|
@@ -76,11 +79,19 @@ class DeclarativeOauth2Authenticator(AbstractOauth2Authenticator, DeclarativeAut
|
|
76
79
|
else:
|
77
80
|
self._token_refresh_endpoint = None
|
78
81
|
self._client_id_name = InterpolatedString.create(self.client_id_name, parameters=parameters)
|
79
|
-
self._client_id =
|
82
|
+
self._client_id = (
|
83
|
+
InterpolatedString.create(self.client_id, parameters=parameters)
|
84
|
+
if self.client_id
|
85
|
+
else self.client_id
|
86
|
+
)
|
80
87
|
self._client_secret_name = InterpolatedString.create(
|
81
88
|
self.client_secret_name, parameters=parameters
|
82
89
|
)
|
83
|
-
self._client_secret =
|
90
|
+
self._client_secret = (
|
91
|
+
InterpolatedString.create(self.client_secret, parameters=parameters)
|
92
|
+
if self.client_secret
|
93
|
+
else self.client_secret
|
94
|
+
)
|
84
95
|
self._refresh_token_name = InterpolatedString.create(
|
85
96
|
self.refresh_token_name, parameters=parameters
|
86
97
|
)
|
@@ -99,7 +110,12 @@ class DeclarativeOauth2Authenticator(AbstractOauth2Authenticator, DeclarativeAut
|
|
99
110
|
self.grant_type_name = InterpolatedString.create(
|
100
111
|
self.grant_type_name, parameters=parameters
|
101
112
|
)
|
102
|
-
self.grant_type = InterpolatedString.create(
|
113
|
+
self.grant_type = InterpolatedString.create(
|
114
|
+
"urn:ietf:params:oauth:grant-type:jwt-bearer"
|
115
|
+
if self.use_profile_assertion
|
116
|
+
else self.grant_type,
|
117
|
+
parameters=parameters,
|
118
|
+
)
|
103
119
|
self._refresh_request_body = InterpolatedMapping(
|
104
120
|
self.refresh_request_body or {}, parameters=parameters
|
105
121
|
)
|
@@ -115,6 +131,13 @@ class DeclarativeOauth2Authenticator(AbstractOauth2Authenticator, DeclarativeAut
|
|
115
131
|
if self.token_expiry_date
|
116
132
|
else pendulum.now().subtract(days=1) # type: ignore # substract does not have type hints
|
117
133
|
)
|
134
|
+
self.use_profile_assertion = (
|
135
|
+
InterpolatedBoolean(self.use_profile_assertion, parameters=parameters)
|
136
|
+
if isinstance(self.use_profile_assertion, str)
|
137
|
+
else self.use_profile_assertion
|
138
|
+
)
|
139
|
+
self.assertion_name = "assertion"
|
140
|
+
|
118
141
|
if self.access_token_value is not None:
|
119
142
|
self._access_token_value = InterpolatedString.create(
|
120
143
|
self.access_token_value, parameters=parameters
|
@@ -126,9 +149,20 @@ class DeclarativeOauth2Authenticator(AbstractOauth2Authenticator, DeclarativeAut
|
|
126
149
|
self._access_token_value if self.access_token_value else None
|
127
150
|
)
|
128
151
|
|
152
|
+
if not self.use_profile_assertion and any(
|
153
|
+
client_creds is None for client_creds in [self.client_id, self.client_secret]
|
154
|
+
):
|
155
|
+
raise ValueError(
|
156
|
+
"OAuthAuthenticator configuration error: Both 'client_id' and 'client_secret' are required for the "
|
157
|
+
"basic OAuth flow."
|
158
|
+
)
|
159
|
+
if self.profile_assertion is None and self.use_profile_assertion:
|
160
|
+
raise ValueError(
|
161
|
+
"OAuthAuthenticator configuration error: 'profile_assertion' is required when using the profile assertion flow."
|
162
|
+
)
|
129
163
|
if self.get_grant_type() == "refresh_token" and self._refresh_token is None:
|
130
164
|
raise ValueError(
|
131
|
-
"OAuthAuthenticator
|
165
|
+
"OAuthAuthenticator configuration error: A 'refresh_token' is required when the 'grant_type' is set to 'refresh_token'."
|
132
166
|
)
|
133
167
|
|
134
168
|
def get_token_refresh_endpoint(self) -> Optional[str]:
|
@@ -145,19 +179,21 @@ class DeclarativeOauth2Authenticator(AbstractOauth2Authenticator, DeclarativeAut
|
|
145
179
|
return self._client_id_name.eval(self.config) # type: ignore # eval returns a string in this context
|
146
180
|
|
147
181
|
def get_client_id(self) -> str:
|
148
|
-
client_id
|
182
|
+
client_id = self._client_id.eval(self.config) if self._client_id else self._client_id
|
149
183
|
if not client_id:
|
150
184
|
raise ValueError("OAuthAuthenticator was unable to evaluate client_id parameter")
|
151
|
-
return client_id
|
185
|
+
return client_id # type: ignore # value will be returned as a string, or an error will be raised
|
152
186
|
|
153
187
|
def get_client_secret_name(self) -> str:
|
154
188
|
return self._client_secret_name.eval(self.config) # type: ignore # eval returns a string in this context
|
155
189
|
|
156
190
|
def get_client_secret(self) -> str:
|
157
|
-
client_secret
|
191
|
+
client_secret = (
|
192
|
+
self._client_secret.eval(self.config) if self._client_secret else self._client_secret
|
193
|
+
)
|
158
194
|
if not client_secret:
|
159
195
|
raise ValueError("OAuthAuthenticator was unable to evaluate client_secret parameter")
|
160
|
-
return client_secret
|
196
|
+
return client_secret # type: ignore # value will be returned as a string, or an error will be raised
|
161
197
|
|
162
198
|
def get_refresh_token_name(self) -> str:
|
163
199
|
return self._refresh_token_name.eval(self.config) # type: ignore # eval returns a string in this context
|
@@ -192,6 +228,27 @@ class DeclarativeOauth2Authenticator(AbstractOauth2Authenticator, DeclarativeAut
|
|
192
228
|
def set_token_expiry_date(self, value: Union[str, int]) -> None:
|
193
229
|
self._token_expiry_date = self._parse_token_expiration_date(value)
|
194
230
|
|
231
|
+
def get_assertion_name(self) -> str:
|
232
|
+
return self.assertion_name
|
233
|
+
|
234
|
+
def get_assertion(self) -> str:
|
235
|
+
if self.profile_assertion is None:
|
236
|
+
raise ValueError("profile_assertion is not set")
|
237
|
+
return self.profile_assertion.token
|
238
|
+
|
239
|
+
def build_refresh_request_body(self) -> Mapping[str, Any]:
|
240
|
+
"""
|
241
|
+
Returns the request body to set on the refresh request
|
242
|
+
|
243
|
+
Override to define additional parameters
|
244
|
+
"""
|
245
|
+
if self.use_profile_assertion:
|
246
|
+
return {
|
247
|
+
self.get_grant_type_name(): self.get_grant_type(),
|
248
|
+
self.get_assertion_name(): self.get_assertion(),
|
249
|
+
}
|
250
|
+
return super().build_refresh_request_body()
|
251
|
+
|
195
252
|
@property
|
196
253
|
def access_token(self) -> str:
|
197
254
|
if self._access_token is None:
|
@@ -1081,8 +1081,6 @@ definitions:
|
|
1081
1081
|
type: object
|
1082
1082
|
required:
|
1083
1083
|
- type
|
1084
|
-
- client_id
|
1085
|
-
- client_secret
|
1086
1084
|
properties:
|
1087
1085
|
type:
|
1088
1086
|
type: string
|
@@ -1277,6 +1275,15 @@ definitions:
|
|
1277
1275
|
default: []
|
1278
1276
|
examples:
|
1279
1277
|
- ["invalid_grant", "invalid_permissions"]
|
1278
|
+
profile_assertion:
|
1279
|
+
title: Profile Assertion
|
1280
|
+
description: The authenticator being used to authenticate the client authenticator.
|
1281
|
+
"$ref": "#/definitions/JwtAuthenticator"
|
1282
|
+
use_profile_assertion:
|
1283
|
+
title: Use Profile Assertion
|
1284
|
+
description: Enable using profile assertion as a flow for OAuth authorization.
|
1285
|
+
type: boolean
|
1286
|
+
default: false
|
1280
1287
|
$parameters:
|
1281
1288
|
type: object
|
1282
1289
|
additionalProperties: true
|
@@ -222,6 +222,8 @@ class PerPartitionCursor(DeclarativeCursor):
|
|
222
222
|
next_page_token: Optional[Mapping[str, Any]] = None,
|
223
223
|
) -> Mapping[str, Any]:
|
224
224
|
if stream_slice:
|
225
|
+
if self._to_partition_key(stream_slice.partition) not in self._cursor_per_partition:
|
226
|
+
self._create_cursor_for_partition(self._to_partition_key(stream_slice.partition))
|
225
227
|
return self._partition_router.get_request_params( # type: ignore # this always returns a mapping
|
226
228
|
stream_state=stream_state,
|
227
229
|
stream_slice=StreamSlice(partition=stream_slice.partition, cursor_slice={}),
|
@@ -244,6 +246,8 @@ class PerPartitionCursor(DeclarativeCursor):
|
|
244
246
|
next_page_token: Optional[Mapping[str, Any]] = None,
|
245
247
|
) -> Mapping[str, Any]:
|
246
248
|
if stream_slice:
|
249
|
+
if self._to_partition_key(stream_slice.partition) not in self._cursor_per_partition:
|
250
|
+
self._create_cursor_for_partition(self._to_partition_key(stream_slice.partition))
|
247
251
|
return self._partition_router.get_request_headers( # type: ignore # this always returns a mapping
|
248
252
|
stream_state=stream_state,
|
249
253
|
stream_slice=StreamSlice(partition=stream_slice.partition, cursor_slice={}),
|
@@ -266,6 +270,8 @@ class PerPartitionCursor(DeclarativeCursor):
|
|
266
270
|
next_page_token: Optional[Mapping[str, Any]] = None,
|
267
271
|
) -> Union[Mapping[str, Any], str]:
|
268
272
|
if stream_slice:
|
273
|
+
if self._to_partition_key(stream_slice.partition) not in self._cursor_per_partition:
|
274
|
+
self._create_cursor_for_partition(self._to_partition_key(stream_slice.partition))
|
269
275
|
return self._partition_router.get_request_body_data( # type: ignore # this always returns a mapping
|
270
276
|
stream_state=stream_state,
|
271
277
|
stream_slice=StreamSlice(partition=stream_slice.partition, cursor_slice={}),
|
@@ -288,6 +294,8 @@ class PerPartitionCursor(DeclarativeCursor):
|
|
288
294
|
next_page_token: Optional[Mapping[str, Any]] = None,
|
289
295
|
) -> Mapping[str, Any]:
|
290
296
|
if stream_slice:
|
297
|
+
if self._to_partition_key(stream_slice.partition) not in self._cursor_per_partition:
|
298
|
+
self._create_cursor_for_partition(self._to_partition_key(stream_slice.partition))
|
291
299
|
return self._partition_router.get_request_body_json( # type: ignore # this always returns a mapping
|
292
300
|
stream_state=stream_state,
|
293
301
|
stream_slice=StreamSlice(partition=stream_slice.partition, cursor_slice={}),
|
@@ -303,21 +311,6 @@ class PerPartitionCursor(DeclarativeCursor):
|
|
303
311
|
raise ValueError("A partition needs to be provided in order to get request body json")
|
304
312
|
|
305
313
|
def should_be_synced(self, record: Record) -> bool:
|
306
|
-
if (
|
307
|
-
record.associated_slice
|
308
|
-
and self._to_partition_key(record.associated_slice.partition)
|
309
|
-
not in self._cursor_per_partition
|
310
|
-
):
|
311
|
-
partition_state = (
|
312
|
-
self._state_to_migrate_from
|
313
|
-
if self._state_to_migrate_from
|
314
|
-
else self._NO_CURSOR_STATE
|
315
|
-
)
|
316
|
-
cursor = self._create_cursor(partition_state)
|
317
|
-
|
318
|
-
self._cursor_per_partition[
|
319
|
-
self._to_partition_key(record.associated_slice.partition)
|
320
|
-
] = cursor
|
321
314
|
return self._get_cursor(record).should_be_synced(
|
322
315
|
self._convert_record_to_cursor_record(record)
|
323
316
|
)
|
@@ -356,8 +349,32 @@ class PerPartitionCursor(DeclarativeCursor):
|
|
356
349
|
)
|
357
350
|
partition_key = self._to_partition_key(record.associated_slice.partition)
|
358
351
|
if partition_key not in self._cursor_per_partition:
|
359
|
-
|
360
|
-
"Invalid state as stream slices that are emitted should refer to an existing cursor"
|
361
|
-
)
|
352
|
+
self._create_cursor_for_partition(partition_key)
|
362
353
|
cursor = self._cursor_per_partition[partition_key]
|
363
354
|
return cursor
|
355
|
+
|
356
|
+
def _create_cursor_for_partition(self, partition_key: str) -> None:
|
357
|
+
"""
|
358
|
+
Dynamically creates and initializes a cursor for the specified partition.
|
359
|
+
|
360
|
+
This method is required for `ConcurrentPerPartitionCursor`. For concurrent cursors,
|
361
|
+
stream_slices is executed only for the concurrent cursor, so cursors per partition
|
362
|
+
are not created for the declarative cursor. This method ensures that a cursor is available
|
363
|
+
to create requests for the specified partition. The cursor is initialized
|
364
|
+
with the per-partition state if present in the initial state, or with the global state
|
365
|
+
adjusted by the lookback window, or with the state to migrate from.
|
366
|
+
|
367
|
+
Note:
|
368
|
+
This is a temporary workaround and should be removed once the declarative cursor
|
369
|
+
is decoupled from the concurrent cursor implementation.
|
370
|
+
|
371
|
+
Args:
|
372
|
+
partition_key (str): The unique identifier for the partition for which the cursor
|
373
|
+
needs to be created.
|
374
|
+
"""
|
375
|
+
partition_state = (
|
376
|
+
self._state_to_migrate_from if self._state_to_migrate_from else self._NO_CURSOR_STATE
|
377
|
+
)
|
378
|
+
cursor = self._create_cursor(partition_state)
|
379
|
+
|
380
|
+
self._cursor_per_partition[partition_key] = cursor
|
@@ -506,8 +506,8 @@ class OAuthAuthenticator(BaseModel):
|
|
506
506
|
examples=["custom_app_id"],
|
507
507
|
title="Client ID Property Name",
|
508
508
|
)
|
509
|
-
client_id: str = Field(
|
510
|
-
|
509
|
+
client_id: Optional[str] = Field(
|
510
|
+
None,
|
511
511
|
description="The OAuth client ID. Fill it in the user inputs.",
|
512
512
|
examples=["{{ config['client_id }}", "{{ config['credentials']['client_id }}"],
|
513
513
|
title="Client ID",
|
@@ -518,8 +518,8 @@ class OAuthAuthenticator(BaseModel):
|
|
518
518
|
examples=["custom_app_secret"],
|
519
519
|
title="Client Secret Property Name",
|
520
520
|
)
|
521
|
-
client_secret: str = Field(
|
522
|
-
|
521
|
+
client_secret: Optional[str] = Field(
|
522
|
+
None,
|
523
523
|
description="The OAuth client secret. Fill it in the user inputs.",
|
524
524
|
examples=[
|
525
525
|
"{{ config['client_secret }}",
|
@@ -624,6 +624,16 @@ class OAuthAuthenticator(BaseModel):
|
|
624
624
|
description="When the token updater is defined, new refresh tokens, access tokens and the access token expiry date are written back from the authentication response to the config object. This is important if the refresh token can only used once.",
|
625
625
|
title="Token Updater",
|
626
626
|
)
|
627
|
+
profile_assertion: Optional[JwtAuthenticator] = Field(
|
628
|
+
None,
|
629
|
+
description="The authenticator being used to authenticate the client authenticator.",
|
630
|
+
title="Profile Assertion",
|
631
|
+
)
|
632
|
+
use_profile_assertion: Optional[bool] = Field(
|
633
|
+
False,
|
634
|
+
description="Enable using profile assertion as a flow for OAuth authorization.",
|
635
|
+
title="Use Profile Assertion",
|
636
|
+
)
|
627
637
|
parameters: Optional[Dict[str, Any]] = Field(None, alias="$parameters")
|
628
638
|
|
629
639
|
|
@@ -2100,6 +2100,12 @@ class ModelToComponentFactory:
|
|
2100
2100
|
def create_oauth_authenticator(
|
2101
2101
|
self, model: OAuthAuthenticatorModel, config: Config, **kwargs: Any
|
2102
2102
|
) -> DeclarativeOauth2Authenticator:
|
2103
|
+
profile_assertion = (
|
2104
|
+
self._create_component_from_model(model.profile_assertion, config=config)
|
2105
|
+
if model.profile_assertion
|
2106
|
+
else None
|
2107
|
+
)
|
2108
|
+
|
2103
2109
|
if model.refresh_token_updater:
|
2104
2110
|
# ignore type error because fixing it would have a lot of dependencies, revisit later
|
2105
2111
|
return DeclarativeSingleUseRefreshTokenOauth2Authenticator( # type: ignore
|
@@ -2120,13 +2126,17 @@ class ModelToComponentFactory:
|
|
2120
2126
|
).eval(config),
|
2121
2127
|
client_id=InterpolatedString.create(
|
2122
2128
|
model.client_id, parameters=model.parameters or {}
|
2123
|
-
).eval(config)
|
2129
|
+
).eval(config)
|
2130
|
+
if model.client_id
|
2131
|
+
else model.client_id,
|
2124
2132
|
client_secret_name=InterpolatedString.create(
|
2125
2133
|
model.client_secret_name or "client_secret", parameters=model.parameters or {}
|
2126
2134
|
).eval(config),
|
2127
2135
|
client_secret=InterpolatedString.create(
|
2128
2136
|
model.client_secret, parameters=model.parameters or {}
|
2129
|
-
).eval(config)
|
2137
|
+
).eval(config)
|
2138
|
+
if model.client_secret
|
2139
|
+
else model.client_secret,
|
2130
2140
|
access_token_config_path=model.refresh_token_updater.access_token_config_path,
|
2131
2141
|
refresh_token_config_path=model.refresh_token_updater.refresh_token_config_path,
|
2132
2142
|
token_expiry_date_config_path=model.refresh_token_updater.token_expiry_date_config_path,
|
@@ -2172,6 +2182,8 @@ class ModelToComponentFactory:
|
|
2172
2182
|
config=config,
|
2173
2183
|
parameters=model.parameters or {},
|
2174
2184
|
message_repository=self._message_repository,
|
2185
|
+
profile_assertion=profile_assertion,
|
2186
|
+
use_profile_assertion=model.use_profile_assertion,
|
2175
2187
|
)
|
2176
2188
|
|
2177
2189
|
def create_offset_increment(
|
@@ -2374,7 +2386,7 @@ class ModelToComponentFactory:
|
|
2374
2386
|
if (
|
2375
2387
|
not isinstance(stream_slicer, DatetimeBasedCursor)
|
2376
2388
|
or type(stream_slicer) is not DatetimeBasedCursor
|
2377
|
-
)
|
2389
|
+
):
|
2378
2390
|
# Many of the custom component implementations of DatetimeBasedCursor override get_request_params() (or other methods).
|
2379
2391
|
# Because we're decoupling RequestOptionsProvider from the Cursor, custom components will eventually need to reimplement
|
2380
2392
|
# their own RequestOptionsProvider. However, right now the existing StreamSlicer/Cursor still can act as the SimpleRetriever's
|
@@ -296,8 +296,12 @@ class SubstreamPartitionRouter(PartitionRouter):
|
|
296
296
|
|
297
297
|
if not parent_state and incremental_dependency:
|
298
298
|
# Attempt to retrieve child state
|
299
|
-
|
300
|
-
substream_state =
|
299
|
+
substream_state_values = list(stream_state.values())
|
300
|
+
substream_state = substream_state_values[0] if substream_state_values else {}
|
301
|
+
# Filter out per partition state. Because we pass the state to the parent stream in the format {cursor_field: substream_state}
|
302
|
+
if isinstance(substream_state, (list, dict)):
|
303
|
+
substream_state = {}
|
304
|
+
|
301
305
|
parent_state = {}
|
302
306
|
|
303
307
|
# Copy child state to parent streams with incremental dependencies
|
@@ -8,6 +8,7 @@ from typing import Any, List, Mapping, Optional, Union
|
|
8
8
|
import requests
|
9
9
|
|
10
10
|
from airbyte_cdk.sources.streams.http.error_handlers import ErrorHandler
|
11
|
+
from airbyte_cdk.sources.streams.http.error_handlers.backoff_strategy import BackoffStrategy
|
11
12
|
from airbyte_cdk.sources.streams.http.error_handlers.response_models import (
|
12
13
|
ErrorResolution,
|
13
14
|
ResponseAction,
|
@@ -77,3 +78,24 @@ class CompositeErrorHandler(ErrorHandler):
|
|
77
78
|
return matched_error_resolution
|
78
79
|
|
79
80
|
return create_fallback_error_resolution(response_or_exception)
|
81
|
+
|
82
|
+
@property
|
83
|
+
def backoff_strategies(self) -> Optional[List[BackoffStrategy]]:
|
84
|
+
"""
|
85
|
+
Combines backoff strategies from all child error handlers into a single flattened list.
|
86
|
+
|
87
|
+
When used with HttpRequester, note the following behavior:
|
88
|
+
- In HttpRequester.__post_init__, the entire list of backoff strategies is assigned to the error handler
|
89
|
+
- However, the error handler's backoff_time() method only ever uses the first non-None strategy in the list
|
90
|
+
- This means that if any backoff strategies are present, the first non-None strategy becomes the default
|
91
|
+
- This applies to both user-defined response filters and errors from DEFAULT_ERROR_MAPPING
|
92
|
+
- The list structure is not used to map different strategies to different error conditions
|
93
|
+
- Therefore, subsequent strategies in the list will not be used
|
94
|
+
|
95
|
+
Returns None if no handlers have strategies defined, which will result in HttpRequester using its default backoff strategy.
|
96
|
+
"""
|
97
|
+
all_strategies = []
|
98
|
+
for handler in self.error_handlers:
|
99
|
+
if hasattr(handler, "backoff_strategies") and handler.backoff_strategies:
|
100
|
+
all_strategies.extend(handler.backoff_strategies)
|
101
|
+
return all_strategies if all_strategies else None
|
@@ -11,6 +11,9 @@ from pydantic.v1 import AnyUrl, BaseModel, Field
|
|
11
11
|
|
12
12
|
from airbyte_cdk import OneOfOptionConfig
|
13
13
|
from airbyte_cdk.sources.file_based.config.file_based_stream_config import FileBasedStreamConfig
|
14
|
+
from airbyte_cdk.sources.file_based.config.identities_based_stream_config import (
|
15
|
+
IdentitiesStreamConfig,
|
16
|
+
)
|
14
17
|
from airbyte_cdk.sources.utils import schema_helpers
|
15
18
|
|
16
19
|
|
@@ -22,6 +25,18 @@ class DeliverRecords(BaseModel):
|
|
22
25
|
|
23
26
|
delivery_type: Literal["use_records_transfer"] = Field("use_records_transfer", const=True)
|
24
27
|
|
28
|
+
sync_acl_permissions: bool = Field(
|
29
|
+
title="Include ACL Permissions",
|
30
|
+
description="Joins Document allowlists to each stream.",
|
31
|
+
default=False,
|
32
|
+
airbyte_hidden=True,
|
33
|
+
)
|
34
|
+
identities: Optional[IdentitiesStreamConfig] = Field(
|
35
|
+
title="Identities configuration",
|
36
|
+
description="Configuration for identities",
|
37
|
+
airbyte_hidden=True,
|
38
|
+
)
|
39
|
+
|
25
40
|
|
26
41
|
class DeliverRawFiles(BaseModel):
|
27
42
|
class Config(OneOfOptionConfig):
|
@@ -0,0 +1,8 @@
|
|
1
|
+
from typing import Literal
|
2
|
+
|
3
|
+
from pydantic.v1 import BaseModel, Field
|
4
|
+
|
5
|
+
|
6
|
+
class IdentitiesStreamConfig(BaseModel):
|
7
|
+
name: Literal["identities"] = Field("identities", const=True, airbyte_hidden=True)
|
8
|
+
domain: str = Field(title="Domain", description="The domain of the identities.")
|
@@ -0,0 +1,34 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2024 Airbyte, Inc., all rights reserved.
|
3
|
+
#
|
4
|
+
|
5
|
+
import uuid
|
6
|
+
from datetime import datetime
|
7
|
+
from enum import Enum
|
8
|
+
|
9
|
+
from pydantic.v1 import BaseModel
|
10
|
+
|
11
|
+
|
12
|
+
class RemoteFileIdentityType(Enum):
|
13
|
+
USER = "user"
|
14
|
+
GROUP = "group"
|
15
|
+
|
16
|
+
|
17
|
+
class RemoteFileIdentity(BaseModel):
|
18
|
+
id: uuid.UUID
|
19
|
+
remote_id: str
|
20
|
+
parent_id: str | None = None
|
21
|
+
name: str | None = None
|
22
|
+
description: str | None = None
|
23
|
+
email_address: str | None = None
|
24
|
+
member_email_addresses: list[str] | None = None
|
25
|
+
type: RemoteFileIdentityType
|
26
|
+
modified_at: datetime
|
27
|
+
|
28
|
+
|
29
|
+
class RemoteFilePermissions(BaseModel):
|
30
|
+
id: str
|
31
|
+
file_path: str
|
32
|
+
allowed_identity_remote_ids: list[str] | None = None
|
33
|
+
denied_identity_remote_ids: list[str] | None = None
|
34
|
+
publicly_accessible: bool = False
|
@@ -33,6 +33,9 @@ from airbyte_cdk.sources.file_based.config.file_based_stream_config import (
|
|
33
33
|
FileBasedStreamConfig,
|
34
34
|
ValidationPolicy,
|
35
35
|
)
|
36
|
+
from airbyte_cdk.sources.file_based.config.identities_based_stream_config import (
|
37
|
+
IdentitiesStreamConfig,
|
38
|
+
)
|
36
39
|
from airbyte_cdk.sources.file_based.discovery_policy import (
|
37
40
|
AbstractDiscoveryPolicy,
|
38
41
|
DefaultDiscoveryPolicy,
|
@@ -49,7 +52,11 @@ from airbyte_cdk.sources.file_based.schema_validation_policies import (
|
|
49
52
|
DEFAULT_SCHEMA_VALIDATION_POLICIES,
|
50
53
|
AbstractSchemaValidationPolicy,
|
51
54
|
)
|
52
|
-
from airbyte_cdk.sources.file_based.stream import
|
55
|
+
from airbyte_cdk.sources.file_based.stream import (
|
56
|
+
AbstractFileBasedStream,
|
57
|
+
DefaultFileBasedStream,
|
58
|
+
IdentitiesStream,
|
59
|
+
)
|
53
60
|
from airbyte_cdk.sources.file_based.stream.concurrent.adapters import FileBasedStreamFacade
|
54
61
|
from airbyte_cdk.sources.file_based.stream.concurrent.cursor import (
|
55
62
|
AbstractConcurrentFileBasedCursor,
|
@@ -157,6 +164,9 @@ class FileBasedSource(ConcurrentSourceAdapter, ABC):
|
|
157
164
|
errors = []
|
158
165
|
tracebacks = []
|
159
166
|
for stream in streams:
|
167
|
+
if isinstance(stream, IdentitiesStream):
|
168
|
+
# Probably need to check identities endpoint/api access but will skip for now.
|
169
|
+
continue
|
160
170
|
if not isinstance(stream, AbstractFileBasedStream):
|
161
171
|
raise ValueError(f"Stream {stream} is not a file-based stream.")
|
162
172
|
try:
|
@@ -164,6 +174,7 @@ class FileBasedSource(ConcurrentSourceAdapter, ABC):
|
|
164
174
|
availability_method = (
|
165
175
|
stream.availability_strategy.check_availability
|
166
176
|
if self._use_file_transfer(parsed_config)
|
177
|
+
or self._sync_acl_permissions(parsed_config)
|
167
178
|
else stream.availability_strategy.check_availability_and_parsability
|
168
179
|
)
|
169
180
|
(
|
@@ -289,6 +300,13 @@ class FileBasedSource(ConcurrentSourceAdapter, ABC):
|
|
289
300
|
)
|
290
301
|
|
291
302
|
streams.append(stream)
|
303
|
+
|
304
|
+
identities_stream_config = self._get_identities_stream_config(parsed_config)
|
305
|
+
if identities_stream_config:
|
306
|
+
identities_stream = self._make_identities_stream(
|
307
|
+
stream_config=identities_stream_config
|
308
|
+
)
|
309
|
+
streams.append(identities_stream)
|
292
310
|
return streams
|
293
311
|
|
294
312
|
except ValidationError as exc:
|
@@ -312,6 +330,19 @@ class FileBasedSource(ConcurrentSourceAdapter, ABC):
|
|
312
330
|
cursor=cursor,
|
313
331
|
use_file_transfer=self._use_file_transfer(parsed_config),
|
314
332
|
preserve_directory_structure=self._preserve_directory_structure(parsed_config),
|
333
|
+
sync_acl_permissions=self._sync_acl_permissions(parsed_config),
|
334
|
+
)
|
335
|
+
|
336
|
+
def _make_identities_stream(
|
337
|
+
self,
|
338
|
+
stream_config: IdentitiesStreamConfig,
|
339
|
+
) -> Stream:
|
340
|
+
return IdentitiesStream(
|
341
|
+
config=stream_config,
|
342
|
+
catalog_schema=self.stream_schemas.get(stream_config.name),
|
343
|
+
stream_reader=self.stream_reader,
|
344
|
+
discovery_policy=self.discovery_policy,
|
345
|
+
errors_collector=self.errors_collector,
|
315
346
|
)
|
316
347
|
|
317
348
|
def _get_stream_from_catalog(
|
@@ -387,6 +418,14 @@ class FileBasedSource(ConcurrentSourceAdapter, ABC):
|
|
387
418
|
)
|
388
419
|
return use_file_transfer
|
389
420
|
|
421
|
+
@staticmethod
|
422
|
+
def _use_records_transfer(parsed_config: AbstractFileBasedSpec) -> bool:
|
423
|
+
use_records_transfer = (
|
424
|
+
hasattr(parsed_config.delivery_method, "delivery_type")
|
425
|
+
and parsed_config.delivery_method.delivery_type == "use_records_transfer"
|
426
|
+
)
|
427
|
+
return use_records_transfer
|
428
|
+
|
390
429
|
@staticmethod
|
391
430
|
def _preserve_directory_structure(parsed_config: AbstractFileBasedSpec) -> bool:
|
392
431
|
"""
|
@@ -408,3 +447,28 @@ class FileBasedSource(ConcurrentSourceAdapter, ABC):
|
|
408
447
|
):
|
409
448
|
return parsed_config.delivery_method.preserve_directory_structure
|
410
449
|
return True
|
450
|
+
|
451
|
+
@staticmethod
|
452
|
+
def _sync_acl_permissions(parsed_config: AbstractFileBasedSpec) -> bool:
|
453
|
+
if (
|
454
|
+
FileBasedSource._use_records_transfer(parsed_config)
|
455
|
+
and hasattr(parsed_config.delivery_method, "sync_acl_permissions")
|
456
|
+
and parsed_config.delivery_method.sync_acl_permissions is not None
|
457
|
+
):
|
458
|
+
return parsed_config.delivery_method.sync_acl_permissions
|
459
|
+
return False
|
460
|
+
|
461
|
+
@staticmethod
|
462
|
+
def _get_identities_stream_config(
|
463
|
+
parsed_config: AbstractFileBasedSpec,
|
464
|
+
) -> Optional[IdentitiesStreamConfig]:
|
465
|
+
identities_stream_config = None
|
466
|
+
if (
|
467
|
+
FileBasedSource._sync_acl_permissions(parsed_config)
|
468
|
+
and hasattr(parsed_config.delivery_method, "identities")
|
469
|
+
and parsed_config.delivery_method.identities is not None
|
470
|
+
and isinstance(parsed_config.delivery_method.identities, IdentitiesStreamConfig)
|
471
|
+
and parsed_config.delivery_method.identities.domain
|
472
|
+
):
|
473
|
+
identities_stream_config = parsed_config.delivery_method.identities
|
474
|
+
return identities_stream_config
|
@@ -135,6 +135,15 @@ class AbstractFileBasedStreamReader(ABC):
|
|
135
135
|
return use_file_transfer
|
136
136
|
return False
|
137
137
|
|
138
|
+
def use_records_transfer(self) -> bool:
|
139
|
+
if self.config:
|
140
|
+
use_records_transfer = (
|
141
|
+
hasattr(self.config.delivery_method, "delivery_type")
|
142
|
+
and self.config.delivery_method.delivery_type == "use_records_transfer"
|
143
|
+
)
|
144
|
+
return use_records_transfer
|
145
|
+
return False
|
146
|
+
|
138
147
|
def preserve_directory_structure(self) -> bool:
|
139
148
|
# fall back to preserve subdirectories if config is not present or incomplete
|
140
149
|
if (
|
@@ -146,6 +155,16 @@ class AbstractFileBasedStreamReader(ABC):
|
|
146
155
|
return self.config.delivery_method.preserve_directory_structure
|
147
156
|
return True
|
148
157
|
|
158
|
+
def sync_acl_permissions(self) -> bool:
|
159
|
+
if (
|
160
|
+
self.config
|
161
|
+
and self.use_records_transfer()
|
162
|
+
and hasattr(self.config.delivery_method, "sync_acl_permissions")
|
163
|
+
and self.config.delivery_method.sync_acl_permissions is not None
|
164
|
+
):
|
165
|
+
return self.config.delivery_method.sync_acl_permissions
|
166
|
+
return False
|
167
|
+
|
149
168
|
@abstractmethod
|
150
169
|
def get_file(
|
151
170
|
self, file: RemoteFile, local_directory: str, logger: logging.Logger
|
@@ -183,3 +202,17 @@ class AbstractFileBasedStreamReader(ABC):
|
|
183
202
|
makedirs(path.dirname(local_file_path), exist_ok=True)
|
184
203
|
absolute_file_path = path.abspath(local_file_path)
|
185
204
|
return [file_relative_path, local_file_path, absolute_file_path]
|
205
|
+
|
206
|
+
def get_file_acl_permissions(self, file: RemoteFile, logger: logging.Logger) -> Dict[str, Any]:
|
207
|
+
"""
|
208
|
+
This is required for connectors that will support syncing
|
209
|
+
ACL Permissions from files.
|
210
|
+
"""
|
211
|
+
return {}
|
212
|
+
|
213
|
+
def load_identity_groups(self, logger: logging.Logger) -> Iterable[Dict[str, Any]]:
|
214
|
+
"""
|
215
|
+
This is required for connectors that will support syncing
|
216
|
+
identities.
|
217
|
+
"""
|
218
|
+
yield {}
|
@@ -23,6 +23,31 @@ file_transfer_schema = {
|
|
23
23
|
"properties": {"data": {"type": "object"}, "file": {"type": "object"}},
|
24
24
|
}
|
25
25
|
|
26
|
+
remote_file_permissions_schema = {
|
27
|
+
"type": "object",
|
28
|
+
"properties": {
|
29
|
+
"id": {"type": "string"},
|
30
|
+
"file_path": {"type": "string"},
|
31
|
+
"allowed_identity_remote_ids": {"type": "array", "items": {"type": "string"}},
|
32
|
+
"publicly_accessible": {"type": "boolean"},
|
33
|
+
},
|
34
|
+
}
|
35
|
+
|
36
|
+
remote_file_identity_schema = {
|
37
|
+
"type": "object",
|
38
|
+
"properties": {
|
39
|
+
"id": {"type": "string"},
|
40
|
+
"remote_id": {"type": "string"},
|
41
|
+
"parent_id": {"type": ["null", "string"]},
|
42
|
+
"name": {"type": ["null", "string"]},
|
43
|
+
"description": {"type": ["null", "string"]},
|
44
|
+
"email_address": {"type": ["null", "string"]},
|
45
|
+
"member_email_addresses": {"type": ["null", "array"]},
|
46
|
+
"type": {"type": "string"},
|
47
|
+
"modified_at": {"type": "string"},
|
48
|
+
},
|
49
|
+
}
|
50
|
+
|
26
51
|
|
27
52
|
@total_ordering
|
28
53
|
class ComparableType(Enum):
|
@@ -1,4 +1,5 @@
|
|
1
1
|
from airbyte_cdk.sources.file_based.stream.abstract_file_based_stream import AbstractFileBasedStream
|
2
2
|
from airbyte_cdk.sources.file_based.stream.default_file_based_stream import DefaultFileBasedStream
|
3
|
+
from airbyte_cdk.sources.file_based.stream.identities_stream import IdentitiesStream
|
3
4
|
|
4
|
-
__all__ = ["AbstractFileBasedStream", "DefaultFileBasedStream"]
|
5
|
+
__all__ = ["AbstractFileBasedStream", "DefaultFileBasedStream", "IdentitiesStream"]
|
@@ -29,6 +29,7 @@ from airbyte_cdk.sources.file_based.schema_helpers import (
|
|
29
29
|
SchemaType,
|
30
30
|
file_transfer_schema,
|
31
31
|
merge_schemas,
|
32
|
+
remote_file_permissions_schema,
|
32
33
|
schemaless_schema,
|
33
34
|
)
|
34
35
|
from airbyte_cdk.sources.file_based.stream import AbstractFileBasedStream
|
@@ -47,6 +48,7 @@ class DefaultFileBasedStream(AbstractFileBasedStream, IncrementalMixin):
|
|
47
48
|
|
48
49
|
FILE_TRANSFER_KW = "use_file_transfer"
|
49
50
|
PRESERVE_DIRECTORY_STRUCTURE_KW = "preserve_directory_structure"
|
51
|
+
SYNC_ACL_PERMISSIONS_KW = "sync_acl_permissions"
|
50
52
|
FILES_KEY = "files"
|
51
53
|
DATE_TIME_FORMAT = "%Y-%m-%dT%H:%M:%S.%fZ"
|
52
54
|
ab_last_mod_col = "_ab_source_file_last_modified"
|
@@ -56,6 +58,7 @@ class DefaultFileBasedStream(AbstractFileBasedStream, IncrementalMixin):
|
|
56
58
|
airbyte_columns = [ab_last_mod_col, ab_file_name_col]
|
57
59
|
use_file_transfer = False
|
58
60
|
preserve_directory_structure = True
|
61
|
+
sync_acl_permissions = False
|
59
62
|
|
60
63
|
def __init__(self, **kwargs: Any):
|
61
64
|
if self.FILE_TRANSFER_KW in kwargs:
|
@@ -64,6 +67,8 @@ class DefaultFileBasedStream(AbstractFileBasedStream, IncrementalMixin):
|
|
64
67
|
self.preserve_directory_structure = kwargs.pop(
|
65
68
|
self.PRESERVE_DIRECTORY_STRUCTURE_KW, True
|
66
69
|
)
|
70
|
+
if self.SYNC_ACL_PERMISSIONS_KW in kwargs:
|
71
|
+
self.sync_acl_permissions = kwargs.pop(self.SYNC_ACL_PERMISSIONS_KW, False)
|
67
72
|
super().__init__(**kwargs)
|
68
73
|
|
69
74
|
@property
|
@@ -105,6 +110,8 @@ class DefaultFileBasedStream(AbstractFileBasedStream, IncrementalMixin):
|
|
105
110
|
self.ab_file_name_col: {"type": "string"},
|
106
111
|
},
|
107
112
|
}
|
113
|
+
elif self.sync_acl_permissions:
|
114
|
+
return remote_file_permissions_schema
|
108
115
|
else:
|
109
116
|
return super()._filter_schema_invalid_properties(configured_catalog_json_schema)
|
110
117
|
|
@@ -187,6 +194,26 @@ class DefaultFileBasedStream(AbstractFileBasedStream, IncrementalMixin):
|
|
187
194
|
yield stream_data_to_airbyte_message(
|
188
195
|
self.name, record, is_file_transfer_message=True
|
189
196
|
)
|
197
|
+
elif self.sync_acl_permissions:
|
198
|
+
try:
|
199
|
+
metadata_record = self.stream_reader.get_file_acl_permissions(
|
200
|
+
file, logger=self.logger
|
201
|
+
)
|
202
|
+
yield stream_data_to_airbyte_message(
|
203
|
+
self.name, metadata_record, is_file_transfer_message=False
|
204
|
+
)
|
205
|
+
except Exception as e:
|
206
|
+
self.logger.error(
|
207
|
+
f"Failed to retrieve metadata for file {file.uri}: {str(e)}"
|
208
|
+
)
|
209
|
+
yield AirbyteMessage(
|
210
|
+
type=MessageType.LOG,
|
211
|
+
log=AirbyteLogMessage(
|
212
|
+
level=Level.ERROR,
|
213
|
+
message=f"Error retrieving metadata: stream={self.name} file={file.uri}",
|
214
|
+
stack_trace=traceback.format_exc(),
|
215
|
+
),
|
216
|
+
)
|
190
217
|
else:
|
191
218
|
for record in parser.parse_records(
|
192
219
|
self.config, file, self.stream_reader, self.logger, schema
|
@@ -284,6 +311,8 @@ class DefaultFileBasedStream(AbstractFileBasedStream, IncrementalMixin):
|
|
284
311
|
def _get_raw_json_schema(self) -> JsonSchema:
|
285
312
|
if self.use_file_transfer:
|
286
313
|
return file_transfer_schema
|
314
|
+
elif self.sync_acl_permissions:
|
315
|
+
return remote_file_permissions_schema
|
287
316
|
elif self.config.input_schema:
|
288
317
|
return self.config.get_input_schema() # type: ignore
|
289
318
|
elif self.config.schemaless:
|
@@ -0,0 +1,99 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2024 Airbyte, Inc., all rights reserved.
|
3
|
+
#
|
4
|
+
|
5
|
+
import traceback
|
6
|
+
from functools import cache
|
7
|
+
from typing import Any, Iterable, List, Mapping, MutableMapping, Optional
|
8
|
+
|
9
|
+
from airbyte_protocol_dataclasses.models import SyncMode
|
10
|
+
|
11
|
+
from airbyte_cdk.models import AirbyteLogMessage, AirbyteMessage, Level
|
12
|
+
from airbyte_cdk.models import Type as MessageType
|
13
|
+
from airbyte_cdk.sources.file_based.config.file_based_stream_config import PrimaryKeyType
|
14
|
+
from airbyte_cdk.sources.file_based.config.identities_based_stream_config import (
|
15
|
+
IdentitiesStreamConfig,
|
16
|
+
)
|
17
|
+
from airbyte_cdk.sources.file_based.discovery_policy import AbstractDiscoveryPolicy
|
18
|
+
from airbyte_cdk.sources.file_based.exceptions import FileBasedErrorsCollector, FileBasedSourceError
|
19
|
+
from airbyte_cdk.sources.file_based.file_based_stream_reader import AbstractFileBasedStreamReader
|
20
|
+
from airbyte_cdk.sources.file_based.schema_helpers import remote_file_identity_schema
|
21
|
+
from airbyte_cdk.sources.file_based.types import StreamSlice
|
22
|
+
from airbyte_cdk.sources.streams import Stream
|
23
|
+
from airbyte_cdk.sources.streams.checkpoint import Cursor
|
24
|
+
from airbyte_cdk.sources.streams.core import JsonSchema
|
25
|
+
from airbyte_cdk.sources.utils.record_helper import stream_data_to_airbyte_message
|
26
|
+
from airbyte_cdk.utils.traced_exception import AirbyteTracedException
|
27
|
+
|
28
|
+
|
29
|
+
class IdentitiesStream(Stream):
|
30
|
+
"""
|
31
|
+
The identities stream. A full refresh stream to sync identities from a certain domain.
|
32
|
+
The stream reader manage the logic to get such data, which is implemented on connector side.
|
33
|
+
"""
|
34
|
+
|
35
|
+
is_resumable = False
|
36
|
+
|
37
|
+
def __init__(
|
38
|
+
self,
|
39
|
+
config: IdentitiesStreamConfig,
|
40
|
+
catalog_schema: Optional[Mapping[str, Any]],
|
41
|
+
stream_reader: AbstractFileBasedStreamReader,
|
42
|
+
discovery_policy: AbstractDiscoveryPolicy,
|
43
|
+
errors_collector: FileBasedErrorsCollector,
|
44
|
+
):
|
45
|
+
super().__init__()
|
46
|
+
self.config = config
|
47
|
+
self.catalog_schema = catalog_schema
|
48
|
+
self.stream_reader = stream_reader
|
49
|
+
self._discovery_policy = discovery_policy
|
50
|
+
self.errors_collector = errors_collector
|
51
|
+
self._cursor: MutableMapping[str, Any] = {}
|
52
|
+
|
53
|
+
@property
|
54
|
+
def state(self) -> MutableMapping[str, Any]:
|
55
|
+
return self._cursor
|
56
|
+
|
57
|
+
@state.setter
|
58
|
+
def state(self, value: MutableMapping[str, Any]) -> None:
|
59
|
+
"""State setter, accept state serialized by state getter."""
|
60
|
+
self._cursor = value
|
61
|
+
|
62
|
+
@property
|
63
|
+
def primary_key(self) -> PrimaryKeyType:
|
64
|
+
return None
|
65
|
+
|
66
|
+
def read_records(
|
67
|
+
self,
|
68
|
+
sync_mode: SyncMode,
|
69
|
+
cursor_field: Optional[List[str]] = None,
|
70
|
+
stream_slice: Optional[StreamSlice] = None,
|
71
|
+
stream_state: Optional[Mapping[str, Any]] = None,
|
72
|
+
) -> Iterable[Mapping[str, Any] | AirbyteMessage]:
|
73
|
+
try:
|
74
|
+
identity_groups = self.stream_reader.load_identity_groups(logger=self.logger)
|
75
|
+
for record in identity_groups:
|
76
|
+
yield stream_data_to_airbyte_message(self.name, record)
|
77
|
+
except AirbyteTracedException as exc:
|
78
|
+
# Re-raise the exception to stop the whole sync immediately as this is a fatal error
|
79
|
+
raise exc
|
80
|
+
except Exception:
|
81
|
+
yield AirbyteMessage(
|
82
|
+
type=MessageType.LOG,
|
83
|
+
log=AirbyteLogMessage(
|
84
|
+
level=Level.ERROR,
|
85
|
+
message=f"{FileBasedSourceError.ERROR_PARSING_RECORD.value} stream={self.name}",
|
86
|
+
stack_trace=traceback.format_exc(),
|
87
|
+
),
|
88
|
+
)
|
89
|
+
|
90
|
+
@cache
|
91
|
+
def get_json_schema(self) -> JsonSchema:
|
92
|
+
return remote_file_identity_schema
|
93
|
+
|
94
|
+
@property
|
95
|
+
def name(self) -> str:
|
96
|
+
return self.config.name
|
97
|
+
|
98
|
+
def get_cursor(self) -> Optional[Cursor]:
|
99
|
+
return None
|
@@ -45,7 +45,7 @@ def format_http_message(
|
|
45
45
|
log_message["http"]["is_auxiliary"] = is_auxiliary # type: ignore [index]
|
46
46
|
if stream_name:
|
47
47
|
log_message["airbyte_cdk"] = {"stream": {"name": stream_name}}
|
48
|
-
return log_message # type: ignore
|
48
|
+
return log_message # type: ignore[return-value] # got "dict[str, object]", expected "dict[str, JsonType]"
|
49
49
|
|
50
50
|
|
51
51
|
def _normalize_body_string(body_str: Optional[Union[str, bytes]]) -> Optional[str]:
|
@@ -95,16 +95,16 @@ class Oauth2Authenticator(AbstractOauth2Authenticator):
|
|
95
95
|
return self._access_token_name
|
96
96
|
|
97
97
|
def get_scopes(self) -> list[str]:
|
98
|
-
return self._scopes # type: ignore
|
98
|
+
return self._scopes # type: ignore[return-value]
|
99
99
|
|
100
100
|
def get_expires_in_name(self) -> str:
|
101
101
|
return self._expires_in_name
|
102
102
|
|
103
103
|
def get_refresh_request_body(self) -> Mapping[str, Any]:
|
104
|
-
return self._refresh_request_body # type: ignore
|
104
|
+
return self._refresh_request_body # type: ignore[return-value]
|
105
105
|
|
106
106
|
def get_refresh_request_headers(self) -> Mapping[str, Any]:
|
107
|
-
return self._refresh_request_headers # type: ignore
|
107
|
+
return self._refresh_request_headers # type: ignore[return-value]
|
108
108
|
|
109
109
|
def get_grant_type_name(self) -> str:
|
110
110
|
return self._grant_type_name
|
@@ -128,11 +128,11 @@ class Oauth2Authenticator(AbstractOauth2Authenticator):
|
|
128
128
|
|
129
129
|
@property
|
130
130
|
def access_token(self) -> str:
|
131
|
-
return self._access_token # type: ignore
|
131
|
+
return self._access_token # type: ignore[return-value]
|
132
132
|
|
133
133
|
@access_token.setter
|
134
134
|
def access_token(self, value: str) -> None:
|
135
|
-
self._access_token = value # type: ignore
|
135
|
+
self._access_token = value # type: ignore[assignment] # Incorrect type for assignment
|
136
136
|
|
137
137
|
|
138
138
|
class SingleUseRefreshTokenOauth2Authenticator(Oauth2Authenticator):
|
@@ -192,15 +192,15 @@ class SingleUseRefreshTokenOauth2Authenticator(Oauth2Authenticator):
|
|
192
192
|
message_repository (MessageRepository): the message repository used to emit logs on HTTP requests and control message on config update
|
193
193
|
"""
|
194
194
|
self._client_id = (
|
195
|
-
client_id # type: ignore
|
195
|
+
client_id # type: ignore[assignment] # Incorrect type for assignment
|
196
196
|
if client_id is not None
|
197
|
-
else dpath.get(connector_config, ("credentials", "client_id")) # type: ignore
|
197
|
+
else dpath.get(connector_config, ("credentials", "client_id")) # type: ignore[arg-type]
|
198
198
|
)
|
199
199
|
self._client_secret = (
|
200
|
-
client_secret # type: ignore
|
200
|
+
client_secret # type: ignore[assignment] # Incorrect type for assignment
|
201
201
|
if client_secret is not None
|
202
202
|
else dpath.get(
|
203
|
-
connector_config, # type: ignore
|
203
|
+
connector_config, # type: ignore[arg-type]
|
204
204
|
("credentials", "client_secret"),
|
205
205
|
)
|
206
206
|
)
|
@@ -248,8 +248,8 @@ class SingleUseRefreshTokenOauth2Authenticator(Oauth2Authenticator):
|
|
248
248
|
|
249
249
|
@property
|
250
250
|
def access_token(self) -> str:
|
251
|
-
return dpath.get( # type: ignore
|
252
|
-
self._connector_config, # type: ignore
|
251
|
+
return dpath.get( # type: ignore[return-value]
|
252
|
+
self._connector_config, # type: ignore[arg-type]
|
253
253
|
self._access_token_config_path,
|
254
254
|
default="",
|
255
255
|
)
|
@@ -257,39 +257,39 @@ class SingleUseRefreshTokenOauth2Authenticator(Oauth2Authenticator):
|
|
257
257
|
@access_token.setter
|
258
258
|
def access_token(self, new_access_token: str) -> None:
|
259
259
|
dpath.new(
|
260
|
-
self._connector_config, # type: ignore
|
260
|
+
self._connector_config, # type: ignore[arg-type]
|
261
261
|
self._access_token_config_path,
|
262
262
|
new_access_token,
|
263
263
|
)
|
264
264
|
|
265
265
|
def get_refresh_token(self) -> str:
|
266
|
-
return dpath.get( # type: ignore
|
267
|
-
self._connector_config, # type: ignore
|
266
|
+
return dpath.get( # type: ignore[return-value]
|
267
|
+
self._connector_config, # type: ignore[arg-type]
|
268
268
|
self._refresh_token_config_path,
|
269
269
|
default="",
|
270
270
|
)
|
271
271
|
|
272
272
|
def set_refresh_token(self, new_refresh_token: str) -> None:
|
273
273
|
dpath.new(
|
274
|
-
self._connector_config, # type: ignore
|
274
|
+
self._connector_config, # type: ignore[arg-type]
|
275
275
|
self._refresh_token_config_path,
|
276
276
|
new_refresh_token,
|
277
277
|
)
|
278
278
|
|
279
279
|
def get_token_expiry_date(self) -> pendulum.DateTime:
|
280
280
|
expiry_date = dpath.get(
|
281
|
-
self._connector_config, # type: ignore
|
281
|
+
self._connector_config, # type: ignore[arg-type]
|
282
282
|
self._token_expiry_date_config_path,
|
283
283
|
default="",
|
284
284
|
)
|
285
|
-
return pendulum.now().subtract(days=1) if expiry_date == "" else pendulum.parse(expiry_date) # type: ignore
|
285
|
+
return pendulum.now().subtract(days=1) if expiry_date == "" else pendulum.parse(expiry_date) # type: ignore[arg-type, return-value, no-untyped-call]
|
286
286
|
|
287
287
|
def set_token_expiry_date( # type: ignore[override]
|
288
288
|
self,
|
289
289
|
new_token_expiry_date: pendulum.DateTime,
|
290
290
|
) -> None:
|
291
291
|
dpath.new(
|
292
|
-
self._connector_config, # type: ignore
|
292
|
+
self._connector_config, # type: ignore[arg-type]
|
293
293
|
self._token_expiry_date_config_path,
|
294
294
|
str(new_token_expiry_date),
|
295
295
|
)
|
@@ -329,10 +329,10 @@ class SingleUseRefreshTokenOauth2Authenticator(Oauth2Authenticator):
|
|
329
329
|
# message directly in the console, this is needed
|
330
330
|
if not isinstance(self._message_repository, NoopMessageRepository):
|
331
331
|
self._message_repository.emit_message(
|
332
|
-
create_connector_config_control_message(self._connector_config) # type: ignore
|
332
|
+
create_connector_config_control_message(self._connector_config) # type: ignore[arg-type]
|
333
333
|
)
|
334
334
|
else:
|
335
|
-
emit_configuration_as_airbyte_control_message(self._connector_config) # type: ignore
|
335
|
+
emit_configuration_as_airbyte_control_message(self._connector_config) # type: ignore[arg-type]
|
336
336
|
return self.access_token
|
337
337
|
|
338
338
|
def refresh_access_token( # type: ignore[override] # Signature doesn't match base class
|
@@ -53,7 +53,7 @@ airbyte_cdk/sources/declarative/async_job/timer.py,sha256=Fb8P72CQ7jIzJyzMSSNuBf
|
|
53
53
|
airbyte_cdk/sources/declarative/auth/__init__.py,sha256=e2CRrcBWGhz3sQu3Oh34d1riEIwXipGS8hrSB1pu0Oo,284
|
54
54
|
airbyte_cdk/sources/declarative/auth/declarative_authenticator.py,sha256=nf-OmRUHYG4ORBwyb5CANzuHEssE-oNmL-Lccn41Td8,1099
|
55
55
|
airbyte_cdk/sources/declarative/auth/jwt.py,sha256=7r5q1zOekjw8kEmEk1oUyovzVt3cbD6BuFnRILeLZi8,8250
|
56
|
-
airbyte_cdk/sources/declarative/auth/oauth.py,sha256=
|
56
|
+
airbyte_cdk/sources/declarative/auth/oauth.py,sha256=EoSPxwe40A6VT5K4N7n7TnrGr7wQD4eMltfOBVeuMMQ,13506
|
57
57
|
airbyte_cdk/sources/declarative/auth/selective_authenticator.py,sha256=qGwC6YsCldr1bIeKG6Qo-A9a5cTdHw-vcOn3OtQrS4c,1540
|
58
58
|
airbyte_cdk/sources/declarative/auth/token.py,sha256=r4u3WXyVa7WmiSZ9-eZXlrUI-pS0D4YWJnwjLzwV-Fk,11210
|
59
59
|
airbyte_cdk/sources/declarative/auth/token_provider.py,sha256=9oq3dcBPAPwXSfkISjhA05dMhIzxaDQTmwOydBrnsMk,3028
|
@@ -67,7 +67,7 @@ airbyte_cdk/sources/declarative/concurrent_declarative_source.py,sha256=wbfk5udu
|
|
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=40Ts1-r0UnF3AhAj9pXE2pf6Y8WBqRAksjTaBiCuxq0,139243
|
71
71
|
airbyte_cdk/sources/declarative/declarative_source.py,sha256=nF7wBqFd3AQmEKAm4CnIo29CJoQL562cJGSCeL8U8bA,1531
|
72
72
|
airbyte_cdk/sources/declarative/declarative_stream.py,sha256=JRyNeOIpsFu4ztVZsN6sncqUEIqIE-bUkD2TPgbMgk0,10375
|
73
73
|
airbyte_cdk/sources/declarative/decoders/__init__.py,sha256=KSpQetKGqPCv-38QgcVJ5kzM5nzbFldTSsYDCS3Xf0Y,1035
|
@@ -92,7 +92,7 @@ airbyte_cdk/sources/declarative/incremental/concurrent_partition_cursor.py,sha25
|
|
92
92
|
airbyte_cdk/sources/declarative/incremental/datetime_based_cursor.py,sha256=_UzUnSIUsDbRgbFTXgSyZEFb4ws-KdhdQPWO8mFbV7U,22028
|
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
|
-
airbyte_cdk/sources/declarative/incremental/per_partition_cursor.py,sha256=
|
95
|
+
airbyte_cdk/sources/declarative/incremental/per_partition_cursor.py,sha256=9IAJTCiRUXvhFFz-IhZtYh_KfAjLHqthsYf2jErQRls,17728
|
96
96
|
airbyte_cdk/sources/declarative/incremental/per_partition_with_global.py,sha256=2YBOA2NnwAeIKlIhSwUB_W-FaGnPcmrG_liY7b4mV2Y,8365
|
97
97
|
airbyte_cdk/sources/declarative/incremental/resumable_full_refresh_cursor.py,sha256=10LFv1QPM-agVKl6eaANmEBOfd7gZgBrkoTcMggsieQ,4809
|
98
98
|
airbyte_cdk/sources/declarative/interpolation/__init__.py,sha256=tjUJkn3B-iZ-p7RP2c3dVZejrGiQeooGmS5ibWTuUL4,437
|
@@ -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=9SHqGpoMDIysyFLzkZoAehbsroHQKYPctIwXmSqO4Zw,97888
|
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=a96vNwEc3J8S99KGtSt2G147leh8GADfkTrejVCBXzs,122064
|
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=n82J15S8bjeMZ5uROu--P3hnbQoxkY5v7RPHYx7g7ro,2929
|
121
121
|
airbyte_cdk/sources/declarative/partition_routers/cartesian_product_stream_slicer.py,sha256=c5cuVFM6NFkuQqG8Z5IwkBuwDrvXZN1CunUOM_L0ezg,6892
|
122
122
|
airbyte_cdk/sources/declarative/partition_routers/list_partition_router.py,sha256=t7pRdFWfFWJtQQG19c9PVeMODyO2BknRTakpM5U9N-8,4844
|
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=pPb0blnNx598bk47Khgs0cvwhN02SWSmg7lnJKb9K6Q,15577
|
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
|
@@ -133,7 +133,7 @@ airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/hea
|
|
133
133
|
airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/wait_time_from_header_backoff_strategy.py,sha256=I3KYCJHhPiRfxYUzOa293YH4U3wGFISDsdY1OMHWRtw,2942
|
134
134
|
airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/wait_until_time_from_header_backoff_strategy.py,sha256=T2JTIdHdPzPiW0MpkCNYPsuaHUtF9V-ijNqUqdTDl6U,3069
|
135
135
|
airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategy.py,sha256=ZN5kcaVAQDinX0Ld5NXA8M_7Sax5BoPsknVwH7v06as,634
|
136
|
-
airbyte_cdk/sources/declarative/requesters/error_handlers/composite_error_handler.py,sha256=
|
136
|
+
airbyte_cdk/sources/declarative/requesters/error_handlers/composite_error_handler.py,sha256=4_PegbHBUiNbqa5ndZ2n9rm69O2iEfWU-NcIhSXZDIs,4137
|
137
137
|
airbyte_cdk/sources/declarative/requesters/error_handlers/default_error_handler.py,sha256=BGED9TcbA3mlvd9D7sog_u5AiyjWGVOUq_00aK3PNzg,5111
|
138
138
|
airbyte_cdk/sources/declarative/requesters/error_handlers/default_http_response_filter.py,sha256=q0YkeYUUWO6iErUy0vjqiOkhg8_9d5YcCmtlpXAJJ9E,1314
|
139
139
|
airbyte_cdk/sources/declarative/requesters/error_handlers/error_handler.py,sha256=Tan66odx8VHzfdyyXMQkXz2pJYksllGqvxmpoajgcK4,669
|
@@ -201,20 +201,22 @@ airbyte_cdk/sources/file_based/availability_strategy/__init__.py,sha256=ddKQfUmk
|
|
201
201
|
airbyte_cdk/sources/file_based/availability_strategy/abstract_file_based_availability_strategy.py,sha256=01Nd4b7ERAbp-OZo_8rrAzFXWPTMwr02SnWiN17nx8Q,2363
|
202
202
|
airbyte_cdk/sources/file_based/availability_strategy/default_file_based_availability_strategy.py,sha256=j9T5TimfWFUz7nqsaj-83G3xWmDpsmeSbDnaUNmz0UM,5849
|
203
203
|
airbyte_cdk/sources/file_based/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
204
|
-
airbyte_cdk/sources/file_based/config/abstract_file_based_spec.py,sha256=
|
204
|
+
airbyte_cdk/sources/file_based/config/abstract_file_based_spec.py,sha256=891uAzErUPQIPqr9mdzNBdU5aXGHCFCH4X64puVzQvI,7402
|
205
205
|
airbyte_cdk/sources/file_based/config/avro_format.py,sha256=NxTF96ewzn6HuhgodsY7Rpb-ybr1ZEWW5d4Vid64g5A,716
|
206
206
|
airbyte_cdk/sources/file_based/config/csv_format.py,sha256=NWekkyT8dTwiVK0mwa_krQD4FJPHSDfILo8kPAg3-Vs,8006
|
207
207
|
airbyte_cdk/sources/file_based/config/excel_format.py,sha256=9qAmTsT6SoVzNfNv0oBVkVCmiyqQuVAbfRKajjoa7Js,378
|
208
208
|
airbyte_cdk/sources/file_based/config/file_based_stream_config.py,sha256=rkTuHpz9G8o2YEnCkOZJM2vJZt_hEE4zklHivRfx43s,4647
|
209
|
+
airbyte_cdk/sources/file_based/config/identities_based_stream_config.py,sha256=VNlY8otsCLT8nvB6drT9CO1UqDGaRqP0VeQpdCJApw8,284
|
209
210
|
airbyte_cdk/sources/file_based/config/jsonl_format.py,sha256=cxtpz4t9_ERQyj_1Bx4DjOxuYLykWt0B02S4dWW5BgM,378
|
210
211
|
airbyte_cdk/sources/file_based/config/parquet_format.py,sha256=XOp-7nmm_WcbGI8SjKH2fs3Mkf1H4RAOYSWeUFYAz3w,741
|
212
|
+
airbyte_cdk/sources/file_based/config/permissions.py,sha256=CmXKilhNQOfm4NFlXVBFF2pz3hIUrt3JFp5bPVerE_8,781
|
211
213
|
airbyte_cdk/sources/file_based/config/unstructured_format.py,sha256=tIbB9Pn1HqU67ju7hEZ9dBstRrb2eojUNMsdckzbj58,3565
|
212
214
|
airbyte_cdk/sources/file_based/discovery_policy/__init__.py,sha256=gl3ey6mZbyfraB9P3pFhf9UJp2JeTZ1SUFAopy2iBvY,301
|
213
215
|
airbyte_cdk/sources/file_based/discovery_policy/abstract_discovery_policy.py,sha256=dCfXX529Rd5rtopg4VeEgTPJjFtqjtjzPq6LCw18Wt0,605
|
214
216
|
airbyte_cdk/sources/file_based/discovery_policy/default_discovery_policy.py,sha256=-xujTidtrq6HC00WKbjQh1CZdT5LMuzkp5BLjqDmfTY,1007
|
215
217
|
airbyte_cdk/sources/file_based/exceptions.py,sha256=WP0qkG6fpWoBpOyyicgp5YNE393VWyegq5qSy0v4QtM,7362
|
216
|
-
airbyte_cdk/sources/file_based/file_based_source.py,sha256=
|
217
|
-
airbyte_cdk/sources/file_based/file_based_stream_reader.py,sha256=
|
218
|
+
airbyte_cdk/sources/file_based/file_based_source.py,sha256=EQSua5n3OihEt95ROCmdv7pVLCZfdNn4jxJKyLvhMR0,20267
|
219
|
+
airbyte_cdk/sources/file_based/file_based_stream_reader.py,sha256=5xcCnTd_zaMVBL1Xp215JAiyNQBf4wAs_WwVgcLi2l4,8036
|
218
220
|
airbyte_cdk/sources/file_based/file_types/__init__.py,sha256=blCLn0-2LC-ZdgcNyDEhqM2RiUvEjEBh-G4-t32ZtuM,1268
|
219
221
|
airbyte_cdk/sources/file_based/file_types/avro_parser.py,sha256=XNx-JC-sgzH9u3nOJ2M59FxBXvtig8LN6BIkeDOavZA,10858
|
220
222
|
airbyte_cdk/sources/file_based/file_types/csv_parser.py,sha256=QlCXB-ry3np67Q_VerQEPoWDOTcPTB6Go4ydZxY9ae4,20445
|
@@ -225,11 +227,11 @@ airbyte_cdk/sources/file_based/file_types/jsonl_parser.py,sha256=GwyNyxmST4RX-Xp
|
|
225
227
|
airbyte_cdk/sources/file_based/file_types/parquet_parser.py,sha256=XenFg5sJ-UBnIkSmsiNJRou11NO0zZXx-RXgPHMT2NA,10487
|
226
228
|
airbyte_cdk/sources/file_based/file_types/unstructured_parser.py,sha256=2TYOQl62FQPCa8otLbkDIk_j01EP3oWaKSfXGhCjCHg,19492
|
227
229
|
airbyte_cdk/sources/file_based/remote_file.py,sha256=yqRz93vPe8PBXLIMJ5W5u2JRlZRhg6sBrAjn3pPjJ8A,315
|
228
|
-
airbyte_cdk/sources/file_based/schema_helpers.py,sha256=
|
230
|
+
airbyte_cdk/sources/file_based/schema_helpers.py,sha256=GJJgJPDtd0MjcB5Qt2ulqBrUT-A308loQOvoHvynUTk,10408
|
229
231
|
airbyte_cdk/sources/file_based/schema_validation_policies/__init__.py,sha256=FkByIyEy56x2_awYnxGPqGaOp7zAzpAoRkPZHKySI9M,536
|
230
232
|
airbyte_cdk/sources/file_based/schema_validation_policies/abstract_schema_validation_policy.py,sha256=kjvX7nOmUALYd7HuZHilUzgJPZ-MnZ08mtvuBnt2tQ0,618
|
231
233
|
airbyte_cdk/sources/file_based/schema_validation_policies/default_schema_validation_policies.py,sha256=vjTlmYT_nqzY3DbT5xem7X-bwgA9RyXHoKFqiMO2URk,1728
|
232
|
-
airbyte_cdk/sources/file_based/stream/__init__.py,sha256=
|
234
|
+
airbyte_cdk/sources/file_based/stream/__init__.py,sha256=Ib2D59Ehj3PKCfFvn_pamg_aMC1IN-XcYpWCfTw0oxg,370
|
233
235
|
airbyte_cdk/sources/file_based/stream/abstract_file_based_stream.py,sha256=9pQh3BHYcxm8CRC8XawfmBxL8O9HggpWwCCbX_ncINE,7509
|
234
236
|
airbyte_cdk/sources/file_based/stream/concurrent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
235
237
|
airbyte_cdk/sources/file_based/stream/concurrent/adapters.py,sha256=WZ5q2uovgohauJgwfxq_LFeZ92WMZd0LoH6c5QQURPo,13931
|
@@ -240,10 +242,11 @@ airbyte_cdk/sources/file_based/stream/concurrent/cursor/file_based_final_state_c
|
|
240
242
|
airbyte_cdk/sources/file_based/stream/cursor/__init__.py,sha256=MhFB5hOo8sjwvCh8gangaymdg3EJWYt_72brFOZt068,191
|
241
243
|
airbyte_cdk/sources/file_based/stream/cursor/abstract_file_based_cursor.py,sha256=om-x3gZFPgWDpi15S9RxZmR36VHnk8sytgN6LlBQhAw,1934
|
242
244
|
airbyte_cdk/sources/file_based/stream/cursor/default_file_based_cursor.py,sha256=VGV7xLyBribuBMVrXtO1xqkWJD86bl7yhXtjnwLMohM,7051
|
243
|
-
airbyte_cdk/sources/file_based/stream/default_file_based_stream.py,sha256=
|
245
|
+
airbyte_cdk/sources/file_based/stream/default_file_based_stream.py,sha256=SFb1UXtJtaqo-1t7hM3ahM1ZTH2k01oqX1mAJvz08Y0,19529
|
246
|
+
airbyte_cdk/sources/file_based/stream/identities_stream.py,sha256=Rge8_yEsY04S5QjQ6BpmQPevvz5h-siHXJG4xe0H8fs,3781
|
244
247
|
airbyte_cdk/sources/file_based/types.py,sha256=INxG7OPnkdUP69oYNKMAbwhvV1AGvLRHs1J6pIia2FI,218
|
245
248
|
airbyte_cdk/sources/http_config.py,sha256=OBZeuyFilm6NlDlBhFQvHhTWabEvZww6OHDIlZujIS0,730
|
246
|
-
airbyte_cdk/sources/http_logger.py,sha256=
|
249
|
+
airbyte_cdk/sources/http_logger.py,sha256=l_1fk5YwdonZ1wvAsTwjj6d36fj2WrVraIAMj5jTQdM,1575
|
247
250
|
airbyte_cdk/sources/message/__init__.py,sha256=y98fzHsQBwXwp2zEa4K5mxGFqjnx9lDn9O0pTk-VS4U,395
|
248
251
|
airbyte_cdk/sources/message/repository.py,sha256=SG7avgti_-dj8FcRHTTrhgLLGJbElv14_zIB0SH8AIc,4763
|
249
252
|
airbyte_cdk/sources/source.py,sha256=KIBBH5VLEb8BZ8B9aROlfaI6OLoJqKDPMJ10jkAR7nk,3611
|
@@ -297,7 +300,7 @@ airbyte_cdk/sources/streams/http/rate_limiting.py,sha256=IwdjrHKUnU97XO4qONgYRv4
|
|
297
300
|
airbyte_cdk/sources/streams/http/requests_native_auth/__init__.py,sha256=RN0D3nOX1xLgwEwKWu6pkGy3XqBFzKSNZ8Lf6umU2eY,413
|
298
301
|
airbyte_cdk/sources/streams/http/requests_native_auth/abstract_oauth.py,sha256=-GDNyqccdutOftFpqCvvk81NwkskHhDZ8QcsUKzNjRQ,11660
|
299
302
|
airbyte_cdk/sources/streams/http/requests_native_auth/abstract_token.py,sha256=Y3n7J-sk5yGjv_OxtY6Z6k0PEsFZmtIRi-x0KCbaHdA,1010
|
300
|
-
airbyte_cdk/sources/streams/http/requests_native_auth/oauth.py,sha256=
|
303
|
+
airbyte_cdk/sources/streams/http/requests_native_auth/oauth.py,sha256=Zse4ve1MvPJBx-7CDtTBTmPuT6b9koGLMGpmd5188Y8,16698
|
301
304
|
airbyte_cdk/sources/streams/http/requests_native_auth/token.py,sha256=h5PTzcdH-RQLeCg7xZ45w_484OPUDSwNWl_iMJQmZoI,2526
|
302
305
|
airbyte_cdk/sources/streams/utils/__init__.py,sha256=4Hw-PX1-VgESLF16cDdvuYCzGJtHntThLF4qIiULWeo,61
|
303
306
|
airbyte_cdk/sources/types.py,sha256=nLPkTpyfGV4E6e99qcBWX4r8C3fE4I8Fvgx2EjvT9ic,5005
|
@@ -350,8 +353,8 @@ airbyte_cdk/utils/slice_hasher.py,sha256=-pHexlNYoWYPnXNH-M7HEbjmeJe9Zk7SJijdQ7d
|
|
350
353
|
airbyte_cdk/utils/spec_schema_transformations.py,sha256=-5HTuNsnDBAhj-oLeQXwpTGA0HdcjFOf2zTEMUTTg_Y,816
|
351
354
|
airbyte_cdk/utils/stream_status_utils.py,sha256=ZmBoiy5HVbUEHAMrUONxZvxnvfV9CesmQJLDTAIWnWw,1171
|
352
355
|
airbyte_cdk/utils/traced_exception.py,sha256=C8uIBuCL_E4WnBAOPSxBicD06JAldoN9fGsQDp463OY,6292
|
353
|
-
airbyte_cdk-6.
|
354
|
-
airbyte_cdk-6.
|
355
|
-
airbyte_cdk-6.
|
356
|
-
airbyte_cdk-6.
|
357
|
-
airbyte_cdk-6.
|
356
|
+
airbyte_cdk-6.26.0.dev4103.dist-info/LICENSE.txt,sha256=Wfe61S4BaGPj404v8lrAbvhjYR68SHlkzeYrg3_bbuM,1051
|
357
|
+
airbyte_cdk-6.26.0.dev4103.dist-info/METADATA,sha256=i2W3PFSBxp7zeO81GPe_lFCBFDa0Bcec9M17M8wTSiA,6004
|
358
|
+
airbyte_cdk-6.26.0.dev4103.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
|
359
|
+
airbyte_cdk-6.26.0.dev4103.dist-info/entry_points.txt,sha256=fj-e3PAQvsxsQzyyq8UkG1k8spunWnD4BAH2AwlR6NM,95
|
360
|
+
airbyte_cdk-6.26.0.dev4103.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|