| """ Generic API Client """ |
| from copy import deepcopy |
| import json |
| import requests |
|
|
| try: |
| from urlparse import urljoin |
| except ImportError: |
| from urllib.parse import urljoin |
|
|
|
|
| class ApiClient(object): |
| """ Client to interact with a generic Rest API. |
| |
| Subclasses should implement functionality accordingly with the provided |
| service methods, i.e. ``get``, ``post``, ``put`` and ``delete``. |
| """ |
|
|
| accept_type = 'application/xml' |
| api_base = None |
|
|
| def __init__( |
| self, |
| base_url, |
| username=None, |
| api_key=None, |
| status_endpoint=None, |
| timeout=60 |
| ): |
| """ Initialise client. |
| |
| Args: |
| base_url (str): The base URL to the service being used. |
| username (str): The username to authenticate with. |
| api_key (str): The API key to authenticate with. |
| timeout (int): Maximum time before timing out. |
| """ |
| self.base_url = base_url |
| self.username = username |
| self.api_key = api_key |
| self.status_endpoint = urljoin(self.base_url, status_endpoint) |
| self.timeout = timeout |
|
|
| @staticmethod |
| def encode(request, data): |
| """ Add request content data to request body, set Content-type header. |
| |
| Should be overridden by subclasses if not using JSON encoding. |
| |
| Args: |
| request (HTTPRequest): The request object. |
| data (dict, None): Data to be encoded. |
| |
| Returns: |
| HTTPRequest: The request object. |
| """ |
| if data is None: |
| return request |
|
|
| request.add_header('Content-Type', 'application/json') |
| request.extracted_data = json.dumps(data) |
|
|
| return request |
|
|
| @staticmethod |
| def decode(response): |
| """ Decode the returned data in the response. |
| |
| Should be overridden by subclasses if something else than JSON is |
| expected. |
| |
| Args: |
| response (HTTPResponse): The response object. |
| |
| Returns: |
| dict or None. |
| """ |
| try: |
| return response.json() |
| except ValueError as e: |
| return e.message |
|
|
| def get_credentials(self): |
| """ Returns parameters to be added to authenticate the request. |
| |
| This lives on its own to make it easier to re-implement it if needed. |
| |
| Returns: |
| dict: A dictionary containing the credentials. |
| """ |
| return {"username": self.username, "api_key": self.api_key} |
|
|
| def call_api( |
| self, |
| method, |
| url, |
| headers=None, |
| params=None, |
| data=None, |
| files=None, |
| timeout=None, |
| ): |
| """ Call API. |
| |
| This returns object containing data, with error details if applicable. |
| |
| Args: |
| method (str): The HTTP method to use. |
| url (str): Resource location relative to the base URL. |
| headers (dict or None): Extra request headers to set. |
| params (dict or None): Query-string parameters. |
| data (dict or None): Request body contents for POST or PUT requests. |
| files (dict or None: Files to be passed to the request. |
| timeout (int): Maximum time before timing out. |
| |
| Returns: |
| ResultParser or ErrorParser. |
| """ |
| headers = deepcopy(headers) or {} |
| headers['Accept'] = self.accept_type if 'Accept' not in headers else headers['Accept'] |
| params = deepcopy(params) or {} |
| data = data or {} |
| files = files or {} |
| |
| |
| r = requests.request( |
| method, |
| url, |
| headers=headers, |
| params=params, |
| files=files, |
| data=data, |
| timeout=timeout, |
| ) |
|
|
| return r, r.status_code |
|
|
| def get(self, url, params=None, **kwargs): |
| """ Call the API with a GET request. |
| |
| Args: |
| url (str): Resource location relative to the base URL. |
| params (dict or None): Query-string parameters. |
| |
| Returns: |
| ResultParser or ErrorParser. |
| """ |
| return self.call_api( |
| "GET", |
| url, |
| params=params, |
| **kwargs |
| ) |
|
|
| def delete(self, url, params=None, **kwargs): |
| """ Call the API with a DELETE request. |
| |
| Args: |
| url (str): Resource location relative to the base URL. |
| params (dict or None): Query-string parameters. |
| |
| Returns: |
| ResultParser or ErrorParser. |
| """ |
| return self.call_api( |
| "DELETE", |
| url, |
| params=params, |
| **kwargs |
| ) |
|
|
| def put(self, url, params=None, data=None, files=None, **kwargs): |
| """ Call the API with a PUT request. |
| |
| Args: |
| url (str): Resource location relative to the base URL. |
| params (dict or None): Query-string parameters. |
| data (dict or None): Request body contents. |
| files (dict or None: Files to be passed to the request. |
| |
| Returns: |
| An instance of ResultParser or ErrorParser. |
| """ |
| return self.call_api( |
| "PUT", |
| url, |
| params=params, |
| data=data, |
| files=files, |
| **kwargs |
| ) |
|
|
| def post(self, url, params=None, data=None, files=None, **kwargs): |
| """ Call the API with a POST request. |
| |
| Args: |
| url (str): Resource location relative to the base URL. |
| params (dict or None): Query-string parameters. |
| data (dict or None): Request body contents. |
| files (dict or None: Files to be passed to the request. |
| |
| Returns: |
| An instance of ResultParser or ErrorParser. |
| """ |
| return self.call_api( |
| method="POST", |
| url=url, |
| params=params, |
| data=data, |
| files=files, |
| **kwargs |
| ) |
|
|
| def service_status(self, **kwargs): |
| """ Call the API to get the status of the service. |
| |
| Returns: |
| An instance of ResultParser or ErrorParser. |
| """ |
| return self.call_api( |
| 'GET', |
| self.status_endpoint, |
| params={'format': 'json'}, |
| **kwargs |
| ) |
|
|