From f0c8fb7f11b2f2c78957c8d45dfa57430ac61160 Mon Sep 17 00:00:00 2001 From: Romulo Quidute Filho Date: Mon, 22 Jun 2026 17:59:06 -0300 Subject: [PATCH 01/10] feat: add 'project logs' CLI command (#1024) Add 'th-cli project logs' subcommand that downloads all logs for a given project as a single zip archive. Usage: th-cli project logs --id th-cli project logs --id --grouped th-cli project logs --id --output-file ./my-logs.zip - Without --grouped: outer zip contains one flat .log file per execution - With --grouped: outer zip contains one grouped zip per execution (logs organized by test case state) - Output filename defaults to -logs.zip Also updates the auto-generated API client (projects_api.py) with: - _build_for_download_project_logs_api_v1_projects__id__logs_get - async download_project_logs_api_v1_projects__id__logs_get - sync download_project_logs_api_v1_projects__id__logs_get Calls the new backend endpoint: GET /api/v1/projects/{id}/logs Closes #1024 --- tests/test_project_commands.py | 109 +++++++++++++++++++++ th_cli/api_lib_autogen/api/projects_api.py | 42 +++++++- th_cli/commands/project.py | 64 ++++++++++++ 3 files changed, 214 insertions(+), 1 deletion(-) diff --git a/tests/test_project_commands.py b/tests/test_project_commands.py index 662589e..41ecfd3 100644 --- a/tests/test_project_commands.py +++ b/tests/test_project_commands.py @@ -728,3 +728,112 @@ def test_import_project_help_message(self, cli_runner: CliRunner) -> None: assert result.exit_code == 0 assert "--file" in result.output + + +@pytest.mark.unit +@pytest.mark.cli +class TestProjectLogsCommand: + """Test cases for the project logs command.""" + + def test_logs_flat_success_default_filename( + self, + cli_runner: CliRunner, + mock_sync_apis: Mock, + sample_project: api_models.Project, + temp_dir: Path, + ) -> None: + """Test successful flat log download with auto-generated filename.""" + mock_sync_apis.projects_api.download_project_logs_api_v1_projects__id__logs_get.return_value = b"log content" + mock_sync_apis.projects_api.read_project_api_v1_projects__id__get.return_value = sample_project + + with patch("th_cli.commands.project.SyncApis", return_value=mock_sync_apis): + with cli_runner.isolated_filesystem(temp_dir=temp_dir): + result = cli_runner.invoke(project, ["logs", "--id", "1"]) + + assert result.exit_code == 0 + assert "saved to" in result.output + assert "flat" in result.output + mock_sync_apis.projects_api.download_project_logs_api_v1_projects__id__logs_get.assert_called_once_with( + id=1, grouped=False + ) + + def test_logs_grouped_success_default_filename( + self, + cli_runner: CliRunner, + mock_sync_apis: Mock, + sample_project: api_models.Project, + temp_dir: Path, + ) -> None: + """Test successful grouped log download with auto-generated filename.""" + mock_sync_apis.projects_api.download_project_logs_api_v1_projects__id__logs_get.return_value = b"zip content" + mock_sync_apis.projects_api.read_project_api_v1_projects__id__get.return_value = sample_project + + with patch("th_cli.commands.project.SyncApis", return_value=mock_sync_apis): + with cli_runner.isolated_filesystem(temp_dir=temp_dir): + result = cli_runner.invoke(project, ["logs", "--id", "1", "--grouped"]) + + assert result.exit_code == 0 + assert "grouped" in result.output + mock_sync_apis.projects_api.download_project_logs_api_v1_projects__id__logs_get.assert_called_once_with( + id=1, grouped=True + ) + + def test_logs_custom_output_file( + self, + cli_runner: CliRunner, + mock_sync_apis: Mock, + sample_project: api_models.Project, + temp_dir: Path, + ) -> None: + """Test log download to a specified output file.""" + mock_sync_apis.projects_api.download_project_logs_api_v1_projects__id__logs_get.return_value = b"zip content" + output_path = str(temp_dir / "my_logs.zip") + + with patch("th_cli.commands.project.SyncApis", return_value=mock_sync_apis): + result = cli_runner.invoke(project, ["logs", "--id", "1", "--output-file", output_path]) + + assert result.exit_code == 0 + assert f"saved to '{output_path}'" in result.output + assert Path(output_path).read_bytes() == b"zip content" + + def test_logs_file_content_is_written( + self, + cli_runner: CliRunner, + mock_sync_apis: Mock, + sample_project: api_models.Project, + temp_dir: Path, + ) -> None: + """Test that the downloaded bytes are written correctly to disk.""" + expected_bytes = b"PK\x03\x04fakezipdata" + mock_sync_apis.projects_api.download_project_logs_api_v1_projects__id__logs_get.return_value = expected_bytes + output_path = str(temp_dir / "output.zip") + + with patch("th_cli.commands.project.SyncApis", return_value=mock_sync_apis): + cli_runner.invoke(project, ["logs", "--id", "42", "--output-file", output_path]) + + assert Path(output_path).read_bytes() == expected_bytes + + def test_logs_api_error( + self, + cli_runner: CliRunner, + mock_sync_apis: Mock, + ) -> None: + """Test log download with API error.""" + mock_sync_apis.projects_api.download_project_logs_api_v1_projects__id__logs_get.side_effect = ( + UnexpectedResponse(status_code=404, content=b"Not Found") + ) + + with patch("th_cli.commands.project.SyncApis", return_value=mock_sync_apis): + result = cli_runner.invoke(project, ["logs", "--id", "99"]) + + assert result.exit_code == 1 + assert "404" in result.output + + def test_logs_help_message(self, cli_runner: CliRunner) -> None: + """Test the help message for the logs command.""" + result = cli_runner.invoke(project, ["logs", "--help"]) + + assert result.exit_code == 0 + assert "--id" in result.output + assert "--grouped" in result.output + assert "--output-file" in result.output diff --git a/th_cli/api_lib_autogen/api/projects_api.py b/th_cli/api_lib_autogen/api/projects_api.py index 3633a95..2c0029e 100644 --- a/th_cli/api_lib_autogen/api/projects_api.py +++ b/th_cli/api_lib_autogen/api/projects_api.py @@ -177,7 +177,7 @@ def _build_for_applicable_test_cases_api_v1_projects__id__applicable_test_cases_ path_params=path_params, ) - def _build_for_export_project_config_api_v1_projects__id__export_get( + def _build_for_export_project_config_api_v1_projects__id__export_get( self, id: int ) -> Coroutine[Any, Any, m.ProjectCreate]: """ @@ -189,6 +189,25 @@ def _build_for_export_project_config_api_v1_projects__id__export_get( type_=m.ProjectCreate, method="GET", url="/api/v1/projects/{id}/export", path_params=path_params ) + def _build_for_download_project_logs_api_v1_projects__id__logs_get( + self, id: int, grouped: bool = False + ) -> Coroutine[Any, Any, bytes]: + """ + Download Project Logs + """ + path_params = {"id": str(id)} + query_params: dict[str, Any] = {} + if grouped: + query_params["grouped"] = "true" + + return self.api_client.request( + type_=bytes, + method="GET", + url="/api/v1/projects/{id}/logs", + path_params=path_params, + params=query_params, + ) + def _build_for_importproject_config_api_v1_projects_import_post( self, body: m.BodyImportprojectConfigApiV1ProjectsImportPost ) -> Coroutine[Any, Any, m.Project]: @@ -303,6 +322,16 @@ async def importproject_config_api_v1_projects_import_post( """ return await self._build_for_importproject_config_api_v1_projects_import_post(body=body) + async def download_project_logs_api_v1_projects__id__logs_get( + self, id: int, grouped: bool = False + ) -> bytes: + """ + Download Project Logs + """ + return await self._build_for_download_project_logs_api_v1_projects__id__logs_get( + id=id, grouped=grouped + ) + class SyncProjectsApi(_ProjectsApi): def read_projects_api_v1_projects__get( @@ -407,3 +436,14 @@ def importproject_config_api_v1_projects_import_post( """ coroutine = self._build_for_importproject_config_api_v1_projects_import_post(body=body) return get_event_loop().run_until_complete(coroutine) + + def download_project_logs_api_v1_projects__id__logs_get( + self, id: int, grouped: bool = False + ) -> bytes: + """ + Download Project Logs + """ + coroutine = self._build_for_download_project_logs_api_v1_projects__id__logs_get( + id=id, grouped=grouped + ) + return get_event_loop().run_until_complete(coroutine) diff --git a/th_cli/commands/project.py b/th_cli/commands/project.py index d8db990..4226b91 100644 --- a/th_cli/commands/project.py +++ b/th_cli/commands/project.py @@ -259,6 +259,41 @@ def import_project(file: str) -> None: _import_project(sync_apis, file) +# Click command to download all logs for a project +@project.command( + "logs", + short_help=colorize_help("Download all logs for a project"), +) +@click.option( + "--id", + "-i", + type=int, + required=True, + help=colorize_help("Project ID"), +) +@click.option( + "--grouped", + is_flag=True, + default=False, + help=colorize_help( + "Download grouped logs (organized by test case state) instead of flat log files" + ), +) +@click.option( + "--output-file", + "-o", + type=click.Path(file_okay=True, dir_okay=False), + required=False, + help=colorize_help( + "Output zip file path (defaults to -logs.zip)" + ), +) +def logs(id: int, grouped: bool, output_file: str | None) -> None: + """Download all logs for a project as a single zip archive""" + with get_sync_apis("logs") as sync_apis: + _download_project_logs(sync_apis, id, grouped, output_file) + + def _create_project(sync_apis: SyncApis, name: str, config: str | None, pics_config_folder: str | None) -> None: """Create a new project""" # Get default config @@ -465,3 +500,32 @@ def _import_project(sync_apis: SyncApis, file: str) -> None: click.echo(colorize_success(f"Project '{response.name}' imported with ID {response.id}")) except UnexpectedResponse as e: handle_api_error(e, f"import project from '{file}'") + + +def _download_project_logs( + sync_apis: SyncApis, id: int, grouped: bool, output_file: str | None +) -> None: + """Download all logs for a project as a single zip archive""" + try: + log_bytes: bytes = sync_apis.projects_api.download_project_logs_api_v1_projects__id__logs_get( + id=id, grouped=grouped + ) + except UnexpectedResponse as e: + handle_api_error(e, f"download logs for project ID '{id}'") + + if not output_file: + try: + project = sync_apis.projects_api.read_project_api_v1_projects__id__get(id=id) + safe_name = "".join( + c if c.isalnum() or c in "-_" else "_" for c in (project.name or f"project-{id}") + ) + except UnexpectedResponse: + safe_name = f"project-{id}" + output_file = f"{safe_name}-logs.zip" + + try: + Path(output_file).write_bytes(log_bytes) + mode = "grouped" if grouped else "flat" + click.echo(colorize_success(f"Project {id} logs ({mode}) saved to '{output_file}'")) + except OSError as e: + raise CLIError(f"Failed to write logs file '{output_file}': {e}") From 5b4446f6b0ad2810f6a672ec6f1157d482504fb6 Mon Sep 17 00:00:00 2001 From: Romulo Quidute Filho Date: Mon, 22 Jun 2026 18:02:02 -0300 Subject: [PATCH 02/10] fix: correct indentation error in projects_api.py --- th_cli/api_lib_autogen/api/projects_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/th_cli/api_lib_autogen/api/projects_api.py b/th_cli/api_lib_autogen/api/projects_api.py index 2c0029e..b80ccea 100644 --- a/th_cli/api_lib_autogen/api/projects_api.py +++ b/th_cli/api_lib_autogen/api/projects_api.py @@ -177,7 +177,7 @@ def _build_for_applicable_test_cases_api_v1_projects__id__applicable_test_cases_ path_params=path_params, ) - def _build_for_export_project_config_api_v1_projects__id__export_get( + def _build_for_export_project_config_api_v1_projects__id__export_get( self, id: int ) -> Coroutine[Any, Any, m.ProjectCreate]: """ From 2ec7caf907224f659990ed138e4a57024fc14376 Mon Sep 17 00:00:00 2001 From: Romulo Quidute Filho Date: Wed, 1 Jul 2026 16:28:29 -0300 Subject: [PATCH 03/10] fix: catch broader exceptions when falling back to default log filename Addresses code review feedback on PR #99: at the point we're fetching the project name to build the default output filename, the log bytes have already been downloaded successfully. Only catching UnexpectedResponse left the command vulnerable to crashing (and discarding those bytes) on other failures such as ResponseHandlingException or schema validation errors from the secondary read_project call. Widen the except clause to Exception so any failure in that best-effort lookup falls back to the safe 'project-' filename, consistent with how similar best-effort/fallback exception handling is done elsewhere in this codebase (e.g. get_sync_apis, available_tests.py). Add a regression test covering the fallback. --- tests/test_project_commands.py | 20 ++++++++++++++++++++ th_cli/commands/project.py | 6 +++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/tests/test_project_commands.py b/tests/test_project_commands.py index 41ecfd3..4537358 100644 --- a/tests/test_project_commands.py +++ b/tests/test_project_commands.py @@ -829,6 +829,26 @@ def test_logs_api_error( assert result.exit_code == 1 assert "404" in result.output + def test_logs_falls_back_to_default_filename_when_project_name_lookup_fails( + self, + cli_runner: CliRunner, + mock_sync_apis: Mock, + temp_dir: Path, + ) -> None: + """Logs are still written even if fetching the project name fails.""" + mock_sync_apis.projects_api.download_project_logs_api_v1_projects__id__logs_get.return_value = b"zip content" + mock_sync_apis.projects_api.read_project_api_v1_projects__id__get.side_effect = RuntimeError( + "network glitch" + ) + + with patch("th_cli.commands.project.SyncApis", return_value=mock_sync_apis): + with cli_runner.isolated_filesystem(temp_dir=temp_dir): + result = cli_runner.invoke(project, ["logs", "--id", "7"]) + + assert result.exit_code == 0 + assert "saved to 'project-7-logs.zip'" in result.output + assert Path("project-7-logs.zip").read_bytes() == b"zip content" + def test_logs_help_message(self, cli_runner: CliRunner) -> None: """Test the help message for the logs command.""" result = cli_runner.invoke(project, ["logs", "--help"]) diff --git a/th_cli/commands/project.py b/th_cli/commands/project.py index 4226b91..79ca666 100644 --- a/th_cli/commands/project.py +++ b/th_cli/commands/project.py @@ -519,7 +519,11 @@ def _download_project_logs( safe_name = "".join( c if c.isalnum() or c in "-_" else "_" for c in (project.name or f"project-{id}") ) - except UnexpectedResponse: + except Exception: + # The logs have already been downloaded successfully at this point; + # any failure fetching the project name (network glitch, unexpected + # response shape, etc.) should just fall back to a safe default + # filename instead of discarding the downloaded bytes. safe_name = f"project-{id}" output_file = f"{safe_name}-logs.zip" From 42092e58da1d9a01dd0495c132e5ee0ff5d439f4 Mon Sep 17 00:00:00 2001 From: Romulo Quidute Filho Date: Tue, 7 Jul 2026 18:25:42 +0000 Subject: [PATCH 04/10] Updated api_lib_autogen --- th_cli/api_lib_autogen/api/__init__.py | 37 +- th_cli/api_lib_autogen/api/devices_api.py | 22 +- th_cli/api_lib_autogen/api/operators_api.py | 55 ++- th_cli/api_lib_autogen/api/projects_api.py | 211 ++++---- .../api/test_collections_api.py | 9 +- .../api/test_run_configs_api.py | 60 +-- .../api/test_run_executions_api.py | 393 ++++----------- th_cli/api_lib_autogen/api/utils_api.py | 10 +- th_cli/api_lib_autogen/api/version_api.py | 13 +- th_cli/api_lib_autogen/api_client.py | 28 +- th_cli/api_lib_autogen/exceptions.py | 1 - th_cli/api_lib_autogen/models.py | 458 +++++++++--------- 12 files changed, 537 insertions(+), 760 deletions(-) diff --git a/th_cli/api_lib_autogen/api/__init__.py b/th_cli/api_lib_autogen/api/__init__.py index dcfc013..5473f74 100644 --- a/th_cli/api_lib_autogen/api/__init__.py +++ b/th_cli/api_lib_autogen/api/__init__.py @@ -15,38 +15,13 @@ # """API endpoint classes.""" -from th_cli.api_lib_autogen.api.devices_api import AsyncDevicesApi, SyncDevicesApi -from th_cli.api_lib_autogen.api.operators_api import AsyncOperatorsApi, SyncOperatorsApi -from th_cli.api_lib_autogen.api.projects_api import AsyncProjectsApi, SyncProjectsApi from th_cli.api_lib_autogen.api.test_collections_api import AsyncTestCollectionsApi, SyncTestCollectionsApi -from th_cli.api_lib_autogen.api.test_run_configs_api import AsyncTestRunConfigsApi, SyncTestRunConfigsApi +from th_cli.api_lib_autogen.api.projects_api import AsyncProjectsApi, SyncProjectsApi +from th_cli.api_lib_autogen.api.operators_api import AsyncOperatorsApi, SyncOperatorsApi from th_cli.api_lib_autogen.api.test_run_executions_api import AsyncTestRunExecutionsApi, SyncTestRunExecutionsApi -from th_cli.api_lib_autogen.api.utils_api import AsyncUtilsApi, SyncUtilsApi +from th_cli.api_lib_autogen.api.test_run_configs_api import AsyncTestRunConfigsApi, SyncTestRunConfigsApi from th_cli.api_lib_autogen.api.version_api import AsyncVersionApi, SyncVersionApi +from th_cli.api_lib_autogen.api.utils_api import AsyncUtilsApi, SyncUtilsApi +from th_cli.api_lib_autogen.api.devices_api import AsyncDevicesApi, SyncDevicesApi -__all__ = [ - "TestCollectionsApi", - "AsyncTestCollectionsApi", - "SyncTestCollectionsApi", - "ProjectsApi", - "AsyncProjectsApi", - "SyncProjectsApi", - "OperatorsApi", - "AsyncOperatorsApi", - "SyncOperatorsApi", - "TestRunExecutionsApi", - "AsyncTestRunExecutionsApi", - "SyncTestRunExecutionsApi", - "TestRunConfigsApi", - "AsyncTestRunConfigsApi", - "SyncTestRunConfigsApi", - "VersionApi", - "AsyncVersionApi", - "SyncVersionApi", - "UtilsApi", - "AsyncUtilsApi", - "SyncUtilsApi", - "DevicesApi", - "AsyncDevicesApi", - "SyncDevicesApi", -] +__all__ = ["TestCollectionsApi", "AsyncTestCollectionsApi", "SyncTestCollectionsApi", "ProjectsApi", "AsyncProjectsApi", "SyncProjectsApi", "OperatorsApi", "AsyncOperatorsApi", "SyncOperatorsApi", "TestRunExecutionsApi", "AsyncTestRunExecutionsApi", "SyncTestRunExecutionsApi", "TestRunConfigsApi", "AsyncTestRunConfigsApi", "SyncTestRunConfigsApi", "VersionApi", "AsyncVersionApi", "SyncVersionApi", "UtilsApi", "AsyncUtilsApi", "SyncUtilsApi", "DevicesApi", "AsyncDevicesApi", "SyncDevicesApi"] diff --git a/th_cli/api_lib_autogen/api/devices_api.py b/th_cli/api_lib_autogen/api/devices_api.py index f3e67d4..74ab1cd 100644 --- a/th_cli/api_lib_autogen/api/devices_api.py +++ b/th_cli/api_lib_autogen/api/devices_api.py @@ -15,14 +15,13 @@ # # flake8: noqa E501 from asyncio import get_event_loop -from typing import IO, TYPE_CHECKING, Any, Coroutine +from typing import Coroutine, IO, TYPE_CHECKING, Any from th_cli.api_lib_autogen import models as m if TYPE_CHECKING: from th_cli.api_lib_autogen.api_client import ApiClient - class _DevicesApi: def __init__(self, api_client: "ApiClient"): self.api_client = api_client @@ -31,17 +30,24 @@ def _build_for_get_device_configs_api_v1_devices__get(self) -> Coroutine[Any, An """ Get Device Configs """ - return self.api_client.request(type_=dict[str, Any], method="GET", url="/api/v1/devices/") + return self.api_client.request( + type_=dict[str, Any], + method="GET", + url="/api/v1/devices/" + ) - def _build_for_add_device_config_api_v1_devices__put( - self, body: dict[str, Any] - ) -> Coroutine[Any, Any, dict[str, Any]]: + def _build_for_add_device_config_api_v1_devices__put(self, body: dict[str, Any]) -> Coroutine[Any, Any, dict[str, Any]]: """ Add Device Config """ - json_body = body.model_dump(mode="json") if hasattr(body, "model_dump") else body + json_body = body.model_dump(mode='json') if hasattr(body, 'model_dump') else body - return self.api_client.request(type_=dict[str, Any], method="PUT", url="/api/v1/devices/", json=json_body) + return self.api_client.request( + type_=dict[str, Any], + method="PUT", + url="/api/v1/devices/", + json=json_body + ) class AsyncDevicesApi(_DevicesApi): diff --git a/th_cli/api_lib_autogen/api/operators_api.py b/th_cli/api_lib_autogen/api/operators_api.py index 9626d1a..eb77ae0 100644 --- a/th_cli/api_lib_autogen/api/operators_api.py +++ b/th_cli/api_lib_autogen/api/operators_api.py @@ -15,21 +15,18 @@ # # flake8: noqa E501 from asyncio import get_event_loop -from typing import IO, TYPE_CHECKING, Any, Coroutine +from typing import Coroutine, IO, TYPE_CHECKING, Any from th_cli.api_lib_autogen import models as m if TYPE_CHECKING: from th_cli.api_lib_autogen.api_client import ApiClient - class _OperatorsApi: def __init__(self, api_client: "ApiClient"): self.api_client = api_client - def _build_for_read_operators_api_v1_operators__get( - self, skip: int | None = None, limit: int | None = None - ) -> Coroutine[Any, Any, list[m.Operator]]: + def _build_for_read_operators_api_v1_operators__get(self, skip: int | None = None, limit: int | None = None) -> Coroutine[Any, Any, list[m.Operator]]: """ Read Operators """ @@ -40,18 +37,24 @@ def _build_for_read_operators_api_v1_operators__get( query_params["limit"] = str(limit) return self.api_client.request( - type_=list[m.Operator], method="GET", url="/api/v1/operators/", params=query_params + type_=list[m.Operator], + method="GET", + url="/api/v1/operators/", + params=query_params ) - def _build_for_create_operator_api_v1_operators__post( - self, body: m.OperatorCreate - ) -> Coroutine[Any, Any, m.Operator]: + def _build_for_create_operator_api_v1_operators__post(self, body: m.OperatorCreate) -> Coroutine[Any, Any, m.Operator]: """ Create Operator """ - json_body = body.model_dump(mode="json") if hasattr(body, "model_dump") else body + json_body = body.model_dump(mode='json') if hasattr(body, 'model_dump') else body - return self.api_client.request(type_=m.Operator, method="POST", url="/api/v1/operators/", json=json_body) + return self.api_client.request( + type_=m.Operator, + method="POST", + url="/api/v1/operators/", + json=json_body + ) def _build_for_read_operator_api_v1_operators__id__get(self, id: int) -> Coroutine[Any, Any, m.Operator]: """ @@ -60,21 +63,26 @@ def _build_for_read_operator_api_v1_operators__id__get(self, id: int) -> Corouti path_params = {"id": str(id)} return self.api_client.request( - type_=m.Operator, method="GET", url="/api/v1/operators/{id}", path_params=path_params + type_=m.Operator, + method="GET", + url="/api/v1/operators/{id}", + path_params=path_params ) - def _build_for_update_operator_api_v1_operators__id__put( - self, body: m.OperatorUpdate, id: int - ) -> Coroutine[Any, Any, m.Operator]: + def _build_for_update_operator_api_v1_operators__id__put(self, body: m.OperatorUpdate, id: int) -> Coroutine[Any, Any, m.Operator]: """ Update Operator """ path_params = {"id": str(id)} - json_body = body.model_dump(mode="json") if hasattr(body, "model_dump") else body + json_body = body.model_dump(mode='json') if hasattr(body, 'model_dump') else body return self.api_client.request( - type_=m.Operator, method="PUT", url="/api/v1/operators/{id}", path_params=path_params, json=json_body + type_=m.Operator, + method="PUT", + url="/api/v1/operators/{id}", + path_params=path_params, + json=json_body ) def _build_for_delete_operator_api_v1_operators__id__delete(self, id: int) -> Coroutine[Any, Any, m.Operator]: @@ -84,14 +92,15 @@ def _build_for_delete_operator_api_v1_operators__id__delete(self, id: int) -> Co path_params = {"id": str(id)} return self.api_client.request( - type_=m.Operator, method="DELETE", url="/api/v1/operators/{id}", path_params=path_params + type_=m.Operator, + method="DELETE", + url="/api/v1/operators/{id}", + path_params=path_params ) class AsyncOperatorsApi(_OperatorsApi): - async def read_operators_api_v1_operators__get( - self, skip: int | None = None, limit: int | None = None - ) -> list[m.Operator]: + async def read_operators_api_v1_operators__get(self, skip: int | None = None, limit: int | None = None) -> list[m.Operator]: """ Read Operators """ @@ -123,9 +132,7 @@ async def delete_operator_api_v1_operators__id__delete(self, id: int) -> m.Opera class SyncOperatorsApi(_OperatorsApi): - def read_operators_api_v1_operators__get( - self, skip: int | None = None, limit: int | None = None - ) -> list[m.Operator]: + def read_operators_api_v1_operators__get(self, skip: int | None = None, limit: int | None = None) -> list[m.Operator]: """ Read Operators """ diff --git a/th_cli/api_lib_autogen/api/projects_api.py b/th_cli/api_lib_autogen/api/projects_api.py index b80ccea..11115d1 100644 --- a/th_cli/api_lib_autogen/api/projects_api.py +++ b/th_cli/api_lib_autogen/api/projects_api.py @@ -15,21 +15,18 @@ # # flake8: noqa E501 from asyncio import get_event_loop -from typing import IO, TYPE_CHECKING, Any, Coroutine +from typing import Coroutine, IO, TYPE_CHECKING, Any from th_cli.api_lib_autogen import models as m if TYPE_CHECKING: from th_cli.api_lib_autogen.api_client import ApiClient - class _ProjectsApi: def __init__(self, api_client: "ApiClient"): self.api_client = api_client - def _build_for_read_projects_api_v1_projects__get( - self, archived: bool | None = None, skip: int | None = None, limit: int | None = None - ) -> Coroutine[Any, Any, list[m.Project]]: + def _build_for_read_projects_api_v1_projects__get(self, archived: bool | None = None, skip: int | None = None, limit: int | None = None) -> Coroutine[Any, Any, list[m.Project]]: """ Read Projects """ @@ -42,22 +39,34 @@ def _build_for_read_projects_api_v1_projects__get( query_params["limit"] = str(limit) return self.api_client.request( - type_=list[m.Project], method="GET", url="/api/v1/projects/", params=query_params + type_=list[m.Project], + method="GET", + url="/api/v1/projects/", + params=query_params ) def _build_for_create_project_api_v1_projects__post(self, body: m.ProjectCreate) -> Coroutine[Any, Any, m.Project]: """ Create Project """ - json_body = body.model_dump(mode="json") if hasattr(body, "model_dump") else body + json_body = body.model_dump(mode='json') if hasattr(body, 'model_dump') else body - return self.api_client.request(type_=m.Project, method="POST", url="/api/v1/projects/", json=json_body) + return self.api_client.request( + type_=m.Project, + method="POST", + url="/api/v1/projects/", + json=json_body + ) def _build_for_default_config_api_v1_projects_default_config_get(self) -> Coroutine[Any, Any, dict[str, Any]]: """ Default Config """ - return self.api_client.request(type_=dict[str, Any], method="GET", url="/api/v1/projects/default_config") + return self.api_client.request( + type_=dict[str, Any], + method="GET", + url="/api/v1/projects/default_config" + ) def _build_for_read_project_api_v1_projects__id__get(self, id: int) -> Coroutine[Any, Any, m.Project]: """ @@ -66,21 +75,26 @@ def _build_for_read_project_api_v1_projects__id__get(self, id: int) -> Coroutine path_params = {"id": str(id)} return self.api_client.request( - type_=m.Project, method="GET", url="/api/v1/projects/{id}", path_params=path_params + type_=m.Project, + method="GET", + url="/api/v1/projects/{id}", + path_params=path_params ) - def _build_for_update_project_api_v1_projects__id__put( - self, body: m.ProjectUpdate, id: int - ) -> Coroutine[Any, Any, m.Project]: + def _build_for_update_project_api_v1_projects__id__put(self, body: m.ProjectUpdate, id: int) -> Coroutine[Any, Any, m.Project]: """ Update Project """ path_params = {"id": str(id)} - json_body = body.model_dump(mode="json") if hasattr(body, "model_dump") else body + json_body = body.model_dump(mode='json') if hasattr(body, 'model_dump') else body return self.api_client.request( - type_=m.Project, method="PUT", url="/api/v1/projects/{id}", path_params=path_params, json=json_body + type_=m.Project, + method="PUT", + url="/api/v1/projects/{id}", + path_params=path_params, + json=json_body ) def _build_for_delete_project_api_v1_projects__id__delete(self, id: int) -> Coroutine[Any, Any, m.Project]: @@ -90,7 +104,10 @@ def _build_for_delete_project_api_v1_projects__id__delete(self, id: int) -> Coro path_params = {"id": str(id)} return self.api_client.request( - type_=m.Project, method="DELETE", url="/api/v1/projects/{id}", path_params=path_params + type_=m.Project, + method="DELETE", + url="/api/v1/projects/{id}", + path_params=path_params ) def _build_for_archive_project_api_v1_projects__id__archive_post(self, id: int) -> Coroutine[Any, Any, m.Project]: @@ -100,24 +117,26 @@ def _build_for_archive_project_api_v1_projects__id__archive_post(self, id: int) path_params = {"id": str(id)} return self.api_client.request( - type_=m.Project, method="POST", url="/api/v1/projects/{id}/archive", path_params=path_params + type_=m.Project, + method="POST", + url="/api/v1/projects/{id}/archive", + path_params=path_params ) - def _build_for_unarchive_project_api_v1_projects__id__unarchive_post( - self, id: int - ) -> Coroutine[Any, Any, m.Project]: + def _build_for_unarchive_project_api_v1_projects__id__unarchive_post(self, id: int) -> Coroutine[Any, Any, m.Project]: """ Unarchive Project """ path_params = {"id": str(id)} return self.api_client.request( - type_=m.Project, method="POST", url="/api/v1/projects/{id}/unarchive", path_params=path_params + type_=m.Project, + method="POST", + url="/api/v1/projects/{id}/unarchive", + path_params=path_params ) - def _build_for_upload_pics_api_v1_projects__id__upload_pics_put( - self, body: m.BodyUploadPicsApiV1ProjectsIdUploadPicsPut, id: int - ) -> Coroutine[Any, Any, m.Project]: + def _build_for_upload_pics_api_v1_projects__id__upload_pics_put(self, body: m.BodyUploadPicsApiV1ProjectsIdUploadPicsPut, id: int) -> Coroutine[Any, Any, m.Project]: """ Upload Pics """ @@ -129,11 +148,11 @@ def _build_for_upload_pics_api_v1_projects__id__upload_pics_put( # Process body fields to populate files and data dictionaries if body is not None: # Process field: file - if hasattr(body, "file"): - field_value = getattr(body, "file") + if hasattr(body, 'file'): + field_value = getattr(body, 'file') if field_value is not None: # File field - files["file"] = field_value + files['file'] = field_value return self.api_client.request( type_=m.Project, @@ -141,12 +160,10 @@ def _build_for_upload_pics_api_v1_projects__id__upload_pics_put( url="/api/v1/projects/{id}/upload_pics", path_params=path_params, data=data, - files=files, + files=files ) - def _build_for_remove_pics_cluster_type_api_v1_projects__id__pics_cluster_type_delete( - self, id: int, cluster_name: str - ) -> Coroutine[Any, Any, m.Project]: + def _build_for_remove_pics_cluster_type_api_v1_projects__id__pics_cluster_type_delete(self, id: int, cluster_name: str) -> Coroutine[Any, Any, m.Project]: """ Remove Pics Cluster Type """ @@ -159,12 +176,10 @@ def _build_for_remove_pics_cluster_type_api_v1_projects__id__pics_cluster_type_d method="DELETE", url="/api/v1/projects/{id}/pics_cluster_type", path_params=path_params, - params=query_params, + params=query_params ) - def _build_for_applicable_test_cases_api_v1_projects__id__applicable_test_cases_get( - self, id: int - ) -> Coroutine[Any, Any, m.PICSApplicableTestCases]: + def _build_for_applicable_test_cases_api_v1_projects__id__applicable_test_cases_get(self, id: int) -> Coroutine[Any, Any, m.PICSApplicableTestCases]: """ Applicable Test Cases """ @@ -174,43 +189,23 @@ def _build_for_applicable_test_cases_api_v1_projects__id__applicable_test_cases_ type_=m.PICSApplicableTestCases, method="GET", url="/api/v1/projects/{id}/applicable_test_cases", - path_params=path_params, + path_params=path_params ) - def _build_for_export_project_config_api_v1_projects__id__export_get( - self, id: int - ) -> Coroutine[Any, Any, m.ProjectCreate]: + def _build_for_export_project_config_api_v1_projects__id__export_get(self, id: int) -> Coroutine[Any, Any, m.ProjectCreate]: """ Export Project Config """ path_params = {"id": str(id)} return self.api_client.request( - type_=m.ProjectCreate, method="GET", url="/api/v1/projects/{id}/export", path_params=path_params - ) - - def _build_for_download_project_logs_api_v1_projects__id__logs_get( - self, id: int, grouped: bool = False - ) -> Coroutine[Any, Any, bytes]: - """ - Download Project Logs - """ - path_params = {"id": str(id)} - query_params: dict[str, Any] = {} - if grouped: - query_params["grouped"] = "true" - - return self.api_client.request( - type_=bytes, + type_=m.ProjectCreate, method="GET", - url="/api/v1/projects/{id}/logs", - path_params=path_params, - params=query_params, + url="/api/v1/projects/{id}/export", + path_params=path_params ) - def _build_for_importproject_config_api_v1_projects_import_post( - self, body: m.BodyImportprojectConfigApiV1ProjectsImportPost - ) -> Coroutine[Any, Any, m.Project]: + def _build_for_importproject_config_api_v1_projects_import_post(self, body: m.BodyImportprojectConfigApiV1ProjectsImportPost) -> Coroutine[Any, Any, m.Project]: """ Importproject Config """ @@ -220,21 +215,41 @@ def _build_for_importproject_config_api_v1_projects_import_post( # Process body fields to populate files and data dictionaries if body is not None: # Process field: import_file - if hasattr(body, "import_file"): - field_value = getattr(body, "import_file") + if hasattr(body, 'import_file'): + field_value = getattr(body, 'import_file') if field_value is not None: # File field - files["import_file"] = field_value + files['import_file'] = field_value + + return self.api_client.request( + type_=m.Project, + method="POST", + url="/api/v1/projects/import", + data=data, + files=files + ) + + def _build_for_download_project_logs_api_v1_projects__id__logs_get(self, id: int, grouped: bool | None = None) -> Coroutine[Any, Any, bytes]: + """ + Download Project Logs + """ + path_params = {"id": str(id)} + + query_params = {} + if grouped is not None: + query_params["grouped"] = str(grouped) return self.api_client.request( - type_=m.Project, method="POST", url="/api/v1/projects/import", data=data, files=files + type_=bytes, + method="GET", + url="/api/v1/projects/{id}/logs", + path_params=path_params, + params=query_params ) class AsyncProjectsApi(_ProjectsApi): - async def read_projects_api_v1_projects__get( - self, archived: bool | None = None, skip: int | None = None, limit: int | None = None - ) -> list[m.Project]: + async def read_projects_api_v1_projects__get(self, archived: bool | None = None, skip: int | None = None, limit: int | None = None) -> list[m.Project]: """ Read Projects """ @@ -282,27 +297,19 @@ async def unarchive_project_api_v1_projects__id__unarchive_post(self, id: int) - """ return await self._build_for_unarchive_project_api_v1_projects__id__unarchive_post(id=id) - async def upload_pics_api_v1_projects__id__upload_pics_put( - self, body: m.BodyUploadPicsApiV1ProjectsIdUploadPicsPut, id: int - ) -> m.Project: + async def upload_pics_api_v1_projects__id__upload_pics_put(self, body: m.BodyUploadPicsApiV1ProjectsIdUploadPicsPut, id: int) -> m.Project: """ Upload Pics """ return await self._build_for_upload_pics_api_v1_projects__id__upload_pics_put(body=body, id=id) - async def remove_pics_cluster_type_api_v1_projects__id__pics_cluster_type_delete( - self, id: int, cluster_name: str - ) -> m.Project: + async def remove_pics_cluster_type_api_v1_projects__id__pics_cluster_type_delete(self, id: int, cluster_name: str) -> m.Project: """ Remove Pics Cluster Type """ - return await self._build_for_remove_pics_cluster_type_api_v1_projects__id__pics_cluster_type_delete( - id=id, cluster_name=cluster_name - ) + return await self._build_for_remove_pics_cluster_type_api_v1_projects__id__pics_cluster_type_delete(id=id, cluster_name=cluster_name) - async def applicable_test_cases_api_v1_projects__id__applicable_test_cases_get( - self, id: int - ) -> m.PICSApplicableTestCases: + async def applicable_test_cases_api_v1_projects__id__applicable_test_cases_get(self, id: int) -> m.PICSApplicableTestCases: """ Applicable Test Cases """ @@ -314,29 +321,21 @@ async def export_project_config_api_v1_projects__id__export_get(self, id: int) - """ return await self._build_for_export_project_config_api_v1_projects__id__export_get(id=id) - async def importproject_config_api_v1_projects_import_post( - self, body: m.BodyImportprojectConfigApiV1ProjectsImportPost - ) -> m.Project: + async def importproject_config_api_v1_projects_import_post(self, body: m.BodyImportprojectConfigApiV1ProjectsImportPost) -> m.Project: """ Importproject Config """ return await self._build_for_importproject_config_api_v1_projects_import_post(body=body) - async def download_project_logs_api_v1_projects__id__logs_get( - self, id: int, grouped: bool = False - ) -> bytes: + async def download_project_logs_api_v1_projects__id__logs_get(self, id: int, grouped: bool | None = None) -> bytes: """ Download Project Logs """ - return await self._build_for_download_project_logs_api_v1_projects__id__logs_get( - id=id, grouped=grouped - ) + return await self._build_for_download_project_logs_api_v1_projects__id__logs_get(id=id, grouped=grouped) class SyncProjectsApi(_ProjectsApi): - def read_projects_api_v1_projects__get( - self, archived: bool | None = None, skip: int | None = None, limit: int | None = None - ) -> list[m.Project]: + def read_projects_api_v1_projects__get(self, archived: bool | None = None, skip: int | None = None, limit: int | None = None) -> list[m.Project]: """ Read Projects """ @@ -392,29 +391,21 @@ def unarchive_project_api_v1_projects__id__unarchive_post(self, id: int) -> m.Pr coroutine = self._build_for_unarchive_project_api_v1_projects__id__unarchive_post(id=id) return get_event_loop().run_until_complete(coroutine) - def upload_pics_api_v1_projects__id__upload_pics_put( - self, body: m.BodyUploadPicsApiV1ProjectsIdUploadPicsPut, id: int - ) -> m.Project: + def upload_pics_api_v1_projects__id__upload_pics_put(self, body: m.BodyUploadPicsApiV1ProjectsIdUploadPicsPut, id: int) -> m.Project: """ Upload Pics """ coroutine = self._build_for_upload_pics_api_v1_projects__id__upload_pics_put(body=body, id=id) return get_event_loop().run_until_complete(coroutine) - def remove_pics_cluster_type_api_v1_projects__id__pics_cluster_type_delete( - self, id: int, cluster_name: str - ) -> m.Project: + def remove_pics_cluster_type_api_v1_projects__id__pics_cluster_type_delete(self, id: int, cluster_name: str) -> m.Project: """ Remove Pics Cluster Type """ - coroutine = self._build_for_remove_pics_cluster_type_api_v1_projects__id__pics_cluster_type_delete( - id=id, cluster_name=cluster_name - ) + coroutine = self._build_for_remove_pics_cluster_type_api_v1_projects__id__pics_cluster_type_delete(id=id, cluster_name=cluster_name) return get_event_loop().run_until_complete(coroutine) - def applicable_test_cases_api_v1_projects__id__applicable_test_cases_get( - self, id: int - ) -> m.PICSApplicableTestCases: + def applicable_test_cases_api_v1_projects__id__applicable_test_cases_get(self, id: int) -> m.PICSApplicableTestCases: """ Applicable Test Cases """ @@ -428,22 +419,16 @@ def export_project_config_api_v1_projects__id__export_get(self, id: int) -> m.Pr coroutine = self._build_for_export_project_config_api_v1_projects__id__export_get(id=id) return get_event_loop().run_until_complete(coroutine) - def importproject_config_api_v1_projects_import_post( - self, body: m.BodyImportprojectConfigApiV1ProjectsImportPost - ) -> m.Project: + def importproject_config_api_v1_projects_import_post(self, body: m.BodyImportprojectConfigApiV1ProjectsImportPost) -> m.Project: """ Importproject Config """ coroutine = self._build_for_importproject_config_api_v1_projects_import_post(body=body) return get_event_loop().run_until_complete(coroutine) - def download_project_logs_api_v1_projects__id__logs_get( - self, id: int, grouped: bool = False - ) -> bytes: + def download_project_logs_api_v1_projects__id__logs_get(self, id: int, grouped: bool | None = None) -> bytes: """ Download Project Logs """ - coroutine = self._build_for_download_project_logs_api_v1_projects__id__logs_get( - id=id, grouped=grouped - ) + coroutine = self._build_for_download_project_logs_api_v1_projects__id__logs_get(id=id, grouped=grouped) return get_event_loop().run_until_complete(coroutine) diff --git a/th_cli/api_lib_autogen/api/test_collections_api.py b/th_cli/api_lib_autogen/api/test_collections_api.py index f3c414b..eba4858 100644 --- a/th_cli/api_lib_autogen/api/test_collections_api.py +++ b/th_cli/api_lib_autogen/api/test_collections_api.py @@ -15,14 +15,13 @@ # # flake8: noqa E501 from asyncio import get_event_loop -from typing import IO, TYPE_CHECKING, Any, Coroutine +from typing import Coroutine, IO, TYPE_CHECKING, Any from th_cli.api_lib_autogen import models as m if TYPE_CHECKING: from th_cli.api_lib_autogen.api_client import ApiClient - class _TestCollectionsApi: def __init__(self, api_client: "ApiClient"): self.api_client = api_client @@ -31,7 +30,11 @@ def _build_for_read_test_collections_api_v1_test_collections__get(self) -> Corou """ Read Test Collections """ - return self.api_client.request(type_=m.TestCollections, method="GET", url="/api/v1/test_collections/") + return self.api_client.request( + type_=m.TestCollections, + method="GET", + url="/api/v1/test_collections/" + ) class AsyncTestCollectionsApi(_TestCollectionsApi): diff --git a/th_cli/api_lib_autogen/api/test_run_configs_api.py b/th_cli/api_lib_autogen/api/test_run_configs_api.py index 135df60..e55d93a 100644 --- a/th_cli/api_lib_autogen/api/test_run_configs_api.py +++ b/th_cli/api_lib_autogen/api/test_run_configs_api.py @@ -15,21 +15,18 @@ # # flake8: noqa E501 from asyncio import get_event_loop -from typing import IO, TYPE_CHECKING, Any, Coroutine +from typing import Coroutine, IO, TYPE_CHECKING, Any from th_cli.api_lib_autogen import models as m if TYPE_CHECKING: from th_cli.api_lib_autogen.api_client import ApiClient - class _TestRunConfigsApi: def __init__(self, api_client: "ApiClient"): self.api_client = api_client - def _build_for_read_test_run_configs_api_v1_test_run_configs__get( - self, skip: int | None = None, limit: int | None = None - ) -> Coroutine[Any, Any, list[m.TestRunConfig]]: + def _build_for_read_test_run_configs_api_v1_test_run_configs__get(self, skip: int | None = None, limit: int | None = None) -> Coroutine[Any, Any, list[m.TestRunConfig]]: """ Read Test Run Configs """ @@ -40,64 +37,63 @@ def _build_for_read_test_run_configs_api_v1_test_run_configs__get( query_params["limit"] = str(limit) return self.api_client.request( - type_=list[m.TestRunConfig], method="GET", url="/api/v1/test_run_configs/", params=query_params + type_=list[m.TestRunConfig], + method="GET", + url="/api/v1/test_run_configs/", + params=query_params ) - def _build_for_create_test_run_config_api_v1_test_run_configs__post( - self, body: m.TestRunConfigCreate - ) -> Coroutine[Any, Any, m.TestRunConfig]: + def _build_for_create_test_run_config_api_v1_test_run_configs__post(self, body: m.TestRunConfigCreate) -> Coroutine[Any, Any, m.TestRunConfig]: """ Create Test Run Config """ - json_body = body.model_dump(mode="json") if hasattr(body, "model_dump") else body + json_body = body.model_dump(mode='json') if hasattr(body, 'model_dump') else body return self.api_client.request( - type_=m.TestRunConfig, method="POST", url="/api/v1/test_run_configs/", json=json_body + type_=m.TestRunConfig, + method="POST", + url="/api/v1/test_run_configs/", + json=json_body ) - def _build_for_read_test_run_config_api_v1_test_run_configs__id__get( - self, id: int - ) -> Coroutine[Any, Any, m.TestRunConfig]: + def _build_for_read_test_run_config_api_v1_test_run_configs__id__get(self, id: int) -> Coroutine[Any, Any, m.TestRunConfig]: """ Read Test Run Config """ path_params = {"id": str(id)} return self.api_client.request( - type_=m.TestRunConfig, method="GET", url="/api/v1/test_run_configs/{id}", path_params=path_params + type_=m.TestRunConfig, + method="GET", + url="/api/v1/test_run_configs/{id}", + path_params=path_params ) - def _build_for_update_test_run_config_api_v1_test_run_configs__id__put( - self, body: m.TestRunConfigUpdate, id: int - ) -> Coroutine[Any, Any, m.TestRunConfig]: + def _build_for_update_test_run_config_api_v1_test_run_configs__id__put(self, body: m.TestRunConfigUpdate, id: int) -> Coroutine[Any, Any, m.TestRunConfig]: """ Update Test Run Config """ path_params = {"id": str(id)} - json_body = body.model_dump(mode="json") if hasattr(body, "model_dump") else body + json_body = body.model_dump(mode='json') if hasattr(body, 'model_dump') else body return self.api_client.request( type_=m.TestRunConfig, method="PUT", url="/api/v1/test_run_configs/{id}", path_params=path_params, - json=json_body, + json=json_body ) class AsyncTestRunConfigsApi(_TestRunConfigsApi): - async def read_test_run_configs_api_v1_test_run_configs__get( - self, skip: int | None = None, limit: int | None = None - ) -> list[m.TestRunConfig]: + async def read_test_run_configs_api_v1_test_run_configs__get(self, skip: int | None = None, limit: int | None = None) -> list[m.TestRunConfig]: """ Read Test Run Configs """ return await self._build_for_read_test_run_configs_api_v1_test_run_configs__get(skip=skip, limit=limit) - async def create_test_run_config_api_v1_test_run_configs__post( - self, body: m.TestRunConfigCreate - ) -> m.TestRunConfig: + async def create_test_run_config_api_v1_test_run_configs__post(self, body: m.TestRunConfigCreate) -> m.TestRunConfig: """ Create Test Run Config """ @@ -109,9 +105,7 @@ async def read_test_run_config_api_v1_test_run_configs__id__get(self, id: int) - """ return await self._build_for_read_test_run_config_api_v1_test_run_configs__id__get(id=id) - async def update_test_run_config_api_v1_test_run_configs__id__put( - self, body: m.TestRunConfigUpdate, id: int - ) -> m.TestRunConfig: + async def update_test_run_config_api_v1_test_run_configs__id__put(self, body: m.TestRunConfigUpdate, id: int) -> m.TestRunConfig: """ Update Test Run Config """ @@ -119,9 +113,7 @@ async def update_test_run_config_api_v1_test_run_configs__id__put( class SyncTestRunConfigsApi(_TestRunConfigsApi): - def read_test_run_configs_api_v1_test_run_configs__get( - self, skip: int | None = None, limit: int | None = None - ) -> list[m.TestRunConfig]: + def read_test_run_configs_api_v1_test_run_configs__get(self, skip: int | None = None, limit: int | None = None) -> list[m.TestRunConfig]: """ Read Test Run Configs """ @@ -142,9 +134,7 @@ def read_test_run_config_api_v1_test_run_configs__id__get(self, id: int) -> m.Te coroutine = self._build_for_read_test_run_config_api_v1_test_run_configs__id__get(id=id) return get_event_loop().run_until_complete(coroutine) - def update_test_run_config_api_v1_test_run_configs__id__put( - self, body: m.TestRunConfigUpdate, id: int - ) -> m.TestRunConfig: + def update_test_run_config_api_v1_test_run_configs__id__put(self, body: m.TestRunConfigUpdate, id: int) -> m.TestRunConfig: """ Update Test Run Config """ diff --git a/th_cli/api_lib_autogen/api/test_run_executions_api.py b/th_cli/api_lib_autogen/api/test_run_executions_api.py index ccf7abe..43df849 100644 --- a/th_cli/api_lib_autogen/api/test_run_executions_api.py +++ b/th_cli/api_lib_autogen/api/test_run_executions_api.py @@ -15,27 +15,18 @@ # # flake8: noqa E501 from asyncio import get_event_loop -from typing import IO, TYPE_CHECKING, Any, Coroutine +from typing import Coroutine, IO, TYPE_CHECKING, Any from th_cli.api_lib_autogen import models as m if TYPE_CHECKING: from th_cli.api_lib_autogen.api_client import ApiClient - class _TestRunExecutionsApi: def __init__(self, api_client: "ApiClient"): self.api_client = api_client - def _build_for_read_test_run_executions_api_v1_test_run_executions__get( - self, - project_id: int | None = None, - archived: bool | None = None, - search_query: str | None = None, - skip: int | None = None, - limit: int | None = None, - sort_order: str | None = None, - ) -> Coroutine[Any, Any, list[m.TestRunExecutionWithStats]]: + def _build_for_read_test_run_executions_api_v1_test_run_executions__get(self, project_id: int | None = None, archived: bool | None = None, search_query: str | None = None, skip: int | None = None, limit: int | None = None, sort_order: str | None = None) -> Coroutine[Any, Any, list[m.TestRunExecutionWithStats]]: """ Read Test Run Executions """ @@ -57,12 +48,10 @@ def _build_for_read_test_run_executions_api_v1_test_run_executions__get( type_=list[m.TestRunExecutionWithStats], method="GET", url="/api/v1/test_run_executions/", - params=query_params, + params=query_params ) - def _build_for_create_test_run_execution_api_v1_test_run_executions__post( - self, body: m.BodyCreateTestRunExecutionApiV1TestRunExecutionsPost, certification_mode: bool | None = None - ) -> Coroutine[Any, Any, m.TestRunExecutionWithChildren]: + def _build_for_create_test_run_execution_api_v1_test_run_executions__post(self, body: m.BodyCreateTestRunExecutionApiV1TestRunExecutionsPost, certification_mode: bool | None = None) -> Coroutine[Any, Any, m.TestRunExecutionWithChildren]: """ Create Test Run Execution """ @@ -70,31 +59,30 @@ def _build_for_create_test_run_execution_api_v1_test_run_executions__post( if certification_mode is not None: query_params["certification_mode"] = str(certification_mode) - json_body = body.model_dump(mode="json") if hasattr(body, "model_dump") else body + json_body = body.model_dump(mode='json') if hasattr(body, 'model_dump') else body return self.api_client.request( type_=m.TestRunExecutionWithChildren, method="POST", url="/api/v1/test_run_executions/", params=query_params, - json=json_body, + json=json_body ) - def _build_for_create_cli_test_run_execution_api_v1_test_run_executions_cli_post( - self, body: m.BodyCreateCliTestRunExecutionApiV1TestRunExecutionsCliPost - ) -> Coroutine[Any, Any, m.TestRunExecutionWithChildren]: + def _build_for_create_cli_test_run_execution_api_v1_test_run_executions_cli_post(self, body: m.BodyCreateCliTestRunExecutionApiV1TestRunExecutionsCliPost) -> Coroutine[Any, Any, m.TestRunExecutionWithChildren]: """ Create Cli Test Run Execution """ - json_body = body.model_dump(mode="json") if hasattr(body, "model_dump") else body + json_body = body.model_dump(mode='json') if hasattr(body, 'model_dump') else body return self.api_client.request( - type_=m.TestRunExecutionWithChildren, method="POST", url="/api/v1/test_run_executions/cli", json=json_body + type_=m.TestRunExecutionWithChildren, + method="POST", + url="/api/v1/test_run_executions/cli", + json=json_body ) - def _build_for_rename_test_run_execution_api_v1_test_run_executions__id__rename_put( - self, id: int, new_execution_name: str - ) -> Coroutine[Any, Any, m.TestRunExecutionWithChildren]: + def _build_for_rename_test_run_execution_api_v1_test_run_executions__id__rename_put(self, id: int, new_execution_name: str) -> Coroutine[Any, Any, m.TestRunExecutionWithChildren]: """ Rename Test Run Execution """ @@ -107,35 +95,30 @@ def _build_for_rename_test_run_execution_api_v1_test_run_executions__id__rename_ method="PUT", url="/api/v1/test_run_executions/{id}/rename", path_params=path_params, - params=query_params, + params=query_params ) - def _build_for_abort_testing_api_v1_test_run_executions_abort_testing_post( - self, - ) -> Coroutine[Any, Any, dict[str, str]]: + def _build_for_abort_testing_api_v1_test_run_executions_abort_testing_post(self) -> Coroutine[Any, Any, dict[str, str]]: """ Abort Testing """ return self.api_client.request( - type_=dict[str, str], method="POST", url="/api/v1/test_run_executions/abort-testing" + type_=dict[str, str], + method="POST", + url="/api/v1/test_run_executions/abort-testing" ) - def _build_for_get_test_runner_status_api_v1_test_run_executions_status_get( - self, - ) -> Coroutine[Any, Any, m.TestRunnerStatus]: + def _build_for_get_test_runner_status_api_v1_test_run_executions_status_get(self) -> Coroutine[Any, Any, m.TestRunnerStatus]: """ Get Test Runner Status """ - return self.api_client.request(type_=m.TestRunnerStatus, method="GET", url="/api/v1/test_run_executions/status") + return self.api_client.request( + type_=m.TestRunnerStatus, + method="GET", + url="/api/v1/test_run_executions/status" + ) - def _build_for_get_chip_server_info_api_v1_test_run_executions_chip_server_info_get( - self, - discriminator: str | None = None, - setup_pin_code: str | None = None, - version: int | None = None, - vendor_id: int | None = None, - product_id: int | None = None, - ) -> Coroutine[Any, Any, m.ChipServerInfo]: + def _build_for_get_chip_server_info_api_v1_test_run_executions_chip_server_info_get(self, discriminator: str | None = None, setup_pin_code: str | None = None, version: int | None = None, vendor_id: int | None = None, product_id: int | None = None) -> Coroutine[Any, Any, m.ChipServerInfo]: """ Get Chip Server Info """ @@ -155,12 +138,10 @@ def _build_for_get_chip_server_info_api_v1_test_run_executions_chip_server_info_ type_=m.ChipServerInfo, method="GET", url="/api/v1/test_run_executions/chip-server/info", - params=query_params, + params=query_params ) - def _build_for_read_test_run_execution_api_v1_test_run_executions__id__get( - self, id: int - ) -> Coroutine[Any, Any, m.TestRunExecutionWithChildren]: + def _build_for_read_test_run_execution_api_v1_test_run_executions__id__get(self, id: int) -> Coroutine[Any, Any, m.TestRunExecutionWithChildren]: """ Read Test Run Execution """ @@ -170,12 +151,10 @@ def _build_for_read_test_run_execution_api_v1_test_run_executions__id__get( type_=m.TestRunExecutionWithChildren, method="GET", url="/api/v1/test_run_executions/{id}", - path_params=path_params, + path_params=path_params ) - def _build_for_remove_test_run_execution_api_v1_test_run_executions__id__delete( - self, id: int - ) -> Coroutine[Any, Any, m.TestRunExecutionInDBBase]: + def _build_for_remove_test_run_execution_api_v1_test_run_executions__id__delete(self, id: int) -> Coroutine[Any, Any, m.TestRunExecutionInDBBase]: """ Remove Test Run Execution """ @@ -185,12 +164,10 @@ def _build_for_remove_test_run_execution_api_v1_test_run_executions__id__delete( type_=m.TestRunExecutionInDBBase, method="DELETE", url="/api/v1/test_run_executions/{id}", - path_params=path_params, + path_params=path_params ) - def _build_for_start_test_run_execution_api_v1_test_run_executions__id__start_post( - self, id: int - ) -> Coroutine[Any, Any, m.TestRunExecutionWithChildren]: + def _build_for_start_test_run_execution_api_v1_test_run_executions__id__start_post(self, id: int) -> Coroutine[Any, Any, m.TestRunExecutionWithChildren]: """ Start Test Run Execution """ @@ -200,12 +177,10 @@ def _build_for_start_test_run_execution_api_v1_test_run_executions__id__start_po type_=m.TestRunExecutionWithChildren, method="POST", url="/api/v1/test_run_executions/{id}/start", - path_params=path_params, + path_params=path_params ) - def _build_for_archive_api_v1_test_run_executions__id__archive_post( - self, id: int - ) -> Coroutine[Any, Any, m.TestRunExecution]: + def _build_for_archive_api_v1_test_run_executions__id__archive_post(self, id: int) -> Coroutine[Any, Any, m.TestRunExecution]: """ Archive """ @@ -215,12 +190,10 @@ def _build_for_archive_api_v1_test_run_executions__id__archive_post( type_=m.TestRunExecution, method="POST", url="/api/v1/test_run_executions/{id}/archive", - path_params=path_params, + path_params=path_params ) - def _build_for_unarchive_api_v1_test_run_executions__id__unarchive_post( - self, id: int - ) -> Coroutine[Any, Any, m.TestRunExecution]: + def _build_for_unarchive_api_v1_test_run_executions__id__unarchive_post(self, id: int) -> Coroutine[Any, Any, m.TestRunExecution]: """ Unarchive """ @@ -230,12 +203,10 @@ def _build_for_unarchive_api_v1_test_run_executions__id__unarchive_post( type_=m.TestRunExecution, method="POST", url="/api/v1/test_run_executions/{id}/unarchive", - path_params=path_params, + path_params=path_params ) - def _build_for_repeat_test_run_execution_api_v1_test_run_executions__id__repeat_post( - self, id: int, title: str | None = None - ) -> Coroutine[Any, Any, m.TestRunExecutionWithChildren]: + def _build_for_repeat_test_run_execution_api_v1_test_run_executions__id__repeat_post(self, id: int, title: str | None = None) -> Coroutine[Any, Any, m.TestRunExecutionWithChildren]: """ Repeat Test Run Execution """ @@ -250,12 +221,10 @@ def _build_for_repeat_test_run_execution_api_v1_test_run_executions__id__repeat_ method="POST", url="/api/v1/test_run_executions/{id}/repeat", path_params=path_params, - params=query_params, + params=query_params ) - def _build_for_download_log_api_v1_test_run_executions__id__log_get( - self, id: int, json_entries: bool | None = None, download: bool | None = None - ) -> Coroutine[Any, Any, None]: + def _build_for_download_log_api_v1_test_run_executions__id__log_get(self, id: int, json_entries: bool | None = None, download: bool | None = None) -> Coroutine[Any, Any, None]: """ Download Log """ @@ -272,24 +241,23 @@ def _build_for_download_log_api_v1_test_run_executions__id__log_get( method="GET", url="/api/v1/test_run_executions/{id}/log", path_params=path_params, - params=query_params, + params=query_params ) - def _build_for_download_grouped_log_api_v1_test_run_executions__id__grouped_log_get( - self, id: int - ) -> Coroutine[Any, Any, bytes]: + def _build_for_download_grouped_log_api_v1_test_run_executions__id__grouped_log_get(self, id: int) -> Coroutine[Any, Any, bytes]: """ Download Grouped Log """ path_params = {"id": str(id)} return self.api_client.request( - type_=bytes, method="GET", url="/api/v1/test_run_executions/{id}/grouped-log", path_params=path_params + type_=bytes, + method="GET", + url="/api/v1/test_run_executions/{id}/grouped-log", + path_params=path_params ) - def _build_for_upload_file_api_v1_test_run_executions_file_upload__post( - self, body: m.BodyUploadFileApiV1TestRunExecutionsFileUploadPost - ) -> Coroutine[Any, Any, dict[str, Any]]: + def _build_for_upload_file_api_v1_test_run_executions_file_upload__post(self, body: m.BodyUploadFileApiV1TestRunExecutionsFileUploadPost) -> Coroutine[Any, Any, dict[str, Any]]: """ Upload File """ @@ -299,19 +267,21 @@ def _build_for_upload_file_api_v1_test_run_executions_file_upload__post( # Process body fields to populate files and data dictionaries if body is not None: # Process field: file - if hasattr(body, "file"): - field_value = getattr(body, "file") + if hasattr(body, 'file'): + field_value = getattr(body, 'file') if field_value is not None: # File field - files["file"] = field_value + files['file'] = field_value return self.api_client.request( - type_=dict[str, Any], method="POST", url="/api/v1/test_run_executions/file_upload/", data=data, files=files + type_=dict[str, Any], + method="POST", + url="/api/v1/test_run_executions/file_upload/", + data=data, + files=files ) - def _build_for_export_test_run_execution_api_v1_test_run_executions__id__export_get( - self, id: int, download: bool | None = None - ) -> Coroutine[Any, Any, m.ExportedTestRunExecution]: + def _build_for_export_test_run_execution_api_v1_test_run_executions__id__export_get(self, id: int, download: bool | None = None) -> Coroutine[Any, Any, m.ExportedTestRunExecution]: """ Export Test Run Execution """ @@ -326,12 +296,10 @@ def _build_for_export_test_run_execution_api_v1_test_run_executions__id__export_ method="GET", url="/api/v1/test_run_executions/{id}/export", path_params=path_params, - params=query_params, + params=query_params ) - def _build_for_import_test_run_execution_api_v1_test_run_executions_import_post( - self, body: m.BodyImportTestRunExecutionApiV1TestRunExecutionsImportPost, project_id: int - ) -> Coroutine[Any, Any, m.TestRunExecutionWithChildren]: + def _build_for_import_test_run_execution_api_v1_test_run_executions_import_post(self, body: m.BodyImportTestRunExecutionApiV1TestRunExecutionsImportPost, project_id: int) -> Coroutine[Any, Any, m.TestRunExecutionWithChildren]: """ Import Test Run Execution """ @@ -343,11 +311,11 @@ def _build_for_import_test_run_execution_api_v1_test_run_executions_import_post( # Process body fields to populate files and data dictionaries if body is not None: # Process field: import_file - if hasattr(body, "import_file"): - field_value = getattr(body, "import_file") + if hasattr(body, 'import_file'): + field_value = getattr(body, 'import_file') if field_value is not None: # File field - files["import_file"] = field_value + files['import_file'] = field_value return self.api_client.request( type_=m.TestRunExecutionWithChildren, @@ -355,77 +323,34 @@ def _build_for_import_test_run_execution_api_v1_test_run_executions_import_post( url="/api/v1/test_run_executions/import", params=query_params, data=data, - files=files, - ) - - def _build_for_generate_summary_log_api_v1_test_run_executions__id__performance_summary_post( - self, id: int, project_id: int - ) -> Coroutine[Any, Any, dict[str, Any]]: - """ - Generate Summary Log - """ - path_params = {"id": str(id)} - - query_params = {"project_id": str(project_id)} - - return self.api_client.request( - type_=dict[str, Any], - method="POST", - url="/api/v1/test_run_executions/{id}/performance_summary", - path_params=path_params, - params=query_params, + files=files ) class AsyncTestRunExecutionsApi(_TestRunExecutionsApi): - async def read_test_run_executions_api_v1_test_run_executions__get( - self, - project_id: int | None = None, - archived: bool | None = None, - search_query: str | None = None, - skip: int | None = None, - limit: int | None = None, - sort_order: str | None = None, - ) -> list[m.TestRunExecutionWithStats]: + async def read_test_run_executions_api_v1_test_run_executions__get(self, project_id: int | None = None, archived: bool | None = None, search_query: str | None = None, skip: int | None = None, limit: int | None = None, sort_order: str | None = None) -> list[m.TestRunExecutionWithStats]: """ Read Test Run Executions """ - return await self._build_for_read_test_run_executions_api_v1_test_run_executions__get( - project_id=project_id, - archived=archived, - search_query=search_query, - skip=skip, - limit=limit, - sort_order=sort_order, - ) + return await self._build_for_read_test_run_executions_api_v1_test_run_executions__get(project_id=project_id, archived=archived, search_query=search_query, skip=skip, limit=limit, sort_order=sort_order) - async def create_test_run_execution_api_v1_test_run_executions__post( - self, body: m.BodyCreateTestRunExecutionApiV1TestRunExecutionsPost, certification_mode: bool | None = None - ) -> m.TestRunExecutionWithChildren: + async def create_test_run_execution_api_v1_test_run_executions__post(self, body: m.BodyCreateTestRunExecutionApiV1TestRunExecutionsPost, certification_mode: bool | None = None) -> m.TestRunExecutionWithChildren: """ Create Test Run Execution """ - return await self._build_for_create_test_run_execution_api_v1_test_run_executions__post( - body=body, certification_mode=certification_mode - ) + return await self._build_for_create_test_run_execution_api_v1_test_run_executions__post(body=body, certification_mode=certification_mode) - async def create_cli_test_run_execution_api_v1_test_run_executions_cli_post( - self, body: m.BodyCreateCliTestRunExecutionApiV1TestRunExecutionsCliPost - ) -> m.TestRunExecutionWithChildren: + async def create_cli_test_run_execution_api_v1_test_run_executions_cli_post(self, body: m.BodyCreateCliTestRunExecutionApiV1TestRunExecutionsCliPost) -> m.TestRunExecutionWithChildren: """ Create Cli Test Run Execution """ return await self._build_for_create_cli_test_run_execution_api_v1_test_run_executions_cli_post(body=body) - async def rename_test_run_execution_api_v1_test_run_executions__id__rename_put( - self, id: int, new_execution_name: str - ) -> m.TestRunExecutionWithChildren: + async def rename_test_run_execution_api_v1_test_run_executions__id__rename_put(self, id: int, new_execution_name: str) -> m.TestRunExecutionWithChildren: """ Rename Test Run Execution """ - return await self._build_for_rename_test_run_execution_api_v1_test_run_executions__id__rename_put( - id=id, new_execution_name=new_execution_name - ) + return await self._build_for_rename_test_run_execution_api_v1_test_run_executions__id__rename_put(id=id, new_execution_name=new_execution_name) async def abort_testing_api_v1_test_run_executions_abort_testing_post(self) -> dict[str, str]: """ @@ -439,44 +364,25 @@ async def get_test_runner_status_api_v1_test_run_executions_status_get(self) -> """ return await self._build_for_get_test_runner_status_api_v1_test_run_executions_status_get() - async def get_chip_server_info_api_v1_test_run_executions_chip_server_info_get( - self, - discriminator: str | None = None, - setup_pin_code: str | None = None, - version: int | None = None, - vendor_id: int | None = None, - product_id: int | None = None, - ) -> m.ChipServerInfo: + async def get_chip_server_info_api_v1_test_run_executions_chip_server_info_get(self, discriminator: str | None = None, setup_pin_code: str | None = None, version: int | None = None, vendor_id: int | None = None, product_id: int | None = None) -> m.ChipServerInfo: """ Get Chip Server Info """ - return await self._build_for_get_chip_server_info_api_v1_test_run_executions_chip_server_info_get( - discriminator=discriminator, - setup_pin_code=setup_pin_code, - version=version, - vendor_id=vendor_id, - product_id=product_id, - ) + return await self._build_for_get_chip_server_info_api_v1_test_run_executions_chip_server_info_get(discriminator=discriminator, setup_pin_code=setup_pin_code, version=version, vendor_id=vendor_id, product_id=product_id) - async def read_test_run_execution_api_v1_test_run_executions__id__get( - self, id: int - ) -> m.TestRunExecutionWithChildren: + async def read_test_run_execution_api_v1_test_run_executions__id__get(self, id: int) -> m.TestRunExecutionWithChildren: """ Read Test Run Execution """ return await self._build_for_read_test_run_execution_api_v1_test_run_executions__id__get(id=id) - async def remove_test_run_execution_api_v1_test_run_executions__id__delete( - self, id: int - ) -> m.TestRunExecutionInDBBase: + async def remove_test_run_execution_api_v1_test_run_executions__id__delete(self, id: int) -> m.TestRunExecutionInDBBase: """ Remove Test Run Execution """ return await self._build_for_remove_test_run_execution_api_v1_test_run_executions__id__delete(id=id) - async def start_test_run_execution_api_v1_test_run_executions__id__start_post( - self, id: int - ) -> m.TestRunExecutionWithChildren: + async def start_test_run_execution_api_v1_test_run_executions__id__start_post(self, id: int) -> m.TestRunExecutionWithChildren: """ Start Test Run Execution """ @@ -494,25 +400,17 @@ async def unarchive_api_v1_test_run_executions__id__unarchive_post(self, id: int """ return await self._build_for_unarchive_api_v1_test_run_executions__id__unarchive_post(id=id) - async def repeat_test_run_execution_api_v1_test_run_executions__id__repeat_post( - self, id: int, title: str | None = None - ) -> m.TestRunExecutionWithChildren: + async def repeat_test_run_execution_api_v1_test_run_executions__id__repeat_post(self, id: int, title: str | None = None) -> m.TestRunExecutionWithChildren: """ Repeat Test Run Execution """ - return await self._build_for_repeat_test_run_execution_api_v1_test_run_executions__id__repeat_post( - id=id, title=title - ) + return await self._build_for_repeat_test_run_execution_api_v1_test_run_executions__id__repeat_post(id=id, title=title) - async def download_log_api_v1_test_run_executions__id__log_get( - self, id: int, json_entries: bool | None = None, download: bool | None = None - ) -> None: + async def download_log_api_v1_test_run_executions__id__log_get(self, id: int, json_entries: bool | None = None, download: bool | None = None) -> None: """ Download Log """ - return await self._build_for_download_log_api_v1_test_run_executions__id__log_get( - id=id, json_entries=json_entries, download=download - ) + return await self._build_for_download_log_api_v1_test_run_executions__id__log_get(id=id, json_entries=json_entries, download=download) async def download_grouped_log_api_v1_test_run_executions__id__grouped_log_get(self, id: int) -> bytes: """ @@ -520,97 +418,52 @@ async def download_grouped_log_api_v1_test_run_executions__id__grouped_log_get(s """ return await self._build_for_download_grouped_log_api_v1_test_run_executions__id__grouped_log_get(id=id) - async def upload_file_api_v1_test_run_executions_file_upload__post( - self, body: m.BodyUploadFileApiV1TestRunExecutionsFileUploadPost - ) -> dict[str, Any]: + async def upload_file_api_v1_test_run_executions_file_upload__post(self, body: m.BodyUploadFileApiV1TestRunExecutionsFileUploadPost) -> dict[str, Any]: """ Upload File """ return await self._build_for_upload_file_api_v1_test_run_executions_file_upload__post(body=body) - async def export_test_run_execution_api_v1_test_run_executions__id__export_get( - self, id: int, download: bool | None = None - ) -> m.ExportedTestRunExecution: + async def export_test_run_execution_api_v1_test_run_executions__id__export_get(self, id: int, download: bool | None = None) -> m.ExportedTestRunExecution: """ Export Test Run Execution """ - return await self._build_for_export_test_run_execution_api_v1_test_run_executions__id__export_get( - id=id, download=download - ) + return await self._build_for_export_test_run_execution_api_v1_test_run_executions__id__export_get(id=id, download=download) - async def import_test_run_execution_api_v1_test_run_executions_import_post( - self, body: m.BodyImportTestRunExecutionApiV1TestRunExecutionsImportPost, project_id: int - ) -> m.TestRunExecutionWithChildren: + async def import_test_run_execution_api_v1_test_run_executions_import_post(self, body: m.BodyImportTestRunExecutionApiV1TestRunExecutionsImportPost, project_id: int) -> m.TestRunExecutionWithChildren: """ Import Test Run Execution """ - return await self._build_for_import_test_run_execution_api_v1_test_run_executions_import_post( - body=body, project_id=project_id - ) - - async def generate_summary_log_api_v1_test_run_executions__id__performance_summary_post( - self, id: int, project_id: int - ) -> dict[str, Any]: - """ - Generate Summary Log - """ - return await self._build_for_generate_summary_log_api_v1_test_run_executions__id__performance_summary_post( - id=id, project_id=project_id - ) + return await self._build_for_import_test_run_execution_api_v1_test_run_executions_import_post(body=body, project_id=project_id) class SyncTestRunExecutionsApi(_TestRunExecutionsApi): - def read_test_run_executions_api_v1_test_run_executions__get( - self, - project_id: int | None = None, - archived: bool | None = None, - search_query: str | None = None, - skip: int | None = None, - limit: int | None = None, - sort_order: str | None = None, - ) -> list[m.TestRunExecutionWithStats]: + def read_test_run_executions_api_v1_test_run_executions__get(self, project_id: int | None = None, archived: bool | None = None, search_query: str | None = None, skip: int | None = None, limit: int | None = None, sort_order: str | None = None) -> list[m.TestRunExecutionWithStats]: """ Read Test Run Executions """ - coroutine = self._build_for_read_test_run_executions_api_v1_test_run_executions__get( - project_id=project_id, - archived=archived, - search_query=search_query, - skip=skip, - limit=limit, - sort_order=sort_order, - ) + coroutine = self._build_for_read_test_run_executions_api_v1_test_run_executions__get(project_id=project_id, archived=archived, search_query=search_query, skip=skip, limit=limit, sort_order=sort_order) return get_event_loop().run_until_complete(coroutine) - def create_test_run_execution_api_v1_test_run_executions__post( - self, body: m.BodyCreateTestRunExecutionApiV1TestRunExecutionsPost, certification_mode: bool | None = None - ) -> m.TestRunExecutionWithChildren: + def create_test_run_execution_api_v1_test_run_executions__post(self, body: m.BodyCreateTestRunExecutionApiV1TestRunExecutionsPost, certification_mode: bool | None = None) -> m.TestRunExecutionWithChildren: """ Create Test Run Execution """ - coroutine = self._build_for_create_test_run_execution_api_v1_test_run_executions__post( - body=body, certification_mode=certification_mode - ) + coroutine = self._build_for_create_test_run_execution_api_v1_test_run_executions__post(body=body, certification_mode=certification_mode) return get_event_loop().run_until_complete(coroutine) - def create_cli_test_run_execution_api_v1_test_run_executions_cli_post( - self, body: m.BodyCreateCliTestRunExecutionApiV1TestRunExecutionsCliPost - ) -> m.TestRunExecutionWithChildren: + def create_cli_test_run_execution_api_v1_test_run_executions_cli_post(self, body: m.BodyCreateCliTestRunExecutionApiV1TestRunExecutionsCliPost) -> m.TestRunExecutionWithChildren: """ Create Cli Test Run Execution """ coroutine = self._build_for_create_cli_test_run_execution_api_v1_test_run_executions_cli_post(body=body) return get_event_loop().run_until_complete(coroutine) - def rename_test_run_execution_api_v1_test_run_executions__id__rename_put( - self, id: int, new_execution_name: str - ) -> m.TestRunExecutionWithChildren: + def rename_test_run_execution_api_v1_test_run_executions__id__rename_put(self, id: int, new_execution_name: str) -> m.TestRunExecutionWithChildren: """ Rename Test Run Execution """ - coroutine = self._build_for_rename_test_run_execution_api_v1_test_run_executions__id__rename_put( - id=id, new_execution_name=new_execution_name - ) + coroutine = self._build_for_rename_test_run_execution_api_v1_test_run_executions__id__rename_put(id=id, new_execution_name=new_execution_name) return get_event_loop().run_until_complete(coroutine) def abort_testing_api_v1_test_run_executions_abort_testing_post(self) -> dict[str, str]: @@ -627,24 +480,11 @@ def get_test_runner_status_api_v1_test_run_executions_status_get(self) -> m.Test coroutine = self._build_for_get_test_runner_status_api_v1_test_run_executions_status_get() return get_event_loop().run_until_complete(coroutine) - def get_chip_server_info_api_v1_test_run_executions_chip_server_info_get( - self, - discriminator: str | None = None, - setup_pin_code: str | None = None, - version: int | None = None, - vendor_id: int | None = None, - product_id: int | None = None, - ) -> m.ChipServerInfo: + def get_chip_server_info_api_v1_test_run_executions_chip_server_info_get(self, discriminator: str | None = None, setup_pin_code: str | None = None, version: int | None = None, vendor_id: int | None = None, product_id: int | None = None) -> m.ChipServerInfo: """ Get Chip Server Info """ - coroutine = self._build_for_get_chip_server_info_api_v1_test_run_executions_chip_server_info_get( - discriminator=discriminator, - setup_pin_code=setup_pin_code, - version=version, - vendor_id=vendor_id, - product_id=product_id, - ) + coroutine = self._build_for_get_chip_server_info_api_v1_test_run_executions_chip_server_info_get(discriminator=discriminator, setup_pin_code=setup_pin_code, version=version, vendor_id=vendor_id, product_id=product_id) return get_event_loop().run_until_complete(coroutine) def read_test_run_execution_api_v1_test_run_executions__id__get(self, id: int) -> m.TestRunExecutionWithChildren: @@ -661,9 +501,7 @@ def remove_test_run_execution_api_v1_test_run_executions__id__delete(self, id: i coroutine = self._build_for_remove_test_run_execution_api_v1_test_run_executions__id__delete(id=id) return get_event_loop().run_until_complete(coroutine) - def start_test_run_execution_api_v1_test_run_executions__id__start_post( - self, id: int - ) -> m.TestRunExecutionWithChildren: + def start_test_run_execution_api_v1_test_run_executions__id__start_post(self, id: int) -> m.TestRunExecutionWithChildren: """ Start Test Run Execution """ @@ -684,26 +522,18 @@ def unarchive_api_v1_test_run_executions__id__unarchive_post(self, id: int) -> m coroutine = self._build_for_unarchive_api_v1_test_run_executions__id__unarchive_post(id=id) return get_event_loop().run_until_complete(coroutine) - def repeat_test_run_execution_api_v1_test_run_executions__id__repeat_post( - self, id: int, title: str | None = None - ) -> m.TestRunExecutionWithChildren: + def repeat_test_run_execution_api_v1_test_run_executions__id__repeat_post(self, id: int, title: str | None = None) -> m.TestRunExecutionWithChildren: """ Repeat Test Run Execution """ - coroutine = self._build_for_repeat_test_run_execution_api_v1_test_run_executions__id__repeat_post( - id=id, title=title - ) + coroutine = self._build_for_repeat_test_run_execution_api_v1_test_run_executions__id__repeat_post(id=id, title=title) return get_event_loop().run_until_complete(coroutine) - def download_log_api_v1_test_run_executions__id__log_get( - self, id: int, json_entries: bool | None = None, download: bool | None = None - ) -> None: + def download_log_api_v1_test_run_executions__id__log_get(self, id: int, json_entries: bool | None = None, download: bool | None = None) -> None: """ Download Log """ - coroutine = self._build_for_download_log_api_v1_test_run_executions__id__log_get( - id=id, json_entries=json_entries, download=download - ) + coroutine = self._build_for_download_log_api_v1_test_run_executions__id__log_get(id=id, json_entries=json_entries, download=download) return get_event_loop().run_until_complete(coroutine) def download_grouped_log_api_v1_test_run_executions__id__grouped_log_get(self, id: int) -> bytes: @@ -713,44 +543,23 @@ def download_grouped_log_api_v1_test_run_executions__id__grouped_log_get(self, i coroutine = self._build_for_download_grouped_log_api_v1_test_run_executions__id__grouped_log_get(id=id) return get_event_loop().run_until_complete(coroutine) - def upload_file_api_v1_test_run_executions_file_upload__post( - self, body: m.BodyUploadFileApiV1TestRunExecutionsFileUploadPost - ) -> dict[str, Any]: + def upload_file_api_v1_test_run_executions_file_upload__post(self, body: m.BodyUploadFileApiV1TestRunExecutionsFileUploadPost) -> dict[str, Any]: """ Upload File """ coroutine = self._build_for_upload_file_api_v1_test_run_executions_file_upload__post(body=body) return get_event_loop().run_until_complete(coroutine) - def export_test_run_execution_api_v1_test_run_executions__id__export_get( - self, id: int, download: bool | None = None - ) -> m.ExportedTestRunExecution: + def export_test_run_execution_api_v1_test_run_executions__id__export_get(self, id: int, download: bool | None = None) -> m.ExportedTestRunExecution: """ Export Test Run Execution """ - coroutine = self._build_for_export_test_run_execution_api_v1_test_run_executions__id__export_get( - id=id, download=download - ) + coroutine = self._build_for_export_test_run_execution_api_v1_test_run_executions__id__export_get(id=id, download=download) return get_event_loop().run_until_complete(coroutine) - def import_test_run_execution_api_v1_test_run_executions_import_post( - self, body: m.BodyImportTestRunExecutionApiV1TestRunExecutionsImportPost, project_id: int - ) -> m.TestRunExecutionWithChildren: + def import_test_run_execution_api_v1_test_run_executions_import_post(self, body: m.BodyImportTestRunExecutionApiV1TestRunExecutionsImportPost, project_id: int) -> m.TestRunExecutionWithChildren: """ Import Test Run Execution """ - coroutine = self._build_for_import_test_run_execution_api_v1_test_run_executions_import_post( - body=body, project_id=project_id - ) - return get_event_loop().run_until_complete(coroutine) - - def generate_summary_log_api_v1_test_run_executions__id__performance_summary_post( - self, id: int, project_id: int - ) -> dict[str, Any]: - """ - Generate Summary Log - """ - coroutine = self._build_for_generate_summary_log_api_v1_test_run_executions__id__performance_summary_post( - id=id, project_id=project_id - ) + coroutine = self._build_for_import_test_run_execution_api_v1_test_run_executions_import_post(body=body, project_id=project_id) return get_event_loop().run_until_complete(coroutine) diff --git a/th_cli/api_lib_autogen/api/utils_api.py b/th_cli/api_lib_autogen/api/utils_api.py index 7bb9170..7cf9cc1 100644 --- a/th_cli/api_lib_autogen/api/utils_api.py +++ b/th_cli/api_lib_autogen/api/utils_api.py @@ -15,14 +15,13 @@ # # flake8: noqa E501 from asyncio import get_event_loop -from typing import IO, TYPE_CHECKING, Any, Coroutine +from typing import Coroutine, IO, TYPE_CHECKING, Any from th_cli.api_lib_autogen import models as m if TYPE_CHECKING: from th_cli.api_lib_autogen.api_client import ApiClient - class _UtilsApi: def __init__(self, api_client: "ApiClient"): self.api_client = api_client @@ -33,7 +32,12 @@ def _build_for_test_email_api_v1_utils_test_email__post(self, email_to: str) -> """ query_params = {"email_to": str(email_to)} - return self.api_client.request(type_=m.Msg, method="POST", url="/api/v1/utils/test-email/", params=query_params) + return self.api_client.request( + type_=m.Msg, + method="POST", + url="/api/v1/utils/test-email/", + params=query_params + ) class AsyncUtilsApi(_UtilsApi): diff --git a/th_cli/api_lib_autogen/api/version_api.py b/th_cli/api_lib_autogen/api/version_api.py index 2937dc2..4037ce4 100644 --- a/th_cli/api_lib_autogen/api/version_api.py +++ b/th_cli/api_lib_autogen/api/version_api.py @@ -15,25 +15,26 @@ # # flake8: noqa E501 from asyncio import get_event_loop -from typing import IO, TYPE_CHECKING, Any, Coroutine +from typing import Coroutine, IO, TYPE_CHECKING, Any from th_cli.api_lib_autogen import models as m if TYPE_CHECKING: from th_cli.api_lib_autogen.api_client import ApiClient - class _VersionApi: def __init__(self, api_client: "ApiClient"): self.api_client = api_client - def _build_for_get_test_harness_backend_version_api_v1_version_get( - self, - ) -> Coroutine[Any, Any, m.TestHarnessBackendVersion]: + def _build_for_get_test_harness_backend_version_api_v1_version_get(self) -> Coroutine[Any, Any, m.TestHarnessBackendVersion]: """ Get Test Harness Backend Version """ - return self.api_client.request(type_=m.TestHarnessBackendVersion, method="GET", url="/api/v1/version") + return self.api_client.request( + type_=m.TestHarnessBackendVersion, + method="GET", + url="/api/v1/version" + ) class AsyncVersionApi(_VersionApi): diff --git a/th_cli/api_lib_autogen/api_client.py b/th_cli/api_lib_autogen/api_client.py index 843482c..d117df6 100644 --- a/th_cli/api_lib_autogen/api_client.py +++ b/th_cli/api_lib_autogen/api_client.py @@ -19,14 +19,14 @@ from httpx import AsyncClient, Request, Response from pydantic import TypeAdapter -from th_cli.api_lib_autogen.api.devices_api import AsyncDevicesApi, SyncDevicesApi -from th_cli.api_lib_autogen.api.operators_api import AsyncOperatorsApi, SyncOperatorsApi -from th_cli.api_lib_autogen.api.projects_api import AsyncProjectsApi, SyncProjectsApi from th_cli.api_lib_autogen.api.test_collections_api import AsyncTestCollectionsApi, SyncTestCollectionsApi -from th_cli.api_lib_autogen.api.test_run_configs_api import AsyncTestRunConfigsApi, SyncTestRunConfigsApi +from th_cli.api_lib_autogen.api.projects_api import AsyncProjectsApi, SyncProjectsApi +from th_cli.api_lib_autogen.api.operators_api import AsyncOperatorsApi, SyncOperatorsApi from th_cli.api_lib_autogen.api.test_run_executions_api import AsyncTestRunExecutionsApi, SyncTestRunExecutionsApi -from th_cli.api_lib_autogen.api.utils_api import AsyncUtilsApi, SyncUtilsApi +from th_cli.api_lib_autogen.api.test_run_configs_api import AsyncTestRunConfigsApi, SyncTestRunConfigsApi from th_cli.api_lib_autogen.api.version_api import AsyncVersionApi, SyncVersionApi +from th_cli.api_lib_autogen.api.utils_api import AsyncUtilsApi, SyncUtilsApi +from th_cli.api_lib_autogen.api.devices_api import AsyncDevicesApi, SyncDevicesApi from th_cli.api_lib_autogen.exceptions import ResponseHandlingException, UnexpectedResponse ClientT = TypeVar("ClientT", bound="ApiClient") @@ -80,14 +80,12 @@ def close(self) -> None: @overload async def request( self, *, type_: Type[T], method: str, url: str, path_params: dict[str, Any] | None = None, **kwargs: Any - ) -> T: - ... + ) -> T: ... @overload async def request( self, *, type_: None, method: str, url: str, path_params: dict[str, Any] | None = None, **kwargs: Any - ) -> None: - ... + ) -> None: ... async def request( self, *, type_: Any, method: str, url: str, path_params: dict[str, Any] | None = None, **kwargs: Any @@ -99,12 +97,10 @@ async def request( return await self.send(request, type_) @overload - def request_sync(self, *, type_: Type[T], **kwargs: Any) -> T: - ... + def request_sync(self, *, type_: Type[T], **kwargs: Any) -> T: ... @overload - def request_sync(self, *, type_: None, **kwargs: Any) -> None: - ... + def request_sync(self, *, type_: None, **kwargs: Any) -> None: ... def request_sync(self, *, type_: Any, **kwargs: Any) -> Any: """ @@ -114,13 +110,15 @@ def request_sync(self, *, type_: Any, **kwargs: Any) -> Any: async def send(self, request: Request, type_: Type[T]) -> T | str: response = await self.middleware(request, self.send_inner) - if response.status_code in [200, 201]: + if response.status_code in [200, 201, 204]: try: # Use Pydantic v2 TypeAdapter for validation + if type_ is None or response.status_code == 204: + return None adapter = TypeAdapter(type_) if type_ == bytes: return adapter.validate_python(response.content) - return adapter.validate_python(response.json()) if type_ else response.text + return adapter.validate_python(response.json()) except Exception as e: raise ResponseHandlingException(e) raise UnexpectedResponse.for_response(response) diff --git a/th_cli/api_lib_autogen/exceptions.py b/th_cli/api_lib_autogen/exceptions.py index ca3e7d7..7e5f220 100644 --- a/th_cli/api_lib_autogen/exceptions.py +++ b/th_cli/api_lib_autogen/exceptions.py @@ -20,7 +20,6 @@ class ApiException(Exception): """Base exception for API client errors.""" - pass diff --git a/th_cli/api_lib_autogen/models.py b/th_cli/api_lib_autogen/models.py index 351767b..0a5dd3e 100644 --- a/th_cli/api_lib_autogen/models.py +++ b/th_cli/api_lib_autogen/models.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: -# filename: openapi.json -# timestamp: 2026-04-08T20:07:43+00:00 +# filename: tmpxwopvtxa.json +# timestamp: 2026-07-07T18:20:10+00:00 from __future__ import annotations @@ -12,11 +12,11 @@ class ImportFile(RootModel[bytes]): - root: Annotated[bytes, Field(title="Import File")] + root: Annotated[bytes, Field(title='Import File')] class BodyImportTestRunExecutionApiV1TestRunExecutionsImportPost(BaseModel): - import_file: Annotated[bytes, Field(title="Import File")] + import_file: Annotated[bytes, Field(title='Import File')] class BodyImportprojectConfigApiV1ProjectsImportPost(BodyImportTestRunExecutionApiV1TestRunExecutionsImportPost): @@ -24,11 +24,11 @@ class BodyImportprojectConfigApiV1ProjectsImportPost(BodyImportTestRunExecutionA class File(RootModel[bytes]): - root: Annotated[bytes, Field(title="File")] + root: Annotated[bytes, Field(title='File')] class BodyUploadFileApiV1TestRunExecutionsFileUploadPost(BaseModel): - file: Annotated[bytes, Field(title="File")] + file: Annotated[bytes, Field(title='File')] class BodyUploadPicsApiV1ProjectsIdUploadPicsPut(BodyUploadFileApiV1TestRunExecutionsFileUploadPost): @@ -36,30 +36,30 @@ class BodyUploadPicsApiV1ProjectsIdUploadPicsPut(BodyUploadFileApiV1TestRunExecu class ChipServerInfo(BaseModel): - node_id: Annotated[int, Field(title="Node Id")] - node_id_hex: Annotated[str, Field(title="Node Id Hex")] - manual_pairing_code: Annotated[str | None, Field(title="Manual Pairing Code")] = None + node_id: Annotated[int, Field(title='Node Id')] + node_id_hex: Annotated[str, Field(title='Node Id Hex')] + manual_pairing_code: Annotated[str | None, Field(title='Manual Pairing Code')] = None class Msg(BaseModel): - msg: Annotated[str, Field(title="Msg")] + msg: Annotated[str, Field(title='Msg')] class Name(RootModel[str]): - root: Annotated[str, Field(title="Name")] + root: Annotated[str, Field(title='Name')] class Id(RootModel[int]): - root: Annotated[int, Field(title="Id")] + root: Annotated[int, Field(title='Id')] class Operator(BaseModel): - name: Annotated[str, Field(title="Name")] - id: Annotated[int, Field(title="Id")] + name: Annotated[str, Field(title='Name')] + id: Annotated[int, Field(title='Id')] class OperatorCreate(BaseModel): - name: Annotated[str, Field(title="Name")] + name: Annotated[str, Field(title='Name')] class OperatorToExport(OperatorCreate): @@ -67,99 +67,99 @@ class OperatorToExport(OperatorCreate): class OperatorUpdate(BaseModel): - name: Annotated[str | None, Field(title="Name")] = None + name: Annotated[str | None, Field(title='Name')] = None class PICSApplicableTestCases(BaseModel): - test_cases: Annotated[list[str], Field(title="Test Cases")] + test_cases: Annotated[list[str], Field(title='Test Cases')] class PICSItem(BaseModel): - number: Annotated[str, Field(title="Number")] - enabled: Annotated[bool, Field(title="Enabled")] + number: Annotated[str, Field(title='Number')] + enabled: Annotated[bool, Field(title='Enabled')] class PublicId(RootModel[str]): - root: Annotated[str, Field(title="Public Id")] + root: Annotated[str, Field(title='Public Id')] class Title(RootModel[str]): - root: Annotated[str, Field(title="Title")] + root: Annotated[str, Field(title='Title')] class Description(RootModel[str]): - root: Annotated[str, Field(title="Description")] + root: Annotated[str, Field(title='Description')] class Version(RootModel[str]): - root: Annotated[str, Field(title="Version")] + root: Annotated[str, Field(title='Version')] class SourceHash(RootModel[str]): - root: Annotated[str, Field(title="Source Hash")] + root: Annotated[str, Field(title='Source Hash')] class Mandatory(RootModel[bool]): - root: Annotated[bool, Field(title="Mandatory")] = False + root: Annotated[bool, Field(title='Mandatory')] = False class TestCaseMetadata(BaseModel): - public_id: Annotated[str, Field(title="Public Id")] - title: Annotated[str, Field(title="Title")] - description: Annotated[str, Field(title="Description")] - version: Annotated[str, Field(title="Version")] - source_hash: Annotated[str, Field(title="Source Hash")] - mandatory: Annotated[bool | None, Field(title="Mandatory")] = False - id: Annotated[int, Field(title="Id")] + public_id: Annotated[str, Field(title='Public Id')] + title: Annotated[str, Field(title='Title')] + description: Annotated[str, Field(title='Description')] + version: Annotated[str, Field(title='Version')] + source_hash: Annotated[str, Field(title='Source Hash')] + mandatory: Annotated[bool | None, Field(title='Mandatory')] = False + id: Annotated[int, Field(title='Id')] class TestCaseMetadataBase(BaseModel): - public_id: Annotated[str, Field(title="Public Id")] - title: Annotated[str, Field(title="Title")] - description: Annotated[str, Field(title="Description")] - version: Annotated[str, Field(title="Version")] - source_hash: Annotated[str, Field(title="Source Hash")] - mandatory: Annotated[bool | None, Field(title="Mandatory")] = False + public_id: Annotated[str, Field(title='Public Id')] + title: Annotated[str, Field(title='Title')] + description: Annotated[str, Field(title='Description')] + version: Annotated[str, Field(title='Version')] + source_hash: Annotated[str, Field(title='Source Hash')] + mandatory: Annotated[bool | None, Field(title='Mandatory')] = False class TestEnvironmentConfig(BaseModel): - test_parameters: Annotated[dict[str, Any] | None, Field(title="Test Parameters")] = None + test_parameters: Annotated[dict[str, Any] | None, Field(title='Test Parameters')] = None class TestHarnessBackendVersion(BaseModel): - version: Annotated[str, Field(title="Version")] - sha: Annotated[str, Field(title="Sha")] - sdk_sha: Annotated[str, Field(title="Sdk Sha")] - sdk_docker_tag: Annotated[str, Field(title="Sdk Docker Tag")] - db_revision: Annotated[str, Field(title="Db Revision")] + version: Annotated[str, Field(title='Version')] + sha: Annotated[str, Field(title='Sha')] + sdk_sha: Annotated[str, Field(title='Sdk Sha')] + sdk_docker_tag: Annotated[str, Field(title='Sdk Docker Tag')] + db_revision: Annotated[str, Field(title='Db Revision')] class TestMetadata(BaseModel): - public_id: Annotated[str, Field(title="Public Id")] - version: Annotated[str, Field(title="Version")] - title: Annotated[str, Field(title="Title")] - description: Annotated[str, Field(title="Description")] - mandatory: Annotated[bool | None, Field(title="Mandatory")] = False + public_id: Annotated[str, Field(title='Public Id')] + version: Annotated[str, Field(title='Version')] + title: Annotated[str, Field(title='Title')] + description: Annotated[str, Field(title='Description')] + mandatory: Annotated[bool | None, Field(title='Mandatory')] = False class TestRunConfig(BaseModel): - name: Annotated[str, Field(title="Name")] - dut_name: Annotated[str, Field(title="Dut Name")] - selected_tests: Annotated[dict[str, dict[str, dict[str, int]]] | None, Field(title="Selected Tests")] = {} - id: Annotated[int, Field(title="Id")] + name: Annotated[str, Field(title='Name')] + dut_name: Annotated[str, Field(title='Dut Name')] + selected_tests: Annotated[dict[str, dict[str, dict[str, int]]] | None, Field(title='Selected Tests')] = {} + id: Annotated[int, Field(title='Id')] class TestRunConfigCreate(BaseModel): - name: Annotated[str, Field(title="Name")] - dut_name: Annotated[str, Field(title="Dut Name")] - selected_tests: Annotated[dict[str, dict[str, dict[str, int]]] | None, Field(title="Selected Tests")] = {} + name: Annotated[str, Field(title='Name')] + dut_name: Annotated[str, Field(title='Dut Name')] + selected_tests: Annotated[dict[str, dict[str, dict[str, int]]] | None, Field(title='Selected Tests')] = {} class TestRunConfigToExport(BaseModel): - name: Annotated[str, Field(title="Name")] - dut_name: Annotated[str, Field(title="Dut Name")] - selected_tests: Annotated[dict[str, dict[str, dict[str, int]]] | None, Field(title="Selected Tests")] = {} - created_at: Annotated[datetime, Field(title="Created At")] + name: Annotated[str, Field(title='Name')] + dut_name: Annotated[str, Field(title='Dut Name')] + selected_tests: Annotated[dict[str, dict[str, dict[str, int]]] | None, Field(title='Selected Tests')] = {} + created_at: Annotated[datetime, Field(title='Created At')] class TestRunConfigUpdate(OperatorCreate): @@ -167,74 +167,74 @@ class TestRunConfigUpdate(OperatorCreate): class TestRunExecutionCreate(BaseModel): - title: Annotated[str, Field(title="Title")] - description: Annotated[str | None, Field(title="Description")] = None - execution_config: Annotated[dict[str, Any] | None, Field(title="Execution Config")] = None - execution_pics: Annotated[dict[str, Any] | None, Field(title="Execution Pics")] = None - certification_mode: Annotated[bool | None, Field(title="Certification Mode")] = False - test_run_config_id: Annotated[int | None, Field(title="Test Run Config Id")] = None - project_id: Annotated[int | None, Field(title="Project Id")] = None - operator_id: Annotated[int | None, Field(title="Operator Id")] = None + title: Annotated[str, Field(title='Title')] + description: Annotated[str | None, Field(title='Description')] = None + execution_config: Annotated[dict[str, Any] | None, Field(title='Execution Config')] = None + execution_pics: Annotated[dict[str, Any] | None, Field(title='Execution Pics')] = None + certification_mode: Annotated[bool | None, Field(title='Certification Mode')] = False + test_run_config_id: Annotated[int | None, Field(title='Test Run Config Id')] = None + project_id: Annotated[int | None, Field(title='Project Id')] = None + operator_id: Annotated[int | None, Field(title='Operator Id')] = None class TestRunExecutionStats(BaseModel): - test_case_count: Annotated[int | None, Field(title="Test Case Count")] = 0 - states: Annotated[dict[str, int] | None, Field(title="States")] = {} + test_case_count: Annotated[int | None, Field(title='Test Case Count')] = 0 + states: Annotated[dict[str, int] | None, Field(title='States')] = {} class TestRunLogEntry(BaseModel): - level: Annotated[str, Field(title="Level")] - timestamp: Annotated[float, Field(title="Timestamp")] - message: Annotated[str, Field(title="Message")] - test_suite_execution_index: Annotated[int | None, Field(title="Test Suite Execution Index")] = None - test_case_execution_index: Annotated[int | None, Field(title="Test Case Execution Index")] = None - test_step_execution_index: Annotated[int | None, Field(title="Test Step Execution Index")] = None + level: Annotated[str, Field(title='Level')] + timestamp: Annotated[float, Field(title='Timestamp')] + message: Annotated[str, Field(title='Message')] + test_suite_execution_index: Annotated[int | None, Field(title='Test Suite Execution Index')] = None + test_case_execution_index: Annotated[int | None, Field(title='Test Case Execution Index')] = None + test_step_execution_index: Annotated[int | None, Field(title='Test Step Execution Index')] = None class TestRunnerState(Enum): - idle = "idle" - loading = "loading" - ready = "ready" - running = "running" + idle = 'idle' + loading = 'loading' + ready = 'ready' + running = 'running' class TestRunnerStatus(BaseModel): state: TestRunnerState - test_run_execution_id: Annotated[int | None, Field(title="Test Run Execution Id")] = None + test_run_execution_id: Annotated[int | None, Field(title='Test Run Execution Id')] = None class TestStateEnum(Enum): - pending = "pending" - executing = "executing" - pending_actuation = "pending_actuation" - passed = "passed" - failed = "failed" - error = "error" - not_applicable = "not_applicable" - cancelled = "cancelled" + pending = 'pending' + executing = 'executing' + pending_actuation = 'pending_actuation' + passed = 'passed' + failed = 'failed' + error = 'error' + not_applicable = 'not_applicable' + cancelled = 'cancelled' class TestStepExecution(BaseModel): state: TestStateEnum - title: Annotated[str, Field(title="Title")] - execution_index: Annotated[int, Field(title="Execution Index")] - id: Annotated[int, Field(title="Id")] - test_case_execution_id: Annotated[int, Field(title="Test Case Execution Id")] - started_at: Annotated[datetime | None, Field(title="Started At")] = None - completed_at: Annotated[datetime | None, Field(title="Completed At")] = None - errors: Annotated[list[str] | None, Field(title="Errors")] = None - failures: Annotated[list[str] | None, Field(title="Failures")] = None + title: Annotated[str, Field(title='Title')] + execution_index: Annotated[int, Field(title='Execution Index')] + id: Annotated[int, Field(title='Id')] + test_case_execution_id: Annotated[int, Field(title='Test Case Execution Id')] + started_at: Annotated[datetime | None, Field(title='Started At')] = None + completed_at: Annotated[datetime | None, Field(title='Completed At')] = None + errors: Annotated[list[str] | None, Field(title='Errors')] = None + failures: Annotated[list[str] | None, Field(title='Failures')] = None class TestStepExecutionToExport(BaseModel): state: TestStateEnum - title: Annotated[str, Field(title="Title")] - execution_index: Annotated[int, Field(title="Execution Index")] - started_at: Annotated[datetime | None, Field(title="Started At")] = None - completed_at: Annotated[datetime | None, Field(title="Completed At")] = None - errors: Annotated[list[str] | None, Field(title="Errors")] = None - failures: Annotated[list[str] | None, Field(title="Failures")] = None - created_at: Annotated[datetime, Field(title="Created At")] + title: Annotated[str, Field(title='Title')] + execution_index: Annotated[int, Field(title='Execution Index')] + started_at: Annotated[datetime | None, Field(title='Started At')] = None + completed_at: Annotated[datetime | None, Field(title='Completed At')] = None + errors: Annotated[list[str] | None, Field(title='Errors')] = None + failures: Annotated[list[str] | None, Field(title='Failures')] = None + created_at: Annotated[datetime, Field(title='Created At')] class TestSuiteMetadata(TestCaseMetadata): @@ -246,31 +246,31 @@ class TestSuiteMetadataBase(TestCaseMetadataBase): class ValidationError(BaseModel): - loc: Annotated[list[str | int], Field(title="Location")] - msg: Annotated[str, Field(title="Message")] - type: Annotated[str, Field(title="Error Type")] + loc: Annotated[list[str | int], Field(title='Location')] + msg: Annotated[str, Field(title='Message')] + type: Annotated[str, Field(title='Error Type')] class BodyCreateCliTestRunExecutionApiV1TestRunExecutionsCliPost(BaseModel): test_run_execution_in: TestRunExecutionCreate - selected_tests: Annotated[dict[str, dict[str, dict[str, int]]], Field(title="Selected Tests")] - execution_config: Annotated[dict[str, Any] | None, Field(title="Execution Config")] = None - execution_pics: Annotated[dict[str, Any] | None, Field(title="Execution Pics")] = None + selected_tests: Annotated[dict[str, dict[str, dict[str, int]]], Field(title='Selected Tests')] + execution_config: Annotated[dict[str, Any] | None, Field(title='Execution Config')] = None + execution_pics: Annotated[dict[str, Any] | None, Field(title='Execution Pics')] = None class BodyCreateTestRunExecutionApiV1TestRunExecutionsPost(BaseModel): test_run_execution_in: TestRunExecutionCreate - selected_tests: Annotated[dict[str, dict[str, dict[str, int]]], Field(title="Selected Tests")] + selected_tests: Annotated[dict[str, dict[str, dict[str, int]]], Field(title='Selected Tests')] class HTTPValidationError(BaseModel): - detail: Annotated[list[ValidationError] | None, Field(title="Detail")] = None + detail: Annotated[list[ValidationError] | None, Field(title='Detail')] = None class PICSCluster(BaseModel): - name: Annotated[str, Field(title="Name")] + name: Annotated[str, Field(title='Name')] items: Annotated[ - dict[str, PICSItem] | None, Field(default_factory=lambda: PICSItem.model_validate({}), title="Items") + dict[str, PICSItem] | None, Field(default_factory=lambda: PICSItem.model_validate({}), title='Items') ] @@ -280,191 +280,191 @@ class TestCase(BaseModel): class TestCaseExecution(BaseModel): state: TestStateEnum - public_id: Annotated[str, Field(title="Public Id")] - execution_index: Annotated[int, Field(title="Execution Index")] - id: Annotated[int, Field(title="Id")] - test_suite_execution_id: Annotated[int, Field(title="Test Suite Execution Id")] - test_case_metadata_id: Annotated[int, Field(title="Test Case Metadata Id")] - started_at: Annotated[datetime | None, Field(title="Started At")] = None - completed_at: Annotated[datetime | None, Field(title="Completed At")] = None - errors: Annotated[list[str] | None, Field(title="Errors")] = None + public_id: Annotated[str, Field(title='Public Id')] + execution_index: Annotated[int, Field(title='Execution Index')] + id: Annotated[int, Field(title='Id')] + test_suite_execution_id: Annotated[int, Field(title='Test Suite Execution Id')] + test_case_metadata_id: Annotated[int, Field(title='Test Case Metadata Id')] + started_at: Annotated[datetime | None, Field(title='Started At')] = None + completed_at: Annotated[datetime | None, Field(title='Completed At')] = None + errors: Annotated[list[str] | None, Field(title='Errors')] = None test_case_metadata: TestCaseMetadata - test_step_executions: Annotated[list[TestStepExecution], Field(title="Test Step Executions")] + test_step_executions: Annotated[list[TestStepExecution], Field(title='Test Step Executions')] class TestCaseExecutionToExport(BaseModel): state: TestStateEnum - public_id: Annotated[str, Field(title="Public Id")] - execution_index: Annotated[int, Field(title="Execution Index")] - started_at: Annotated[datetime | None, Field(title="Started At")] = None - completed_at: Annotated[datetime | None, Field(title="Completed At")] = None - errors: Annotated[list[str] | None, Field(title="Errors")] = None + public_id: Annotated[str, Field(title='Public Id')] + execution_index: Annotated[int, Field(title='Execution Index')] + started_at: Annotated[datetime | None, Field(title='Started At')] = None + completed_at: Annotated[datetime | None, Field(title='Completed At')] = None + errors: Annotated[list[str] | None, Field(title='Errors')] = None test_case_metadata: TestCaseMetadataBase - test_step_executions: Annotated[list[TestStepExecutionToExport], Field(title="Test Step Executions")] - created_at: Annotated[datetime, Field(title="Created At")] + test_step_executions: Annotated[list[TestStepExecutionToExport], Field(title='Test Step Executions')] + created_at: Annotated[datetime, Field(title='Created At')] class TestRunExecution(BaseModel): - title: Annotated[str, Field(title="Title")] - description: Annotated[str | None, Field(title="Description")] = None - execution_config: Annotated[dict[str, Any] | None, Field(title="Execution Config")] = None - execution_pics: Annotated[dict[str, Any] | None, Field(title="Execution Pics")] = None - certification_mode: Annotated[bool | None, Field(title="Certification Mode")] = False - test_run_config_id: Annotated[int | None, Field(title="Test Run Config Id")] = None - project_id: Annotated[int | None, Field(title="Project Id")] = None - id: Annotated[int, Field(title="Id")] + title: Annotated[str, Field(title='Title')] + description: Annotated[str | None, Field(title='Description')] = None + execution_config: Annotated[dict[str, Any] | None, Field(title='Execution Config')] = None + execution_pics: Annotated[dict[str, Any] | None, Field(title='Execution Pics')] = None + certification_mode: Annotated[bool | None, Field(title='Certification Mode')] = False + test_run_config_id: Annotated[int | None, Field(title='Test Run Config Id')] = None + project_id: Annotated[int | None, Field(title='Project Id')] = None + id: Annotated[int, Field(title='Id')] state: TestStateEnum - started_at: Annotated[datetime | None, Field(title="Started At")] = None - completed_at: Annotated[datetime | None, Field(title="Completed At")] = None - imported_at: Annotated[datetime | None, Field(title="Imported At")] = None - archived_at: Annotated[datetime | None, Field(title="Archived At")] = None + started_at: Annotated[datetime | None, Field(title='Started At')] = None + completed_at: Annotated[datetime | None, Field(title='Completed At')] = None + imported_at: Annotated[datetime | None, Field(title='Imported At')] = None + archived_at: Annotated[datetime | None, Field(title='Archived At')] = None operator: Operator | None = None class TestRunExecutionInDBBase(BaseModel): - title: Annotated[str, Field(title="Title")] - description: Annotated[str | None, Field(title="Description")] = None - execution_config: Annotated[dict[str, Any] | None, Field(title="Execution Config")] = None - execution_pics: Annotated[dict[str, Any] | None, Field(title="Execution Pics")] = None - certification_mode: Annotated[bool | None, Field(title="Certification Mode")] = False - test_run_config_id: Annotated[int | None, Field(title="Test Run Config Id")] = None - project_id: Annotated[int | None, Field(title="Project Id")] = None - id: Annotated[int, Field(title="Id")] + title: Annotated[str, Field(title='Title')] + description: Annotated[str | None, Field(title='Description')] = None + execution_config: Annotated[dict[str, Any] | None, Field(title='Execution Config')] = None + execution_pics: Annotated[dict[str, Any] | None, Field(title='Execution Pics')] = None + certification_mode: Annotated[bool | None, Field(title='Certification Mode')] = False + test_run_config_id: Annotated[int | None, Field(title='Test Run Config Id')] = None + project_id: Annotated[int | None, Field(title='Project Id')] = None + id: Annotated[int, Field(title='Id')] state: TestStateEnum - started_at: Annotated[datetime | None, Field(title="Started At")] = None - completed_at: Annotated[datetime | None, Field(title="Completed At")] = None - imported_at: Annotated[datetime | None, Field(title="Imported At")] = None - archived_at: Annotated[datetime | None, Field(title="Archived At")] = None + started_at: Annotated[datetime | None, Field(title='Started At')] = None + completed_at: Annotated[datetime | None, Field(title='Completed At')] = None + imported_at: Annotated[datetime | None, Field(title='Imported At')] = None + archived_at: Annotated[datetime | None, Field(title='Archived At')] = None class TestRunExecutionWithStats(BaseModel): - title: Annotated[str, Field(title="Title")] - description: Annotated[str | None, Field(title="Description")] = None - execution_config: Annotated[dict[str, Any] | None, Field(title="Execution Config")] = None - execution_pics: Annotated[dict[str, Any] | None, Field(title="Execution Pics")] = None - certification_mode: Annotated[bool | None, Field(title="Certification Mode")] = False - test_run_config_id: Annotated[int | None, Field(title="Test Run Config Id")] = None - project_id: Annotated[int | None, Field(title="Project Id")] = None - id: Annotated[int, Field(title="Id")] + title: Annotated[str, Field(title='Title')] + description: Annotated[str | None, Field(title='Description')] = None + execution_config: Annotated[dict[str, Any] | None, Field(title='Execution Config')] = None + execution_pics: Annotated[dict[str, Any] | None, Field(title='Execution Pics')] = None + certification_mode: Annotated[bool | None, Field(title='Certification Mode')] = False + test_run_config_id: Annotated[int | None, Field(title='Test Run Config Id')] = None + project_id: Annotated[int | None, Field(title='Project Id')] = None + id: Annotated[int, Field(title='Id')] state: TestStateEnum - started_at: Annotated[datetime | None, Field(title="Started At")] = None - completed_at: Annotated[datetime | None, Field(title="Completed At")] = None - imported_at: Annotated[datetime | None, Field(title="Imported At")] = None - archived_at: Annotated[datetime | None, Field(title="Archived At")] = None + started_at: Annotated[datetime | None, Field(title='Started At')] = None + completed_at: Annotated[datetime | None, Field(title='Completed At')] = None + imported_at: Annotated[datetime | None, Field(title='Imported At')] = None + archived_at: Annotated[datetime | None, Field(title='Archived At')] = None operator: Operator | None = None test_case_stats: TestRunExecutionStats class TestSuite(BaseModel): metadata: TestMetadata - test_cases: Annotated[dict[str, TestCase], Field(title="Test Cases")] + test_cases: Annotated[dict[str, TestCase], Field(title='Test Cases')] class TestSuiteExecution(BaseModel): state: TestStateEnum - public_id: Annotated[str, Field(title="Public Id")] - execution_index: Annotated[int, Field(title="Execution Index")] - collection_id: Annotated[str, Field(title="Collection Id")] - mandatory: Annotated[bool | None, Field(title="Mandatory")] = False - id: Annotated[int, Field(title="Id")] - test_run_execution_id: Annotated[int, Field(title="Test Run Execution Id")] - test_suite_metadata_id: Annotated[int, Field(title="Test Suite Metadata Id")] - started_at: Annotated[datetime | None, Field(title="Started At")] = None - completed_at: Annotated[datetime | None, Field(title="Completed At")] = None - errors: Annotated[list[str] | None, Field(title="Errors")] = None - test_case_executions: Annotated[list[TestCaseExecution], Field(title="Test Case Executions")] + public_id: Annotated[str, Field(title='Public Id')] + execution_index: Annotated[int, Field(title='Execution Index')] + collection_id: Annotated[str, Field(title='Collection Id')] + mandatory: Annotated[bool | None, Field(title='Mandatory')] = False + id: Annotated[int, Field(title='Id')] + test_run_execution_id: Annotated[int, Field(title='Test Run Execution Id')] + test_suite_metadata_id: Annotated[int, Field(title='Test Suite Metadata Id')] + started_at: Annotated[datetime | None, Field(title='Started At')] = None + completed_at: Annotated[datetime | None, Field(title='Completed At')] = None + errors: Annotated[list[str] | None, Field(title='Errors')] = None + test_case_executions: Annotated[list[TestCaseExecution], Field(title='Test Case Executions')] test_suite_metadata: TestSuiteMetadata class TestSuiteExecutionToExport(BaseModel): state: TestStateEnum - public_id: Annotated[str, Field(title="Public Id")] - execution_index: Annotated[int, Field(title="Execution Index")] - collection_id: Annotated[str, Field(title="Collection Id")] - mandatory: Annotated[bool | None, Field(title="Mandatory")] = False - started_at: Annotated[datetime | None, Field(title="Started At")] = None - completed_at: Annotated[datetime | None, Field(title="Completed At")] = None - errors: Annotated[list[str] | None, Field(title="Errors")] = None - test_case_executions: Annotated[list[TestCaseExecutionToExport], Field(title="Test Case Executions")] + public_id: Annotated[str, Field(title='Public Id')] + execution_index: Annotated[int, Field(title='Execution Index')] + collection_id: Annotated[str, Field(title='Collection Id')] + mandatory: Annotated[bool | None, Field(title='Mandatory')] = False + started_at: Annotated[datetime | None, Field(title='Started At')] = None + completed_at: Annotated[datetime | None, Field(title='Completed At')] = None + errors: Annotated[list[str] | None, Field(title='Errors')] = None + test_case_executions: Annotated[list[TestCaseExecutionToExport], Field(title='Test Case Executions')] test_suite_metadata: TestSuiteMetadataBase - created_at: Annotated[datetime, Field(title="Created At")] + created_at: Annotated[datetime, Field(title='Created At')] class PICS(BaseModel): clusters: Annotated[ - dict[str, PICSCluster] | None, Field(default_factory=lambda: PICSCluster.model_validate({}), title="Clusters") + dict[str, PICSCluster] | None, Field(default_factory=lambda: PICSCluster.model_validate({}), title='Clusters') ] class Project(BaseModel): - name: Annotated[str, Field(title="Name")] - config: Annotated[dict[str, Any] | None, Field(title="Config")] = None - pics: Annotated[PICS | None, Field(default_factory=lambda: PICS.model_validate({"clusters": {}}), title="Pics")] - id: Annotated[int, Field(title="Id")] - created_at: Annotated[datetime, Field(title="Created At")] - updated_at: Annotated[datetime, Field(title="Updated At")] - archived_at: Annotated[datetime | None, Field(title="Archived At")] = None + name: Annotated[str, Field(title='Name')] + config: Annotated[dict[str, Any] | None, Field(title='Config')] = None + pics: Annotated[PICS | None, Field(default_factory=lambda: PICS.model_validate({'clusters': {}}), title='Pics')] + id: Annotated[int, Field(title='Id')] + created_at: Annotated[datetime, Field(title='Created At')] + updated_at: Annotated[datetime, Field(title='Updated At')] + archived_at: Annotated[datetime | None, Field(title='Archived At')] = None class ProjectCreate(BaseModel): - name: Annotated[str, Field(title="Name")] - config: Annotated[dict[str, Any] | None, Field(title="Config")] = None - pics: Annotated[PICS | None, Field(default_factory=lambda: PICS.model_validate({"clusters": {}}), title="Pics")] + name: Annotated[str, Field(title='Name')] + config: Annotated[dict[str, Any] | None, Field(title='Config')] = None + pics: Annotated[PICS | None, Field(default_factory=lambda: PICS.model_validate({'clusters': {}}), title='Pics')] class ProjectUpdate(BaseModel): - name: Annotated[str | None, Field(title="Name")] = None - config: Annotated[dict[str, Any] | None, Field(title="Config")] = None + name: Annotated[str | None, Field(title='Name')] = None + config: Annotated[dict[str, Any] | None, Field(title='Config')] = None pics: PICS | None = None class TestCollection(BaseModel): - name: Annotated[str, Field(title="Name")] - path: Annotated[str, Field(title="Path")] - test_suites: Annotated[dict[str, TestSuite], Field(title="Test Suites")] + name: Annotated[str, Field(title='Name')] + path: Annotated[str, Field(title='Path')] + test_suites: Annotated[dict[str, TestSuite], Field(title='Test Suites')] class TestCollections(BaseModel): - test_collections: Annotated[dict[str, TestCollection], Field(title="Test Collections")] + test_collections: Annotated[dict[str, TestCollection], Field(title='Test Collections')] class TestRunExecutionToExport(BaseModel): - title: Annotated[str, Field(title="Title")] - description: Annotated[str | None, Field(title="Description")] = None - execution_config: Annotated[dict[str, Any] | None, Field(title="Execution Config")] = None - execution_pics: Annotated[dict[str, Any] | None, Field(title="Execution Pics")] = None - certification_mode: Annotated[bool | None, Field(title="Certification Mode")] = False + title: Annotated[str, Field(title='Title')] + description: Annotated[str | None, Field(title='Description')] = None + execution_config: Annotated[dict[str, Any] | None, Field(title='Execution Config')] = None + execution_pics: Annotated[dict[str, Any] | None, Field(title='Execution Pics')] = None + certification_mode: Annotated[bool | None, Field(title='Certification Mode')] = False state: TestStateEnum - started_at: Annotated[datetime | None, Field(title="Started At")] = None - completed_at: Annotated[datetime | None, Field(title="Completed At")] = None - archived_at: Annotated[datetime | None, Field(title="Archived At")] = None + started_at: Annotated[datetime | None, Field(title='Started At')] = None + completed_at: Annotated[datetime | None, Field(title='Completed At')] = None + archived_at: Annotated[datetime | None, Field(title='Archived At')] = None test_suite_executions: Annotated[ - list[TestSuiteExecutionToExport] | None, Field(title="Test Suite Executions") + list[TestSuiteExecutionToExport] | None, Field(title='Test Suite Executions') ] = None - created_at: Annotated[datetime, Field(title="Created At")] - log: Annotated[list[TestRunLogEntry], Field(title="Log")] + created_at: Annotated[datetime, Field(title='Created At')] + log: Annotated[list[TestRunLogEntry], Field(title='Log')] operator: OperatorToExport | None = None test_run_config: TestRunConfigToExport | None = None class TestRunExecutionWithChildren(BaseModel): - title: Annotated[str, Field(title="Title")] - description: Annotated[str | None, Field(title="Description")] = None - execution_config: Annotated[dict[str, Any] | None, Field(title="Execution Config")] = None - execution_pics: Annotated[dict[str, Any] | None, Field(title="Execution Pics")] = None - certification_mode: Annotated[bool | None, Field(title="Certification Mode")] = False - test_run_config_id: Annotated[int | None, Field(title="Test Run Config Id")] = None - project_id: Annotated[int | None, Field(title="Project Id")] = None - id: Annotated[int, Field(title="Id")] + title: Annotated[str, Field(title='Title')] + description: Annotated[str | None, Field(title='Description')] = None + execution_config: Annotated[dict[str, Any] | None, Field(title='Execution Config')] = None + execution_pics: Annotated[dict[str, Any] | None, Field(title='Execution Pics')] = None + certification_mode: Annotated[bool | None, Field(title='Certification Mode')] = False + test_run_config_id: Annotated[int | None, Field(title='Test Run Config Id')] = None + project_id: Annotated[int | None, Field(title='Project Id')] = None + id: Annotated[int, Field(title='Id')] state: TestStateEnum - started_at: Annotated[datetime | None, Field(title="Started At")] = None - completed_at: Annotated[datetime | None, Field(title="Completed At")] = None - imported_at: Annotated[datetime | None, Field(title="Imported At")] = None - archived_at: Annotated[datetime | None, Field(title="Archived At")] = None + started_at: Annotated[datetime | None, Field(title='Started At')] = None + completed_at: Annotated[datetime | None, Field(title='Completed At')] = None + imported_at: Annotated[datetime | None, Field(title='Imported At')] = None + archived_at: Annotated[datetime | None, Field(title='Archived At')] = None operator: Operator | None = None - test_suite_executions: Annotated[list[TestSuiteExecution] | None, Field(title="Test Suite Executions")] = None + test_suite_executions: Annotated[list[TestSuiteExecution] | None, Field(title='Test Suite Executions')] = None class ExportedTestRunExecution(BaseModel): - db_revision: Annotated[str, Field(title="Db Revision")] + db_revision: Annotated[str, Field(title='Db Revision')] test_run_execution: TestRunExecutionToExport From a5296328c7c477c523ad24af4f0b866eab630cef Mon Sep 17 00:00:00 2001 From: Romulo Quidute Filho Date: Tue, 7 Jul 2026 15:56:47 -0300 Subject: [PATCH 05/10] Notify user that project logs download may take a while Downloading logs builds the whole zip server-side before streaming, which can take noticeable time for projects with many or large test run executions. Print a heads-up before the request so the CLI doesn't appear to hang. --- th_cli/commands/project.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/th_cli/commands/project.py b/th_cli/commands/project.py index 79ca666..ec64527 100644 --- a/th_cli/commands/project.py +++ b/th_cli/commands/project.py @@ -506,6 +506,10 @@ def _download_project_logs( sync_apis: SyncApis, id: int, grouped: bool, output_file: str | None ) -> None: """Download all logs for a project as a single zip archive""" + click.echo( + f"Downloading logs for project {id}... this may take a while for " + "projects with many or large test run executions." + ) try: log_bytes: bytes = sync_apis.projects_api.download_project_logs_api_v1_projects__id__logs_get( id=id, grouped=grouped From 83b038b40d8ee714c1f13864854d432e30dcb494 Mon Sep 17 00:00:00 2001 From: Romulo Quidute Filho Date: Tue, 7 Jul 2026 16:19:45 -0300 Subject: [PATCH 06/10] Fix flaky logs test assertion and add coverage for no-executions and progress notice test_logs_falls_back_to_default_filename_when_project_name_lookup_fails asserted file contents after isolated_filesystem had already exited, so it only passed by accident depending on leftover state from prior tests. Move the assertion inside the context. Also add tests for the backend's 404-on-no-executions response (confirms no file is written) and for the pre-download progress notice. --- tests/test_project_commands.py | 49 +++++++++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/tests/test_project_commands.py b/tests/test_project_commands.py index 4537358..d586c0d 100644 --- a/tests/test_project_commands.py +++ b/tests/test_project_commands.py @@ -829,6 +829,49 @@ def test_logs_api_error( assert result.exit_code == 1 assert "404" in result.output + def test_logs_no_executions_returns_error_and_writes_no_file( + self, + cli_runner: CliRunner, + mock_sync_apis: Mock, + temp_dir: Path, + ) -> None: + """A project with no test run executions surfaces the backend's 404 + instead of writing an empty/unusable zip file to disk.""" + mock_sync_apis.projects_api.download_project_logs_api_v1_projects__id__logs_get.side_effect = ( + UnexpectedResponse( + status_code=404, + content=b"Project 11 has no test run executions to download logs for", + ) + ) + + with patch("th_cli.commands.project.SyncApis", return_value=mock_sync_apis): + with cli_runner.isolated_filesystem(temp_dir=temp_dir): + result = cli_runner.invoke(project, ["logs", "--id", "11"]) + + assert result.exit_code == 1 + assert "no test run executions" in result.output + assert list(Path(".").glob("*.zip")) == [] + + def test_logs_prints_progress_notice_before_download( + self, + cli_runner: CliRunner, + mock_sync_apis: Mock, + sample_project: api_models.Project, + temp_dir: Path, + ) -> None: + """A heads-up is printed before the request, since building the zip + server-side can take a while for large projects.""" + mock_sync_apis.projects_api.download_project_logs_api_v1_projects__id__logs_get.return_value = b"log content" + mock_sync_apis.projects_api.read_project_api_v1_projects__id__get.return_value = sample_project + + with patch("th_cli.commands.project.SyncApis", return_value=mock_sync_apis): + with cli_runner.isolated_filesystem(temp_dir=temp_dir): + result = cli_runner.invoke(project, ["logs", "--id", "1"]) + + assert result.exit_code == 0 + assert "Downloading logs for project 1" in result.output + assert "may take a while" in result.output + def test_logs_falls_back_to_default_filename_when_project_name_lookup_fails( self, cli_runner: CliRunner, @@ -845,9 +888,9 @@ def test_logs_falls_back_to_default_filename_when_project_name_lookup_fails( with cli_runner.isolated_filesystem(temp_dir=temp_dir): result = cli_runner.invoke(project, ["logs", "--id", "7"]) - assert result.exit_code == 0 - assert "saved to 'project-7-logs.zip'" in result.output - assert Path("project-7-logs.zip").read_bytes() == b"zip content" + assert result.exit_code == 0 + assert "saved to 'project-7-logs.zip'" in result.output + assert Path("project-7-logs.zip").read_bytes() == b"zip content" def test_logs_help_message(self, cli_runner: CliRunner) -> None: """Test the help message for the logs command.""" From 0ed8070244677ee321a0cd6b39f714e85e067622 Mon Sep 17 00:00:00 2001 From: Romulo Quidute Filho Date: Tue, 7 Jul 2026 19:20:40 +0000 Subject: [PATCH 07/10] Code formating --- th_cli/api_lib_autogen/api/__init__.py | 37 +- th_cli/api_lib_autogen/api/devices_api.py | 22 +- th_cli/api_lib_autogen/api/operators_api.py | 55 +-- th_cli/api_lib_autogen/api/projects_api.py | 170 +++---- .../api/test_collections_api.py | 9 +- .../api/test_run_configs_api.py | 60 ++- .../api/test_run_executions_api.py | 354 ++++++++++---- th_cli/api_lib_autogen/api/utils_api.py | 10 +- th_cli/api_lib_autogen/api/version_api.py | 13 +- th_cli/api_lib_autogen/api_client.py | 10 +- th_cli/api_lib_autogen/exceptions.py | 1 + th_cli/api_lib_autogen/models.py | 462 +++++++++--------- th_cli/commands/project.py | 31 +- th_cli/commands/run_tests.py | 12 +- th_cli/test_run/log_stream_handler.py | 57 +-- th_cli/test_run/logging.py | 15 +- th_cli/test_run/logs_http_server.py | 40 +- th_cli/utils.py | 8 +- th_cli/validation.py | 3 +- 19 files changed, 767 insertions(+), 602 deletions(-) diff --git a/th_cli/api_lib_autogen/api/__init__.py b/th_cli/api_lib_autogen/api/__init__.py index 5473f74..dcfc013 100644 --- a/th_cli/api_lib_autogen/api/__init__.py +++ b/th_cli/api_lib_autogen/api/__init__.py @@ -15,13 +15,38 @@ # """API endpoint classes.""" -from th_cli.api_lib_autogen.api.test_collections_api import AsyncTestCollectionsApi, SyncTestCollectionsApi -from th_cli.api_lib_autogen.api.projects_api import AsyncProjectsApi, SyncProjectsApi +from th_cli.api_lib_autogen.api.devices_api import AsyncDevicesApi, SyncDevicesApi from th_cli.api_lib_autogen.api.operators_api import AsyncOperatorsApi, SyncOperatorsApi -from th_cli.api_lib_autogen.api.test_run_executions_api import AsyncTestRunExecutionsApi, SyncTestRunExecutionsApi +from th_cli.api_lib_autogen.api.projects_api import AsyncProjectsApi, SyncProjectsApi +from th_cli.api_lib_autogen.api.test_collections_api import AsyncTestCollectionsApi, SyncTestCollectionsApi from th_cli.api_lib_autogen.api.test_run_configs_api import AsyncTestRunConfigsApi, SyncTestRunConfigsApi -from th_cli.api_lib_autogen.api.version_api import AsyncVersionApi, SyncVersionApi +from th_cli.api_lib_autogen.api.test_run_executions_api import AsyncTestRunExecutionsApi, SyncTestRunExecutionsApi from th_cli.api_lib_autogen.api.utils_api import AsyncUtilsApi, SyncUtilsApi -from th_cli.api_lib_autogen.api.devices_api import AsyncDevicesApi, SyncDevicesApi +from th_cli.api_lib_autogen.api.version_api import AsyncVersionApi, SyncVersionApi -__all__ = ["TestCollectionsApi", "AsyncTestCollectionsApi", "SyncTestCollectionsApi", "ProjectsApi", "AsyncProjectsApi", "SyncProjectsApi", "OperatorsApi", "AsyncOperatorsApi", "SyncOperatorsApi", "TestRunExecutionsApi", "AsyncTestRunExecutionsApi", "SyncTestRunExecutionsApi", "TestRunConfigsApi", "AsyncTestRunConfigsApi", "SyncTestRunConfigsApi", "VersionApi", "AsyncVersionApi", "SyncVersionApi", "UtilsApi", "AsyncUtilsApi", "SyncUtilsApi", "DevicesApi", "AsyncDevicesApi", "SyncDevicesApi"] +__all__ = [ + "TestCollectionsApi", + "AsyncTestCollectionsApi", + "SyncTestCollectionsApi", + "ProjectsApi", + "AsyncProjectsApi", + "SyncProjectsApi", + "OperatorsApi", + "AsyncOperatorsApi", + "SyncOperatorsApi", + "TestRunExecutionsApi", + "AsyncTestRunExecutionsApi", + "SyncTestRunExecutionsApi", + "TestRunConfigsApi", + "AsyncTestRunConfigsApi", + "SyncTestRunConfigsApi", + "VersionApi", + "AsyncVersionApi", + "SyncVersionApi", + "UtilsApi", + "AsyncUtilsApi", + "SyncUtilsApi", + "DevicesApi", + "AsyncDevicesApi", + "SyncDevicesApi", +] diff --git a/th_cli/api_lib_autogen/api/devices_api.py b/th_cli/api_lib_autogen/api/devices_api.py index 74ab1cd..f3e67d4 100644 --- a/th_cli/api_lib_autogen/api/devices_api.py +++ b/th_cli/api_lib_autogen/api/devices_api.py @@ -15,13 +15,14 @@ # # flake8: noqa E501 from asyncio import get_event_loop -from typing import Coroutine, IO, TYPE_CHECKING, Any +from typing import IO, TYPE_CHECKING, Any, Coroutine from th_cli.api_lib_autogen import models as m if TYPE_CHECKING: from th_cli.api_lib_autogen.api_client import ApiClient + class _DevicesApi: def __init__(self, api_client: "ApiClient"): self.api_client = api_client @@ -30,24 +31,17 @@ def _build_for_get_device_configs_api_v1_devices__get(self) -> Coroutine[Any, An """ Get Device Configs """ - return self.api_client.request( - type_=dict[str, Any], - method="GET", - url="/api/v1/devices/" - ) + return self.api_client.request(type_=dict[str, Any], method="GET", url="/api/v1/devices/") - def _build_for_add_device_config_api_v1_devices__put(self, body: dict[str, Any]) -> Coroutine[Any, Any, dict[str, Any]]: + def _build_for_add_device_config_api_v1_devices__put( + self, body: dict[str, Any] + ) -> Coroutine[Any, Any, dict[str, Any]]: """ Add Device Config """ - json_body = body.model_dump(mode='json') if hasattr(body, 'model_dump') else body + json_body = body.model_dump(mode="json") if hasattr(body, "model_dump") else body - return self.api_client.request( - type_=dict[str, Any], - method="PUT", - url="/api/v1/devices/", - json=json_body - ) + return self.api_client.request(type_=dict[str, Any], method="PUT", url="/api/v1/devices/", json=json_body) class AsyncDevicesApi(_DevicesApi): diff --git a/th_cli/api_lib_autogen/api/operators_api.py b/th_cli/api_lib_autogen/api/operators_api.py index eb77ae0..9626d1a 100644 --- a/th_cli/api_lib_autogen/api/operators_api.py +++ b/th_cli/api_lib_autogen/api/operators_api.py @@ -15,18 +15,21 @@ # # flake8: noqa E501 from asyncio import get_event_loop -from typing import Coroutine, IO, TYPE_CHECKING, Any +from typing import IO, TYPE_CHECKING, Any, Coroutine from th_cli.api_lib_autogen import models as m if TYPE_CHECKING: from th_cli.api_lib_autogen.api_client import ApiClient + class _OperatorsApi: def __init__(self, api_client: "ApiClient"): self.api_client = api_client - def _build_for_read_operators_api_v1_operators__get(self, skip: int | None = None, limit: int | None = None) -> Coroutine[Any, Any, list[m.Operator]]: + def _build_for_read_operators_api_v1_operators__get( + self, skip: int | None = None, limit: int | None = None + ) -> Coroutine[Any, Any, list[m.Operator]]: """ Read Operators """ @@ -37,24 +40,18 @@ def _build_for_read_operators_api_v1_operators__get(self, skip: int | None = Non query_params["limit"] = str(limit) return self.api_client.request( - type_=list[m.Operator], - method="GET", - url="/api/v1/operators/", - params=query_params + type_=list[m.Operator], method="GET", url="/api/v1/operators/", params=query_params ) - def _build_for_create_operator_api_v1_operators__post(self, body: m.OperatorCreate) -> Coroutine[Any, Any, m.Operator]: + def _build_for_create_operator_api_v1_operators__post( + self, body: m.OperatorCreate + ) -> Coroutine[Any, Any, m.Operator]: """ Create Operator """ - json_body = body.model_dump(mode='json') if hasattr(body, 'model_dump') else body + json_body = body.model_dump(mode="json") if hasattr(body, "model_dump") else body - return self.api_client.request( - type_=m.Operator, - method="POST", - url="/api/v1/operators/", - json=json_body - ) + return self.api_client.request(type_=m.Operator, method="POST", url="/api/v1/operators/", json=json_body) def _build_for_read_operator_api_v1_operators__id__get(self, id: int) -> Coroutine[Any, Any, m.Operator]: """ @@ -63,26 +60,21 @@ def _build_for_read_operator_api_v1_operators__id__get(self, id: int) -> Corouti path_params = {"id": str(id)} return self.api_client.request( - type_=m.Operator, - method="GET", - url="/api/v1/operators/{id}", - path_params=path_params + type_=m.Operator, method="GET", url="/api/v1/operators/{id}", path_params=path_params ) - def _build_for_update_operator_api_v1_operators__id__put(self, body: m.OperatorUpdate, id: int) -> Coroutine[Any, Any, m.Operator]: + def _build_for_update_operator_api_v1_operators__id__put( + self, body: m.OperatorUpdate, id: int + ) -> Coroutine[Any, Any, m.Operator]: """ Update Operator """ path_params = {"id": str(id)} - json_body = body.model_dump(mode='json') if hasattr(body, 'model_dump') else body + json_body = body.model_dump(mode="json") if hasattr(body, "model_dump") else body return self.api_client.request( - type_=m.Operator, - method="PUT", - url="/api/v1/operators/{id}", - path_params=path_params, - json=json_body + type_=m.Operator, method="PUT", url="/api/v1/operators/{id}", path_params=path_params, json=json_body ) def _build_for_delete_operator_api_v1_operators__id__delete(self, id: int) -> Coroutine[Any, Any, m.Operator]: @@ -92,15 +84,14 @@ def _build_for_delete_operator_api_v1_operators__id__delete(self, id: int) -> Co path_params = {"id": str(id)} return self.api_client.request( - type_=m.Operator, - method="DELETE", - url="/api/v1/operators/{id}", - path_params=path_params + type_=m.Operator, method="DELETE", url="/api/v1/operators/{id}", path_params=path_params ) class AsyncOperatorsApi(_OperatorsApi): - async def read_operators_api_v1_operators__get(self, skip: int | None = None, limit: int | None = None) -> list[m.Operator]: + async def read_operators_api_v1_operators__get( + self, skip: int | None = None, limit: int | None = None + ) -> list[m.Operator]: """ Read Operators """ @@ -132,7 +123,9 @@ async def delete_operator_api_v1_operators__id__delete(self, id: int) -> m.Opera class SyncOperatorsApi(_OperatorsApi): - def read_operators_api_v1_operators__get(self, skip: int | None = None, limit: int | None = None) -> list[m.Operator]: + def read_operators_api_v1_operators__get( + self, skip: int | None = None, limit: int | None = None + ) -> list[m.Operator]: """ Read Operators """ diff --git a/th_cli/api_lib_autogen/api/projects_api.py b/th_cli/api_lib_autogen/api/projects_api.py index 11115d1..8cf761b 100644 --- a/th_cli/api_lib_autogen/api/projects_api.py +++ b/th_cli/api_lib_autogen/api/projects_api.py @@ -15,18 +15,21 @@ # # flake8: noqa E501 from asyncio import get_event_loop -from typing import Coroutine, IO, TYPE_CHECKING, Any +from typing import IO, TYPE_CHECKING, Any, Coroutine from th_cli.api_lib_autogen import models as m if TYPE_CHECKING: from th_cli.api_lib_autogen.api_client import ApiClient + class _ProjectsApi: def __init__(self, api_client: "ApiClient"): self.api_client = api_client - def _build_for_read_projects_api_v1_projects__get(self, archived: bool | None = None, skip: int | None = None, limit: int | None = None) -> Coroutine[Any, Any, list[m.Project]]: + def _build_for_read_projects_api_v1_projects__get( + self, archived: bool | None = None, skip: int | None = None, limit: int | None = None + ) -> Coroutine[Any, Any, list[m.Project]]: """ Read Projects """ @@ -39,34 +42,22 @@ def _build_for_read_projects_api_v1_projects__get(self, archived: bool | None = query_params["limit"] = str(limit) return self.api_client.request( - type_=list[m.Project], - method="GET", - url="/api/v1/projects/", - params=query_params + type_=list[m.Project], method="GET", url="/api/v1/projects/", params=query_params ) def _build_for_create_project_api_v1_projects__post(self, body: m.ProjectCreate) -> Coroutine[Any, Any, m.Project]: """ Create Project """ - json_body = body.model_dump(mode='json') if hasattr(body, 'model_dump') else body + json_body = body.model_dump(mode="json") if hasattr(body, "model_dump") else body - return self.api_client.request( - type_=m.Project, - method="POST", - url="/api/v1/projects/", - json=json_body - ) + return self.api_client.request(type_=m.Project, method="POST", url="/api/v1/projects/", json=json_body) def _build_for_default_config_api_v1_projects_default_config_get(self) -> Coroutine[Any, Any, dict[str, Any]]: """ Default Config """ - return self.api_client.request( - type_=dict[str, Any], - method="GET", - url="/api/v1/projects/default_config" - ) + return self.api_client.request(type_=dict[str, Any], method="GET", url="/api/v1/projects/default_config") def _build_for_read_project_api_v1_projects__id__get(self, id: int) -> Coroutine[Any, Any, m.Project]: """ @@ -75,26 +66,21 @@ def _build_for_read_project_api_v1_projects__id__get(self, id: int) -> Coroutine path_params = {"id": str(id)} return self.api_client.request( - type_=m.Project, - method="GET", - url="/api/v1/projects/{id}", - path_params=path_params + type_=m.Project, method="GET", url="/api/v1/projects/{id}", path_params=path_params ) - def _build_for_update_project_api_v1_projects__id__put(self, body: m.ProjectUpdate, id: int) -> Coroutine[Any, Any, m.Project]: + def _build_for_update_project_api_v1_projects__id__put( + self, body: m.ProjectUpdate, id: int + ) -> Coroutine[Any, Any, m.Project]: """ Update Project """ path_params = {"id": str(id)} - json_body = body.model_dump(mode='json') if hasattr(body, 'model_dump') else body + json_body = body.model_dump(mode="json") if hasattr(body, "model_dump") else body return self.api_client.request( - type_=m.Project, - method="PUT", - url="/api/v1/projects/{id}", - path_params=path_params, - json=json_body + type_=m.Project, method="PUT", url="/api/v1/projects/{id}", path_params=path_params, json=json_body ) def _build_for_delete_project_api_v1_projects__id__delete(self, id: int) -> Coroutine[Any, Any, m.Project]: @@ -104,10 +90,7 @@ def _build_for_delete_project_api_v1_projects__id__delete(self, id: int) -> Coro path_params = {"id": str(id)} return self.api_client.request( - type_=m.Project, - method="DELETE", - url="/api/v1/projects/{id}", - path_params=path_params + type_=m.Project, method="DELETE", url="/api/v1/projects/{id}", path_params=path_params ) def _build_for_archive_project_api_v1_projects__id__archive_post(self, id: int) -> Coroutine[Any, Any, m.Project]: @@ -117,26 +100,24 @@ def _build_for_archive_project_api_v1_projects__id__archive_post(self, id: int) path_params = {"id": str(id)} return self.api_client.request( - type_=m.Project, - method="POST", - url="/api/v1/projects/{id}/archive", - path_params=path_params + type_=m.Project, method="POST", url="/api/v1/projects/{id}/archive", path_params=path_params ) - def _build_for_unarchive_project_api_v1_projects__id__unarchive_post(self, id: int) -> Coroutine[Any, Any, m.Project]: + def _build_for_unarchive_project_api_v1_projects__id__unarchive_post( + self, id: int + ) -> Coroutine[Any, Any, m.Project]: """ Unarchive Project """ path_params = {"id": str(id)} return self.api_client.request( - type_=m.Project, - method="POST", - url="/api/v1/projects/{id}/unarchive", - path_params=path_params + type_=m.Project, method="POST", url="/api/v1/projects/{id}/unarchive", path_params=path_params ) - def _build_for_upload_pics_api_v1_projects__id__upload_pics_put(self, body: m.BodyUploadPicsApiV1ProjectsIdUploadPicsPut, id: int) -> Coroutine[Any, Any, m.Project]: + def _build_for_upload_pics_api_v1_projects__id__upload_pics_put( + self, body: m.BodyUploadPicsApiV1ProjectsIdUploadPicsPut, id: int + ) -> Coroutine[Any, Any, m.Project]: """ Upload Pics """ @@ -148,11 +129,11 @@ def _build_for_upload_pics_api_v1_projects__id__upload_pics_put(self, body: m.Bo # Process body fields to populate files and data dictionaries if body is not None: # Process field: file - if hasattr(body, 'file'): - field_value = getattr(body, 'file') + if hasattr(body, "file"): + field_value = getattr(body, "file") if field_value is not None: # File field - files['file'] = field_value + files["file"] = field_value return self.api_client.request( type_=m.Project, @@ -160,10 +141,12 @@ def _build_for_upload_pics_api_v1_projects__id__upload_pics_put(self, body: m.Bo url="/api/v1/projects/{id}/upload_pics", path_params=path_params, data=data, - files=files + files=files, ) - def _build_for_remove_pics_cluster_type_api_v1_projects__id__pics_cluster_type_delete(self, id: int, cluster_name: str) -> Coroutine[Any, Any, m.Project]: + def _build_for_remove_pics_cluster_type_api_v1_projects__id__pics_cluster_type_delete( + self, id: int, cluster_name: str + ) -> Coroutine[Any, Any, m.Project]: """ Remove Pics Cluster Type """ @@ -176,10 +159,12 @@ def _build_for_remove_pics_cluster_type_api_v1_projects__id__pics_cluster_type_d method="DELETE", url="/api/v1/projects/{id}/pics_cluster_type", path_params=path_params, - params=query_params + params=query_params, ) - def _build_for_applicable_test_cases_api_v1_projects__id__applicable_test_cases_get(self, id: int) -> Coroutine[Any, Any, m.PICSApplicableTestCases]: + def _build_for_applicable_test_cases_api_v1_projects__id__applicable_test_cases_get( + self, id: int + ) -> Coroutine[Any, Any, m.PICSApplicableTestCases]: """ Applicable Test Cases """ @@ -189,23 +174,24 @@ def _build_for_applicable_test_cases_api_v1_projects__id__applicable_test_cases_ type_=m.PICSApplicableTestCases, method="GET", url="/api/v1/projects/{id}/applicable_test_cases", - path_params=path_params + path_params=path_params, ) - def _build_for_export_project_config_api_v1_projects__id__export_get(self, id: int) -> Coroutine[Any, Any, m.ProjectCreate]: + def _build_for_export_project_config_api_v1_projects__id__export_get( + self, id: int + ) -> Coroutine[Any, Any, m.ProjectCreate]: """ Export Project Config """ path_params = {"id": str(id)} return self.api_client.request( - type_=m.ProjectCreate, - method="GET", - url="/api/v1/projects/{id}/export", - path_params=path_params + type_=m.ProjectCreate, method="GET", url="/api/v1/projects/{id}/export", path_params=path_params ) - def _build_for_importproject_config_api_v1_projects_import_post(self, body: m.BodyImportprojectConfigApiV1ProjectsImportPost) -> Coroutine[Any, Any, m.Project]: + def _build_for_importproject_config_api_v1_projects_import_post( + self, body: m.BodyImportprojectConfigApiV1ProjectsImportPost + ) -> Coroutine[Any, Any, m.Project]: """ Importproject Config """ @@ -215,21 +201,19 @@ def _build_for_importproject_config_api_v1_projects_import_post(self, body: m.Bo # Process body fields to populate files and data dictionaries if body is not None: # Process field: import_file - if hasattr(body, 'import_file'): - field_value = getattr(body, 'import_file') + if hasattr(body, "import_file"): + field_value = getattr(body, "import_file") if field_value is not None: # File field - files['import_file'] = field_value + files["import_file"] = field_value return self.api_client.request( - type_=m.Project, - method="POST", - url="/api/v1/projects/import", - data=data, - files=files + type_=m.Project, method="POST", url="/api/v1/projects/import", data=data, files=files ) - def _build_for_download_project_logs_api_v1_projects__id__logs_get(self, id: int, grouped: bool | None = None) -> Coroutine[Any, Any, bytes]: + def _build_for_download_project_logs_api_v1_projects__id__logs_get( + self, id: int, grouped: bool | None = None + ) -> Coroutine[Any, Any, bytes]: """ Download Project Logs """ @@ -240,16 +224,14 @@ def _build_for_download_project_logs_api_v1_projects__id__logs_get(self, id: int query_params["grouped"] = str(grouped) return self.api_client.request( - type_=bytes, - method="GET", - url="/api/v1/projects/{id}/logs", - path_params=path_params, - params=query_params + type_=bytes, method="GET", url="/api/v1/projects/{id}/logs", path_params=path_params, params=query_params ) class AsyncProjectsApi(_ProjectsApi): - async def read_projects_api_v1_projects__get(self, archived: bool | None = None, skip: int | None = None, limit: int | None = None) -> list[m.Project]: + async def read_projects_api_v1_projects__get( + self, archived: bool | None = None, skip: int | None = None, limit: int | None = None + ) -> list[m.Project]: """ Read Projects """ @@ -297,19 +279,27 @@ async def unarchive_project_api_v1_projects__id__unarchive_post(self, id: int) - """ return await self._build_for_unarchive_project_api_v1_projects__id__unarchive_post(id=id) - async def upload_pics_api_v1_projects__id__upload_pics_put(self, body: m.BodyUploadPicsApiV1ProjectsIdUploadPicsPut, id: int) -> m.Project: + async def upload_pics_api_v1_projects__id__upload_pics_put( + self, body: m.BodyUploadPicsApiV1ProjectsIdUploadPicsPut, id: int + ) -> m.Project: """ Upload Pics """ return await self._build_for_upload_pics_api_v1_projects__id__upload_pics_put(body=body, id=id) - async def remove_pics_cluster_type_api_v1_projects__id__pics_cluster_type_delete(self, id: int, cluster_name: str) -> m.Project: + async def remove_pics_cluster_type_api_v1_projects__id__pics_cluster_type_delete( + self, id: int, cluster_name: str + ) -> m.Project: """ Remove Pics Cluster Type """ - return await self._build_for_remove_pics_cluster_type_api_v1_projects__id__pics_cluster_type_delete(id=id, cluster_name=cluster_name) + return await self._build_for_remove_pics_cluster_type_api_v1_projects__id__pics_cluster_type_delete( + id=id, cluster_name=cluster_name + ) - async def applicable_test_cases_api_v1_projects__id__applicable_test_cases_get(self, id: int) -> m.PICSApplicableTestCases: + async def applicable_test_cases_api_v1_projects__id__applicable_test_cases_get( + self, id: int + ) -> m.PICSApplicableTestCases: """ Applicable Test Cases """ @@ -321,7 +311,9 @@ async def export_project_config_api_v1_projects__id__export_get(self, id: int) - """ return await self._build_for_export_project_config_api_v1_projects__id__export_get(id=id) - async def importproject_config_api_v1_projects_import_post(self, body: m.BodyImportprojectConfigApiV1ProjectsImportPost) -> m.Project: + async def importproject_config_api_v1_projects_import_post( + self, body: m.BodyImportprojectConfigApiV1ProjectsImportPost + ) -> m.Project: """ Importproject Config """ @@ -335,7 +327,9 @@ async def download_project_logs_api_v1_projects__id__logs_get(self, id: int, gro class SyncProjectsApi(_ProjectsApi): - def read_projects_api_v1_projects__get(self, archived: bool | None = None, skip: int | None = None, limit: int | None = None) -> list[m.Project]: + def read_projects_api_v1_projects__get( + self, archived: bool | None = None, skip: int | None = None, limit: int | None = None + ) -> list[m.Project]: """ Read Projects """ @@ -391,21 +385,29 @@ def unarchive_project_api_v1_projects__id__unarchive_post(self, id: int) -> m.Pr coroutine = self._build_for_unarchive_project_api_v1_projects__id__unarchive_post(id=id) return get_event_loop().run_until_complete(coroutine) - def upload_pics_api_v1_projects__id__upload_pics_put(self, body: m.BodyUploadPicsApiV1ProjectsIdUploadPicsPut, id: int) -> m.Project: + def upload_pics_api_v1_projects__id__upload_pics_put( + self, body: m.BodyUploadPicsApiV1ProjectsIdUploadPicsPut, id: int + ) -> m.Project: """ Upload Pics """ coroutine = self._build_for_upload_pics_api_v1_projects__id__upload_pics_put(body=body, id=id) return get_event_loop().run_until_complete(coroutine) - def remove_pics_cluster_type_api_v1_projects__id__pics_cluster_type_delete(self, id: int, cluster_name: str) -> m.Project: + def remove_pics_cluster_type_api_v1_projects__id__pics_cluster_type_delete( + self, id: int, cluster_name: str + ) -> m.Project: """ Remove Pics Cluster Type """ - coroutine = self._build_for_remove_pics_cluster_type_api_v1_projects__id__pics_cluster_type_delete(id=id, cluster_name=cluster_name) + coroutine = self._build_for_remove_pics_cluster_type_api_v1_projects__id__pics_cluster_type_delete( + id=id, cluster_name=cluster_name + ) return get_event_loop().run_until_complete(coroutine) - def applicable_test_cases_api_v1_projects__id__applicable_test_cases_get(self, id: int) -> m.PICSApplicableTestCases: + def applicable_test_cases_api_v1_projects__id__applicable_test_cases_get( + self, id: int + ) -> m.PICSApplicableTestCases: """ Applicable Test Cases """ @@ -419,7 +421,9 @@ def export_project_config_api_v1_projects__id__export_get(self, id: int) -> m.Pr coroutine = self._build_for_export_project_config_api_v1_projects__id__export_get(id=id) return get_event_loop().run_until_complete(coroutine) - def importproject_config_api_v1_projects_import_post(self, body: m.BodyImportprojectConfigApiV1ProjectsImportPost) -> m.Project: + def importproject_config_api_v1_projects_import_post( + self, body: m.BodyImportprojectConfigApiV1ProjectsImportPost + ) -> m.Project: """ Importproject Config """ diff --git a/th_cli/api_lib_autogen/api/test_collections_api.py b/th_cli/api_lib_autogen/api/test_collections_api.py index eba4858..f3c414b 100644 --- a/th_cli/api_lib_autogen/api/test_collections_api.py +++ b/th_cli/api_lib_autogen/api/test_collections_api.py @@ -15,13 +15,14 @@ # # flake8: noqa E501 from asyncio import get_event_loop -from typing import Coroutine, IO, TYPE_CHECKING, Any +from typing import IO, TYPE_CHECKING, Any, Coroutine from th_cli.api_lib_autogen import models as m if TYPE_CHECKING: from th_cli.api_lib_autogen.api_client import ApiClient + class _TestCollectionsApi: def __init__(self, api_client: "ApiClient"): self.api_client = api_client @@ -30,11 +31,7 @@ def _build_for_read_test_collections_api_v1_test_collections__get(self) -> Corou """ Read Test Collections """ - return self.api_client.request( - type_=m.TestCollections, - method="GET", - url="/api/v1/test_collections/" - ) + return self.api_client.request(type_=m.TestCollections, method="GET", url="/api/v1/test_collections/") class AsyncTestCollectionsApi(_TestCollectionsApi): diff --git a/th_cli/api_lib_autogen/api/test_run_configs_api.py b/th_cli/api_lib_autogen/api/test_run_configs_api.py index e55d93a..135df60 100644 --- a/th_cli/api_lib_autogen/api/test_run_configs_api.py +++ b/th_cli/api_lib_autogen/api/test_run_configs_api.py @@ -15,18 +15,21 @@ # # flake8: noqa E501 from asyncio import get_event_loop -from typing import Coroutine, IO, TYPE_CHECKING, Any +from typing import IO, TYPE_CHECKING, Any, Coroutine from th_cli.api_lib_autogen import models as m if TYPE_CHECKING: from th_cli.api_lib_autogen.api_client import ApiClient + class _TestRunConfigsApi: def __init__(self, api_client: "ApiClient"): self.api_client = api_client - def _build_for_read_test_run_configs_api_v1_test_run_configs__get(self, skip: int | None = None, limit: int | None = None) -> Coroutine[Any, Any, list[m.TestRunConfig]]: + def _build_for_read_test_run_configs_api_v1_test_run_configs__get( + self, skip: int | None = None, limit: int | None = None + ) -> Coroutine[Any, Any, list[m.TestRunConfig]]: """ Read Test Run Configs """ @@ -37,63 +40,64 @@ def _build_for_read_test_run_configs_api_v1_test_run_configs__get(self, skip: in query_params["limit"] = str(limit) return self.api_client.request( - type_=list[m.TestRunConfig], - method="GET", - url="/api/v1/test_run_configs/", - params=query_params + type_=list[m.TestRunConfig], method="GET", url="/api/v1/test_run_configs/", params=query_params ) - def _build_for_create_test_run_config_api_v1_test_run_configs__post(self, body: m.TestRunConfigCreate) -> Coroutine[Any, Any, m.TestRunConfig]: + def _build_for_create_test_run_config_api_v1_test_run_configs__post( + self, body: m.TestRunConfigCreate + ) -> Coroutine[Any, Any, m.TestRunConfig]: """ Create Test Run Config """ - json_body = body.model_dump(mode='json') if hasattr(body, 'model_dump') else body + json_body = body.model_dump(mode="json") if hasattr(body, "model_dump") else body return self.api_client.request( - type_=m.TestRunConfig, - method="POST", - url="/api/v1/test_run_configs/", - json=json_body + type_=m.TestRunConfig, method="POST", url="/api/v1/test_run_configs/", json=json_body ) - def _build_for_read_test_run_config_api_v1_test_run_configs__id__get(self, id: int) -> Coroutine[Any, Any, m.TestRunConfig]: + def _build_for_read_test_run_config_api_v1_test_run_configs__id__get( + self, id: int + ) -> Coroutine[Any, Any, m.TestRunConfig]: """ Read Test Run Config """ path_params = {"id": str(id)} return self.api_client.request( - type_=m.TestRunConfig, - method="GET", - url="/api/v1/test_run_configs/{id}", - path_params=path_params + type_=m.TestRunConfig, method="GET", url="/api/v1/test_run_configs/{id}", path_params=path_params ) - def _build_for_update_test_run_config_api_v1_test_run_configs__id__put(self, body: m.TestRunConfigUpdate, id: int) -> Coroutine[Any, Any, m.TestRunConfig]: + def _build_for_update_test_run_config_api_v1_test_run_configs__id__put( + self, body: m.TestRunConfigUpdate, id: int + ) -> Coroutine[Any, Any, m.TestRunConfig]: """ Update Test Run Config """ path_params = {"id": str(id)} - json_body = body.model_dump(mode='json') if hasattr(body, 'model_dump') else body + json_body = body.model_dump(mode="json") if hasattr(body, "model_dump") else body return self.api_client.request( type_=m.TestRunConfig, method="PUT", url="/api/v1/test_run_configs/{id}", path_params=path_params, - json=json_body + json=json_body, ) class AsyncTestRunConfigsApi(_TestRunConfigsApi): - async def read_test_run_configs_api_v1_test_run_configs__get(self, skip: int | None = None, limit: int | None = None) -> list[m.TestRunConfig]: + async def read_test_run_configs_api_v1_test_run_configs__get( + self, skip: int | None = None, limit: int | None = None + ) -> list[m.TestRunConfig]: """ Read Test Run Configs """ return await self._build_for_read_test_run_configs_api_v1_test_run_configs__get(skip=skip, limit=limit) - async def create_test_run_config_api_v1_test_run_configs__post(self, body: m.TestRunConfigCreate) -> m.TestRunConfig: + async def create_test_run_config_api_v1_test_run_configs__post( + self, body: m.TestRunConfigCreate + ) -> m.TestRunConfig: """ Create Test Run Config """ @@ -105,7 +109,9 @@ async def read_test_run_config_api_v1_test_run_configs__id__get(self, id: int) - """ return await self._build_for_read_test_run_config_api_v1_test_run_configs__id__get(id=id) - async def update_test_run_config_api_v1_test_run_configs__id__put(self, body: m.TestRunConfigUpdate, id: int) -> m.TestRunConfig: + async def update_test_run_config_api_v1_test_run_configs__id__put( + self, body: m.TestRunConfigUpdate, id: int + ) -> m.TestRunConfig: """ Update Test Run Config """ @@ -113,7 +119,9 @@ async def update_test_run_config_api_v1_test_run_configs__id__put(self, body: m. class SyncTestRunConfigsApi(_TestRunConfigsApi): - def read_test_run_configs_api_v1_test_run_configs__get(self, skip: int | None = None, limit: int | None = None) -> list[m.TestRunConfig]: + def read_test_run_configs_api_v1_test_run_configs__get( + self, skip: int | None = None, limit: int | None = None + ) -> list[m.TestRunConfig]: """ Read Test Run Configs """ @@ -134,7 +142,9 @@ def read_test_run_config_api_v1_test_run_configs__id__get(self, id: int) -> m.Te coroutine = self._build_for_read_test_run_config_api_v1_test_run_configs__id__get(id=id) return get_event_loop().run_until_complete(coroutine) - def update_test_run_config_api_v1_test_run_configs__id__put(self, body: m.TestRunConfigUpdate, id: int) -> m.TestRunConfig: + def update_test_run_config_api_v1_test_run_configs__id__put( + self, body: m.TestRunConfigUpdate, id: int + ) -> m.TestRunConfig: """ Update Test Run Config """ diff --git a/th_cli/api_lib_autogen/api/test_run_executions_api.py b/th_cli/api_lib_autogen/api/test_run_executions_api.py index 43df849..464663a 100644 --- a/th_cli/api_lib_autogen/api/test_run_executions_api.py +++ b/th_cli/api_lib_autogen/api/test_run_executions_api.py @@ -15,18 +15,27 @@ # # flake8: noqa E501 from asyncio import get_event_loop -from typing import Coroutine, IO, TYPE_CHECKING, Any +from typing import IO, TYPE_CHECKING, Any, Coroutine from th_cli.api_lib_autogen import models as m if TYPE_CHECKING: from th_cli.api_lib_autogen.api_client import ApiClient + class _TestRunExecutionsApi: def __init__(self, api_client: "ApiClient"): self.api_client = api_client - def _build_for_read_test_run_executions_api_v1_test_run_executions__get(self, project_id: int | None = None, archived: bool | None = None, search_query: str | None = None, skip: int | None = None, limit: int | None = None, sort_order: str | None = None) -> Coroutine[Any, Any, list[m.TestRunExecutionWithStats]]: + def _build_for_read_test_run_executions_api_v1_test_run_executions__get( + self, + project_id: int | None = None, + archived: bool | None = None, + search_query: str | None = None, + skip: int | None = None, + limit: int | None = None, + sort_order: str | None = None, + ) -> Coroutine[Any, Any, list[m.TestRunExecutionWithStats]]: """ Read Test Run Executions """ @@ -48,10 +57,12 @@ def _build_for_read_test_run_executions_api_v1_test_run_executions__get(self, pr type_=list[m.TestRunExecutionWithStats], method="GET", url="/api/v1/test_run_executions/", - params=query_params + params=query_params, ) - def _build_for_create_test_run_execution_api_v1_test_run_executions__post(self, body: m.BodyCreateTestRunExecutionApiV1TestRunExecutionsPost, certification_mode: bool | None = None) -> Coroutine[Any, Any, m.TestRunExecutionWithChildren]: + def _build_for_create_test_run_execution_api_v1_test_run_executions__post( + self, body: m.BodyCreateTestRunExecutionApiV1TestRunExecutionsPost, certification_mode: bool | None = None + ) -> Coroutine[Any, Any, m.TestRunExecutionWithChildren]: """ Create Test Run Execution """ @@ -59,30 +70,31 @@ def _build_for_create_test_run_execution_api_v1_test_run_executions__post(self, if certification_mode is not None: query_params["certification_mode"] = str(certification_mode) - json_body = body.model_dump(mode='json') if hasattr(body, 'model_dump') else body + json_body = body.model_dump(mode="json") if hasattr(body, "model_dump") else body return self.api_client.request( type_=m.TestRunExecutionWithChildren, method="POST", url="/api/v1/test_run_executions/", params=query_params, - json=json_body + json=json_body, ) - def _build_for_create_cli_test_run_execution_api_v1_test_run_executions_cli_post(self, body: m.BodyCreateCliTestRunExecutionApiV1TestRunExecutionsCliPost) -> Coroutine[Any, Any, m.TestRunExecutionWithChildren]: + def _build_for_create_cli_test_run_execution_api_v1_test_run_executions_cli_post( + self, body: m.BodyCreateCliTestRunExecutionApiV1TestRunExecutionsCliPost + ) -> Coroutine[Any, Any, m.TestRunExecutionWithChildren]: """ Create Cli Test Run Execution """ - json_body = body.model_dump(mode='json') if hasattr(body, 'model_dump') else body + json_body = body.model_dump(mode="json") if hasattr(body, "model_dump") else body return self.api_client.request( - type_=m.TestRunExecutionWithChildren, - method="POST", - url="/api/v1/test_run_executions/cli", - json=json_body + type_=m.TestRunExecutionWithChildren, method="POST", url="/api/v1/test_run_executions/cli", json=json_body ) - def _build_for_rename_test_run_execution_api_v1_test_run_executions__id__rename_put(self, id: int, new_execution_name: str) -> Coroutine[Any, Any, m.TestRunExecutionWithChildren]: + def _build_for_rename_test_run_execution_api_v1_test_run_executions__id__rename_put( + self, id: int, new_execution_name: str + ) -> Coroutine[Any, Any, m.TestRunExecutionWithChildren]: """ Rename Test Run Execution """ @@ -95,30 +107,35 @@ def _build_for_rename_test_run_execution_api_v1_test_run_executions__id__rename_ method="PUT", url="/api/v1/test_run_executions/{id}/rename", path_params=path_params, - params=query_params + params=query_params, ) - def _build_for_abort_testing_api_v1_test_run_executions_abort_testing_post(self) -> Coroutine[Any, Any, dict[str, str]]: + def _build_for_abort_testing_api_v1_test_run_executions_abort_testing_post( + self, + ) -> Coroutine[Any, Any, dict[str, str]]: """ Abort Testing """ return self.api_client.request( - type_=dict[str, str], - method="POST", - url="/api/v1/test_run_executions/abort-testing" + type_=dict[str, str], method="POST", url="/api/v1/test_run_executions/abort-testing" ) - def _build_for_get_test_runner_status_api_v1_test_run_executions_status_get(self) -> Coroutine[Any, Any, m.TestRunnerStatus]: + def _build_for_get_test_runner_status_api_v1_test_run_executions_status_get( + self, + ) -> Coroutine[Any, Any, m.TestRunnerStatus]: """ Get Test Runner Status """ - return self.api_client.request( - type_=m.TestRunnerStatus, - method="GET", - url="/api/v1/test_run_executions/status" - ) + return self.api_client.request(type_=m.TestRunnerStatus, method="GET", url="/api/v1/test_run_executions/status") - def _build_for_get_chip_server_info_api_v1_test_run_executions_chip_server_info_get(self, discriminator: str | None = None, setup_pin_code: str | None = None, version: int | None = None, vendor_id: int | None = None, product_id: int | None = None) -> Coroutine[Any, Any, m.ChipServerInfo]: + def _build_for_get_chip_server_info_api_v1_test_run_executions_chip_server_info_get( + self, + discriminator: str | None = None, + setup_pin_code: str | None = None, + version: int | None = None, + vendor_id: int | None = None, + product_id: int | None = None, + ) -> Coroutine[Any, Any, m.ChipServerInfo]: """ Get Chip Server Info """ @@ -138,10 +155,12 @@ def _build_for_get_chip_server_info_api_v1_test_run_executions_chip_server_info_ type_=m.ChipServerInfo, method="GET", url="/api/v1/test_run_executions/chip-server/info", - params=query_params + params=query_params, ) - def _build_for_read_test_run_execution_api_v1_test_run_executions__id__get(self, id: int) -> Coroutine[Any, Any, m.TestRunExecutionWithChildren]: + def _build_for_read_test_run_execution_api_v1_test_run_executions__id__get( + self, id: int + ) -> Coroutine[Any, Any, m.TestRunExecutionWithChildren]: """ Read Test Run Execution """ @@ -151,10 +170,12 @@ def _build_for_read_test_run_execution_api_v1_test_run_executions__id__get(self, type_=m.TestRunExecutionWithChildren, method="GET", url="/api/v1/test_run_executions/{id}", - path_params=path_params + path_params=path_params, ) - def _build_for_remove_test_run_execution_api_v1_test_run_executions__id__delete(self, id: int) -> Coroutine[Any, Any, m.TestRunExecutionInDBBase]: + def _build_for_remove_test_run_execution_api_v1_test_run_executions__id__delete( + self, id: int + ) -> Coroutine[Any, Any, m.TestRunExecutionInDBBase]: """ Remove Test Run Execution """ @@ -164,10 +185,12 @@ def _build_for_remove_test_run_execution_api_v1_test_run_executions__id__delete( type_=m.TestRunExecutionInDBBase, method="DELETE", url="/api/v1/test_run_executions/{id}", - path_params=path_params + path_params=path_params, ) - def _build_for_start_test_run_execution_api_v1_test_run_executions__id__start_post(self, id: int) -> Coroutine[Any, Any, m.TestRunExecutionWithChildren]: + def _build_for_start_test_run_execution_api_v1_test_run_executions__id__start_post( + self, id: int + ) -> Coroutine[Any, Any, m.TestRunExecutionWithChildren]: """ Start Test Run Execution """ @@ -177,10 +200,12 @@ def _build_for_start_test_run_execution_api_v1_test_run_executions__id__start_po type_=m.TestRunExecutionWithChildren, method="POST", url="/api/v1/test_run_executions/{id}/start", - path_params=path_params + path_params=path_params, ) - def _build_for_archive_api_v1_test_run_executions__id__archive_post(self, id: int) -> Coroutine[Any, Any, m.TestRunExecution]: + def _build_for_archive_api_v1_test_run_executions__id__archive_post( + self, id: int + ) -> Coroutine[Any, Any, m.TestRunExecution]: """ Archive """ @@ -190,10 +215,12 @@ def _build_for_archive_api_v1_test_run_executions__id__archive_post(self, id: in type_=m.TestRunExecution, method="POST", url="/api/v1/test_run_executions/{id}/archive", - path_params=path_params + path_params=path_params, ) - def _build_for_unarchive_api_v1_test_run_executions__id__unarchive_post(self, id: int) -> Coroutine[Any, Any, m.TestRunExecution]: + def _build_for_unarchive_api_v1_test_run_executions__id__unarchive_post( + self, id: int + ) -> Coroutine[Any, Any, m.TestRunExecution]: """ Unarchive """ @@ -203,10 +230,12 @@ def _build_for_unarchive_api_v1_test_run_executions__id__unarchive_post(self, id type_=m.TestRunExecution, method="POST", url="/api/v1/test_run_executions/{id}/unarchive", - path_params=path_params + path_params=path_params, ) - def _build_for_repeat_test_run_execution_api_v1_test_run_executions__id__repeat_post(self, id: int, title: str | None = None) -> Coroutine[Any, Any, m.TestRunExecutionWithChildren]: + def _build_for_repeat_test_run_execution_api_v1_test_run_executions__id__repeat_post( + self, id: int, title: str | None = None + ) -> Coroutine[Any, Any, m.TestRunExecutionWithChildren]: """ Repeat Test Run Execution """ @@ -221,10 +250,12 @@ def _build_for_repeat_test_run_execution_api_v1_test_run_executions__id__repeat_ method="POST", url="/api/v1/test_run_executions/{id}/repeat", path_params=path_params, - params=query_params + params=query_params, ) - def _build_for_download_log_api_v1_test_run_executions__id__log_get(self, id: int, json_entries: bool | None = None, download: bool | None = None) -> Coroutine[Any, Any, None]: + def _build_for_download_log_api_v1_test_run_executions__id__log_get( + self, id: int, json_entries: bool | None = None, download: bool | None = None + ) -> Coroutine[Any, Any, None]: """ Download Log """ @@ -241,23 +272,24 @@ def _build_for_download_log_api_v1_test_run_executions__id__log_get(self, id: in method="GET", url="/api/v1/test_run_executions/{id}/log", path_params=path_params, - params=query_params + params=query_params, ) - def _build_for_download_grouped_log_api_v1_test_run_executions__id__grouped_log_get(self, id: int) -> Coroutine[Any, Any, bytes]: + def _build_for_download_grouped_log_api_v1_test_run_executions__id__grouped_log_get( + self, id: int + ) -> Coroutine[Any, Any, bytes]: """ Download Grouped Log """ path_params = {"id": str(id)} return self.api_client.request( - type_=bytes, - method="GET", - url="/api/v1/test_run_executions/{id}/grouped-log", - path_params=path_params + type_=bytes, method="GET", url="/api/v1/test_run_executions/{id}/grouped-log", path_params=path_params ) - def _build_for_upload_file_api_v1_test_run_executions_file_upload__post(self, body: m.BodyUploadFileApiV1TestRunExecutionsFileUploadPost) -> Coroutine[Any, Any, dict[str, Any]]: + def _build_for_upload_file_api_v1_test_run_executions_file_upload__post( + self, body: m.BodyUploadFileApiV1TestRunExecutionsFileUploadPost + ) -> Coroutine[Any, Any, dict[str, Any]]: """ Upload File """ @@ -267,21 +299,19 @@ def _build_for_upload_file_api_v1_test_run_executions_file_upload__post(self, bo # Process body fields to populate files and data dictionaries if body is not None: # Process field: file - if hasattr(body, 'file'): - field_value = getattr(body, 'file') + if hasattr(body, "file"): + field_value = getattr(body, "file") if field_value is not None: # File field - files['file'] = field_value + files["file"] = field_value return self.api_client.request( - type_=dict[str, Any], - method="POST", - url="/api/v1/test_run_executions/file_upload/", - data=data, - files=files + type_=dict[str, Any], method="POST", url="/api/v1/test_run_executions/file_upload/", data=data, files=files ) - def _build_for_export_test_run_execution_api_v1_test_run_executions__id__export_get(self, id: int, download: bool | None = None) -> Coroutine[Any, Any, m.ExportedTestRunExecution]: + def _build_for_export_test_run_execution_api_v1_test_run_executions__id__export_get( + self, id: int, download: bool | None = None + ) -> Coroutine[Any, Any, m.ExportedTestRunExecution]: """ Export Test Run Execution """ @@ -296,10 +326,12 @@ def _build_for_export_test_run_execution_api_v1_test_run_executions__id__export_ method="GET", url="/api/v1/test_run_executions/{id}/export", path_params=path_params, - params=query_params + params=query_params, ) - def _build_for_import_test_run_execution_api_v1_test_run_executions_import_post(self, body: m.BodyImportTestRunExecutionApiV1TestRunExecutionsImportPost, project_id: int) -> Coroutine[Any, Any, m.TestRunExecutionWithChildren]: + def _build_for_import_test_run_execution_api_v1_test_run_executions_import_post( + self, body: m.BodyImportTestRunExecutionApiV1TestRunExecutionsImportPost, project_id: int + ) -> Coroutine[Any, Any, m.TestRunExecutionWithChildren]: """ Import Test Run Execution """ @@ -311,11 +343,11 @@ def _build_for_import_test_run_execution_api_v1_test_run_executions_import_post( # Process body fields to populate files and data dictionaries if body is not None: # Process field: import_file - if hasattr(body, 'import_file'): - field_value = getattr(body, 'import_file') + if hasattr(body, "import_file"): + field_value = getattr(body, "import_file") if field_value is not None: # File field - files['import_file'] = field_value + files["import_file"] = field_value return self.api_client.request( type_=m.TestRunExecutionWithChildren, @@ -323,34 +355,59 @@ def _build_for_import_test_run_execution_api_v1_test_run_executions_import_post( url="/api/v1/test_run_executions/import", params=query_params, data=data, - files=files + files=files, ) class AsyncTestRunExecutionsApi(_TestRunExecutionsApi): - async def read_test_run_executions_api_v1_test_run_executions__get(self, project_id: int | None = None, archived: bool | None = None, search_query: str | None = None, skip: int | None = None, limit: int | None = None, sort_order: str | None = None) -> list[m.TestRunExecutionWithStats]: + async def read_test_run_executions_api_v1_test_run_executions__get( + self, + project_id: int | None = None, + archived: bool | None = None, + search_query: str | None = None, + skip: int | None = None, + limit: int | None = None, + sort_order: str | None = None, + ) -> list[m.TestRunExecutionWithStats]: """ Read Test Run Executions """ - return await self._build_for_read_test_run_executions_api_v1_test_run_executions__get(project_id=project_id, archived=archived, search_query=search_query, skip=skip, limit=limit, sort_order=sort_order) + return await self._build_for_read_test_run_executions_api_v1_test_run_executions__get( + project_id=project_id, + archived=archived, + search_query=search_query, + skip=skip, + limit=limit, + sort_order=sort_order, + ) - async def create_test_run_execution_api_v1_test_run_executions__post(self, body: m.BodyCreateTestRunExecutionApiV1TestRunExecutionsPost, certification_mode: bool | None = None) -> m.TestRunExecutionWithChildren: + async def create_test_run_execution_api_v1_test_run_executions__post( + self, body: m.BodyCreateTestRunExecutionApiV1TestRunExecutionsPost, certification_mode: bool | None = None + ) -> m.TestRunExecutionWithChildren: """ Create Test Run Execution """ - return await self._build_for_create_test_run_execution_api_v1_test_run_executions__post(body=body, certification_mode=certification_mode) + return await self._build_for_create_test_run_execution_api_v1_test_run_executions__post( + body=body, certification_mode=certification_mode + ) - async def create_cli_test_run_execution_api_v1_test_run_executions_cli_post(self, body: m.BodyCreateCliTestRunExecutionApiV1TestRunExecutionsCliPost) -> m.TestRunExecutionWithChildren: + async def create_cli_test_run_execution_api_v1_test_run_executions_cli_post( + self, body: m.BodyCreateCliTestRunExecutionApiV1TestRunExecutionsCliPost + ) -> m.TestRunExecutionWithChildren: """ Create Cli Test Run Execution """ return await self._build_for_create_cli_test_run_execution_api_v1_test_run_executions_cli_post(body=body) - async def rename_test_run_execution_api_v1_test_run_executions__id__rename_put(self, id: int, new_execution_name: str) -> m.TestRunExecutionWithChildren: + async def rename_test_run_execution_api_v1_test_run_executions__id__rename_put( + self, id: int, new_execution_name: str + ) -> m.TestRunExecutionWithChildren: """ Rename Test Run Execution """ - return await self._build_for_rename_test_run_execution_api_v1_test_run_executions__id__rename_put(id=id, new_execution_name=new_execution_name) + return await self._build_for_rename_test_run_execution_api_v1_test_run_executions__id__rename_put( + id=id, new_execution_name=new_execution_name + ) async def abort_testing_api_v1_test_run_executions_abort_testing_post(self) -> dict[str, str]: """ @@ -364,25 +421,44 @@ async def get_test_runner_status_api_v1_test_run_executions_status_get(self) -> """ return await self._build_for_get_test_runner_status_api_v1_test_run_executions_status_get() - async def get_chip_server_info_api_v1_test_run_executions_chip_server_info_get(self, discriminator: str | None = None, setup_pin_code: str | None = None, version: int | None = None, vendor_id: int | None = None, product_id: int | None = None) -> m.ChipServerInfo: + async def get_chip_server_info_api_v1_test_run_executions_chip_server_info_get( + self, + discriminator: str | None = None, + setup_pin_code: str | None = None, + version: int | None = None, + vendor_id: int | None = None, + product_id: int | None = None, + ) -> m.ChipServerInfo: """ Get Chip Server Info """ - return await self._build_for_get_chip_server_info_api_v1_test_run_executions_chip_server_info_get(discriminator=discriminator, setup_pin_code=setup_pin_code, version=version, vendor_id=vendor_id, product_id=product_id) + return await self._build_for_get_chip_server_info_api_v1_test_run_executions_chip_server_info_get( + discriminator=discriminator, + setup_pin_code=setup_pin_code, + version=version, + vendor_id=vendor_id, + product_id=product_id, + ) - async def read_test_run_execution_api_v1_test_run_executions__id__get(self, id: int) -> m.TestRunExecutionWithChildren: + async def read_test_run_execution_api_v1_test_run_executions__id__get( + self, id: int + ) -> m.TestRunExecutionWithChildren: """ Read Test Run Execution """ return await self._build_for_read_test_run_execution_api_v1_test_run_executions__id__get(id=id) - async def remove_test_run_execution_api_v1_test_run_executions__id__delete(self, id: int) -> m.TestRunExecutionInDBBase: + async def remove_test_run_execution_api_v1_test_run_executions__id__delete( + self, id: int + ) -> m.TestRunExecutionInDBBase: """ Remove Test Run Execution """ return await self._build_for_remove_test_run_execution_api_v1_test_run_executions__id__delete(id=id) - async def start_test_run_execution_api_v1_test_run_executions__id__start_post(self, id: int) -> m.TestRunExecutionWithChildren: + async def start_test_run_execution_api_v1_test_run_executions__id__start_post( + self, id: int + ) -> m.TestRunExecutionWithChildren: """ Start Test Run Execution """ @@ -400,17 +476,25 @@ async def unarchive_api_v1_test_run_executions__id__unarchive_post(self, id: int """ return await self._build_for_unarchive_api_v1_test_run_executions__id__unarchive_post(id=id) - async def repeat_test_run_execution_api_v1_test_run_executions__id__repeat_post(self, id: int, title: str | None = None) -> m.TestRunExecutionWithChildren: + async def repeat_test_run_execution_api_v1_test_run_executions__id__repeat_post( + self, id: int, title: str | None = None + ) -> m.TestRunExecutionWithChildren: """ Repeat Test Run Execution """ - return await self._build_for_repeat_test_run_execution_api_v1_test_run_executions__id__repeat_post(id=id, title=title) + return await self._build_for_repeat_test_run_execution_api_v1_test_run_executions__id__repeat_post( + id=id, title=title + ) - async def download_log_api_v1_test_run_executions__id__log_get(self, id: int, json_entries: bool | None = None, download: bool | None = None) -> None: + async def download_log_api_v1_test_run_executions__id__log_get( + self, id: int, json_entries: bool | None = None, download: bool | None = None + ) -> None: """ Download Log """ - return await self._build_for_download_log_api_v1_test_run_executions__id__log_get(id=id, json_entries=json_entries, download=download) + return await self._build_for_download_log_api_v1_test_run_executions__id__log_get( + id=id, json_entries=json_entries, download=download + ) async def download_grouped_log_api_v1_test_run_executions__id__grouped_log_get(self, id: int) -> bytes: """ @@ -418,52 +502,87 @@ async def download_grouped_log_api_v1_test_run_executions__id__grouped_log_get(s """ return await self._build_for_download_grouped_log_api_v1_test_run_executions__id__grouped_log_get(id=id) - async def upload_file_api_v1_test_run_executions_file_upload__post(self, body: m.BodyUploadFileApiV1TestRunExecutionsFileUploadPost) -> dict[str, Any]: + async def upload_file_api_v1_test_run_executions_file_upload__post( + self, body: m.BodyUploadFileApiV1TestRunExecutionsFileUploadPost + ) -> dict[str, Any]: """ Upload File """ return await self._build_for_upload_file_api_v1_test_run_executions_file_upload__post(body=body) - async def export_test_run_execution_api_v1_test_run_executions__id__export_get(self, id: int, download: bool | None = None) -> m.ExportedTestRunExecution: + async def export_test_run_execution_api_v1_test_run_executions__id__export_get( + self, id: int, download: bool | None = None + ) -> m.ExportedTestRunExecution: """ Export Test Run Execution """ - return await self._build_for_export_test_run_execution_api_v1_test_run_executions__id__export_get(id=id, download=download) + return await self._build_for_export_test_run_execution_api_v1_test_run_executions__id__export_get( + id=id, download=download + ) - async def import_test_run_execution_api_v1_test_run_executions_import_post(self, body: m.BodyImportTestRunExecutionApiV1TestRunExecutionsImportPost, project_id: int) -> m.TestRunExecutionWithChildren: + async def import_test_run_execution_api_v1_test_run_executions_import_post( + self, body: m.BodyImportTestRunExecutionApiV1TestRunExecutionsImportPost, project_id: int + ) -> m.TestRunExecutionWithChildren: """ Import Test Run Execution """ - return await self._build_for_import_test_run_execution_api_v1_test_run_executions_import_post(body=body, project_id=project_id) + return await self._build_for_import_test_run_execution_api_v1_test_run_executions_import_post( + body=body, project_id=project_id + ) class SyncTestRunExecutionsApi(_TestRunExecutionsApi): - def read_test_run_executions_api_v1_test_run_executions__get(self, project_id: int | None = None, archived: bool | None = None, search_query: str | None = None, skip: int | None = None, limit: int | None = None, sort_order: str | None = None) -> list[m.TestRunExecutionWithStats]: + def read_test_run_executions_api_v1_test_run_executions__get( + self, + project_id: int | None = None, + archived: bool | None = None, + search_query: str | None = None, + skip: int | None = None, + limit: int | None = None, + sort_order: str | None = None, + ) -> list[m.TestRunExecutionWithStats]: """ Read Test Run Executions """ - coroutine = self._build_for_read_test_run_executions_api_v1_test_run_executions__get(project_id=project_id, archived=archived, search_query=search_query, skip=skip, limit=limit, sort_order=sort_order) + coroutine = self._build_for_read_test_run_executions_api_v1_test_run_executions__get( + project_id=project_id, + archived=archived, + search_query=search_query, + skip=skip, + limit=limit, + sort_order=sort_order, + ) return get_event_loop().run_until_complete(coroutine) - def create_test_run_execution_api_v1_test_run_executions__post(self, body: m.BodyCreateTestRunExecutionApiV1TestRunExecutionsPost, certification_mode: bool | None = None) -> m.TestRunExecutionWithChildren: + def create_test_run_execution_api_v1_test_run_executions__post( + self, body: m.BodyCreateTestRunExecutionApiV1TestRunExecutionsPost, certification_mode: bool | None = None + ) -> m.TestRunExecutionWithChildren: """ Create Test Run Execution """ - coroutine = self._build_for_create_test_run_execution_api_v1_test_run_executions__post(body=body, certification_mode=certification_mode) + coroutine = self._build_for_create_test_run_execution_api_v1_test_run_executions__post( + body=body, certification_mode=certification_mode + ) return get_event_loop().run_until_complete(coroutine) - def create_cli_test_run_execution_api_v1_test_run_executions_cli_post(self, body: m.BodyCreateCliTestRunExecutionApiV1TestRunExecutionsCliPost) -> m.TestRunExecutionWithChildren: + def create_cli_test_run_execution_api_v1_test_run_executions_cli_post( + self, body: m.BodyCreateCliTestRunExecutionApiV1TestRunExecutionsCliPost + ) -> m.TestRunExecutionWithChildren: """ Create Cli Test Run Execution """ coroutine = self._build_for_create_cli_test_run_execution_api_v1_test_run_executions_cli_post(body=body) return get_event_loop().run_until_complete(coroutine) - def rename_test_run_execution_api_v1_test_run_executions__id__rename_put(self, id: int, new_execution_name: str) -> m.TestRunExecutionWithChildren: + def rename_test_run_execution_api_v1_test_run_executions__id__rename_put( + self, id: int, new_execution_name: str + ) -> m.TestRunExecutionWithChildren: """ Rename Test Run Execution """ - coroutine = self._build_for_rename_test_run_execution_api_v1_test_run_executions__id__rename_put(id=id, new_execution_name=new_execution_name) + coroutine = self._build_for_rename_test_run_execution_api_v1_test_run_executions__id__rename_put( + id=id, new_execution_name=new_execution_name + ) return get_event_loop().run_until_complete(coroutine) def abort_testing_api_v1_test_run_executions_abort_testing_post(self) -> dict[str, str]: @@ -480,11 +599,24 @@ def get_test_runner_status_api_v1_test_run_executions_status_get(self) -> m.Test coroutine = self._build_for_get_test_runner_status_api_v1_test_run_executions_status_get() return get_event_loop().run_until_complete(coroutine) - def get_chip_server_info_api_v1_test_run_executions_chip_server_info_get(self, discriminator: str | None = None, setup_pin_code: str | None = None, version: int | None = None, vendor_id: int | None = None, product_id: int | None = None) -> m.ChipServerInfo: + def get_chip_server_info_api_v1_test_run_executions_chip_server_info_get( + self, + discriminator: str | None = None, + setup_pin_code: str | None = None, + version: int | None = None, + vendor_id: int | None = None, + product_id: int | None = None, + ) -> m.ChipServerInfo: """ Get Chip Server Info """ - coroutine = self._build_for_get_chip_server_info_api_v1_test_run_executions_chip_server_info_get(discriminator=discriminator, setup_pin_code=setup_pin_code, version=version, vendor_id=vendor_id, product_id=product_id) + coroutine = self._build_for_get_chip_server_info_api_v1_test_run_executions_chip_server_info_get( + discriminator=discriminator, + setup_pin_code=setup_pin_code, + version=version, + vendor_id=vendor_id, + product_id=product_id, + ) return get_event_loop().run_until_complete(coroutine) def read_test_run_execution_api_v1_test_run_executions__id__get(self, id: int) -> m.TestRunExecutionWithChildren: @@ -501,7 +633,9 @@ def remove_test_run_execution_api_v1_test_run_executions__id__delete(self, id: i coroutine = self._build_for_remove_test_run_execution_api_v1_test_run_executions__id__delete(id=id) return get_event_loop().run_until_complete(coroutine) - def start_test_run_execution_api_v1_test_run_executions__id__start_post(self, id: int) -> m.TestRunExecutionWithChildren: + def start_test_run_execution_api_v1_test_run_executions__id__start_post( + self, id: int + ) -> m.TestRunExecutionWithChildren: """ Start Test Run Execution """ @@ -522,18 +656,26 @@ def unarchive_api_v1_test_run_executions__id__unarchive_post(self, id: int) -> m coroutine = self._build_for_unarchive_api_v1_test_run_executions__id__unarchive_post(id=id) return get_event_loop().run_until_complete(coroutine) - def repeat_test_run_execution_api_v1_test_run_executions__id__repeat_post(self, id: int, title: str | None = None) -> m.TestRunExecutionWithChildren: + def repeat_test_run_execution_api_v1_test_run_executions__id__repeat_post( + self, id: int, title: str | None = None + ) -> m.TestRunExecutionWithChildren: """ Repeat Test Run Execution """ - coroutine = self._build_for_repeat_test_run_execution_api_v1_test_run_executions__id__repeat_post(id=id, title=title) + coroutine = self._build_for_repeat_test_run_execution_api_v1_test_run_executions__id__repeat_post( + id=id, title=title + ) return get_event_loop().run_until_complete(coroutine) - def download_log_api_v1_test_run_executions__id__log_get(self, id: int, json_entries: bool | None = None, download: bool | None = None) -> None: + def download_log_api_v1_test_run_executions__id__log_get( + self, id: int, json_entries: bool | None = None, download: bool | None = None + ) -> None: """ Download Log """ - coroutine = self._build_for_download_log_api_v1_test_run_executions__id__log_get(id=id, json_entries=json_entries, download=download) + coroutine = self._build_for_download_log_api_v1_test_run_executions__id__log_get( + id=id, json_entries=json_entries, download=download + ) return get_event_loop().run_until_complete(coroutine) def download_grouped_log_api_v1_test_run_executions__id__grouped_log_get(self, id: int) -> bytes: @@ -543,23 +685,33 @@ def download_grouped_log_api_v1_test_run_executions__id__grouped_log_get(self, i coroutine = self._build_for_download_grouped_log_api_v1_test_run_executions__id__grouped_log_get(id=id) return get_event_loop().run_until_complete(coroutine) - def upload_file_api_v1_test_run_executions_file_upload__post(self, body: m.BodyUploadFileApiV1TestRunExecutionsFileUploadPost) -> dict[str, Any]: + def upload_file_api_v1_test_run_executions_file_upload__post( + self, body: m.BodyUploadFileApiV1TestRunExecutionsFileUploadPost + ) -> dict[str, Any]: """ Upload File """ coroutine = self._build_for_upload_file_api_v1_test_run_executions_file_upload__post(body=body) return get_event_loop().run_until_complete(coroutine) - def export_test_run_execution_api_v1_test_run_executions__id__export_get(self, id: int, download: bool | None = None) -> m.ExportedTestRunExecution: + def export_test_run_execution_api_v1_test_run_executions__id__export_get( + self, id: int, download: bool | None = None + ) -> m.ExportedTestRunExecution: """ Export Test Run Execution """ - coroutine = self._build_for_export_test_run_execution_api_v1_test_run_executions__id__export_get(id=id, download=download) + coroutine = self._build_for_export_test_run_execution_api_v1_test_run_executions__id__export_get( + id=id, download=download + ) return get_event_loop().run_until_complete(coroutine) - def import_test_run_execution_api_v1_test_run_executions_import_post(self, body: m.BodyImportTestRunExecutionApiV1TestRunExecutionsImportPost, project_id: int) -> m.TestRunExecutionWithChildren: + def import_test_run_execution_api_v1_test_run_executions_import_post( + self, body: m.BodyImportTestRunExecutionApiV1TestRunExecutionsImportPost, project_id: int + ) -> m.TestRunExecutionWithChildren: """ Import Test Run Execution """ - coroutine = self._build_for_import_test_run_execution_api_v1_test_run_executions_import_post(body=body, project_id=project_id) + coroutine = self._build_for_import_test_run_execution_api_v1_test_run_executions_import_post( + body=body, project_id=project_id + ) return get_event_loop().run_until_complete(coroutine) diff --git a/th_cli/api_lib_autogen/api/utils_api.py b/th_cli/api_lib_autogen/api/utils_api.py index 7cf9cc1..7bb9170 100644 --- a/th_cli/api_lib_autogen/api/utils_api.py +++ b/th_cli/api_lib_autogen/api/utils_api.py @@ -15,13 +15,14 @@ # # flake8: noqa E501 from asyncio import get_event_loop -from typing import Coroutine, IO, TYPE_CHECKING, Any +from typing import IO, TYPE_CHECKING, Any, Coroutine from th_cli.api_lib_autogen import models as m if TYPE_CHECKING: from th_cli.api_lib_autogen.api_client import ApiClient + class _UtilsApi: def __init__(self, api_client: "ApiClient"): self.api_client = api_client @@ -32,12 +33,7 @@ def _build_for_test_email_api_v1_utils_test_email__post(self, email_to: str) -> """ query_params = {"email_to": str(email_to)} - return self.api_client.request( - type_=m.Msg, - method="POST", - url="/api/v1/utils/test-email/", - params=query_params - ) + return self.api_client.request(type_=m.Msg, method="POST", url="/api/v1/utils/test-email/", params=query_params) class AsyncUtilsApi(_UtilsApi): diff --git a/th_cli/api_lib_autogen/api/version_api.py b/th_cli/api_lib_autogen/api/version_api.py index 4037ce4..2937dc2 100644 --- a/th_cli/api_lib_autogen/api/version_api.py +++ b/th_cli/api_lib_autogen/api/version_api.py @@ -15,26 +15,25 @@ # # flake8: noqa E501 from asyncio import get_event_loop -from typing import Coroutine, IO, TYPE_CHECKING, Any +from typing import IO, TYPE_CHECKING, Any, Coroutine from th_cli.api_lib_autogen import models as m if TYPE_CHECKING: from th_cli.api_lib_autogen.api_client import ApiClient + class _VersionApi: def __init__(self, api_client: "ApiClient"): self.api_client = api_client - def _build_for_get_test_harness_backend_version_api_v1_version_get(self) -> Coroutine[Any, Any, m.TestHarnessBackendVersion]: + def _build_for_get_test_harness_backend_version_api_v1_version_get( + self, + ) -> Coroutine[Any, Any, m.TestHarnessBackendVersion]: """ Get Test Harness Backend Version """ - return self.api_client.request( - type_=m.TestHarnessBackendVersion, - method="GET", - url="/api/v1/version" - ) + return self.api_client.request(type_=m.TestHarnessBackendVersion, method="GET", url="/api/v1/version") class AsyncVersionApi(_VersionApi): diff --git a/th_cli/api_lib_autogen/api_client.py b/th_cli/api_lib_autogen/api_client.py index d117df6..4d7d04c 100644 --- a/th_cli/api_lib_autogen/api_client.py +++ b/th_cli/api_lib_autogen/api_client.py @@ -19,14 +19,14 @@ from httpx import AsyncClient, Request, Response from pydantic import TypeAdapter -from th_cli.api_lib_autogen.api.test_collections_api import AsyncTestCollectionsApi, SyncTestCollectionsApi -from th_cli.api_lib_autogen.api.projects_api import AsyncProjectsApi, SyncProjectsApi +from th_cli.api_lib_autogen.api.devices_api import AsyncDevicesApi, SyncDevicesApi from th_cli.api_lib_autogen.api.operators_api import AsyncOperatorsApi, SyncOperatorsApi -from th_cli.api_lib_autogen.api.test_run_executions_api import AsyncTestRunExecutionsApi, SyncTestRunExecutionsApi +from th_cli.api_lib_autogen.api.projects_api import AsyncProjectsApi, SyncProjectsApi +from th_cli.api_lib_autogen.api.test_collections_api import AsyncTestCollectionsApi, SyncTestCollectionsApi from th_cli.api_lib_autogen.api.test_run_configs_api import AsyncTestRunConfigsApi, SyncTestRunConfigsApi -from th_cli.api_lib_autogen.api.version_api import AsyncVersionApi, SyncVersionApi +from th_cli.api_lib_autogen.api.test_run_executions_api import AsyncTestRunExecutionsApi, SyncTestRunExecutionsApi from th_cli.api_lib_autogen.api.utils_api import AsyncUtilsApi, SyncUtilsApi -from th_cli.api_lib_autogen.api.devices_api import AsyncDevicesApi, SyncDevicesApi +from th_cli.api_lib_autogen.api.version_api import AsyncVersionApi, SyncVersionApi from th_cli.api_lib_autogen.exceptions import ResponseHandlingException, UnexpectedResponse ClientT = TypeVar("ClientT", bound="ApiClient") diff --git a/th_cli/api_lib_autogen/exceptions.py b/th_cli/api_lib_autogen/exceptions.py index 7e5f220..ca3e7d7 100644 --- a/th_cli/api_lib_autogen/exceptions.py +++ b/th_cli/api_lib_autogen/exceptions.py @@ -20,6 +20,7 @@ class ApiException(Exception): """Base exception for API client errors.""" + pass diff --git a/th_cli/api_lib_autogen/models.py b/th_cli/api_lib_autogen/models.py index 0a5dd3e..00eddbe 100644 --- a/th_cli/api_lib_autogen/models.py +++ b/th_cli/api_lib_autogen/models.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: -# filename: tmpxwopvtxa.json -# timestamp: 2026-07-07T18:20:10+00:00 +# filename: tmp8d039ovi.json +# timestamp: 2026-07-07T19:00:11+00:00 from __future__ import annotations @@ -12,11 +12,11 @@ class ImportFile(RootModel[bytes]): - root: Annotated[bytes, Field(title='Import File')] + root: Annotated[bytes, Field(title="Import File")] class BodyImportTestRunExecutionApiV1TestRunExecutionsImportPost(BaseModel): - import_file: Annotated[bytes, Field(title='Import File')] + import_file: Annotated[bytes, Field(title="Import File")] class BodyImportprojectConfigApiV1ProjectsImportPost(BodyImportTestRunExecutionApiV1TestRunExecutionsImportPost): @@ -24,11 +24,11 @@ class BodyImportprojectConfigApiV1ProjectsImportPost(BodyImportTestRunExecutionA class File(RootModel[bytes]): - root: Annotated[bytes, Field(title='File')] + root: Annotated[bytes, Field(title="File")] class BodyUploadFileApiV1TestRunExecutionsFileUploadPost(BaseModel): - file: Annotated[bytes, Field(title='File')] + file: Annotated[bytes, Field(title="File")] class BodyUploadPicsApiV1ProjectsIdUploadPicsPut(BodyUploadFileApiV1TestRunExecutionsFileUploadPost): @@ -36,30 +36,30 @@ class BodyUploadPicsApiV1ProjectsIdUploadPicsPut(BodyUploadFileApiV1TestRunExecu class ChipServerInfo(BaseModel): - node_id: Annotated[int, Field(title='Node Id')] - node_id_hex: Annotated[str, Field(title='Node Id Hex')] - manual_pairing_code: Annotated[str | None, Field(title='Manual Pairing Code')] = None + node_id: Annotated[int, Field(title="Node Id")] + node_id_hex: Annotated[str, Field(title="Node Id Hex")] + manual_pairing_code: Annotated[str | None, Field(title="Manual Pairing Code")] = None class Msg(BaseModel): - msg: Annotated[str, Field(title='Msg')] + msg: Annotated[str, Field(title="Msg")] class Name(RootModel[str]): - root: Annotated[str, Field(title='Name')] + root: Annotated[str, Field(title="Name")] class Id(RootModel[int]): - root: Annotated[int, Field(title='Id')] + root: Annotated[int, Field(title="Id")] class Operator(BaseModel): - name: Annotated[str, Field(title='Name')] - id: Annotated[int, Field(title='Id')] + name: Annotated[str, Field(title="Name")] + id: Annotated[int, Field(title="Id")] class OperatorCreate(BaseModel): - name: Annotated[str, Field(title='Name')] + name: Annotated[str, Field(title="Name")] class OperatorToExport(OperatorCreate): @@ -67,99 +67,99 @@ class OperatorToExport(OperatorCreate): class OperatorUpdate(BaseModel): - name: Annotated[str | None, Field(title='Name')] = None + name: Annotated[str | None, Field(title="Name")] = None class PICSApplicableTestCases(BaseModel): - test_cases: Annotated[list[str], Field(title='Test Cases')] + test_cases: Annotated[list[str], Field(title="Test Cases")] class PICSItem(BaseModel): - number: Annotated[str, Field(title='Number')] - enabled: Annotated[bool, Field(title='Enabled')] + number: Annotated[str, Field(title="Number")] + enabled: Annotated[bool, Field(title="Enabled")] class PublicId(RootModel[str]): - root: Annotated[str, Field(title='Public Id')] + root: Annotated[str, Field(title="Public Id")] class Title(RootModel[str]): - root: Annotated[str, Field(title='Title')] + root: Annotated[str, Field(title="Title")] class Description(RootModel[str]): - root: Annotated[str, Field(title='Description')] + root: Annotated[str, Field(title="Description")] class Version(RootModel[str]): - root: Annotated[str, Field(title='Version')] + root: Annotated[str, Field(title="Version")] class SourceHash(RootModel[str]): - root: Annotated[str, Field(title='Source Hash')] + root: Annotated[str, Field(title="Source Hash")] class Mandatory(RootModel[bool]): - root: Annotated[bool, Field(title='Mandatory')] = False + root: Annotated[bool, Field(title="Mandatory")] = False class TestCaseMetadata(BaseModel): - public_id: Annotated[str, Field(title='Public Id')] - title: Annotated[str, Field(title='Title')] - description: Annotated[str, Field(title='Description')] - version: Annotated[str, Field(title='Version')] - source_hash: Annotated[str, Field(title='Source Hash')] - mandatory: Annotated[bool | None, Field(title='Mandatory')] = False - id: Annotated[int, Field(title='Id')] + public_id: Annotated[str, Field(title="Public Id")] + title: Annotated[str, Field(title="Title")] + description: Annotated[str, Field(title="Description")] + version: Annotated[str, Field(title="Version")] + source_hash: Annotated[str, Field(title="Source Hash")] + mandatory: Annotated[bool | None, Field(title="Mandatory")] = False + id: Annotated[int, Field(title="Id")] class TestCaseMetadataBase(BaseModel): - public_id: Annotated[str, Field(title='Public Id')] - title: Annotated[str, Field(title='Title')] - description: Annotated[str, Field(title='Description')] - version: Annotated[str, Field(title='Version')] - source_hash: Annotated[str, Field(title='Source Hash')] - mandatory: Annotated[bool | None, Field(title='Mandatory')] = False + public_id: Annotated[str, Field(title="Public Id")] + title: Annotated[str, Field(title="Title")] + description: Annotated[str, Field(title="Description")] + version: Annotated[str, Field(title="Version")] + source_hash: Annotated[str, Field(title="Source Hash")] + mandatory: Annotated[bool | None, Field(title="Mandatory")] = False class TestEnvironmentConfig(BaseModel): - test_parameters: Annotated[dict[str, Any] | None, Field(title='Test Parameters')] = None + test_parameters: Annotated[dict[str, Any] | None, Field(title="Test Parameters")] = None class TestHarnessBackendVersion(BaseModel): - version: Annotated[str, Field(title='Version')] - sha: Annotated[str, Field(title='Sha')] - sdk_sha: Annotated[str, Field(title='Sdk Sha')] - sdk_docker_tag: Annotated[str, Field(title='Sdk Docker Tag')] - db_revision: Annotated[str, Field(title='Db Revision')] + version: Annotated[str, Field(title="Version")] + sha: Annotated[str, Field(title="Sha")] + sdk_sha: Annotated[str, Field(title="Sdk Sha")] + sdk_docker_tag: Annotated[str, Field(title="Sdk Docker Tag")] + db_revision: Annotated[str, Field(title="Db Revision")] class TestMetadata(BaseModel): - public_id: Annotated[str, Field(title='Public Id')] - version: Annotated[str, Field(title='Version')] - title: Annotated[str, Field(title='Title')] - description: Annotated[str, Field(title='Description')] - mandatory: Annotated[bool | None, Field(title='Mandatory')] = False + public_id: Annotated[str, Field(title="Public Id")] + version: Annotated[str, Field(title="Version")] + title: Annotated[str, Field(title="Title")] + description: Annotated[str, Field(title="Description")] + mandatory: Annotated[bool | None, Field(title="Mandatory")] = False class TestRunConfig(BaseModel): - name: Annotated[str, Field(title='Name')] - dut_name: Annotated[str, Field(title='Dut Name')] - selected_tests: Annotated[dict[str, dict[str, dict[str, int]]] | None, Field(title='Selected Tests')] = {} - id: Annotated[int, Field(title='Id')] + name: Annotated[str, Field(title="Name")] + dut_name: Annotated[str, Field(title="Dut Name")] + selected_tests: Annotated[dict[str, dict[str, dict[str, int]]] | None, Field(title="Selected Tests")] = {} + id: Annotated[int, Field(title="Id")] class TestRunConfigCreate(BaseModel): - name: Annotated[str, Field(title='Name')] - dut_name: Annotated[str, Field(title='Dut Name')] - selected_tests: Annotated[dict[str, dict[str, dict[str, int]]] | None, Field(title='Selected Tests')] = {} + name: Annotated[str, Field(title="Name")] + dut_name: Annotated[str, Field(title="Dut Name")] + selected_tests: Annotated[dict[str, dict[str, dict[str, int]]] | None, Field(title="Selected Tests")] = {} class TestRunConfigToExport(BaseModel): - name: Annotated[str, Field(title='Name')] - dut_name: Annotated[str, Field(title='Dut Name')] - selected_tests: Annotated[dict[str, dict[str, dict[str, int]]] | None, Field(title='Selected Tests')] = {} - created_at: Annotated[datetime, Field(title='Created At')] + name: Annotated[str, Field(title="Name")] + dut_name: Annotated[str, Field(title="Dut Name")] + selected_tests: Annotated[dict[str, dict[str, dict[str, int]]] | None, Field(title="Selected Tests")] = {} + created_at: Annotated[datetime, Field(title="Created At")] class TestRunConfigUpdate(OperatorCreate): @@ -167,74 +167,74 @@ class TestRunConfigUpdate(OperatorCreate): class TestRunExecutionCreate(BaseModel): - title: Annotated[str, Field(title='Title')] - description: Annotated[str | None, Field(title='Description')] = None - execution_config: Annotated[dict[str, Any] | None, Field(title='Execution Config')] = None - execution_pics: Annotated[dict[str, Any] | None, Field(title='Execution Pics')] = None - certification_mode: Annotated[bool | None, Field(title='Certification Mode')] = False - test_run_config_id: Annotated[int | None, Field(title='Test Run Config Id')] = None - project_id: Annotated[int | None, Field(title='Project Id')] = None - operator_id: Annotated[int | None, Field(title='Operator Id')] = None + title: Annotated[str, Field(title="Title")] + description: Annotated[str | None, Field(title="Description")] = None + execution_config: Annotated[dict[str, Any] | None, Field(title="Execution Config")] = None + execution_pics: Annotated[dict[str, Any] | None, Field(title="Execution Pics")] = None + certification_mode: Annotated[bool | None, Field(title="Certification Mode")] = False + test_run_config_id: Annotated[int | None, Field(title="Test Run Config Id")] = None + project_id: Annotated[int | None, Field(title="Project Id")] = None + operator_id: Annotated[int | None, Field(title="Operator Id")] = None class TestRunExecutionStats(BaseModel): - test_case_count: Annotated[int | None, Field(title='Test Case Count')] = 0 - states: Annotated[dict[str, int] | None, Field(title='States')] = {} + test_case_count: Annotated[int | None, Field(title="Test Case Count")] = 0 + states: Annotated[dict[str, int] | None, Field(title="States")] = {} class TestRunLogEntry(BaseModel): - level: Annotated[str, Field(title='Level')] - timestamp: Annotated[float, Field(title='Timestamp')] - message: Annotated[str, Field(title='Message')] - test_suite_execution_index: Annotated[int | None, Field(title='Test Suite Execution Index')] = None - test_case_execution_index: Annotated[int | None, Field(title='Test Case Execution Index')] = None - test_step_execution_index: Annotated[int | None, Field(title='Test Step Execution Index')] = None + level: Annotated[str, Field(title="Level")] + timestamp: Annotated[float, Field(title="Timestamp")] + message: Annotated[str, Field(title="Message")] + test_suite_execution_index: Annotated[int | None, Field(title="Test Suite Execution Index")] = None + test_case_execution_index: Annotated[int | None, Field(title="Test Case Execution Index")] = None + test_step_execution_index: Annotated[int | None, Field(title="Test Step Execution Index")] = None class TestRunnerState(Enum): - idle = 'idle' - loading = 'loading' - ready = 'ready' - running = 'running' + idle = "idle" + loading = "loading" + ready = "ready" + running = "running" class TestRunnerStatus(BaseModel): state: TestRunnerState - test_run_execution_id: Annotated[int | None, Field(title='Test Run Execution Id')] = None + test_run_execution_id: Annotated[int | None, Field(title="Test Run Execution Id")] = None class TestStateEnum(Enum): - pending = 'pending' - executing = 'executing' - pending_actuation = 'pending_actuation' - passed = 'passed' - failed = 'failed' - error = 'error' - not_applicable = 'not_applicable' - cancelled = 'cancelled' + pending = "pending" + executing = "executing" + pending_actuation = "pending_actuation" + passed = "passed" + failed = "failed" + error = "error" + not_applicable = "not_applicable" + cancelled = "cancelled" class TestStepExecution(BaseModel): state: TestStateEnum - title: Annotated[str, Field(title='Title')] - execution_index: Annotated[int, Field(title='Execution Index')] - id: Annotated[int, Field(title='Id')] - test_case_execution_id: Annotated[int, Field(title='Test Case Execution Id')] - started_at: Annotated[datetime | None, Field(title='Started At')] = None - completed_at: Annotated[datetime | None, Field(title='Completed At')] = None - errors: Annotated[list[str] | None, Field(title='Errors')] = None - failures: Annotated[list[str] | None, Field(title='Failures')] = None + title: Annotated[str, Field(title="Title")] + execution_index: Annotated[int, Field(title="Execution Index")] + id: Annotated[int, Field(title="Id")] + test_case_execution_id: Annotated[int, Field(title="Test Case Execution Id")] + started_at: Annotated[datetime | None, Field(title="Started At")] = None + completed_at: Annotated[datetime | None, Field(title="Completed At")] = None + errors: Annotated[list[str] | None, Field(title="Errors")] = None + failures: Annotated[list[str] | None, Field(title="Failures")] = None class TestStepExecutionToExport(BaseModel): state: TestStateEnum - title: Annotated[str, Field(title='Title')] - execution_index: Annotated[int, Field(title='Execution Index')] - started_at: Annotated[datetime | None, Field(title='Started At')] = None - completed_at: Annotated[datetime | None, Field(title='Completed At')] = None - errors: Annotated[list[str] | None, Field(title='Errors')] = None - failures: Annotated[list[str] | None, Field(title='Failures')] = None - created_at: Annotated[datetime, Field(title='Created At')] + title: Annotated[str, Field(title="Title")] + execution_index: Annotated[int, Field(title="Execution Index")] + started_at: Annotated[datetime | None, Field(title="Started At")] = None + completed_at: Annotated[datetime | None, Field(title="Completed At")] = None + errors: Annotated[list[str] | None, Field(title="Errors")] = None + failures: Annotated[list[str] | None, Field(title="Failures")] = None + created_at: Annotated[datetime, Field(title="Created At")] class TestSuiteMetadata(TestCaseMetadata): @@ -246,31 +246,31 @@ class TestSuiteMetadataBase(TestCaseMetadataBase): class ValidationError(BaseModel): - loc: Annotated[list[str | int], Field(title='Location')] - msg: Annotated[str, Field(title='Message')] - type: Annotated[str, Field(title='Error Type')] + loc: Annotated[list[str | int], Field(title="Location")] + msg: Annotated[str, Field(title="Message")] + type: Annotated[str, Field(title="Error Type")] class BodyCreateCliTestRunExecutionApiV1TestRunExecutionsCliPost(BaseModel): test_run_execution_in: TestRunExecutionCreate - selected_tests: Annotated[dict[str, dict[str, dict[str, int]]], Field(title='Selected Tests')] - execution_config: Annotated[dict[str, Any] | None, Field(title='Execution Config')] = None - execution_pics: Annotated[dict[str, Any] | None, Field(title='Execution Pics')] = None + selected_tests: Annotated[dict[str, dict[str, dict[str, int]]], Field(title="Selected Tests")] + execution_config: Annotated[dict[str, Any] | None, Field(title="Execution Config")] = None + execution_pics: Annotated[dict[str, Any] | None, Field(title="Execution Pics")] = None class BodyCreateTestRunExecutionApiV1TestRunExecutionsPost(BaseModel): test_run_execution_in: TestRunExecutionCreate - selected_tests: Annotated[dict[str, dict[str, dict[str, int]]], Field(title='Selected Tests')] + selected_tests: Annotated[dict[str, dict[str, dict[str, int]]], Field(title="Selected Tests")] class HTTPValidationError(BaseModel): - detail: Annotated[list[ValidationError] | None, Field(title='Detail')] = None + detail: Annotated[list[ValidationError] | None, Field(title="Detail")] = None class PICSCluster(BaseModel): - name: Annotated[str, Field(title='Name')] + name: Annotated[str, Field(title="Name")] items: Annotated[ - dict[str, PICSItem] | None, Field(default_factory=lambda: PICSItem.model_validate({}), title='Items') + dict[str, PICSItem] | None, Field(default_factory=lambda: PICSItem.model_validate({}), title="Items") ] @@ -280,191 +280,191 @@ class TestCase(BaseModel): class TestCaseExecution(BaseModel): state: TestStateEnum - public_id: Annotated[str, Field(title='Public Id')] - execution_index: Annotated[int, Field(title='Execution Index')] - id: Annotated[int, Field(title='Id')] - test_suite_execution_id: Annotated[int, Field(title='Test Suite Execution Id')] - test_case_metadata_id: Annotated[int, Field(title='Test Case Metadata Id')] - started_at: Annotated[datetime | None, Field(title='Started At')] = None - completed_at: Annotated[datetime | None, Field(title='Completed At')] = None - errors: Annotated[list[str] | None, Field(title='Errors')] = None + public_id: Annotated[str, Field(title="Public Id")] + execution_index: Annotated[int, Field(title="Execution Index")] + id: Annotated[int, Field(title="Id")] + test_suite_execution_id: Annotated[int, Field(title="Test Suite Execution Id")] + test_case_metadata_id: Annotated[int, Field(title="Test Case Metadata Id")] + started_at: Annotated[datetime | None, Field(title="Started At")] = None + completed_at: Annotated[datetime | None, Field(title="Completed At")] = None + errors: Annotated[list[str] | None, Field(title="Errors")] = None test_case_metadata: TestCaseMetadata - test_step_executions: Annotated[list[TestStepExecution], Field(title='Test Step Executions')] + test_step_executions: Annotated[list[TestStepExecution], Field(title="Test Step Executions")] class TestCaseExecutionToExport(BaseModel): state: TestStateEnum - public_id: Annotated[str, Field(title='Public Id')] - execution_index: Annotated[int, Field(title='Execution Index')] - started_at: Annotated[datetime | None, Field(title='Started At')] = None - completed_at: Annotated[datetime | None, Field(title='Completed At')] = None - errors: Annotated[list[str] | None, Field(title='Errors')] = None + public_id: Annotated[str, Field(title="Public Id")] + execution_index: Annotated[int, Field(title="Execution Index")] + started_at: Annotated[datetime | None, Field(title="Started At")] = None + completed_at: Annotated[datetime | None, Field(title="Completed At")] = None + errors: Annotated[list[str] | None, Field(title="Errors")] = None test_case_metadata: TestCaseMetadataBase - test_step_executions: Annotated[list[TestStepExecutionToExport], Field(title='Test Step Executions')] - created_at: Annotated[datetime, Field(title='Created At')] + test_step_executions: Annotated[list[TestStepExecutionToExport], Field(title="Test Step Executions")] + created_at: Annotated[datetime, Field(title="Created At")] class TestRunExecution(BaseModel): - title: Annotated[str, Field(title='Title')] - description: Annotated[str | None, Field(title='Description')] = None - execution_config: Annotated[dict[str, Any] | None, Field(title='Execution Config')] = None - execution_pics: Annotated[dict[str, Any] | None, Field(title='Execution Pics')] = None - certification_mode: Annotated[bool | None, Field(title='Certification Mode')] = False - test_run_config_id: Annotated[int | None, Field(title='Test Run Config Id')] = None - project_id: Annotated[int | None, Field(title='Project Id')] = None - id: Annotated[int, Field(title='Id')] + title: Annotated[str, Field(title="Title")] + description: Annotated[str | None, Field(title="Description")] = None + execution_config: Annotated[dict[str, Any] | None, Field(title="Execution Config")] = None + execution_pics: Annotated[dict[str, Any] | None, Field(title="Execution Pics")] = None + certification_mode: Annotated[bool | None, Field(title="Certification Mode")] = False + test_run_config_id: Annotated[int | None, Field(title="Test Run Config Id")] = None + project_id: Annotated[int | None, Field(title="Project Id")] = None + id: Annotated[int, Field(title="Id")] state: TestStateEnum - started_at: Annotated[datetime | None, Field(title='Started At')] = None - completed_at: Annotated[datetime | None, Field(title='Completed At')] = None - imported_at: Annotated[datetime | None, Field(title='Imported At')] = None - archived_at: Annotated[datetime | None, Field(title='Archived At')] = None + started_at: Annotated[datetime | None, Field(title="Started At")] = None + completed_at: Annotated[datetime | None, Field(title="Completed At")] = None + imported_at: Annotated[datetime | None, Field(title="Imported At")] = None + archived_at: Annotated[datetime | None, Field(title="Archived At")] = None operator: Operator | None = None class TestRunExecutionInDBBase(BaseModel): - title: Annotated[str, Field(title='Title')] - description: Annotated[str | None, Field(title='Description')] = None - execution_config: Annotated[dict[str, Any] | None, Field(title='Execution Config')] = None - execution_pics: Annotated[dict[str, Any] | None, Field(title='Execution Pics')] = None - certification_mode: Annotated[bool | None, Field(title='Certification Mode')] = False - test_run_config_id: Annotated[int | None, Field(title='Test Run Config Id')] = None - project_id: Annotated[int | None, Field(title='Project Id')] = None - id: Annotated[int, Field(title='Id')] + title: Annotated[str, Field(title="Title")] + description: Annotated[str | None, Field(title="Description")] = None + execution_config: Annotated[dict[str, Any] | None, Field(title="Execution Config")] = None + execution_pics: Annotated[dict[str, Any] | None, Field(title="Execution Pics")] = None + certification_mode: Annotated[bool | None, Field(title="Certification Mode")] = False + test_run_config_id: Annotated[int | None, Field(title="Test Run Config Id")] = None + project_id: Annotated[int | None, Field(title="Project Id")] = None + id: Annotated[int, Field(title="Id")] state: TestStateEnum - started_at: Annotated[datetime | None, Field(title='Started At')] = None - completed_at: Annotated[datetime | None, Field(title='Completed At')] = None - imported_at: Annotated[datetime | None, Field(title='Imported At')] = None - archived_at: Annotated[datetime | None, Field(title='Archived At')] = None + started_at: Annotated[datetime | None, Field(title="Started At")] = None + completed_at: Annotated[datetime | None, Field(title="Completed At")] = None + imported_at: Annotated[datetime | None, Field(title="Imported At")] = None + archived_at: Annotated[datetime | None, Field(title="Archived At")] = None class TestRunExecutionWithStats(BaseModel): - title: Annotated[str, Field(title='Title')] - description: Annotated[str | None, Field(title='Description')] = None - execution_config: Annotated[dict[str, Any] | None, Field(title='Execution Config')] = None - execution_pics: Annotated[dict[str, Any] | None, Field(title='Execution Pics')] = None - certification_mode: Annotated[bool | None, Field(title='Certification Mode')] = False - test_run_config_id: Annotated[int | None, Field(title='Test Run Config Id')] = None - project_id: Annotated[int | None, Field(title='Project Id')] = None - id: Annotated[int, Field(title='Id')] + title: Annotated[str, Field(title="Title")] + description: Annotated[str | None, Field(title="Description")] = None + execution_config: Annotated[dict[str, Any] | None, Field(title="Execution Config")] = None + execution_pics: Annotated[dict[str, Any] | None, Field(title="Execution Pics")] = None + certification_mode: Annotated[bool | None, Field(title="Certification Mode")] = False + test_run_config_id: Annotated[int | None, Field(title="Test Run Config Id")] = None + project_id: Annotated[int | None, Field(title="Project Id")] = None + id: Annotated[int, Field(title="Id")] state: TestStateEnum - started_at: Annotated[datetime | None, Field(title='Started At')] = None - completed_at: Annotated[datetime | None, Field(title='Completed At')] = None - imported_at: Annotated[datetime | None, Field(title='Imported At')] = None - archived_at: Annotated[datetime | None, Field(title='Archived At')] = None + started_at: Annotated[datetime | None, Field(title="Started At")] = None + completed_at: Annotated[datetime | None, Field(title="Completed At")] = None + imported_at: Annotated[datetime | None, Field(title="Imported At")] = None + archived_at: Annotated[datetime | None, Field(title="Archived At")] = None operator: Operator | None = None test_case_stats: TestRunExecutionStats class TestSuite(BaseModel): metadata: TestMetadata - test_cases: Annotated[dict[str, TestCase], Field(title='Test Cases')] + test_cases: Annotated[dict[str, TestCase], Field(title="Test Cases")] class TestSuiteExecution(BaseModel): state: TestStateEnum - public_id: Annotated[str, Field(title='Public Id')] - execution_index: Annotated[int, Field(title='Execution Index')] - collection_id: Annotated[str, Field(title='Collection Id')] - mandatory: Annotated[bool | None, Field(title='Mandatory')] = False - id: Annotated[int, Field(title='Id')] - test_run_execution_id: Annotated[int, Field(title='Test Run Execution Id')] - test_suite_metadata_id: Annotated[int, Field(title='Test Suite Metadata Id')] - started_at: Annotated[datetime | None, Field(title='Started At')] = None - completed_at: Annotated[datetime | None, Field(title='Completed At')] = None - errors: Annotated[list[str] | None, Field(title='Errors')] = None - test_case_executions: Annotated[list[TestCaseExecution], Field(title='Test Case Executions')] + public_id: Annotated[str, Field(title="Public Id")] + execution_index: Annotated[int, Field(title="Execution Index")] + collection_id: Annotated[str, Field(title="Collection Id")] + mandatory: Annotated[bool | None, Field(title="Mandatory")] = False + id: Annotated[int, Field(title="Id")] + test_run_execution_id: Annotated[int, Field(title="Test Run Execution Id")] + test_suite_metadata_id: Annotated[int, Field(title="Test Suite Metadata Id")] + started_at: Annotated[datetime | None, Field(title="Started At")] = None + completed_at: Annotated[datetime | None, Field(title="Completed At")] = None + errors: Annotated[list[str] | None, Field(title="Errors")] = None + test_case_executions: Annotated[list[TestCaseExecution], Field(title="Test Case Executions")] test_suite_metadata: TestSuiteMetadata class TestSuiteExecutionToExport(BaseModel): state: TestStateEnum - public_id: Annotated[str, Field(title='Public Id')] - execution_index: Annotated[int, Field(title='Execution Index')] - collection_id: Annotated[str, Field(title='Collection Id')] - mandatory: Annotated[bool | None, Field(title='Mandatory')] = False - started_at: Annotated[datetime | None, Field(title='Started At')] = None - completed_at: Annotated[datetime | None, Field(title='Completed At')] = None - errors: Annotated[list[str] | None, Field(title='Errors')] = None - test_case_executions: Annotated[list[TestCaseExecutionToExport], Field(title='Test Case Executions')] + public_id: Annotated[str, Field(title="Public Id")] + execution_index: Annotated[int, Field(title="Execution Index")] + collection_id: Annotated[str, Field(title="Collection Id")] + mandatory: Annotated[bool | None, Field(title="Mandatory")] = False + started_at: Annotated[datetime | None, Field(title="Started At")] = None + completed_at: Annotated[datetime | None, Field(title="Completed At")] = None + errors: Annotated[list[str] | None, Field(title="Errors")] = None + test_case_executions: Annotated[list[TestCaseExecutionToExport], Field(title="Test Case Executions")] test_suite_metadata: TestSuiteMetadataBase - created_at: Annotated[datetime, Field(title='Created At')] + created_at: Annotated[datetime, Field(title="Created At")] class PICS(BaseModel): clusters: Annotated[ - dict[str, PICSCluster] | None, Field(default_factory=lambda: PICSCluster.model_validate({}), title='Clusters') + dict[str, PICSCluster] | None, Field(default_factory=lambda: PICSCluster.model_validate({}), title="Clusters") ] class Project(BaseModel): - name: Annotated[str, Field(title='Name')] - config: Annotated[dict[str, Any] | None, Field(title='Config')] = None - pics: Annotated[PICS | None, Field(default_factory=lambda: PICS.model_validate({'clusters': {}}), title='Pics')] - id: Annotated[int, Field(title='Id')] - created_at: Annotated[datetime, Field(title='Created At')] - updated_at: Annotated[datetime, Field(title='Updated At')] - archived_at: Annotated[datetime | None, Field(title='Archived At')] = None + name: Annotated[str, Field(title="Name")] + config: Annotated[dict[str, Any] | None, Field(title="Config")] = None + pics: Annotated[PICS | None, Field(default_factory=lambda: PICS.model_validate({"clusters": {}}), title="Pics")] + id: Annotated[int, Field(title="Id")] + created_at: Annotated[datetime, Field(title="Created At")] + updated_at: Annotated[datetime, Field(title="Updated At")] + archived_at: Annotated[datetime | None, Field(title="Archived At")] = None class ProjectCreate(BaseModel): - name: Annotated[str, Field(title='Name')] - config: Annotated[dict[str, Any] | None, Field(title='Config')] = None - pics: Annotated[PICS | None, Field(default_factory=lambda: PICS.model_validate({'clusters': {}}), title='Pics')] + name: Annotated[str, Field(title="Name")] + config: Annotated[dict[str, Any] | None, Field(title="Config")] = None + pics: Annotated[PICS | None, Field(default_factory=lambda: PICS.model_validate({"clusters": {}}), title="Pics")] class ProjectUpdate(BaseModel): - name: Annotated[str | None, Field(title='Name')] = None - config: Annotated[dict[str, Any] | None, Field(title='Config')] = None + name: Annotated[str | None, Field(title="Name")] = None + config: Annotated[dict[str, Any] | None, Field(title="Config")] = None pics: PICS | None = None class TestCollection(BaseModel): - name: Annotated[str, Field(title='Name')] - path: Annotated[str, Field(title='Path')] - test_suites: Annotated[dict[str, TestSuite], Field(title='Test Suites')] + name: Annotated[str, Field(title="Name")] + path: Annotated[str, Field(title="Path")] + test_suites: Annotated[dict[str, TestSuite], Field(title="Test Suites")] class TestCollections(BaseModel): - test_collections: Annotated[dict[str, TestCollection], Field(title='Test Collections')] + test_collections: Annotated[dict[str, TestCollection], Field(title="Test Collections")] class TestRunExecutionToExport(BaseModel): - title: Annotated[str, Field(title='Title')] - description: Annotated[str | None, Field(title='Description')] = None - execution_config: Annotated[dict[str, Any] | None, Field(title='Execution Config')] = None - execution_pics: Annotated[dict[str, Any] | None, Field(title='Execution Pics')] = None - certification_mode: Annotated[bool | None, Field(title='Certification Mode')] = False + title: Annotated[str, Field(title="Title")] + description: Annotated[str | None, Field(title="Description")] = None + execution_config: Annotated[dict[str, Any] | None, Field(title="Execution Config")] = None + execution_pics: Annotated[dict[str, Any] | None, Field(title="Execution Pics")] = None + certification_mode: Annotated[bool | None, Field(title="Certification Mode")] = False state: TestStateEnum - started_at: Annotated[datetime | None, Field(title='Started At')] = None - completed_at: Annotated[datetime | None, Field(title='Completed At')] = None - archived_at: Annotated[datetime | None, Field(title='Archived At')] = None - test_suite_executions: Annotated[ - list[TestSuiteExecutionToExport] | None, Field(title='Test Suite Executions') - ] = None - created_at: Annotated[datetime, Field(title='Created At')] - log: Annotated[list[TestRunLogEntry], Field(title='Log')] + started_at: Annotated[datetime | None, Field(title="Started At")] = None + completed_at: Annotated[datetime | None, Field(title="Completed At")] = None + archived_at: Annotated[datetime | None, Field(title="Archived At")] = None + test_suite_executions: Annotated[list[TestSuiteExecutionToExport] | None, Field(title="Test Suite Executions")] = ( + None + ) + created_at: Annotated[datetime, Field(title="Created At")] + log: Annotated[list[TestRunLogEntry], Field(title="Log")] operator: OperatorToExport | None = None test_run_config: TestRunConfigToExport | None = None class TestRunExecutionWithChildren(BaseModel): - title: Annotated[str, Field(title='Title')] - description: Annotated[str | None, Field(title='Description')] = None - execution_config: Annotated[dict[str, Any] | None, Field(title='Execution Config')] = None - execution_pics: Annotated[dict[str, Any] | None, Field(title='Execution Pics')] = None - certification_mode: Annotated[bool | None, Field(title='Certification Mode')] = False - test_run_config_id: Annotated[int | None, Field(title='Test Run Config Id')] = None - project_id: Annotated[int | None, Field(title='Project Id')] = None - id: Annotated[int, Field(title='Id')] + title: Annotated[str, Field(title="Title")] + description: Annotated[str | None, Field(title="Description")] = None + execution_config: Annotated[dict[str, Any] | None, Field(title="Execution Config")] = None + execution_pics: Annotated[dict[str, Any] | None, Field(title="Execution Pics")] = None + certification_mode: Annotated[bool | None, Field(title="Certification Mode")] = False + test_run_config_id: Annotated[int | None, Field(title="Test Run Config Id")] = None + project_id: Annotated[int | None, Field(title="Project Id")] = None + id: Annotated[int, Field(title="Id")] state: TestStateEnum - started_at: Annotated[datetime | None, Field(title='Started At')] = None - completed_at: Annotated[datetime | None, Field(title='Completed At')] = None - imported_at: Annotated[datetime | None, Field(title='Imported At')] = None - archived_at: Annotated[datetime | None, Field(title='Archived At')] = None + started_at: Annotated[datetime | None, Field(title="Started At")] = None + completed_at: Annotated[datetime | None, Field(title="Completed At")] = None + imported_at: Annotated[datetime | None, Field(title="Imported At")] = None + archived_at: Annotated[datetime | None, Field(title="Archived At")] = None operator: Operator | None = None - test_suite_executions: Annotated[list[TestSuiteExecution] | None, Field(title='Test Suite Executions')] = None + test_suite_executions: Annotated[list[TestSuiteExecution] | None, Field(title="Test Suite Executions")] = None class ExportedTestRunExecution(BaseModel): - db_revision: Annotated[str, Field(title='Db Revision')] + db_revision: Annotated[str, Field(title="Db Revision")] test_run_execution: TestRunExecutionToExport diff --git a/th_cli/commands/project.py b/th_cli/commands/project.py index ec64527..c8c3b90 100644 --- a/th_cli/commands/project.py +++ b/th_cli/commands/project.py @@ -23,7 +23,13 @@ from th_cli.api_lib_autogen.api_client import SyncApis from th_cli.api_lib_autogen.exceptions import UnexpectedResponse -from th_cli.api_lib_autogen.models import PICS, BodyImportprojectConfigApiV1ProjectsImportPost, Project, ProjectCreate, ProjectUpdate +from th_cli.api_lib_autogen.models import ( + PICS, + BodyImportprojectConfigApiV1ProjectsImportPost, + Project, + ProjectCreate, + ProjectUpdate, +) from th_cli.client import get_client from th_cli.colorize import ( colorize_cmd_help, @@ -40,6 +46,7 @@ TABLE_FORMAT = "{:<5} {:25} {:28}" + # Click command group for project management @click.group( short_help=colorize_help("Manage projects"), @@ -132,11 +139,7 @@ def create(name: str, config: str | None, pics_config_folder: str | None) -> Non help=colorize_help("Print JSON response for more details"), ) @click.option( - "--config", - "-c", - is_flag=True, - default=False, - help=colorize_help("Print project configuration in JSON format") + "--config", "-c", is_flag=True, default=False, help=colorize_help("Print project configuration in JSON format") ) def list_projects( id: int | None, @@ -275,18 +278,14 @@ def import_project(file: str) -> None: "--grouped", is_flag=True, default=False, - help=colorize_help( - "Download grouped logs (organized by test case state) instead of flat log files" - ), + help=colorize_help("Download grouped logs (organized by test case state) instead of flat log files"), ) @click.option( "--output-file", "-o", type=click.Path(file_okay=True, dir_okay=False), required=False, - help=colorize_help( - "Output zip file path (defaults to -logs.zip)" - ), + help=colorize_help("Output zip file path (defaults to -logs.zip)"), ) def logs(id: int, grouped: bool, output_file: str | None) -> None: """Download all logs for a project as a single zip archive""" @@ -502,9 +501,7 @@ def _import_project(sync_apis: SyncApis, file: str) -> None: handle_api_error(e, f"import project from '{file}'") -def _download_project_logs( - sync_apis: SyncApis, id: int, grouped: bool, output_file: str | None -) -> None: +def _download_project_logs(sync_apis: SyncApis, id: int, grouped: bool, output_file: str | None) -> None: """Download all logs for a project as a single zip archive""" click.echo( f"Downloading logs for project {id}... this may take a while for " @@ -520,9 +517,7 @@ def _download_project_logs( if not output_file: try: project = sync_apis.projects_api.read_project_api_v1_projects__id__get(id=id) - safe_name = "".join( - c if c.isalnum() or c in "-_" else "_" for c in (project.name or f"project-{id}") - ) + safe_name = "".join(c if c.isalnum() or c in "-_" else "_" for c in (project.name or f"project-{id}")) except Exception: # The logs have already been downloaded successfully at this point; # any failure fetching the project name (network glitch, unexpected diff --git a/th_cli/commands/run_tests.py b/th_cli/commands/run_tests.py index ef74a6e..6fa9e51 100644 --- a/th_cli/commands/run_tests.py +++ b/th_cli/commands/run_tests.py @@ -42,7 +42,15 @@ from th_cli.exceptions import CLIError, handle_api_error from th_cli.test_run.camera.two_way_talk_handler import TwoWayTalkHandler from th_cli.test_run.websocket import TestRunSocket -from th_cli.utils import DEFAULT_CLI_PROJECT_NAME, build_test_selection, convert_nested_to_dict, load_json_config, load_tc_params_mapping, merge_configs, read_pics_config +from th_cli.utils import ( + DEFAULT_CLI_PROJECT_NAME, + build_test_selection, + convert_nested_to_dict, + load_json_config, + load_tc_params_mapping, + merge_configs, + read_pics_config, +) from th_cli.validation import validate_directory_path, validate_file_path, validate_tc_params_file, validate_test_ids # Constants @@ -310,7 +318,7 @@ async def run_tests( finally: # Stop log streaming test_logging.stop_log_streaming() - + if client: await client.aclose() if _webrtc_handler: diff --git a/th_cli/test_run/log_stream_handler.py b/th_cli/test_run/log_stream_handler.py index 0c56aca..5f6059b 100644 --- a/th_cli/test_run/log_stream_handler.py +++ b/th_cli/test_run/log_stream_handler.py @@ -28,7 +28,7 @@ class LogStreamHandler: def __init__(self, port: int = 8998): """Initialize the log stream handler. - + Args: port: Port number for the HTTP server (default: 8998) """ @@ -37,28 +37,28 @@ def __init__(self, port: int = 8998): self.log_queue: queue.Queue = queue.Queue(maxsize=1000) self.is_running = False self.log_file_path: Optional[str] = None - + def start(self, test_run_title: str = "Test Execution", log_file_path: Optional[str] = None) -> str: """Start the log streaming HTTP server. - + Args: test_run_title: Title of the test run for display log_file_path: Path to the log file for download functionality - + Returns: URL where logs can be viewed """ if self.is_running: logger.warning("Log stream handler already running") return self._get_log_viewer_url() - + try: # Store log file path for download functionality self.log_file_path = log_file_path - + # Get local IP address local_ip = self._get_local_ip() - + # Start HTTP server self.http_server.start( log_queue=self.log_queue, @@ -66,23 +66,23 @@ def start(self, test_run_title: str = "Test Execution", log_file_path: Optional[ local_ip=local_ip, log_file_path=log_file_path, ) - + self.is_running = True - + viewer_url = f"http://{local_ip}:{self.port}" logger.info(f"Log stream viewer started: {viewer_url}") - + return viewer_url - + except Exception as e: logger.error(f"Failed to start log stream handler: {e}") raise - + def stop(self): """Stop the log streaming HTTP server.""" if not self.is_running: return - + try: # Signal end of stream if not self.log_queue.full(): @@ -90,24 +90,19 @@ def stop(self): self.log_queue.put_nowait(None) except queue.Full: pass - + # Stop HTTP server self.http_server.stop() - + self.is_running = False logger.info("Log stream handler stopped") - + except Exception as e: logger.error(f"Error stopping log stream handler: {e}") - - def add_log_entry( - self, - message: str, - level: str = "INFO", - timestamp: Optional[str] = None - ): + + def add_log_entry(self, message: str, level: str = "INFO", timestamp: Optional[str] = None): """Add a log entry to the stream. - + Args: message: Log message text level: Log level (INFO, WARNING, ERROR, DEBUG, etc.) @@ -115,16 +110,16 @@ def add_log_entry( """ if not self.is_running: return - + if timestamp is None: timestamp = datetime.datetime.now().isoformat() - + log_entry = { "message": message, "level": level.upper(), "timestamp": timestamp, } - + try: # Try to add to queue without blocking self.log_queue.put_nowait(log_entry) @@ -132,10 +127,10 @@ def add_log_entry( # Queue is full, skip this entry silently to avoid blocking # This is acceptable for real-time streaming when no browser is connected pass - + def _get_local_ip(self) -> str: """Get the local IP address of the machine. - + Returns: Local IP address as string, or 'localhost' if unable to determine """ @@ -149,10 +144,10 @@ def _get_local_ip(self) -> str: return local_ip except Exception: return "localhost" - + def _get_log_viewer_url(self) -> str: """Get the URL for the log viewer. - + Returns: Log viewer URL """ diff --git a/th_cli/test_run/logging.py b/th_cli/test_run/logging.py index 05fdcd6..3480665 100644 --- a/th_cli/test_run/logging.py +++ b/th_cli/test_run/logging.py @@ -35,16 +35,16 @@ def configure_logger_for_run(title: str, enable_log_streaming: bool = False) -> str: """Configure logger for a test run. - + Args: title: Title of the test run enable_log_streaming: Whether to enable real-time log streaming - + Returns: Path to the log file """ global _log_stream_handler - + # Reset (Remove all sinks from logger) logger.remove() @@ -63,15 +63,14 @@ def configure_logger_for_run(title: str, enable_log_streaming: bool = False) -> _log_stream_handler = LogStreamHandler(port=8998) viewer_url = _log_stream_handler.start(test_run_title=title, log_file_path=log_path) + # Add custom sink that forwards logs to the stream handler def stream_sink(message): """Custom sink that forwards logs to the HTTP stream.""" try: record = message.record _log_stream_handler.add_log_entry( - message=record["message"], - level=record["level"].name, - timestamp=record["time"].isoformat() + message=record["message"], level=record["level"].name, timestamp=record["time"].isoformat() ) except Exception: # Silently fail to avoid disrupting logging @@ -91,7 +90,7 @@ def stream_sink(message): def stop_log_streaming(): """Stop the log streaming server if it's running.""" global _log_stream_handler - + if _log_stream_handler: try: _log_stream_handler.stop() @@ -104,7 +103,7 @@ def stop_log_streaming(): def get_log_stream_url() -> Optional[str]: """Get the URL for the log viewer if streaming is enabled. - + Returns: URL string or None if streaming is not enabled """ diff --git a/th_cli/test_run/logs_http_server.py b/th_cli/test_run/logs_http_server.py index 3a14027..32176b5 100644 --- a/th_cli/test_run/logs_http_server.py +++ b/th_cli/test_run/logs_http_server.py @@ -44,20 +44,20 @@ def do_GET(self): else: logger.warning(f"404 for GET {self.path}") self.send_error(404) - + def download_logs(self): """Serve the log file for download using chunked streaming.""" log_file_path = getattr(self.server, "log_file_path", None) - + if not log_file_path or not Path(log_file_path).exists(): self.send_error(404, "Log file not found") return - + try: file_path = Path(log_file_path) filename = file_path.name file_size = file_path.stat().st_size - + # Send headers self.send_response(200) self.send_header("Content-Type", "text/plain; charset=utf-8") @@ -65,18 +65,18 @@ def download_logs(self): self.send_header("Content-Length", str(file_size)) self.send_header("Access-Control-Allow-Origin", "*") self.end_headers() - + # Stream file in chunks to avoid loading entire file into memory CHUNK_SIZE = 65536 # 64KB chunks bytes_sent = 0 - with open(log_file_path, 'rb') as f: + with open(log_file_path, "rb") as f: while chunk := f.read(CHUNK_SIZE): self.wfile.write(chunk) bytes_sent += len(chunk) logger.info(f"Log file downloaded: {filename} ({bytes_sent} bytes)") - + except (BrokenPipeError, ConnectionResetError, ConnectionAbortedError): # Client disconnected during download - normal, not an error logger.debug("Client disconnected during log file download") @@ -87,7 +87,7 @@ def download_logs(self): def stream_logs(self): """Stream logs using Server-Sent Events (SSE).""" logger.info("Client connected for log stream") - + # Send SSE headers self.send_response(200) self.send_header("Content-Type", "text/event-stream") @@ -105,18 +105,18 @@ def stream_logs(self): logger.info("Starting to stream logs to client via SSE") sent_count = 0 client_disconnected = False - + try: # Send initial connection message if not self._send_sse_event("connected", {"message": "Log stream connected"}): logger.debug("Client disconnected during initial connection") return - + while not client_disconnected: try: # Get log entry from queue with timeout log_entry = log_queue.get(timeout=1.0) - + if log_entry is None: # Signal to stop logger.info("Received end-of-stream signal for logs") self._send_sse_event("end", {"message": "Log stream ended"}) @@ -127,9 +127,9 @@ def stream_logs(self): logger.debug("Client disconnected while streaming") client_disconnected = True break - + sent_count += 1 - + if sent_count % 100 == 0: logger.debug(f"Sent {sent_count} log entries to client") @@ -153,13 +153,13 @@ def stream_logs(self): def _send_sse_event(self, event_type: str, data: dict) -> bool: """Send a Server-Sent Event. - + Returns: True if event was sent successfully, False if client disconnected """ try: event_data = f"event: {event_type}\ndata: {json.dumps(data)}\n\n" - self.wfile.write(event_data.encode('utf-8')) + self.wfile.write(event_data.encode("utf-8")) self.wfile.flush() return True except (BrokenPipeError, ConnectionResetError, ConnectionAbortedError): @@ -173,7 +173,7 @@ def serve_log_viewer(self): """Serve the log viewer HTML page.""" # Get configuration from server test_run_title = getattr(self.server, "test_run_title", "Test Execution") - + # Read HTML template from file try: template_path = Path(__file__).parent / "log_viewer.html" @@ -181,9 +181,7 @@ def serve_log_viewer(self): html_template = f.read() # Replace placeholders - html_content = html_template.format( - test_run_title=html.escape(test_run_title) - ) + html_content = html_template.format(test_run_title=html.escape(test_run_title)) except Exception as e: logger.error(f"Failed to load HTML template: {e}") # Fallback to simple HTML @@ -218,7 +216,7 @@ class LogsHTTPServer: def __init__(self, port: int = 8998): """Initialize the logs HTTP server. - + Args: port: Port number for the HTTP server (default: 8998) """ @@ -234,7 +232,7 @@ def start( log_file_path: Optional[str] = None, ): """Start HTTP server for log streaming. - + Args: log_queue: Queue containing log entries to stream test_run_title: Title of the test run for display diff --git a/th_cli/utils.py b/th_cli/utils.py index e63b5de..2c804ad 100644 --- a/th_cli/utils.py +++ b/th_cli/utils.py @@ -39,12 +39,14 @@ def __print_json(object: Any) -> None: click.echo(colorize_dump(__json_string(object))) + def __print_config(project: Any) -> None: if isinstance(project, list): raise CLIError("Please provide a single project ID (using --id) to print the configuration.") config = project.config if project.config is not None else {} click.echo(colorize_dump(json.dumps(config, indent=4, default=str))) + def __json_string(object: Any) -> str: if object is None: return "None" @@ -432,9 +434,7 @@ def _normalise(tc_id: str) -> str: raw = parse_and_validate_tc_params_file(mapping_path) # Build a normalised-key → params lookup. - normalised_mapping: dict[str, dict[str, Any]] = { - _normalise(key): value for key, value in raw.items() - } + normalised_mapping: dict[str, dict[str, Any]] = {_normalise(key): value for key, value in raw.items()} # Match each requested test ID against the normalised mapping. merged_params: dict[str, Any] = {} @@ -502,7 +502,7 @@ def get_versions() -> dict: sync_apis = SyncApis(client) version_api = sync_apis.version_api versions_info = version_api.get_test_harness_backend_version_api_v1_version_get() - return versions_info.model_dump(mode='json') + return versions_info.model_dump(mode="json") except CLIError: raise # Re-raise CLI Errors as-is except UnexpectedResponse: diff --git a/th_cli/validation.py b/th_cli/validation.py index 51cf86c..17a0e69 100644 --- a/th_cli/validation.py +++ b/th_cli/validation.py @@ -139,8 +139,7 @@ def parse_and_validate_tc_params_file(file_path: str) -> dict[str, dict[str, Any data: Any = json.load(f) except json.JSONDecodeError as e: raise CLIError( - f"Invalid JSON in TC params mapping file '{file_path}': " - f"{e.msg} (line {e.lineno}, column {e.colno})" + f"Invalid JSON in TC params mapping file '{file_path}': " f"{e.msg} (line {e.lineno}, column {e.colno})" ) except OSError as e: raise CLIError(f"Failed to read TC params mapping file '{file_path}': {e}") From e83dbe3de4bb9a528ce0549f04e601ec95150b0b Mon Sep 17 00:00:00 2001 From: Romulo Quidute Filho Date: Tue, 7 Jul 2026 16:28:43 -0300 Subject: [PATCH 08/10] Show a friendly message instead of raw JSON for logs 404 errors The 'project logs' command previously surfaced the backend's raw error body (e.g. "Status: 404 - {'detail': 'Project 11 has no test run executions...'}"). Parse the 404 detail and show a clean, specific message for the two cases the endpoint can return: project not found, and project has no executions yet. Other status codes still go through the generic error handler. --- tests/test_project_commands.py | 30 +++++++++++++++++++++++------- th_cli/commands/project.py | 12 ++++++++++++ 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/tests/test_project_commands.py b/tests/test_project_commands.py index d586c0d..a5b658d 100644 --- a/tests/test_project_commands.py +++ b/tests/test_project_commands.py @@ -813,21 +813,37 @@ def test_logs_file_content_is_written( assert Path(output_path).read_bytes() == expected_bytes - def test_logs_api_error( + def test_logs_api_error_project_not_found( self, cli_runner: CliRunner, mock_sync_apis: Mock, ) -> None: - """Test log download with API error.""" + """A 404 for a missing project gives a friendly, non-JSON message.""" mock_sync_apis.projects_api.download_project_logs_api_v1_projects__id__logs_get.side_effect = ( - UnexpectedResponse(status_code=404, content=b"Not Found") + UnexpectedResponse(status_code=404, content=b'{"detail": "Project not found"}') + ) + + with patch("th_cli.commands.project.SyncApis", return_value=mock_sync_apis): + result = cli_runner.invoke(project, ["logs", "--id", "99"]) + + assert result.exit_code == 1 + assert "Project ID '99' not found." in result.output + + def test_logs_api_error_non_404_uses_generic_handler( + self, + cli_runner: CliRunner, + mock_sync_apis: Mock, + ) -> None: + """Non-404 API errors still go through the generic error handler.""" + mock_sync_apis.projects_api.download_project_logs_api_v1_projects__id__logs_get.side_effect = ( + UnexpectedResponse(status_code=500, content=b"Internal Server Error") ) with patch("th_cli.commands.project.SyncApis", return_value=mock_sync_apis): result = cli_runner.invoke(project, ["logs", "--id", "99"]) assert result.exit_code == 1 - assert "404" in result.output + assert "500" in result.output def test_logs_no_executions_returns_error_and_writes_no_file( self, @@ -835,12 +851,12 @@ def test_logs_no_executions_returns_error_and_writes_no_file( mock_sync_apis: Mock, temp_dir: Path, ) -> None: - """A project with no test run executions surfaces the backend's 404 + """A project with no test run executions surfaces a friendly message instead of writing an empty/unusable zip file to disk.""" mock_sync_apis.projects_api.download_project_logs_api_v1_projects__id__logs_get.side_effect = ( UnexpectedResponse( status_code=404, - content=b"Project 11 has no test run executions to download logs for", + content=b'{"detail": "Project 11 has no test run executions to download logs for"}', ) ) @@ -849,7 +865,7 @@ def test_logs_no_executions_returns_error_and_writes_no_file( result = cli_runner.invoke(project, ["logs", "--id", "11"]) assert result.exit_code == 1 - assert "no test run executions" in result.output + assert "Nothing to download" in result.output assert list(Path(".").glob("*.zip")) == [] def test_logs_prints_progress_notice_before_download( diff --git a/th_cli/commands/project.py b/th_cli/commands/project.py index c8c3b90..3a9ef04 100644 --- a/th_cli/commands/project.py +++ b/th_cli/commands/project.py @@ -512,6 +512,18 @@ def _download_project_logs(sync_apis: SyncApis, id: int, grouped: bool, output_f id=id, grouped=grouped ) except UnexpectedResponse as e: + if e.status_code == 404: + content = e.content.decode("utf-8", errors="ignore") if isinstance(e.content, bytes) else e.content + detail = "" + try: + parsed = json.loads(content) if content else {} + if isinstance(parsed, dict): + detail = parsed.get("detail", "") + except json.JSONDecodeError: + pass + if "no test run executions" in detail: + raise CLIError(f"Project {id} has no test run executions yet. Nothing to download.") + raise CLIError(f"Project ID '{id}' not found.") handle_api_error(e, f"download logs for project ID '{id}'") if not output_file: From 0edb5670988e6b6551de344ca9a726a9c3ebff8e Mon Sep 17 00:00:00 2001 From: Romulo Quidute Filho Date: Tue, 7 Jul 2026 16:41:21 -0300 Subject: [PATCH 09/10] Fix TypeError when logs 404 content is already a parsed dict UnexpectedResponse.for_response calls response.json(), so e.content is a dict when the body is valid JSON -- not a str/bytes to feed json.loads. That crashed the new friendly-message logic with 'the JSON object must be str, bytes or bytearray, not dict'. Check for dict content first; only fall back to json.loads for str/bytes content. --- tests/test_project_commands.py | 30 +++++++++++++++++++++++++++--- th_cli/commands/project.py | 19 +++++++++++-------- 2 files changed, 38 insertions(+), 11 deletions(-) diff --git a/tests/test_project_commands.py b/tests/test_project_commands.py index a5b658d..15ba9de 100644 --- a/tests/test_project_commands.py +++ b/tests/test_project_commands.py @@ -820,7 +820,7 @@ def test_logs_api_error_project_not_found( ) -> None: """A 404 for a missing project gives a friendly, non-JSON message.""" mock_sync_apis.projects_api.download_project_logs_api_v1_projects__id__logs_get.side_effect = ( - UnexpectedResponse(status_code=404, content=b'{"detail": "Project not found"}') + UnexpectedResponse(status_code=404, content={"detail": "Project not found"}) ) with patch("th_cli.commands.project.SyncApis", return_value=mock_sync_apis): @@ -852,11 +852,35 @@ def test_logs_no_executions_returns_error_and_writes_no_file( temp_dir: Path, ) -> None: """A project with no test run executions surfaces a friendly message - instead of writing an empty/unusable zip file to disk.""" + instead of writing an empty/unusable zip file to disk. Mirrors the + dict-shaped content the real API client delivers via response.json().""" mock_sync_apis.projects_api.download_project_logs_api_v1_projects__id__logs_get.side_effect = ( UnexpectedResponse( status_code=404, - content=b'{"detail": "Project 11 has no test run executions to download logs for"}', + content={"detail": "Project 11 has no test run executions to download logs for"}, + ) + ) + + with patch("th_cli.commands.project.SyncApis", return_value=mock_sync_apis): + with cli_runner.isolated_filesystem(temp_dir=temp_dir): + result = cli_runner.invoke(project, ["logs", "--id", "11"]) + + assert result.exit_code == 1 + assert "Nothing to download" in result.output + assert list(Path(".").glob("*.zip")) == [] + + def test_logs_no_executions_with_string_content_fallback( + self, + cli_runner: CliRunner, + mock_sync_apis: Mock, + temp_dir: Path, + ) -> None: + """Also handles JSON-string content, in case the client ever + surfaces the raw response body instead of a parsed dict.""" + mock_sync_apis.projects_api.download_project_logs_api_v1_projects__id__logs_get.side_effect = ( + UnexpectedResponse( + status_code=404, + content='{"detail": "Project 11 has no test run executions to download logs for"}', ) ) diff --git a/th_cli/commands/project.py b/th_cli/commands/project.py index 3a9ef04..8911535 100644 --- a/th_cli/commands/project.py +++ b/th_cli/commands/project.py @@ -513,14 +513,17 @@ def _download_project_logs(sync_apis: SyncApis, id: int, grouped: bool, output_f ) except UnexpectedResponse as e: if e.status_code == 404: - content = e.content.decode("utf-8", errors="ignore") if isinstance(e.content, bytes) else e.content - detail = "" - try: - parsed = json.loads(content) if content else {} - if isinstance(parsed, dict): - detail = parsed.get("detail", "") - except json.JSONDecodeError: - pass + if isinstance(e.content, dict): + detail = e.content.get("detail", "") + else: + content = e.content.decode("utf-8", errors="ignore") if isinstance(e.content, bytes) else e.content + detail = "" + try: + parsed = json.loads(content) if content else {} + if isinstance(parsed, dict): + detail = parsed.get("detail", "") + except json.JSONDecodeError: + pass if "no test run executions" in detail: raise CLIError(f"Project {id} has no test run executions yet. Nothing to download.") raise CLIError(f"Project ID '{id}' not found.") From 678c3011c5de6e729ccc0499cca8eb339ea9bcf8 Mon Sep 17 00:00:00 2001 From: Romulo Quidute Filho Date: Tue, 7 Jul 2026 20:23:18 +0000 Subject: [PATCH 10/10] Updated generated file --- th_cli/api_lib_autogen/models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/th_cli/api_lib_autogen/models.py b/th_cli/api_lib_autogen/models.py index 00eddbe..31a9e68 100644 --- a/th_cli/api_lib_autogen/models.py +++ b/th_cli/api_lib_autogen/models.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: -# filename: tmp8d039ovi.json -# timestamp: 2026-07-07T19:00:11+00:00 +# filename: tmp1nhhp82y.json +# timestamp: 2026-07-07T19:42:35+00:00 from __future__ import annotations