Skip to content

Commit 65916c4

Browse files
committed
add test for getting latest solver
1 parent e6e6baa commit 65916c4

File tree

4 files changed

+60
-45
lines changed

4 files changed

+60
-45
lines changed

services/api-server/src/simcore_service_api_server/_service_solvers.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,20 @@
11
from typing import Annotated
22

3+
from common_library.pagination_tools import iter_pagination_params
34
from fastapi import Depends
45
from models_library.basic_types import VersionStr
56
from models_library.products import ProductName
7+
from models_library.rest_pagination import MAXIMUM_NUMBER_OF_ITEMS_PER_PAGE
68
from models_library.services_enums import ServiceType
9+
from models_library.services_history import ServiceRelease
710
from models_library.users import UserID
11+
from packaging.version import Version
812

913
from .models.schemas.solvers import Solver, SolverKeyId
1014
from .services_rpc.catalog import CatalogService
1115

16+
DEFAULT_PAGINATION_LIMIT = MAXIMUM_NUMBER_OF_ITEMS_PER_PAGE - 1
17+
1218

1319
class SolverService:
1420
_catalog_service: CatalogService
@@ -37,3 +43,32 @@ async def get_solver(
3743
), "Expected by SolverName regex"
3844

3945
return Solver.create_from_service(service)
46+
47+
async def get_latest_release(
48+
self,
49+
*,
50+
user_id: int,
51+
solver_key: SolverKeyId,
52+
product_name: str,
53+
) -> Solver:
54+
service_releases: list[ServiceRelease] = []
55+
for page_params in iter_pagination_params(limit=DEFAULT_PAGINATION_LIMIT):
56+
releases, page_meta = await self._catalog_service.list_release_history(
57+
user_id=user_id,
58+
service_key=solver_key,
59+
product_name=product_name,
60+
offset=page_params.offset,
61+
limit=page_params.limit,
62+
)
63+
page_params.total_number_of_items = page_meta.total
64+
service_releases.extend(releases)
65+
66+
release = sorted(service_releases, key=lambda s: Version(s.version))[-1]
67+
service = await self._catalog_service.get(
68+
user_id=user_id,
69+
service_key=solver_key,
70+
service_version=release.version,
71+
product_name=product_name,
72+
)
73+
74+
return Solver.create_from_service(service)

services/api-server/src/simcore_service_api_server/api/routes/solvers.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,15 +136,15 @@ async def get_solvers_releases_page(
136136
async def get_solver(
137137
solver_key: SolverKeyId,
138138
user_id: Annotated[int, Depends(get_current_user_id)],
139-
catalog_client: Annotated[CatalogApi, Depends(get_api_client(CatalogApi))],
139+
solver_service: Annotated[SolverService, Depends(SolverService)],
140140
url_for: Annotated[Callable, Depends(get_reverse_url_mapper)],
141141
product_name: Annotated[str, Depends(get_product_name)],
142142
) -> Solver:
143143
"""Gets latest release of a solver"""
144144
# IMPORTANT: by adding /latest, we avoid changing the order of this entry in the router list
145145
# otherwise, {solver_key:path} will override and consume any of the paths that follow.
146146
try:
147-
solver = await catalog_client.get_latest_release(
147+
solver = await solver_service.get_latest_release(
148148
user_id=user_id,
149149
solver_key=solver_key,
150150
product_name=product_name,

services/api-server/src/simcore_service_api_server/services_http/catalog.py

Lines changed: 0 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
from collections.abc import Callable
55
from dataclasses import dataclass
66
from functools import partial
7-
from operator import attrgetter
87
from typing import Final, Literal
98

109
from fastapi import FastAPI, status
@@ -145,32 +144,6 @@ async def list_services(
145144
services = [service for service in services if predicate(service)]
146145
return services
147146

148-
@_exception_mapper(
149-
http_status_map={status.HTTP_404_NOT_FOUND: SolverOrStudyNotFoundError}
150-
)
151-
async def _get_service(
152-
self, *, user_id: int, name: SolverKeyId, version: VersionStr, product_name: str
153-
) -> TruncatedCatalogServiceOut:
154-
155-
assert version != LATEST_VERSION # nosec
156-
157-
service_key = urllib.parse.quote_plus(name)
158-
service_version = version
159-
160-
response = await self.client.get(
161-
f"/services/{service_key}/{service_version}",
162-
params={"user_id": user_id},
163-
headers={"x-simcore-products-name": product_name},
164-
)
165-
response.raise_for_status()
166-
167-
service: (
168-
TruncatedCatalogServiceOut
169-
) = await asyncio.get_event_loop().run_in_executor(
170-
None, _parse_response, TruncatedCatalogServiceOutAdapter, response
171-
)
172-
return service
173-
174147
@_exception_mapper(
175148
http_status_map={status.HTTP_404_NOT_FOUND: SolverOrStudyNotFoundError}
176149
)
@@ -236,22 +209,6 @@ def _this_solver(solver: Solver) -> bool:
236209
solvers = [service.to_solver() for service in services]
237210
return [solver for solver in solvers if _this_solver(solver)]
238211

239-
async def get_latest_release(
240-
self,
241-
*,
242-
user_id: int,
243-
solver_key: SolverKeyId,
244-
product_name: str,
245-
) -> Solver:
246-
releases = await self.list_service_releases(
247-
user_id=user_id,
248-
solver_key=solver_key,
249-
product_name=product_name,
250-
)
251-
252-
# raises IndexError if None
253-
return sorted(releases, key=attrgetter("pep404_version"))[-1]
254-
255212

256213
# MODULES APP SETUP -------------------------------------------------------------
257214

services/api-server/tests/unit/test_api_solvers.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,26 @@ async def test_get_solver_pricing_plan(
5050
assert expected_status_code == response.status_code
5151
if response.status_code == status.HTTP_200_OK:
5252
_ = ServicePricingPlanGet.model_validate(response.json())
53+
54+
55+
@pytest.mark.parametrize(
56+
"solver_key,expected_status_code",
57+
[
58+
("simcore/services/comp/valid_solver", status.HTTP_200_OK),
59+
],
60+
)
61+
async def test_get_latest_solver_release(
62+
client: AsyncClient,
63+
mocked_rpc_catalog_service_api,
64+
auth: httpx.BasicAuth,
65+
solver_key: str,
66+
expected_status_code: int,
67+
):
68+
response = await client.get(
69+
f"{API_VTAG}/solvers/{solver_key}/latest",
70+
auth=auth,
71+
)
72+
assert response.status_code == expected_status_code
73+
if response.status_code == status.HTTP_200_OK:
74+
assert "id" in response.json()
75+
assert response.json()["id"] == solver_key

0 commit comments

Comments
 (0)