airbyte-cdk 6.44.0__py3-none-any.whl → 6.45.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.
Files changed (18) hide show
  1. airbyte_cdk/sources/declarative/declarative_component_schema.yaml +118 -2
  2. airbyte_cdk/sources/declarative/models/declarative_component_schema.py +75 -2
  3. airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py +180 -8
  4. airbyte_cdk/sources/declarative/requesters/query_properties/__init__.py +13 -0
  5. airbyte_cdk/sources/declarative/requesters/query_properties/properties_from_endpoint.py +40 -0
  6. airbyte_cdk/sources/declarative/requesters/query_properties/property_chunking.py +69 -0
  7. airbyte_cdk/sources/declarative/requesters/query_properties/query_properties.py +58 -0
  8. airbyte_cdk/sources/declarative/requesters/query_properties/strategies/__init__.py +10 -0
  9. airbyte_cdk/sources/declarative/requesters/query_properties/strategies/group_by_key.py +33 -0
  10. airbyte_cdk/sources/declarative/requesters/query_properties/strategies/merge_strategy.py +19 -0
  11. airbyte_cdk/sources/declarative/requesters/request_options/interpolated_request_options_provider.py +25 -2
  12. airbyte_cdk/sources/declarative/retrievers/simple_retriever.py +101 -30
  13. {airbyte_cdk-6.44.0.dist-info → airbyte_cdk-6.45.0.dist-info}/METADATA +1 -1
  14. {airbyte_cdk-6.44.0.dist-info → airbyte_cdk-6.45.0.dist-info}/RECORD +18 -11
  15. {airbyte_cdk-6.44.0.dist-info → airbyte_cdk-6.45.0.dist-info}/LICENSE.txt +0 -0
  16. {airbyte_cdk-6.44.0.dist-info → airbyte_cdk-6.45.0.dist-info}/LICENSE_SHORT +0 -0
  17. {airbyte_cdk-6.44.0.dist-info → airbyte_cdk-6.45.0.dist-info}/WHEEL +0 -0
  18. {airbyte_cdk-6.44.0.dist-info → airbyte_cdk-6.45.0.dist-info}/entry_points.txt +0 -0
@@ -341,7 +341,7 @@ definitions:
341
341
  properties:
342
342
  type:
343
343
  type: string
344
- enum: [ DynamicStreamCheckConfig ]
344
+ enum: [DynamicStreamCheckConfig]
345
345
  dynamic_stream_name:
346
346
  title: Dynamic Stream Name
347
347
  description: The dynamic stream name.
@@ -1752,6 +1752,30 @@ definitions:
1752
1752
  $parameters:
1753
1753
  type: object
1754
1754
  additionalProperties: true
1755
+ GroupByKeyMergeStrategy:
1756
+ title: Group by Key
1757
+ description: Record merge strategy that combines records according to fields on the record.
1758
+ required:
1759
+ - type
1760
+ - key
1761
+ properties:
1762
+ type:
1763
+ type: string
1764
+ enum: [GroupByKeyMergeStrategy]
1765
+ key:
1766
+ title: Key
1767
+ description: The name of the field on the record whose value will be used to group properties that were retrieved through multiple API requests.
1768
+ anyOf:
1769
+ - type: string
1770
+ - type: array
1771
+ items:
1772
+ type: string
1773
+ examples:
1774
+ - "id"
1775
+ - ["parent_id", "end_date"]
1776
+ $parameters:
1777
+ type: object
1778
+ additionalProperties: true
1755
1779
  SessionTokenAuthenticator:
1756
1780
  type: object
1757
1781
  required:
@@ -1971,7 +1995,9 @@ definitions:
1971
1995
  - type: string
1972
1996
  - type: object
1973
1997
  additionalProperties:
1974
- type: string
1998
+ anyOf:
1999
+ - type: string
2000
+ - $ref": "#/definitions/QueryProperties"
1975
2001
  interpolation_context:
1976
2002
  - next_page_token
1977
2003
  - stream_interval
@@ -2989,6 +3015,96 @@ definitions:
2989
3015
  examples:
2990
3016
  - id
2991
3017
  - ["code", "type"]
3018
+ PropertiesFromEndpoint:
3019
+ title: Properties from Endpoint
3020
+ description: Defines the behavior for fetching the list of properties from an API that will be loaded into the requests to extract records.
3021
+ type: object
3022
+ required:
3023
+ - type
3024
+ - property_field_path
3025
+ - retriever
3026
+ properties:
3027
+ type:
3028
+ type: string
3029
+ enum: [PropertiesFromEndpoint]
3030
+ property_field_path:
3031
+ description: Describes the path to the field that should be extracted
3032
+ type: array
3033
+ items:
3034
+ type: string
3035
+ examples:
3036
+ - ["name"]
3037
+ interpolation_context:
3038
+ - config
3039
+ - parameters
3040
+ retriever:
3041
+ description: Requester component that describes how to fetch the properties to query from a remote API endpoint.
3042
+ anyOf:
3043
+ - "$ref": "#/definitions/CustomRetriever"
3044
+ - "$ref": "#/definitions/SimpleRetriever"
3045
+ $parameters:
3046
+ type: object
3047
+ additionalProperties: true
3048
+ PropertyChunking:
3049
+ title: Property Chunking
3050
+ description: For APIs with restrictions on the amount of properties that can be requester per request, property chunking can be applied to make multiple requests with a subset of the properties.
3051
+ type: object
3052
+ required:
3053
+ - type
3054
+ - property_limit_type
3055
+ properties:
3056
+ type:
3057
+ type: string
3058
+ enum: [PropertyChunking]
3059
+ property_limit_type:
3060
+ title: Property Limit Type
3061
+ description: The type used to determine the maximum number of properties per chunk
3062
+ enum:
3063
+ - characters
3064
+ - property_count
3065
+ property_limit:
3066
+ title: Property Limit
3067
+ description: The maximum amount of properties that can be retrieved per request according to the limit type.
3068
+ type: integer
3069
+ record_merge_strategy:
3070
+ title: Record Merge Strategy
3071
+ description: Dictates how to records that require multiple requests to get all properties should be emitted to the destination
3072
+ "$ref": "#/definitions/GroupByKeyMergeStrategy"
3073
+ $parameters:
3074
+ type: object
3075
+ additionalProperties: true
3076
+ QueryProperties:
3077
+ title: Query Properties
3078
+ description: For APIs that require explicit specification of the properties to query for, this component specifies which property fields and how they are supplied to outbound requests.
3079
+ type: object
3080
+ required:
3081
+ - type
3082
+ - property_list
3083
+ properties:
3084
+ type:
3085
+ type: string
3086
+ enum: [QueryProperties]
3087
+ property_list:
3088
+ title: Property List
3089
+ description: The set of properties that will be queried for in the outbound request. This can either be statically defined or dynamic based on an API endpoint
3090
+ anyOf:
3091
+ - type: array
3092
+ items:
3093
+ type: string
3094
+ - "$ref": "#/definitions/PropertiesFromEndpoint"
3095
+ always_include_properties:
3096
+ title: Always Include Properties
3097
+ description: The list of properties that should be included in every set of properties when multiple chunks of properties are being requested.
3098
+ type: array
3099
+ items:
3100
+ type: string
3101
+ property_chunking:
3102
+ title: Property Chunking
3103
+ description: Defines how query properties will be grouped into smaller sets for APIs with limitations on the number of properties fetched per API request.
3104
+ "$ref": "#/definitions/PropertyChunking"
3105
+ $parameters:
3106
+ type: object
3107
+ additionalProperties: true
2992
3108
  RecordFilter:
2993
3109
  title: Record Filter
2994
3110
  description: Filter applied on a list of records.
@@ -1,3 +1,5 @@
1
+ # Copyright (c) 2025 Airbyte, Inc., all rights reserved.
2
+
1
3
  # generated by datamodel-codegen:
2
4
  # filename: declarative_component_schema.yaml
3
5
 
@@ -49,7 +51,7 @@ class DynamicStreamCheckConfig(BaseModel):
49
51
  )
50
52
  stream_count: Optional[int] = Field(
51
53
  0,
52
- description="Numbers of the streams to try reading from when running a check operation.",
54
+ description="The number of streams to attempt reading from during a check operation. If `stream_count` exceeds the total number of available streams, the minimum of the two values will be used.",
53
55
  title="Stream Count",
54
56
  )
55
57
 
@@ -718,6 +720,17 @@ class ExponentialBackoffStrategy(BaseModel):
718
720
  parameters: Optional[Dict[str, Any]] = Field(None, alias="$parameters")
719
721
 
720
722
 
723
+ class GroupByKeyMergeStrategy(BaseModel):
724
+ type: Literal["GroupByKeyMergeStrategy"]
725
+ key: Union[str, List[str]] = Field(
726
+ ...,
727
+ description="The name of the field on the record whose value will be used to group properties that were retrieved through multiple API requests.",
728
+ examples=["id", ["parent_id", "end_date"]],
729
+ title="Key",
730
+ )
731
+ parameters: Optional[Dict[str, Any]] = Field(None, alias="$parameters")
732
+
733
+
721
734
  class SessionTokenRequestBearerAuthenticator(BaseModel):
722
735
  type: Literal["Bearer"]
723
736
 
@@ -1189,6 +1202,31 @@ class PrimaryKey(BaseModel):
1189
1202
  )
1190
1203
 
1191
1204
 
1205
+ class PropertyLimitType(Enum):
1206
+ characters = "characters"
1207
+ property_count = "property_count"
1208
+
1209
+
1210
+ class PropertyChunking(BaseModel):
1211
+ type: Literal["PropertyChunking"]
1212
+ property_limit_type: PropertyLimitType = Field(
1213
+ ...,
1214
+ description="The type used to determine the maximum number of properties per chunk",
1215
+ title="Property Limit Type",
1216
+ )
1217
+ property_limit: Optional[int] = Field(
1218
+ None,
1219
+ description="The maximum amount of properties that can be retrieved per request according to the limit type.",
1220
+ title="Property Limit",
1221
+ )
1222
+ record_merge_strategy: Optional[GroupByKeyMergeStrategy] = Field(
1223
+ None,
1224
+ description="Dictates how to records that require multiple requests to get all properties should be emitted to the destination",
1225
+ title="Record Merge Strategy",
1226
+ )
1227
+ parameters: Optional[Dict[str, Any]] = Field(None, alias="$parameters")
1228
+
1229
+
1192
1230
  class RecordFilter(BaseModel):
1193
1231
  type: Literal["RecordFilter"]
1194
1232
  condition: Optional[str] = Field(
@@ -2187,7 +2225,7 @@ class HttpRequester(BaseModel):
2187
2225
  examples=[{"Output-Format": "JSON"}, {"Version": "{{ config['version'] }}"}],
2188
2226
  title="Request Headers",
2189
2227
  )
2190
- request_parameters: Optional[Union[str, Dict[str, str]]] = Field(
2228
+ request_parameters: Optional[Union[str, Dict[str, Union[str, Any]]]] = Field(
2191
2229
  None,
2192
2230
  description="Specifies the query parameters that should be set on an outgoing HTTP request given the inputs.",
2193
2231
  examples=[
@@ -2277,6 +2315,40 @@ class ParentStreamConfig(BaseModel):
2277
2315
  parameters: Optional[Dict[str, Any]] = Field(None, alias="$parameters")
2278
2316
 
2279
2317
 
2318
+ class PropertiesFromEndpoint(BaseModel):
2319
+ type: Literal["PropertiesFromEndpoint"]
2320
+ property_field_path: List[str] = Field(
2321
+ ...,
2322
+ description="Describes the path to the field that should be extracted",
2323
+ examples=[["name"]],
2324
+ )
2325
+ retriever: Union[CustomRetriever, SimpleRetriever] = Field(
2326
+ ...,
2327
+ description="Requester component that describes how to fetch the properties to query from a remote API endpoint.",
2328
+ )
2329
+ parameters: Optional[Dict[str, Any]] = Field(None, alias="$parameters")
2330
+
2331
+
2332
+ class QueryProperties(BaseModel):
2333
+ type: Literal["QueryProperties"]
2334
+ property_list: Union[List[str], PropertiesFromEndpoint] = Field(
2335
+ ...,
2336
+ description="The set of properties that will be queried for in the outbound request. This can either be statically defined or dynamic based on an API endpoint",
2337
+ title="Property List",
2338
+ )
2339
+ always_include_properties: Optional[List[str]] = Field(
2340
+ None,
2341
+ description="The list of properties that should be included in every set of properties when multiple chunks of properties are being requested.",
2342
+ title="Always Include Properties",
2343
+ )
2344
+ property_chunking: Optional[PropertyChunking] = Field(
2345
+ None,
2346
+ description="Defines how query properties will be grouped into smaller sets for APIs with limitations on the number of properties fetched per API request.",
2347
+ title="Property Chunking",
2348
+ )
2349
+ parameters: Optional[Dict[str, Any]] = Field(None, alias="$parameters")
2350
+
2351
+
2280
2352
  class StateDelegatingStream(BaseModel):
2281
2353
  type: Literal["StateDelegatingStream"]
2282
2354
  name: str = Field(..., description="The stream name.", example=["Users"], title="Name")
@@ -2525,5 +2597,6 @@ DeclarativeStream.update_forward_refs()
2525
2597
  SessionTokenAuthenticator.update_forward_refs()
2526
2598
  DynamicSchemaLoader.update_forward_refs()
2527
2599
  ParentStreamConfig.update_forward_refs()
2600
+ PropertiesFromEndpoint.update_forward_refs()
2528
2601
  SimpleRetriever.update_forward_refs()
2529
2602
  AsyncRetriever.update_forward_refs()
@@ -1,5 +1,5 @@
1
1
  #
2
- # Copyright (c) 2023 Airbyte, Inc., all rights reserved.
2
+ # Copyright (c) 2025 Airbyte, Inc., all rights reserved.
3
3
  #
4
4
 
5
5
  from __future__ import annotations
@@ -234,6 +234,9 @@ from airbyte_cdk.sources.declarative.models.declarative_component_schema import
234
234
  from airbyte_cdk.sources.declarative.models.declarative_component_schema import (
235
235
  FlattenFields as FlattenFieldsModel,
236
236
  )
237
+ from airbyte_cdk.sources.declarative.models.declarative_component_schema import (
238
+ GroupByKeyMergeStrategy as GroupByKeyMergeStrategyModel,
239
+ )
237
240
  from airbyte_cdk.sources.declarative.models.declarative_component_schema import (
238
241
  GroupingPartitionRouter as GroupingPartitionRouterModel,
239
242
  )
@@ -324,6 +327,18 @@ from airbyte_cdk.sources.declarative.models.declarative_component_schema import
324
327
  from airbyte_cdk.sources.declarative.models.declarative_component_schema import (
325
328
  ParentStreamConfig as ParentStreamConfigModel,
326
329
  )
330
+ from airbyte_cdk.sources.declarative.models.declarative_component_schema import (
331
+ PropertiesFromEndpoint as PropertiesFromEndpointModel,
332
+ )
333
+ from airbyte_cdk.sources.declarative.models.declarative_component_schema import (
334
+ PropertyChunking as PropertyChunkingModel,
335
+ )
336
+ from airbyte_cdk.sources.declarative.models.declarative_component_schema import (
337
+ PropertyLimitType as PropertyLimitTypeModel,
338
+ )
339
+ from airbyte_cdk.sources.declarative.models.declarative_component_schema import (
340
+ QueryProperties as QueryPropertiesModel,
341
+ )
327
342
  from airbyte_cdk.sources.declarative.models.declarative_component_schema import (
328
343
  Rate as RateModel,
329
344
  )
@@ -432,6 +447,17 @@ from airbyte_cdk.sources.declarative.requesters.paginators.strategies import (
432
447
  PageIncrement,
433
448
  StopConditionPaginationStrategyDecorator,
434
449
  )
450
+ from airbyte_cdk.sources.declarative.requesters.query_properties import (
451
+ PropertiesFromEndpoint,
452
+ PropertyChunking,
453
+ QueryProperties,
454
+ )
455
+ from airbyte_cdk.sources.declarative.requesters.query_properties.property_chunking import (
456
+ PropertyLimitType,
457
+ )
458
+ from airbyte_cdk.sources.declarative.requesters.query_properties.strategies import (
459
+ GroupByKey,
460
+ )
435
461
  from airbyte_cdk.sources.declarative.requesters.request_option import RequestOptionType
436
462
  from airbyte_cdk.sources.declarative.requesters.request_options import (
437
463
  DatetimeBasedRequestOptionsProvider,
@@ -596,6 +622,7 @@ class ModelToComponentFactory:
596
622
  ResponseToFileExtractorModel: self.create_response_to_file_extractor,
597
623
  ExponentialBackoffStrategyModel: self.create_exponential_backoff_strategy,
598
624
  SessionTokenAuthenticatorModel: self.create_session_token_authenticator,
625
+ GroupByKeyMergeStrategyModel: self.create_group_by_key,
599
626
  HttpRequesterModel: self.create_http_requester,
600
627
  HttpResponseFilterModel: self.create_http_response_filter,
601
628
  InlineSchemaLoaderModel: self.create_inline_schema_loader,
@@ -625,6 +652,9 @@ class ModelToComponentFactory:
625
652
  OffsetIncrementModel: self.create_offset_increment,
626
653
  PageIncrementModel: self.create_page_increment,
627
654
  ParentStreamConfigModel: self.create_parent_stream_config,
655
+ PropertiesFromEndpointModel: self.create_properties_from_endpoint,
656
+ PropertyChunkingModel: self.create_property_chunking,
657
+ QueryPropertiesModel: self.create_query_properties,
628
658
  RecordFilterModel: self.create_record_filter,
629
659
  RecordSelectorModel: self.create_record_selector,
630
660
  RemoveFieldsModel: self.create_remove_fields,
@@ -2083,8 +2113,8 @@ class ModelToComponentFactory:
2083
2113
  parameters=model.parameters or {},
2084
2114
  )
2085
2115
 
2116
+ @staticmethod
2086
2117
  def create_response_to_file_extractor(
2087
- self,
2088
2118
  model: ResponseToFileExtractorModel,
2089
2119
  **kwargs: Any,
2090
2120
  ) -> ResponseToFileExtractor:
@@ -2098,11 +2128,17 @@ class ModelToComponentFactory:
2098
2128
  factor=model.factor or 5, parameters=model.parameters or {}, config=config
2099
2129
  )
2100
2130
 
2131
+ @staticmethod
2132
+ def create_group_by_key(model: GroupByKeyMergeStrategyModel, config: Config) -> GroupByKey:
2133
+ return GroupByKey(model.key, config=config, parameters=model.parameters or {})
2134
+
2101
2135
  def create_http_requester(
2102
2136
  self,
2103
2137
  model: HttpRequesterModel,
2104
2138
  config: Config,
2105
2139
  decoder: Decoder = JsonDecoder(parameters={}),
2140
+ query_properties_key: Optional[str] = None,
2141
+ use_cache: Optional[bool] = None,
2106
2142
  *,
2107
2143
  name: str,
2108
2144
  ) -> HttpRequester:
@@ -2135,6 +2171,7 @@ class ModelToComponentFactory:
2135
2171
  request_body_json=model.request_body_json,
2136
2172
  request_headers=model.request_headers,
2137
2173
  request_parameters=model.request_parameters,
2174
+ query_properties_key=query_properties_key,
2138
2175
  config=config,
2139
2176
  parameters=model.parameters or {},
2140
2177
  )
@@ -2142,7 +2179,7 @@ class ModelToComponentFactory:
2142
2179
  assert model.use_cache is not None # for mypy
2143
2180
  assert model.http_method is not None # for mypy
2144
2181
 
2145
- use_cache = model.use_cache and not self._disable_cache
2182
+ should_use_cache = (model.use_cache or bool(use_cache)) and not self._disable_cache
2146
2183
 
2147
2184
  return HttpRequester(
2148
2185
  name=name,
@@ -2157,7 +2194,7 @@ class ModelToComponentFactory:
2157
2194
  disable_retries=self._disable_retries,
2158
2195
  parameters=model.parameters or {},
2159
2196
  message_repository=self._message_repository,
2160
- use_cache=use_cache,
2197
+ use_cache=should_use_cache,
2161
2198
  decoder=decoder,
2162
2199
  stream_response=decoder.is_stream_response() if decoder else False,
2163
2200
  )
@@ -2261,10 +2298,11 @@ class ModelToComponentFactory:
2261
2298
  retriever = self._create_component_from_model(
2262
2299
  model=model.retriever,
2263
2300
  config=config,
2264
- name="",
2301
+ name="dynamic_properties",
2265
2302
  primary_key=None,
2266
2303
  stream_slicer=combined_slicers,
2267
2304
  transformations=[],
2305
+ use_cache=True,
2268
2306
  )
2269
2307
  schema_type_identifier = self._create_component_from_model(
2270
2308
  model.schema_type_identifier, config=config, parameters=model.parameters or {}
@@ -2602,6 +2640,79 @@ class ModelToComponentFactory:
2602
2640
  lazy_read_pointer=model_lazy_read_pointer,
2603
2641
  )
2604
2642
 
2643
+ def create_properties_from_endpoint(
2644
+ self, model: PropertiesFromEndpointModel, config: Config, **kwargs: Any
2645
+ ) -> PropertiesFromEndpoint:
2646
+ retriever = self._create_component_from_model(
2647
+ model=model.retriever,
2648
+ config=config,
2649
+ name="dynamic_properties",
2650
+ primary_key=None,
2651
+ stream_slicer=None,
2652
+ transformations=[],
2653
+ use_cache=True, # Enable caching on the HttpRequester/HttpClient because the properties endpoint will be called for every slice being processed, and it is highly unlikely for the response to different
2654
+ )
2655
+ return PropertiesFromEndpoint(
2656
+ property_field_path=model.property_field_path,
2657
+ retriever=retriever,
2658
+ config=config,
2659
+ parameters=model.parameters or {},
2660
+ )
2661
+
2662
+ def create_property_chunking(
2663
+ self, model: PropertyChunkingModel, config: Config, **kwargs: Any
2664
+ ) -> PropertyChunking:
2665
+ record_merge_strategy = (
2666
+ self._create_component_from_model(
2667
+ model=model.record_merge_strategy, config=config, **kwargs
2668
+ )
2669
+ if model.record_merge_strategy
2670
+ else None
2671
+ )
2672
+
2673
+ property_limit_type: PropertyLimitType
2674
+ match model.property_limit_type:
2675
+ case PropertyLimitTypeModel.property_count:
2676
+ property_limit_type = PropertyLimitType.property_count
2677
+ case PropertyLimitTypeModel.characters:
2678
+ property_limit_type = PropertyLimitType.characters
2679
+ case _:
2680
+ raise ValueError(f"Invalid PropertyLimitType {property_limit_type}")
2681
+
2682
+ return PropertyChunking(
2683
+ property_limit_type=property_limit_type,
2684
+ property_limit=model.property_limit,
2685
+ record_merge_strategy=record_merge_strategy,
2686
+ config=config,
2687
+ parameters=model.parameters or {},
2688
+ )
2689
+
2690
+ def create_query_properties(
2691
+ self, model: QueryPropertiesModel, config: Config, **kwargs: Any
2692
+ ) -> QueryProperties:
2693
+ if isinstance(model.property_list, list):
2694
+ property_list = model.property_list
2695
+ else:
2696
+ property_list = self._create_component_from_model(
2697
+ model=model.property_list, config=config, **kwargs
2698
+ )
2699
+
2700
+ property_chunking = (
2701
+ self._create_component_from_model(
2702
+ model=model.property_chunking, config=config, **kwargs
2703
+ )
2704
+ if model.property_chunking
2705
+ else None
2706
+ )
2707
+
2708
+ return QueryProperties(
2709
+ property_list=property_list,
2710
+ always_include_properties=model.always_include_properties,
2711
+ property_chunking=property_chunking,
2712
+ config=config,
2713
+ parameters=model.parameters or {},
2714
+ )
2715
+
2605
2716
  @staticmethod
2606
2717
  def create_record_filter(
2607
2718
  model: RecordFilterModel, config: Config, **kwargs: Any
@@ -2747,6 +2858,7 @@ class ModelToComponentFactory:
2747
2858
  IncrementingCountCursorModel, DatetimeBasedCursorModel, CustomIncrementalSyncModel
2748
2859
  ]
2749
2860
  ] = None,
2861
+ use_cache: Optional[bool] = None,
2750
2862
  **kwargs: Any,
2751
2863
  ) -> SimpleRetriever:
2752
2864
  decoder = (
@@ -2754,9 +2866,6 @@ class ModelToComponentFactory:
2754
2866
  if model.decoder
2755
2867
  else JsonDecoder(parameters={})
2756
2868
  )
2757
- requester = self._create_component_from_model(
2758
- model=model.requester, decoder=decoder, config=config, name=name
2759
- )
2760
2869
  record_selector = self._create_component_from_model(
2761
2870
  model=model.record_selector,
2762
2871
  name=name,
@@ -2765,6 +2874,57 @@ class ModelToComponentFactory:
2765
2874
  transformations=transformations,
2766
2875
  client_side_incremental_sync=client_side_incremental_sync,
2767
2876
  )
2877
+
2878
+ query_properties: Optional[QueryProperties] = None
2879
+ query_properties_key: Optional[str] = None
2880
+ if (
2881
+ hasattr(model.requester, "request_parameters")
2882
+ and model.requester.request_parameters
2883
+ and isinstance(model.requester.request_parameters, Mapping)
2884
+ ):
2885
+ query_properties_definitions = []
2886
+ for key, request_parameter in model.requester.request_parameters.items():
2887
+ # When translating JSON schema into Pydantic models, enforcing types for arrays containing both
2888
+ # concrete string complex object definitions like QueryProperties would get resolved to Union[str, Any].
2889
+ # This adds the extra validation that we couldn't get for free in Pydantic model generation
2890
+ if (
2891
+ isinstance(request_parameter, Mapping)
2892
+ and request_parameter.get("type") == "QueryProperties"
2893
+ ):
2894
+ query_properties_key = key
2895
+ query_properties_definitions.append(request_parameter)
2896
+ elif not isinstance(request_parameter, str):
2897
+ raise ValueError(
2898
+ f"Each element of request_parameters should be of type str or QueryProperties, but received {request_parameter.get('type')}"
2899
+ )
2900
+
2901
+ if len(query_properties_definitions) > 1:
2902
+ raise ValueError(
2903
+ f"request_parameters only supports defining one QueryProperties field, but found {len(query_properties_definitions)} usages"
2904
+ )
2905
+
2906
+ if len(query_properties_definitions) == 1:
2907
+ query_properties = self.create_component(
2908
+ model_type=QueryPropertiesModel,
2909
+ component_definition=query_properties_definitions[0],
2910
+ config=config,
2911
+ )
2912
+
2913
+ # Removes QueryProperties components from the interpolated mappings because it will be resolved in
2914
+ # the provider from the slice directly instead of through jinja interpolation
2915
+ if isinstance(model.requester.request_parameters, Mapping):
2916
+ model.requester.request_parameters = self._remove_query_properties(
2917
+ model.requester.request_parameters
2918
+ )
2919
+
2920
+ requester = self._create_component_from_model(
2921
+ model=model.requester,
2922
+ decoder=decoder,
2923
+ name=name,
2924
+ query_properties_key=query_properties_key,
2925
+ use_cache=use_cache,
2926
+ config=config,
2927
+ )
2768
2928
  url_base = (
2769
2929
  model.requester.url_base
2770
2930
  if hasattr(model.requester, "url_base")
@@ -2870,9 +3030,21 @@ class ModelToComponentFactory:
2870
3030
  cursor=cursor,
2871
3031
  config=config,
2872
3032
  ignore_stream_slicer_parameters_on_paginated_requests=ignore_stream_slicer_parameters_on_paginated_requests,
3033
+ additional_query_properties=query_properties,
2873
3034
  parameters=model.parameters or {},
2874
3035
  )
2875
3036
 
3037
+ @staticmethod
3038
+ def _remove_query_properties(
3039
+ request_parameters: Mapping[str, Union[Any, str]],
3040
+ ) -> Mapping[str, Union[Any, str]]:
3041
+ return {
3042
+ parameter_field: request_parameter
3043
+ for parameter_field, request_parameter in request_parameters.items()
3044
+ if not isinstance(request_parameter, Mapping)
3045
+ or not request_parameter.get("type") == "QueryProperties"
3046
+ }
3047
+
2876
3048
  def create_state_delegating_stream(
2877
3049
  self,
2878
3050
  model: StateDelegatingStreamModel,
@@ -0,0 +1,13 @@
1
+ # Copyright (c) 2025 Airbyte, Inc., all rights reserved.
2
+
3
+ from airbyte_cdk.sources.declarative.requesters.query_properties.properties_from_endpoint import (
4
+ PropertiesFromEndpoint,
5
+ )
6
+ from airbyte_cdk.sources.declarative.requesters.query_properties.property_chunking import (
7
+ PropertyChunking,
8
+ )
9
+ from airbyte_cdk.sources.declarative.requesters.query_properties.query_properties import (
10
+ QueryProperties,
11
+ )
12
+
13
+ __all__ = ["PropertiesFromEndpoint", "PropertyChunking", "QueryProperties"]
@@ -0,0 +1,40 @@
1
+ # Copyright (c) 2025 Airbyte, Inc., all rights reserved.
2
+
3
+ from dataclasses import InitVar, dataclass
4
+ from typing import Any, Iterable, List, Mapping, Optional
5
+
6
+ import dpath
7
+
8
+ from airbyte_cdk.sources.declarative.interpolation import InterpolatedString
9
+ from airbyte_cdk.sources.declarative.retrievers import Retriever
10
+ from airbyte_cdk.sources.types import Config, StreamSlice
11
+
12
+
13
+ @dataclass
14
+ class PropertiesFromEndpoint:
15
+ """
16
+ Component that defines the behavior around how to dynamically retrieve a set of request properties from an
17
+ API endpoint. The set retrieved can then be injected into the requests to extract records from an API source.
18
+ """
19
+
20
+ property_field_path: List[str]
21
+ retriever: Retriever
22
+ config: Config
23
+ parameters: InitVar[Mapping[str, Any]]
24
+
25
+ def __post_init__(self, parameters: Mapping[str, Any]) -> None:
26
+ self._property_field_path = [
27
+ InterpolatedString(string=property_field, parameters=parameters)
28
+ for property_field in self.property_field_path
29
+ ]
30
+
31
+ def get_properties_from_endpoint(self, stream_slice: Optional[StreamSlice]) -> Iterable[str]:
32
+ response_properties = self.retriever.read_records(
33
+ records_schema={}, stream_slice=stream_slice
34
+ )
35
+ for property_obj in response_properties:
36
+ path = [
37
+ node.eval(self.config) if not isinstance(node, str) else node
38
+ for node in self._property_field_path
39
+ ]
40
+ yield dpath.get(property_obj, path, default=[]) # type: ignore # extracted will be a MutableMapping, given input data structure
@@ -0,0 +1,69 @@
1
+ # Copyright (c) 2025 Airbyte, Inc., all rights reserved.
2
+
3
+ from dataclasses import InitVar, dataclass
4
+ from enum import Enum
5
+ from typing import Any, Iterable, List, Mapping, Optional
6
+
7
+ from airbyte_cdk.sources.declarative.requesters.query_properties.strategies import GroupByKey
8
+ from airbyte_cdk.sources.declarative.requesters.query_properties.strategies.merge_strategy import (
9
+ RecordMergeStrategy,
10
+ )
11
+ from airbyte_cdk.sources.types import Config, Record
12
+
13
+
14
+ class PropertyLimitType(Enum):
15
+ """
16
+ The heuristic that determines when the maximum size of the current chunk of properties and when a new
17
+ one should be started.
18
+ """
19
+
20
+ characters = "characters"
21
+ property_count = "property_count"
22
+
23
+
24
+ @dataclass
25
+ class PropertyChunking:
26
+ """
27
+ Defines the behavior for how the complete list of properties to query for are broken down into smaller groups
28
+ that will be used for multiple requests to the target API.
29
+ """
30
+
31
+ property_limit_type: PropertyLimitType
32
+ property_limit: Optional[int]
33
+ record_merge_strategy: Optional[RecordMergeStrategy]
34
+ parameters: InitVar[Mapping[str, Any]]
35
+ config: Config
36
+
37
+ def __post_init__(self, parameters: Mapping[str, Any]) -> None:
38
+ self._record_merge_strategy = self.record_merge_strategy or GroupByKey(
39
+ key="id", config=self.config, parameters=parameters
40
+ )
41
+
42
+ def get_request_property_chunks(
43
+ self, property_fields: Iterable[str], always_include_properties: Optional[List[str]]
44
+ ) -> Iterable[List[str]]:
45
+ if not self.property_limit:
46
+ single_property_chunk = list(property_fields)
47
+ if always_include_properties:
48
+ single_property_chunk.extend(always_include_properties)
49
+ yield single_property_chunk
50
+ return
51
+ current_chunk = list(always_include_properties) if always_include_properties else []
52
+ chunk_size = 0
53
+ for property_field in property_fields:
54
+ # If property_limit_type is not defined, we default to property_count which is just an incrementing count
55
+ property_field_size = (
56
+ len(property_field)
57
+ if self.property_limit_type == PropertyLimitType.characters
58
+ else 1
59
+ )
60
+ if chunk_size + property_field_size > self.property_limit:
61
+ yield current_chunk
62
+ current_chunk = list(always_include_properties) if always_include_properties else []
63
+ chunk_size = 0
64
+ current_chunk.append(property_field)
65
+ chunk_size += property_field_size
66
+ yield current_chunk
67
+
68
+ def get_merge_key(self, record: Record) -> Optional[str]:
69
+ return self._record_merge_strategy.get_group_key(record=record)
@@ -0,0 +1,58 @@
1
+ # Copyright (c) 2025 Airbyte, Inc., all rights reserved.
2
+
3
+ from dataclasses import InitVar, dataclass
4
+ from typing import Any, Iterable, List, Mapping, Optional, Union
5
+
6
+ from airbyte_cdk.sources.declarative.requesters.query_properties import (
7
+ PropertiesFromEndpoint,
8
+ PropertyChunking,
9
+ )
10
+ from airbyte_cdk.sources.types import Config, StreamSlice
11
+
12
+
13
+ @dataclass
14
+ class QueryProperties:
15
+ """
16
+ Low-code component that encompasses the behavior to inject additional property values into the outbound API
17
+ requests. Property values can be defined statically within the manifest or dynamically by making requests
18
+ to a partner API to retrieve the properties. Query properties also allow for splitting of the total set of
19
+ properties into smaller chunks to satisfy API restrictions around the total amount of data retrieved
20
+ """
21
+
22
+ property_list: Optional[Union[List[str], PropertiesFromEndpoint]]
23
+ always_include_properties: Optional[List[str]]
24
+ property_chunking: Optional[PropertyChunking]
25
+ config: Config
26
+ parameters: InitVar[Mapping[str, Any]]
27
+
28
+ def get_request_property_chunks(
29
+ self, stream_slice: Optional[StreamSlice] = None
30
+ ) -> Iterable[List[str]]:
31
+ """
32
+ Uses the defined property_list to fetch the total set of properties dynamically or from a static list
33
+ and based on the resulting properties, performs property chunking if applicable.
34
+ :param stream_slice: The StreamSlice of the current partition being processed during the sync. This is included
35
+ because subcomponents of QueryProperties can make use of interpolation of the top-level StreamSlice object
36
+ """
37
+ fields: Union[Iterable[str], List[str]]
38
+ if isinstance(self.property_list, PropertiesFromEndpoint):
39
+ fields = self.property_list.get_properties_from_endpoint(stream_slice=stream_slice)
40
+ else:
41
+ fields = self.property_list if self.property_list else []
42
+
43
+ if self.property_chunking:
44
+ yield from self.property_chunking.get_request_property_chunks(
45
+ property_fields=fields, always_include_properties=self.always_include_properties
46
+ )
47
+ else:
48
+ yield list(fields)
49
+
50
+ # delete later, but leaving this to keep the discussion thread on the PR from getting hidden
51
+ def has_multiple_chunks(self, stream_slice: Optional[StreamSlice]) -> bool:
52
+ property_chunks = iter(self.get_request_property_chunks(stream_slice=stream_slice))
53
+ try:
54
+ next(property_chunks)
55
+ next(property_chunks)
56
+ return True
57
+ except StopIteration:
58
+ return False
@@ -0,0 +1,10 @@
1
+ # Copyright (c) 2025 Airbyte, Inc., all rights reserved.
2
+
3
+ from airbyte_cdk.sources.declarative.requesters.query_properties.strategies.group_by_key import (
4
+ GroupByKey,
5
+ )
6
+ from airbyte_cdk.sources.declarative.requesters.query_properties.strategies.merge_strategy import (
7
+ RecordMergeStrategy,
8
+ )
9
+
10
+ __all__ = ["GroupByKey", "RecordMergeStrategy"]
@@ -0,0 +1,33 @@
1
+ # Copyright (c) 2025 Airbyte, Inc., all rights reserved.
2
+
3
+ from dataclasses import InitVar, dataclass
4
+ from typing import Any, List, Mapping, Optional, Union
5
+
6
+ from airbyte_cdk.sources.declarative.requesters.query_properties.strategies.merge_strategy import (
7
+ RecordMergeStrategy,
8
+ )
9
+ from airbyte_cdk.sources.types import Config, Record
10
+
11
+
12
+ @dataclass
13
+ class GroupByKey(RecordMergeStrategy):
14
+ """
15
+ Record merge strategy that combines records together according to values on the record for one or many keys.
16
+ """
17
+
18
+ key: Union[str, List[str]]
19
+ parameters: InitVar[Mapping[str, Any]]
20
+ config: Config
21
+
22
+ def __post_init__(self, parameters: Mapping[str, Any]) -> None:
23
+ self._keys = [self.key] if isinstance(self.key, str) else self.key
24
+
25
+ def get_group_key(self, record: Record) -> Optional[str]:
26
+ resolved_keys = []
27
+ for key in self._keys:
28
+ key_value = record.data.get(key)
29
+ if key_value:
30
+ resolved_keys.append(key_value)
31
+ else:
32
+ return None
33
+ return ",".join(resolved_keys)
@@ -0,0 +1,19 @@
1
+ # Copyright (c) 2025 Airbyte, Inc., all rights reserved.
2
+
3
+ from abc import ABC, abstractmethod
4
+ from dataclasses import dataclass
5
+ from typing import Optional
6
+
7
+ from airbyte_cdk.sources.types import Record
8
+
9
+
10
+ @dataclass
11
+ class RecordMergeStrategy(ABC):
12
+ """
13
+ Describe the interface for how records that required multiple requests to get the complete set of fields
14
+ should be merged back into a single record.
15
+ """
16
+
17
+ @abstractmethod
18
+ def get_group_key(self, record: Record) -> Optional[str]:
19
+ pass
@@ -1,9 +1,9 @@
1
1
  #
2
- # Copyright (c) 2023 Airbyte, Inc., all rights reserved.
2
+ # Copyright (c) 2025 Airbyte, Inc., all rights reserved.
3
3
  #
4
4
 
5
5
  from dataclasses import InitVar, dataclass, field
6
- from typing import Any, Mapping, MutableMapping, Optional, Union
6
+ from typing import Any, List, Mapping, MutableMapping, Optional, Union
7
7
 
8
8
  from airbyte_cdk.sources.declarative.interpolation.interpolated_nested_mapping import NestedMapping
9
9
  from airbyte_cdk.sources.declarative.requesters.request_options.interpolated_nested_request_input_provider import (
@@ -40,6 +40,7 @@ class InterpolatedRequestOptionsProvider(RequestOptionsProvider):
40
40
  request_headers: Optional[RequestInput] = None
41
41
  request_body_data: Optional[RequestInput] = None
42
42
  request_body_json: Optional[NestedMapping] = None
43
+ query_properties_key: Optional[str] = None
43
44
 
44
45
  def __post_init__(self, parameters: Mapping[str, Any]) -> None:
45
46
  if self.request_parameters is None:
@@ -83,6 +84,28 @@ class InterpolatedRequestOptionsProvider(RequestOptionsProvider):
83
84
  valid_value_types=ValidRequestTypes,
84
85
  )
85
86
  if isinstance(interpolated_value, dict):
87
+ if self.query_properties_key:
88
+ if not stream_slice:
89
+ raise ValueError(
90
+ "stream_slice should not be None if query properties in requests is enabled. Please contact Airbyte Support"
91
+ )
92
+ elif (
93
+ "query_properties" not in stream_slice.extra_fields
94
+ or stream_slice.extra_fields.get("query_properties") is None
95
+ ):
96
+ raise ValueError(
97
+ "QueryProperties component is defined but stream_partition does not contain query_properties. Please contact Airbyte Support"
98
+ )
99
+ elif not isinstance(stream_slice.extra_fields.get("query_properties"), List):
100
+ raise ValueError(
101
+ "QueryProperties component is defined but stream_slice.extra_fields.query_properties is not a List. Please contact Airbyte Support"
102
+ )
103
+ interpolated_value = {
104
+ **interpolated_value,
105
+ self.query_properties_key: ",".join(
106
+ stream_slice.extra_fields.get("query_properties") # type: ignore # Earlier type checks validate query_properties type
107
+ ),
108
+ }
86
109
  return interpolated_value
87
110
  return {}
88
111
 
@@ -1,8 +1,9 @@
1
1
  #
2
- # Copyright (c) 2023 Airbyte, Inc., all rights reserved.
2
+ # Copyright (c) 2025 Airbyte, Inc., all rights reserved.
3
3
  #
4
4
 
5
5
  import json
6
+ from collections import defaultdict
6
7
  from dataclasses import InitVar, dataclass, field
7
8
  from functools import partial
8
9
  from itertools import islice
@@ -12,6 +13,7 @@ from typing import (
12
13
  Iterable,
13
14
  List,
14
15
  Mapping,
16
+ MutableMapping,
15
17
  Optional,
16
18
  Set,
17
19
  Tuple,
@@ -31,6 +33,7 @@ from airbyte_cdk.sources.declarative.partition_routers.single_partition_router i
31
33
  )
32
34
  from airbyte_cdk.sources.declarative.requesters.paginators.no_pagination import NoPagination
33
35
  from airbyte_cdk.sources.declarative.requesters.paginators.paginator import Paginator
36
+ from airbyte_cdk.sources.declarative.requesters.query_properties import QueryProperties
34
37
  from airbyte_cdk.sources.declarative.requesters.request_options import (
35
38
  DefaultRequestOptionsProvider,
36
39
  RequestOptionsProvider,
@@ -88,6 +91,7 @@ class SimpleRetriever(Retriever):
88
91
  )
89
92
  cursor: Optional[DeclarativeCursor] = None
90
93
  ignore_stream_slicer_parameters_on_paginated_requests: bool = False
94
+ additional_query_properties: Optional[QueryProperties] = None
91
95
 
92
96
  def __post_init__(self, parameters: Mapping[str, Any]) -> None:
93
97
  self._paginator = self.paginator or NoPagination(parameters=parameters)
@@ -445,43 +449,110 @@ class SimpleRetriever(Retriever):
445
449
  :param stream_slice: The stream slice to read data for
446
450
  :return: The records read from the API source
447
451
  """
448
- _slice = stream_slice or StreamSlice(partition={}, cursor_slice={}) # None-check
449
452
 
450
- most_recent_record_from_slice = None
451
- record_generator = partial(
452
- self._parse_records,
453
- stream_slice=stream_slice,
454
- stream_state=self.state or {},
455
- records_schema=records_schema,
453
+ property_chunks = (
454
+ list(
455
+ self.additional_query_properties.get_request_property_chunks(
456
+ stream_slice=stream_slice
457
+ )
458
+ )
459
+ if self.additional_query_properties
460
+ else []
456
461
  )
462
+ records_without_merge_key = []
463
+ merged_records: MutableMapping[str, Any] = defaultdict(dict)
457
464
 
458
- if self.cursor and isinstance(self.cursor, ResumableFullRefreshCursor):
459
- stream_state = self.state
460
-
461
- # Before syncing the RFR stream, we check if the job's prior attempt was successful and don't need to
462
- # fetch more records. The platform deletes stream state for full refresh streams before starting a
463
- # new job, so we don't need to worry about this value existing for the initial attempt
464
- if stream_state.get(FULL_REFRESH_SYNC_COMPLETE_KEY):
465
- return
465
+ _slice = stream_slice or StreamSlice(partition={}, cursor_slice={}) # None-check
466
+ most_recent_record_from_slice = None
466
467
 
467
- yield from self._read_single_page(record_generator, stream_state, _slice)
468
- else:
469
- for stream_data in self._read_pages(record_generator, self.state, _slice):
470
- current_record = self._extract_record(stream_data, _slice)
471
- if self.cursor and current_record:
472
- self.cursor.observe(_slice, current_record)
473
-
474
- # Latest record read, not necessarily within slice boundaries.
475
- # TODO Remove once all custom components implement `observe` method.
476
- # https://github.com/airbytehq/airbyte-internal-issues/issues/6955
477
- most_recent_record_from_slice = self._get_most_recent_record(
478
- most_recent_record_from_slice, current_record, _slice
468
+ if self.additional_query_properties:
469
+ for properties in property_chunks:
470
+ _slice = StreamSlice(
471
+ partition=_slice.partition or {},
472
+ cursor_slice=_slice.cursor_slice or {},
473
+ extra_fields={"query_properties": properties},
474
+ ) # None-check
475
+
476
+ record_generator = partial(
477
+ self._parse_records,
478
+ stream_slice=_slice,
479
+ stream_state=self.state or {},
480
+ records_schema=records_schema,
479
481
  )
480
- yield stream_data
481
482
 
483
+ for stream_data in self._read_pages(record_generator, self.state, _slice):
484
+ current_record = self._extract_record(stream_data, _slice)
485
+ if self.cursor and current_record:
486
+ self.cursor.observe(_slice, current_record)
487
+
488
+ # Latest record read, not necessarily within slice boundaries.
489
+ # TODO Remove once all custom components implement `observe` method.
490
+ # https://github.com/airbytehq/airbyte-internal-issues/issues/6955
491
+ most_recent_record_from_slice = self._get_most_recent_record(
492
+ most_recent_record_from_slice, current_record, _slice
493
+ )
494
+
495
+ if current_record and self.additional_query_properties.property_chunking:
496
+ merge_key = (
497
+ self.additional_query_properties.property_chunking.get_merge_key(
498
+ current_record
499
+ )
500
+ )
501
+ if merge_key:
502
+ merged_records[merge_key].update(current_record)
503
+ else:
504
+ # We should still emit records even if the record did not have a merge key
505
+ records_without_merge_key.append(current_record)
506
+ else:
507
+ yield stream_data
482
508
  if self.cursor:
483
509
  self.cursor.close_slice(_slice, most_recent_record_from_slice)
484
- return
510
+
511
+ if len(merged_records) > 0:
512
+ yield from [
513
+ Record(data=merged_record, stream_name=self.name, associated_slice=stream_slice)
514
+ for merged_record in merged_records.values()
515
+ ]
516
+ if len(records_without_merge_key) > 0:
517
+ yield from records_without_merge_key
518
+ else:
519
+ _slice = stream_slice or StreamSlice(partition={}, cursor_slice={}) # None-check
520
+
521
+ most_recent_record_from_slice = None
522
+ record_generator = partial(
523
+ self._parse_records,
524
+ stream_slice=stream_slice,
525
+ stream_state=self.state or {},
526
+ records_schema=records_schema,
527
+ )
528
+
529
+ if self.cursor and isinstance(self.cursor, ResumableFullRefreshCursor):
530
+ stream_state = self.state
531
+
532
+ # Before syncing the RFR stream, we check if the job's prior attempt was successful and don't need to
533
+ # fetch more records. The platform deletes stream state for full refresh streams before starting a
534
+ # new job, so we don't need to worry about this value existing for the initial attempt
535
+ if stream_state.get(FULL_REFRESH_SYNC_COMPLETE_KEY):
536
+ return
537
+
538
+ yield from self._read_single_page(record_generator, stream_state, _slice)
539
+ else:
540
+ for stream_data in self._read_pages(record_generator, self.state, _slice):
541
+ current_record = self._extract_record(stream_data, _slice)
542
+ if self.cursor and current_record:
543
+ self.cursor.observe(_slice, current_record)
544
+
545
+ # Latest record read, not necessarily within slice boundaries.
546
+ # TODO Remove once all custom components implement `observe` method.
547
+ # https://github.com/airbytehq/airbyte-internal-issues/issues/6955
548
+ most_recent_record_from_slice = self._get_most_recent_record(
549
+ most_recent_record_from_slice, current_record, _slice
550
+ )
551
+ yield stream_data
552
+
553
+ if self.cursor:
554
+ self.cursor.close_slice(_slice, most_recent_record_from_slice)
555
+ return
485
556
 
486
557
  def _get_most_recent_record(
487
558
  self,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: airbyte-cdk
3
- Version: 6.44.0
3
+ Version: 6.45.0
4
4
  Summary: A framework for writing Airbyte Connectors.
5
5
  Home-page: https://airbyte.com
6
6
  License: MIT
@@ -71,7 +71,7 @@ airbyte_cdk/sources/declarative/concurrent_declarative_source.py,sha256=uhy0dRkA
71
71
  airbyte_cdk/sources/declarative/datetime/__init__.py,sha256=4Hw-PX1-VgESLF16cDdvuYCzGJtHntThLF4qIiULWeo,61
72
72
  airbyte_cdk/sources/declarative/datetime/datetime_parser.py,sha256=_zGNGq31RNy_0QBLt_EcTvgPyhj7urPdx6oA3M5-r3o,3150
73
73
  airbyte_cdk/sources/declarative/datetime/min_max_datetime.py,sha256=0BHBtDNQZfvwM45-tY5pNlTcKAFSGGNxemoi0Jic-0E,5785
74
- airbyte_cdk/sources/declarative/declarative_component_schema.yaml,sha256=pMzxm5YCtLM30wPIniTQk39oFRzvzsq7R_Mj2KEmaSg,153851
74
+ airbyte_cdk/sources/declarative/declarative_component_schema.yaml,sha256=pGFwleIC0sXFY-a6QphWyjY9QjUfHasgChPAMZFWjU8,158152
75
75
  airbyte_cdk/sources/declarative/declarative_source.py,sha256=nF7wBqFd3AQmEKAm4CnIo29CJoQL562cJGSCeL8U8bA,1531
76
76
  airbyte_cdk/sources/declarative/declarative_stream.py,sha256=dCRlddBUSaJmBNBz1pSO1r2rTw8AP5d2_vlmIeGs2gg,10767
77
77
  airbyte_cdk/sources/declarative/decoders/__init__.py,sha256=JHb_0d3SE6kNY10mxA5YBEKPeSbsWYjByq1gUQxepoE,953
@@ -114,13 +114,13 @@ airbyte_cdk/sources/declarative/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW
114
114
  airbyte_cdk/sources/declarative/migrations/legacy_to_per_partition_state_migration.py,sha256=iemy3fKLczcU0-Aor7tx5jcT6DRedKMqyK7kCOp01hg,3924
115
115
  airbyte_cdk/sources/declarative/migrations/state_migration.py,sha256=KWPjealMLKSMtajXgkdGgKg7EmTLR-CqqD7UIh0-eDU,794
116
116
  airbyte_cdk/sources/declarative/models/__init__.py,sha256=nUFxNCiKeYRVXuZEKA7GD-lTHxsiKcQ8FitZjKhPIvE,100
117
- airbyte_cdk/sources/declarative/models/declarative_component_schema.py,sha256=7lR9dulMMnCJps7hpbuN4Moh9FFG8sGaEIcsK4zi_7s,108868
117
+ airbyte_cdk/sources/declarative/models/declarative_component_schema.py,sha256=EfhCFVz_UHl7_cbRlf5hvFO1J5osxAZjISeqgbXE2t8,112007
118
118
  airbyte_cdk/sources/declarative/parsers/__init__.py,sha256=ZnqYNxHsKCgO38IwB34RQyRMXTs4GTvlRi3ImKnIioo,61
119
119
  airbyte_cdk/sources/declarative/parsers/custom_code_compiler.py,sha256=nlVvHC511NUyDEEIRBkoeDTAvLqKNp-hRy8D19z8tdk,5941
120
120
  airbyte_cdk/sources/declarative/parsers/custom_exceptions.py,sha256=Rir9_z3Kcd5Es0-LChrzk-0qubAsiK_RSEnLmK2OXm8,553
121
121
  airbyte_cdk/sources/declarative/parsers/manifest_component_transformer.py,sha256=4C15MKV-zOrMVQAm4FyohDsrJUBCSpMv5tZw0SK3aeI,9685
122
122
  airbyte_cdk/sources/declarative/parsers/manifest_reference_resolver.py,sha256=IWUOdF03o-aQn0Occo1BJCxU0Pz-QILk5L67nzw2thw,6803
123
- airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py,sha256=9nlof-3xlaYC_WxOFJ5gj065DCAIxkUo1Re5FOPiTnM,151021
123
+ airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py,sha256=Qim2qR64SKWbxD-Au0KAB3fLf6G2YfppUv3Q0w4QYtE,158619
124
124
  airbyte_cdk/sources/declarative/partition_routers/__init__.py,sha256=TBC9AkGaUqHm2IKHMPN6punBIcY5tWGULowcLoAVkfw,1109
125
125
  airbyte_cdk/sources/declarative/partition_routers/async_job_partition_router.py,sha256=VelO7zKqKtzMJ35jyFeg0ypJLQC0plqqIBNXoBW1G2E,3001
126
126
  airbyte_cdk/sources/declarative/partition_routers/cartesian_product_stream_slicer.py,sha256=c5cuVFM6NFkuQqG8Z5IwkBuwDrvXZN1CunUOM_L0ezg,6892
@@ -156,13 +156,20 @@ airbyte_cdk/sources/declarative/requesters/paginators/strategies/offset_incremen
156
156
  airbyte_cdk/sources/declarative/requesters/paginators/strategies/page_increment.py,sha256=Z2i6a-oKMmOTxHxsTVSnyaShkJ3u8xZw1xIJdx2yxss,2731
157
157
  airbyte_cdk/sources/declarative/requesters/paginators/strategies/pagination_strategy.py,sha256=ZBshGQNr5Bb_V8dqnWRISqdXFcjm1CKIXnlfbRhNl8g,1308
158
158
  airbyte_cdk/sources/declarative/requesters/paginators/strategies/stop_condition.py,sha256=LoKXdUbSgHEtSwtA8DFrnX6SpQbRVVwreY8NguTKTcI,2229
159
+ airbyte_cdk/sources/declarative/requesters/query_properties/__init__.py,sha256=sHwHVuN6djuRBF7zQb-HmINV0By4wE5j_i6TjmIPMzQ,494
160
+ airbyte_cdk/sources/declarative/requesters/query_properties/properties_from_endpoint.py,sha256=3h9Ae6TNGagh9sMYWdG5KoEFWDlqUWZ5fkswTPreveM,1616
161
+ airbyte_cdk/sources/declarative/requesters/query_properties/property_chunking.py,sha256=YmUeeY3ZpsuK2VTF3SkdVuJcplI1I4UfhgzOrggifag,2748
162
+ airbyte_cdk/sources/declarative/requesters/query_properties/query_properties.py,sha256=2VWhgphAFKmHJhzp-UoSP9_QR3eYOLPT0nzMDyglBV4,2650
163
+ airbyte_cdk/sources/declarative/requesters/query_properties/strategies/__init__.py,sha256=ojiPj9eVU7SuNpF3RZwhZWW21IYLQYEoxpzg1rCdvNM,350
164
+ airbyte_cdk/sources/declarative/requesters/query_properties/strategies/group_by_key.py,sha256=np4uTwSpQvXxubIzVbwSDX0Xf3EgVn8kkhs6zYLOdAQ,1081
165
+ airbyte_cdk/sources/declarative/requesters/query_properties/strategies/merge_strategy.py,sha256=iuk9QxpwvKVtdrq9eadQVkZ-Sfk3qhyyAAErprBfw2s,516
159
166
  airbyte_cdk/sources/declarative/requesters/request_option.py,sha256=Bl0gxGWudmwT3FXBozTN00WYle2jd6ry_S1YylCnwqM,4825
160
167
  airbyte_cdk/sources/declarative/requesters/request_options/__init__.py,sha256=WCwpKqM4wKqy-DHJaCHbKAlFqRVOqMi9K5qonxIfi_Y,809
161
168
  airbyte_cdk/sources/declarative/requesters/request_options/datetime_based_request_options_provider.py,sha256=31nG6_0igidJFQon37-WeQkTpG3g2A5ZmlluI3ilZdE,3632
162
169
  airbyte_cdk/sources/declarative/requesters/request_options/default_request_options_provider.py,sha256=SRROdPJZ5kuqHLOlkh115pWP9nDGfDxRYPgH9oD3hPo,1798
163
170
  airbyte_cdk/sources/declarative/requesters/request_options/interpolated_nested_request_input_provider.py,sha256=86YozYuBDfu0t9NbevIvQoGU0vqTP4rt3dRSTsHz3PA,2269
164
171
  airbyte_cdk/sources/declarative/requesters/request_options/interpolated_request_input_provider.py,sha256=rR00kE64U2yL0McU1gPr4_W5_sLUqwDgL3Nvj691nRU,2884
165
- airbyte_cdk/sources/declarative/requesters/request_options/interpolated_request_options_provider.py,sha256=vOsdHfWHiTFc89WENHPv1hcxLgdzycMXVT_IEtLuhfs,5012
172
+ airbyte_cdk/sources/declarative/requesters/request_options/interpolated_request_options_provider.py,sha256=dRlG1IyEOVzWFw7wm-8TBPn7JUtZw3jz6oAoH5yuuf0,6375
166
173
  airbyte_cdk/sources/declarative/requesters/request_options/request_options_provider.py,sha256=8YRiDzjYvqJ-aMmKFcjqzv_-e8OZ5QG_TbpZ-nuCu6s,2590
167
174
  airbyte_cdk/sources/declarative/requesters/request_path.py,sha256=S3MeFvcaQrMbOkSY2W2VbXLNomqt_3eXqVd9ZhgNwUs,299
168
175
  airbyte_cdk/sources/declarative/requesters/requester.py,sha256=OcDzuCBgD1owK_lBPG0KbRRHRn9kzbuRveU4HejDiv4,5116
@@ -173,7 +180,7 @@ airbyte_cdk/sources/declarative/resolvers/http_components_resolver.py,sha256=Aio
173
180
  airbyte_cdk/sources/declarative/retrievers/__init__.py,sha256=nQepwG_RfW53sgwvK5dLPqfCx0VjsQ83nYoPjBMAaLM,527
174
181
  airbyte_cdk/sources/declarative/retrievers/async_retriever.py,sha256=6oZtnCHm9NdDvjTSrVwPQOXGSdETSIR7eWH2vFjM7jI,4855
175
182
  airbyte_cdk/sources/declarative/retrievers/retriever.py,sha256=XPLs593Xv8c5cKMc37XzUAYmzlXd1a7eSsspM-CMuWA,1696
176
- airbyte_cdk/sources/declarative/retrievers/simple_retriever.py,sha256=p6O4FYS7zzPq6uQT2NVnughUjI66tePaXVlyhCAyyv0,27746
183
+ airbyte_cdk/sources/declarative/retrievers/simple_retriever.py,sha256=Ixs_byM-DMfYiYB-iOa97yH4WGAvxgTdTYeAsskHdYM,31134
177
184
  airbyte_cdk/sources/declarative/schema/__init__.py,sha256=xU45UvM5O4c1PSM13UHpCdh5hpW3HXy9vRRGEiAC1rg,795
178
185
  airbyte_cdk/sources/declarative/schema/default_schema_loader.py,sha256=KTACrIE23a83wsm3Rd9Eb4K6-20lrGqYxTHNp9yxsso,1820
179
186
  airbyte_cdk/sources/declarative/schema/dynamic_schema_loader.py,sha256=J8Q_iJYhcSQLWyt0bTZCbDAGpxt9G8FCc6Q9jtGsNzw,10703
@@ -359,9 +366,9 @@ airbyte_cdk/utils/slice_hasher.py,sha256=EDxgROHDbfG-QKQb59m7h_7crN1tRiawdf5uU7G
359
366
  airbyte_cdk/utils/spec_schema_transformations.py,sha256=-5HTuNsnDBAhj-oLeQXwpTGA0HdcjFOf2zTEMUTTg_Y,816
360
367
  airbyte_cdk/utils/stream_status_utils.py,sha256=ZmBoiy5HVbUEHAMrUONxZvxnvfV9CesmQJLDTAIWnWw,1171
361
368
  airbyte_cdk/utils/traced_exception.py,sha256=C8uIBuCL_E4WnBAOPSxBicD06JAldoN9fGsQDp463OY,6292
362
- airbyte_cdk-6.44.0.dist-info/LICENSE.txt,sha256=Wfe61S4BaGPj404v8lrAbvhjYR68SHlkzeYrg3_bbuM,1051
363
- airbyte_cdk-6.44.0.dist-info/LICENSE_SHORT,sha256=aqF6D1NcESmpn-cqsxBtszTEnHKnlsp8L4x9wAh3Nxg,55
364
- airbyte_cdk-6.44.0.dist-info/METADATA,sha256=kV2dhymFTY01knD0E2Ep9U3mb92lVtiEOAix38sgyCI,6071
365
- airbyte_cdk-6.44.0.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
366
- airbyte_cdk-6.44.0.dist-info/entry_points.txt,sha256=fj-e3PAQvsxsQzyyq8UkG1k8spunWnD4BAH2AwlR6NM,95
367
- airbyte_cdk-6.44.0.dist-info/RECORD,,
369
+ airbyte_cdk-6.45.0.dist-info/LICENSE.txt,sha256=Wfe61S4BaGPj404v8lrAbvhjYR68SHlkzeYrg3_bbuM,1051
370
+ airbyte_cdk-6.45.0.dist-info/LICENSE_SHORT,sha256=aqF6D1NcESmpn-cqsxBtszTEnHKnlsp8L4x9wAh3Nxg,55
371
+ airbyte_cdk-6.45.0.dist-info/METADATA,sha256=QFEPcdQP0d_cCYIUW4uSBOL-muBtxr0wu7kbmc65LkQ,6071
372
+ airbyte_cdk-6.45.0.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
373
+ airbyte_cdk-6.45.0.dist-info/entry_points.txt,sha256=fj-e3PAQvsxsQzyyq8UkG1k8spunWnD4BAH2AwlR6NM,95
374
+ airbyte_cdk-6.45.0.dist-info/RECORD,,