airbyte-cdk 6.54.11__py3-none-any.whl → 6.55.1__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.
- airbyte_cdk/cli/airbyte_cdk/_connector.py +32 -8
- airbyte_cdk/cli/airbyte_cdk/_image.py +76 -0
- airbyte_cdk/cli/airbyte_cdk/_secrets.py +13 -12
- airbyte_cdk/models/airbyte_protocol_serializers.py +4 -0
- airbyte_cdk/models/connector_metadata.py +14 -0
- airbyte_cdk/sources/declarative/concurrent_declarative_source.py +1 -1
- airbyte_cdk/sources/declarative/declarative_component_schema.yaml +31 -0
- airbyte_cdk/sources/declarative/manifest_declarative_source.py +28 -9
- airbyte_cdk/sources/declarative/models/declarative_component_schema.py +23 -2
- airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py +2 -2
- airbyte_cdk/test/entrypoint_wrapper.py +163 -26
- airbyte_cdk/test/models/scenario.py +49 -10
- airbyte_cdk/test/standard_tests/__init__.py +2 -4
- airbyte_cdk/test/standard_tests/connector_base.py +12 -80
- airbyte_cdk/test/standard_tests/docker_base.py +388 -0
- airbyte_cdk/test/standard_tests/pytest_hooks.py +115 -2
- airbyte_cdk/test/standard_tests/source_base.py +13 -7
- airbyte_cdk/test/standard_tests/util.py +4 -3
- airbyte_cdk/utils/connector_paths.py +3 -3
- airbyte_cdk/utils/docker.py +83 -34
- {airbyte_cdk-6.54.11.dist-info → airbyte_cdk-6.55.1.dist-info}/METADATA +2 -1
- {airbyte_cdk-6.54.11.dist-info → airbyte_cdk-6.55.1.dist-info}/RECORD +26 -25
- {airbyte_cdk-6.54.11.dist-info → airbyte_cdk-6.55.1.dist-info}/LICENSE.txt +0 -0
- {airbyte_cdk-6.54.11.dist-info → airbyte_cdk-6.55.1.dist-info}/LICENSE_SHORT +0 -0
- {airbyte_cdk-6.54.11.dist-info → airbyte_cdk-6.55.1.dist-info}/WHEEL +0 -0
- {airbyte_cdk-6.54.11.dist-info → airbyte_cdk-6.55.1.dist-info}/entry_points.txt +0 -0
@@ -101,7 +101,7 @@ def connector_cli_group() -> None:
|
|
101
101
|
pass
|
102
102
|
|
103
103
|
|
104
|
-
@connector_cli_group.command()
|
104
|
+
@connector_cli_group.command("test")
|
105
105
|
@click.argument(
|
106
106
|
"connector",
|
107
107
|
required=False,
|
@@ -114,10 +114,18 @@ def connector_cli_group() -> None:
|
|
114
114
|
default=False,
|
115
115
|
help="Only collect tests, do not run them.",
|
116
116
|
)
|
117
|
-
|
117
|
+
@click.option(
|
118
|
+
"--pytest-arg",
|
119
|
+
"pytest_args", # ← map --pytest-arg into pytest_args
|
120
|
+
type=str,
|
121
|
+
multiple=True,
|
122
|
+
help="Additional argument(s) to pass to pytest. Can be specified multiple times.",
|
123
|
+
)
|
124
|
+
def connector_test(
|
118
125
|
connector: str | Path | None = None,
|
119
126
|
*,
|
120
127
|
collect_only: bool = False,
|
128
|
+
pytest_args: list[str] | None = None,
|
121
129
|
) -> None:
|
122
130
|
"""Run connector tests.
|
123
131
|
|
@@ -130,19 +138,36 @@ def test(
|
|
130
138
|
directory. If the current working directory is not a connector directory (e.g. starting
|
131
139
|
with 'source-') and no connector name or path is provided, the process will fail.
|
132
140
|
"""
|
141
|
+
click.echo("Connector test command executed.")
|
142
|
+
connector_name, connector_directory = resolve_connector_name_and_directory(connector)
|
143
|
+
|
144
|
+
pytest_args = pytest_args or []
|
145
|
+
if collect_only:
|
146
|
+
pytest_args.append("--collect-only")
|
147
|
+
|
148
|
+
run_connector_tests(
|
149
|
+
connector_name=connector_name,
|
150
|
+
connector_directory=connector_directory,
|
151
|
+
extra_pytest_args=pytest_args,
|
152
|
+
)
|
153
|
+
|
154
|
+
|
155
|
+
def run_connector_tests(
|
156
|
+
connector_name: str,
|
157
|
+
connector_directory: Path,
|
158
|
+
extra_pytest_args: list[str],
|
159
|
+
) -> None:
|
133
160
|
if pytest is None:
|
134
161
|
raise ImportError(
|
135
162
|
"pytest is not installed. Please install pytest to run the connector tests."
|
136
163
|
)
|
137
|
-
click.echo("Connector test command executed.")
|
138
|
-
connector_name, connector_directory = resolve_connector_name_and_directory(connector)
|
139
164
|
|
140
165
|
connector_test_suite = create_connector_test_suite(
|
141
166
|
connector_name=connector_name if not connector_directory else None,
|
142
167
|
connector_directory=connector_directory,
|
143
168
|
)
|
144
169
|
|
145
|
-
pytest_args: list[str] = []
|
170
|
+
pytest_args: list[str] = ["-p", "airbyte_cdk.test.standard_tests.pytest_hooks"]
|
146
171
|
if connector_directory:
|
147
172
|
pytest_args.append(f"--rootdir={connector_directory}")
|
148
173
|
os.chdir(str(connector_directory))
|
@@ -158,8 +183,8 @@ def test(
|
|
158
183
|
test_file_path.parent.mkdir(parents=True, exist_ok=True)
|
159
184
|
test_file_path.write_text(file_text)
|
160
185
|
|
161
|
-
if
|
162
|
-
pytest_args.
|
186
|
+
if extra_pytest_args:
|
187
|
+
pytest_args.extend(extra_pytest_args)
|
163
188
|
|
164
189
|
pytest_args.append(str(test_file_path))
|
165
190
|
|
@@ -170,7 +195,6 @@ def test(
|
|
170
195
|
|
171
196
|
click.echo(f"Running tests from connector directory: {connector_directory}...")
|
172
197
|
click.echo(f"Test file: {test_file_path}")
|
173
|
-
click.echo(f"Collect only: {collect_only}")
|
174
198
|
click.echo(f"Pytest args: {pytest_args}")
|
175
199
|
click.echo("Invoking Pytest...")
|
176
200
|
exit_code = pytest.main(
|
@@ -10,6 +10,7 @@ from pathlib import Path
|
|
10
10
|
|
11
11
|
import rich_click as click
|
12
12
|
|
13
|
+
from airbyte_cdk.cli.airbyte_cdk._connector import run_connector_tests
|
13
14
|
from airbyte_cdk.models.connector_metadata import MetadataFile
|
14
15
|
from airbyte_cdk.utils.connector_paths import resolve_connector_name_and_directory
|
15
16
|
from airbyte_cdk.utils.docker import (
|
@@ -88,6 +89,81 @@ def build(
|
|
88
89
|
sys.exit(1)
|
89
90
|
|
90
91
|
|
92
|
+
@image_cli_group.command("test")
|
93
|
+
@click.argument(
|
94
|
+
"connector",
|
95
|
+
required=False,
|
96
|
+
type=str,
|
97
|
+
metavar="[CONNECTOR]",
|
98
|
+
)
|
99
|
+
@click.option(
|
100
|
+
"--image",
|
101
|
+
help="Image to test, instead of building a new one.",
|
102
|
+
)
|
103
|
+
def image_test( # "image test" command
|
104
|
+
connector: str | None = None,
|
105
|
+
*,
|
106
|
+
image: str | None = None,
|
107
|
+
) -> None:
|
108
|
+
"""Test a connector Docker image.
|
109
|
+
|
110
|
+
[CONNECTOR] can be a connector name (e.g. 'source-pokeapi'), a path to a connector directory, or omitted to use the current working directory.
|
111
|
+
If a string containing '/' is provided, it is treated as a path. Otherwise, it is treated as a connector name.
|
112
|
+
|
113
|
+
If an image is provided, it will be used for testing instead of building a new one.
|
114
|
+
|
115
|
+
Note: You should run `airbyte-cdk secrets fetch` before running this command to ensure
|
116
|
+
that the secrets are available for the connector tests.
|
117
|
+
"""
|
118
|
+
if not verify_docker_installation():
|
119
|
+
click.echo(
|
120
|
+
"Docker is not installed or not running. Please install Docker and try again.", err=True
|
121
|
+
)
|
122
|
+
sys.exit(1)
|
123
|
+
|
124
|
+
connector_name, connector_directory = resolve_connector_name_and_directory(connector)
|
125
|
+
|
126
|
+
# Select only tests with the 'image_tests' mark
|
127
|
+
pytest_args = ["-m", "image_tests"]
|
128
|
+
if not image:
|
129
|
+
metadata_file_path: Path = connector_directory / "metadata.yaml"
|
130
|
+
try:
|
131
|
+
metadata = MetadataFile.from_file(metadata_file_path)
|
132
|
+
except (FileNotFoundError, ValueError) as e:
|
133
|
+
click.echo(
|
134
|
+
f"Error loading metadata file '{metadata_file_path}': {e!s}",
|
135
|
+
err=True,
|
136
|
+
)
|
137
|
+
sys.exit(1)
|
138
|
+
|
139
|
+
tag = "dev-latest"
|
140
|
+
image = f"{metadata.data.dockerRepository}:{tag}"
|
141
|
+
click.echo(f"Building Image for Connector: {image}")
|
142
|
+
try:
|
143
|
+
image = build_connector_image(
|
144
|
+
connector_directory=connector_directory,
|
145
|
+
connector_name=connector_name,
|
146
|
+
metadata=metadata,
|
147
|
+
tag=tag,
|
148
|
+
no_verify=True,
|
149
|
+
)
|
150
|
+
except ConnectorImageBuildError as e:
|
151
|
+
click.echo(
|
152
|
+
f"Error building connector image: {e!s}",
|
153
|
+
err=True,
|
154
|
+
)
|
155
|
+
sys.exit(1)
|
156
|
+
|
157
|
+
pytest_args.extend(["--connector-image", image])
|
158
|
+
|
159
|
+
click.echo(f"Testing Connector Image: {image}")
|
160
|
+
run_connector_tests(
|
161
|
+
connector_name=connector_name,
|
162
|
+
connector_directory=connector_directory,
|
163
|
+
extra_pytest_args=pytest_args,
|
164
|
+
)
|
165
|
+
|
166
|
+
|
91
167
|
__all__ = [
|
92
168
|
"image_cli_group",
|
93
169
|
]
|
@@ -99,12 +99,12 @@ def secrets_cli_group() -> None:
|
|
99
99
|
help="Print GitHub CI mask for secrets.",
|
100
100
|
type=bool,
|
101
101
|
is_flag=True,
|
102
|
-
default=
|
102
|
+
default=None,
|
103
103
|
)
|
104
104
|
def fetch(
|
105
105
|
connector: str | Path | None = None,
|
106
106
|
gcp_project_id: str = GCP_PROJECT_ID,
|
107
|
-
print_ci_secrets_masks: bool =
|
107
|
+
print_ci_secrets_masks: bool | None = None,
|
108
108
|
) -> None:
|
109
109
|
"""Fetch secrets for a connector from Google Secret Manager.
|
110
110
|
|
@@ -181,22 +181,23 @@ def fetch(
|
|
181
181
|
if secret_count == 0:
|
182
182
|
raise exceptions[0]
|
183
183
|
|
184
|
-
if not
|
185
|
-
return
|
186
|
-
|
187
|
-
if not os.environ.get("CI", None):
|
184
|
+
if print_ci_secrets_masks and "CI" not in os.environ:
|
188
185
|
click.echo(
|
189
186
|
"The `--print-ci-secrets-masks` option is only available in CI environments. "
|
190
187
|
"The `CI` env var is either not set or not set to a truthy value. "
|
191
188
|
"Skipping printing secret masks.",
|
192
189
|
err=True,
|
193
190
|
)
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
191
|
+
print_ci_secrets_masks = False
|
192
|
+
elif print_ci_secrets_masks is None:
|
193
|
+
# If not explicitly set, we check if we are in a CI environment
|
194
|
+
# and set to True if so.
|
195
|
+
print_ci_secrets_masks = os.environ.get("CI", "") != ""
|
196
|
+
|
197
|
+
if print_ci_secrets_masks:
|
198
|
+
_print_ci_secrets_masks(
|
199
|
+
secrets_dir=secrets_dir,
|
200
|
+
)
|
200
201
|
|
201
202
|
|
202
203
|
@secrets_cli_group.command("list")
|
@@ -4,9 +4,11 @@ from typing import Any, Dict
|
|
4
4
|
from serpyco_rs import CustomType, Serializer
|
5
5
|
|
6
6
|
from .airbyte_protocol import ( # type: ignore[attr-defined] # all classes are imported to airbyte_protocol via *
|
7
|
+
AirbyteCatalog,
|
7
8
|
AirbyteMessage,
|
8
9
|
AirbyteStateBlob,
|
9
10
|
AirbyteStateMessage,
|
11
|
+
AirbyteStream,
|
10
12
|
AirbyteStreamState,
|
11
13
|
ConfiguredAirbyteCatalog,
|
12
14
|
ConfiguredAirbyteStream,
|
@@ -30,6 +32,8 @@ def custom_type_resolver(t: type) -> CustomType[AirbyteStateBlob, Dict[str, Any]
|
|
30
32
|
return AirbyteStateBlobType() if t is AirbyteStateBlob else None
|
31
33
|
|
32
34
|
|
35
|
+
AirbyteCatalogSerializer = Serializer(AirbyteCatalog, omit_none=True)
|
36
|
+
AirbyteStreamSerializer = Serializer(AirbyteStream, omit_none=True)
|
33
37
|
AirbyteStreamStateSerializer = Serializer(
|
34
38
|
AirbyteStreamState, omit_none=True, custom_type_resolver=custom_type_resolver
|
35
39
|
)
|
@@ -34,6 +34,15 @@ class ConnectorBuildOptions(BaseModel):
|
|
34
34
|
)
|
35
35
|
|
36
36
|
|
37
|
+
class SuggestedStreams(BaseModel):
|
38
|
+
"""Suggested streams from metadata.yaml."""
|
39
|
+
|
40
|
+
streams: list[str] = Field(
|
41
|
+
default=[],
|
42
|
+
description="List of suggested streams for the connector",
|
43
|
+
)
|
44
|
+
|
45
|
+
|
37
46
|
class ConnectorMetadata(BaseModel):
|
38
47
|
"""Connector metadata from metadata.yaml."""
|
39
48
|
|
@@ -47,6 +56,11 @@ class ConnectorMetadata(BaseModel):
|
|
47
56
|
description="List of tags for the connector",
|
48
57
|
)
|
49
58
|
|
59
|
+
suggestedStreams: SuggestedStreams | None = Field(
|
60
|
+
default=None,
|
61
|
+
description="Suggested streams for the connector",
|
62
|
+
)
|
63
|
+
|
50
64
|
@property
|
51
65
|
def language(self) -> ConnectorLanguage:
|
52
66
|
"""Get the connector language."""
|
@@ -202,7 +202,7 @@ class ConcurrentDeclarativeSource(ManifestDeclarativeSource, Generic[TState]):
|
|
202
202
|
|
203
203
|
# Combine streams and dynamic_streams. Note: both cannot be empty at the same time,
|
204
204
|
# and this is validated during the initialization of the source.
|
205
|
-
streams = self._stream_configs(self._source_config) + self._dynamic_stream_configs(
|
205
|
+
streams = self._stream_configs(self._source_config, config) + self._dynamic_stream_configs(
|
206
206
|
self._source_config, config
|
207
207
|
)
|
208
208
|
|
@@ -25,6 +25,7 @@ properties:
|
|
25
25
|
type: array
|
26
26
|
items:
|
27
27
|
anyOf:
|
28
|
+
- "$ref": "#/definitions/ConditionalStreams"
|
28
29
|
- "$ref": "#/definitions/DeclarativeStream"
|
29
30
|
- "$ref": "#/definitions/StateDelegatingStream"
|
30
31
|
dynamic_streams:
|
@@ -424,6 +425,36 @@ definitions:
|
|
424
425
|
$parameters:
|
425
426
|
type: object
|
426
427
|
additionalProperties: true
|
428
|
+
ConditionalStreams:
|
429
|
+
title: Conditional Streams
|
430
|
+
description: Streams that are only available while performing a connector operation when the condition is met.
|
431
|
+
type: object
|
432
|
+
required:
|
433
|
+
- type
|
434
|
+
- streams
|
435
|
+
- condition
|
436
|
+
properties:
|
437
|
+
type:
|
438
|
+
type: string
|
439
|
+
enum: [ConditionalStreams]
|
440
|
+
condition:
|
441
|
+
title: Condition
|
442
|
+
description: Condition that will be evaluated to determine if a set of streams should be available.
|
443
|
+
type: string
|
444
|
+
interpolation_context:
|
445
|
+
- config
|
446
|
+
- parameters
|
447
|
+
examples:
|
448
|
+
- "{{ config['is_sandbox'] }}"
|
449
|
+
streams:
|
450
|
+
title: Streams
|
451
|
+
description: Streams that will be used during an operation based on the condition.
|
452
|
+
type: array
|
453
|
+
items:
|
454
|
+
"$ref": "#/definitions/DeclarativeStream"
|
455
|
+
$parameters:
|
456
|
+
type: object
|
457
|
+
additionalProperties: true
|
427
458
|
ConstantBackoffStrategy:
|
428
459
|
title: Constant Backoff
|
429
460
|
description: Backoff strategy with a constant backoff interval.
|
@@ -8,7 +8,7 @@ import pkgutil
|
|
8
8
|
from copy import deepcopy
|
9
9
|
from importlib import metadata
|
10
10
|
from types import ModuleType
|
11
|
-
from typing import Any, Dict, Iterator, List, Mapping,
|
11
|
+
from typing import Any, Dict, Iterator, List, Mapping, Optional, Set
|
12
12
|
|
13
13
|
import orjson
|
14
14
|
import yaml
|
@@ -35,6 +35,10 @@ from airbyte_cdk.models.airbyte_protocol_serializers import AirbyteMessageSerial
|
|
35
35
|
from airbyte_cdk.sources.declarative.checks import COMPONENTS_CHECKER_TYPE_MAPPING
|
36
36
|
from airbyte_cdk.sources.declarative.checks.connection_checker import ConnectionChecker
|
37
37
|
from airbyte_cdk.sources.declarative.declarative_source import DeclarativeSource
|
38
|
+
from airbyte_cdk.sources.declarative.interpolation import InterpolatedBoolean
|
39
|
+
from airbyte_cdk.sources.declarative.models.declarative_component_schema import (
|
40
|
+
ConditionalStreams as ConditionalStreamsModel,
|
41
|
+
)
|
38
42
|
from airbyte_cdk.sources.declarative.models.declarative_component_schema import (
|
39
43
|
DeclarativeStream as DeclarativeStreamModel,
|
40
44
|
)
|
@@ -300,7 +304,9 @@ class ManifestDeclarativeSource(DeclarativeSource):
|
|
300
304
|
}
|
301
305
|
)
|
302
306
|
|
303
|
-
stream_configs =
|
307
|
+
stream_configs = (
|
308
|
+
self._stream_configs(self._source_config, config=config) + self.dynamic_streams
|
309
|
+
)
|
304
310
|
|
305
311
|
api_budget_model = self._source_config.get("api_budget")
|
306
312
|
if api_budget_model:
|
@@ -319,7 +325,6 @@ class ManifestDeclarativeSource(DeclarativeSource):
|
|
319
325
|
)
|
320
326
|
for stream_config in self._initialize_cache_for_parent_streams(deepcopy(stream_configs))
|
321
327
|
]
|
322
|
-
|
323
328
|
return source_streams
|
324
329
|
|
325
330
|
@staticmethod
|
@@ -373,7 +378,6 @@ class ManifestDeclarativeSource(DeclarativeSource):
|
|
373
378
|
)
|
374
379
|
else:
|
375
380
|
stream_config["retriever"]["requester"]["use_cache"] = True
|
376
|
-
|
377
381
|
return stream_configs
|
378
382
|
|
379
383
|
def spec(self, logger: logging.Logger) -> ConnectorSpecification:
|
@@ -477,12 +481,27 @@ class ManifestDeclarativeSource(DeclarativeSource):
|
|
477
481
|
# No exception
|
478
482
|
return parsed_version
|
479
483
|
|
480
|
-
def _stream_configs(
|
484
|
+
def _stream_configs(
|
485
|
+
self, manifest: Mapping[str, Any], config: Mapping[str, Any]
|
486
|
+
) -> List[Dict[str, Any]]:
|
481
487
|
# This has a warning flag for static, but after we finish part 4 we'll replace manifest with self._source_config
|
482
|
-
stream_configs
|
483
|
-
for
|
484
|
-
if
|
485
|
-
|
488
|
+
stream_configs = []
|
489
|
+
for current_stream_config in manifest.get("streams", []):
|
490
|
+
if (
|
491
|
+
"type" in current_stream_config
|
492
|
+
and current_stream_config["type"] == "ConditionalStreams"
|
493
|
+
):
|
494
|
+
interpolated_boolean = InterpolatedBoolean(
|
495
|
+
condition=current_stream_config.get("condition"),
|
496
|
+
parameters={},
|
497
|
+
)
|
498
|
+
|
499
|
+
if interpolated_boolean.eval(config=config):
|
500
|
+
stream_configs.extend(current_stream_config.get("streams", []))
|
501
|
+
else:
|
502
|
+
if "type" not in current_stream_config:
|
503
|
+
current_stream_config["type"] = "DeclarativeStream"
|
504
|
+
stream_configs.append(current_stream_config)
|
486
505
|
return stream_configs
|
487
506
|
|
488
507
|
def _dynamic_stream_configs(
|
@@ -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
|
|
@@ -2168,7 +2170,7 @@ class DeclarativeSource1(BaseModel):
|
|
2168
2170
|
|
2169
2171
|
type: Literal["DeclarativeSource"]
|
2170
2172
|
check: Union[CheckStream, CheckDynamicStream]
|
2171
|
-
streams: List[Union[DeclarativeStream, StateDelegatingStream]]
|
2173
|
+
streams: List[Union[ConditionalStreams, DeclarativeStream, StateDelegatingStream]]
|
2172
2174
|
dynamic_streams: Optional[List[DynamicDeclarativeStream]] = None
|
2173
2175
|
version: str = Field(
|
2174
2176
|
...,
|
@@ -2201,7 +2203,9 @@ class DeclarativeSource2(BaseModel):
|
|
2201
2203
|
|
2202
2204
|
type: Literal["DeclarativeSource"]
|
2203
2205
|
check: Union[CheckStream, CheckDynamicStream]
|
2204
|
-
streams: Optional[List[Union[DeclarativeStream, StateDelegatingStream]]] =
|
2206
|
+
streams: Optional[List[Union[ConditionalStreams, DeclarativeStream, StateDelegatingStream]]] = (
|
2207
|
+
None
|
2208
|
+
)
|
2205
2209
|
dynamic_streams: List[DynamicDeclarativeStream]
|
2206
2210
|
version: str = Field(
|
2207
2211
|
...,
|
@@ -2280,6 +2284,22 @@ class SelectiveAuthenticator(BaseModel):
|
|
2280
2284
|
parameters: Optional[Dict[str, Any]] = Field(None, alias="$parameters")
|
2281
2285
|
|
2282
2286
|
|
2287
|
+
class ConditionalStreams(BaseModel):
|
2288
|
+
type: Literal["ConditionalStreams"]
|
2289
|
+
condition: str = Field(
|
2290
|
+
...,
|
2291
|
+
description="Condition that will be evaluated to determine if a set of streams should be available.",
|
2292
|
+
examples=["{{ config['is_sandbox'] }}"],
|
2293
|
+
title="Condition",
|
2294
|
+
)
|
2295
|
+
streams: List[DeclarativeStream] = Field(
|
2296
|
+
...,
|
2297
|
+
description="Streams that will be used during an operation based on the condition.",
|
2298
|
+
title="Streams",
|
2299
|
+
)
|
2300
|
+
parameters: Optional[Dict[str, Any]] = Field(None, alias="$parameters")
|
2301
|
+
|
2302
|
+
|
2283
2303
|
class FileUploader(BaseModel):
|
2284
2304
|
type: Literal["FileUploader"]
|
2285
2305
|
requester: Union[HttpRequester, CustomRequester] = Field(
|
@@ -2936,6 +2956,7 @@ CompositeErrorHandler.update_forward_refs()
|
|
2936
2956
|
DeclarativeSource1.update_forward_refs()
|
2937
2957
|
DeclarativeSource2.update_forward_refs()
|
2938
2958
|
SelectiveAuthenticator.update_forward_refs()
|
2959
|
+
ConditionalStreams.update_forward_refs()
|
2939
2960
|
FileUploader.update_forward_refs()
|
2940
2961
|
DeclarativeStream.update_forward_refs()
|
2941
2962
|
SessionTokenAuthenticator.update_forward_refs()
|
@@ -3150,12 +3150,12 @@ class ModelToComponentFactory:
|
|
3150
3150
|
This is needed because the URL is not set until the requester is created.
|
3151
3151
|
"""
|
3152
3152
|
|
3153
|
-
_url = (
|
3153
|
+
_url: str = (
|
3154
3154
|
model.requester.url
|
3155
3155
|
if hasattr(model.requester, "url") and model.requester.url is not None
|
3156
3156
|
else requester.get_url()
|
3157
3157
|
)
|
3158
|
-
_url_base = (
|
3158
|
+
_url_base: str = (
|
3159
3159
|
model.requester.url_base
|
3160
3160
|
if hasattr(model.requester, "url_base") and model.requester.url_base is not None
|
3161
3161
|
else requester.get_url_base()
|