sanic-org / sanic-ext

Extended Sanic functionality
https://sanic.dev/en/plugins/sanic-ext/getting-started.html
MIT License
50 stars 36 forks source link

[Bug] Can't document/exclude websocket routes with OpenAPI decorator #232

Open aiman opened 1 year ago

aiman commented 1 year ago

Describe the bug

The sanic-ext OpenAPI decorators don't seem to have any effect with websocket handlers. When there are websocket routes in an app or a blueprint, it is not possible to document them, or even exclude them.

So the OpenAPI documentation cannot be used out of the box for documenting an API that contains at least one websocket route.

Also, if autodoc is enabled, the generated summary and description contain some unhelpful information.

Screenshots

Screenshot from 2023-10-01 14-09-27

To Reproduce

from sanic import Sanic, json
from sanic_ext import openapi

app = Sanic("Sanic")

app.ext.openapi.describe(
    "Problem",
    version="1.0",
)
# app.config.OAS_AUTODOC = False

@app.get("/foo")
async def get_foo(request):
    """Get a foo"""
    return json({"foo": "bar"})

@app.websocket("/feed")
@openapi.exclude()
async def get_feed(request, ws):
    while True:
        await ws.recv()

Expected behavior

It should be possible to at least exclude websocket routes from OpenAPI documentation, whether through the sanic_ext.openapi decorator, or an OAS configuration option, or this could be the default setting.

Environment (please complete the following information):

Additional context

Here is the generated OpenAPI JSON:

{"openapi":"3.0.3","info":{"title":"Problem","version":"1.0","contact":{}},"paths":{"/foo":{"get":{"operationId":"get~get_foo","summary":"Get a foo","responses":{"default":{"description":"OK"}}}},"/feed":{"get":{"operationId":"get~get_feed","summary":"partial(func, *args, **keywords) - new function with partial application","description":"of the given arguments and keywords.","responses":{"default":{"description":"OK"}}}}},"tags":[],"servers":[],"security":[]}

Pretty-printing the /feed endpoint:

   "/feed": {
      "get": {
        "operationId": "get~get_feed",
        "summary": "partial(func, *args, **keywords) - new function with partial application",
        "description": "of the given arguments and keywords.",
        "responses": {
          "default": {
            "description": "OK"
          }
        }
      }
    }
  }