Skip to content

Commit

Permalink
✨ introduce search parameter to the listing workspaces (ITISFoundatio…
Browse files Browse the repository at this point in the history
…n#6872)

Co-authored-by: Odei Maiz <[email protected]>
  • Loading branch information
matusdrobuliak66 and odeimaiz authored Dec 2, 2024
1 parent 0c5a068 commit 931595e
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4540,7 +4540,7 @@ paths:
'403':
description: ProjectInvalidRightsError
'404':
description: UserDefaultWalletNotFoundError, ProjectNotFoundError
description: ProjectNotFoundError, UserDefaultWalletNotFoundError
'409':
description: ProjectTooManyProjectOpenedError
'422':
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import logging
from typing import Annotated

from models_library.basic_types import IDStr
from models_library.rest_base import RequestParameters, StrictRequestParameters
Expand All @@ -11,8 +12,9 @@
from models_library.rest_pagination import PageQueryParameters
from models_library.trash import RemoveQueryParams
from models_library.users import GroupID, UserID
from models_library.utils.common_validators import empty_str_to_none_pre_validator
from models_library.workspaces import WorkspaceID
from pydantic import BaseModel, ConfigDict, Field
from pydantic import BaseModel, BeforeValidator, ConfigDict, Field
from servicelib.request_keys import RQT_USERID_KEY

from .._constants import RQ_PRODUCT_KEY
Expand Down Expand Up @@ -46,6 +48,14 @@ class WorkspacesFilters(Filters):
default=False,
description="Set to true to list trashed, false to list non-trashed (default), None to list all",
)
text: Annotated[
str | None, BeforeValidator(empty_str_to_none_pre_validator)
] = Field(
default=None,
description="Multi column full text search",
max_length=100,
examples=["My Workspace"],
)


class WorkspacesListQueryParams(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ async def list_workspaces(
user_id: UserID,
product_name: ProductName,
filter_trashed: bool | None,
filter_by_text: str | None,
offset: NonNegativeInt,
limit: int,
order_by: OrderBy,
Expand All @@ -100,6 +101,7 @@ async def list_workspaces(
user_id=user_id,
product_name=product_name,
filter_trashed=filter_trashed,
filter_by_text=filter_by_text,
offset=offset,
limit=limit,
order_by=order_by,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ async def list_workspaces_for_user(
user_id: UserID,
product_name: ProductName,
filter_trashed: bool | None,
filter_by_text: str | None,
offset: NonNegativeInt,
limit: NonNegativeInt,
order_by: OrderBy,
Expand All @@ -140,6 +141,11 @@ async def list_workspaces_for_user(
if filter_trashed
else workspaces.c.trashed.is_(None)
)
if filter_by_text is not None:
base_query = base_query.where(
(workspaces.c.name.ilike(f"%{filter_by_text}%"))
| (workspaces.c.description.ilike(f"%{filter_by_text}%"))
)

# Select total count from base_query
subquery = base_query.subquery()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ async def list_workspaces(request: web.Request):
user_id=req_ctx.user_id,
product_name=req_ctx.product_name,
filter_trashed=query_params.filters.trashed,
filter_by_text=query_params.filters.text,
offset=query_params.offset,
limit=query_params.limit,
order_by=OrderBy.model_construct(**query_params.order_by.model_dump()),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from collections.abc import AsyncIterator

# pylint: disable=redefined-outer-name
# pylint: disable=unused-argument
# pylint: disable=unused-variable
Expand Down Expand Up @@ -46,6 +48,7 @@ async def test_workspaces_user_role_permissions(
logged_user: UserInfoDict,
user_project: ProjectDict,
expected: ExpectedResponse,
workspaces_clean_db: AsyncIterator[None],
):
assert client.app

Expand All @@ -60,6 +63,7 @@ async def test_workspaces_workflow(
logged_user: UserInfoDict,
user_project: ProjectDict,
expected: HTTPStatus,
workspaces_clean_db: AsyncIterator[None],
):
assert client.app

Expand Down Expand Up @@ -139,13 +143,87 @@ async def test_workspaces_workflow(


@pytest.mark.parametrize("user_role,expected", [(UserRole.USER, status.HTTP_200_OK)])
async def test_project_workspace_movement_full_workflow(
async def test_list_workspaces_with_text_search(
client: TestClient,
logged_user: UserInfoDict,
user_project: ProjectDict,
expected: HTTPStatus,
workspaces_clean_db: AsyncIterator[None],
):
assert client.app

# NOTE: MD: not yet implemented
# SEE https://github.com/ITISFoundation/osparc-simcore/issues/6778
# list user workspaces
url = client.app.router["list_workspaces"].url_for()
resp = await client.get(f"{url}")
data, _ = await assert_status(resp, status.HTTP_200_OK)
assert data == []

# CREATE a new workspace
url = client.app.router["create_workspace"].url_for()
resp = await client.post(
f"{url}",
json={
"name": "My first workspace",
"description": "Custom description",
"thumbnail": None,
},
)
data, _ = await assert_status(resp, status.HTTP_201_CREATED)
added_workspace = WorkspaceGet.model_validate(data)

# CREATE a new workspace
url = client.app.router["create_workspace"].url_for()
resp = await client.post(
f"{url}",
json={
"name": "My second workspace",
"description": "Sharing important projects",
"thumbnail": None,
},
)
data, _ = await assert_status(resp, status.HTTP_201_CREATED)
added_workspace = WorkspaceGet.model_validate(data)

# LIST user workspaces
url = client.app.router["list_workspaces"].url_for()
resp = await client.get(f"{url}")
data, _, meta, links = await assert_status(
resp, status.HTTP_200_OK, include_meta=True, include_links=True
)
assert len(data) == 2

# LIST user workspaces
url = (
client.app.router["list_workspaces"]
.url_for()
.with_query({"filters": '{"text": "first"}'})
)
resp = await client.get(f"{url}")
data, _, meta, links = await assert_status(
resp, status.HTTP_200_OK, include_meta=True, include_links=True
)
assert len(data) == 1

# LIST user workspaces
url = (
client.app.router["list_workspaces"]
.url_for()
.with_query({"filters": '{"text": "important"}'})
)
resp = await client.get(f"{url}")
data, _, meta, links = await assert_status(
resp, status.HTTP_200_OK, include_meta=True, include_links=True
)
assert len(data) == 1

# LIST user workspaces
url = (
client.app.router["list_workspaces"]
.url_for()
.with_query({"filters": '{"text": "non-existing"}'})
)
resp = await client.get(f"{url}")
data, _, meta, links = await assert_status(
resp, status.HTTP_200_OK, include_meta=True, include_links=True
)
assert len(data) == 0

0 comments on commit 931595e

Please sign in to comment.