Skip to content

Commit

Permalink
🐛 Add project name search parameter for project listing for the API s…
Browse files Browse the repository at this point in the history
…erver usecase. (ITISFoundation#7066)
  • Loading branch information
matusdrobuliak66 authored Jan 22, 2025
1 parent 21561a6 commit e2aeff5
Show file tree
Hide file tree
Showing 6 changed files with 63 additions and 24 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# pylint: disable=too-many-public-methods

import json
import logging
import urllib.parse
from collections.abc import Mapping
Expand Down Expand Up @@ -185,14 +186,16 @@ async def _page_projects(
limit: int,
offset: int,
show_hidden: bool,
search: str | None = None,
search_by_project_name: str | None = None,
) -> Page[ProjectGet]:
assert 1 <= limit <= MAXIMUM_NUMBER_OF_ITEMS_PER_PAGE # nosec
assert offset >= 0 # nosec

optional: dict[str, Any] = {}
if search is not None:
optional["search"] = search
if search_by_project_name is not None:
filters_dict = {"search_by_project_name": search_by_project_name}
filters_json = json.dumps(filters_dict)
optional["filters"] = filters_json

with service_exception_handler(
service_name="Webserver",
Expand Down Expand Up @@ -353,9 +356,7 @@ async def get_projects_w_solver_page(
limit=limit,
offset=offset,
show_hidden=True,
# WARNING: better way to match jobs with projects (Next PR if this works fine!)
# WARNING: search text has a limit that I needed to increase for the example!
search=solver_name,
search_by_project_name=solver_name,
)

async def get_projects_page(self, *, limit: int, offset: int):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,14 @@ async def list_projects( # pylint: disable=too-many-arguments
project_type: ProjectTypeAPI,
show_hidden: bool,
trashed: bool | None,
# search
search_by_multi_columns: str | None = None,
search_by_project_name: str | None = None,
# pagination
offset: NonNegativeInt,
limit: int,
# ordering
order_by: OrderBy,
# search
search: str | None,
) -> tuple[list[ProjectDict], int]:
app = request.app
db = ProjectDBAPI.get_from_app_context(app)
Expand Down Expand Up @@ -116,7 +118,8 @@ async def list_projects( # pylint: disable=too-many-arguments
filter_trashed=trashed,
filter_hidden=show_hidden,
# composed attrs
filter_by_text=search,
search_by_multi_columns=search_by_multi_columns,
search_by_project_name=search_by_project_name,
# pagination
offset=offset,
limit=limit,
Expand Down Expand Up @@ -154,7 +157,8 @@ async def list_projects_full_depth(
limit: int,
order_by: OrderBy,
# search
text: str | None,
search_by_multi_columns: str | None,
search_by_project_name: str | None,
) -> tuple[list[ProjectDict], int]:
db = ProjectDBAPI.get_from_app_context(request.app)

Expand All @@ -169,9 +173,10 @@ async def list_projects_full_depth(
folder_query=FolderQuery(folder_scope=FolderScope.ALL),
filter_trashed=trashed,
filter_by_services=user_available_services,
filter_by_text=text,
filter_tag_ids_list=tag_ids_list,
filter_by_project_type=ProjectType.STANDARD,
search_by_multi_columns=search_by_multi_columns,
search_by_project_name=search_by_project_name,
offset=offset,
limit=limit,
order_by=order_by,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,12 +203,13 @@ async def list_projects(request: web.Request):
project_type=query_params.project_type,
show_hidden=query_params.show_hidden,
trashed=query_params.filters.trashed,
limit=query_params.limit,
offset=query_params.offset,
search=query_params.search,
order_by=OrderBy.model_construct(**query_params.order_by.model_dump()),
folder_id=query_params.folder_id,
workspace_id=query_params.workspace_id,
search_by_multi_columns=query_params.search,
search_by_project_name=query_params.filters.search_by_project_name,
offset=query_params.offset,
limit=query_params.limit,
order_by=OrderBy.model_construct(**query_params.order_by.model_dump()),
)

page = Page[ProjectDict].model_validate(
Expand Down Expand Up @@ -246,10 +247,11 @@ async def list_projects_full_search(request: web.Request):
product_name=req_ctx.product_name,
trashed=query_params.filters.trashed,
tag_ids_list=tag_ids_list,
search_by_multi_columns=query_params.text,
search_by_project_name=query_params.filters.search_by_project_name,
offset=query_params.offset,
limit=query_params.limit,
order_by=OrderBy.model_construct(**query_params.order_by.model_dump()),
text=query_params.text,
)

page = Page[ProjectDict].model_validate(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,10 @@ class ProjectFilters(Filters):
default=False,
description="Set to true to list trashed, false to list non-trashed (default), None to list all",
)
search_by_project_name: str | None = Field(
default=None,
description="A search query to filter projects by their name. This field performs a case-insensitive partial match against the project name field.",
)


ProjectsListOrderParams = create_ordering_query_model_class(
Expand Down
22 changes: 14 additions & 8 deletions services/web/server/src/simcore_service_webserver/projects/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -380,8 +380,10 @@ async def list_projects( # pylint: disable=too-many-arguments,too-many-statemen
filter_published: bool | None = None,
filter_hidden: bool | None = False,
filter_trashed: bool | None = False,
filter_by_text: str | None = None,
filter_tag_ids_list: list[int] | None = None,
# search
search_by_multi_columns: str | None = None,
search_by_project_name: str | None = None,
# pagination
offset: int | None = 0,
limit: int | None = None,
Expand Down Expand Up @@ -472,7 +474,7 @@ async def list_projects( # pylint: disable=too-many-arguments,too-many-statemen
& (projects_to_products.c.product_name == product_name)
)
)
if filter_by_text is not None:
if search_by_multi_columns is not None:
private_workspace_query = private_workspace_query.join(
users, users.c.id == projects.c.prj_owner, isouter=True
)
Expand Down Expand Up @@ -546,7 +548,7 @@ async def list_projects( # pylint: disable=too-many-arguments,too-many-statemen
== workspace_query.workspace_id # <-- Specific shared workspace
)

if filter_by_text is not None:
if search_by_multi_columns is not None:
# NOTE: fields searched with text include user's email
shared_workspace_query = shared_workspace_query.join(
users, users.c.id == projects.c.prj_owner, isouter=True
Expand Down Expand Up @@ -582,12 +584,16 @@ async def list_projects( # pylint: disable=too-many-arguments,too-many-statemen
# not marked as trashed
else projects.c.trashed.is_(None)
)
if filter_by_text is not None:
if search_by_multi_columns is not None:
attributes_filters.append(
(projects.c.name.ilike(f"%{filter_by_text}%"))
| (projects.c.description.ilike(f"%{filter_by_text}%"))
| (projects.c.uuid.ilike(f"%{filter_by_text}%"))
| (users.c.name.ilike(f"%{filter_by_text}%"))
(projects.c.name.ilike(f"%{search_by_multi_columns}%"))
| (projects.c.description.ilike(f"%{search_by_multi_columns}%"))
| (projects.c.uuid.ilike(f"%{search_by_multi_columns}%"))
| (users.c.name.ilike(f"%{search_by_multi_columns}%"))
)
if search_by_project_name is not None:
attributes_filters.append(
projects.c.name.like(f"%{search_by_project_name}%")
)
if filter_tag_ids_list:
attributes_filters.append(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,27 @@ async def test_list_projects_with_search_parameter(
data, 1, 0, 1, "/v0/projects?search=nAmE+5&offset=0&limit=20", 1
)

# Now we will test specific project name search (used by the API server)
query_parameters = {"filters": '{"search_by_project_name": "Yoda"}'}
url = base_url.with_query(**query_parameters)
assert (
f"{url}"
== f"/{api_version_prefix}/projects?filters=%7B%22search_by_project_name%22:+%22Yoda%22%7D"
)

resp = await client.get(f"{url}")
data = await resp.json()

assert resp.status == 200
_assert_response_data(
data,
1,
0,
1,
"/v0/projects?filters=%7B%22search_by_project_name%22:+%22Yoda%22%7D&offset=0&limit=20",
1,
)

# Now we will test part of uuid search
query_parameters = {"search": "2-fe1b-11ed-b038-cdb1"}
url = base_url.with_query(**query_parameters)
Expand Down

0 comments on commit e2aeff5

Please sign in to comment.