Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -172,23 +172,57 @@ def _update_json_schema_extra(schema: JsonDict) -> None:
)


class _BaseServiceGetV2(CatalogOutputSchema):
# Model used in catalog's rpc and rest interfaces
class ServiceSummary(CatalogOutputSchema):
key: ServiceKey
version: ServiceVersion

name: str
thumbnail: HttpUrl | None = None
icon: HttpUrl | None = None
description: str

description_ui: bool = False

version_display: str | None = None

contact: LowerCaseEmailStr | None

@staticmethod
def _update_json_schema_extra(schema: JsonDict) -> None:
schema.update(
{
"examples": [
{
"key": _EXAMPLE_SLEEPER["key"],
"version": _EXAMPLE_SLEEPER["version"],
"name": _EXAMPLE_SLEEPER["name"],
"description": _EXAMPLE_SLEEPER["description"],
"version_display": _EXAMPLE_SLEEPER["version_display"],
"contact": _EXAMPLE_SLEEPER["contact"],
},
{
"key": _EXAMPLE_FILEPICKER["key"],
"version": _EXAMPLE_FILEPICKER["version"],
"name": _EXAMPLE_FILEPICKER["name"],
"description": _EXAMPLE_FILEPICKER["description"],
"version_display": None,
"contact": _EXAMPLE_FILEPICKER["contact"],
},
]
}
)

model_config = ConfigDict(
extra="ignore",
populate_by_name=True,
alias_generator=snake_to_camel,
json_schema_extra=_update_json_schema_extra,
)


class _BaseServiceGetV2(ServiceSummary):
service_type: Annotated[ServiceType, Field(alias="type")]

contact: LowerCaseEmailStr | None
thumbnail: HttpUrl | None = None
icon: HttpUrl | None = None

description_ui: bool = False

authors: Annotated[list[Author], Field(min_length=1)]
owner: Annotated[
LowerCaseEmailStr | None,
Expand Down Expand Up @@ -217,6 +251,7 @@ class _BaseServiceGetV2(CatalogOutputSchema):
extra="forbid",
populate_by_name=True,
alias_generator=snake_to_camel,
json_schema_extra={"example": _EXAMPLE_SLEEPER},
)


Expand Down Expand Up @@ -249,7 +284,6 @@ def _update_json_schema_extra(schema: JsonDict) -> None:


class ServiceGetV2(_BaseServiceGetV2):
# Model used in catalog's rpc and rest interfaces
history: Annotated[
list[ServiceRelease],
Field(
Expand Down Expand Up @@ -338,6 +372,9 @@ def _update_json_schema_extra(schema: JsonDict) -> None:
ServiceRelease
]

# Create PageRpc types
PageRpcServiceSummary = PageRpc[ServiceSummary]

ServiceResourcesGet: TypeAlias = ServiceResourcesDict


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
LatestServiceGet,
ServiceGetV2,
ServiceListFilters,
ServiceSummary,
ServiceUpdateV2,
)
from models_library.api_schemas_catalog.services_ports import ServicePortGet
Expand Down Expand Up @@ -200,6 +201,66 @@ async def get_service_ports(
ServicePortGet.model_json_schema()["examples"],
)

@validate_call(config={"arbitrary_types_allowed": True})
async def list_all_services_summaries_paginated(
self,
rpc_client: RabbitMQRPCClient | MockType,
*,
product_name: ProductName,
user_id: UserID,
limit: PageLimitInt,
offset: NonNegativeInt,
filters: ServiceListFilters | None = None,
):
assert rpc_client
assert product_name
assert user_id

service_summaries = TypeAdapter(list[ServiceSummary]).validate_python(
ServiceSummary.model_json_schema()["examples"],
)
if filters:
filtered_summaries = []
for summary in service_summaries:
# Match service type if specified
if (
filters.service_type
and {
ServiceType.COMPUTATIONAL: "/comp/",
ServiceType.DYNAMIC: "/dynamic/",
}[filters.service_type]
not in summary.key
):
continue

# Match service key pattern if specified
if filters.service_key_pattern and not fnmatch.fnmatch(
summary.key, filters.service_key_pattern
):
continue

# Match version display pattern if specified
if filters.version_display_pattern and (
summary.version_display is None
or not fnmatch.fnmatch(
summary.version_display, filters.version_display_pattern
)
):
continue

filtered_summaries.append(summary)

service_summaries = filtered_summaries

total_count = len(service_summaries)

return PageRpc[ServiceSummary].create(
service_summaries[offset : offset + limit],
total=total_count,
limit=limit,
offset=offset,
)


@dataclass
class ZeroListingCatalogRpcSideEffects:
Expand All @@ -216,3 +277,11 @@ async def list_my_service_history_latest_first(self, *args, **kwargs):
limit=10,
offset=0,
)

async def list_all_services_summaries_paginated(self, *args, **kwargs):
return PageRpc[ServiceSummary].create(
[],
total=0,
limit=10,
offset=0,
)
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@
MyServiceGet,
PageRpcLatestServiceGet,
PageRpcServiceRelease,
PageRpcServiceSummary,
ServiceGetV2,
ServiceListFilters,
ServiceRelease,
ServiceSummary,
ServiceUpdateV2,
)
from models_library.api_schemas_catalog.services_ports import ServicePortGet
Expand Down Expand Up @@ -256,3 +258,51 @@ async def get_service_ports(
TypeAdapter(list[ServicePortGet]).validate_python(result) is not None
) # nosec
return cast(list[ServicePortGet], result)


@validate_call(config={"arbitrary_types_allowed": True})
async def list_all_services_summaries_paginated( # pylint: disable=too-many-arguments
rpc_client: RabbitMQRPCClient,
*,
product_name: ProductName,
user_id: UserID,
limit: PageLimitInt = DEFAULT_NUMBER_OF_ITEMS_PER_PAGE,
offset: PageOffsetInt = 0,
filters: ServiceListFilters | None = None,
) -> PageRpcServiceSummary:
"""Lists all services with pagination, including all versions of each service.

Returns a lightweight summary view of services for better performance.

Args:
rpc_client: RPC client instance
product_name: Product name
user_id: User ID
limit: Maximum number of items to return
offset: Number of items to skip
filters: Optional filters to apply

Returns:
Paginated list of all services as summaries

Raises:
ValidationError: on invalid arguments
CatalogForbiddenError: no access-rights to list services
"""
result = await rpc_client.request(
CATALOG_RPC_NAMESPACE,
TypeAdapter(RPCMethodName).validate_python(
list_all_services_summaries_paginated.__name__
),
product_name=product_name,
user_id=user_id,
limit=limit,
offset=offset,
filters=filters,
timeout_s=40 * RPC_REQUEST_DEFAULT_TIMEOUT_S,
)

assert (
TypeAdapter(PageRpc[ServiceSummary]).validate_python(result) is not None
) # nosec
return cast(PageRpc[ServiceSummary], result)
Loading
Loading