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.
Files changed (23) hide show
  1. airbyte_cdk/sources/declarative/auth/oauth.py +68 -11
  2. airbyte_cdk/sources/declarative/declarative_component_schema.yaml +9 -2
  3. airbyte_cdk/sources/declarative/incremental/per_partition_cursor.py +35 -18
  4. airbyte_cdk/sources/declarative/models/declarative_component_schema.py +14 -4
  5. airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py +15 -3
  6. airbyte_cdk/sources/declarative/partition_routers/substream_partition_router.py +6 -2
  7. airbyte_cdk/sources/declarative/requesters/error_handlers/composite_error_handler.py +22 -0
  8. airbyte_cdk/sources/file_based/config/abstract_file_based_spec.py +15 -0
  9. airbyte_cdk/sources/file_based/config/identities_based_stream_config.py +8 -0
  10. airbyte_cdk/sources/file_based/config/permissions.py +34 -0
  11. airbyte_cdk/sources/file_based/file_based_source.py +65 -1
  12. airbyte_cdk/sources/file_based/file_based_stream_reader.py +33 -0
  13. airbyte_cdk/sources/file_based/schema_helpers.py +25 -0
  14. airbyte_cdk/sources/file_based/stream/__init__.py +2 -1
  15. airbyte_cdk/sources/file_based/stream/default_file_based_stream.py +29 -0
  16. airbyte_cdk/sources/file_based/stream/identities_stream.py +99 -0
  17. airbyte_cdk/sources/http_logger.py +1 -1
  18. airbyte_cdk/sources/streams/http/requests_native_auth/oauth.py +20 -20
  19. {airbyte_cdk-6.23.0.dev1.dist-info → airbyte_cdk-6.26.0.dev4103.dist-info}/METADATA +1 -1
  20. {airbyte_cdk-6.23.0.dev1.dist-info → airbyte_cdk-6.26.0.dev4103.dist-info}/RECORD +23 -20
  21. {airbyte_cdk-6.23.0.dev1.dist-info → airbyte_cdk-6.26.0.dev4103.dist-info}/LICENSE.txt +0 -0
  22. {airbyte_cdk-6.23.0.dev1.dist-info → airbyte_cdk-6.26.0.dev4103.dist-info}/WHEEL +0 -0
  23. {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 = InterpolatedString.create(self.client_id, parameters=parameters)
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 = InterpolatedString.create(self.client_secret, parameters=parameters)
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(self.grant_type, parameters=parameters)
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 needs a refresh_token parameter if grant_type is set to `refresh_token`"
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: str = self._client_id.eval(self.config)
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: str = self._client_secret.eval(self.config)
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
- raise ValueError(
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
- ) and not isinstance(stream_slicer, PerPartitionWithGlobalCursor):
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
- substream_state = list(stream_state.values())
300
- substream_state = substream_state[0] if substream_state else {} # type: ignore [assignment] # Incorrect type for assignment
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 AbstractFileBasedStream, DefaultFileBasedStream
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 [return-value] # got "dict[str, object]", expected "dict[str, JsonType]"
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 [return-value]
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 [return-value]
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 [return-value]
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 [return-value]
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 [assignment] # Incorrect type for assignment
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 [assignment] # Incorrect type for assignment
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 [arg-type]
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 [assignment] # Incorrect type for assignment
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 [arg-type]
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 [return-value]
252
- self._connector_config, # type: ignore [arg-type]
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 [arg-type]
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 [return-value]
267
- self._connector_config, # type: ignore [arg-type]
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 [arg-type]
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 [arg-type]
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 [arg-type, return-value, no-untyped-call]
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 [arg-type]
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 [arg-type]
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 [arg-type]
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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: airbyte-cdk
3
- Version: 6.23.0.dev1
3
+ Version: 6.26.0.dev4103
4
4
  Summary: A framework for writing Airbyte Connectors.
5
5
  License: MIT
6
6
  Keywords: airbyte,connector-development-kit,cdk
@@ -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=GhXWheC5GkKV7req3jBCY0aTbFwCuQ5RRSfZi3jFphM,11002
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=TDpCeMw4_8X_omCEvgqhYoHD5mKfJGUoswdfHFqPio8,138886
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=_FSJjAwL4Zu-i2CngnhTtx8j-NPVSBKj5LwDSPta3Cg,16305
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=gyG17G9wExCx2E5H5D5N1IySKBbaqZPhOzP1iMwgU0Q,97458
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=iZTPBBRhPOh00X7sfKGHDGQBC9rs4jq12wL3n2xdZJg,121676
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=5bgXoJfBg_6i53krQMptAGb50XB5XoVfqQxKQhlLtBA,15383
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=tw5TMrzBAixZ0Z57Y90CtBuUVYQ-V4vob74IkcBHiAQ,2745
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=gXlZwnEKLWknnK_n7j14lANgR6vkqhlLJ-G3rRu-ox4,6897
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=Biv2QufYQtHZQCBZs4iCUpqTd82rk7xo8SDYkEeau3k,17616
217
- airbyte_cdk/sources/file_based/file_based_stream_reader.py,sha256=e1KhgTh7mzvkBOz9DjLwzOsDwevrTmbxSYIcvhgWgGM,6856
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=Cf8FH1bDFP0qCDDfEYir_WjP4exXUnikz8hZ40y1Ek0,9601
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=QPDqdgjsabOQD93dSFqHGaFS_3pIwm-chEabZHiPJi0,265
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=XLU5cNqQ-5mj243gNzMyXtm_oCtg1ORyoqbCsUo9Dn4,18044
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=TyBmtRA6D9g0XDkKGvdM415b36RXDjgfkwRewDsH8-0,1576
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=OCNokL0GypjN8PUVJk1UFJVE5THkHAbNDmB984_F7W0,16718
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.23.0.dev1.dist-info/LICENSE.txt,sha256=Wfe61S4BaGPj404v8lrAbvhjYR68SHlkzeYrg3_bbuM,1051
354
- airbyte_cdk-6.23.0.dev1.dist-info/METADATA,sha256=i6I1HcYKqAUk81RuehhoKF3aBClUS_KBB7r3dY7b2B8,6001
355
- airbyte_cdk-6.23.0.dev1.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
356
- airbyte_cdk-6.23.0.dev1.dist-info/entry_points.txt,sha256=fj-e3PAQvsxsQzyyq8UkG1k8spunWnD4BAH2AwlR6NM,95
357
- airbyte_cdk-6.23.0.dev1.dist-info/RECORD,,
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,,