Skip to content

Commit 91da37d

Browse files
author
Andrei Neagu
committed
Merge remote-tracking branch 'upstream/master' into pr-osparc-overwite-can-save-templates
2 parents 1c7cdce + 05f8837 commit 91da37d

File tree

19 files changed

+1108
-604
lines changed

19 files changed

+1108
-604
lines changed

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

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,3 +85,37 @@ class BatchGetEnvelope(BaseModel, Generic[ResourceT, IdentifierT]):
8585
description="List of identifiers for items that were not found",
8686
),
8787
] = DEFAULT_FACTORY
88+
89+
90+
class BatchCreateEnvelope(BaseModel, Generic[SchemaT]):
91+
"""Generic envelope model for batch-create operations.
92+
93+
This model represents the result of a strict batch create operation,
94+
containing the list of created items. The operation is expected to be "strict"
95+
in the sense that it either creates all requested items or fails entirely.
96+
"""
97+
98+
created_items: Annotated[
99+
list[SchemaT],
100+
Field(
101+
min_length=1,
102+
description="List of successfully created items",
103+
),
104+
]
105+
106+
107+
class BatchUpdateEnvelope(BaseModel, Generic[SchemaT]):
108+
"""Generic envelope model for batch-update operations.
109+
110+
This model represents the result of a strict batch update operation,
111+
containing the list of updated items. The operation is expected to be "strict"
112+
in the sense that it either updates all requested items or fails entirely. See https://google.aip.dev/234
113+
"""
114+
115+
updated_items: Annotated[
116+
list[SchemaT],
117+
Field(
118+
min_length=1,
119+
description="List of successfully updated items",
120+
),
121+
]

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

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
11
import datetime
22
from collections.abc import Mapping
33
from enum import Enum
4-
from typing import Annotated, Any, Literal, TypeAlias
4+
from typing import Annotated, Any, Final, Literal, TypeAlias
55
from uuid import UUID
66

77
from models_library import projects
88
from models_library.basic_regex import UUID_RE_BASE
99
from models_library.basic_types import ConstrainedStr
10+
from models_library.batch_operations import BatchCreateEnvelope
1011
from models_library.groups import GroupID
1112
from models_library.products import ProductName
1213
from models_library.services_types import ServiceKey, ServiceVersion
1314
from models_library.users import UserID
1415
from models_library.utils.enums import StrAutoEnum
1516
from pydantic import BaseModel, ConfigDict, Field
1617

18+
from .batch_operations import BatchGetEnvelope, BatchUpdateEnvelope
1719
from .projects import ProjectID
1820
from .utils.change_case import snake_to_camel
1921

@@ -23,6 +25,7 @@
2325
FileID: TypeAlias = UUID
2426

2527
InputTypes: TypeAlias = FileID | float | int | bool | str | list
28+
_MAX_LIST_LENGTH: Final[int] = 50
2629

2730

2831
class FunctionSchemaClass(str, Enum):
@@ -80,9 +83,10 @@ class FunctionClass(str, Enum):
8083

8184
FunctionInputsList: TypeAlias = Annotated[
8285
list[FunctionInputs],
83-
Field(max_length=50),
86+
Field(max_length=_MAX_LIST_LENGTH),
8487
]
8588

89+
8690
FunctionOutputs: TypeAlias = dict[str, Any] | None
8791

8892
FunctionOutputsLogfile: TypeAlias = Any
@@ -238,6 +242,9 @@ class RegisteredPythonCodeFunctionJobPatch(BaseModel):
238242
ProjectFunctionJob | PythonCodeFunctionJob | SolverFunctionJob,
239243
Field(discriminator="function_class"),
240244
]
245+
FunctionJobList: TypeAlias = Annotated[
246+
list[FunctionJob], Field(max_length=_MAX_LIST_LENGTH)
247+
]
241248

242249

243250
class RegisteredFunctionJobBase(FunctionJobBase):
@@ -264,6 +271,21 @@ class RegisteredPythonCodeFunctionJob(PythonCodeFunctionJob, RegisteredFunctionJ
264271
Field(discriminator="function_class"),
265272
]
266273

274+
275+
class BatchCreateRegisteredFunctionJobs(BatchCreateEnvelope[RegisteredFunctionJob]):
276+
pass
277+
278+
279+
class BatchUpdateRegisteredFunctionJobs(BatchUpdateEnvelope[RegisteredFunctionJob]):
280+
pass
281+
282+
283+
class BatchGetCachedRegisteredFunctionJobs(
284+
BatchGetEnvelope[RegisteredFunctionJob, FunctionInputs]
285+
):
286+
pass
287+
288+
267289
RegisteredFunctionJobPatch = Annotated[
268290
RegisteredProjectFunctionJobPatch
269291
| RegisteredPythonCodeFunctionJobPatch
@@ -272,6 +294,20 @@ class RegisteredPythonCodeFunctionJob(PythonCodeFunctionJob, RegisteredFunctionJ
272294
]
273295

274296

297+
class FunctionJobPatchRequest(BaseModel):
298+
uid: FunctionJobID
299+
patch: RegisteredFunctionJobPatch
300+
301+
302+
FunctionJobPatchRequestList: TypeAlias = Annotated[
303+
list[FunctionJobPatchRequest],
304+
Field(
305+
max_length=_MAX_LIST_LENGTH,
306+
description="List of function job patch requests",
307+
),
308+
]
309+
310+
275311
class FunctionJobStatus(BaseModel):
276312
status: str
277313

@@ -340,6 +376,20 @@ class RegisteredFunctionJobDB(FunctionJobDB):
340376
created: datetime.datetime
341377

342378

379+
class BatchGetCachedRegisteredFunctionJobsDB(
380+
BatchGetEnvelope[RegisteredFunctionJobDB, FunctionInputs]
381+
):
382+
pass
383+
384+
385+
class BatchCreateRegisteredFunctionJobsDB(BatchCreateEnvelope[RegisteredFunctionJobDB]):
386+
pass
387+
388+
389+
class BatchUpdateRegisteredFunctionJobsDB(BatchUpdateEnvelope[RegisteredFunctionJobDB]):
390+
pass
391+
392+
343393
class RegisteredFunctionJobWithStatusDB(FunctionJobDB):
344394
uuid: FunctionJobID
345395
created: datetime.datetime

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,3 +170,8 @@ class FunctionJobCollectionsExecuteApiAccessDeniedError(FunctionBaseError):
170170
class FunctionJobPatchModelIncompatibleError(FunctionBaseError):
171171
msg_template = "Incompatible patch model for Function '{function_id}' in product '{product_name}'."
172172
status_code: int = 422
173+
174+
175+
class FunctionUnrecoverableError(FunctionBaseError):
176+
msg_template = "Unrecoverable error."
177+
status_code: int = 500

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

Lines changed: 6 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,11 @@
2121
from models_library.functions import (
2222
FunctionClass,
2323
FunctionGroupAccessRights,
24+
FunctionInputsList,
2425
FunctionJobStatus,
2526
FunctionOutputs,
2627
FunctionUserAccessRights,
2728
FunctionUserApiAccessRights,
28-
RegisteredFunctionJobPatch,
2929
RegisteredFunctionJobWithStatus,
3030
)
3131
from models_library.products import ProductName
@@ -359,29 +359,6 @@ async def register_function_job(
359359
) # Validates the result as a RegisteredFunctionJob
360360

361361

362-
@log_decorator(_logger, level=logging.DEBUG)
363-
async def patch_registered_function_job(
364-
rabbitmq_rpc_client: RabbitMQRPCClient,
365-
*,
366-
user_id: UserID,
367-
product_name: ProductName,
368-
function_job_uuid: FunctionJobID,
369-
registered_function_job_patch: RegisteredFunctionJobPatch,
370-
) -> RegisteredFunctionJob:
371-
result = await rabbitmq_rpc_client.request(
372-
DEFAULT_WEBSERVER_RPC_NAMESPACE,
373-
TypeAdapter(RPCMethodName).validate_python("patch_registered_function_job"),
374-
user_id=user_id,
375-
product_name=product_name,
376-
function_job_uuid=function_job_uuid,
377-
registered_function_job_patch=registered_function_job_patch,
378-
timeout_s=_FUNCTION_RPC_TIMEOUT_SEC,
379-
)
380-
return TypeAdapter(RegisteredFunctionJob).validate_python(
381-
result
382-
) # Validates the result as a RegisteredFunctionJob
383-
384-
385362
@log_decorator(_logger, level=logging.DEBUG)
386363
async def get_function_job(
387364
rabbitmq_rpc_client: RabbitMQRPCClient,
@@ -512,20 +489,20 @@ async def find_cached_function_jobs(
512489
user_id: UserID,
513490
product_name: ProductName,
514491
function_id: FunctionID,
515-
inputs: FunctionInputs,
516-
) -> list[RegisteredFunctionJob] | None:
492+
inputs: FunctionInputsList,
493+
status_filter: list[FunctionJobStatus] | None = None,
494+
) -> list[RegisteredFunctionJob | None]:
517495
result = await rabbitmq_rpc_client.request(
518496
DEFAULT_WEBSERVER_RPC_NAMESPACE,
519497
TypeAdapter(RPCMethodName).validate_python("find_cached_function_jobs"),
520498
function_id=function_id,
521499
inputs=inputs,
500+
status_filter=status_filter,
522501
user_id=user_id,
523502
product_name=product_name,
524503
timeout_s=_FUNCTION_RPC_TIMEOUT_SEC,
525504
)
526-
if result is None:
527-
return None
528-
return TypeAdapter(list[RegisteredFunctionJob]).validate_python(result)
505+
return TypeAdapter(list[RegisteredFunctionJob | None]).validate_python(result)
529506

530507

531508
@log_decorator(_logger, level=logging.DEBUG)

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

Lines changed: 48 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
FunctionID,
88
FunctionInputs,
99
FunctionInputSchema,
10-
FunctionJob,
1110
FunctionJobCollection,
1211
FunctionJobCollectionID,
1312
FunctionJobCollectionsListFilters,
@@ -18,13 +17,19 @@
1817
RegisteredFunctionJobCollection,
1918
)
2019
from models_library.functions import (
20+
BatchCreateRegisteredFunctionJobs,
21+
BatchUpdateRegisteredFunctionJobs,
2122
FunctionClass,
2223
FunctionGroupAccessRights,
24+
FunctionInputsList,
25+
FunctionJob,
26+
FunctionJobList,
27+
FunctionJobPatchRequest,
28+
FunctionJobPatchRequestList,
2329
FunctionJobStatus,
2430
FunctionOutputs,
2531
FunctionUserAccessRights,
2632
FunctionUserApiAccessRights,
27-
RegisteredFunctionJobPatch,
2833
RegisteredFunctionJobWithStatus,
2934
)
3035
from models_library.products import ProductName
@@ -329,22 +334,54 @@ async def register_function_job(
329334
),
330335
)
331336

337+
async def batch_register_function_jobs(
338+
self,
339+
*,
340+
product_name: ProductName,
341+
user_id: UserID,
342+
function_jobs: FunctionJobList,
343+
) -> BatchCreateRegisteredFunctionJobs:
344+
"""Register a function job."""
345+
return TypeAdapter(BatchCreateRegisteredFunctionJobs).validate_python(
346+
await self._request(
347+
"batch_register_function_jobs",
348+
product_name=product_name,
349+
user_id=user_id,
350+
function_jobs=function_jobs,
351+
),
352+
)
353+
332354
async def patch_registered_function_job(
333355
self,
334356
*,
335357
product_name: ProductName,
336358
user_id: UserID,
337-
function_job_uuid: FunctionJobID,
338-
registered_function_job_patch: RegisteredFunctionJobPatch,
359+
function_job_patch_request: FunctionJobPatchRequest,
339360
) -> RegisteredFunctionJob:
340361
"""Patch a registered function job."""
341362
return TypeAdapter(RegisteredFunctionJob).validate_python(
342363
await self._request(
343364
"patch_registered_function_job",
344365
product_name=product_name,
345366
user_id=user_id,
346-
function_job_uuid=function_job_uuid,
347-
registered_function_job_patch=registered_function_job_patch,
367+
function_job_patch_request=function_job_patch_request,
368+
),
369+
)
370+
371+
async def batch_patch_registered_function_job(
372+
self,
373+
*,
374+
product_name: ProductName,
375+
user_id: UserID,
376+
function_job_patch_requests: FunctionJobPatchRequestList,
377+
) -> BatchUpdateRegisteredFunctionJobs:
378+
"""Patch a registered function job."""
379+
return BatchUpdateRegisteredFunctionJobs.model_validate(
380+
await self._request(
381+
"batch_patch_registered_function_jobs",
382+
product_name=product_name,
383+
user_id=user_id,
384+
function_job_patch_requests=function_job_patch_requests,
348385
),
349386
)
350387

@@ -462,16 +499,18 @@ async def find_cached_function_jobs(
462499
product_name: ProductName,
463500
user_id: UserID,
464501
function_id: FunctionID,
465-
inputs: FunctionInputs,
466-
) -> list[RegisteredFunctionJob] | None:
502+
inputs: FunctionInputsList,
503+
cached_job_statuses: list[FunctionJobStatus] | None = None,
504+
) -> list[RegisteredFunctionJob | None]:
467505
"""Find cached function jobs."""
468-
return TypeAdapter(list[RegisteredFunctionJob] | None).validate_python(
506+
return TypeAdapter(list[RegisteredFunctionJob | None]).validate_python(
469507
await self._request(
470508
"find_cached_function_jobs",
471509
product_name=product_name,
472510
user_id=user_id,
473511
function_id=function_id,
474512
inputs=inputs,
513+
cached_job_statuses=cached_job_statuses,
475514
),
476515
)
477516

0 commit comments

Comments
 (0)