Skip to content

Commit 2f98a0b

Browse files
🎨 Program jobs: Pass metadata and return display version (#7562)
1 parent 7ea3b05 commit 2f98a0b

File tree

10 files changed

+200
-59
lines changed

10 files changed

+200
-59
lines changed

services/api-server/openapi.json

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1321,7 +1321,7 @@
13211321
"programs"
13221322
],
13231323
"summary": "Create Program Job",
1324-
"description": "Creates a job in a specific release with given inputs.\n\nNOTE: This operation does **not** start the job",
1324+
"description": "Creates a program job",
13251325
"operationId": "create_program_job",
13261326
"security": [
13271327
{
@@ -1384,6 +1384,15 @@
13841384
}
13851385
}
13861386
],
1387+
"requestBody": {
1388+
"content": {
1389+
"application/json": {
1390+
"schema": {
1391+
"$ref": "#/components/schemas/Body_create_program_job_v0_programs__program_key__releases__version__jobs_post"
1392+
}
1393+
}
1394+
}
1395+
},
13871396
"responses": {
13881397
"201": {
13891398
"description": "Successful Response",
@@ -6056,6 +6065,36 @@
60566065
],
60576066
"title": "Body_complete_multipart_upload_v0_files__file_id__complete_post"
60586067
},
6068+
"Body_create_program_job_v0_programs__program_key__releases__version__jobs_post": {
6069+
"properties": {
6070+
"name": {
6071+
"anyOf": [
6072+
{
6073+
"type": "string",
6074+
"maxLength": 500
6075+
},
6076+
{
6077+
"type": "null"
6078+
}
6079+
],
6080+
"title": "Name"
6081+
},
6082+
"description": {
6083+
"anyOf": [
6084+
{
6085+
"type": "string",
6086+
"maxLength": 500
6087+
},
6088+
{
6089+
"type": "null"
6090+
}
6091+
],
6092+
"title": "Description"
6093+
}
6094+
},
6095+
"type": "object",
6096+
"title": "Body_create_program_job_v0_programs__program_key__releases__version__jobs_post"
6097+
},
60596098
"Body_upload_file_v0_files_content_put": {
60606099
"properties": {
60616100
"file": {
@@ -7715,14 +7754,26 @@
77157754
],
77167755
"title": "Url",
77177756
"description": "Link to get this resource"
7757+
},
7758+
"version_display": {
7759+
"anyOf": [
7760+
{
7761+
"type": "string"
7762+
},
7763+
{
7764+
"type": "null"
7765+
}
7766+
],
7767+
"title": "Version Display"
77187768
}
77197769
},
77207770
"type": "object",
77217771
"required": [
77227772
"id",
77237773
"version",
77247774
"title",
7725-
"url"
7775+
"url",
7776+
"version_display"
77267777
],
77277778
"title": "Program",
77287779
"description": "A released program with a specific version",
@@ -7732,7 +7783,8 @@
77327783
"maintainer": "info@itis.swiss",
77337784
"title": "Sim4life",
77347785
"url": "https://api.osparc.io/v0/solvers/simcore%2Fservices%2Fdynamic%2Fsim4life/releases/8.0.0",
7735-
"version": "8.0.0"
7786+
"version": "8.0.0",
7787+
"version_display": "8.0.0"
77367788
}
77377789
},
77387790
"RunningState": {

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

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,22 @@
44

55
from fastapi import Depends
66
from models_library.api_schemas_webserver.projects import ProjectCreateNew, ProjectGet
7+
from models_library.products import ProductName
78
from models_library.projects import ProjectID
89
from models_library.projects_nodes_io import NodeID
10+
from models_library.users import UserID
911
from pydantic import HttpUrl
1012
from servicelib.fastapi.app_state import SingletonInAppStateMixin
1113
from servicelib.logging_utils import log_context
1214

15+
from .api.dependencies.authentication import (
16+
get_current_user_id,
17+
get_product_name,
18+
)
1319
from .api.dependencies.webserver_http import get_webserver_session
20+
from .api.dependencies.webserver_rpc import (
21+
get_wb_api_rpc_client,
22+
)
1423
from .models.schemas.jobs import Job, JobInputs
1524
from .models.schemas.programs import Program
1625
from .models.schemas.solvers import Solver
@@ -19,18 +28,30 @@
1928
create_new_project_for_job,
2029
)
2130
from .services_http.webserver import AuthSession
31+
from .services_rpc.wb_api_server import WbApiRpcClient
2232

2333
_logger = logging.getLogger(__name__)
2434

2535

2636
class JobService(SingletonInAppStateMixin):
2737
app_state_name = "JobService"
2838
_web_rest_api: AuthSession
39+
_web_rpc_api: WbApiRpcClient
40+
_user_id: UserID
41+
_product_name: ProductName
2942

3043
def __init__(
31-
self, web_rest_api: Annotated[AuthSession, Depends(get_webserver_session)]
44+
self,
45+
*,
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)],
3250
):
3351
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
3455

3556
async def create_job(
3657
self,
@@ -41,7 +62,10 @@ async def create_job(
4162
parent_node_id: NodeID | None,
4263
url_for: Callable[..., HttpUrl],
4364
hidden: bool,
65+
project_name: str | None,
66+
description: str | None,
4467
) -> tuple[Job, ProjectGet]:
68+
"""If no project_name is provided, the job name is used as project name."""
4569
# creates NEW job as prototype
4670
pre_job = Job.create_job_from_solver_or_program(
4771
solver_or_program_name=solver_or_program.name, inputs=inputs
@@ -50,14 +74,24 @@ async def create_job(
5074
logger=_logger, level=logging.DEBUG, msg=f"Creating job {pre_job.name}"
5175
):
5276
project_in: ProjectCreateNew = create_new_project_for_job(
53-
solver_or_program, pre_job, inputs
77+
solver_or_program=solver_or_program,
78+
job=pre_job,
79+
inputs=inputs,
80+
description=description,
81+
project_name=project_name,
5482
)
5583
new_project: ProjectGet = await self._web_rest_api.create_project(
5684
project_in,
5785
is_hidden=hidden,
5886
parent_project_uuid=parent_project_uuid,
5987
parent_node_id=parent_node_id,
6088
)
89+
await self._web_rpc_api.mark_project_as_job(
90+
product_name=self._product_name,
91+
user_id=self._user_id,
92+
project_uuid=new_project.uuid,
93+
job_parent_resource_name=pre_job.runner_name,
94+
)
6195

6296
assert new_project # nosec
6397
assert new_project.uuid == pre_job.id # nosec

services/api-server/src/simcore_service_api_server/api/routes/_constants.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
"Please use `{}` instead.\n\n"
2727
)
2828

29+
DEFAULT_MAX_STRING_LENGTH: Final[int] = 500
30+
2931

3032
def create_route_description(
3133
*,

services/api-server/src/simcore_service_api_server/api/routes/programs.py

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
1+
# pylint: disable=too-many-arguments
12
import logging
23
from collections.abc import Callable
34
from typing import Annotated
45

5-
from fastapi import APIRouter, Depends, Header, HTTPException, status
6+
from fastapi import APIRouter, Body, Depends, Header, HTTPException, status
67
from httpx import HTTPStatusError
78
from models_library.api_schemas_storage.storage_schemas import (
89
LinkType,
910
)
1011
from models_library.projects import ProjectID
1112
from models_library.projects_nodes_io import NodeID
12-
from pydantic import ByteSize, PositiveInt, ValidationError
13+
from pydantic import ByteSize, PositiveInt, StringConstraints, ValidationError
1314
from servicelib.fastapi.dependencies import get_reverse_url_mapper
1415
from simcore_sdk.node_ports_common.constants import SIMCORE_LOCATION
1516
from simcore_sdk.node_ports_common.filemanager import (
@@ -19,6 +20,7 @@
1920

2021
from ..._service_job import JobService
2122
from ..._service_programs import ProgramService
23+
from ...api.routes._constants import DEFAULT_MAX_STRING_LENGTH
2224
from ...models.basic_types import VersionStr
2325
from ...models.schemas.jobs import Job, JobInputs
2426
from ...models.schemas.programs import Program, ProgramKeyId
@@ -81,11 +83,14 @@ async def create_program_job(
8183
product_name: Annotated[str, Depends(get_product_name)],
8284
x_simcore_parent_project_uuid: Annotated[ProjectID | None, Header()] = None,
8385
x_simcore_parent_node_id: Annotated[NodeID | None, Header()] = None,
86+
name: Annotated[
87+
str | None, StringConstraints(max_length=DEFAULT_MAX_STRING_LENGTH), Body()
88+
] = None,
89+
description: Annotated[
90+
str | None, StringConstraints(max_length=DEFAULT_MAX_STRING_LENGTH), Body()
91+
] = None,
8492
):
85-
"""Creates a job in a specific release with given inputs.
86-
87-
NOTE: This operation does **not** start the job
88-
"""
93+
"""Creates a program job"""
8994

9095
# ensures user has access to solver
9196
inputs = JobInputs(values={})
@@ -97,13 +102,16 @@ async def create_program_job(
97102
)
98103

99104
job, project = await job_service.create_job(
105+
project_name=name,
106+
description=description,
100107
solver_or_program=program,
101108
inputs=inputs,
102109
parent_project_uuid=x_simcore_parent_project_uuid,
103110
parent_node_id=x_simcore_parent_node_id,
104111
url_for=url_for,
105112
hidden=False,
106113
)
114+
107115
# create workspace directory so files can be uploaded to it
108116
assert len(project.workbench) > 0 # nosec
109117
node_id = next(iter(project.workbench))

services/api-server/src/simcore_service_api_server/api/routes/solvers_jobs.py

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,10 @@
3232
from ...services_http.solver_job_models_converters import (
3333
create_jobstatus_from_task,
3434
)
35-
from ...services_rpc.wb_api_server import WbApiRpcClient
3635
from ..dependencies.application import get_reverse_url_mapper
3736
from ..dependencies.authentication import get_current_user_id, get_product_name
3837
from ..dependencies.services import get_api_client
3938
from ..dependencies.webserver_http import AuthSession, get_webserver_session
40-
from ..dependencies.webserver_rpc import (
41-
get_wb_api_rpc_client,
42-
)
4339
from ._constants import (
4440
FMSG_CHANGELOG_ADDED_IN_VERSION,
4541
FMSG_CHANGELOG_CHANGED_IN_VERSION,
@@ -97,7 +93,6 @@ async def create_solver_job(
9793
user_id: Annotated[PositiveInt, Depends(get_current_user_id)],
9894
solver_service: Annotated[SolverService, Depends()],
9995
job_service: Annotated[JobService, Depends()],
100-
wb_api_rpc: Annotated[WbApiRpcClient, Depends(get_wb_api_rpc_client)],
10196
url_for: Annotated[Callable, Depends(get_reverse_url_mapper)],
10297
product_name: Annotated[str, Depends(get_product_name)],
10398
hidden: Annotated[bool, Query()] = True,
@@ -116,7 +111,9 @@ async def create_solver_job(
116111
version=version,
117112
product_name=product_name,
118113
)
119-
job, project = await job_service.create_job(
114+
job, _ = await job_service.create_job(
115+
project_name=None,
116+
description=None,
120117
solver_or_program=solver,
121118
inputs=inputs,
122119
url_for=url_for,
@@ -125,12 +122,6 @@ async def create_solver_job(
125122
parent_node_id=x_simcore_parent_node_id,
126123
)
127124

128-
await wb_api_rpc.mark_project_as_job(
129-
product_name=product_name,
130-
user_id=user_id,
131-
project_uuid=project.uuid,
132-
job_parent_resource_name=job.runner_name,
133-
)
134125
return job
135126

136127

services/api-server/src/simcore_service_api_server/models/schemas/jobs.py

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,6 @@
2424
StringConstraints,
2525
TypeAdapter,
2626
ValidationError,
27-
ValidationInfo,
28-
field_validator,
2927
)
3028
from servicelib.logging_utils import LogLevelInt, LogMessageStr
3129
from starlette.datastructures import Headers
@@ -275,20 +273,13 @@ class Job(BaseModel):
275273
}
276274
)
277275

278-
@field_validator("name", mode="before")
279-
@classmethod
280-
def _check_name(cls, v, info: ValidationInfo):
281-
_id = str(info.data["id"])
282-
if not v.endswith(f"/{_id}"):
283-
msg = f"Resource name [{v}] and id [{_id}] do not match"
284-
raise ValueError(msg)
285-
return v
286-
287276
# constructors ------
288277

289278
@classmethod
290279
def create_now(
291-
cls, parent_name: RelativeResourceName, inputs_checksum: str
280+
cls,
281+
parent_name: RelativeResourceName,
282+
inputs_checksum: str,
292283
) -> "Job":
293284
global_uuid = uuid4()
294285

0 commit comments

Comments
 (0)