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

How do I specify a parameter that is a string enum type? #141

Closed MartinDelVecchio closed 6 years ago

MartinDelVecchio commented 7 years ago

I currently have a parameter 'cls' that is a string. I am able to execute an operation like this:

response = self.client.request (self.app.op['operation-id'](cls='System'))

And it works. I then added an enum type for the class:

 AlarmClass:
   type: string
   enum: &AlarmClassEnum
   - Network
   - Service
   - Storage
   - System

And change the parameter to use that type:

     parameters:
     - name: cls
       description:  Alarm class
       in: query
       schema:
         $ref: '#/definitions/AlarmClass'

Now when I try to execute the operation, I get an error from pyswagger:

smoky/api.py:140: in execute_operation_by_id
    response = self.client.request (self.app.op[operation_id](**kwargs), opt=opt, headers=headers)
/usr/lib/python2.7/site-packages/pyswagger/spec/v2_0/objects.py:283: in __call__
    _convert_parameter(final(p))
/usr/lib/python2.7/site-packages/pyswagger/spec/v2_0/objects.py:264: in _convert_parameter
    c = p._prim_(v, self._prim_factory, ctx=dict(read=False))
/usr/lib/python2.7/site-packages/pyswagger/spec/v2_0/objects.py:184: in _prim_
    return prim_factory.produce(self.schema, v, ctx) if i == 'body' else prim_factory.produce(self, v, ctx)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <pyswagger.primitives.Primitive object at 0x6fffd7f7990>, obj = <pyswagger.spec.v2_0.objects.Parameter object at 0x6fffd75d190>, val = 'System'
ctx = {'2nd_pass': None, 'addp': False, 'addp_schema': None, 'factory': <pyswagger.primitives.Primitive object at 0x6fffd7f7990>, ...}

    def produce(self, obj, val, ctx=None):
        """ factory function to create primitives

            :param pyswagger.spec.v2_0.objects.Schema obj: spec to construct primitives
            :param val: value to construct primitives

            :return: the created primitive
            """
        val = obj.default if val == None else val
        if val == None:
            return None

        obj = deref(obj)
        ctx = {} if ctx == None else ctx
        if 'name' not in ctx and hasattr(obj, 'name'):
            ctx['name'] = obj.name
        if 'guard' not in ctx:
            ctx['guard'] = CycleGuard()
        if 'addp_schema' not in ctx:
            # Schema Object of additionalProperties
            ctx['addp_schema'] = None
        if 'addp' not in ctx:
            # additionalProperties
            ctx['addp'] = False
        if '2nd_pass' not in ctx:
            # 2nd pass processing function
            ctx['2nd_pass'] = None
        if 'factory' not in ctx:
            # primitive factory
            ctx['factory'] = self
        if 'read' not in ctx:
            # default is in 'read' context
            ctx['read'] = True

        # cycle guard
        ctx['guard'].update(obj)

        ret = None
        if obj.type:
            creater, _2nd = self.get(_type=obj.type, _format=obj.format)
            if not creater:
                raise ValueError('Can\'t resolve type from:(' + str(obj.type) + ', ' + str(obj.format) + ')')

            ret = creater(obj, val, ctx)
            if _2nd:
                val = _2nd(obj, ret, val, ctx)
                ctx['2nd_pass'] = _2nd
>       elif len(obj.properties) or obj.additionalProperties:
E       AttributeError: 'Parameter' object has no attribute 'properties'

I am assuming that since the 'cls' parameter's underlying type is still a string, I should be able to specify it with cls='System'.

What am I missing?

Thanks.

mission-liao commented 7 years ago

@MartinDelVecchio Sorry for late reply, the first thing I noticed is the way you wrote your parameter definition:

 parameters:
     - name: cls
       description:  Alarm class
       in: query
       schema:
         $ref: '#/definitions/AlarmClass'

The Parameter.schema can only be used when in equals body. You could rewrite your spec in this way and try again.

 parameters:
     - name: cls
       description:  Alarm class
       in: query
       type: string
       enum:
          - Network
          - Service
          - Storage
          - System

Refer to here for details about Parameter object (it would be much consistent in OpenAPI 3.0.0) and please feel free to contact if it still goes wrong.

MartinDelVecchio commented 6 years ago

It turns out that "enum types" cannot be referred to by $ref in Swagger 2.0.

Instead, I have to use the tag. For example, this type:

 AlarmClass:
   type: string
   enum: &AlarmClassEnum
   - Network
   - Service
   - Storage
   - System

Has a tag &AlarmClassEnum. When I want to define a property of this type, I have to do this:

      class:
        type: string
        enum: *AlarmClassEnum

Switching to this makes 'class' a string, which works.