Description
Problem description
When creating a Connexion app with an OpenAPI spec which uses $ref
to reference the request body schema, requesting /openapi.json
leads to an error after the endpoint which uses $ref
in the specification has been called.
Minimal example
- Install
connexion["swagger-ui"]
(anduvicorn
for serving), and paste the files below. - Run the app with
uvicorn run:app
- Navigate to Swagger UI: http://localhost:8000/ui and confirm that everything looks good
- Call the
/greeting
endpoint - Reload Swagger UI, which leads to an error (see below)
Contents of openapi.yaml:
openapi: "3.0.0"
info:
title: Greeting application
version: 0.0.1
paths:
/greeting:
post:
operationId: run.post_greeting
responses:
'200':
description: "Greeting response"
content:
text/plain:
schema:
type: string
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/Test'
components:
schemas:
Test:
type: object
properties:
name:
type: string
description: A name
Contents of run.py:
from connexion import AsyncApp
app = AsyncApp(__name__)
def post_greeting(body):
return f"Hello {body['name']}", 200
app.add_api("openapi.yaml")
Error message
connexion.exceptions.InvalidSpecification: {'content': {'application/json': {'schema': {'type': 'object', 'properties': {'name': {'type': 'string', 'description': 'A name'}}, 'components': {'schemas': {'Test': {'type': 'object', 'properties': {'name': {'type': 'string', 'description': 'A name'}}}}}}}}} is not valid under any of the given schemas
Failed validating 'oneOf' in schema['properties']['paths']['patternProperties']['^\\/']['patternProperties']['^(get|put|post|delete|options|head|patch|trace)$']['properties']['requestBody']:
{'oneOf': [{'$ref': '#/definitions/RequestBody'},
{'$ref': '#/definitions/Reference'}]}
On instance['paths']['/greeting']['post']['requestBody']:
{'content': {'application/json': {'schema': {'type': 'object',
'properties': {'name': {'type': 'string',
'description': 'A '
'name'}},
'components': {'schemas': {'Test': {'type': 'object',
'properties': {'name': {'type': 'string',
'description': 'A '
'name'}}}}}}}}}
More details
The issue seems to be that #2002 returns the processed OpenAPI spec, which contains a components
key inside the schema
, so that the OpenAPI schema validator complains (correctly) about an invalid spec and /openapi.json
refuses to deliver the spec. In fact, some testing shows that reverting bb48fb3 fixes the issue.
I'm not really sure but this might be related to the discussion in #1829.
Sample test
Something like the following might work as an integration test with the openapi.yaml
from above (I used this for finding bb48fb3; maybe this helps):
from connexion import AsyncApp
from os import path
def post_greeting(body):
return f"Hello {body['name']}", 200
def test_esoteric_error():
app = AsyncApp(__name__)
app.add_api(path.join(path.dirname(__file__), "fixtures/simple/esoteric-error.yaml"))
client = app.test_client()
response = client.get("/openapi.json")
assert response.status_code == 200
response = client.post("/greeting", data={"name": "XYZ"}, headers={"Content-Type": "application/json"})
# assert response.status_code == 200 <-- returns Bad Request for some reason I didn't investigate; however it's sufficient to reproduce the error
response = client.get("/openapi.json")
assert response.status_code == 200