Skip to content

Commit b5836c9

Browse files
✨👽️ Add patch registered function job rpc endpoint in webserver (#8268)
1 parent edf0acc commit b5836c9

File tree

15 files changed

+701
-100
lines changed

15 files changed

+701
-100
lines changed

packages/models-library/src/models_library/functions.py

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from .projects import ProjectID
1818
from .utils.change_case import snake_to_camel
1919

20+
TaskID: TypeAlias = str
2021
FunctionID: TypeAlias = UUID
2122
FunctionJobID: TypeAlias = UUID
2223
FileID: TypeAlias = UUID
@@ -166,22 +167,44 @@ class RegisteredFunctionJobBase(FunctionJobBase):
166167

167168
class ProjectFunctionJob(FunctionJobBase):
168169
function_class: Literal[FunctionClass.PROJECT] = FunctionClass.PROJECT
169-
project_job_id: ProjectID
170+
project_job_id: ProjectID | None
171+
job_creation_task_id: TaskID | None
170172

171173

172174
class RegisteredProjectFunctionJob(ProjectFunctionJob, RegisteredFunctionJobBase):
173175
pass
174176

175177

178+
class RegisteredProjectFunctionJobPatch(BaseModel):
179+
function_class: Literal[FunctionClass.PROJECT] = FunctionClass.PROJECT
180+
title: str | None
181+
description: str | None
182+
inputs: FunctionInputs
183+
outputs: FunctionOutputs
184+
project_job_id: ProjectID | None
185+
job_creation_task_id: TaskID | None
186+
187+
176188
class SolverFunctionJob(FunctionJobBase):
177189
function_class: Literal[FunctionClass.SOLVER] = FunctionClass.SOLVER
178-
solver_job_id: ProjectID
190+
solver_job_id: ProjectID | None
191+
job_creation_task_id: TaskID | None
179192

180193

181194
class RegisteredSolverFunctionJob(SolverFunctionJob, RegisteredFunctionJobBase):
182195
pass
183196

184197

198+
class RegisteredSolverFunctionJobPatch(BaseModel):
199+
function_class: Literal[FunctionClass.SOLVER] = FunctionClass.SOLVER
200+
title: str | None
201+
description: str | None
202+
inputs: FunctionInputs
203+
outputs: FunctionOutputs
204+
solver_job_id: ProjectID | None
205+
job_creation_task_id: TaskID | None
206+
207+
185208
class PythonCodeFunctionJob(FunctionJobBase):
186209
function_class: Literal[FunctionClass.PYTHON_CODE] = FunctionClass.PYTHON_CODE
187210

@@ -190,6 +213,14 @@ class RegisteredPythonCodeFunctionJob(PythonCodeFunctionJob, RegisteredFunctionJ
190213
pass
191214

192215

216+
class RegisteredPythonCodeFunctionJobPatch(BaseModel):
217+
function_class: Literal[FunctionClass.PYTHON_CODE] = FunctionClass.PYTHON_CODE
218+
title: str | None
219+
inputs: FunctionInputs
220+
outputs: FunctionOutputs
221+
description: str | None
222+
223+
193224
FunctionJob: TypeAlias = Annotated[
194225
ProjectFunctionJob | PythonCodeFunctionJob | SolverFunctionJob,
195226
Field(discriminator="function_class"),
@@ -202,6 +233,13 @@ class RegisteredPythonCodeFunctionJob(PythonCodeFunctionJob, RegisteredFunctionJ
202233
Field(discriminator="function_class"),
203234
]
204235

236+
RegisteredFunctionJobPatch = Annotated[
237+
RegisteredProjectFunctionJobPatch
238+
| RegisteredPythonCodeFunctionJobPatch
239+
| RegisteredSolverFunctionJobPatch,
240+
Field(discriminator="function_class"),
241+
]
242+
205243

206244
class FunctionJobStatus(BaseModel):
207245
status: str

packages/models-library/src/models_library/functions_errors.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,3 +158,8 @@ class FunctionJobCollectionsExecuteApiAccessDeniedError(FunctionBaseError):
158158
"User {user_id} does not have the permission to execute function job collections"
159159
)
160160
status_code: int = 403 # Forbidden
161+
162+
163+
class FunctionJobPatchModelIncompatibleError(FunctionBaseError):
164+
msg_template = "Incompatible patch model for Function '{function_id}' in product '{product_name}'."
165+
status_code: int = 422

packages/service-library/src/servicelib/rabbitmq/rpc_interfaces/webserver/functions/functions_rpc_interface.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
FunctionOutputs,
2323
FunctionUserAccessRights,
2424
FunctionUserApiAccessRights,
25+
RegisteredFunctionJobPatch,
2526
)
2627
from models_library.products import ProductName
2728
from models_library.rabbitmq_basic_types import RPCMethodName
@@ -297,6 +298,28 @@ async def register_function_job(
297298
) # Validates the result as a RegisteredFunctionJob
298299

299300

301+
@log_decorator(_logger, level=logging.DEBUG)
302+
async def patch_registered_function_job(
303+
rabbitmq_rpc_client: RabbitMQRPCClient,
304+
*,
305+
user_id: UserID,
306+
product_name: ProductName,
307+
function_job_uuid: FunctionJobID,
308+
registered_function_job_patch: RegisteredFunctionJobPatch,
309+
) -> RegisteredFunctionJob:
310+
result = await rabbitmq_rpc_client.request(
311+
WEBSERVER_RPC_NAMESPACE,
312+
TypeAdapter(RPCMethodName).validate_python("patch_registered_function_job"),
313+
user_id=user_id,
314+
product_name=product_name,
315+
function_job_uuid=function_job_uuid,
316+
registered_function_job_patch=registered_function_job_patch,
317+
)
318+
return TypeAdapter(RegisteredFunctionJob).validate_python(
319+
result
320+
) # Validates the result as a RegisteredFunctionJob
321+
322+
300323
@log_decorator(_logger, level=logging.DEBUG)
301324
async def get_function_job(
302325
rabbitmq_rpc_client: RabbitMQRPCClient,

services/api-server/openapi.json

Lines changed: 88 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10749,17 +10749,36 @@
1074910749
"default": "PROJECT"
1075010750
},
1075110751
"project_job_id": {
10752-
"type": "string",
10753-
"format": "uuid",
10752+
"anyOf": [
10753+
{
10754+
"type": "string",
10755+
"format": "uuid"
10756+
},
10757+
{
10758+
"type": "null"
10759+
}
10760+
],
1075410761
"title": "Project Job Id"
10762+
},
10763+
"job_creation_task_id": {
10764+
"anyOf": [
10765+
{
10766+
"type": "string"
10767+
},
10768+
{
10769+
"type": "null"
10770+
}
10771+
],
10772+
"title": "Job Creation Task Id"
1075510773
}
1075610774
},
1075710775
"type": "object",
1075810776
"required": [
1075910777
"function_uid",
1076010778
"inputs",
1076110779
"outputs",
10762-
"project_job_id"
10780+
"project_job_id",
10781+
"job_creation_task_id"
1076310782
],
1076410783
"title": "ProjectFunctionJob"
1076510784
},
@@ -11079,9 +11098,27 @@
1107911098
"title": "Created At"
1108011099
},
1108111100
"project_job_id": {
11082-
"type": "string",
11083-
"format": "uuid",
11101+
"anyOf": [
11102+
{
11103+
"type": "string",
11104+
"format": "uuid"
11105+
},
11106+
{
11107+
"type": "null"
11108+
}
11109+
],
1108411110
"title": "Project Job Id"
11111+
},
11112+
"job_creation_task_id": {
11113+
"anyOf": [
11114+
{
11115+
"type": "string"
11116+
},
11117+
{
11118+
"type": "null"
11119+
}
11120+
],
11121+
"title": "Job Creation Task Id"
1108511122
}
1108611123
},
1108711124
"type": "object",
@@ -11091,7 +11128,8 @@
1109111128
"outputs",
1109211129
"uid",
1109311130
"created_at",
11094-
"project_job_id"
11131+
"project_job_id",
11132+
"job_creation_task_id"
1109511133
],
1109611134
"title": "RegisteredProjectFunctionJob"
1109711135
},
@@ -11408,9 +11446,27 @@
1140811446
"title": "Created At"
1140911447
},
1141011448
"solver_job_id": {
11411-
"type": "string",
11412-
"format": "uuid",
11449+
"anyOf": [
11450+
{
11451+
"type": "string",
11452+
"format": "uuid"
11453+
},
11454+
{
11455+
"type": "null"
11456+
}
11457+
],
1141311458
"title": "Solver Job Id"
11459+
},
11460+
"job_creation_task_id": {
11461+
"anyOf": [
11462+
{
11463+
"type": "string"
11464+
},
11465+
{
11466+
"type": "null"
11467+
}
11468+
],
11469+
"title": "Job Creation Task Id"
1141411470
}
1141511471
},
1141611472
"type": "object",
@@ -11420,7 +11476,8 @@
1142011476
"outputs",
1142111477
"uid",
1142211478
"created_at",
11423-
"solver_job_id"
11479+
"solver_job_id",
11480+
"job_creation_task_id"
1142411481
],
1142511482
"title": "RegisteredSolverFunctionJob"
1142611483
},
@@ -11701,17 +11758,36 @@
1170111758
"default": "SOLVER"
1170211759
},
1170311760
"solver_job_id": {
11704-
"type": "string",
11705-
"format": "uuid",
11761+
"anyOf": [
11762+
{
11763+
"type": "string",
11764+
"format": "uuid"
11765+
},
11766+
{
11767+
"type": "null"
11768+
}
11769+
],
1170611770
"title": "Solver Job Id"
11771+
},
11772+
"job_creation_task_id": {
11773+
"anyOf": [
11774+
{
11775+
"type": "string"
11776+
},
11777+
{
11778+
"type": "null"
11779+
}
11780+
],
11781+
"title": "Job Creation Task Id"
1170711782
}
1170811783
},
1170911784
"type": "object",
1171011785
"required": [
1171111786
"function_uid",
1171211787
"inputs",
1171311788
"outputs",
11714-
"solver_job_id"
11789+
"solver_job_id",
11790+
"job_creation_task_id"
1171511791
],
1171611792
"title": "SolverFunctionJob"
1171711793
},

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,12 +134,14 @@ async def inspect_function_job(
134134
function.function_class == FunctionClass.PROJECT
135135
and function_job.function_class == FunctionClass.PROJECT
136136
):
137+
assert function_job.project_job_id is not None # nosec
137138
job_status = await self._job_service.inspect_study_job(
138139
job_id=function_job.project_job_id,
139140
)
140141
elif (function.function_class == FunctionClass.SOLVER) and (
141142
function_job.function_class == FunctionClass.SOLVER
142143
):
144+
assert function_job.solver_job_id is not None # nosec
143145
job_status = await self._job_service.inspect_solver_job(
144146
solver_key=function.solver_key,
145147
version=function.solver_version,
@@ -242,6 +244,7 @@ async def run_function(
242244
inputs=joined_inputs,
243245
outputs=None,
244246
project_job_id=study_job.id,
247+
job_creation_task_id=None,
245248
),
246249
user_id=self.user_id,
247250
product_name=self.product_name,
@@ -271,6 +274,7 @@ async def run_function(
271274
inputs=joined_inputs,
272275
outputs=None,
273276
solver_job_id=solver_job.id,
277+
job_creation_task_id=None,
274278
),
275279
user_id=self.user_id,
276280
product_name=self.product_name,

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

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from typing import Annotated, Final
22

3-
from fastapi import APIRouter, Depends, FastAPI, status
3+
from fastapi import APIRouter, Depends, FastAPI, HTTPException, status
44
from fastapi_pagination.api import create_page
55
from fastapi_pagination.bases import AbstractPage
66
from models_library.api_schemas_long_running_tasks.tasks import TaskGet
@@ -266,6 +266,7 @@ async def function_job_outputs(
266266
function.function_class == FunctionClass.PROJECT
267267
and function_job.function_class == FunctionClass.PROJECT
268268
):
269+
assert function_job.project_job_id is not None # nosec
269270
new_outputs = dict(
270271
(
271272
await studies_jobs.get_study_job_outputs(
@@ -281,6 +282,7 @@ async def function_job_outputs(
281282
function.function_class == FunctionClass.SOLVER
282283
and function_job.function_class == FunctionClass.SOLVER
283284
):
285+
assert function_job.solver_job_id is not None # nosec
284286
new_outputs = dict(
285287
(
286288
await solvers_jobs_read.get_job_outputs(
@@ -337,6 +339,11 @@ async def get_function_job_logs_task(
337339
function.function_class == FunctionClass.PROJECT
338340
and function_job.function_class == FunctionClass.PROJECT
339341
):
342+
if function_job.project_job_id is None:
343+
raise HTTPException(
344+
status_code=status.HTTP_404_NOT_FOUND,
345+
detail="Could not find project job",
346+
)
340347
async_job_get = await job_service.start_log_export(
341348
job_id=function_job.project_job_id,
342349
)
@@ -353,6 +360,11 @@ async def get_function_job_logs_task(
353360
function.function_class == FunctionClass.SOLVER
354361
and function_job.function_class == FunctionClass.SOLVER
355362
):
363+
if function_job.solver_job_id is None:
364+
raise HTTPException(
365+
status_code=status.HTTP_404_NOT_FOUND,
366+
detail="Could not find solver job",
367+
)
356368
async_job_get = await job_service.start_log_export(
357369
job_id=function_job.solver_job_id,
358370
)

services/api-server/tests/unit/api_functions/conftest.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,7 @@ def mock_project_function_job(
180180
"outputs": None,
181181
"project_job_id": f"{uuid4()}",
182182
"function_class": FunctionClass.PROJECT,
183+
"job_creation_task_id": None,
183184
}
184185
return ProjectFunctionJob(**mock_function_job)
185186

@@ -209,6 +210,7 @@ def mock_solver_function_job(
209210
outputs=None,
210211
function_class=FunctionClass.SOLVER,
211212
solver_job_id=ProjectID(f"{uuid4()}"),
213+
job_creation_task_id=None,
212214
)
213215

214216

0 commit comments

Comments
 (0)