Open MartinDelVecchio opened 6 years ago
I've done something similar to what you described (refer to here). You can also refer to here if you need some detail to implement something similar on your own.
BTW, You can access each parameter by calling App.resolve
and it structure is just following Swagger 2.0 definition. However, the definition of parameter
would change when pyswagger upgrading its internal OpenAPI spec version to 3.0.0. So it's not recommended to design a tool based on parameters
structure on your own.
Instead, a better way is defining a common interface to describe what you need, and re-implement the interface when upgrading OAI spec version. Therefore, if you have some thought about designing a tool to render requests automatically based on API spec from service provider, please feel to share your idea here.
Looking at this code:
# assume you have an Operation to test
input_ = renderer.render_all(
app.s('user').post # the Operation
)
# this generated input could be passed to Operation.__call__,
# to get a pair of (Request, Response), or just
# pass them to client
resp = client.request(app.s('user').post(**input_))
I figured out from the source that renderer.render_all() expects a pyswagger Operation object, which I have for each operation I am trying to execute. So my code attempts to render a default set of parameters with:
rendered = renderer.render_all (op)
This works for most operations, but not all. When it does not work, I get an error like this:
15:02:14 Error rendering api 'config' operation 'get-snapshot-by-time' parameters: Traceback (most recent call last):
File "/cygdrive/c/smoker/cgp/automation/src/python/smoky/smoky/cgp.py", line 464, in conjure_required_parameters
rendered = renderer.render_all (op)
File "/usr/lib/python2.7/site-packages/pyswagger/primitives/render.py", line 309, in render_all
out.update({p.name: self.render(p, opt=opt)})
File "/usr/lib/python2.7/site-packages/pyswagger/primitives/render.py", line 277, in render
return self._generate(obj, opt=opt)
File "/usr/lib/python2.7/site-packages/pyswagger/primitives/render.py", line 219, in _generate
raise Exception('Unable to locate generator: {0}'.format(obj))
Exception: Unable to locate generator: <pyswagger.spec.v2_0.objects.Parameter object at 0x6fffbb1cf10>
In this example, the operation has two parameters, both required, and both in the path:
paths:
/status/cfg/snapshot/{domain}/time/{timestamp}:
get:
summary: Fetch the config snapshot specified by the time stamp
description: Fetch the config snapshot specified by the time stamp
operationId: get-snapshot-by-time
tags:
- Cfg
parameters:
- name: domain
in: path
type: string
required: true
description: Configuration domain (complete, system, etc.)
- name: timestamp
description: The time stamp (seconds since UTC epoch)
in: path
required: true
type: integer
In another case, I get this error:
File "/cygdrive/c/smoker/cgp/automation/src/python/smoky/smoky/cgp.py", line 464, in conjure_required_parameters
rendered = renderer.render_all (op)
File "/usr/lib/python2.7/site-packages/pyswagger/primitives/render.py", line 309, in render_all
out.update({p.name: self.render(p, opt=opt)})
File "/usr/lib/python2.7/site-packages/pyswagger/primitives/render.py", line 277, in render
return self._generate(obj, opt=opt)
File "/usr/lib/python2.7/site-packages/pyswagger/primitives/render.py", line 219, in _generate
raise Exception('Unable to locate generator: {0}'.format(obj))
Exception: Unable to locate generator: <pyswagger.spec.v2_0.objects.Parameter object at 0x6fff9dd7810>
From a simpler operation:
paths:
/:
get:
summary: Get the alarm
description: Get the system alarms
operationId: get
tags:
- Alarms
parameters:
- name: page
description: the page of results to get
in: query
type: integer
default: 1
- name: page_size
description: the number of items for this page
in: query
type: integer
default: 25
Can you tell what is going on here?
Thanks.
@MartinDelVecchio It's because the page_size
parameter has type: integer
but without format
. There is a bug report and I didn't fix renderer part.
You can refer to here for detail and I would submit another fix after work tonight.
Ah yes, that was my bug!
I will work around it by adding format to my Swaggers. Then I can test your fix when it is ready.
Thanks.
@MartinDelVecchio I just submit a PR to address this issue, would be released once I finished Windows Support reported in another issue.
I am now getting an error when I use this option:
# Generate all properties
opt['max_property'] = True
It seems to want to generate values for read-only parameters, such as:
serialNumber:
type: string
title: Serial Number
description: The issuer of the certificates serial number
readOnly: true
Is there a way to render all properties, except those marked with "readOnly: true"?
Thanks.
@MartinDelVecchio readOnly
is not handled in pyswagger
, would submit another PR for this.
I am also seeing problems with integer values, in which the selected random value does not meet the min/max criteria in the Swagger.
And it doesn't look like the default() map includes any limits on integer values.
And finally, I can't seem to have any influence on the values specified for nested-object parameters.
For example, if I have a simple parameter 'ServiceName', I am able to specify it by setting its value in the opt dictionary:
parameter_template['ServiceName'] = 'MyServiceName'
But if my API has an object parameter 'MyObject', and that object has a property called 'ServiceName', it does not get my desired 'MyServiceName' setting. Nor does it work when I try to specify it this way:
parameter_template['MyObject/ServiceName'] = 'MyServiceName'
parameter_template['MyObject.ServiceName'] = 'MyServiceName'
parameter_template['MyObject#ServiceName'] = 'MyServiceName'
@MartinDelVecchio The renderer I wrote didn't handle this case, reason:
opt
in our case)It's still can be fixed to accept something described above (ex. parameter_template['MyObject/ServiceName'] = 'MyServiceName'
). However, I'm spending my time to upgrade pyswagger to support OpenAPI 3.0.0 and have no time on this part. It's would be very helpful if you can submit a PR or to design a spec/use case/usage for a more flexible parameter render
OK, I understand. I was just hoping that I was missing something.
I will add a post-rendering step, in which I refine the object based on my knowledge of what should be there and what shouldn't.
Thanks.
To date, I have been using pyswagger to execute API operations where I know the parameters that are specified, their types, which are required, etc. For example:
But now I want to programatically and generically generate these parameters, based on the definitions in the Swagger spec. And since Pyswagger has already interpreted the Swagger, I don't want to have to do it myself.
So in the above example, I would like to fetch the operation by ID:
op = self.op['addEmployee']
Then find somewhere in this operation object the guides needed for me to create the employee object. I would need to know that this operation takes one parameter, called 'employee', which is required. And that that object has two required parameters, both strings, called 'firstName' and 'lastName'. And also an optional age parameter, which is an integer, with an allowed range of 0 to 128. And also an optional group parameter, a string property with a default of 'none'.
With this information, I could build an employee object, which I could then pass to the operation execution:
Does pyswagger have any facility like this?
Thanks.