Search code examples
pythonswaggerswagger-2.0openapisanic

Sanic OpenAPI Swagger docs generation throws attribute paths operationId is repeated


I am using sanic to build a simple API. I have two end points defined:

from sanic import Blueprint, response
from sanic_openapi import doc

bp = Blueprint('default', url_prefix="", version=2, strict_slashes=True)


@bp.get("")
@doc.summary('Default GET endpoint for API v2')
async def route_get(request):
    resp = {"message": "New API - GET", "version": 2.0}
    return response.json(resp)


@bp.post("")
@doc.summary('Default POST endpoint for API v2')
async def route_post(request):
    resp = {"message": "New API - POST", "version": 2.0}
    return response.json(resp)

The generated swagger documents fails validation, with the following message

Error:

"attribute paths.'/v2/'(post).operationId is repeated",

I am expecting to have several routes that have multiple HTTP verbs going to the same path:

GET /v2/product
POST /v2/product 

DELETE /v2/product/{id}
PUT /v2/product/{id}

I've tested with these endpoints too, and I get two errors about operationId being repeated. One is for the /v2/product path and one is for /v2/product/{id}

How can I solve this error?


Solution

  • Something like the below code can help , So that you can use the same path for POST,GET,PUT,DELETE Separately and write your own logic.

    I have Modified your Code a Little Bit , Here below is the code :

    from sanic import Blueprint, response
    from sanic_openapi import doc
    
    bp = Blueprint('default', url_prefix="", version=2, strict_slashes=True)
    
    @bp.get("/<product_id:int>", strict_slashes=True)
    @doc.summary('Default GET endpoint for API v2')
    async def route_get(request):
        resp = {"message": "New API - GET", "version": 2.0}
        return response.json(resp)
    
    
    @bp.put("/<product_id:int>", strict_slashes=True)
    @doc.summary('Default POST endpoint for API v2')
    async def route_post(request):
        resp = {"message": "New API - POST", "version": 2.0}
        return response.json(resp)
    

    If you have to use the Model Object with product , You can use something like the below, I have Created a full fledged example for your reference.

    Folder Structure :

     Project
        blueprints
            product.py
        data.py
        main.py
        models.py
    

    product.py

    from sanic.blueprints import Blueprint
    from sanic.response import json
    from sanic_openapi import doc
    
    from models import Product
    from data import test_product
    
    
    blueprint = Blueprint('Products', '/products')
    
    @blueprint.get("/<product_id:int>", strict_slashes=True)
    @doc.summary("Fetches a product with product Id")
    def route_get(request, product_id):
        resp = {"message": "New API - GET", "version": 2.0}
        return json(resp)
    
    
    @blueprint.put("/<product_id:int>", strict_slashes=True)
    @doc.summary("Updates a Product with the Updated product Details from the contends from Body")
    @doc.consumes(Product, location='body')
    def route_post(request, product_id):
        resp = {"message": "New API - POST", "version": 2.0}
        return json(resp)
    

    data.py

    from models import Product
    import datetime
    
    test_product = Product()
    
    test_product = {
        'id': 1,
        'name': 'Gross'
    }
    

    main.py

    from sanic import Sanic
    
    from sanic_openapi import swagger_blueprint
    from blueprints.product import blueprint as product_blueprint
    
    app = Sanic()
    
    app.blueprint(product_blueprint)
    
    app.config.API_VERSION = '1.0.0'
    app.config.API_TITLE = 'Product API'
    
    app.run(host="0.0.0.0", debug=True)
    

    models.py

    from sanic_openapi import doc
    
    class Product:
        id = int
        name = str
    

    Now running python main.py

    Console Output :

    [2019-11-22 16:20:53 +0530] [15744] [INFO] Goin' Fast @ http://0.0.0.0:8000
    [2019-11-22 16:20:53 +0530] [15744] [WARNING] Sanic tried to use loop.add_signal_handler but it is not implemented on this platform.
    [2019-11-22 16:20:53 +0530] [15744] [WARNING] Sanic tried to use loop.add_signal_handler but it is not implemented on this platform.
    [2019-11-22 16:20:53 +0530] [15744] [INFO] Starting worker [15744]
    

    Accessing Swagger UI with API :

    If You have to use a Class based view Demonstration , Here below are the two files which can help.

    Project Structure
     Project
         main.py
         blueprint.py
    

    main.py

    from sanic_openapi import doc
    from sanic_openapi import swagger_blueprint
    from sanic import Sanic
    from sanic.response import json
    
    from blueprint import blueprint
    
    app = Sanic()
    
    app.blueprint(swagger_blueprint)
    app.blueprint(blueprint)
    
    
    @app.get("/product", strict_slashes=True)
    @doc.summary("Displays the Product Details ")
    async def get_product(request):
        return json({})
    
    @app.post("/product", strict_slashes=True)
    @doc.summary("Creates a product in Repositary")
    @doc.consumes({"product": {"name": str}}, location="body")
    async def create_product(request):
        return json({})
    
    
    app.config.API_VERSION = 'pre-alpha'
    app.config.API_TITLE = 'Display Products Demonstration API'
    
    app.run(host="0.0.0.0", debug=True)
    

    blueprint.py

    from sanic.blueprints import Blueprint
    from sanic.views import HTTPMethodView
    from sanic_openapi import doc
    from sanic.response import json
    
    blueprint = Blueprint('Class-based View', url_prefix='/class-based-view')
    class MyView(HTTPMethodView):
        @doc.summary("Get my view")
        def get(self, request):
            return json({"method": "GET"})
    
        @doc.summary("Post my view")
        @doc.consumes({"view": {"name": str}}, location="body")
        def post(self, request):
            return json({"method": "POST"})
    
    
    blueprint.add_route(MyView.as_view(), '/view', strict_slashes=True)
    

    Now run python main.py

    [2019-11-22 16:50:41 +0530] [12212] [INFO] Goin' Fast @ http://0.0.0.0:8000
    [2019-11-22 16:50:41 +0530] [12212] [WARNING] Sanic tried to use loop.add_signal_handler but it is not implemented on this platform.
    [2019-11-22 16:50:41 +0530] [12212] [WARNING] Sanic tried to use loop.add_signal_handler but it is not implemented on this platform.
    [2019-11-22 16:50:41 +0530] [12212] [INFO] Starting worker [12212]
    

    You can now see the SWAGGER UI at http://localhost:8000/swagger/