Skip to content

Commit 3a485c4

Browse files
committed
WIP
1 parent 8bda6a4 commit 3a485c4

File tree

3 files changed

+65
-21
lines changed

3 files changed

+65
-21
lines changed

api/specs/web-server/_catalog.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
ServiceInputKey,
1010
ServiceOutputGet,
1111
ServiceOutputKey,
12-
ServiceResourcesGet,
1312
)
1413
from models_library.generics import Envelope
1514
from models_library.rest_pagination import Page
@@ -127,7 +126,7 @@ def get_compatible_outputs_given_target_input(
127126

128127
@router.get(
129128
"/catalog/services/{service_key}/{service_version}/resources",
130-
response_model=ServiceResourcesGet,
129+
# response_model=ServiceResourcesGet,
131130
)
132131
def get_service_resources(
133132
_params: Annotated[ServicePathParams, Depends()],

api/specs/web-server/_common.py

Lines changed: 57 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,16 @@
55
import sys
66
from collections.abc import Callable
77
from pathlib import Path
8-
from typing import Any, ClassVar, NamedTuple
8+
from typing import (
9+
Annotated,
10+
Any,
11+
ClassVar,
12+
NamedTuple,
13+
Optional,
14+
Union,
15+
get_args,
16+
get_origin,
17+
)
918

1019
import yaml
1120
from common_library.json_serialization import json_dumps
@@ -36,33 +45,65 @@ def __modify_schema__(cls, field_schema: dict[str, Any]) -> None:
3645
return _Json
3746

3847

48+
def replace_basemodel_in_annotation(annotation, new_type):
49+
origin = get_origin(annotation)
50+
51+
# Handle Annotated
52+
if origin is Annotated:
53+
args = get_args(annotation)
54+
base_type = args[0]
55+
metadata = args[1:]
56+
if isinstance(base_type, type) and issubclass(base_type, BaseModel):
57+
# Replace the BaseModel subclass
58+
base_type = new_type
59+
60+
return Annotated[(base_type, *metadata)]
61+
62+
# Handle Optionals, Unions, or other generic types
63+
if origin in (Optional, Union, list, dict, tuple): # Extendable for other generics
64+
new_args = tuple(
65+
replace_basemodel_in_annotation(arg, new_type)
66+
for arg in get_args(annotation)
67+
)
68+
return origin[new_args]
69+
70+
# Replace BaseModel subclass directly
71+
if isinstance(annotation, type) and issubclass(annotation, BaseModel):
72+
return new_type
73+
74+
# Return as-is if no changes
75+
return annotation
76+
77+
3978
def as_query(model_class: type[BaseModel]) -> type[BaseModel]:
4079
fields = {}
4180
for field_name, field_info in model_class.model_fields.items():
4281

43-
field_type = get_type(field_info)
44-
default_value = field_info.default
45-
46-
kwargs = {
82+
query_kwargs = {
83+
"default": field_info.default,
4784
"alias": field_info.alias,
4885
"title": field_info.title,
4986
"description": field_info.description,
5087
"metadata": field_info.metadata,
51-
"json_schema_extra": field_info.json_schema_extra,
88+
"json_schema_extra": field_info.json_schema_extra or {},
5289
}
5390

54-
if issubclass(field_type, BaseModel):
55-
# Complex fields
56-
assert "json_schema_extra" in kwargs # nosec
57-
assert kwargs["json_schema_extra"] # nosec
58-
field_type = _create_json_type(
59-
description=kwargs["description"],
60-
example=kwargs.get("json_schema_extra", {}).get("example_json"),
61-
)
91+
json_field_type = _create_json_type(
92+
description=query_kwargs["description"],
93+
example=query_kwargs.get("json_schema_extra", {}).get("example_json"),
94+
)
6295

63-
default_value = json_dumps(default_value) if default_value else None
96+
annotation = replace_basemodel_in_annotation(
97+
field_info.annotation, new_type=json_field_type
98+
)
99+
100+
if annotation != field_info.annotation:
101+
# Complex fields are transformed to Json
102+
query_kwargs["default"] = (
103+
json_dumps(query_kwargs["default"]) if query_kwargs["default"] else None
104+
)
64105

65-
fields[field_name] = (field_type, Query(default=default_value, **kwargs))
106+
fields[field_name] = (annotation, Query(**query_kwargs))
66107

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

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

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
from typing import Generic, TypeVar
1+
from typing import Annotated, Generic, TypeVar
22

3-
from pydantic import BaseModel, Field, Json
3+
from pydantic import BaseModel, BeforeValidator, Field
4+
5+
from .utils.common_validators import parse_json_pre_validator
46

57

68
class Filters(BaseModel):
@@ -15,7 +17,9 @@ class Filters(BaseModel):
1517

1618

1719
class FiltersQueryParameters(BaseModel, Generic[FilterT]):
18-
filters: Json[FilterT] | None = Field( # pylint: disable=unsubscriptable-object
20+
filters: Annotated[
21+
FilterT | None, BeforeValidator(parse_json_pre_validator)
22+
] = Field( # pylint: disable=unsubscriptable-object
1923
default=None,
2024
description="Custom filter query parameter encoded as JSON",
2125
)

0 commit comments

Comments
 (0)