Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce links for async jobs actions (hateos style) in webserver #7320

Merged
Changes from 138 commits
Commits
Show all changes
142 commits
Select commit Hold shift + click to select a range
024bec7
start adding links to output schemas
bisgaard-itis Mar 6, 2025
1e1a444
add links also to status response
bisgaard-itis Mar 7, 2025
4f42d2c
fix signatures
bisgaard-itis Mar 7, 2025
2022cc4
minor fix
bisgaard-itis Mar 7, 2025
1062013
fix tests
bisgaard-itis Mar 7, 2025
85cb157
add 404 in case result is not done
bisgaard-itis Mar 7, 2025
1a0ae0a
add distribute task queue
giancarloromeo Feb 12, 2025
52d001a
add settings
giancarloromeo Feb 12, 2025
6b2fb6f
update reqs
giancarloromeo Feb 12, 2025
eab1aa7
add celery task
giancarloromeo Feb 12, 2025
6767d14
add celery task queue class
giancarloromeo Feb 13, 2025
f7ab399
rename
giancarloromeo Feb 13, 2025
9b8c700
make testable
giancarloromeo Feb 13, 2025
ac53169
add storage worker
giancarloromeo Feb 13, 2025
2cb3b0f
continue working
giancarloromeo Feb 14, 2025
5a9be83
continue
giancarloromeo Feb 17, 2025
8bf39ff
use rabbit
giancarloromeo Feb 17, 2025
5c38184
continue
giancarloromeo Feb 17, 2025
7395621
continue
giancarloromeo Feb 17, 2025
6445728
add unit tests
giancarloromeo Feb 18, 2025
7557716
continue
giancarloromeo Feb 18, 2025
fef45ef
base working tests
giancarloromeo Feb 19, 2025
4b08136
add progress
giancarloromeo Feb 20, 2025
3e189b8
continue fixing
giancarloromeo Feb 21, 2025
992da83
continue fixing
giancarloromeo Feb 21, 2025
52f113b
working
giancarloromeo Feb 24, 2025
1955e9d
continue fix
giancarloromeo Feb 24, 2025
fde7387
fix files endpoint
giancarloromeo Feb 24, 2025
7cdf8a2
rename
giancarloromeo Feb 24, 2025
582c537
add healthcheck
giancarloromeo Feb 25, 2025
0c923d2
add settings
giancarloromeo Feb 25, 2025
ac93263
remuve unused pytest plugin
giancarloromeo Feb 25, 2025
a931d7f
fix tests
giancarloromeo Feb 25, 2025
a2e7e9e
add utils
giancarloromeo Feb 25, 2025
94732a4
update utils
giancarloromeo Feb 25, 2025
18452fc
add async interface
giancarloromeo Feb 26, 2025
779e525
add tests
giancarloromeo Feb 26, 2025
3374d22
improve typehint
giancarloromeo Feb 26, 2025
48bfc1d
remove unused
giancarloromeo Feb 26, 2025
ae3e0e6
add rabbit
giancarloromeo Feb 27, 2025
66c47f9
update interface
giancarloromeo Feb 27, 2025
9859062
adapt code
giancarloromeo Feb 27, 2025
8ea7362
refactor
giancarloromeo Mar 3, 2025
0b6c986
fix settings
giancarloromeo Mar 3, 2025
8e5a684
add enums
giancarloromeo Mar 3, 2025
ac63188
add redis on startup
giancarloromeo Mar 3, 2025
f9c3fdf
add enum
giancarloromeo Mar 3, 2025
09b37b4
fix worker startup
giancarloromeo Mar 3, 2025
d18d63e
continue
giancarloromeo Mar 4, 2025
9873115
progress not nullable
giancarloromeo Mar 4, 2025
4df9a9d
removed for now
giancarloromeo Mar 4, 2025
bb8ee46
typecheck
giancarloromeo Mar 4, 2025
9484ccd
fix tests
giancarloromeo Mar 4, 2025
1369d8d
fix tests
giancarloromeo Mar 4, 2025
7ec5e20
progress
giancarloromeo Mar 5, 2025
f7e16f2
improve error handling
giancarloromeo Mar 6, 2025
903e5b4
add task
giancarloromeo Mar 6, 2025
89f1d98
update
giancarloromeo Mar 6, 2025
8ef8118
fix import
giancarloromeo Mar 6, 2025
9ac1d6e
update
giancarloromeo Mar 6, 2025
9277714
add proper exception handling
bisgaard-itis Mar 7, 2025
a5d6827
fix start task endpoint
bisgaard-itis Mar 7, 2025
1f558dc
start enhancing tests
bisgaard-itis Mar 7, 2025
66bb387
add test in case of scheduler error
bisgaard-itis Mar 7, 2025
c46bc0a
add tests for get_export status
bisgaard-itis Mar 7, 2025
8620972
add tests for get_export status
bisgaard-itis Mar 7, 2025
ba809d8
add test for get_status method
bisgaard-itis Mar 7, 2025
50a7bf1
remove db from filename
bisgaard-itis Mar 7, 2025
a1e0947
cover all exception cases in result error endpoint
bisgaard-itis Mar 10, 2025
41b3b90
cleanup parametrization
bisgaard-itis Mar 10, 2025
3aa4ef0
handle list_jobs endpoint
bisgaard-itis Mar 10, 2025
4a84997
propagate backend errors to webserver
bisgaard-itis Mar 10, 2025
bee9cf0
handle all exception types in test_data_export
bisgaard-itis Mar 10, 2025
4ef3e88
handle different exceptions in test_get_async_jobs_status
bisgaard-itis Mar 10, 2025
f6fc5b9
Handle all exceptions in test_abort_async_jobs
bisgaard-itis Mar 10, 2025
723ae64
handle all exception types in test_get_async_job_result
bisgaard-itis Mar 10, 2025
2b6adda
handle all exceptions in test_get_user_async_jobs
bisgaard-itis Mar 10, 2025
54f3d58
parametrize user roles
bisgaard-itis Mar 10, 2025
0708173
update openapi specs with status codes
bisgaard-itis Mar 10, 2025
2f01ba4
only add response types to data_export endpoints
bisgaard-itis Mar 10, 2025
0b6ccfd
add test for hateos links
bisgaard-itis Mar 11, 2025
7765161
Merge branch 'master' into introduce-links-for-webserver-async-jobs
bisgaard-itis Mar 11, 2025
8e44e97
revert unwanted changes after merge
bisgaard-itis Mar 11, 2025
359199a
fix storage settings after rebase
bisgaard-itis Mar 11, 2025
bc1d070
regenserate openapi specs
bisgaard-itis Mar 11, 2025
fb3cf82
add __init__ file
bisgaard-itis Mar 11, 2025
da40501
Merge branch 'master' into introduce-links-for-webserver-async-jobs
bisgaard-itis Mar 11, 2025
8efe566
minor fix
bisgaard-itis Mar 11, 2025
20439ba
add import of SimcoreS3Dsm
bisgaard-itis Mar 11, 2025
59c90b3
make pylint happy
bisgaard-itis Mar 11, 2025
6010f61
@pcrespov remove aiohttp from models library
bisgaard-itis Mar 12, 2025
c20806f
fix validation errors
bisgaard-itis Mar 12, 2025
6fdf753
@pcrespov revert changes in storage settings
bisgaard-itis Mar 12, 2025
2d22d65
Merge branch 'master' into introduce-links-for-webserver-async-jobs
bisgaard-itis Mar 13, 2025
45df1ee
Merge branch 'master' into introduce-links-for-webserver-async-jobs
bisgaard-itis Mar 14, 2025
10aab9c
Update packages/models-library/src/models_library/api_schemas_rpc_asy…
bisgaard-itis Mar 14, 2025
8af0068
@sanderegg align status codes between long running tasks and async jobs
bisgaard-itis Mar 14, 2025
cc1e57c
fix task name
bisgaard-itis Mar 14, 2025
2e7bd10
fix async job listing signature
bisgaard-itis Mar 14, 2025
02b44f0
update webserver openapi specs
bisgaard-itis Mar 14, 2025
ceff8eb
Merge branch 'master' into introduce-links-for-webserver-async-jobs
bisgaard-itis Mar 14, 2025
6e5b186
remove cast
bisgaard-itis Mar 14, 2025
7c04230
merge master into introduce-links-for-webserver-async-jobs
bisgaard-itis Mar 17, 2025
dd449b2
adhere to long running tasks interface
bisgaard-itis Mar 17, 2025
842ab75
fix tests
bisgaard-itis Mar 17, 2025
2240282
update openapi specs
bisgaard-itis Mar 17, 2025
9010342
move async jobs endpoints to tasks
bisgaard-itis Mar 17, 2025
902c152
make initial tests pass
bisgaard-itis Mar 18, 2025
1787266
polish list jobs endpoint
bisgaard-itis Mar 18, 2025
30ca456
minor fix
bisgaard-itis Mar 18, 2025
c8c46ee
fix tests
bisgaard-itis Mar 18, 2025
9e05a7a
update openapi specs
bisgaard-itis Mar 18, 2025
bb32ddc
clean up storage rpc tests
bisgaard-itis Mar 18, 2025
f5670f9
merge master into introduce-links-for-webserver-async-jobs
bisgaard-itis Mar 18, 2025
6e837fa
add init file to tasks
bisgaard-itis Mar 18, 2025
fb98b97
make mypy happy
bisgaard-itis Mar 18, 2025
46ee24a
merge master into introduce-links-for-webserver-async-jobs
bisgaard-itis Mar 18, 2025
8881c85
fix location_id parametrization of test
bisgaard-itis Mar 18, 2025
a305032
make pylint happy
bisgaard-itis Mar 18, 2025
9736c84
update path size computations
bisgaard-itis Mar 18, 2025
c4bc27c
remove wrongly commited function
bisgaard-itis Mar 18, 2025
2f2cbb6
merge master into introduce-links-for-webserver-async-jobs
bisgaard-itis Mar 19, 2025
b175f85
remove redundant webserver schemas
bisgaard-itis Mar 19, 2025
1d714b8
add test for case when legacy task listing fails
bisgaard-itis Mar 19, 2025
59a607a
add job existence check for abort method
bisgaard-itis Mar 19, 2025
e89d397
check job existence in status
bisgaard-itis Mar 19, 2025
d550797
add job existence check to result method
bisgaard-itis Mar 19, 2025
6ce3495
fix storage tests
bisgaard-itis Mar 19, 2025
5e6f408
fix
bisgaard-itis Mar 19, 2025
3555b13
merge master into introduce-links-for-webserver-async-jobs
bisgaard-itis Mar 19, 2025
b0544bd
fix pylint errors
bisgaard-itis Mar 19, 2025
686c9f1
fix import
bisgaard-itis Mar 19, 2025
43b018e
fix openapi test
bisgaard-itis Mar 19, 2025
0ebfb6c
@GitHK use TypeAdapter
bisgaard-itis Mar 19, 2025
6be292e
@GitHK _type -> selection_type
bisgaard-itis Mar 19, 2025
93fb7ca
to_check -> selection_type @GitHK
bisgaard-itis Mar 19, 2025
f49ab7c
@GitHK remove comment
bisgaard-itis Mar 19, 2025
ef97f04
make pylint happy
bisgaard-itis Mar 19, 2025
26d92f1
add comment for future improvement
bisgaard-itis Mar 20, 2025
97f641e
@sanderegg renmaing rpc methods
bisgaard-itis Mar 20, 2025
f581b63
consolidate test files @sanderegg
bisgaard-itis Mar 20, 2025
253f65f
merge master into introduce-links-for-webserver-async-jobs
bisgaard-itis Mar 20, 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
45 changes: 32 additions & 13 deletions api/specs/web-server/_long_running_tasks.py
Original file line number Diff line number Diff line change
@@ -4,13 +4,17 @@
# pylint: disable=too-many-arguments


from typing import Annotated
from typing import Annotated, Any

from fastapi import APIRouter, Depends, status
from models_library.generics import Envelope
from models_library.rest_error import EnvelopedError
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
from simcore_service_webserver.tasks._exception_handlers import (
_TO_HTTP_ERROR_MAP as data_export_http_error_map,
)

router = APIRouter(
prefix=f"/{API_VTAG}",
@@ -19,37 +23,52 @@
],
)

_data_export_responses: dict[int | str, dict[str, Any]] = {
i.status_code: {"model": EnvelopedError}
for i in data_export_http_error_map.values()
}


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


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


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


@router.get("/tasks/{task_id}/result")
def get_task_result(
@router.get(
"/tasks/{task_id}/result",
name="get_task_result",
description="Retrieves the result of a task",
responses=_data_export_responses,
)
def get_async_job_result(
_path_params: Annotated[_PathParam, Depends()],
):
...
): ...
60 changes: 17 additions & 43 deletions api/specs/web-server/_storage.py
Original file line number Diff line number Diff line change
@@ -4,10 +4,12 @@
# pylint: disable=too-many-arguments


from typing import Annotated, TypeAlias
from uuid import UUID
from typing import Annotated, Any, TypeAlias

from fastapi import APIRouter, Depends, Query, status
from models_library.api_schemas_long_running_tasks.tasks import (
TaskGet,
)
from models_library.api_schemas_storage.storage_schemas import (
FileLocation,
FileMetaDataGet,
@@ -22,19 +24,19 @@
from models_library.api_schemas_webserver.storage import (
DataExportPost,
ListPathsQueryParams,
StorageAsyncJobGet,
StorageAsyncJobResult,
StorageAsyncJobStatus,
StorageLocationPathParams,
StoragePathComputeSizeParams,
)
from models_library.generics import Envelope
from models_library.projects_nodes_io import LocationID
from models_library.users import UserID
from models_library.rest_error import EnvelopedError
from pydantic import AnyUrl, ByteSize
from servicelib.fastapi.rest_pagination import CustomizedPathsCursorPage
from simcore_service_webserver._meta import API_VTAG
from simcore_service_webserver.storage.schemas import DatasetMetaData, FileMetaData
from simcore_service_webserver.tasks._exception_handlers import (
_TO_HTTP_ERROR_MAP as data_export_http_error_map,
)

router = APIRouter(
prefix=f"/{API_VTAG}",
@@ -71,7 +73,7 @@ async def list_storage_paths(

@router.post(
"/storage/locations/{location_id}/paths/{path}:size",
response_model=Envelope[StorageAsyncJobGet],
response_model=Envelope[TaskGet],
status_code=status.HTTP_202_ACCEPTED,
)
async def compute_path_size(_path: Annotated[StoragePathComputeSizeParams, Depends()]):
@@ -205,46 +207,18 @@ async def is_completed_upload_file(


# data export
_data_export_responses: dict[int | str, dict[str, Any]] = {
i.status_code: {"model": EnvelopedError}
for i in data_export_http_error_map.values()
}


@router.post(
"/storage/locations/{location_id}/export-data",
response_model=Envelope[StorageAsyncJobGet],
response_model=Envelope[TaskGet],
name="export_data",
description="Export data",
responses=_data_export_responses,
)
async def export_data(data_export: DataExportPost, location_id: LocationID):
"""Trigger data export. Returns async job id for getting status and results"""


@router.get(
"/storage/async-jobs/{job_id}/status",
response_model=Envelope[StorageAsyncJobStatus],
name="get_async_job_status",
)
async def get_async_job_status(job_id: UUID):
"""Get async job status"""


@router.post(
"/storage/async-jobs/{job_id}:abort",
name="abort_async_job",
)
async def abort_async_job(job_id: UUID):
"""aborts execution of an async job"""


@router.get(
"/storage/async-jobs/{job_id}/result",
response_model=Envelope[StorageAsyncJobResult],
name="get_async_job_result",
)
async def get_async_job_result(job_id: UUID):
"""Get the result of the async job"""


@router.get(
"/storage/async-jobs",
response_model=Envelope[list[StorageAsyncJobGet]],
name="get_async_jobs",
)
async def get_async_jobs(user_id: UserID):
"""Retrunsa list of async jobs for the user"""
43 changes: 0 additions & 43 deletions api/specs/web-server/_tasks.py

This file was deleted.

Original file line number Diff line number Diff line change
@@ -10,7 +10,7 @@
class TaskStatus(BaseModel):
task_progress: TaskProgress
done: bool
started: datetime
started: datetime | None


class TaskResult(BaseModel):
Original file line number Diff line number Diff line change
@@ -16,8 +16,7 @@ class AsyncJobStatus(BaseModel):


class AsyncJobResult(BaseModel):
result: Any | None
error: Any | None
result: Any


class AsyncJobGet(BaseModel):
Original file line number Diff line number Diff line change
@@ -5,9 +5,27 @@ class BaseAsyncjobRpcError(OsparcErrorMixin, RuntimeError):
pass


class StatusError(BaseAsyncjobRpcError):
class JobSchedulerError(BaseAsyncjobRpcError):
msg_template: str = "Celery exception: {exc}"


class JobMissingError(BaseAsyncjobRpcError):
msg_template: str = "Job {job_id} does not exist"


class JobStatusError(BaseAsyncjobRpcError):
msg_template: str = "Could not get status of job {job_id}"


class ResultError(BaseAsyncjobRpcError):
msg_template: str = "Could not get results of job {job_id}"
class JobNotDoneError(BaseAsyncjobRpcError):
msg_template: str = "Job {job_id} not done"


class JobAbortedError(BaseAsyncjobRpcError):
msg_template: str = "Job {job_id} aborted"


class JobError(BaseAsyncjobRpcError):
msg_template: str = (
"Job {job_id} failed with exception type {exc_type} and message {exc_msg}"
)
Original file line number Diff line number Diff line change
@@ -17,17 +17,11 @@ class StorageRpcBaseError(OsparcErrorMixin, RuntimeError):
pass


class InvalidLocationIdError(StorageRpcBaseError):
msg_template: str = "Invalid location_id {location_id}"


class InvalidFileIdentifierError(StorageRpcBaseError):
msg_template: str = "Could not find the file {file_id}"


class AccessRightError(StorageRpcBaseError):
msg_template: str = "User {user_id} does not have access to file {file_id} with location {location_id}"


class DataExportError(StorageRpcBaseError):
msg_template: str = "Could not complete data export job with id {job_id}"
msg_template: str = (
"User {user_id} does not have access to file {file_id} with location {location_id}"
)
Original file line number Diff line number Diff line change
@@ -1,25 +1,18 @@
from pathlib import Path
from typing import Annotated, Any
from typing import Annotated

from pydantic import BaseModel, Field

from ..api_schemas_rpc_async_jobs.async_jobs import (
AsyncJobGet,
AsyncJobId,
AsyncJobResult,
AsyncJobStatus,
)
from ..api_schemas_storage.data_export_async_jobs import DataExportTaskStartInput
from ..api_schemas_storage.storage_schemas import (
DEFAULT_NUMBER_OF_PATHS_PER_PAGE,
MAX_NUMBER_OF_PATHS_PER_PAGE,
)
from ..progress_bar import ProgressReport
from ..projects_nodes_io import LocationID, StorageFileID
from ..rest_pagination import (
CursorQueryParameters,
)
from ._base import InputSchema, OutputSchema
from ._base import InputSchema


class StorageLocationPathParams(BaseModel):
@@ -51,40 +44,3 @@ def to_rpc_schema(self, location_id: LocationID) -> DataExportTaskStartInput:
file_and_folder_ids=self.paths,
location_id=location_id,
)


class StorageAsyncJobGet(OutputSchema):
job_id: AsyncJobId

@classmethod
def from_rpc_schema(cls, async_job_rpc_get: AsyncJobGet) -> "StorageAsyncJobGet":
return StorageAsyncJobGet(job_id=async_job_rpc_get.job_id)


class StorageAsyncJobStatus(OutputSchema):
job_id: AsyncJobId
progress: ProgressReport
done: bool

@classmethod
def from_rpc_schema(
cls, async_job_rpc_status: AsyncJobStatus
) -> "StorageAsyncJobStatus":
return StorageAsyncJobStatus(
job_id=async_job_rpc_status.job_id,
progress=async_job_rpc_status.progress,
done=async_job_rpc_status.done,
)


class StorageAsyncJobResult(OutputSchema):
result: Any | None
error: Any | None

@classmethod
def from_rpc_schema(
cls, async_job_rpc_result: AsyncJobResult
) -> "StorageAsyncJobResult":
return StorageAsyncJobResult(
result=async_job_rpc_result.result, error=async_job_rpc_result.error
)
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from typing import Final

from models_library.api_schemas_rpc_async_jobs.async_jobs import (
AsyncJobAbort,
AsyncJobGet,
AsyncJobId,
AsyncJobNameData,
@@ -24,16 +23,14 @@ async def abort(
rpc_namespace: RPCNamespace,
job_id: AsyncJobId,
job_id_data: AsyncJobNameData,
) -> AsyncJobAbort:
result = await rabbitmq_rpc_client.request(
) -> None:
await rabbitmq_rpc_client.request(
rpc_namespace,
_RPC_METHOD_NAME_ADAPTER.validate_python("abort"),
job_id=job_id,
job_id_data=job_id_data,
timeout_s=_DEFAULT_TIMEOUT_S,
)
assert isinstance(result, AsyncJobAbort)
return result


async def get_status(
Loading