Skip to content

openapi.json cannot be delivered after calling an endpoint with a $ref request body #2029

Open
@bene1618

Description

@bene1618

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"] (and uvicorn 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions