airbyte-cdk 6.31.1.dev0__py3-none-any.whl → 6.31.2.dev0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. airbyte_cdk/cli/source_declarative_manifest/_run.py +3 -9
  2. airbyte_cdk/connector_builder/connector_builder_handler.py +2 -3
  3. airbyte_cdk/sources/declarative/async_job/job_orchestrator.py +4 -4
  4. airbyte_cdk/sources/declarative/auth/jwt.py +11 -17
  5. airbyte_cdk/sources/declarative/auth/oauth.py +23 -89
  6. airbyte_cdk/sources/declarative/auth/token.py +3 -8
  7. airbyte_cdk/sources/declarative/auth/token_provider.py +5 -4
  8. airbyte_cdk/sources/declarative/checks/check_dynamic_stream.py +9 -19
  9. airbyte_cdk/sources/declarative/concurrent_declarative_source.py +43 -134
  10. airbyte_cdk/sources/declarative/declarative_component_schema.yaml +16 -55
  11. airbyte_cdk/sources/declarative/declarative_stream.py +1 -3
  12. airbyte_cdk/sources/declarative/extractors/record_filter.py +5 -3
  13. airbyte_cdk/sources/declarative/incremental/__init__.py +0 -6
  14. airbyte_cdk/sources/declarative/incremental/datetime_based_cursor.py +7 -6
  15. airbyte_cdk/sources/declarative/incremental/global_substream_cursor.py +0 -3
  16. airbyte_cdk/sources/declarative/incremental/per_partition_cursor.py +3 -35
  17. airbyte_cdk/sources/declarative/manifest_declarative_source.py +7 -15
  18. airbyte_cdk/sources/declarative/models/declarative_component_schema.py +15 -45
  19. airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py +64 -343
  20. airbyte_cdk/sources/declarative/partition_routers/async_job_partition_router.py +5 -5
  21. airbyte_cdk/sources/declarative/partition_routers/list_partition_router.py +4 -2
  22. airbyte_cdk/sources/declarative/partition_routers/substream_partition_router.py +15 -55
  23. airbyte_cdk/sources/declarative/requesters/error_handlers/composite_error_handler.py +0 -22
  24. airbyte_cdk/sources/declarative/requesters/error_handlers/http_response_filter.py +4 -4
  25. airbyte_cdk/sources/declarative/requesters/http_requester.py +5 -1
  26. airbyte_cdk/sources/declarative/requesters/paginators/default_paginator.py +6 -5
  27. airbyte_cdk/sources/declarative/requesters/request_option.py +83 -4
  28. airbyte_cdk/sources/declarative/requesters/request_options/datetime_based_request_options_provider.py +7 -6
  29. airbyte_cdk/sources/declarative/retrievers/async_retriever.py +12 -6
  30. airbyte_cdk/sources/declarative/retrievers/simple_retriever.py +5 -2
  31. airbyte_cdk/sources/declarative/schema/__init__.py +0 -2
  32. airbyte_cdk/sources/declarative/schema/dynamic_schema_loader.py +5 -44
  33. airbyte_cdk/sources/http_logger.py +1 -1
  34. airbyte_cdk/sources/streams/concurrent/cursor.py +57 -51
  35. airbyte_cdk/sources/streams/concurrent/state_converters/datetime_stream_state_converter.py +13 -22
  36. airbyte_cdk/sources/streams/core.py +6 -6
  37. airbyte_cdk/sources/streams/http/http.py +2 -1
  38. airbyte_cdk/sources/streams/http/requests_native_auth/abstract_oauth.py +6 -17
  39. airbyte_cdk/sources/streams/http/requests_native_auth/oauth.py +31 -43
  40. airbyte_cdk/sources/types.py +2 -4
  41. airbyte_cdk/sources/utils/transform.py +2 -23
  42. airbyte_cdk/test/utils/manifest_only_fixtures.py +2 -1
  43. airbyte_cdk/utils/mapping_helpers.py +86 -27
  44. airbyte_cdk/utils/slice_hasher.py +1 -8
  45. {airbyte_cdk-6.31.1.dev0.dist-info → airbyte_cdk-6.31.2.dev0.dist-info}/METADATA +6 -6
  46. {airbyte_cdk-6.31.1.dev0.dist-info → airbyte_cdk-6.31.2.dev0.dist-info}/RECORD +49 -55
  47. {airbyte_cdk-6.31.1.dev0.dist-info → airbyte_cdk-6.31.2.dev0.dist-info}/WHEEL +1 -1
  48. airbyte_cdk/sources/declarative/incremental/concurrent_partition_cursor.py +0 -400
  49. airbyte_cdk/sources/declarative/parsers/custom_code_compiler.py +0 -143
  50. airbyte_cdk/sources/streams/concurrent/clamping.py +0 -99
  51. airbyte_cdk/sources/streams/concurrent/cursor_types.py +0 -32
  52. airbyte_cdk/utils/datetime_helpers.py +0 -499
  53. airbyte_cdk-6.31.1.dev0.dist-info/LICENSE_SHORT +0 -1
  54. {airbyte_cdk-6.31.1.dev0.dist-info → airbyte_cdk-6.31.2.dev0.dist-info}/LICENSE.txt +0 -0
  55. {airbyte_cdk-6.31.1.dev0.dist-info → airbyte_cdk-6.31.2.dev0.dist-info}/entry_points.txt +0 -0
@@ -21,6 +21,7 @@ import pkgutil
21
21
  import sys
22
22
  import traceback
23
23
  from collections.abc import Mapping
24
+ from datetime import datetime
24
25
  from pathlib import Path
25
26
  from typing import Any, cast
26
27
 
@@ -43,7 +44,6 @@ from airbyte_cdk.sources.declarative.concurrent_declarative_source import (
43
44
  )
44
45
  from airbyte_cdk.sources.declarative.yaml_declarative_source import YamlDeclarativeSource
45
46
  from airbyte_cdk.sources.source import TState
46
- from airbyte_cdk.utils.datetime_helpers import ab_datetime_now
47
47
 
48
48
 
49
49
  class SourceLocalYaml(YamlDeclarativeSource):
@@ -101,7 +101,7 @@ def _get_local_yaml_source(args: list[str]) -> SourceLocalYaml:
101
101
  type=Type.TRACE,
102
102
  trace=AirbyteTraceMessage(
103
103
  type=TraceType.ERROR,
104
- emitted_at=ab_datetime_now().to_epoch_millis(),
104
+ emitted_at=int(datetime.now().timestamp() * 1000),
105
105
  error=AirbyteErrorTraceMessage(
106
106
  message=f"Error starting the sync. This could be due to an invalid configuration or catalog. Please contact Support for assistance. Error: {error}",
107
107
  stack_trace=traceback.format_exc(),
@@ -171,12 +171,6 @@ def create_declarative_source(
171
171
  "Invalid config: `__injected_declarative_manifest` should be provided at the root "
172
172
  f"of the config but config only has keys: {list(config.keys() if config else [])}"
173
173
  )
174
- if not isinstance(config["__injected_declarative_manifest"], dict):
175
- raise ValueError(
176
- "Invalid config: `__injected_declarative_manifest` should be a dictionary, "
177
- f"but got type: {type(config['__injected_declarative_manifest'])}"
178
- )
179
-
180
174
  return ConcurrentDeclarativeSource(
181
175
  config=config,
182
176
  catalog=catalog,
@@ -191,7 +185,7 @@ def create_declarative_source(
191
185
  type=Type.TRACE,
192
186
  trace=AirbyteTraceMessage(
193
187
  type=TraceType.ERROR,
194
- emitted_at=ab_datetime_now().to_epoch_millis(),
188
+ emitted_at=int(datetime.now().timestamp() * 1000),
195
189
  error=AirbyteErrorTraceMessage(
196
190
  message=f"Error starting the sync. This could be due to an invalid configuration or catalog. Please contact Support for assistance. Error: {error}",
197
191
  stack_trace=traceback.format_exc(),
@@ -3,6 +3,7 @@
3
3
  #
4
4
 
5
5
  import dataclasses
6
+ from datetime import datetime
6
7
  from typing import Any, List, Mapping
7
8
 
8
9
  from airbyte_cdk.connector_builder.message_grouper import MessageGrouper
@@ -20,7 +21,6 @@ from airbyte_cdk.sources.declarative.parsers.model_to_component_factory import (
20
21
  ModelToComponentFactory,
21
22
  )
22
23
  from airbyte_cdk.utils.airbyte_secrets_utils import filter_secrets
23
- from airbyte_cdk.utils.datetime_helpers import ab_datetime_now
24
24
  from airbyte_cdk.utils.traced_exception import AirbyteTracedException
25
25
 
26
26
  DEFAULT_MAXIMUM_NUMBER_OF_PAGES_PER_SLICE = 5
@@ -52,7 +52,6 @@ def get_limits(config: Mapping[str, Any]) -> TestReadLimits:
52
52
  def create_source(config: Mapping[str, Any], limits: TestReadLimits) -> ManifestDeclarativeSource:
53
53
  manifest = config["__injected_declarative_manifest"]
54
54
  return ManifestDeclarativeSource(
55
- config=config,
56
55
  emit_connector_builder_messages=True,
57
56
  source_config=manifest,
58
57
  component_factory=ModelToComponentFactory(
@@ -114,4 +113,4 @@ def resolve_manifest(source: ManifestDeclarativeSource) -> AirbyteMessage:
114
113
 
115
114
 
116
115
  def _emitted_at() -> int:
117
- return ab_datetime_now().to_epoch_millis()
116
+ return int(datetime.now().timestamp()) * 1000
@@ -482,16 +482,16 @@ class AsyncJobOrchestrator:
482
482
  and exception.failure_type == FailureType.config_error
483
483
  )
484
484
 
485
- def fetch_records(self, async_jobs: Iterable[AsyncJob]) -> Iterable[Mapping[str, Any]]:
485
+ def fetch_records(self, partition: AsyncPartition) -> Iterable[Mapping[str, Any]]:
486
486
  """
487
- Fetches records from the given jobs.
487
+ Fetches records from the given partition's jobs.
488
488
 
489
489
  Args:
490
- async_jobs Iterable[AsyncJob]: The list of AsyncJobs.
490
+ partition (AsyncPartition): The partition containing the jobs.
491
491
 
492
492
  Yields:
493
493
  Iterable[Mapping[str, Any]]: The fetched records from the jobs.
494
494
  """
495
- for job in async_jobs:
495
+ for job in partition.jobs:
496
496
  yield from self._job_repository.fetch_records(job)
497
497
  self._job_repository.delete(job)
@@ -3,7 +3,6 @@
3
3
  #
4
4
 
5
5
  import base64
6
- import json
7
6
  from dataclasses import InitVar, dataclass
8
7
  from datetime import datetime
9
8
  from typing import Any, Mapping, Optional, Union
@@ -105,21 +104,21 @@ class JwtAuthenticator(DeclarativeAuthenticator):
105
104
  )
106
105
 
107
106
  def _get_jwt_headers(self) -> dict[str, Any]:
108
- """
107
+ """ "
109
108
  Builds and returns the headers used when signing the JWT.
110
109
  """
111
- headers = self._additional_jwt_headers.eval(self.config, json_loads=json.loads)
110
+ headers = self._additional_jwt_headers.eval(self.config)
112
111
  if any(prop in headers for prop in ["kid", "alg", "typ", "cty"]):
113
112
  raise ValueError(
114
113
  "'kid', 'alg', 'typ', 'cty' are reserved headers and should not be set as part of 'additional_jwt_headers'"
115
114
  )
116
115
 
117
116
  if self._kid:
118
- headers["kid"] = self._kid.eval(self.config, json_loads=json.loads)
117
+ headers["kid"] = self._kid.eval(self.config)
119
118
  if self._typ:
120
- headers["typ"] = self._typ.eval(self.config, json_loads=json.loads)
119
+ headers["typ"] = self._typ.eval(self.config)
121
120
  if self._cty:
122
- headers["cty"] = self._cty.eval(self.config, json_loads=json.loads)
121
+ headers["cty"] = self._cty.eval(self.config)
123
122
  headers["alg"] = self._algorithm
124
123
  return headers
125
124
 
@@ -131,19 +130,18 @@ class JwtAuthenticator(DeclarativeAuthenticator):
131
130
  exp = now + self._token_duration if isinstance(self._token_duration, int) else now
132
131
  nbf = now
133
132
 
134
- payload = self._additional_jwt_payload.eval(self.config, json_loads=json.loads)
133
+ payload = self._additional_jwt_payload.eval(self.config)
135
134
  if any(prop in payload for prop in ["iss", "sub", "aud", "iat", "exp", "nbf"]):
136
135
  raise ValueError(
137
136
  "'iss', 'sub', 'aud', 'iat', 'exp', 'nbf' are reserved properties and should not be set as part of 'additional_jwt_payload'"
138
137
  )
139
138
 
140
139
  if self._iss:
141
- payload["iss"] = self._iss.eval(self.config, json_loads=json.loads)
140
+ payload["iss"] = self._iss.eval(self.config)
142
141
  if self._sub:
143
- payload["sub"] = self._sub.eval(self.config, json_loads=json.loads)
142
+ payload["sub"] = self._sub.eval(self.config)
144
143
  if self._aud:
145
- payload["aud"] = self._aud.eval(self.config, json_loads=json.loads)
146
-
144
+ payload["aud"] = self._aud.eval(self.config)
147
145
  payload["iat"] = now
148
146
  payload["exp"] = exp
149
147
  payload["nbf"] = nbf
@@ -153,7 +151,7 @@ class JwtAuthenticator(DeclarativeAuthenticator):
153
151
  """
154
152
  Returns the secret key used to sign the JWT.
155
153
  """
156
- secret_key: str = self._secret_key.eval(self.config, json_loads=json.loads)
154
+ secret_key: str = self._secret_key.eval(self.config)
157
155
  return (
158
156
  base64.b64encode(secret_key.encode()).decode()
159
157
  if self._base64_encode_secret_key
@@ -178,11 +176,7 @@ class JwtAuthenticator(DeclarativeAuthenticator):
178
176
  """
179
177
  Returns the header prefix to be used when attaching the token to the request.
180
178
  """
181
- return (
182
- self._header_prefix.eval(self.config, json_loads=json.loads)
183
- if self._header_prefix
184
- else None
185
- )
179
+ return self._header_prefix.eval(self.config) if self._header_prefix else None
186
180
 
187
181
  @property
188
182
  def auth_header(self) -> str:
@@ -3,11 +3,11 @@
3
3
  #
4
4
 
5
5
  from dataclasses import InitVar, dataclass, field
6
- from datetime import timedelta
7
- from typing import Any, List, Mapping, MutableMapping, Optional, Union
6
+ from typing import Any, List, Mapping, Optional, Union
7
+
8
+ import pendulum
8
9
 
9
10
  from airbyte_cdk.sources.declarative.auth.declarative_authenticator import DeclarativeAuthenticator
10
- from airbyte_cdk.sources.declarative.interpolation.interpolated_boolean import InterpolatedBoolean
11
11
  from airbyte_cdk.sources.declarative.interpolation.interpolated_mapping import InterpolatedMapping
12
12
  from airbyte_cdk.sources.declarative.interpolation.interpolated_string import InterpolatedString
13
13
  from airbyte_cdk.sources.message import MessageRepository, NoopMessageRepository
@@ -17,7 +17,6 @@ from airbyte_cdk.sources.streams.http.requests_native_auth.abstract_oauth import
17
17
  from airbyte_cdk.sources.streams.http.requests_native_auth.oauth import (
18
18
  SingleUseRefreshTokenOauth2Authenticator,
19
19
  )
20
- from airbyte_cdk.utils.datetime_helpers import AirbyteDateTime, ab_datetime_now, ab_datetime_parse
21
20
 
22
21
 
23
22
  @dataclass
@@ -45,15 +44,15 @@ class DeclarativeOauth2Authenticator(AbstractOauth2Authenticator, DeclarativeAut
45
44
  message_repository (MessageRepository): the message repository used to emit logs on HTTP requests
46
45
  """
47
46
 
47
+ client_id: Union[InterpolatedString, str]
48
+ client_secret: Union[InterpolatedString, str]
48
49
  config: Mapping[str, Any]
49
50
  parameters: InitVar[Mapping[str, Any]]
50
- client_id: Optional[Union[InterpolatedString, str]] = None
51
- client_secret: Optional[Union[InterpolatedString, str]] = None
52
51
  token_refresh_endpoint: Optional[Union[InterpolatedString, str]] = None
53
52
  refresh_token: Optional[Union[InterpolatedString, str]] = None
54
53
  scopes: Optional[List[str]] = None
55
54
  token_expiry_date: Optional[Union[InterpolatedString, str]] = None
56
- _token_expiry_date: Optional[AirbyteDateTime] = field(init=False, repr=False, default=None)
55
+ _token_expiry_date: Optional[pendulum.DateTime] = field(init=False, repr=False, default=None)
57
56
  token_expiry_date_format: Optional[str] = None
58
57
  token_expiry_is_time_of_expiration: bool = False
59
58
  access_token_name: Union[InterpolatedString, str] = "access_token"
@@ -67,8 +66,6 @@ class DeclarativeOauth2Authenticator(AbstractOauth2Authenticator, DeclarativeAut
67
66
  grant_type_name: Union[InterpolatedString, str] = "grant_type"
68
67
  grant_type: Union[InterpolatedString, str] = "refresh_token"
69
68
  message_repository: MessageRepository = NoopMessageRepository()
70
- profile_assertion: Optional[DeclarativeAuthenticator] = None
71
- use_profile_assertion: Optional[Union[InterpolatedBoolean, str, bool]] = False
72
69
 
73
70
  def __post_init__(self, parameters: Mapping[str, Any]) -> None:
74
71
  super().__init__()
@@ -79,19 +76,11 @@ class DeclarativeOauth2Authenticator(AbstractOauth2Authenticator, DeclarativeAut
79
76
  else:
80
77
  self._token_refresh_endpoint = None
81
78
  self._client_id_name = InterpolatedString.create(self.client_id_name, 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
- )
79
+ self._client_id = InterpolatedString.create(self.client_id, parameters=parameters)
87
80
  self._client_secret_name = InterpolatedString.create(
88
81
  self.client_secret_name, parameters=parameters
89
82
  )
90
- self._client_secret = (
91
- InterpolatedString.create(self.client_secret, parameters=parameters)
92
- if self.client_secret
93
- else self.client_secret
94
- )
83
+ self._client_secret = InterpolatedString.create(self.client_secret, parameters=parameters)
95
84
  self._refresh_token_name = InterpolatedString.create(
96
85
  self.refresh_token_name, parameters=parameters
97
86
  )
@@ -110,43 +99,22 @@ class DeclarativeOauth2Authenticator(AbstractOauth2Authenticator, DeclarativeAut
110
99
  self.grant_type_name = InterpolatedString.create(
111
100
  self.grant_type_name, parameters=parameters
112
101
  )
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
- )
102
+ self.grant_type = InterpolatedString.create(self.grant_type, parameters=parameters)
119
103
  self._refresh_request_body = InterpolatedMapping(
120
104
  self.refresh_request_body or {}, parameters=parameters
121
105
  )
122
106
  self._refresh_request_headers = InterpolatedMapping(
123
107
  self.refresh_request_headers or {}, parameters=parameters
124
108
  )
125
- try:
126
- if (
127
- isinstance(self.token_expiry_date, (int, str))
128
- and str(self.token_expiry_date).isdigit()
129
- ):
130
- self._token_expiry_date = ab_datetime_parse(self.token_expiry_date)
131
- else:
132
- self._token_expiry_date = (
133
- ab_datetime_parse(
134
- InterpolatedString.create(
135
- self.token_expiry_date, parameters=parameters
136
- ).eval(self.config)
137
- )
138
- if self.token_expiry_date
139
- else ab_datetime_now() - timedelta(days=1)
109
+ self._token_expiry_date: pendulum.DateTime = (
110
+ pendulum.parse(
111
+ InterpolatedString.create(self.token_expiry_date, parameters=parameters).eval(
112
+ self.config
140
113
  )
141
- except ValueError as e:
142
- raise ValueError(f"Invalid token expiry date format: {e}")
143
- self.use_profile_assertion = (
144
- InterpolatedBoolean(self.use_profile_assertion, parameters=parameters)
145
- if isinstance(self.use_profile_assertion, str)
146
- else self.use_profile_assertion
114
+ ) # type: ignore # pendulum.parse returns a datetime in this context
115
+ if self.token_expiry_date
116
+ else pendulum.now().subtract(days=1) # type: ignore # substract does not have type hints
147
117
  )
148
- self.assertion_name = "assertion"
149
-
150
118
  if self.access_token_value is not None:
151
119
  self._access_token_value = InterpolatedString.create(
152
120
  self.access_token_value, parameters=parameters
@@ -158,20 +126,9 @@ class DeclarativeOauth2Authenticator(AbstractOauth2Authenticator, DeclarativeAut
158
126
  self._access_token_value if self.access_token_value else None
159
127
  )
160
128
 
161
- if not self.use_profile_assertion and any(
162
- client_creds is None for client_creds in [self.client_id, self.client_secret]
163
- ):
164
- raise ValueError(
165
- "OAuthAuthenticator configuration error: Both 'client_id' and 'client_secret' are required for the "
166
- "basic OAuth flow."
167
- )
168
- if self.profile_assertion is None and self.use_profile_assertion:
169
- raise ValueError(
170
- "OAuthAuthenticator configuration error: 'profile_assertion' is required when using the profile assertion flow."
171
- )
172
129
  if self.get_grant_type() == "refresh_token" and self._refresh_token is None:
173
130
  raise ValueError(
174
- "OAuthAuthenticator configuration error: A 'refresh_token' is required when the 'grant_type' is set to 'refresh_token'."
131
+ "OAuthAuthenticator needs a refresh_token parameter if grant_type is set to `refresh_token`"
175
132
  )
176
133
 
177
134
  def get_token_refresh_endpoint(self) -> Optional[str]:
@@ -188,21 +145,19 @@ class DeclarativeOauth2Authenticator(AbstractOauth2Authenticator, DeclarativeAut
188
145
  return self._client_id_name.eval(self.config) # type: ignore # eval returns a string in this context
189
146
 
190
147
  def get_client_id(self) -> str:
191
- client_id = self._client_id.eval(self.config) if self._client_id else self._client_id
148
+ client_id: str = self._client_id.eval(self.config)
192
149
  if not client_id:
193
150
  raise ValueError("OAuthAuthenticator was unable to evaluate client_id parameter")
194
- return client_id # type: ignore # value will be returned as a string, or an error will be raised
151
+ return client_id
195
152
 
196
153
  def get_client_secret_name(self) -> str:
197
154
  return self._client_secret_name.eval(self.config) # type: ignore # eval returns a string in this context
198
155
 
199
156
  def get_client_secret(self) -> str:
200
- client_secret = (
201
- self._client_secret.eval(self.config) if self._client_secret else self._client_secret
202
- )
157
+ client_secret: str = self._client_secret.eval(self.config)
203
158
  if not client_secret:
204
159
  raise ValueError("OAuthAuthenticator was unable to evaluate client_secret parameter")
205
- return client_secret # type: ignore # value will be returned as a string, or an error will be raised
160
+ return client_secret
206
161
 
207
162
  def get_refresh_token_name(self) -> str:
208
163
  return self._refresh_token_name.eval(self.config) # type: ignore # eval returns a string in this context
@@ -231,33 +186,12 @@ class DeclarativeOauth2Authenticator(AbstractOauth2Authenticator, DeclarativeAut
231
186
  def get_refresh_request_headers(self) -> Mapping[str, Any]:
232
187
  return self._refresh_request_headers.eval(self.config)
233
188
 
234
- def get_token_expiry_date(self) -> AirbyteDateTime:
235
- return self._token_expiry_date # type: ignore # _token_expiry_date is an AirbyteDateTime. It is never None despite what mypy thinks
189
+ def get_token_expiry_date(self) -> pendulum.DateTime:
190
+ return self._token_expiry_date # type: ignore # _token_expiry_date is a pendulum.DateTime. It is never None despite what mypy thinks
236
191
 
237
192
  def set_token_expiry_date(self, value: Union[str, int]) -> None:
238
193
  self._token_expiry_date = self._parse_token_expiration_date(value)
239
194
 
240
- def get_assertion_name(self) -> str:
241
- return self.assertion_name
242
-
243
- def get_assertion(self) -> str:
244
- if self.profile_assertion is None:
245
- raise ValueError("profile_assertion is not set")
246
- return self.profile_assertion.token
247
-
248
- def build_refresh_request_body(self) -> Mapping[str, Any]:
249
- """
250
- Returns the request body to set on the refresh request
251
-
252
- Override to define additional parameters
253
- """
254
- if self.use_profile_assertion:
255
- return {
256
- self.get_grant_type_name(): self.get_grant_type(),
257
- self.get_assertion_name(): self.get_assertion(),
258
- }
259
- return super().build_refresh_request_body()
260
-
261
195
  @property
262
196
  def access_token(self) -> str:
263
197
  if self._access_token is None:
@@ -5,7 +5,7 @@
5
5
  import base64
6
6
  import logging
7
7
  from dataclasses import InitVar, dataclass
8
- from typing import Any, Mapping, Union
8
+ from typing import Any, Mapping, MutableMapping, Union
9
9
 
10
10
  import requests
11
11
  from cachetools import TTLCache, cached
@@ -45,11 +45,6 @@ class ApiKeyAuthenticator(DeclarativeAuthenticator):
45
45
  config: Config
46
46
  parameters: InitVar[Mapping[str, Any]]
47
47
 
48
- def __post_init__(self, parameters: Mapping[str, Any]) -> None:
49
- self._field_name = InterpolatedString.create(
50
- self.request_option.field_name, parameters=parameters
51
- )
52
-
53
48
  @property
54
49
  def auth_header(self) -> str:
55
50
  options = self._get_request_options(RequestOptionType.header)
@@ -60,9 +55,9 @@ class ApiKeyAuthenticator(DeclarativeAuthenticator):
60
55
  return self.token_provider.get_token()
61
56
 
62
57
  def _get_request_options(self, option_type: RequestOptionType) -> Mapping[str, Any]:
63
- options = {}
58
+ options: MutableMapping[str, Any] = {}
64
59
  if self.request_option.inject_into == option_type:
65
- options[self._field_name.eval(self.config)] = self.token
60
+ self.request_option.inject_into_request(options, self.token, self.config)
66
61
  return options
67
62
 
68
63
  def get_request_params(self) -> Mapping[str, Any]:
@@ -9,7 +9,9 @@ from dataclasses import InitVar, dataclass, field
9
9
  from typing import Any, List, Mapping, Optional, Union
10
10
 
11
11
  import dpath
12
+ import pendulum
12
13
  from isodate import Duration
14
+ from pendulum import DateTime
13
15
 
14
16
  from airbyte_cdk.sources.declarative.decoders.decoder import Decoder
15
17
  from airbyte_cdk.sources.declarative.decoders.json_decoder import JsonDecoder
@@ -19,7 +21,6 @@ from airbyte_cdk.sources.declarative.requesters.requester import Requester
19
21
  from airbyte_cdk.sources.http_logger import format_http_message
20
22
  from airbyte_cdk.sources.message import MessageRepository, NoopMessageRepository
21
23
  from airbyte_cdk.sources.types import Config
22
- from airbyte_cdk.utils.datetime_helpers import AirbyteDateTime, ab_datetime_now
23
24
 
24
25
 
25
26
  class TokenProvider:
@@ -37,7 +38,7 @@ class SessionTokenProvider(TokenProvider):
37
38
  message_repository: MessageRepository = NoopMessageRepository()
38
39
  decoder: Decoder = field(default_factory=lambda: JsonDecoder(parameters={}))
39
40
 
40
- _next_expiration_time: Optional[AirbyteDateTime] = None
41
+ _next_expiration_time: Optional[DateTime] = None
41
42
  _token: Optional[str] = None
42
43
 
43
44
  def get_token(self) -> str:
@@ -47,7 +48,7 @@ class SessionTokenProvider(TokenProvider):
47
48
  return self._token
48
49
 
49
50
  def _refresh_if_necessary(self) -> None:
50
- if self._next_expiration_time is None or self._next_expiration_time < ab_datetime_now():
51
+ if self._next_expiration_time is None or self._next_expiration_time < pendulum.now():
51
52
  self._refresh()
52
53
 
53
54
  def _refresh(self) -> None:
@@ -64,7 +65,7 @@ class SessionTokenProvider(TokenProvider):
64
65
  raise ReadException("Failed to get session token, response got ignored by requester")
65
66
  session_token = dpath.get(next(self.decoder.decode(response)), self.session_token_path)
66
67
  if self.expiration_duration is not None:
67
- self._next_expiration_time = ab_datetime_now() + self.expiration_duration
68
+ self._next_expiration_time = pendulum.now() + self.expiration_duration
68
69
  self._token = session_token # type: ignore # Returned decoded response will be Mapping and therefore session_token will be str or None
69
70
 
70
71
 
@@ -21,12 +21,8 @@ class CheckDynamicStream(ConnectionChecker):
21
21
  stream_count (int): numbers of streams to check
22
22
  """
23
23
 
24
- # TODO: Add field stream_names to check_connection for static streams
25
- # https://github.com/airbytehq/airbyte-python-cdk/pull/293#discussion_r1934933483
26
-
27
24
  stream_count: int
28
25
  parameters: InitVar[Mapping[str, Any]]
29
- use_check_availability: bool = True
30
26
 
31
27
  def __post_init__(self, parameters: Mapping[str, Any]) -> None:
32
28
  self._parameters = parameters
@@ -35,27 +31,21 @@ class CheckDynamicStream(ConnectionChecker):
35
31
  self, source: AbstractSource, logger: logging.Logger, config: Mapping[str, Any]
36
32
  ) -> Tuple[bool, Any]:
37
33
  streams = source.streams(config=config)
38
-
39
34
  if len(streams) == 0:
40
35
  return False, f"No streams to connect to from source {source}"
41
- if not self.use_check_availability:
42
- return True, None
43
-
44
- availability_strategy = HttpAvailabilityStrategy()
45
36
 
46
- try:
47
- for stream in streams[: min(self.stream_count, len(streams))]:
37
+ for stream_index in range(min(self.stream_count, len(streams))):
38
+ stream = streams[stream_index]
39
+ availability_strategy = HttpAvailabilityStrategy()
40
+ try:
48
41
  stream_is_available, reason = availability_strategy.check_availability(
49
42
  stream, logger
50
43
  )
51
44
  if not stream_is_available:
52
- logger.warning(f"Stream {stream.name} is not available: {reason}")
53
45
  return False, reason
54
- except Exception as error:
55
- error_message = (
56
- f"Encountered an error trying to connect to stream {stream.name}. Error: {error}"
57
- )
58
- logger.error(error_message, exc_info=True)
59
- return False, error_message
60
-
46
+ except Exception as error:
47
+ logger.error(
48
+ f"Encountered an error trying to connect to stream {stream.name}. Error: \n {traceback.format_exc()}"
49
+ )
50
+ return False, f"Unable to connect to stream {stream.name} - {error}"
61
51
  return True, None