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

🎨 make folders optional + improvements ⚠️ #6155

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
d729b90
make folders optional
matusdrobuliak66 Aug 8, 2024
b9cf440
Merge branch 'master' into enable-disable-folders
matusdrobuliak66 Aug 8, 2024
dc27a0a
review @sanderegg
matusdrobuliak66 Aug 8, 2024
48dafdc
Merge branch 'master' into enable-disable-folders
matusdrobuliak66 Aug 13, 2024
993f5de
review @pcrespov
matusdrobuliak66 Aug 13, 2024
ff821ff
review @pcrespov
matusdrobuliak66 Aug 13, 2024
3796b70
Merge branch 'master' into enable-disable-folders
matusdrobuliak66 Aug 13, 2024
fb284ea
improvements
matusdrobuliak66 Aug 13, 2024
d08a5cd
fix open api specs
matusdrobuliak66 Aug 13, 2024
87f1346
adding test
matusdrobuliak66 Aug 13, 2024
0c7c64b
review @pcrespov
matusdrobuliak66 Aug 13, 2024
61bdf2c
Merge branch 'master' into enable-disable-folders
matusdrobuliak66 Aug 13, 2024
8a18da4
adding order by to folders
matusdrobuliak66 Aug 13, 2024
d8a72db
Merge branch 'master' into enable-disable-folders
matusdrobuliak66 Aug 14, 2024
d0b19cd
adding pagination to folders
matusdrobuliak66 Aug 14, 2024
402548d
adding pagination tests
matusdrobuliak66 Aug 14, 2024
e8e2a1f
add filter by product name
matusdrobuliak66 Aug 14, 2024
0fa36a8
add WEBSERVER_FOLDERS env var
matusdrobuliak66 Aug 14, 2024
ba2efa4
fix test
matusdrobuliak66 Aug 14, 2024
3e76b73
Merge branch 'master' into enable-disable-folders
matusdrobuliak66 Aug 14, 2024
203175d
fix camel case
matusdrobuliak66 Aug 14, 2024
fc1e503
fix test
matusdrobuliak66 Aug 14, 2024
1dc0dcc
Merge branch 'master' into enable-disable-folders
matusdrobuliak66 Aug 14, 2024
e4989a0
fix test
matusdrobuliak66 Aug 14, 2024
98a81ab
Merge branch 'enable-disable-folders' of github.com:matusdrobuliak66/…
matusdrobuliak66 Aug 14, 2024
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
1 change: 1 addition & 0 deletions .env-devel
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,7 @@ WEBSERVER_DIAGNOSTICS={}
WEBSERVER_EMAIL={}
WEBSERVER_EXPORTER={}
WEBSERVER_FRONTEND={}
WEBSERVER_FOLDERS=1
WEBSERVER_GARBAGE_COLLECTOR=null
WEBSERVER_GROUPS=1
WEBSERVER_GUNICORN_CMD_ARGS=--timeout=180
Expand Down
9 changes: 4 additions & 5 deletions api/specs/web-server/_folders.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
FolderGet,
PutFolderBodyParams,
)
from models_library.api_schemas_webserver.wallets import WalletGet
from models_library.generics import Envelope
from models_library.rest_pagination import PageQueryParameters
from pydantic import Json
Expand All @@ -39,7 +38,7 @@

@router.post(
"/folders",
response_model=Envelope[WalletGet],
response_model=Envelope[FolderGet],
status_code=status.HTTP_201_CREATED,
)
async def create_folder(_body: CreateFolderBodyParams):
Expand All @@ -55,10 +54,10 @@ async def list_folders(
order_by: Annotated[
Json,
Query(
description="Order by field (name|description) and direction (asc|desc). The default sorting order is ascending.",
description="Order by field (modified_at|name|description) and direction (asc|desc). The default sorting order is ascending.",
example='{"field": "name", "direction": "desc"}',
),
] = '{"field": "name", "direction": "desc"}',
] = '{"field": "modified_at", "direction": "desc"}',
):
...

Expand All @@ -83,7 +82,7 @@ async def replace_folder(

@router.delete(
"/folders/{folder_id}",
response_model=Envelope[FolderGet],
status_code=status.HTTP_204_NO_CONTENT,
)
async def delete_folder(_path: Annotated[FoldersPathParams, Depends()]):
...
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
from datetime import datetime
from typing import NamedTuple

from models_library.basic_types import IDStr
from models_library.folders import FolderID
from models_library.projects_access import AccessRights
from models_library.users import GroupID
from pydantic import Extra
from models_library.utils.common_validators import null_or_none_str_to_none_validator
from pydantic import Extra, PositiveInt, validator

from ._base import InputSchema, OutputSchema

Expand All @@ -21,6 +23,11 @@ class FolderGet(OutputSchema):
access_rights: dict[GroupID, AccessRights]


class FolderGetPage(NamedTuple):
items: list[FolderGet]
total: PositiveInt


class CreateFolderBodyParams(InputSchema):
name: IDStr
description: str
Expand All @@ -29,6 +36,10 @@ class CreateFolderBodyParams(InputSchema):
class Config:
extra = Extra.forbid

_null_or_none_str_to_none_validator = validator(
"parent_folder_id", allow_reuse=True, pre=True
)(null_or_none_str_to_none_validator)


class PutFolderBodyParams(InputSchema):
name: IDStr
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,9 @@ def ensure_unique_dict_values_validator(dict_data: dict) -> dict:
msg = f"Dictionary values must be unique, provided: {dict_data}"
raise ValueError(msg)
return dict_data


def null_or_none_str_to_none_validator(value: Any):
if isinstance(value, str) and value.lower() in ("null", "none"):
return None
return value
28 changes: 28 additions & 0 deletions packages/models-library/tests/test_utils_common_validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
create_enums_pre_validator,
empty_str_to_none_pre_validator,
none_to_empty_str_pre_validator,
null_or_none_str_to_none_validator,
)
from pydantic import BaseModel, ValidationError, validator

Expand Down Expand Up @@ -59,3 +60,30 @@ class Model(BaseModel):

model = Model.parse_obj({"message": ""})
assert model == Model.parse_obj({"message": None})


def test_null_or_none_str_to_none_validator():
class Model(BaseModel):
message: str | None

_null_or_none_str_to_none_validator = validator(
"message", allow_reuse=True, pre=True
)(null_or_none_str_to_none_validator)

model = Model.parse_obj({"message": "none"})
assert model == Model.parse_obj({"message": None})

model = Model.parse_obj({"message": "null"})
assert model == Model.parse_obj({"message": None})

model = Model.parse_obj({"message": "NoNe"})
assert model == Model.parse_obj({"message": None})

model = Model.parse_obj({"message": "NuLl"})
assert model == Model.parse_obj({"message": None})

model = Model.parse_obj({"message": None})
assert model == Model.parse_obj({"message": None})

model = Model.parse_obj({"message": ""})
assert model == Model.parse_obj({"message": ""})
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from datetime import datetime
from enum import Enum
from functools import reduce
from typing import Any, ClassVar, Final, TypeAlias
from typing import Any, ClassVar, Final, TypeAlias, cast

import sqlalchemy as sa
from aiopg.sa.connection import SAConnection
Expand All @@ -20,12 +20,14 @@
parse_obj_as,
)
from pydantic.errors import PydanticErrorMixin
from simcore_postgres_database.utils_ordering import OrderByDict
from sqlalchemy.dialects import postgresql
from sqlalchemy.sql.elements import ColumnElement
from sqlalchemy.sql.selectable import ScalarSelect

from .models.folders import folders, folders_access_rights, folders_to_projects
from .models.groups import GroupType, groups
from .utils_ordering import OrderDirection

_ProductName: TypeAlias = str
_ProjectID: TypeAlias = uuid.UUID
Expand Down Expand Up @@ -986,8 +988,11 @@ async def folder_list(
*,
offset: NonNegativeInt,
limit: NonNegativeInt,
order_by: OrderByDict = OrderByDict(
field="modified", direction=OrderDirection.DESC
),
_required_permissions=_requires(_BasePermissions.LIST_FOLDERS), # noqa: B008
) -> list[FolderEntry]:
) -> tuple[int, list[FolderEntry]]:
"""
Raises:
FolderNotFoundError
Expand Down Expand Up @@ -1015,7 +1020,7 @@ async def folder_list(
access_via_gid = resolved_access_rights.gid
access_via_folder_id = resolved_access_rights.folder_id

query = (
base_query = (
sa.select(
folders,
folders_access_rights,
Expand Down Expand Up @@ -1047,14 +1052,30 @@ async def folder_list(
if folder_id is None
else True
)
.offset(offset)
.limit(limit)
.where(folders.c.product_name == product_name)
)

async for entry in connection.execute(query):
# Select total count from base_query
subquery = base_query.subquery()
count_query = sa.select(sa.func.count()).select_from(subquery)
count_result = await connection.execute(count_query)
total_count = await count_result.scalar()

# Ordering and pagination
if order_by["direction"] == OrderDirection.ASC:
list_query = base_query.order_by(
sa.asc(getattr(folders.c, order_by["field"]))
)
else:
list_query = base_query.order_by(
sa.desc(getattr(folders.c, order_by["field"]))
)
list_query = list_query.offset(offset).limit(limit)

async for entry in connection.execute(list_query):
results.append(FolderEntry.from_orm(entry)) # noqa: PERF401s

return results
return cast(int, total_count), results


async def folder_get(
Expand Down Expand Up @@ -1101,6 +1122,7 @@ async def folder_get(
if folder_id is None
else True
)
.where(folders.c.product_name == product_name)
)

query_result: RowProxy | None = await (
Expand All @@ -1113,3 +1135,6 @@ async def folder_get(
)

return FolderEntry.from_orm(query_result)


__all__ = ["OrderByDict"]
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from enum import Enum
from typing import TypedDict


class OrderDirection(str, Enum):
ASC = "asc"
DESC = "desc"


class OrderByDict(TypedDict):
field: str
direction: OrderDirection


# Example usage
order_by_example: OrderByDict = {
"field": "example_field",
"direction": OrderDirection.ASC,
}
3 changes: 2 additions & 1 deletion packages/postgres-database/tests/test_utils_folders.py
Original file line number Diff line number Diff line change
Expand Up @@ -1633,9 +1633,10 @@ async def _list_folder_as(
limit: NonNegativeInt = ALL_IN_ONE_PAGE_LIMIT,
) -> list[FolderEntry]:

return await folder_list(
total_count, folders_db = await folder_list(
connection, default_product_name, folder_id, gid, offset=offset, limit=limit
)
return folders_db


async def test_folder_list(
Expand Down
1 change: 1 addition & 0 deletions services/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -725,6 +725,7 @@ services:
WEBSERVER_TAGS: ${WEBSERVER_TAGS}
WEBSERVER_USERS: ${WEBSERVER_USERS}
WEBSERVER_VERSION_CONTROL: ${WEBSERVER_VERSION_CONTROL}
WEBSERVER_FOLDERS: ${WEBSERVER_FOLDERS}

deploy:
labels:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1296,14 +1296,14 @@ paths:
summary: List Folders
operationId: list_folders
parameters:
- description: Order by field (name|description) and direction (asc|desc). The
default sorting order is ascending.
- description: Order by field (modified_at|name|description) and direction (asc|desc).
The default sorting order is ascending.
required: false
schema:
title: Order By
description: Order by field (name|description) and direction (asc|desc).
The default sorting order is ascending.
default: '{"field": "name", "direction": "desc"}'
description: Order by field (modified_at|name|description) and direction
(asc|desc). The default sorting order is ascending.
default: '{"field": "modified_at", "direction": "desc"}'
example: '{"field": "name", "direction": "desc"}'
name: order_by
in: query
Expand Down Expand Up @@ -1349,7 +1349,7 @@ paths:
content:
application/json:
schema:
$ref: '#/components/schemas/Envelope_WalletGet_'
$ref: '#/components/schemas/Envelope_FolderGet_'
/v0/folders/{folder_id}:
get:
tags:
Expand Down Expand Up @@ -1414,12 +1414,8 @@ paths:
name: folder_id
in: path
responses:
'200':
'204':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/Envelope_FolderGet_'
/v0/folders/{folder_id}/groups/{group_id}:
put:
tags:
Expand Down Expand Up @@ -6008,8 +6004,8 @@ components:
quality: {}
accessRights:
'1':
execute_access: true
write_access: false
execute: true
write: false
key: simcore/services/comp/itis/sleeper
version: 2.2.1
version_display: 2 Xtreme
Expand Down Expand Up @@ -10788,6 +10784,7 @@ components:
title: Write
type: boolean
default: false
additionalProperties: false
ServiceInputGet:
title: ServiceInputGet
required:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ def convert_to_app_config(app_settings: ApplicationSettings) -> dict[str, Any]:
"users": {"enabled": app_settings.WEBSERVER_USERS is not None},
"version_control": {"enabled": app_settings.WEBSERVER_VERSION_CONTROL},
"wallets": {"enabled": app_settings.WEBSERVER_WALLETS},
"folders": {"enabled": app_settings.WEBSERVER_FOLDERS},
}


Expand Down Expand Up @@ -323,6 +324,7 @@ def _set_if_disabled(field_name, section):
"WEBSERVER_USERS",
"WEBSERVER_VERSION_CONTROL",
"WEBSERVER_WALLETS",
"WEBSERVER_FOLDERS",
):
section_name = settings_name.replace("WEBSERVER_", "").lower()
if section := cfg.get(section_name):
Expand Down
Loading
Loading