airbyte-cdk 6.45.9__py3-none-any.whl → 6.46.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 (31) hide show
  1. airbyte_cdk/cli/__init__.py +9 -1
  2. airbyte_cdk/cli/airbyte_cdk/__init__.py +86 -0
  3. airbyte_cdk/cli/airbyte_cdk/_connector.py +179 -0
  4. airbyte_cdk/cli/airbyte_cdk/_image.py +95 -0
  5. airbyte_cdk/cli/airbyte_cdk/_manifest.py +24 -0
  6. airbyte_cdk/cli/airbyte_cdk/_secrets.py +150 -0
  7. airbyte_cdk/cli/airbyte_cdk/_util.py +43 -0
  8. airbyte_cdk/cli/airbyte_cdk/_version.py +13 -0
  9. airbyte_cdk/connector_builder/connector_builder_handler.py +10 -0
  10. airbyte_cdk/models/connector_metadata.py +97 -0
  11. airbyte_cdk/sources/declarative/declarative_component_schema.yaml +108 -79
  12. airbyte_cdk/sources/declarative/manifest_declarative_source.py +122 -45
  13. airbyte_cdk/sources/declarative/models/declarative_component_schema.py +87 -82
  14. airbyte_cdk/sources/declarative/parsers/custom_exceptions.py +9 -0
  15. airbyte_cdk/sources/declarative/parsers/manifest_component_transformer.py +2 -2
  16. airbyte_cdk/sources/declarative/parsers/manifest_normalizer.py +462 -0
  17. airbyte_cdk/sources/declarative/parsers/manifest_reference_resolver.py +2 -2
  18. airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py +24 -24
  19. airbyte_cdk/sources/file_based/file_record_data.py +1 -1
  20. airbyte_cdk/test/standard_tests/connector_base.py +51 -25
  21. airbyte_cdk/test/standard_tests/declarative_sources.py +3 -1
  22. airbyte_cdk/test/standard_tests/test_resources.py +69 -0
  23. airbyte_cdk/test/standard_tests/util.py +79 -0
  24. airbyte_cdk/utils/docker.py +337 -0
  25. airbyte_cdk/utils/docker_image_templates.py +101 -0
  26. {airbyte_cdk-6.45.9.dist-info → airbyte_cdk-6.46.0.dist-info}/METADATA +6 -1
  27. {airbyte_cdk-6.45.9.dist-info → airbyte_cdk-6.46.0.dist-info}/RECORD +31 -18
  28. {airbyte_cdk-6.45.9.dist-info → airbyte_cdk-6.46.0.dist-info}/entry_points.txt +1 -0
  29. {airbyte_cdk-6.45.9.dist-info → airbyte_cdk-6.46.0.dist-info}/LICENSE.txt +0 -0
  30. {airbyte_cdk-6.45.9.dist-info → airbyte_cdk-6.46.0.dist-info}/LICENSE_SHORT +0 -0
  31. {airbyte_cdk-6.45.9.dist-info → airbyte_cdk-6.46.0.dist-info}/WHEEL +0 -0
@@ -1,5 +1,3 @@
1
- # Copyright (c) 2025 Airbyte, Inc., all rights reserved.
2
-
3
1
  # generated by datamodel-codegen:
4
2
  # filename: declarative_component_schema.yaml
5
3
 
@@ -417,15 +415,18 @@ class JwtAuthenticator(BaseModel):
417
415
  ...,
418
416
  description="Secret used to sign the JSON web token.",
419
417
  examples=["{{ config['secret_key'] }}"],
418
+ title="Secret Key",
420
419
  )
421
420
  base64_encode_secret_key: Optional[bool] = Field(
422
421
  False,
423
422
  description='When set to true, the secret key will be base64 encoded prior to being encoded as part of the JWT. Only set to "true" when required by the API.',
423
+ title="Base64-encode Secret Key",
424
424
  )
425
425
  algorithm: Algorithm = Field(
426
426
  ...,
427
427
  description="Algorithm used to sign the JSON web token.",
428
428
  examples=["ES256", "HS256", "RS256", "{{ config['algorithm'] }}"],
429
+ title="Algorithm",
429
430
  )
430
431
  token_duration: Optional[int] = Field(
431
432
  1200,
@@ -880,20 +881,17 @@ class FlattenFields(BaseModel):
880
881
 
881
882
 
882
883
  class KeyTransformation(BaseModel):
883
- prefix: Optional[Union[str, None]] = Field(
884
+ type: Literal["KeyTransformation"]
885
+ prefix: Optional[str] = Field(
884
886
  None,
885
887
  description="Prefix to add for object keys. If not provided original keys remain unchanged.",
886
- examples=[
887
- "flattened_",
888
- ],
888
+ examples=["flattened_"],
889
889
  title="Key Prefix",
890
890
  )
891
- suffix: Optional[Union[str, None]] = Field(
891
+ suffix: Optional[str] = Field(
892
892
  None,
893
893
  description="Suffix to add for object keys. If not provided original keys remain unchanged.",
894
- examples=[
895
- "_flattened",
896
- ],
894
+ examples=["_flattened"],
897
895
  title="Key Suffix",
898
896
  )
899
897
 
@@ -916,7 +914,7 @@ class DpathFlattenFields(BaseModel):
916
914
  description="Whether to replace the origin record or not. Default is False.",
917
915
  title="Replace Origin Record",
918
916
  )
919
- key_transformation: Optional[Union[KeyTransformation, None]] = Field(
917
+ key_transformation: Optional[KeyTransformation] = Field(
920
918
  None,
921
919
  description="Transformation for object keys. If not provided, original key will be used.",
922
920
  title="Key transformation",
@@ -1260,13 +1258,14 @@ class RecordFilter(BaseModel):
1260
1258
  "{{ record['created_at'] >= stream_interval['start_time'] }}",
1261
1259
  "{{ record.status in ['active', 'expired'] }}",
1262
1260
  ],
1261
+ title="Condition",
1263
1262
  )
1264
1263
  parameters: Optional[Dict[str, Any]] = Field(None, alias="$parameters")
1265
1264
 
1266
1265
 
1267
1266
  class SchemaNormalization(Enum):
1268
- None_ = "None"
1269
1267
  Default = "Default"
1268
+ None_ = "None"
1270
1269
 
1271
1270
 
1272
1271
  class RemoveFields(BaseModel):
@@ -1802,14 +1801,18 @@ class DefaultErrorHandler(BaseModel):
1802
1801
  class DefaultPaginator(BaseModel):
1803
1802
  type: Literal["DefaultPaginator"]
1804
1803
  pagination_strategy: Union[
1805
- CursorPagination, CustomPaginationStrategy, OffsetIncrement, PageIncrement
1804
+ PageIncrement, OffsetIncrement, CursorPagination, CustomPaginationStrategy
1806
1805
  ] = Field(
1807
1806
  ...,
1808
1807
  description="Strategy defining how records are paginated.",
1809
1808
  title="Pagination Strategy",
1810
1809
  )
1811
- page_size_option: Optional[RequestOption] = None
1812
- page_token_option: Optional[Union[RequestOption, RequestPath]] = None
1810
+ page_size_option: Optional[RequestOption] = Field(
1811
+ None, title="Inject Page Size Into Outgoing HTTP Request"
1812
+ )
1813
+ page_token_option: Optional[Union[RequestOption, RequestPath]] = Field(
1814
+ None, title="Inject Page Token Into Outgoing HTTP Request"
1815
+ )
1813
1816
  parameters: Optional[Dict[str, Any]] = Field(None, alias="$parameters")
1814
1817
 
1815
1818
 
@@ -1850,20 +1853,21 @@ class ListPartitionRouter(BaseModel):
1850
1853
 
1851
1854
  class RecordSelector(BaseModel):
1852
1855
  type: Literal["RecordSelector"]
1853
- extractor: Union[CustomRecordExtractor, DpathExtractor]
1854
- record_filter: Optional[Union[CustomRecordFilter, RecordFilter]] = Field(
1856
+ extractor: Union[DpathExtractor, CustomRecordExtractor]
1857
+ record_filter: Optional[Union[RecordFilter, CustomRecordFilter]] = Field(
1855
1858
  None,
1856
1859
  description="Responsible for filtering records to be emitted by the Source.",
1857
1860
  title="Record Filter",
1858
1861
  )
1859
1862
  schema_normalization: Optional[Union[SchemaNormalization, CustomSchemaNormalization]] = Field(
1860
- SchemaNormalization.None_,
1863
+ None,
1861
1864
  description="Responsible for normalization according to the schema.",
1862
1865
  title="Schema Normalization",
1863
1866
  )
1864
1867
  transform_before_filtering: Optional[bool] = Field(
1865
- False,
1868
+ None,
1866
1869
  description="If true, transformation will be applied before record filtering.",
1870
+ title="Transform Before Filtering",
1867
1871
  )
1868
1872
  parameters: Optional[Dict[str, Any]] = Field(None, alias="$parameters")
1869
1873
 
@@ -2096,26 +2100,26 @@ class DeclarativeStream(BaseModel):
2096
2100
  extra = Extra.allow
2097
2101
 
2098
2102
  type: Literal["DeclarativeStream"]
2099
- retriever: Union[AsyncRetriever, CustomRetriever, SimpleRetriever] = Field(
2103
+ name: Optional[str] = Field("", description="The stream name.", example=["Users"], title="Name")
2104
+ retriever: Union[SimpleRetriever, AsyncRetriever, CustomRetriever] = Field(
2100
2105
  ...,
2101
2106
  description="Component used to coordinate how records are extracted across stream slices and request pages.",
2102
2107
  title="Retriever",
2103
2108
  )
2104
2109
  incremental_sync: Optional[
2105
- Union[CustomIncrementalSync, DatetimeBasedCursor, IncrementingCountCursor]
2110
+ Union[DatetimeBasedCursor, IncrementingCountCursor, CustomIncrementalSync]
2106
2111
  ] = Field(
2107
2112
  None,
2108
2113
  description="Component used to fetch data incrementally based on a time field in the data.",
2109
2114
  title="Incremental Sync",
2110
2115
  )
2111
- name: Optional[str] = Field("", description="The stream name.", example=["Users"], title="Name")
2112
2116
  primary_key: Optional[PrimaryKey] = Field(
2113
2117
  "", description="The primary key of the stream.", title="Primary Key"
2114
2118
  )
2115
2119
  schema_loader: Optional[
2116
2120
  Union[
2117
- DynamicSchemaLoader,
2118
2121
  InlineSchemaLoader,
2122
+ DynamicSchemaLoader,
2119
2123
  JsonFileSchemaLoader,
2120
2124
  CustomSchemaLoader,
2121
2125
  ]
@@ -2205,7 +2209,7 @@ class HttpRequester(BaseModel):
2205
2209
  type: Literal["HttpRequester"]
2206
2210
  url_base: str = Field(
2207
2211
  ...,
2208
- description="Base URL of the API source. Do not put sensitive information (e.g. API tokens) into this field - Use the Authentication component for this.",
2212
+ description="The Base URL of the API source. Do not put sensitive information (e.g. API tokens) into this field - Use the Authentication component for this.",
2209
2213
  examples=[
2210
2214
  "https://connect.squareup.com/v2",
2211
2215
  "{{ config['base_url'] or 'https://app.posthog.com'}}/api",
@@ -2216,7 +2220,7 @@ class HttpRequester(BaseModel):
2216
2220
  )
2217
2221
  path: Optional[str] = Field(
2218
2222
  None,
2219
- description="Path the specific API endpoint that this stream represents. Do not put sensitive information (e.g. API tokens) into this field - Use the Authentication component for this.",
2223
+ description="The Path the specific API endpoint that this stream represents. Do not put sensitive information (e.g. API tokens) into this field - Use the Authentication component for this.",
2220
2224
  examples=[
2221
2225
  "/products",
2222
2226
  "/quotes/{{ stream_partition['id'] }}/quote_line_groups",
@@ -2224,38 +2228,31 @@ class HttpRequester(BaseModel):
2224
2228
  ],
2225
2229
  title="URL Path",
2226
2230
  )
2231
+ http_method: Optional[HttpMethod] = Field(
2232
+ HttpMethod.GET,
2233
+ description="The HTTP method used to fetch data from the source (can be GET or POST).",
2234
+ examples=["GET", "POST"],
2235
+ title="HTTP Method",
2236
+ )
2227
2237
  authenticator: Optional[
2228
2238
  Union[
2239
+ NoAuth,
2229
2240
  ApiKeyAuthenticator,
2230
2241
  BasicHttpAuthenticator,
2231
2242
  BearerAuthenticator,
2232
- CustomAuthenticator,
2233
2243
  OAuthAuthenticator,
2234
2244
  JwtAuthenticator,
2235
- NoAuth,
2236
2245
  SessionTokenAuthenticator,
2237
- LegacySessionTokenAuthenticator,
2238
2246
  SelectiveAuthenticator,
2247
+ CustomAuthenticator,
2248
+ LegacySessionTokenAuthenticator,
2239
2249
  ]
2240
2250
  ] = Field(
2241
2251
  None,
2242
2252
  description="Authentication method to use for requests sent to the API.",
2243
2253
  title="Authenticator",
2244
2254
  )
2245
- error_handler: Optional[
2246
- Union[DefaultErrorHandler, CustomErrorHandler, CompositeErrorHandler]
2247
- ] = Field(
2248
- None,
2249
- description="Error handler component that defines how to handle errors.",
2250
- title="Error Handler",
2251
- )
2252
- http_method: Optional[HttpMethod] = Field(
2253
- HttpMethod.GET,
2254
- description="The HTTP method used to fetch data from the source (can be GET or POST).",
2255
- examples=["GET", "POST"],
2256
- title="HTTP Method",
2257
- )
2258
- request_body_data: Optional[Union[str, Dict[str, str]]] = Field(
2255
+ request_body_data: Optional[Union[Dict[str, str], str]] = Field(
2259
2256
  None,
2260
2257
  description="Specifies how to populate the body of the request with a non-JSON payload. Plain text will be sent as is, whereas objects will be converted to a urlencoded form.",
2261
2258
  examples=[
@@ -2263,7 +2260,7 @@ class HttpRequester(BaseModel):
2263
2260
  ],
2264
2261
  title="Request Body Payload (Non-JSON)",
2265
2262
  )
2266
- request_body_json: Optional[Union[str, Dict[str, Any]]] = Field(
2263
+ request_body_json: Optional[Union[Dict[str, Any], str]] = Field(
2267
2264
  None,
2268
2265
  description="Specifies how to populate the body of the request with a JSON payload. Can contain nested objects.",
2269
2266
  examples=[
@@ -2273,13 +2270,13 @@ class HttpRequester(BaseModel):
2273
2270
  ],
2274
2271
  title="Request Body JSON Payload",
2275
2272
  )
2276
- request_headers: Optional[Union[str, Dict[str, str]]] = Field(
2273
+ request_headers: Optional[Union[Dict[str, str], str]] = Field(
2277
2274
  None,
2278
2275
  description="Return any non-auth headers. Authentication headers will overwrite any overlapping headers returned from this method.",
2279
2276
  examples=[{"Output-Format": "JSON"}, {"Version": "{{ config['version'] }}"}],
2280
2277
  title="Request Headers",
2281
2278
  )
2282
- request_parameters: Optional[Union[str, Dict[str, Union[str, Any]]]] = Field(
2279
+ request_parameters: Optional[Union[Dict[str, Union[str, QueryProperties]], str]] = Field(
2283
2280
  None,
2284
2281
  description="Specifies the query parameters that should be set on an outgoing HTTP request given the inputs.",
2285
2282
  examples=[
@@ -2292,6 +2289,13 @@ class HttpRequester(BaseModel):
2292
2289
  ],
2293
2290
  title="Query Parameters",
2294
2291
  )
2292
+ error_handler: Optional[
2293
+ Union[DefaultErrorHandler, CompositeErrorHandler, CustomErrorHandler]
2294
+ ] = Field(
2295
+ None,
2296
+ description="Error handler component that defines how to handle errors.",
2297
+ title="Error Handler",
2298
+ )
2295
2299
  use_cache: Optional[bool] = Field(
2296
2300
  False,
2297
2301
  description="Enables stream requests caching. This field is automatically set by the CDK.",
@@ -2409,25 +2413,41 @@ class StateDelegatingStream(BaseModel):
2409
2413
  full_refresh_stream: DeclarativeStream = Field(
2410
2414
  ...,
2411
2415
  description="Component used to coordinate how records are extracted across stream slices and request pages when the state is empty or not provided.",
2412
- title="Retriever",
2416
+ title="Full Refresh Stream",
2413
2417
  )
2414
2418
  incremental_stream: DeclarativeStream = Field(
2415
2419
  ...,
2416
2420
  description="Component used to coordinate how records are extracted across stream slices and request pages when the state provided.",
2417
- title="Retriever",
2421
+ title="Incremental Stream",
2418
2422
  )
2419
2423
  parameters: Optional[Dict[str, Any]] = Field(None, alias="$parameters")
2420
2424
 
2421
2425
 
2422
2426
  class SimpleRetriever(BaseModel):
2423
2427
  type: Literal["SimpleRetriever"]
2424
- record_selector: RecordSelector = Field(
2428
+ requester: Union[HttpRequester, CustomRequester] = Field(
2425
2429
  ...,
2426
- description="Component that describes how to extract records from a HTTP response.",
2430
+ description="Requester component that describes how to prepare HTTP requests to send to the source API.",
2427
2431
  )
2428
- requester: Union[CustomRequester, HttpRequester] = Field(
2432
+ decoder: Optional[
2433
+ Union[
2434
+ CsvDecoder,
2435
+ GzipDecoder,
2436
+ JsonDecoder,
2437
+ JsonlDecoder,
2438
+ IterableDecoder,
2439
+ XmlDecoder,
2440
+ ZipfileDecoder,
2441
+ CustomDecoder,
2442
+ ]
2443
+ ] = Field(
2444
+ None,
2445
+ description="Component decoding the response so records can be extracted.",
2446
+ title="Decoder",
2447
+ )
2448
+ record_selector: RecordSelector = Field(
2429
2449
  ...,
2430
- description="Requester component that describes how to prepare HTTP requests to send to the source API.",
2450
+ description="Component that describes how to extract records from a HTTP response.",
2431
2451
  )
2432
2452
  paginator: Optional[Union[DefaultPaginator, NoPagination]] = Field(
2433
2453
  None,
@@ -2439,16 +2459,16 @@ class SimpleRetriever(BaseModel):
2439
2459
  )
2440
2460
  partition_router: Optional[
2441
2461
  Union[
2442
- CustomPartitionRouter,
2443
2462
  ListPartitionRouter,
2444
2463
  SubstreamPartitionRouter,
2445
2464
  GroupingPartitionRouter,
2465
+ CustomPartitionRouter,
2446
2466
  List[
2447
2467
  Union[
2448
- CustomPartitionRouter,
2449
2468
  ListPartitionRouter,
2450
2469
  SubstreamPartitionRouter,
2451
2470
  GroupingPartitionRouter,
2471
+ CustomPartitionRouter,
2452
2472
  ]
2453
2473
  ],
2454
2474
  ]
@@ -2457,22 +2477,6 @@ class SimpleRetriever(BaseModel):
2457
2477
  description="PartitionRouter component that describes how to partition the stream, enabling incremental syncs and checkpointing.",
2458
2478
  title="Partition Router",
2459
2479
  )
2460
- decoder: Optional[
2461
- Union[
2462
- CustomDecoder,
2463
- CsvDecoder,
2464
- GzipDecoder,
2465
- JsonDecoder,
2466
- JsonlDecoder,
2467
- IterableDecoder,
2468
- XmlDecoder,
2469
- ZipfileDecoder,
2470
- ]
2471
- ] = Field(
2472
- None,
2473
- description="Component decoding the response so records can be extracted.",
2474
- title="Decoder",
2475
- )
2476
2480
  parameters: Optional[Dict[str, Any]] = Field(None, alias="$parameters")
2477
2481
 
2478
2482
 
@@ -2485,21 +2489,21 @@ class AsyncRetriever(BaseModel):
2485
2489
  status_mapping: AsyncJobStatusMap = Field(
2486
2490
  ..., description="Async Job Status to Airbyte CDK Async Job Status mapping."
2487
2491
  )
2488
- status_extractor: Union[CustomRecordExtractor, DpathExtractor] = Field(
2492
+ status_extractor: Union[DpathExtractor, CustomRecordExtractor] = Field(
2489
2493
  ..., description="Responsible for fetching the actual status of the async job."
2490
2494
  )
2491
- download_target_extractor: Union[CustomRecordExtractor, DpathExtractor] = Field(
2495
+ download_target_extractor: Union[DpathExtractor, CustomRecordExtractor] = Field(
2492
2496
  ...,
2493
2497
  description="Responsible for fetching the final result `urls` provided by the completed / finished / ready async job.",
2494
2498
  )
2495
2499
  download_extractor: Optional[
2496
- Union[CustomRecordExtractor, DpathExtractor, ResponseToFileExtractor]
2500
+ Union[DpathExtractor, CustomRecordExtractor, ResponseToFileExtractor]
2497
2501
  ] = Field(None, description="Responsible for fetching the records from provided urls.")
2498
- creation_requester: Union[CustomRequester, HttpRequester] = Field(
2502
+ creation_requester: Union[HttpRequester, CustomRequester] = Field(
2499
2503
  ...,
2500
2504
  description="Requester component that describes how to prepare HTTP requests to send to the source API to create the async server-side job.",
2501
2505
  )
2502
- polling_requester: Union[CustomRequester, HttpRequester] = Field(
2506
+ polling_requester: Union[HttpRequester, CustomRequester] = Field(
2503
2507
  ...,
2504
2508
  description="Requester component that describes how to prepare HTTP requests to send to the source API to fetch the status of the running async job.",
2505
2509
  )
@@ -2507,11 +2511,11 @@ class AsyncRetriever(BaseModel):
2507
2511
  None,
2508
2512
  description="The time in minutes after which the single Async Job should be considered as Timed Out.",
2509
2513
  )
2510
- download_target_requester: Optional[Union[CustomRequester, HttpRequester]] = Field(
2514
+ download_target_requester: Optional[Union[HttpRequester, CustomRequester]] = Field(
2511
2515
  None,
2512
2516
  description="Requester component that describes how to prepare HTTP requests to send to the source API to extract the url from polling response by the completed async job.",
2513
2517
  )
2514
- download_requester: Union[CustomRequester, HttpRequester] = Field(
2518
+ download_requester: Union[HttpRequester, CustomRequester] = Field(
2515
2519
  ...,
2516
2520
  description="Requester component that describes how to prepare HTTP requests to send to the source API to download the data provided by the completed async job.",
2517
2521
  )
@@ -2519,26 +2523,26 @@ class AsyncRetriever(BaseModel):
2519
2523
  None,
2520
2524
  description="Paginator component that describes how to navigate through the API's pages during download.",
2521
2525
  )
2522
- abort_requester: Optional[Union[CustomRequester, HttpRequester]] = Field(
2526
+ abort_requester: Optional[Union[HttpRequester, CustomRequester]] = Field(
2523
2527
  None,
2524
2528
  description="Requester component that describes how to prepare HTTP requests to send to the source API to abort a job once it is timed out from the source's perspective.",
2525
2529
  )
2526
- delete_requester: Optional[Union[CustomRequester, HttpRequester]] = Field(
2530
+ delete_requester: Optional[Union[HttpRequester, CustomRequester]] = Field(
2527
2531
  None,
2528
2532
  description="Requester component that describes how to prepare HTTP requests to send to the source API to delete a job once the records are extracted.",
2529
2533
  )
2530
2534
  partition_router: Optional[
2531
2535
  Union[
2532
- CustomPartitionRouter,
2533
2536
  ListPartitionRouter,
2534
2537
  SubstreamPartitionRouter,
2535
2538
  GroupingPartitionRouter,
2539
+ CustomPartitionRouter,
2536
2540
  List[
2537
2541
  Union[
2538
- CustomPartitionRouter,
2539
2542
  ListPartitionRouter,
2540
2543
  SubstreamPartitionRouter,
2541
2544
  GroupingPartitionRouter,
2545
+ CustomPartitionRouter,
2542
2546
  ]
2543
2547
  ],
2544
2548
  ]
@@ -2549,7 +2553,6 @@ class AsyncRetriever(BaseModel):
2549
2553
  )
2550
2554
  decoder: Optional[
2551
2555
  Union[
2552
- CustomDecoder,
2553
2556
  CsvDecoder,
2554
2557
  GzipDecoder,
2555
2558
  JsonDecoder,
@@ -2557,6 +2560,7 @@ class AsyncRetriever(BaseModel):
2557
2560
  IterableDecoder,
2558
2561
  XmlDecoder,
2559
2562
  ZipfileDecoder,
2563
+ CustomDecoder,
2560
2564
  ]
2561
2565
  ] = Field(
2562
2566
  None,
@@ -2565,7 +2569,6 @@ class AsyncRetriever(BaseModel):
2565
2569
  )
2566
2570
  download_decoder: Optional[
2567
2571
  Union[
2568
- CustomDecoder,
2569
2572
  CsvDecoder,
2570
2573
  GzipDecoder,
2571
2574
  JsonDecoder,
@@ -2573,6 +2576,7 @@ class AsyncRetriever(BaseModel):
2573
2576
  IterableDecoder,
2574
2577
  XmlDecoder,
2575
2578
  ZipfileDecoder,
2579
+ CustomDecoder,
2576
2580
  ]
2577
2581
  ] = Field(
2578
2582
  None,
@@ -2650,6 +2654,7 @@ SelectiveAuthenticator.update_forward_refs()
2650
2654
  FileUploader.update_forward_refs()
2651
2655
  DeclarativeStream.update_forward_refs()
2652
2656
  SessionTokenAuthenticator.update_forward_refs()
2657
+ HttpRequester.update_forward_refs()
2653
2658
  DynamicSchemaLoader.update_forward_refs()
2654
2659
  ParentStreamConfig.update_forward_refs()
2655
2660
  PropertiesFromEndpoint.update_forward_refs()
@@ -19,3 +19,12 @@ class UndefinedReferenceException(Exception):
19
19
 
20
20
  def __init__(self, path: str, reference: str) -> None:
21
21
  super().__init__(f"Undefined reference {reference} from {path}")
22
+
23
+
24
+ class ManifestNormalizationException(Exception):
25
+ """
26
+ Raised when a circular reference is detected in a manifest.
27
+ """
28
+
29
+ def __init__(self, message: str) -> None:
30
+ super().__init__(f"Failed to deduplicate manifest: {message}")
@@ -4,7 +4,7 @@
4
4
 
5
5
  import copy
6
6
  import typing
7
- from typing import Any, Mapping, Optional
7
+ from typing import Any, Dict, Mapping, Optional
8
8
 
9
9
  PARAMETERS_STR = "$parameters"
10
10
 
@@ -95,7 +95,7 @@ class ManifestComponentTransformer:
95
95
  declarative_component: Mapping[str, Any],
96
96
  parent_parameters: Mapping[str, Any],
97
97
  use_parent_parameters: Optional[bool] = None,
98
- ) -> Mapping[str, Any]:
98
+ ) -> Dict[str, Any]:
99
99
  """
100
100
  Recursively transforms the specified declarative component and subcomponents to propagate parameters and insert the
101
101
  default component type if it was not already present. The resulting transformed components are a deep copy of the input