Navigation Menu

Skip to content

Commit

Permalink
Operations can now be called from the spec by operationId
Browse files Browse the repository at this point in the history
Intended usage is this::

   spec = OpenAPI(spec_yaml)
   result = spec.call_getLinodeInstances(security={"personalAccessToken": token})

This uses a custom __getattribute__ to convert all attributes started
with call_ to an OperationCallable configured with the (first) base_url
it finds such that it is immediately called with the given arguments.
  • Loading branch information
Dorthu committed May 19, 2018
1 parent ded7ea4 commit 2f585ff
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 2 deletions.
36 changes: 34 additions & 2 deletions openapi/openapi.py
Expand Up @@ -7,7 +7,8 @@ class OpenAPI(ObjectBase):
.. _the spec: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.1.md#openapi-object
"""
__slots__ = ['openapi','info','servers','paths','components','security','tags','externalDocs']
__slots__ = ['openapi','info','servers','paths','components','security','tags',
'externalDocs','_operation_map']
required_fields=['openapi','info','paths']

def __init__(self, raw_document):
Expand All @@ -26,7 +27,7 @@ def _parse_data(self):
"""
Implementation of :any:`ObjectBase._parse_data`
"""
self._required_fields('openapi', 'info', 'paths')
self._operation_map = {}

self.openapi = self._get('openapi', str)
self.info = self._get('info', 'Info')
Expand All @@ -36,3 +37,34 @@ def _parse_data(self):
self.security = self._get('security', dict)
self.tags = self._get('tags', dict)
self.externalDocs = self._get('externalDocs', dict)

def _get_callable(self, operation):
"""
TODO - explain this
"""
base_url = self.servers[0].url

return OperationCallable(operation, base_url)

def __getattribute__(self, attr):
"""
TODO - describe what this does
"""
if attr.startswith('call_'):
_, operationId = attr.split('_', 1)
if operationId in self._operation_map:
return self._get_callable(self._operation_map[operationId].request)
else:
raise AttributeError('{} has no operation {}'.format(
self.info.title, operationId))

return object.__getattribute__(self, attr)


class OperationCallable:
def __init__(self, operation, base_url):
self.operation = operation
self.base_url = base_url

def __call__(self, *args, **kwargs):
return self.operation(self.base_url, *args, **kwargs)
5 changes: 5 additions & 0 deletions openapi/paths.py
Expand Up @@ -100,6 +100,11 @@ def _parse_data(self):
self.servers = self._get('servers', ['Server'], is_list=True)
raw_servers = self._get('servers', list)

# gather all operations into the spec object
if self.operationId is not None:
# TODO - how to store without an operationId?
self._root._operation_map[self.operationId] = self

def request(self, base_url, security={}, data=None):
"""
Sends an HTTP request as described by this Path
Expand Down

0 comments on commit 2f585ff

Please sign in to comment.