"""
API calls to Dojot.
"""
import json
from typing import Callable, List, Dict
import requests
import gevent

from config import CONFIG
from utils import Utils


LOGGER = Utils.create_logger("api")


class APICallError(Exception):
    """
    Error when trying to call Dojot API.
    """


class DojotAPI():
    """
    Utility class with API calls to Dojot.
    """
    @staticmethod
    def get_jwt() -> str:
        """
        Request a JWT token.
        """
        LOGGER.debug("Retrieving JWT...")

        args = {
            "url": "{0}/auth".format(CONFIG['dojot']['url']),
            "data": json.dumps({
                "username": CONFIG['dojot']['user'],
                "passwd": CONFIG['dojot']['passwd'],
            }),
            "headers": {
                "Content-Type": "application/json"
            },
        }

        res = DojotAPI.call_api(requests.post, args)

        LOGGER.debug(".. retrieved JWT")
        return res["jwt"]

    @staticmethod
    def get_ws_ticket(jwt: str) -> str:
        """
        Request a web socket ticket
        """
        LOGGER.debug("Retrieving ticket...")

        args = {
            "url": "{0}/kafka-ws/v1/ticket".format(CONFIG['dojot']['url']),
            "headers": {
                "Content-Type": "application/json",
                "Authorization": "Bearer {0}".format(jwt),
            },
        }

        res = DojotAPI.call_api(requests.get, args)

        LOGGER.debug(".. retrieved ticket")
        return res["ticket"]

    @staticmethod
    def create_devices(jwt: str, template_id: str, total: int, batch: int) -> None:
        """
        Create the devices.

        Parameters:
            jwt: Dojot JWT token
            template_id: template ID to be used by the devices
            n: total number of devices to be created
            batch: number of devices to be created in each iteration
        """
        LOGGER.debug("Creating devices...")

        args = {
            "headers": {
                "Content-Type": "application/json",
                "Authorization": "Bearer {0}".format(jwt),
            },
        }

        loads = DojotAPI.divide_loads(total, batch)

        for i, load in enumerate(loads):
            args["data"] = json.dumps({
                "templates": [template_id],
                "attrs": {},
                "label": "CargoContainer_{0}".format(i)
            })
            args["url"] = "{0}/device?count={1}&verbose=false".format(CONFIG['dojot']['url'], load)

            DojotAPI.call_api(requests.post, args)

        LOGGER.debug("... created the devices")

    @staticmethod
    def create_template(jwt: str) -> str:
        """
        Create the default template for test devices.

        Returns the created template ID.
        """
        LOGGER.debug("Creating template...")

        args = {
            "url": "{0}/template".format(CONFIG['dojot']['url']),
            "headers": {
                "Content-Type": "application/json",
                "Authorization": "Bearer {0}".format(jwt),
            },
            "data": json.dumps({
                "label": "CargoContainer",
                "attrs": [
                    {
                        "label": "timestamp",
                        "type": "dynamic",
                        "value_type": "integer"
                    },
                ]
            }),
        }

        res = DojotAPI.call_api(requests.post, args)

        LOGGER.debug("... created the template")
        return res["template"]["id"]

    @staticmethod
    def create_device(jwt: str, template_id: str, label: str) -> str:
        """
        Create a device in Dojot.

        Parameters:
            jwt: JWT authorization.
            template_id: template to be used by the device.
            label: name for the device in Dojot.

        Returns the created device ID.
        """
        LOGGER.debug("Creating template...")

        args = {
            "url": "{0}/device".format(CONFIG['dojot']['url']),
            "headers": {
                "Content-Type": "application/json",
                "Authorization": "Bearer {0}".format(jwt),
            },
            "data": json.dumps({
                "templates": [template_id],
                "attrs": {},
                "label": label,
            }),
        }

        res = DojotAPI.call_api(requests.post, args)

        LOGGER.debug("... created the template")
        return res["devices"][0]["id"]

    @staticmethod
    def delete_devices(jwt: str) -> None:
        """
        Delete all devices.
        """
        LOGGER.debug("Deleting devices...")

        args = {
            "url": "{0}/device".format(CONFIG['dojot']['url']),
            "headers": {
                "Authorization": "Bearer {0}".format(jwt),
            },
        }

        DojotAPI.call_api(requests.delete, args)

        LOGGER.debug("... deleted devices")

    @staticmethod
    def delete_templates(jwt: str) -> None:
        """
        Delete all templates.
        """
        LOGGER.debug("Deleting templates...")

        args = {
            "url": "{0}/template".format(CONFIG['dojot']['url']),
            "headers": {
                "Authorization": "Bearer {0}".format(jwt),
            },
        }

        DojotAPI.call_api(requests.delete, args)

        LOGGER.debug("... deleted templates")

    @staticmethod
    def get_devices(jwt: str) -> List:
        """
        Retrieves the devices from Dojot.

        Parameters:
            jwt: Dojot JWT token

        Returns a list of IDs.
        """
        LOGGER.debug("Retrieving devices...")

        args = {
            "url": "{0}/device".format(CONFIG['dojot']['url']),
            "headers": {
                "Content-Type": "application/json",
                "Authorization": "Bearer {0}".format(jwt),
            },
        }

        res = DojotAPI.call_api(requests.get, args)

        devices_ids = [device['id'] for device in res['devices']]

        LOGGER.debug("... retrieved the devices")

        return devices_ids

    @staticmethod
    def divide_loads(total: int, batch: int) -> List:
        """
        Divides `n` in a list with each element being up to `batch`.
        """
        loads = []

        if total > batch:
            iterations = total // batch
            exceeding = total % batch
            # This will create a list with the number `batch` repeated `iterations` times
            # and then `exceeding` at the final
            loads = [batch] * iterations
            if exceeding > 0:
                loads.append(exceeding)

        else:
            loads.append(total)

        return loads

    @staticmethod
    def call_api(func: Callable[..., requests.Response], args: dict) -> Dict:
        """
        Calls the Dojot API using `func` and `args`.

        Parameters:
            func: function to call Dojot API.
            args: dictionary of arguments to `func`

        Returns the response in a dictionary
        """
        for _ in range(CONFIG['dojot']['api']['retries'] + 1):
            try:
                res = func(**args)
                res.raise_for_status()

            except Exception as exception:
                LOGGER.debug(str(exception))
                gevent.sleep(CONFIG['dojot']['api']['time'])

            else:
                return res.json()

        raise APICallError("exceeded the number of retries to {0}".format(args['url']))

8c821a6e57169dd9bebf8679445665c608441739
<mask token>


class DojotAPI:
    <mask token>

    @staticmethod
    def get_jwt() ->str:
        """
        Request a JWT token.
        """
        LOGGER.debug('Retrieving JWT...')
        args = {'url': '{0}/auth'.format(CONFIG['dojot']['url']), 'data':
            json.dumps({'username': CONFIG['dojot']['user'], 'passwd':
            CONFIG['dojot']['passwd']}), 'headers': {'Content-Type':
            'application/json'}}
        res = DojotAPI.call_api(requests.post, args)
        LOGGER.debug('.. retrieved JWT')
        return res['jwt']

    @staticmethod
    def get_ws_ticket(jwt: str) ->str:
        """
        Request a web socket ticket
        """
        LOGGER.debug('Retrieving ticket...')
        args = {'url': '{0}/kafka-ws/v1/ticket'.format(CONFIG['dojot'][
            'url']), 'headers': {'Content-Type': 'application/json',
            'Authorization': 'Bearer {0}'.format(jwt)}}
        res = DojotAPI.call_api(requests.get, args)
        LOGGER.debug('.. retrieved ticket')
        return res['ticket']
    <mask token>

    @staticmethod
    def create_template(jwt: str) ->str:
        """
        Create the default template for test devices.

        Returns the created template ID.
        """
        LOGGER.debug('Creating template...')
        args = {'url': '{0}/template'.format(CONFIG['dojot']['url']),
            'headers': {'Content-Type': 'application/json', 'Authorization':
            'Bearer {0}'.format(jwt)}, 'data': json.dumps({'label':
            'CargoContainer', 'attrs': [{'label': 'timestamp', 'type':
            'dynamic', 'value_type': 'integer'}]})}
        res = DojotAPI.call_api(requests.post, args)
        LOGGER.debug('... created the template')
        return res['template']['id']

    @staticmethod
    def create_device(jwt: str, template_id: str, label: str) ->str:
        """
        Create a device in Dojot.

        Parameters:
            jwt: JWT authorization.
            template_id: template to be used by the device.
            label: name for the device in Dojot.

        Returns the created device ID.
        """
        LOGGER.debug('Creating template...')
        args = {'url': '{0}/device'.format(CONFIG['dojot']['url']),
            'headers': {'Content-Type': 'application/json', 'Authorization':
            'Bearer {0}'.format(jwt)}, 'data': json.dumps({'templates': [
            template_id], 'attrs': {}, 'label': label})}
        res = DojotAPI.call_api(requests.post, args)
        LOGGER.debug('... created the template')
        return res['devices'][0]['id']

    @staticmethod
    def delete_devices(jwt: str) ->None:
        """
        Delete all devices.
        """
        LOGGER.debug('Deleting devices...')
        args = {'url': '{0}/device'.format(CONFIG['dojot']['url']),
            'headers': {'Authorization': 'Bearer {0}'.format(jwt)}}
        DojotAPI.call_api(requests.delete, args)
        LOGGER.debug('... deleted devices')

    @staticmethod
    def delete_templates(jwt: str) ->None:
        """
        Delete all templates.
        """
        LOGGER.debug('Deleting templates...')
        args = {'url': '{0}/template'.format(CONFIG['dojot']['url']),
            'headers': {'Authorization': 'Bearer {0}'.format(jwt)}}
        DojotAPI.call_api(requests.delete, args)
        LOGGER.debug('... deleted templates')

    @staticmethod
    def get_devices(jwt: str) ->List:
        """
        Retrieves the devices from Dojot.

        Parameters:
            jwt: Dojot JWT token

        Returns a list of IDs.
        """
        LOGGER.debug('Retrieving devices...')
        args = {'url': '{0}/device'.format(CONFIG['dojot']['url']),
            'headers': {'Content-Type': 'application/json', 'Authorization':
            'Bearer {0}'.format(jwt)}}
        res = DojotAPI.call_api(requests.get, args)
        devices_ids = [device['id'] for device in res['devices']]
        LOGGER.debug('... retrieved the devices')
        return devices_ids

    @staticmethod
    def divide_loads(total: int, batch: int) ->List:
        """
        Divides `n` in a list with each element being up to `batch`.
        """
        loads = []
        if total > batch:
            iterations = total // batch
            exceeding = total % batch
            loads = [batch] * iterations
            if exceeding > 0:
                loads.append(exceeding)
        else:
            loads.append(total)
        return loads

    @staticmethod
    def call_api(func: Callable[..., requests.Response], args: dict) ->Dict:
        """
        Calls the Dojot API using `func` and `args`.

        Parameters:
            func: function to call Dojot API.
            args: dictionary of arguments to `func`

        Returns the response in a dictionary
        """
        for _ in range(CONFIG['dojot']['api']['retries'] + 1):
            try:
                res = func(**args)
                res.raise_for_status()
            except Exception as exception:
                LOGGER.debug(str(exception))
                gevent.sleep(CONFIG['dojot']['api']['time'])
            else:
                return res.json()
        raise APICallError('exceeded the number of retries to {0}'.format(
            args['url']))

<mask token>


class APICallError(Exception):
    <mask token>


class DojotAPI:
    """
    Utility class with API calls to Dojot.
    """

    @staticmethod
    def get_jwt() ->str:
        """
        Request a JWT token.
        """
        LOGGER.debug('Retrieving JWT...')
        args = {'url': '{0}/auth'.format(CONFIG['dojot']['url']), 'data':
            json.dumps({'username': CONFIG['dojot']['user'], 'passwd':
            CONFIG['dojot']['passwd']}), 'headers': {'Content-Type':
            'application/json'}}
        res = DojotAPI.call_api(requests.post, args)
        LOGGER.debug('.. retrieved JWT')
        return res['jwt']

    @staticmethod
    def get_ws_ticket(jwt: str) ->str:
        """
        Request a web socket ticket
        """
        LOGGER.debug('Retrieving ticket...')
        args = {'url': '{0}/kafka-ws/v1/ticket'.format(CONFIG['dojot'][
            'url']), 'headers': {'Content-Type': 'application/json',
            'Authorization': 'Bearer {0}'.format(jwt)}}
        res = DojotAPI.call_api(requests.get, args)
        LOGGER.debug('.. retrieved ticket')
        return res['ticket']

    @staticmethod
    def create_devices(jwt: str, template_id: str, total: int, batch: int
        ) ->None:
        """
        Create the devices.

        Parameters:
            jwt: Dojot JWT token
            template_id: template ID to be used by the devices
            n: total number of devices to be created
            batch: number of devices to be created in each iteration
        """
        LOGGER.debug('Creating devices...')
        args = {'headers': {'Content-Type': 'application/json',
            'Authorization': 'Bearer {0}'.format(jwt)}}
        loads = DojotAPI.divide_loads(total, batch)
        for i, load in enumerate(loads):
            args['data'] = json.dumps({'templates': [template_id], 'attrs':
                {}, 'label': 'CargoContainer_{0}'.format(i)})
            args['url'] = '{0}/device?count={1}&verbose=false'.format(CONFIG
                ['dojot']['url'], load)
            DojotAPI.call_api(requests.post, args)
        LOGGER.debug('... created the devices')

    @staticmethod
    def create_template(jwt: str) ->str:
        """
        Create the default template for test devices.

        Returns the created template ID.
        """
        LOGGER.debug('Creating template...')
        args = {'url': '{0}/template'.format(CONFIG['dojot']['url']),
            'headers': {'Content-Type': 'application/json', 'Authorization':
            'Bearer {0}'.format(jwt)}, 'data': json.dumps({'label':
            'CargoContainer', 'attrs': [{'label': 'timestamp', 'type':
            'dynamic', 'value_type': 'integer'}]})}
        res = DojotAPI.call_api(requests.post, args)
        LOGGER.debug('... created the template')
        return res['template']['id']

    @staticmethod
    def create_device(jwt: str, template_id: str, label: str) ->str:
        """
        Create a device in Dojot.

        Parameters:
            jwt: JWT authorization.
            template_id: template to be used by the device.
            label: name for the device in Dojot.

        Returns the created device ID.
        """
        LOGGER.debug('Creating template...')
        args = {'url': '{0}/device'.format(CONFIG['dojot']['url']),
            'headers': {'Content-Type': 'application/json', 'Authorization':
            'Bearer {0}'.format(jwt)}, 'data': json.dumps({'templates': [
            template_id], 'attrs': {}, 'label': label})}
        res = DojotAPI.call_api(requests.post, args)
        LOGGER.debug('... created the template')
        return res['devices'][0]['id']

    @staticmethod
    def delete_devices(jwt: str) ->None:
        """
        Delete all devices.
        """
        LOGGER.debug('Deleting devices...')
        args = {'url': '{0}/device'.format(CONFIG['dojot']['url']),
            'headers': {'Authorization': 'Bearer {0}'.format(jwt)}}
        DojotAPI.call_api(requests.delete, args)
        LOGGER.debug('... deleted devices')

    @staticmethod
    def delete_templates(jwt: str) ->None:
        """
        Delete all templates.
        """
        LOGGER.debug('Deleting templates...')
        args = {'url': '{0}/template'.format(CONFIG['dojot']['url']),
            'headers': {'Authorization': 'Bearer {0}'.format(jwt)}}
        DojotAPI.call_api(requests.delete, args)
        LOGGER.debug('... deleted templates')

    @staticmethod
    def get_devices(jwt: str) ->List:
        """
        Retrieves the devices from Dojot.

        Parameters:
            jwt: Dojot JWT token

        Returns a list of IDs.
        """
        LOGGER.debug('Retrieving devices...')
        args = {'url': '{0}/device'.format(CONFIG['dojot']['url']),
            'headers': {'Content-Type': 'application/json', 'Authorization':
            'Bearer {0}'.format(jwt)}}
        res = DojotAPI.call_api(requests.get, args)
        devices_ids = [device['id'] for device in res['devices']]
        LOGGER.debug('... retrieved the devices')
        return devices_ids

    @staticmethod
    def divide_loads(total: int, batch: int) ->List:
        """
        Divides `n` in a list with each element being up to `batch`.
        """
        loads = []
        if total > batch:
            iterations = total // batch
            exceeding = total % batch
            loads = [batch] * iterations
            if exceeding > 0:
                loads.append(exceeding)
        else:
            loads.append(total)
        return loads

    @staticmethod
    def call_api(func: Callable[..., requests.Response], args: dict) ->Dict:
        """
        Calls the Dojot API using `func` and `args`.

        Parameters:
            func: function to call Dojot API.
            args: dictionary of arguments to `func`

        Returns the response in a dictionary
        """
        for _ in range(CONFIG['dojot']['api']['retries'] + 1):
            try:
                res = func(**args)
                res.raise_for_status()
            except Exception as exception:
                LOGGER.debug(str(exception))
                gevent.sleep(CONFIG['dojot']['api']['time'])
            else:
                return res.json()
        raise APICallError('exceeded the number of retries to {0}'.format(
            args['url']))

<mask token>


class APICallError(Exception):
    """
    Error when trying to call Dojot API.
    """


class DojotAPI:
    """
    Utility class with API calls to Dojot.
    """

    @staticmethod
    def get_jwt() ->str:
        """
        Request a JWT token.
        """
        LOGGER.debug('Retrieving JWT...')
        args = {'url': '{0}/auth'.format(CONFIG['dojot']['url']), 'data':
            json.dumps({'username': CONFIG['dojot']['user'], 'passwd':
            CONFIG['dojot']['passwd']}), 'headers': {'Content-Type':
            'application/json'}}
        res = DojotAPI.call_api(requests.post, args)
        LOGGER.debug('.. retrieved JWT')
        return res['jwt']

    @staticmethod
    def get_ws_ticket(jwt: str) ->str:
        """
        Request a web socket ticket
        """
        LOGGER.debug('Retrieving ticket...')
        args = {'url': '{0}/kafka-ws/v1/ticket'.format(CONFIG['dojot'][
            'url']), 'headers': {'Content-Type': 'application/json',
            'Authorization': 'Bearer {0}'.format(jwt)}}
        res = DojotAPI.call_api(requests.get, args)
        LOGGER.debug('.. retrieved ticket')
        return res['ticket']

    @staticmethod
    def create_devices(jwt: str, template_id: str, total: int, batch: int
        ) ->None:
        """
        Create the devices.

        Parameters:
            jwt: Dojot JWT token
            template_id: template ID to be used by the devices
            n: total number of devices to be created
            batch: number of devices to be created in each iteration
        """
        LOGGER.debug('Creating devices...')
        args = {'headers': {'Content-Type': 'application/json',
            'Authorization': 'Bearer {0}'.format(jwt)}}
        loads = DojotAPI.divide_loads(total, batch)
        for i, load in enumerate(loads):
            args['data'] = json.dumps({'templates': [template_id], 'attrs':
                {}, 'label': 'CargoContainer_{0}'.format(i)})
            args['url'] = '{0}/device?count={1}&verbose=false'.format(CONFIG
                ['dojot']['url'], load)
            DojotAPI.call_api(requests.post, args)
        LOGGER.debug('... created the devices')

    @staticmethod
    def create_template(jwt: str) ->str:
        """
        Create the default template for test devices.

        Returns the created template ID.
        """
        LOGGER.debug('Creating template...')
        args = {'url': '{0}/template'.format(CONFIG['dojot']['url']),
            'headers': {'Content-Type': 'application/json', 'Authorization':
            'Bearer {0}'.format(jwt)}, 'data': json.dumps({'label':
            'CargoContainer', 'attrs': [{'label': 'timestamp', 'type':
            'dynamic', 'value_type': 'integer'}]})}
        res = DojotAPI.call_api(requests.post, args)
        LOGGER.debug('... created the template')
        return res['template']['id']

    @staticmethod
    def create_device(jwt: str, template_id: str, label: str) ->str:
        """
        Create a device in Dojot.

        Parameters:
            jwt: JWT authorization.
            template_id: template to be used by the device.
            label: name for the device in Dojot.

        Returns the created device ID.
        """
        LOGGER.debug('Creating template...')
        args = {'url': '{0}/device'.format(CONFIG['dojot']['url']),
            'headers': {'Content-Type': 'application/json', 'Authorization':
            'Bearer {0}'.format(jwt)}, 'data': json.dumps({'templates': [
            template_id], 'attrs': {}, 'label': label})}
        res = DojotAPI.call_api(requests.post, args)
        LOGGER.debug('... created the template')
        return res['devices'][0]['id']

    @staticmethod
    def delete_devices(jwt: str) ->None:
        """
        Delete all devices.
        """
        LOGGER.debug('Deleting devices...')
        args = {'url': '{0}/device'.format(CONFIG['dojot']['url']),
            'headers': {'Authorization': 'Bearer {0}'.format(jwt)}}
        DojotAPI.call_api(requests.delete, args)
        LOGGER.debug('... deleted devices')

    @staticmethod
    def delete_templates(jwt: str) ->None:
        """
        Delete all templates.
        """
        LOGGER.debug('Deleting templates...')
        args = {'url': '{0}/template'.format(CONFIG['dojot']['url']),
            'headers': {'Authorization': 'Bearer {0}'.format(jwt)}}
        DojotAPI.call_api(requests.delete, args)
        LOGGER.debug('... deleted templates')

    @staticmethod
    def get_devices(jwt: str) ->List:
        """
        Retrieves the devices from Dojot.

        Parameters:
            jwt: Dojot JWT token

        Returns a list of IDs.
        """
        LOGGER.debug('Retrieving devices...')
        args = {'url': '{0}/device'.format(CONFIG['dojot']['url']),
            'headers': {'Content-Type': 'application/json', 'Authorization':
            'Bearer {0}'.format(jwt)}}
        res = DojotAPI.call_api(requests.get, args)
        devices_ids = [device['id'] for device in res['devices']]
        LOGGER.debug('... retrieved the devices')
        return devices_ids

    @staticmethod
    def divide_loads(total: int, batch: int) ->List:
        """
        Divides `n` in a list with each element being up to `batch`.
        """
        loads = []
        if total > batch:
            iterations = total // batch
            exceeding = total % batch
            loads = [batch] * iterations
            if exceeding > 0:
                loads.append(exceeding)
        else:
            loads.append(total)
        return loads

    @staticmethod
    def call_api(func: Callable[..., requests.Response], args: dict) ->Dict:
        """
        Calls the Dojot API using `func` and `args`.

        Parameters:
            func: function to call Dojot API.
            args: dictionary of arguments to `func`

        Returns the response in a dictionary
        """
        for _ in range(CONFIG['dojot']['api']['retries'] + 1):
            try:
                res = func(**args)
                res.raise_for_status()
            except Exception as exception:
                LOGGER.debug(str(exception))
                gevent.sleep(CONFIG['dojot']['api']['time'])
            else:
                return res.json()
        raise APICallError('exceeded the number of retries to {0}'.format(
            args['url']))

<mask token>
import json
from typing import Callable, List, Dict
import requests
import gevent
from config import CONFIG
from utils import Utils
LOGGER = Utils.create_logger('api')


class APICallError(Exception):
    """
    Error when trying to call Dojot API.
    """


class DojotAPI:
    """
    Utility class with API calls to Dojot.
    """

    @staticmethod
    def get_jwt() ->str:
        """
        Request a JWT token.
        """
        LOGGER.debug('Retrieving JWT...')
        args = {'url': '{0}/auth'.format(CONFIG['dojot']['url']), 'data':
            json.dumps({'username': CONFIG['dojot']['user'], 'passwd':
            CONFIG['dojot']['passwd']}), 'headers': {'Content-Type':
            'application/json'}}
        res = DojotAPI.call_api(requests.post, args)
        LOGGER.debug('.. retrieved JWT')
        return res['jwt']

    @staticmethod
    def get_ws_ticket(jwt: str) ->str:
        """
        Request a web socket ticket
        """
        LOGGER.debug('Retrieving ticket...')
        args = {'url': '{0}/kafka-ws/v1/ticket'.format(CONFIG['dojot'][
            'url']), 'headers': {'Content-Type': 'application/json',
            'Authorization': 'Bearer {0}'.format(jwt)}}
        res = DojotAPI.call_api(requests.get, args)
        LOGGER.debug('.. retrieved ticket')
        return res['ticket']

    @staticmethod
    def create_devices(jwt: str, template_id: str, total: int, batch: int
        ) ->None:
        """
        Create the devices.

        Parameters:
            jwt: Dojot JWT token
            template_id: template ID to be used by the devices
            n: total number of devices to be created
            batch: number of devices to be created in each iteration
        """
        LOGGER.debug('Creating devices...')
        args = {'headers': {'Content-Type': 'application/json',
            'Authorization': 'Bearer {0}'.format(jwt)}}
        loads = DojotAPI.divide_loads(total, batch)
        for i, load in enumerate(loads):
            args['data'] = json.dumps({'templates': [template_id], 'attrs':
                {}, 'label': 'CargoContainer_{0}'.format(i)})
            args['url'] = '{0}/device?count={1}&verbose=false'.format(CONFIG
                ['dojot']['url'], load)
            DojotAPI.call_api(requests.post, args)
        LOGGER.debug('... created the devices')

    @staticmethod
    def create_template(jwt: str) ->str:
        """
        Create the default template for test devices.

        Returns the created template ID.
        """
        LOGGER.debug('Creating template...')
        args = {'url': '{0}/template'.format(CONFIG['dojot']['url']),
            'headers': {'Content-Type': 'application/json', 'Authorization':
            'Bearer {0}'.format(jwt)}, 'data': json.dumps({'label':
            'CargoContainer', 'attrs': [{'label': 'timestamp', 'type':
            'dynamic', 'value_type': 'integer'}]})}
        res = DojotAPI.call_api(requests.post, args)
        LOGGER.debug('... created the template')
        return res['template']['id']

    @staticmethod
    def create_device(jwt: str, template_id: str, label: str) ->str:
        """
        Create a device in Dojot.

        Parameters:
            jwt: JWT authorization.
            template_id: template to be used by the device.
            label: name for the device in Dojot.

        Returns the created device ID.
        """
        LOGGER.debug('Creating template...')
        args = {'url': '{0}/device'.format(CONFIG['dojot']['url']),
            'headers': {'Content-Type': 'application/json', 'Authorization':
            'Bearer {0}'.format(jwt)}, 'data': json.dumps({'templates': [
            template_id], 'attrs': {}, 'label': label})}
        res = DojotAPI.call_api(requests.post, args)
        LOGGER.debug('... created the template')
        return res['devices'][0]['id']

    @staticmethod
    def delete_devices(jwt: str) ->None:
        """
        Delete all devices.
        """
        LOGGER.debug('Deleting devices...')
        args = {'url': '{0}/device'.format(CONFIG['dojot']['url']),
            'headers': {'Authorization': 'Bearer {0}'.format(jwt)}}
        DojotAPI.call_api(requests.delete, args)
        LOGGER.debug('... deleted devices')

    @staticmethod
    def delete_templates(jwt: str) ->None:
        """
        Delete all templates.
        """
        LOGGER.debug('Deleting templates...')
        args = {'url': '{0}/template'.format(CONFIG['dojot']['url']),
            'headers': {'Authorization': 'Bearer {0}'.format(jwt)}}
        DojotAPI.call_api(requests.delete, args)
        LOGGER.debug('... deleted templates')

    @staticmethod
    def get_devices(jwt: str) ->List:
        """
        Retrieves the devices from Dojot.

        Parameters:
            jwt: Dojot JWT token

        Returns a list of IDs.
        """
        LOGGER.debug('Retrieving devices...')
        args = {'url': '{0}/device'.format(CONFIG['dojot']['url']),
            'headers': {'Content-Type': 'application/json', 'Authorization':
            'Bearer {0}'.format(jwt)}}
        res = DojotAPI.call_api(requests.get, args)
        devices_ids = [device['id'] for device in res['devices']]
        LOGGER.debug('... retrieved the devices')
        return devices_ids

    @staticmethod
    def divide_loads(total: int, batch: int) ->List:
        """
        Divides `n` in a list with each element being up to `batch`.
        """
        loads = []
        if total > batch:
            iterations = total // batch
            exceeding = total % batch
            loads = [batch] * iterations
            if exceeding > 0:
                loads.append(exceeding)
        else:
            loads.append(total)
        return loads

    @staticmethod
    def call_api(func: Callable[..., requests.Response], args: dict) ->Dict:
        """
        Calls the Dojot API using `func` and `args`.

        Parameters:
            func: function to call Dojot API.
            args: dictionary of arguments to `func`

        Returns the response in a dictionary
        """
        for _ in range(CONFIG['dojot']['api']['retries'] + 1):
            try:
                res = func(**args)
                res.raise_for_status()
            except Exception as exception:
                LOGGER.debug(str(exception))
                gevent.sleep(CONFIG['dojot']['api']['time'])
            else:
                return res.json()
        raise APICallError('exceeded the number of retries to {0}'.format(
            args['url']))

"""
API calls to Dojot.
"""
import json
from typing import Callable, List, Dict
import requests
import gevent

from config import CONFIG
from utils import Utils


LOGGER = Utils.create_logger("api")


class APICallError(Exception):
    """
    Error when trying to call Dojot API.
    """


class DojotAPI():
    """
    Utility class with API calls to Dojot.
    """
    @staticmethod
    def get_jwt() -> str:
        """
        Request a JWT token.
        """
        LOGGER.debug("Retrieving JWT...")

        args = {
            "url": "{0}/auth".format(CONFIG['dojot']['url']),
            "data": json.dumps({
                "username": CONFIG['dojot']['user'],
                "passwd": CONFIG['dojot']['passwd'],
            }),
            "headers": {
                "Content-Type": "application/json"
            },
        }

        res = DojotAPI.call_api(requests.post, args)

        LOGGER.debug(".. retrieved JWT")
        return res["jwt"]

    @staticmethod
    def get_ws_ticket(jwt: str) -> str:
        """
        Request a web socket ticket
        """
        LOGGER.debug("Retrieving ticket...")

        args = {
            "url": "{0}/kafka-ws/v1/ticket".format(CONFIG['dojot']['url']),
            "headers": {
                "Content-Type": "application/json",
                "Authorization": "Bearer {0}".format(jwt),
            },
        }

        res = DojotAPI.call_api(requests.get, args)

        LOGGER.debug(".. retrieved ticket")
        return res["ticket"]

    @staticmethod
    def create_devices(jwt: str, template_id: str, total: int, batch: int) -> None:
        """
        Create the devices.

        Parameters:
            jwt: Dojot JWT token
            template_id: template ID to be used by the devices
            n: total number of devices to be created
            batch: number of devices to be created in each iteration
        """
        LOGGER.debug("Creating devices...")

        args = {
            "headers": {
                "Content-Type": "application/json",
                "Authorization": "Bearer {0}".format(jwt),
            },
        }

        loads = DojotAPI.divide_loads(total, batch)

        for i, load in enumerate(loads):
            args["data"] = json.dumps({
                "templates": [template_id],
                "attrs": {},
                "label": "CargoContainer_{0}".format(i)
            })
            args["url"] = "{0}/device?count={1}&verbose=false".format(CONFIG['dojot']['url'], load)

            DojotAPI.call_api(requests.post, args)

        LOGGER.debug("... created the devices")

    @staticmethod
    def create_template(jwt: str) -> str:
        """
        Create the default template for test devices.

        Returns the created template ID.
        """
        LOGGER.debug("Creating template...")

        args = {
            "url": "{0}/template".format(CONFIG['dojot']['url']),
            "headers": {
                "Content-Type": "application/json",
                "Authorization": "Bearer {0}".format(jwt),
            },
            "data": json.dumps({
                "label": "CargoContainer",
                "attrs": [
                    {
                        "label": "timestamp",
                        "type": "dynamic",
                        "value_type": "integer"
                    },
                ]
            }),
        }

        res = DojotAPI.call_api(requests.post, args)

        LOGGER.debug("... created the template")
        return res["template"]["id"]

    @staticmethod
    def create_device(jwt: str, template_id: str, label: str) -> str:
        """
        Create a device in Dojot.

        Parameters:
            jwt: JWT authorization.
            template_id: template to be used by the device.
            label: name for the device in Dojot.

        Returns the created device ID.
        """
        LOGGER.debug("Creating template...")

        args = {
            "url": "{0}/device".format(CONFIG['dojot']['url']),
            "headers": {
                "Content-Type": "application/json",
                "Authorization": "Bearer {0}".format(jwt),
            },
            "data": json.dumps({
                "templates": [template_id],
                "attrs": {},
                "label": label,
            }),
        }

        res = DojotAPI.call_api(requests.post, args)

        LOGGER.debug("... created the template")
        return res["devices"][0]["id"]

    @staticmethod
    def delete_devices(jwt: str) -> None:
        """
        Delete all devices.
        """
        LOGGER.debug("Deleting devices...")

        args = {
            "url": "{0}/device".format(CONFIG['dojot']['url']),
            "headers": {
                "Authorization": "Bearer {0}".format(jwt),
            },
        }

        DojotAPI.call_api(requests.delete, args)

        LOGGER.debug("... deleted devices")

    @staticmethod
    def delete_templates(jwt: str) -> None:
        """
        Delete all templates.
        """
        LOGGER.debug("Deleting templates...")

        args = {
            "url": "{0}/template".format(CONFIG['dojot']['url']),
            "headers": {
                "Authorization": "Bearer {0}".format(jwt),
            },
        }

        DojotAPI.call_api(requests.delete, args)

        LOGGER.debug("... deleted templates")

    @staticmethod
    def get_devices(jwt: str) -> List:
        """
        Retrieves the devices from Dojot.

        Parameters:
            jwt: Dojot JWT token

        Returns a list of IDs.
        """
        LOGGER.debug("Retrieving devices...")

        args = {
            "url": "{0}/device".format(CONFIG['dojot']['url']),
            "headers": {
                "Content-Type": "application/json",
                "Authorization": "Bearer {0}".format(jwt),
            },
        }

        res = DojotAPI.call_api(requests.get, args)

        devices_ids = [device['id'] for device in res['devices']]

        LOGGER.debug("... retrieved the devices")

        return devices_ids

    @staticmethod
    def divide_loads(total: int, batch: int) -> List:
        """
        Divides `n` in a list with each element being up to `batch`.
        """
        loads = []

        if total > batch:
            iterations = total // batch
            exceeding = total % batch
            # This will create a list with the number `batch` repeated `iterations` times
            # and then `exceeding` at the final
            loads = [batch] * iterations
            if exceeding > 0:
                loads.append(exceeding)

        else:
            loads.append(total)

        return loads

    @staticmethod
    def call_api(func: Callable[..., requests.Response], args: dict) -> Dict:
        """
        Calls the Dojot API using `func` and `args`.

        Parameters:
            func: function to call Dojot API.
            args: dictionary of arguments to `func`

        Returns the response in a dictionary
        """
        for _ in range(CONFIG['dojot']['api']['retries'] + 1):
            try:
                res = func(**args)
                res.raise_for_status()

            except Exception as exception:
                LOGGER.debug(str(exception))
                gevent.sleep(CONFIG['dojot']['api']['time'])

            else:
                return res.json()

        raise APICallError("exceeded the number of retries to {0}".format(args['url']))
