Skip to content

Commit 15b0d4f

Browse files
committed
base rest
1 parent 62a56d9 commit 15b0d4f

File tree

10 files changed

+59
-56
lines changed

10 files changed

+59
-56
lines changed
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
from pydantic import BaseModel, Extra
2+
3+
4+
class RequestParameters(BaseModel):
5+
"""
6+
Base model for any type of request parameters,
7+
i.e. context, path, query, headers
8+
"""
9+
10+
def as_params(self, **export_options) -> dict[str, str]:
11+
data = self.dict(**export_options)
12+
return {k: f"{v}" for k, v in data.items()}
13+
14+
15+
class StrictRequestParameters(RequestParameters):
16+
"""Use a base class for context, path and query parameters"""
17+
18+
class Config:
19+
extra = Extra.forbid # strict

packages/models-library/src/models_library/rest_ordering.py

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from pydantic import BaseModel, Field, Json, validator
66

77
from .basic_types import IDStr
8+
from .rest_base import RequestParameters
89

910

1011
class OrderDirection(str, Enum):
@@ -30,7 +31,7 @@ class Config:
3031
}
3132

3233

33-
class BaseOrderByQueryParams(BaseModel):
34+
class _BaseOrderByQueryParams(RequestParameters):
3435
order_by: OrderBy | None = None
3536

3637

@@ -39,7 +40,7 @@ def create_order_by_query_model_classes(
3940
sortable_fields: set[str],
4041
default_order_by: OrderBy,
4142
override_direction_default: bool = False,
42-
) -> tuple[type[BaseOrderByQueryParams], type[BaseModel]]:
43+
) -> tuple[type[_BaseOrderByQueryParams], type[BaseModel]]:
4344
"""
4445
Factory to create an uniform model used as ordering parameters in a query
4546
@@ -52,7 +53,7 @@ def create_order_by_query_model_classes(
5253
msg_direction_options = "|".join(sorted(OrderDirection))
5354
order_by_example: dict[str, Any] = OrderBy.Config.schema_extra["example"]
5455

55-
class _JsonOrderBy(OrderBy):
56+
class _OrderByJsonable(OrderBy):
5657
direction: OrderDirection = Field(
5758
default=default_order_by.direction
5859
if override_direction_default
@@ -87,9 +88,9 @@ def _check_if_sortable_field(cls, v):
8788
f"The default sorting order is '{default_order_by.direction.value}' on '{default_order_by.field}'."
8889
)
8990

90-
class _RequestValidatorModel(BaseOrderByQueryParams):
91+
class _RequestValidatorModel(_BaseOrderByQueryParams):
9192
# Used in rest handler for verification
92-
order_by: _JsonOrderBy = Field(
93+
order_by: _OrderByJsonable = Field(
9394
default=default_order_by,
9495
description=description,
9596
)
@@ -109,14 +110,9 @@ class _OpenapiModel(BaseModel):
109110
description=description,
110111
)
111112

112-
@validator("order_by", allow_reuse=True)
113-
@classmethod
114-
def _validate_json_content(cls, v):
115-
if v:
116-
_RequestValidatorModel(order_by=v)
117-
return v
118-
119113
class Config:
120-
schema_extra: ClassVar[dict[str, Any]] = {"title": "Order By Parameters"}
114+
schema_extra: ClassVar[dict[str, Any]] = {
115+
"title": "Order By Parameters",
116+
}
121117

122118
return _RequestValidatorModel, _OpenapiModel

packages/models-library/src/models_library/rest_pagination.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
)
1414
from pydantic.generics import GenericModel
1515

16+
from .rest_base import RequestParameters
1617
from .utils.common_validators import none_to_empty_list_pre_validator
1718

1819
# Default limit values
@@ -29,7 +30,7 @@ class PageLimitInt(ConstrainedInt):
2930
DEFAULT_NUMBER_OF_ITEMS_PER_PAGE: Final[PageLimitInt] = parse_obj_as(PageLimitInt, 20)
3031

3132

32-
class PageQueryParameters(BaseModel):
33+
class PageQueryParameters(RequestParameters):
3334
"""Use as pagination options in query parameters"""
3435

3536
limit: PageLimitInt = Field(

packages/service-library/src/servicelib/aiohttp/requests_validation.py

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
from aiohttp import web
1616
from models_library.utils.json_serialization import json_dumps
17-
from pydantic import BaseModel, Extra, ValidationError, parse_obj_as
17+
from pydantic import BaseModel, ValidationError, parse_obj_as
1818

1919
from ..mimetype_constants import MIMETYPE_APPLICATION_JSON
2020
from . import status
@@ -24,17 +24,6 @@
2424
UnionOfModelTypes: TypeAlias = Union[type[ModelClass], type[ModelClass]] # noqa: UP007
2525

2626

27-
class RequestParams(BaseModel):
28-
...
29-
30-
31-
class StrictRequestParams(BaseModel):
32-
"""Use a base class for context, path and query parameters"""
33-
34-
class Config:
35-
extra = Extra.forbid # strict
36-
37-
3827
@contextmanager
3928
def handle_validation_as_http_error(
4029
*, error_msg_template: str, resource_name: str, use_error_v1: bool

packages/service-library/tests/aiohttp/test_requests_validation.py

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from aiohttp import web
1111
from aiohttp.test_utils import TestClient, make_mocked_request
1212
from faker import Faker
13+
from models_library.rest_base import RequestParameters, StrictRequestParameters
1314
from models_library.rest_ordering import (
1415
OrderBy,
1516
OrderDirection,
@@ -19,8 +20,6 @@
1920
from pydantic import BaseModel, Field
2021
from servicelib.aiohttp import status
2122
from servicelib.aiohttp.requests_validation import (
22-
RequestParams,
23-
StrictRequestParams,
2423
parse_request_body_as,
2524
parse_request_headers_as,
2625
parse_request_path_parameters_as,
@@ -38,7 +37,7 @@ def jsonable_encoder(data):
3837
return json.loads(json_dumps(data))
3938

4039

41-
class MyRequestContext(RequestParams):
40+
class MyRequestContext(RequestParameters):
4241
user_id: int = Field(alias=RQT_USERID_KEY)
4342
secret: str = Field(alias=APP_SECRET_KEY)
4443

@@ -47,15 +46,15 @@ def create_fake(cls, faker: Faker):
4746
return cls(user_id=faker.pyint(), secret=faker.password())
4847

4948

50-
class MyRequestPathParams(StrictRequestParams):
49+
class MyRequestPathParams(StrictRequestParameters):
5150
project_uuid: UUID
5251

5352
@classmethod
5453
def create_fake(cls, faker: Faker):
5554
return cls(project_uuid=faker.uuid4())
5655

5756

58-
class MyRequestQueryParams(RequestParams):
57+
class MyRequestQueryParams(RequestParameters):
5958
is_ok: bool = True
6059
label: str
6160

@@ -64,7 +63,7 @@ def create_fake(cls, faker: Faker):
6463
return cls(is_ok=faker.pybool(), label=faker.word())
6564

6665

67-
class MyRequestHeadersParams(RequestParams):
66+
class MyRequestHeadersParams(RequestParameters):
6867
user_agent: str = Field(alias="X-Simcore-User-Agent")
6968
optional_header: str | None = Field(default=None, alias="X-Simcore-Optional-Header")
7069

@@ -364,7 +363,7 @@ async def test_parse_request_with_invalid_headers_params(
364363

365364
def test_parse_request_query_parameters_as_with_order_by_query_models():
366365

367-
OrderByModel, _ = create_order_by_query_model_classes(
366+
OrderByModel, OrderByModelOAS = create_order_by_query_model_classes(
368367
sortable_fields={"modified", "name"}, default_order_by=OrderBy(field="name")
369368
)
370369

@@ -377,5 +376,9 @@ def test_parse_request_query_parameters_as_with_order_by_query_models():
377376
query_params = parse_request_query_parameters_as(OrderByModel, request)
378377
assert query_params.order_by == expected
379378

380-
assert OrderByModel.schema()["properties"]["order_by"]["type"] == "string"
381-
assert OrderByModel.schema()["properties"]["order_by"]["format"] == "json-string"
379+
expected_schema = {"type": "string", "format": "json-string"}
380+
assert {
381+
k: v
382+
for k, v in OrderByModel.schema()["properties"]["order_by"]
383+
if k in expected
384+
} == expected_schema

services/web/server/src/simcore_service_webserver/folders/_models.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from models_library.basic_types import IDStr
44
from models_library.folders import FolderID
5+
from models_library.rest_base import RequestParameters, StrictRequestParameters
56
from models_library.rest_filters import Filters, FiltersQueryParameters
67
from models_library.rest_ordering import OrderBy, OrderDirection
78
from models_library.rest_pagination import PageQueryParameters
@@ -13,20 +14,19 @@
1314
)
1415
from models_library.workspaces import WorkspaceID
1516
from pydantic import BaseModel, Extra, Field, Json, validator
16-
from servicelib.aiohttp.requests_validation import RequestParams, StrictRequestParams
1717
from servicelib.request_keys import RQT_USERID_KEY
1818

1919
from .._constants import RQ_PRODUCT_KEY
2020

2121
_logger = logging.getLogger(__name__)
2222

2323

24-
class FoldersRequestContext(RequestParams):
24+
class FoldersRequestContext(RequestParameters):
2525
user_id: UserID = Field(..., alias=RQT_USERID_KEY) # type: ignore[literal-required]
2626
product_name: str = Field(..., alias=RQ_PRODUCT_KEY) # type: ignore[literal-required]
2727

2828

29-
class FoldersPathParams(StrictRequestParams):
29+
class FoldersPathParams(StrictRequestParameters):
3030
folder_id: FolderID
3131

3232

services/web/server/src/simcore_service_webserver/products/_handlers.py

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,10 @@
44
from aiohttp import web
55
from models_library.api_schemas_webserver.product import GetCreditPrice, GetProduct
66
from models_library.basic_types import IDStr
7+
from models_library.rest_base import RequestParameters, StrictRequestParameters
78
from models_library.users import UserID
89
from pydantic import Extra, Field
9-
from servicelib.aiohttp.requests_validation import (
10-
RequestParams,
11-
StrictRequestParams,
12-
parse_request_path_parameters_as,
13-
)
10+
from servicelib.aiohttp.requests_validation import parse_request_path_parameters_as
1411
from servicelib.request_keys import RQT_USERID_KEY
1512
from simcore_service_webserver.utils_aiohttp import envelope_json_response
1613

@@ -27,7 +24,7 @@
2724
_logger = logging.getLogger(__name__)
2825

2926

30-
class _ProductsRequestContext(RequestParams):
27+
class _ProductsRequestContext(RequestParameters):
3128
user_id: UserID = Field(..., alias=RQT_USERID_KEY) # type: ignore[literal-required]
3229
product_name: str = Field(..., alias=RQ_PRODUCT_KEY) # type: ignore[literal-required]
3330

@@ -49,7 +46,7 @@ async def _get_current_product_price(request: web.Request):
4946
return envelope_json_response(credit_price)
5047

5148

52-
class _ProductsRequestParams(StrictRequestParams):
49+
class _ProductsRequestParams(StrictRequestParameters):
5350
product_name: IDStr | Literal["current"]
5451

5552

services/web/server/src/simcore_service_webserver/tags/schemas.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,18 @@
22
from datetime import datetime
33

44
from models_library.api_schemas_webserver._base import InputSchema, OutputSchema
5+
from models_library.rest_base import RequestParameters, StrictRequestParameters
56
from models_library.users import GroupID, UserID
67
from pydantic import ConstrainedStr, Field, PositiveInt
7-
from servicelib.aiohttp.requests_validation import RequestParams, StrictRequestParams
88
from servicelib.request_keys import RQT_USERID_KEY
99
from simcore_postgres_database.utils_tags import TagDict
1010

1111

12-
class TagRequestContext(RequestParams):
12+
class TagRequestContext(RequestParameters):
1313
user_id: UserID = Field(..., alias=RQT_USERID_KEY) # type: ignore[literal-required]
1414

1515

16-
class TagPathParams(StrictRequestParams):
16+
class TagPathParams(StrictRequestParameters):
1717
tag_id: PositiveInt
1818

1919

services/web/server/src/simcore_service_webserver/wallets/_handlers.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,11 @@
99
WalletGetWithAvailableCredits,
1010
)
1111
from models_library.error_codes import create_error_code
12+
from models_library.rest_base import RequestParameters, StrictRequestParameters
1213
from models_library.users import UserID
1314
from models_library.wallets import WalletID
1415
from pydantic import Field
1516
from servicelib.aiohttp.requests_validation import (
16-
RequestParams,
17-
StrictRequestParams,
1817
parse_request_body_as,
1918
parse_request_path_parameters_as,
2019
)
@@ -106,19 +105,18 @@ async def wrapper(request: web.Request) -> web.StreamResponse:
106105
return wrapper
107106

108107

109-
#
110108
# wallets COLLECTION -------------------------
111109
#
112110

113111
routes = web.RouteTableDef()
114112

115113

116-
class WalletsRequestContext(RequestParams):
114+
class WalletsRequestContext(RequestParameters):
117115
user_id: UserID = Field(..., alias=RQT_USERID_KEY) # type: ignore[literal-required]
118116
product_name: str = Field(..., alias=RQ_PRODUCT_KEY) # type: ignore[literal-required]
119117

120118

121-
class WalletsPathParams(StrictRequestParams):
119+
class WalletsPathParams(StrictRequestParameters):
122120
wallet_id: WalletID
123121

124122

services/web/server/src/simcore_service_webserver/workspaces/_models.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import logging
22

33
from models_library.basic_types import IDStr
4+
from models_library.rest_base import RequestParameters, StrictRequestParameters
45
from models_library.rest_filters import Filters, FiltersQueryParameters
56
from models_library.rest_ordering import (
67
OrderBy,
@@ -12,20 +13,19 @@
1213
from models_library.users import GroupID, UserID
1314
from models_library.workspaces import WorkspaceID
1415
from pydantic import BaseModel, Extra, Field
15-
from servicelib.aiohttp.requests_validation import RequestParams, StrictRequestParams
1616
from servicelib.request_keys import RQT_USERID_KEY
1717

1818
from .._constants import RQ_PRODUCT_KEY
1919

2020
_logger = logging.getLogger(__name__)
2121

2222

23-
class WorkspacesRequestContext(RequestParams):
23+
class WorkspacesRequestContext(RequestParameters):
2424
user_id: UserID = Field(..., alias=RQT_USERID_KEY) # type: ignore[literal-required]
2525
product_name: str = Field(..., alias=RQ_PRODUCT_KEY) # type: ignore[literal-required]
2626

2727

28-
class WorkspacesPathParams(StrictRequestParams):
28+
class WorkspacesPathParams(StrictRequestParameters):
2929
workspace_id: WorkspaceID
3030

3131

0 commit comments

Comments
 (0)