Dorthu / openapi3

A Python3 OpenAPI 3 Spec Parser
BSD 3-Clause "New" or "Revised" License
118 stars 47 forks source link

Catching openapi3.errors.SpecError: Expected ... to be one of [['Schema', 'Reference']] #61

Closed dsuch closed 1 year ago

dsuch commented 2 years ago

Hello,

this is just a quick report that openapi3 1.5.0 fails to parse a definition that is correct. The YAML file is attached and the exception is below.

I believe this is happening because instead of an inline schema, I use a reference in the form of $ref: '#/components/schemas/zato.server.service.internal.helpers.MyUser but I have not investigated it closer.

The YAML definition is correct and it validates with several other tools.

Thank you.

zato-test.txt

Traceback (most recent call last):
  File "code/zato-cli/test/zato/test_openapi.py", line 122, in test_apispec
    client = OpenAPI(data)
  File "/home/code/lib/python3.8/site-packages/openapi3/openapi.py", line 50, in __init__
    super(OpenAPI, self).__init__([], raw_document, self)
  File "/home/code/lib/python3.8/site-packages/openapi3/object_base.py", line 67, in __init__
    self._parse_data()
  File "/home/code/lib/python3.8/site-packages/openapi3/openapi.py", line 150, in _parse_data
    self.components   = self._get('components', ['Components'])
  File "/home/code/lib/python3.8/site-packages/openapi3/object_base.py", line 221, in _get
    ret = python_type(self.path + [field], ret, self._root)
  File "/home/code/lib/python3.8/site-packages/openapi3/object_base.py", line 67, in __init__
    self._parse_data()
  File "/home/code/lib/python3.8/site-packages/openapi3/components.py", line 22, in _parse_data
    self.schemas         = self._get('schemas', ['Schema', 'Reference'], is_map=True)
  File "/home/code/lib/python3.8/site-packages/openapi3/object_base.py", line 201, in _get
    ret = Map(self.path + [field], ret, object_types, self._root)
  File "/home/code/lib/python3.8/site-packages/openapi3/object_base.py", line 536, in __init__
    dct[k] = t(path + [k], v, self._root)
  File "/home/code/lib/python3.8/site-packages/openapi3/object_base.py", line 67, in __init__
    self._parse_data()
  File "/home/code/lib/python3.8/site-packages/openapi3/schemas.py", line 51, in _parse_data
    self.properties           = self._get('properties', ['Schema', 'Reference'], is_map=True)
  File "/home/code/lib/python3.8/site-packages/openapi3/object_base.py", line 201, in _get
    ret = Map(self.path + [field], ret, object_types, self._root)
  File "/home/code/lib/python3.8/site-packages/openapi3/object_base.py", line 543, in __init__
    raise SpecError('Expected {}.{} to be one of [{}], but found {}'.format(
openapi3.errors.SpecError: Expected components.schemas.request_helpers_dataclass_service.properties.user to be one of [['Schema', 'Reference']], but found {'$ref': '#/components/schemas/zato.server.service.internal.helpers.MyUser', 'description': ''}
chriswhite199 commented 2 years ago

Your specific issue appears to be in general.py::Reference.can_parse(), specifically these lines:

        # TODO - can a reference object have spec extensions?
        cleaned_keys = [k for k in dct.keys() if not k.startswith("x-")]

        return len(cleaned_keys) == 1 and "$ref" in dct

In your sample YAML you have description fields (albeit empty strings), but these cause the len check to evaluate to false (as there are two keys - $ref and description).

If you can remove all but the $ref fields, your spec now parses.

It's not clear from the spec docs I can find whether it is legal to have a $ref field and additional fields

transfluxus commented 1 year ago

I found the same in the Spotify OpenAPI specs

Dorthu commented 1 year ago

It looks like, as of OpenAPI 3.1.0, the Reference Object was updated to include optional summary and description fields: 3.1.0 Reference Object vs 3.0.0 Reference Object definition. I must've missed that - I'll add this behavior in.