Skip to content

Commit

Permalink
Merge branch 'master' into e2e/fix-tests-iii
Browse files Browse the repository at this point in the history
  • Loading branch information
odeimaiz authored Nov 18, 2024
2 parents 05b1c78 + 625d2cb commit 0d6606f
Show file tree
Hide file tree
Showing 43 changed files with 906 additions and 872 deletions.
66 changes: 61 additions & 5 deletions api/specs/web-server/_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,68 @@
from typing import Any, ClassVar, NamedTuple

import yaml
from fastapi import FastAPI
from fastapi import FastAPI, Query
from models_library.basic_types import LogLevel
from pydantic import BaseModel, Field
from models_library.utils.json_serialization import json_dumps
from pydantic import BaseModel, Field, create_model
from pydantic.fields import FieldInfo
from servicelib.fastapi.openapi import override_fastapi_openapi_method

CURRENT_DIR = Path(sys.argv[0] if __name__ == "__main__" else __file__).resolve().parent


def _create_json_type(**schema_extras):
class _Json(str):
__slots__ = ()

@classmethod
def __modify_schema__(cls, field_schema: dict[str, Any]) -> None:
# openapi.json schema is corrected here
field_schema.update(
type="string",
# format="json-string" NOTE: we need to get rid of openapi-core in web-server before using this!
)
if schema_extras:
field_schema.update(schema_extras)

return _Json


def as_query(model_class: type[BaseModel]) -> type[BaseModel]:
fields = {}
for field_name, model_field in model_class.__fields__.items():

field_type = model_field.type_
default_value = model_field.default

kwargs = {
"alias": model_field.field_info.alias,
"title": model_field.field_info.title,
"description": model_field.field_info.description,
"gt": model_field.field_info.gt,
"ge": model_field.field_info.ge,
"lt": model_field.field_info.lt,
"le": model_field.field_info.le,
"min_length": model_field.field_info.min_length,
"max_length": model_field.field_info.max_length,
"regex": model_field.field_info.regex,
**model_field.field_info.extra,
}

if issubclass(field_type, BaseModel):
# Complex fields
field_type = _create_json_type(
description=kwargs["description"],
example=kwargs.get("example_json"),
)
default_value = json_dumps(default_value) if default_value else None

fields[field_name] = (field_type, Query(default=default_value, **kwargs))

new_model_name = f"{model_class.__name__}Query"
return create_model(new_model_name, **fields)


class Log(BaseModel):
level: LogLevel | None = Field("INFO", description="log level")
message: str = Field(
Expand Down Expand Up @@ -120,6 +173,9 @@ def assert_handler_signature_against_model(
for field in model_cls.__fields__.values()
]

assert {p.name for p in implemented_params}.issubset( # nosec
{p.name for p in specs_params}
), f"Entrypoint {handler} does not implement OAS"
implemented_names = {p.name for p in implemented_params}
specified_names = {p.name for p in specs_params}

if not implemented_names.issubset(specified_names):
msg = f"Entrypoint {handler} does not implement OAS: {implemented_names} not in {specified_names}"
raise AssertionError(msg)
57 changes: 20 additions & 37 deletions api/specs/web-server/_folders.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,20 @@

from typing import Annotated

from fastapi import APIRouter, Depends, Query, status
from _common import as_query
from fastapi import APIRouter, Depends, status
from models_library.api_schemas_webserver.folders_v2 import (
CreateFolderBodyParams,
FolderGet,
PutFolderBodyParams,
)
from models_library.folders import FolderID
from models_library.generics import Envelope
from models_library.rest_pagination import PageQueryParameters
from models_library.workspaces import WorkspaceID
from pydantic import Json
from simcore_service_webserver._meta import API_VTAG
from simcore_service_webserver.folders._models import FolderFilters, FoldersPathParams
from simcore_service_webserver.folders._models import (
FolderSearchQueryParams,
FoldersListQueryParams,
FoldersPathParams,
)

router = APIRouter(
prefix=f"/{API_VTAG}",
Expand All @@ -36,7 +37,9 @@
response_model=Envelope[FolderGet],
status_code=status.HTTP_201_CREATED,
)
async def create_folder(_body: CreateFolderBodyParams):
async def create_folder(
_body: CreateFolderBodyParams,
):
...


Expand All @@ -45,20 +48,7 @@ async def create_folder(_body: CreateFolderBodyParams):
response_model=Envelope[list[FolderGet]],
)
async def list_folders(
params: Annotated[PageQueryParameters, Depends()],
folder_id: FolderID | None = None,
workspace_id: WorkspaceID | None = None,
order_by: Annotated[
Json,
Query(
description="Order by field (modified_at|name|description) and direction (asc|desc). The default sorting order is ascending.",
example='{"field": "name", "direction": "desc"}',
),
] = '{"field": "modified_at", "direction": "desc"}',
filters: Annotated[
Json | None,
Query(description=FolderFilters.schema_json(indent=1)),
] = None,
_query: Annotated[as_query(FoldersListQueryParams), Depends()],
):
...

Expand All @@ -68,19 +58,7 @@ async def list_folders(
response_model=Envelope[list[FolderGet]],
)
async def list_folders_full_search(
params: Annotated[PageQueryParameters, Depends()],
text: str | None = None,
order_by: Annotated[
Json,
Query(
description="Order by field (modified_at|name|description) and direction (asc|desc). The default sorting order is ascending.",
example='{"field": "name", "direction": "desc"}',
),
] = '{"field": "modified_at", "direction": "desc"}',
filters: Annotated[
Json | None,
Query(description=FolderFilters.schema_json(indent=1)),
] = None,
_query: Annotated[as_query(FolderSearchQueryParams), Depends()],
):
...

Expand All @@ -89,7 +67,9 @@ async def list_folders_full_search(
"/folders/{folder_id}",
response_model=Envelope[FolderGet],
)
async def get_folder(_path: Annotated[FoldersPathParams, Depends()]):
async def get_folder(
_path: Annotated[FoldersPathParams, Depends()],
):
...


Expand All @@ -98,7 +78,8 @@ async def get_folder(_path: Annotated[FoldersPathParams, Depends()]):
response_model=Envelope[FolderGet],
)
async def replace_folder(
_path: Annotated[FoldersPathParams, Depends()], _body: PutFolderBodyParams
_path: Annotated[FoldersPathParams, Depends()],
_body: PutFolderBodyParams,
):
...

Expand All @@ -107,5 +88,7 @@ async def replace_folder(
"/folders/{folder_id}",
status_code=status.HTTP_204_NO_CONTENT,
)
async def delete_folder(_path: Annotated[FoldersPathParams, Depends()]):
async def delete_folder(
_path: Annotated[FoldersPathParams, Depends()],
):
...
28 changes: 14 additions & 14 deletions api/specs/web-server/_groups.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ async def list_groups():
response_model=Envelope[GroupGet],
status_code=status.HTTP_201_CREATED,
)
async def create_group(_b: GroupCreate):
async def create_group(_body: GroupCreate):
"""
Creates an organization group
"""
Expand All @@ -58,7 +58,7 @@ async def create_group(_b: GroupCreate):
"/groups/{gid}",
response_model=Envelope[GroupGet],
)
async def get_group(_p: Annotated[_GroupPathParams, Depends()]):
async def get_group(_path: Annotated[_GroupPathParams, Depends()]):
"""
Get an organization group
"""
Expand All @@ -69,8 +69,8 @@ async def get_group(_p: Annotated[_GroupPathParams, Depends()]):
response_model=Envelope[GroupGet],
)
async def update_group(
_p: Annotated[_GroupPathParams, Depends()],
_b: GroupUpdate,
_path: Annotated[_GroupPathParams, Depends()],
_body: GroupUpdate,
):
"""
Updates organization groups
Expand All @@ -81,7 +81,7 @@ async def update_group(
"/groups/{gid}",
status_code=status.HTTP_204_NO_CONTENT,
)
async def delete_group(_p: Annotated[_GroupPathParams, Depends()]):
async def delete_group(_path: Annotated[_GroupPathParams, Depends()]):
"""
Deletes organization groups
"""
Expand All @@ -91,7 +91,7 @@ async def delete_group(_p: Annotated[_GroupPathParams, Depends()]):
"/groups/{gid}/users",
response_model=Envelope[list[GroupUserGet]],
)
async def get_all_group_users(_p: Annotated[_GroupPathParams, Depends()]):
async def get_all_group_users(_path: Annotated[_GroupPathParams, Depends()]):
"""
Gets users in organization groups
"""
Expand All @@ -102,8 +102,8 @@ async def get_all_group_users(_p: Annotated[_GroupPathParams, Depends()]):
status_code=status.HTTP_204_NO_CONTENT,
)
async def add_group_user(
_p: Annotated[_GroupPathParams, Depends()],
_b: GroupUserAdd,
_path: Annotated[_GroupPathParams, Depends()],
_body: GroupUserAdd,
):
"""
Adds a user to an organization group
Expand All @@ -115,7 +115,7 @@ async def add_group_user(
response_model=Envelope[GroupUserGet],
)
async def get_group_user(
_p: Annotated[_GroupUserPathParams, Depends()],
_path: Annotated[_GroupUserPathParams, Depends()],
):
"""
Gets specific user in an organization group
Expand All @@ -127,8 +127,8 @@ async def get_group_user(
response_model=Envelope[GroupUserGet],
)
async def update_group_user(
_p: Annotated[_GroupUserPathParams, Depends()],
_b: GroupUserUpdate,
_path: Annotated[_GroupUserPathParams, Depends()],
_body: GroupUserUpdate,
):
"""
Updates user (access-rights) to an organization group
Expand All @@ -140,7 +140,7 @@ async def update_group_user(
status_code=status.HTTP_204_NO_CONTENT,
)
async def delete_group_user(
_p: Annotated[_GroupUserPathParams, Depends()],
_path: Annotated[_GroupUserPathParams, Depends()],
):
"""
Removes a user from an organization group
Expand All @@ -157,8 +157,8 @@ async def delete_group_user(
response_model=Envelope[dict[str, Any]],
)
async def get_group_classifiers(
_p: Annotated[_GroupPathParams, Depends()],
_q: Annotated[_ClassifiersQuery, Depends()],
_path: Annotated[_GroupPathParams, Depends()],
_query: Annotated[_ClassifiersQuery, Depends()],
):
...

Expand Down
Loading

0 comments on commit 0d6606f

Please sign in to comment.