Skip to content

Commit 781606f

Browse files
committed
Merge branch 'main' into brian/remerge_concurrent_cdk_builder_changes
2 parents 1034fd3 + e849b98 commit 781606f

File tree

9 files changed

+57
-54
lines changed

9 files changed

+57
-54
lines changed

.github/pr-welcome-internal.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ Airbyte Maintainers can execute the following slash commands on your PR:
2626
- `/autofix` - Fixes most formatting and linting issues
2727
- `/poetry-lock` - Updates poetry.lock file
2828
- `/test` - Runs connector tests with the updated CDK
29+
- `/poe build` - Regenerate git-committed build artifacts, such as the pydantic models which are generated from the manifest JSON schema in YAML.
2930
- `/poe <command>` - Runs any poe command in the CDK environment
3031

3132
[📝 _Edit this welcome message._](https://github.com/airbytehq/airbyte-python-cdk/blob/main/.github/pr-welcome-internal.md)

airbyte_cdk/cli/airbyte_cdk/_connector.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,11 +123,18 @@ def connector_cli_group() -> None:
123123
multiple=True,
124124
help="Additional argument(s) to pass to pytest. Can be specified multiple times.",
125125
)
126+
@click.option(
127+
"--no-creds",
128+
is_flag=True,
129+
default=False,
130+
help="Skip tests that require credentials (marked with 'requires_creds').",
131+
)
126132
def connector_test(
127133
connector: str | Path | None = None,
128134
*,
129135
collect_only: bool = False,
130136
pytest_args: list[str] | None = None,
137+
no_creds: bool = False,
131138
) -> None:
132139
"""Run connector tests.
133140
@@ -147,6 +154,9 @@ def connector_test(
147154
if collect_only:
148155
pytest_args.append("--collect-only")
149156

157+
if no_creds:
158+
pytest_args.extend(["-m", "not requires_creds"])
159+
150160
run_connector_tests(
151161
connector_name=connector_name,
152162
connector_directory=connector_directory,

airbyte_cdk/cli/airbyte_cdk/_image.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,10 +100,17 @@ def build(
100100
"--image",
101101
help="Image to test, instead of building a new one.",
102102
)
103+
@click.option(
104+
"--no-creds",
105+
is_flag=True,
106+
default=False,
107+
help="Skip tests that require credentials (marked with 'requires_creds').",
108+
)
103109
def image_test( # "image test" command
104110
connector: str | None = None,
105111
*,
106112
image: str | None = None,
113+
no_creds: bool = False,
107114
) -> None:
108115
"""Test a connector Docker image.
109116
@@ -124,7 +131,11 @@ def image_test( # "image test" command
124131
connector_name, connector_directory = resolve_connector_name_and_directory(connector)
125132

126133
# Select only tests with the 'image_tests' mark
127-
pytest_args = ["-m", "image_tests"]
134+
pytest_filter = "image_tests"
135+
if no_creds:
136+
pytest_filter += " and not requires_creds"
137+
138+
pytest_args = ["-m", pytest_filter]
128139
if not image:
129140
metadata_file_path: Path = connector_directory / "metadata.yaml"
130141
try:

airbyte_cdk/sources/declarative/requesters/http_requester.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,13 @@ def _get_url(
168168
next_page_token=next_page_token,
169169
)
170170

171-
full_url = self._join_url(url_base, path) if url_base else url + path if path else url
171+
full_url = (
172+
self._join_url(url_base, path)
173+
if url_base
174+
else self._join_url(url, path)
175+
if path
176+
else url
177+
)
172178

173179
return full_url
174180

airbyte_cdk/sources/declarative/requesters/paginators/default_paginator.py

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -159,14 +159,7 @@ def path(
159159
) -> Optional[str]:
160160
token = next_page_token.get("next_page_token") if next_page_token else None
161161
if token and self.page_token_option and isinstance(self.page_token_option, RequestPath):
162-
# make additional interpolation context
163-
interpolation_context = get_interpolation_context(
164-
stream_state=stream_state,
165-
stream_slice=stream_slice,
166-
next_page_token=next_page_token,
167-
)
168-
# Replace url base to only return the path
169-
return str(token).replace(self.url_base.eval(self.config, **interpolation_context), "") # type: ignore # url_base is casted to a InterpolatedString in __post_init__
162+
return str(token)
170163
else:
171164
return None
172165

airbyte_cdk/test/models/scenario.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,3 +186,8 @@ def with_expecting_success(self) -> ConnectorTestScenario:
186186
**self.model_dump(exclude={"status"}),
187187
status="succeed",
188188
)
189+
190+
@property
191+
def requires_creds(self) -> bool:
192+
"""Return True if the scenario requires credentials to run."""
193+
return bool(self.config_path and "secrets" in self.config_path.parts)

airbyte_cdk/test/standard_tests/pytest_hooks.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -161,14 +161,29 @@ class TestMyConnector(ConnectorTestSuiteBase):
161161
if test_class is None:
162162
return
163163

164-
# Get the 'scenarios' attribute from the class
164+
# Check that the class is compatible with our test suite
165165
scenarios_attr = getattr(test_class, "get_scenarios", None)
166166
if scenarios_attr is None:
167167
raise ValueError(
168168
f"Test class {test_class} does not have a 'scenarios' attribute. "
169169
"Please define the 'scenarios' attribute in the test class."
170170
)
171171

172+
# Get the scenarios defined or discovered in the test class
172173
scenarios = test_class.get_scenarios()
173-
ids = [str(scenario) for scenario in scenarios]
174-
metafunc.parametrize("scenario", scenarios, ids=ids)
174+
175+
# Create pytest.param objects with special marks as needed
176+
parametrized_scenarios = [
177+
pytest.param(
178+
scenario,
179+
marks=[pytest.mark.requires_creds] if scenario.requires_creds else [],
180+
)
181+
for scenario in scenarios
182+
]
183+
184+
# Parametrize the 'scenario' argument with the scenarios
185+
metafunc.parametrize(
186+
"scenario",
187+
parametrized_scenarios,
188+
ids=[str(scenario) for scenario in scenarios],
189+
)

docs/CONTRIBUTING.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ Only Airbyte CDK maintainers can run slash commands. The most common slash comma
113113

114114
- `/autofix`- Corrects any linting or formatting issues and commits the change back to the repo.
115115
- `/poetry-lock` - Re-locks dependencies and updates the `poetry.lock` file, then commits the changes back to the repo. This is helpful after merging in updates from main, or when creating a PR in the browser - such as for version bumps or dependency updates directly in the PR.
116+
- `/poe ...` - Runs a Poe task in CI. If files are modified (e.g. as with `/poe build`), they will be committed back to your branch.
116117

117118
The full list of available slash commands can be found in the [slash command dispatch file](https://github.com/airbytehq/airbyte-python-cdk/blob/main/.github/workflows/slash_command_dispatch.yml#L21-L25).
118119

unit_tests/sources/declarative/requesters/paginators/test_default_paginator.py

Lines changed: 2 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
(
3737
RequestPath(parameters={}),
3838
None,
39-
"/next_url",
39+
"https://airbyte.io/next_url",
4040
{"limit": 2},
4141
{},
4242
{},
@@ -128,7 +128,7 @@
128128
(
129129
RequestPath(parameters={}),
130130
None,
131-
"/next_url",
131+
"https://airbyte.io/next_url",
132132
{"limit": 2},
133133
{},
134134
{},
@@ -539,42 +539,3 @@ def test_path_returns_none_when_option_not_request_path() -> None:
539539
)
540540
result = paginator.path(next_page_token)
541541
assert result is None
542-
543-
544-
def test_path_with_additional_interpolation_context() -> None:
545-
page_token_option = RequestPath(parameters={})
546-
paginator = DefaultPaginator(
547-
pagination_strategy=Mock(),
548-
config={},
549-
url_base="https://api.domain.com/{{ stream_slice['campaign_id'] }}",
550-
parameters={},
551-
page_token_option=page_token_option,
552-
)
553-
# define stream_state here
554-
stream_state = {"state": "state_value"}
555-
# define stream_slice here
556-
stream_slice = StreamSlice(
557-
partition={
558-
"campaign_id": "123_abcd",
559-
},
560-
cursor_slice={
561-
"start": "A",
562-
"end": "B",
563-
},
564-
extra_fields={
565-
"extra_field_A": "value_A",
566-
"extra_field_B": "value_B",
567-
},
568-
)
569-
# define next_page_token here
570-
next_page_token = {
571-
"next_page_token": "https://api.domain.com/123_abcd/some_next_page_token_here"
572-
}
573-
574-
expected_after_interpolation = "/some_next_page_token_here"
575-
576-
assert expected_after_interpolation == paginator.path(
577-
next_page_token=next_page_token,
578-
stream_state=stream_state,
579-
stream_slice=stream_slice,
580-
)

0 commit comments

Comments
 (0)