wellapi 0.2.1__py3-none-any.whl
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.
- wellapi/__init__.py +5 -0
- wellapi/__main__.py +3 -0
- wellapi/applications.py +389 -0
- wellapi/awsmodel.py +17 -0
- wellapi/build/__init__.py +0 -0
- wellapi/build/cdk.py +141 -0
- wellapi/build/packager.py +82 -0
- wellapi/build/sam_openapi.py +10 -0
- wellapi/cli/__init__.py +0 -0
- wellapi/cli/main.py +67 -0
- wellapi/convertors.py +89 -0
- wellapi/datastructures.py +383 -0
- wellapi/dependencies/__init__.py +0 -0
- wellapi/dependencies/models.py +138 -0
- wellapi/dependencies/utils.py +923 -0
- wellapi/exceptions.py +53 -0
- wellapi/local/__init__.py +0 -0
- wellapi/local/reloader.py +94 -0
- wellapi/local/router.py +116 -0
- wellapi/local/server.py +154 -0
- wellapi/middleware/__init__.py +0 -0
- wellapi/middleware/base.py +18 -0
- wellapi/middleware/error.py +239 -0
- wellapi/middleware/exceptions.py +74 -0
- wellapi/middleware/main.py +26 -0
- wellapi/models.py +150 -0
- wellapi/openapi/__init__.py +0 -0
- wellapi/openapi/docs.py +344 -0
- wellapi/openapi/models.py +404 -0
- wellapi/openapi/utils.py +535 -0
- wellapi/params.py +481 -0
- wellapi/routing.py +248 -0
- wellapi/security.py +82 -0
- wellapi/utils.py +37 -0
- wellapi-0.2.1.dist-info/METADATA +32 -0
- wellapi-0.2.1.dist-info/RECORD +38 -0
- wellapi-0.2.1.dist-info/WHEEL +4 -0
- wellapi-0.2.1.dist-info/entry_points.txt +2 -0
wellapi/routing.py
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import re
|
|
3
|
+
from collections.abc import Callable
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from pydantic.main import IncEx
|
|
7
|
+
|
|
8
|
+
from wellapi.convertors import CONVERTOR_TYPES
|
|
9
|
+
from wellapi.datastructures import Default, DefaultPlaceholder
|
|
10
|
+
from wellapi.dependencies.models import Dependant, ModelField
|
|
11
|
+
from wellapi.dependencies.utils import solve_dependencies
|
|
12
|
+
from wellapi.exceptions import (
|
|
13
|
+
HTTPException,
|
|
14
|
+
RequestValidationError,
|
|
15
|
+
ResponseValidationError,
|
|
16
|
+
WellAPIError,
|
|
17
|
+
)
|
|
18
|
+
from wellapi.models import RequestAPIGateway, RequestJob, RequestSQS, ResponseAPIGateway
|
|
19
|
+
|
|
20
|
+
# Match parameters in URL paths, eg. '{param}', and '{param:int}'
|
|
21
|
+
PARAM_REGEX = re.compile("{([a-zA-Z_][a-zA-Z0-9_]*)(:[a-zA-Z_][a-zA-Z0-9_]*)?}")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def compile_path(path: str) -> tuple:
|
|
25
|
+
"""
|
|
26
|
+
Given a path string, like: "/{username:str}",
|
|
27
|
+
or a host string, like: "{subdomain}.mydomain.org", return a three-tuple
|
|
28
|
+
of (regex, format, {param_name:convertor}).
|
|
29
|
+
|
|
30
|
+
regex: "/(?P<username>[^/]+)"
|
|
31
|
+
format: "/{username}"
|
|
32
|
+
convertors: {"username": StringConvertor()}
|
|
33
|
+
"""
|
|
34
|
+
is_host = not path.startswith("/")
|
|
35
|
+
|
|
36
|
+
path_regex = "^"
|
|
37
|
+
path_format = ""
|
|
38
|
+
duplicated_params = set()
|
|
39
|
+
|
|
40
|
+
idx = 0
|
|
41
|
+
param_convertors = {}
|
|
42
|
+
for match in PARAM_REGEX.finditer(path):
|
|
43
|
+
param_name, convertor_type = match.groups("str")
|
|
44
|
+
convertor_type = convertor_type.lstrip(":")
|
|
45
|
+
assert convertor_type in CONVERTOR_TYPES, (
|
|
46
|
+
f"Unknown path convertor '{convertor_type}'"
|
|
47
|
+
)
|
|
48
|
+
convertor = CONVERTOR_TYPES[convertor_type]
|
|
49
|
+
|
|
50
|
+
path_regex += re.escape(path[idx : match.start()])
|
|
51
|
+
path_regex += f"(?P<{param_name}>{convertor.regex})"
|
|
52
|
+
|
|
53
|
+
path_format += path[idx : match.start()]
|
|
54
|
+
path_format += "{%s}" % param_name # noqa: UP031
|
|
55
|
+
|
|
56
|
+
if param_name in param_convertors:
|
|
57
|
+
duplicated_params.add(param_name)
|
|
58
|
+
|
|
59
|
+
param_convertors[param_name] = convertor
|
|
60
|
+
|
|
61
|
+
idx = match.end()
|
|
62
|
+
|
|
63
|
+
if duplicated_params:
|
|
64
|
+
names = ", ".join(sorted(duplicated_params))
|
|
65
|
+
ending = "s" if len(duplicated_params) > 1 else ""
|
|
66
|
+
raise ValueError(f"Duplicated param name{ending} {names} at path {path}")
|
|
67
|
+
|
|
68
|
+
if is_host:
|
|
69
|
+
# Align with `Host.matches()` behavior, which ignores port.
|
|
70
|
+
hostname = path[idx:].split(":")[0]
|
|
71
|
+
path_regex += re.escape(hostname) + "$"
|
|
72
|
+
else:
|
|
73
|
+
path_regex += re.escape(path[idx:]) + "$"
|
|
74
|
+
|
|
75
|
+
path_format += path[idx:]
|
|
76
|
+
|
|
77
|
+
return re.compile(path_regex), path_format, param_convertors
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def get_request_handler(
|
|
81
|
+
dependant: Dependant,
|
|
82
|
+
status_code: int | None = None,
|
|
83
|
+
response_class: type[ResponseAPIGateway] | DefaultPlaceholder = Default(
|
|
84
|
+
ResponseAPIGateway
|
|
85
|
+
),
|
|
86
|
+
response_field: ModelField | None = None,
|
|
87
|
+
response_model_include: IncEx | None = None,
|
|
88
|
+
response_model_exclude: IncEx | None = None,
|
|
89
|
+
response_model_by_alias: bool = True,
|
|
90
|
+
response_model_exclude_unset: bool = False,
|
|
91
|
+
response_model_exclude_defaults: bool = False,
|
|
92
|
+
response_model_exclude_none: bool = False,
|
|
93
|
+
embed_body_fields: bool = False,
|
|
94
|
+
) -> Callable[[RequestAPIGateway], ResponseAPIGateway]:
|
|
95
|
+
assert dependant.call is not None, "dependant.call must be a function"
|
|
96
|
+
|
|
97
|
+
if isinstance(response_class, DefaultPlaceholder):
|
|
98
|
+
actual_response_class: type[ResponseAPIGateway] = response_class.value
|
|
99
|
+
else:
|
|
100
|
+
actual_response_class = response_class
|
|
101
|
+
|
|
102
|
+
def app(request: RequestAPIGateway) -> ResponseAPIGateway:
|
|
103
|
+
response: ResponseAPIGateway | None = None
|
|
104
|
+
try:
|
|
105
|
+
body: Any = request.json()
|
|
106
|
+
except json.JSONDecodeError as e:
|
|
107
|
+
validation_error = RequestValidationError(
|
|
108
|
+
[
|
|
109
|
+
{
|
|
110
|
+
"type": "json_invalid",
|
|
111
|
+
"loc": ("body", e.pos),
|
|
112
|
+
"msg": "JSON decode error",
|
|
113
|
+
"input": {},
|
|
114
|
+
"ctx": {"error": e.msg},
|
|
115
|
+
}
|
|
116
|
+
],
|
|
117
|
+
body=e.doc,
|
|
118
|
+
)
|
|
119
|
+
raise validation_error from e
|
|
120
|
+
except HTTPException:
|
|
121
|
+
# If a middleware raises an HTTPException, it should be raised again
|
|
122
|
+
raise
|
|
123
|
+
except Exception as e:
|
|
124
|
+
http_error = HTTPException(
|
|
125
|
+
status_code=400, detail="There was an error parsing the body"
|
|
126
|
+
)
|
|
127
|
+
raise http_error from e
|
|
128
|
+
errors: list[Any] = []
|
|
129
|
+
solved_result = solve_dependencies(
|
|
130
|
+
request=request,
|
|
131
|
+
dependant=dependant,
|
|
132
|
+
body=body,
|
|
133
|
+
embed_body_fields=embed_body_fields,
|
|
134
|
+
)
|
|
135
|
+
errors = solved_result.errors
|
|
136
|
+
if not errors:
|
|
137
|
+
raw_response = dependant.call(**solved_result.values)
|
|
138
|
+
if isinstance(raw_response, ResponseAPIGateway):
|
|
139
|
+
response = raw_response
|
|
140
|
+
else:
|
|
141
|
+
response_args: dict[str, Any] = {}
|
|
142
|
+
# If status_code was set, use it, otherwise use the default from the
|
|
143
|
+
# response class, in the case of redirect it's 307
|
|
144
|
+
current_status_code = (
|
|
145
|
+
status_code if status_code else solved_result.response.statusCode
|
|
146
|
+
)
|
|
147
|
+
if current_status_code is not None:
|
|
148
|
+
response_args["status_code"] = current_status_code
|
|
149
|
+
if solved_result.response.statusCode:
|
|
150
|
+
response_args["status_code"] = solved_result.response.statusCode
|
|
151
|
+
content = serialize_response(
|
|
152
|
+
field=response_field,
|
|
153
|
+
response_content=raw_response,
|
|
154
|
+
include=response_model_include,
|
|
155
|
+
exclude=response_model_exclude,
|
|
156
|
+
by_alias=response_model_by_alias,
|
|
157
|
+
exclude_unset=response_model_exclude_unset,
|
|
158
|
+
exclude_defaults=response_model_exclude_defaults,
|
|
159
|
+
exclude_none=response_model_exclude_none,
|
|
160
|
+
)
|
|
161
|
+
response = actual_response_class(content, **response_args)
|
|
162
|
+
if not is_body_allowed_for_status_code(response.statusCode):
|
|
163
|
+
response.body = ""
|
|
164
|
+
response.headers.raw.extend(solved_result.response.headers.raw)
|
|
165
|
+
if errors:
|
|
166
|
+
validation_error = RequestValidationError(errors, body=body)
|
|
167
|
+
raise validation_error
|
|
168
|
+
if response is None:
|
|
169
|
+
raise WellAPIError(
|
|
170
|
+
"No response object was returned. There's a high chance that the "
|
|
171
|
+
"application code is raising an exception and a dependency with yield "
|
|
172
|
+
"has a block with a bare except, or a block with except Exception, "
|
|
173
|
+
"and is not raising the exception again. Read more about it in the "
|
|
174
|
+
"docs: https://fastapi.tiangolo.com/tutorial/dependencies/dependencies-with-yield/#dependencies-with-yield-and-except"
|
|
175
|
+
)
|
|
176
|
+
return response
|
|
177
|
+
|
|
178
|
+
return app
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def serialize_response(
|
|
182
|
+
*,
|
|
183
|
+
field: ModelField | None = None,
|
|
184
|
+
response_content: Any,
|
|
185
|
+
include: IncEx | None = None,
|
|
186
|
+
exclude: IncEx | None = None,
|
|
187
|
+
by_alias: bool = True,
|
|
188
|
+
exclude_unset: bool = False,
|
|
189
|
+
exclude_defaults: bool = False,
|
|
190
|
+
exclude_none: bool = False,
|
|
191
|
+
) -> Any:
|
|
192
|
+
if field:
|
|
193
|
+
errors = []
|
|
194
|
+
value, errors_ = field.validate(response_content, {}, loc=("response",))
|
|
195
|
+
|
|
196
|
+
if isinstance(errors_, list):
|
|
197
|
+
errors.extend(errors_)
|
|
198
|
+
elif errors_:
|
|
199
|
+
errors.append(errors_)
|
|
200
|
+
if errors:
|
|
201
|
+
raise ResponseValidationError(errors=errors, body=response_content)
|
|
202
|
+
|
|
203
|
+
return field.serialize(
|
|
204
|
+
value,
|
|
205
|
+
include=include,
|
|
206
|
+
exclude=exclude,
|
|
207
|
+
by_alias=by_alias,
|
|
208
|
+
exclude_unset=exclude_unset,
|
|
209
|
+
exclude_defaults=exclude_defaults,
|
|
210
|
+
exclude_none=exclude_none,
|
|
211
|
+
)
|
|
212
|
+
else:
|
|
213
|
+
return json.dumps(response_content)
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def is_body_allowed_for_status_code(status_code: int | str | None) -> bool:
|
|
217
|
+
if status_code is None:
|
|
218
|
+
return True
|
|
219
|
+
# Ref: https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#patterned-fields-1
|
|
220
|
+
if status_code in {
|
|
221
|
+
"default",
|
|
222
|
+
"1XX",
|
|
223
|
+
"2XX",
|
|
224
|
+
"3XX",
|
|
225
|
+
"4XX",
|
|
226
|
+
"5XX",
|
|
227
|
+
}:
|
|
228
|
+
return True
|
|
229
|
+
current_status_code = int(status_code)
|
|
230
|
+
return not (current_status_code < 200 or current_status_code in {204, 205, 304})
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def request_response(
|
|
234
|
+
func: Callable[[RequestAPIGateway], ResponseAPIGateway],
|
|
235
|
+
) -> Callable[[dict, dict], dict]:
|
|
236
|
+
def app(event: dict, context: dict) -> dict:
|
|
237
|
+
if "Records" in event:
|
|
238
|
+
request = RequestSQS.create_request_from_event(event)
|
|
239
|
+
elif "source" in event and event["source"] == "aws.events":
|
|
240
|
+
request = RequestJob.create_request_from_event(event)
|
|
241
|
+
else:
|
|
242
|
+
request = RequestAPIGateway.create_request_from_event(event)
|
|
243
|
+
|
|
244
|
+
resp = func(request)
|
|
245
|
+
|
|
246
|
+
return resp.to_aws_response()
|
|
247
|
+
|
|
248
|
+
return app
|
wellapi/security.py
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
from typing import Any, cast
|
|
2
|
+
|
|
3
|
+
from wellapi.exceptions import HTTPException
|
|
4
|
+
from wellapi.models import RequestAPIGateway
|
|
5
|
+
from wellapi.openapi.models import OAuth2 as OAuth2Model
|
|
6
|
+
from wellapi.openapi.models import OAuthFlows as OAuthFlowsModel
|
|
7
|
+
from wellapi.openapi.models import SecurityBase as SecurityBaseModel
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class SecurityBase:
|
|
11
|
+
model: SecurityBaseModel
|
|
12
|
+
scheme_name: str
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class OAuth2(SecurityBase):
|
|
16
|
+
def __init__(
|
|
17
|
+
self,
|
|
18
|
+
*,
|
|
19
|
+
flows: OAuthFlowsModel | dict[str, dict[str, Any]] = OAuthFlowsModel(),
|
|
20
|
+
scheme_name: str | None = None,
|
|
21
|
+
description: str | None = None,
|
|
22
|
+
auto_error: bool = True,
|
|
23
|
+
):
|
|
24
|
+
self.model = OAuth2Model(
|
|
25
|
+
flows=cast(OAuthFlowsModel, flows), description=description
|
|
26
|
+
)
|
|
27
|
+
self.scheme_name = scheme_name or self.__class__.__name__
|
|
28
|
+
self.auto_error = auto_error
|
|
29
|
+
|
|
30
|
+
def __call__(self, request: RequestAPIGateway) -> str | None:
|
|
31
|
+
authorization = request.headers.get("Authorization")
|
|
32
|
+
if not authorization:
|
|
33
|
+
if self.auto_error:
|
|
34
|
+
raise HTTPException(status_code=403, detail="Not authenticated")
|
|
35
|
+
else:
|
|
36
|
+
return None
|
|
37
|
+
return authorization
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class OAuth2PasswordBearer(OAuth2):
|
|
41
|
+
def __init__(
|
|
42
|
+
self,
|
|
43
|
+
tokenUrl: str,
|
|
44
|
+
scheme_name: str | None = None,
|
|
45
|
+
scopes: dict[str, str] | None = None,
|
|
46
|
+
description: str | None = None,
|
|
47
|
+
auto_error: bool = True,
|
|
48
|
+
):
|
|
49
|
+
if not scopes:
|
|
50
|
+
scopes = {}
|
|
51
|
+
flows = OAuthFlowsModel(
|
|
52
|
+
password=cast(Any, {"tokenUrl": tokenUrl, "scopes": scopes})
|
|
53
|
+
)
|
|
54
|
+
super().__init__(
|
|
55
|
+
flows=flows,
|
|
56
|
+
scheme_name=scheme_name,
|
|
57
|
+
description=description,
|
|
58
|
+
auto_error=auto_error,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
def __call__(self, request: RequestAPIGateway) -> str | None:
|
|
62
|
+
authorization = request.headers.get("Authorization")
|
|
63
|
+
scheme, param = get_authorization_scheme_param(authorization)
|
|
64
|
+
if not authorization or scheme.lower() != "bearer":
|
|
65
|
+
if self.auto_error:
|
|
66
|
+
raise HTTPException(
|
|
67
|
+
status_code=401,
|
|
68
|
+
detail="Not authenticated",
|
|
69
|
+
headers={"WWW-Authenticate": "Bearer"},
|
|
70
|
+
)
|
|
71
|
+
else:
|
|
72
|
+
return None
|
|
73
|
+
return param
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def get_authorization_scheme_param(
|
|
77
|
+
authorization_header_value: str | None,
|
|
78
|
+
) -> tuple[str, str]:
|
|
79
|
+
if not authorization_header_value:
|
|
80
|
+
return "", ""
|
|
81
|
+
scheme, _, param = authorization_header_value.partition(" ")
|
|
82
|
+
return scheme, param
|
wellapi/utils.py
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import importlib
|
|
2
|
+
import os
|
|
3
|
+
import sys
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def import_app(app: str):
|
|
8
|
+
app_modal, app_name = app.split(":")
|
|
9
|
+
app_path = f"{os.path.abspath(Path(app_modal))}.py"
|
|
10
|
+
|
|
11
|
+
spec = importlib.util.spec_from_file_location(app_modal, app_path)
|
|
12
|
+
main = importlib.util.module_from_spec(spec)
|
|
13
|
+
sys.modules[app_modal] = main
|
|
14
|
+
spec.loader.exec_module(main)
|
|
15
|
+
|
|
16
|
+
return getattr(main, app_name)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def load_handlers(handlers_dir: str):
|
|
20
|
+
handlers_path = Path(handlers_dir)
|
|
21
|
+
handlers_module = handlers_path.name
|
|
22
|
+
base_path = Path(os.path.dirname(handlers_path))
|
|
23
|
+
|
|
24
|
+
if not handlers_path.exists() or not handlers_path.is_dir():
|
|
25
|
+
print(f"Директорія {handlers_path} не існує")
|
|
26
|
+
return
|
|
27
|
+
|
|
28
|
+
# Додаємо шлях до директорії у sys.path для імпорту
|
|
29
|
+
sys.path.insert(0, str(base_path))
|
|
30
|
+
|
|
31
|
+
# Імпортуємо всі Python файли з директорії
|
|
32
|
+
for file_path in handlers_path.glob("*.py"):
|
|
33
|
+
module_name = f"{handlers_module}.{file_path.stem}"
|
|
34
|
+
try:
|
|
35
|
+
importlib.import_module(module_name)
|
|
36
|
+
except ImportError as e:
|
|
37
|
+
print(f"Помилка імпорту {module_name}: {e}")
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: wellapi
|
|
3
|
+
Version: 0.2.1
|
|
4
|
+
Summary: Add your description here
|
|
5
|
+
Author-email: ramses <romayuhym@gmail.com>
|
|
6
|
+
Requires-Python: >=3.12
|
|
7
|
+
Requires-Dist: aws-cdk-lib>=2.189.1
|
|
8
|
+
Requires-Dist: email-validator>=2.0.0
|
|
9
|
+
Requires-Dist: pydantic==2.11.3
|
|
10
|
+
Requires-Dist: typing-extensions>=4.8.0
|
|
11
|
+
Provides-Extra: standard
|
|
12
|
+
Requires-Dist: click>=8.1.8; extra == 'standard'
|
|
13
|
+
Requires-Dist: watchdog>=4.0.2; extra == 'standard'
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
|
|
16
|
+
TODO
|
|
17
|
+
|
|
18
|
+
- [ ] OpenAPI
|
|
19
|
+
- [X] Request validation
|
|
20
|
+
- [ ] CORS
|
|
21
|
+
- [ ] x-api-key
|
|
22
|
+
- [ ] cache
|
|
23
|
+
- [ ] access log
|
|
24
|
+
- [ ] Build
|
|
25
|
+
- [ ] Clean up
|
|
26
|
+
- [x] Use multi Value Request Parameters (Headers, QueryString)
|
|
27
|
+
- [ ] Check AWS warning
|
|
28
|
+
|
|
29
|
+
The API with ID 6526xo4n92 doesn’t include a resource with path /* having an integration arn:aws:lambda:eu-central-1:985539757029:function:MainHelloFunction on the ANY method.
|
|
30
|
+
|
|
31
|
+
The API with ID doesn’t include a resource with path /* having an integration on the ANY method.
|
|
32
|
+
- [ ] [Dependency groups](https://docs.astral.sh/uv/concepts/projects/dependencies/)
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
wellapi/__init__.py,sha256=N6ifyrbNKflKx8oJ7xYi3vV5SWLuiQMqJJtUxU5K3Ww,115
|
|
2
|
+
wellapi/__main__.py,sha256=nEdSvtvjzSyjlucTTawydRW7_Yrn-8tIvpbn-k4s3pQ,33
|
|
3
|
+
wellapi/applications.py,sha256=U6vThhLg3qQtC9rCa0PtEHK1DEtfqzeqaVX1cwlxJRU,14468
|
|
4
|
+
wellapi/awsmodel.py,sha256=Vnx_HcQUAxH5EKhZRFwd7CsThZ2RPaO6asF3qX4EZZk,303
|
|
5
|
+
wellapi/convertors.py,sha256=Z8alTWkTTahOYLw-O6oLpd7kvXT11qtF4lbRI_7yq_o,2317
|
|
6
|
+
wellapi/datastructures.py,sha256=Wl6dMoD-E1F5ZozoYQecZZv-wNfIxCvCLTM9hE97FBM,12923
|
|
7
|
+
wellapi/exceptions.py,sha256=FvcBiLayr27drUy8xVX5y3SCXlNrm8DAyZOvFjnk8VE,1495
|
|
8
|
+
wellapi/models.py,sha256=dXhBcKsQuuQv4Ja7DTg2wOz9W5xxbM0qcc-57VE0MnY,4271
|
|
9
|
+
wellapi/params.py,sha256=cwrXYmFarMP1r3OqgQ6aeLK4rleprcwn6Canwhz-c20,16190
|
|
10
|
+
wellapi/routing.py,sha256=2n9K4KnlMhkCtiwn1aUHmwzcKZkKZbtmEAQQv9usXh8,8999
|
|
11
|
+
wellapi/security.py,sha256=qdSETiJZXIrbbSJFN9Ilyk-U3nagzYpE1XZtA_IFtWE,2625
|
|
12
|
+
wellapi/utils.py,sha256=AH45QMkL96FTizoIBGBIzSBelrMeWdr477rJRpw_bfo,1220
|
|
13
|
+
wellapi/build/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
14
|
+
wellapi/build/cdk.py,sha256=BLMRQ8cEHyj2q3jHEC7rduIyeHwebHokYRgxkdYb2v4,4648
|
|
15
|
+
wellapi/build/packager.py,sha256=cmQaEAVT_F7X2tkFSZug9LZJpfpRO54ysoYtfde18w0,1883
|
|
16
|
+
wellapi/build/sam_openapi.py,sha256=SAbgR5HnscCNEVXQBH7eMlJd9h0jeW17mbFTeJiwHiI,356
|
|
17
|
+
wellapi/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
18
|
+
wellapi/cli/main.py,sha256=75VhVYryXjadu3Tr_7CKCRbDMOiwLXGXxoA5oYRt7DA,1966
|
|
19
|
+
wellapi/dependencies/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
20
|
+
wellapi/dependencies/models.py,sha256=GIagLUOKFp9FndQlSrmcRXcvlj2kaaSdXdFvB0aGHcU,4322
|
|
21
|
+
wellapi/dependencies/utils.py,sha256=qcx38SjVtYlFIRKUhWbBwu0TmnS9KAK_VBF1ZqlIwGE,32581
|
|
22
|
+
wellapi/local/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
23
|
+
wellapi/local/reloader.py,sha256=BgF4zSipXhfX-Lk9NkKBaaTCtIz_gV9p1sSkeADP67c,3190
|
|
24
|
+
wellapi/local/router.py,sha256=m_1LZ-DGL2TPJyq8Czrb-cc7HFMdKhqgfjyw9lFPeyQ,3294
|
|
25
|
+
wellapi/local/server.py,sha256=wKS-jarw66wXhAGfTpYbZqtH1bmP4wY7rz5rNqhU9wA,5611
|
|
26
|
+
wellapi/middleware/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
27
|
+
wellapi/middleware/base.py,sha256=0zAN4ydARbVf-3_q_9KVEep4LBvUDklhl_zEWjnIlwM,647
|
|
28
|
+
wellapi/middleware/error.py,sha256=ozpCk980nkpQa_jWLv0J4YxKvWUtnP8RJcv0HyvCGGI,7066
|
|
29
|
+
wellapi/middleware/exceptions.py,sha256=pQGWR5lnJcSvkBDhPg54CKg4ximgaYWNJU3fJ5caRLI,2370
|
|
30
|
+
wellapi/middleware/main.py,sha256=orMTtLPETezQOeFsrPju9p68v2Cdi3EZ2iHi9UzMV30,761
|
|
31
|
+
wellapi/openapi/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
32
|
+
wellapi/openapi/docs.py,sha256=bCwrv9OlJNfh3WA5gKlsbMYM1irYkDi5fP9y8Y2WNMg,10323
|
|
33
|
+
wellapi/openapi/models.py,sha256=9BD9G2FrstD4uuZZ2aHSrCBoWuU_jv2gQwo7LBWbf7c,13434
|
|
34
|
+
wellapi/openapi/utils.py,sha256=ecPSmcS6FUzbU6-ATYwuEgLbXa8wj55jmbjHhT7xECg,20147
|
|
35
|
+
wellapi-0.2.1.dist-info/METADATA,sha256=xkSpufFydjtrc5CLhrqSDoHriIeLG5YnqiCpL4iX4XM,1063
|
|
36
|
+
wellapi-0.2.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
37
|
+
wellapi-0.2.1.dist-info/entry_points.txt,sha256=O4XF2o637XkqrRiaZjPXxU_P1y6-2sRaYaBw9Jc7IJM,49
|
|
38
|
+
wellapi-0.2.1.dist-info/RECORD,,
|