star-openapi 0.9.2__tar.gz → 0.9.4__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.
- star_openapi-0.9.4/CHANGELOG.md +19 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/PKG-INFO +1 -1
- star_openapi-0.9.4/docs/images/logo-text.gif +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/examples/api_router_demo.py +6 -1
- {star_openapi-0.9.2 → star_openapi-0.9.4}/examples/form_demo.py +1 -1
- {star_openapi-0.9.2 → star_openapi-0.9.4}/examples/request_body_demo.py +2 -2
- {star_openapi-0.9.2 → star_openapi-0.9.4}/pyproject.toml +14 -15
- star_openapi-0.9.4/star_openapi/__version__.py +1 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/star_openapi/cli.py +4 -16
- {star_openapi-0.9.2 → star_openapi-0.9.4}/star_openapi/endpoint.py +9 -0
- star_openapi-0.9.4/star_openapi/models/__init__.py +83 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/star_openapi/openapi.py +120 -16
- {star_openapi-0.9.2 → star_openapi-0.9.4}/star_openapi/request.py +5 -5
- {star_openapi-0.9.2 → star_openapi-0.9.4}/star_openapi/router.py +211 -20
- {star_openapi-0.9.2 → star_openapi-0.9.4}/star_openapi/utils.py +5 -20
- star_openapi-0.9.4/tests/config.py +10 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/tests/test_api_router.py +12 -1
- star_openapi-0.9.4/tests/test_body.py +39 -0
- star_openapi-0.9.4/tests/test_cli.py +41 -0
- star_openapi-0.9.4/tests/test_config.py +9 -0
- star_openapi-0.9.4/tests/test_cookie.py +49 -0
- star_openapi-0.9.4/tests/test_form.py +49 -0
- star_openapi-0.9.4/tests/test_header.py +87 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/tests/test_nested_api_router.py +22 -3
- star_openapi-0.9.4/tests/test_openapi.py +78 -0
- star_openapi-0.9.4/tests/test_path.py +44 -0
- star_openapi-0.9.4/tests/test_query.py +80 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/tests/test_request_body.py +35 -15
- {star_openapi-0.9.2 → star_openapi-0.9.4}/tests/test_response.py +17 -2
- {star_openapi-0.9.2 → star_openapi-0.9.4}/tests/test_restapi.py +11 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/tests/test_websocket.py +2 -2
- {star_openapi-0.9.2 → star_openapi-0.9.4}/uv.lock +126 -111
- star_openapi-0.9.2/CHANGELOG.md +0 -11
- star_openapi-0.9.2/docs/images/logo-text.gif +0 -0
- star_openapi-0.9.2/star_openapi/__version__.py +0 -1
- star_openapi-0.9.2/star_openapi/models/__init__.py +0 -83
- star_openapi-0.9.2/tests/test_cli.py +0 -26
- star_openapi-0.9.2/tests/test_form.py +0 -26
- {star_openapi-0.9.2 → star_openapi-0.9.4}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/.github/dependabot.yml +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/.github/pull_request_template.md +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/.github/workflows/docs.yml +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/.github/workflows/publish.yml +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/.github/workflows/stale.yml +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/.github/workflows/tests.yml +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/.gitignore +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/CONTRIBUTING.md +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/LICENSE +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/README.md +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/docs/Changelog.md +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/docs/Contributing.md +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/docs/Example.md +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/docs/LICENSE.md +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/docs/Quickstart.md +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/docs/Reference/APIRouter.md +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/docs/Reference/Models.md +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/docs/Reference/OpenAPI.md +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/docs/Usage/CLI.md +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/docs/Usage/Configuration.md +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/docs/Usage/Request.md +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/docs/Usage/Response.md +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/docs/Usage/Route_Operation.md +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/docs/Usage/Specification.md +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/docs/Usage/UI_Templates.md +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/docs/assets/Snipaste_2022-03-19_15-10-06.png +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/docs/assets/Snipaste_2022-09-04_10-10-03.png +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/docs/assets/Snipaste_2023-06-02_11-05-11.png +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/docs/assets/Snipaste_2023-06-02_11-06-59.png +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/docs/assets/Snipaste_2023-06-02_11-08-40.png +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/docs/assets/image-20210525160157057.png +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/docs/assets/image-20210525160744617.png +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/docs/assets/image-20210525165350520.png +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/docs/assets/image-20210526104627124.png +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/docs/assets/image-20210605115557426.png +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/docs/assets/openapi-all.png +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/docs/images/logo-animation.svg +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/docs/images/logo-blue.svg +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/docs/images/logo-text.svg +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/docs/images/logo-white.svg +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/docs/index.md +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/examples/query_list_demo.py +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/examples/response_demo.py +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/examples/restapi_demo.py +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/examples/simple_demo.py +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/examples/upload_file_demo.py +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/examples/websocket_demo.py +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/mkdocs.yml +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/star_openapi/__init__.py +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/star_openapi/__main__.py +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/star_openapi/config.py +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/star_openapi/models/callback.py +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/star_openapi/models/components.py +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/star_openapi/models/contact.py +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/star_openapi/models/data_type.py +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/star_openapi/models/discriminator.py +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/star_openapi/models/encoding.py +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/star_openapi/models/example.py +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/star_openapi/models/external_documentation.py +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/star_openapi/models/file.py +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/star_openapi/models/header.py +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/star_openapi/models/info.py +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/star_openapi/models/license.py +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/star_openapi/models/link.py +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/star_openapi/models/media_type.py +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/star_openapi/models/oauth_flow.py +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/star_openapi/models/oauth_flows.py +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/star_openapi/models/operation.py +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/star_openapi/models/parameter.py +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/star_openapi/models/parameter_in_type.py +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/star_openapi/models/path_item.py +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/star_openapi/models/paths.py +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/star_openapi/models/reference.py +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/star_openapi/models/request_body.py +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/star_openapi/models/response.py +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/star_openapi/models/responses.py +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/star_openapi/models/schema.py +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/star_openapi/models/security_requirement.py +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/star_openapi/models/security_scheme.py +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/star_openapi/models/security_scheme_in_type.py +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/star_openapi/models/server.py +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/star_openapi/models/server_variable.py +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/star_openapi/models/style_values.py +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/star_openapi/models/tag.py +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/star_openapi/models/validation_error.py +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/star_openapi/models/xml.py +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/star_openapi/plugins.py +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/star_openapi/py.typed +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/star_openapi/templates.py +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/star_openapi/types.py +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/tests/test_query_list.py +0 -0
- {star_openapi-0.9.2 → star_openapi-0.9.4}/tests/test_upload_file.py +0 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
## v0.9.4 2026-03-11
|
|
2
|
+
|
|
3
|
+
- Add create_websocket_endpoint method compatible with cython build by @luolingchun in #9
|
|
4
|
+
|
|
5
|
+
## v0.9.3 2026-03-02
|
|
6
|
+
|
|
7
|
+
- APIRouter allows no slash suffix by @luolingchun in #7
|
|
8
|
+
|
|
9
|
+
## v0.9.2 2026-01-26
|
|
10
|
+
|
|
11
|
+
- Add parameter app to StarGroup by @luolingchun in #4
|
|
12
|
+
|
|
13
|
+
## v0.9.1 2026-01-12
|
|
14
|
+
|
|
15
|
+
- Support multi content type in request body and responses by @luolingchun in #2
|
|
16
|
+
|
|
17
|
+
## v0.9.0 2026-01-04
|
|
18
|
+
|
|
19
|
+
- first version
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: star-openapi
|
|
3
|
-
Version: 0.9.
|
|
3
|
+
Version: 0.9.4
|
|
4
4
|
Summary: A simple async API framework based on Starlette.
|
|
5
5
|
Project-URL: Homepage, https://github.com/luolingchun/star-openapi
|
|
6
6
|
Project-URL: Documentation, https://luolingchun.github.io/star-openapi
|
|
Binary file
|
|
@@ -40,11 +40,16 @@ async def delete_book(path: IdModel):
|
|
|
40
40
|
return JSONResponse({"id": path.id})
|
|
41
41
|
|
|
42
42
|
|
|
43
|
-
@api2.get("
|
|
43
|
+
@api2.get("")
|
|
44
44
|
async def get_api2():
|
|
45
45
|
return JSONResponse({"message": "Hello World2"})
|
|
46
46
|
|
|
47
47
|
|
|
48
|
+
@api2.websocket("")
|
|
49
|
+
async def api2_websocket():
|
|
50
|
+
return JSONResponse({"message": "Hello World2"})
|
|
51
|
+
|
|
52
|
+
|
|
48
53
|
api1.register_api(api2)
|
|
49
54
|
|
|
50
55
|
app.register_api(api1)
|
|
@@ -21,7 +21,7 @@ request_body_json = RequestBody(
|
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
@app.post("/json", request_body=request_body_json)
|
|
24
|
-
async def
|
|
24
|
+
async def post_json(request: Request, body: BookModel):
|
|
25
25
|
print(request.headers.get("content-type"))
|
|
26
26
|
print(body.model_json_schema())
|
|
27
27
|
return JSONResponse({"message": "Hello World"})
|
|
@@ -38,7 +38,7 @@ request_body = RequestBody(
|
|
|
38
38
|
|
|
39
39
|
|
|
40
40
|
@app.post("/text", request_body=request_body)
|
|
41
|
-
async def
|
|
41
|
+
async def post_csv(request: Request):
|
|
42
42
|
print(request.headers.get("content-type"))
|
|
43
43
|
data = await request.body()
|
|
44
44
|
print(data)
|
|
@@ -34,6 +34,10 @@ dependencies = [
|
|
|
34
34
|
"click>=8.0.0",
|
|
35
35
|
]
|
|
36
36
|
|
|
37
|
+
[project.urls]
|
|
38
|
+
Homepage = "https://github.com/luolingchun/star-openapi"
|
|
39
|
+
Documentation = "https://luolingchun.github.io/star-openapi"
|
|
40
|
+
|
|
37
41
|
[project.optional-dependencies]
|
|
38
42
|
full = [
|
|
39
43
|
"itsdangerous",
|
|
@@ -49,21 +53,6 @@ rapipdf = ["star-openapi-rapipdf"]
|
|
|
49
53
|
scalar = ["star-openapi-scalar"]
|
|
50
54
|
elements = ["star-openapi-elements"]
|
|
51
55
|
|
|
52
|
-
|
|
53
|
-
[project.urls]
|
|
54
|
-
Homepage = "https://github.com/luolingchun/star-openapi"
|
|
55
|
-
Documentation = "https://luolingchun.github.io/star-openapi"
|
|
56
|
-
|
|
57
|
-
[project.scripts]
|
|
58
|
-
star = "star_openapi.cli:main"
|
|
59
|
-
|
|
60
|
-
[build-system]
|
|
61
|
-
requires = ["hatchling"]
|
|
62
|
-
build-backend = "hatchling.build"
|
|
63
|
-
|
|
64
|
-
[tool.hatch.version]
|
|
65
|
-
path = "star_openapi/__version__.py"
|
|
66
|
-
|
|
67
56
|
[dependency-groups]
|
|
68
57
|
ty = ["ty"]
|
|
69
58
|
ruff = ["ruff"]
|
|
@@ -89,6 +78,16 @@ dev = [
|
|
|
89
78
|
"star-openapi-elements"
|
|
90
79
|
]
|
|
91
80
|
|
|
81
|
+
[project.scripts]
|
|
82
|
+
star = "star_openapi.cli:main"
|
|
83
|
+
|
|
84
|
+
[build-system]
|
|
85
|
+
requires = ["hatchling"]
|
|
86
|
+
build-backend = "hatchling.build"
|
|
87
|
+
|
|
88
|
+
[tool.hatch.version]
|
|
89
|
+
path = "star_openapi/__version__.py"
|
|
90
|
+
|
|
92
91
|
[tool.uv]
|
|
93
92
|
default-groups = "all"
|
|
94
93
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.9.4"
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import importlib
|
|
2
|
-
import os
|
|
3
2
|
import ssl
|
|
4
|
-
import sys
|
|
5
3
|
from typing import TYPE_CHECKING, Any, get_args
|
|
6
4
|
|
|
7
5
|
import click
|
|
@@ -18,7 +16,7 @@ from uvicorn.config import (
|
|
|
18
16
|
)
|
|
19
17
|
from uvicorn.main import INTERFACE_CHOICES, LEVEL_CHOICES, LIFESPAN_CHOICES, print_version
|
|
20
18
|
|
|
21
|
-
if TYPE_CHECKING:
|
|
19
|
+
if TYPE_CHECKING: # pragma: no cover
|
|
22
20
|
from .openapi import OpenAPI
|
|
23
21
|
|
|
24
22
|
|
|
@@ -387,7 +385,7 @@ def run_command(
|
|
|
387
385
|
app_dir: str,
|
|
388
386
|
h11_max_incomplete_event_size: int | None,
|
|
389
387
|
factory: bool,
|
|
390
|
-
) -> None:
|
|
388
|
+
) -> None: # pragma: no cover
|
|
391
389
|
app = ctx.parent.params["app"]
|
|
392
390
|
uvicorn_run(
|
|
393
391
|
app,
|
|
@@ -441,15 +439,9 @@ def run_command(
|
|
|
441
439
|
)
|
|
442
440
|
|
|
443
441
|
|
|
444
|
-
def _load_app(app=
|
|
445
|
-
if app is None:
|
|
446
|
-
return None
|
|
447
|
-
|
|
442
|
+
def _load_app(app: str = "asgi:app", verbose: bool = False):
|
|
448
443
|
try:
|
|
449
444
|
module, attr = app.split(":")
|
|
450
|
-
path = os.getcwd()
|
|
451
|
-
if path not in sys.path:
|
|
452
|
-
sys.path.append(path)
|
|
453
445
|
importlib.import_module(module)
|
|
454
446
|
except Exception as e:
|
|
455
447
|
if verbose:
|
|
@@ -545,9 +537,5 @@ cli = StarGroup(
|
|
|
545
537
|
cli.add_command(run_command, "run")
|
|
546
538
|
|
|
547
539
|
|
|
548
|
-
def main():
|
|
540
|
+
def main(): # pragma: no cover
|
|
549
541
|
cli.main()
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
if __name__ == "__main__":
|
|
553
|
-
main()
|
|
@@ -5,6 +5,7 @@ from typing import Any, Type
|
|
|
5
5
|
from pydantic import BaseModel
|
|
6
6
|
from starlette.requests import Request
|
|
7
7
|
from starlette.responses import Response
|
|
8
|
+
from starlette.websockets import WebSocket
|
|
8
9
|
|
|
9
10
|
from .request import _validate_request
|
|
10
11
|
|
|
@@ -37,3 +38,11 @@ def create_endpoint(
|
|
|
37
38
|
return func(**kwargs)
|
|
38
39
|
|
|
39
40
|
return endpoint
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def create_websocket_endpoint(func):
|
|
44
|
+
@wraps(func)
|
|
45
|
+
async def endpoint(websocket: WebSocket):
|
|
46
|
+
return await func(websocket)
|
|
47
|
+
|
|
48
|
+
return endpoint
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"""
|
|
2
|
+
OpenAPI v3.1.0 schema types, created according to the specification:
|
|
3
|
+
https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md
|
|
4
|
+
|
|
5
|
+
The type orders are according to the contents of the specification:
|
|
6
|
+
https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#table-of-contents
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from pydantic import BaseModel
|
|
10
|
+
|
|
11
|
+
from .callback import Callback
|
|
12
|
+
from .components import Components
|
|
13
|
+
from .contact import Contact
|
|
14
|
+
from .discriminator import Discriminator
|
|
15
|
+
from .encoding import Encoding
|
|
16
|
+
from .example import Example
|
|
17
|
+
from .external_documentation import ExternalDocumentation
|
|
18
|
+
from .file import UploadFile
|
|
19
|
+
from .header import Header
|
|
20
|
+
from .info import Info
|
|
21
|
+
from .license import License
|
|
22
|
+
from .link import Link
|
|
23
|
+
from .media_type import MediaType
|
|
24
|
+
from .oauth_flow import OAuthFlow
|
|
25
|
+
from .oauth_flows import OAuthFlows
|
|
26
|
+
from .operation import Operation
|
|
27
|
+
from .parameter import Parameter
|
|
28
|
+
from .parameter_in_type import ParameterInType
|
|
29
|
+
from .path_item import PathItem
|
|
30
|
+
from .paths import Paths
|
|
31
|
+
from .reference import Reference
|
|
32
|
+
from .request_body import RequestBody
|
|
33
|
+
from .response import Response
|
|
34
|
+
from .responses import Responses
|
|
35
|
+
from .schema import Schema
|
|
36
|
+
from .security_requirement import SecurityRequirement
|
|
37
|
+
from .security_scheme import SecurityScheme
|
|
38
|
+
from .server import Server
|
|
39
|
+
from .server_variable import ServerVariable
|
|
40
|
+
from .style_values import StyleValues
|
|
41
|
+
from .tag import Tag
|
|
42
|
+
from .validation_error import ValidationErrorModel
|
|
43
|
+
from .xml import XML
|
|
44
|
+
|
|
45
|
+
OPENAPI3_REF_PREFIX = "#/components/schemas"
|
|
46
|
+
OPENAPI3_REF_TEMPLATE = OPENAPI3_REF_PREFIX + "/{model}"
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class OpenAPISpec(BaseModel):
|
|
50
|
+
"""https://spec.openapis.org/oas/v3.1.0#openapi-object"""
|
|
51
|
+
|
|
52
|
+
openapi: str
|
|
53
|
+
info: Info
|
|
54
|
+
servers: list[Server] | None = None
|
|
55
|
+
paths: Paths
|
|
56
|
+
components: Components | None = None
|
|
57
|
+
security: list[SecurityRequirement] | None = None
|
|
58
|
+
tags: list[Tag] | None = None
|
|
59
|
+
externalDocs: ExternalDocumentation | None = None
|
|
60
|
+
webhooks: dict[str, PathItem | Reference] | None = None
|
|
61
|
+
|
|
62
|
+
model_config = {"extra": "allow"}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class OAuthConfig(BaseModel):
|
|
66
|
+
"""
|
|
67
|
+
https://github.com/swagger-api/swagger-ui/blob/master/docs/usage/oauth2.md#oauth-20-configuration
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
clientId: str | None = None
|
|
71
|
+
clientSecret: str | None = None
|
|
72
|
+
realm: str | None = None
|
|
73
|
+
appName: str | None = None
|
|
74
|
+
scopeSeparator: str | None = None
|
|
75
|
+
scopes: str | None = None
|
|
76
|
+
additionalQueryStringParams: dict[str, str] | None = None
|
|
77
|
+
useBasicAuthenticationWithAccessCodeGrant: bool | None = False
|
|
78
|
+
usePkceWithAuthorizationCodeGrant: bool | None = False
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
Encoding.model_rebuild()
|
|
82
|
+
Operation.model_rebuild()
|
|
83
|
+
PathItem.model_rebuild()
|
|
@@ -9,11 +9,11 @@ from jinja2 import Template
|
|
|
9
9
|
from pydantic import BaseModel, ValidationError
|
|
10
10
|
from starlette.applications import Starlette
|
|
11
11
|
from starlette.responses import HTMLResponse, JSONResponse
|
|
12
|
-
from starlette.routing import Mount, Route
|
|
12
|
+
from starlette.routing import Mount, Route
|
|
13
13
|
|
|
14
14
|
from .cli import cli
|
|
15
15
|
from .config import Config
|
|
16
|
-
from .endpoint import create_endpoint
|
|
16
|
+
from .endpoint import create_endpoint, create_websocket_endpoint
|
|
17
17
|
from .models import (
|
|
18
18
|
OPENAPI3_REF_PREFIX,
|
|
19
19
|
Components,
|
|
@@ -27,7 +27,7 @@ from .models import (
|
|
|
27
27
|
Tag,
|
|
28
28
|
ValidationErrorModel,
|
|
29
29
|
)
|
|
30
|
-
from .router import APIRouter
|
|
30
|
+
from .router import APIRoute, APIRouter, APIWebSocketRoute
|
|
31
31
|
from .templates import openapi_html_string
|
|
32
32
|
from .types import ParametersTuple, ResponseDict
|
|
33
33
|
from .utils import (
|
|
@@ -200,8 +200,6 @@ class OpenAPI(Starlette):
|
|
|
200
200
|
if isinstance(self.info, dict):
|
|
201
201
|
self.info = Info.model_validate(self.info)
|
|
202
202
|
spec = OpenAPISpec(openapi=self.openapi_version, info=self.info, paths=self.paths)
|
|
203
|
-
spec.openapi = self.openapi_version
|
|
204
|
-
spec.info = self.info
|
|
205
203
|
|
|
206
204
|
if self.severs:
|
|
207
205
|
spec.servers = [Server(**server) if isinstance(server, dict) else server for server in self.severs]
|
|
@@ -238,8 +236,6 @@ class OpenAPI(Starlette):
|
|
|
238
236
|
# Handle validation error response
|
|
239
237
|
for rule, path_item in self.spec_json["paths"].items():
|
|
240
238
|
for http_method, operation in path_item.items():
|
|
241
|
-
if operation.get("responses") is None:
|
|
242
|
-
operation["responses"] = {}
|
|
243
239
|
if operation["responses"].get(self.validation_error_status):
|
|
244
240
|
continue
|
|
245
241
|
operation["responses"][self.validation_error_status] = {
|
|
@@ -255,6 +251,12 @@ class OpenAPI(Starlette):
|
|
|
255
251
|
}
|
|
256
252
|
|
|
257
253
|
def register_api(self, api: APIRouter):
|
|
254
|
+
"""
|
|
255
|
+
Register an APIRouter.
|
|
256
|
+
|
|
257
|
+
Args:
|
|
258
|
+
api: The APIBlueprint instance to register.
|
|
259
|
+
"""
|
|
258
260
|
for tag in api.tags:
|
|
259
261
|
if tag.name not in self.tag_names:
|
|
260
262
|
# Append tag to the list of tags
|
|
@@ -270,16 +272,16 @@ class OpenAPI(Starlette):
|
|
|
270
272
|
|
|
271
273
|
# Register the APIRouter with the current instance
|
|
272
274
|
for route in api.routes:
|
|
273
|
-
if isinstance(route,
|
|
274
|
-
path_with_prefix = api.url_prefix + route.
|
|
275
|
+
if isinstance(route, APIRoute):
|
|
276
|
+
path_with_prefix = api.url_prefix + route.origin_path
|
|
275
277
|
self.router.add_route(
|
|
276
278
|
path=path_with_prefix,
|
|
277
279
|
endpoint=route.endpoint,
|
|
278
280
|
methods=route.methods,
|
|
279
281
|
name=route.name,
|
|
280
282
|
)
|
|
281
|
-
elif isinstance(route,
|
|
282
|
-
path_with_prefix = api.url_prefix + route.
|
|
283
|
+
elif isinstance(route, APIWebSocketRoute):
|
|
284
|
+
path_with_prefix = api.url_prefix + route.origin_path
|
|
283
285
|
self.router.add_websocket_route(path=path_with_prefix, endpoint=route.endpoint, name=route.name)
|
|
284
286
|
|
|
285
287
|
def _collect_openapi_info(
|
|
@@ -375,6 +377,26 @@ class OpenAPI(Starlette):
|
|
|
375
377
|
responses: ResponseDict | None = None,
|
|
376
378
|
doc_ui: bool = True,
|
|
377
379
|
):
|
|
380
|
+
"""
|
|
381
|
+
Decorator for defining a REST API endpoint with the HTTP GET method.
|
|
382
|
+
More information goto https://spec.openapis.org/oas/v3.1.0#operation-object
|
|
383
|
+
|
|
384
|
+
Args:
|
|
385
|
+
rule: The URL rule string.
|
|
386
|
+
name: The URL name string.
|
|
387
|
+
tags: Adds metadata to a single tag.
|
|
388
|
+
summary: A short summary of what the operation does.
|
|
389
|
+
description: A verbose explanation of the operation behavior.
|
|
390
|
+
external_docs: Additional external documentation for this operation.
|
|
391
|
+
operation_id: Unique string used to identify the operation.
|
|
392
|
+
deprecated: Declares this operation to be deprecated.
|
|
393
|
+
security: A declaration of which security mechanisms can be used for this operation.
|
|
394
|
+
servers: An alternative server array to service this operation.
|
|
395
|
+
openapi_extensions: Allows extensions to the OpenAPI Schema.
|
|
396
|
+
responses: API responses should be either a subclass of BaseModel, a dictionary, or None.
|
|
397
|
+
doc_ui: Declares this operation to be shown. Default to True.
|
|
398
|
+
"""
|
|
399
|
+
|
|
378
400
|
def decorator(func) -> Callable:
|
|
379
401
|
header, cookie, path, query, form, body = self._collect_openapi_info(
|
|
380
402
|
rule,
|
|
@@ -417,6 +439,27 @@ class OpenAPI(Starlette):
|
|
|
417
439
|
responses: ResponseDict | None = None,
|
|
418
440
|
doc_ui: bool = True,
|
|
419
441
|
):
|
|
442
|
+
"""
|
|
443
|
+
Decorator for defining a REST API endpoint with the HTTP POST method.
|
|
444
|
+
More information goto https://spec.openapis.org/oas/v3.1.0#operation-object
|
|
445
|
+
|
|
446
|
+
Args:
|
|
447
|
+
rule: The URL rule string.
|
|
448
|
+
name: The URL name string.
|
|
449
|
+
tags: Adds metadata to a single tag.
|
|
450
|
+
summary: A short summary of what the operation does.
|
|
451
|
+
description: A verbose explanation of the operation behavior.
|
|
452
|
+
external_docs: Additional external documentation for this operation.
|
|
453
|
+
operation_id: Unique string used to identify the operation.
|
|
454
|
+
deprecated: Declares this operation to be deprecated.
|
|
455
|
+
security: A declaration of which security mechanisms can be used for this operation.
|
|
456
|
+
servers: An alternative server array to service this operation.
|
|
457
|
+
openapi_extensions: Allows extensions to the OpenAPI Schema.
|
|
458
|
+
request_body: Advanced configuration in OpenAPI.
|
|
459
|
+
responses: API responses should be either a subclass of BaseModel, a dictionary, or None.
|
|
460
|
+
doc_ui: Declares this operation to be shown. Default to True.
|
|
461
|
+
"""
|
|
462
|
+
|
|
420
463
|
def decorator(func) -> Callable:
|
|
421
464
|
header, cookie, path, query, form, body = self._collect_openapi_info(
|
|
422
465
|
rule,
|
|
@@ -460,6 +503,27 @@ class OpenAPI(Starlette):
|
|
|
460
503
|
responses: ResponseDict | None = None,
|
|
461
504
|
doc_ui: bool = True,
|
|
462
505
|
):
|
|
506
|
+
"""
|
|
507
|
+
Decorator for defining a REST API endpoint with the HTTP PUT method.
|
|
508
|
+
More information goto https://spec.openapis.org/oas/v3.1.0#operation-object
|
|
509
|
+
|
|
510
|
+
Args:
|
|
511
|
+
rule: The URL rule string.
|
|
512
|
+
name: The URL name string.
|
|
513
|
+
tags: Adds metadata to a single tag.
|
|
514
|
+
summary: A short summary of what the operation does.
|
|
515
|
+
description: A verbose explanation of the operation behavior.
|
|
516
|
+
external_docs: Additional external documentation for this operation.
|
|
517
|
+
operation_id: Unique string used to identify the operation.
|
|
518
|
+
deprecated: Declares this operation to be deprecated.
|
|
519
|
+
security: A declaration of which security mechanisms can be used for this operation.
|
|
520
|
+
servers: An alternative server array to service this operation.
|
|
521
|
+
openapi_extensions: Allows extensions to the OpenAPI Schema.
|
|
522
|
+
request_body: Advanced configuration in OpenAPI.
|
|
523
|
+
responses: API responses should be either a subclass of BaseModel, a dictionary, or None.
|
|
524
|
+
doc_ui: Declares this operation to be shown. Default to True.
|
|
525
|
+
"""
|
|
526
|
+
|
|
463
527
|
def decorator(func) -> Callable:
|
|
464
528
|
header, cookie, path, query, form, body = self._collect_openapi_info(
|
|
465
529
|
rule,
|
|
@@ -503,6 +567,27 @@ class OpenAPI(Starlette):
|
|
|
503
567
|
responses: ResponseDict | None = None,
|
|
504
568
|
doc_ui: bool = True,
|
|
505
569
|
):
|
|
570
|
+
"""
|
|
571
|
+
Decorator for defining a REST API endpoint with the HTTP DELETE method.
|
|
572
|
+
More information goto https://spec.openapis.org/oas/v3.1.0#operation-object
|
|
573
|
+
|
|
574
|
+
Args:
|
|
575
|
+
rule: The URL rule string.
|
|
576
|
+
name: The URL name string.
|
|
577
|
+
tags: Adds metadata to a single tag.
|
|
578
|
+
summary: A short summary of what the operation does.
|
|
579
|
+
description: A verbose explanation of the operation behavior.
|
|
580
|
+
external_docs: Additional external documentation for this operation.
|
|
581
|
+
operation_id: Unique string used to identify the operation.
|
|
582
|
+
deprecated: Declares this operation to be deprecated.
|
|
583
|
+
security: A declaration of which security mechanisms can be used for this operation.
|
|
584
|
+
servers: An alternative server array to service this operation.
|
|
585
|
+
openapi_extensions: Allows extensions to the OpenAPI Schema.
|
|
586
|
+
request_body: Advanced configuration in OpenAPI.
|
|
587
|
+
responses: API responses should be either a subclass of BaseModel, a dictionary, or None.
|
|
588
|
+
doc_ui: Declares this operation to be shown. Default to True.
|
|
589
|
+
"""
|
|
590
|
+
|
|
506
591
|
def decorator(func) -> Callable:
|
|
507
592
|
header, cookie, path, query, form, body = self._collect_openapi_info(
|
|
508
593
|
rule,
|
|
@@ -546,6 +631,27 @@ class OpenAPI(Starlette):
|
|
|
546
631
|
responses: ResponseDict | None = None,
|
|
547
632
|
doc_ui: bool = True,
|
|
548
633
|
):
|
|
634
|
+
"""
|
|
635
|
+
Decorator for defining a REST API endpoint with the HTTP PATCH method.
|
|
636
|
+
More information goto https://spec.openapis.org/oas/v3.1.0#operation-object
|
|
637
|
+
|
|
638
|
+
Args:
|
|
639
|
+
rule: The URL rule string.
|
|
640
|
+
name: The URL name string.
|
|
641
|
+
tags: Adds metadata to a single tag.
|
|
642
|
+
summary: A short summary of what the operation does.
|
|
643
|
+
description: A verbose explanation of the operation behavior.
|
|
644
|
+
external_docs: Additional external documentation for this operation.
|
|
645
|
+
operation_id: Unique string used to identify the operation.
|
|
646
|
+
deprecated: Declares this operation to be deprecated.
|
|
647
|
+
security: A declaration of which security mechanisms can be used for this operation.
|
|
648
|
+
servers: An alternative server array to service this operation.
|
|
649
|
+
openapi_extensions: Allows extensions to the OpenAPI Schema.
|
|
650
|
+
request_body: Advanced configuration in OpenAPI.
|
|
651
|
+
responses: API responses should be either a subclass of BaseModel, a dictionary, or None.
|
|
652
|
+
doc_ui: Declares this operation to be shown. Default to True.
|
|
653
|
+
"""
|
|
654
|
+
|
|
549
655
|
def decorator(func) -> Callable:
|
|
550
656
|
header, cookie, path, query, form, body = self._collect_openapi_info(
|
|
551
657
|
rule,
|
|
@@ -578,11 +684,9 @@ class OpenAPI(Starlette):
|
|
|
578
684
|
name: str | None = None,
|
|
579
685
|
):
|
|
580
686
|
def decorator(func) -> Callable:
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
name=name,
|
|
585
|
-
)
|
|
687
|
+
endpoint = create_websocket_endpoint(func)
|
|
688
|
+
self.add_websocket_route(rule, endpoint, name=name)
|
|
689
|
+
|
|
586
690
|
return func
|
|
587
691
|
|
|
588
692
|
return decorator
|
|
@@ -43,19 +43,19 @@ async def _validate_header(request: Request, header: Type[BaseModel]):
|
|
|
43
43
|
header_dict = {}
|
|
44
44
|
model_properties = header.model_json_schema().get("properties", {})
|
|
45
45
|
for model_field_key, model_field_value in header.model_fields.items():
|
|
46
|
-
|
|
46
|
+
key_lower = model_field_key.lower()
|
|
47
47
|
model_field_schema = model_properties.get(model_field_value.alias or model_field_key)
|
|
48
48
|
if model_field_value.alias and header.model_config.get("populate_by_name"):
|
|
49
49
|
key = model_field_value.alias
|
|
50
|
-
key_alias_title = model_field_value.alias.
|
|
51
|
-
value = request_headers.get(key_alias_title) or request_headers.get(
|
|
50
|
+
key_alias_title = model_field_value.alias.lower()
|
|
51
|
+
value = request_headers.get(key_alias_title) or request_headers.get(key_lower)
|
|
52
52
|
elif model_field_value.alias:
|
|
53
53
|
key = model_field_value.alias
|
|
54
|
-
key_alias_title = model_field_value.alias.
|
|
54
|
+
key_alias_title = model_field_value.alias.lower()
|
|
55
55
|
value = request_headers.get(key_alias_title)
|
|
56
56
|
else:
|
|
57
57
|
key = model_field_key
|
|
58
|
-
value = request_headers
|
|
58
|
+
value = request_headers.get(key_lower)
|
|
59
59
|
if value is not None:
|
|
60
60
|
header_dict[key] = value
|
|
61
61
|
if model_field_schema.get("type") == "null":
|