Skip to content

♻️ Refactor catalog domain in webserver #7308

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
cbc06aa
use service terminology
bisgaard-itis Mar 4, 2025
75515cf
further refactoring
bisgaard-itis Mar 4, 2025
da6debd
make the catalog rest client private
bisgaard-itis Mar 4, 2025
8fd6b61
make the catalog rest client private
bisgaard-itis Mar 4, 2025
4b1bc03
make the catalog rest client private
bisgaard-itis Mar 4, 2025
f2ea112
make the catalog rest client privat
bisgaard-itis Mar 4, 2025
560a2ee
remove _handler_exceptions file
bisgaard-itis Mar 4, 2025
16f89d7
start adding test for getting inputs REST endpoint
bisgaard-itis Mar 4, 2025
ba066aa
add response data to call to catalog
bisgaard-itis Mar 4, 2025
4a23dce
improve unit test
bisgaard-itis Mar 5, 2025
35663ca
add get_inputs_test
bisgaard-itis Mar 5, 2025
73edc8c
add test for checking input-output compatibility
bisgaard-itis Mar 5, 2025
456c638
add test for matching output endpoints
bisgaard-itis Mar 5, 2025
997b55d
add tests directly for catalog rest client
bisgaard-itis Mar 5, 2025
039e14f
add test for getting access rights
bisgaard-itis Mar 5, 2025
476657e
add test for exceptions
bisgaard-itis Mar 5, 2025
c826a6d
merge master into refactor-catalog-domain-in-webserver
bisgaard-itis Mar 5, 2025
ec359b0
Merge branch 'master' into refactor-catalog-domain-in-webserver
bisgaard-itis Mar 6, 2025
3a1d60f
@pcrespov models.py -> controller_rest_schemas.py
bisgaard-itis Mar 6, 2025
3de0572
@pcrespov import from controller_rest_schemas
bisgaard-itis Mar 6, 2025
5eec0f5
improve file namings
bisgaard-itis Mar 6, 2025
2678c71
minor correction
bisgaard-itis Mar 6, 2025
5cace5b
@pcrespov use new exception handling system
bisgaard-itis Mar 6, 2025
16b56ee
make pylint happy
bisgaard-itis Mar 6, 2025
4d53197
@pcrespov _service_api_units.py -> _service_units.py
bisgaard-itis Mar 6, 2025
9bc18ff
ddd instead of sa @matusdrobuliak66 @pcrespov
bisgaard-itis Mar 6, 2025
2925ec3
merge master into refactor-catalog-domain-in-webserver
bisgaard-itis Mar 6, 2025
6cb01a1
fix typecheck
bisgaard-itis Mar 6, 2025
19c51f1
fix further tests
bisgaard-itis Mar 6, 2025
28b1544
fix mocks
bisgaard-itis Mar 6, 2025
7e56849
fix tests
bisgaard-itis Mar 6, 2025
608ef23
Merge branch 'master' into refactor-catalog-domain-in-webserver
bisgaard-itis Mar 6, 2025
79ad133
further fix to tests
bisgaard-itis Mar 6, 2025
9d55786
fix yet another test
bisgaard-itis Mar 6, 2025
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
2 changes: 1 addition & 1 deletion api/specs/web-server/_catalog.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from models_library.generics import Envelope
from models_library.rest_pagination import Page
from simcore_service_webserver._meta import API_VTAG
from simcore_service_webserver.catalog._handlers import (
from simcore_service_webserver.catalog._rest_controller import (
ListServiceParams,
ServicePathParams,
_FromServiceOutputParams,
Expand Down
11 changes: 4 additions & 7 deletions api/specs/web-server/_catalog_tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from models_library.api_schemas_webserver.catalog import CatalogServiceGet
from models_library.generics import Envelope
from simcore_service_webserver._meta import API_VTAG
from simcore_service_webserver.catalog._tags_handlers import (
from simcore_service_webserver.catalog._rest_tags_controller import (
ServicePathParams,
ServiceTagPathParams,
)
Expand All @@ -31,8 +31,7 @@
)
def list_service_tags(
_path_params: Annotated[ServicePathParams, Depends()],
):
...
): ...


@router.post(
Expand All @@ -41,8 +40,7 @@ def list_service_tags(
)
def add_service_tag(
_path_params: Annotated[ServiceTagPathParams, Depends()],
):
...
): ...


@router.post(
Expand All @@ -51,5 +49,4 @@ def add_service_tag(
)
def remove_service_tag(
_path_params: Annotated[ServiceTagPathParams, Depends()],
):
...
): ...
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
""" Requests to catalog service API
"""Requests to catalog service API"""

"""
import asyncio
import logging
import urllib.parse
from collections.abc import Iterator
Expand Down Expand Up @@ -47,7 +45,7 @@ def _handle_client_exceptions(app: web.Application) -> Iterator[ClientSession]:
reason=MSG_CATALOG_SERVICE_UNAVAILABLE
) from err

except (asyncio.TimeoutError, ClientConnectionError) as err:
except (TimeoutError, ClientConnectionError) as err:
_logger.debug("Request to catalog service failed: %s", err)
raise web.HTTPServiceUnavailable(
reason=MSG_CATALOG_SERVICE_UNAVAILABLE
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
"""Defines the different exceptions that may arise in the catalog subpackage"""

from servicelib.aiohttp import status
from servicelib.rabbitmq.rpc_interfaces.catalog.errors import (
CatalogForbiddenError,
CatalogItemNotFoundError,
)

from ..errors import WebServerBaseError
from ..exception_handling import (
ExceptionToHttpErrorMap,
HttpErrorInfo,
exception_handling_decorator,
to_exceptions_handlers_map,
)
from ..resource_usage.errors import DefaultPricingPlanNotFoundError


class BaseCatalogError(WebServerBaseError):
Expand Down Expand Up @@ -35,6 +43,28 @@ def __init__(self, *, service_key: str, service_version: str, **ctxs):
assert CatalogItemNotFoundError # nosec


_TO_HTTP_ERROR_MAP: ExceptionToHttpErrorMap = {
CatalogItemNotFoundError: HttpErrorInfo(
status.HTTP_404_NOT_FOUND,
"Catalog item not found",
),
DefaultPricingPlanNotFoundError: HttpErrorInfo(
status.HTTP_404_NOT_FOUND,
"Default pricing plan not found",
),
DefaultPricingUnitForServiceNotFoundError: HttpErrorInfo(
status.HTTP_404_NOT_FOUND, "Default pricing unit not found"
),
CatalogForbiddenError: HttpErrorInfo(
status.HTTP_403_FORBIDDEN, "Forbidden catalog access"
),
}

handle_plugin_requests_exceptions = exception_handling_decorator(
to_exceptions_handlers_map(_TO_HTTP_ERROR_MAP)
)


__all__: tuple[str, ...] = (
"CatalogForbiddenError",
"CatalogItemNotFoundError",
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
""" rest api handlers
"""rest api handlers

- Take into account that part of the API is also needed in the public API so logic
should live in the catalog service in his final version
Expand All @@ -7,7 +7,6 @@

import asyncio
import logging
import urllib.parse
from typing import Final

from aiohttp import web
Expand All @@ -26,7 +25,7 @@
ServiceResourcesDict,
ServiceResourcesDictHelpers,
)
from pydantic import BaseModel, ConfigDict, Field, field_validator
from pydantic import BaseModel, Field
from servicelib.aiohttp.requests_validation import (
parse_request_body_as,
parse_request_path_parameters_as,
Expand All @@ -39,9 +38,16 @@
from ..resource_usage.service import get_default_service_pricing_plan
from ..security.decorators import permission_required
from ..utils_aiohttp import envelope_json_response
from . import _api, _handlers_errors, client
from ._api import CatalogRequestContext
from .exceptions import DefaultPricingUnitForServiceNotFoundError
from . import _catalog_rest_client_service, _service
from ._exceptions import (
DefaultPricingUnitForServiceNotFoundError,
handle_plugin_requests_exceptions,
)
from .controller_rest_schemas import (
CatalogRequestContext,
ListServiceParams,
ServicePathParams,
)

_logger = logging.getLogger(__name__)

Expand All @@ -51,41 +57,20 @@
routes = RouteTableDef()


class ServicePathParams(BaseModel):
service_key: ServiceKey
service_version: ServiceVersion
model_config = ConfigDict(
populate_by_name=True,
extra="forbid",
)

@field_validator("service_key", mode="before")
@classmethod
def ensure_unquoted(cls, v):
# NOTE: this is needed as in pytest mode, the aiohttp server does not seem to unquote automatically
if v is not None:
return urllib.parse.unquote(v)
return v


class ListServiceParams(PageQueryParameters):
...


@routes.get(
f"{VTAG}/catalog/services/-/latest",
name="list_services_latest",
)
@login_required
@permission_required("services.catalog.*")
@_handlers_errors.reraise_catalog_exceptions_as_http_errors
@handle_plugin_requests_exceptions
async def list_services_latest(request: Request):
request_ctx = CatalogRequestContext.create(request)
query_params: ListServiceParams = parse_request_query_parameters_as(
ListServiceParams, request
)

page_items, page_meta = await _api.list_latest_services(
page_items, page_meta = await _service.list_latest_services(
request.app,
user_id=request_ctx.user_id,
product_name=request_ctx.product_name,
Expand Down Expand Up @@ -116,15 +101,15 @@ async def list_services_latest(request: Request):
)
@login_required
@permission_required("services.catalog.*")
@_handlers_errors.reraise_catalog_exceptions_as_http_errors
@handle_plugin_requests_exceptions
async def get_service(request: Request):
request_ctx = CatalogRequestContext.create(request)
path_params = parse_request_path_parameters_as(ServicePathParams, request)

assert request_ctx # nosec
assert path_params # nosec

service = await _api.get_service_v2(
service = await _service.get_service_v2(
request.app,
user_id=request_ctx.user_id,
product_name=request_ctx.product_name,
Expand All @@ -142,7 +127,7 @@ async def get_service(request: Request):
)
@login_required
@permission_required("services.catalog.*")
@_handlers_errors.reraise_catalog_exceptions_as_http_errors
@handle_plugin_requests_exceptions
async def update_service(request: Request):
request_ctx = CatalogRequestContext.create(request)
path_params = parse_request_path_parameters_as(ServicePathParams, request)
Expand All @@ -154,7 +139,7 @@ async def update_service(request: Request):
assert path_params # nosec
assert update # nosec

updated = await _api.update_service_v2(
updated = await _service.update_service_v2(
request.app,
user_id=request_ctx.user_id,
product_name=request_ctx.product_name,
Expand All @@ -178,7 +163,7 @@ async def list_service_inputs(request: Request):
path_params = parse_request_path_parameters_as(ServicePathParams, request)

# Evaluate and return validated model
response_model = await _api.list_service_inputs(
response_model = await _service.list_service_inputs(
path_params.service_key, path_params.service_version, ctx
)

Expand All @@ -203,7 +188,7 @@ async def get_service_input(request: Request):
path_params = parse_request_path_parameters_as(_ServiceInputsPathParams, request)

# Evaluate and return validated model
response_model = await _api.get_service_input(
response_model = await _service.get_service_input(
path_params.service_key,
path_params.service_version,
path_params.input_key,
Expand Down Expand Up @@ -236,7 +221,7 @@ async def get_compatible_inputs_given_source_output(request: Request):
)

# Evaluate and return validated model
data = await _api.get_compatible_inputs_given_source_output(
data = await _service.get_compatible_inputs_given_source_output(
path_params.service_key,
path_params.service_version,
query_params.from_service_key,
Expand All @@ -261,7 +246,7 @@ async def list_service_outputs(request: Request):
path_params = parse_request_path_parameters_as(ServicePathParams, request)

# Evaluate and return validated model
response_model = await _api.list_service_outputs(
response_model = await _service.list_service_outputs(
path_params.service_key, path_params.service_version, ctx
)

Expand All @@ -286,7 +271,7 @@ async def get_service_output(request: Request):
path_params = parse_request_path_parameters_as(_ServiceOutputsPathParams, request)

# Evaluate and return validated model
response_model = await _api.get_service_output(
response_model = await _service.get_service_output(
path_params.service_key,
path_params.service_version,
path_params.output_key,
Expand Down Expand Up @@ -323,7 +308,7 @@ async def get_compatible_outputs_given_target_input(request: Request):
_ToServiceInputsParams, request
)

data = await _api.get_compatible_outputs_given_target_input(
data = await _service.get_compatible_outputs_given_target_input(
path_params.service_key,
path_params.service_version,
query_params.to_service_key,
Expand Down Expand Up @@ -351,11 +336,13 @@ async def get_service_resources(request: Request):
"""
ctx = CatalogRequestContext.create(request)
path_params = parse_request_path_parameters_as(ServicePathParams, request)
service_resources: ServiceResourcesDict = await client.get_service_resources(
request.app,
user_id=ctx.user_id,
service_key=path_params.service_key,
service_version=path_params.service_version,
service_resources: ServiceResourcesDict = (
await _catalog_rest_client_service.get_service_resources(
request.app,
user_id=ctx.user_id,
service_key=path_params.service_key,
service_version=path_params.service_version,
)
)

data = ServiceResourcesDictHelpers.create_jsonable(service_resources)
Expand All @@ -370,7 +357,7 @@ async def get_service_resources(request: Request):
)
@login_required
@permission_required("services.catalog.*")
@_handlers_errors.reraise_catalog_exceptions_as_http_errors
@handle_plugin_requests_exceptions
async def get_service_pricing_plan(request: Request):
ctx = CatalogRequestContext.create(request)
path_params = parse_request_path_parameters_as(ServicePathParams, request)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,16 @@
import logging

from aiohttp import web
from models_library.basic_types import IdInt
from servicelib.aiohttp.requests_validation import parse_request_path_parameters_as

from .._meta import API_VTAG
from ..login.decorators import login_required
from ..security.decorators import permission_required
from ._handlers import ServicePathParams
from .controller_rest_schemas import ServicePathParams, ServiceTagPathParams

_logger = logging.getLogger(__name__)


class ServiceTagPathParams(ServicePathParams):
tag_id: IdInt


routes = web.RouteTableDef()


Expand Down
Loading
Loading