Skip to content

Commit 6b1b81e

Browse files
🎨 Add type and template_type query parameter filter to projects:search endpoint (#7995)
1 parent 1598ada commit 6b1b81e

File tree

8 files changed

+275
-15
lines changed

8 files changed

+275
-15
lines changed

services/static-webserver/client/source/class/osparc/data/Resources.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,12 +142,12 @@ qx.Class.define("osparc.data.Resources", {
142142
getPageSearch: {
143143
useCache: false,
144144
method: "GET",
145-
url: statics.API + "/projects:search?offset={offset}&limit={limit}&text={text}&tag_ids={tagIds}&order_by={orderBy}"
145+
url: statics.API + "/projects:search?offset={offset}&limit={limit}&text={text}&tag_ids={tagIds}&order_by={orderBy}&type=user"
146146
},
147147
getPageTrashed: {
148148
useCache: false,
149149
method: "GET",
150-
url: statics.API + "/projects:search?filters={%22trashed%22:%22true%22}&offset={offset}&limit={limit}&order_by={orderBy}"
150+
url: statics.API + "/projects:search?filters={%22trashed%22:%22true%22}&offset={offset}&limit={limit}&order_by={orderBy}&type=user"
151151
},
152152
postToTemplate: {
153153
method: "POST",

services/web/server/src/simcore_service_webserver/api/v0/openapi.yaml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4608,6 +4608,20 @@ paths:
46084608
type: integer
46094609
default: 0
46104610
title: Offset
4611+
- name: type
4612+
in: query
4613+
required: false
4614+
schema:
4615+
$ref: '#/components/schemas/ProjectTypeAPI'
4616+
default: all
4617+
- name: template_type
4618+
in: query
4619+
required: false
4620+
schema:
4621+
anyOf:
4622+
- $ref: '#/components/schemas/ProjectTemplateType'
4623+
- type: 'null'
4624+
title: Template Type
46114625
- name: text
46124626
in: query
46134627
required: false

services/web/server/src/simcore_service_webserver/projects/_controller/projects_rest.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,8 @@ async def list_projects_full_search(request: web.Request):
187187
user_id=req_ctx.user_id,
188188
product_name=req_ctx.product_name,
189189
trashed=query_params.filters.trashed,
190+
filter_by_project_type=query_params.project_type,
191+
filter_by_template_type=query_params.template_type,
190192
search_by_multi_columns=query_params.text,
191193
search_by_project_name=query_params.filters.search_by_project_name,
192194
offset=query_params.offset,

services/web/server/src/simcore_service_webserver/projects/_controller/projects_rest_schemas.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,8 @@ class ProjectSearchExtraQueryParams(
207207
PageQueryParameters,
208208
FiltersQueryParameters[ProjectFilters],
209209
):
210+
project_type: Annotated[ProjectTypeAPI, Field(alias="type")] = ProjectTypeAPI.all
211+
template_type: ProjectTemplateType | None = None
210212
text: Annotated[
211213
str | None,
212214
Field(
@@ -227,6 +229,20 @@ class ProjectSearchExtraQueryParams(
227229
empty_str_to_none_pre_validator
228230
)
229231

232+
_template_type_null_or_none_str_to_none_validator = field_validator(
233+
"template_type", mode="before"
234+
)(null_or_none_str_to_none_validator)
235+
236+
@model_validator(mode="after")
237+
def _check_template_type_compatibility(self):
238+
if (
239+
self.project_type in [ProjectTypeAPI.all, ProjectTypeAPI.user]
240+
and self.template_type is not None
241+
):
242+
msg = f"When {self.project_type=} is `all` or `user` the {self.template_type=} should be None"
243+
raise ValueError(msg)
244+
return self
245+
230246

231247
class ProjectsSearchQueryParams(
232248
ProjectSearchExtraQueryParams, ProjectsListOrderParams # type: ignore[misc, valid-type]

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

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,15 @@
1010

1111
from aiohttp import web
1212
from models_library.folders import FolderID, FolderQuery, FolderScope
13-
from models_library.projects import ProjectTemplateType
13+
from models_library.projects import ProjectTemplateType, ProjectType
1414
from models_library.rest_ordering import OrderBy
1515
from models_library.users import UserID
1616
from models_library.workspaces import WorkspaceID, WorkspaceQuery, WorkspaceScope
1717
from pydantic import NonNegativeInt
1818
from servicelib.utils import logged_gather
19-
from simcore_postgres_database.models.projects import ProjectType
2019
from simcore_postgres_database.webserver_models import (
2120
ProjectTemplateType as ProjectTemplateTypeDB,
2221
)
23-
from simcore_postgres_database.webserver_models import ProjectType as ProjectTypeDB
2422

2523
from ..folders import _folders_repository
2624
from ..users.api import get_user_email_legacy
@@ -80,7 +78,7 @@ async def _aggregate_data_to_projects_from_other_sources(
8078
_projects_service.add_project_states_for_user(
8179
user_id=user_id,
8280
project=prj,
83-
is_template=prj["type"] == ProjectTypeDB.TEMPLATE.value,
81+
is_template=prj["type"] == ProjectType.TEMPLATE.value,
8482
app=app,
8583
)
8684
for prj in db_projects
@@ -202,13 +200,15 @@ async def list_projects( # pylint: disable=too-many-arguments
202200
return final_projects, total_number_projects
203201

204202

205-
async def list_projects_full_depth(
203+
async def list_projects_full_depth( # pylint: disable=too-many-arguments
206204
app: web.Application,
207205
*,
208206
user_id: UserID,
209207
product_name: str,
210208
# attrs filter
211209
trashed: bool | None,
210+
filter_by_project_type: ProjectTypeAPI,
211+
filter_by_template_type: ProjectTemplateType | None,
212212
# pagination
213213
offset: NonNegativeInt,
214214
limit: int,
@@ -225,7 +225,14 @@ async def list_projects_full_depth(
225225
workspace_query=WorkspaceQuery(workspace_scope=WorkspaceScope.ALL),
226226
folder_query=FolderQuery(folder_scope=FolderScope.ALL),
227227
filter_trashed=trashed,
228-
filter_by_project_type=ProjectType.STANDARD,
228+
filter_by_project_type=ProjectTypeAPI.to_project_type_db(
229+
filter_by_project_type
230+
),
231+
filter_by_template_type=(
232+
ProjectTemplateTypeDB(filter_by_template_type)
233+
if filter_by_template_type
234+
else None
235+
),
229236
search_by_multi_columns=search_by_multi_columns,
230237
search_by_project_name=search_by_project_name,
231238
offset=offset,

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
ProjectRunningConflictError,
2727
ProjectsBatchDeleteError,
2828
)
29-
from .models import ProjectDict, ProjectPatchInternalExtended
29+
from .models import ProjectDict, ProjectPatchInternalExtended, ProjectTypeAPI
3030

3131
_logger = logging.getLogger(__name__)
3232

@@ -172,6 +172,8 @@ async def list_explicitly_trashed_projects(
172172
user_id=user_id,
173173
product_name=product_name,
174174
trashed=True,
175+
filter_by_project_type=ProjectTypeAPI.user,
176+
filter_by_template_type=None,
175177
offset=page_params.offset,
176178
limit=page_params.limit,
177179
order_by=OrderBy(field=IDStr("trashed"), direction=OrderDirection.ASC),

services/web/server/tests/unit/with_dbs/03/trash/test_trash_rest.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -980,7 +980,7 @@ async def test_trash_project_in_subfolder(
980980
assert f"{url}" == "/v0/projects:search"
981981

982982
resp = await client.get(
983-
"/v0/projects:search", params={"filters": '{"trashed": true}'}
983+
"/v0/projects:search", params={"filters": '{"trashed": true}', "type": "user"}
984984
)
985985
await assert_status(resp, status.HTTP_200_OK)
986986
page = Page[ProjectGet].model_validate(await resp.json())
@@ -1008,14 +1008,14 @@ async def test_trash_project_in_subfolder(
10081008
await assert_status(resp, status.HTTP_204_NO_CONTENT)
10091009

10101010
resp = await client.get(
1011-
"/v0/projects:search", params={"filters": '{"trashed": true}'}
1011+
"/v0/projects:search", params={"filters": '{"trashed": true}', "type": "user"}
10121012
)
10131013
await assert_status(resp, status.HTTP_200_OK)
10141014
page = Page[ProjectGet].model_validate(await resp.json())
10151015
assert page.meta.total == 0
10161016

10171017
resp = await client.get(
1018-
"/v0/projects:search", params={"filters": '{"trashed": false}'}
1018+
"/v0/projects:search", params={"filters": '{"trashed": false}', "type": "user"}
10191019
)
10201020
await assert_status(resp, status.HTTP_200_OK)
10211021
page = Page[ProjectGet].model_validate(await resp.json())
@@ -1043,7 +1043,9 @@ async def test_trash_project_explitictly_and_empty_trash_bin(
10431043
await assert_status(resp, status.HTTP_204_NO_CONTENT)
10441044

10451045
# LIST trashed projects
1046-
resp = await client.get("/v0/projects", params={"filters": '{"trashed": true}'})
1046+
resp = await client.get(
1047+
"/v0/projects", params={"filters": '{"trashed": true}', "type": "user"}
1048+
)
10471049
await assert_status(resp, status.HTTP_200_OK)
10481050

10491051
page = Page[ProjectListItem].model_validate(await resp.json())
@@ -1126,7 +1128,7 @@ async def test_trash_folder_with_subfolder_and_project_and_empty_bin(
11261128

11271129
# - LIST trashed projects (will show only explicit!)
11281130
resp = await client.get(
1129-
"/v0/projects:search", params={"filters": '{"trashed": true}'}
1131+
"/v0/projects:search", params={"filters": '{"trashed": true}', "type": "user"}
11301132
)
11311133
await assert_status(resp, status.HTTP_200_OK)
11321134
page = Page[ProjectListItem].model_validate(await resp.json())
@@ -1186,7 +1188,7 @@ async def test_trash_folder_with_subfolder_and_project_and_empty_bin(
11861188

11871189
# - LIST trashed projects (will show only explicit!)
11881190
resp = await client.get(
1189-
"/v0/projects:search", params={"filters": '{"trashed": true}'}
1191+
"/v0/projects:search", params={"filters": '{"trashed": true}', "type": "user"}
11901192
)
11911193
await assert_status(resp, status.HTTP_200_OK)
11921194
page = Page[ProjectListItem].model_validate(await resp.json())

0 commit comments

Comments
 (0)