Skip to content

Commit 400cb25

Browse files
authored
Merge branch 'master' into feature/trash-bin
2 parents b536c87 + dc0c37d commit 400cb25

File tree

15 files changed

+120
-35
lines changed

15 files changed

+120
-35
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,6 @@ class Position(BaseModel):
1616

1717

1818
class Marker(BaseModel):
19-
color: Annotated[Color, PlainSerializer(str), Field(...)]
19+
color: Annotated[Color, PlainSerializer(Color.as_hex), Field(...)]
2020

2121
model_config = ConfigDict(extra="forbid")

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ class Slideshow(_SlideshowRequired, total=False):
3131

3232
class Annotation(BaseModel):
3333
type: Literal["note", "rect", "text"] = Field(...)
34-
color: Annotated[Color, PlainSerializer(str), Field(...)]
34+
color: Annotated[Color, PlainSerializer(Color.as_hex), Field(...)]
3535
attributes: dict = Field(..., description="svg attributes")
3636
model_config = ConfigDict(
3737
extra="forbid",

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,10 @@ class OrderBy(BaseModel):
2727

2828

2929
class _BaseOrderQueryParams(RequestParameters):
30-
order_by: OrderBy | None = None
30+
order_by: OrderBy
3131

3232

33-
def create_ordering_query_model_classes(
33+
def create_ordering_query_model_class(
3434
*,
3535
ordering_fields: set[str],
3636
default: OrderBy,
Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1+
import pytest
12
from models_library.projects_nodes_ui import Marker
23
from pydantic_extra_types.color import Color
34

45

5-
def test_marker_serialization():
6-
m = Marker(color=Color("#b7e28d"))
7-
8-
assert m.model_dump_json() == '{"color":"#b7e28d"}'
6+
@pytest.mark.parametrize(
7+
"color_str,expected_color_str", [("#b7e28d", "#b7e28d"), ("Cyan", "#0ff")]
8+
)
9+
def test_marker_color_serialized_to_hex(color_str, expected_color_str):
10+
m = Marker(color=Color(color_str))
11+
assert m.model_dump_json() == f'{{"color":"{expected_color_str}"}}'
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import pytest
2+
from models_library.projects_ui import Annotation
3+
from pydantic_extra_types.color import Color
4+
5+
6+
@pytest.mark.parametrize(
7+
"color_str,expected_color_str", [("#b7e28d", "#b7e28d"), ("Cyan", "#0ff")]
8+
)
9+
def test_annotation_color_serialized_to_hex(color_str, expected_color_str):
10+
m = Annotation(type="text", color=Color(color_str), attributes={})
11+
assert (
12+
m.model_dump_json()
13+
== f'{{"type":"text","color":"{expected_color_str}","attributes":{{}}}}'
14+
)

packages/models-library/tests/test_rest_ordering.py

Lines changed: 74 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
1+
import pickle
2+
13
import pytest
24
from common_library.json_serialization import json_dumps
35
from models_library.basic_types import IDStr
46
from models_library.rest_ordering import (
57
OrderBy,
68
OrderDirection,
7-
create_ordering_query_model_classes,
9+
create_ordering_query_model_class,
810
)
911
from pydantic import (
1012
BaseModel,
1113
ConfigDict,
1214
Field,
1315
Json,
16+
TypeAdapter,
1417
ValidationError,
1518
field_validator,
1619
)
@@ -47,8 +50,72 @@ def _validate_order_by_field(cls, v):
4750
)
4851

4952

53+
@pytest.mark.xfail(
54+
reason="create_ordering_query_model_class.<locals>._OrderBy is still not pickable"
55+
)
56+
def test_pickle_ordering_query_model_class():
57+
OrderQueryParamsModel = create_ordering_query_model_class(
58+
ordering_fields={"name", "description"},
59+
default=OrderBy(field=IDStr("name"), direction=OrderDirection.DESC),
60+
)
61+
62+
data = {"order_by": {"field": "name", "direction": "asc"}}
63+
query_model = OrderQueryParamsModel.model_validate(data)
64+
65+
# https://docs.pydantic.dev/latest/concepts/serialization/#pickledumpsmodel
66+
expected = query_model.order_by
67+
68+
# see https://github.com/ITISFoundation/osparc-simcore/pull/6828
69+
# FAILURE: raises `AttributeError: Can't pickle local object 'create_ordering_query_model_class.<locals>._OrderBy'`
70+
data = pickle.dumps(expected)
71+
72+
loaded = pickle.loads(data)
73+
assert loaded == expected
74+
75+
76+
def test_conversion_order_by_from_query_to_domain_model():
77+
OrderQueryParamsModel = create_ordering_query_model_class(
78+
ordering_fields={"modified_at", "name", "description"},
79+
default=OrderBy(field=IDStr("modified_at"), direction=OrderDirection.DESC),
80+
)
81+
82+
# normal
83+
data = {"order_by": {"field": "modified_at", "direction": "asc"}}
84+
query_model = OrderQueryParamsModel.model_validate(data)
85+
86+
expected_data = data["order_by"]
87+
88+
assert type(query_model.order_by) is not OrderBy
89+
assert isinstance(query_model.order_by, OrderBy)
90+
91+
# NOTE: This does NOT convert to OrderBy but has correct data
92+
order_by = TypeAdapter(OrderBy).validate_python(
93+
query_model.order_by, from_attributes=True
94+
)
95+
assert type(order_by) is not OrderBy
96+
assert order_by.model_dump(mode="json") == expected_data
97+
98+
order_by = OrderBy.model_validate(query_model.order_by.model_dump())
99+
assert type(order_by) is OrderBy
100+
assert order_by.model_dump(mode="json") == expected_data
101+
102+
# NOTE: This does NOT convert to OrderBy but has correct data
103+
order_by = OrderBy.model_validate(query_model.order_by, from_attributes=True)
104+
assert type(order_by) is not OrderBy
105+
assert order_by.model_dump(mode="json") == expected_data
106+
107+
order_by = OrderBy(**query_model.order_by.model_dump())
108+
assert type(order_by) is OrderBy
109+
assert order_by.model_dump(mode="json") == expected_data
110+
111+
# we should use this !!!
112+
order_by = OrderBy.model_construct(**query_model.order_by.model_dump())
113+
assert type(order_by) is OrderBy
114+
assert order_by.model_dump(mode="json") == expected_data
115+
116+
50117
def test_ordering_query_model_class_factory():
51-
BaseOrderingQueryModel = create_ordering_query_model_classes(
118+
BaseOrderingQueryModel = create_ordering_query_model_class(
52119
ordering_fields={"modified_at", "name", "description"},
53120
default=OrderBy(field=IDStr("modified_at"), direction=OrderDirection.DESC),
54121
ordering_fields_api_to_column_map={"modified_at": "modified_column"},
@@ -77,7 +144,7 @@ class OrderQueryParamsModel(BaseOrderingQueryModel):
77144

78145
def test_ordering_query_model_class__fails_with_invalid_fields():
79146

80-
OrderQueryParamsModel = create_ordering_query_model_classes(
147+
OrderQueryParamsModel = create_ordering_query_model_class(
81148
ordering_fields={"modified", "name", "description"},
82149
default=OrderBy(field=IDStr("modified"), direction=OrderDirection.DESC),
83150
)
@@ -94,7 +161,7 @@ def test_ordering_query_model_class__fails_with_invalid_fields():
94161

95162

96163
def test_ordering_query_model_class__fails_with_invalid_direction():
97-
OrderQueryParamsModel = create_ordering_query_model_classes(
164+
OrderQueryParamsModel = create_ordering_query_model_class(
98165
ordering_fields={"modified", "name", "description"},
99166
default=OrderBy(field=IDStr("modified"), direction=OrderDirection.DESC),
100167
)
@@ -112,7 +179,7 @@ def test_ordering_query_model_class__fails_with_invalid_direction():
112179

113180
def test_ordering_query_model_class__defaults():
114181

115-
OrderQueryParamsModel = create_ordering_query_model_classes(
182+
OrderQueryParamsModel = create_ordering_query_model_class(
116183
ordering_fields={"modified", "name", "description"},
117184
default=OrderBy(field=IDStr("modified"), direction=OrderDirection.DESC),
118185
ordering_fields_api_to_column_map={"modified": "modified_at"},
@@ -142,7 +209,7 @@ def test_ordering_query_model_class__defaults():
142209

143210

144211
def test_ordering_query_model_with_map():
145-
OrderQueryParamsModel = create_ordering_query_model_classes(
212+
OrderQueryParamsModel = create_ordering_query_model_class(
146213
ordering_fields={"modified", "name", "description"},
147214
default=OrderBy(field=IDStr("modified"), direction=OrderDirection.DESC),
148215
ordering_fields_api_to_column_map={"modified": "some_db_column_name"},
@@ -155,7 +222,7 @@ def test_ordering_query_model_with_map():
155222

156223
def test_ordering_query_parse_json_pre_validator():
157224

158-
OrderQueryParamsModel = create_ordering_query_model_classes(
225+
OrderQueryParamsModel = create_ordering_query_model_class(
159226
ordering_fields={"modified", "name"},
160227
default=OrderBy(field=IDStr("modified"), direction=OrderDirection.DESC),
161228
)

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
from models_library.rest_ordering import (
1616
OrderBy,
1717
OrderDirection,
18-
create_ordering_query_model_classes,
18+
create_ordering_query_model_class,
1919
)
2020
from pydantic import BaseModel, ConfigDict, Field
2121
from servicelib.aiohttp import status
@@ -365,7 +365,7 @@ async def test_parse_request_with_invalid_headers_params(
365365

366366
def test_parse_request_query_parameters_as_with_order_by_query_models():
367367

368-
OrderQueryModel = create_ordering_query_model_classes(
368+
OrderQueryModel = create_ordering_query_model_class(
369369
ordering_fields={"modified", "name"}, default=OrderBy(field="name")
370370
)
371371

@@ -376,4 +376,5 @@ def test_parse_request_query_parameters_as_with_order_by_query_models():
376376
request = make_mocked_request("GET", path=f"{url}")
377377

378378
query_params = parse_request_query_parameters_as(OrderQueryModel, request)
379-
assert query_params.order_by.model_dump() == expected.model_dump()
379+
380+
assert OrderBy.model_construct(**query_params.order_by.model_dump()) == expected

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
from models_library.rest_ordering import OrderBy
1111
from models_library.rest_pagination import Page
1212
from models_library.rest_pagination_utils import paginate_data
13-
from pydantic import TypeAdapter
1413
from servicelib.aiohttp import status
1514
from servicelib.aiohttp.requests_validation import (
1615
parse_request_body_as,
@@ -82,7 +81,7 @@ async def list_folders(request: web.Request):
8281
trashed=query_params.filters.trashed,
8382
offset=query_params.offset,
8483
limit=query_params.limit,
85-
order_by=OrderBy.model_validate(query_params.order_by),
84+
order_by=OrderBy.model_construct(**query_params.order_by.model_dump()),
8685
)
8786

8887
page = Page[FolderGet].model_validate(
@@ -121,7 +120,7 @@ async def list_folders_full_search(request: web.Request):
121120
trashed=query_params.filters.trashed,
122121
offset=query_params.offset,
123122
limit=query_params.limit,
124-
order_by=TypeAdapter(OrderBy).validate_python(query_params.order_by),
123+
order_by=OrderBy.model_construct(**query_params.order_by.model_dump()),
125124
)
126125

127126
page = Page[FolderGet].model_validate(

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from models_library.rest_ordering import (
99
OrderBy,
1010
OrderDirection,
11-
create_ordering_query_model_classes,
11+
create_ordering_query_model_class,
1212
)
1313
from models_library.rest_pagination import PageQueryParameters
1414
from models_library.trash import RemoveQueryParams
@@ -42,7 +42,7 @@ class FolderFilters(Filters):
4242
)
4343

4444

45-
_FolderOrderQueryParams: type[RequestParameters] = create_ordering_query_model_classes(
45+
_FolderOrderQueryParams: type[RequestParameters] = create_ordering_query_model_class(
4646
ordering_fields={
4747
"modified_at",
4848
"name",

services/web/server/src/simcore_service_webserver/projects/_crud_handlers.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ async def list_projects(request: web.Request):
203203
limit=query_params.limit,
204204
offset=query_params.offset,
205205
search=query_params.search,
206-
order_by=OrderBy.model_validate(query_params.order_by),
206+
order_by=OrderBy.model_construct(**query_params.order_by.model_dump()),
207207
folder_id=query_params.folder_id,
208208
workspace_id=query_params.workspace_id,
209209
)
@@ -241,7 +241,7 @@ async def list_projects_full_search(request: web.Request):
241241
limit=query_params.limit,
242242
offset=query_params.offset,
243243
text=query_params.text,
244-
order_by=query_params.order_by,
244+
order_by=OrderBy.model_construct(**query_params.order_by.model_dump()),
245245
tag_ids_list=tag_ids_list,
246246
)
247247

0 commit comments

Comments
 (0)