sanic-org / sanic-openapi

Easily document your Sanic API with a UI
https://sanic-openapi.readthedocs.io/
MIT License
505 stars 108 forks source link

[Bug] Decorator doc/openapi not work when using `routes of HTTPMethodView under Bluebprint instanc` way #233

Closed hsz1273327 closed 3 years ago

hsz1273327 commented 3 years ago

Describe the bug Decorator doc/openapi not work when using routes of HTTPMethodView under Bluebprint instanc way,

Screenshots api-2

To Reproduce

from sanic import Blueprint
from .userlistsource import UserListSource
from .usersource import UserSource

usernamespace_v1 = Blueprint("user", url_prefix="/user", version=1)
usernamespace_v1.add_route(UserListSource.as_view(), "/")
usernamespace_v1.add_route(UserSource.as_view(), "/<uid:int>")
from sanic.response import json, HTTPResponse
from sanic.request import Request
from sanic.views import HTTPMethodView
from sanic_openapi import doc
from decorators.checkjsonschema import checkjsonschema
from modules.usermodule import UserDB

post_query_schema = {
    "$schema": "http://json-schema.org/draft-07/schema#",
    "type": "object",
    "additionalProperties": False,
    "required": ["name"],
    "properties": {
        "name": {
            "type": "string",
            "description": "用户注册名"
        }
    }
}

class UserListSource(HTTPMethodView):

    @doc.operation('UserListSource_GET')
    @doc.summary("user接口总览")
    async def get(self, _: Request) -> HTTPResponse:
        cnt = await UserDB.len()
        result = {
            "description": "测试api,User总览",
            "user-count": cnt,
            "links": [
                {
                    "uri": "/user",
                    "method": "POST",
                    "description": "创建一个新用户"
                },
                {
                    "uri": "/user/<int:uid>",
                    "method": "GET",
                    "description": "用户号为<id>的用户信息"
                },
                {
                    "uri": "/user/<int:uid>",
                    "method": "PUT",
                    "description": "更新用户号为<id>用户信息"
                },
                {
                    "uri": "/user/<int:uid>",
                    "method": "DELETE",
                    "description": "删除用户号为<id>用户"
                },
            ]
        }

        return json(result, ensure_ascii=False)

    @doc.operation('UserListSource_POST')
    @doc.summary("创建新用户")
    @checkjsonschema(post_query_schema)
    async def post(self, request: Request) -> HTTPResponse:
        query_json = request.json
        try:
            u = await UserDB.add(query_json.get("name", ""))
        except Exception as e:
            return json({
                "msg": "执行错误",
            }, status=500, ensure_ascii=False)
        else:
            return json({
                "msg": "插入成功",
                "uid": u.ID
            }, ensure_ascii=False)
from dataclasses import asdict
from sanic.views import HTTPMethodView
from sanic.request import Request
from sanic.response import json, HTTPResponse
from sanic_openapi import doc
from jsonschema import validate
from decorators.checkjsonschema import checkjsonschema
from modules.usermodule  import UserDB

put_query_schema = {
    "$schema": "http://json-schema.org/draft-07/schema#",
    "type": "object",
    "additionalProperties": False,
    "required": ["name"],
    "properties": {
        "name": {
            "type": "string",
            "description": "用户注册名"
        }
    }
}

class UserSource(HTTPMethodView):

    @doc.operation('UserSource_GET')
    @doc.summary("获取用户信息")
    async def get(self, _: Request, uid: int) -> HTTPResponse:
        try:
            u = await UserDB.find(uid)
        except AttributeError:
            return json({
                "msg": "未找到用户",
            }, status=404, ensure_ascii=False)
        except Exception as e:
            return json({
                "msg": "执行错误",
            }, status=500, ensure_ascii=False)
        else:
            return json(asdict(u), ensure_ascii=False)
    @doc.operation('UserSource_PUT')
    @doc.summary("更新用户信息")
    @checkjsonschema(put_query_schema)
    async def put(self, request: Request, uid: int) -> HTTPResponse:
        query_json = request.json
        try:
            validate(instance=query_json, schema=put_query_schema)
        except Exception as e:
            return json({
                "msg": "参数错误",
                "error": str(e)
            }, status=401, ensure_ascii=False)
        else:
            try:
                await UserDB.update(uid, query_json.get("name", ""))
            except AttributeError:
                return json({
                    "msg": "未找到用户",
                }, status=404, ensure_ascii=False)
            except Exception as e:
                return json({
                    "msg": "执行错误",
                }, status=500, ensure_ascii=False)
            else:
                return json({"msg": "更新成功"}, ensure_ascii=False)

    @doc.operation('UserSource_DELETE')
    @doc.summary("删除用户")
    async def delete(self, _: Request, uid: int) -> HTTPResponse:
        try:
            await UserDB.delete(uid)
        except AttributeError:
            return json({
                "msg": "未找到用户",
            }, status=404, ensure_ascii=False)
        except Exception as e:
            return json({
                "msg": "执行错误",
            }, status=500, ensure_ascii=False)
        else:
            return json({"msg": "删除成功"}, ensure_ascii=False)

Expected behavior

there should be summary,and these sections should in user tag

Environment (please complete the following information):

Additional context Add any other context about the problem here. This might be how to fix this bug or some hint to fix it.

ahopkins commented 3 years ago

I just was looking at this. Sanic supports decorators on methods:

Agreed. This should work, but does not.

class HelloView(HTTPMethodView):
    @openapi.parameter("name", str, location="query")
    async def get(self, request: Request) -> HTTPResponse:
        name = request.args.get("name", "world")
        return text(f"Hello, {name}.")

bp.add_route(HelloView.as_view(), "/")

This does work, but is not always a viable option.


class HelloView(HTTPMethodView):
    decorators = [openapi.parameter("name", str, location="query")]

    async def get(self, request: Request) -> HTTPResponse:
        ...