Skip to content

Commit d1c397d

Browse files
authored
Merge branch 'master' into mai/new-changelog-for-apis
2 parents dac49f5 + adc0218 commit d1c397d

File tree

31 files changed

+1194
-688
lines changed

31 files changed

+1194
-688
lines changed

packages/models-library/src/models_library/api_schemas_webserver/projects.py

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
""" rest API schema models for projects
1+
"""rest API schema models for projects
22
33
44
SEE rationale in https://fastapi.tiangolo.com/tutorial/extra-models/#multiple-models
@@ -19,6 +19,7 @@
1919
PlainSerializer,
2020
field_validator,
2121
)
22+
from pydantic.config import JsonDict
2223

2324
from ..api_schemas_long_running_tasks.tasks import TaskGet
2425
from ..basic_types import LongTruncatedStr, ShortTruncatedStr
@@ -53,9 +54,9 @@ class ProjectCreateNew(InputSchema):
5354
access_rights: dict[GroupIDStr, AccessRights]
5455

5556
tags: Annotated[list[int], Field(default_factory=list)] = DEFAULT_FACTORY
56-
classifiers: Annotated[
57-
list[ClassifierID], Field(default_factory=list)
58-
] = DEFAULT_FACTORY
57+
classifiers: Annotated[list[ClassifierID], Field(default_factory=list)] = (
58+
DEFAULT_FACTORY
59+
)
5960

6061
ui: StudyUI | None = None
6162

@@ -143,7 +144,31 @@ class ProjectGet(OutputSchema):
143144
none_to_empty_str_pre_validator
144145
)
145146

146-
model_config = ConfigDict(frozen=False)
147+
@staticmethod
148+
def _update_json_schema_extra(schema: JsonDict) -> None:
149+
schema.update(
150+
examples=[
151+
{
152+
"uuid": "a8b0f384-bd08-4793-ab25-65d5a755f4b6",
153+
"name": "My Project",
154+
"description": "This is a sample project",
155+
"thumbnail": "https://example.com/thumbnail.png",
156+
"workbench": {},
157+
"prj_owner": "[email protected]",
158+
"access_rights": {},
159+
"trashed_at": None,
160+
"trashed_by": None,
161+
"dev": {},
162+
"tags": [],
163+
"workspace_id": None,
164+
"folder_id": None,
165+
"creation_date": "2023-01-01T00:00:00Z",
166+
"last_change_date": "2023-01-02T00:00:00Z",
167+
}
168+
]
169+
)
170+
171+
model_config = ConfigDict(frozen=False, json_schema_extra=_update_json_schema_extra)
147172

148173
@classmethod
149174
def from_domain_model(cls, project_data: dict[str, Any]) -> Self:
@@ -168,8 +193,7 @@ def from_domain_model(cls, project_data: dict[str, Any]) -> Self:
168193
TaskProjectGet: TypeAlias = TaskGet
169194

170195

171-
class ProjectListItem(ProjectGet):
172-
...
196+
class ProjectListItem(ProjectGet): ...
173197

174198

175199
class ProjectReplace(InputSchema):

packages/postgres-database/src/simcore_postgres_database/models/products.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ class Vendor(TypedDict, total=False):
5151
invitation_url: str # How to request a trial invitation? (if applies)
5252
invitation_form: bool # If True, it takes precendence over invitation_url and asks the FE to show the form (if defined)
5353

54-
release_notes_url_template: str # a template url where `{vtag}` will be replaced, eg: "http://example.com/{vtag}.md"
54+
release_notes_url_template: str # a template url where `{vtag}` will be replaced, eg: "https://example.com/{vtag}.md"
5555

5656
ui: VendorUI
5757

services/api-server/docs/api-server.drawio.svg

Lines changed: 285 additions & 166 deletions
Loading

services/api-server/openapi.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2365,7 +2365,7 @@
23652365
"solvers"
23662366
],
23672367
"summary": "List Jobs",
2368-
"description": "\ud83d\udea8 **Deprecated**: This endpoint is deprecated and will be removed in a future release.\nPlease use `GET /{solver_key}/releases/{version}/jobs/page` instead.\n\n\n\nList of jobs in a specific released solver\n\nNew in *version 0.5*\n\nRemoved in *version 0.7*: This endpoint is deprecated and will be removed in a future version",
2368+
"description": "\ud83d\udea8 **Deprecated**: This endpoint is deprecated and will be removed in a future release.\nPlease use `GET /{solver_key}/releases/{version}/jobs/page` instead.\n\n\n\nList of jobs in a specific released solver (limited to 20 jobs)\n\nNew in *version 0.5*\n\nRemoved in *version 0.7*: This endpoint is deprecated and will be removed in a future version",
23692369
"operationId": "list_jobs",
23702370
"security": [
23712371
{

services/api-server/src/simcore_service_api_server/_service_job.py renamed to services/api-server/src/simcore_service_api_server/_service_jobs.py

Lines changed: 66 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,27 @@
11
import logging
22
from collections.abc import Callable
3-
from typing import Annotated
3+
from dataclasses import dataclass
44

5-
from fastapi import Depends
65
from models_library.api_schemas_webserver.projects import ProjectCreateNew, ProjectGet
76
from models_library.products import ProductName
87
from models_library.projects import ProjectID
98
from models_library.projects_nodes_io import NodeID
9+
from models_library.rest_pagination import (
10+
MAXIMUM_NUMBER_OF_ITEMS_PER_PAGE,
11+
PageMetaInfoLimitOffset,
12+
PageOffsetInt,
13+
)
14+
from models_library.rpc_pagination import PageLimitInt
1015
from models_library.users import UserID
1116
from pydantic import HttpUrl
12-
from servicelib.fastapi.app_state import SingletonInAppStateMixin
1317
from servicelib.logging_utils import log_context
1418

15-
from .api.dependencies.authentication import (
16-
get_current_user_id,
17-
get_product_name,
18-
)
19-
from .api.dependencies.webserver_http import get_webserver_session
20-
from .api.dependencies.webserver_rpc import (
21-
get_wb_api_rpc_client,
22-
)
2319
from .models.schemas.jobs import Job, JobInputs
2420
from .models.schemas.programs import Program
2521
from .models.schemas.solvers import Solver
2622
from .services_http.solver_job_models_converters import (
2723
create_job_from_project,
24+
create_job_inputs_from_node_inputs,
2825
create_new_project_for_job,
2926
)
3027
from .services_http.webserver import AuthSession
@@ -33,25 +30,60 @@
3330
_logger = logging.getLogger(__name__)
3431

3532

36-
class JobService(SingletonInAppStateMixin):
37-
app_state_name = "JobService"
38-
_web_rest_api: AuthSession
39-
_web_rpc_api: WbApiRpcClient
40-
_user_id: UserID
41-
_product_name: ProductName
33+
@dataclass(frozen=True, kw_only=True)
34+
class JobService:
35+
_web_rest_client: AuthSession
36+
_web_rpc_client: WbApiRpcClient
37+
user_id: UserID
38+
product_name: ProductName
4239

43-
def __init__(
40+
async def list_jobs(
4441
self,
4542
*,
46-
web_rest_api: Annotated[AuthSession, Depends(get_webserver_session)],
47-
web_rpc_api: Annotated[WbApiRpcClient, Depends(get_wb_api_rpc_client)],
48-
user_id: Annotated[UserID, Depends(get_current_user_id)],
49-
product_name: Annotated[ProductName, Depends(get_product_name)],
50-
):
51-
self._web_rest_api = web_rest_api
52-
self._web_rpc_api = web_rpc_api
53-
self._user_id = user_id
54-
self._product_name = product_name
43+
filter_by_job_parent_resource_name_prefix: str,
44+
pagination_offset: PageOffsetInt = 0,
45+
pagination_limit: PageLimitInt = MAXIMUM_NUMBER_OF_ITEMS_PER_PAGE - 1,
46+
) -> tuple[list[Job], PageMetaInfoLimitOffset]:
47+
"""Lists all jobs for a user with pagination based on resource name prefix"""
48+
49+
# 1. List projects marked as jobs
50+
projects_page = await self._web_rpc_client.list_projects_marked_as_jobs(
51+
product_name=self.product_name,
52+
user_id=self.user_id,
53+
offset=pagination_offset,
54+
limit=pagination_limit,
55+
job_parent_resource_name_prefix=filter_by_job_parent_resource_name_prefix,
56+
)
57+
58+
# 2. Convert projects to jobs
59+
jobs: list[Job] = []
60+
for project_job in projects_page.data:
61+
assert ( # nosec
62+
len(project_job.workbench) == 1
63+
), "Expected only one solver node in workbench"
64+
65+
solver_node = next(iter(project_job.workbench.values()))
66+
job_inputs: JobInputs = create_job_inputs_from_node_inputs(
67+
inputs=solver_node.inputs or {}
68+
)
69+
assert project_job.job_parent_resource_name # nosec
70+
71+
jobs.append(
72+
Job(
73+
id=project_job.uuid,
74+
name=Job.compose_resource_name(
75+
project_job.job_parent_resource_name, project_job.uuid
76+
),
77+
inputs_checksum=job_inputs.compute_checksum(),
78+
created_at=project_job.created_at,
79+
runner_name=project_job.job_parent_resource_name,
80+
url=None,
81+
runner_url=None,
82+
outputs_url=None,
83+
)
84+
)
85+
86+
return jobs, projects_page.meta
5587

5688
async def create_job(
5789
self,
@@ -65,8 +97,10 @@ async def create_job(
6597
project_name: str | None,
6698
description: str | None,
6799
) -> tuple[Job, ProjectGet]:
68-
"""If no project_name is provided, the job name is used as project name."""
100+
"""If no project_name is provided, the job name is used as project name"""
101+
69102
# creates NEW job as prototype
103+
70104
pre_job = Job.create_job_from_solver_or_program(
71105
solver_or_program_name=solver_or_program.name, inputs=inputs
72106
)
@@ -80,15 +114,15 @@ async def create_job(
80114
description=description,
81115
project_name=project_name,
82116
)
83-
new_project: ProjectGet = await self._web_rest_api.create_project(
117+
new_project: ProjectGet = await self._web_rest_client.create_project(
84118
project_in,
85119
is_hidden=hidden,
86120
parent_project_uuid=parent_project_uuid,
87121
parent_node_id=parent_node_id,
88122
)
89-
await self._web_rpc_api.mark_project_as_job(
90-
product_name=self._product_name,
91-
user_id=self._user_id,
123+
await self._web_rpc_client.mark_project_as_job(
124+
product_name=self.product_name,
125+
user_id=self.user_id,
92126
project_uuid=new_project.uuid,
93127
job_parent_resource_name=pre_job.runner_name,
94128
)

services/api-server/src/simcore_service_api_server/_service_programs.py

Lines changed: 14 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
from typing import Annotated
1+
from dataclasses import dataclass
22

3-
from fastapi import Depends
43
from models_library.api_schemas_catalog.services import ServiceListFilters
54
from models_library.basic_types import VersionStr
65
from models_library.rest_pagination import PageMetaInfoLimitOffset
@@ -11,25 +10,19 @@
1110
from .services_rpc.catalog import CatalogService
1211

1312

13+
@dataclass(frozen=True, kw_only=True)
1414
class ProgramService:
15-
_catalog_service: CatalogService
16-
17-
def __init__(self, _catalog_service: Annotated[CatalogService, Depends()]):
18-
self._catalog_service = _catalog_service
15+
catalog_service: CatalogService
1916

2017
async def get_program(
2118
self,
2219
*,
23-
user_id: int,
2420
name: ProgramKeyId,
2521
version: VersionStr,
26-
product_name: str,
2722
) -> Program:
28-
service = await self._catalog_service.get(
29-
user_id=user_id,
23+
service = await self.catalog_service.get(
3024
name=name,
3125
version=version,
32-
product_name=product_name,
3326
)
3427
assert service.service_type == ServiceType.DYNAMIC # nosec
3528

@@ -38,16 +31,12 @@ async def get_program(
3831
async def list_latest_programs(
3932
self,
4033
*,
41-
user_id: int,
42-
product_name: str,
43-
offset: NonNegativeInt,
44-
limit: PositiveInt,
34+
pagination_offset: NonNegativeInt,
35+
pagination_limit: PositiveInt,
4536
) -> tuple[list[Program], PageMetaInfoLimitOffset]:
46-
page, page_meta = await self._catalog_service.list_latest_releases(
47-
user_id=user_id,
48-
product_name=product_name,
49-
offset=offset,
50-
limit=limit,
37+
page, page_meta = await self.catalog_service.list_latest_releases(
38+
pagination_offset=pagination_offset,
39+
pagination_limit=pagination_limit,
5140
filters=ServiceListFilters(service_type=ServiceType.DYNAMIC),
5241
)
5342

@@ -57,27 +46,21 @@ async def list_latest_programs(
5746
async def list_program_history(
5847
self,
5948
*,
60-
user_id: int,
6149
program_key: ProgramKeyId,
62-
product_name: str,
6350
offset: NonNegativeInt,
6451
limit: PositiveInt,
6552
) -> tuple[list[Program], PageMetaInfoLimitOffset]:
66-
page, page_meta = await self._catalog_service.list_release_history_latest_first(
67-
user_id=user_id,
68-
service_key=program_key,
69-
product_name=product_name,
70-
offset=offset,
71-
limit=limit,
53+
page, page_meta = await self.catalog_service.list_release_history_latest_first(
54+
filter_by_service_key=program_key,
55+
pagination_offset=offset,
56+
pagination_limit=limit,
7257
)
7358
if len(page) == 0:
7459
return [], page_meta
7560

76-
program_instance = await self._catalog_service.get(
77-
user_id=user_id,
61+
program_instance = await self.catalog_service.get(
7862
name=program_key,
7963
version=page[-1].version,
80-
product_name=product_name,
8164
)
8265

8366
items = [

0 commit comments

Comments
 (0)