diff --git a/packages/service-library/src/servicelib/rabbitmq/rpc_interfaces/webserver/functions/__init__.py b/packages/service-library/src/servicelib/rabbitmq/rpc_interfaces/webserver/functions/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/packages/service-library/src/servicelib/rabbitmq/rpc_interfaces/webserver/functions/functions.py b/packages/service-library/src/servicelib/rabbitmq/rpc_interfaces/webserver/functions/functions.py new file mode 100644 index 000000000000..d53adfafa664 --- /dev/null +++ b/packages/service-library/src/servicelib/rabbitmq/rpc_interfaces/webserver/functions/functions.py @@ -0,0 +1,22 @@ +import logging + +from models_library.api_schemas_webserver import WEBSERVER_RPC_NAMESPACE +from models_library.rabbitmq_basic_types import RPCMethodName +from pydantic import TypeAdapter + +from .....logging_utils import log_decorator +from .....rabbitmq import RabbitMQRPCClient + +_logger = logging.getLogger(__name__) + + +@log_decorator(_logger, level=logging.DEBUG) +async def ping( + rabbitmq_rpc_client: RabbitMQRPCClient, +) -> str: + result = await rabbitmq_rpc_client.request( + WEBSERVER_RPC_NAMESPACE, + TypeAdapter(RPCMethodName).validate_python("ping"), + ) + assert isinstance(result, str) # nosec + return result diff --git a/services/api-server/src/simcore_service_api_server/api/root.py b/services/api-server/src/simcore_service_api_server/api/root.py index d73247b7baf9..c50511dda882 100644 --- a/services/api-server/src/simcore_service_api_server/api/root.py +++ b/services/api-server/src/simcore_service_api_server/api/root.py @@ -6,6 +6,7 @@ from .routes import credits as _credits from .routes import ( files, + functions, health, licensed_items, meta, @@ -44,6 +45,7 @@ def create_router(settings: ApplicationSettings): router.include_router( licensed_items.router, tags=["licensed-items"], prefix="/licensed-items" ) + router.include_router(functions.router, tags=["functions"], prefix="/functions") # NOTE: multiple-files upload is currently disabled # Web form to upload files at http://localhost:8000/v0/upload-form-view diff --git a/services/api-server/src/simcore_service_api_server/api/routes/functions.py b/services/api-server/src/simcore_service_api_server/api/routes/functions.py new file mode 100644 index 000000000000..6d5c277451db --- /dev/null +++ b/services/api-server/src/simcore_service_api_server/api/routes/functions.py @@ -0,0 +1,17 @@ +from typing import Annotated + +from fastapi import APIRouter, Depends + +from ...services_rpc.wb_api_server import WbApiRpcClient +from ..dependencies.webserver_rpc import ( + get_wb_api_rpc_client, +) + +router = APIRouter() + + +@router.post("/ping", include_in_schema=False) +async def ping( + wb_api_rpc: Annotated[WbApiRpcClient, Depends(get_wb_api_rpc_client)], +): + return await wb_api_rpc.ping() diff --git a/services/api-server/src/simcore_service_api_server/services_rpc/wb_api_server.py b/services/api-server/src/simcore_service_api_server/services_rpc/wb_api_server.py index 0cee0a7aef18..3781de9e4b77 100644 --- a/services/api-server/src/simcore_service_api_server/services_rpc/wb_api_server.py +++ b/services/api-server/src/simcore_service_api_server/services_rpc/wb_api_server.py @@ -26,6 +26,9 @@ from servicelib.rabbitmq.rpc_interfaces.resource_usage_tracker.errors import ( NotEnoughAvailableSeatsError, ) +from servicelib.rabbitmq.rpc_interfaces.webserver.functions.functions import ( + ping as _ping, +) from servicelib.rabbitmq.rpc_interfaces.webserver.licenses.licensed_items import ( checkout_licensed_item_for_wallet as _checkout_licensed_item_for_wallet, ) @@ -194,6 +197,9 @@ async def release_licensed_item_for_wallet( num_of_seats=licensed_item_checkout_get.num_of_seats, ) + async def ping(self) -> str: + return await _ping(self._client) + def setup(app: FastAPI, rabbitmq_rmp_client: RabbitMQRPCClient): wb_api_rpc_client = WbApiRpcClient(_client=rabbitmq_rmp_client) diff --git a/services/docker-compose.devel.yml b/services/docker-compose.devel.yml index a9928bc4cdaa..f94001090212 100644 --- a/services/docker-compose.devel.yml +++ b/services/docker-compose.devel.yml @@ -140,6 +140,7 @@ services: WEBSERVER_LOGLEVEL: DEBUG WEBSERVER_PROFILING: ${WEBSERVER_PROFILING} WEBSERVER_REMOTE_DEBUGGING_PORT: 3000 + WEBSERVER_FUNCTIONS: ${WEBSERVER_FUNCTIONS} wb-api-server: diff --git a/services/docker-compose.local.yml b/services/docker-compose.local.yml index 12a2abd44c6b..97fb82732dc0 100644 --- a/services/docker-compose.local.yml +++ b/services/docker-compose.local.yml @@ -136,6 +136,7 @@ services: webserver: environment: &webserver_environment_local <<: *common_environment + WEBSERVER_FUNCTIONS: ${WEBSERVER_FUNCTIONS} ports: - "8080" - "3001:3000" diff --git a/services/storage/openapi.json b/services/storage/openapi.json index b9a9e0021df2..d79f5cf76bd4 100644 --- a/services/storage/openapi.json +++ b/services/storage/openapi.json @@ -3,7 +3,7 @@ "info": { "title": "simcore_service_storage", "description": "Service that manages osparc storage backend", - "version": "0.6.0" + "version": "0.7.0" }, "paths": { "/v0/": { diff --git a/services/web/server/src/simcore_service_webserver/application.py b/services/web/server/src/simcore_service_webserver/application.py index 073ebb0c08b1..5c15845990f3 100644 --- a/services/web/server/src/simcore_service_webserver/application.py +++ b/services/web/server/src/simcore_service_webserver/application.py @@ -22,6 +22,7 @@ from .email.plugin import setup_email from .exporter.plugin import setup_exporter from .folders.plugin import setup_folders +from .functions.plugin import setup_functions from .garbage_collector.plugin import setup_garbage_collector from .groups.plugin import setup_groups from .invitations.plugin import setup_invitations @@ -133,6 +134,9 @@ def create_application() -> web.Application: # folders setup_folders(app) + # functions + setup_functions(app) + # projects setup_projects(app) diff --git a/services/web/server/src/simcore_service_webserver/application_settings.py b/services/web/server/src/simcore_service_webserver/application_settings.py index d1916698b397..38eb7f9fb0ab 100644 --- a/services/web/server/src/simcore_service_webserver/application_settings.py +++ b/services/web/server/src/simcore_service_webserver/application_settings.py @@ -105,6 +105,14 @@ class ApplicationSettings(BaseApplicationSettings, MixinLoggingSettings): bool, Field(description="Enables credit computation features.") ] = False + WEBSERVER_FUNCTIONS: Annotated[ + bool, + Field( + validation_alias=AliasChoices("WEBSERVER_FUNCTIONS"), + json_schema_extra={_X_DEV_FEATURE_FLAG: True}, + ), + ] = False + WEBSERVER_LOGLEVEL: Annotated[ LogLevel, Field( diff --git a/services/web/server/src/simcore_service_webserver/functions/__init__.py b/services/web/server/src/simcore_service_webserver/functions/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/services/web/server/src/simcore_service_webserver/functions/_controller_rpc.py b/services/web/server/src/simcore_service_webserver/functions/_controller_rpc.py new file mode 100644 index 000000000000..483fcdc26e33 --- /dev/null +++ b/services/web/server/src/simcore_service_webserver/functions/_controller_rpc.py @@ -0,0 +1,21 @@ +from aiohttp import web +from models_library.api_schemas_webserver import WEBSERVER_RPC_NAMESPACE +from servicelib.rabbitmq import RPCRouter + +from ..rabbitmq import get_rabbitmq_rpc_server + +# this is the rpc interface exposed to the api-server +# this interface should call the service layer + +router = RPCRouter() + + +@router.expose() +async def ping(app: web.Application) -> str: + assert app + return "pong from webserver" + + +async def register_rpc_routes_on_startup(app: web.Application): + rpc_server = get_rabbitmq_rpc_server(app) + await rpc_server.register_router(router, WEBSERVER_RPC_NAMESPACE, app) diff --git a/services/web/server/src/simcore_service_webserver/functions/_repo.py b/services/web/server/src/simcore_service_webserver/functions/_repo.py new file mode 100644 index 000000000000..baf76a9ee8d4 --- /dev/null +++ b/services/web/server/src/simcore_service_webserver/functions/_repo.py @@ -0,0 +1,2 @@ +# the repository layer. Calls directly to the db (function table) should be done here. +# see e.g. licenses/_licensed_resources_repository.py file for an example diff --git a/services/web/server/src/simcore_service_webserver/functions/_service.py b/services/web/server/src/simcore_service_webserver/functions/_service.py new file mode 100644 index 000000000000..2c967d7c8418 --- /dev/null +++ b/services/web/server/src/simcore_service_webserver/functions/_service.py @@ -0,0 +1,21 @@ +# This is where calls to the business logic is done. +# calls to the `projects` interface should be done here. +# calls to _repo.py should also be done here + +from aiohttp import web +from models_library.users import UserID + +from ..projects import _projects_service +from ..projects.models import ProjectDict + + +# example function +async def get_project_from_function( + app: web.Application, + function_uuid: str, + user_id: UserID, +) -> ProjectDict: + + return await _projects_service.get_project_for_user( + app=app, project_uuid=function_uuid, user_id=user_id + ) diff --git a/services/web/server/src/simcore_service_webserver/functions/plugin.py b/services/web/server/src/simcore_service_webserver/functions/plugin.py new file mode 100644 index 000000000000..364436f4898c --- /dev/null +++ b/services/web/server/src/simcore_service_webserver/functions/plugin.py @@ -0,0 +1,18 @@ +import logging + +from aiohttp import web +from servicelib.aiohttp.application_setup import ModuleCategory, app_module_setup + +from . import _controller_rpc + +_logger = logging.getLogger(__name__) + + +@app_module_setup( + __name__, + ModuleCategory.ADDON, + settings_name="WEBSERVER_FUNCTIONS", + logger=_logger, +) +def setup_functions(app: web.Application): + app.on_startup.append(_controller_rpc.register_rpc_routes_on_startup)