Skip to content

Commit 7bbe389

Browse files
authored
✨ Implementing new list_services_paginated entrypoint in web-api (part 1) (#5991)
1 parent e494952 commit 7bbe389

File tree

23 files changed

+1059
-197
lines changed

23 files changed

+1059
-197
lines changed

packages/models-library/src/models_library/api_schemas_catalog/services.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
from typing import Any, ClassVar
1+
from typing import Any, ClassVar, TypeAlias
22

33
from models_library.rpc_pagination import PageRpc
44
from pydantic import BaseModel, Extra, Field, HttpUrl, NonNegativeInt
55

66
from ..boot_options import BootOptions
77
from ..emails import LowerCaseEmailStr
8-
from ..services_access import ServiceAccessRights, ServiceGroupAccessRightsApi
8+
from ..services_access import ServiceAccessRights
99
from ..services_authoring import Author, Badge
1010
from ..services_enums import ServiceType
1111
from ..services_history import ServiceRelease
@@ -205,6 +205,15 @@ class Config:
205205
}
206206

207207

208+
class ServiceGroupAccessRightsV2(BaseModel):
209+
execute: bool = False
210+
write: bool = False
211+
212+
class Config:
213+
alias_generator = snake_to_camel
214+
allow_population_by_field_name = True
215+
216+
208217
class ServiceGetV2(BaseModel):
209218
key: ServiceKey
210219
version: ServiceVersion
@@ -229,9 +238,9 @@ class ServiceGetV2(BaseModel):
229238
boot_options: BootOptions | None = None
230239
min_visible_inputs: NonNegativeInt | None = None
231240

232-
access_rights: dict[GroupID, ServiceGroupAccessRightsApi] | None
241+
access_rights: dict[GroupID, ServiceGroupAccessRightsV2] | None
233242

234-
classifiers: list[str] | None
243+
classifiers: list[str] | None = None
235244
quality: dict[str, Any] = {}
236245

237246
history: list[ServiceRelease] = Field(
@@ -298,9 +307,9 @@ class Config:
298307
}
299308

300309

301-
PageRpcServicesGetV2 = PageRpc[
310+
PageRpcServicesGetV2: TypeAlias = PageRpc[
302311
# WARNING: keep this definition in models_library and not in the RPC interface
303312
ServiceGetV2
304313
]
305314

306-
ServiceResourcesGet = ServiceResourcesDict
315+
ServiceResourcesGet: TypeAlias = ServiceResourcesDict

packages/models-library/src/models_library/services_access.py

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
from pydantic import BaseModel, Field
77

88
from .users import GroupID
9-
from .utils.change_case import snake_to_camel
109

1110

1211
class ServiceGroupAccessRights(BaseModel):
@@ -19,12 +18,6 @@ class ServiceGroupAccessRights(BaseModel):
1918
)
2019

2120

22-
class ServiceGroupAccessRightsApi(ServiceGroupAccessRights):
23-
class Config:
24-
alias_generator = snake_to_camel
25-
allow_population_by_field_name = True
26-
27-
2821
class ServiceAccessRights(BaseModel):
2922
access_rights: dict[GroupID, ServiceGroupAccessRights] | None = Field(
3023
None,
Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from typing import Any, ClassVar
2+
13
from pydantic import BaseModel, Field, HttpUrl
24

35
from .emails import LowerCaseEmailStr
@@ -7,35 +9,48 @@ class Badge(BaseModel):
79
name: str = Field(
810
...,
911
description="Name of the subject",
10-
examples=["travis-ci", "coverals.io", "github.io"],
1112
)
1213
image: HttpUrl = Field(
1314
...,
1415
description="Url to the badge",
15-
examples=[
16-
"https://travis-ci.org/ITISFoundation/osparc-simcore.svg?branch=master",
17-
"https://coveralls.io/repos/github/ITISFoundation/osparc-simcore/badge.svg?branch=master",
18-
"https://img.shields.io/website-up-down-green-red/https/itisfoundation.github.io.svg?label=documentation",
19-
],
2016
)
2117
url: HttpUrl = Field(
2218
...,
2319
description="Link to the status",
24-
examples=[
25-
"https://travis-ci.org/ITISFoundation/osparc-simcore 'State of CI: build, test and pushing images'",
26-
"https://coveralls.io/github/ITISFoundation/osparc-simcore?branch=master 'Test coverage'",
27-
"https://itisfoundation.github.io/",
28-
],
2920
)
3021

22+
class Config:
23+
schema_extra: ClassVar[dict[str, Any]] = {
24+
"example": {
25+
"name": "osparc.io",
26+
"image": "https://img.shields.io/website-up-down-green-red/https/itisfoundation.github.io.svg?label=documentation",
27+
"url": "https://itisfoundation.github.io/",
28+
}
29+
}
30+
3131

3232
class Author(BaseModel):
33-
name: str = Field(..., description="Name of the author", example="Jim Knopf")
33+
name: str = Field(
34+
...,
35+
description="Name of the author",
36+
)
3437
email: LowerCaseEmailStr = Field(
3538
...,
36-
3739
description="Email address",
3840
)
39-
affiliation: str | None = Field(
40-
None, examples=["Sense8", "Babylon 5"], description="Affiliation of the author"
41-
)
41+
affiliation: str | None = Field(None)
42+
43+
class Config:
44+
schema_extra: ClassVar[dict[str, Any]] = {
45+
"examples": [
46+
{
47+
"name": "Jim Knopf",
48+
"email": "[email protected]",
49+
"affiliation": "Babylon 5",
50+
},
51+
{
52+
"name": "John Smith",
53+
"email": "[email protected]",
54+
},
55+
]
56+
}

packages/models-library/src/models_library/services_history.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ class Config:
2323
class ServiceRelease(BaseModel):
2424
# from ServiceMetaDataPublished
2525
version: ServiceVersion
26-
version_display: str | None = Field(default=None)
26+
version_display: str | None = Field(
27+
default=None, description="If None, then display `version`"
28+
)
2729
released: datetime | None = Field(
2830
default=None, description="When provided, it indicates the release timestamp"
2931
)

packages/service-library/src/servicelib/rabbitmq/rpc_interfaces/catalog/services.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,12 @@ async def list_services_paginated( # pylint: disable=too-many-arguments
3232
limit: PageLimitInt = DEFAULT_NUMBER_OF_ITEMS_PER_PAGE,
3333
offset: NonNegativeInt = 0,
3434
) -> PageRpc[ServiceGetV2]:
35+
"""
36+
Raises:
37+
ValidationError: on invalid arguments
38+
39+
"""
40+
3541
@validate_arguments()
3642
async def _call(
3743
product_name: ProductName,
@@ -64,6 +70,12 @@ async def get_service(
6470
service_key: ServiceKey,
6571
service_version: ServiceVersion,
6672
) -> ServiceGetV2:
73+
"""
74+
Raises:
75+
ValidationError: on invalid arguments
76+
77+
"""
78+
6779
@validate_arguments()
6880
async def _call(
6981
product_name: ProductName,
@@ -100,7 +112,11 @@ async def update_service(
100112
service_version: ServiceVersion,
101113
update: ServiceUpdate,
102114
) -> ServiceGetV2:
103-
"""Updates editable fields of a service"""
115+
"""Updates editable fields of a service
116+
117+
Raises:
118+
ValidationError: on invalid arguments
119+
"""
104120

105121
@validate_arguments()
106122
async def _call(

services/catalog/src/simcore_service_catalog/api/rest/_services.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,14 @@
99
from fastapi import APIRouter, Depends, Header, HTTPException, status
1010
from models_library.api_schemas_catalog.services import ServiceGet, ServiceUpdate
1111
from models_library.services import ServiceKey, ServiceType, ServiceVersion
12-
from models_library.services_db import ServiceAccessRightsAtDB, ServiceMetaDataAtDB
1312
from pydantic import ValidationError
1413
from pydantic.types import PositiveInt
1514
from servicelib.fastapi.requests_decorators import cancel_on_disconnect
1615
from starlette.requests import Request
1716

1817
from ...db.repositories.groups import GroupsRepository
1918
from ...db.repositories.services import ServicesRepository
19+
from ...models.services_db import ServiceAccessRightsAtDB, ServiceMetaDataAtDB
2020
from ...services.director import DirectorApi
2121
from ...services.function_services import is_function_service
2222
from ..dependencies.database import get_repository

services/catalog/src/simcore_service_catalog/api/rest/_services_access_rights.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@
66
ServiceAccessRightsGet,
77
)
88
from models_library.services import ServiceKey, ServiceVersion
9-
from models_library.services_db import ServiceAccessRightsAtDB
109

1110
from ...db.repositories.services import ServicesRepository
11+
from ...models.services_db import ServiceAccessRightsAtDB
1212
from ..dependencies.database import get_repository
1313
from ..dependencies.services import AccessInfo, check_service_read_access
1414
from ._constants import RESPONSE_MODEL_POLICY

services/catalog/src/simcore_service_catalog/api/rpc/_services.py

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@
1414
from servicelib.logging_utils import log_decorator
1515
from servicelib.rabbitmq import RPCRouter
1616

17+
from ...db.repositories.services import ServicesRepository
18+
from ...services import catalog
19+
1720
_logger = logging.getLogger(__name__)
1821

1922
router = RPCRouter()
@@ -29,17 +32,21 @@ async def list_services_paginated(
2932
limit: PageLimitInt = DEFAULT_NUMBER_OF_ITEMS_PER_PAGE,
3033
offset: NonNegativeInt = 0,
3134
) -> PageRpcServicesGetV2:
32-
assert app # nosec
33-
assert product_name # nosec
35+
assert app.state.engine # nosec
3436

35-
_logger.debug("Moking list_services_paginated for %s...", f"{user_id=}")
36-
items = parse_obj_as(
37-
list[ServiceGetV2], ServiceGetV2.Config.schema_extra["examples"]
37+
total_count, items = await catalog.list_services_paginated(
38+
repo=ServicesRepository(app.state.engine),
39+
product_name=product_name,
40+
user_id=user_id,
41+
limit=limit,
42+
offset=offset,
3843
)
39-
total_count = len(items)
44+
45+
assert len(items) <= total_count # nosec
46+
assert len(items) <= limit # nosec
4047

4148
return PageRpcServicesGetV2.create(
42-
items[offset : offset + limit],
49+
items,
4350
total=total_count,
4451
limit=limit,
4552
offset=offset,

services/catalog/src/simcore_service_catalog/core/background_tasks.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
from fastapi import FastAPI
1919
from models_library.function_services_catalog.api import iter_service_docker_data
2020
from models_library.services import ServiceMetaDataPublished
21-
from models_library.services_db import ServiceAccessRightsAtDB, ServiceMetaDataAtDB
2221
from packaging.version import Version
2322
from pydantic import ValidationError
2423
from sqlalchemy.ext.asyncio import AsyncEngine
@@ -27,6 +26,7 @@
2726
from ..db.repositories.groups import GroupsRepository
2827
from ..db.repositories.projects import ProjectsRepository
2928
from ..db.repositories.services import ServicesRepository
29+
from ..models.services_db import ServiceAccessRightsAtDB, ServiceMetaDataAtDB
3030
from ..services import access_rights
3131

3232
_logger = logging.getLogger(__name__)
@@ -120,10 +120,9 @@ def _by_version(t: tuple[ServiceKey, ServiceVersion]) -> Version:
120120
service_access_rights
121121
)
122122

123-
service_metadata_dict = service_metadata.dict()
124123
# set the service in the DB
125-
await services_repo.create_service(
126-
ServiceMetaDataAtDB(**service_metadata_dict, owner=owner_gid),
124+
await services_repo.create_or_update_service(
125+
ServiceMetaDataAtDB(**service_metadata.dict(), owner=owner_gid),
127126
service_access_rights,
128127
)
129128

0 commit comments

Comments
 (0)