Skip to content
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
5dfa550
_service.py -> service_jobs.py
bisgaard-itis Apr 14, 2025
e894814
start program service
bisgaard-itis Apr 14, 2025
9923b39
create program service
bisgaard-itis Apr 15, 2025
be7bff6
add unit test for getting program
bisgaard-itis Apr 15, 2025
ca5e159
cleanup function arguments
bisgaard-itis Apr 15, 2025
9e7bd41
introduce job_service and 'constuct' service layers in dependency inj…
bisgaard-itis Apr 15, 2025
03c5eb3
start adding test for creating job
bisgaard-itis Apr 15, 2025
0cf0dd1
minor changes
bisgaard-itis Apr 15, 2025
d94caa7
add legacy tasks to webserver
bisgaard-itis Apr 15, 2025
b744dd0
mock webserver part of job creation test
bisgaard-itis Apr 15, 2025
5c06e9c
make create job test pass
bisgaard-itis Apr 15, 2025
09c9507
use dependency indenction in class constructor
bisgaard-itis Apr 16, 2025
ec6a680
use dependency injection in JobService constructor
bisgaard-itis Apr 16, 2025
93b9966
minor changes
bisgaard-itis Apr 16, 2025
aac464c
fix tests
bisgaard-itis Apr 16, 2025
e6e6baa
remove get_solver method from http catalog client
bisgaard-itis Apr 16, 2025
65916c4
add test for getting latest solver
bisgaard-itis Apr 16, 2025
f51e7c7
Merge branch 'master' into 7525-use-catalog-rpc-client
bisgaard-itis Apr 16, 2025
d07461e
make pylint happy
bisgaard-itis Apr 16, 2025
52a97f2
fix webserver openapi test
bisgaard-itis Apr 16, 2025
105b498
Revert "fix webserver openapi test"
bisgaard-itis Apr 16, 2025
d67c526
Revert "add legacy tasks to webserver"
bisgaard-itis Apr 16, 2025
35f8097
add exception handling in CatalogService
bisgaard-itis Apr 16, 2025
b69c304
add error handling to CatalogService
bisgaard-itis Apr 16, 2025
27ec715
@pcrespov clean up JobService
bisgaard-itis Apr 16, 2025
086f9ea
Merge branch 'master' into 7525-use-catalog-rpc-client
bisgaard-itis Apr 17, 2025
fc9245f
add Deprecated note @pcrespov
bisgaard-itis Apr 17, 2025
7318dc1
add deprecation warnings to all listing endpoints
bisgaard-itis Apr 17, 2025
9f591b1
fixes to deprecation warnings
bisgaard-itis Apr 17, 2025
c6ee0c8
remove listing programs endpoint
bisgaard-itis Apr 17, 2025
06a98bc
Merge branch 'master' into 7525-use-catalog-rpc-client
bisgaard-itis Apr 17, 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
61 changes: 61 additions & 0 deletions api/specs/web-server/_long_running_tasks_legacy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# pylint: disable=redefined-outer-name
# pylint: disable=unused-argument
# pylint: disable=unused-variable
# pylint: disable=too-many-arguments


from typing import Annotated

from fastapi import APIRouter, Depends, status
from models_library.generics import Envelope
from servicelib.aiohttp.long_running_tasks._routes import _PathParam
from servicelib.long_running_tasks._models import TaskGet, TaskStatus
from simcore_service_webserver._meta import API_VTAG

router = APIRouter(
prefix=f"/{API_VTAG}/tasks-legacy",
tags=[
"long-running-tasks-legacy",
],
)


@router.get(
"",
response_model=Envelope[list[TaskGet]],
name="list_tasks",
description="Lists all long running tasks",
)
def list_tasks(): ...


@router.get(
"/{task_id}",
response_model=Envelope[TaskStatus],
name="get_task_status",
description="Retrieves the status of a task",
)
def get_task_status(
_path_params: Annotated[_PathParam, Depends()],
): ...


@router.delete(
"/{task_id}",
name="cancel_and_delete_task",
description="Cancels and deletes a task",
status_code=status.HTTP_204_NO_CONTENT,
)
def cancel_and_delete_task(
_path_params: Annotated[_PathParam, Depends()],
): ...


@router.get(
"/{task_id}/result",
name="get_task_result",
description="Retrieves the result of a task",
)
def get_task_result(
_path_params: Annotated[_PathParam, Depends()],
): ...
1 change: 1 addition & 0 deletions api/specs/web-server/openapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"_exporter",
"_folders",
"_long_running_tasks",
"_long_running_tasks_legacy",
"_licensed_items",
"_licensed_items_purchases",
"_licensed_items_checkouts",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,12 @@
from models_library.products import ProductName
from models_library.rest_pagination import PageOffsetInt
from models_library.rpc_pagination import PageLimitInt, PageRpc
from models_library.services_enums import ServiceType
from models_library.services_history import ServiceRelease
from models_library.services_regex import (
COMPUTATIONAL_SERVICE_KEY_RE,
DYNAMIC_SERVICE_KEY_RE,
)
from models_library.services_types import ServiceKey, ServiceVersion
from models_library.users import UserID
from pydantic import NonNegativeInt, TypeAdapter
Expand Down Expand Up @@ -65,6 +70,14 @@ async def get_service(
got.version = service_version
got.key = service_key

if DYNAMIC_SERVICE_KEY_RE.match(got.key):
got.service_type = ServiceType.DYNAMIC
elif COMPUTATIONAL_SERVICE_KEY_RE.match(got.key):
got.service_type = ServiceType.COMPUTATIONAL
else:
msg = "Service type not recognized. Please extend the mock yourself"
raise RuntimeError(msg)

return got

async def update_service(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@

class CapturedParameterSchema(BaseModel):
title: str | None = None
type_: Literal["str", "int", "float", "bool"] | None = Field(None, alias="type")
type_: Literal["str", "int", "float", "bool", "null"] | None = Field(
None, alias="type"
)
pattern: str | None = None
format_: Literal["uuid"] | None = Field(None, alias="format")
exclusiveMinimum: bool | None = None
Expand Down
58 changes: 0 additions & 58 deletions services/api-server/src/simcore_service_api_server/_service.py

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import logging
from collections.abc import Callable
from typing import Annotated

from fastapi import Depends
from models_library.api_schemas_webserver.projects import ProjectCreateNew, ProjectGet
from models_library.projects import ProjectID
from models_library.projects_nodes_io import NodeID
from pydantic import HttpUrl
from servicelib.fastapi.app_state import SingletonInAppStateMixin

from .api.dependencies.webserver_http import get_webserver_session
from .models.schemas.jobs import Job, JobInputs
from .models.schemas.programs import Program
from .models.schemas.solvers import Solver
from .services_http.solver_job_models_converters import (
create_job_from_project,
create_new_project_for_job,
)
from .services_http.webserver import AuthSession

_logger = logging.getLogger(__name__)


class JobService(SingletonInAppStateMixin):
app_state_name = "JobService"
_webserver_api: AuthSession

def __init__(
self, webserver_api: Annotated[AuthSession, Depends(get_webserver_session)]
):
self._webserver_api = webserver_api

async def create_job(
self,
*,
solver_or_program: Solver | Program,
inputs: JobInputs,
parent_project_uuid: ProjectID | None,
parent_node_id: NodeID | None,
url_for: Callable[..., HttpUrl],
hidden: bool
) -> tuple[Job, ProjectGet]:
# creates NEW job as prototype
pre_job = Job.create_job_from_solver_or_program(
solver_or_program_name=solver_or_program.name, inputs=inputs
)
_logger.debug("Creating Job '%s'", pre_job.name)

project_in: ProjectCreateNew = create_new_project_for_job(
solver_or_program, pre_job, inputs
)
new_project: ProjectGet = await self._webserver_api.create_project(
project_in,
is_hidden=hidden,
parent_project_uuid=parent_project_uuid,
parent_node_id=parent_node_id,
)
assert new_project # nosec
assert new_project.uuid == pre_job.id # nosec

# for consistency, it rebuild job
job = create_job_from_project(
solver_or_program=solver_or_program, project=new_project, url_for=url_for
)
assert job.id == pre_job.id # nosec
assert job.name == pre_job.name # nosec
assert job.name == Job.compose_resource_name(
parent_name=solver_or_program.resource_name,
job_id=job.id,
)
return job, new_project
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from typing import Annotated

from fastapi import Depends
from models_library.basic_types import VersionStr
from models_library.services_enums import ServiceType

from .models.schemas.programs import Program, ProgramKeyId
from .services_rpc.catalog import CatalogService


class ProgramService:
_catalog_service: CatalogService

def __init__(
self, _catalog_service: Annotated[CatalogService, Depends(CatalogService)]
):
self._catalog_service = _catalog_service

async def get_program(
self,
*,
user_id: int,
name: ProgramKeyId,
version: VersionStr,
product_name: str,
) -> Program:
service = await self._catalog_service.get(
user_id=user_id,
service_key=name,
service_version=version,
product_name=product_name,
)
assert service.service_type == ServiceType.DYNAMIC # nosec

return Program.create_from_service(service)
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
from typing import Annotated

from common_library.pagination_tools import iter_pagination_params
from fastapi import Depends
from models_library.basic_types import VersionStr
from models_library.products import ProductName
from models_library.rest_pagination import MAXIMUM_NUMBER_OF_ITEMS_PER_PAGE
from models_library.services_enums import ServiceType
from models_library.services_history import ServiceRelease
from models_library.users import UserID
from packaging.version import Version

from .models.schemas.solvers import Solver, SolverKeyId
from .services_rpc.catalog import CatalogService

DEFAULT_PAGINATION_LIMIT = MAXIMUM_NUMBER_OF_ITEMS_PER_PAGE - 1


class SolverService:
_catalog_service: CatalogService

def __init__(
self, catalog_service: Annotated[CatalogService, Depends(CatalogService)]
):
self._catalog_service = catalog_service

async def get_solver(
self,
*,
user_id: UserID,
name: SolverKeyId,
version: VersionStr,
product_name: ProductName,
) -> Solver:
service = await self._catalog_service.get(
user_id=user_id,
service_key=name,
service_version=version,
product_name=product_name,
)
assert ( # nosec
service.service_type == ServiceType.COMPUTATIONAL
), "Expected by SolverName regex"

return Solver.create_from_service(service)

async def get_latest_release(
self,
*,
user_id: int,
solver_key: SolverKeyId,
product_name: str,
) -> Solver:
service_releases: list[ServiceRelease] = []
for page_params in iter_pagination_params(limit=DEFAULT_PAGINATION_LIMIT):
releases, page_meta = await self._catalog_service.list_release_history(
user_id=user_id,
service_key=solver_key,
product_name=product_name,
offset=page_params.offset,
limit=page_params.limit,
)
page_params.total_number_of_items = page_meta.total
service_releases.extend(releases)

release = sorted(service_releases, key=lambda s: Version(s.version))[-1]
service = await self._catalog_service.get(
user_id=user_id,
service_key=solver_key,
service_version=release.version,
product_name=product_name,
)

return Solver.create_from_service(service)
Loading
Loading