Skip to content

Commit e36b197

Browse files
authored
✨ Adds filtering for Service Listing in Catalog's RPC API (#7537)
1 parent e42e1a3 commit e36b197

File tree

17 files changed

+435
-176
lines changed

17 files changed

+435
-176
lines changed

.coveragerc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ branch = True
33
omit =
44
*/tests/*
55
*/generated_code/*
6+
*/_original_fastapi_encoders.py
67
parallel = True
78

89
[report]

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from ..boot_options import BootOptions
1010
from ..emails import LowerCaseEmailStr
1111
from ..groups import GroupID
12+
from ..rest_filters import Filters
1213
from ..services_access import ServiceAccessRights, ServiceGroupAccessRightsV2
1314
from ..services_authoring import Author
1415
from ..services_enums import ServiceType
@@ -376,4 +377,13 @@ class MyServiceGet(CatalogOutputSchema):
376377
my_access_rights: ServiceGroupAccessRightsV2
377378

378379

380+
class ServiceListFilters(Filters):
381+
service_type: Annotated[
382+
ServiceType | None,
383+
Field(
384+
description="Filter only services of a given type. If None, then all types are returned"
385+
),
386+
] = None
387+
388+
379389
__all__: tuple[str, ...] = ("ServiceRelease",)

packages/models-library/src/models_library/function_services_catalog/_key_labels.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
from typing import Final
22

33
from ..services import ServiceKey
4+
from ..services_constants import FRONTEND_SERVICE_KEY_PREFIX
45

56
# NOTE: due to legacy reasons, the name remains with 'frontend' in it but
67
# it now refers to a more general group: function sections that contains front-end services as well
7-
FUNCTION_SERVICE_KEY_PREFIX: Final[str] = "simcore/services/frontend"
8+
FUNCTION_SERVICE_KEY_PREFIX: Final[str] = FRONTEND_SERVICE_KEY_PREFIX
89

910

1011
def is_function_service(service_key: ServiceKey) -> bool:
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from .rest_filters import Filters
2+
3+
__all__: tuple[str, ...] = ("Filters",)
4+
5+
# nopycln:file
Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,27 @@
1+
from types import MappingProxyType
12
from typing import Final
23

3-
LATEST_INTEGRATION_VERSION: Final[str] = "1.0.0"
4+
from .services_enums import ServiceType
45

6+
LATEST_INTEGRATION_VERSION: Final[str] = "1.0.0"
57

68
ANY_FILETYPE: Final[str] = "data:*/*"
9+
10+
SERVICE_TYPE_TO_NAME_MAP = MappingProxyType(
11+
{
12+
ServiceType.COMPUTATIONAL: "comp",
13+
ServiceType.DYNAMIC: "dynamic",
14+
ServiceType.FRONTEND: "frontend",
15+
}
16+
)
17+
18+
19+
def _create_key_prefix(service_type: ServiceType) -> str:
20+
return f"simcore/services/{SERVICE_TYPE_TO_NAME_MAP[service_type]}"
21+
22+
23+
COMPUTATIONAL_SERVICE_KEY_PREFIX: Final[str] = _create_key_prefix(
24+
ServiceType.COMPUTATIONAL
25+
)
26+
DYNAMIC_SERVICE_KEY_PREFIX: Final[str] = _create_key_prefix(ServiceType.DYNAMIC)
27+
FRONTEND_SERVICE_KEY_PREFIX: Final[str] = _create_key_prefix(ServiceType.FRONTEND)
Lines changed: 48 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
11
import re
2+
from types import MappingProxyType
23
from typing import Final
34

5+
from .services_constants import (
6+
COMPUTATIONAL_SERVICE_KEY_PREFIX,
7+
DYNAMIC_SERVICE_KEY_PREFIX,
8+
FRONTEND_SERVICE_KEY_PREFIX,
9+
SERVICE_TYPE_TO_NAME_MAP,
10+
)
11+
from .services_enums import ServiceType
12+
413
PROPERTY_TYPE_RE = r"^(number|integer|boolean|string|ref_contentSchema|data:([^/\s,]+/[^/\s,]+|\[[^/\s,]+/[^/\s,]+(,[^/\s]+/[^/,\s]+)*\]))$"
514
PROPERTY_TYPE_TO_PYTHON_TYPE_MAP = {
615
"integer": int,
@@ -11,34 +20,59 @@
1120

1221
FILENAME_RE = r".+"
1322

14-
1523
# e.g. simcore/services/comp/opencor
1624
SERVICE_KEY_RE: Final[re.Pattern[str]] = re.compile(
1725
r"^simcore/services/"
18-
r"(?P<type>(comp|dynamic|frontend))/"
26+
rf"(?P<type>({ '|'.join(SERVICE_TYPE_TO_NAME_MAP.values()) }))/"
1927
r"(?P<subdir>[a-z0-9][a-z0-9_.-]*/)*"
2028
r"(?P<name>[a-z0-9-_]+[a-z0-9])$"
2129
)
30+
2231
# e.g. simcore%2Fservices%2Fcomp%2Fopencor
2332
SERVICE_ENCODED_KEY_RE: Final[re.Pattern[str]] = re.compile(
2433
r"^simcore%2Fservices%2F"
25-
r"(?P<type>(comp|dynamic|frontend))%2F"
34+
rf"(?P<type>({'|'.join(SERVICE_TYPE_TO_NAME_MAP.values())}))%2F"
2635
r"(?P<subdir>[a-z0-9][a-z0-9_.-]*%2F)*"
2736
r"(?P<name>[a-z0-9-_]+[a-z0-9])$"
2837
)
2938

30-
DYNAMIC_SERVICE_KEY_RE: Final[re.Pattern[str]] = re.compile(
31-
r"^simcore/services/dynamic/"
32-
r"(?P<subdir>[a-z0-9][a-z0-9_.-]*/)*"
33-
r"(?P<name>[a-z0-9-_]+[a-z0-9])$"
39+
40+
def _create_key_regex(service_type: ServiceType) -> re.Pattern[str]:
41+
return re.compile(
42+
rf"^simcore/services/{SERVICE_TYPE_TO_NAME_MAP[service_type]}/"
43+
r"(?P<subdir>[a-z0-9][a-z0-9_.-]*/)*"
44+
r"(?P<name>[a-z0-9-_]+[a-z0-9])$"
45+
)
46+
47+
48+
def _create_key_format(service_type: ServiceType) -> str:
49+
return f"simcore/services/{SERVICE_TYPE_TO_NAME_MAP[service_type]}/{{service_name}}"
50+
51+
52+
COMPUTATIONAL_SERVICE_KEY_RE: Final[re.Pattern[str]] = _create_key_regex(
53+
ServiceType.COMPUTATIONAL
54+
)
55+
COMPUTATIONAL_SERVICE_KEY_FORMAT: Final[str] = _create_key_format(
56+
ServiceType.COMPUTATIONAL
3457
)
35-
DYNAMIC_SERVICE_KEY_FORMAT = "simcore/services/dynamic/{service_name}"
3658

59+
DYNAMIC_SERVICE_KEY_RE: Final[re.Pattern[str]] = _create_key_regex(ServiceType.DYNAMIC)
60+
DYNAMIC_SERVICE_KEY_FORMAT: Final[str] = _create_key_format(ServiceType.DYNAMIC)
3761

38-
# Computational regex & format
39-
COMPUTATIONAL_SERVICE_KEY_RE: Final[re.Pattern[str]] = re.compile(
40-
r"^simcore/services/comp/"
41-
r"(?P<subdir>[a-z0-9][a-z0-9_.-]*/)*"
42-
r"(?P<name>[a-z0-9-_]+[a-z0-9])$"
62+
FRONTEND_SERVICE_KEY_RE: Final[re.Pattern[str]] = _create_key_regex(
63+
ServiceType.FRONTEND
4364
)
44-
COMPUTATIONAL_SERVICE_KEY_FORMAT: Final[str] = "simcore/services/comp/{service_name}"
65+
FRONTEND_SERVICE_KEY_FORMAT: Final[str] = _create_key_format(ServiceType.FRONTEND)
66+
67+
68+
SERVICE_TYPE_TO_PREFIX_MAP = MappingProxyType(
69+
{
70+
ServiceType.COMPUTATIONAL: COMPUTATIONAL_SERVICE_KEY_PREFIX,
71+
ServiceType.DYNAMIC: DYNAMIC_SERVICE_KEY_PREFIX,
72+
ServiceType.FRONTEND: FRONTEND_SERVICE_KEY_PREFIX,
73+
}
74+
)
75+
76+
assert all( # nosec
77+
not prefix.endswith("/") for prefix in SERVICE_TYPE_TO_PREFIX_MAP.values()
78+
), "Service type prefixes must not end with '/'"

packages/pytest-simcore/src/pytest_simcore/helpers/catalog_rpc_server.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,11 @@
55
# pylint: disable=unused-variable
66

77

8-
from models_library.api_schemas_catalog.services import LatestServiceGet, ServiceGetV2
8+
from models_library.api_schemas_catalog.services import (
9+
LatestServiceGet,
10+
ServiceGetV2,
11+
ServiceListFilters,
12+
)
913
from models_library.api_schemas_webserver.catalog import (
1014
CatalogServiceUpdate,
1115
)
@@ -34,10 +38,12 @@ async def list_services_paginated(
3438
user_id: UserID,
3539
limit: PageLimitInt,
3640
offset: NonNegativeInt,
41+
filters: ServiceListFilters | None = None,
3742
):
3843
assert rpc_client
3944
assert product_name
4045
assert user_id
46+
assert filters is None, "filters not mocked yet"
4147

4248
items = TypeAdapter(list[LatestServiceGet]).validate_python(
4349
LatestServiceGet.model_json_schema()["examples"],
@@ -110,12 +116,14 @@ async def list_my_service_history_paginated(
110116
service_key: ServiceKey,
111117
offset: PageOffsetInt,
112118
limit: PageLimitInt,
119+
filters: ServiceListFilters | None = None,
113120
) -> PageRpc[ServiceRelease]:
114121

115122
assert rpc_client
116123
assert product_name
117124
assert user_id
118125
assert service_key
126+
assert filters is None, "filters not mocked yet"
119127

120128
items = TypeAdapter(list[ServiceRelease]).validate_python(
121129
ServiceRelease.model_json_schema()["examples"],

packages/service-integration/src/service_integration/osparc_config.py

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
""" 'osparc config' is a set of stardard file forms (yaml) that the user fills to describe how his/her service works and
1+
"""'osparc config' is a set of stardard file forms (yaml) that the user fills to describe how his/her service works and
22
integrates with osparc.
33
44
- config files are stored under '.osparc/' folder in the root repo folder (analogous to other configs like .github, .vscode, etc)
@@ -26,11 +26,7 @@
2626
RestartPolicy,
2727
)
2828
from models_library.service_settings_nat_rule import NATRule
29-
from models_library.services import BootOptions, ServiceMetaDataPublished, ServiceType
30-
from models_library.services_regex import (
31-
COMPUTATIONAL_SERVICE_KEY_FORMAT,
32-
DYNAMIC_SERVICE_KEY_FORMAT,
33-
)
29+
from models_library.services import BootOptions, ServiceMetaDataPublished
3430
from models_library.services_types import ServiceKey
3531
from models_library.utils.labels_annotations import (
3632
OSPARC_LABEL_PREFIXES,
@@ -62,12 +58,6 @@
6258
OSPARC_CONFIG_RUNTIME_NAME: Final[str] = "runtime.yml"
6359

6460

65-
SERVICE_KEY_FORMATS = {
66-
ServiceType.COMPUTATIONAL: COMPUTATIONAL_SERVICE_KEY_FORMAT,
67-
ServiceType.DYNAMIC: DYNAMIC_SERVICE_KEY_FORMAT,
68-
}
69-
70-
7161
class DockerComposeOverwriteConfig(ComposeSpecification):
7262
"""Content of docker-compose.overwrite.yml configuration file"""
7363

@@ -231,9 +221,9 @@ class RuntimeConfig(BaseModel):
231221

232222
containers_allowed_outgoing_internet: set[str] | None = None
233223

234-
settings: Annotated[
235-
list[SettingsItem], Field(default_factory=list)
236-
] = DEFAULT_FACTORY
224+
settings: Annotated[list[SettingsItem], Field(default_factory=list)] = (
225+
DEFAULT_FACTORY
226+
)
237227

238228
@model_validator(mode="before")
239229
@classmethod

0 commit comments

Comments
 (0)