Skip to content

Commit

Permalink
Merge branch 'master' into fix/drop-on-folder
Browse files Browse the repository at this point in the history
  • Loading branch information
odeimaiz authored Jan 6, 2025
2 parents 69b3f0f + fee208d commit e61178a
Show file tree
Hide file tree
Showing 64 changed files with 1,954 additions and 541 deletions.
2 changes: 1 addition & 1 deletion api/specs/web-server/_admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
response_model=Envelope[Union[EmailTestFailed, EmailTestPassed]],
)
async def test_email(
_test: TestEmail, x_simcore_products_name: str | None = Header(default=None)
_body: TestEmail, x_simcore_products_name: str | None = Header(default=None)
):
# X-Simcore-Products-Name
...
11 changes: 10 additions & 1 deletion api/specs/web-server/_groups.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
# pylint: disable=too-many-arguments


from enum import Enum
from typing import Annotated, Any

from fastapi import APIRouter, Depends, status
Expand Down Expand Up @@ -87,19 +88,24 @@ async def delete_group(_path: Annotated[GroupsPathParams, Depends()]):
"""


_extra_tags: list[str | Enum] = ["users"]


@router.get(
"/groups/{gid}/users",
response_model=Envelope[list[GroupUserGet]],
tags=_extra_tags,
)
async def get_all_group_users(_path: Annotated[GroupsPathParams, Depends()]):
"""
Gets users in organization groups
Gets users in organization or primary groups
"""


@router.post(
"/groups/{gid}/users",
status_code=status.HTTP_204_NO_CONTENT,
tags=_extra_tags,
)
async def add_group_user(
_path: Annotated[GroupsPathParams, Depends()],
Expand All @@ -113,6 +119,7 @@ async def add_group_user(
@router.get(
"/groups/{gid}/users/{uid}",
response_model=Envelope[GroupUserGet],
tags=_extra_tags,
)
async def get_group_user(
_path: Annotated[GroupsUsersPathParams, Depends()],
Expand All @@ -125,6 +132,7 @@ async def get_group_user(
@router.patch(
"/groups/{gid}/users/{uid}",
response_model=Envelope[GroupUserGet],
tags=_extra_tags,
)
async def update_group_user(
_path: Annotated[GroupsUsersPathParams, Depends()],
Expand All @@ -138,6 +146,7 @@ async def update_group_user(
@router.delete(
"/groups/{gid}/users/{uid}",
status_code=status.HTTP_204_NO_CONTENT,
tags=_extra_tags,
)
async def delete_group_user(
_path: Annotated[GroupsUsersPathParams, Depends()],
Expand Down
57 changes: 57 additions & 0 deletions api/specs/web-server/_licensed_items_checkouts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
""" Helper script to generate OAS automatically
"""

# pylint: disable=redefined-outer-name
# pylint: disable=unused-argument
# pylint: disable=unused-variable
# pylint: disable=too-many-arguments

from typing import Annotated

from _common import as_query
from fastapi import APIRouter, Depends
from models_library.api_schemas_webserver.licensed_items_purchases import (
LicensedItemPurchaseGet,
)
from models_library.generics import Envelope
from models_library.rest_error import EnvelopedError
from models_library.rest_pagination import Page
from simcore_service_webserver._meta import API_VTAG
from simcore_service_webserver.licenses._exceptions_handlers import _TO_HTTP_ERROR_MAP
from simcore_service_webserver.licenses._licensed_items_checkouts_models import (
LicensedItemCheckoutPathParams,
LicensedItemsCheckoutsListQueryParams,
)
from simcore_service_webserver.wallets._handlers import WalletsPathParams

router = APIRouter(
prefix=f"/{API_VTAG}",
tags=[
"licenses",
],
responses={
i.status_code: {"model": EnvelopedError} for i in _TO_HTTP_ERROR_MAP.values()
},
)


@router.get(
"/wallets/{wallet_id}/licensed-items-checkouts",
response_model=Page[LicensedItemPurchaseGet],
tags=["wallets"],
)
async def list_licensed_item_checkouts_for_wallet(
_path: Annotated[WalletsPathParams, Depends()],
_query: Annotated[as_query(LicensedItemsCheckoutsListQueryParams), Depends()],
):
...


@router.get(
"/licensed-items-checkouts/{licensed_item_checkout_id}",
response_model=Envelope[LicensedItemPurchaseGet],
)
async def get_licensed_item_checkout(
_path: Annotated[LicensedItemCheckoutPathParams, Depends()],
):
...
70 changes: 48 additions & 22 deletions api/specs/web-server/_users.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
# pylint: disable=too-many-arguments


from enum import Enum
from typing import Annotated

from fastapi import APIRouter, Depends, status
Expand All @@ -13,8 +14,10 @@
MyProfilePatch,
MyTokenCreate,
MyTokenGet,
UserForAdminGet,
UserGet,
UsersSearchQueryParams,
UsersForAdminSearchQueryParams,
UsersSearch,
)
from models_library.api_schemas_webserver.users_preferences import PatchRequestBody
from models_library.generics import Envelope
Expand All @@ -29,7 +32,7 @@
from simcore_service_webserver.users._notifications_rest import _NotificationPathParams
from simcore_service_webserver.users._tokens_rest import _TokenPathParams

router = APIRouter(prefix=f"/{API_VTAG}", tags=["user"])
router = APIRouter(prefix=f"/{API_VTAG}", tags=["users"])


@router.get(
Expand All @@ -44,7 +47,7 @@ async def get_my_profile():
"/me",
status_code=status.HTTP_204_NO_CONTENT,
)
async def update_my_profile(_profile: MyProfilePatch):
async def update_my_profile(_body: MyProfilePatch):
...


Expand All @@ -54,7 +57,7 @@ async def update_my_profile(_profile: MyProfilePatch):
deprecated=True,
description="Use PATCH instead",
)
async def replace_my_profile(_profile: MyProfilePatch):
async def replace_my_profile(_body: MyProfilePatch):
...


Expand All @@ -64,7 +67,7 @@ async def replace_my_profile(_profile: MyProfilePatch):
)
async def set_frontend_preference(
preference_id: PreferenceIdentifier,
body_item: PatchRequestBody,
_body: PatchRequestBody,
):
...

Expand All @@ -82,23 +85,25 @@ async def list_tokens():
response_model=Envelope[MyTokenGet],
status_code=status.HTTP_201_CREATED,
)
async def create_token(_token: MyTokenCreate):
async def create_token(_body: MyTokenCreate):
...


@router.get(
"/me/tokens/{service}",
response_model=Envelope[MyTokenGet],
)
async def get_token(_params: Annotated[_TokenPathParams, Depends()]):
async def get_token(
_path: Annotated[_TokenPathParams, Depends()],
):
...


@router.delete(
"/me/tokens/{service}",
status_code=status.HTTP_204_NO_CONTENT,
)
async def delete_token(_params: Annotated[_TokenPathParams, Depends()]):
async def delete_token(_path: Annotated[_TokenPathParams, Depends()]):
...


Expand All @@ -114,7 +119,9 @@ async def list_user_notifications():
"/me/notifications",
status_code=status.HTTP_204_NO_CONTENT,
)
async def create_user_notification(_notification: UserNotificationCreate):
async def create_user_notification(
_body: UserNotificationCreate,
):
...


Expand All @@ -123,8 +130,8 @@ async def create_user_notification(_notification: UserNotificationCreate):
status_code=status.HTTP_204_NO_CONTENT,
)
async def mark_notification_as_read(
_params: Annotated[_NotificationPathParams, Depends()],
_notification: UserNotificationPatch,
_path: Annotated[_NotificationPathParams, Depends()],
_body: UserNotificationPatch,
):
...

Expand All @@ -137,24 +144,43 @@ async def list_user_permissions():
...


@router.get(
#
# USERS public
#


@router.post(
"/users:search",
response_model=Envelope[list[UserGet]],
tags=[
"po",
],
description="Search among users who are publicly visible to the caller (i.e., me) based on their privacy settings.",
)
async def search_users(_params: Annotated[UsersSearchQueryParams, Depends()]):
async def search_users(_body: UsersSearch):
...


#
# USERS admin
#

_extra_tags: list[str | Enum] = ["admin"]


@router.get(
"/admin/users:search",
response_model=Envelope[list[UserForAdminGet]],
tags=_extra_tags,
)
async def search_users_for_admin(
_query: Annotated[UsersForAdminSearchQueryParams, Depends()]
):
# NOTE: see `Search` in `Common Custom Methods` in https://cloud.google.com/apis/design/custom_methods
...


@router.post(
"/users:pre-register",
response_model=Envelope[UserGet],
tags=[
"po",
],
"/admin/users:pre-register",
response_model=Envelope[UserForAdminGet],
tags=_extra_tags,
)
async def pre_register_user(_body: PreRegisteredUserGet):
async def pre_register_user_for_admin(_body: PreRegisteredUserGet):
...
1 change: 1 addition & 0 deletions api/specs/web-server/openapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"_long_running_tasks",
"_licensed_items",
"_licensed_items_purchases",
"_licensed_items_checkouts",
"_metamodeling",
"_nih_sparc",
"_nih_sparc_redirections",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,7 @@ class UnSet:

def as_dict_exclude_unset(**params) -> dict[str, Any]:
return {k: v for k, v in params.items() if not isinstance(v, UnSet)}


def as_dict_exclude_none(**params) -> dict[str, Any]:
return {k: v for k, v in params.items() if v is not None}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from typing import Any

from common_library.unset import UnSet, as_dict_exclude_unset
from common_library.exclude import UnSet, as_dict_exclude_none, as_dict_exclude_unset


def test_as_dict_exclude_unset():
Expand All @@ -13,3 +13,10 @@ def f(
assert f(par1="hi") == {"par1": "hi"}
assert f(par2=4) == {"par2": 4}
assert f(par1="hi", par2=4) == {"par1": "hi", "par2": 4}

# still expected behavior
assert as_dict_exclude_unset(par1=None) == {"par1": None}


def test_as_dict_exclude_none():
assert as_dict_exclude_none(par1=None) == {}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
)
from ..users import UserID, UserNameID
from ..utils.common_validators import create__check_only_one_is_set__root_validator
from ._base import InputSchema, OutputSchema
from ._base import InputSchema, OutputSchema, OutputSchemaWithoutCamelCase

S = TypeVar("S", bound=BaseModel)

Expand Down Expand Up @@ -248,8 +248,7 @@ def from_model(
)


class GroupUserGet(BaseModel):
# OutputSchema
class GroupUserGet(OutputSchemaWithoutCamelCase):

# Identifiers
id: Annotated[UserID | None, Field(description="the user's id")] = None
Expand All @@ -275,7 +274,14 @@ class GroupUserGet(BaseModel):
] = None

# Access Rights
access_rights: GroupAccessRights = Field(..., alias="accessRights")
access_rights: Annotated[
GroupAccessRights | None,
Field(
alias="accessRights",
description="If group is standard, these are these are the access rights of the user to it."
"None if primary group.",
),
] = None

model_config = ConfigDict(
populate_by_name=True,
Expand All @@ -293,7 +299,23 @@ class GroupUserGet(BaseModel):
"write": False,
"delete": False,
},
}
},
"examples": [
# unique member on a primary group with two different primacy settings
{
"id": "16",
"userName": "mrprivate",
"gid": "55",
},
{
"id": "56",
"userName": "mrpublic",
"login": "[email protected]",
"first_name": "Mr",
"last_name": "Public",
"gid": "42",
},
],
},
)

Expand Down
Loading

0 comments on commit e61178a

Please sign in to comment.