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.
Files changed (92) hide show
  1. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/CHANGES.md +24 -0
  2. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/PKG-INFO +2 -2
  3. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/README.md +1 -1
  4. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/examples/README.md +2 -1
  5. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/examples/auth/basic_auth/app.py +1 -4
  6. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/examples/auth/token_auth/app.py +3 -9
  7. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/examples/base_response/app.py +4 -16
  8. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/examples/basic/app.py +1 -1
  9. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/examples/blueprint_tags/app.py +1 -1
  10. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/examples/cbv/app.py +1 -4
  11. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/examples/dataclass/app.py +9 -15
  12. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/examples/openapi/basic/app.py +11 -28
  13. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/examples/openapi/custom_decorators/app.py +1 -4
  14. apiflask-2.1.4.dev0/examples/openapi/static_docs/app.py +54 -0
  15. apiflask-2.1.4.dev0/examples/openapi/static_docs/generate_docs.sh +7 -0
  16. apiflask-2.1.4.dev0/examples/openapi/static_docs/index.html +68 -0
  17. apiflask-2.1.4.dev0/examples/openapi/static_docs/openapi.json +1 -0
  18. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/examples/orm/app.py +1 -1
  19. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/examples/pagination/app.py +2 -7
  20. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/examples/test_examples.py +24 -33
  21. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/pyproject.toml +46 -3
  22. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/requirements/dev.txt +3 -3
  23. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/requirements/docs.txt +8 -6
  24. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/requirements/examples.txt +1 -1
  25. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/requirements/min-versions.txt +1 -1
  26. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/requirements/tests.txt +5 -5
  27. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/requirements/typing.txt +1 -1
  28. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/src/APIFlask.egg-info/PKG-INFO +2 -2
  29. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/src/APIFlask.egg-info/SOURCES.txt +5 -0
  30. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/src/APIFlask.egg-info/entry_points.txt +2 -0
  31. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/src/apiflask/__init__.py +1 -1
  32. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/src/apiflask/app.py +157 -177
  33. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/src/apiflask/blueprint.py +10 -10
  34. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/src/apiflask/commands.py +4 -8
  35. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/src/apiflask/exceptions.py +13 -10
  36. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/src/apiflask/fields.py +1 -0
  37. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/src/apiflask/helpers.py +4 -4
  38. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/src/apiflask/openapi.py +15 -25
  39. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/src/apiflask/route.py +8 -6
  40. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/src/apiflask/scaffold.py +64 -48
  41. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/src/apiflask/schemas.py +15 -29
  42. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/src/apiflask/security.py +14 -17
  43. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/src/apiflask/settings.py +23 -21
  44. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/src/apiflask/types.py +3 -1
  45. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/tests/schemas.py +1 -3
  46. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/tests/test_app.py +29 -21
  47. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/tests/test_async.py +9 -12
  48. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/tests/test_base_response.py +24 -33
  49. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/tests/test_blueprint.py +3 -3
  50. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/tests/test_commands.py +4 -4
  51. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/tests/test_decorator_auth_required.py +8 -9
  52. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/tests/test_decorator_doc.py +66 -90
  53. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/tests/test_decorator_input.py +114 -95
  54. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/tests/test_decorator_output.py +51 -50
  55. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/tests/test_exceptions.py +30 -27
  56. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/tests/test_fields.py +9 -7
  57. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/tests/test_openapi_basic.py +14 -32
  58. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/tests/test_openapi_blueprint.py +1 -1
  59. apiflask-2.1.4.dev0/tests/test_openapi_extensions.py +13 -0
  60. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/tests/test_openapi_headers.py +11 -8
  61. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/tests/test_openapi_info.py +12 -28
  62. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/tests/test_openapi_paths.py +73 -66
  63. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/tests/test_openapi_security.py +15 -14
  64. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/tests/test_openapi_tags.py +22 -20
  65. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/tests/test_route.py +9 -7
  66. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/tests/test_schemas.py +7 -9
  67. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/tests/test_security.py +11 -7
  68. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/tests/test_settings_api_docs.py +7 -22
  69. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/tests/test_settings_auto_behaviour.py +43 -28
  70. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/tests/test_settings_openapi_fields.py +29 -51
  71. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/tests/test_settings_response_customization.py +32 -38
  72. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/tox.ini +1 -1
  73. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/LICENSE +0 -0
  74. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/MANIFEST.in +0 -0
  75. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/NOTICE +0 -0
  76. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/examples/file_upload/app.py +0 -0
  77. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/examples/file_upload/upload/.gitkeep +0 -0
  78. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/examples/requirements.txt +0 -0
  79. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/setup.cfg +0 -0
  80. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/src/APIFlask.egg-info/dependency_links.txt +0 -0
  81. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/src/APIFlask.egg-info/requires.txt +0 -0
  82. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/src/APIFlask.egg-info/top_level.txt +0 -0
  83. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/src/apiflask/py.typed +0 -0
  84. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/src/apiflask/ui_templates.py +0 -0
  85. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/src/apiflask/validators.py +0 -0
  86. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/src/apiflask/views.py +0 -0
  87. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/tests/__init__.py +0 -0
  88. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/tests/conftest.py +0 -0
  89. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/tests/test_apps/__init__.py +0 -0
  90. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/tests/test_decorators.py +0 -0
  91. {apiflask-2.1.2 → apiflask-2.1.4.dev0}/tests/test_helpers.py +0 -0
  92. {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.2
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/migrating)* for more details):
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/migrating)* for more details):
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 example: [/examples/openapi][_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
- 'id': self.id
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>')
@@ -7,7 +7,7 @@ app = APIFlask(__name__)
7
7
  pets = [
8
8
  {'id': 0, 'name': 'Kitty', 'category': 'cat'},
9
9
  {'id': 1, 'name': 'Coco', 'category': 'dog'},
10
- {'id': 2, 'name': 'Flash', 'category': 'cat'}
10
+ {'id': 2, 'name': 'Flash', 'category': 'cat'},
11
11
  ]
12
12
 
13
13
 
@@ -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
- 'example': 'Medor',
17
- 'description': 'This will be printed in the generated doc. '
18
- 'The "example" value "Medor" will be fed '
19
- 'into the "try it"/"Send API request".',
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
- 'name': 'Development Server',
74
- 'url': 'http://localhost:5000'
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": []}
@@ -22,7 +22,7 @@ def init_database():
22
22
  pets = [
23
23
  {'name': 'Kitty', 'category': 'cat'},
24
24
  {'name': 'Coco', 'category': 'dog'},
25
- {'name': 'Flash', 'category': 'cat'}
25
+ {'name': 'Flash', 'category': 'cat'},
26
26
  ]
27
27
  for pet_data in pets:
28
28
  pet = PetModel(**pet_data)
@@ -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():