Page MenuHome

sdk.py
No OneTemporary

File Metadata

Created
Fri, Dec 6, 6:49 PM
"""PillarSDK subclass for direct Flask-internal calls."""
import logging
import urllib.parse
from flask import current_app
import pillarsdk
from pillarsdk import exceptions
log = logging.getLogger(__name__)
class FlaskInternalApi(pillarsdk.Api):
"""SDK API subclass that calls Flask directly.
Can only be used from the same Python process the Pillar server itself is
running on.
"""
def http_call(self, url, method, **kwargs):
"""Fakes a http call through Flask/Werkzeug."""
client = current_app.test_client()
self.requests_to_flask_kwargs(kwargs)
# Leave out the query string and fragment from the URL.
split_url = urllib.parse.urlsplit(url)
path = urllib.parse.urlunsplit(split_url[:-2] + (None, None))
try:
response = client.open(path=path, query_string=split_url.query, method=method,
**kwargs)
except Exception as ex:
log.warning('Error performing HTTP %s request to %s: %s', method,
url, str(ex))
raise
if method == 'OPTIONS':
return response
self.flask_to_requests_response(response)
try:
content = self.handle_response(response, response.data)
except:
log.debug("%s: Response[%s]: %s", url, response.status_code,
response.data, exc_info=True)
raise
return content
def requests_to_flask_kwargs(self, kwargs):
"""Converts Requests arguments to Flask test client arguments."""
kwargs.pop('verify', None)
# No network connection, so nothing to verify.
# Files to upload need to be sent in the 'data' kwarg instead of the
# 'files' kwarg, and have a different order.
if 'files' in kwargs:
# By default, 'data' is there but None, so setdefault('data', {})
# won't work.
data = kwargs.get('data') or {}
for file_name, file_value in kwargs['files'].items():
fname, fobj, mimeytpe = file_value
data[file_name] = (fobj, fname)
del kwargs['files']
kwargs['data'] = data
def flask_to_requests_response(self, response):
"""Adds some properties to a Flask response object to mimick a Requests
object.
"""
# Our API always sends back UTF8, so we don't have to check headers for
# that.
if response.mimetype.startswith('text'):
response.text = response.data.decode('utf8')
else:
response.text = None
def OPTIONS(self, action, headers=None):
"""Make OPTIONS request.
Contrary to other requests, this method returns the raw requests.Response object.
:rtype: requests.Response
"""
import os
url = os.path.join(self.endpoint, action.strip('/'))
response = self.request(url, 'OPTIONS', headers=headers)
if 200 <= response.status_code <= 299:
return response
exception = exceptions.exception_for_status(response.status_code)
text = getattr(response, 'text', '')
if exception:
raise exception(response, text)
raise exceptions.ConnectionError(response, text,
"Unknown response code: %s" % response.status_code)

Event Timeline