connexion icon indicating copy to clipboard operation
connexion copied to clipboard

Schema using `allOf`, `anyOf`, `oneOf` in parameter or form data raises `KeyError: type` during validation

Open sisp opened this issue 3 years ago • 3 comments

Description

Currently, query parameters with a merged schema via allOf are not supported. A merged schema is sometimes useful for extending a base schema with additional constraints, and it's a legal schema declaration, so Connexion should support it.

Expected behaviour

It should be possible to use a merged schema via allOf for declaring the schema of a query parameter.

Actual behaviour

When a merged schema via allOf is used, a KeyError: type is raised:

https://github.com/spec-first/connexion/blob/64f42547dc76316c09d53ce57484beb03206a38a/connexion/decorators/uri_parsing.py#L114

This happens because with a merged schema via allOf there is no type key in the schema field directly, instead the child schemas of allOf must be parsed.

Steps to reproduce

I've created two test cases that both fail because of a raised KeyError: type:

  1. # tests/fixtures/simple/openapi.yaml
    
    /test_query_param_allof:
      get:
        operationId: fakeapi.hello.test_query_param_allof
        parameters:
          - name: 'n'
            in: query
            required: true
            schema:
              allOf:
                - type: number
                - type: number
                  minimum: 0
        responses:
          '200':
            description: OK
    
    # tests/api/test_parameters.py
    
    def test_query_param_allof(simple_app):
        app_client = simple_app.app.test_client()
    
        url = "/v1.0/test_query_param_allof"
        response = app_client.get(url)
        assert response.status_code == 400
    
        response = app_client.get(url, query_string={"n": "1.23"})
        assert response.status_code == 200
    
  2. # tests/fixtures/simple/openapi.yaml
    
    /test_array_multi_query_param_allof:
      get:
        operationId: fakeapi.hello.test_array_multi_query_param_allof
        parameters:
          - name: items
            in: query
            description: An comma separated array of items
            style: form
            explode: true
            schema:
              allOf:
                - type: array
                  default: ["squash", "banana"]
                  items:
                    type: string
                - type: array
                  maxItems: 10
        responses:
          '200':
            description: OK
    
    # tests/api/test_parameters.py
    
    def test_array_query_param(simple_app):
        app_client = simple_app.app.test_client()
    
        # ...
    
        url = "/v1.0/test_array_multi_query_param_allof?items=A&items=B&items=C&items=D,E,F"
        response = app_client.get(url, headers=headers)
        array_response: List[str] = json.loads(
            response.data.decode("utf-8", "replace")
        )  # multi array with csv format
        assert array_response == ["A", "B", "C", "D", "E", "F"]
    
        # ...
    

Additional info:

Output of the commands:

  • python --version: Python 3.9.7
  • pip show connexion | grep "^Version\:": Version: -2020.0.dev1-

sisp avatar Jul 24 '22 15:07 sisp

Hi @sisp,

This is a known issue (see #1409, #1263, #1300, #1013, #1231) and is caused by some custom validation code. I'd like to tackle this as part of the validation refactoring for connexion 3.X (#1525).

Let me close those other issues and rephrase this one slightly to cover the general issue, as I appreciate the effort you put into this report.

RobbeSneyders avatar Aug 23 '22 07:08 RobbeSneyders

Sounds good, @RobbeSneyders. Thanks!

sisp avatar Aug 23 '22 07:08 sisp

Version

2.14.2

Description

Hey @RobbeSneyders. I discovered this open issue after running into the same issue.The offending line of code is this in "url_parsing.py":

if defn and defn["type"] == "array":
        form_data[k] = self._split(form_data[k], encoding, "form")

Steps to reproduce

It's simple to reproduce - same as @sisp mentioned: when I use anyOf in the OpenAPI 3.0 schema, a "KeyError: 'type'" is raised at the line I quoted above.

I tried to debug this issue a bit myself by printing out defn to see what key it has.

Here's an example defn object when anyOf is used. Notice that the type dictionary key is wrapped inside each element of the anyOf JSON value array, and does not exist at root level of defn.

# json.dumps(defn, indent=2)
{
  "anyOf": [
    {
      "properties": {
      # ......
      },
      "title": "AWS S3",
      "type": "object",
    },
    # ......
    {
      "properties": {
      # ......
      },
      "title": "GCP Cloud Storage",
      "type": "object",
    }
  ],
  "title": "data_source"
}

Question

Since this issue has been open for almost a year, I wonder is there plan to support anyof and the other aggregate functions in form data parsing any time soon? If not, do you see any immediate workaround or hotfix other than asking users to not use these aggregation keywords? Thanks!

renxinhe avatar Jun 02 '23 07:06 renxinhe