From 15869d0e9d31bfe44ce33dbd2673abf8777c8632 Mon Sep 17 00:00:00 2001 From: Mads Bisgaard Date: Wed, 23 Oct 2024 16:01:12 +0200 Subject: [PATCH 01/52] begin converting to new generator --- clients/python/Makefile | 4 +- clients/python/requirements/dev.txt | 1 - clients/python/src/osparc/_api_client.py | 3 +- ...t_body_upload_file_v0_files_content_put.py | 49 ------------------- scripts/common.Makefile | 4 +- 5 files changed, 5 insertions(+), 56 deletions(-) delete mode 100644 clients/python/test/test_osparc/test_osparc_client/test_body_upload_file_v0_files_content_put.py diff --git a/clients/python/Makefile b/clients/python/Makefile index aad3decc..544ef3f7 100644 --- a/clients/python/Makefile +++ b/clients/python/Makefile @@ -116,8 +116,8 @@ _check_venv_active: .PHONY: install-dev install-dev: _check_venv_active .install-dev-reqs python-client ## installs osparc_client and osparc in edit mode - uv pip install -e artifacts/client - uv pip install -e . + # for some reason this command refuses to run with uv. Looks related to https://github.com/astral-sh/uv/issues/1661 + pip install -e artifacts/client -e . uv pip list .PHONY: install-unit-test diff --git a/clients/python/requirements/dev.txt b/clients/python/requirements/dev.txt index 13ce4643..50bca975 100644 --- a/clients/python/requirements/dev.txt +++ b/clients/python/requirements/dev.txt @@ -1,4 +1,3 @@ -r ../../../requirements.txt -r unit-test.txt --r e2e-test.txt pylint diff --git a/clients/python/src/osparc/_api_client.py b/clients/python/src/osparc/_api_client.py index 70cc5b40..d16f9ee7 100644 --- a/clients/python/src/osparc/_api_client.py +++ b/clients/python/src/osparc/_api_client.py @@ -16,7 +16,6 @@ def __init__( header_name=None, header_value=None, cookie=None, - pool_threads=1, ): if configuration is None: try: @@ -36,4 +35,4 @@ def __init__( "osparc.Configuration object explicitly" ) from exc - super().__init__(configuration, header_name, header_value, cookie, pool_threads) + super().__init__(configuration, header_name, header_value, cookie) diff --git a/clients/python/test/test_osparc/test_osparc_client/test_body_upload_file_v0_files_content_put.py b/clients/python/test/test_osparc/test_osparc_client/test_body_upload_file_v0_files_content_put.py deleted file mode 100644 index f46560f2..00000000 --- a/clients/python/test/test_osparc/test_osparc_client/test_body_upload_file_v0_files_content_put.py +++ /dev/null @@ -1,49 +0,0 @@ -# coding: utf-8 - -""" - osparc.io web API - - osparc-simcore public web API specifications # noqa: E501 - - The version of the OpenAPI document: 0.4.0 - Generated by: https://openapi-generator.tech -""" - - -from __future__ import absolute_import - -import unittest - -from osparc import BodyUploadFileV0FilesContentPut # noqa: E501 - - -class TestBodyUploadFileV0FilesContentPut(unittest.TestCase): - """BodyUploadFileV0FilesContentPut unit test stubs""" - - def setUp(self): - pass - - def tearDown(self): - pass - - def make_instance(self, include_optional): - """Test BodyUploadFileV0FilesContentPut - include_option is a boolean, when False only required - params are included, when True both required and - optional params are included""" - # model = osparc.models.body_upload_file_v0_files_content_put.BodyUploadFileV0FilesContentPut() # noqa: E501 - if include_optional: - return BodyUploadFileV0FilesContentPut(file=bytes(b"blah")) - else: - return BodyUploadFileV0FilesContentPut( - file=bytes(b"blah"), - ) - - def testBodyUploadFileV0FilesContentPut(self): - """Test BodyUploadFileV0FilesContentPut""" - self.make_instance(include_optional=False) - self.make_instance(include_optional=True) - - -if __name__ == "__main__": - unittest.main() diff --git a/scripts/common.Makefile b/scripts/common.Makefile index 7a89fdfa..fa309f24 100644 --- a/scripts/common.Makefile +++ b/scripts/common.Makefile @@ -14,8 +14,8 @@ APP_NAME := $(notdir $(CURDIR)) # Specify which openapi generator should be used to generate the clients in this repo # Build from modified fork https://github.com/ITISFoundation/openapi-generator/tree/openapi-generator-v4.2.3 -OPENAPI_GENERATOR_NAME := itisfoundation/openapi-generator-cli-openapi-generator-v4.2.3 -OPENAPI_GENERATOR_TAG := v0 +OPENAPI_GENERATOR_NAME := openapitools/openapi-generator-cli +OPENAPI_GENERATOR_TAG := latest OPENAPI_GENERATOR_IMAGE := $(OPENAPI_GENERATOR_NAME):$(OPENAPI_GENERATOR_TAG) # openapi specification From 6df235f3d3300f764b46569cc260dee783c0b575 Mon Sep 17 00:00:00 2001 From: Mads Bisgaard Date: Wed, 23 Oct 2024 16:32:19 +0200 Subject: [PATCH 02/52] remove invalid import --- clients/python/src/osparc/models.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/clients/python/src/osparc/models.py b/clients/python/src/osparc/models.py index 9c4aa508..660065a4 100644 --- a/clients/python/src/osparc/models.py +++ b/clients/python/src/osparc/models.py @@ -12,9 +12,6 @@ from osparc_client.models.body_complete_multipart_upload_v0_files_file_id_complete_post import ( BodyCompleteMultipartUploadV0FilesFileIdCompletePost as BodyCompleteMultipartUploadV0FilesFileIdCompletePost, ) -from osparc_client.models.body_upload_file_v0_files_content_put import ( - BodyUploadFileV0FilesContentPut as BodyUploadFileV0FilesContentPut, -) from osparc_client.models.client_file import ClientFile as ClientFile from osparc_client.models.client_file_upload_data import ( ClientFileUploadData as ClientFileUploadData, From e6663e6343dba73d9062079319af711d9c24e697 Mon Sep 17 00:00:00 2001 From: Mads Bisgaard Date: Thu, 24 Oct 2024 10:23:59 +0200 Subject: [PATCH 03/52] fix minor bug in tests --- clients/python/test/e2e/conftest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clients/python/test/e2e/conftest.py b/clients/python/test/e2e/conftest.py index 738146f8..703ff32e 100644 --- a/clients/python/test/e2e/conftest.py +++ b/clients/python/test/e2e/conftest.py @@ -96,7 +96,7 @@ def pytest_configure(config): @pytest.fixture def api_client() -> Iterable[osparc.ApiClient]: - if Version(osparc.__version__) >= Version("8.0.0"): + if Version(osparc.__version__) >= Version("0.8.0"): with osparc.ApiClient() as api_client: yield api_client else: From 456a98b2b81705e7595d9dbf511b3b634cf5aa21 Mon Sep 17 00:00:00 2001 From: Mads Bisgaard Date: Thu, 24 Oct 2024 10:44:57 +0200 Subject: [PATCH 04/52] fix download file method --- clients/python/src/osparc/_api_files_api.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/clients/python/src/osparc/_api_files_api.py b/clients/python/src/osparc/_api_files_api.py index 88bdea55..4d1cf91e 100644 --- a/clients/python/src/osparc/_api_files_api.py +++ b/clients/python/src/osparc/_api_files_api.py @@ -9,6 +9,7 @@ import string from pathlib import Path from typing import Any, Iterator, List, Optional, Tuple, Union +from tempfile import NamedTemporaryFile import httpx from httpx import Response @@ -71,7 +72,10 @@ def download_file( raise RuntimeError( f"destination_folder: {destination_folder} must be a directory" ) - downloaded_file: Path = Path(super().download_file(file_id, **kwargs)) + with NamedTemporaryFile(delete=False) as tmp_file: + downloaded_file = Path(tmp_file.name) + data = super().download_file(file_id, **kwargs) + downloaded_file.write_bytes(data) if destination_folder is not None: dest_file: Path = destination_folder / downloaded_file.name while dest_file.is_file(): From e7756b150df76043e5dfc1927c41d44e584bd401 Mon Sep 17 00:00:00 2001 From: Mads Bisgaard Date: Thu, 24 Oct 2024 11:29:38 +0200 Subject: [PATCH 05/52] remove skip if dev version decorator --- clients/python/test/e2e/_utils.py | 14 -------------- clients/python/test/e2e/test_files_api.py | 3 --- clients/python/test/e2e/test_solvers_api.py | 3 +-- 3 files changed, 1 insertion(+), 19 deletions(-) diff --git a/clients/python/test/e2e/_utils.py b/clients/python/test/e2e/_utils.py index 83026596..2fc7c516 100644 --- a/clients/python/test/e2e/_utils.py +++ b/clients/python/test/e2e/_utils.py @@ -24,20 +24,6 @@ def repo_version() -> Version: return Version(version_file.read_text()) -def skip_if_no_dev_features(test): - if ( - Version(osparc.__version__) < repo_version() - or not osparc_dev_features_enabled() - ): - return pytest.mark.skip( - ( - f"{osparc.__version__=}<{str(repo_version)} " - f"or {osparc_dev_features_enabled()=}" - ) - )(test) - return test - - def skip_if_osparc_version( *, at_least: Optional[Version] = None, diff --git a/clients/python/test/e2e/test_files_api.py b/clients/python/test/e2e/test_files_api.py index 57736dd3..5338efe3 100644 --- a/clients/python/test/e2e/test_files_api.py +++ b/clients/python/test/e2e/test_files_api.py @@ -9,7 +9,6 @@ import osparc import pytest -from _utils import skip_if_no_dev_features from conftest import _KB @@ -25,7 +24,6 @@ def _hash_file(file: Path) -> str: return sha256.hexdigest() -@skip_if_no_dev_features def test_upload_file(tmp_file: Path, api_client: osparc.ApiClient) -> None: """Test that we can upload a file via the multipart upload""" tmp_path: Path = tmp_file.parent @@ -43,7 +41,6 @@ def test_upload_file(tmp_file: Path, api_client: osparc.ApiClient) -> None: files_api.delete_file(uploaded_file1.id) -@skip_if_no_dev_features @pytest.mark.parametrize("use_checksum", [True, False]) @pytest.mark.parametrize("use_id", [True, False]) def test_search_files( diff --git a/clients/python/test/e2e/test_solvers_api.py b/clients/python/test/e2e/test_solvers_api.py index e0181732..41a5439d 100644 --- a/clients/python/test/e2e/test_solvers_api.py +++ b/clients/python/test/e2e/test_solvers_api.py @@ -7,14 +7,13 @@ import json import osparc -from _utils import skip_if_no_dev_features, skip_if_osparc_version +from _utils import skip_if_osparc_version from httpx import AsyncClient from packaging.version import Version DEFAULT_TIMEOUT_SECONDS = 10 * 60 # 10 min -@skip_if_no_dev_features def test_jobs(api_client: osparc.ApiClient, sleeper: osparc.Solver): """Test the jobs method From 373089b7d58ee32554722080999acfd39315488a Mon Sep 17 00:00:00 2001 From: Mads Bisgaard Date: Thu, 24 Oct 2024 13:35:09 +0200 Subject: [PATCH 06/52] update models in wrapper --- clients/python/src/osparc/models.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/clients/python/src/osparc/models.py b/clients/python/src/osparc/models.py index 660065a4..ed3ff5c4 100644 --- a/clients/python/src/osparc/models.py +++ b/clients/python/src/osparc/models.py @@ -28,12 +28,13 @@ HTTPValidationError as HTTPValidationError, ) from osparc_client.models.job import Job as Job -from osparc_client.models.job_inputs import JobInputs as JobInputs +from osparc_client.models.job_inputs import JobInputs as _JobInputs from osparc_client.models.job_logs_map import JobLogsMap as JobLogsMap from osparc_client.models.job_metadata import JobMetadata as JobMetadata from osparc_client.models.job_metadata_update import ( JobMetadataUpdate as JobMetadataUpdate, ) +from osparc_client.models.values_value import ValuesValue as ValuesValue from osparc_client.models.job_outputs import JobOutputs as JobOutputs from osparc_client.models.job_status import JobStatus as JobStatus from osparc_client.models.links import Links as Links @@ -75,3 +76,11 @@ # renames TaskStates = _RunningState + + +class JobInputs(_JobInputs): + def __init__(self, *args, **kwargs): + if len(args) == 1: + return _JobInputs.from_dict({"values": args[0]}) + else: + return _JobInputs(*args, **kwargs) From 4bb8a1c6e0cd0fb8f5c1cdabb9347639a77d520d Mon Sep 17 00:00:00 2001 From: Mads Bisgaard Date: Thu, 24 Oct 2024 13:56:23 +0200 Subject: [PATCH 07/52] patch JobInputs model --- clients/python/src/osparc/_api_files_api.py | 3 ++- clients/python/src/osparc/models.py | 8 +++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/clients/python/src/osparc/_api_files_api.py b/clients/python/src/osparc/_api_files_api.py index 4d1cf91e..5da81364 100644 --- a/clients/python/src/osparc/_api_files_api.py +++ b/clients/python/src/osparc/_api_files_api.py @@ -14,6 +14,7 @@ import httpx from httpx import Response from osparc_client.api.files_api import FilesApi as _FilesApi +from osparc_client import Filesize as _Filesize from tqdm.asyncio import tqdm from tqdm.contrib.logging import logging_redirect_tqdm @@ -119,7 +120,7 @@ async def upload_file_async( return file_result client_file: ClientFile = ClientFile( filename=file.name, - filesize=file.stat().st_size, + filesize=_Filesize(file.stat().st_size), sha256_checksum=checksum, ) client_upload_schema: ClientFileUploadData = super().get_upload_links( diff --git a/clients/python/src/osparc/models.py b/clients/python/src/osparc/models.py index ed3ff5c4..3163f855 100644 --- a/clients/python/src/osparc/models.py +++ b/clients/python/src/osparc/models.py @@ -80,7 +80,9 @@ class JobInputs(_JobInputs): def __init__(self, *args, **kwargs): - if len(args) == 1: - return _JobInputs.from_dict({"values": args[0]}) + if len(args) == 1 and len(kwargs) == 0: + input = args[0] + assert isinstance(input, dict) + super().__init__(values={k: ValuesValue(v) for k, v in input.items()}) else: - return _JobInputs(*args, **kwargs) + super().__init__(*args, **kwargs) From f55455b1c691e51e8dcef0aac36f4a768851c167 Mon Sep 17 00:00:00 2001 From: Mads Bisgaard Date: Thu, 24 Oct 2024 15:06:55 +0200 Subject: [PATCH 08/52] make JobOutputs backwards compatible --- clients/python/src/osparc/_api_solvers_api.py | 18 +++++++++++++-- clients/python/src/osparc/models.py | 22 +++++++++++++++++-- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/clients/python/src/osparc/_api_solvers_api.py b/clients/python/src/osparc/_api_solvers_api.py index 7b66d854..947935ee 100644 --- a/clients/python/src/osparc/_api_solvers_api.py +++ b/clients/python/src/osparc/_api_solvers_api.py @@ -4,7 +4,7 @@ import httpx from osparc_client.api.solvers_api import SolversApi as _SolversApi -from .models import JobInputs, OnePageSolverPort, SolverPort +from .models import JobInputs, OnePageSolverPort, SolverPort, JobOutputs from ._api_client import ApiClient from ._settings import ParentProjectInfo @@ -15,8 +15,9 @@ dev_feature, dev_features_enabled, ) - import warnings +from tempfile import NamedTemporaryFile +from pathlib import Path class SolversApi(_SolversApi): @@ -102,3 +103,16 @@ def create_job( ): kwargs = {**kwargs, **ParentProjectInfo().model_dump(exclude_none=True)} return super().create_job(solver_key, version, job_inputs, **kwargs) + + def get_job_output_logfile(self, *args, **kwargs): + data = super().get_job_output_logfile(*args, **kwargs) + with NamedTemporaryFile(delete=False) as tmp_file: + log_file = Path(tmp_file.name) + log_file.write_bytes(data) + return log_file + + def get_job_outputs(self, *args, **kwargs) -> JobOutputs: + _osparc_client_outputs = super().get_job_outputs(*args, **kwargs) + _outputs = JobOutputs(outputs=_osparc_client_outputs) + assert _outputs is not None + return _outputs diff --git a/clients/python/src/osparc/models.py b/clients/python/src/osparc/models.py index 3163f855..deeb0b36 100644 --- a/clients/python/src/osparc/models.py +++ b/clients/python/src/osparc/models.py @@ -35,7 +35,7 @@ JobMetadataUpdate as JobMetadataUpdate, ) from osparc_client.models.values_value import ValuesValue as ValuesValue -from osparc_client.models.job_outputs import JobOutputs as JobOutputs +from osparc_client.models.job_outputs import JobOutputs as _JobOutputs from osparc_client.models.job_status import JobStatus as JobStatus from osparc_client.models.links import Links as Links from osparc_client.models.log_link import LogLink as LogLink @@ -72,7 +72,7 @@ WalletGetWithAvailableCredits as WalletGetWithAvailableCredits, ) from osparc_client.models.wallet_status import WalletStatus as WalletStatus - +from pydantic import BaseModel # renames TaskStates = _RunningState @@ -86,3 +86,21 @@ def __init__(self, *args, **kwargs): super().__init__(values={k: ValuesValue(v) for k, v in input.items()}) else: super().__init__(*args, **kwargs) + + +class JobOutputs(BaseModel): + outputs: _JobOutputs + + @property + def results(self): + _results = {} + for k, v in self.outputs.results.items(): + if isinstance(v, ValuesValue): + _results[k] = v.actual_instance + else: + _results[k] = v + return _results + + @property + def job_id(self): + return self.outputs.job_id From 4351ea510a1d948f437987b31cb643c23ec3cbec Mon Sep 17 00:00:00 2001 From: Mads Bisgaard Date: Thu, 24 Oct 2024 15:29:47 +0200 Subject: [PATCH 09/52] improve wrapping of JobOutputs --- clients/python/src/osparc/_api_solvers_api.py | 2 +- clients/python/src/osparc/models.py | 17 ++++++++--------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/clients/python/src/osparc/_api_solvers_api.py b/clients/python/src/osparc/_api_solvers_api.py index 947935ee..39d95a95 100644 --- a/clients/python/src/osparc/_api_solvers_api.py +++ b/clients/python/src/osparc/_api_solvers_api.py @@ -113,6 +113,6 @@ def get_job_output_logfile(self, *args, **kwargs): def get_job_outputs(self, *args, **kwargs) -> JobOutputs: _osparc_client_outputs = super().get_job_outputs(*args, **kwargs) - _outputs = JobOutputs(outputs=_osparc_client_outputs) + _outputs = JobOutputs.from_osparc_client_job_outputs(_osparc_client_outputs) assert _outputs is not None return _outputs diff --git a/clients/python/src/osparc/models.py b/clients/python/src/osparc/models.py index deeb0b36..af8b0cde 100644 --- a/clients/python/src/osparc/models.py +++ b/clients/python/src/osparc/models.py @@ -72,7 +72,8 @@ WalletGetWithAvailableCredits as WalletGetWithAvailableCredits, ) from osparc_client.models.wallet_status import WalletStatus as WalletStatus -from pydantic import BaseModel +from pydantic import BaseModel, Field, StrictStr +from typing import Any, Dict # renames TaskStates = _RunningState @@ -89,18 +90,16 @@ def __init__(self, *args, **kwargs): class JobOutputs(BaseModel): - outputs: _JobOutputs + job_id: StrictStr = Field(description="Job that produced this output") + results: Dict[str, Any] - @property - def results(self): + @classmethod + def from_osparc_client_job_outputs(cls, outputs: _JobOutputs) -> "JobOutputs": _results = {} - for k, v in self.outputs.results.items(): + for k, v in outputs.results.items(): if isinstance(v, ValuesValue): _results[k] = v.actual_instance else: _results[k] = v - return _results - @property - def job_id(self): - return self.outputs.job_id + return cls(job_id=outputs.job_id, results=_results) From 883747209882e84c673974b35094c1299ed9488b Mon Sep 17 00:00:00 2001 From: Mads Bisgaard Date: Thu, 24 Oct 2024 16:12:45 +0200 Subject: [PATCH 10/52] improve JobInputs constructor --- clients/python/src/osparc/models.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/clients/python/src/osparc/models.py b/clients/python/src/osparc/models.py index af8b0cde..67043755 100644 --- a/clients/python/src/osparc/models.py +++ b/clients/python/src/osparc/models.py @@ -79,12 +79,29 @@ TaskStates = _RunningState +def _values_dict(v: Dict[str, Any]) -> Dict[str, ValuesValue | None]: + result = {} + for k, v in v.items(): + if v is not None: + result[k] = ValuesValue(v) + else: + result[k] = v + return result + + class JobInputs(_JobInputs): def __init__(self, *args, **kwargs): if len(args) == 1 and len(kwargs) == 0: input = args[0] assert isinstance(input, dict) - super().__init__(values={k: ValuesValue(v) for k, v in input.items()}) + super().__init__(values=_values_dict(input)) + return + if len(args) == 0 and len(kwargs) == 1: + values = kwargs.get("values") + if values is None: + raise RuntimeError("When passing a single kwarg it must be 'values'") + super().__init__(values=_values_dict(values)) + return else: super().__init__(*args, **kwargs) From dae27fed17816b14a7e59419caf08e73d062719b Mon Sep 17 00:00:00 2001 From: Mads Bisgaard Date: Thu, 24 Oct 2024 16:26:56 +0200 Subject: [PATCH 11/52] resolve configuration issue --- clients/python/src/osparc/_api_client.py | 3 +- clients/python/src/osparc/_configuration.py | 45 +++++++++++++++++++++ 2 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 clients/python/src/osparc/_configuration.py diff --git a/clients/python/src/osparc/_api_client.py b/clients/python/src/osparc/_api_client.py index d16f9ee7..76d6693c 100644 --- a/clients/python/src/osparc/_api_client.py +++ b/clients/python/src/osparc/_api_client.py @@ -3,9 +3,8 @@ from typing import Optional from osparc_client.api_client import ApiClient as _ApiClient -from osparc_client import Configuration +from ._configuration import Configuration from pydantic import ValidationError - from ._settings import ConfigurationEnvVars diff --git a/clients/python/src/osparc/_configuration.py b/clients/python/src/osparc/_configuration.py new file mode 100644 index 00000000..a2c8c210 --- /dev/null +++ b/clients/python/src/osparc/_configuration.py @@ -0,0 +1,45 @@ +from osparc_client import Configuration as _Configuration +from typing import Set +from urllib3 import Retry + + +class Configuration(_Configuration): + def __init__( + self, + host="https://api.osparc.io", + api_key=None, + api_key_prefix=None, + username=None, + password=None, + *, + retry_max_count: int = 4, + retry_methods: Set[str] = { + "DELETE", + "GET", + "HEAD", + "OPTIONS", + "PUT", + "TRACE", + "POST", + "PATCH", + "CONNECT", + }, + retry_status_codes: Set[int] = {429, 503, 504}, + retry_backoff_factor=4.0, + ): + retries = Retry( + total=retry_max_count, + backoff_factor=retry_backoff_factor, + status_forcelist=retry_status_codes, + allowed_methods=retry_methods, + respect_retry_after_header=True, + raise_on_status=True, + ) + super().__init__( + host=host, + api_key=api_key, + api_key_prefix=api_key_prefix, + username=username, + password=password, + retries=retries, + ) From 35b66fd1a506cce039a7db956ebbb7730e325a5c Mon Sep 17 00:00:00 2001 From: Mads Bisgaard Date: Thu, 24 Oct 2024 17:02:22 +0200 Subject: [PATCH 12/52] include configuration directly in api --- clients/python/src/osparc/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clients/python/src/osparc/api.py b/clients/python/src/osparc/api.py index 24a07c0d..b3d751da 100644 --- a/clients/python/src/osparc/api.py +++ b/clients/python/src/osparc/api.py @@ -4,7 +4,7 @@ # NOTE: this is an interface. Keep it clean! -from osparc_client.configuration import Configuration as Configuration +from ._configuration import Configuration as Configuration from ._api_client import ApiClient as ApiClient From bc3bfe13cb0b5616d767cdac356d43d4cbb6027f Mon Sep 17 00:00:00 2001 From: Mads Bisgaard Date: Thu, 24 Oct 2024 17:19:04 +0200 Subject: [PATCH 13/52] update to latest openapi.json --- api/openapi.json | 2079 +++++++++++++++++++++++++++------------------- 1 file changed, 1235 insertions(+), 844 deletions(-) diff --git a/api/openapi.json b/api/openapi.json index 7965ae50..51ba054e 100644 --- a/api/openapi.json +++ b/api/openapi.json @@ -1,5 +1,5 @@ { - "openapi": "3.0.2", + "openapi": "3.1.0", "info": { "title": "osparc.io public API", "description": "osparc-simcore public API specifications", @@ -316,26 +316,38 @@ "summary": "Upload File", "description": "Uploads a single file to the system", "operationId": "upload_file", + "security": [ + { + "HTTPBasic": [] + } + ], "parameters": [ { + "name": "content-length", + "in": "header", "required": false, "schema": { - "type": "string", + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], "title": "Content-Length" - }, - "name": "content-length", - "in": "header" + } } ], "requestBody": { + "required": true, "content": { "multipart/form-data": { "schema": { "$ref": "#/components/schemas/Body_upload_file_v0_files_content_put" } } - }, - "required": true + } }, "responses": { "200": { @@ -418,12 +430,7 @@ } } } - }, - "security": [ - { - "HTTPBasic": [] - } - ] + } }, "post": { "tags": [ @@ -432,15 +439,20 @@ "summary": "Get Upload Links", "description": "Get upload links for uploading a file to storage", "operationId": "get_upload_links", + "security": [ + { + "HTTPBasic": [] + } + ], "requestBody": { + "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClientFile" } } - }, - "required": true + } }, "responses": { "200": { @@ -523,12 +535,7 @@ } } } - }, - "security": [ - { - "HTTPBasic": [] - } - ] + } } }, "/v0/files/{file_id}": { @@ -539,16 +546,21 @@ "summary": "Get File", "description": "Gets metadata for a given file resource", "operationId": "get_file", + "security": [ + { + "HTTPBasic": [] + } + ], "parameters": [ { + "name": "file_id", + "in": "path", "required": true, "schema": { "type": "string", "format": "uuid", "title": "File Id" - }, - "name": "file_id", - "in": "path" + } } ], "responses": { @@ -632,12 +644,7 @@ } } } - }, - "security": [ - { - "HTTPBasic": [] - } - ] + } }, "delete": { "tags": [ @@ -645,16 +652,21 @@ ], "summary": "Delete File", "operationId": "delete_file", + "security": [ + { + "HTTPBasic": [] + } + ], "parameters": [ { + "name": "file_id", + "in": "path", "required": true, "schema": { "type": "string", "format": "uuid", "title": "File Id" - }, - "name": "file_id", - "in": "path" + } } ], "responses": { @@ -736,12 +748,7 @@ } } } - }, - "security": [ - { - "HTTPBasic": [] - } - ] + } } }, "/v0/files:search": { @@ -752,49 +759,68 @@ "summary": "Search Files Page", "description": "Search files", "operationId": "search_files_page", + "security": [ + { + "HTTPBasic": [] + } + ], "parameters": [ { + "name": "sha256_checksum", + "in": "query", "required": false, "schema": { - "type": "string", - "pattern": "^[a-fA-F0-9]{64}$", + "anyOf": [ + { + "type": "string", + "pattern": "^[a-fA-F0-9]{64}$" + }, + { + "type": "null" + } + ], "title": "Sha256 Checksum" - }, - "name": "sha256_checksum", - "in": "query" + } }, { + "name": "file_id", + "in": "query", "required": false, "schema": { - "type": "string", - "format": "uuid", + "anyOf": [ + { + "type": "string", + "format": "uuid" + }, + { + "type": "null" + } + ], "title": "File Id" - }, - "name": "file_id", - "in": "query" + } }, { + "name": "limit", + "in": "query", "required": false, "schema": { "type": "integer", "maximum": 100, "minimum": 1, - "title": "Limit", - "default": 50 - }, - "name": "limit", - "in": "query" + "default": 50, + "title": "Limit" + } }, { + "name": "offset", + "in": "query", "required": false, "schema": { "type": "integer", "minimum": 0, - "title": "Offset", - "default": 0 - }, - "name": "offset", - "in": "query" + "default": 0, + "title": "Offset" + } } ], "responses": { @@ -878,12 +904,7 @@ } } } - }, - "security": [ - { - "HTTPBasic": [] - } - ] + } } }, "/v0/files/{file_id}:abort": { @@ -893,27 +914,32 @@ ], "summary": "Abort Multipart Upload", "operationId": "abort_multipart_upload", + "security": [ + { + "HTTPBasic": [] + } + ], "parameters": [ { + "name": "file_id", + "in": "path", "required": true, "schema": { "type": "string", "format": "uuid", "title": "File Id" - }, - "name": "file_id", - "in": "path" + } } ], "requestBody": { + "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Body_abort_multipart_upload_v0_files__file_id__abort_post" } } - }, - "required": true + } }, "responses": { "200": { @@ -984,12 +1010,7 @@ } } } - }, - "security": [ - { - "HTTPBasic": [] - } - ] + } } }, "/v0/files/{file_id}:complete": { @@ -999,27 +1020,32 @@ ], "summary": "Complete Multipart Upload", "operationId": "complete_multipart_upload", + "security": [ + { + "HTTPBasic": [] + } + ], "parameters": [ { + "name": "file_id", + "in": "path", "required": true, "schema": { "type": "string", "format": "uuid", "title": "File Id" - }, - "name": "file_id", - "in": "path" + } } ], "requestBody": { + "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Body_complete_multipart_upload_v0_files__file_id__complete_post" } } - }, - "required": true + } }, "responses": { "200": { @@ -1102,12 +1128,7 @@ } } } - }, - "security": [ - { - "HTTPBasic": [] - } - ] + } } }, "/v0/files/{file_id}/content": { @@ -1117,16 +1138,21 @@ ], "summary": "Download File", "operationId": "download_file", + "security": [ + { + "HTTPBasic": [] + } + ], "parameters": [ { + "name": "file_id", + "in": "path", "required": true, "schema": { "type": "string", "format": "uuid", "title": "File Id" - }, - "name": "file_id", - "in": "path" + } } ], "responses": { @@ -1194,7 +1220,6 @@ } }, "200": { - "description": "Returns a arbitrary binary data", "content": { "application/octet-stream": { "schema": { @@ -1207,7 +1232,8 @@ "type": "string" } } - } + }, + "description": "Returns a arbitrary binary data" }, "422": { "description": "Validation Error", @@ -1219,12 +1245,7 @@ } } } - }, - "security": [ - { - "HTTPBasic": [] - } - ] + } } }, "/v0/solvers": { @@ -1417,16 +1438,21 @@ "summary": "Get Latest Release of a Solver", "description": "Gets latest release of a solver", "operationId": "get_solver", + "security": [ + { + "HTTPBasic": [] + } + ], "parameters": [ { + "name": "solver_key", + "in": "path", "required": true, "schema": { "type": "string", "pattern": "^simcore/services/comp/([a-z0-9][a-z0-9_.-]*/)*([a-z0-9-_]+[a-z0-9])$", "title": "Solver Key" - }, - "name": "solver_key", - "in": "path" + } } ], "responses": { @@ -1510,12 +1536,7 @@ } } } - }, - "security": [ - { - "HTTPBasic": [] - } - ] + } } }, "/v0/solvers/{solver_key}/releases": { @@ -1526,16 +1547,21 @@ "summary": "List Solver Releases", "description": "Lists all releases of a given (one) solver\n\nSEE get_solver_releases_page for a paginated version of this function", "operationId": "list_solver_releases", + "security": [ + { + "HTTPBasic": [] + } + ], "parameters": [ { + "name": "solver_key", + "in": "path", "required": true, "schema": { "type": "string", "pattern": "^simcore/services/comp/([a-z0-9][a-z0-9_.-]*/)*([a-z0-9-_]+[a-z0-9])$", "title": "Solver Key" - }, - "name": "solver_key", - "in": "path" + } } ], "responses": { @@ -1544,10 +1570,10 @@ "content": { "application/json": { "schema": { + "type": "array", "items": { "$ref": "#/components/schemas/Solver" }, - "type": "array", "title": "Response List Solver Releases V0 Solvers Solver Key Releases Get" } } @@ -1623,12 +1649,7 @@ } } } - }, - "security": [ - { - "HTTPBasic": [] - } - ] + } } }, "/v0/solvers/{solver_key}/releases/{version}": { @@ -1639,26 +1660,31 @@ "summary": "Get Solver Release", "description": "Gets a specific release of a solver", "operationId": "get_solver_release", + "security": [ + { + "HTTPBasic": [] + } + ], "parameters": [ { + "name": "solver_key", + "in": "path", "required": true, "schema": { "type": "string", "pattern": "^simcore/services/comp/([a-z0-9][a-z0-9_.-]*/)*([a-z0-9-_]+[a-z0-9])$", "title": "Solver Key" - }, - "name": "solver_key", - "in": "path" + } }, { + "name": "version", + "in": "path", "required": true, "schema": { "type": "string", "pattern": "^(0|[1-9]\\d*)(\\.(0|[1-9]\\d*)){2}(-(0|[1-9]\\d*|\\d*[-a-zA-Z][-\\da-zA-Z]*)(\\.(0|[1-9]\\d*|\\d*[-a-zA-Z][-\\da-zA-Z]*))*)?(\\+[-\\da-zA-Z]+(\\.[-\\da-zA-Z-]+)*)?$", "title": "Version" - }, - "name": "version", - "in": "path" + } } ], "responses": { @@ -1742,12 +1768,7 @@ } } } - }, - "security": [ - { - "HTTPBasic": [] - } - ] + } } }, "/v0/solvers/{solver_key}/releases/{version}/ports": { @@ -1758,26 +1779,31 @@ "summary": "List Solver Ports", "description": "Lists inputs and outputs of a given solver\n\nNew in *version 0.5.0*", "operationId": "list_solver_ports", + "security": [ + { + "HTTPBasic": [] + } + ], "parameters": [ { + "name": "solver_key", + "in": "path", "required": true, "schema": { "type": "string", "pattern": "^simcore/services/comp/([a-z0-9][a-z0-9_.-]*/)*([a-z0-9-_]+[a-z0-9])$", "title": "Solver Key" - }, - "name": "solver_key", - "in": "path" + } }, { + "name": "version", + "in": "path", "required": true, "schema": { "type": "string", "pattern": "^(0|[1-9]\\d*)(\\.(0|[1-9]\\d*)){2}(-(0|[1-9]\\d*|\\d*[-a-zA-Z][-\\da-zA-Z]*)(\\.(0|[1-9]\\d*|\\d*[-a-zA-Z][-\\da-zA-Z]*))*)?(\\+[-\\da-zA-Z]+(\\.[-\\da-zA-Z-]+)*)?$", "title": "Version" - }, - "name": "version", - "in": "path" + } } ], "responses": { @@ -1861,12 +1887,7 @@ } } } - }, - "security": [ - { - "HTTPBasic": [] - } - ] + } } }, "/v0/solvers/{solver_key}/releases/{version}/pricing_plan": { @@ -1877,26 +1898,31 @@ "summary": "Get Solver Pricing Plan", "description": "Gets solver pricing plan\n\nNew in *version 0.7*", "operationId": "get_solver_pricing_plan", + "security": [ + { + "HTTPBasic": [] + } + ], "parameters": [ { + "name": "solver_key", + "in": "path", "required": true, "schema": { "type": "string", "pattern": "^simcore/services/comp/([a-z0-9][a-z0-9_.-]*/)*([a-z0-9-_]+[a-z0-9])$", "title": "Solver Key" - }, - "name": "solver_key", - "in": "path" + } }, { + "name": "version", + "in": "path", "required": true, "schema": { "type": "string", "pattern": "^(0|[1-9]\\d*)(\\.(0|[1-9]\\d*)){2}(-(0|[1-9]\\d*|\\d*[-a-zA-Z][-\\da-zA-Z]*)(\\.(0|[1-9]\\d*|\\d*[-a-zA-Z][-\\da-zA-Z]*))*)?(\\+[-\\da-zA-Z]+(\\.[-\\da-zA-Z-]+)*)?$", "title": "Version" - }, - "name": "version", - "in": "path" + } } ], "responses": { @@ -1980,55 +2006,105 @@ } } } - }, - "security": [ - { - "HTTPBasic": [] - } - ] + } } }, "/v0/solvers/{solver_key}/releases/{version}/jobs": { - "get": { + "post": { "tags": [ "solvers" ], - "summary": "List Jobs", - "description": "List of jobs in a specific released solver (limited to 20 jobs)\n\n- DEPRECATION: This implementation and returned values are deprecated and the will be replaced by that of get_jobs_page\n- SEE `get_jobs_page` for paginated version of this function", - "operationId": "list_jobs", + "summary": "Create Job", + "description": "Creates a job in a specific release with given inputs.\n\nNOTE: This operation does **not** start the job", + "operationId": "create_job", + "security": [ + { + "HTTPBasic": [] + } + ], "parameters": [ { + "name": "solver_key", + "in": "path", "required": true, "schema": { "type": "string", "pattern": "^simcore/services/comp/([a-z0-9][a-z0-9_.-]*/)*([a-z0-9-_]+[a-z0-9])$", "title": "Solver Key" - }, - "name": "solver_key", - "in": "path" + } }, { + "name": "version", + "in": "path", "required": true, "schema": { "type": "string", "pattern": "^(0|[1-9]\\d*)(\\.(0|[1-9]\\d*)){2}(-(0|[1-9]\\d*|\\d*[-a-zA-Z][-\\da-zA-Z]*)(\\.(0|[1-9]\\d*|\\d*[-a-zA-Z][-\\da-zA-Z]*))*)?(\\+[-\\da-zA-Z]+(\\.[-\\da-zA-Z-]+)*)?$", "title": "Version" - }, - "name": "version", - "in": "path" + } + }, + { + "name": "hidden", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "default": true, + "title": "Hidden" + } + }, + { + "name": "x-simcore-parent-project-uuid", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string", + "format": "uuid" + }, + { + "type": "null" + } + ], + "title": "X-Simcore-Parent-Project-Uuid" + } + }, + { + "name": "x-simcore-parent-node-id", + "in": "header", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string", + "format": "uuid" + }, + { + "type": "null" + } + ], + "title": "X-Simcore-Parent-Node-Id" + } } ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/JobInputs" + } + } + } + }, "responses": { - "200": { + "201": { "description": "Successful Response", "content": { "application/json": { "schema": { - "items": { - "$ref": "#/components/schemas/Job" - }, - "type": "array", - "title": "Response List Jobs V0 Solvers Solver Key Releases Version Jobs Get" + "$ref": "#/components/schemas/Job" } } } @@ -2113,89 +2189,53 @@ } } } - }, + } + }, + "get": { + "tags": [ + "solvers" + ], + "summary": "List Jobs", + "description": "List of jobs in a specific released solver (limited to 20 jobs)\n\n- DEPRECATION: This implementation and returned values are deprecated and the will be replaced by that of get_jobs_page\n- SEE `get_jobs_page` for paginated version of this function", + "operationId": "list_jobs", "security": [ { "HTTPBasic": [] } - ] - }, - "post": { - "tags": [ - "solvers" ], - "summary": "Create Job", - "description": "Creates a job in a specific release with given inputs.\n\nNOTE: This operation does **not** start the job", - "operationId": "create_job", "parameters": [ { + "name": "solver_key", + "in": "path", "required": true, "schema": { "type": "string", "pattern": "^simcore/services/comp/([a-z0-9][a-z0-9_.-]*/)*([a-z0-9-_]+[a-z0-9])$", "title": "Solver Key" - }, - "name": "solver_key", - "in": "path" + } }, { + "name": "version", + "in": "path", "required": true, "schema": { "type": "string", "pattern": "^(0|[1-9]\\d*)(\\.(0|[1-9]\\d*)){2}(-(0|[1-9]\\d*|\\d*[-a-zA-Z][-\\da-zA-Z]*)(\\.(0|[1-9]\\d*|\\d*[-a-zA-Z][-\\da-zA-Z]*))*)?(\\+[-\\da-zA-Z]+(\\.[-\\da-zA-Z-]+)*)?$", "title": "Version" - }, - "name": "version", - "in": "path" - }, - { - "required": false, - "schema": { - "type": "boolean", - "title": "Hidden", - "default": true - }, - "name": "hidden", - "in": "query" - }, - { - "required": false, - "schema": { - "type": "string", - "format": "uuid", - "title": "X-Simcore-Parent-Project-Uuid" - }, - "name": "x-simcore-parent-project-uuid", - "in": "header" - }, - { - "required": false, - "schema": { - "type": "string", - "format": "uuid", - "title": "X-Simcore-Parent-Node-Id" - }, - "name": "x-simcore-parent-node-id", - "in": "header" + } } ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/JobInputs" - } - } - }, - "required": true - }, "responses": { - "201": { + "200": { "description": "Successful Response", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Job" + "type": "array", + "items": { + "$ref": "#/components/schemas/Job" + }, + "title": "Response List Jobs V0 Solvers Solver Key Releases Version Jobs Get" } } } @@ -2280,64 +2320,57 @@ } } } - }, - "security": [ - { - "HTTPBasic": [] - } - ] + } } }, "/v0/solvers/{solver_key}/releases/{version}/jobs/{job_id}": { - "get": { + "delete": { "tags": [ "solvers" ], - "summary": "Get Job", - "description": "Gets job of a given solver", - "operationId": "get_job", + "summary": "Delete Job", + "description": "Deletes an existing solver job\n\nNew in *version 0.7*", + "operationId": "delete_job", + "security": [ + { + "HTTPBasic": [] + } + ], "parameters": [ { + "name": "solver_key", + "in": "path", "required": true, "schema": { "type": "string", "pattern": "^simcore/services/comp/([a-z0-9][a-z0-9_.-]*/)*([a-z0-9-_]+[a-z0-9])$", "title": "Solver Key" - }, - "name": "solver_key", - "in": "path" + } }, { + "name": "version", + "in": "path", "required": true, "schema": { "type": "string", "pattern": "^(0|[1-9]\\d*)(\\.(0|[1-9]\\d*)){2}(-(0|[1-9]\\d*|\\d*[-a-zA-Z][-\\da-zA-Z]*)(\\.(0|[1-9]\\d*|\\d*[-a-zA-Z][-\\da-zA-Z]*))*)?(\\+[-\\da-zA-Z]+(\\.[-\\da-zA-Z-]+)*)?$", "title": "Version" - }, - "name": "version", - "in": "path" + } }, { + "name": "job_id", + "in": "path", "required": true, "schema": { "type": "string", "format": "uuid", "title": "Job Id" - }, - "name": "job_id", - "in": "path" + } } ], "responses": { - "200": { - "description": "Successful Response", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Job" - } - } - } + "204": { + "description": "Successful Response" }, "402": { "description": "Payment required", @@ -2419,55 +2452,62 @@ } } } - }, + } + }, + "get": { + "tags": [ + "solvers" + ], + "summary": "Get Job", + "description": "Gets job of a given solver", + "operationId": "get_job", "security": [ { "HTTPBasic": [] } - ] - }, - "delete": { - "tags": [ - "solvers" ], - "summary": "Delete Job", - "description": "Deletes an existing solver job\n\nNew in *version 0.7*", - "operationId": "delete_job", "parameters": [ { + "name": "solver_key", + "in": "path", "required": true, "schema": { "type": "string", "pattern": "^simcore/services/comp/([a-z0-9][a-z0-9_.-]*/)*([a-z0-9-_]+[a-z0-9])$", "title": "Solver Key" - }, - "name": "solver_key", - "in": "path" + } }, { + "name": "version", + "in": "path", "required": true, "schema": { "type": "string", "pattern": "^(0|[1-9]\\d*)(\\.(0|[1-9]\\d*)){2}(-(0|[1-9]\\d*|\\d*[-a-zA-Z][-\\da-zA-Z]*)(\\.(0|[1-9]\\d*|\\d*[-a-zA-Z][-\\da-zA-Z]*))*)?(\\+[-\\da-zA-Z]+(\\.[-\\da-zA-Z-]+)*)?$", "title": "Version" - }, - "name": "version", - "in": "path" + } }, { + "name": "job_id", + "in": "path", "required": true, "schema": { "type": "string", "format": "uuid", "title": "Job Id" - }, - "name": "job_id", - "in": "path" + } } ], "responses": { - "204": { - "description": "Successful Response" + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Job" + } + } + } }, "402": { "description": "Payment required", @@ -2549,12 +2589,7 @@ } } } - }, - "security": [ - { - "HTTPBasic": [] - } - ] + } } }, "/v0/solvers/{solver_key}/releases/{version}/jobs/{job_id}:start": { @@ -2565,46 +2600,58 @@ "summary": "Start Job", "description": "Starts job job_id created with the solver solver_key:version\n\nAdded in *version 0.4.3*: query parameter `cluster_id`\nAdded in *version 0.6*: responds with a 202 when successfully starting a computation", "operationId": "start_job", + "security": [ + { + "HTTPBasic": [] + } + ], "parameters": [ { + "name": "solver_key", + "in": "path", "required": true, "schema": { "type": "string", "pattern": "^simcore/services/comp/([a-z0-9][a-z0-9_.-]*/)*([a-z0-9-_]+[a-z0-9])$", "title": "Solver Key" - }, - "name": "solver_key", - "in": "path" + } }, { + "name": "version", + "in": "path", "required": true, "schema": { "type": "string", "pattern": "^(0|[1-9]\\d*)(\\.(0|[1-9]\\d*)){2}(-(0|[1-9]\\d*|\\d*[-a-zA-Z][-\\da-zA-Z]*)(\\.(0|[1-9]\\d*|\\d*[-a-zA-Z][-\\da-zA-Z]*))*)?(\\+[-\\da-zA-Z]+(\\.[-\\da-zA-Z-]+)*)?$", "title": "Version" - }, - "name": "version", - "in": "path" + } }, { + "name": "job_id", + "in": "path", "required": true, "schema": { "type": "string", "format": "uuid", "title": "Job Id" - }, - "name": "job_id", - "in": "path" + } }, { + "name": "cluster_id", + "in": "query", "required": false, "schema": { - "type": "integer", - "minimum": 0, + "anyOf": [ + { + "type": "integer", + "minimum": 0 + }, + { + "type": "null" + } + ], "title": "Cluster Id" - }, - "name": "cluster_id", - "in": "query" + } } ], "responses": { @@ -2718,12 +2765,7 @@ } } } - }, - "security": [ - { - "HTTPBasic": [] - } - ] + } } }, "/v0/solvers/{solver_key}/releases/{version}/jobs/{job_id}:stop": { @@ -2733,36 +2775,41 @@ ], "summary": "Stop Job", "operationId": "stop_job", + "security": [ + { + "HTTPBasic": [] + } + ], "parameters": [ { + "name": "solver_key", + "in": "path", "required": true, "schema": { "type": "string", "pattern": "^simcore/services/comp/([a-z0-9][a-z0-9_.-]*/)*([a-z0-9-_]+[a-z0-9])$", "title": "Solver Key" - }, - "name": "solver_key", - "in": "path" + } }, { + "name": "version", + "in": "path", "required": true, "schema": { "type": "string", "pattern": "^(0|[1-9]\\d*)(\\.(0|[1-9]\\d*)){2}(-(0|[1-9]\\d*|\\d*[-a-zA-Z][-\\da-zA-Z]*)(\\.(0|[1-9]\\d*|\\d*[-a-zA-Z][-\\da-zA-Z]*))*)?(\\+[-\\da-zA-Z]+(\\.[-\\da-zA-Z-]+)*)?$", "title": "Version" - }, - "name": "version", - "in": "path" + } }, { + "name": "job_id", + "in": "path", "required": true, "schema": { "type": "string", "format": "uuid", "title": "Job Id" - }, - "name": "job_id", - "in": "path" + } } ], "responses": { @@ -2856,12 +2903,7 @@ } } } - }, - "security": [ - { - "HTTPBasic": [] - } - ] + } } }, "/v0/solvers/{solver_key}/releases/{version}/jobs/{job_id}:inspect": { @@ -2871,36 +2913,41 @@ ], "summary": "Inspect Job", "operationId": "inspect_job", + "security": [ + { + "HTTPBasic": [] + } + ], "parameters": [ { + "name": "solver_key", + "in": "path", "required": true, "schema": { "type": "string", "pattern": "^simcore/services/comp/([a-z0-9][a-z0-9_.-]*/)*([a-z0-9-_]+[a-z0-9])$", "title": "Solver Key" - }, - "name": "solver_key", - "in": "path" + } }, { + "name": "version", + "in": "path", "required": true, "schema": { "type": "string", "pattern": "^(0|[1-9]\\d*)(\\.(0|[1-9]\\d*)){2}(-(0|[1-9]\\d*|\\d*[-a-zA-Z][-\\da-zA-Z]*)(\\.(0|[1-9]\\d*|\\d*[-a-zA-Z][-\\da-zA-Z]*))*)?(\\+[-\\da-zA-Z]+(\\.[-\\da-zA-Z-]+)*)?$", "title": "Version" - }, - "name": "version", - "in": "path" + } }, { + "name": "job_id", + "in": "path", "required": true, "schema": { "type": "string", "format": "uuid", "title": "Job Id" - }, - "name": "job_id", - "in": "path" + } } ], "responses": { @@ -2994,54 +3041,64 @@ } } } - }, - "security": [ - { - "HTTPBasic": [] - } - ] + } } }, "/v0/solvers/{solver_key}/releases/{version}/jobs/{job_id}/metadata": { - "get": { + "patch": { "tags": [ "solvers" ], - "summary": "Get Job Custom Metadata", - "description": "Gets custom metadata from a job\n\nNew in *version 0.7*", - "operationId": "get_job_custom_metadata", + "summary": "Replace Job Custom Metadata", + "description": "Updates custom metadata from a job\n\nNew in *version 0.7*", + "operationId": "replace_job_custom_metadata", + "security": [ + { + "HTTPBasic": [] + } + ], "parameters": [ { + "name": "solver_key", + "in": "path", "required": true, "schema": { "type": "string", "pattern": "^simcore/services/comp/([a-z0-9][a-z0-9_.-]*/)*([a-z0-9-_]+[a-z0-9])$", "title": "Solver Key" - }, - "name": "solver_key", - "in": "path" + } }, { + "name": "version", + "in": "path", "required": true, "schema": { "type": "string", "pattern": "^(0|[1-9]\\d*)(\\.(0|[1-9]\\d*)){2}(-(0|[1-9]\\d*|\\d*[-a-zA-Z][-\\da-zA-Z]*)(\\.(0|[1-9]\\d*|\\d*[-a-zA-Z][-\\da-zA-Z]*))*)?(\\+[-\\da-zA-Z]+(\\.[-\\da-zA-Z-]+)*)?$", "title": "Version" - }, - "name": "version", - "in": "path" + } }, { + "name": "job_id", + "in": "path", "required": true, "schema": { "type": "string", "format": "uuid", "title": "Job Id" - }, - "name": "job_id", - "in": "path" + } } ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/JobMetadataUpdate" + } + } + } + }, "responses": { "200": { "description": "Successful Response", @@ -3123,62 +3180,52 @@ } } } - }, + } + }, + "get": { + "tags": [ + "solvers" + ], + "summary": "Get Job Custom Metadata", + "description": "Gets custom metadata from a job\n\nNew in *version 0.7*", + "operationId": "get_job_custom_metadata", "security": [ { "HTTPBasic": [] } - ] - }, - "patch": { - "tags": [ - "solvers" ], - "summary": "Replace Job Custom Metadata", - "description": "Updates custom metadata from a job\n\nNew in *version 0.7*", - "operationId": "replace_job_custom_metadata", "parameters": [ { + "name": "solver_key", + "in": "path", "required": true, "schema": { "type": "string", "pattern": "^simcore/services/comp/([a-z0-9][a-z0-9_.-]*/)*([a-z0-9-_]+[a-z0-9])$", "title": "Solver Key" - }, - "name": "solver_key", - "in": "path" + } }, { + "name": "version", + "in": "path", "required": true, "schema": { "type": "string", "pattern": "^(0|[1-9]\\d*)(\\.(0|[1-9]\\d*)){2}(-(0|[1-9]\\d*|\\d*[-a-zA-Z][-\\da-zA-Z]*)(\\.(0|[1-9]\\d*|\\d*[-a-zA-Z][-\\da-zA-Z]*))*)?(\\+[-\\da-zA-Z]+(\\.[-\\da-zA-Z-]+)*)?$", "title": "Version" - }, - "name": "version", - "in": "path" + } }, { + "name": "job_id", + "in": "path", "required": true, "schema": { "type": "string", "format": "uuid", "title": "Job Id" - }, - "name": "job_id", - "in": "path" + } } ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/JobMetadataUpdate" - } - } - }, - "required": true - }, "responses": { "200": { "description": "Successful Response", @@ -3260,12 +3307,7 @@ } } } - }, - "security": [ - { - "HTTPBasic": [] - } - ] + } } }, "/v0/solvers/{solver_key}/releases/{version}/jobs/page": { @@ -3276,49 +3318,54 @@ "summary": "Get Jobs Page", "description": "List of jobs on a specific released solver (includes pagination)\n\nNew in *version 0.7*", "operationId": "get_jobs_page", + "security": [ + { + "HTTPBasic": [] + } + ], "parameters": [ { + "name": "solver_key", + "in": "path", "required": true, "schema": { "type": "string", "pattern": "^simcore/services/comp/([a-z0-9][a-z0-9_.-]*/)*([a-z0-9-_]+[a-z0-9])$", "title": "Solver Key" - }, - "name": "solver_key", - "in": "path" + } }, { + "name": "version", + "in": "path", "required": true, "schema": { "type": "string", "pattern": "^(0|[1-9]\\d*)(\\.(0|[1-9]\\d*)){2}(-(0|[1-9]\\d*|\\d*[-a-zA-Z][-\\da-zA-Z]*)(\\.(0|[1-9]\\d*|\\d*[-a-zA-Z][-\\da-zA-Z]*))*)?(\\+[-\\da-zA-Z]+(\\.[-\\da-zA-Z-]+)*)?$", "title": "Version" - }, - "name": "version", - "in": "path" + } }, { + "name": "limit", + "in": "query", "required": false, "schema": { "type": "integer", "maximum": 100, "minimum": 1, - "title": "Limit", - "default": 50 - }, - "name": "limit", - "in": "query" + "default": 50, + "title": "Limit" + } }, { + "name": "offset", + "in": "query", "required": false, "schema": { "type": "integer", "minimum": 0, - "title": "Offset", - "default": 0 - }, - "name": "offset", - "in": "query" + "default": 0, + "title": "Offset" + } } ], "responses": { @@ -3412,12 +3459,7 @@ } } } - }, - "security": [ - { - "HTTPBasic": [] - } - ] + } } }, "/v0/solvers/{solver_key}/releases/{version}/jobs/{job_id}/outputs": { @@ -3427,36 +3469,41 @@ ], "summary": "Get Job Outputs", "operationId": "get_job_outputs", + "security": [ + { + "HTTPBasic": [] + } + ], "parameters": [ { + "name": "solver_key", + "in": "path", "required": true, "schema": { "type": "string", "pattern": "^simcore/services/comp/([a-z0-9][a-z0-9_.-]*/)*([a-z0-9-_]+[a-z0-9])$", "title": "Solver Key" - }, - "name": "solver_key", - "in": "path" + } }, { + "name": "version", + "in": "path", "required": true, "schema": { "type": "string", "pattern": "^(0|[1-9]\\d*)(\\.(0|[1-9]\\d*)){2}(-(0|[1-9]\\d*|\\d*[-a-zA-Z][-\\da-zA-Z]*)(\\.(0|[1-9]\\d*|\\d*[-a-zA-Z][-\\da-zA-Z]*))*)?(\\+[-\\da-zA-Z]+(\\.[-\\da-zA-Z-]+)*)?$", "title": "Version" - }, - "name": "version", - "in": "path" + } }, { + "name": "job_id", + "in": "path", "required": true, "schema": { "type": "string", "format": "uuid", "title": "Job Id" - }, - "name": "job_id", - "in": "path" + } } ], "responses": { @@ -3550,12 +3597,7 @@ } } } - }, - "security": [ - { - "HTTPBasic": [] - } - ] + } } }, "/v0/solvers/{solver_key}/releases/{version}/jobs/{job_id}/outputs/logfile": { @@ -3566,36 +3608,41 @@ "summary": "Get Job Output Logfile", "description": "Special extra output with persistent logs file for the solver run.\n\n**NOTE**: this is not a log stream but a predefined output that is only\navailable after the job is done.\n\nNew in *version 0.4.0*", "operationId": "get_job_output_logfile", + "security": [ + { + "HTTPBasic": [] + } + ], "parameters": [ { + "name": "solver_key", + "in": "path", "required": true, "schema": { "type": "string", "pattern": "^simcore/services/comp/([a-z0-9][a-z0-9_.-]*/)*([a-z0-9-_]+[a-z0-9])$", "title": "Solver Key" - }, - "name": "solver_key", - "in": "path" + } }, { + "name": "version", + "in": "path", "required": true, "schema": { "type": "string", "pattern": "^(0|[1-9]\\d*)(\\.(0|[1-9]\\d*)){2}(-(0|[1-9]\\d*|\\d*[-a-zA-Z][-\\da-zA-Z]*)(\\.(0|[1-9]\\d*|\\d*[-a-zA-Z][-\\da-zA-Z]*))*)?(\\+[-\\da-zA-Z]+(\\.[-\\da-zA-Z-]+)*)?$", "title": "Version" - }, - "name": "version", - "in": "path" + } }, { + "name": "job_id", + "in": "path", "required": true, "schema": { "type": "string", "format": "uuid", "title": "Job Id" - }, - "name": "job_id", - "in": "path" + } } ], "responses": { @@ -3603,7 +3650,6 @@ "description": "Successful Response" }, "200": { - "description": "Returns a log file", "content": { "application/octet-stream": { "schema": { @@ -3622,7 +3668,8 @@ "type": "string" } } - } + }, + "description": "Returns a log file" }, "404": { "description": "Log not found" @@ -3687,12 +3734,7 @@ } } } - }, - "security": [ - { - "HTTPBasic": [] - } - ] + } } }, "/v0/solvers/{solver_key}/releases/{version}/jobs/{job_id}/wallet": { @@ -3703,36 +3745,41 @@ "summary": "Get Job Wallet", "description": "Get job wallet\n\nNew in *version 0.7*", "operationId": "get_job_wallet", + "security": [ + { + "HTTPBasic": [] + } + ], "parameters": [ { + "name": "solver_key", + "in": "path", "required": true, "schema": { "type": "string", "pattern": "^simcore/services/comp/([a-z0-9][a-z0-9_.-]*/)*([a-z0-9-_]+[a-z0-9])$", "title": "Solver Key" - }, - "name": "solver_key", - "in": "path" + } }, { + "name": "version", + "in": "path", "required": true, "schema": { "type": "string", "pattern": "^(0|[1-9]\\d*)(\\.(0|[1-9]\\d*)){2}(-(0|[1-9]\\d*|\\d*[-a-zA-Z][-\\da-zA-Z]*)(\\.(0|[1-9]\\d*|\\d*[-a-zA-Z][-\\da-zA-Z]*))*)?(\\+[-\\da-zA-Z]+(\\.[-\\da-zA-Z-]+)*)?$", "title": "Version" - }, - "name": "version", - "in": "path" + } }, { + "name": "job_id", + "in": "path", "required": true, "schema": { "type": "string", "format": "uuid", "title": "Job Id" - }, - "name": "job_id", - "in": "path" + } } ], "responses": { @@ -3741,7 +3788,15 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/WalletGetWithAvailableCredits" + "anyOf": [ + { + "$ref": "#/components/schemas/WalletGetWithAvailableCredits" + }, + { + "type": "null" + } + ], + "title": "Response Get Job Wallet V0 Solvers Solver Key Releases Version Jobs Job Id Wallet Get" } } } @@ -3826,12 +3881,7 @@ } } } - }, - "security": [ - { - "HTTPBasic": [] - } - ] + } } }, "/v0/solvers/{solver_key}/releases/{version}/jobs/{job_id}/pricing_unit": { @@ -3842,36 +3892,41 @@ "summary": "Get Job Pricing Unit", "description": "Get job pricing unit\n\nNew in *version 0.7*", "operationId": "get_job_pricing_unit", + "security": [ + { + "HTTPBasic": [] + } + ], "parameters": [ { + "name": "solver_key", + "in": "path", "required": true, "schema": { "type": "string", "pattern": "^simcore/services/comp/([a-z0-9][a-z0-9_.-]*/)*([a-z0-9-_]+[a-z0-9])$", "title": "Solver Key" - }, - "name": "solver_key", - "in": "path" + } }, { + "name": "version", + "in": "path", "required": true, "schema": { "type": "string", "pattern": "^(0|[1-9]\\d*)(\\.(0|[1-9]\\d*)){2}(-(0|[1-9]\\d*|\\d*[-a-zA-Z][-\\da-zA-Z]*)(\\.(0|[1-9]\\d*|\\d*[-a-zA-Z][-\\da-zA-Z]*))*)?(\\+[-\\da-zA-Z]+(\\.[-\\da-zA-Z-]+)*)?$", "title": "Version" - }, - "name": "version", - "in": "path" + } }, { + "name": "job_id", + "in": "path", "required": true, "schema": { "type": "string", "format": "uuid", "title": "Job Id" - }, - "name": "job_id", - "in": "path" + } } ], "responses": { @@ -3880,7 +3935,15 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PricingUnitGet" + "anyOf": [ + { + "$ref": "#/components/schemas/PricingUnitGet" + }, + { + "type": "null" + } + ], + "title": "Response Get Job Pricing Unit V0 Solvers Solver Key Releases Version Jobs Job Id Pricing Unit Get" } } } @@ -3955,12 +4018,7 @@ } } } - }, - "security": [ - { - "HTTPBasic": [] - } - ] + } } }, "/v0/solvers/{solver_key}/releases/{version}/jobs/{job_id}/logstream": { @@ -3970,36 +4028,41 @@ ], "summary": "Get Log Stream", "operationId": "get_log_stream", + "security": [ + { + "HTTPBasic": [] + } + ], "parameters": [ { + "name": "solver_key", + "in": "path", "required": true, "schema": { "type": "string", "pattern": "^simcore/services/comp/([a-z0-9][a-z0-9_.-]*/)*([a-z0-9-_]+[a-z0-9])$", "title": "Solver Key" - }, - "name": "solver_key", - "in": "path" + } }, { + "name": "version", + "in": "path", "required": true, "schema": { "type": "string", "pattern": "^(0|[1-9]\\d*)(\\.(0|[1-9]\\d*)){2}(-(0|[1-9]\\d*|\\d*[-a-zA-Z][-\\da-zA-Z]*)(\\.(0|[1-9]\\d*|\\d*[-a-zA-Z][-\\da-zA-Z]*))*)?(\\+[-\\da-zA-Z]+(\\.[-\\da-zA-Z-]+)*)?$", "title": "Version" - }, - "name": "version", - "in": "path" + } }, { + "name": "job_id", + "in": "path", "required": true, "schema": { "type": "string", "format": "uuid", "title": "Job Id" - }, - "name": "job_id", - "in": "path" + } } ], "responses": { @@ -4008,6 +4071,7 @@ "content": { "application/x-ndjson": { "schema": { + "type": "string", "anyOf": [ { "$ref": "#/components/schemas/JobLog" @@ -4016,7 +4080,6 @@ "$ref": "#/components/schemas/ErrorGet" } ], - "type": "string", "title": "Response 200 Get Log Stream V0 Solvers Solver Key Releases Version Jobs Job Id Logstream Get" } } @@ -4092,12 +4155,7 @@ } } } - }, - "security": [ - { - "HTTPBasic": [] - } - ] + } } }, "/v0/studies": { @@ -4108,29 +4166,34 @@ "summary": "List Studies", "description": "New in *version 0.5.0*", "operationId": "list_studies", + "security": [ + { + "HTTPBasic": [] + } + ], "parameters": [ { + "name": "limit", + "in": "query", "required": false, "schema": { "type": "integer", "maximum": 100, "minimum": 1, - "title": "Limit", - "default": 50 - }, - "name": "limit", - "in": "query" + "default": 50, + "title": "Limit" + } }, { + "name": "offset", + "in": "query", "required": false, "schema": { "type": "integer", "minimum": 0, - "title": "Offset", - "default": 0 - }, - "name": "offset", - "in": "query" + "default": 0, + "title": "Offset" + } } ], "responses": { @@ -4154,12 +4217,7 @@ } } } - }, - "security": [ - { - "HTTPBasic": [] - } - ] + } } }, "/v0/studies/{study_id}": { @@ -4170,16 +4228,21 @@ "summary": "Get Study", "description": "New in *version 0.5.0*", "operationId": "get_study", + "security": [ + { + "HTTPBasic": [] + } + ], "parameters": [ { + "name": "study_id", + "in": "path", "required": true, "schema": { "type": "string", "format": "uuid", "title": "Study Id" - }, - "name": "study_id", - "in": "path" + } } ], "responses": { @@ -4213,12 +4276,7 @@ } } } - }, - "security": [ - { - "HTTPBasic": [] - } - ] + } } }, "/v0/studies/{study_id}:clone": { @@ -4228,36 +4286,55 @@ ], "summary": "Clone Study", "operationId": "clone_study", + "security": [ + { + "HTTPBasic": [] + } + ], "parameters": [ { + "name": "study_id", + "in": "path", "required": true, "schema": { "type": "string", "format": "uuid", "title": "Study Id" - }, - "name": "study_id", - "in": "path" + } }, { + "name": "x-simcore-parent-project-uuid", + "in": "header", "required": false, "schema": { - "type": "string", - "format": "uuid", + "anyOf": [ + { + "type": "string", + "format": "uuid" + }, + { + "type": "null" + } + ], "title": "X-Simcore-Parent-Project-Uuid" - }, - "name": "x-simcore-parent-project-uuid", - "in": "header" + } }, { + "name": "x-simcore-parent-node-id", + "in": "header", "required": false, "schema": { - "type": "string", - "format": "uuid", + "anyOf": [ + { + "type": "string", + "format": "uuid" + }, + { + "type": "null" + } + ], "title": "X-Simcore-Parent-Node-Id" - }, - "name": "x-simcore-parent-node-id", - "in": "header" + } } ], "responses": { @@ -4291,12 +4368,7 @@ } } } - }, - "security": [ - { - "HTTPBasic": [] - } - ] + } } }, "/v0/studies/{study_id}/ports": { @@ -4307,16 +4379,21 @@ "summary": "List Study Ports", "description": "Lists metadata on ports of a given study\n\nNew in *version 0.5.0*", "operationId": "list_study_ports", + "security": [ + { + "HTTPBasic": [] + } + ], "parameters": [ { + "name": "study_id", + "in": "path", "required": true, "schema": { "type": "string", "format": "uuid", "title": "Study Id" - }, - "name": "study_id", - "in": "path" + } } ], "responses": { @@ -4350,12 +4427,7 @@ } } } - }, - "security": [ - { - "HTTPBasic": [] - } - ] + } } }, "/v0/studies/{study_id}/jobs": { @@ -4366,57 +4438,76 @@ "summary": "Create Study Job", "description": "hidden -- if True (default) hides project from UI", "operationId": "create_study_job", + "security": [ + { + "HTTPBasic": [] + } + ], "parameters": [ { + "name": "study_id", + "in": "path", "required": true, "schema": { "type": "string", "format": "uuid", "title": "Study Id" - }, - "name": "study_id", - "in": "path" + } }, { + "name": "hidden", + "in": "query", "required": false, "schema": { "type": "boolean", - "title": "Hidden", - "default": true - }, - "name": "hidden", - "in": "query" + "default": true, + "title": "Hidden" + } }, { + "name": "x-simcore-parent-project-uuid", + "in": "header", "required": false, "schema": { - "type": "string", - "format": "uuid", + "anyOf": [ + { + "type": "string", + "format": "uuid" + }, + { + "type": "null" + } + ], "title": "X-Simcore-Parent-Project-Uuid" - }, - "name": "x-simcore-parent-project-uuid", - "in": "header" + } }, { + "name": "x-simcore-parent-node-id", + "in": "header", "required": false, "schema": { - "type": "string", - "format": "uuid", + "anyOf": [ + { + "type": "string", + "format": "uuid" + }, + { + "type": "null" + } + ], "title": "X-Simcore-Parent-Node-Id" - }, - "name": "x-simcore-parent-node-id", - "in": "header" + } } ], "requestBody": { + "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/JobInputs" } } - }, - "required": true + } }, "responses": { "200": { @@ -4439,12 +4530,7 @@ } } } - }, - "security": [ - { - "HTTPBasic": [] - } - ] + } } }, "/v0/studies/{study_id}/jobs/{job_id}": { @@ -4455,26 +4541,31 @@ "summary": "Delete Study Job", "description": "Deletes an existing study job", "operationId": "delete_study_job", + "security": [ + { + "HTTPBasic": [] + } + ], "parameters": [ { + "name": "study_id", + "in": "path", "required": true, "schema": { "type": "string", "format": "uuid", "title": "Study Id" - }, - "name": "study_id", - "in": "path" + } }, { + "name": "job_id", + "in": "path", "required": true, "schema": { "type": "string", "format": "uuid", "title": "Job Id" - }, - "name": "job_id", - "in": "path" + } } ], "responses": { @@ -4482,14 +4573,14 @@ "description": "Successful Response" }, "404": { - "description": "Not Found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorGet" } } - } + }, + "description": "Not Found" }, "422": { "description": "Validation Error", @@ -4499,14 +4590,9 @@ "$ref": "#/components/schemas/HTTPValidationError" } } - } - } - }, - "security": [ - { - "HTTPBasic": [] + } } - ] + } } }, "/v0/studies/{study_id}/jobs/{job_id}:start": { @@ -4517,36 +4603,48 @@ "summary": "Start Study Job", "description": "Changed in *version 0.6.0*: Now responds with a 202 when successfully starting a computation", "operationId": "start_study_job", + "security": [ + { + "HTTPBasic": [] + } + ], "parameters": [ { + "name": "study_id", + "in": "path", "required": true, "schema": { "type": "string", "format": "uuid", "title": "Study Id" - }, - "name": "study_id", - "in": "path" + } }, { + "name": "job_id", + "in": "path", "required": true, "schema": { "type": "string", "format": "uuid", "title": "Job Id" - }, - "name": "job_id", - "in": "path" + } }, { + "name": "cluster_id", + "in": "query", "required": false, "schema": { - "type": "integer", - "minimum": 0, + "anyOf": [ + { + "type": "integer", + "minimum": 0 + }, + { + "type": "null" + } + ], "title": "Cluster Id" - }, - "name": "cluster_id", - "in": "query" + } } ], "responses": { @@ -4660,12 +4758,7 @@ } } } - }, - "security": [ - { - "HTTPBasic": [] - } - ] + } } }, "/v0/studies/{study_id}/jobs/{job_id}:stop": { @@ -4675,26 +4768,31 @@ ], "summary": "Stop Study Job", "operationId": "stop_study_job", + "security": [ + { + "HTTPBasic": [] + } + ], "parameters": [ { + "name": "study_id", + "in": "path", "required": true, "schema": { "type": "string", "format": "uuid", "title": "Study Id" - }, - "name": "study_id", - "in": "path" + } }, { + "name": "job_id", + "in": "path", "required": true, "schema": { "type": "string", "format": "uuid", "title": "Job Id" - }, - "name": "job_id", - "in": "path" + } } ], "responses": { @@ -4718,12 +4816,7 @@ } } } - }, - "security": [ - { - "HTTPBasic": [] - } - ] + } } }, "/v0/studies/{study_id}/jobs/{job_id}:inspect": { @@ -4733,26 +4826,31 @@ ], "summary": "Inspect Study Job", "operationId": "inspect_study_job", + "security": [ + { + "HTTPBasic": [] + } + ], "parameters": [ { + "name": "study_id", + "in": "path", "required": true, "schema": { "type": "string", "format": "uuid", "title": "Study Id" - }, - "name": "study_id", - "in": "path" + } }, { + "name": "job_id", + "in": "path", "required": true, "schema": { "type": "string", "format": "uuid", "title": "Job Id" - }, - "name": "job_id", - "in": "path" + } } ], "responses": { @@ -4776,12 +4874,7 @@ } } } - }, - "security": [ - { - "HTTPBasic": [] - } - ] + } } }, "/v0/studies/{study_id}/jobs/{job_id}/outputs": { @@ -4791,26 +4884,31 @@ ], "summary": "Get Study Job Outputs", "operationId": "get_study_job_outputs", + "security": [ + { + "HTTPBasic": [] + } + ], "parameters": [ { + "name": "study_id", + "in": "path", "required": true, "schema": { "type": "string", "format": "uuid", "title": "Study Id" - }, - "name": "study_id", - "in": "path" + } }, { + "name": "job_id", + "in": "path", "required": true, "schema": { "type": "string", "format": "uuid", "title": "Job Id" - }, - "name": "job_id", - "in": "path" + } } ], "responses": { @@ -4834,12 +4932,7 @@ } } } - }, - "security": [ - { - "HTTPBasic": [] - } - ] + } } }, "/v0/studies/{study_id}/jobs/{job_id}/outputs/log-links": { @@ -4849,26 +4942,31 @@ ], "summary": "Get download links for study job log files", "operationId": "get_study_job_output_logfile", + "security": [ + { + "HTTPBasic": [] + } + ], "parameters": [ { + "name": "study_id", + "in": "path", "required": true, "schema": { "type": "string", "format": "uuid", "title": "Study Id" - }, - "name": "study_id", - "in": "path" + } }, { + "name": "job_id", + "in": "path", "required": true, "schema": { "type": "string", "format": "uuid", "title": "Job Id" - }, - "name": "job_id", - "in": "path" + } } ], "responses": { @@ -4892,12 +4990,7 @@ } } } - }, - "security": [ - { - "HTTPBasic": [] - } - ] + } } }, "/v0/studies/{study_id}/jobs/{job_id}/metadata": { @@ -4908,26 +5001,31 @@ "summary": "Get Study Job Custom Metadata", "description": "Get custom metadata from a study's job\n\nNew in *version 0.7*", "operationId": "get_study_job_custom_metadata", + "security": [ + { + "HTTPBasic": [] + } + ], "parameters": [ { + "name": "study_id", + "in": "path", "required": true, "schema": { "type": "string", "format": "uuid", "title": "Study Id" - }, - "name": "study_id", - "in": "path" + } }, { + "name": "job_id", + "in": "path", "required": true, "schema": { "type": "string", "format": "uuid", "title": "Job Id" - }, - "name": "job_id", - "in": "path" + } } ], "responses": { @@ -4951,12 +5049,7 @@ } } } - }, - "security": [ - { - "HTTPBasic": [] - } - ] + } }, "put": { "tags": [ @@ -4965,37 +5058,42 @@ "summary": "Replace Study Job Custom Metadata", "description": "Changes custom metadata of a study's job\n\nNew in *version 0.7*", "operationId": "replace_study_job_custom_metadata", + "security": [ + { + "HTTPBasic": [] + } + ], "parameters": [ { + "name": "study_id", + "in": "path", "required": true, "schema": { "type": "string", "format": "uuid", "title": "Study Id" - }, - "name": "study_id", - "in": "path" + } }, { + "name": "job_id", + "in": "path", "required": true, "schema": { "type": "string", "format": "uuid", "title": "Job Id" - }, - "name": "job_id", - "in": "path" + } } ], "requestBody": { + "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/JobMetadataUpdate" } } - }, - "required": true + } }, "responses": { "200": { @@ -5018,12 +5116,7 @@ } } } - }, - "security": [ - { - "HTTPBasic": [] - } - ] + } } }, "/v0/wallets/default": { @@ -5131,15 +5224,20 @@ "summary": "Get Wallet", "description": "Get wallet\n\nNew in *version 0.7*", "operationId": "get_wallet", + "security": [ + { + "HTTPBasic": [] + } + ], "parameters": [ { + "name": "wallet_id", + "in": "path", "required": true, "schema": { "type": "integer", "title": "Wallet Id" - }, - "name": "wallet_id", - "in": "path" + } } ], "responses": { @@ -5233,12 +5331,7 @@ } } } - }, - "security": [ - { - "HTTPBasic": [] - } - ] + } } }, "/v0/credits/price": { @@ -5321,7 +5414,16 @@ "description": "File name" }, "filesize": { - "type": "integer", + "anyOf": [ + { + "type": "string", + "pattern": "^\\s*(\\d*\\.?\\d+)\\s*(\\w+)?" + }, + { + "type": "integer", + "minimum": 0 + } + ], "title": "Filesize", "description": "File size in bytes" }, @@ -5350,12 +5452,7 @@ "description": "The file resource id" }, "upload_schema": { - "allOf": [ - { - "$ref": "#/components/schemas/FileUploadData" - } - ], - "title": "Upload Schema", + "$ref": "#/components/schemas/FileUploadData", "description": "Schema for uploading file" } }, @@ -5400,18 +5497,39 @@ "description": "Name of the file with extension" }, "content_type": { - "type": "string", + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], "title": "Content Type", "description": "Guess of type content [EXPERIMENTAL]" }, "checksum": { - "type": "string", - "pattern": "^[a-fA-F0-9]{64}$", + "anyOf": [ + { + "type": "string", + "pattern": "^[a-fA-F0-9]{64}$" + }, + { + "type": "null" + } + ], "title": "Checksum", "description": "SHA256 hash of the file's content" }, "e_tag": { - "type": "string", + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], "title": "E Tag", "description": "S3 entity tag" } @@ -5444,12 +5562,12 @@ "properties": { "chunk_size": { "type": "integer", + "minimum": 0, "title": "Chunk Size" }, "urls": { "items": { "type": "string", - "maxLength": 65536, "minLength": 1, "format": "uri" }, @@ -5475,14 +5593,27 @@ "title": "Productname" }, "usdPerCredit": { - "type": "number", - "minimum": 0.0, + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], "title": "Usdpercredit", "description": "Price of a credit in USD. If None, then this product's price is UNDEFINED" }, "minPaymentAmountUsd": { - "type": "integer", - "minimum": 0, + "anyOf": [ + { + "type": "integer", + "minimum": 0 + }, + { + "type": "null" + } + ], "title": "Minpaymentamountusd", "description": "Minimum amount (included) in USD that can be paid for this productCan be None if this product's price is UNDEFINED" } @@ -5501,10 +5632,17 @@ "$ref": "#/components/schemas/UsersGroup" }, "organizations": { - "items": { - "$ref": "#/components/schemas/UsersGroup" - }, - "type": "array", + "anyOf": [ + { + "items": { + "$ref": "#/components/schemas/UsersGroup" + }, + "type": "array" + }, + { + "type": "null" + } + ], "title": "Organizations", "default": [] }, @@ -5562,26 +5700,47 @@ "description": "Runner that executes job" }, "url": { - "type": "string", - "maxLength": 2083, - "minLength": 1, - "format": "uri", + "anyOf": [ + { + "type": "string", + "maxLength": 2083, + "minLength": 1, + "format": "uri" + }, + { + "type": "null" + } + ], "title": "Url", "description": "Link to get this resource (self)" }, "runner_url": { - "type": "string", - "maxLength": 2083, - "minLength": 1, - "format": "uri", + "anyOf": [ + { + "type": "string", + "maxLength": 2083, + "minLength": 1, + "format": "uri" + }, + { + "type": "null" + } + ], "title": "Runner Url", "description": "Link to the solver's job (parent collection)" }, "outputs_url": { - "type": "string", - "maxLength": 2083, - "minLength": 1, - "format": "uri", + "anyOf": [ + { + "type": "string", + "maxLength": 2083, + "minLength": 1, + "format": "uri" + }, + { + "type": "null" + } + ], "title": "Outputs Url", "description": "Link to the job outputs (sub-collection)" } @@ -5599,14 +5758,14 @@ ], "title": "Job", "example": { + "created_at": "2021-01-22T23:59:52.322176", "id": "f622946d-fd29-35b9-a193-abdd1095167c", + "inputs_checksum": "12345", "name": "solvers/isolve/releases/1.3.4/jobs/f622946d-fd29-35b9-a193-abdd1095167c", + "outputs_url": "https://api.osparc.io/v0/solvers/isolve/releases/1.3.4/jobs/f622946d-fd29-35b9-a193-abdd1095167c/outputs", "runner_name": "solvers/isolve/releases/1.3.4", - "inputs_checksum": "12345", - "created_at": "2021-01-22T23:59:52.322176", - "url": "https://api.osparc.io/v0/solvers/isolve/releases/1.3.4/jobs/f622946d-fd29-35b9-a193-abdd1095167c", "runner_url": "https://api.osparc.io/v0/solvers/isolve/releases/1.3.4", - "outputs_url": "https://api.osparc.io/v0/solvers/isolve/releases/1.3.4/jobs/f622946d-fd29-35b9-a193-abdd1095167c/outputs" + "url": "https://api.osparc.io/v0/solvers/isolve/releases/1.3.4/jobs/f622946d-fd29-35b9-a193-abdd1095167c" } }, "JobInputs": { @@ -5632,6 +5791,9 @@ { "items": {}, "type": "array" + }, + { + "type": "null" } ] }, @@ -5646,14 +5808,14 @@ "title": "JobInputs", "example": { "values": { - "x": 4.33, - "n": 55, - "title": "Temperature", "enabled": true, "input_file": { "filename": "input.txt", "id": "0a3b2c56-dbcd-4871-b93b-d454b7883f9f" - } + }, + "n": 55, + "title": "Temperature", + "x": 4.33 } } }, @@ -5665,8 +5827,15 @@ "title": "Job Id" }, "node_id": { - "type": "string", - "format": "uuid", + "anyOf": [ + { + "type": "string", + "format": "uuid" + }, + { + "type": "null" + } + ], "title": "Node Id" }, "log_level": { @@ -5690,11 +5859,11 @@ "title": "JobLog", "example": { "job_id": "145beae4-a3a8-4fde-adbb-4e8257c2c083", - "node_id": "3742215e-6756-48d2-8b73-4d043065309f", "log_level": 10, "messages": [ "PROGRESS: 5/10" - ] + ], + "node_id": "3742215e-6756-48d2-8b73-4d043065309f" } }, "JobLogsMap": { @@ -5744,10 +5913,17 @@ "description": "Custom key-value map" }, "url": { - "type": "string", - "maxLength": 2083, - "minLength": 1, - "format": "uri", + "anyOf": [ + { + "type": "string", + "maxLength": 2083, + "minLength": 1, + "format": "uri" + }, + { + "type": "null" + } + ], "title": "Url", "description": "Link to get this resource (self)" } @@ -5816,6 +5992,9 @@ { "items": {}, "type": "array" + }, + { + "type": "null" } ] }, @@ -5832,14 +6011,14 @@ "example": { "job_id": "99d9ac65-9f10-4e2f-a433-b5e412bb037b", "results": { + "enabled": false, "maxSAR": 4.33, "n": 55, - "title": "Specific Absorption Rate", - "enabled": false, "output_file": { "filename": "sar_matrix.txt", "id": "0a3b2c56-dbcd-4871-b93b-d454b7883f9f" - } + }, + "title": "Specific Absorption Rate" } } }, @@ -5867,14 +6046,28 @@ "description": "Last modification timestamp of the solver job" }, "started_at": { - "type": "string", - "format": "date-time", + "anyOf": [ + { + "type": "string", + "format": "date-time" + }, + { + "type": "null" + } + ], "title": "Started At", "description": "Timestamp that indicate the moment the solver starts execution or None if the event did not occur" }, "stopped_at": { - "type": "string", - "format": "date-time", + "anyOf": [ + { + "type": "string", + "format": "date-time" + }, + { + "type": "null" + } + ], "title": "Stopped At", "description": "Timestamp at which the solver finished or killed execution or None if the event did not occur" } @@ -5888,41 +6081,78 @@ "title": "JobStatus", "example": { "job_id": "145beae4-a3a8-4fde-adbb-4e8257c2c083", - "state": "STARTED", "progress": 3, - "submitted_at": "2021-04-01 07:15:54.631007", - "started_at": "2021-04-01 07:16:43.670610" + "started_at": "2021-04-01 07:16:43.670610", + "state": "STARTED", + "submitted_at": "2021-04-01 07:15:54.631007" } }, "Links": { "properties": { "first": { - "type": "string", - "title": "First", - "example": "/api/v1/users?limit=1&offset1" + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "First" }, "last": { - "type": "string", - "title": "Last", - "example": "/api/v1/users?limit=1&offset1" + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Last" }, "self": { - "type": "string", - "title": "Self", - "example": "/api/v1/users?limit=1&offset1" + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Self" }, "next": { - "type": "string", - "title": "Next", - "example": "/api/v1/users?limit=1&offset1" + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Next" }, "prev": { - "type": "string", - "title": "Prev", - "example": "/api/v1/users?limit=1&offset1" + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Prev" } }, "type": "object", + "required": [ + "first", + "last", + "self", + "next", + "prev" + ], "title": "Links" }, "LogLink": { @@ -5933,7 +6163,6 @@ }, "download_link": { "type": "string", - "maxLength": 65536, "minLength": 1, "format": "uri", "title": "Download Link" @@ -5958,24 +6187,29 @@ "title": "Version" }, "released": { - "additionalProperties": { - "type": "string", - "pattern": "^(0|[1-9]\\d*)(\\.(0|[1-9]\\d*)){2}(-(0|[1-9]\\d*|\\d*[-a-zA-Z][-\\da-zA-Z]*)(\\.(0|[1-9]\\d*|\\d*[-a-zA-Z][-\\da-zA-Z]*))*)?(\\+[-\\da-zA-Z]+(\\.[-\\da-zA-Z-]+)*)?$" - }, - "type": "object", + "anyOf": [ + { + "additionalProperties": { + "type": "string", + "pattern": "^(0|[1-9]\\d*)(\\.(0|[1-9]\\d*)){2}(-(0|[1-9]\\d*|\\d*[-a-zA-Z][-\\da-zA-Z]*)(\\.(0|[1-9]\\d*|\\d*[-a-zA-Z][-\\da-zA-Z]*))*)?(\\+[-\\da-zA-Z]+(\\.[-\\da-zA-Z-]+)*)?$" + }, + "type": "object" + }, + { + "type": "null" + } + ], "title": "Released", "description": "Maps every route's path tag with a released version" }, "docs_url": { "type": "string", - "maxLength": 65536, "minLength": 1, "format": "uri", "title": "Docs Url" }, "docs_dev_url": { "type": "string", - "maxLength": 65536, "minLength": 1, "format": "uri", "title": "Docs Dev Url" @@ -5990,14 +6224,14 @@ ], "title": "Meta", "example": { + "docs_dev_url": "https://api.osparc.io/dev/doc", + "docs_url": "https://api.osparc.io/dev/doc", "name": "simcore_service_foo", - "version": "2.4.45", "released": { "v1": "1.3.4", "v2": "2.4.45" }, - "docs_url": "https://api.osparc.io/dev/doc", - "docs_dev_url": "https://api.osparc.io/dev/doc" + "version": "2.4.45" } }, "OnePage_SolverPort_": { @@ -6019,8 +6253,7 @@ "required": [ "items" ], - "title": "OnePage[SolverPort]", - "description": "A single page is used to envelope a small sequence that does not require\npagination\n\nIf total > MAXIMUM_NUMBER_OF_ITEMS_PER_PAGE, we should consider extending this\nentrypoint to proper pagination" + "title": "OnePage[SolverPort]" }, "OnePage_StudyPort_": { "properties": { @@ -6041,8 +6274,7 @@ "required": [ "items" ], - "title": "OnePage[StudyPort]", - "description": "A single page is used to envelope a small sequence that does not require\npagination\n\nIf total > MAXIMUM_NUMBER_OF_ITEMS_PER_PAGE, we should consider extending this\nentrypoint to proper pagination" + "title": "OnePage[StudyPort]" }, "Page_File_": { "properties": { @@ -6054,18 +6286,39 @@ "title": "Items" }, "total": { - "type": "integer", - "minimum": 0, + "anyOf": [ + { + "type": "integer", + "minimum": 0 + }, + { + "type": "null" + } + ], "title": "Total" }, "limit": { - "type": "integer", - "minimum": 1, + "anyOf": [ + { + "type": "integer", + "minimum": 1 + }, + { + "type": "null" + } + ], "title": "Limit" }, "offset": { - "type": "integer", - "minimum": 0, + "anyOf": [ + { + "type": "integer", + "minimum": 0 + }, + { + "type": "null" + } + ], "title": "Offset" }, "links": { @@ -6075,6 +6328,9 @@ "type": "object", "required": [ "items", + "total", + "limit", + "offset", "links" ], "title": "Page[File]" @@ -6089,18 +6345,39 @@ "title": "Items" }, "total": { - "type": "integer", - "minimum": 0, + "anyOf": [ + { + "type": "integer", + "minimum": 0 + }, + { + "type": "null" + } + ], "title": "Total" }, "limit": { - "type": "integer", - "minimum": 1, + "anyOf": [ + { + "type": "integer", + "minimum": 1 + }, + { + "type": "null" + } + ], "title": "Limit" }, "offset": { - "type": "integer", - "minimum": 0, + "anyOf": [ + { + "type": "integer", + "minimum": 0 + }, + { + "type": "null" + } + ], "title": "Offset" }, "links": { @@ -6110,6 +6387,9 @@ "type": "object", "required": [ "items", + "total", + "limit", + "offset", "links" ], "title": "Page[Job]" @@ -6124,18 +6404,39 @@ "title": "Items" }, "total": { - "type": "integer", - "minimum": 0, + "anyOf": [ + { + "type": "integer", + "minimum": 0 + }, + { + "type": "null" + } + ], "title": "Total" }, "limit": { - "type": "integer", - "minimum": 1, + "anyOf": [ + { + "type": "integer", + "minimum": 1 + }, + { + "type": "null" + } + ], "title": "Limit" }, "offset": { - "type": "integer", - "minimum": 0, + "anyOf": [ + { + "type": "integer", + "minimum": 0 + }, + { + "type": "null" + } + ], "title": "Offset" }, "links": { @@ -6145,6 +6446,9 @@ "type": "object", "required": [ "items", + "total", + "limit", + "offset", "links" ], "title": "Page[Study]" @@ -6154,8 +6458,8 @@ "enum": [ "TIER" ], - "title": "PricingPlanClassification", - "description": "An enumeration." + "const": "TIER", + "title": "PricingPlanClassification" }, "PricingUnitGet": { "properties": { @@ -6174,7 +6478,7 @@ "title": "Unitextrainfo" }, "currentCostPerUnit": { - "type": "number", + "type": "string", "title": "Currentcostperunit" }, "default": { @@ -6195,16 +6499,28 @@ "Profile": { "properties": { "first_name": { - "type": "string", - "maxLength": 255, - "title": "First Name", - "example": "James" + "anyOf": [ + { + "type": "string", + "maxLength": 255 + }, + { + "type": "null" + } + ], + "title": "First Name" }, "last_name": { - "type": "string", - "maxLength": 255, - "title": "Last Name", - "example": "Maxwell" + "anyOf": [ + { + "type": "string", + "maxLength": 255 + }, + { + "type": "null" + } + ], + "title": "Last Name" }, "id": { "type": "integer", @@ -6221,11 +6537,25 @@ "$ref": "#/components/schemas/UserRoleEnum" }, "groups": { - "$ref": "#/components/schemas/Groups" + "anyOf": [ + { + "$ref": "#/components/schemas/Groups" + }, + { + "type": "null" + } + ] }, "gravatar_id": { - "type": "string", - "maxLength": 40, + "anyOf": [ + { + "type": "string", + "maxLength": 40 + }, + { + "type": "null" + } + ], "title": "Gravatar Id", "description": "md5 hash value of email to retrieve an avatar image from https://www.gravatar.com" } @@ -6238,40 +6568,52 @@ ], "title": "Profile", "example": { - "id": "20", "first_name": "James", - "last_name": "Maxwell", - "login": "james-maxwell@itis.swiss", - "role": "USER", + "gravatar_id": "9a8930a5b20d7048e37740bac5c1ca4f", "groups": { + "all": { + "description": "all users", + "gid": "1", + "label": "Everyone" + }, "me": { + "description": "primary group", "gid": "123", - "label": "maxy", - "description": "primary group" + "label": "maxy" }, - "organizations": [], - "all": { - "gid": "1", - "label": "Everyone", - "description": "all users" - } + "organizations": [] }, - "gravatar_id": "9a8930a5b20d7048e37740bac5c1ca4f" + "id": "20", + "last_name": "Maxwell", + "login": "james-maxwell@itis.swiss", + "role": "USER" } }, "ProfileUpdate": { "properties": { "first_name": { - "type": "string", - "maxLength": 255, - "title": "First Name", - "example": "James" + "anyOf": [ + { + "type": "string", + "maxLength": 255 + }, + { + "type": "null" + } + ], + "title": "First Name" }, "last_name": { - "type": "string", - "maxLength": 255, - "title": "Last Name", - "example": "Maxwell" + "anyOf": [ + { + "type": "string", + "maxLength": 255 + }, + { + "type": "null" + } + ], + "title": "Last Name" } }, "type": "object", @@ -6362,7 +6704,14 @@ "description": "Human readable name" }, "description": { - "type": "string", + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], "title": "Description" }, "maintainer": { @@ -6370,10 +6719,17 @@ "title": "Maintainer" }, "url": { - "type": "string", - "maxLength": 2083, - "minLength": 1, - "format": "uri", + "anyOf": [ + { + "type": "string", + "maxLength": 2083, + "minLength": 1, + "format": "uri" + }, + { + "type": "null" + } + ], "title": "Url", "description": "Link to get this resource" } @@ -6389,12 +6745,12 @@ "title": "Solver", "description": "A released solver with a specific version", "example": { - "id": "simcore/services/comp/isolve", - "version": "2.1.1", - "title": "iSolve", "description": "EM solver", + "id": "simcore/services/comp/isolve", "maintainer": "info@itis.swiss", - "url": "https://api.osparc.io/v0/solvers/simcore%2Fservices%2Fcomp%2Fisolve/releases/2.1.1" + "title": "iSolve", + "url": "https://api.osparc.io/v0/solvers/simcore%2Fservices%2Fcomp%2Fisolve/releases/2.1.1", + "version": "2.1.1" } }, "SolverPort": { @@ -6414,7 +6770,14 @@ "title": "Kind" }, "content_schema": { - "type": "object", + "anyOf": [ + { + "type": "object" + }, + { + "type": "null" + } + ], "title": "Content Schema", "description": "jsonschema for the port's value. SEE https://json-schema.org" } @@ -6426,15 +6789,15 @@ ], "title": "SolverPort", "example": { - "key": "input_2", - "kind": "input", "content_schema": { + "maximum": 5, + "minimum": 0, "title": "Sleep interval", "type": "integer", - "x_unit": "second", - "minimum": 0, - "maximum": 5 - } + "x_unit": "second" + }, + "key": "input_2", + "kind": "input" } }, "Study": { @@ -6476,7 +6839,14 @@ "title": "Kind" }, "content_schema": { - "type": "object", + "anyOf": [ + { + "type": "object" + }, + { + "type": "null" + } + ], "title": "Content Schema", "description": "jsonschema for the port's value. SEE https://json-schema.org" } @@ -6488,15 +6858,15 @@ ], "title": "StudyPort", "example": { - "key": "input_2", - "kind": "input", "content_schema": { + "maximum": 5, + "minimum": 0, "title": "Sleep interval", "type": "integer", - "x_unit": "second", - "minimum": 0, - "maximum": 5 - } + "x_unit": "second" + }, + "key": "input_2", + "kind": "input" } }, "UploadLinks": { @@ -6547,8 +6917,7 @@ "PRODUCT_OWNER", "ADMIN" ], - "title": "UserRoleEnum", - "description": "An enumeration." + "title": "UserRoleEnum" }, "UsersGroup": { "properties": { @@ -6561,7 +6930,14 @@ "title": "Label" }, "description": { - "type": "string", + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], "title": "Description" } }, @@ -6620,7 +6996,14 @@ "title": "Name" }, "description": { - "type": "string", + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], "title": "Description" }, "owner": { @@ -6630,7 +7013,14 @@ "minimum": 0 }, "thumbnail": { - "type": "string", + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], "title": "Thumbnail" }, "status": { @@ -6655,7 +7045,9 @@ "required": [ "walletId", "name", + "description", "owner", + "thumbnail", "status", "created", "modified", @@ -6669,8 +7061,7 @@ "ACTIVE", "INACTIVE" ], - "title": "WalletStatus", - "description": "An enumeration." + "title": "WalletStatus" } }, "securitySchemes": { From a5985bd0ccec7b91c39b9aaa1c175bec2d92158e Mon Sep 17 00:00:00 2001 From: Mads Bisgaard Date: Thu, 24 Oct 2024 17:32:29 +0200 Subject: [PATCH 14/52] cleanup model location --- clients/python/src/osparc/_models.py | 48 ++++++++++++++++++++++++++ clients/python/src/osparc/models.py | 50 ++-------------------------- 2 files changed, 51 insertions(+), 47 deletions(-) create mode 100644 clients/python/src/osparc/_models.py diff --git a/clients/python/src/osparc/_models.py b/clients/python/src/osparc/_models.py new file mode 100644 index 00000000..8223d808 --- /dev/null +++ b/clients/python/src/osparc/_models.py @@ -0,0 +1,48 @@ +from osparc_client import ValuesValue +from osparc_client import JobInputs as _JobInputs +from osparc_client import JobOutputs as _JobOutputs +from typing import Dict, Any +from pydantic import BaseModel, StrictStr, Field + + +def _values_dict(v: Dict[str, Any]) -> Dict[str, ValuesValue | None]: + result = {} + for k, v in v.items(): + if v is not None: + result[k] = ValuesValue(v) + else: + result[k] = v + return result + + +class JobInputs(_JobInputs): + def __init__(self, *args, **kwargs): + if len(args) == 1 and len(kwargs) == 0: + input = args[0] + assert isinstance(input, dict) + super().__init__(values=_values_dict(input)) + return + if len(args) == 0 and len(kwargs) == 1: + values = kwargs.get("values") + if values is None: + raise RuntimeError("When passing a single kwarg it must be 'values'") + super().__init__(values=_values_dict(values)) + return + else: + super().__init__(*args, **kwargs) + + +class JobOutputs(BaseModel): + job_id: StrictStr = Field(description="Job that produced this output") + results: Dict[str, Any] + + @classmethod + def from_osparc_client_job_outputs(cls, outputs: _JobOutputs) -> "JobOutputs": + _results = {} + for k, v in outputs.results.items(): + if isinstance(v, ValuesValue): + _results[k] = v.actual_instance + else: + _results[k] = v + + return cls(job_id=outputs.job_id, results=_results) diff --git a/clients/python/src/osparc/models.py b/clients/python/src/osparc/models.py index 67043755..d1ff4a8f 100644 --- a/clients/python/src/osparc/models.py +++ b/clients/python/src/osparc/models.py @@ -28,14 +28,12 @@ HTTPValidationError as HTTPValidationError, ) from osparc_client.models.job import Job as Job -from osparc_client.models.job_inputs import JobInputs as _JobInputs from osparc_client.models.job_logs_map import JobLogsMap as JobLogsMap from osparc_client.models.job_metadata import JobMetadata as JobMetadata from osparc_client.models.job_metadata_update import ( JobMetadataUpdate as JobMetadataUpdate, ) from osparc_client.models.values_value import ValuesValue as ValuesValue -from osparc_client.models.job_outputs import JobOutputs as _JobOutputs from osparc_client.models.job_status import JobStatus as JobStatus from osparc_client.models.links import Links as Links from osparc_client.models.log_link import LogLink as LogLink @@ -72,51 +70,9 @@ WalletGetWithAvailableCredits as WalletGetWithAvailableCredits, ) from osparc_client.models.wallet_status import WalletStatus as WalletStatus -from pydantic import BaseModel, Field, StrictStr -from typing import Any, Dict + +from ._models import JobInputs as JobInputs +from ._models import JobOutputs as JobOutputs # renames TaskStates = _RunningState - - -def _values_dict(v: Dict[str, Any]) -> Dict[str, ValuesValue | None]: - result = {} - for k, v in v.items(): - if v is not None: - result[k] = ValuesValue(v) - else: - result[k] = v - return result - - -class JobInputs(_JobInputs): - def __init__(self, *args, **kwargs): - if len(args) == 1 and len(kwargs) == 0: - input = args[0] - assert isinstance(input, dict) - super().__init__(values=_values_dict(input)) - return - if len(args) == 0 and len(kwargs) == 1: - values = kwargs.get("values") - if values is None: - raise RuntimeError("When passing a single kwarg it must be 'values'") - super().__init__(values=_values_dict(values)) - return - else: - super().__init__(*args, **kwargs) - - -class JobOutputs(BaseModel): - job_id: StrictStr = Field(description="Job that produced this output") - results: Dict[str, Any] - - @classmethod - def from_osparc_client_job_outputs(cls, outputs: _JobOutputs) -> "JobOutputs": - _results = {} - for k, v in outputs.results.items(): - if isinstance(v, ValuesValue): - _results[k] = v.actual_instance - else: - _results[k] = v - - return cls(job_id=outputs.job_id, results=_results) From 4039885a294bf6a85c5603c3df7b22429a938886 Mon Sep 17 00:00:00 2001 From: Mads Bisgaard Date: Sun, 27 Oct 2024 22:00:35 +0100 Subject: [PATCH 15/52] start adding metadata test --- clients/python/test/test_osparc/test_solvers_api.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 clients/python/test/test_osparc/test_solvers_api.py diff --git a/clients/python/test/test_osparc/test_solvers_api.py b/clients/python/test/test_osparc/test_solvers_api.py new file mode 100644 index 00000000..9b4a4642 --- /dev/null +++ b/clients/python/test/test_osparc/test_solvers_api.py @@ -0,0 +1,13 @@ +import pytest +from pytest_mock import MockerFixture +from osparc import JobMetadata +from faker import Faker + + +@pytest.fixture +def job_metadata(faker: Faker): + JobMetadata(job_id=faker.uuid4(), metadata={"int": 2}) + + +def test_job_metadata_serialization(mocker: MockerFixture): + pass From 728c497a788f3410e3acfc3554e63da28f90b895 Mon Sep 17 00:00:00 2001 From: Mads Bisgaard Date: Sun, 27 Oct 2024 22:34:39 +0100 Subject: [PATCH 16/52] expose MetadataValue --- clients/python/src/osparc/models.py | 1 + 1 file changed, 1 insertion(+) diff --git a/clients/python/src/osparc/models.py b/clients/python/src/osparc/models.py index d1ff4a8f..04321c5f 100644 --- a/clients/python/src/osparc/models.py +++ b/clients/python/src/osparc/models.py @@ -44,6 +44,7 @@ from osparc_client.models.one_page_study_port import ( OnePageStudyPort as OnePageStudyPort, ) +from osparc_client import MetadataValue as MetadataValue from osparc_client.models.page_file import PageFile as PageFile from osparc_client.models.page_job import PageJob as PageJob from osparc_client.models.page_study import PageStudy as PageStudy From 7a6ca5f1c0c0aecd20abf3d44c0f25af6ec9f772 Mon Sep 17 00:00:00 2001 From: Mads Bisgaard Date: Sun, 27 Oct 2024 23:21:29 +0100 Subject: [PATCH 17/52] only missing check that objects coincide --- .../test/test_osparc/test_solvers_api.py | 43 ++++++++++++++++--- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/clients/python/test/test_osparc/test_solvers_api.py b/clients/python/test/test_osparc/test_solvers_api.py index 9b4a4642..bb9ab245 100644 --- a/clients/python/test/test_osparc/test_solvers_api.py +++ b/clients/python/test/test_osparc/test_solvers_api.py @@ -1,13 +1,46 @@ import pytest from pytest_mock import MockerFixture -from osparc import JobMetadata +from osparc import JobMetadata, MetadataValue, ApiClient, SolversApi from faker import Faker +from urllib3 import HTTPResponse @pytest.fixture -def job_metadata(faker: Faker): - JobMetadata(job_id=faker.uuid4(), metadata={"int": 2}) +def job_metadata(faker: Faker) -> JobMetadata: + _job_id = faker.uuid4() + return JobMetadata( + job_id=f"{_job_id}", + metadata={ + "job_id": MetadataValue(_job_id), + "job_name": MetadataValue(faker.text()), + "node_id": MetadataValue(faker.uuid4()), + }, + url=faker.url(), + ) -def test_job_metadata_serialization(mocker: MockerFixture): - pass +def test_job_metadata_serialization( + mocker: MockerFixture, + job_metadata: JobMetadata, + api_client: ApiClient, + faker: Faker, +): + def _get_job_sideeffect( + method: str, + url: str, + body=None, + fields=None, + headers=None, + json=None, + **urlopen_kw, + ) -> HTTPResponse: + response = HTTPResponse(status=200, body=job_metadata.to_json().encode()) + return response + + mocker.patch("urllib3.PoolManager.request", side_effect=_get_job_sideeffect) + + _solvers_api = SolversApi(api_client=api_client) + metadata = _solvers_api.get_job_custom_metadata( + solver_key="mysolver", version="1.2.3", job_id=f"{faker.uuid4()}" + ) + print(metadata) From 98b53e56062c24751b41e20750de2f855ad99f58 Mon Sep 17 00:00:00 2001 From: Mads Bisgaard Date: Mon, 28 Oct 2024 23:40:05 +0100 Subject: [PATCH 18/52] improve wrapper models --- clients/python/src/osparc/_api_solvers_api.py | 14 ++-- clients/python/src/osparc/_models.py | 67 ++++++++----------- .../test/test_osparc/test_solvers_api.py | 14 ++-- 3 files changed, 45 insertions(+), 50 deletions(-) diff --git a/clients/python/src/osparc/_api_solvers_api.py b/clients/python/src/osparc/_api_solvers_api.py index 39d95a95..d3a8ffe9 100644 --- a/clients/python/src/osparc/_api_solvers_api.py +++ b/clients/python/src/osparc/_api_solvers_api.py @@ -4,7 +4,8 @@ import httpx from osparc_client.api.solvers_api import SolversApi as _SolversApi -from .models import JobInputs, OnePageSolverPort, SolverPort, JobOutputs +from .models import JobInputs, OnePageSolverPort, SolverPort, JobOutputs, JobMetadata +from osparc_client import JobInputs as _JobInputs from ._api_client import ApiClient from ._settings import ParentProjectInfo @@ -101,8 +102,9 @@ def jobs(self, solver_key: str, version: str, **kwargs) -> PaginationGenerator: def create_job( self, solver_key: str, version: str, job_inputs: JobInputs, **kwargs ): + _job_inputs = _JobInputs.from_json(job_inputs.model_dump_json()) kwargs = {**kwargs, **ParentProjectInfo().model_dump(exclude_none=True)} - return super().create_job(solver_key, version, job_inputs, **kwargs) + return super().create_job(solver_key, version, _job_inputs, **kwargs) def get_job_output_logfile(self, *args, **kwargs): data = super().get_job_output_logfile(*args, **kwargs) @@ -113,6 +115,8 @@ def get_job_output_logfile(self, *args, **kwargs): def get_job_outputs(self, *args, **kwargs) -> JobOutputs: _osparc_client_outputs = super().get_job_outputs(*args, **kwargs) - _outputs = JobOutputs.from_osparc_client_job_outputs(_osparc_client_outputs) - assert _outputs is not None - return _outputs + return JobOutputs.model_validate_json(_osparc_client_outputs.to_json()) + + def get_job_custom_metadata(self, *args, **kwargs) -> JobMetadata: + metadata = super().get_job_custom_metadata(*args, **kwargs) + return JobMetadata.model_validate_json(metadata.to_json()) diff --git a/clients/python/src/osparc/_models.py b/clients/python/src/osparc/_models.py index 8223d808..35dee5fe 100644 --- a/clients/python/src/osparc/_models.py +++ b/clients/python/src/osparc/_models.py @@ -1,48 +1,37 @@ -from osparc_client import ValuesValue from osparc_client import JobInputs as _JobInputs from osparc_client import JobOutputs as _JobOutputs -from typing import Dict, Any +from osparc_client import JobMetadata as _JobMetadata +from .models import File +from typing import Dict, Union, List from pydantic import BaseModel, StrictStr, Field -def _values_dict(v: Dict[str, Any]) -> Dict[str, ValuesValue | None]: - result = {} - for k, v in v.items(): - if v is not None: - result[k] = ValuesValue(v) - else: - result[k] = v - return result - - -class JobInputs(_JobInputs): - def __init__(self, *args, **kwargs): - if len(args) == 1 and len(kwargs) == 0: - input = args[0] - assert isinstance(input, dict) - super().__init__(values=_values_dict(input)) - return - if len(args) == 0 and len(kwargs) == 1: - values = kwargs.get("values") - if values is None: - raise RuntimeError("When passing a single kwarg it must be 'values'") - super().__init__(values=_values_dict(values)) - return - else: - super().__init__(*args, **kwargs) +class JobInputs(BaseModel): + values: Dict[str, Union[File, List[object], bool, float, int, str, None]] = Field( + kw_only=False + ) + + def __init__( + self, values: Dict[str, Union[File, List[object], bool, float, int, str, None]] + ): + super().__init__(values=values) + + +assert set(_JobInputs.model_fields.keys()) == set(JobInputs.model_fields.keys()) class JobOutputs(BaseModel): job_id: StrictStr = Field(description="Job that produced this output") - results: Dict[str, Any] - - @classmethod - def from_osparc_client_job_outputs(cls, outputs: _JobOutputs) -> "JobOutputs": - _results = {} - for k, v in outputs.results.items(): - if isinstance(v, ValuesValue): - _results[k] = v.actual_instance - else: - _results[k] = v - - return cls(job_id=outputs.job_id, results=_results) + results: Dict[str, Union[File, List[object], bool, float, int, str, None]] + + +assert set(_JobOutputs.model_fields.keys()) == set(JobOutputs.model_fields.keys()) + + +class JobMetadata(BaseModel): + job_id: StrictStr + metadata: Dict[str, Union[bool, float, int, str, None]] + url: str | None + + +assert set(_JobMetadata.model_fields.keys()) == set(JobMetadata.model_fields.keys()) diff --git a/clients/python/test/test_osparc/test_solvers_api.py b/clients/python/test/test_osparc/test_solvers_api.py index bb9ab245..16b9fddd 100644 --- a/clients/python/test/test_osparc/test_solvers_api.py +++ b/clients/python/test/test_osparc/test_solvers_api.py @@ -1,6 +1,6 @@ import pytest from pytest_mock import MockerFixture -from osparc import JobMetadata, MetadataValue, ApiClient, SolversApi +from osparc import JobMetadata, ApiClient, SolversApi from faker import Faker from urllib3 import HTTPResponse @@ -11,9 +11,9 @@ def job_metadata(faker: Faker) -> JobMetadata: return JobMetadata( job_id=f"{_job_id}", metadata={ - "job_id": MetadataValue(_job_id), - "job_name": MetadataValue(faker.text()), - "node_id": MetadataValue(faker.uuid4()), + "job_id": _job_id, + "job_name": faker.text(), + "node_id": faker.uuid4(), }, url=faker.url(), ) @@ -34,7 +34,9 @@ def _get_job_sideeffect( json=None, **urlopen_kw, ) -> HTTPResponse: - response = HTTPResponse(status=200, body=job_metadata.to_json().encode()) + response = HTTPResponse( + status=200, body=job_metadata.model_dump_json().encode() + ) return response mocker.patch("urllib3.PoolManager.request", side_effect=_get_job_sideeffect) @@ -43,4 +45,4 @@ def _get_job_sideeffect( metadata = _solvers_api.get_job_custom_metadata( solver_key="mysolver", version="1.2.3", job_id=f"{faker.uuid4()}" ) - print(metadata) + assert metadata == job_metadata From f7030024654a1851151c9175595a1f673162bab4 Mon Sep 17 00:00:00 2001 From: Mads Bisgaard Date: Tue, 29 Oct 2024 11:11:13 +0100 Subject: [PATCH 19/52] improve jobinputs model --- clients/python/src/osparc/_api_solvers_api.py | 1 + clients/python/src/osparc/_api_studies_api.py | 5 ++++- clients/python/src/osparc/_models.py | 4 +--- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/clients/python/src/osparc/_api_solvers_api.py b/clients/python/src/osparc/_api_solvers_api.py index d3a8ffe9..8dec814d 100644 --- a/clients/python/src/osparc/_api_solvers_api.py +++ b/clients/python/src/osparc/_api_solvers_api.py @@ -103,6 +103,7 @@ def create_job( self, solver_key: str, version: str, job_inputs: JobInputs, **kwargs ): _job_inputs = _JobInputs.from_json(job_inputs.model_dump_json()) + assert _job_inputs is not None kwargs = {**kwargs, **ParentProjectInfo().model_dump(exclude_none=True)} return super().create_job(solver_key, version, _job_inputs, **kwargs) diff --git a/clients/python/src/osparc/_api_studies_api.py b/clients/python/src/osparc/_api_studies_api.py index bb78bd55..8cfb0605 100644 --- a/clients/python/src/osparc/_api_studies_api.py +++ b/clients/python/src/osparc/_api_studies_api.py @@ -9,6 +9,7 @@ import httpx from .models import JobInputs, JobLogsMap, PageStudy from osparc_client.api.studies_api import StudiesApi as _StudiesApi +from osparc_client import JobInputs as _JobInputs from tqdm.asyncio import tqdm_asyncio from ._api_client import ApiClient @@ -64,8 +65,10 @@ def __getattr__(self, name: str) -> Any: return super().__getattribute__(name) def create_study_job(self, study_id: str, job_inputs: JobInputs, **kwargs): + _job_inputs = _JobInputs.from_json(job_inputs.model_dump_json()) + assert _job_inputs is not None kwargs = {**kwargs, **ParentProjectInfo().model_dump(exclude_none=True)} - return super().create_study_job(study_id, job_inputs, **kwargs) + return super().create_study_job(study_id, _job_inputs, **kwargs) def clone_study(self, study_id: str, **kwargs): kwargs = {**kwargs, **ParentProjectInfo().model_dump(exclude_none=True)} diff --git a/clients/python/src/osparc/_models.py b/clients/python/src/osparc/_models.py index 35dee5fe..9530d423 100644 --- a/clients/python/src/osparc/_models.py +++ b/clients/python/src/osparc/_models.py @@ -7,9 +7,7 @@ class JobInputs(BaseModel): - values: Dict[str, Union[File, List[object], bool, float, int, str, None]] = Field( - kw_only=False - ) + values: Dict[str, Union[File, List[object], bool, float, int, str, None]] def __init__( self, values: Dict[str, Union[File, List[object], bool, float, int, str, None]] From 040d1f92f413c2f0d6949f7c4ebece9cd26383b9 Mon Sep 17 00:00:00 2001 From: Mads Bisgaard Date: Tue, 29 Oct 2024 11:43:04 +0100 Subject: [PATCH 20/52] handle metadata and job inputs/outputs in studies api --- clients/python/src/osparc/_api_solvers_api.py | 29 ++++++++++++++- clients/python/src/osparc/_api_studies_api.py | 35 ++++++++++++++++++- clients/python/src/osparc/_models.py | 10 ++++++ clients/python/src/osparc/models.py | 6 ++-- 4 files changed, 74 insertions(+), 6 deletions(-) diff --git a/clients/python/src/osparc/_api_solvers_api.py b/clients/python/src/osparc/_api_solvers_api.py index 8dec814d..2237ec12 100644 --- a/clients/python/src/osparc/_api_solvers_api.py +++ b/clients/python/src/osparc/_api_solvers_api.py @@ -4,8 +4,16 @@ import httpx from osparc_client.api.solvers_api import SolversApi as _SolversApi -from .models import JobInputs, OnePageSolverPort, SolverPort, JobOutputs, JobMetadata +from .models import ( + JobInputs, + OnePageSolverPort, + SolverPort, + JobOutputs, + JobMetadata, + JobMetadataUpdate, +) from osparc_client import JobInputs as _JobInputs +from osparc_client import JobMetadataUpdate as _JobMetadataUpdate from ._api_client import ApiClient from ._settings import ParentProjectInfo @@ -19,6 +27,7 @@ import warnings from tempfile import NamedTemporaryFile from pathlib import Path +from pydantic import validate_call class SolversApi(_SolversApi): @@ -99,6 +108,7 @@ def jobs(self, solver_key: str, version: str, **kwargs) -> PaginationGenerator: ) return self.iter_jobs(solver_key, version, **kwargs) + @validate_call def create_job( self, solver_key: str, version: str, job_inputs: JobInputs, **kwargs ): @@ -121,3 +131,20 @@ def get_job_outputs(self, *args, **kwargs) -> JobOutputs: def get_job_custom_metadata(self, *args, **kwargs) -> JobMetadata: metadata = super().get_job_custom_metadata(*args, **kwargs) return JobMetadata.model_validate_json(metadata.to_json()) + + @validate_call + def replace_job_custom_metadata( + self, + solver_key: str, + version: str, + job_id: str, + job_metadata_update: JobMetadataUpdate, + ) -> JobMetadata: + _job_metadata_update = _JobMetadataUpdate.from_json( + job_metadata_update.model_dump_json() + ) + assert _job_metadata_update is not None + _job_custom_metadata = super().replace_job_custom_metadata( + solver_key, version, job_id, _job_metadata_update + ) + return JobMetadata.model_validate_json(_job_custom_metadata.to_json()) diff --git a/clients/python/src/osparc/_api_studies_api.py b/clients/python/src/osparc/_api_studies_api.py index 8cfb0605..c7e171fa 100644 --- a/clients/python/src/osparc/_api_studies_api.py +++ b/clients/python/src/osparc/_api_studies_api.py @@ -7,9 +7,19 @@ from typing import Any, Optional import httpx -from .models import JobInputs, JobLogsMap, PageStudy +from pydantic import StrictStr + +from .models import ( + JobInputs, + JobLogsMap, + PageStudy, + JobOutputs, + JobMetadata, + JobMetadataUpdate, +) from osparc_client.api.studies_api import StudiesApi as _StudiesApi from osparc_client import JobInputs as _JobInputs +from osparc_client import JobMetadataUpdate as _JobMetadataUpdate from tqdm.asyncio import tqdm_asyncio from ._api_client import ApiClient @@ -70,6 +80,10 @@ def create_study_job(self, study_id: str, job_inputs: JobInputs, **kwargs): kwargs = {**kwargs, **ParentProjectInfo().model_dump(exclude_none=True)} return super().create_study_job(study_id, _job_inputs, **kwargs) + def get_study_job_outputs(self, *args, **kwargs) -> JobOutputs: + _job_outputs = super().get_study_job_outputs(*args, **kwargs) + return JobOutputs.model_validate_json(_job_outputs.to_json()) + def clone_study(self, study_id: str, **kwargs): kwargs = {**kwargs, **ParentProjectInfo().model_dump(exclude_none=True)} return super().clone_study(study_id, **kwargs) @@ -149,3 +163,22 @@ async def _download(unique_node_name: str, download_link: str) -> None: ) return folder + + def get_study_job_custom_metadata(self, study_id: str, job_id: str) -> JobMetadata: + _job_metadata = super().get_study_job_custom_metadata(study_id, job_id) + return JobMetadata.model_validate_json(_job_metadata.to_json()) + + def replace_study_job_custom_metadata( + self, + study_id: StrictStr, + job_id: StrictStr, + job_metadata_update: JobMetadataUpdate, + ) -> JobMetadata: + _job_metadata_update = _JobMetadataUpdate.from_json( + job_metadata_update.model_dump_json() + ) + assert _job_metadata_update is not None + _job_metadata = super().replace_study_job_custom_metadata( + study_id, job_id, _job_metadata_update + ) + return JobMetadata.model_validate_json(_job_metadata.to_json()) diff --git a/clients/python/src/osparc/_models.py b/clients/python/src/osparc/_models.py index 9530d423..ab824126 100644 --- a/clients/python/src/osparc/_models.py +++ b/clients/python/src/osparc/_models.py @@ -1,6 +1,7 @@ from osparc_client import JobInputs as _JobInputs from osparc_client import JobOutputs as _JobOutputs from osparc_client import JobMetadata as _JobMetadata +from osparc_client import JobMetadataUpdate as _JobMetadataUpdate from .models import File from typing import Dict, Union, List from pydantic import BaseModel, StrictStr, Field @@ -33,3 +34,12 @@ class JobMetadata(BaseModel): assert set(_JobMetadata.model_fields.keys()) == set(JobMetadata.model_fields.keys()) + + +class JobMetadataUpdate(BaseModel): + metadata: Dict[str, Union[bool, float, int, str, None]] + + +assert set(_JobMetadataUpdate.model_fields.keys()) == set( + JobMetadataUpdate.model_fields.keys() +) diff --git a/clients/python/src/osparc/models.py b/clients/python/src/osparc/models.py index 04321c5f..f15551ea 100644 --- a/clients/python/src/osparc/models.py +++ b/clients/python/src/osparc/models.py @@ -29,10 +29,6 @@ ) from osparc_client.models.job import Job as Job from osparc_client.models.job_logs_map import JobLogsMap as JobLogsMap -from osparc_client.models.job_metadata import JobMetadata as JobMetadata -from osparc_client.models.job_metadata_update import ( - JobMetadataUpdate as JobMetadataUpdate, -) from osparc_client.models.values_value import ValuesValue as ValuesValue from osparc_client.models.job_status import JobStatus as JobStatus from osparc_client.models.links import Links as Links @@ -74,6 +70,8 @@ from ._models import JobInputs as JobInputs from ._models import JobOutputs as JobOutputs +from ._models import JobMetadata as JobMetadata +from ._models import JobMetadataUpdate as JobMetadataUpdate # renames TaskStates = _RunningState From 5d0b7b58f39dbdc067964161bc27df47c8e157e2 Mon Sep 17 00:00:00 2001 From: Mads Bisgaard Date: Tue, 29 Oct 2024 11:48:34 +0100 Subject: [PATCH 21/52] fix several unittests --- clients/python/setup.py | 1 + clients/python/test/test_osparc/test_apis.py | 10 ++++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/clients/python/setup.py b/clients/python/setup.py index 28e09da5..c87ff413 100644 --- a/clients/python/setup.py +++ b/clients/python/setup.py @@ -30,6 +30,7 @@ "tenacity", "tqdm>=4.48.0", f"osparc_client=={VERSION}", + "urllib3", ] SETUP = dict( diff --git a/clients/python/test/test_osparc/test_apis.py b/clients/python/test/test_osparc/test_apis.py index 4b27d101..2ac1a94b 100644 --- a/clients/python/test/test_osparc/test_apis.py +++ b/clients/python/test/test_osparc/test_apis.py @@ -8,7 +8,7 @@ import pytest from faker import Faker -from osparc import ApiClient, SolversApi, StudiesApi +from osparc import ApiClient, SolversApi, StudiesApi, JobInputs from pytest_mock import MockerFixture @@ -60,11 +60,13 @@ def check_headers(**kwargs): ) solvers_api = SolversApi(api_client=api_client) - solvers_api.create_job(solver_key="mysolver", version="1.2.3", job_inputs={}) + solvers_api.create_job( + solver_key="mysolver", version="1.2.3", job_inputs=JobInputs({}) + ) studies_api = StudiesApi(api_client=api_client) - studies_api.create_study_job(study_id=faker.uuid4(), job_inputs={}) - studies_api.clone_study(study_id=faker.uuid4()) + studies_api.create_study_job(study_id=f"{faker.uuid4()}", job_inputs=JobInputs({})) + studies_api.clone_study(study_id=f"{faker.uuid4()}") @pytest.mark.parametrize( From 425733565b1a7f99f5d7fcb472415b8017205afb Mon Sep 17 00:00:00 2001 From: Mads Bisgaard Date: Tue, 29 Oct 2024 12:50:30 +0100 Subject: [PATCH 22/52] improve design of unittests --- clients/python/test/test_osparc/conftest.py | 28 +++++++++++ .../test/test_osparc/test_solvers_api.py | 46 +++++++++---------- 2 files changed, 50 insertions(+), 24 deletions(-) diff --git a/clients/python/test/test_osparc/conftest.py b/clients/python/test/test_osparc/conftest.py index ecf5face..a256ccd0 100644 --- a/clients/python/test/test_osparc/conftest.py +++ b/clients/python/test/test_osparc/conftest.py @@ -6,6 +6,10 @@ import osparc import pytest from faker import Faker +from pytest_mock import MockerFixture +from urllib3 import HTTPResponse +from pydantic import BaseModel +from typing import Callable, Generator @pytest.fixture @@ -25,3 +29,27 @@ def api_client(cfg: osparc.Configuration) -> osparc.ApiClient: @pytest.fixture def dev_mode_enabled(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setenv("OSPARC_DEV_FEATURES_ENABLED", "1") + + +@pytest.fixture +def create_server_mock( + mocker: MockerFixture, +) -> Generator[Callable[[int, BaseModel], None], None, None]: + def _mock_server(_status: int, _body: BaseModel) -> None: + def _sideeffect( + method: str, + url: str, + body=None, + fields=None, + headers=None, + json=None, + **urlopen_kw, + ) -> HTTPResponse: + response = HTTPResponse( + status=_status, body=_body.model_dump_json().encode() + ) + return response + + mocker.patch("urllib3.PoolManager.request", side_effect=_sideeffect) + + yield _mock_server diff --git a/clients/python/test/test_osparc/test_solvers_api.py b/clients/python/test/test_osparc/test_solvers_api.py index 16b9fddd..ac6b3cff 100644 --- a/clients/python/test/test_osparc/test_solvers_api.py +++ b/clients/python/test/test_osparc/test_solvers_api.py @@ -1,45 +1,43 @@ import pytest -from pytest_mock import MockerFixture -from osparc import JobMetadata, ApiClient, SolversApi +from osparc import JobMetadata, ApiClient, SolversApi, JobMetadataUpdate from faker import Faker -from urllib3 import HTTPResponse +from typing import Callable +from pydantic import BaseModel @pytest.fixture def job_metadata(faker: Faker) -> JobMetadata: - _job_id = faker.uuid4() + _job_id = f"{faker.uuid4()}" return JobMetadata( - job_id=f"{_job_id}", + job_id=_job_id, metadata={ "job_id": _job_id, - "job_name": faker.text(), - "node_id": faker.uuid4(), + "job_name": f"{faker.text()}", + "node_id": f"{faker.uuid4()}", }, url=faker.url(), ) -def test_job_metadata_serialization( - mocker: MockerFixture, +@pytest.fixture +def job_metadata_update(faker: Faker): + return JobMetadataUpdate( + metadata={ + "var1": faker.boolean(), + "var2": faker.pyfloat(), + "var3": faker.pyint(), + "var4": faker.text(), + } + ) + + +def test_get_job_custom_metadata( + create_server_mock: Callable[[int, BaseModel], None], job_metadata: JobMetadata, api_client: ApiClient, faker: Faker, ): - def _get_job_sideeffect( - method: str, - url: str, - body=None, - fields=None, - headers=None, - json=None, - **urlopen_kw, - ) -> HTTPResponse: - response = HTTPResponse( - status=200, body=job_metadata.model_dump_json().encode() - ) - return response - - mocker.patch("urllib3.PoolManager.request", side_effect=_get_job_sideeffect) + create_server_mock(200, job_metadata) _solvers_api = SolversApi(api_client=api_client) metadata = _solvers_api.get_job_custom_metadata( From c38eac2bd432b33a76f9cc7eb75cfc3fe9dac899 Mon Sep 17 00:00:00 2001 From: Mads Bisgaard Date: Tue, 29 Oct 2024 13:02:24 +0100 Subject: [PATCH 23/52] minor refactoring --- clients/python/test/test_osparc/conftest.py | 24 ++++++++++ .../test/test_osparc/test_solvers_api.py | 46 ++++++++----------- 2 files changed, 43 insertions(+), 27 deletions(-) diff --git a/clients/python/test/test_osparc/conftest.py b/clients/python/test/test_osparc/conftest.py index a256ccd0..b26f9bfc 100644 --- a/clients/python/test/test_osparc/conftest.py +++ b/clients/python/test/test_osparc/conftest.py @@ -53,3 +53,27 @@ def _sideeffect( mocker.patch("urllib3.PoolManager.request", side_effect=_sideeffect) yield _mock_server + + +@pytest.fixture +def job_metadata_update(faker: Faker): + return osparc.JobMetadataUpdate( + metadata={ + "boolean": faker.boolean(), + "float": faker.pyfloat(), + "int": faker.pyint(), + "str": faker.text(), + "None": None, + } + ) + + +@pytest.fixture +def job_metadata( + faker: Faker, job_metadata_update: osparc.JobMetadataUpdate +) -> osparc.JobMetadata: + return osparc.JobMetadata( + job_id=f"{faker.uuid4()}", + metadata=job_metadata_update.metadata, + url=faker.url(), + ) diff --git a/clients/python/test/test_osparc/test_solvers_api.py b/clients/python/test/test_osparc/test_solvers_api.py index ac6b3cff..10d6f593 100644 --- a/clients/python/test/test_osparc/test_solvers_api.py +++ b/clients/python/test/test_osparc/test_solvers_api.py @@ -1,36 +1,9 @@ -import pytest from osparc import JobMetadata, ApiClient, SolversApi, JobMetadataUpdate from faker import Faker from typing import Callable from pydantic import BaseModel -@pytest.fixture -def job_metadata(faker: Faker) -> JobMetadata: - _job_id = f"{faker.uuid4()}" - return JobMetadata( - job_id=_job_id, - metadata={ - "job_id": _job_id, - "job_name": f"{faker.text()}", - "node_id": f"{faker.uuid4()}", - }, - url=faker.url(), - ) - - -@pytest.fixture -def job_metadata_update(faker: Faker): - return JobMetadataUpdate( - metadata={ - "var1": faker.boolean(), - "var2": faker.pyfloat(), - "var3": faker.pyint(), - "var4": faker.text(), - } - ) - - def test_get_job_custom_metadata( create_server_mock: Callable[[int, BaseModel], None], job_metadata: JobMetadata, @@ -44,3 +17,22 @@ def test_get_job_custom_metadata( solver_key="mysolver", version="1.2.3", job_id=f"{faker.uuid4()}" ) assert metadata == job_metadata + + +def test_replace_job_custom_metadata( + create_server_mock: Callable[[int, BaseModel], None], + job_metadata: JobMetadata, + job_metadata_update: JobMetadataUpdate, + api_client: ApiClient, + faker: Faker, +): + create_server_mock(200, job_metadata) + + _solvers_api = SolversApi(api_client=api_client) + _job_metadata = _solvers_api.replace_job_custom_metadata( + solver_key="mysolver", + version="1.2.3", + job_id=f"{faker.uuid4()}", + job_metadata_update=job_metadata_update, + ) + assert _job_metadata == job_metadata From 8cd2ff3aab64a314f5d2bd3e344018469789533a Mon Sep 17 00:00:00 2001 From: Mads Bisgaard Date: Tue, 29 Oct 2024 13:12:22 +0100 Subject: [PATCH 24/52] start creating test for create solver jobs --- clients/python/test/test_osparc/conftest.py | 14 +++++++++++ .../test/test_osparc/test_solvers_api.py | 25 +++++++++++++------ 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/clients/python/test/test_osparc/conftest.py b/clients/python/test/test_osparc/conftest.py index b26f9bfc..90b7e193 100644 --- a/clients/python/test/test_osparc/conftest.py +++ b/clients/python/test/test_osparc/conftest.py @@ -77,3 +77,17 @@ def job_metadata( metadata=job_metadata_update.metadata, url=faker.url(), ) + + +@pytest.fixture +def job_inputs(faker: Faker) -> osparc.JobInputs: + return osparc.JobInputs( + { + "File": osparc.File(id=f"{faker.uuid4()}", filename=faker.file_name()), + "bool": faker.boolean(), + "float": faker.pyfloat(), + "int": faker.pyint(), + "str": faker.text(), + "None": None, + } + ) diff --git a/clients/python/test/test_osparc/test_solvers_api.py b/clients/python/test/test_osparc/test_solvers_api.py index 10d6f593..f5c68e35 100644 --- a/clients/python/test/test_osparc/test_solvers_api.py +++ b/clients/python/test/test_osparc/test_solvers_api.py @@ -1,19 +1,31 @@ from osparc import JobMetadata, ApiClient, SolversApi, JobMetadataUpdate from faker import Faker -from typing import Callable +from typing import Callable, Generator from pydantic import BaseModel +import pytest + + +@pytest.fixture +def solvers_api(api_client: ApiClient) -> Generator[SolversApi, None, None]: + yield SolversApi(api_client=api_client) + + +# def test_create_job( +# create_server_mock: Callable[[int, BaseModel], None], +# job_inputs: JobInputs): +# job: Job +# create_server_mock(201, job) def test_get_job_custom_metadata( create_server_mock: Callable[[int, BaseModel], None], job_metadata: JobMetadata, - api_client: ApiClient, faker: Faker, + solvers_api: SolversApi, ): create_server_mock(200, job_metadata) - _solvers_api = SolversApi(api_client=api_client) - metadata = _solvers_api.get_job_custom_metadata( + metadata = solvers_api.get_job_custom_metadata( solver_key="mysolver", version="1.2.3", job_id=f"{faker.uuid4()}" ) assert metadata == job_metadata @@ -23,13 +35,12 @@ def test_replace_job_custom_metadata( create_server_mock: Callable[[int, BaseModel], None], job_metadata: JobMetadata, job_metadata_update: JobMetadataUpdate, - api_client: ApiClient, + solvers_api: SolversApi, faker: Faker, ): create_server_mock(200, job_metadata) - _solvers_api = SolversApi(api_client=api_client) - _job_metadata = _solvers_api.replace_job_custom_metadata( + _job_metadata = solvers_api.replace_job_custom_metadata( solver_key="mysolver", version="1.2.3", job_id=f"{faker.uuid4()}", From 295c827577430f30c98e462c5aee28e6d198537f Mon Sep 17 00:00:00 2001 From: Mads Bisgaard Date: Tue, 29 Oct 2024 13:19:36 +0100 Subject: [PATCH 25/52] add create job test --- clients/python/test/test_osparc/conftest.py | 14 ++++++++++++++ .../test/test_osparc/test_solvers_api.py | 19 +++++++++++++------ 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/clients/python/test/test_osparc/conftest.py b/clients/python/test/test_osparc/conftest.py index 90b7e193..2b6a779c 100644 --- a/clients/python/test/test_osparc/conftest.py +++ b/clients/python/test/test_osparc/conftest.py @@ -91,3 +91,17 @@ def job_inputs(faker: Faker) -> osparc.JobInputs: "None": None, } ) + + +@pytest.fixture +def job(faker: Faker) -> osparc.Job: + return osparc.Job( + id=f"{faker.uuid4()}", + name=faker.file_name(), + inputs_checksum=f"{faker.sha256()}", + created_at=faker.date_time(), + runner_name="runner1", # must validate regexp, hence hardcoded + url=None, + runner_url=None, + outputs_url=None, + ) diff --git a/clients/python/test/test_osparc/test_solvers_api.py b/clients/python/test/test_osparc/test_solvers_api.py index f5c68e35..9927a739 100644 --- a/clients/python/test/test_osparc/test_solvers_api.py +++ b/clients/python/test/test_osparc/test_solvers_api.py @@ -1,4 +1,4 @@ -from osparc import JobMetadata, ApiClient, SolversApi, JobMetadataUpdate +from osparc import JobMetadata, ApiClient, SolversApi, JobMetadataUpdate, JobInputs, Job from faker import Faker from typing import Callable, Generator from pydantic import BaseModel @@ -10,11 +10,18 @@ def solvers_api(api_client: ApiClient) -> Generator[SolversApi, None, None]: yield SolversApi(api_client=api_client) -# def test_create_job( -# create_server_mock: Callable[[int, BaseModel], None], -# job_inputs: JobInputs): -# job: Job -# create_server_mock(201, job) +def test_create_job( + create_server_mock: Callable[[int, BaseModel], None], + job_inputs: JobInputs, + job: Job, + solvers_api: SolversApi, +): + create_server_mock(201, job) + + _job = solvers_api.create_job( + solver_key="mysolver", version="1.2.3", job_inputs=job_inputs + ) + assert _job == job def test_get_job_custom_metadata( From 1ef0fd5a802fde295bc64ca6dcb85451d62e1af9 Mon Sep 17 00:00:00 2001 From: Mads Bisgaard Date: Fri, 1 Nov 2024 14:01:57 +0100 Subject: [PATCH 26/52] extract models from openapi specs --- clients/python/test/test_osparc/conftest.py | 76 +++++++------------ .../test/test_osparc/test_solvers_api.py | 17 +++-- 2 files changed, 41 insertions(+), 52 deletions(-) diff --git a/clients/python/test/test_osparc/conftest.py b/clients/python/test/test_osparc/conftest.py index 2b6a779c..f2517e1c 100644 --- a/clients/python/test/test_osparc/conftest.py +++ b/clients/python/test/test_osparc/conftest.py @@ -10,6 +10,11 @@ from urllib3 import HTTPResponse from pydantic import BaseModel from typing import Callable, Generator +from prance import ResolvingParser +import json +from tempfile import NamedTemporaryFile +from pathlib import Path +from typing import Any, Type, TypeVar @pytest.fixture @@ -21,6 +26,16 @@ def cfg(faker: Faker) -> osparc.Configuration: ) +@pytest.fixture +def osparc_openapi_specs() -> Generator[dict[str, Any], None, None]: + with NamedTemporaryFile(suffix=".json") as file: + file = Path(file.name) + file.write_text(json.dumps(osparc.openapi())) + osparc_spec = ResolvingParser(f"{file.resolve()}").specification + assert osparc_spec is not None + yield osparc_spec + + @pytest.fixture def api_client(cfg: osparc.Configuration) -> osparc.ApiClient: return osparc.ApiClient(configuration=cfg) @@ -55,53 +70,20 @@ def _sideeffect( yield _mock_server -@pytest.fixture -def job_metadata_update(faker: Faker): - return osparc.JobMetadataUpdate( - metadata={ - "boolean": faker.boolean(), - "float": faker.pyfloat(), - "int": faker.pyint(), - "str": faker.text(), - "None": None, - } - ) - - -@pytest.fixture -def job_metadata( - faker: Faker, job_metadata_update: osparc.JobMetadataUpdate -) -> osparc.JobMetadata: - return osparc.JobMetadata( - job_id=f"{faker.uuid4()}", - metadata=job_metadata_update.metadata, - url=faker.url(), - ) +T = TypeVar("T", bound=BaseModel) @pytest.fixture -def job_inputs(faker: Faker) -> osparc.JobInputs: - return osparc.JobInputs( - { - "File": osparc.File(id=f"{faker.uuid4()}", filename=faker.file_name()), - "bool": faker.boolean(), - "float": faker.pyfloat(), - "int": faker.pyint(), - "str": faker.text(), - "None": None, - } - ) - - -@pytest.fixture -def job(faker: Faker) -> osparc.Job: - return osparc.Job( - id=f"{faker.uuid4()}", - name=faker.file_name(), - inputs_checksum=f"{faker.sha256()}", - created_at=faker.date_time(), - runner_name="runner1", # must validate regexp, hence hardcoded - url=None, - runner_url=None, - outputs_url=None, - ) +def create_osparc_response_model( + osparc_openapi_specs: dict[str, Any], +) -> Generator[Callable[[Type[T]], T], None, None]: + def _create_model(model_type: Type[T]) -> T: + schemas = osparc_openapi_specs.get("components", {}).get("schemas", {}) + example_data = schemas.get(model_type.__name__, {}).get("example", {}) + assert example_data, ( + "Could not extract example data for", + " '{model_type.__name__}' from openapi specs", + ) + return model_type.model_validate(example_data) + + yield _create_model diff --git a/clients/python/test/test_osparc/test_solvers_api.py b/clients/python/test/test_osparc/test_solvers_api.py index 9927a739..eac3a369 100644 --- a/clients/python/test/test_osparc/test_solvers_api.py +++ b/clients/python/test/test_osparc/test_solvers_api.py @@ -3,6 +3,9 @@ from typing import Callable, Generator from pydantic import BaseModel import pytest +from typing import Type, TypeVar + +T = TypeVar("T", bound=BaseModel) @pytest.fixture @@ -12,10 +15,12 @@ def solvers_api(api_client: ApiClient) -> Generator[SolversApi, None, None]: def test_create_job( create_server_mock: Callable[[int, BaseModel], None], - job_inputs: JobInputs, - job: Job, + create_osparc_response_model: Callable, solvers_api: SolversApi, ): + job_inputs = create_osparc_response_model(JobInputs) + job = create_osparc_response_model(Job) + create_server_mock(201, job) _job = solvers_api.create_job( @@ -26,10 +31,11 @@ def test_create_job( def test_get_job_custom_metadata( create_server_mock: Callable[[int, BaseModel], None], - job_metadata: JobMetadata, + create_osparc_response_model: Callable[[Type[JobMetadata]], JobMetadata], faker: Faker, solvers_api: SolversApi, ): + job_metadata = create_osparc_response_model(JobMetadata) create_server_mock(200, job_metadata) metadata = solvers_api.get_job_custom_metadata( @@ -40,11 +46,12 @@ def test_get_job_custom_metadata( def test_replace_job_custom_metadata( create_server_mock: Callable[[int, BaseModel], None], - job_metadata: JobMetadata, - job_metadata_update: JobMetadataUpdate, + create_osparc_response_model: Callable, solvers_api: SolversApi, faker: Faker, ): + job_metadata = create_osparc_response_model(JobMetadata) + job_metadata_update = create_osparc_response_model(JobMetadataUpdate) create_server_mock(200, job_metadata) _job_metadata = solvers_api.replace_job_custom_metadata( From 5b47cc26e22f9f6a0080475adbf4df3c4c01854d Mon Sep 17 00:00:00 2001 From: Mads Bisgaard Date: Fri, 1 Nov 2024 14:11:53 +0100 Subject: [PATCH 27/52] add test for joboutputs --- clients/python/test/test_osparc/conftest.py | 7 +++--- .../test/test_osparc/test_solvers_api.py | 25 ++++++++++++++++++- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/clients/python/test/test_osparc/conftest.py b/clients/python/test/test_osparc/conftest.py index f2517e1c..b7b346e7 100644 --- a/clients/python/test/test_osparc/conftest.py +++ b/clients/python/test/test_osparc/conftest.py @@ -80,10 +80,9 @@ def create_osparc_response_model( def _create_model(model_type: Type[T]) -> T: schemas = osparc_openapi_specs.get("components", {}).get("schemas", {}) example_data = schemas.get(model_type.__name__, {}).get("example", {}) - assert example_data, ( - "Could not extract example data for", - " '{model_type.__name__}' from openapi specs", - ) + error_msg = "Could not extract example data for" + error_msg += f" '{model_type.__name__}' from openapi specs" + assert example_data, error_msg return model_type.model_validate(example_data) yield _create_model diff --git a/clients/python/test/test_osparc/test_solvers_api.py b/clients/python/test/test_osparc/test_solvers_api.py index eac3a369..5060e44c 100644 --- a/clients/python/test/test_osparc/test_solvers_api.py +++ b/clients/python/test/test_osparc/test_solvers_api.py @@ -1,4 +1,12 @@ -from osparc import JobMetadata, ApiClient, SolversApi, JobMetadataUpdate, JobInputs, Job +from osparc import ( + JobMetadata, + ApiClient, + SolversApi, + JobMetadataUpdate, + JobInputs, + Job, + JobOutputs, +) from faker import Faker from typing import Callable, Generator from pydantic import BaseModel @@ -29,6 +37,21 @@ def test_create_job( assert _job == job +def test_get_job_outputs( + create_server_mock: Callable[[int, BaseModel], None], + create_osparc_response_model: Callable, + solvers_api: SolversApi, + faker: Faker, +): + job_outputs = create_osparc_response_model(JobOutputs) + create_server_mock(200, job_outputs) + + _job_outputs = solvers_api.get_job_outputs( + solver_key="mysolver", version="1.2.3", job_id=faker.uuid4() + ) + assert _job_outputs == job_outputs + + def test_get_job_custom_metadata( create_server_mock: Callable[[int, BaseModel], None], create_osparc_response_model: Callable[[Type[JobMetadata]], JobMetadata], From da0965f8f55599352db5c50b0150c5b94ddf4deb Mon Sep 17 00:00:00 2001 From: Mads Bisgaard Date: Fri, 1 Nov 2024 15:03:05 +0100 Subject: [PATCH 28/52] minor cleanup --- clients/python/test/test_osparc/conftest.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/clients/python/test/test_osparc/conftest.py b/clients/python/test/test_osparc/conftest.py index b7b346e7..eac73522 100644 --- a/clients/python/test/test_osparc/conftest.py +++ b/clients/python/test/test_osparc/conftest.py @@ -14,7 +14,7 @@ import json from tempfile import NamedTemporaryFile from pathlib import Path -from typing import Any, Type, TypeVar +from typing import Any, TypeVar @pytest.fixture @@ -49,7 +49,7 @@ def dev_mode_enabled(monkeypatch: pytest.MonkeyPatch) -> None: @pytest.fixture def create_server_mock( mocker: MockerFixture, -) -> Generator[Callable[[int, BaseModel], None], None, None]: +) -> Callable[[int, BaseModel], None]: def _mock_server(_status: int, _body: BaseModel) -> None: def _sideeffect( method: str, @@ -67,7 +67,7 @@ def _sideeffect( mocker.patch("urllib3.PoolManager.request", side_effect=_sideeffect) - yield _mock_server + return _mock_server T = TypeVar("T", bound=BaseModel) @@ -76,8 +76,8 @@ def _sideeffect( @pytest.fixture def create_osparc_response_model( osparc_openapi_specs: dict[str, Any], -) -> Generator[Callable[[Type[T]], T], None, None]: - def _create_model(model_type: Type[T]) -> T: +) -> Callable[[type[T]], T]: + def _create_model(model_type: type[T]) -> T: schemas = osparc_openapi_specs.get("components", {}).get("schemas", {}) example_data = schemas.get(model_type.__name__, {}).get("example", {}) error_msg = "Could not extract example data for" @@ -85,4 +85,4 @@ def _create_model(model_type: Type[T]) -> T: assert example_data, error_msg return model_type.model_validate(example_data) - yield _create_model + return _create_model From 3fd820772957edccbce49f2c6d300a2842632747 Mon Sep 17 00:00:00 2001 From: Mads Bisgaard Date: Fri, 1 Nov 2024 15:05:53 +0100 Subject: [PATCH 29/52] remove obsolete tests --- .../test_osparc_client/__init__.py | 0 .../test_osparc_client/test_file.py | 55 -------- .../test_osparc_client/test_files_api.py | 61 --------- .../test_osparc_client/test_groups.py | 76 ----------- .../test_http_validation_error.py | 56 -------- .../test_osparc_client/test_job.py | 70 ---------- .../test_osparc_client/test_job_inputs.py | 49 ------- .../test_osparc_client/test_job_outputs.py | 50 ------- .../test_osparc_client/test_job_status.py | 67 ---------- .../test_osparc_client/test_meta.py | 58 -------- .../test_osparc_client/test_meta_api.py | 40 ------ .../test_osparc_client/test_profile.py | 74 ----------- .../test_osparc_client/test_profile_update.py | 47 ------- .../test_osparc_client/test_solver.py | 60 --------- .../test_osparc_client/test_solvers_api.py | 124 ------------------ .../test_osparc_client/test_task_states.py | 47 ------- .../test_osparc_client/test_user_role_enum.py | 47 ------- .../test_osparc_client/test_users_api.py | 47 ------- .../test_osparc_client/test_users_group.py | 50 ------- .../test_validation_error.py | 51 ------- 20 files changed, 1129 deletions(-) delete mode 100644 clients/python/test/test_osparc/test_osparc_client/__init__.py delete mode 100644 clients/python/test/test_osparc/test_osparc_client/test_file.py delete mode 100644 clients/python/test/test_osparc/test_osparc_client/test_files_api.py delete mode 100644 clients/python/test/test_osparc/test_osparc_client/test_groups.py delete mode 100644 clients/python/test/test_osparc/test_osparc_client/test_http_validation_error.py delete mode 100644 clients/python/test/test_osparc/test_osparc_client/test_job.py delete mode 100644 clients/python/test/test_osparc/test_osparc_client/test_job_inputs.py delete mode 100644 clients/python/test/test_osparc/test_osparc_client/test_job_outputs.py delete mode 100644 clients/python/test/test_osparc/test_osparc_client/test_job_status.py delete mode 100644 clients/python/test/test_osparc/test_osparc_client/test_meta.py delete mode 100644 clients/python/test/test_osparc/test_osparc_client/test_meta_api.py delete mode 100644 clients/python/test/test_osparc/test_osparc_client/test_profile.py delete mode 100644 clients/python/test/test_osparc/test_osparc_client/test_profile_update.py delete mode 100644 clients/python/test/test_osparc/test_osparc_client/test_solver.py delete mode 100644 clients/python/test/test_osparc/test_osparc_client/test_solvers_api.py delete mode 100644 clients/python/test/test_osparc/test_osparc_client/test_task_states.py delete mode 100644 clients/python/test/test_osparc/test_osparc_client/test_user_role_enum.py delete mode 100644 clients/python/test/test_osparc/test_osparc_client/test_users_api.py delete mode 100644 clients/python/test/test_osparc/test_osparc_client/test_users_group.py delete mode 100644 clients/python/test/test_osparc/test_osparc_client/test_validation_error.py diff --git a/clients/python/test/test_osparc/test_osparc_client/__init__.py b/clients/python/test/test_osparc/test_osparc_client/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/clients/python/test/test_osparc/test_osparc_client/test_file.py b/clients/python/test/test_osparc/test_osparc_client/test_file.py deleted file mode 100644 index f2882aa3..00000000 --- a/clients/python/test/test_osparc/test_osparc_client/test_file.py +++ /dev/null @@ -1,55 +0,0 @@ -# coding: utf-8 - -""" - osparc.io web API - - osparc-simcore public web API specifications # noqa: E501 - - The version of the OpenAPI document: 0.4.0 - Generated by: https://openapi-generator.tech -""" - - -from __future__ import absolute_import - -import unittest - -from osparc import File # noqa: E501 - - -class TestFile(unittest.TestCase): - """File unit test stubs""" - - def setUp(self): - pass - - def tearDown(self): - pass - - def make_instance(self, include_optional): - """Test File - include_option is a boolean, when False only required - params are included, when True both required and - optional params are included""" - # model = osparc.models.file.File() # noqa: E501 - if include_optional: - return File( - id="0", - filename="0", - content_type="0", - checksum="333daca44bef79b1af3126bb4925bca5c86e7de27f69679913104b9f6d1a40fb", - ) - else: - return File( - id="0", - filename="0", - ) - - def testFile(self): - """Test File""" - self.make_instance(include_optional=False) - self.make_instance(include_optional=True) - - -if __name__ == "__main__": - unittest.main() diff --git a/clients/python/test/test_osparc/test_osparc_client/test_files_api.py b/clients/python/test/test_osparc/test_osparc_client/test_files_api.py deleted file mode 100644 index d08ccad7..00000000 --- a/clients/python/test/test_osparc/test_osparc_client/test_files_api.py +++ /dev/null @@ -1,61 +0,0 @@ -# coding: utf-8 - -""" - osparc.io web API - - osparc-simcore public web API specifications # noqa: E501 - - The version of the OpenAPI document: 0.4.0 - Generated by: https://openapi-generator.tech -""" - - -from __future__ import absolute_import - -import unittest - -from osparc import ApiClient, Configuration, FilesApi # noqa: E501 - - -class TestFilesApi(unittest.TestCase): - """FilesApi unit test stubs""" - - def setUp(self): - self.api = FilesApi( - api_client=ApiClient(configuration=Configuration()) - ) # noqa: E501 - - def tearDown(self): - pass - - def test_download_file(self): - """Test case for download_file - - Download File # noqa: E501 - """ - pass - - def test_get_file(self): - """Test case for get_file - - Get File # noqa: E501 - """ - pass - - def test_list_files(self): - """Test case for list_files - - List Files # noqa: E501 - """ - pass - - def test_upload_file(self): - """Test case for upload_file - - Upload File # noqa: E501 - """ - pass - - -if __name__ == "__main__": - unittest.main() diff --git a/clients/python/test/test_osparc/test_osparc_client/test_groups.py b/clients/python/test/test_osparc/test_osparc_client/test_groups.py deleted file mode 100644 index 794b0221..00000000 --- a/clients/python/test/test_osparc/test_osparc_client/test_groups.py +++ /dev/null @@ -1,76 +0,0 @@ -# coding: utf-8 - -""" - osparc.io web API - - osparc-simcore public web API specifications # noqa: E501 - - The version of the OpenAPI document: 0.4.0 - Generated by: https://openapi-generator.tech -""" - - -from __future__ import absolute_import - -import unittest - -from osparc import Groups, UsersGroup # noqa: E501 - - -class TestGroups(unittest.TestCase): - """Groups unit test stubs""" - - def setUp(self): - pass - - def tearDown(self): - pass - - def make_instance(self, include_optional): - """Test Groups - include_option is a boolean, when False only required - params are included, when True both required and - optional params are included""" - # model = osparc.models.groups.Groups() # noqa: E501 - if include_optional: - return Groups( - me=UsersGroup( - gid="0", - label="0", - description="0", - ), - organizations=[ - UsersGroup( - gid="0", - label="0", - description="0", - ) - ], - all=UsersGroup( - gid="0", - label="0", - description="0", - ), - ) - else: - return Groups( - me=UsersGroup( - gid="0", - label="0", - description="0", - ), - all=UsersGroup( - gid="0", - label="0", - description="0", - ), - ) - - def testGroups(self): - """Test Groups""" - self.make_instance(include_optional=False) - self.make_instance(include_optional=True) - - -if __name__ == "__main__": - unittest.main() diff --git a/clients/python/test/test_osparc/test_osparc_client/test_http_validation_error.py b/clients/python/test/test_osparc/test_osparc_client/test_http_validation_error.py deleted file mode 100644 index c6699821..00000000 --- a/clients/python/test/test_osparc/test_osparc_client/test_http_validation_error.py +++ /dev/null @@ -1,56 +0,0 @@ -# coding: utf-8 - -""" - osparc.io web API - - osparc-simcore public web API specifications # noqa: E501 - - The version of the OpenAPI document: 0.4.0 - Generated by: https://openapi-generator.tech -""" - - -from __future__ import absolute_import - -import unittest - -from osparc import HTTPValidationError # noqa: E501 -from osparc import ValidationError - - -class TestHTTPValidationError(unittest.TestCase): - """HTTPValidationError unit test stubs""" - - def setUp(self): - pass - - def tearDown(self): - pass - - def make_instance(self, include_optional): - """Test HTTPValidationError - include_option is a boolean, when False only required - params are included, when True both required and - optional params are included""" - # model = osparc.models.http_validation_error.HTTPValidationError() # noqa: E501 - if include_optional: - return HTTPValidationError( - errors=[ - ValidationError( - loc=["0"], - msg="0", - type="0", - ) - ] - ) - else: - return HTTPValidationError() - - def testHTTPValidationError(self): - """Test HTTPValidationError""" - self.make_instance(include_optional=False) - self.make_instance(include_optional=True) - - -if __name__ == "__main__": - unittest.main() diff --git a/clients/python/test/test_osparc/test_osparc_client/test_job.py b/clients/python/test/test_osparc/test_osparc_client/test_job.py deleted file mode 100644 index 3c9754eb..00000000 --- a/clients/python/test/test_osparc/test_osparc_client/test_job.py +++ /dev/null @@ -1,70 +0,0 @@ -# coding: utf-8 - -""" - osparc.io web API - - osparc-simcore public web API specifications # noqa: E501 - - The version of the OpenAPI document: 0.4.0 - Generated by: https://openapi-generator.tech -""" - - -from __future__ import absolute_import - -import datetime -import unittest - -from osparc import Job # noqa: E501 - - -class TestJob(unittest.TestCase): - """Job unit test stubs""" - - def setUp(self): - pass - - def tearDown(self): - pass - - def make_instance(self, include_optional): - """Test Job - include_option is a boolean, when False only required - params are included, when True both required and - optional params are included""" - # model = osparc.models.job.Job() # noqa: E501 - if include_optional: - return Job( - id="0", - name="a", - inputs_checksum="0", - created_at=datetime.datetime.strptime( - "2013-10-20 19:20:30.00", "%Y-%m-%d %H:%M:%S.%f" - ), - runner_name="a", - url="0", - runner_url="0", - outputs_url="0", - ) - else: - return Job( - id="0", - name="a", - inputs_checksum="0", - created_at=datetime.datetime.strptime( - "2013-10-20 19:20:30.00", "%Y-%m-%d %H:%M:%S.%f" - ), - runner_name="a", - url="0", - runner_url="0", - outputs_url="0", - ) - - def testJob(self): - """Test Job""" - self.make_instance(include_optional=False) - self.make_instance(include_optional=True) - - -if __name__ == "__main__": - unittest.main() diff --git a/clients/python/test/test_osparc/test_osparc_client/test_job_inputs.py b/clients/python/test/test_osparc/test_osparc_client/test_job_inputs.py deleted file mode 100644 index 6390b43a..00000000 --- a/clients/python/test/test_osparc/test_osparc_client/test_job_inputs.py +++ /dev/null @@ -1,49 +0,0 @@ -# coding: utf-8 - -""" - osparc.io web API - - osparc-simcore public web API specifications # noqa: E501 - - The version of the OpenAPI document: 0.4.0 - Generated by: https://openapi-generator.tech -""" - - -from __future__ import absolute_import - -import unittest - -from osparc import JobInputs # noqa: E501 - - -class TestJobInputs(unittest.TestCase): - """JobInputs unit test stubs""" - - def setUp(self): - pass - - def tearDown(self): - pass - - def make_instance(self, include_optional): - """Test JobInputs - include_option is a boolean, when False only required - params are included, when True both required and - optional params are included""" - # model = osparc.models.job_inputs.JobInputs() # noqa: E501 - if include_optional: - return JobInputs(values={"key": 42}) - else: - return JobInputs( - values={"key": 3.14}, - ) - - def testJobInputs(self): - """Test JobInputs""" - self.make_instance(include_optional=False) - self.make_instance(include_optional=True) - - -if __name__ == "__main__": - unittest.main() diff --git a/clients/python/test/test_osparc/test_osparc_client/test_job_outputs.py b/clients/python/test/test_osparc/test_osparc_client/test_job_outputs.py deleted file mode 100644 index 77d40ee9..00000000 --- a/clients/python/test/test_osparc/test_osparc_client/test_job_outputs.py +++ /dev/null @@ -1,50 +0,0 @@ -# coding: utf-8 - -""" - osparc.io web API - - osparc-simcore public web API specifications # noqa: E501 - - The version of the OpenAPI document: 0.4.0 - Generated by: https://openapi-generator.tech -""" - - -from __future__ import absolute_import - -import unittest - -from osparc import JobOutputs # noqa: E501 - - -class TestJobOutputs(unittest.TestCase): - """JobOutputs unit test stubs""" - - def setUp(self): - pass - - def tearDown(self): - pass - - def make_instance(self, include_optional): - """Test JobOutputs - include_option is a boolean, when False only required - params are included, when True both required and - optional params are included""" - # model = osparc.models.job_outputs.JobOutputs() # noqa: E501 - if include_optional: - return JobOutputs(job_id="0", results={"key": 3.14}) - else: - return JobOutputs( - job_id="0", - results={"key": 42}, - ) - - def testJobOutputs(self): - """Test JobOutputs""" - self.make_instance(include_optional=False) - self.make_instance(include_optional=True) - - -if __name__ == "__main__": - unittest.main() diff --git a/clients/python/test/test_osparc/test_osparc_client/test_job_status.py b/clients/python/test/test_osparc/test_osparc_client/test_job_status.py deleted file mode 100644 index 2cc5e301..00000000 --- a/clients/python/test/test_osparc/test_osparc_client/test_job_status.py +++ /dev/null @@ -1,67 +0,0 @@ -# coding: utf-8 - -""" - osparc.io web API - - osparc-simcore public web API specifications # noqa: E501 - - The version of the OpenAPI document: 0.4.0 - Generated by: https://openapi-generator.tech -""" - - -from __future__ import absolute_import - -import datetime -import unittest - -from osparc import JobStatus # noqa: E501 - - -class TestJobStatus(unittest.TestCase): - """JobStatus unit test stubs""" - - def setUp(self): - pass - - def tearDown(self): - pass - - def make_instance(self, include_optional): - """Test JobStatus - include_option is a boolean, when False only required - params are included, when True both required and - optional params are included""" - # model = osparc.models.job_status.JobStatus() # noqa: E501 - if include_optional: - return JobStatus( - job_id="0", - state="UNKNOWN", - progress=0.0, - submitted_at=datetime.datetime.strptime( - "2013-10-20 19:20:30.00", "%Y-%m-%d %H:%M:%S.%f" - ), - started_at=datetime.datetime.strptime( - "2013-10-20 19:20:30.00", "%Y-%m-%d %H:%M:%S.%f" - ), - stopped_at=datetime.datetime.strptime( - "2013-10-20 19:20:30.00", "%Y-%m-%d %H:%M:%S.%f" - ), - ) - else: - return JobStatus( - job_id="0", - state="UNKNOWN", - submitted_at=datetime.datetime.strptime( - "2013-10-20 19:20:30.00", "%Y-%m-%d %H:%M:%S.%f" - ), - ) - - def testJobStatus(self): - """Test JobStatus""" - self.make_instance(include_optional=False) - self.make_instance(include_optional=True) - - -if __name__ == "__main__": - unittest.main() diff --git a/clients/python/test/test_osparc/test_osparc_client/test_meta.py b/clients/python/test/test_osparc/test_osparc_client/test_meta.py deleted file mode 100644 index 37b4fc97..00000000 --- a/clients/python/test/test_osparc/test_osparc_client/test_meta.py +++ /dev/null @@ -1,58 +0,0 @@ -# coding: utf-8 - -""" - osparc.io web API - - osparc-simcore public web API specifications # noqa: E501 - - The version of the OpenAPI document: 0.4.0 - Generated by: https://openapi-generator.tech -""" - - -from __future__ import absolute_import - -import unittest - -from osparc import Meta # noqa: E501 - - -class TestMeta(unittest.TestCase): - """Meta unit test stubs""" - - def setUp(self): - pass - - def tearDown(self): - pass - - def make_instance(self, include_optional): - """Test Meta - include_option is a boolean, when False only required - params are included, when True both required and - optional params are included""" - # model = osparc.models.meta.Meta() # noqa: E501 - if include_optional: - return Meta( - name="0", - version="0.5.0", - released={"key": "a"}, - docs_url="https://docs.osparc.io", - docs_dev_url="https://api.osparc.io/dev/docs", - ) - else: - return Meta( - name="0", - version="0.5.0", - docs_url="https://docs.osparc.io", - docs_dev_url="https://api.osparc.io/dev/docs", - ) - - def testMeta(self): - """Test Meta""" - self.make_instance(include_optional=False) - self.make_instance(include_optional=True) - - -if __name__ == "__main__": - unittest.main() diff --git a/clients/python/test/test_osparc/test_osparc_client/test_meta_api.py b/clients/python/test/test_osparc/test_osparc_client/test_meta_api.py deleted file mode 100644 index 3499ff77..00000000 --- a/clients/python/test/test_osparc/test_osparc_client/test_meta_api.py +++ /dev/null @@ -1,40 +0,0 @@ -# coding: utf-8 - -""" - osparc.io web API - - osparc-simcore public web API specifications # noqa: E501 - - The version of the OpenAPI document: 0.4.0 - Generated by: https://openapi-generator.tech -""" - - -from __future__ import absolute_import - -import unittest - -from osparc import ApiClient, Configuration, MetaApi # noqa: E501 - - -class TestMetaApi(unittest.TestCase): - """MetaApi unit test stubs""" - - def setUp(self): - self.api = MetaApi( - api_client=ApiClient(configuration=Configuration()) - ) # noqa: E501 - - def tearDown(self): - pass - - def test_get_service_metadata(self): - """Test case for get_service_metadata - - Get Service Metadata # noqa: E501 - """ - pass - - -if __name__ == "__main__": - unittest.main() diff --git a/clients/python/test/test_osparc/test_osparc_client/test_profile.py b/clients/python/test/test_osparc/test_osparc_client/test_profile.py deleted file mode 100644 index 5f9209fe..00000000 --- a/clients/python/test/test_osparc/test_osparc_client/test_profile.py +++ /dev/null @@ -1,74 +0,0 @@ -# coding: utf-8 - -""" - osparc.io web API - - osparc-simcore public web API specifications # noqa: E501 - - The version of the OpenAPI document: 0.4.0 - Generated by: https://openapi-generator.tech -""" - - -from __future__ import absolute_import - -import unittest - -from osparc import Profile # noqa: E501 -from osparc import Groups, UsersGroup - - -class TestProfile(unittest.TestCase): - """Profile unit test stubs""" - - def setUp(self): - pass - - def tearDown(self): - pass - - def make_instance(self, include_optional): - """Test Profile - include_option is a boolean, when False only required - params are included, when True both required and - optional params are included""" - # model = osparc.models.profile.Profile() # noqa: E501 - if include_optional: - return Profile( - first_name="James", - last_name="Maxwell", - id=4, - login="0", - role="ANONYMOUS", - groups=Groups( - me=UsersGroup( - gid="0", - label="0", - description="0", - ), - organizations=[ - UsersGroup( - gid="0", - label="0", - description="0", - ) - ], - all=UsersGroup( - gid="0", - label="0", - description="0", - ), - ), - gravatar_id="0", - ) - else: - return Profile(login="0", role="ANONYMOUS", id=4) - - def testProfile(self): - """Test Profile""" - self.make_instance(include_optional=False) - self.make_instance(include_optional=True) - - -if __name__ == "__main__": - unittest.main() diff --git a/clients/python/test/test_osparc/test_osparc_client/test_profile_update.py b/clients/python/test/test_osparc/test_osparc_client/test_profile_update.py deleted file mode 100644 index 2abbcd35..00000000 --- a/clients/python/test/test_osparc/test_osparc_client/test_profile_update.py +++ /dev/null @@ -1,47 +0,0 @@ -# coding: utf-8 - -""" - osparc.io web API - - osparc-simcore public web API specifications # noqa: E501 - - The version of the OpenAPI document: 0.4.0 - Generated by: https://openapi-generator.tech -""" - - -from __future__ import absolute_import - -import unittest - -from osparc import ProfileUpdate # noqa: E501 - - -class TestProfileUpdate(unittest.TestCase): - """ProfileUpdate unit test stubs""" - - def setUp(self): - pass - - def tearDown(self): - pass - - def make_instance(self, include_optional): - """Test ProfileUpdate - include_option is a boolean, when False only required - params are included, when True both required and - optional params are included""" - # model = osparc.models.profile_update.ProfileUpdate() # noqa: E501 - if include_optional: - return ProfileUpdate(first_name="James", last_name="Maxwell") - else: - return ProfileUpdate() - - def testProfileUpdate(self): - """Test ProfileUpdate""" - self.make_instance(include_optional=False) - self.make_instance(include_optional=True) - - -if __name__ == "__main__": - unittest.main() diff --git a/clients/python/test/test_osparc/test_osparc_client/test_solver.py b/clients/python/test/test_osparc/test_osparc_client/test_solver.py deleted file mode 100644 index 2cdeef1a..00000000 --- a/clients/python/test/test_osparc/test_osparc_client/test_solver.py +++ /dev/null @@ -1,60 +0,0 @@ -# coding: utf-8 - -""" - osparc.io web API - - osparc-simcore public web API specifications # noqa: E501 - - The version of the OpenAPI document: 0.4.0 - Generated by: https://openapi-generator.tech -""" - - -from __future__ import absolute_import - -import unittest - -from osparc import Solver # noqa: E501 - - -class TestSolver(unittest.TestCase): - """Solver unit test stubs""" - - def setUp(self): - pass - - def tearDown(self): - pass - - def make_instance(self, include_optional): - """Test Solver - include_option is a boolean, when False only required - params are included, when True both required and - optional params are included""" - # model = osparc.models.solver.Solver() # noqa: E501 - if include_optional: - return Solver( - id="simcore/services/comp/sleeper", - version="0.5.0", - title="0", - description="0", - maintainer="0", - url="0", - ) - else: - return Solver( - id="simcore/services/comp/sleeper", - version="0.5.0", - title="0", - maintainer="0", - url="0", - ) - - def testSolver(self): - """Test Solver""" - self.make_instance(include_optional=False) - self.make_instance(include_optional=True) - - -if __name__ == "__main__": - unittest.main() diff --git a/clients/python/test/test_osparc/test_osparc_client/test_solvers_api.py b/clients/python/test/test_osparc/test_osparc_client/test_solvers_api.py deleted file mode 100644 index 71078984..00000000 --- a/clients/python/test/test_osparc/test_osparc_client/test_solvers_api.py +++ /dev/null @@ -1,124 +0,0 @@ -# coding: utf-8 - -""" - osparc.io web API - - osparc-simcore public web API specifications # noqa: E501 - - The version of the OpenAPI document: 0.4.0 - Generated by: https://openapi-generator.tech -""" - - -from __future__ import absolute_import - -import unittest - -from osparc import ApiClient, Configuration, SolversApi # noqa: E501 - - -class TestSolversApi(unittest.TestCase): - """SolversApi unit test stubs""" - - def setUp(self): - self.api = SolversApi( - api_client=ApiClient(configuration=Configuration()) - ) # noqa: E501 - - def tearDown(self): - pass - - def test_create_job(self): - """Test case for create_job - - Create Job # noqa: E501 - """ - pass - - def test_get_job(self): - """Test case for get_job - - Get Job # noqa: E501 - """ - pass - - def test_get_job_output_logfile(self): - """Test case for get_job_output_logfile - - Get Job Output Logfile # noqa: E501 - """ - pass - - def test_get_job_outputs(self): - """Test case for get_job_outputs - - Get Job Outputs # noqa: E501 - """ - pass - - def test_get_solver(self): - """Test case for get_solver - - Get Latest Release of a Solver # noqa: E501 - """ - pass - - def test_get_solver_release(self): - """Test case for get_solver_release - - Get Solver Release # noqa: E501 - """ - pass - - def test_inspect_job(self): - """Test case for inspect_job - - Inspect Job # noqa: E501 - """ - pass - - def test_list_jobs(self): - """Test case for list_jobs - - List Jobs # noqa: E501 - """ - pass - - def test_list_solver_releases(self): - """Test case for list_solver_releases - - List Solver Releases # noqa: E501 - """ - pass - - def test_list_solvers(self): - """Test case for list_solvers - - List Solvers # noqa: E501 - """ - pass - - def test_list_solvers_releases(self): - """Test case for list_solvers_releases - - Lists All Releases # noqa: E501 - """ - pass - - def test_start_job(self): - """Test case for start_job - - Start Job # noqa: E501 - """ - pass - - def test_stop_job(self): - """Test case for stop_job - - Stop Job # noqa: E501 - """ - pass - - -if __name__ == "__main__": - unittest.main() diff --git a/clients/python/test/test_osparc/test_osparc_client/test_task_states.py b/clients/python/test/test_osparc/test_osparc_client/test_task_states.py deleted file mode 100644 index 549c4bcf..00000000 --- a/clients/python/test/test_osparc/test_osparc_client/test_task_states.py +++ /dev/null @@ -1,47 +0,0 @@ -# coding: utf-8 - -""" - osparc.io web API - - osparc-simcore public web API specifications # noqa: E501 - - The version of the OpenAPI document: 0.4.0 - Generated by: https://openapi-generator.tech -""" - - -from __future__ import absolute_import - -import unittest - -from osparc import TaskStates # noqa: E501 - - -class TestTaskStates(unittest.TestCase): - """TaskStates unit test stubs""" - - def setUp(self): - pass - - def tearDown(self): - pass - - def make_instance(self, include_optional): - """Test TaskStates - include_option is a boolean, when False only required - params are included, when True both required and - optional params are included""" - # model = osparc.models.task_states.TaskStates() # noqa: E501 - if include_optional: - return TaskStates() - else: - return TaskStates() - - def testTaskStates(self): - """Test TaskStates""" - self.make_instance(include_optional=False) - self.make_instance(include_optional=True) - - -if __name__ == "__main__": - unittest.main() diff --git a/clients/python/test/test_osparc/test_osparc_client/test_user_role_enum.py b/clients/python/test/test_osparc/test_osparc_client/test_user_role_enum.py deleted file mode 100644 index 9e50ef03..00000000 --- a/clients/python/test/test_osparc/test_osparc_client/test_user_role_enum.py +++ /dev/null @@ -1,47 +0,0 @@ -# coding: utf-8 - -""" - osparc.io web API - - osparc-simcore public web API specifications # noqa: E501 - - The version of the OpenAPI document: 0.4.0 - Generated by: https://openapi-generator.tech -""" - - -from __future__ import absolute_import - -import unittest - -from osparc import UserRoleEnum # noqa: E501 - - -class TestUserRoleEnum(unittest.TestCase): - """UserRoleEnum unit test stubs""" - - def setUp(self): - pass - - def tearDown(self): - pass - - def make_instance(self, include_optional): - """Test UserRoleEnum - include_option is a boolean, when False only required - params are included, when True both required and - optional params are included""" - # model = osparc.models.user_role_enum.UserRoleEnum() # noqa: E501 - if include_optional: - return UserRoleEnum() - else: - return UserRoleEnum() - - def testUserRoleEnum(self): - """Test UserRoleEnum""" - self.make_instance(include_optional=False) - self.make_instance(include_optional=True) - - -if __name__ == "__main__": - unittest.main() diff --git a/clients/python/test/test_osparc/test_osparc_client/test_users_api.py b/clients/python/test/test_osparc/test_osparc_client/test_users_api.py deleted file mode 100644 index ae4073c6..00000000 --- a/clients/python/test/test_osparc/test_osparc_client/test_users_api.py +++ /dev/null @@ -1,47 +0,0 @@ -# coding: utf-8 - -""" - osparc.io web API - - osparc-simcore public web API specifications # noqa: E501 - - The version of the OpenAPI document: 0.4.0 - Generated by: https://openapi-generator.tech -""" - - -from __future__ import absolute_import - -import unittest - -from osparc import ApiClient, Configuration, UsersApi # noqa: E501 - - -class TestUsersApi(unittest.TestCase): - """UsersApi unit test stubs""" - - def setUp(self): - self.api = UsersApi( - api_client=ApiClient(configuration=Configuration()) - ) # noqa: E501 - - def tearDown(self): - pass - - def test_get_my_profile(self): - """Test case for get_my_profile - - Get My Profile # noqa: E501 - """ - pass - - def test_update_my_profile(self): - """Test case for update_my_profile - - Update My Profile # noqa: E501 - """ - pass - - -if __name__ == "__main__": - unittest.main() diff --git a/clients/python/test/test_osparc/test_osparc_client/test_users_group.py b/clients/python/test/test_osparc/test_osparc_client/test_users_group.py deleted file mode 100644 index 4516ba28..00000000 --- a/clients/python/test/test_osparc/test_osparc_client/test_users_group.py +++ /dev/null @@ -1,50 +0,0 @@ -# coding: utf-8 - -""" - osparc.io web API - - osparc-simcore public web API specifications # noqa: E501 - - The version of the OpenAPI document: 0.4.0 - Generated by: https://openapi-generator.tech -""" - - -from __future__ import absolute_import - -import unittest - -from osparc import UsersGroup # noqa: E501 - - -class TestUsersGroup(unittest.TestCase): - """UsersGroup unit test stubs""" - - def setUp(self): - pass - - def tearDown(self): - pass - - def make_instance(self, include_optional): - """Test UsersGroup - include_option is a boolean, when False only required - params are included, when True both required and - optional params are included""" - # model = osparc.models.users_group.UsersGroup() # noqa: E501 - if include_optional: - return UsersGroup(gid="0", label="0", description="0") - else: - return UsersGroup( - gid="0", - label="0", - ) - - def testUsersGroup(self): - """Test UsersGroup""" - self.make_instance(include_optional=False) - self.make_instance(include_optional=True) - - -if __name__ == "__main__": - unittest.main() diff --git a/clients/python/test/test_osparc/test_osparc_client/test_validation_error.py b/clients/python/test/test_osparc/test_osparc_client/test_validation_error.py deleted file mode 100644 index b3ac303f..00000000 --- a/clients/python/test/test_osparc/test_osparc_client/test_validation_error.py +++ /dev/null @@ -1,51 +0,0 @@ -# coding: utf-8 - -""" - osparc.io web API - - osparc-simcore public web API specifications # noqa: E501 - - The version of the OpenAPI document: 0.4.0 - Generated by: https://openapi-generator.tech -""" - - -from __future__ import absolute_import - -import unittest - -from osparc import ValidationError # noqa: E501 - - -class TestValidationError(unittest.TestCase): - """ValidationError unit test stubs""" - - def setUp(self): - pass - - def tearDown(self): - pass - - def make_instance(self, include_optional): - """Test ValidationError - include_option is a boolean, when False only required - params are included, when True both required and - optional params are included""" - # model = osparc.models.validation_error.ValidationError() # noqa: E501 - if include_optional: - return ValidationError(loc=["0"], msg="0", type="0") - else: - return ValidationError( - loc=["0"], - msg="0", - type="0", - ) - - def testValidationError(self): - """Test ValidationError""" - self.make_instance(include_optional=False) - self.make_instance(include_optional=True) - - -if __name__ == "__main__": - unittest.main() From 99a4a658df9f476667dcaee41d38049a550653fd Mon Sep 17 00:00:00 2001 From: Mads Bisgaard Date: Sat, 2 Nov 2024 00:13:02 +0100 Subject: [PATCH 30/52] fix create job function --- clients/python/requirements/unit-test.txt | 2 ++ clients/python/src/osparc/_api_solvers_api.py | 6 ++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/clients/python/requirements/unit-test.txt b/clients/python/requirements/unit-test.txt index 91a9931b..fda9098d 100644 --- a/clients/python/requirements/unit-test.txt +++ b/clients/python/requirements/unit-test.txt @@ -1,8 +1,10 @@ -r ../../../requirements.txt black faker +openapi-spec-validator #installed as backend for prance pipdeptree pipreqs +prance pytest pytest-asyncio pytest-mock diff --git a/clients/python/src/osparc/_api_solvers_api.py b/clients/python/src/osparc/_api_solvers_api.py index 2237ec12..2e64f9d1 100644 --- a/clients/python/src/osparc/_api_solvers_api.py +++ b/clients/python/src/osparc/_api_solvers_api.py @@ -8,6 +8,7 @@ JobInputs, OnePageSolverPort, SolverPort, + Job, JobOutputs, JobMetadata, JobMetadataUpdate, @@ -111,11 +112,12 @@ def jobs(self, solver_key: str, version: str, **kwargs) -> PaginationGenerator: @validate_call def create_job( self, solver_key: str, version: str, job_inputs: JobInputs, **kwargs - ): + ) -> Job: _job_inputs = _JobInputs.from_json(job_inputs.model_dump_json()) assert _job_inputs is not None kwargs = {**kwargs, **ParentProjectInfo().model_dump(exclude_none=True)} - return super().create_job(solver_key, version, _job_inputs, **kwargs) + _job = super().create_job(solver_key, version, _job_inputs, **kwargs) + return Job.model_validate(_job.to_dict()) def get_job_output_logfile(self, *args, **kwargs): data = super().get_job_output_logfile(*args, **kwargs) From 9ba877cd5b106c9c12bfc02972f5b35d7102dcf7 Mon Sep 17 00:00:00 2001 From: Mads Bisgaard Date: Sat, 2 Nov 2024 00:41:45 +0100 Subject: [PATCH 31/52] use monkeypatch to avoid rouge env vars --- clients/python/test/test_osparc/test_basic.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/clients/python/test/test_osparc/test_basic.py b/clients/python/test/test_osparc/test_basic.py index d8f75070..00745cb2 100644 --- a/clients/python/test/test_osparc/test_basic.py +++ b/clients/python/test/test_osparc/test_basic.py @@ -1,9 +1,8 @@ import json -import os import subprocess from pathlib import Path from typing import Any, Dict, List, Set - +from faker import Faker import osparc import osparc._settings import pydantic @@ -75,15 +74,17 @@ def test_dependencies(tmp_path: Path): @pytest.mark.parametrize("valid", [True, False]) -def test_parent_project_validation(faker, valid: bool): +def test_parent_project_validation( + faker: Faker, monkeypatch: pytest.MonkeyPatch, valid: bool +): if valid: - os.environ["OSPARC_STUDY_ID"] = f"{faker.uuid4()}" - os.environ["OSPARC_NODE_ID"] = f"{faker.uuid4()}" + monkeypatch.setenv("OSPARC_STUDY_ID", faker.uuid4()) + monkeypatch.setenv("OSPARC_NODE_ID", faker.uuid4()) parent_info = osparc._settings.ParentProjectInfo() assert parent_info.x_simcore_parent_project_uuid is not None assert parent_info.x_simcore_parent_node_id is not None else: - os.environ["OSPARC_STUDY_ID"] = f"{faker.text()}" - os.environ["OSPARC_NODE_ID"] = f"{faker.text()}" + monkeypatch.setenv("OSPARC_STUDY_ID", faker.text()) + monkeypatch.setenv("OSPARC_NODE_ID", faker.text()) with pytest.raises(pydantic.ValidationError): _ = osparc._settings.ParentProjectInfo() From a11cab234f9ffc5613e3218649ce5af359495471 Mon Sep 17 00:00:00 2001 From: Mads Bisgaard Date: Sun, 3 Nov 2024 22:57:10 +0100 Subject: [PATCH 32/52] redo uneeded change --- clients/python/src/osparc/_api_solvers_api.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/clients/python/src/osparc/_api_solvers_api.py b/clients/python/src/osparc/_api_solvers_api.py index 2e64f9d1..f1926c81 100644 --- a/clients/python/src/osparc/_api_solvers_api.py +++ b/clients/python/src/osparc/_api_solvers_api.py @@ -116,8 +116,7 @@ def create_job( _job_inputs = _JobInputs.from_json(job_inputs.model_dump_json()) assert _job_inputs is not None kwargs = {**kwargs, **ParentProjectInfo().model_dump(exclude_none=True)} - _job = super().create_job(solver_key, version, _job_inputs, **kwargs) - return Job.model_validate(_job.to_dict()) + return super().create_job(solver_key, version, _job_inputs, **kwargs) def get_job_output_logfile(self, *args, **kwargs): data = super().get_job_output_logfile(*args, **kwargs) From 36f5c1c2e57880bd6f6caf99db7984a008c4c028 Mon Sep 17 00:00:00 2001 From: Mads Bisgaard Date: Sun, 3 Nov 2024 23:04:37 +0100 Subject: [PATCH 33/52] improve some signatures in the wrapped client --- clients/python/src/osparc/_api_solvers_api.py | 26 ++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/clients/python/src/osparc/_api_solvers_api.py b/clients/python/src/osparc/_api_solvers_api.py index f1926c81..1d5f7cc6 100644 --- a/clients/python/src/osparc/_api_solvers_api.py +++ b/clients/python/src/osparc/_api_solvers_api.py @@ -29,6 +29,8 @@ from tempfile import NamedTemporaryFile from pathlib import Path from pydantic import validate_call +from pydantic import Field, StrictStr +from typing import Annotated class SolversApi(_SolversApi): @@ -118,15 +120,31 @@ def create_job( kwargs = {**kwargs, **ParentProjectInfo().model_dump(exclude_none=True)} return super().create_job(solver_key, version, _job_inputs, **kwargs) - def get_job_output_logfile(self, *args, **kwargs): - data = super().get_job_output_logfile(*args, **kwargs) + def get_job_output_logfile( + self, + solver_key: Annotated[str, Field(strict=True)], + version: Annotated[str, Field(strict=True)], + job_id: StrictStr, + **kwargs, + ): + data = super().get_job_output_logfile( + solver_key=solver_key, version=version, job_id=job_id, **kwargs + ) with NamedTemporaryFile(delete=False) as tmp_file: log_file = Path(tmp_file.name) log_file.write_bytes(data) return log_file - def get_job_outputs(self, *args, **kwargs) -> JobOutputs: - _osparc_client_outputs = super().get_job_outputs(*args, **kwargs) + def get_job_outputs( + self, + solver_key: Annotated[str, Field(strict=True)], + version: Annotated[str, Field(strict=True)], + job_id: StrictStr, + **kwargs, + ) -> JobOutputs: + _osparc_client_outputs = super().get_job_outputs( + solver_key=solver_key, version=version, job_id=job_id, **kwargs + ) return JobOutputs.model_validate_json(_osparc_client_outputs.to_json()) def get_job_custom_metadata(self, *args, **kwargs) -> JobMetadata: From 3e1e831c623dec699ffb7aae2a95ddb7aea76c0d Mon Sep 17 00:00:00 2001 From: Mads Bisgaard Date: Mon, 4 Nov 2024 09:42:49 +0100 Subject: [PATCH 34/52] automate response model creation when mocking --- clients/python/requirements/unit-test.txt | 1 + clients/python/test/test_osparc/conftest.py | 107 ++++++++++++++---- .../test/test_osparc/test_solvers_api.py | 30 ++--- 3 files changed, 99 insertions(+), 39 deletions(-) diff --git a/clients/python/requirements/unit-test.txt b/clients/python/requirements/unit-test.txt index fda9098d..609a8c9f 100644 --- a/clients/python/requirements/unit-test.txt +++ b/clients/python/requirements/unit-test.txt @@ -2,6 +2,7 @@ black faker openapi-spec-validator #installed as backend for prance +parse pipdeptree pipreqs prance diff --git a/clients/python/test/test_osparc/conftest.py b/clients/python/test/test_osparc/conftest.py index eac73522..621a85e4 100644 --- a/clients/python/test/test_osparc/conftest.py +++ b/clients/python/test/test_osparc/conftest.py @@ -14,7 +14,9 @@ import json from tempfile import NamedTemporaryFile from pathlib import Path -from typing import Any, TypeVar +from typing import Any, TypeVar, NamedTuple, Final +from urllib.parse import urlparse +from parse import parse, with_pattern @pytest.fixture @@ -46,11 +48,63 @@ def dev_mode_enabled(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setenv("OSPARC_DEV_FEATURES_ENABLED", "1") +@with_pattern(pattern=r"[^/]+") +def _path_segment(text): + return text + + +class ServerPath(NamedTuple): + path: str + formatted_path: str + + +_PATH_SEGMENT_CONVERTER: Final[str] = "path_segment" + + +@pytest.fixture +def all_server_paths(osparc_openapi_specs: dict[str, Any]) -> set[ServerPath]: + server_paths = set() + for path in osparc_openapi_specs["paths"]: + for method in osparc_openapi_specs["paths"][path]: + if params := osparc_openapi_specs["paths"][path][method].get("parameters"): + formatted_path = path + for p in params: + pname = p.get("name") + assert pname is not None + formatted_path = formatted_path.replace( + "{" + f"{pname}" + "}", + "{" + f"{pname}:{_PATH_SEGMENT_CONVERTER}" + "}", + ) + server_paths.add(ServerPath(path=path, formatted_path=formatted_path)) + return server_paths + + +T = TypeVar("T", bound=BaseModel) + + +@pytest.fixture +def create_osparc_response_model( + osparc_openapi_specs: dict[str, Any], +) -> Callable[[str], BaseModel]: + def _create_model(model_type: type[T]) -> T: + schemas = osparc_openapi_specs.get("components", {}).get("schemas", {}) + example_data = schemas.get(model_type.__name__, {}).get("example", {}) + error_msg = "Could not extract example data for" + error_msg += f" '{model_type.__name__}' from openapi specs" + assert example_data, error_msg + return model_type.model_validate(example_data) + + return _create_model + + @pytest.fixture def create_server_mock( mocker: MockerFixture, + osparc_openapi_specs: dict[str, Any], + all_server_paths: set[ServerPath], + create_osparc_response_model: Callable[[str], BaseModel], ) -> Callable[[int, BaseModel], None]: - def _mock_server(_status: int, _body: BaseModel) -> None: + def _mock_server(_status: int) -> None: def _sideeffect( method: str, url: str, @@ -60,29 +114,40 @@ def _sideeffect( json=None, **urlopen_kw, ) -> HTTPResponse: + matching_paths = set( + p.path + for p in all_server_paths + if parse( + p.formatted_path, + urlparse(url=url).path, + {_PATH_SEGMENT_CONVERTER: _path_segment}, + ) + ) + assert len(matching_paths) == 1 + matching_path = list(matching_paths)[0] + responses = ( + osparc_openapi_specs["paths"] + .get(matching_path, {}) + .get(method.lower(), {}) + .get("responses") + ) + assert ( + responses is not None + ), f"status code {_status} is not a return code of {method.upper()} {matching_path}" + schema = ( + responses.get(f"{_status}", {}) + .get("content", {}) + .get("application/json", {}) + .get("schema") + ) + assert schema is not None + response_model_type = getattr(osparc, schema.get("title")) + response_model = create_osparc_response_model(response_model_type) response = HTTPResponse( - status=_status, body=_body.model_dump_json().encode() + status=_status, body=response_model.model_dump_json().encode() ) return response mocker.patch("urllib3.PoolManager.request", side_effect=_sideeffect) return _mock_server - - -T = TypeVar("T", bound=BaseModel) - - -@pytest.fixture -def create_osparc_response_model( - osparc_openapi_specs: dict[str, Any], -) -> Callable[[type[T]], T]: - def _create_model(model_type: type[T]) -> T: - schemas = osparc_openapi_specs.get("components", {}).get("schemas", {}) - example_data = schemas.get(model_type.__name__, {}).get("example", {}) - error_msg = "Could not extract example data for" - error_msg += f" '{model_type.__name__}' from openapi specs" - assert example_data, error_msg - return model_type.model_validate(example_data) - - return _create_model diff --git a/clients/python/test/test_osparc/test_solvers_api.py b/clients/python/test/test_osparc/test_solvers_api.py index 5060e44c..fd204fdd 100644 --- a/clients/python/test/test_osparc/test_solvers_api.py +++ b/clients/python/test/test_osparc/test_solvers_api.py @@ -11,7 +11,7 @@ from typing import Callable, Generator from pydantic import BaseModel import pytest -from typing import Type, TypeVar +from typing import TypeVar T = TypeVar("T", bound=BaseModel) @@ -22,49 +22,44 @@ def solvers_api(api_client: ApiClient) -> Generator[SolversApi, None, None]: def test_create_job( - create_server_mock: Callable[[int, BaseModel], None], + create_server_mock: Callable[[int], None], create_osparc_response_model: Callable, solvers_api: SolversApi, ): job_inputs = create_osparc_response_model(JobInputs) - job = create_osparc_response_model(Job) - create_server_mock(201, job) + create_server_mock(201) _job = solvers_api.create_job( solver_key="mysolver", version="1.2.3", job_inputs=job_inputs ) - assert _job == job + assert isinstance(_job, Job) def test_get_job_outputs( - create_server_mock: Callable[[int, BaseModel], None], - create_osparc_response_model: Callable, + create_server_mock: Callable[[int], None], solvers_api: SolversApi, faker: Faker, ): - job_outputs = create_osparc_response_model(JobOutputs) - create_server_mock(200, job_outputs) + create_server_mock(200) _job_outputs = solvers_api.get_job_outputs( solver_key="mysolver", version="1.2.3", job_id=faker.uuid4() ) - assert _job_outputs == job_outputs + assert isinstance(_job_outputs, JobOutputs) def test_get_job_custom_metadata( - create_server_mock: Callable[[int, BaseModel], None], - create_osparc_response_model: Callable[[Type[JobMetadata]], JobMetadata], + create_server_mock: Callable[[int], None], faker: Faker, solvers_api: SolversApi, ): - job_metadata = create_osparc_response_model(JobMetadata) - create_server_mock(200, job_metadata) + create_server_mock(200) metadata = solvers_api.get_job_custom_metadata( solver_key="mysolver", version="1.2.3", job_id=f"{faker.uuid4()}" ) - assert metadata == job_metadata + assert isinstance(metadata, JobMetadata) def test_replace_job_custom_metadata( @@ -73,9 +68,8 @@ def test_replace_job_custom_metadata( solvers_api: SolversApi, faker: Faker, ): - job_metadata = create_osparc_response_model(JobMetadata) job_metadata_update = create_osparc_response_model(JobMetadataUpdate) - create_server_mock(200, job_metadata) + create_server_mock(200) _job_metadata = solvers_api.replace_job_custom_metadata( solver_key="mysolver", @@ -83,4 +77,4 @@ def test_replace_job_custom_metadata( job_id=f"{faker.uuid4()}", job_metadata_update=job_metadata_update, ) - assert _job_metadata == job_metadata + assert isinstance(_job_metadata, JobMetadata) From 3fc17678d1df70ff4340abef85efb698c9aa6df4 Mon Sep 17 00:00:00 2001 From: Mads Bisgaard Date: Mon, 4 Nov 2024 10:10:06 +0100 Subject: [PATCH 35/52] add study api unit tests --- clients/python/src/osparc/_api_studies_api.py | 8 ++- .../test/test_osparc/test_solvers_api.py | 2 +- .../test/test_osparc/test_studies_api.py | 69 +++++++++++++++++++ 3 files changed, 76 insertions(+), 3 deletions(-) create mode 100644 clients/python/test/test_osparc/test_studies_api.py diff --git a/clients/python/src/osparc/_api_studies_api.py b/clients/python/src/osparc/_api_studies_api.py index c7e171fa..4cfd1990 100644 --- a/clients/python/src/osparc/_api_studies_api.py +++ b/clients/python/src/osparc/_api_studies_api.py @@ -80,8 +80,12 @@ def create_study_job(self, study_id: str, job_inputs: JobInputs, **kwargs): kwargs = {**kwargs, **ParentProjectInfo().model_dump(exclude_none=True)} return super().create_study_job(study_id, _job_inputs, **kwargs) - def get_study_job_outputs(self, *args, **kwargs) -> JobOutputs: - _job_outputs = super().get_study_job_outputs(*args, **kwargs) + def get_study_job_outputs( + self, study_id: StrictStr, job_id: StrictStr, **kwargs + ) -> JobOutputs: + _job_outputs = super().get_study_job_outputs( + study_id=study_id, job_id=job_id, **kwargs + ) return JobOutputs.model_validate_json(_job_outputs.to_json()) def clone_study(self, study_id: str, **kwargs): diff --git a/clients/python/test/test_osparc/test_solvers_api.py b/clients/python/test/test_osparc/test_solvers_api.py index fd204fdd..a5941e28 100644 --- a/clients/python/test/test_osparc/test_solvers_api.py +++ b/clients/python/test/test_osparc/test_solvers_api.py @@ -63,7 +63,7 @@ def test_get_job_custom_metadata( def test_replace_job_custom_metadata( - create_server_mock: Callable[[int, BaseModel], None], + create_server_mock: Callable[[int], None], create_osparc_response_model: Callable, solvers_api: SolversApi, faker: Faker, diff --git a/clients/python/test/test_osparc/test_studies_api.py b/clients/python/test/test_osparc/test_studies_api.py new file mode 100644 index 00000000..6c988993 --- /dev/null +++ b/clients/python/test/test_osparc/test_studies_api.py @@ -0,0 +1,69 @@ +import pytest +from osparc import ( + ApiClient, + StudiesApi, + JobInputs, + Job, + JobOutputs, + JobMetadata, + JobMetadataUpdate, +) +from typing import Callable +from faker import Faker + + +@pytest.fixture +def studies_api(api_client: ApiClient) -> StudiesApi: + return StudiesApi(api_client=api_client) + + +def test_create_study_job( + create_server_mock: Callable[[int], None], + create_osparc_response_model: Callable, + studies_api: StudiesApi, + faker: Faker, +): + job_inputs = create_osparc_response_model(JobInputs) + + create_server_mock(200) + + _job = studies_api.create_study_job(study_id=faker.uuid4(), job_inputs=job_inputs) + assert isinstance(_job, Job) + + +def test_get_study_job_outputs( + create_server_mock: Callable[[int], None], studies_api: StudiesApi, faker: Faker +): + create_server_mock(200) + + _study_job_outputs = studies_api.get_study_job_outputs( + study_id=faker.uuid4(), job_id=faker.uuid4() + ) + assert isinstance(_study_job_outputs, JobOutputs) + + +def test_get_study_job_custom_metadata( + create_server_mock: Callable[[int], None], studies_api: StudiesApi, faker: Faker +): + create_server_mock(200) + metadata = studies_api.get_study_job_custom_metadata( + study_id=faker.uuid4(), job_id=faker.uuid4() + ) + assert isinstance(metadata, JobMetadata) + + +def test_replace_study_job_custom_metadata( + create_server_mock: Callable[[int], None], + create_osparc_response_model: Callable, + studies_api: StudiesApi, + faker: Faker, +): + job_metadata_update: JobMetadataUpdate = create_osparc_response_model( + JobMetadataUpdate + ) + job_metadata = studies_api.replace_study_job_custom_metadata( + study_id=faker.uuid4(), + job_id=faker.uuid4(), + job_metadata_update=job_metadata_update, + ) + assert isinstance(job_metadata, JobMetadata) From bc42b5e24a85aae538932d377dafc05e661fbacf Mon Sep 17 00:00:00 2001 From: Mads Bisgaard Date: Mon, 4 Nov 2024 10:15:51 +0100 Subject: [PATCH 36/52] minor corrections --- clients/python/test/test_osparc/conftest.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/clients/python/test/test_osparc/conftest.py b/clients/python/test/test_osparc/conftest.py index 621a85e4..a6586e89 100644 --- a/clients/python/test/test_osparc/conftest.py +++ b/clients/python/test/test_osparc/conftest.py @@ -85,10 +85,10 @@ def all_server_paths(osparc_openapi_specs: dict[str, Any]) -> set[ServerPath]: @pytest.fixture def create_osparc_response_model( osparc_openapi_specs: dict[str, Any], -) -> Callable[[str], BaseModel]: +) -> Callable[[type[T]], T]: def _create_model(model_type: type[T]) -> T: schemas = osparc_openapi_specs.get("components", {}).get("schemas", {}) - example_data = schemas.get(model_type.__name__, {}).get("example", {}) + example_data = schemas.get(model_type.__name__, {}).get("example") error_msg = "Could not extract example data for" error_msg += f" '{model_type.__name__}' from openapi specs" assert example_data, error_msg @@ -103,7 +103,7 @@ def create_server_mock( osparc_openapi_specs: dict[str, Any], all_server_paths: set[ServerPath], create_osparc_response_model: Callable[[str], BaseModel], -) -> Callable[[int, BaseModel], None]: +) -> Callable[[int], None]: def _mock_server(_status: int) -> None: def _sideeffect( method: str, @@ -131,16 +131,16 @@ def _sideeffect( .get(method.lower(), {}) .get("responses") ) - assert ( - responses is not None - ), f"status code {_status} is not a return code of {method.upper()} {matching_path}" + assert responses is not None schema = ( responses.get(f"{_status}", {}) .get("content", {}) .get("application/json", {}) .get("schema") ) - assert schema is not None + assert ( + schema is not None + ), f"probably status code {_status} is not a return code of {method.upper()} {matching_path}" response_model_type = getattr(osparc, schema.get("title")) response_model = create_osparc_response_model(response_model_type) response = HTTPResponse( From 31e62ee84a47a806d7b7378ad535402570b4e51e Mon Sep 17 00:00:00 2001 From: Mads Bisgaard Date: Mon, 4 Nov 2024 10:23:41 +0100 Subject: [PATCH 37/52] delete old comment @pcrespov --- scripts/common.Makefile | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/common.Makefile b/scripts/common.Makefile index fa309f24..879ac796 100644 --- a/scripts/common.Makefile +++ b/scripts/common.Makefile @@ -13,7 +13,6 @@ NOW_TIMESTAMP := $(shell date -u +"%Y-%m-%dT%H:%M:%SZ") APP_NAME := $(notdir $(CURDIR)) # Specify which openapi generator should be used to generate the clients in this repo -# Build from modified fork https://github.com/ITISFoundation/openapi-generator/tree/openapi-generator-v4.2.3 OPENAPI_GENERATOR_NAME := openapitools/openapi-generator-cli OPENAPI_GENERATOR_TAG := latest OPENAPI_GENERATOR_IMAGE := $(OPENAPI_GENERATOR_NAME):$(OPENAPI_GENERATOR_TAG) From 2ceb1ad98d033943d6dc230a2b0c5b834548e875 Mon Sep 17 00:00:00 2001 From: Mads Bisgaard Date: Mon, 4 Nov 2024 10:40:14 +0100 Subject: [PATCH 38/52] fix faker.uuid4 type issue --- clients/python/test/test_osparc/conftest.py | 7 +++++- clients/python/test/test_osparc/test_basic.py | 6 ++--- .../test/test_osparc/test_studies_api.py | 23 ++++++++----------- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/clients/python/test/test_osparc/conftest.py b/clients/python/test/test_osparc/conftest.py index a6586e89..8f378d44 100644 --- a/clients/python/test/test_osparc/conftest.py +++ b/clients/python/test/test_osparc/conftest.py @@ -14,7 +14,7 @@ import json from tempfile import NamedTemporaryFile from pathlib import Path -from typing import Any, TypeVar, NamedTuple, Final +from typing import Any, TypeVar, NamedTuple, Final, cast from urllib.parse import urlparse from parse import parse, with_pattern @@ -43,6 +43,11 @@ def api_client(cfg: osparc.Configuration) -> osparc.ApiClient: return osparc.ApiClient(configuration=cfg) +@pytest.fixture +def uuid(faker: Faker): + return cast(str, faker.uuid4()) + + @pytest.fixture def dev_mode_enabled(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setenv("OSPARC_DEV_FEATURES_ENABLED", "1") diff --git a/clients/python/test/test_osparc/test_basic.py b/clients/python/test/test_osparc/test_basic.py index 00745cb2..a2d10fe3 100644 --- a/clients/python/test/test_osparc/test_basic.py +++ b/clients/python/test/test_osparc/test_basic.py @@ -75,11 +75,11 @@ def test_dependencies(tmp_path: Path): @pytest.mark.parametrize("valid", [True, False]) def test_parent_project_validation( - faker: Faker, monkeypatch: pytest.MonkeyPatch, valid: bool + faker: Faker, monkeypatch: pytest.MonkeyPatch, valid: bool, uuid: str ): if valid: - monkeypatch.setenv("OSPARC_STUDY_ID", faker.uuid4()) - monkeypatch.setenv("OSPARC_NODE_ID", faker.uuid4()) + monkeypatch.setenv("OSPARC_STUDY_ID", uuid) + monkeypatch.setenv("OSPARC_NODE_ID", uuid) parent_info = osparc._settings.ParentProjectInfo() assert parent_info.x_simcore_parent_project_uuid is not None assert parent_info.x_simcore_parent_node_id is not None diff --git a/clients/python/test/test_osparc/test_studies_api.py b/clients/python/test/test_osparc/test_studies_api.py index 6c988993..27ec6d2f 100644 --- a/clients/python/test/test_osparc/test_studies_api.py +++ b/clients/python/test/test_osparc/test_studies_api.py @@ -9,7 +9,6 @@ JobMetadataUpdate, ) from typing import Callable -from faker import Faker @pytest.fixture @@ -21,34 +20,30 @@ def test_create_study_job( create_server_mock: Callable[[int], None], create_osparc_response_model: Callable, studies_api: StudiesApi, - faker: Faker, + uuid: str, ): job_inputs = create_osparc_response_model(JobInputs) create_server_mock(200) - _job = studies_api.create_study_job(study_id=faker.uuid4(), job_inputs=job_inputs) + _job = studies_api.create_study_job(study_id=uuid, job_inputs=job_inputs) assert isinstance(_job, Job) def test_get_study_job_outputs( - create_server_mock: Callable[[int], None], studies_api: StudiesApi, faker: Faker + create_server_mock: Callable[[int], None], studies_api: StudiesApi, uuid: str ): create_server_mock(200) - _study_job_outputs = studies_api.get_study_job_outputs( - study_id=faker.uuid4(), job_id=faker.uuid4() - ) + _study_job_outputs = studies_api.get_study_job_outputs(study_id=uuid, job_id=uuid) assert isinstance(_study_job_outputs, JobOutputs) def test_get_study_job_custom_metadata( - create_server_mock: Callable[[int], None], studies_api: StudiesApi, faker: Faker + create_server_mock: Callable[[int], None], studies_api: StudiesApi, uuid: str ): create_server_mock(200) - metadata = studies_api.get_study_job_custom_metadata( - study_id=faker.uuid4(), job_id=faker.uuid4() - ) + metadata = studies_api.get_study_job_custom_metadata(study_id=uuid, job_id=uuid) assert isinstance(metadata, JobMetadata) @@ -56,14 +51,14 @@ def test_replace_study_job_custom_metadata( create_server_mock: Callable[[int], None], create_osparc_response_model: Callable, studies_api: StudiesApi, - faker: Faker, + uuid: str, ): job_metadata_update: JobMetadataUpdate = create_osparc_response_model( JobMetadataUpdate ) job_metadata = studies_api.replace_study_job_custom_metadata( - study_id=faker.uuid4(), - job_id=faker.uuid4(), + study_id=uuid, + job_id=uuid, job_metadata_update=job_metadata_update, ) assert isinstance(job_metadata, JobMetadata) From e76b901e022fb3f0d7a1d022bbb2346a40f5061b Mon Sep 17 00:00:00 2001 From: Mads Bisgaard Date: Mon, 4 Nov 2024 10:47:51 +0100 Subject: [PATCH 39/52] fix --- clients/python/test/test_osparc/test_studies_api.py | 1 + 1 file changed, 1 insertion(+) diff --git a/clients/python/test/test_osparc/test_studies_api.py b/clients/python/test/test_osparc/test_studies_api.py index 27ec6d2f..3119dbc9 100644 --- a/clients/python/test/test_osparc/test_studies_api.py +++ b/clients/python/test/test_osparc/test_studies_api.py @@ -53,6 +53,7 @@ def test_replace_study_job_custom_metadata( studies_api: StudiesApi, uuid: str, ): + create_server_mock(200) job_metadata_update: JobMetadataUpdate = create_osparc_response_model( JobMetadataUpdate ) From 41f0df13e32f17e3a3bf4599f35400f81eee1599 Mon Sep 17 00:00:00 2001 From: Mads Bisgaard Date: Mon, 4 Nov 2024 11:42:09 +0100 Subject: [PATCH 40/52] update openapi.json with new model examples --- api/openapi.json | 62 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 54 insertions(+), 8 deletions(-) diff --git a/api/openapi.json b/api/openapi.json index 51ba054e..ebe40551 100644 --- a/api/openapi.json +++ b/api/openapi.json @@ -5934,7 +5934,17 @@ "metadata", "url" ], - "title": "JobMetadata" + "title": "JobMetadata", + "example": { + "job_id": "3497e4de-0e69-41fb-b08f-7f3875a1ac4b", + "metadata": { + "bool": "true", + "float": "3.14", + "int": "42", + "str": "hej med dig" + }, + "url": "https://f02b2452-1dd8-4882-b673-af06373b41b3.fake" + } }, "JobMetadataUpdate": { "properties": { @@ -5961,7 +5971,15 @@ } }, "type": "object", - "title": "JobMetadataUpdate" + "title": "JobMetadataUpdate", + "example": { + "metadata": { + "bool": "true", + "float": "3.14", + "int": "42", + "str": "hej med dig" + } + } }, "JobOutputs": { "properties": { @@ -6244,8 +6262,15 @@ "title": "Items" }, "total": { - "type": "integer", - "minimum": 0, + "anyOf": [ + { + "type": "integer", + "minimum": 0 + }, + { + "type": "null" + } + ], "title": "Total" } }, @@ -6265,8 +6290,15 @@ "title": "Items" }, "total": { - "type": "integer", - "minimum": 0, + "anyOf": [ + { + "type": "integer", + "minimum": 0 + }, + { + "type": "null" + } + ], "title": "Total" } }, @@ -6808,11 +6840,25 @@ "title": "Uid" }, "title": { - "type": "string", + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], "title": "Title" }, "description": { - "type": "string", + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], "title": "Description" } }, From 99341ad1ca760f24b3344414d75aed95068baa3a Mon Sep 17 00:00:00 2001 From: Mads Bisgaard Date: Mon, 4 Nov 2024 11:45:41 +0100 Subject: [PATCH 41/52] make Make recipe more readable @pcrespov --- clients/python/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clients/python/Makefile b/clients/python/Makefile index 544ef3f7..4e4cba4a 100644 --- a/clients/python/Makefile +++ b/clients/python/Makefile @@ -117,7 +117,7 @@ _check_venv_active: .PHONY: install-dev install-dev: _check_venv_active .install-dev-reqs python-client ## installs osparc_client and osparc in edit mode # for some reason this command refuses to run with uv. Looks related to https://github.com/astral-sh/uv/issues/1661 - pip install -e artifacts/client -e . + pip install --editable artifacts/client --editable . uv pip list .PHONY: install-unit-test From f0f31f6e49fa398bfceadb5cdeb0982c9b42b67d Mon Sep 17 00:00:00 2001 From: Mads Bisgaard Date: Wed, 6 Nov 2024 08:38:35 +0100 Subject: [PATCH 42/52] python 3.8 compatible --- clients/python/src/osparc/_models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clients/python/src/osparc/_models.py b/clients/python/src/osparc/_models.py index ab824126..21f19e73 100644 --- a/clients/python/src/osparc/_models.py +++ b/clients/python/src/osparc/_models.py @@ -3,7 +3,7 @@ from osparc_client import JobMetadata as _JobMetadata from osparc_client import JobMetadataUpdate as _JobMetadataUpdate from .models import File -from typing import Dict, Union, List +from typing import Dict, Union, List, Optional from pydantic import BaseModel, StrictStr, Field @@ -30,7 +30,7 @@ class JobOutputs(BaseModel): class JobMetadata(BaseModel): job_id: StrictStr metadata: Dict[str, Union[bool, float, int, str, None]] - url: str | None + url: Optional[str] assert set(_JobMetadata.model_fields.keys()) == set(JobMetadata.model_fields.keys()) From b9bcdc75d58d8629361b0ba402bc541cc0e8439d Mon Sep 17 00:00:00 2001 From: Mads Bisgaard Date: Wed, 6 Nov 2024 08:46:16 +0100 Subject: [PATCH 43/52] remove Annotated import from typing --- clients/python/src/osparc/_api_solvers_api.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/clients/python/src/osparc/_api_solvers_api.py b/clients/python/src/osparc/_api_solvers_api.py index 1d5f7cc6..14f08f32 100644 --- a/clients/python/src/osparc/_api_solvers_api.py +++ b/clients/python/src/osparc/_api_solvers_api.py @@ -29,8 +29,7 @@ from tempfile import NamedTemporaryFile from pathlib import Path from pydantic import validate_call -from pydantic import Field, StrictStr -from typing import Annotated +from pydantic import StrictStr class SolversApi(_SolversApi): @@ -122,8 +121,8 @@ def create_job( def get_job_output_logfile( self, - solver_key: Annotated[str, Field(strict=True)], - version: Annotated[str, Field(strict=True)], + solver_key: str, + version: str, job_id: StrictStr, **kwargs, ): @@ -137,8 +136,8 @@ def get_job_output_logfile( def get_job_outputs( self, - solver_key: Annotated[str, Field(strict=True)], - version: Annotated[str, Field(strict=True)], + solver_key: str, + version: str, job_id: StrictStr, **kwargs, ) -> JobOutputs: From ff7c0d5f1e62ded5c13846f64a7c3ca6e3fc8e54 Mon Sep 17 00:00:00 2001 From: Mads Bisgaard Date: Wed, 6 Nov 2024 08:53:19 +0100 Subject: [PATCH 44/52] fix type hinting dict to Dict for py 3.8 --- clients/python/test/test_osparc/conftest.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clients/python/test/test_osparc/conftest.py b/clients/python/test/test_osparc/conftest.py index 8f378d44..cd9672c0 100644 --- a/clients/python/test/test_osparc/conftest.py +++ b/clients/python/test/test_osparc/conftest.py @@ -14,7 +14,7 @@ import json from tempfile import NamedTemporaryFile from pathlib import Path -from typing import Any, TypeVar, NamedTuple, Final, cast +from typing import Any, TypeVar, NamedTuple, Final, cast, Dict from urllib.parse import urlparse from parse import parse, with_pattern @@ -29,7 +29,7 @@ def cfg(faker: Faker) -> osparc.Configuration: @pytest.fixture -def osparc_openapi_specs() -> Generator[dict[str, Any], None, None]: +def osparc_openapi_specs() -> Generator[Dict[str, Any], None, None]: with NamedTemporaryFile(suffix=".json") as file: file = Path(file.name) file.write_text(json.dumps(osparc.openapi())) From 6323aaf7d1f2736da04336868e7134dfddb4988e Mon Sep 17 00:00:00 2001 From: Mads Bisgaard Date: Wed, 6 Nov 2024 09:07:34 +0100 Subject: [PATCH 45/52] more py3.8 compatibility changes --- clients/python/test/test_osparc/conftest.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clients/python/test/test_osparc/conftest.py b/clients/python/test/test_osparc/conftest.py index cd9672c0..57ef8846 100644 --- a/clients/python/test/test_osparc/conftest.py +++ b/clients/python/test/test_osparc/conftest.py @@ -14,7 +14,7 @@ import json from tempfile import NamedTemporaryFile from pathlib import Path -from typing import Any, TypeVar, NamedTuple, Final, cast, Dict +from typing import Any, TypeVar, NamedTuple, Final, cast, Dict, Type from urllib.parse import urlparse from parse import parse, with_pattern @@ -90,7 +90,7 @@ def all_server_paths(osparc_openapi_specs: dict[str, Any]) -> set[ServerPath]: @pytest.fixture def create_osparc_response_model( osparc_openapi_specs: dict[str, Any], -) -> Callable[[type[T]], T]: +) -> Callable[[Type[T]], T]: def _create_model(model_type: type[T]) -> T: schemas = osparc_openapi_specs.get("components", {}).get("schemas", {}) example_data = schemas.get(model_type.__name__, {}).get("example") From 084fa5692c337767af0731a6a1c00552dd4f1dff Mon Sep 17 00:00:00 2001 From: Mads Bisgaard Date: Wed, 6 Nov 2024 09:13:42 +0100 Subject: [PATCH 46/52] more py3.8 compatibility changes --- clients/python/test/test_osparc/conftest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clients/python/test/test_osparc/conftest.py b/clients/python/test/test_osparc/conftest.py index 57ef8846..8360382f 100644 --- a/clients/python/test/test_osparc/conftest.py +++ b/clients/python/test/test_osparc/conftest.py @@ -67,7 +67,7 @@ class ServerPath(NamedTuple): @pytest.fixture -def all_server_paths(osparc_openapi_specs: dict[str, Any]) -> set[ServerPath]: +def all_server_paths(osparc_openapi_specs: Dict[str, Any]) -> set[ServerPath]: server_paths = set() for path in osparc_openapi_specs["paths"]: for method in osparc_openapi_specs["paths"][path]: From db2d5d2d87e9430b4cac4e0d58ea5006bb10c9ee Mon Sep 17 00:00:00 2001 From: Mads Bisgaard Date: Wed, 6 Nov 2024 09:16:59 +0100 Subject: [PATCH 47/52] more py3.8 compatibility changes --- clients/python/test/test_osparc/conftest.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/clients/python/test/test_osparc/conftest.py b/clients/python/test/test_osparc/conftest.py index 8360382f..eeda2b9e 100644 --- a/clients/python/test/test_osparc/conftest.py +++ b/clients/python/test/test_osparc/conftest.py @@ -14,7 +14,7 @@ import json from tempfile import NamedTemporaryFile from pathlib import Path -from typing import Any, TypeVar, NamedTuple, Final, cast, Dict, Type +from typing import Any, TypeVar, NamedTuple, Final, cast, Dict, Type, Set from urllib.parse import urlparse from parse import parse, with_pattern @@ -67,7 +67,7 @@ class ServerPath(NamedTuple): @pytest.fixture -def all_server_paths(osparc_openapi_specs: Dict[str, Any]) -> set[ServerPath]: +def all_server_paths(osparc_openapi_specs: Dict[str, Any]) -> Set[ServerPath]: server_paths = set() for path in osparc_openapi_specs["paths"]: for method in osparc_openapi_specs["paths"][path]: @@ -89,7 +89,7 @@ def all_server_paths(osparc_openapi_specs: Dict[str, Any]) -> set[ServerPath]: @pytest.fixture def create_osparc_response_model( - osparc_openapi_specs: dict[str, Any], + osparc_openapi_specs: Dict[str, Any], ) -> Callable[[Type[T]], T]: def _create_model(model_type: type[T]) -> T: schemas = osparc_openapi_specs.get("components", {}).get("schemas", {}) @@ -105,8 +105,8 @@ def _create_model(model_type: type[T]) -> T: @pytest.fixture def create_server_mock( mocker: MockerFixture, - osparc_openapi_specs: dict[str, Any], - all_server_paths: set[ServerPath], + osparc_openapi_specs: Dict[str, Any], + all_server_paths: Set[ServerPath], create_osparc_response_model: Callable[[str], BaseModel], ) -> Callable[[int], None]: def _mock_server(_status: int) -> None: From 866fa8abcd88a26537a307db988934a0439ea830 Mon Sep 17 00:00:00 2001 From: Mads Bisgaard Date: Wed, 6 Nov 2024 09:21:28 +0100 Subject: [PATCH 48/52] more py3.8 compatibility changes --- clients/python/test/test_osparc/conftest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clients/python/test/test_osparc/conftest.py b/clients/python/test/test_osparc/conftest.py index eeda2b9e..f55fe816 100644 --- a/clients/python/test/test_osparc/conftest.py +++ b/clients/python/test/test_osparc/conftest.py @@ -91,7 +91,7 @@ def all_server_paths(osparc_openapi_specs: Dict[str, Any]) -> Set[ServerPath]: def create_osparc_response_model( osparc_openapi_specs: Dict[str, Any], ) -> Callable[[Type[T]], T]: - def _create_model(model_type: type[T]) -> T: + def _create_model(model_type: Type[T]) -> T: schemas = osparc_openapi_specs.get("components", {}).get("schemas", {}) example_data = schemas.get(model_type.__name__, {}).get("example") error_msg = "Could not extract example data for" From 122e912af960a1ac5db3adaa056d5fd58d2b9d49 Mon Sep 17 00:00:00 2001 From: Mads Bisgaard Date: Fri, 8 Nov 2024 13:49:16 +0100 Subject: [PATCH 49/52] update openapi.json --- api/openapi.json | 74 +++++++++++++++++++++++++++--------------------- 1 file changed, 41 insertions(+), 33 deletions(-) diff --git a/api/openapi.json b/api/openapi.json index ebe40551..2f12ad9a 100644 --- a/api/openapi.json +++ b/api/openapi.json @@ -3788,15 +3788,7 @@ "content": { "application/json": { "schema": { - "anyOf": [ - { - "$ref": "#/components/schemas/WalletGetWithAvailableCredits" - }, - { - "type": "null" - } - ], - "title": "Response Get Job Wallet V0 Solvers Solver Key Releases Version Jobs Job Id Wallet Get" + "$ref": "#/components/schemas/WalletGetWithAvailableCredits" } } } @@ -3935,15 +3927,7 @@ "content": { "application/json": { "schema": { - "anyOf": [ - { - "$ref": "#/components/schemas/PricingUnitGet" - }, - { - "type": "null" - } - ], - "title": "Response Get Job Pricing Unit V0 Solvers Solver Key Releases Version Jobs Job Id Pricing Unit Get" + "$ref": "#/components/schemas/PricingUnitGet" } } } @@ -5414,16 +5398,8 @@ "description": "File name" }, "filesize": { - "anyOf": [ - { - "type": "string", - "pattern": "^\\s*(\\d*\\.?\\d+)\\s*(\\w+)?" - }, - { - "type": "integer", - "minimum": 0 - } - ], + "type": "integer", + "minimum": 0, "title": "Filesize", "description": "File size in bytes" }, @@ -5568,6 +5544,7 @@ "urls": { "items": { "type": "string", + "maxLength": 65536, "minLength": 1, "format": "uri" }, @@ -5595,7 +5572,8 @@ "usdPerCredit": { "anyOf": [ { - "type": "string" + "type": "number", + "minimum": 0.0 }, { "type": "null" @@ -6181,6 +6159,7 @@ }, "download_link": { "type": "string", + "maxLength": 65536, "minLength": 1, "format": "uri", "title": "Download Link" @@ -6222,12 +6201,14 @@ }, "docs_url": { "type": "string", + "maxLength": 65536, "minLength": 1, "format": "uri", "title": "Docs Url" }, "docs_dev_url": { "type": "string", + "maxLength": 65536, "minLength": 1, "format": "uri", "title": "Docs Dev Url" @@ -6506,11 +6487,10 @@ "title": "Unitname" }, "unitExtraInfo": { - "type": "object", - "title": "Unitextrainfo" + "$ref": "#/components/schemas/UnitExtraInfo" }, "currentCostPerUnit": { - "type": "string", + "type": "number", "title": "Currentcostperunit" }, "default": { @@ -6911,10 +6891,38 @@ "type": "integer", "x_unit": "second" }, - "key": "input_2", + "key": "f763658f-a89a-4a90-ace4-c44631290f12", "kind": "input" } }, + "UnitExtraInfo": { + "properties": { + "CPU": { + "type": "integer", + "minimum": 0, + "title": "Cpu" + }, + "RAM": { + "type": "integer", + "minimum": 0, + "title": "Ram" + }, + "VRAM": { + "type": "integer", + "minimum": 0, + "title": "Vram" + } + }, + "additionalProperties": true, + "type": "object", + "required": [ + "CPU", + "RAM", + "VRAM" + ], + "title": "UnitExtraInfo", + "description": "Custom information that is propagated to the frontend. Defined fields are mandatory." + }, "UploadLinks": { "properties": { "abort_upload": { From 002b157ffaeead8ff5663f4c989ab194e3456c8e Mon Sep 17 00:00:00 2001 From: Mads Bisgaard Date: Fri, 8 Nov 2024 13:53:22 +0100 Subject: [PATCH 50/52] minor fix after openapi.json update --- clients/python/src/osparc/_api_files_api.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/clients/python/src/osparc/_api_files_api.py b/clients/python/src/osparc/_api_files_api.py index 5da81364..4d1cf91e 100644 --- a/clients/python/src/osparc/_api_files_api.py +++ b/clients/python/src/osparc/_api_files_api.py @@ -14,7 +14,6 @@ import httpx from httpx import Response from osparc_client.api.files_api import FilesApi as _FilesApi -from osparc_client import Filesize as _Filesize from tqdm.asyncio import tqdm from tqdm.contrib.logging import logging_redirect_tqdm @@ -120,7 +119,7 @@ async def upload_file_async( return file_result client_file: ClientFile = ClientFile( filename=file.name, - filesize=_Filesize(file.stat().st_size), + filesize=file.stat().st_size, sha256_checksum=checksum, ) client_upload_schema: ClientFileUploadData = super().get_upload_links( From 5d3723095b82772960152f67a3df7373cbba6a81 Mon Sep 17 00:00:00 2001 From: Mads Bisgaard Date: Fri, 8 Nov 2024 13:58:30 +0100 Subject: [PATCH 51/52] test fix for https://github.com/ITISFoundation/osparc-simcore/issues/6556 --- clients/python/test/test_osparc/test_solvers_api.py | 9 +++++++++ clients/python/test/test_osparc/test_studies_api.py | 13 ++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/clients/python/test/test_osparc/test_solvers_api.py b/clients/python/test/test_osparc/test_solvers_api.py index a5941e28..cb4056ff 100644 --- a/clients/python/test/test_osparc/test_solvers_api.py +++ b/clients/python/test/test_osparc/test_solvers_api.py @@ -53,13 +53,18 @@ def test_get_job_custom_metadata( create_server_mock: Callable[[int], None], faker: Faker, solvers_api: SolversApi, + create_osparc_response_model: Callable, ): + _job_metadata = create_osparc_response_model(JobMetadata) create_server_mock(200) metadata = solvers_api.get_job_custom_metadata( solver_key="mysolver", version="1.2.3", job_id=f"{faker.uuid4()}" ) assert isinstance(metadata, JobMetadata) + assert ( + _job_metadata == metadata + ) # check fix for https://github.com/ITISFoundation/osparc-simcore/issues/6556 def test_replace_job_custom_metadata( @@ -68,6 +73,7 @@ def test_replace_job_custom_metadata( solvers_api: SolversApi, faker: Faker, ): + job_metadata = create_osparc_response_model(JobMetadata) job_metadata_update = create_osparc_response_model(JobMetadataUpdate) create_server_mock(200) @@ -78,3 +84,6 @@ def test_replace_job_custom_metadata( job_metadata_update=job_metadata_update, ) assert isinstance(_job_metadata, JobMetadata) + assert ( + _job_metadata == job_metadata + ) # check fix for https://github.com/ITISFoundation/osparc-simcore/issues/6556 diff --git a/clients/python/test/test_osparc/test_studies_api.py b/clients/python/test/test_osparc/test_studies_api.py index 3119dbc9..4945f995 100644 --- a/clients/python/test/test_osparc/test_studies_api.py +++ b/clients/python/test/test_osparc/test_studies_api.py @@ -40,11 +40,18 @@ def test_get_study_job_outputs( def test_get_study_job_custom_metadata( - create_server_mock: Callable[[int], None], studies_api: StudiesApi, uuid: str + create_osparc_response_model: Callable, + create_server_mock: Callable[[int], None], + studies_api: StudiesApi, + uuid: str, ): + _job_metadata: JobMetadata = create_osparc_response_model(JobMetadata) create_server_mock(200) metadata = studies_api.get_study_job_custom_metadata(study_id=uuid, job_id=uuid) assert isinstance(metadata, JobMetadata) + assert ( + _job_metadata == metadata + ) # check fix for https://github.com/ITISFoundation/osparc-simcore/issues/6556 def test_replace_study_job_custom_metadata( @@ -54,6 +61,7 @@ def test_replace_study_job_custom_metadata( uuid: str, ): create_server_mock(200) + _job_metadata: JobMetadata = create_osparc_response_model(JobMetadata) job_metadata_update: JobMetadataUpdate = create_osparc_response_model( JobMetadataUpdate ) @@ -63,3 +71,6 @@ def test_replace_study_job_custom_metadata( job_metadata_update=job_metadata_update, ) assert isinstance(job_metadata, JobMetadata) + assert ( + _job_metadata == job_metadata + ) # check fix for https://github.com/ITISFoundation/osparc-simcore/issues/6556 From 4041620849d5ae736cd61f0de27f8143797d9e30 Mon Sep 17 00:00:00 2001 From: Mads Bisgaard Date: Mon, 18 Nov 2024 08:54:26 +0100 Subject: [PATCH 52/52] add make target for installing local client for running e2e tests --- clients/python/Makefile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/clients/python/Makefile b/clients/python/Makefile index 4e4cba4a..57d4393d 100644 --- a/clients/python/Makefile +++ b/clients/python/Makefile @@ -129,6 +129,10 @@ install-e2e-test: _check_venv_active guard-OSPARC_VERSION ## install packages fo $(eval version := $(shell bash $(CLIENTS_PYTHON_DIR)/test/e2e/ci/bash/osparc_version.bash $(OSPARC_VERSION))) uv pip install -r $(CLIENTS_PYTHON_DIR)/requirements/e2e-test.txt osparc-client==$(version) osparc==$(version) +.PHONY: install-e2e-test-local +install-e2e-test-local: _check_venv_active python-client ## install packages for e2e testing with local (repo) client [e2e] + pip install -r $(CLIENTS_PYTHON_DIR)/requirements/e2e-test.txt --editable artifacts/client --editable . + .PHONY: install-doc install-doc: _check_venv_active .install-dev-reqs ## install packages for generating documentation uv pip install -r $(CLIENTS_PYTHON_DIR)/requirements/doc.txt