Skip to content

Commit 6efc8bc

Browse files
committed
get_category_items and settings
1 parent 3be3e49 commit 6efc8bc

File tree

3 files changed

+97
-41
lines changed

3 files changed

+97
-41
lines changed
Lines changed: 18 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,12 @@
11
import re
22
from typing import Annotated, Any, Literal
3-
from urllib.parse import urlparse
43

54
import httpx
6-
import pytest
7-
from pydantic import BaseModel, BeforeValidator, Field, HttpUrl, ValidationError
8-
from servicelib.aiohttp import status
9-
from settings_library.base import BaseCustomSettings
5+
from pydantic import BaseModel, BeforeValidator, Field, HttpUrl
106

11-
12-
class ItisVipSettings(BaseCustomSettings):
13-
ITIS_VIP_API_URL: str
14-
ITIS_VIP_CATEGORIES: list[str]
15-
16-
def get_urls(self) -> list[HttpUrl]:
17-
return [
18-
HttpUrl(self.ITIS_VIP_API_URL.format(category=category))
19-
for category in self.ITIS_VIP_CATEGORIES
20-
]
7+
#
8+
# MODELS
9+
#
2110

2211

2312
def _feature_descriptor_to_dict(descriptor: str) -> dict[str, Any]:
@@ -50,27 +39,22 @@ class ResponseData(BaseModel):
5039
]
5140

5241

53-
async def get_downloadable_items(
42+
#
43+
# API
44+
#
45+
46+
47+
async def get_category_items(
5448
client: httpx.AsyncClient, url: HttpUrl
5549
) -> list[AvailableDownload]:
50+
"""
51+
52+
Raises:
53+
Any https://www.python-httpx.org/exceptions/
54+
httpx.HTTPStatusCode:
55+
56+
"""
5657
response = await client.post(f"{url}")
57-
assert response.status_code == status.HTTP_200_OK
58+
response.raise_for_status()
5859
validated_data = ResponseData.model_validate(**response.json())
5960
return validated_data.available_downloads
60-
61-
62-
async def fetch_vip_downloadables(settings: ItisVipSettings):
63-
urls, categories = settings.get_urls(), settings.ITIS_VIP_CATEGORIES
64-
65-
base_url = f"{urls[0].scheme}://{urlparse(f'{urls[0]}').netloc}"
66-
67-
async with httpx.AsyncClient() as client:
68-
for url in urls:
69-
response = await client.post(url)
70-
assert response.status_code == status.HTTP_200_OK
71-
response_json = response.json()
72-
73-
try:
74-
validated_data = ResponseData(**response_json)
75-
except ValidationError as e:
76-
pytest.fail(f"Response validation failed: {e}")
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
from typing import Annotated
2+
3+
from pydantic import AfterValidator, Field, HttpUrl
4+
from settings_library.base import BaseCustomSettings
5+
6+
7+
def _validate_url_contains_category(url: str) -> str:
8+
if "{category}" not in url:
9+
msg = "URL must contain '{category}'"
10+
raise ValueError(msg)
11+
return url
12+
13+
14+
class ItisVipSettings(BaseCustomSettings):
15+
ITIS_VIP_API_URL: Annotated[str, AfterValidator(_validate_url_contains_category)]
16+
ITIS_VIP_CATEGORIES: list[str]
17+
18+
def get_urls(self) -> list[HttpUrl]:
19+
return [
20+
HttpUrl(self.ITIS_VIP_API_URL.format(category=category))
21+
for category in self.ITIS_VIP_CATEGORIES
22+
]
23+
24+
25+
class LicensesSettings(BaseCustomSettings):
26+
LICENSES_ITIS_VIP: Annotated[
27+
ItisVipSettings | None, Field(description="Settings for VIP license models")
28+
]
29+
# other licenses resources come here

services/web/server/tests/unit/with_dbs/04/licenses/test_itis_vip_service.py

Lines changed: 50 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,38 @@
1111
from faker import Faker
1212
from httpx import AsyncClient
1313
from pydantic import ValidationError
14+
from pytest_simcore.helpers.monkeypatch_envs import setenvs_from_dict
15+
from pytest_simcore.helpers.typing_env import EnvVarsDict
1416
from servicelib.aiohttp import status
17+
from simcore_service_webserver.licenses import _itis_vip_service
1518
from simcore_service_webserver.licenses._itis_vip_service import ResponseData
19+
from simcore_service_webserver.licenses.settings import ItisVipSettings
20+
21+
22+
@pytest.fixture(scope="session")
23+
def fake_api_base_url() -> str:
24+
return "https://testserver"
1625

1726

1827
@pytest.fixture
19-
def mock_itis_vip_downloadables_api(faker: Faker) -> Iterator[respx.MockRouter]:
20-
api_url = "http://testserver"
28+
def app_environment(
29+
monkeypatch: pytest.MonkeyPatch,
30+
app_environment: EnvVarsDict,
31+
fake_api_base_url: str,
32+
):
33+
return app_environment | setenvs_from_dict(
34+
monkeypatch,
35+
{
36+
"ITIS_VIP_API_URL": f"{fake_api_base_url}/PD_DirectDownload/getDownloadableItems/{{category}}",
37+
"ITIS_VIP_CATEGORIES": "ComputationalPantom,Foo,Bar",
38+
},
39+
)
40+
41+
42+
@pytest.fixture
43+
def mock_itis_vip_downloadables_api(
44+
faker: Faker, fake_api_base_url: str
45+
) -> Iterator[respx.MockRouter]:
2146
response_data = {
2247
"msg": 0,
2348
"availableDownloads": [
@@ -37,17 +62,17 @@ def mock_itis_vip_downloadables_api(faker: Faker) -> Iterator[respx.MockRouter]:
3762
],
3863
}
3964

40-
with respx.mock(base_url=api_url) as mock:
41-
mock.post("getDownloadableItems/ComputationalPantom").respond(
65+
with respx.mock(base_url=fake_api_base_url) as mock:
66+
mock.post(path__regex=r"/getDownloadableItems/(?P<category>\d+)").respond(
4267
status_code=200, json=response_data
4368
)
4469
yield mock
4570

4671

47-
async def test_fetch_itis_vip_api(
48-
mock_itis_vip_downloadables_api: respx.MockRouter,
72+
async def test_fetch_and_validate_itis_vip_api(
73+
mock_itis_vip_downloadables_api: respx.MockRouter, fake_api_base_url: str
4974
):
50-
async with AsyncClient(base_url="http://testserver") as client:
75+
async with AsyncClient(base_url=fake_api_base_url) as client:
5176
response = await client.post("getDownloadableItems/ComputationalPantom")
5277
assert response.status_code == status.HTTP_200_OK
5378
response_json = response.json()
@@ -65,3 +90,21 @@ async def test_fetch_itis_vip_api(
6590
)
6691

6792
print(validated_data.model_dump_json(indent=1))
93+
94+
95+
async def test_get_category_items(
96+
mock_itis_vip_downloadables_api: respx.MockRouter,
97+
app_environment: EnvVarsDict,
98+
):
99+
settings = ItisVipSettings.create_from_envs()
100+
assert settings.ITIS_VIP_CATEGORIES
101+
102+
async with AsyncClient() as client:
103+
for url, category in zip(
104+
settings.get_urls(), settings.ITIS_VIP_CATEGORIES, strict=True
105+
):
106+
assert f"{url}".endswith(category)
107+
108+
items = await _itis_vip_service.get_category_items(client, url)
109+
110+
assert items[0].features["functionality"] == "Posable"

0 commit comments

Comments
 (0)