diff --git a/ara/clients/http.py b/ara/clients/http.py
new file mode 100644
index 0000000..30c8dd0
--- /dev/null
+++ b/ara/clients/http.py
@@ -0,0 +1,127 @@
+# Copyright (c) 2018 Red Hat, Inc.
+#
+# This file is part of ARA: Ansible Run Analysis.
+#
+# ARA is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# ARA is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with ARA. If not, see .
+
+# This is an "offline" API client that does not require standing up
+# an API server and does not execute actual HTTP calls.
+
+import json
+import logging
+import requests
+
+
+class HttpClient(object):
+ def __init__(self, endpoint='http://127.0.0.1:8000', timeout=30, **params):
+ self.endpoint = endpoint
+ self.timeout = timeout
+ self.params = params
+
+ self.log = logging.getLogger(__name__)
+ self.user_agent = 'ara-http-client'
+ self.log.debug("%s: %s" % (self.user_agent, str(self.params)))
+
+ self.http = requests.Session()
+
+ def _request(self, method, url, **kwargs):
+ # Override timeout and headers only if user supplied
+ kwargs.setdefault('timeout', self.timeout)
+ kwargs.setdefault('headers', kwargs.get('headers', {}))
+
+ # Headers we're enforcing (kind of)
+ kwargs['headers']['User-Agent'] = self.user_agent
+ kwargs['headers']['Accept'] = 'application/json'
+ kwargs['headers']['Content-Type'] = 'application/json'
+
+ self.log.debug("%s on %s" % (method, url))
+
+ # Use requests.Session to do the query
+ # The actual endpoint is:
+ #
+ # http://127.0.0.1:8000 / api/v1/playbooks
+ return self.http.request(method, self.endpoint + url, **kwargs)
+
+ def get(self, url, **kwargs):
+ return self._request('get', url, **kwargs)
+
+ def patch(self, url, **kwargs):
+ return self._request('patch', url, **kwargs)
+
+ def post(self, url, **kwargs):
+ return self._request('post', url, **kwargs)
+
+ def put(self, url, **kwargs):
+ return self._request('put', url, **kwargs)
+
+ def delete(self, url, **kwargs):
+ return self._request('delete', url, **kwargs)
+
+
+class AraHttpClient(object):
+ def __init__(self):
+ self.log = logging.getLogger(__name__)
+ self.client = HttpClient()
+
+ def _request(self, method, url, **kwargs):
+ func = getattr(self.client, method)
+ # TODO: Is there a better way than doing this if/else ?
+ if kwargs:
+ response = func(url, json.dumps(kwargs))
+ else:
+ response = func(url)
+
+ if response.status_code >= 500:
+ self.log.error(
+ 'Failed to {method} on {url}: {content}'.format(
+ method=method,
+ url=url,
+ content=kwargs
+ )
+ )
+
+ self.log.debug('HTTP {status}: {method} on {url}'.format(
+ status=response.status_code,
+ method=method,
+ url=url
+ ))
+
+ if response.status_code not in [200, 201, 204]:
+ self.log.error(
+ 'Failed to {method} on {url}: {content}'.format(
+ method=method,
+ url=url,
+ content=kwargs
+ )
+ )
+
+ if response.status_code == 204:
+ return response
+
+ return response.json()
+
+ def get(self, endpoint, **kwargs):
+ return self._request('get', endpoint, **kwargs)
+
+ def patch(self, endpoint, **kwargs):
+ return self._request('patch', endpoint, **kwargs)
+
+ def post(self, endpoint, **kwargs):
+ return self._request('post', endpoint, **kwargs)
+
+ def put(self, endpoint, **kwargs):
+ return self._request('put', endpoint, **kwargs)
+
+ def delete(self, endpoint, **kwargs):
+ return self._request('delete', endpoint, **kwargs)
diff --git a/ara/clients/offline.py b/ara/clients/offline.py
index 2d25a5b..f2876ed 100644
--- a/ara/clients/offline.py
+++ b/ara/clients/offline.py
@@ -22,16 +22,21 @@ import json
import logging
import os
-from django import setup as django_setup
-from django.core.management import execute_from_command_line
-from django.test import Client
+try:
+ from django import setup as django_setup
+ from django.core.management import execute_from_command_line
+ from django.test import Client
-os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'ara.server.settings')
-# Automatically create the database and run migrations (is there a better way?)
-execute_from_command_line(['django', 'migrate'])
+ os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'ara.server.settings')
-# Set up the things Django needs
-django_setup()
+ # Automatically create the database and run migrations (is there a better way?)
+ execute_from_command_line(['django', 'migrate'])
+
+ # Set up the things Django needs
+ django_setup()
+except ImportError as e:
+ print('ERROR: The offline client requires ara-server to be installed')
+ raise e
class AraOfflineClient(object):
diff --git a/ara/clients/utils.py b/ara/clients/utils.py
index 99006a8..e1ce500 100644
--- a/ara/clients/utils.py
+++ b/ara/clients/utils.py
@@ -16,10 +16,13 @@
# along with ARA. If not, see .
from ara.clients.offline import AraOfflineClient
+from ara.clients.http import AraHttpClient
def get_client(client=None, **kwargs):
if client is None or client == 'offline':
return AraOfflineClient(**kwargs)
+ elif client == 'http':
+ return AraHttpClient(**kwargs)
else:
raise ValueError('Unsupported client: %s' % client)