Skip to content

Commit 0666c91

Browse files
committed
adds project trash
1 parent 559a82e commit 0666c91

File tree

9 files changed

+141
-84
lines changed

9 files changed

+141
-84
lines changed

api/specs/web-server/_trash.py

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,8 @@
77
from enum import Enum
88

99
from fastapi import APIRouter
10-
from models_library.api_schemas_webserver.folders import FolderGet
11-
from models_library.api_schemas_webserver.projects import ProjectGet
12-
from models_library.generics import Envelope
1310
from models_library.projects import ProjectID
1411
from simcore_service_webserver._meta import API_VTAG
15-
from simcore_service_webserver.folders._folders_handlers import FolderID
1612

1713
router = APIRouter(
1814
prefix=f"/{API_VTAG}",
@@ -34,7 +30,6 @@ def empty_trash():
3430

3531
@router.post(
3632
"/projects/{project_uuid}:trash",
37-
response_model=Envelope[ProjectGet],
3833
tags=_extra_tags,
3934
)
4035
def trash_project(
@@ -48,27 +43,3 @@ def untrash_project(
4843
project_uuid: ProjectID,
4944
):
5045
...
51-
52-
53-
### Folders
54-
55-
_extra_tags = ["folders"]
56-
57-
58-
@router.post(
59-
"/folders/{folder_id}:trash", response_model=Envelope[FolderGet], tags=_extra_tags
60-
)
61-
def trash_folder(
62-
folder_id: FolderID,
63-
):
64-
...
65-
66-
67-
@router.post("/folders/{folder_id}:untrash", tags=_extra_tags)
68-
def untrash_folder(
69-
folder_id: FolderID,
70-
):
71-
...
72-
73-
74-
## TODO: workspaces

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,11 @@ class ProjectPatch(InputSchema):
146146
quality: dict[str, Any] = FieldNotRequired()
147147

148148

149+
class ProjectPatchExtended(ProjectPatch):
150+
# Only used internally
151+
trashed_at: datetime | None = None
152+
153+
149154
__all__: tuple[str, ...] = (
150155
"EmptyModel",
151156
"ProjectCopyOverride",
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import arrow
2+
from aiohttp import web
3+
from models_library.api_schemas_webserver.projects import ProjectPatchExtended
4+
from models_library.products import ProductName
5+
from models_library.projects import ProjectID
6+
from models_library.users import UserID
7+
8+
from . import projects_api
9+
10+
11+
async def empty_trash(app: web.Application, product_name: ProductName, user_id: UserID):
12+
# filter trashed=True and set them to False
13+
raise NotImplementedError
14+
15+
16+
async def update_project(
17+
app: web.Application,
18+
*,
19+
product_name: ProductName,
20+
user_id: UserID,
21+
project_id: ProjectID,
22+
trashed: bool,
23+
):
24+
# FIXME: can you trash something that is running?
25+
26+
await projects_api.patch_project(
27+
app,
28+
user_id=user_id,
29+
product_name=product_name,
30+
project_uuid=project_id,
31+
project_patch=ProjectPatchExtended(
32+
trashed_at=arrow.utcnow().datetime if trashed else None
33+
),
34+
)
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import functools
2+
3+
from aiohttp import web
4+
from servicelib.aiohttp import status
5+
from servicelib.aiohttp.requests_validation import parse_request_path_parameters_as
6+
from servicelib.aiohttp.typing_extension import Handler
7+
from simcore_postgres_database.utils_tags import (
8+
TagNotFoundError,
9+
TagOperationNotAllowedError,
10+
)
11+
12+
from .._meta import API_VTAG as VTAG
13+
from ..login.decorators import get_user_id, login_required
14+
from ..products.api import get_product_name
15+
from ..projects._common_models import ProjectPathParams
16+
from ..security.decorators import permission_required
17+
from . import _trash_api
18+
19+
20+
def _handle_trash_exceptions(handler: Handler):
21+
@functools.wraps(handler)
22+
async def wrapper(request: web.Request) -> web.StreamResponse:
23+
try:
24+
return await handler(request)
25+
26+
except TagNotFoundError as exc:
27+
raise web.HTTPNotFound(reason=f"{exc}") from exc
28+
29+
except TagOperationNotAllowedError as exc:
30+
raise web.HTTPForbidden(reason=f"{exc}") from exc
31+
32+
return wrapper
33+
34+
35+
routes = web.RouteTableDef()
36+
37+
38+
@routes.delete(f"/{VTAG}/trash", name="empty_trash")
39+
@login_required
40+
@permission_required("project.delete")
41+
@_handle_trash_exceptions
42+
async def empty_trash(request: web.Request):
43+
user_id = get_user_id(request)
44+
product_name = get_product_name(request)
45+
46+
await _trash_api.empty_trash(
47+
request.app, product_name=product_name, user_id=user_id
48+
)
49+
50+
return web.json_response(status=status.HTTP_204_NO_CONTENT)
51+
52+
53+
#
54+
# Projects
55+
#
56+
57+
58+
@routes.post(f"/{VTAG}/projects/{{project_id}}:trash", name="trash_project")
59+
@login_required
60+
@permission_required("project.delete")
61+
@_handle_trash_exceptions
62+
async def trash_project(request: web.Request):
63+
user_id = get_user_id(request)
64+
product_name = get_product_name(request)
65+
path_params = parse_request_path_parameters_as(ProjectPathParams, request)
66+
67+
await _trash_api.update_project(
68+
request.app,
69+
product_name=product_name,
70+
user_id=user_id,
71+
project_id=path_params.project_id,
72+
trashed=True,
73+
)
74+
75+
return web.json_response(status=status.HTTP_204_NO_CONTENT)
76+
77+
78+
@routes.post(f"/{VTAG}/projects/{{project_uuid}}:untrash", name="untrash_project")
79+
@login_required
80+
@permission_required("project.delete")
81+
@_handle_trash_exceptions
82+
async def untrash_project(request: web.Request):
83+
user_id = get_user_id(request)
84+
product_name = get_product_name(request)
85+
path_params = parse_request_path_parameters_as(ProjectPathParams, request)
86+
87+
await _trash_api.update_project(
88+
request.app,
89+
product_name=product_name,
90+
user_id=user_id,
91+
project_id=path_params.project_id,
92+
trashed=False,
93+
)
94+
95+
return web.json_response(status=status.HTTP_204_NO_CONTENT)

services/web/server/src/simcore_service_webserver/projects/plugin.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
_projects_nodes_pricing_unit_handlers,
2121
_states_handlers,
2222
_tags_handlers,
23+
_trash_handlers,
2324
_wallets_handlers,
2425
_workspaces_handlers,
2526
)
@@ -61,5 +62,6 @@ def setup_projects(app: web.Application) -> bool:
6162
app.router.add_routes(_folders_handlers.routes)
6263
app.router.add_routes(_projects_nodes_pricing_unit_handlers.routes)
6364
app.router.add_routes(_workspaces_handlers.routes)
65+
app.router.add_routes(_trash_handlers.routes)
6466

6567
return True

services/web/server/src/simcore_service_webserver/projects/projects_api.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,10 @@
3030
DynamicServiceStart,
3131
DynamicServiceStop,
3232
)
33-
from models_library.api_schemas_webserver.projects import ProjectPatch
33+
from models_library.api_schemas_webserver.projects import (
34+
ProjectPatch,
35+
ProjectPatchExtended,
36+
)
3437
from models_library.api_schemas_webserver.projects_nodes import NodePatch
3538
from models_library.basic_types import KeyIDStr
3639
from models_library.errors import ErrorDict
@@ -249,7 +252,7 @@ async def patch_project(
249252
*,
250253
user_id: UserID,
251254
project_uuid: ProjectID,
252-
project_patch: ProjectPatch,
255+
project_patch: ProjectPatch | ProjectPatchExtended,
253256
product_name: ProductName,
254257
):
255258
_project_patch_exclude_unset: dict[str, Any] = jsonable_encoder(

services/web/server/src/simcore_service_webserver/trash/__init__.py

Whitespace-only changes.

services/web/server/src/simcore_service_webserver/trash/_api.py

Lines changed: 0 additions & 7 deletions
This file was deleted.

services/web/server/src/simcore_service_webserver/trash/_handlers.py

Lines changed: 0 additions & 46 deletions
This file was deleted.

0 commit comments

Comments
 (0)