airbyte-cdk 6.61.1__py3-none-any.whl → 6.61.2__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/__init__.py +0 -2
  2. airbyte_cdk/connector_builder/connector_builder_handler.py +7 -5
  3. airbyte_cdk/connector_builder/main.py +4 -2
  4. airbyte_cdk/connector_builder/test_reader/reader.py +10 -8
  5. airbyte_cdk/legacy/__init__.py +1 -0
  6. airbyte_cdk/legacy/sources/__init__.py +1 -0
  7. airbyte_cdk/legacy/sources/declarative/__init__.py +1 -0
  8. airbyte_cdk/legacy/sources/declarative/incremental/__init__.py +1 -0
  9. airbyte_cdk/{sources → legacy/sources}/declarative/manifest_declarative_source.py +1 -1
  10. airbyte_cdk/sources/declarative/concurrent_declarative_source.py +507 -24
  11. airbyte_cdk/sources/declarative/incremental/__init__.py +4 -4
  12. airbyte_cdk/sources/declarative/incremental/per_partition_with_global.py +4 -4
  13. airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py +12 -0
  14. airbyte_cdk/sources/declarative/retrievers/retriever.py +1 -2
  15. airbyte_cdk/sources/streams/http/http_client.py +21 -0
  16. {airbyte_cdk-6.61.1.dist-info → airbyte_cdk-6.61.2.dist-info}/METADATA +1 -1
  17. {airbyte_cdk-6.61.1.dist-info → airbyte_cdk-6.61.2.dist-info}/RECORD +23 -19
  18. /airbyte_cdk/{sources → legacy/sources}/declarative/declarative_source.py +0 -0
  19. /airbyte_cdk/{sources → legacy/sources}/declarative/incremental/per_partition_cursor.py +0 -0
  20. {airbyte_cdk-6.61.1.dist-info → airbyte_cdk-6.61.2.dist-info}/LICENSE.txt +0 -0
  21. {airbyte_cdk-6.61.1.dist-info → airbyte_cdk-6.61.2.dist-info}/LICENSE_SHORT +0 -0
  22. {airbyte_cdk-6.61.1.dist-info → airbyte_cdk-6.61.2.dist-info}/WHEEL +0 -0
  23. {airbyte_cdk-6.61.1.dist-info → airbyte_cdk-6.61.2.dist-info}/entry_points.txt +0 -0
airbyte_cdk/__init__.py CHANGED
@@ -107,7 +107,6 @@ from .sources.declarative.extractors.record_extractor import RecordExtractor
107
107
  from .sources.declarative.extractors.record_filter import RecordFilter
108
108
  from .sources.declarative.incremental import DatetimeBasedCursor
109
109
  from .sources.declarative.interpolation import InterpolatedBoolean, InterpolatedString
110
- from .sources.declarative.manifest_declarative_source import ManifestDeclarativeSource
111
110
  from .sources.declarative.migrations.legacy_to_per_partition_state_migration import (
112
111
  LegacyToPerPartitionStateMigration,
113
112
  )
@@ -253,7 +252,6 @@ __all__ = [
253
252
  "JsonDecoder",
254
253
  "JsonFileSchemaLoader",
255
254
  "LegacyToPerPartitionStateMigration",
256
- "ManifestDeclarativeSource",
257
255
  "MinMaxDatetime",
258
256
  "NoAuth",
259
257
  "OffsetIncrement",
@@ -19,8 +19,6 @@ from airbyte_cdk.sources.declarative.concurrent_declarative_source import (
19
19
  ConcurrentDeclarativeSource,
20
20
  TestLimits,
21
21
  )
22
- from airbyte_cdk.sources.declarative.declarative_source import DeclarativeSource
23
- from airbyte_cdk.sources.declarative.manifest_declarative_source import ManifestDeclarativeSource
24
22
  from airbyte_cdk.utils.airbyte_secrets_utils import filter_secrets
25
23
  from airbyte_cdk.utils.datetime_helpers import ab_datetime_now
26
24
  from airbyte_cdk.utils.traced_exception import AirbyteTracedException
@@ -90,7 +88,7 @@ def create_source(
90
88
 
91
89
 
92
90
  def read_stream(
93
- source: DeclarativeSource,
91
+ source: ConcurrentDeclarativeSource[Optional[List[AirbyteStateMessage]]],
94
92
  config: Mapping[str, Any],
95
93
  configured_catalog: ConfiguredAirbyteCatalog,
96
94
  state: List[AirbyteStateMessage],
@@ -128,7 +126,9 @@ def read_stream(
128
126
  return error.as_airbyte_message()
129
127
 
130
128
 
131
- def resolve_manifest(source: ManifestDeclarativeSource) -> AirbyteMessage:
129
+ def resolve_manifest(
130
+ source: ConcurrentDeclarativeSource[Optional[List[AirbyteStateMessage]]],
131
+ ) -> AirbyteMessage:
132
132
  try:
133
133
  return AirbyteMessage(
134
134
  type=Type.RECORD,
@@ -145,7 +145,9 @@ def resolve_manifest(source: ManifestDeclarativeSource) -> AirbyteMessage:
145
145
  return error.as_airbyte_message()
146
146
 
147
147
 
148
- def full_resolve_manifest(source: ManifestDeclarativeSource, limits: TestLimits) -> AirbyteMessage:
148
+ def full_resolve_manifest(
149
+ source: ConcurrentDeclarativeSource[Optional[List[AirbyteStateMessage]]], limits: TestLimits
150
+ ) -> AirbyteMessage:
149
151
  try:
150
152
  manifest = {**source.resolved_manifest}
151
153
  streams = manifest.get("streams", [])
@@ -25,7 +25,9 @@ from airbyte_cdk.models import (
25
25
  ConfiguredAirbyteCatalog,
26
26
  ConfiguredAirbyteCatalogSerializer,
27
27
  )
28
- from airbyte_cdk.sources.declarative.manifest_declarative_source import ManifestDeclarativeSource
28
+ from airbyte_cdk.sources.declarative.concurrent_declarative_source import (
29
+ ConcurrentDeclarativeSource,
30
+ )
29
31
  from airbyte_cdk.sources.source import Source
30
32
  from airbyte_cdk.utils.traced_exception import AirbyteTracedException
31
33
 
@@ -68,7 +70,7 @@ def get_config_and_catalog_from_args(
68
70
 
69
71
 
70
72
  def handle_connector_builder_request(
71
- source: ManifestDeclarativeSource,
73
+ source: ConcurrentDeclarativeSource[Optional[List[AirbyteStateMessage]]],
72
74
  command: str,
73
75
  config: Mapping[str, Any],
74
76
  catalog: Optional[ConfiguredAirbyteCatalog],
@@ -23,7 +23,9 @@ from airbyte_cdk.models import (
23
23
  ConfiguredAirbyteCatalog,
24
24
  TraceType,
25
25
  )
26
- from airbyte_cdk.sources.declarative.declarative_source import DeclarativeSource
26
+ from airbyte_cdk.sources.declarative.concurrent_declarative_source import (
27
+ ConcurrentDeclarativeSource,
28
+ )
27
29
  from airbyte_cdk.utils import AirbyteTracedException
28
30
  from airbyte_cdk.utils.datetime_format_inferrer import DatetimeFormatInferrer
29
31
  from airbyte_cdk.utils.schema_inferrer import (
@@ -55,7 +57,7 @@ class TestReader:
55
57
  that contains slices of data, log messages, auxiliary requests, and any inferred schema or datetime formats.
56
58
 
57
59
  Parameters:
58
- source (DeclarativeSource): The data source to read from.
60
+ source (ConcurrentDeclarativeSource): The data source to read from.
59
61
  config (Mapping[str, Any]): Configuration parameters for the source.
60
62
  configured_catalog (ConfiguredAirbyteCatalog): Catalog containing stream configuration.
61
63
  state (List[AirbyteStateMessage]): Current state information for the read.
@@ -83,7 +85,7 @@ class TestReader:
83
85
 
84
86
  def run_test_read(
85
87
  self,
86
- source: DeclarativeSource,
88
+ source: ConcurrentDeclarativeSource[Optional[List[AirbyteStateMessage]]],
87
89
  config: Mapping[str, Any],
88
90
  configured_catalog: ConfiguredAirbyteCatalog,
89
91
  stream_name: str,
@@ -94,7 +96,7 @@ class TestReader:
94
96
  Run a test read for the connector by reading from a single stream and inferring schema and datetime formats.
95
97
 
96
98
  Parameters:
97
- source (DeclarativeSource): The source instance providing the streams.
99
+ source (ConcurrentDeclarativeSource): The source instance providing the streams.
98
100
  config (Mapping[str, Any]): The configuration settings to use for reading.
99
101
  configured_catalog (ConfiguredAirbyteCatalog): The catalog specifying the stream configuration.
100
102
  state (List[AirbyteStateMessage]): A list of state messages to resume the read.
@@ -126,7 +128,7 @@ class TestReader:
126
128
  if stream
127
129
  else None,
128
130
  self._cursor_field_to_nested_and_composite_field(stream.cursor_field)
129
- if stream
131
+ if stream and stream.cursor_field
130
132
  else None,
131
133
  )
132
134
  datetime_format_inferrer = DatetimeFormatInferrer()
@@ -381,13 +383,13 @@ class TestReader:
381
383
 
382
384
  def _read_stream(
383
385
  self,
384
- source: DeclarativeSource,
386
+ source: ConcurrentDeclarativeSource[Optional[List[AirbyteStateMessage]]],
385
387
  config: Mapping[str, Any],
386
388
  configured_catalog: ConfiguredAirbyteCatalog,
387
389
  state: List[AirbyteStateMessage],
388
390
  ) -> Iterator[AirbyteMessage]:
389
391
  """
390
- Reads messages from the given DeclarativeSource using an AirbyteEntrypoint.
392
+ Reads messages from the given ConcurrentDeclarativeSource using an AirbyteEntrypoint.
391
393
 
392
394
  This method attempts to yield messages from the source's read generator. If the generator
393
395
  raises an AirbyteTracedException, it checks whether the exception message indicates a non-actionable
@@ -396,7 +398,7 @@ class TestReader:
396
398
  wrapped into an AirbyteTracedException, and yielded as an AirbyteMessage.
397
399
 
398
400
  Parameters:
399
- source (DeclarativeSource): The source object that provides data reading logic.
401
+ source (ConcurrentDeclarativeSource): The source object that provides data reading logic.
400
402
  config (Mapping[str, Any]): The configuration dictionary for the source.
401
403
  configured_catalog (ConfiguredAirbyteCatalog): The catalog defining the streams and their configurations.
402
404
  state (List[AirbyteStateMessage]): A list representing the current state for incremental sync.
@@ -0,0 +1 @@
1
+ # Copyright (c) 2025 Airbyte, Inc., all rights reserved.
@@ -0,0 +1 @@
1
+ # Copyright (c) 2025 Airbyte, Inc., all rights reserved.
@@ -0,0 +1 @@
1
+ # Copyright (c) 2025 Airbyte, Inc., all rights reserved.
@@ -0,0 +1 @@
1
+ # Copyright (c) 2025 Airbyte, Inc., all rights reserved.
@@ -20,6 +20,7 @@ from airbyte_cdk.config_observation import create_connector_config_control_messa
20
20
  from airbyte_cdk.connector_builder.models import (
21
21
  LogMessage as ConnectorBuilderLogMessage,
22
22
  )
23
+ from airbyte_cdk.legacy.sources.declarative.declarative_source import DeclarativeSource
23
24
  from airbyte_cdk.manifest_migrations.migration_handler import (
24
25
  ManifestMigrationHandler,
25
26
  )
@@ -34,7 +35,6 @@ from airbyte_cdk.models import (
34
35
  from airbyte_cdk.models.airbyte_protocol_serializers import AirbyteMessageSerializer
35
36
  from airbyte_cdk.sources.declarative.checks import COMPONENTS_CHECKER_TYPE_MAPPING
36
37
  from airbyte_cdk.sources.declarative.checks.connection_checker import ConnectionChecker
37
- from airbyte_cdk.sources.declarative.declarative_source import DeclarativeSource
38
38
  from airbyte_cdk.sources.declarative.interpolation import InterpolatedBoolean
39
39
  from airbyte_cdk.sources.declarative.models.declarative_component_schema import (
40
40
  ConditionalStreams as ConditionalStreamsModel,
@@ -1,33 +1,55 @@
1
- #
2
- # Copyright (c) 2024 Airbyte, Inc., all rights reserved.
3
- #
1
+ # Copyright (c) 2025 Airbyte, Inc., all rights reserved.
4
2
 
3
+ import json
5
4
  import logging
5
+ import pkgutil
6
+ from copy import deepcopy
6
7
  from dataclasses import dataclass, field
7
8
  from queue import Queue
9
+ from types import ModuleType
8
10
  from typing import (
9
11
  Any,
10
12
  ClassVar,
13
+ Dict,
11
14
  Generic,
12
15
  Iterator,
13
16
  List,
14
17
  Mapping,
15
18
  MutableMapping,
16
19
  Optional,
20
+ Set,
17
21
  Tuple,
18
22
  Union,
19
23
  )
20
24
 
25
+ import orjson
26
+ import yaml
21
27
  from airbyte_protocol_dataclasses.models import Level
28
+ from jsonschema.exceptions import ValidationError
29
+ from jsonschema.validators import validate
22
30
 
31
+ from airbyte_cdk.config_observation import create_connector_config_control_message
32
+ from airbyte_cdk.connector_builder.models import (
33
+ LogMessage as ConnectorBuilderLogMessage,
34
+ )
35
+ from airbyte_cdk.manifest_migrations.migration_handler import (
36
+ ManifestMigrationHandler,
37
+ )
23
38
  from airbyte_cdk.models import (
24
39
  AirbyteCatalog,
40
+ AirbyteConnectionStatus,
25
41
  AirbyteMessage,
26
42
  AirbyteStateMessage,
27
43
  ConfiguredAirbyteCatalog,
44
+ ConnectorSpecification,
45
+ FailureType,
28
46
  )
47
+ from airbyte_cdk.models.airbyte_protocol_serializers import AirbyteMessageSerializer
48
+ from airbyte_cdk.sources.abstract_source import AbstractSource
29
49
  from airbyte_cdk.sources.concurrent_source.concurrent_source import ConcurrentSource
30
50
  from airbyte_cdk.sources.connector_state_manager import ConnectorStateManager
51
+ from airbyte_cdk.sources.declarative.checks import COMPONENTS_CHECKER_TYPE_MAPPING
52
+ from airbyte_cdk.sources.declarative.checks.connection_checker import ConnectionChecker
31
53
  from airbyte_cdk.sources.declarative.concurrency_level import ConcurrencyLevel
32
54
  from airbyte_cdk.sources.declarative.declarative_stream import DeclarativeStream
33
55
  from airbyte_cdk.sources.declarative.incremental import (
@@ -38,26 +60,50 @@ from airbyte_cdk.sources.declarative.incremental.datetime_based_cursor import Da
38
60
  from airbyte_cdk.sources.declarative.incremental.per_partition_with_global import (
39
61
  PerPartitionWithGlobalCursor,
40
62
  )
41
- from airbyte_cdk.sources.declarative.manifest_declarative_source import ManifestDeclarativeSource
63
+ from airbyte_cdk.sources.declarative.interpolation import InterpolatedBoolean
64
+ from airbyte_cdk.sources.declarative.models import FileUploader
42
65
  from airbyte_cdk.sources.declarative.models.declarative_component_schema import (
43
66
  ConcurrencyLevel as ConcurrencyLevelModel,
44
67
  )
45
68
  from airbyte_cdk.sources.declarative.models.declarative_component_schema import (
46
69
  DatetimeBasedCursor as DatetimeBasedCursorModel,
47
70
  )
71
+ from airbyte_cdk.sources.declarative.models.declarative_component_schema import (
72
+ DeclarativeStream as DeclarativeStreamModel,
73
+ )
48
74
  from airbyte_cdk.sources.declarative.models.declarative_component_schema import (
49
75
  IncrementingCountCursor as IncrementingCountCursorModel,
50
76
  )
77
+ from airbyte_cdk.sources.declarative.models.declarative_component_schema import (
78
+ Spec as SpecModel,
79
+ )
80
+ from airbyte_cdk.sources.declarative.models.declarative_component_schema import (
81
+ StateDelegatingStream as StateDelegatingStreamModel,
82
+ )
83
+ from airbyte_cdk.sources.declarative.parsers.custom_code_compiler import (
84
+ get_registered_components_module,
85
+ )
86
+ from airbyte_cdk.sources.declarative.parsers.manifest_component_transformer import (
87
+ ManifestComponentTransformer,
88
+ )
89
+ from airbyte_cdk.sources.declarative.parsers.manifest_normalizer import (
90
+ ManifestNormalizer,
91
+ )
92
+ from airbyte_cdk.sources.declarative.parsers.manifest_reference_resolver import (
93
+ ManifestReferenceResolver,
94
+ )
51
95
  from airbyte_cdk.sources.declarative.parsers.model_to_component_factory import (
52
96
  ModelToComponentFactory,
53
97
  )
54
98
  from airbyte_cdk.sources.declarative.partition_routers import AsyncJobPartitionRouter
99
+ from airbyte_cdk.sources.declarative.resolvers import COMPONENTS_RESOLVER_TYPE_MAPPING
55
100
  from airbyte_cdk.sources.declarative.retrievers import AsyncRetriever, Retriever, SimpleRetriever
101
+ from airbyte_cdk.sources.declarative.spec.spec import Spec
56
102
  from airbyte_cdk.sources.declarative.stream_slicers.declarative_partition_generator import (
57
103
  DeclarativePartitionFactory,
58
104
  StreamSlicerPartitionGenerator,
59
105
  )
60
- from airbyte_cdk.sources.declarative.types import ConnectionDefinition
106
+ from airbyte_cdk.sources.declarative.types import Config, ConnectionDefinition
61
107
  from airbyte_cdk.sources.message.concurrent_repository import ConcurrentMessageRepository
62
108
  from airbyte_cdk.sources.message.repository import InMemoryMessageRepository, MessageRepository
63
109
  from airbyte_cdk.sources.source import TState
@@ -68,6 +114,12 @@ from airbyte_cdk.sources.streams.concurrent.cursor import ConcurrentCursor, Fina
68
114
  from airbyte_cdk.sources.streams.concurrent.default_stream import DefaultStream
69
115
  from airbyte_cdk.sources.streams.concurrent.helpers import get_primary_key_from_stream
70
116
  from airbyte_cdk.sources.streams.concurrent.partitions.types import QueueItem
117
+ from airbyte_cdk.sources.utils.slice_logger import (
118
+ AlwaysLogSliceLogger,
119
+ DebugSliceLogger,
120
+ SliceLogger,
121
+ )
122
+ from airbyte_cdk.utils.traced_exception import AirbyteTracedException
71
123
 
72
124
 
73
125
  @dataclass
@@ -85,8 +137,33 @@ class TestLimits:
85
137
  max_streams: int = field(default=DEFAULT_MAX_STREAMS)
86
138
 
87
139
 
88
- class ConcurrentDeclarativeSource(ManifestDeclarativeSource, Generic[TState]):
89
- # By default, we defer to a value of 2. A value lower than than could cause a PartitionEnqueuer to be stuck in a state of deadlock
140
+ def _get_declarative_component_schema() -> Dict[str, Any]:
141
+ try:
142
+ raw_component_schema = pkgutil.get_data(
143
+ "airbyte_cdk", "sources/declarative/declarative_component_schema.yaml"
144
+ )
145
+ if raw_component_schema is not None:
146
+ declarative_component_schema = yaml.load(raw_component_schema, Loader=yaml.SafeLoader)
147
+ return declarative_component_schema # type: ignore
148
+ else:
149
+ raise RuntimeError(
150
+ "Failed to read manifest component json schema required for deduplication"
151
+ )
152
+ except FileNotFoundError as e:
153
+ raise FileNotFoundError(
154
+ f"Failed to read manifest component json schema required for deduplication: {e}"
155
+ )
156
+
157
+
158
+ # todo: AbstractSource can be removed once we've completely moved off all legacy synchronous CDK code paths
159
+ # and replaced with implementing the source.py:Source class
160
+ #
161
+ # todo: The `ConcurrentDeclarativeSource.message_repository()` method can also be removed once AbstractSource
162
+ # is no longer inherited from since the only external dependency is from that class.
163
+ #
164
+ # todo: It is worth investigating removal of the Generic[TState] since it will always be Optional[List[AirbyteStateMessage]]
165
+ class ConcurrentDeclarativeSource(AbstractSource, Generic[TState]):
166
+ # By default, we defer to a value of 2. A value lower than could cause a PartitionEnqueuer to be stuck in a state of deadlock
90
167
  # because it has hit the limit of futures but not partition reader is consuming them.
91
168
  _LOWEST_SAFE_CONCURRENCY_LEVEL = 2
92
169
 
@@ -104,6 +181,10 @@ class ConcurrentDeclarativeSource(ManifestDeclarativeSource, Generic[TState]):
104
181
  config_path: Optional[str] = None,
105
182
  **kwargs: Any,
106
183
  ) -> None:
184
+ self.logger = logging.getLogger(f"airbyte.{self.name}")
185
+
186
+ self._limits = limits
187
+
107
188
  # todo: We could remove state from initialization. Now that streams are grouped during the read(), a source
108
189
  # no longer needs to store the original incoming state. But maybe there's an edge case?
109
190
  self._connector_state_manager = ConnectorStateManager(state=state) # type: ignore # state is always in the form of List[AirbyteStateMessage]. The ConnectorStateManager should use generics, but this can be done later
@@ -132,18 +213,40 @@ class ConcurrentDeclarativeSource(ManifestDeclarativeSource, Generic[TState]):
132
213
  disable_cache=True if limits else False,
133
214
  )
134
215
 
135
- self._limits = limits
216
+ self._should_normalize = normalize_manifest
217
+ self._should_migrate = migrate_manifest
218
+ self._declarative_component_schema = _get_declarative_component_schema()
219
+ # If custom components are needed, locate and/or register them.
220
+ self.components_module: ModuleType | None = get_registered_components_module(config=config)
221
+ # set additional attributes
222
+ self._debug = debug
223
+ self._emit_connector_builder_messages = emit_connector_builder_messages
224
+ self._constructor = (
225
+ component_factory
226
+ if component_factory
227
+ else ModelToComponentFactory(
228
+ emit_connector_builder_messages=emit_connector_builder_messages,
229
+ max_concurrent_async_job_count=source_config.get("max_concurrent_async_job_count"),
230
+ )
231
+ )
136
232
 
137
- super().__init__(
138
- source_config=source_config,
139
- config=config,
140
- debug=debug,
141
- emit_connector_builder_messages=emit_connector_builder_messages,
142
- migrate_manifest=migrate_manifest,
143
- normalize_manifest=normalize_manifest,
144
- component_factory=component_factory,
145
- config_path=config_path,
233
+ self._message_repository = self._constructor.get_message_repository()
234
+ self._slice_logger: SliceLogger = (
235
+ AlwaysLogSliceLogger() if emit_connector_builder_messages else DebugSliceLogger()
236
+ )
237
+
238
+ # resolve all components in the manifest
239
+ self._source_config = self._pre_process_manifest(dict(source_config))
240
+ # validate resolved manifest against the declarative component schema
241
+ self._validate_source()
242
+ # apply additional post-processing to the manifest
243
+ self._post_process_manifest()
244
+
245
+ spec: Optional[Mapping[str, Any]] = self._source_config.get("spec")
246
+ self._spec_component: Optional[Spec] = (
247
+ self._constructor.create_component(SpecModel, spec, dict()) if spec else None
146
248
  )
249
+ self._config = self._migrate_and_transform_config(config_path, config) or {}
147
250
 
148
251
  concurrency_level_from_manifest = self._source_config.get("concurrency_level")
149
252
  if concurrency_level_from_manifest:
@@ -171,15 +274,141 @@ class ConcurrentDeclarativeSource(ManifestDeclarativeSource, Generic[TState]):
171
274
  logger=self.logger,
172
275
  slice_logger=self._slice_logger,
173
276
  queue=queue,
174
- message_repository=self.message_repository,
277
+ message_repository=self._message_repository,
278
+ )
279
+
280
+ def _pre_process_manifest(self, manifest: Dict[str, Any]) -> Dict[str, Any]:
281
+ """
282
+ Preprocesses the provided manifest dictionary by resolving any manifest references.
283
+
284
+ This method modifies the input manifest in place, resolving references using the
285
+ ManifestReferenceResolver to ensure all references within the manifest are properly handled.
286
+
287
+ Args:
288
+ manifest (Dict[str, Any]): The manifest dictionary to preprocess and resolve references in.
289
+
290
+ Returns:
291
+ None
292
+ """
293
+ # For ease of use we don't require the type to be specified at the top level manifest, but it should be included during processing
294
+ manifest = self._fix_source_type(manifest)
295
+ # Resolve references in the manifest
296
+ resolved_manifest = ManifestReferenceResolver().preprocess_manifest(manifest)
297
+ # Propagate types and parameters throughout the manifest
298
+ propagated_manifest = ManifestComponentTransformer().propagate_types_and_parameters(
299
+ "", resolved_manifest, {}
175
300
  )
176
301
 
302
+ return propagated_manifest
303
+
304
+ def _fix_source_type(self, manifest: Dict[str, Any]) -> Dict[str, Any]:
305
+ """
306
+ Fix the source type in the manifest. This is necessary because the source type is not always set in the manifest.
307
+ """
308
+ if "type" not in manifest:
309
+ manifest["type"] = "DeclarativeSource"
310
+
311
+ return manifest
312
+
313
+ def _post_process_manifest(self) -> None:
314
+ """
315
+ Post-processes the manifest after validation.
316
+ This method is responsible for any additional modifications or transformations needed
317
+ after the manifest has been validated and before it is used in the source.
318
+ """
319
+ # apply manifest migration, if required
320
+ self._migrate_manifest()
321
+ # apply manifest normalization, if required
322
+ self._normalize_manifest()
323
+
324
+ def _migrate_manifest(self) -> None:
325
+ """
326
+ This method is used to migrate the manifest. It should be called after the manifest has been validated.
327
+ The migration is done in place, so the original manifest is modified.
328
+
329
+ The original manifest is returned if any error occurs during migration.
330
+ """
331
+ if self._should_migrate:
332
+ manifest_migrator = ManifestMigrationHandler(self._source_config)
333
+ self._source_config = manifest_migrator.apply_migrations()
334
+ # validate migrated manifest against the declarative component schema
335
+ self._validate_source()
336
+
337
+ def _normalize_manifest(self) -> None:
338
+ """
339
+ This method is used to normalize the manifest. It should be called after the manifest has been validated.
340
+
341
+ Connector Builder UI rendering requires the manifest to be in a specific format.
342
+ - references have been resolved
343
+ - the commonly used definitions are extracted to the `definitions.linked.*`
344
+ """
345
+ if self._should_normalize:
346
+ normalizer = ManifestNormalizer(self._source_config, self._declarative_component_schema)
347
+ self._source_config = normalizer.normalize()
348
+
349
+ def _validate_source(self) -> None:
350
+ """
351
+ Validates the connector manifest against the declarative component schema
352
+ """
353
+
354
+ try:
355
+ validate(self._source_config, self._declarative_component_schema)
356
+ except ValidationError as e:
357
+ raise ValidationError(
358
+ "Validation against json schema defined in declarative_component_schema.yaml schema failed"
359
+ ) from e
360
+
361
+ def _migrate_and_transform_config(
362
+ self,
363
+ config_path: Optional[str],
364
+ config: Optional[Config],
365
+ ) -> Optional[Config]:
366
+ if not config:
367
+ return None
368
+ if not self._spec_component:
369
+ return config
370
+ mutable_config = dict(config)
371
+ self._spec_component.migrate_config(mutable_config)
372
+ if mutable_config != config:
373
+ if config_path:
374
+ with open(config_path, "w") as f:
375
+ json.dump(mutable_config, f)
376
+ control_message = create_connector_config_control_message(mutable_config)
377
+ print(orjson.dumps(AirbyteMessageSerializer.dump(control_message)).decode())
378
+ self._spec_component.transform_config(mutable_config)
379
+ return mutable_config
380
+
381
+ def configure(self, config: Mapping[str, Any], temp_dir: str) -> Mapping[str, Any]:
382
+ config = self._config or config
383
+ return super().configure(config, temp_dir)
384
+
385
+ @property
386
+ def resolved_manifest(self) -> Mapping[str, Any]:
387
+ """
388
+ Returns the resolved manifest configuration for the source.
389
+
390
+ This property provides access to the internal source configuration as a mapping,
391
+ which contains all settings and parameters required to define the source's behavior.
392
+
393
+ Returns:
394
+ Mapping[str, Any]: The resolved source configuration manifest.
395
+ """
396
+ return self._source_config
397
+
398
+ # TODO: Deprecate this class once ConcurrentDeclarativeSource no longer inherits AbstractSource
399
+ @property
400
+ def message_repository(self) -> MessageRepository:
401
+ return self._message_repository
402
+
177
403
  # TODO: Remove this. This property is necessary to safely migrate Stripe during the transition state.
178
404
  @property
179
405
  def is_partially_declarative(self) -> bool:
180
406
  """This flag used to avoid unexpected AbstractStreamFacade processing as concurrent streams."""
181
407
  return False
182
408
 
409
+ def deprecation_warnings(self) -> List[ConnectorBuilderLogMessage]:
410
+ return self._constructor.get_model_deprecations()
411
+
183
412
  def read(
184
413
  self,
185
414
  logger: logging.Logger,
@@ -237,7 +466,140 @@ class ConcurrentDeclarativeSource(ManifestDeclarativeSource, Generic[TState]):
237
466
 
238
467
  In both case, we will assume that calling the DeclarativeStream is perfectly fine as the result for these is the same regardless of if it is a DeclarativeStream or a DefaultStream (concurrent). This should simply be removed once we have moved away from the mentioned code paths above.
239
468
  """
240
- return super().streams(config)
469
+
470
+ if self._spec_component:
471
+ self._spec_component.validate_config(config)
472
+
473
+ stream_configs = (
474
+ self._stream_configs(self._source_config, config=config) + self.dynamic_streams
475
+ )
476
+
477
+ api_budget_model = self._source_config.get("api_budget")
478
+ if api_budget_model:
479
+ self._constructor.set_api_budget(api_budget_model, config)
480
+
481
+ source_streams = [
482
+ self._constructor.create_component(
483
+ (
484
+ StateDelegatingStreamModel
485
+ if stream_config.get("type") == StateDelegatingStreamModel.__name__
486
+ else DeclarativeStreamModel
487
+ ),
488
+ stream_config,
489
+ config,
490
+ emit_connector_builder_messages=self._emit_connector_builder_messages,
491
+ )
492
+ for stream_config in self._initialize_cache_for_parent_streams(deepcopy(stream_configs))
493
+ ]
494
+ return source_streams
495
+
496
+ @staticmethod
497
+ def _initialize_cache_for_parent_streams(
498
+ stream_configs: List[Dict[str, Any]],
499
+ ) -> List[Dict[str, Any]]:
500
+ parent_streams = set()
501
+
502
+ def update_with_cache_parent_configs(
503
+ parent_configs: list[dict[str, Any]],
504
+ ) -> None:
505
+ for parent_config in parent_configs:
506
+ parent_streams.add(parent_config["stream"]["name"])
507
+ if parent_config["stream"]["type"] == "StateDelegatingStream":
508
+ parent_config["stream"]["full_refresh_stream"]["retriever"]["requester"][
509
+ "use_cache"
510
+ ] = True
511
+ parent_config["stream"]["incremental_stream"]["retriever"]["requester"][
512
+ "use_cache"
513
+ ] = True
514
+ else:
515
+ parent_config["stream"]["retriever"]["requester"]["use_cache"] = True
516
+
517
+ for stream_config in stream_configs:
518
+ if stream_config.get("incremental_sync", {}).get("parent_stream"):
519
+ parent_streams.add(stream_config["incremental_sync"]["parent_stream"]["name"])
520
+ stream_config["incremental_sync"]["parent_stream"]["retriever"]["requester"][
521
+ "use_cache"
522
+ ] = True
523
+
524
+ elif stream_config.get("retriever", {}).get("partition_router", {}):
525
+ partition_router = stream_config["retriever"]["partition_router"]
526
+
527
+ if isinstance(partition_router, dict) and partition_router.get(
528
+ "parent_stream_configs"
529
+ ):
530
+ update_with_cache_parent_configs(partition_router["parent_stream_configs"])
531
+ elif isinstance(partition_router, list):
532
+ for router in partition_router:
533
+ if router.get("parent_stream_configs"):
534
+ update_with_cache_parent_configs(router["parent_stream_configs"])
535
+
536
+ for stream_config in stream_configs:
537
+ if stream_config["name"] in parent_streams:
538
+ if stream_config["type"] == "StateDelegatingStream":
539
+ stream_config["full_refresh_stream"]["retriever"]["requester"]["use_cache"] = (
540
+ True
541
+ )
542
+ stream_config["incremental_stream"]["retriever"]["requester"]["use_cache"] = (
543
+ True
544
+ )
545
+ else:
546
+ stream_config["retriever"]["requester"]["use_cache"] = True
547
+ return stream_configs
548
+
549
+ def spec(self, logger: logging.Logger) -> ConnectorSpecification:
550
+ """
551
+ Returns the connector specification (spec) as defined in the Airbyte Protocol. The spec is an object describing the possible
552
+ configurations (e.g: username and password) which can be configured when running this connector. For low-code connectors, this
553
+ will first attempt to load the spec from the manifest's spec block, otherwise it will load it from "spec.yaml" or "spec.json"
554
+ in the project root.
555
+ """
556
+ return (
557
+ self._spec_component.generate_spec() if self._spec_component else super().spec(logger)
558
+ )
559
+
560
+ def check(self, logger: logging.Logger, config: Mapping[str, Any]) -> AirbyteConnectionStatus:
561
+ return super().check(logger, config)
562
+
563
+ def check_connection(
564
+ self, logger: logging.Logger, config: Mapping[str, Any]
565
+ ) -> Tuple[bool, Any]:
566
+ """
567
+ :param logger: The source logger
568
+ :param config: The user-provided configuration as specified by the source's spec.
569
+ This usually contains information required to check connection e.g. tokens, secrets and keys etc.
570
+ :return: A tuple of (boolean, error). If boolean is true, then the connection check is successful
571
+ and we can connect to the underlying data source using the provided configuration.
572
+ Otherwise, the input config cannot be used to connect to the underlying data source,
573
+ and the "error" object should describe what went wrong.
574
+ The error object will be cast to string to display the problem to the user.
575
+ """
576
+ return self.connection_checker.check_connection(self, logger, config)
577
+
578
+ @property
579
+ def connection_checker(self) -> ConnectionChecker:
580
+ check = self._source_config["check"]
581
+ if "type" not in check:
582
+ check["type"] = "CheckStream"
583
+ check_stream = self._constructor.create_component(
584
+ COMPONENTS_CHECKER_TYPE_MAPPING[check["type"]],
585
+ check,
586
+ dict(),
587
+ emit_connector_builder_messages=self._emit_connector_builder_messages,
588
+ )
589
+ if isinstance(check_stream, ConnectionChecker):
590
+ return check_stream
591
+ else:
592
+ raise ValueError(
593
+ f"Expected to generate a ConnectionChecker component, but received {check_stream.__class__}"
594
+ )
595
+
596
+ @property
597
+ def dynamic_streams(self) -> List[Dict[str, Any]]:
598
+ return self._dynamic_stream_configs(
599
+ manifest=self._source_config,
600
+ config=self._config,
601
+ with_dynamic_stream_name=True,
602
+ )
241
603
 
242
604
  def _group_streams(
243
605
  self, config: Mapping[str, Any]
@@ -333,7 +695,7 @@ class ConcurrentDeclarativeSource(ManifestDeclarativeSource, Generic[TState]):
333
695
  stream_name=declarative_stream.name,
334
696
  schema_loader=declarative_stream._schema_loader, # type: ignore # We are accessing the private property but the public one is optional and we will remove this code soonish
335
697
  retriever=retriever,
336
- message_repository=self.message_repository,
698
+ message_repository=self._message_repository,
337
699
  max_records_limit=self._limits.max_records
338
700
  if self._limits
339
701
  else None,
@@ -370,7 +732,7 @@ class ConcurrentDeclarativeSource(ManifestDeclarativeSource, Generic[TState]):
370
732
  stream_name=declarative_stream.name,
371
733
  schema_loader=declarative_stream._schema_loader, # type: ignore # We are accessing the private property but the public one is optional and we will remove this code soonish
372
734
  retriever=retriever,
373
- message_repository=self.message_repository,
735
+ message_repository=self._message_repository,
374
736
  max_records_limit=self._limits.max_records
375
737
  if self._limits
376
738
  else None,
@@ -404,7 +766,7 @@ class ConcurrentDeclarativeSource(ManifestDeclarativeSource, Generic[TState]):
404
766
  stream_name=declarative_stream.name,
405
767
  schema_loader=declarative_stream._schema_loader, # type: ignore # We are accessing the private property but the public one is optional and we will remove this code soonish
406
768
  retriever=declarative_stream.retriever,
407
- message_repository=self.message_repository,
769
+ message_repository=self._message_repository,
408
770
  max_records_limit=self._limits.max_records if self._limits else None,
409
771
  ),
410
772
  declarative_stream.retriever.stream_slicer,
@@ -416,7 +778,7 @@ class ConcurrentDeclarativeSource(ManifestDeclarativeSource, Generic[TState]):
416
778
  final_state_cursor = FinalStateCursor(
417
779
  stream_name=declarative_stream.name,
418
780
  stream_namespace=declarative_stream.namespace,
419
- message_repository=self.message_repository,
781
+ message_repository=self._message_repository,
420
782
  )
421
783
 
422
784
  concurrent_streams.append(
@@ -468,7 +830,7 @@ class ConcurrentDeclarativeSource(ManifestDeclarativeSource, Generic[TState]):
468
830
  stream_name=declarative_stream.name,
469
831
  schema_loader=declarative_stream._schema_loader, # type: ignore # We are accessing the private property but the public one is optional and we will remove this code soonish
470
832
  retriever=retriever,
471
- message_repository=self.message_repository,
833
+ message_repository=self._message_repository,
472
834
  max_records_limit=self._limits.max_records if self._limits else None,
473
835
  ),
474
836
  perpartition_cursor,
@@ -502,6 +864,127 @@ class ConcurrentDeclarativeSource(ManifestDeclarativeSource, Generic[TState]):
502
864
 
503
865
  return concurrent_streams, synchronous_streams
504
866
 
867
+ def _stream_configs(
868
+ self, manifest: Mapping[str, Any], config: Mapping[str, Any]
869
+ ) -> List[Dict[str, Any]]:
870
+ # This has a warning flag for static, but after we finish part 4 we'll replace manifest with self._source_config
871
+ stream_configs = []
872
+ for current_stream_config in manifest.get("streams", []):
873
+ if (
874
+ "type" in current_stream_config
875
+ and current_stream_config["type"] == "ConditionalStreams"
876
+ ):
877
+ interpolated_boolean = InterpolatedBoolean(
878
+ condition=current_stream_config.get("condition"),
879
+ parameters={},
880
+ )
881
+
882
+ if interpolated_boolean.eval(config=config):
883
+ stream_configs.extend(current_stream_config.get("streams", []))
884
+ else:
885
+ if "type" not in current_stream_config:
886
+ current_stream_config["type"] = "DeclarativeStream"
887
+ stream_configs.append(current_stream_config)
888
+ return stream_configs
889
+
890
+ def _dynamic_stream_configs(
891
+ self,
892
+ manifest: Mapping[str, Any],
893
+ config: Mapping[str, Any],
894
+ with_dynamic_stream_name: Optional[bool] = None,
895
+ ) -> List[Dict[str, Any]]:
896
+ dynamic_stream_definitions: List[Dict[str, Any]] = manifest.get("dynamic_streams", [])
897
+ dynamic_stream_configs: List[Dict[str, Any]] = []
898
+ seen_dynamic_streams: Set[str] = set()
899
+
900
+ for dynamic_definition_index, dynamic_definition in enumerate(dynamic_stream_definitions):
901
+ components_resolver_config = dynamic_definition["components_resolver"]
902
+
903
+ if not components_resolver_config:
904
+ raise ValueError(
905
+ f"Missing 'components_resolver' in dynamic definition: {dynamic_definition}"
906
+ )
907
+
908
+ resolver_type = components_resolver_config.get("type")
909
+ if not resolver_type:
910
+ raise ValueError(
911
+ f"Missing 'type' in components resolver configuration: {components_resolver_config}"
912
+ )
913
+
914
+ if resolver_type not in COMPONENTS_RESOLVER_TYPE_MAPPING:
915
+ raise ValueError(
916
+ f"Invalid components resolver type '{resolver_type}'. "
917
+ f"Expected one of {list(COMPONENTS_RESOLVER_TYPE_MAPPING.keys())}."
918
+ )
919
+
920
+ if "retriever" in components_resolver_config:
921
+ components_resolver_config["retriever"]["requester"]["use_cache"] = True
922
+
923
+ # Create a resolver for dynamic components based on type
924
+ if resolver_type == "HttpComponentsResolver":
925
+ components_resolver = self._constructor.create_component(
926
+ model_type=COMPONENTS_RESOLVER_TYPE_MAPPING[resolver_type],
927
+ component_definition=components_resolver_config,
928
+ config=config,
929
+ stream_name=dynamic_definition.get("name"),
930
+ )
931
+ else:
932
+ components_resolver = self._constructor.create_component(
933
+ model_type=COMPONENTS_RESOLVER_TYPE_MAPPING[resolver_type],
934
+ component_definition=components_resolver_config,
935
+ config=config,
936
+ )
937
+
938
+ stream_template_config = dynamic_definition["stream_template"]
939
+
940
+ for dynamic_stream in components_resolver.resolve_components(
941
+ stream_template_config=stream_template_config
942
+ ):
943
+ # Get the use_parent_parameters configuration from the dynamic definition
944
+ # Default to True for backward compatibility, since connectors were already using it by default when this param was added
945
+ use_parent_parameters = dynamic_definition.get("use_parent_parameters", True)
946
+
947
+ dynamic_stream = {
948
+ **ManifestComponentTransformer().propagate_types_and_parameters(
949
+ "", dynamic_stream, {}, use_parent_parameters=use_parent_parameters
950
+ )
951
+ }
952
+
953
+ if "type" not in dynamic_stream:
954
+ dynamic_stream["type"] = "DeclarativeStream"
955
+
956
+ # Ensure that each stream is created with a unique name
957
+ name = dynamic_stream.get("name")
958
+
959
+ if with_dynamic_stream_name:
960
+ dynamic_stream["dynamic_stream_name"] = dynamic_definition.get(
961
+ "name", f"dynamic_stream_{dynamic_definition_index}"
962
+ )
963
+
964
+ if not isinstance(name, str):
965
+ raise ValueError(
966
+ f"Expected stream name {name} to be a string, got {type(name)}."
967
+ )
968
+
969
+ if name in seen_dynamic_streams:
970
+ error_message = f"Dynamic streams list contains a duplicate name: {name}. Please contact Airbyte Support."
971
+ failure_type = FailureType.system_error
972
+
973
+ if resolver_type == "ConfigComponentsResolver":
974
+ error_message = f"Dynamic streams list contains a duplicate name: {name}. Please check your configuration."
975
+ failure_type = FailureType.config_error
976
+
977
+ raise AirbyteTracedException(
978
+ message=error_message,
979
+ internal_message=error_message,
980
+ failure_type=failure_type,
981
+ )
982
+
983
+ seen_dynamic_streams.add(name)
984
+ dynamic_stream_configs.append(dynamic_stream)
985
+
986
+ return dynamic_stream_configs
987
+
505
988
  def _is_concurrent_cursor_incremental_without_partition_routing(
506
989
  self,
507
990
  declarative_stream: DeclarativeStream,
@@ -2,6 +2,10 @@
2
2
  # Copyright (c) 2022 Airbyte, Inc., all rights reserved.
3
3
  #
4
4
 
5
+ from airbyte_cdk.legacy.sources.declarative.incremental.per_partition_cursor import (
6
+ CursorFactory,
7
+ PerPartitionCursor,
8
+ )
5
9
  from airbyte_cdk.sources.declarative.incremental.concurrent_partition_cursor import (
6
10
  ConcurrentCursorFactory,
7
11
  ConcurrentPerPartitionCursor,
@@ -11,10 +15,6 @@ from airbyte_cdk.sources.declarative.incremental.declarative_cursor import Decla
11
15
  from airbyte_cdk.sources.declarative.incremental.global_substream_cursor import (
12
16
  GlobalSubstreamCursor,
13
17
  )
14
- from airbyte_cdk.sources.declarative.incremental.per_partition_cursor import (
15
- CursorFactory,
16
- PerPartitionCursor,
17
- )
18
18
  from airbyte_cdk.sources.declarative.incremental.per_partition_with_global import (
19
19
  PerPartitionWithGlobalCursor,
20
20
  )
@@ -3,16 +3,16 @@
3
3
  #
4
4
  from typing import Any, Iterable, Mapping, MutableMapping, Optional, Union
5
5
 
6
+ from airbyte_cdk.legacy.sources.declarative.incremental.per_partition_cursor import (
7
+ CursorFactory,
8
+ PerPartitionCursor,
9
+ )
6
10
  from airbyte_cdk.sources.declarative.incremental.datetime_based_cursor import DatetimeBasedCursor
7
11
  from airbyte_cdk.sources.declarative.incremental.declarative_cursor import DeclarativeCursor
8
12
  from airbyte_cdk.sources.declarative.incremental.global_substream_cursor import (
9
13
  GlobalSubstreamCursor,
10
14
  iterate_with_last_flag_and_state,
11
15
  )
12
- from airbyte_cdk.sources.declarative.incremental.per_partition_cursor import (
13
- CursorFactory,
14
- PerPartitionCursor,
15
- )
16
16
  from airbyte_cdk.sources.declarative.partition_routers.partition_router import PartitionRouter
17
17
  from airbyte_cdk.sources.types import Record, StreamSlice, StreamState
18
18
 
@@ -1278,6 +1278,12 @@ class ModelToComponentFactory:
1278
1278
  f"Expected manifest component of type {model_type.__name__}, but received {component_type} instead"
1279
1279
  )
1280
1280
 
1281
+ # FIXME the interfaces of the concurrent cursor are kind of annoying as they take a `ComponentDefinition` instead of the actual model. This was done because the ConcurrentDeclarativeSource didn't have access to the models [here for example](https://github.com/airbytehq/airbyte-python-cdk/blob/f525803b3fec9329e4cc8478996a92bf884bfde9/airbyte_cdk/sources/declarative/concurrent_declarative_source.py#L354C54-L354C91). So now we have two cases:
1282
+ # * The ComponentDefinition comes from model.__dict__ in which case we have `parameters`
1283
+ # * The ComponentDefinition comes from the manifest as a dict in which case we have `$parameters`
1284
+ # We should change those interfaces to use the model once we clean up the code in CDS at which point the parameter propagation should happen as part of the ModelToComponentFactory.
1285
+ if "$parameters" not in component_definition and "parameters" in component_definition:
1286
+ component_definition["$parameters"] = component_definition.get("parameters") # type: ignore # This is a dict
1281
1287
  datetime_based_cursor_model = model_type.parse_obj(component_definition)
1282
1288
 
1283
1289
  if not isinstance(datetime_based_cursor_model, DatetimeBasedCursorModel):
@@ -1582,6 +1588,12 @@ class ModelToComponentFactory:
1582
1588
  f"Expected manifest component of type {model_type.__name__}, but received {component_type} instead"
1583
1589
  )
1584
1590
 
1591
+ # FIXME the interfaces of the concurrent cursor are kind of annoying as they take a `ComponentDefinition` instead of the actual model. This was done because the ConcurrentDeclarativeSource didn't have access to the models [here for example](https://github.com/airbytehq/airbyte-python-cdk/blob/f525803b3fec9329e4cc8478996a92bf884bfde9/airbyte_cdk/sources/declarative/concurrent_declarative_source.py#L354C54-L354C91). So now we have two cases:
1592
+ # * The ComponentDefinition comes from model.__dict__ in which case we have `parameters`
1593
+ # * The ComponentDefinition comes from the manifest as a dict in which case we have `$parameters`
1594
+ # We should change those interfaces to use the model once we clean up the code in CDS at which point the parameter propagation should happen as part of the ModelToComponentFactory.
1595
+ if "$parameters" not in component_definition and "parameters" in component_definition:
1596
+ component_definition["$parameters"] = component_definition.get("parameters") # type: ignore # This is a dict
1585
1597
  datetime_based_cursor_model = model_type.parse_obj(component_definition)
1586
1598
 
1587
1599
  if not isinstance(datetime_based_cursor_model, DatetimeBasedCursorModel):
@@ -5,9 +5,8 @@
5
5
  from abc import abstractmethod
6
6
  from typing import Any, Iterable, Mapping, Optional
7
7
 
8
- from airbyte_cdk.sources.declarative.incremental.per_partition_cursor import StreamSlice
9
8
  from airbyte_cdk.sources.streams.core import StreamData
10
- from airbyte_cdk.sources.types import StreamState
9
+ from airbyte_cdk.sources.types import StreamSlice, StreamState
11
10
 
12
11
 
13
12
  class Retriever:
@@ -56,6 +56,27 @@ from airbyte_cdk.utils.traced_exception import AirbyteTracedException
56
56
  BODY_REQUEST_METHODS = ("GET", "POST", "PUT", "PATCH")
57
57
 
58
58
 
59
+ def monkey_patched_get_item(self, key): # type: ignore # this interface is a copy/paste from the requests_cache lib
60
+ """
61
+ con.execute can lead to `sqlite3.InterfaceError: bad parameter or other API misuse`. There was a fix implemented
62
+ [here](https://github.com/requests-cache/requests-cache/commit/5ca6b9cdcb2797dd2fed485872110ccd72aee55d#diff-f43db4a5edf931647c32dec28ea7557aae4cae8444af4b26c8ecbe88d8c925aaL330-R332)
63
+ but there is still no official releases of requests_cache that this is part of. Hence, we will monkeypatch it for now.
64
+ """
65
+ with self.connection() as con:
66
+ # Using placeholders here with python 3.12+ and concurrency results in the error:
67
+ # sqlite3.InterfaceError: bad parameter or other API misuse
68
+ cur = con.execute(f"SELECT value FROM {self.table_name} WHERE key='{key}'")
69
+ row = cur.fetchone()
70
+ cur.close()
71
+ if not row:
72
+ raise KeyError(key)
73
+
74
+ return self.deserialize(key, row[0])
75
+
76
+
77
+ requests_cache.SQLiteDict.__getitem__ = monkey_patched_get_item # type: ignore # see the method doc for more information
78
+
79
+
59
80
  class MessageRepresentationAirbyteTracedErrors(AirbyteTracedException):
60
81
  """
61
82
  Before the migration to the HttpClient in low-code, the exception raised was
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: airbyte-cdk
3
- Version: 6.61.1
3
+ Version: 6.61.2
4
4
  Summary: A framework for writing Airbyte Connectors.
5
5
  Home-page: https://airbyte.com
6
6
  License: MIT
@@ -1,4 +1,4 @@
1
- airbyte_cdk/__init__.py,sha256=lZNgioD1bHnzRbPK-TXjo7JOVKEL8mz9b5uflj-wIFI,12232
1
+ airbyte_cdk/__init__.py,sha256=G15TRy6OMNpo0F8rQugXcZpNHwl1DUQP-oUrSMCWD2E,12112
2
2
  airbyte_cdk/cli/__init__.py,sha256=CXsai3MYMLZ_sqi2vPAIVcKDun8VRqlv0cKffBI0iSY,346
3
3
  airbyte_cdk/cli/airbyte_cdk/__init__.py,sha256=8IoEcbdYr7CMAh97Xut5__uHH9vV4LKUtSBNTk3qEWY,2031
4
4
  airbyte_cdk/cli/airbyte_cdk/_connector.py,sha256=3AaKtp7QIuJcVrk7eHB9JokDyw2mTSBNiHCdpreL5no,6500
@@ -15,13 +15,13 @@ airbyte_cdk/config_observation.py,sha256=7SSPxtN0nXPkm4euGNcTTr1iLbwUL01jy-24V1H
15
15
  airbyte_cdk/connector.py,sha256=N6TUlrZOMjLAI85JrNAKkfyTqnO5xfBCw4oEfgjJd9o,4254
16
16
  airbyte_cdk/connector_builder/README.md,sha256=Hw3wvVewuHG9-QgsAq1jDiKuLlStDxKBz52ftyNRnBw,1665
17
17
  airbyte_cdk/connector_builder/__init__.py,sha256=4Hw-PX1-VgESLF16cDdvuYCzGJtHntThLF4qIiULWeo,61
18
- airbyte_cdk/connector_builder/connector_builder_handler.py,sha256=jRtSfj3aca006-01Hax-THJpuoysd8QR6JPGnr8q1Xg,6371
19
- airbyte_cdk/connector_builder/main.py,sha256=F9bmdz252pvGXAdDgPwIOPw3fl5fwTU41uG49BQyItI,3883
18
+ airbyte_cdk/connector_builder/connector_builder_handler.py,sha256=B5Zpt9u81n8BJjRCF-EvUUk0Y4-tTp6mPx0dSPP-pFg,6330
19
+ airbyte_cdk/connector_builder/main.py,sha256=IkudoYUvjFieB9RJaHPrKd95yC90f9jOb6EBl1PaO7M,3935
20
20
  airbyte_cdk/connector_builder/models.py,sha256=9pIZ98LW_d6fRS39VdnUOf3cxGt4TkC5MJ0_OrzcCRk,1578
21
21
  airbyte_cdk/connector_builder/test_reader/__init__.py,sha256=iTwBMoI9vaJotEgpqZbFjlxRcbxXYypSVJ9YxeHk7wc,120
22
22
  airbyte_cdk/connector_builder/test_reader/helpers.py,sha256=5GSrK9EVBDm5dwtudVbA-73EHh53-niRA-oj8eQVFHI,29236
23
23
  airbyte_cdk/connector_builder/test_reader/message_grouper.py,sha256=bFoQMKCXJob98O6F4tgMW81cCquNOqCx2tkNXP7lPqc,7062
24
- airbyte_cdk/connector_builder/test_reader/reader.py,sha256=DugoqS6SMrtOJ--2Y0F0h_9x8m632i7fSOPMAA0JHnc,21654
24
+ airbyte_cdk/connector_builder/test_reader/reader.py,sha256=Zuq0LCtrT-KHaELn2pVl7eQd39c_BwYbVeZBX7HEeeQ,21842
25
25
  airbyte_cdk/connector_builder/test_reader/types.py,sha256=hPZG3jO03kBaPyW94NI3JHRS1jxXGSNBcN1HFzOxo5Y,2528
26
26
  airbyte_cdk/destinations/__init__.py,sha256=FyDp28PT_YceJD5HDFhA-mrGfX9AONIyMQ4d68CHNxQ,213
27
27
  airbyte_cdk/destinations/destination.py,sha256=CIq-yb8C_0QvcKCtmStaHfiqn53GEfRAIGGCkJhKP1Q,5880
@@ -36,6 +36,13 @@ airbyte_cdk/destinations/vector_db_based/utils.py,sha256=FOyEo8Lc-fY8UyhpCivhZtI
36
36
  airbyte_cdk/destinations/vector_db_based/writer.py,sha256=nZ00xPiohElJmYktEZZIhr0m5EDETCHGhg0Lb2S7A20,5095
37
37
  airbyte_cdk/entrypoint.py,sha256=dxPRrHJGQfkLmno-n3AF-J0cHgKDoTtG6PwPMNEFB7o,19842
38
38
  airbyte_cdk/exception_handler.py,sha256=D_doVl3Dt60ASXlJsfviOCswxGyKF2q0RL6rif3fNks,2013
39
+ airbyte_cdk/legacy/__init__.py,sha256=952Q-F0w7H-z0JUR2ZOQjreGokdGQa9B-LPxWFvaac0,57
40
+ airbyte_cdk/legacy/sources/__init__.py,sha256=952Q-F0w7H-z0JUR2ZOQjreGokdGQa9B-LPxWFvaac0,57
41
+ airbyte_cdk/legacy/sources/declarative/__init__.py,sha256=952Q-F0w7H-z0JUR2ZOQjreGokdGQa9B-LPxWFvaac0,57
42
+ airbyte_cdk/legacy/sources/declarative/declarative_source.py,sha256=qmyMnnet92eGc3C22yBtpvD5UZjqdhsAafP_zxI5wp8,1814
43
+ airbyte_cdk/legacy/sources/declarative/incremental/__init__.py,sha256=952Q-F0w7H-z0JUR2ZOQjreGokdGQa9B-LPxWFvaac0,57
44
+ airbyte_cdk/legacy/sources/declarative/incremental/per_partition_cursor.py,sha256=DW56OT7H6_lE2CZagXitaSv8B9pAB6P78Y5TSI5qHBg,16937
45
+ airbyte_cdk/legacy/sources/declarative/manifest_declarative_source.py,sha256=pkAYkQ-FXyyrBfe5jLf4gbFstcG0q38h6H9XWhJoL10,27161
39
46
  airbyte_cdk/logger.py,sha256=V8E7yO4RerDkyELzFv-NCJx4vEp9dhMkGAbfOnn9Krc,5080
40
47
  airbyte_cdk/manifest_migrations/README.md,sha256=YX1h0xyc4jHdwH3I25ZHqB7R3hcUUCHMvnexpfzF2E8,3020
41
48
  airbyte_cdk/manifest_migrations/__init__.py,sha256=0eq9ic_6GGXMwzE31eAOSA7PLtBauMfgM9XshjYHF84,61
@@ -86,12 +93,11 @@ airbyte_cdk/sources/declarative/checks/check_stream.py,sha256=sV-ZY7dZ03V8GdAxPY
86
93
  airbyte_cdk/sources/declarative/checks/connection_checker.py,sha256=MBRJo6WJlZQHpIfOGaNOkkHUmgUl_4wDM6VPo41z5Ss,1383
87
94
  airbyte_cdk/sources/declarative/concurrency_level/__init__.py,sha256=5XUqrmlstYlMM0j6crktlKQwALek0uiz2D3WdM46MyA,191
88
95
  airbyte_cdk/sources/declarative/concurrency_level/concurrency_level.py,sha256=YIwCTCpOr_QSNW4ltQK0yUGWInI8PKNY216HOOegYLk,2101
89
- airbyte_cdk/sources/declarative/concurrent_declarative_source.py,sha256=5nvL9wbcAnReCeXM1Krezvyh-Plgx_yPS-CVhO_thHY,31043
96
+ airbyte_cdk/sources/declarative/concurrent_declarative_source.py,sha256=3Trf6Qh5thzJM-NitcmbBGomQ06w_B_XCWDwenw4vvA,52912
90
97
  airbyte_cdk/sources/declarative/datetime/__init__.py,sha256=4Hw-PX1-VgESLF16cDdvuYCzGJtHntThLF4qIiULWeo,61
91
98
  airbyte_cdk/sources/declarative/datetime/datetime_parser.py,sha256=_zGNGq31RNy_0QBLt_EcTvgPyhj7urPdx6oA3M5-r3o,3150
92
99
  airbyte_cdk/sources/declarative/datetime/min_max_datetime.py,sha256=0BHBtDNQZfvwM45-tY5pNlTcKAFSGGNxemoi0Jic-0E,5785
93
100
  airbyte_cdk/sources/declarative/declarative_component_schema.yaml,sha256=vA7Rt2XswBk027Hw61i-0A2sJW5ohptlCAI77xLoQkU,188222
94
- airbyte_cdk/sources/declarative/declarative_source.py,sha256=qmyMnnet92eGc3C22yBtpvD5UZjqdhsAafP_zxI5wp8,1814
95
101
  airbyte_cdk/sources/declarative/declarative_stream.py,sha256=dCRlddBUSaJmBNBz1pSO1r2rTw8AP5d2_vlmIeGs2gg,10767
96
102
  airbyte_cdk/sources/declarative/decoders/__init__.py,sha256=JHb_0d3SE6kNY10mxA5YBEKPeSbsWYjByq1gUQxepoE,953
97
103
  airbyte_cdk/sources/declarative/decoders/composite_raw_decoder.py,sha256=qB4lRUrCXLTE-a3VlpOLaazHiC7RIF_FIVJesuz7ebw,8078
@@ -111,13 +117,12 @@ airbyte_cdk/sources/declarative/extractors/record_filter.py,sha256=sNLGjFX0fnqO_
111
117
  airbyte_cdk/sources/declarative/extractors/record_selector.py,sha256=vCpwX1PVRFPYKMzm0DHHP3YEZ0Gmd3bBzggOsRha038,7192
112
118
  airbyte_cdk/sources/declarative/extractors/response_to_file_extractor.py,sha256=WJyA2OYIEgFpVP5Y3o0tIj69AV6IKkn9B16MeXaEItI,6513
113
119
  airbyte_cdk/sources/declarative/extractors/type_transformer.py,sha256=d6Y2Rfg8pMVEEnHllfVksWZdNVOU55yk34O03dP9muY,1626
114
- airbyte_cdk/sources/declarative/incremental/__init__.py,sha256=U1oZKtBaEC6IACmvziY9Wzg7Z8EgF4ZuR7NwvjlB_Sk,1255
120
+ airbyte_cdk/sources/declarative/incremental/__init__.py,sha256=UVP6mMZXP1lb8y2iCNjyPwoVn67ricNZ0m3529HM5ng,1262
115
121
  airbyte_cdk/sources/declarative/incremental/concurrent_partition_cursor.py,sha256=LagQ5ON8zdsltOg81fmc7FX--C38gfdo4QLeT2E_Qas,23622
116
122
  airbyte_cdk/sources/declarative/incremental/datetime_based_cursor.py,sha256=AD5qJSryosA9p3rzdl_vX60uwG9_mOk5Q8sGD8XSTjE,21592
117
123
  airbyte_cdk/sources/declarative/incremental/declarative_cursor.py,sha256=5Bhw9VRPyIuCaD0wmmq_L3DZsa-rJgtKSEUzSd8YYD0,536
118
124
  airbyte_cdk/sources/declarative/incremental/global_substream_cursor.py,sha256=69XbGqqTHBCSXi4MV6qO7uTEsTUPRN7uML0VJDjl8qU,15809
119
- airbyte_cdk/sources/declarative/incremental/per_partition_cursor.py,sha256=DW56OT7H6_lE2CZagXitaSv8B9pAB6P78Y5TSI5qHBg,16937
120
- airbyte_cdk/sources/declarative/incremental/per_partition_with_global.py,sha256=rjsH7XwrJON5lXVfU0FIrEGglNesVzlr8hfwS5_A9sY,8210
125
+ airbyte_cdk/sources/declarative/incremental/per_partition_with_global.py,sha256=zVo1eSBbpVZEB5IrfgKwoo-HcJDQlVUmD5oucZZflVY,8217
121
126
  airbyte_cdk/sources/declarative/incremental/resumable_full_refresh_cursor.py,sha256=I8AwJkbl9AKbYYqwZUSP7hLoRMMuNkDQ90Zv5Z7CWdo,4609
122
127
  airbyte_cdk/sources/declarative/interpolation/__init__.py,sha256=Kh7FxhfetyNVDnAQ9zSxNe4oUbb8CvoW7Mqz7cs2iPg,437
123
128
  airbyte_cdk/sources/declarative/interpolation/filters.py,sha256=cYap5zzOxIJWCLIfbkNlpyfUhjZ8FklLroIG4WGzYVs,5537
@@ -128,7 +133,6 @@ airbyte_cdk/sources/declarative/interpolation/interpolated_string.py,sha256=CQkH
128
133
  airbyte_cdk/sources/declarative/interpolation/interpolation.py,sha256=9IoeuWam3L6GyN10L6U8xNWXmkt9cnahSDNkez1OmFY,982
129
134
  airbyte_cdk/sources/declarative/interpolation/jinja.py,sha256=oFGKs3oX0xO6DOL4E9x8rhxwbEoRcgx4HJVIL1RQ9c4,7269
130
135
  airbyte_cdk/sources/declarative/interpolation/macros.py,sha256=RpsAYG75bW0js2fQCzAN1nf3oeGyXwyt0LhJCHnlaUA,6031
131
- airbyte_cdk/sources/declarative/manifest_declarative_source.py,sha256=VqR3lti_RLRRe0_1EwUn8_OsJTxQrGqU3n-T9GowAKk,27154
132
136
  airbyte_cdk/sources/declarative/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
133
137
  airbyte_cdk/sources/declarative/migrations/legacy_to_per_partition_state_migration.py,sha256=V2lpYE9LJKvz6BUViHk4vaRGndxNABmPbDCtyYdkqaE,4013
134
138
  airbyte_cdk/sources/declarative/migrations/state_migration.py,sha256=KWPjealMLKSMtajXgkdGgKg7EmTLR-CqqD7UIh0-eDU,794
@@ -141,7 +145,7 @@ airbyte_cdk/sources/declarative/parsers/custom_exceptions.py,sha256=wnRUP0Xeru9R
141
145
  airbyte_cdk/sources/declarative/parsers/manifest_component_transformer.py,sha256=2UdpCz3yi7ISZTyqkQXSSy3dMxeyOWqV7OlAS5b9GVg,11568
142
146
  airbyte_cdk/sources/declarative/parsers/manifest_normalizer.py,sha256=EtKjS9c94yNp3AwQC8KUCQaAYW5T3zvFYxoWYjc_buI,19729
143
147
  airbyte_cdk/sources/declarative/parsers/manifest_reference_resolver.py,sha256=pJmg78vqE5VfUrF_KJnWjucQ4k9IWFULeAxHCowrHXE,6806
144
- airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py,sha256=dh8lCF7ZyhXW-euR_v4MqoHY2zQsPOeSPA8Hm4Yu4h8,184820
148
+ airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py,sha256=BTJ9CtBOukVrOhgkxejbXjpDvLgMVDt6nseR8K2k3xo,186944
145
149
  airbyte_cdk/sources/declarative/partition_routers/__init__.py,sha256=TBC9AkGaUqHm2IKHMPN6punBIcY5tWGULowcLoAVkfw,1109
146
150
  airbyte_cdk/sources/declarative/partition_routers/async_job_partition_router.py,sha256=VelO7zKqKtzMJ35jyFeg0ypJLQC0plqqIBNXoBW1G2E,3001
147
151
  airbyte_cdk/sources/declarative/partition_routers/cartesian_product_stream_slicer.py,sha256=c5cuVFM6NFkuQqG8Z5IwkBuwDrvXZN1CunUOM_L0ezg,6892
@@ -208,7 +212,7 @@ airbyte_cdk/sources/declarative/retrievers/file_uploader/file_uploader.py,sha256
208
212
  airbyte_cdk/sources/declarative/retrievers/file_uploader/file_writer.py,sha256=V8gAFjQXkhX5mwj1NafdcUrMfMBNF1hi0mrdXIl5qEc,359
209
213
  airbyte_cdk/sources/declarative/retrievers/file_uploader/local_file_system_file_writer.py,sha256=jLpdonre1UHfbjGSD5AK_T0codLABJByTvbqepDZtEQ,422
210
214
  airbyte_cdk/sources/declarative/retrievers/file_uploader/noop_file_writer.py,sha256=1yfimzxm09d2j605cu_HhiYVDNVL1rUMi3vs_jYlIyY,330
211
- airbyte_cdk/sources/declarative/retrievers/retriever.py,sha256=XPLs593Xv8c5cKMc37XzUAYmzlXd1a7eSsspM-CMuWA,1696
215
+ airbyte_cdk/sources/declarative/retrievers/retriever.py,sha256=mrwMbKU0t1_SGRuyID7Hf3GaGvhG4kqHaDWaEzRt6dA,1620
212
216
  airbyte_cdk/sources/declarative/retrievers/simple_retriever.py,sha256=1-owE0r2RA8AsP8Yc6CVjNRNodMcOFl0RBCgCJY5MAY,27505
213
217
  airbyte_cdk/sources/declarative/schema/__init__.py,sha256=xU45UvM5O4c1PSM13UHpCdh5hpW3HXy9vRRGEiAC1rg,795
214
218
  airbyte_cdk/sources/declarative/schema/composite_schema_loader.py,sha256=ymGbvxS_QyGc4nnjEyRo5ch8bVedELO41PAUxKXZyMw,1113
@@ -350,7 +354,7 @@ airbyte_cdk/sources/streams/http/error_handlers/json_error_message_parser.py,sha
350
354
  airbyte_cdk/sources/streams/http/error_handlers/response_models.py,sha256=xGIVELBFY0TmH9aUq1ikoqJz8oHLr6di2JLvKWVEO-s,2236
351
355
  airbyte_cdk/sources/streams/http/exceptions.py,sha256=njC7MlMJoFYcSGz4mIp6-bqLFTr6vC8ej25X0oSeyjE,1824
352
356
  airbyte_cdk/sources/streams/http/http.py,sha256=0uariNq8OFnlX7iqOHwBhecxA-Hfd5hSY8_XCEgn3jI,28499
353
- airbyte_cdk/sources/streams/http/http_client.py,sha256=FXOBMBocszQkbK1ENfcMBQoHtL1EUWA4KsI6oJE8Ni8,23071
357
+ airbyte_cdk/sources/streams/http/http_client.py,sha256=8E8RBDzkBUgvDqFA5OgVBEvCjGbcUeuQPiCkDLHg31U,24182
354
358
  airbyte_cdk/sources/streams/http/rate_limiting.py,sha256=IwdjrHKUnU97XO4qONgYRv4YYW51xQ8SJm4WLafXDB8,6351
355
359
  airbyte_cdk/sources/streams/http/requests_native_auth/__init__.py,sha256=RN0D3nOX1xLgwEwKWu6pkGy3XqBFzKSNZ8Lf6umU2eY,413
356
360
  airbyte_cdk/sources/streams/http/requests_native_auth/abstract_oauth.py,sha256=0WfnxuxDwRYeq-PIwdUjJujDnxuJPhNfHlX_8aNHtYU,19663
@@ -425,9 +429,9 @@ airbyte_cdk/utils/slice_hasher.py,sha256=EDxgROHDbfG-QKQb59m7h_7crN1tRiawdf5uU7G
425
429
  airbyte_cdk/utils/spec_schema_transformations.py,sha256=-5HTuNsnDBAhj-oLeQXwpTGA0HdcjFOf2zTEMUTTg_Y,816
426
430
  airbyte_cdk/utils/stream_status_utils.py,sha256=ZmBoiy5HVbUEHAMrUONxZvxnvfV9CesmQJLDTAIWnWw,1171
427
431
  airbyte_cdk/utils/traced_exception.py,sha256=C8uIBuCL_E4WnBAOPSxBicD06JAldoN9fGsQDp463OY,6292
428
- airbyte_cdk-6.61.1.dist-info/LICENSE.txt,sha256=Wfe61S4BaGPj404v8lrAbvhjYR68SHlkzeYrg3_bbuM,1051
429
- airbyte_cdk-6.61.1.dist-info/LICENSE_SHORT,sha256=aqF6D1NcESmpn-cqsxBtszTEnHKnlsp8L4x9wAh3Nxg,55
430
- airbyte_cdk-6.61.1.dist-info/METADATA,sha256=ZaU6cQVOtVQiXUd8WXEI_dIFAN2Gwm6XP_FB-cTVKfM,6477
431
- airbyte_cdk-6.61.1.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
432
- airbyte_cdk-6.61.1.dist-info/entry_points.txt,sha256=AKWbEkHfpzzk9nF9tqBUaw1MbvTM4mGtEzmZQm0ZWvM,139
433
- airbyte_cdk-6.61.1.dist-info/RECORD,,
432
+ airbyte_cdk-6.61.2.dist-info/LICENSE.txt,sha256=Wfe61S4BaGPj404v8lrAbvhjYR68SHlkzeYrg3_bbuM,1051
433
+ airbyte_cdk-6.61.2.dist-info/LICENSE_SHORT,sha256=aqF6D1NcESmpn-cqsxBtszTEnHKnlsp8L4x9wAh3Nxg,55
434
+ airbyte_cdk-6.61.2.dist-info/METADATA,sha256=jrR3TXXz1BFLlHY2ls02vKhGHZRKFAqSfJ7en-6zfHY,6477
435
+ airbyte_cdk-6.61.2.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
436
+ airbyte_cdk-6.61.2.dist-info/entry_points.txt,sha256=AKWbEkHfpzzk9nF9tqBUaw1MbvTM4mGtEzmZQm0ZWvM,139
437
+ airbyte_cdk-6.61.2.dist-info/RECORD,,