starmallow 0.6.2__tar.gz → 0.6.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {starmallow-0.6.2 → starmallow-0.6.3}/PKG-INFO +1 -1
- {starmallow-0.6.2 → starmallow-0.6.3}/starmallow/__init__.py +1 -1
- {starmallow-0.6.2 → starmallow-0.6.3}/starmallow/endpoint.py +1 -0
- starmallow-0.6.3/tests/test_annotated.py +527 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/tests/test_input.py +1 -0
- starmallow-0.6.2/tests/test_annotated.py +0 -244
- {starmallow-0.6.2 → starmallow-0.6.3}/.editorconfig +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/.gitignore +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/.pre-commit-config.yaml +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/Dockerfile +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/LICENSE.md +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/README.md +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/docker-compose.yml +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/docs/design_ideas.md +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/examples/__init__.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/examples/cache_server.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/examples/flask_server.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/examples/goals.ipynb +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/examples/gunicorn.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/examples/recommended_server.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/examples/sample_server.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/pyproject.toml +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/starmallow/applications.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/starmallow/concurrency.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/starmallow/constants.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/starmallow/dataclasses.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/starmallow/datastructures.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/starmallow/decorators.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/starmallow/delimited_field.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/starmallow/docs.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/starmallow/endpoints.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/starmallow/exception_handlers.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/starmallow/exceptions.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/starmallow/ext/__init__.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/starmallow/ext/marshmallow/__init__.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/starmallow/ext/marshmallow/openapi.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/starmallow/fields.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/starmallow/middleware/__init__.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/starmallow/middleware/asyncexitstack.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/starmallow/params.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/starmallow/request_resolver.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/starmallow/requests.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/starmallow/responses.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/starmallow/routing.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/starmallow/schema_generator.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/starmallow/security/__init__.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/starmallow/security/api_key.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/starmallow/security/base.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/starmallow/security/http.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/starmallow/security/oauth2.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/starmallow/security/open_id_connect_url.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/starmallow/security/utils.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/starmallow/serializers.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/starmallow/types.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/starmallow/utils.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/starmallow/websockets.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/tests/__init__.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/tests/basic_api.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/tests/security/__init__.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/tests/security/api_key/__init__.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/tests/security/api_key/test_api_key_cookie.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/tests/security/api_key/test_api_key_cookie_description.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/tests/security/api_key/test_api_key_cookie_optional.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/tests/security/api_key/test_api_key_header.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/tests/security/api_key/test_api_key_header_description.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/tests/security/api_key/test_api_key_header_optional.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/tests/security/api_key/test_api_key_query.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/tests/security/api_key/test_api_key_query_description.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/tests/security/api_key/test_api_key_query_optional.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/tests/security/http/__init__.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/tests/security/http/test_http_base.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/tests/security/http/test_http_base_description.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/tests/security/http/test_http_base_optional.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/tests/security/http/test_http_basic.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/tests/security/http/test_http_basic_realm.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/tests/security/http/test_http_basic_realm_description.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/tests/security/http/test_http_bearer.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/tests/security/http/test_http_bearer_description.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/tests/security/http/test_http_bearer_optional.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/tests/security/http/test_http_digest.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/tests/security/http/test_http_digest_description.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/tests/security/http/test_http_digest_optional.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/tests/security/oauth2/__init__.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/tests/security/oauth2/test_oauth2.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/tests/security/oauth2/test_oauth2_authorization_code_bearer.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/tests/security/oauth2/test_oauth2_authorization_code_bearer_description.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/tests/security/oauth2/test_oauth2_optional.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/tests/security/oauth2/test_oauth2_optional_description.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/tests/security/oauth2/test_oauth2_password_bearer_optional.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/tests/security/oauth2/test_oauth2_password_bearer_optional_description.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/tests/security/openid_connect/__init__.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/tests/security/openid_connect/test_openid_connect.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/tests/security/openid_connect/test_openid_connect_description.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/tests/security/openid_connect/test_openid_connect_optional.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/tests/test_additional_properties.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/tests/test_additional_response_extra.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/tests/test_additional_responses_bad.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/tests/test_additional_responses_custom_model_in_callback.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/tests/test_additional_responses_custom_validationerror.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/tests/test_additional_responses_default_validationerror.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/tests/test_additional_responses_response_class.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/tests/test_additional_responses_router.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/tests/test_basic_api.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/tests/test_dataclass_fields.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/tests/test_delimited_params.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/tests/test_http_endpoints.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/tests/test_middleware.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/tests/test_requests_orjson.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/tests/test_requests_ujson.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/tests/test_resolved_param_contextmanagers.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/tests/test_resolved_params.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/tests/test_responses.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/tests/test_responses_orjson.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/tests/test_responses_ujson.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/tests/test_ws_router.py +0 -0
- {starmallow-0.6.2 → starmallow-0.6.3}/tests/utils.py +0 -0
@@ -211,6 +211,7 @@ class EndpointMixin:
|
|
211
211
|
kwargs.update({
|
212
212
|
'load_default': None,
|
213
213
|
'required': False,
|
214
|
+
'allow_none': True, # Even if a default is provided, we should also allow None
|
214
215
|
})
|
215
216
|
# This does not support Union[A,B,C,None]. Only Union[A,None] and Optional[A]
|
216
217
|
model = next((a for a in get_args(type_annotation) if a is not None), None)
|
@@ -0,0 +1,527 @@
|
|
1
|
+
'''Test Resolved Params'''
|
2
|
+
import datetime as dt
|
3
|
+
from typing import Annotated, Literal, Optional, Union
|
4
|
+
|
5
|
+
import marshmallow as ma
|
6
|
+
import marshmallow.fields as mf
|
7
|
+
import pytest
|
8
|
+
from marshmallow_dataclass import dataclass as ma_dataclass
|
9
|
+
from starlette.testclient import TestClient
|
10
|
+
|
11
|
+
from starmallow import Body, Header, Path, Query, ResolvedParam, StarMallow
|
12
|
+
|
13
|
+
from .utils import assert_json
|
14
|
+
|
15
|
+
app = StarMallow()
|
16
|
+
|
17
|
+
|
18
|
+
############################################################
|
19
|
+
# Models - classes and schemas
|
20
|
+
############################################################
|
21
|
+
# region - VS Code folding marker - https://code.visualstudio.com/docs/editor/codebasics#_folding
|
22
|
+
def paging_parameters(
|
23
|
+
offset: Annotated[int, Query(0)],
|
24
|
+
limit: Annotated[int, Query()] = 1000,
|
25
|
+
):
|
26
|
+
return {"offset": offset, "limit": limit}
|
27
|
+
|
28
|
+
|
29
|
+
def search_parameters(q: Annotated[str, Path()]):
|
30
|
+
return {"q": q}
|
31
|
+
|
32
|
+
|
33
|
+
# To test nested resolved params
|
34
|
+
def searchable_page_parameters(
|
35
|
+
paging_params: Annotated[dict[str, int], ResolvedParam(paging_parameters)],
|
36
|
+
search_params: Annotated[dict[str, str], ResolvedParam(search_parameters)],
|
37
|
+
):
|
38
|
+
return {
|
39
|
+
"offset": paging_params["offset"],
|
40
|
+
"limit": paging_params["limit"],
|
41
|
+
"q": search_params["q"],
|
42
|
+
}
|
43
|
+
|
44
|
+
|
45
|
+
@ma_dataclass
|
46
|
+
class PathParams:
|
47
|
+
item_id: int
|
48
|
+
|
49
|
+
|
50
|
+
# For testing with native marshmallow schemas
|
51
|
+
class PathParamsSchema(ma.Schema):
|
52
|
+
item_id = mf.Integer()
|
53
|
+
|
54
|
+
|
55
|
+
@ma_dataclass
|
56
|
+
class MultiPathParams:
|
57
|
+
item_id: int
|
58
|
+
sub_item_id: int
|
59
|
+
|
60
|
+
|
61
|
+
@ma_dataclass
|
62
|
+
class MultiQueryParams:
|
63
|
+
name: str
|
64
|
+
|
65
|
+
|
66
|
+
@ma_dataclass
|
67
|
+
class MultiBodyParams:
|
68
|
+
weight: float
|
69
|
+
|
70
|
+
|
71
|
+
@ma_dataclass
|
72
|
+
class MultiHeaderParams:
|
73
|
+
color: str = 'blue'
|
74
|
+
# endregion
|
75
|
+
# endregion
|
76
|
+
|
77
|
+
|
78
|
+
############################################################
|
79
|
+
# Test API
|
80
|
+
############################################################
|
81
|
+
# region
|
82
|
+
@app.get("/paging")
|
83
|
+
def get_paging(paging_params: Annotated[dict[str, int], ResolvedParam(paging_parameters)]):
|
84
|
+
return paging_params
|
85
|
+
|
86
|
+
|
87
|
+
@app.get("/filtered_paging_1/{q}")
|
88
|
+
def get_filtered_paging_1(search_params: Annotated[dict[str, str], ResolvedParam(search_parameters)]):
|
89
|
+
return search_params
|
90
|
+
|
91
|
+
|
92
|
+
@app.get("/filtered_paging_2/{q}")
|
93
|
+
def get_filtered_paging_2(filtered_paging_params: Annotated[dict[str, int | str], ResolvedParam(searchable_page_parameters)]):
|
94
|
+
return filtered_paging_params
|
95
|
+
|
96
|
+
|
97
|
+
@app.post("/optional_with_default")
|
98
|
+
def post_optional_with_default(
|
99
|
+
optional_body: Annotated[dt.datetime | None, Body()] = dt.datetime.max,
|
100
|
+
):
|
101
|
+
return {'optional_body': optional_body}
|
102
|
+
|
103
|
+
|
104
|
+
@app.post('/multi_combo_optional/{item_id}/{sub_item_id}')
|
105
|
+
def post_multi_combo_optional(
|
106
|
+
item_id: int,
|
107
|
+
path_params: Annotated[MultiPathParams, Path()],
|
108
|
+
query_params: Annotated[MultiQueryParams | None, Query()],
|
109
|
+
body_params: Annotated[MultiBodyParams | None, Body()],
|
110
|
+
weight_unit: Annotated[Optional[Literal['lbs', 'kg']], Query(title='Weight')],
|
111
|
+
color: Annotated[Union[str, None], Header('blue')],
|
112
|
+
# Tests convert_underscores
|
113
|
+
user_agent: Annotated[Optional[str], Header(None)],
|
114
|
+
# Tests aliasing
|
115
|
+
aliased_header: Annotated[Optional[str], Header(None, alias="myalias")],
|
116
|
+
):
|
117
|
+
return {
|
118
|
+
'item_id': item_id,
|
119
|
+
'param_item_id': path_params.item_id,
|
120
|
+
'sub_item': path_params.sub_item_id,
|
121
|
+
# Special None response to signify that the entire object is None
|
122
|
+
'name': query_params.name if query_params is not None else '__NONE__',
|
123
|
+
'weight': body_params.weight if body_params is not None else '__NONE__',
|
124
|
+
'weight_unit': weight_unit,
|
125
|
+
'color': color,
|
126
|
+
'user_agent': user_agent,
|
127
|
+
'aliased_header': aliased_header,
|
128
|
+
}
|
129
|
+
# endregion
|
130
|
+
|
131
|
+
|
132
|
+
############################################################
|
133
|
+
# Tests
|
134
|
+
############################################################
|
135
|
+
# region
|
136
|
+
client = TestClient(app)
|
137
|
+
|
138
|
+
openapi_schema = {
|
139
|
+
"openapi": "3.0.2",
|
140
|
+
"info": {"title": "StarMallow", "version": "0.1.0"},
|
141
|
+
"paths": {
|
142
|
+
"/paging": {
|
143
|
+
"get": {
|
144
|
+
"responses": {
|
145
|
+
"200": {
|
146
|
+
"description": "Successful Response",
|
147
|
+
"content": {"application/json": {"schema": {}}},
|
148
|
+
},
|
149
|
+
"422": {
|
150
|
+
"description": "Validation Error",
|
151
|
+
"content": {
|
152
|
+
"application/json": {
|
153
|
+
"schema": {
|
154
|
+
"$ref": "#/components/schemas/HTTPValidationError",
|
155
|
+
},
|
156
|
+
},
|
157
|
+
},
|
158
|
+
},
|
159
|
+
},
|
160
|
+
"summary": "Get Paging",
|
161
|
+
"operationId": "get_paging_paging_get",
|
162
|
+
"parameters": [
|
163
|
+
{
|
164
|
+
"required": False,
|
165
|
+
"schema": {
|
166
|
+
"default": 0,
|
167
|
+
"type": "integer",
|
168
|
+
"title": "Offset",
|
169
|
+
},
|
170
|
+
"name": "offset",
|
171
|
+
"in": "query",
|
172
|
+
},
|
173
|
+
{
|
174
|
+
"required": False,
|
175
|
+
"schema": {
|
176
|
+
"default": 1000,
|
177
|
+
"type": "integer",
|
178
|
+
"title": "Limit",
|
179
|
+
},
|
180
|
+
"name": "limit",
|
181
|
+
"in": "query",
|
182
|
+
},
|
183
|
+
],
|
184
|
+
},
|
185
|
+
},
|
186
|
+
"/filtered_paging_1/{q}": {
|
187
|
+
"get": {
|
188
|
+
"responses": {
|
189
|
+
"200": {
|
190
|
+
"description": "Successful Response",
|
191
|
+
"content": {"application/json": {"schema": {}}},
|
192
|
+
},
|
193
|
+
"422": {
|
194
|
+
"description": "Validation Error",
|
195
|
+
"content": {
|
196
|
+
"application/json": {
|
197
|
+
"schema": {
|
198
|
+
"$ref": "#/components/schemas/HTTPValidationError",
|
199
|
+
},
|
200
|
+
},
|
201
|
+
},
|
202
|
+
},
|
203
|
+
},
|
204
|
+
"summary": "Get Filtered Paging 1",
|
205
|
+
"operationId": "get_filtered_paging_1_filtered_paging_1__q__get",
|
206
|
+
"parameters": [
|
207
|
+
{
|
208
|
+
"required": True,
|
209
|
+
"schema": {
|
210
|
+
"type": "string",
|
211
|
+
"title": "Q",
|
212
|
+
},
|
213
|
+
"name": "q",
|
214
|
+
"in": "path",
|
215
|
+
},
|
216
|
+
],
|
217
|
+
},
|
218
|
+
},
|
219
|
+
"/filtered_paging_2/{q}": {
|
220
|
+
"get": {
|
221
|
+
"responses": {
|
222
|
+
"200": {
|
223
|
+
"description": "Successful Response",
|
224
|
+
"content": {"application/json": {"schema": {}}},
|
225
|
+
},
|
226
|
+
"422": {
|
227
|
+
"description": "Validation Error",
|
228
|
+
"content": {
|
229
|
+
"application/json": {
|
230
|
+
"schema": {
|
231
|
+
"$ref": "#/components/schemas/HTTPValidationError",
|
232
|
+
},
|
233
|
+
},
|
234
|
+
},
|
235
|
+
},
|
236
|
+
},
|
237
|
+
"summary": "Get Filtered Paging 2",
|
238
|
+
"operationId": "get_filtered_paging_2_filtered_paging_2__q__get",
|
239
|
+
"parameters": [
|
240
|
+
{
|
241
|
+
"required": False,
|
242
|
+
"schema": {
|
243
|
+
"default": 0,
|
244
|
+
"type": "integer",
|
245
|
+
"title": "Offset",
|
246
|
+
},
|
247
|
+
"name": "offset",
|
248
|
+
"in": "query",
|
249
|
+
},
|
250
|
+
{
|
251
|
+
"required": False,
|
252
|
+
"schema": {
|
253
|
+
"default": 1000,
|
254
|
+
"type": "integer",
|
255
|
+
"title": "Limit",
|
256
|
+
},
|
257
|
+
"name": "limit",
|
258
|
+
"in": "query",
|
259
|
+
},
|
260
|
+
{
|
261
|
+
"required": True,
|
262
|
+
"schema": {
|
263
|
+
"type": "string",
|
264
|
+
"title": "Q",
|
265
|
+
},
|
266
|
+
"name": "q",
|
267
|
+
"in": "path",
|
268
|
+
},
|
269
|
+
],
|
270
|
+
},
|
271
|
+
},
|
272
|
+
"/optional_with_default": {
|
273
|
+
"post": {
|
274
|
+
"responses": {
|
275
|
+
"200": {
|
276
|
+
"description": "Successful Response",
|
277
|
+
"content": {"application/json": {"schema": {}}},
|
278
|
+
},
|
279
|
+
"422": {
|
280
|
+
"description": "Validation Error",
|
281
|
+
"content": {
|
282
|
+
"application/json": {
|
283
|
+
"schema": {
|
284
|
+
"$ref": "#/components/schemas/HTTPValidationError",
|
285
|
+
},
|
286
|
+
},
|
287
|
+
},
|
288
|
+
},
|
289
|
+
},
|
290
|
+
"summary": "Post Optional With Default",
|
291
|
+
"operationId": "post_optional_with_default_optional_with_default_post",
|
292
|
+
"requestBody": {
|
293
|
+
"content": {
|
294
|
+
"application/json": {
|
295
|
+
"schema": {
|
296
|
+
"$ref": "#/components/schemas/Body_post_optional_with_default_optional_with_default_post",
|
297
|
+
},
|
298
|
+
},
|
299
|
+
},
|
300
|
+
"required": True,
|
301
|
+
},
|
302
|
+
},
|
303
|
+
},
|
304
|
+
"/multi_combo_optional/{item_id}/{sub_item_id}": {
|
305
|
+
"post": {
|
306
|
+
"responses": {
|
307
|
+
"200": {
|
308
|
+
"description": "Successful Response",
|
309
|
+
"content": {"application/json": {"schema": {}}},
|
310
|
+
},
|
311
|
+
"422": {
|
312
|
+
"description": "Validation Error",
|
313
|
+
"content": {
|
314
|
+
"application/json": {
|
315
|
+
"schema": {
|
316
|
+
"$ref": "#/components/schemas/HTTPValidationError",
|
317
|
+
},
|
318
|
+
},
|
319
|
+
},
|
320
|
+
},
|
321
|
+
},
|
322
|
+
"summary": "Post Multi Combo Optional",
|
323
|
+
"operationId": "post_multi_combo_optional_multi_combo_optional__item_id___sub_item_id__post",
|
324
|
+
"parameters": [
|
325
|
+
{
|
326
|
+
'in': 'query',
|
327
|
+
'name': 'name',
|
328
|
+
'required': True,
|
329
|
+
'schema': {
|
330
|
+
'title': 'Name',
|
331
|
+
'type': 'string',
|
332
|
+
},
|
333
|
+
},
|
334
|
+
{
|
335
|
+
'in': 'query',
|
336
|
+
'name': 'weight_unit',
|
337
|
+
'required': False,
|
338
|
+
'schema': {
|
339
|
+
"default": None,
|
340
|
+
'enum': ['lbs', 'kg'],
|
341
|
+
"nullable": True,
|
342
|
+
'title': 'Weight',
|
343
|
+
},
|
344
|
+
},
|
345
|
+
{
|
346
|
+
'in': 'path',
|
347
|
+
'name': 'item_id',
|
348
|
+
'required': True,
|
349
|
+
'schema': {
|
350
|
+
'title': 'Item Id',
|
351
|
+
'type': 'integer',
|
352
|
+
},
|
353
|
+
},
|
354
|
+
{
|
355
|
+
'in': 'path',
|
356
|
+
'name': 'sub_item_id',
|
357
|
+
'required': True,
|
358
|
+
'schema': {
|
359
|
+
'title': 'Sub Item Id',
|
360
|
+
'type': 'integer',
|
361
|
+
},
|
362
|
+
},
|
363
|
+
{
|
364
|
+
'in': 'header',
|
365
|
+
'name': 'color',
|
366
|
+
'required': False,
|
367
|
+
'schema': {
|
368
|
+
'default': 'blue',
|
369
|
+
"nullable": True,
|
370
|
+
'title': 'Color',
|
371
|
+
'type': 'string',
|
372
|
+
},
|
373
|
+
},
|
374
|
+
{
|
375
|
+
'in': 'header',
|
376
|
+
'name': 'user_agent',
|
377
|
+
'required': False,
|
378
|
+
'schema': {
|
379
|
+
"default": None,
|
380
|
+
"nullable": True,
|
381
|
+
'title': 'User Agent',
|
382
|
+
'type': 'string',
|
383
|
+
},
|
384
|
+
},
|
385
|
+
{
|
386
|
+
'in': 'header',
|
387
|
+
'name': 'aliased_header',
|
388
|
+
'required': False,
|
389
|
+
'schema': {
|
390
|
+
"default": None,
|
391
|
+
"nullable": True,
|
392
|
+
'title': 'Myalias',
|
393
|
+
'type': 'string',
|
394
|
+
},
|
395
|
+
},
|
396
|
+
],
|
397
|
+
'requestBody': {
|
398
|
+
'content': {
|
399
|
+
'application/json': {
|
400
|
+
'schema': {'$ref': '#/components/schemas/MultiBodyParams'},
|
401
|
+
},
|
402
|
+
},
|
403
|
+
'required': False,
|
404
|
+
},
|
405
|
+
},
|
406
|
+
},
|
407
|
+
},
|
408
|
+
"components": {
|
409
|
+
"schemas": {
|
410
|
+
"Body_post_optional_with_default_optional_with_default_post": {
|
411
|
+
"properties": {
|
412
|
+
"optional_body": {
|
413
|
+
"default": "9999-12-31T23:59:59.999999",
|
414
|
+
"format": "date-time",
|
415
|
+
"nullable": True,
|
416
|
+
"title": "Optional Body",
|
417
|
+
"type": "string",
|
418
|
+
},
|
419
|
+
},
|
420
|
+
"required": [],
|
421
|
+
"title": "Body_post_optional_with_default_optional_with_default_post",
|
422
|
+
"type": "object",
|
423
|
+
},
|
424
|
+
"HTTPValidationError": {
|
425
|
+
'properties': {
|
426
|
+
'detail': {
|
427
|
+
'description': 'Error detail',
|
428
|
+
'title': 'Detail',
|
429
|
+
},
|
430
|
+
'errors': {
|
431
|
+
'description': 'Exception or error type',
|
432
|
+
'title': 'Errors',
|
433
|
+
},
|
434
|
+
'status_code': {
|
435
|
+
'description': 'HTTP status code',
|
436
|
+
'title': 'Status Code',
|
437
|
+
'type': 'integer',
|
438
|
+
},
|
439
|
+
},
|
440
|
+
'required': ['detail', 'status_code'],
|
441
|
+
'title': 'HTTPValidationError',
|
442
|
+
'type': 'object',
|
443
|
+
},
|
444
|
+
"MultiBodyParams": {
|
445
|
+
"type": "object",
|
446
|
+
"properties": {
|
447
|
+
"weight": {
|
448
|
+
"type": "number",
|
449
|
+
"title": "Weight",
|
450
|
+
},
|
451
|
+
},
|
452
|
+
"required": [
|
453
|
+
"weight",
|
454
|
+
],
|
455
|
+
"title": "Body Params",
|
456
|
+
},
|
457
|
+
},
|
458
|
+
},
|
459
|
+
}
|
460
|
+
|
461
|
+
|
462
|
+
@pytest.mark.parametrize(
|
463
|
+
"path,expected_status,expected_response",
|
464
|
+
[
|
465
|
+
("/paging?limit=50", 200, {"offset": 0, "limit": 50}),
|
466
|
+
("/filtered_paging_1/name=foobar", 200, {"q": "name=foobar"}),
|
467
|
+
(
|
468
|
+
"/filtered_paging_2/name=foobar?limit=50", 200,
|
469
|
+
{"offset": 0, "limit": 50, "q": "name=foobar"},
|
470
|
+
),
|
471
|
+
("/openapi.json", 200, openapi_schema),
|
472
|
+
],
|
473
|
+
)
|
474
|
+
def test_get_path(path, expected_status, expected_response):
|
475
|
+
response = client.get(path)
|
476
|
+
assert response.status_code == expected_status
|
477
|
+
assert_json(response.json(), expected_response)
|
478
|
+
|
479
|
+
|
480
|
+
@pytest.mark.parametrize(
|
481
|
+
"path,headers,body,expected_status,expected_response",
|
482
|
+
[
|
483
|
+
(
|
484
|
+
"/optional_with_default",
|
485
|
+
{},
|
486
|
+
{},
|
487
|
+
200,
|
488
|
+
{
|
489
|
+
'optional_body': '9999-12-31T23:59:59.999999',
|
490
|
+
},
|
491
|
+
),
|
492
|
+
(
|
493
|
+
"/optional_with_default",
|
494
|
+
{},
|
495
|
+
{
|
496
|
+
'optional_body': None,
|
497
|
+
},
|
498
|
+
200,
|
499
|
+
{
|
500
|
+
'optional_body': None,
|
501
|
+
},
|
502
|
+
),
|
503
|
+
(
|
504
|
+
"/multi_combo_optional/5/3",
|
505
|
+
{},
|
506
|
+
None,
|
507
|
+
200,
|
508
|
+
{
|
509
|
+
'item_id': 5,
|
510
|
+
'param_item_id': 5,
|
511
|
+
'sub_item': 3,
|
512
|
+
'name': '__NONE__',
|
513
|
+
'weight': '__NONE__',
|
514
|
+
'weight_unit': None,
|
515
|
+
'color': 'blue',
|
516
|
+
"user_agent": "testclient",
|
517
|
+
"aliased_header": None,
|
518
|
+
},
|
519
|
+
),
|
520
|
+
],
|
521
|
+
)
|
522
|
+
def test_post_path(path, headers, body, expected_status, expected_response):
|
523
|
+
response = client.post(path, headers=headers, json=body)
|
524
|
+
assert response.status_code == expected_status
|
525
|
+
assert_json(response.json(), expected_response)
|
526
|
+
|
527
|
+
# endregion
|
@@ -1,244 +0,0 @@
|
|
1
|
-
'''Test Resolved Params'''
|
2
|
-
from typing import Annotated
|
3
|
-
|
4
|
-
import pytest
|
5
|
-
from starlette.testclient import TestClient
|
6
|
-
|
7
|
-
from starmallow import Path, Query, ResolvedParam, StarMallow
|
8
|
-
|
9
|
-
from .utils import assert_json
|
10
|
-
|
11
|
-
app = StarMallow()
|
12
|
-
|
13
|
-
|
14
|
-
############################################################
|
15
|
-
# Models - classes and schemas
|
16
|
-
############################################################
|
17
|
-
# region - VS Code folding marker - https://code.visualstudio.com/docs/editor/codebasics#_folding
|
18
|
-
def paging_parameters(
|
19
|
-
offset: Annotated[int, Query(0)],
|
20
|
-
limit: Annotated[int, Query()] = 1000,
|
21
|
-
):
|
22
|
-
return {"offset": offset, "limit": limit}
|
23
|
-
|
24
|
-
|
25
|
-
def search_parameters(q: Annotated[str, Path()]):
|
26
|
-
return {"q": q}
|
27
|
-
|
28
|
-
|
29
|
-
# To test nested resolved params
|
30
|
-
def searchable_page_parameters(
|
31
|
-
paging_params: Annotated[dict[str, int], ResolvedParam(paging_parameters)],
|
32
|
-
search_params: Annotated[dict[str, str], ResolvedParam(search_parameters)],
|
33
|
-
):
|
34
|
-
return {
|
35
|
-
"offset": paging_params["offset"],
|
36
|
-
"limit": paging_params["limit"],
|
37
|
-
"q": search_params["q"],
|
38
|
-
}
|
39
|
-
# endregion
|
40
|
-
|
41
|
-
|
42
|
-
############################################################
|
43
|
-
# Test API
|
44
|
-
############################################################
|
45
|
-
# region
|
46
|
-
@app.get("/paging")
|
47
|
-
def get_paging(paging_params: Annotated[dict[str, int], ResolvedParam(paging_parameters)]):
|
48
|
-
return paging_params
|
49
|
-
|
50
|
-
|
51
|
-
@app.get("/filtered_paging_1/{q}")
|
52
|
-
def get_filtered_paging_1(search_params: Annotated[dict[str, str], ResolvedParam(search_parameters)]):
|
53
|
-
return search_params
|
54
|
-
|
55
|
-
|
56
|
-
@app.get("/filtered_paging_2/{q}")
|
57
|
-
def get_filtered_paging_2(filtered_paging_params: Annotated[dict[str, int | str], ResolvedParam(searchable_page_parameters)]):
|
58
|
-
return filtered_paging_params
|
59
|
-
# endregion
|
60
|
-
|
61
|
-
|
62
|
-
############################################################
|
63
|
-
# Tests
|
64
|
-
############################################################
|
65
|
-
# region
|
66
|
-
client = TestClient(app)
|
67
|
-
|
68
|
-
openapi_schema = {
|
69
|
-
"openapi": "3.0.2",
|
70
|
-
"info": {"title": "StarMallow", "version": "0.1.0"},
|
71
|
-
"paths": {
|
72
|
-
"/paging": {
|
73
|
-
"get": {
|
74
|
-
"responses": {
|
75
|
-
"200": {
|
76
|
-
"description": "Successful Response",
|
77
|
-
"content": {"application/json": {"schema": {}}},
|
78
|
-
},
|
79
|
-
"422": {
|
80
|
-
"description": "Validation Error",
|
81
|
-
"content": {
|
82
|
-
"application/json": {
|
83
|
-
"schema": {
|
84
|
-
"$ref": "#/components/schemas/HTTPValidationError",
|
85
|
-
},
|
86
|
-
},
|
87
|
-
},
|
88
|
-
},
|
89
|
-
},
|
90
|
-
"summary": "Get Paging",
|
91
|
-
"operationId": "get_paging_paging_get",
|
92
|
-
"parameters": [
|
93
|
-
{
|
94
|
-
"required": False,
|
95
|
-
"schema": {
|
96
|
-
"default": 0,
|
97
|
-
"type": "integer",
|
98
|
-
"title": "Offset",
|
99
|
-
},
|
100
|
-
"name": "offset",
|
101
|
-
"in": "query",
|
102
|
-
},
|
103
|
-
{
|
104
|
-
"required": False,
|
105
|
-
"schema": {
|
106
|
-
"default": 1000,
|
107
|
-
"type": "integer",
|
108
|
-
"title": "Limit",
|
109
|
-
},
|
110
|
-
"name": "limit",
|
111
|
-
"in": "query",
|
112
|
-
},
|
113
|
-
],
|
114
|
-
},
|
115
|
-
},
|
116
|
-
"/filtered_paging_1/{q}": {
|
117
|
-
"get": {
|
118
|
-
"responses": {
|
119
|
-
"200": {
|
120
|
-
"description": "Successful Response",
|
121
|
-
"content": {"application/json": {"schema": {}}},
|
122
|
-
},
|
123
|
-
"422": {
|
124
|
-
"description": "Validation Error",
|
125
|
-
"content": {
|
126
|
-
"application/json": {
|
127
|
-
"schema": {
|
128
|
-
"$ref": "#/components/schemas/HTTPValidationError",
|
129
|
-
},
|
130
|
-
},
|
131
|
-
},
|
132
|
-
},
|
133
|
-
},
|
134
|
-
"summary": "Get Filtered Paging 1",
|
135
|
-
"operationId": "get_filtered_paging_1_filtered_paging_1__q__get",
|
136
|
-
"parameters": [
|
137
|
-
{
|
138
|
-
"required": True,
|
139
|
-
"schema": {
|
140
|
-
"type": "string",
|
141
|
-
"title": "Q",
|
142
|
-
},
|
143
|
-
"name": "q",
|
144
|
-
"in": "path",
|
145
|
-
},
|
146
|
-
],
|
147
|
-
},
|
148
|
-
},
|
149
|
-
"/filtered_paging_2/{q}": {
|
150
|
-
"get": {
|
151
|
-
"responses": {
|
152
|
-
"200": {
|
153
|
-
"description": "Successful Response",
|
154
|
-
"content": {"application/json": {"schema": {}}},
|
155
|
-
},
|
156
|
-
"422": {
|
157
|
-
"description": "Validation Error",
|
158
|
-
"content": {
|
159
|
-
"application/json": {
|
160
|
-
"schema": {
|
161
|
-
"$ref": "#/components/schemas/HTTPValidationError",
|
162
|
-
},
|
163
|
-
},
|
164
|
-
},
|
165
|
-
},
|
166
|
-
},
|
167
|
-
"summary": "Get Filtered Paging 2",
|
168
|
-
"operationId": "get_filtered_paging_2_filtered_paging_2__q__get",
|
169
|
-
"parameters": [
|
170
|
-
{
|
171
|
-
"required": False,
|
172
|
-
"schema": {
|
173
|
-
"default": 0,
|
174
|
-
"type": "integer",
|
175
|
-
"title": "Offset",
|
176
|
-
},
|
177
|
-
"name": "offset",
|
178
|
-
"in": "query",
|
179
|
-
},
|
180
|
-
{
|
181
|
-
"required": False,
|
182
|
-
"schema": {
|
183
|
-
"default": 1000,
|
184
|
-
"type": "integer",
|
185
|
-
"title": "Limit",
|
186
|
-
},
|
187
|
-
"name": "limit",
|
188
|
-
"in": "query",
|
189
|
-
},
|
190
|
-
{
|
191
|
-
"required": True,
|
192
|
-
"schema": {
|
193
|
-
"type": "string",
|
194
|
-
"title": "Q",
|
195
|
-
},
|
196
|
-
"name": "q",
|
197
|
-
"in": "path",
|
198
|
-
},
|
199
|
-
],
|
200
|
-
},
|
201
|
-
},
|
202
|
-
},
|
203
|
-
"components": {
|
204
|
-
"schemas": {
|
205
|
-
"HTTPValidationError": {
|
206
|
-
'properties': {
|
207
|
-
'detail': {
|
208
|
-
'description': 'Error detail',
|
209
|
-
'title': 'Detail',
|
210
|
-
},
|
211
|
-
'errors': {
|
212
|
-
'description': 'Exception or error type',
|
213
|
-
'title': 'Errors',
|
214
|
-
},
|
215
|
-
'status_code': {
|
216
|
-
'description': 'HTTP status code',
|
217
|
-
'title': 'Status Code',
|
218
|
-
'type': 'integer',
|
219
|
-
},
|
220
|
-
},
|
221
|
-
'required': ['detail', 'status_code'],
|
222
|
-
'title': 'HTTPValidationError',
|
223
|
-
'type': 'object',
|
224
|
-
},
|
225
|
-
},
|
226
|
-
},
|
227
|
-
}
|
228
|
-
|
229
|
-
|
230
|
-
@pytest.mark.parametrize(
|
231
|
-
"path,expected_status,expected_response",
|
232
|
-
[
|
233
|
-
("/paging?limit=50", 200, {"offset": 0, "limit": 50}),
|
234
|
-
("/filtered_paging_1/name=foobar", 200, {"q": "name=foobar"}),
|
235
|
-
("/filtered_paging_2/name=foobar?limit=50", 200, {"offset": 0, "limit": 50, "q": "name=foobar"}),
|
236
|
-
("/openapi.json", 200, openapi_schema),
|
237
|
-
],
|
238
|
-
)
|
239
|
-
def test_get_path(path, expected_status, expected_response):
|
240
|
-
response = client.get(path)
|
241
|
-
assert response.status_code == expected_status
|
242
|
-
assert_json(response.json(), expected_response)
|
243
|
-
|
244
|
-
# endregion
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{starmallow-0.6.2 → starmallow-0.6.3}/tests/security/api_key/test_api_key_cookie_description.py
RENAMED
File without changes
|
{starmallow-0.6.2 → starmallow-0.6.3}/tests/security/api_key/test_api_key_cookie_optional.py
RENAMED
File without changes
|
File without changes
|
{starmallow-0.6.2 → starmallow-0.6.3}/tests/security/api_key/test_api_key_header_description.py
RENAMED
File without changes
|
{starmallow-0.6.2 → starmallow-0.6.3}/tests/security/api_key/test_api_key_header_optional.py
RENAMED
File without changes
|
File without changes
|
{starmallow-0.6.2 → starmallow-0.6.3}/tests/security/api_key/test_api_key_query_description.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{starmallow-0.6.2 → starmallow-0.6.3}/tests/security/http/test_http_basic_realm_description.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{starmallow-0.6.2 → starmallow-0.6.3}/tests/security/oauth2/test_oauth2_authorization_code_bearer.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
{starmallow-0.6.2 → starmallow-0.6.3}/tests/security/oauth2/test_oauth2_optional_description.py
RENAMED
File without changes
|
{starmallow-0.6.2 → starmallow-0.6.3}/tests/security/oauth2/test_oauth2_password_bearer_optional.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{starmallow-0.6.2 → starmallow-0.6.3}/tests/security/openid_connect/test_openid_connect_optional.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{starmallow-0.6.2 → starmallow-0.6.3}/tests/test_additional_responses_custom_model_in_callback.py
RENAMED
File without changes
|
{starmallow-0.6.2 → starmallow-0.6.3}/tests/test_additional_responses_custom_validationerror.py
RENAMED
File without changes
|
{starmallow-0.6.2 → starmallow-0.6.3}/tests/test_additional_responses_default_validationerror.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|