pyopenapi / pyswagger

An OpenAPI (fka Swagger) client & converter in python, which is type-safe, dynamic, spec-compliant.
MIT License
385 stars 89 forks source link

ValueError: Can't resolve type from:(number, int32) #127

Closed Kyria closed 7 years ago

Kyria commented 7 years ago
>>> from pyswagger import App
>>> app = App.create('https://esi.tech.ccp.is/latest/swagger.json?datasource=tranquility')
>>> app.op['get_markets_region_id_orders']( region_id=1002, type_id=34, order_type='all')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/Kyria/dev/LazyBlacksmith/env/local/lib/python2.7/site-packages/pyswagger/spec/v2_0/objects.py", line 283, in __call__
    _convert_parameter(final(p))
  File "/home/Kyria/dev/LazyBlacksmith/env/local/lib/python2.7/site-packages/pyswagger/spec/v2_0/objects.py", line 264, in _convert_parameter
    c = p._prim_(v, self._prim_factory, ctx=dict(read=False))
  File "/home/Kyria/dev/LazyBlacksmith/env/local/lib/python2.7/site-packages/pyswagger/spec/v2_0/objects.py", line 184, in _prim_
    return prim_factory.produce(self.schema, v, ctx) if i == 'body' else prim_factory.produce(self, v, ctx)
  File "/home/Kyria/dev/LazyBlacksmith/env/local/lib/python2.7/site-packages/pyswagger/primitives/__init__.py", line 178, in produce
    raise ValueError('Can\'t resolve type from:(' + str(obj.type) + ', ' + str(obj.format) + ')')
ValueError: Can't resolve type from:(number, int32)

>>> app.op['get_markets_structures_structure_id'](structure_id=12345)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/Kyria/dev/LazyBlacksmith/env/local/lib/python2.7/site-packages/pyswagger/spec/v2_0/objects.py", line 283, in __call__
    _convert_parameter(final(p))
  File "/home/Kyria/dev/LazyBlacksmith/env/local/lib/python2.7/site-packages/pyswagger/spec/v2_0/objects.py", line 264, in _convert_parameter
    c = p._prim_(v, self._prim_factory, ctx=dict(read=False))
  File "/home/Kyria/dev/LazyBlacksmith/env/local/lib/python2.7/site-packages/pyswagger/spec/v2_0/objects.py", line 184, in _prim_
    return prim_factory.produce(self.schema, v, ctx) if i == 'body' else prim_factory.produce(self, v, ctx)
  File "/home/Kyria/dev/LazyBlacksmith/env/local/lib/python2.7/site-packages/pyswagger/primitives/__init__.py", line 178, in produce
    raise ValueError('Can\'t resolve type from:(' + str(obj.type) + ', ' + str(obj.format) + ')')
ValueError: Can't resolve type from:(number, int32)

Link the the swagger.json used: https://esi.tech.ccp.is/latest/swagger.json?datasource=tranquility

It looks like every endpoints that have this issue use a referenced parameters which is a "number" / "int32" format.

    "page": {
      "default": 1,
      "description": "Which page of results to return",
      "format": "int32",
      "in": "query",
      "name": "page",
      "type": "number"
    },

If I understand correctly types defined in the JSON-Schema Draft 4. Models, used by swagger,

   integer  A JSON number without a fraction or exponent part.
   number  Any JSON number.  Number includes integer.

number also contains integer by definition, so integer/int32 should work (which is not the case currently). (also swagger validator do not report any issues with number/int32 and number/int64. )

Can you correct this ?

Thanks in advance !

mission-liao commented 7 years ago

You can extend the supported primitives by following this tutorial. If it doesn't work or not fit your needs, please feel free to let me know.

Kyria commented 7 years ago

Is that the correct way to do it ?

>>> from pyswagger import App
>>> from pyswagger.primitives import Primitive
>>> from pyswagger.primitives._int import create_int, validate_int
>>> factory = Primitive()
>>> factory.register('number', 'int64', create_int, validate_int)
>>> factory.register('number', 'int32', create_int, validate_int)
>>> app = App.load('https://esi.tech.ccp.is/latest/swagger.json?datasource=tranquility', prim=factory)
>>> app.prepare(True)
>>> app.op['get_markets_structures_structure_id'](structure_id=12345)
(<pyswagger.io.Request object at 0x700383b36c10>, <pyswagger.io.Response object at 0x700382d9fc50>)

FYI: your doc is missing app.prepare(strict=True) at the end (just in case people don't know that)


About the issue itself: i don't mind having to implement myself primitive builder for custom types. But I still feel sad that pyswagger do not support swagger defined primitives itself, because they are a bit edgy (number/int32 and number/int64 which are defined in the spec by the fact that number includes integer).

Imho, you should support these: because swagger-codegen didn't throw any errors with that type/format, neither does other libs like bravado for example... and swagger validators do not yell at these either.

But well, it's up to you at the end. At least I have a solution for this :)

Thanks !

mission-liao commented 7 years ago

@Kyria Really? Do swagger 2.0 support number/int32 and number/int64?

The primitive part of pyswagger is based on here and both of them are not listed on that table.

But if swagger-codegen support them, I think I can add them to pyswagger now. Thanks for point this out.

Kyria commented 7 years ago

Do you know this one is outdated https://github.com/OAI/OpenAPI-Specification/blob/OpenAPI.next/versions/2.0.md#data-types ? (no update since early 2016, and https://swagger.io/specification/ redirects to https://github.com/OAI/OpenAPI-Specification/blob/master//versions/2.0.md ) (branch master)

To quote the spec (from swagger.io):

Primitive data types in the Swagger Specification are based on the types supported by the JSON-Schema Draft 4. Models are described using the Schema Object which is a subset of JSON Schema Draft 4.

And if you follow the JSON-Schema Draft 4 link, you get what I quoted before :

integer A JSON number without a fraction or exponent part. number Any JSON number. Number includes integer.

FYI, from my test, this is how is defined the "number/int32" using swagger-codegen in the API class comments:

        :param float page: Which page of results to return

But tbh, since there are no validation in the client, I don't believe that having it as float is the right solution (especially since it's an int32, it's supposed to be an integer, not a float). Also testing the endpoint with a float (the server have a validator integrated) it returns an error because it waits for an integer (number/int32) and not a float (number/float).

Hopes this helps / explains better everything :)

And thanks for your work ! :+1:

mission-liao commented 7 years ago

number/int32, number/int64 are added as supported primitives in v0.8.31