airbyte-cdk 6.8.2rc1__py3-none-any.whl → 6.9.0__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.
@@ -181,9 +181,13 @@ class ConcurrentDeclarativeSource(ManifestDeclarativeSource, Generic[TState]):
181
181
 
182
182
  state_manager = ConnectorStateManager(state=self._state) # type: ignore # state is always in the form of List[AirbyteStateMessage]. The ConnectorStateManager should use generics, but this can be done later
183
183
 
184
- name_to_stream_mapping = {
185
- stream["name"]: stream for stream in self.resolved_manifest["streams"]
186
- }
184
+ # Combine streams and dynamic_streams. Note: both cannot be empty at the same time,
185
+ # and this is validated during the initialization of the source.
186
+ streams = self._stream_configs(self._source_config) + self._dynamic_stream_configs(
187
+ self._source_config, config
188
+ )
189
+
190
+ name_to_stream_mapping = {stream["name"]: stream for stream in streams}
187
191
 
188
192
  for declarative_stream in self.streams(config=config):
189
193
  # Some low-code sources use a combination of DeclarativeStream and regular Python streams. We can't inspect
@@ -191,7 +195,7 @@ class ConcurrentDeclarativeSource(ManifestDeclarativeSource, Generic[TState]):
191
195
  # so we need to treat them as synchronous
192
196
  if (
193
197
  isinstance(declarative_stream, DeclarativeStream)
194
- and name_to_stream_mapping[declarative_stream.name].get("retriever")["type"]
198
+ and name_to_stream_mapping[declarative_stream.name]["retriever"]["type"]
195
199
  == "SimpleRetriever"
196
200
  ):
197
201
  incremental_sync_component_definition = name_to_stream_mapping[
@@ -200,7 +204,7 @@ class ConcurrentDeclarativeSource(ManifestDeclarativeSource, Generic[TState]):
200
204
 
201
205
  partition_router_component_definition = (
202
206
  name_to_stream_mapping[declarative_stream.name]
203
- .get("retriever")
207
+ .get("retriever", {})
204
208
  .get("partition_router")
205
209
  )
206
210
  is_without_partition_router_or_cursor = not bool(
@@ -222,7 +226,7 @@ class ConcurrentDeclarativeSource(ManifestDeclarativeSource, Generic[TState]):
222
226
  cursor = self._constructor.create_concurrent_cursor_from_datetime_based_cursor(
223
227
  state_manager=state_manager,
224
228
  model_type=DatetimeBasedCursorModel,
225
- component_definition=incremental_sync_component_definition,
229
+ component_definition=incremental_sync_component_definition, # type: ignore # Not None because of the if condition above
226
230
  stream_name=declarative_stream.name,
227
231
  stream_namespace=declarative_stream.namespace,
228
232
  config=config or {},
@@ -305,10 +309,11 @@ class ConcurrentDeclarativeSource(ManifestDeclarativeSource, Generic[TState]):
305
309
  def _is_datetime_incremental_without_partition_routing(
306
310
  self,
307
311
  declarative_stream: DeclarativeStream,
308
- incremental_sync_component_definition: Mapping[str, Any],
312
+ incremental_sync_component_definition: Mapping[str, Any] | None,
309
313
  ) -> bool:
310
314
  return (
311
- bool(incremental_sync_component_definition)
315
+ incremental_sync_component_definition is not None
316
+ and bool(incremental_sync_component_definition)
312
317
  and incremental_sync_component_definition.get("type", "")
313
318
  == DatetimeBasedCursorModel.__name__
314
319
  and self._stream_supports_concurrent_partition_processing(
@@ -7,8 +7,12 @@ version: 1.0.0
7
7
  required:
8
8
  - type
9
9
  - check
10
- - streams
11
10
  - version
11
+ anyOf:
12
+ - required:
13
+ - streams
14
+ - required:
15
+ - dynamic_streams
12
16
  properties:
13
17
  type:
14
18
  type: string
@@ -19,6 +23,10 @@ properties:
19
23
  type: array
20
24
  items:
21
25
  "$ref": "#/definitions/DeclarativeStream"
26
+ dynamic_streams:
27
+ type: array
28
+ items:
29
+ "$ref": "#/definitions/DynamicDeclarativeStream"
22
30
  version:
23
31
  type: string
24
32
  description: The version of the Airbyte CDK used to build and test the source.
@@ -1321,7 +1329,7 @@ definitions:
1321
1329
  type: array
1322
1330
  items:
1323
1331
  - type: string
1324
- interpolation_content:
1332
+ interpolation_context:
1325
1333
  - config
1326
1334
  examples:
1327
1335
  - ["data"]
@@ -2895,6 +2903,96 @@ definitions:
2895
2903
  $parameters:
2896
2904
  type: object
2897
2905
  additionalProperties: true
2906
+ ComponentMappingDefinition:
2907
+ title: Component Mapping Definition
2908
+ description: (This component is experimental. Use at your own risk.) Specifies a mapping definition to update or add fields in a record or configuration. This allows dynamic mapping of data by interpolating values into the template based on provided contexts.
2909
+ type: object
2910
+ required:
2911
+ - type
2912
+ - field_path
2913
+ - value
2914
+ properties:
2915
+ type:
2916
+ type: string
2917
+ enum: [ComponentMappingDefinition]
2918
+ field_path:
2919
+ title: Field Path
2920
+ description: A list of potentially nested fields indicating the full path where value will be added or updated.
2921
+ type: array
2922
+ items:
2923
+ - type: string
2924
+ interpolation_context:
2925
+ - config
2926
+ - components_values
2927
+ - stream_template_config
2928
+ examples:
2929
+ - ["data"]
2930
+ - ["data", "records"]
2931
+ - ["data", "{{ parameters.name }}"]
2932
+ - ["data", "*", "record"]
2933
+ value:
2934
+ title: Value
2935
+ description: The dynamic or static value to assign to the key. Interpolated values can be used to dynamically determine the value during runtime.
2936
+ type: string
2937
+ interpolation_context:
2938
+ - config
2939
+ - stream_template_config
2940
+ - components_values
2941
+ examples:
2942
+ - "{{ components_values['updates'] }}"
2943
+ - "{{ components_values['MetaData']['LastUpdatedTime'] }}"
2944
+ - "{{ config['segment_id'] }}"
2945
+ value_type:
2946
+ title: Value Type
2947
+ description: The expected data type of the value. If omitted, the type will be inferred from the value provided.
2948
+ "$ref": "#/definitions/ValueType"
2949
+ $parameters:
2950
+ type: object
2951
+ additionalProperties: true
2952
+ HttpComponentsResolver:
2953
+ type: object
2954
+ description: (This component is experimental. Use at your own risk.) Component resolve and populates stream templates with components fetched via an HTTP retriever.
2955
+ properties:
2956
+ type:
2957
+ type: string
2958
+ enum: [HttpComponentsResolver]
2959
+ retriever:
2960
+ title: Retriever
2961
+ description: Component used to coordinate how records are extracted across stream slices and request pages.
2962
+ anyOf:
2963
+ - "$ref": "#/definitions/AsyncRetriever"
2964
+ - "$ref": "#/definitions/CustomRetriever"
2965
+ - "$ref": "#/definitions/SimpleRetriever"
2966
+ components_mapping:
2967
+ type: array
2968
+ items:
2969
+ "$ref": "#/definitions/ComponentMappingDefinition"
2970
+ $parameters:
2971
+ type: object
2972
+ additionalProperties: true
2973
+ required:
2974
+ - type
2975
+ - retriever
2976
+ - components_mapping
2977
+ DynamicDeclarativeStream:
2978
+ type: object
2979
+ description: (This component is experimental. Use at your own risk.) A component that described how will be created declarative streams based on stream template.
2980
+ properties:
2981
+ type:
2982
+ type: string
2983
+ enum: [DynamicDeclarativeStream]
2984
+ stream_template:
2985
+ title: Stream Template
2986
+ description: Reference to the stream template.
2987
+ "$ref": "#/definitions/DeclarativeStream"
2988
+ components_resolver:
2989
+ title: Components Resolver
2990
+ description: Component resolve and populates stream templates with components values.
2991
+ "$ref": "#/definitions/HttpComponentsResolver"
2992
+ required:
2993
+ - type
2994
+ - stream_template
2995
+ - components_resolver
2898
2996
  interpolation:
2899
2997
  variables:
2900
2998
  - title: config
@@ -39,6 +39,7 @@ from airbyte_cdk.sources.declarative.parsers.manifest_reference_resolver import
39
39
  from airbyte_cdk.sources.declarative.parsers.model_to_component_factory import (
40
40
  ModelToComponentFactory,
41
41
  )
42
+ from airbyte_cdk.sources.declarative.resolvers import COMPONENTS_RESOLVER_TYPE_MAPPING
42
43
  from airbyte_cdk.sources.message import MessageRepository
43
44
  from airbyte_cdk.sources.streams.core import Stream
44
45
  from airbyte_cdk.sources.types import ConnectionDefinition
@@ -120,7 +121,10 @@ class ManifestDeclarativeSource(DeclarativeSource):
120
121
  self._emit_manifest_debug_message(
121
122
  extra_args={"source_name": self.name, "parsed_config": json.dumps(self._source_config)}
122
123
  )
123
- stream_configs = self._stream_configs(self._source_config)
124
+
125
+ stream_configs = self._stream_configs(self._source_config) + self._dynamic_stream_configs(
126
+ self._source_config, config
127
+ )
124
128
 
125
129
  source_streams = [
126
130
  self._constructor.create_component(
@@ -234,7 +238,8 @@ class ManifestDeclarativeSource(DeclarativeSource):
234
238
  )
235
239
 
236
240
  streams = self._source_config.get("streams")
237
- if not streams:
241
+ dynamic_streams = self._source_config.get("dynamic_streams")
242
+ if not (streams or dynamic_streams):
238
243
  raise ValidationError(
239
244
  f"A valid manifest should have at least one stream defined. Got {streams}"
240
245
  )
@@ -303,5 +308,51 @@ class ManifestDeclarativeSource(DeclarativeSource):
303
308
  s["type"] = "DeclarativeStream"
304
309
  return stream_configs
305
310
 
311
+ def _dynamic_stream_configs(
312
+ self, manifest: Mapping[str, Any], config: Mapping[str, Any]
313
+ ) -> List[Dict[str, Any]]:
314
+ dynamic_stream_definitions: List[Dict[str, Any]] = manifest.get("dynamic_streams", [])
315
+ dynamic_stream_configs: List[Dict[str, Any]] = []
316
+
317
+ for dynamic_definition in dynamic_stream_definitions:
318
+ components_resolver_config = dynamic_definition["components_resolver"]
319
+
320
+ if not components_resolver_config:
321
+ raise ValueError(
322
+ f"Missing 'components_resolver' in dynamic definition: {dynamic_definition}"
323
+ )
324
+
325
+ resolver_type = components_resolver_config.get("type")
326
+ if not resolver_type:
327
+ raise ValueError(
328
+ f"Missing 'type' in components resolver configuration: {components_resolver_config}"
329
+ )
330
+
331
+ if resolver_type not in COMPONENTS_RESOLVER_TYPE_MAPPING:
332
+ raise ValueError(
333
+ f"Invalid components resolver type '{resolver_type}'. "
334
+ f"Expected one of {list(COMPONENTS_RESOLVER_TYPE_MAPPING.keys())}."
335
+ )
336
+
337
+ if "retriever" in components_resolver_config:
338
+ components_resolver_config["retriever"]["requester"]["use_cache"] = True
339
+
340
+ # Create a resolver for dynamic components based on type
341
+ components_resolver = self._constructor.create_component(
342
+ COMPONENTS_RESOLVER_TYPE_MAPPING[resolver_type], components_resolver_config, config
343
+ )
344
+
345
+ stream_template_config = dynamic_definition["stream_template"]
346
+
347
+ for dynamic_stream in components_resolver.resolve_components(
348
+ stream_template_config=stream_template_config
349
+ ):
350
+ if "type" not in dynamic_stream:
351
+ dynamic_stream["type"] = "DeclarativeStream"
352
+
353
+ dynamic_stream_configs.append(dynamic_stream)
354
+
355
+ return dynamic_stream_configs
356
+
306
357
  def _emit_manifest_debug_message(self, extra_args: dict[str, Any]) -> None:
307
358
  self.logger.debug("declarative source created from manifest", extra=extra_args)
@@ -1158,6 +1158,37 @@ class WaitUntilTimeFromHeader(BaseModel):
1158
1158
  parameters: Optional[Dict[str, Any]] = Field(None, alias="$parameters")
1159
1159
 
1160
1160
 
1161
+ class ComponentMappingDefinition(BaseModel):
1162
+ type: Literal["ComponentMappingDefinition"]
1163
+ field_path: List[str] = Field(
1164
+ ...,
1165
+ description="A list of potentially nested fields indicating the full path where value will be added or updated.",
1166
+ examples=[
1167
+ ["data"],
1168
+ ["data", "records"],
1169
+ ["data", "{{ parameters.name }}"],
1170
+ ["data", "*", "record"],
1171
+ ],
1172
+ title="Field Path",
1173
+ )
1174
+ value: str = Field(
1175
+ ...,
1176
+ description="The dynamic or static value to assign to the key. Interpolated values can be used to dynamically determine the value during runtime.",
1177
+ examples=[
1178
+ "{{ components_values['updates'] }}",
1179
+ "{{ components_values['MetaData']['LastUpdatedTime'] }}",
1180
+ "{{ config['segment_id'] }}",
1181
+ ],
1182
+ title="Value",
1183
+ )
1184
+ value_type: Optional[ValueType] = Field(
1185
+ None,
1186
+ description="The expected data type of the value. If omitted, the type will be inferred from the value provided.",
1187
+ title="Value Type",
1188
+ )
1189
+ parameters: Optional[Dict[str, Any]] = Field(None, alias="$parameters")
1190
+
1191
+
1161
1192
  class AddedFieldDefinition(BaseModel):
1162
1193
  type: Literal["AddedFieldDefinition"]
1163
1194
  path: List[str] = Field(
@@ -1455,13 +1486,40 @@ class CompositeErrorHandler(BaseModel):
1455
1486
  parameters: Optional[Dict[str, Any]] = Field(None, alias="$parameters")
1456
1487
 
1457
1488
 
1458
- class DeclarativeSource(BaseModel):
1489
+ class DeclarativeSource1(BaseModel):
1459
1490
  class Config:
1460
1491
  extra = Extra.forbid
1461
1492
 
1462
1493
  type: Literal["DeclarativeSource"]
1463
1494
  check: CheckStream
1464
1495
  streams: List[DeclarativeStream]
1496
+ dynamic_streams: Optional[List[DynamicDeclarativeStream]] = None
1497
+ version: str = Field(
1498
+ ...,
1499
+ description="The version of the Airbyte CDK used to build and test the source.",
1500
+ )
1501
+ schemas: Optional[Schemas] = None
1502
+ definitions: Optional[Dict[str, Any]] = None
1503
+ spec: Optional[Spec] = None
1504
+ concurrency_level: Optional[ConcurrencyLevel] = None
1505
+ metadata: Optional[Dict[str, Any]] = Field(
1506
+ None,
1507
+ description="For internal Airbyte use only - DO NOT modify manually. Used by consumers of declarative manifests for storing related metadata.",
1508
+ )
1509
+ description: Optional[str] = Field(
1510
+ None,
1511
+ description="A description of the connector. It will be presented on the Source documentation page.",
1512
+ )
1513
+
1514
+
1515
+ class DeclarativeSource2(BaseModel):
1516
+ class Config:
1517
+ extra = Extra.forbid
1518
+
1519
+ type: Literal["DeclarativeSource"]
1520
+ check: CheckStream
1521
+ streams: Optional[List[DeclarativeStream]] = None
1522
+ dynamic_streams: List[DynamicDeclarativeStream]
1465
1523
  version: str = Field(
1466
1524
  ...,
1467
1525
  description="The version of the Airbyte CDK used to build and test the source.",
@@ -1480,6 +1538,17 @@ class DeclarativeSource(BaseModel):
1480
1538
  )
1481
1539
 
1482
1540
 
1541
+ class DeclarativeSource(BaseModel):
1542
+ class Config:
1543
+ extra = Extra.forbid
1544
+
1545
+ __root__: Union[DeclarativeSource1, DeclarativeSource2] = Field(
1546
+ ...,
1547
+ description="An API source that extracts data according to its declarative components.",
1548
+ title="DeclarativeSource",
1549
+ )
1550
+
1551
+
1483
1552
  class SelectiveAuthenticator(BaseModel):
1484
1553
  class Config:
1485
1554
  extra = Extra.allow
@@ -1883,8 +1952,32 @@ class SubstreamPartitionRouter(BaseModel):
1883
1952
  parameters: Optional[Dict[str, Any]] = Field(None, alias="$parameters")
1884
1953
 
1885
1954
 
1955
+ class HttpComponentsResolver(BaseModel):
1956
+ type: Literal["HttpComponentsResolver"]
1957
+ retriever: Union[AsyncRetriever, CustomRetriever, SimpleRetriever] = Field(
1958
+ ...,
1959
+ description="Component used to coordinate how records are extracted across stream slices and request pages.",
1960
+ title="Retriever",
1961
+ )
1962
+ components_mapping: List[ComponentMappingDefinition]
1963
+ parameters: Optional[Dict[str, Any]] = Field(None, alias="$parameters")
1964
+
1965
+
1966
+ class DynamicDeclarativeStream(BaseModel):
1967
+ type: Literal["DynamicDeclarativeStream"]
1968
+ stream_template: DeclarativeStream = Field(
1969
+ ..., description="Reference to the stream template.", title="Stream Template"
1970
+ )
1971
+ components_resolver: HttpComponentsResolver = Field(
1972
+ ...,
1973
+ description="Component resolve and populates stream templates with components values.",
1974
+ title="Components Resolver",
1975
+ )
1976
+
1977
+
1886
1978
  CompositeErrorHandler.update_forward_refs()
1887
- DeclarativeSource.update_forward_refs()
1979
+ DeclarativeSource1.update_forward_refs()
1980
+ DeclarativeSource2.update_forward_refs()
1888
1981
  SelectiveAuthenticator.update_forward_refs()
1889
1982
  DeclarativeStream.update_forward_refs()
1890
1983
  SessionTokenAuthenticator.update_forward_refs()
@@ -31,6 +31,12 @@ DEFAULT_MODEL_TYPES: Mapping[str, str] = {
31
31
  # DeclarativeStream
32
32
  "DeclarativeStream.retriever": "SimpleRetriever",
33
33
  "DeclarativeStream.schema_loader": "JsonFileSchemaLoader",
34
+ # DynamicDeclarativeStream
35
+ "DynamicDeclarativeStream.stream_template": "DeclarativeStream",
36
+ "DynamicDeclarativeStream.components_resolver": "HttpComponentsResolver",
37
+ # HttpComponentsResolver
38
+ "HttpComponentsResolver.retriever": "SimpleRetriever",
39
+ "HttpComponentsResolver.components_mapping": "ComponentMappingDefinition",
34
40
  # DefaultErrorHandler
35
41
  "DefaultErrorHandler.response_filters": "HttpResponseFilter",
36
42
  # DefaultPaginator
@@ -119,6 +119,9 @@ from airbyte_cdk.sources.declarative.models.declarative_component_schema import
119
119
  from airbyte_cdk.sources.declarative.models.declarative_component_schema import (
120
120
  CheckStream as CheckStreamModel,
121
121
  )
122
+ from airbyte_cdk.sources.declarative.models.declarative_component_schema import (
123
+ ComponentMappingDefinition as ComponentMappingDefinitionModel,
124
+ )
122
125
  from airbyte_cdk.sources.declarative.models.declarative_component_schema import (
123
126
  CompositeErrorHandler as CompositeErrorHandlerModel,
124
127
  )
@@ -191,6 +194,9 @@ from airbyte_cdk.sources.declarative.models.declarative_component_schema import
191
194
  from airbyte_cdk.sources.declarative.models.declarative_component_schema import (
192
195
  GzipJsonDecoder as GzipJsonDecoderModel,
193
196
  )
197
+ from airbyte_cdk.sources.declarative.models.declarative_component_schema import (
198
+ HttpComponentsResolver as HttpComponentsResolverModel,
199
+ )
194
200
  from airbyte_cdk.sources.declarative.models.declarative_component_schema import (
195
201
  HttpRequester as HttpRequesterModel,
196
202
  )
@@ -298,6 +304,7 @@ from airbyte_cdk.sources.declarative.models.declarative_component_schema import
298
304
  from airbyte_cdk.sources.declarative.partition_routers import (
299
305
  CartesianProductStreamSlicer,
300
306
  ListPartitionRouter,
307
+ PartitionRouter,
301
308
  SinglePartitionRouter,
302
309
  SubstreamPartitionRouter,
303
310
  )
@@ -338,6 +345,10 @@ from airbyte_cdk.sources.declarative.requesters.request_options import (
338
345
  )
339
346
  from airbyte_cdk.sources.declarative.requesters.request_path import RequestPath
340
347
  from airbyte_cdk.sources.declarative.requesters.requester import HttpMethod
348
+ from airbyte_cdk.sources.declarative.resolvers import (
349
+ ComponentMappingDefinition,
350
+ HttpComponentsResolver,
351
+ )
341
352
  from airbyte_cdk.sources.declarative.retrievers import (
342
353
  AsyncRetriever,
343
354
  SimpleRetriever,
@@ -467,6 +478,8 @@ class ModelToComponentFactory:
467
478
  WaitTimeFromHeaderModel: self.create_wait_time_from_header,
468
479
  WaitUntilTimeFromHeaderModel: self.create_wait_until_time_from_header,
469
480
  AsyncRetrieverModel: self.create_async_retriever,
481
+ HttpComponentsResolverModel: self.create_http_components_resolver,
482
+ ComponentMappingDefinitionModel: self.create_components_mapping_definition,
470
483
  }
471
484
 
472
485
  # Needed for the case where we need to perform a second parse on the fields of a custom component
@@ -1281,19 +1294,20 @@ class ModelToComponentFactory:
1281
1294
  parameters=model.parameters or {},
1282
1295
  )
1283
1296
 
1284
- def _merge_stream_slicers(
1285
- self, model: DeclarativeStreamModel, config: Config
1286
- ) -> Optional[StreamSlicer]:
1287
- stream_slicer = None
1297
+ def _build_stream_slicer_from_partition_router(
1298
+ self,
1299
+ model: Union[AsyncRetrieverModel, CustomRetrieverModel, SimpleRetrieverModel],
1300
+ config: Config,
1301
+ ) -> Optional[PartitionRouter]:
1288
1302
  if (
1289
- hasattr(model.retriever, "partition_router")
1290
- and isinstance(model.retriever, SimpleRetrieverModel)
1291
- and model.retriever.partition_router
1303
+ hasattr(model, "partition_router")
1304
+ and isinstance(model, SimpleRetrieverModel)
1305
+ and model.partition_router
1292
1306
  ):
1293
- stream_slicer_model = model.retriever.partition_router
1307
+ stream_slicer_model = model.partition_router
1294
1308
 
1295
1309
  if isinstance(stream_slicer_model, list):
1296
- stream_slicer = CartesianProductStreamSlicer(
1310
+ return CartesianProductStreamSlicer(
1297
1311
  [
1298
1312
  self._create_component_from_model(model=slicer, config=config)
1299
1313
  for slicer in stream_slicer_model
@@ -1301,9 +1315,24 @@ class ModelToComponentFactory:
1301
1315
  parameters={},
1302
1316
  )
1303
1317
  else:
1304
- stream_slicer = self._create_component_from_model(
1305
- model=stream_slicer_model, config=config
1306
- )
1318
+ return self._create_component_from_model(model=stream_slicer_model, config=config) # type: ignore[no-any-return]
1319
+ # Will be created PartitionRouter as stream_slicer_model is model.partition_router
1320
+ return None
1321
+
1322
+ def _build_resumable_cursor_from_paginator(
1323
+ self,
1324
+ model: Union[AsyncRetrieverModel, CustomRetrieverModel, SimpleRetrieverModel],
1325
+ stream_slicer: Optional[StreamSlicer],
1326
+ ) -> Optional[StreamSlicer]:
1327
+ if hasattr(model, "paginator") and model.paginator and not stream_slicer:
1328
+ # For the regular Full-Refresh streams, we use the high level `ResumableFullRefreshCursor`
1329
+ return ResumableFullRefreshCursor(parameters={})
1330
+ return None
1331
+
1332
+ def _merge_stream_slicers(
1333
+ self, model: DeclarativeStreamModel, config: Config
1334
+ ) -> Optional[StreamSlicer]:
1335
+ stream_slicer = self._build_stream_slicer_from_partition_router(model.retriever, config)
1307
1336
 
1308
1337
  if model.incremental_sync and stream_slicer:
1309
1338
  incremental_sync_model = model.incremental_sync
@@ -1346,15 +1375,7 @@ class ModelToComponentFactory:
1346
1375
  ),
1347
1376
  partition_router=stream_slicer,
1348
1377
  )
1349
- elif (
1350
- hasattr(model.retriever, "paginator")
1351
- and model.retriever.paginator
1352
- and not stream_slicer
1353
- ):
1354
- # For the regular Full-Refresh streams, we use the high level `ResumableFullRefreshCursor`
1355
- return ResumableFullRefreshCursor(parameters={})
1356
- else:
1357
- return None
1378
+ return self._build_resumable_cursor_from_paginator(model.retriever, stream_slicer)
1358
1379
 
1359
1380
  def create_default_error_handler(
1360
1381
  self, model: DefaultErrorHandlerModel, config: Config, **kwargs: Any
@@ -2218,3 +2239,56 @@ class ModelToComponentFactory:
2218
2239
 
2219
2240
  def _evaluate_log_level(self, emit_connector_builder_messages: bool) -> Level:
2220
2241
  return Level.DEBUG if emit_connector_builder_messages else Level.INFO
2242
+
2243
+ @staticmethod
2244
+ def create_components_mapping_definition(
2245
+ model: ComponentMappingDefinitionModel, config: Config, **kwargs: Any
2246
+ ) -> ComponentMappingDefinition:
2247
+ interpolated_value = InterpolatedString.create(
2248
+ model.value, parameters=model.parameters or {}
2249
+ )
2250
+ field_path = [
2251
+ InterpolatedString.create(path, parameters=model.parameters or {})
2252
+ for path in model.field_path
2253
+ ]
2254
+ return ComponentMappingDefinition(
2255
+ field_path=field_path, # type: ignore[arg-type] # field_path can be str and InterpolatedString
2256
+ value=interpolated_value,
2257
+ value_type=ModelToComponentFactory._json_schema_type_name_to_type(model.value_type),
2258
+ parameters=model.parameters or {},
2259
+ )
2260
+
2261
+ def create_http_components_resolver(
2262
+ self, model: HttpComponentsResolverModel, config: Config
2263
+ ) -> Any:
2264
+ stream_slicer = self._build_stream_slicer_from_partition_router(model.retriever, config)
2265
+ combined_slicers = self._build_resumable_cursor_from_paginator(
2266
+ model.retriever, stream_slicer
2267
+ )
2268
+
2269
+ retriever = self._create_component_from_model(
2270
+ model=model.retriever,
2271
+ config=config,
2272
+ name="",
2273
+ primary_key=None,
2274
+ stream_slicer=combined_slicers,
2275
+ transformations=[],
2276
+ )
2277
+
2278
+ components_mapping = [
2279
+ self._create_component_from_model(
2280
+ model=components_mapping_definition_model,
2281
+ value_type=ModelToComponentFactory._json_schema_type_name_to_type(
2282
+ components_mapping_definition_model.value_type
2283
+ ),
2284
+ config=config,
2285
+ )
2286
+ for components_mapping_definition_model in model.components_mapping
2287
+ ]
2288
+
2289
+ return HttpComponentsResolver(
2290
+ retriever=retriever,
2291
+ config=config,
2292
+ components_mapping=components_mapping,
2293
+ parameters=model.parameters or {},
2294
+ )
@@ -6,5 +6,6 @@ from airbyte_cdk.sources.declarative.partition_routers.cartesian_product_stream_
6
6
  from airbyte_cdk.sources.declarative.partition_routers.list_partition_router import ListPartitionRouter
7
7
  from airbyte_cdk.sources.declarative.partition_routers.single_partition_router import SinglePartitionRouter
8
8
  from airbyte_cdk.sources.declarative.partition_routers.substream_partition_router import SubstreamPartitionRouter
9
+ from airbyte_cdk.sources.declarative.partition_routers.partition_router import PartitionRouter
9
10
 
10
- __all__ = ["CartesianProductStreamSlicer", "ListPartitionRouter", "SinglePartitionRouter", "SubstreamPartitionRouter"]
11
+ __all__ = ["CartesianProductStreamSlicer", "ListPartitionRouter", "SinglePartitionRouter", "SubstreamPartitionRouter", "PartitionRouter"]
@@ -0,0 +1,13 @@
1
+ #
2
+ # Copyright (c) 2024 Airbyte, Inc., all rights reserved.
3
+ #
4
+
5
+ from airbyte_cdk.sources.declarative.resolvers.components_resolver import ComponentsResolver, ComponentMappingDefinition, ResolvedComponentMappingDefinition
6
+ from airbyte_cdk.sources.declarative.resolvers.http_components_resolver import HttpComponentsResolver
7
+ from airbyte_cdk.sources.declarative.models import HttpComponentsResolver as HttpComponentsResolverModel
8
+
9
+ COMPONENTS_RESOLVER_TYPE_MAPPING = {
10
+ "HttpComponentsResolver": HttpComponentsResolverModel
11
+ }
12
+
13
+ __all__ = ["ComponentsResolver", "HttpComponentsResolver", "ComponentMappingDefinition", "ResolvedComponentMappingDefinition", "COMPONENTS_RESOLVER_TYPE_MAPPING"]
@@ -0,0 +1,55 @@
1
+ #
2
+ # Copyright (c) 2024 Airbyte, Inc., all rights reserved.
3
+ #
4
+
5
+ from abc import ABC, abstractmethod
6
+ from dataclasses import InitVar, dataclass
7
+ from typing import Any, Dict, Iterable, List, Mapping, Optional, Type, Union
8
+
9
+ from typing_extensions import deprecated
10
+
11
+ from airbyte_cdk.sources.declarative.interpolation import InterpolatedString
12
+ from airbyte_cdk.sources.source import ExperimentalClassWarning
13
+
14
+
15
+ @dataclass(frozen=True)
16
+ class ComponentMappingDefinition:
17
+ """Defines the configuration for mapping a component in a stream. This class specifies
18
+ what field in the stream template should be updated with value, supporting dynamic interpolation
19
+ and type enforcement."""
20
+
21
+ field_path: List["InterpolatedString"]
22
+ value: Union["InterpolatedString", str]
23
+ value_type: Optional[Type[Any]]
24
+ parameters: InitVar[Mapping[str, Any]]
25
+
26
+
27
+ @dataclass(frozen=True)
28
+ class ResolvedComponentMappingDefinition:
29
+ """Defines resolved configuration for mapping a component in a stream. This class specifies
30
+ what field in the stream template should be updated with value, supporting dynamic interpolation
31
+ and type enforcement."""
32
+
33
+ field_path: List["InterpolatedString"]
34
+ value: "InterpolatedString"
35
+ value_type: Optional[Type[Any]]
36
+ parameters: InitVar[Mapping[str, Any]]
37
+
38
+
39
+ @deprecated("This class is experimental. Use at your own risk.", category=ExperimentalClassWarning)
40
+ @dataclass
41
+ class ComponentsResolver(ABC):
42
+ """
43
+ Abstract base class for resolving components in a stream template.
44
+ """
45
+
46
+ @abstractmethod
47
+ def resolve_components(
48
+ self, stream_template_config: Dict[str, Any]
49
+ ) -> Iterable[Dict[str, Any]]:
50
+ """
51
+ Maps and populates values into a stream template configuration.
52
+ :param stream_template_config: The stream template with placeholders for components.
53
+ :yields: The resolved stream config with populated values.
54
+ """
55
+ pass
@@ -0,0 +1,106 @@
1
+ #
2
+ # Copyright (c) 2024 Airbyte, Inc., all rights reserved.
3
+ #
4
+
5
+ from copy import deepcopy
6
+ from dataclasses import InitVar, dataclass, field
7
+ from typing import Any, Dict, Iterable, List, Mapping
8
+
9
+ import dpath
10
+ from typing_extensions import deprecated
11
+
12
+ from airbyte_cdk.sources.declarative.interpolation import InterpolatedString
13
+ from airbyte_cdk.sources.declarative.resolvers.components_resolver import (
14
+ ComponentMappingDefinition,
15
+ ComponentsResolver,
16
+ ResolvedComponentMappingDefinition,
17
+ )
18
+ from airbyte_cdk.sources.declarative.retrievers.retriever import Retriever
19
+ from airbyte_cdk.sources.source import ExperimentalClassWarning
20
+ from airbyte_cdk.sources.types import Config
21
+
22
+
23
+ @deprecated("This class is experimental. Use at your own risk.", category=ExperimentalClassWarning)
24
+ @dataclass
25
+ class HttpComponentsResolver(ComponentsResolver):
26
+ """
27
+ Resolves and populates stream templates with components fetched via an HTTP retriever.
28
+
29
+ Attributes:
30
+ retriever (Retriever): The retriever used to fetch data from an API.
31
+ config (Config): Configuration object for the resolver.
32
+ components_mapping (List[ComponentMappingDefinition]): List of mappings to resolve.
33
+ parameters (InitVar[Mapping[str, Any]]): Additional parameters for interpolation.
34
+ """
35
+
36
+ retriever: Retriever
37
+ config: Config
38
+ components_mapping: List[ComponentMappingDefinition]
39
+ parameters: InitVar[Mapping[str, Any]]
40
+ _resolved_components: List[ResolvedComponentMappingDefinition] = field(
41
+ init=False, repr=False, default_factory=list
42
+ )
43
+
44
+ def __post_init__(self, parameters: Mapping[str, Any]) -> None:
45
+ """
46
+ Initializes and parses component mappings, converting them to resolved definitions.
47
+
48
+ Args:
49
+ parameters (Mapping[str, Any]): Parameters for interpolation.
50
+ """
51
+ for component_mapping in self.components_mapping:
52
+ if isinstance(component_mapping.value, (str, InterpolatedString)):
53
+ interpolated_value = (
54
+ InterpolatedString.create(component_mapping.value, parameters=parameters)
55
+ if isinstance(component_mapping.value, str)
56
+ else component_mapping.value
57
+ )
58
+
59
+ field_path = [
60
+ InterpolatedString.create(path, parameters=parameters)
61
+ for path in component_mapping.field_path
62
+ ]
63
+
64
+ self._resolved_components.append(
65
+ ResolvedComponentMappingDefinition(
66
+ field_path=field_path,
67
+ value=interpolated_value,
68
+ value_type=component_mapping.value_type,
69
+ parameters=parameters,
70
+ )
71
+ )
72
+ else:
73
+ raise ValueError(
74
+ f"Expected a string or InterpolatedString for value in mapping: {component_mapping}"
75
+ )
76
+
77
+ def resolve_components(
78
+ self, stream_template_config: Dict[str, Any]
79
+ ) -> Iterable[Dict[str, Any]]:
80
+ """
81
+ Resolves components in the stream template configuration by populating values.
82
+
83
+ Args:
84
+ stream_template_config (Dict[str, Any]): Stream template to populate.
85
+
86
+ Yields:
87
+ Dict[str, Any]: Updated configurations with resolved components.
88
+ """
89
+ kwargs = {"stream_template_config": stream_template_config}
90
+
91
+ for components_values in self.retriever.read_records({}):
92
+ updated_config = deepcopy(stream_template_config)
93
+ kwargs["components_values"] = components_values # type: ignore[assignment] # component_values will always be of type Mapping[str, Any]
94
+
95
+ for resolved_component in self._resolved_components:
96
+ valid_types = (
97
+ (resolved_component.value_type,) if resolved_component.value_type else None
98
+ )
99
+ value = resolved_component.value.eval(
100
+ self.config, valid_types=valid_types, **kwargs
101
+ )
102
+
103
+ path = [path.eval(self.config, **kwargs) for path in resolved_component.field_path]
104
+ dpath.set(updated_config, path, value)
105
+
106
+ yield updated_config
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: airbyte-cdk
3
- Version: 6.8.2rc1
3
+ Version: 6.9.0
4
4
  Summary: A framework for writing Airbyte Connectors.
5
5
  Home-page: https://airbyte.com
6
6
  License: MIT
@@ -18,12 +18,10 @@ Classifier: Programming Language :: Python :: 3.12
18
18
  Classifier: Topic :: Scientific/Engineering
19
19
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
20
20
  Provides-Extra: file-based
21
- Provides-Extra: sphinx-docs
22
21
  Provides-Extra: sql
23
22
  Provides-Extra: vector-db-based
24
23
  Requires-Dist: Jinja2 (>=3.1.2,<3.2.0)
25
24
  Requires-Dist: PyYAML (>=6.0.1,<7.0.0)
26
- Requires-Dist: Sphinx (>=4.2,<4.3) ; extra == "sphinx-docs"
27
25
  Requires-Dist: airbyte-protocol-models-dataclasses (>=0.14,<0.15)
28
26
  Requires-Dist: avro (>=1.11.2,<1.12.0) ; extra == "file-based"
29
27
  Requires-Dist: backoff
@@ -63,7 +61,6 @@ Requires-Dist: rapidfuzz (>=3.10.1,<4.0.0)
63
61
  Requires-Dist: requests
64
62
  Requires-Dist: requests_cache
65
63
  Requires-Dist: serpyco-rs (>=1.10.2,<2.0.0)
66
- Requires-Dist: sphinx-rtd-theme (>=1.0,<1.1) ; extra == "sphinx-docs"
67
64
  Requires-Dist: sqlalchemy (>=2.0,<3.0,!=2.0.36) ; extra == "sql"
68
65
  Requires-Dist: tiktoken (==0.8.0) ; extra == "vector-db-based"
69
66
  Requires-Dist: unstructured.pytesseract (>=0.3.12) ; extra == "file-based"
@@ -62,11 +62,11 @@ airbyte_cdk/sources/declarative/checks/check_stream.py,sha256=dAA-UhmMj0WLXCkRQr
62
62
  airbyte_cdk/sources/declarative/checks/connection_checker.py,sha256=MBRJo6WJlZQHpIfOGaNOkkHUmgUl_4wDM6VPo41z5Ss,1383
63
63
  airbyte_cdk/sources/declarative/concurrency_level/__init__.py,sha256=5XUqrmlstYlMM0j6crktlKQwALek0uiz2D3WdM46MyA,191
64
64
  airbyte_cdk/sources/declarative/concurrency_level/concurrency_level.py,sha256=YIwCTCpOr_QSNW4ltQK0yUGWInI8PKNY216HOOegYLk,2101
65
- airbyte_cdk/sources/declarative/concurrent_declarative_source.py,sha256=UAkFzFJ62tq7qWfudLLt-Sj-EhOJquYPd-FrwMBSA9I,22928
65
+ airbyte_cdk/sources/declarative/concurrent_declarative_source.py,sha256=9OXOFzG5PBL_MHEJg4ETE0TXfXeuhvv-at38AN3wWEM,23323
66
66
  airbyte_cdk/sources/declarative/datetime/__init__.py,sha256=l9LG7Qm6e5r_qgqfVKnx3mXYtg1I9MmMjomVIPfU4XA,177
67
67
  airbyte_cdk/sources/declarative/datetime/datetime_parser.py,sha256=SX9JjdesN1edN2WVUVMzU_ptqp2QB1OnsnjZ4mwcX7w,2579
68
68
  airbyte_cdk/sources/declarative/datetime/min_max_datetime.py,sha256=0BHBtDNQZfvwM45-tY5pNlTcKAFSGGNxemoi0Jic-0E,5785
69
- airbyte_cdk/sources/declarative/declarative_component_schema.yaml,sha256=Q6zOEKvgAjMUsu89VmNteMhqdP0XwO9WWfA6-rMVn6A,120545
69
+ airbyte_cdk/sources/declarative/declarative_component_schema.yaml,sha256=kZkGoasVzufDE2BkFo_7DVO6xHL9kueNBjddtl-7kaU,124134
70
70
  airbyte_cdk/sources/declarative/declarative_source.py,sha256=nF7wBqFd3AQmEKAm4CnIo29CJoQL562cJGSCeL8U8bA,1531
71
71
  airbyte_cdk/sources/declarative/declarative_stream.py,sha256=JRyNeOIpsFu4ztVZsN6sncqUEIqIE-bUkD2TPgbMgk0,10375
72
72
  airbyte_cdk/sources/declarative/decoders/__init__.py,sha256=hNlhaB5FjNC6IfJyglj5ZJWkYD2nEAukMDmzRz5PC6o,671
@@ -99,18 +99,18 @@ airbyte_cdk/sources/declarative/interpolation/interpolated_string.py,sha256=LYEZ
99
99
  airbyte_cdk/sources/declarative/interpolation/interpolation.py,sha256=-V5UddGm69UKEB6o_O1EIES9kfY8FV_X4Ji8w1yOuSA,981
100
100
  airbyte_cdk/sources/declarative/interpolation/jinja.py,sha256=gcihTEnfD_6sUivxOomoY5r7VMAGqVVnK_HEsid9Y5k,6605
101
101
  airbyte_cdk/sources/declarative/interpolation/macros.py,sha256=jf24RK-1fBhTYDpcGEakZtGNNJfG5NS8CCF5bEgNmRo,3977
102
- airbyte_cdk/sources/declarative/manifest_declarative_source.py,sha256=FEOmFo2mwdpmO8pH9jnw-sUAnijjuigZWYqH_0Gq9oQ,12919
102
+ airbyte_cdk/sources/declarative/manifest_declarative_source.py,sha256=-v3GvuTVHwUonrfUwDj3wYKaZjX6hTyKmMBRgEzj-j0,15201
103
103
  airbyte_cdk/sources/declarative/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
104
104
  airbyte_cdk/sources/declarative/migrations/legacy_to_per_partition_state_migration.py,sha256=iemy3fKLczcU0-Aor7tx5jcT6DRedKMqyK7kCOp01hg,3924
105
105
  airbyte_cdk/sources/declarative/migrations/state_migration.py,sha256=KWPjealMLKSMtajXgkdGgKg7EmTLR-CqqD7UIh0-eDU,794
106
106
  airbyte_cdk/sources/declarative/models/__init__.py,sha256=nUFxNCiKeYRVXuZEKA7GD-lTHxsiKcQ8FitZjKhPIvE,100
107
- airbyte_cdk/sources/declarative/models/declarative_component_schema.py,sha256=tZcVSMRJ5-5MoX4ejpXxt5E2suqiQ5AwprQWQiWHhho,85060
107
+ airbyte_cdk/sources/declarative/models/declarative_component_schema.py,sha256=6iAzpGmUrhwEUQcCL5bW-FXuLXPMeFqs_GR4B1rS3ZE,88511
108
108
  airbyte_cdk/sources/declarative/parsers/__init__.py,sha256=ZnqYNxHsKCgO38IwB34RQyRMXTs4GTvlRi3ImKnIioo,61
109
109
  airbyte_cdk/sources/declarative/parsers/custom_exceptions.py,sha256=Rir9_z3Kcd5Es0-LChrzk-0qubAsiK_RSEnLmK2OXm8,553
110
- airbyte_cdk/sources/declarative/parsers/manifest_component_transformer.py,sha256=jVZ3ZV5YZrmDNIX5cM2mugXmnbH27zHRcD22_3oatpo,8454
110
+ airbyte_cdk/sources/declarative/parsers/manifest_component_transformer.py,sha256=0jfi-ogL-rOVORTIYnu64wNfh1L8fYaLVDWzJ2zGdi8,8799
111
111
  airbyte_cdk/sources/declarative/parsers/manifest_reference_resolver.py,sha256=IWUOdF03o-aQn0Occo1BJCxU0Pz-QILk5L67nzw2thw,6803
112
- airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py,sha256=tO7xkv4y5iH6wGkj5As1T5ItUQxlw6cLflHAH48PKwc,96355
113
- airbyte_cdk/sources/declarative/partition_routers/__init__.py,sha256=8uGos2u7TFTx_EJBdcjdUGn3Eyx6jUuEa1_VB8UP_dI,631
112
+ airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py,sha256=lS2oKfkND54u66hocc2BycS-AIYIbkn4npq6CFRNokc,99573
113
+ airbyte_cdk/sources/declarative/partition_routers/__init__.py,sha256=1NjaZoGAIefvWwj6wx-LOKIXXWS-UnBlZFnuR7y6uYA,745
114
114
  airbyte_cdk/sources/declarative/partition_routers/cartesian_product_stream_slicer.py,sha256=c5cuVFM6NFkuQqG8Z5IwkBuwDrvXZN1CunUOM_L0ezg,6892
115
115
  airbyte_cdk/sources/declarative/partition_routers/list_partition_router.py,sha256=t7pRdFWfFWJtQQG19c9PVeMODyO2BknRTakpM5U9N-8,4844
116
116
  airbyte_cdk/sources/declarative/partition_routers/partition_router.py,sha256=YyEIzdmLd1FjbVP3QbQ2VFCLW_P-OGbVh6VpZShp54k,2218
@@ -152,6 +152,9 @@ airbyte_cdk/sources/declarative/requesters/request_options/interpolated_request_
152
152
  airbyte_cdk/sources/declarative/requesters/request_options/request_options_provider.py,sha256=8YRiDzjYvqJ-aMmKFcjqzv_-e8OZ5QG_TbpZ-nuCu6s,2590
153
153
  airbyte_cdk/sources/declarative/requesters/request_path.py,sha256=S3MeFvcaQrMbOkSY2W2VbXLNomqt_3eXqVd9ZhgNwUs,299
154
154
  airbyte_cdk/sources/declarative/requesters/requester.py,sha256=iVVpXQ4KEd9OyZNwmOofMvx7_06i8ZRxGo3aNTrEQLM,4946
155
+ airbyte_cdk/sources/declarative/resolvers/__init__.py,sha256=8cL--3WCJvtKVIIAqKWoIEhmmf3FemB7PlsnO0Mx_cY,688
156
+ airbyte_cdk/sources/declarative/resolvers/components_resolver.py,sha256=KPjKc0yb9artL4ZkeqN8RmEykHH6FJgqXD7fCEnh1X0,1936
157
+ airbyte_cdk/sources/declarative/resolvers/http_components_resolver.py,sha256=ZA2vrHQKfXNMcH3x1iuyFOTGNzYDhUFT2qcaiOzSK0A,4271
155
158
  airbyte_cdk/sources/declarative/retrievers/__init__.py,sha256=FVQpUGVwp2Gibk4gp07VmLKX5AafUlsZWFSrDpUDuJM,443
156
159
  airbyte_cdk/sources/declarative/retrievers/async_retriever.py,sha256=WDFnjrXLz3-YEjFhmlMkWAn9AJvnZ0mk9FyC8DAhEYk,4976
157
160
  airbyte_cdk/sources/declarative/retrievers/retriever.py,sha256=XPLs593Xv8c5cKMc37XzUAYmzlXd1a7eSsspM-CMuWA,1696
@@ -330,8 +333,8 @@ airbyte_cdk/utils/slice_hasher.py,sha256=-pHexlNYoWYPnXNH-M7HEbjmeJe9Zk7SJijdQ7d
330
333
  airbyte_cdk/utils/spec_schema_transformations.py,sha256=-5HTuNsnDBAhj-oLeQXwpTGA0HdcjFOf2zTEMUTTg_Y,816
331
334
  airbyte_cdk/utils/stream_status_utils.py,sha256=ZmBoiy5HVbUEHAMrUONxZvxnvfV9CesmQJLDTAIWnWw,1171
332
335
  airbyte_cdk/utils/traced_exception.py,sha256=C8uIBuCL_E4WnBAOPSxBicD06JAldoN9fGsQDp463OY,6292
333
- airbyte_cdk-6.8.2rc1.dist-info/LICENSE.txt,sha256=Wfe61S4BaGPj404v8lrAbvhjYR68SHlkzeYrg3_bbuM,1051
334
- airbyte_cdk-6.8.2rc1.dist-info/METADATA,sha256=u5k6gz5XLQCQmR3CLeP-StHB8_iV6QhEqLuuBvh1G1w,6110
335
- airbyte_cdk-6.8.2rc1.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
336
- airbyte_cdk-6.8.2rc1.dist-info/entry_points.txt,sha256=fj-e3PAQvsxsQzyyq8UkG1k8spunWnD4BAH2AwlR6NM,95
337
- airbyte_cdk-6.8.2rc1.dist-info/RECORD,,
336
+ airbyte_cdk-6.9.0.dist-info/LICENSE.txt,sha256=Wfe61S4BaGPj404v8lrAbvhjYR68SHlkzeYrg3_bbuM,1051
337
+ airbyte_cdk-6.9.0.dist-info/METADATA,sha256=zfXBcI2WKOmEe38cVGYmW8cngDts67fzR94zJQ3oNrU,5949
338
+ airbyte_cdk-6.9.0.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
339
+ airbyte_cdk-6.9.0.dist-info/entry_points.txt,sha256=fj-e3PAQvsxsQzyyq8UkG1k8spunWnD4BAH2AwlR6NM,95
340
+ airbyte_cdk-6.9.0.dist-info/RECORD,,