Qedrix
Qedrix

Reputation: 473

Unable to run Connexion 3 /w Flask - Middleware loads swagger twice

I have created a openapi.yml file and initiated a connexion app and started the app without any errors.

My directory structure is as follows:

/my_app
    /app
        __init__.py
        /api
            __init__.py
            api.py  # My API endpoint implementations
        /models
            __init__.py
            models.py  # My SQLAlchemy models
        swagger.yml  # OpenAPI specification
    .env
    config.py  # Configuration settings
    main.py  # Entry point for the application

I am using the following dependencies:

connexion[swagger-ui, uvicorn, flask]==3.0.5
annotated-types==0.6.0
anyio==3.7.1
autopep8==2.0.4
blinker==1.7.0
click==8.1.7
connexion[swagger-ui, uvicorn, flask]==3.0.5
Flask==3.0.1
flask-sqlacodegen==2.0.0
Flask-SQLAlchemy==3.1.1
gunicorn==21.2.0
h11==0.14.0
idna==3.6
inflect==7.0.0
itsdangerous==2.1.2
Jinja2==3.1.3
MarkupSafe==2.1.4
mysqlclient==2.2.1
packaging==23.2
pycodestyle==2.11.1
pydantic_core==2.16.1
pydantic==2.6.0
PyMySQL==1.1.0
python-dotenv==1.0.1
sniffio==1.3.0
SQLAlchemy==2.0.25
SQLAlchemy-serializer==1.4.1
starlette==0.32.0.post1
typing_extensions==4.9.0
uvicorn==0.27.1
Werkzeug==3.0.1

Inside the app/_init_.py I have defined the following function:

def create_app(config_class=Config, **kwargs):
    
    load_dotenv()

    # Create the Connexion application
    connex_app = connexion.FlaskApp(__name__, specification_dir='./')
    
    connex_app.add_api("swagger.yml") #, base_path='/api/v1', arguments={'title': 'API v1'}, strict_validation=False, validate_responses=False, pythonic_params=True, name='api_v1_admin')

    application = connex_app.app

    # Load configuration from .env file
    application.config.from_object(config_class)
    application.config.update(kwargs)

    # initialize db connection
    from app.models import init_app
    init_app(application)
    
    return connex_app

From the main.py I am calling the following:

if __name__ == "__main__":
    app = create_app()
    app.run(host="0.0.0.0", port=8080)

The app starts without any error and the logs suggest that the app loaded the openapi file correctly.

INFO:     Started server process [17447]
INFO:     Waiting for application startup.
DEBUG:connexion.middleware.abstract:Adding /api/v1/agent...
DEBUG:connexion.operations.openapi3:consumes: ['application/json']
DEBUG:connexion.operations.openapi3:produces: ['application/json']
DEBUG:connexion.operations.openapi3:consumes: ['application/json']
DEBUG:connexion.operations.openapi3:produces: ['application/json']
DEBUG:connexion.middleware.abstract:Adding /api/v1/agent/{agentId}...
DEBUG:connexion.operations.openapi3:consumes: []
DEBUG:connexion.operations.openapi3:produces: ['application/json']
DEBUG:connexion.operations.openapi3:consumes: ['application/json']
DEBUG:connexion.operations.openapi3:produces: ['application/json']
DEBUG:connexion.operations.openapi3:consumes: ['application/json']
DEBUG:connexion.operations.openapi3:produces: ['application/json']
DEBUG:connexion.operations.openapi3:consumes: []
DEBUG:connexion.operations.openapi3:produces: ['application/json', 'application/xml']
DEBUG:connexion.operations.openapi3:consumes: []
DEBUG:connexion.operations.openapi3:produces: ['application/json']
DEBUG:connexion.operations.openapi3:consumes: []
DEBUG:connexion.operations.openapi3:produces: ['application/json']
DEBUG:connexion.operations.openapi3:consumes: ['application/json']
DEBUG:connexion.operations.openapi3:produces: ['application/json']
DEBUG:connexion.operations.openapi3:consumes: ['application/json']
DEBUG:connexion.operations.openapi3:produces: ['application/json']
DEBUG:connexion.operations.openapi3:consumes: []
DEBUG:connexion.operations.openapi3:produces: ['application/json', 'application/xml']
DEBUG:connexion.operations.openapi3:consumes: []
DEBUG:connexion.operations.openapi3:produces: ['application/json']
DEBUG:connexion.operations.openapi3:consumes: []
DEBUG:connexion.operations.openapi3:produces: ['application/json']
DEBUG:connexion.middleware.validation:Strict Request Validation: None
DEBUG:connexion.operations.openapi3:consumes: ['application/json']
DEBUG:connexion.operations.openapi3:produces: ['application/json']
DEBUG:connexion.operations.openapi3:consumes: ['application/json']
DEBUG:connexion.operations.openapi3:produces: ['application/json']
DEBUG:connexion.operations.openapi3:consumes: []
DEBUG:connexion.operations.openapi3:produces: ['application/json', 'application/xml']
DEBUG:connexion.operations.openapi3:consumes: []
DEBUG:connexion.operations.openapi3:produces: ['application/json']
DEBUG:connexion.operations.openapi3:consumes: []
DEBUG:connexion.operations.openapi3:produces: ['application/json']
DEBUG:connexion.operations.openapi3:consumes: ['application/json']
DEBUG:connexion.operations.openapi3:produces: ['application/json']
DEBUG:connexion.middleware.security:... Security: [{'admin_auth': ['write:agent', 'read:agent']}]
INFO:     ASGI 'lifespan' protocol appears unsupported.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://127.0.0.1:8080 (Press CTRL+C to quit)

I have verified that my openapi yml file is syntactically correct.

The issue occurs when I try to call an api like so: http://localhost:8080/api/v1/agent/1

Connexion middleware tries to register the openapi endpoints again. Its evident from the logs below:

DEBUG:connexion.middleware.abstract:Adding /api/v1/agent...
DEBUG:connexion.operations.openapi3:consumes: ['application/json']
DEBUG:connexion.operations.openapi3:produces: ['application/json']
DEBUG:connexion.operations.openapi3:consumes: ['application/json']
DEBUG:connexion.operations.openapi3:produces: ['application/json']
DEBUG:connexion.middleware.abstract:Adding /api/v1/agent/{agentId}...
DEBUG:connexion.operations.openapi3:consumes: []
DEBUG:connexion.operations.openapi3:produces: ['application/json']
ERROR:    Exception in ASGI application
Traceback (most recent call last):
  File "xxx/venv/lib/python3.11/site-packages/uvicorn/protocols/http/httptools_impl.py", line 419, in run_asgi
    result = await app(  # type: ignore[func-returns-value]
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "xxx/venv/lib/python3.11/site-packages/uvicorn/middleware/proxy_headers.py", line 84, in __call__
    return await self.app(scope, receive, send)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "xxx/venv/lib/python3.11/site-packages/connexion/middleware/main.py", line 497, in __call__
    self.app, self.middleware_stack = self._build_middleware_stack()
                                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "xxx/venv/lib/python3.11/site-packages/connexion/middleware/main.py", line 338, in _build_middleware_stack
    app.add_api(
  File "xxx/venv/lib/python3.11/site-packages/connexion/apps/flask.py", line 141, in add_api
    self.app.register_blueprint(api.blueprint)
  File "xxx/venv/lib/python3.11/site-packages/flask/sansio/scaffold.py", line 45, in wrapper_func
    return f(self, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^
  File "xxx/venv/lib/python3.11/site-packages/flask/sansio/app.py", line 599, in register_blueprint
    blueprint.register(self, options)
  File "xxx/venv/lib/python3.11/site-packages/flask/sansio/blueprints.py", line 310, in register
    raise ValueError(
ValueError: The name '/api/v1' is already registered for a different blueprint. Use 'name=' to provide a unique name.
INFO:     127.0.0.1:51609 - "GET /api/v1/agent/1 HTTP/1.1" 500 Internal Server Error

Can anyone suggest why this is happening? Why connexion tries adding openapi's endpoints again?

Note: If I manually set routes, the project is working fine.

Upvotes: 1

Views: 337

Answers (0)

Related Questions