Early phase. Manually tested against various swagger files, incl. the petstore demo API.
See the examples folder - the openapi test script runs them all.
Note: In the recording, the 'swagger' keyword was not required before the url. Meanwhile it is, see:
- On CLI: vpe <swagger|openapi>
- VI: After potential pre-parametrization, hit the hotkey on a line with "swagger " =>
- All RPCs are listed, with references to parameter definitions (components).
- Path parameters are extracted and configured globally on module level (e.g.
petId
in the example). - Definitions are wrapped into a
class Defs
- API Methods as top level classes, referencing definitions.
- Tools for actual sending the requests within
class Tools
at the end - i.e. the module is completely self contained, requiring only pyaml if you want yaml output.
- You can now parametrize and then send requests to the endpoint by hitting the hotkey on the methods.
- Set
results
to
- 0 in order to only see request parameters w/o actually sending it
- 1 (default) to see the responses only
- 2 for both
- 3 shows the whole requests object, with all attributes resolved
- Configure any authentication within
class API
. Environ variables are understood. - Directives are at the end of the
methods
block, ready for change.- Default:
# :clear :doc :all :single :wrap p = Tools.send({})
- Remove the
:clear
to not loose output of previous runs - Set
:wrap p = ...
to:wrap y= ...
to get output as yaml
- Default:
If you have downloaded a swagger definition into a file, press the hotkey on the filename
Instead of evaluating swagger ./hcloud.json
in vi you can call the module on the CLI with those args:
gk@axgk hetzner]$ alias vpe
alias vpe='/home/gk/.local/share/nvim/site/pack/packer/start/vpe/plugin/vim_python_eval.py'
[gk@axgk hetzner]$ vpe
no vim api importable
Call me with <module name> <file or url>. Modules: ['swagger', 'openapi']
[gk@axgk hetzner]$ rm -f client_hcloud.py; vpe swagger ./hcloud.json
no vim api importable
Have written: client_hcloud.py
[gk@axgk hetzner]$ cat client_hcloud.py | grep -A 10 methods
methods = lambda: ( # :clear :doc :all :single :wrap p = Tools.send({})
'🟩', actions.get,
'🟩', actions___id_.get,
'🟩', certificates.get,
'🟪', certificates.post,
'🟥', certificates___id_.delete,
'🟩', certificates___id_.get,
'🟧', certificates___id_.put,
'🟩', certificates___id___actions.get,
(...)
- The source module is optimized for jumping around using
gd
(goto definition), which should be part of your editor python setup. Some refs may be wrapped into lambdas, when not resolvable at import time <Ctrl-o>
to jump back in history (:help jump
)- The use of markers is encouraged, e.g.
mm
on the methods block (for'm
to jump there) - The source module is also built for simple indent based folding, allowing to focus on specific methods, also within bigger APIs
Before evaluating the link to a swagger definition file, resulting in source module build, you may parametrize the build by evaluating some other conventional assignments.
These are understood, in addition to the always supported ones (e.g. hide
or filter
):
'hdrs' : None, # headers
'hide' : None, # hide='foo,bar' => values for those keys are x-ed out
'noicos' : None, # do not show the colored req method icons
'params' : None, # dict of global params, in addition to path params parsed
'sep' : None, # seperates lines in method list by this char
'filter' : None, # only show keys/vals which match. '1': Show only first list item
'result' : 1, # 0: Show only req, no API hit; 1: only result; 2 : both; 3 : full req object
'str_dflt' : '', # Sets default for all string params w/o an example
'timeout' : 5, # Sets requests timeout
Note that values in hdrs as API params may come from environ, e.g. hdrs={'API-Token': $token}
.
Here a demo against a DynDNS provider's API, illustrating the use of those parameters.
Note that the API server was a bit... slow at time of recording, we had to raise the request
timeout. Also the sh_req
param was changed to result
param.
In order to configure what to send you can jump to the methods definitions, using gd
(goto
definition). The parameters are listed as attributes of a nested class R
, containing all
information from the swagger spec (again, optimized to jump there and read using gd
).
You typically fold it away after a while:
Example:
If you want to parametrize the id
of that server delete API method you can
- set id globally, since
R.id
is set the the (global)id
. - set it in the method itself, e.g.
R.id = "foo"
, then w/o effect on all other methods using the globalid
CAUTION: dict values (rare) have to be "postfixed" with a comma, e.g.:
class some_api_path:
class put:
R.name = 'foo'
R.tags = ['important']
R.labels = {'customer': 'bar'}, # <-- Comma for dicts(!)
Often not all parameters defined are required / wanted to send.
Example:
The method above the delete method in the screenshot has it's body parameter set to a component
definition reference (components_schemas_create_server_request
).
Jump to it with gd
and set the parameters there like shown:
class components_schemas_create_server_request:
"""#/components/schemas/create_server_request"""
class R:
required = ['name', 'server_type', 'image']
(... all infos about the component)
# R.automount = True
# R.datacenter = 'nbg1-dc3'
# R.firewalls = [{'firewall': 38}]
R.image = 'ubuntu-20.04'
# R.labels = lambda: Defs.components_schemas_labels
R.location = 'nbg1'
R.name = 'my-server'
# R.networks = [456]
# R.placement_group = 1
# R.public_net = dict(enable_ipv4=True, enable_ipv6=True, ipv4=0, ipv6=0)
R.server_type = 'cx11'
# R.ssh_keys = ['my-ssh-key']
# R.start_after_create = True
# R.user_data = (
# '#cloud-config\nruncmd:\n- [touch, /root/cloud-init-worked]\n'
# )
# R.volumes = [123]
The parameters you do NOT want to send,
- EITHER: just comment out (as shown)
- OR: remove from the nested
R
class' _attrs list:
class components_schemas_create_server_request:
"""#/components/schemas/create_server_request"""
class R:
required = ['name', 'server_type', 'image']
title = 'CreateServerRequest'
type = 'object'
description = (
'Request for POST https://api.hetzner.cloud/v1/servers'
)
_attrs = [
'automount',
'datacenter',
'firewalls', # <--- comment if you do NOT want to send
'image',
(...)
If API.passw is set, then by default requests is using BasicAuth.
For digest, set digest = True
into the API class.
You may call the module on a swagger file as well, generating the API module.
You can pre-parametrize generation via a file mods.py
in cwd.
We downloaded the swagger definition of Infrastructure provider Hetzner, from here. The file has 16000 lines.
Their authentication works via header Authorization: Bearer <token>
, so in order for the API to
work right away we prepare this:
~/r/gh/ax/vpe/e/hetzner main +1 !4 ?10 ❯ cat mods.py
hdrs = {'Authorization': '$bearer'}
with $bearer
in environ:
export token="$(pass show HCloud/token)"
export bearer="Bearer $token"
Now we build the module and format it on the cli:
~/r/gh/ax/vpe/e/hetzner main +1 !4 ?10 ❯ alias vpe
vpe=/home/gk/repos/gh/axiros/vpe/vim_python_eval.py
~/r/gh/ax/vpe/e/hetzner main +1 !4 ?10 ❯ vpe swagger hcloud.json
no vim api importable
Building components and definitions
definition #/components/schemas/action
#/components/schemas/action prop command
#/components/schemas/action prop error
(...) # long list of defs and methods
path /volumes/{id}/actions/detach
post
path /volumes/{id}/actions/resize
post
Have written: client_hcloud.py
~/r/gh/ax/vpe/e/hetzner main +1 !4 ?10 ❯ blue client_hcloud.py
reformatted client_hcloud.py
All done! ✨ 🍰 ✨
1 file reformatted.
We can now work with the Hetzner API.
Screencast content
- We query our ssh key id
- Create a server, using that key (requiring an undo in the results window, to see it again)
- Query the existing server(s) (requiring a parametrization of the get request)
Note how we fold away methods we don't work with - by indenting them, then fold.
Jumping to the methods start is done by setting a mark mm
(for 'm
to jump there).
For yet larger APIs, e.g. the kubernetes API, the re-evaluation of the whole module for any request get's too slow:
~/repos/gh/axiros/vpe/examples/k8s main ❯ cat swagger.json| wc -l
85588
~/repos/gh/axiros/vpe/examples/k8s main ❯ cat created_client.py | wc -l
27287
~/repos/gh/axiros/vpe/examples/k8s main ❯
We recommand to remove the :all
directive and just re-evaluate the changed definition or method classes, once you re-parametrize.
Evaluate this to show always all state variables, w/o the callables:
# :always :state :clear
hide = 'callable'
In the example folder resides a run_dirs.py <dirmatch>
which
- goes into the matching directorie(s) and finds the swagger def. files
- builds the client
- runs ALL methods of the client with
result = 0
, i.e. only creates (with defaults), but does not send the requests - records the result into
result.json
The tests/openapi_tests.sh
script does only check if those remain unchanged after code changes.