0b01001001 / spectree Goto Github PK
View Code? Open in Web Editor NEWAPI spec validator and OpenAPI document generator for Python web frameworks.
Home Page: https://0b01001001.github.io/spectree/
License: Apache License 2.0
API spec validator and OpenAPI document generator for Python web frameworks.
Home Page: https://0b01001001.github.io/spectree/
License: Apache License 2.0
I'm currently experimenting with Falcon 3.0.0a1 and was wondering if you have plans to add an async Falcon plugin for SpecTree (3.0 will support both WSGI and ASGI applications)?
If not, this might be something I would be interesting in contributing since I really like that SpecTree provides a way to both handle input/output (de)serialization with Pydantic and autogenerates an OpenAPI document similar to FastAPI. What would your preferred method of implementing this? A completely separate plugin, a subclass of the existing Falcon plugin, something else?
Describe the bug
Openapi.json cannot be properly loaded when host includes sub-domain
To Reproduce
For example:
Given the hostname: http://123.4.0.0/api/
Access the doc: http://123.4.0.0/api/apidoc/swagger
The Swagger UI will attempt to load the openapi.json at /apidoc/openapi.json where the json does not exist
Expected behavior
The Swagger UI should attempt to load the openapi.json at:
/api/apidoc/openapi.json
Error Message
Unable to render definition
Others
The test feature is also unusable, because the Swagger UI use the wrong Request-URL:
http://123.4.0.0 instead of http://123.4.0.0/api/
HTTPEndPoint
classes (sync & async), which makes it hard to implement a general validator(scope, receive, send)
instead of (request)
Currently, starlette
will return its own starlette.Response
like JSONResponse
, while flask
and falcon
return an inherit instance of pydantic.BaseModel
. To reduce users' work, all of these should return the frameworks' own type.
On the other hand, exceptions are not caught by spectree
, so it won't verify if it's the correct format as defined.
The specification keywords for routes are currently hardcoded to only generate name
, in
, schema
, required
, description
as is dictated here, specifically this block of code:
params.append(
{
"name": name,
"in": "query",
"schema": schema,
"required": name in query.get("required", []),
"description": schema.get("description", ""),
}
)
However there are other keywords that might want to be added for different routes and models. For example the style
and explode
keywords as explained here. It would be beneficial to have some way for this to be passed in on a model-by-model basis for different routes. Something akin to the following:
params.append(
{
"name": name,
"in": "query",
"schema": schema,
"required": name in query.get("required", []),
"description": schema.get("description", ""),
**extra_spec_kwargs
}
)
Although I'm not sure what the best way to provide extra_spec_kwargs
would be with the current API.
check Python string template
Describe the bug
When serializers schemes (not only for req/resp for one endpoint) have the same name (it is actual too when nested models of schemes for different endpoints have the same names), the /redoc and /swagger does not display the field data correctly.
To Reproduce
from pydantic import BaseModel, Extra
class BaseSerializer(BaseModel):
class Config:
extra = Extra.forbid
from .base import BaseSerializer
...
class Passengers(BaseSerializer):
type: str = Field(..., description="Возрастная категория пассажира", example="ADULT")
class CreateCartReq(BaseSerializer):
cart_id: UUID4 = Field(
..., description="Уникальный идентификатор корзины", example="c205de04-e789-49d1-b232-4efb741a4e60"
)
passengers: List[Passengers] = Field(..., description="Информация о пассажирах", example=[{"type": "ADULT"}])
order: Order = Field(..., description="Информация о заказе")
updated_at: Optional[int] = Field(description="[old] Timestamp, последнего обновления", example=1619167989)
from .base import BaseSerializer
...
class Passengers(BaseSerializer):
id: UUID4 = Field(
..., description="Уникальный идентификатор пассажира", example="63f84506-d626-4ca0-9247-eb453365ac92"
)
type: str = Field(..., description="Возрастная категория пассажира", example="ADULT")
class CreateCartResp(BaseSerializer):
adult_count: Optional[int] = Field(description="[async] Количество взрослых", example=1)
...
passengers: List[Passengers] = Field(
...,
description="Данные о пассажирах",
example=[{"id": "63f84506-d626-4ca0-9247-eb453365ac92", "type": "ADULT"}],
)
...
return_date: Optional[int] = Field(description="[async] Дата возвращения", example=1622505600)
from .base import BaseSerializer
...
class Passengers(BaseSerializer):
id: UUID4 = Field(..., description="Уникальный идентификатор", example="507a72ef-3af6-4f30-916d-f914485d1606")
doctype: str = Field(..., description="Тип документа удостоверяющего личность", example="ps")
docnumber: str = Field(..., description="Серия и номер документа", example="1401815424")
doccountry: NoneStr = Field(description="Код страны документа", example="RU")
docexpiration: Optional[int] = Field(description="Срок действия документа", example=1861920000)
birthday: int = Field(..., description="Timestamp даты рождения", example=691977600)
gender: str = Field(..., description="Пол", example="M")
first_name: str = Field(..., description="Имя", example="Иван")
last_name: str = Field(..., description="Фамилия", example="Иванов")
second_name: NoneStr = Field(description="Отчество", example="Иванович")
type: str = Field(..., description="Возрастная категория пассажира", example="ADULT")
ff_cardnumber: NoneStr = Field(description="Номер карты лояльности", example="1025060987")
class UpdateCartReq(BaseSerializer):
adult_count: Optional[int] = Field(description="[old] Количество взрослых", example=1)
...
passengers: List[Passengers] = Field(..., description="Информация о пассажирах")
...
user: Optional[User] = Field(
description="Уникальный идентификатор пользователя", example={"id": "5b71cbffba3e9c584cb1a5c2"}
)
from .base import BaseSerializer
...
class Passengers(BaseSerializer):
birthday: int = Field(..., description="Timestamp даты рождения", example=691977600)
doccountry: NoneStr = Field(description="Код страны документа", example="RU")
docnumber: str = Field(..., description="Серия и номер документа", example="1401815424")
doctype: str = Field(..., description="Тип документа удостоверяющего личность", example="ps")
docexpiration: Optional[int] = Field(description="Срок действия документа", example=1861920000)
first_name: str = Field(..., description="Имя", example="Иван")
gender: str = Field(..., description="Пол", example="M")
id: str = Field(..., description="Уникальный идентификатор", example="507a72ef-3af6-4f30-916d-f914485d1606")
last_name: str = Field(..., description="Фамилия", example="Иванов")
second_name: NoneStr = Field(description="Отчество", example="Иванович")
type: str = Field(..., description="Возрастная категория пассажира", example="ADULT")
ff_cardnumber: NoneStr = Field(description="Номер карты лояльности", example="1025060987")
class UpdateCartResp(BaseSerializer):
adult_count: Optional[int] = Field(description="[old] Количество взрослых", example=1)
...
passengers: List[Passengers] = Field(..., description="Информация о пассажирах")
...
user: Optional[User] = Field(
description="Уникальный идентификатор пользователя", example={"id": "5b71cbffba3e9c584cb1a5c2"}
)
Expected behavior
Correct display of the serializers schemes fields.
Actual behavior
if schemes in one endpoint have the same names
if schemes in several endpoints have the same names
Desktop:
Python Information:
Additional context
The problem is also observed, not only within a single endpoint, but also for several endpoints, if their serializers schemes contains (nested) schemes with the same name.
Describe the bug
Using __root__
from pydantic doesn't work in falcon (json).
To Reproduce
Create a Model with custom __root__
as described in https://pydantic-docs.helpmanual.io/usage/models/#custom-root-types.
Example:
class Model(BaseModel):
__root__: Dict[str, str]
It seems that spectree doesn't recognize __root__
as a special entry.
{'a': 'b'} # Doesn't work
{'__root__': {'a': 'b'}} # Does work
Desktop (please complete the following information):
Python Information (please complete the following information):
I'm currently using WSGI (waitress-serve) with falcon.
When I decorate post method in resource class with api.validate(json=User) , request body and parameters doesn't appear in documentation
@api.validate(json=User, resp=Response(HTTP_200=None, HTTP_400=None), tags=['UserSignUp'])
def post(self):
print(reqparse.request.context.json)
self.__add_parser_argument()
user_args = self.parser.parse_args()
Describe the bug
When defining a custom header model with a field Authorization
,
class RequestHeader(BaseModel):
Authorization: str
and using it as such:
@api_validator.validate(headers=RequestHeader)
def query():
...
the rendered openapi.json contains Authorization
as a parameter:
"parameters": [
{
"in": "header",
"name": "Authorization",
"required": true,
"type": "string"
}
],
which is unusable, because it is restricted by swagger-ui per https://swagger.io/docs/specification/describing-parameters/#header-parameters: "Note: Header parameters named Accept, Content-Type and Authorization are not allowed"
To Reproduce
See pydantic model above. To server the swagger UI, I'm using flask-swagger-ui
.
Expected behavior
I'd expect the restricted headers (Accept, Content-Type and Authorization) to be formatted not as parameters, but as their corresponding OpenAPI keywords per https://swagger.io/docs/specification/describing-parameters/#header-parameters
Error Message
No error message from SpecTree, but in Swagger UI, the Authorization header is NOT sent in the request when executing the request.
Desktop (please complete the following information):
Python Information (please complete the following information):
v0.3.3
flask-swagger-ui==3.25.0
Let me know if you need more information. Awesome project btw!
According to API guidelines and GitHub API v3, the response format should be JSON, even for error responses.
So the response parameter may look like Response(HTTP_201, HTTP_409=Error_409)
.
Describe the bug
The form with the same name will be overwritten,even in different modules
To Reproduce
Steps to reproduce the behavior:
/forms1/form1.py
from pydantic import BaseModel, Field
class MyReq(BaseModel):
param2: str = Field(description='This is param2')
class MyResp(BaseModel):
text2: str
/forms2/form2.py
from pydantic import BaseModel, Field
class MyReq(BaseModel):
param1: int = Field(description='This is param1')
class MyResp(BaseModel):
text1: str
app
@app.route('/api/test1')
@api.validate(query=form1.MyReq, resp=Response(HTTP_200=form1.MyResp), tags=['api-get'])
def test_get1(query: form1.MyReq):
"""test_get2 doc"""
return jsonify(text='get1 works')
@app.route('/api/test2')
@api.validate(query=form2.MyReq, resp=Response(HTTP_200=form2.MyResp), tags=['api-get'])
def test_get2(query: form2.MyReq):
"""test_get2 doc"""
return jsonify(text='get2 works')
Expected behavior
The query parameters of test1
and test2
should be different,because they are different forms with the same name.
However, the query parameters are the same as form2.MyReq.
Error Message
Here is the code in spec.py
# register
for name, model in zip(
("query", "json", "headers", "cookies"), (query, json, headers, cookies)
):
if model is not None:
assert issubclass(model, BaseModel)
self.models[model.__name__] = model.schema(
ref_template="#/components/schemas/{model}"
)
setattr(validation, name, model.__name__)
if resp:
for model in resp.models:
self.models[model.__name__] = model.schema(
ref_template="#/components/schemas/{model}"
)
I found the problem is the key of models which is model.__name__
, is this the excepted result?
Python Information (please complete the following information):
Response
Describe the bug
Hi,
consider the following model and request:
class StatsQuery(BaseModel):
events: List[Literal["CREATED", "DEACTIVATED"]]
/stats?events[]=CREATED&events[]=DEACTIVATED
edit above:
/stats?events=CREATED&events=DEACTIVATED
Expected behavior
Error Message
[
{
"loc": [
"events"
],
"msg": "value is not a valid list",
"type": "type_error.list"
}
]
Python Information (please complete the following information):
Additional context
For now I'm using this ugly wokaround: in FlaskPlugin.validate()
:
-arg = request.args or {}
+arg = request.args.to_dict(flat=True) or {}
+if arg:
+ for field_name, field_info in query.schema()['properties'].items():
+ if field_info['type'] == 'array':
+ arg[field_name] = request.args.getlist(field_name)
Describe the bug
Adding a static mount to the routes will result in failure to load openapi.json, with an internal server error.
To Reproduce
Steps to reproduce the behavior:
Given the provided Starlette example (with the needed fix to the api mount path, as it is incorrectly provided in the example):
...
from starlette.staticfiles import StaticFiles
...
app = Starlette(routes=[
Mount('/api', routes=[
Route('/user', user_profile, methods=['POST']),
]),
Mount('/static', StaticFiles(directory="static"), name='static'),
])
...
Expected behavior
Should be able to load openapi.json, redoc, and swagger, even if a static mount location is specified.
Error Message
Traceback (most recent call last):
File "/Users/scott/.pyenv/versions/3.7.7/lib/python3.7/site-packages/uvicorn/protocols/http/h11_impl.py", line 389, in run_asgi
result = await app(self.scope, self.receive, self.send)
File "/Users/scott/.pyenv/versions/3.7.7/lib/python3.7/site-packages/uvicorn/middleware/proxy_headers.py", line 45, in __call__
return await self.app(scope, receive, send)
File "/Users/scott/.pyenv/versions/3.7.7/lib/python3.7/site-packages/starlette/applications.py", line 111, in __call__
await self.middleware_stack(scope, receive, send)
File "/Users/scott/.pyenv/versions/3.7.7/lib/python3.7/site-packages/starlette/middleware/errors.py", line 181, in __call__
raise exc from None
File "/Users/scott/.pyenv/versions/3.7.7/lib/python3.7/site-packages/starlette/middleware/errors.py", line 159, in __call__
await self.app(scope, receive, _send)
File "/Users/scott/.pyenv/versions/3.7.7/lib/python3.7/site-packages/starlette/exceptions.py", line 82, in __call__
raise exc from None
File "/Users/scott/.pyenv/versions/3.7.7/lib/python3.7/site-packages/starlette/exceptions.py", line 71, in __call__
await self.app(scope, receive, sender)
File "/Users/scott/.pyenv/versions/3.7.7/lib/python3.7/site-packages/starlette/routing.py", line 566, in __call__
await route.handle(scope, receive, send)
File "/Users/scott/.pyenv/versions/3.7.7/lib/python3.7/site-packages/starlette/routing.py", line 227, in handle
await self.app(scope, receive, send)
File "/Users/scott/.pyenv/versions/3.7.7/lib/python3.7/site-packages/starlette/routing.py", line 43, in app
response = await run_in_threadpool(func, request)
File "/Users/scott/.pyenv/versions/3.7.7/lib/python3.7/site-packages/starlette/concurrency.py", line 34, in run_in_threadpool
return await loop.run_in_executor(None, func, *args)
File "/Users/scott/.pyenv/versions/3.7.7/lib/python3.7/concurrent/futures/thread.py", line 57, in run
result = self.fn(*self.args, **self.kwargs)
File "/Users/scott/.pyenv/versions/3.7.7/lib/python3.7/site-packages/spectree/plugins/starlette_plugin.py", line 30, in <lambda>
lambda request: JSONResponse(self.spectree.spec),
File "/Users/scott/.pyenv/versions/3.7.7/lib/python3.7/site-packages/spectree/spec.py", line 60, in spec
self._spec = self._generate_spec()
File "/Users/scott/.pyenv/versions/3.7.7/lib/python3.7/site-packages/spectree/spec.py", line 150, in _generate_spec
for route in self.backend.find_routes():
File "/Users/scott/.pyenv/versions/3.7.7/lib/python3.7/site-packages/spectree/plugins/starlette_plugin.py", line 129, in find_routes
parse_route(self.app)
File "/Users/scott/.pyenv/versions/3.7.7/lib/python3.7/site-packages/spectree/plugins/starlette_plugin.py", line 127, in parse_route
parse_route(route, prefix=f'{prefix}{route.path}')
File "/Users/scott/.pyenv/versions/3.7.7/lib/python3.7/site-packages/spectree/plugins/starlette_plugin.py", line 102, in parse_route
for route in app.routes:
TypeError: 'NoneType' object is not iterable
Desktop (please complete the following information):
Python Information (please complete the following information):
Additional context
n/a
utils
Response
plugins
spec
validate
Hello again,
Black is becoming the de facto a standard for formatting Python code. By following the rule "if you can automate it, automate it", this can eliminate some tedious manual formatting, and enforce uniform code style from other potential contributors.
How would you feel about it?
Can we consider integrating static files to ensure that swagger UI can be used in offline environment?
So far, I can't find a way to add a description for path
variables. On the flask side, It would be cool to have a parameter such doc
as flask-restfull provides :
@api.route('/my-resource/<id>', endpoint='my-resource')
@api.doc(params={'id': 'An ID'})
I read the source code of tags:
...
for tag in func_tags:
if tag not in tags:
tags[tag] = {"name": tag}
...
https://github.com/0b01001001/spectree/blob/v0.4.2/spectree/spec.py#L212
Please consider adding a description field to the tag(Because I want to describe the tag in Chinese),just like official documents:
https://swagger.io/specification/
. . .
Adds metadata to a single tag that is used by the Operation Object. It is not mandatory to have a Tag Object per tag defined in the Operation Object instances.
Fixed Fields
Field Name | Type | Description |
---|---|---|
name | string | REQUIRED. The name of the tag. |
description | string | A short description for the tag. CommonMark syntax MAY be used for rich text representation. |
externalDocs | External Documentation Object | Additional external documentation for this tag. |
. . .
Describe the bug
@api.validate
doesn't work on GET
requests.
To Reproduce
The sample code in https://github.com/0b01001001/spectree/blob/master/examples/falcon_demo.py#L56
@api.validate(tags=[demo])
def on_get(self, req, resp):
Expected behavior
I expect at least It supports validation over query
but not for the request body as GET doesn't have any request body.
Error Message
{
"title": "Invalid JSON",
"description": "Could not parse an empty JSON body"
}
Desktop (please complete the following information):
Python Information (please complete the following information):
Additional context
Describe the bug
I'm using this library to generate swagger:2.0 spec and ran into few issues with the generated spec via .spec attribute.
swagger: '2.0'
for the version instead of the generated openapi: '2.0'
operationId
but generated version has operationID
which errors out as invalidshould NOT have additional properties
additionalProperty: operationID
To Reproduce
Steps to reproduce the behavior:
SpecTree('flask', title="API", path="docs", openapi_version="2.0").spec
Expected behavior
a valid swagger data for 2.0 version
Python Information (please complete the following information):
Additional context
I'm happy to submit a PR if allowed to fix above issues as long as the maintainer agrees with above issues.
In __init__
method of Response
class, we have the following code
if code_models and "HTTP_422" not in code_models:
code_models["HTTP_422"] = UnprocessableEntity
It creates UnprocessableEntity and add it into response.
"responses":{
"200":{
"description":"OK",
"content":{
"application/json":{
"schema":{
"$ref":"#/components/schemas/BaseResponse"
}
}
}
},
"422":{
"description":"Unprocessable Entity",
"content":{
"application/json":{
"schema":{
"$ref":"#/components/schemas/UnprocessableEntity"
}
}
}
}
}
Maybe there is another way to not add 422 code into response?
@api.validate(json=BaseRequest, resp=Response(HTTP_200=BaseResponse, HTTP_422=None))
doesn't create schema but add 422 code into response
"responses":{
"422":{
"description":"Unprocessable Entity"
},
"200":{
"description":"OK",
"content":{
"application/json":{
"schema":{
"$ref":"#/components/schemas/BaseResponse"
}
}
}
}
}
Sometimes people may need to do something else when there is a validation error, like recording the metric. Since the validation happens before the endpoint function, there should be a way to do this.
This is available at func.__doc__
Describe the bug
When using flask method views, even though the request and response schemas are generated in the open api spec, they are not associated with api path. So the paths field in the openapi json is this for the example below -
"paths": {
"/api/v1/hello": {
"post": {
"description": "",
"operationId": "post_/api/v1/hello",
"parameters": [],
"responses": {},
"summary": "Hello <POST>",
"tags": []
}
}
}
To Reproduce
You can run the following python code and check the generated openapi spec.
from flask import Flask
from flask.views import MethodView
from spectree import SpecTree, Response
from pydantic import BaseModel
app = Flask(__name__)
spectree = SpecTree('flask')
class HelloResponse(BaseModel):
hello: str
status: int
class HelloRequest(BaseModel):
test: int
class Hello(MethodView):
@spectree.validate(json=HelloRequest, resp=Response(HTTP_200=HelloResponse))
def post():
return {'hello': 'world', 'status': 200}
app.add_url_rule('/api/v1/hello', view_func=Hello.as_view('Hello'))
spectree.register(app)
if __name__ == '__main__':
app.run(port=8081)
You can see the generated openapi spec here - openapi spec
Expected behavior
I expected that the generated request and response schema would be associated with the /api/v1/hello
path instead of it being blank.
Error Message
There's no error message as such.
Desktop (please complete the following information):
Python Information (please complete the following information):
Please let me know if you need any additional information. I'd also be happy to contribute a PR if you could direct me a bit in terms of where in the code the issue might be.
I am using JWT token in OpenAPI.
I understand that securityschemes
defines security schemes. Please consider adding securityschemes
in Components object
https://github.com/0b01001001/spectree/blob/v0.4.2/spectree/spec.py#L236
spec = {
"openapi": self.config.OPENAPI_VERSION,
"info": {
"title": self.config.TITLE,
"version": self.config.VERSION,
"description": self.config.DESCRIPTION,
},
"tags": list(tags.values()),
"paths": {**routes},
"components": {"schemas": {**self.models, **self._get_model_definitions()}},
}
just like:
components:
schemas:
...
securitySchemes:
api_key:
type: apiKey
name: api_key
in: header
jwt:
type: http
scheme: bearer
bearerFormat: JWT
}
The flask plugin currently produces the following spec for a route parameter that uses the any
converter:
if converter == "any":
schema = {
"type": "array",
"items": {
"type": "string",
"enum": args,
},
}
As seen here -> https://github.com/0b01001001/spectree/blob/master/spectree/plugins/flask_plugin.py#L80
However the any
converter behaviour is defined as the following:
Matches one of the items provided
As seen here -> https://github.com/pallets/werkzeug/blob/8be9899df5cf8a930d961fc5295001744373da0b/src/werkzeug/routing.py#L1227
As such the correct spec produced should actually be:
if converter == "any":
schema = {
"type": "string",
"enum": args,
}
Any objection if I create a PR to fix this?
Thank you for this library, this will be very useful for people who wants to use pydantic in their APIs.
I had some issues when I tried the lib:
The view function did not return a valid response. The return type must be a string, tuple, Response instance, or WSGI callable, but it was a int.
. I think you have to keep everything optional.Coming from flask_pydantic library, spectree has similar signature using validate() function and providing models for validation.
However, in flask_pydantic you get access to your model directly and in spectree it gets converted to dictionary from an object. It would be nice to persist that object.
Example:
@api_spec.validate(json=BodyModel, resp=Response(HTTP_200=ResponseModel), tags=["push"])
def push(account: str):
# request.json is a dictionary
# would like access to BodyModel with request data in here
Describe the bug
query always fails validation.
To Reproduce
Steps to reproduce the behavior:
from flask import Flask, request, jsonify
from pydantic import BaseModel
from spectree import SpecTree, Response
class Q(BaseModel):
q: str
app = Flask(__name__)
api = SpecTree('flask')
@app.route('/api/user', methods=['GET'])
@api.validate(query=Q, resp=Response(HTTP_200=Q, HTTP_403=None), tags=['api'])
def user_profile():
print(request.context.query)
return jsonify(q='it works')
if __name__ == "__main__":
api.register(app)
app.run(port=8000)
Expected behavior
[
{
"loc": [
"q"
],
"msg": "str type expected",
"type": "type_error.str"
}
]
Desktop (please complete the following information):
Python Information (please complete the following information):
Hi.
I have this starlette endpoint.
class UserPostListEndpoint(HTTPEndpoint):
@api.validate(resp=Response(HTTP_200=PostListSchema))
async def get(self, request):
user_id = request.path_params["user_id"]
return UJSONResponse(await get_user_posts(user_id))
This endpoint is mounted as:
routes = [
Mount(
"/api/v1",
name="v1",
routes=[
Route(
"/users/{user_id:int}/posts", UserPostListEndpoint, name="user_list"
)
],
)
]
Invoking it throws an exception.
, in run
result = self.fn(*self.args, **self.kwargs)
TypeError: validate() missing 2 required positional arguments: 'receive' and 'send'
Does this not work with starlette endpoint?
To keep the interface clean and flexible, it's better to pass the plugin class instead of backend framework name strings. Also, the sync/async flag should be written to the plugin class as described in https://github.com/0b01001001/spectree/pull/46/files#diff-485ad20a22f45089777317b26137dc90R13-R15
Since the body can be serialized in multiple ways, like JSON, MessagePack, ProtoBuf, etc. Although JSON should be the most common way to do so, it's better to support other methods.
This is due to the decorator.
class Demo:
def test(self): pass
def on_get(self, req, resp):
self.test() # this will raise AttributeError
pass
And the parse_name
function for falcon endpoint function should return the class name instead of the method name. Since the method names are all the same.
Hi, when I add a description for a schema used in query, it can not show in swagger ui but can show in Redoc
@HELLO.route('/', methods=['GET'])
@api.validate(query=HelloForm)
def hello():
"""
hello 注释
:return:
"""
return 'ok'
class HelloForm(BaseModel):
"""
hello表单
"""
user: str # 用户名称
msg: str = Field(description='msg test', example='aa')
index: int
data: HelloGetListForm
list: List[HelloListForm]
Users may need to define their customized headers contain some required parameters.
For example, Authorization and Cookie.
Describe the bug
As json
takes a pynatic model and there is no Pyndatic field with type file
I could not find a way to upload a file.
Does anybody know of a way to create a swagger file upload in Flask?
like:
class Item(BaseModel):
name: str
price: float
@app.route("/", method=["POST"])
@api.validate(json=Item, resp=Response(HTTP_200=List[Item]), tags=["demo"])
def demo():
item = Item.parse_obj(request.context.json)
return [item, item, item]
For example, /{num:int}
should be displayed as /{num}
.
(First of all, I've opened this PR: #91 in order to fix the bug)
Describe the bug
When generating an openapi schema for V3, we have these warnings that pop up: object instance has properties which are not allowed by the schema: [\"definitions\"]
To Reproduce
Steps to reproduce the behavior:
Expected behavior
I'd expect the references to point to #/components/schemas/{model}
and not #/definitions/{model}
.
Also, for this, the definitions need to be moved to the schemas section.
Error Message
object instance has properties which are not allowed by the schema: [\"definitions\"]
Desktop (please complete the following information):
Python Information (please complete the following information):
Additional context
Changelog in Pydantic: https://pydantic-docs.helpmanual.io/changelog/#changes (search for ref_template)
Hi, i use your library in several work projects with flask. And each project i add description for default 422 HTTP Error again and again. Maybe, we can add default exception model?
My solution looks like this:
https://github.com/0b01001001/spectree/compare/master...honeydev:add-422-default-description?expand=1
And we have description out of box :
Thanks!
How authentication and authorization should be used with spectree?
It is difficult to tell why validation error(s) happend because Spectree only returns "response validation error".
try:
model.validate(response.get_json())
except ValidationError as err:
resp_validation_error = err
response = make_response(
jsonify({"message": "response validation error"}), 500
)
(https://github.com/0b01001001/spectree/blob/master/spectree/plugins/flask_plugin.py#L156-L160)
How about making the message more verbose?
For example, FastAPI returns a verbose validation error response.
https://github.com/tiangolo/fastapi/blob/e1758d107ea5eede358cfdbf69ae7829c8e65a92/fastapi/openapi/utils.py#L33-L42
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.