APIFlask 2.1.2__tar.gz → 2.1.4.dev0__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.
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/CHANGES.md +24 -0
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/PKG-INFO +2 -2
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/README.md +1 -1
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/examples/README.md +2 -1
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/examples/auth/basic_auth/app.py +1 -4
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/examples/auth/token_auth/app.py +3 -9
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/examples/base_response/app.py +4 -16
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/examples/basic/app.py +1 -1
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/examples/blueprint_tags/app.py +1 -1
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/examples/cbv/app.py +1 -4
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/examples/dataclass/app.py +9 -15
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/examples/openapi/basic/app.py +11 -28
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/examples/openapi/custom_decorators/app.py +1 -4
- apiflask-2.1.4.dev0/examples/openapi/static_docs/app.py +54 -0
- apiflask-2.1.4.dev0/examples/openapi/static_docs/generate_docs.sh +7 -0
- apiflask-2.1.4.dev0/examples/openapi/static_docs/index.html +68 -0
- apiflask-2.1.4.dev0/examples/openapi/static_docs/openapi.json +1 -0
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/examples/orm/app.py +1 -1
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/examples/pagination/app.py +2 -7
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/examples/test_examples.py +24 -33
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/pyproject.toml +46 -3
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/requirements/dev.txt +3 -3
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/requirements/docs.txt +8 -6
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/requirements/examples.txt +1 -1
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/requirements/min-versions.txt +1 -1
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/requirements/tests.txt +5 -5
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/requirements/typing.txt +1 -1
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/src/APIFlask.egg-info/PKG-INFO +2 -2
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/src/APIFlask.egg-info/SOURCES.txt +5 -0
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/src/APIFlask.egg-info/entry_points.txt +2 -0
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/src/apiflask/__init__.py +1 -1
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/src/apiflask/app.py +157 -177
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/src/apiflask/blueprint.py +10 -10
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/src/apiflask/commands.py +4 -8
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/src/apiflask/exceptions.py +13 -10
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/src/apiflask/fields.py +1 -0
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/src/apiflask/helpers.py +4 -4
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/src/apiflask/openapi.py +15 -25
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/src/apiflask/route.py +8 -6
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/src/apiflask/scaffold.py +64 -48
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/src/apiflask/schemas.py +15 -29
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/src/apiflask/security.py +14 -17
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/src/apiflask/settings.py +23 -21
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/src/apiflask/types.py +3 -1
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/tests/schemas.py +1 -3
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/tests/test_app.py +29 -21
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/tests/test_async.py +9 -12
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/tests/test_base_response.py +24 -33
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/tests/test_blueprint.py +3 -3
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/tests/test_commands.py +4 -4
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/tests/test_decorator_auth_required.py +8 -9
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/tests/test_decorator_doc.py +66 -90
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/tests/test_decorator_input.py +114 -95
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/tests/test_decorator_output.py +51 -50
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/tests/test_exceptions.py +30 -27
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/tests/test_fields.py +9 -7
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/tests/test_openapi_basic.py +14 -32
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/tests/test_openapi_blueprint.py +1 -1
- apiflask-2.1.4.dev0/tests/test_openapi_extensions.py +13 -0
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/tests/test_openapi_headers.py +11 -8
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/tests/test_openapi_info.py +12 -28
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/tests/test_openapi_paths.py +73 -66
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/tests/test_openapi_security.py +15 -14
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/tests/test_openapi_tags.py +22 -20
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/tests/test_route.py +9 -7
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/tests/test_schemas.py +7 -9
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/tests/test_security.py +11 -7
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/tests/test_settings_api_docs.py +7 -22
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/tests/test_settings_auto_behaviour.py +43 -28
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/tests/test_settings_openapi_fields.py +29 -51
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/tests/test_settings_response_customization.py +32 -38
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/tox.ini +1 -1
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/LICENSE +0 -0
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/MANIFEST.in +0 -0
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/NOTICE +0 -0
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/examples/file_upload/app.py +0 -0
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/examples/file_upload/upload/.gitkeep +0 -0
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/examples/requirements.txt +0 -0
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/setup.cfg +0 -0
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/src/APIFlask.egg-info/dependency_links.txt +0 -0
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/src/APIFlask.egg-info/requires.txt +0 -0
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/src/APIFlask.egg-info/top_level.txt +0 -0
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/src/apiflask/py.typed +0 -0
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/src/apiflask/ui_templates.py +0 -0
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/src/apiflask/validators.py +0 -0
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/src/apiflask/views.py +0 -0
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/tests/__init__.py +0 -0
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/tests/conftest.py +0 -0
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/tests/test_apps/__init__.py +0 -0
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/tests/test_decorators.py +0 -0
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/tests/test_helpers.py +0 -0
- {apiflask-2.1.2 → apiflask-2.1.4.dev0}/tests/test_settings_openapi_spec.py +0 -0
|
@@ -1,3 +1,27 @@
|
|
|
1
|
+
## Version 2.2.0
|
|
2
|
+
|
|
3
|
+
Released: 2024/8/3
|
|
4
|
+
|
|
5
|
+
- Fix deprecated warnings in tests ([issue #594][issue_594]).
|
|
6
|
+
- Use postponed evaluation of annotations ([pr #585][pr_585]).
|
|
7
|
+
- Add static OpenAPI docs example ([issue #587][issue_587]).
|
|
8
|
+
- Add spec extensions support with `@doc(extensions=...)` for the view function ([issue #571][issue_571]).
|
|
9
|
+
|
|
10
|
+
[pr_585]: https://github.com/apiflask/apiflask/pull/585
|
|
11
|
+
[issue_594]: https://github.com/apiflask/apiflask/issues/594
|
|
12
|
+
[issue_587]: https://github.com/apiflask/apiflask/issues/587
|
|
13
|
+
[issue_571]: https://github.com/apiflask/apiflask/issues/571
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
## Version 2.1.3
|
|
17
|
+
|
|
18
|
+
Released: 2024/7/14
|
|
19
|
+
|
|
20
|
+
- Fix the `flask spec` command for latest Flask ([issue #582][issue_582]).
|
|
21
|
+
|
|
22
|
+
[issue_582]: https://github.com/apiflask/apiflask/issues/582
|
|
23
|
+
|
|
24
|
+
|
|
1
25
|
## Version 2.1.2
|
|
2
26
|
|
|
3
27
|
Released: 2024/7/6
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: APIFlask
|
|
3
|
-
Version: 2.1.
|
|
3
|
+
Version: 2.1.4.dev0
|
|
4
4
|
Summary: A lightweight web API framework based on Flask and marshmallow-code projects.
|
|
5
5
|
Author-email: Grey Li <withlihui@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -292,7 +292,7 @@ For some complete examples, see [/examples](https://github.com/apiflask/apiflask
|
|
|
292
292
|
|
|
293
293
|
## Relationship with Flask
|
|
294
294
|
|
|
295
|
-
APIFlask is a thin wrapper on top of Flask. You only need to remember the following differences (see *[Migrating from Flask](https://apiflask.com/
|
|
295
|
+
APIFlask is a thin wrapper on top of Flask. You only need to remember the following differences (see *[Migrating from Flask](https://apiflask.com/migrations/flask/)* for more details):
|
|
296
296
|
|
|
297
297
|
- When creating an application instance, use `APIFlask` instead of `Flask`.
|
|
298
298
|
- When creating a blueprint instance, use `APIBlueprint` instead of `Blueprint`.
|
|
@@ -249,7 +249,7 @@ For some complete examples, see [/examples](https://github.com/apiflask/apiflask
|
|
|
249
249
|
|
|
250
250
|
## Relationship with Flask
|
|
251
251
|
|
|
252
|
-
APIFlask is a thin wrapper on top of Flask. You only need to remember the following differences (see *[Migrating from Flask](https://apiflask.com/
|
|
252
|
+
APIFlask is a thin wrapper on top of Flask. You only need to remember the following differences (see *[Migrating from Flask](https://apiflask.com/migrations/flask/)* for more details):
|
|
253
253
|
|
|
254
254
|
- When creating an application instance, use `APIFlask` instead of `Flask`.
|
|
255
255
|
- When creating a blueprint instance, use `APIBlueprint` instead of `Blueprint`.
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
- Class-based view example: [/examples/cbv/app.py][_cbv]
|
|
5
5
|
- ORM example (with Flask-SQLAlchemy): [/examples/orm/app.py][_orm]
|
|
6
6
|
- Pagination example (with Flask-SQLAlchemy): [/examples/pagination/app.py][_pagination]
|
|
7
|
-
- OpenAPI
|
|
7
|
+
- OpenAPI examples: [/examples/openapi][_openapi]
|
|
8
8
|
- Base response example: [/examples/base_response/app.py][_base_response]
|
|
9
9
|
- Token auth example: [/examples/auth/token_auth/app.py][_token_auth]
|
|
10
10
|
- Basic auth example: [/examples/auth/basic_auth/app.py][_basic_auth]
|
|
@@ -65,6 +65,7 @@ Each example application store in a sub-folder:
|
|
|
65
65
|
- `/pagination`: Pagination example (with Flask-SQLAlchemy)
|
|
66
66
|
- `/openapi/basic`: Basic OpenAPI example
|
|
67
67
|
- `/openapi/custom_decorators`: OpenAPI example with custom decorators
|
|
68
|
+
- `/openapi/static_docs`: OpenAPI example with standalone static HTML OpenAPI docs
|
|
68
69
|
- `/base_response`: Base response example
|
|
69
70
|
- `/dataclass`: Dataclass example (with marshmallow-dataclass)
|
|
70
71
|
|
|
@@ -14,10 +14,7 @@ users = {
|
|
|
14
14
|
|
|
15
15
|
@auth.verify_password
|
|
16
16
|
def verify_password(username: str, password: str) -> t.Union[str, None]:
|
|
17
|
-
if (
|
|
18
|
-
username in users
|
|
19
|
-
and check_password_hash(users[username], password)
|
|
20
|
-
):
|
|
17
|
+
if username in users and check_password_hash(users[username], password):
|
|
21
18
|
return username
|
|
22
19
|
return None
|
|
23
20
|
|
|
@@ -16,12 +16,8 @@ class User:
|
|
|
16
16
|
|
|
17
17
|
def get_token(self):
|
|
18
18
|
header = {'alg': 'HS256'}
|
|
19
|
-
payload = {
|
|
20
|
-
|
|
21
|
-
}
|
|
22
|
-
return jwt.encode(
|
|
23
|
-
header, payload, current_app.config['SECRET_KEY']
|
|
24
|
-
).decode()
|
|
19
|
+
payload = {'id': self.id}
|
|
20
|
+
return jwt.encode(header, payload, current_app.config['SECRET_KEY']).decode()
|
|
25
21
|
|
|
26
22
|
|
|
27
23
|
users = [
|
|
@@ -60,9 +56,7 @@ class Token(Schema):
|
|
|
60
56
|
def get_token(id: int):
|
|
61
57
|
if get_user_by_id(id) is None:
|
|
62
58
|
abort(404)
|
|
63
|
-
return {
|
|
64
|
-
'token': f'Bearer {get_user_by_id(id).get_token()}'
|
|
65
|
-
}
|
|
59
|
+
return {'token': f'Bearer {get_user_by_id(id).get_token()}'}
|
|
66
60
|
|
|
67
61
|
|
|
68
62
|
@app.get('/name/<int:id>')
|
|
@@ -19,7 +19,7 @@ app.config['BASE_RESPONSE_DATA_KEY'] = 'data'
|
|
|
19
19
|
pets = [
|
|
20
20
|
{'id': 0, 'name': 'Kitty', 'category': 'cat'},
|
|
21
21
|
{'id': 1, 'name': 'Coco', 'category': 'dog'},
|
|
22
|
-
{'id': 2, 'name': 'Flash', 'category': 'cat'}
|
|
22
|
+
{'id': 2, 'name': 'Flash', 'category': 'cat'},
|
|
23
23
|
]
|
|
24
24
|
|
|
25
25
|
|
|
@@ -37,11 +37,7 @@ class PetOut(Schema):
|
|
|
37
37
|
@app.get('/')
|
|
38
38
|
def say_hello():
|
|
39
39
|
data = {'message': 'Hello!'}
|
|
40
|
-
return {
|
|
41
|
-
'data': data,
|
|
42
|
-
'message': 'Success!',
|
|
43
|
-
'code': 200
|
|
44
|
-
}
|
|
40
|
+
return {'data': data, 'message': 'Success!', 'code': 200}
|
|
45
41
|
|
|
46
42
|
|
|
47
43
|
@app.get('/pets/<int:pet_id>')
|
|
@@ -73,11 +69,7 @@ def create_pet(json_data):
|
|
|
73
69
|
pet_id = len(pets)
|
|
74
70
|
json_data['id'] = pet_id
|
|
75
71
|
pets.append(json_data)
|
|
76
|
-
return {
|
|
77
|
-
'data': pets[pet_id],
|
|
78
|
-
'message': 'Pet created.',
|
|
79
|
-
'code': 201
|
|
80
|
-
}
|
|
72
|
+
return {'data': pets[pet_id], 'message': 'Pet created.', 'code': 201}
|
|
81
73
|
|
|
82
74
|
|
|
83
75
|
@app.patch('/pets/<int:pet_id>')
|
|
@@ -88,11 +80,7 @@ def update_pet(pet_id, json_data):
|
|
|
88
80
|
abort(404)
|
|
89
81
|
for attr, value in json_data.items():
|
|
90
82
|
pets[pet_id][attr] = value
|
|
91
|
-
return {
|
|
92
|
-
'data': pets[pet_id],
|
|
93
|
-
'message': 'Pet updated.',
|
|
94
|
-
'code': 200
|
|
95
|
-
}
|
|
83
|
+
return {'data': pets[pet_id], 'message': 'Pet updated.', 'code': 200}
|
|
96
84
|
|
|
97
85
|
|
|
98
86
|
@app.delete('/pets/<int:pet_id>')
|
|
@@ -16,7 +16,7 @@ pet_bp = APIBlueprint('pet', __name__) # tag name will be "Pet"
|
|
|
16
16
|
pets = [
|
|
17
17
|
{'id': 0, 'name': 'Kitty', 'category': 'cat'},
|
|
18
18
|
{'id': 1, 'name': 'Coco', 'category': 'dog'},
|
|
19
|
-
{'id': 2, 'name': 'Flash', 'category': 'cat'}
|
|
19
|
+
{'id': 2, 'name': 'Flash', 'category': 'cat'},
|
|
20
20
|
]
|
|
21
21
|
|
|
22
22
|
|
|
@@ -8,7 +8,7 @@ app = APIFlask(__name__)
|
|
|
8
8
|
pets = [
|
|
9
9
|
{'id': 0, 'name': 'Kitty', 'category': 'cat'},
|
|
10
10
|
{'id': 1, 'name': 'Coco', 'category': 'dog'},
|
|
11
|
-
{'id': 2, 'name': 'Flash', 'category': 'cat'}
|
|
11
|
+
{'id': 2, 'name': 'Flash', 'category': 'cat'},
|
|
12
12
|
]
|
|
13
13
|
|
|
14
14
|
|
|
@@ -24,13 +24,11 @@ class PetOut(Schema):
|
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
class Hello(MethodView):
|
|
27
|
-
|
|
28
27
|
def get(self):
|
|
29
28
|
return {'message': 'Hello!'}
|
|
30
29
|
|
|
31
30
|
|
|
32
31
|
class Pet(MethodView):
|
|
33
|
-
|
|
34
32
|
@app.output(PetOut)
|
|
35
33
|
def get(self, pet_id):
|
|
36
34
|
"""Get a pet"""
|
|
@@ -59,7 +57,6 @@ class Pet(MethodView):
|
|
|
59
57
|
|
|
60
58
|
|
|
61
59
|
class Pets(MethodView):
|
|
62
|
-
|
|
63
60
|
@app.output(PetOut(many=True))
|
|
64
61
|
def get(self):
|
|
65
62
|
"""Get all pets"""
|
|
@@ -13,18 +13,16 @@ class PetIn:
|
|
|
13
13
|
metadata={
|
|
14
14
|
'required': True,
|
|
15
15
|
'validate': Length(min=1, max=10),
|
|
16
|
-
'
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
'metadata': {
|
|
17
|
+
'example': 'Medor',
|
|
18
|
+
'description': 'This will be printed in the generated doc. '
|
|
19
|
+
'The "example" value "Medor" will be fed '
|
|
20
|
+
'into the "try it"/"Send API request".',
|
|
21
|
+
},
|
|
20
22
|
}
|
|
21
23
|
)
|
|
22
24
|
category: str = field(
|
|
23
|
-
default='dog',
|
|
24
|
-
metadata={
|
|
25
|
-
'required': True,
|
|
26
|
-
'validate': OneOf(['cat', 'dog'])
|
|
27
|
-
}
|
|
25
|
+
default='dog', metadata={'required': True, 'validate': OneOf(['cat', 'dog'])}
|
|
28
26
|
)
|
|
29
27
|
|
|
30
28
|
|
|
@@ -38,7 +36,7 @@ class PetOut:
|
|
|
38
36
|
pets = [
|
|
39
37
|
{'id': 0, 'name': 'Kitty', 'category': 'cat'},
|
|
40
38
|
{'id': 1, 'name': 'Coco', 'category': 'dog'},
|
|
41
|
-
{'id': 2, 'name': 'Flash', 'category': 'cat'}
|
|
39
|
+
{'id': 2, 'name': 'Flash', 'category': 'cat'},
|
|
42
40
|
]
|
|
43
41
|
|
|
44
42
|
|
|
@@ -66,11 +64,7 @@ def get_pets():
|
|
|
66
64
|
@app.output(PetOut.Schema, status_code=201)
|
|
67
65
|
def create_pet(pet: PetIn):
|
|
68
66
|
pet_id = len(pets)
|
|
69
|
-
pets.append({
|
|
70
|
-
'id': pet_id,
|
|
71
|
-
'name': pet.name,
|
|
72
|
-
'category': pet.category
|
|
73
|
-
})
|
|
67
|
+
pets.append({'id': pet_id, 'name': pet.name, 'category': pet.category})
|
|
74
68
|
return pets[pet_id]
|
|
75
69
|
|
|
76
70
|
|
|
@@ -31,14 +31,11 @@ The source can be found at [examples/blueprint_tags/app.py][_blueprint_tags].
|
|
|
31
31
|
app.config['CONTACT'] = {
|
|
32
32
|
'name': 'API Support',
|
|
33
33
|
'url': 'https://greyli.com/en',
|
|
34
|
-
'email': 'withlihui@gmail.com'
|
|
34
|
+
'email': 'withlihui@gmail.com',
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
# openapi.info.license
|
|
38
|
-
app.config['LICENSE'] = {
|
|
39
|
-
'name': 'MIT',
|
|
40
|
-
'url': 'https://opensource.org/licenses/MIT'
|
|
41
|
-
}
|
|
38
|
+
app.config['LICENSE'] = {'name': 'MIT', 'url': 'https://opensource.org/licenses/MIT'}
|
|
42
39
|
|
|
43
40
|
# openapi.info.termsOfService
|
|
44
41
|
app.config['TERMS_OF_SERVICE'] = 'http://example.com'
|
|
@@ -61,7 +58,7 @@ app.config['TERMS_OF_SERVICE'] = 'http://example.com'
|
|
|
61
58
|
# openapi.tags
|
|
62
59
|
app.config['TAGS'] = [
|
|
63
60
|
{'name': 'Hello', 'description': 'The description of the **Hello** tag.'},
|
|
64
|
-
{'name': 'Pet', 'description': 'The description of the **Pet** tag.'}
|
|
61
|
+
{'name': 'Pet', 'description': 'The description of the **Pet** tag.'},
|
|
65
62
|
]
|
|
66
63
|
|
|
67
64
|
# If you don't need to set tag "description" or tag "externalDocs", just pass a list a string:
|
|
@@ -69,30 +66,21 @@ app.config['TAGS'] = [
|
|
|
69
66
|
|
|
70
67
|
# openapi.servers
|
|
71
68
|
app.config['SERVERS'] = [
|
|
72
|
-
{
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
},
|
|
76
|
-
{
|
|
77
|
-
'name': 'Production Server',
|
|
78
|
-
'url': 'http://api.example.com'
|
|
79
|
-
},
|
|
80
|
-
{
|
|
81
|
-
'name': 'Testing Server',
|
|
82
|
-
'url': 'http://test.example.com'
|
|
83
|
-
}
|
|
69
|
+
{'name': 'Development Server', 'url': 'http://localhost:5000'},
|
|
70
|
+
{'name': 'Production Server', 'url': 'http://api.example.com'},
|
|
71
|
+
{'name': 'Testing Server', 'url': 'http://test.example.com'},
|
|
84
72
|
]
|
|
85
73
|
|
|
86
74
|
# openapi.externalDocs
|
|
87
75
|
app.config['EXTERNAL_DOCS'] = {
|
|
88
76
|
'description': 'Find more info here',
|
|
89
|
-
'url': 'https://apiflask.com/docs'
|
|
77
|
+
'url': 'https://apiflask.com/docs',
|
|
90
78
|
}
|
|
91
79
|
|
|
92
80
|
pets = [
|
|
93
81
|
{'id': 0, 'name': 'Kitty', 'category': 'cat'},
|
|
94
82
|
{'id': 1, 'name': 'Coco', 'category': 'dog'},
|
|
95
|
-
{'id': 2, 'name': 'Flash', 'category': 'cat'}
|
|
83
|
+
{'id': 2, 'name': 'Flash', 'category': 'cat'},
|
|
96
84
|
]
|
|
97
85
|
|
|
98
86
|
|
|
@@ -100,12 +88,12 @@ class PetIn(Schema):
|
|
|
100
88
|
name = String(
|
|
101
89
|
required=True,
|
|
102
90
|
validate=Length(0, 10),
|
|
103
|
-
metadata={'title': 'Pet Name', 'description': 'The name of the pet.'}
|
|
91
|
+
metadata={'title': 'Pet Name', 'description': 'The name of the pet.'},
|
|
104
92
|
)
|
|
105
93
|
category = String(
|
|
106
94
|
required=True,
|
|
107
95
|
validate=OneOf(['dog', 'cat']),
|
|
108
|
-
metadata={'title': 'Pet Category', 'description': 'The category of the pet.'}
|
|
96
|
+
metadata={'title': 'Pet Category', 'description': 'The category of the pet.'},
|
|
109
97
|
)
|
|
110
98
|
|
|
111
99
|
|
|
@@ -158,12 +146,7 @@ def get_pets():
|
|
|
158
146
|
PetOut,
|
|
159
147
|
status_code=201,
|
|
160
148
|
description='The pet you just created',
|
|
161
|
-
links={'getPetById': {
|
|
162
|
-
'operationId': 'getPet',
|
|
163
|
-
'parameters': {
|
|
164
|
-
'pet_id': '$response.body#/id'
|
|
165
|
-
}
|
|
166
|
-
}}
|
|
149
|
+
links={'getPetById': {'operationId': 'getPet', 'parameters': {'pet_id': '$response.body#/id'}}},
|
|
167
150
|
)
|
|
168
151
|
@app.doc(tags=['Pet'])
|
|
169
152
|
def create_pet(json_data):
|
|
@@ -23,10 +23,7 @@ app.config['DOCS_DECORATORS'] = [app.auth_required(auth)]
|
|
|
23
23
|
|
|
24
24
|
@auth.verify_password
|
|
25
25
|
def verify_password(username: str, password: str) -> t.Union[str, None]:
|
|
26
|
-
if (
|
|
27
|
-
username in users
|
|
28
|
-
and check_password_hash(users[username], password)
|
|
29
|
-
):
|
|
26
|
+
if username in users and check_password_hash(users[username], password):
|
|
30
27
|
return username
|
|
31
28
|
return None
|
|
32
29
|
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
from apiflask import APIFlask, Schema, abort
|
|
2
|
+
from apiflask.fields import Integer, String
|
|
3
|
+
from apiflask.validators import Length, OneOf
|
|
4
|
+
|
|
5
|
+
app = APIFlask(__name__)
|
|
6
|
+
app.config['SYNC_LOCAL_SPEC'] = True
|
|
7
|
+
app.config['LOCAL_SPEC_PATH'] = 'openapi.json'
|
|
8
|
+
app.config['LOCAL_SPEC_JSON_INDENT'] = 0
|
|
9
|
+
|
|
10
|
+
pets = [{'id': 0, 'name': 'Kitty', 'category': 'cat'}, {'id': 1, 'name': 'Coco', 'category': 'dog'}]
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class PetIn(Schema):
|
|
14
|
+
name = String(required=True, validate=Length(0, 10))
|
|
15
|
+
category = String(required=True, validate=OneOf(['dog', 'cat']))
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class PetOut(Schema):
|
|
19
|
+
id = Integer()
|
|
20
|
+
name = String()
|
|
21
|
+
category = String()
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@app.get('/')
|
|
25
|
+
def say_hello():
|
|
26
|
+
# returning a dict or list equals to use jsonify()
|
|
27
|
+
return {'message': 'Hello!'}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@app.get('/pets/<int:pet_id>')
|
|
31
|
+
@app.output(PetOut)
|
|
32
|
+
def get_pet(pet_id):
|
|
33
|
+
if pet_id > len(pets) - 1:
|
|
34
|
+
abort(404)
|
|
35
|
+
# you can also return an ORM/ODM model class instance directly
|
|
36
|
+
# APIFlask will serialize the object into JSON format
|
|
37
|
+
return pets[pet_id]
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@app.patch('/pets/<int:pet_id>')
|
|
41
|
+
@app.input(PetIn(partial=True)) # -> json_data
|
|
42
|
+
@app.output(PetOut)
|
|
43
|
+
def update_pet(pet_id, json_data):
|
|
44
|
+
# the validated and parsed input data will
|
|
45
|
+
# be injected into the view function as a dict
|
|
46
|
+
if pet_id > len(pets) - 1:
|
|
47
|
+
abort(404)
|
|
48
|
+
for attr, value in json_data.items():
|
|
49
|
+
pets[pet_id][attr] = value
|
|
50
|
+
return pets[pet_id]
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
if __name__ == '__main__':
|
|
54
|
+
app.run()
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
spec_value=$(python3 -c "import json; print(open('openapi.json').read())")
|
|
4
|
+
# Replace var spec = {...} line in index.html
|
|
5
|
+
sed -i "s|var spec = {.*}|var spec = $spec_value|g" index.html
|
|
6
|
+
echo "Swagger HTML generated successfully!"
|
|
7
|
+
echo "Open index.html in your browser to view the Swagger UI."
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
|
|
4
|
+
<head>
|
|
5
|
+
<meta charset="UTF-8">
|
|
6
|
+
<title>APIFlask 0.1.0 - Swagger UI</title>
|
|
7
|
+
<link rel="stylesheet" type="text/css" href="https://unpkg.com/swagger-ui-dist/swagger-ui.css">
|
|
8
|
+
<link rel="icon" type="image/png"
|
|
9
|
+
href="https://apiflask.com/_assets/favicon.png">
|
|
10
|
+
<style>
|
|
11
|
+
html {
|
|
12
|
+
box-sizing: border-box;
|
|
13
|
+
overflow: -moz-scrollbars-vertical;
|
|
14
|
+
overflow-y: scroll;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
*,
|
|
18
|
+
*:before,
|
|
19
|
+
*:after {
|
|
20
|
+
box-sizing: inherit;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
body {
|
|
24
|
+
margin: 0;
|
|
25
|
+
background: #fafafa;
|
|
26
|
+
}
|
|
27
|
+
</style>
|
|
28
|
+
</head>
|
|
29
|
+
|
|
30
|
+
<body>
|
|
31
|
+
<div id="swagger-ui"></div>
|
|
32
|
+
|
|
33
|
+
<script src="https://unpkg.com/swagger-ui-dist/swagger-ui-bundle.js"></script>
|
|
34
|
+
<script src="https://unpkg.com/swagger-ui-dist/swagger-ui-standalone-preset.js"></script>
|
|
35
|
+
<script>
|
|
36
|
+
// we can get several config items of Function type
|
|
37
|
+
// referring to https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration/
|
|
38
|
+
var funcConfigItems = ['operationsSorter', 'tagsSorter', 'onComplete', 'requestInterceptor', 'responseInterceptor', 'modelPropertyMacro', 'parameterMacro']
|
|
39
|
+
|
|
40
|
+
function parseFunc(funcStr) {
|
|
41
|
+
return new Function('"use strict"; return ' + funcStr)()
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
var spec = {"components": {"schemas": {"HTTPError": {"properties": {"detail": {"type": "object"}, "message": {"type": "string"}}, "type": "object"}, "PetInUpdate": {"properties": {"category": {"enum": ["dog", "cat"], "type": "string"}, "name": {"maxLength": 10, "minLength": 0, "type": "string"}}, "type": "object"}, "PetOut": {"properties": {"category": {"type": "string"}, "id": {"type": "integer"}, "name": {"type": "string"}}, "type": "object"}, "ValidationError": {"properties": {"detail": {"properties": {"<location>": {"properties": {"<field_name>": {"items": {"type": "string"}, "type": "array"}}, "type": "object"}}, "type": "object"}, "message": {"type": "string"}}, "type": "object"}}}, "info": {"title": "APIFlask", "version": "0.1.0"}, "openapi": "3.0.3", "paths": {"/": {"get": {"parameters": [], "responses": {"200": {"content": {"application/json": {"schema": {}}}, "description": "Successful response"}}, "summary": "Say Hello"}}, "/pets/{pet_id}": {"get": {"parameters": [{"in": "path", "name": "pet_id", "required": true, "schema": {"type": "integer"}}], "responses": {"200": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/PetOut"}}}, "description": "Successful response"}, "404": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPError"}}}, "description": "Not found"}}, "summary": "Get Pet"}, "patch": {"parameters": [{"in": "path", "name": "pet_id", "required": true, "schema": {"type": "integer"}}], "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/PetInUpdate"}}}}, "responses": {"200": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/PetOut"}}}, "description": "Successful response"}, "404": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPError"}}}, "description": "Not found"}, "422": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/ValidationError"}}}, "description": "Validation error"}}, "summary": "Update Pet"}}}, "tags": []}
|
|
45
|
+
|
|
46
|
+
var baseConfig = {
|
|
47
|
+
spec: spec,
|
|
48
|
+
dom_id: "#swagger-ui",
|
|
49
|
+
deepLinking: true,
|
|
50
|
+
presets: [
|
|
51
|
+
SwaggerUIBundle.presets.apis,
|
|
52
|
+
SwaggerUIStandalonePreset
|
|
53
|
+
],
|
|
54
|
+
plugins: [
|
|
55
|
+
SwaggerUIBundle.plugins.DownloadUrl
|
|
56
|
+
],
|
|
57
|
+
layout: "BaseLayout",
|
|
58
|
+
oauth2RedirectUrl: "/docs/oauth2-redirect"
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
window.onload = function () {
|
|
62
|
+
const ui = SwaggerUIBundle(baseConfig)
|
|
63
|
+
|
|
64
|
+
}
|
|
65
|
+
</script>
|
|
66
|
+
</body>
|
|
67
|
+
|
|
68
|
+
</html>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"components": {"schemas": {"HTTPError": {"properties": {"detail": {"type": "object"}, "message": {"type": "string"}}, "type": "object"}, "PetInUpdate": {"properties": {"category": {"enum": ["dog", "cat"], "type": "string"}, "name": {"maxLength": 10, "minLength": 0, "type": "string"}}, "type": "object"}, "PetOut": {"properties": {"category": {"type": "string"}, "id": {"type": "integer"}, "name": {"type": "string"}}, "type": "object"}, "ValidationError": {"properties": {"detail": {"properties": {"<location>": {"properties": {"<field_name>": {"items": {"type": "string"}, "type": "array"}}, "type": "object"}}, "type": "object"}, "message": {"type": "string"}}, "type": "object"}}}, "info": {"title": "APIFlask", "version": "0.1.0"}, "openapi": "3.0.3", "paths": {"/": {"get": {"parameters": [], "responses": {"200": {"content": {"application/json": {"schema": {}}}, "description": "Successful response"}}, "summary": "Say Hello"}}, "/pets/{pet_id}": {"get": {"parameters": [{"in": "path", "name": "pet_id", "required": true, "schema": {"type": "integer"}}], "responses": {"200": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/PetOut"}}}, "description": "Successful response"}, "404": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPError"}}}, "description": "Not found"}}, "summary": "Get Pet"}, "patch": {"parameters": [{"in": "path", "name": "pet_id", "required": true, "schema": {"type": "integer"}}], "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/PetInUpdate"}}}}, "responses": {"200": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/PetOut"}}}, "description": "Successful response"}, "404": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPError"}}}, "description": "Not found"}, "422": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/ValidationError"}}}, "description": "Validation error"}}, "summary": "Update Pet"}}}, "tags": []}
|
|
@@ -60,15 +60,10 @@ def get_pet(pet_id):
|
|
|
60
60
|
@app.output(PetsOut)
|
|
61
61
|
def get_pets(query_data):
|
|
62
62
|
pagination = db.paginate(
|
|
63
|
-
db.select(PetModel),
|
|
64
|
-
page=query_data['page'],
|
|
65
|
-
per_page=query_data['per_page']
|
|
63
|
+
db.select(PetModel), page=query_data['page'], per_page=query_data['per_page']
|
|
66
64
|
)
|
|
67
65
|
pets = pagination.items
|
|
68
|
-
return {
|
|
69
|
-
'pets': pets,
|
|
70
|
-
'pagination': pagination_builder(pagination)
|
|
71
|
-
}
|
|
66
|
+
return {'pets': pets, 'pagination': pagination_builder(pagination)}
|
|
72
67
|
|
|
73
68
|
|
|
74
69
|
with app.app_context():
|