A Python client for the fixer.io foreign exchange API implementing all subscription levels
from .free_client import FreeClient
from .basic_client import BasicClient
from .professional_client import ProfessionalClient
from .professional_plus_client import ProfessionalPlusClient
from .url import BASE_HTTP_URL, BASE_HTTPS_URL

__version__ = '1.0.0-alpha'
fixerio/__init__.py
isaaclm/fixerio-client
from .free_client import FreeClient
from .url import BASE_HTTPS_URL

class BasicClient(FreeClient):
    """ A client for the Fixer.io Basic Plan. """

    def __init__(self, access_key, symbols=None, base=None):
        """
        :param access_key: your API Key.
        :type access_key: str or unicode
        :param symbols: currency symbols to request specific exchange rates.
        :type symbols: list or tuple
        :param base: currency symbol for base currency. The default base currency is EUR.
        :type base: str
        """
        super().__init__(access_key)
        self.base_url = BASE_HTTPS_URL
        self.symbols = symbols
        self.base = base

    def get_symbols(self):
        """ Gets all supported currencies with their respective three-letter currency codes and names.

        :return: all supported currencies with their respective three-letter currency codes and names.
        :rtype: dict
        :raises FixerioException: if any error making a request.
        """
        return self._symbols()

    def get_latest(self, symbols=None, base=None):
        """ Gets the exchange rate data for the currencies you have requested.

        :param symbols: a list of comma-separated currency codes to limit output currencies.
        :type symbols: list or tuple
        :param base: the three-letter currency code of your preferred base currency.
        :type base: str
        :return: the exchange rate data for the currencies you have requested.
        :rtype: dict
        :raises FixerioException: if any error making a request.
        """
        return self._latest(symbols=symbols, base=base)

    def get_historical_rates(self, date, symbols=None, base=None):
        """ Gets historical rates for any day since `date`.
        Historical rates are available for most currencies all the way back to the year of 1999.

        :param date: a date in the past for which historical rates are requested.
        :type date: date or str (in YYYY-MM-DD format)
        :param symbols: a list of currency codes to limit output currencies.
        :type symbols: list or tuple
        :param base: the three-letter currency code of your preferred base currency.
        :type base: str
        :return: the historical exchange rate data for the currencies you have requested.
        :rtype: dict
        :raises FixerioException: if any error making a request.
        """
        return self._historical_rates(date, symbols=symbols, base=base)

    def convert_amount(self, from_ccy, to_ccy, amount, date=None):
        """ Converts an amount from one currency to another

        :param from_ccy: the three-letter currency code of the currency you would like to convert from.
        :type from_ccy: str
        :param to_ccy: the three-letter currency code of the currency you would like to convert to.
        :type to_ccy: str
        :param amount: the amount to be converted.
        :type amount: int or float
        :param date: specify a date to use historical rates for this conversion.
        :type date: date or str (in YYYY-MM-DD format)
        :return: your conversion result.
        :rtype: dict
        :raises FixerioException: if any error making a request.
        """
        return self._convert(from_ccy, to_ccy, amount, date=date)

fixerio/basic_client.py
isaaclm/fixerio-client
class FixerioException(BaseException):
    """ Class for all fixer.io GET request exceptions. """
    pass

fixerio/exceptions.py
isaaclm/fixerio-client
import datetime
import requests
from .exceptions import FixerioException
from .url import BASE_HTTP_URL


class FixerioClient(object):
    """ A client for Fixer.io. """
    base_url = BASE_HTTP_URL

    def __init__(self, access_key, symbols=None, base=None):
        """
        :param access_key: your API Key.
        :type access_key: str or unicode
        :param symbols: currency symbols to request specific exchange rates.
        :type symbols: list or tuple
        :param base: currency symbol for base currency. The default base currency is EUR.
        :type base: str
        """
        self.access_key = access_key
        self.symbols = symbols
        self.base = base

    def _create_payload(self, **kwargs):
        """ Creates a payload with no None values.

        :param kwargs: keyword arguments for payload creation.
        :return: a payload.
        :rtype: dict
        """
        payload = {'access_key': self.access_key}

        # Define a mapping for variable name translation as 'from' is a reserved keyword
        variable_mapping = {
            "from_ccy": "from",
            "to_ccy": "to"
        }

        for key, value in kwargs.items():
            # Check if the variable name is in the mapping and translate it
            if key in variable_mapping:
                mapped_key = variable_mapping[key]
            else:
                mapped_key = key

            if value is not None:
                if isinstance(value, (str, int, float)):
                    payload[mapped_key] = value
                elif isinstance(value, (list, tuple)) and all(isinstance(item, str) for item in value):
                    payload[mapped_key] = ','.join(value)

        return payload

    def _symbols(self):
        """ Gets all supported currencies with their respective three-letter currency codes and names.

        :return: all supported currencies with their respective three-letter currency codes and names.
        :rtype: dict
        :raises FixerioException: if any error making a request.
        """
        try:
            payload = self._create_payload()

            url = f"{self.base_url}/symbols"

            response = requests.get(url, params=payload)

            response.raise_for_status()

            return response.json()
        except requests.exceptions.RequestException as ex:
            raise FixerioException(str(ex))

    def _latest(self, symbols=None, base=None):
        """ Gets the exchange rate data for the currencies you have requested.

        :param symbols: a list of comma-separated currency codes to limit output currencies.
        :type symbols: list or tuple
        :param base: the three-letter currency code of your preferred base currency.
        :type base: str
        :return: the exchange rate data for the currencies you have requested.
        :rtype: dict
        :raises FixerioException: if any error making a request.
        """
        try:
            symbols = symbols if (symbols is not None) else self.symbols
            base = base if (base is not None) else self.base

            payload = self._create_payload(symbols=symbols, base=base)

            url = f"{self.base_url}/latest"

            response = requests.get(url, params=payload)

            response.raise_for_status()

            return response.json()
        except requests.exceptions.RequestException as ex:
            raise FixerioException(str(ex))

    def _historical_rates(self, date, symbols=None, base=None):
        """ Gets historical rates for any day since `date`.
        Historical rates are available for most currencies all the way back to the year of 1999.

        :param date: a date in the past for which historical rates are requested.
        :type date: date or str (in YYYY-MM-DD format)
        :param symbols: a list of currency codes to limit output currencies.
        :type symbols: list or tuple
        :param base: the three-letter currency code of your preferred base currency.
        :type base: str
        :return: the historical exchange rate data for the currencies you have requested.
        :rtype: dict
        :raises FixerioException: if any error making a request.
        """
        try:
            if isinstance(date, datetime.date):
                # Convert date to ISO 8601 format.
                date = date.isoformat()

            symbols = symbols if (symbols is not None) else self.symbols
            base = base if (base is not None) else self.base

            symbols = symbols or self.symbols
            payload = self._create_payload(symbols=symbols, base=base)

            url = f"{self.base_url}/{date}"

            response = requests.get(url, params=payload)

            response.raise_for_status()

            return response.json()
        except requests.exceptions.RequestException as ex:
            raise FixerioException(str(ex))

    def _convert(self, from_ccy, to_ccy, amount, date=None):
        """ Converts an amount from one currency to another

        :param from_ccy: the three-letter currency code of the currency you would like to convert from.
        :type from_ccy: str
        :param to_ccy: the three-letter currency code of the currency you would like to convert to.
        :type to_ccy: str
        :param amount: the amount to be converted.
        :type amount: int or float
        :param date: specify a date to use historical rates for this conversion.
        :type date: date or str (in YYYY-MM-DD format)
        :return: your conversion result.
        :rtype: dict
        :raises FixerioException: if any error making a request.
        """
        try:
            if isinstance(date, datetime.date):
                # Convert date to ISO 8601 format.
                date = date.isoformat()

            payload = self._create_payload(from_ccy=from_ccy, to_ccy=to_ccy, amount=amount, date=date)

            url = f"{self.base_url}/convert"

            response = requests.get(url, params=payload)

            response.raise_for_status()

            return response.json()
        except requests.exceptions.RequestException as ex:
            raise FixerioException(str(ex))

    def _time_series(self, start_date, end_date, symbols=None, base=None):
        """ Gets daily historical rates between two dates of your choice, with a maximum time frame of 365 days.

        :param start_date: the start date of your preferred timeframe.
        :type start_date: date or str (in YYYY-MM-DD format)
        :param end_date: the end date of your preferred timeframe.
        :type end_date: date or str (in YYYY-MM-DD format)
        :param symbols: a list of currency codes to limit output currencies.
        :type symbols: list or tuple
        :param base: the three-letter currency code of your preferred base currency.
        :type base: str
        :return: the time series of exchange rates for the currencies you have requested.
        :rtype: dict
        :raises FixerioException: if any error making a request.
        """
        try:
            if isinstance(start_date, datetime.date):
                # Convert date to ISO 8601 format.
                start_date = start_date.isoformat()

            if isinstance(end_date, datetime.date):
                # Convert date to ISO 8601 format.
                end_date = end_date.isoformat()

            symbols = symbols if (symbols is not None) else self.symbols
            base = base if (base is not None) else self.base

            payload = self._create_payload(start_date=start_date, end_date=end_date, symbols=symbols, base=base)

            url = f"{self.base_url}/timeseries"

            response = requests.get(url, params=payload)

            response.raise_for_status()

            return response.json()
        except requests.exceptions.RequestException as ex:
            raise FixerioException(str(ex))

    def _fluctuation(self, start_date, end_date, symbols=None, base=None):
        """ Gets the starting and ending rates with absolute and percentage changes over the fluctuation timeframe.

        :param start_date: the start date of your preferred fluctuation timeframe.
        :type start_date: date or str (in YYYY-MM-DD format)
        :param end_date: the end date of your preferred fluctuation timeframe.
        :type end_date: date or str (in YYYY-MM-DD format)
        :param symbols: a list of currency codes to limit output currencies.
        :type symbols: list or tuple
        :param base: the three-letter currency code of your preferred base currency.
        :type base: str
        :return: the exchange rate and fluctuation data for the currencies you have requested.
        :rtype: dict
        :raises FixerioException: if any error making a request.
        """
        try:
            if isinstance(start_date, datetime.date):
                # Convert date to ISO 8601 format.
                start_date = start_date.isoformat()

            if isinstance(end_date, datetime.date):
                # Convert date to ISO 8601 format.
                end_date = end_date.isoformat()

            symbols = symbols if (symbols is not None) else self.symbols
            base = base if (base is not None) else self.base

            payload = self._create_payload(start_date=start_date, end_date=end_date, symbols=symbols, base=base)

            url = f"{self.base_url}/fluctuation"

            response = requests.get(url, params=payload)

            response.raise_for_status()

            return response.json()
        except requests.exceptions.RequestException as ex:
            raise FixerioException(str(ex))

fixerio/fixerio_client.py
isaaclm/fixerio-client
from .fixerio_client import FixerioClient
from .url import BASE_HTTP_URL

class FreeClient(FixerioClient):
    """ A client for the Fixer.io Free Plan. """

    def __init__(self, access_key, symbols=None):
        """
        :param access_key: your API Key.
        :type access_key: str or unicode
        :param symbols: currency symbols to request specific exchange rates.
        :type symbols: list or tuple
        """
        super().__init__(access_key)
        self.base_url = BASE_HTTP_URL
        self.symbols = symbols

    def get_symbols(self):
        """ Gets all supported currencies with their respective three-letter currency codes and names.

        :return: all supported currencies with their respective three-letter currency codes and names.
        :rtype: dict
        :raises FixerioException: if any error making a request.
        """
        return self._symbols()

    def get_latest(self, symbols=None):
        """ Gets the exchange rate data for the currencies you have requested.

        :param symbols: a list of comma-separated currency codes to limit output currencies.
        :type symbols: list or tuple
        :return: the exchange rate data for the currencies you have requested.
        :rtype: dict
        :raises FixerioException: if any error making a request.
        """
        return self._latest(symbols=symbols)

    def get_historical_rates(self, date, symbols=None):
        """ Gets historical rates for any day since `date`.
        Historical rates are available for most currencies all the way back to the year of 1999.

        :param date: a date in the past for which historical rates are requested.
        :type date: date or str (in YYYY-MM-DD format)
        :param symbols: a list of currency codes to limit output currencies.
        :type symbols: list or tuple
        :return: the historical exchange rate data for the currencies you have requested.
        :rtype: dict
        :raises FixerioException: if any error making a request.
        """
        return self._historical_rates(date, symbols=symbols)

fixerio/free_client.py
isaaclm/fixerio-client
from .basic_client import BasicClient
from .url import BASE_HTTPS_URL


class ProfessionalClient(BasicClient):
    """ A client for the Fixer.io Professional Plan. """

    def __init__(self, access_key, symbols=None, base=None):
        """
        :param access_key: your API Key.
        :type access_key: str or unicode
        :param symbols: currency symbols to request specific exchange rates.
        :type symbols: list or tuple
        :param base: currency symbol for base currency. The default base currency is EUR.
        :type base: str
        """
        super().__init__(access_key)
        self.base_url = BASE_HTTPS_URL
        self.symbols = symbols
        self.base = base

    def get_symbols(self):
        """ Gets all supported currencies with their respective three-letter currency codes and names.

        :return: all supported currencies with their respective three-letter currency codes and names.
        :rtype: dict
        :raises FixerioException: if any error making a request.
        """
        return self._symbols()

    def get_latest(self, symbols=None, base=None):
        """ Gets the exchange rate data for the currencies you have requested.

        :param symbols: a list of comma-separated currency codes to limit output currencies.
        :type symbols: list or tuple
        :param base: the three-letter currency code of your preferred base currency.
        :type base: str
        :return: the exchange rate data for the currencies you have requested.
        :rtype: dict
        :raises FixerioException: if any error making a request.
        """
        return self._latest(symbols=symbols, base=base)

    def get_historical_rates(self, date, symbols=None, base=None):
        """ Gets historical rates for any day since `date`.
        Historical rates are available for most currencies all the way back to the year of 1999.

        :param date: a date in the past for which historical rates are requested.
        :type date: date or str (in YYYY-MM-DD format)
        :param symbols: a list of currency codes to limit output currencies.
        :type symbols: list or tuple
        :param base: the three-letter currency code of your preferred base currency.
        :type base: str
        :return: the historical exchange rate data for the currencies you have requested.
        :rtype: dict
        :raises FixerioException: if any error making a request.
        """
        return self._historical_rates(date, symbols=symbols, base=base)

    def convert_amount(self, from_ccy, to_ccy, amount, date=None):
        """ Converts an amount from one currency to another

        :param from_ccy: the three-letter currency code of the currency you would like to convert from.
        :type from_ccy: str
        :param to_ccy: the three-letter currency code of the currency you would like to convert to.
        :type to_ccy: str
        :param amount: the amount to be converted.
        :type amount: int or float
        :param date: specify a date to use historical rates for this conversion.
        :type date: date or str (in YYYY-MM-DD format)
        :return: your conversion result.
        :rtype: dict
        :raises FixerioException: if any error making a request.
        """
        return self._convert(from_ccy, to_ccy, amount, date=date)

    def get_time_series(self, start_date, end_date, symbols=None, base=None):
        """ Gets daily historical rates between two dates of your choice, with a maximum time frame of 365 days.

        :param start_date: the start date of your preferred timeframe.
        :type start_date: date or str (in YYYY-MM-DD format)
        :param end_date: the end date of your preferred timeframe.
        :type end_date: date or str (in YYYY-MM-DD format)
        :param symbols: a list of currency codes to limit output currencies.
        :type symbols: list or tuple
        :param base: the three-letter currency code of your preferred base currency.
        :type base: str
        :return: the time series of exchange rates for the currencies you have requested.
        :rtype: dict
        :raises FixerioException: if any error making a request.
        """
        return self._time_series(start_date, end_date, symbols=symbols, base=base)

fixerio/professional_client.py
isaaclm/fixerio-client
from .professional_client import ProfessionalClient
from .url import BASE_HTTPS_URL


class ProfessionalPlusClient(ProfessionalClient):
    """ A client for the Fixer.io Professional Plus Plan. """

    def __init__(self, access_key, symbols=None, base=None):
        """
        :param access_key: your API Key.
        :type access_key: str or unicode
        :param symbols: currency symbols to request specific exchange rates.
        :type symbols: list or tuple
        :param base: currency symbol for base currency. The default base currency is EUR.
        :type base: str
        """
        super().__init__(access_key)
        self.base_url = BASE_HTTPS_URL
        self.symbols = symbols
        self.base = base

    def get_symbols(self):
        """ Gets all supported currencies with their respective three-letter currency codes and names.

        :return: all supported currencies with their respective three-letter currency codes and names.
        :rtype: dict
        :raises FixerioException: if any error making a request.
        """
        return self._symbols()

    def get_latest(self, symbols=None, base=None):
        """ Gets the exchange rate data for the currencies you have requested.

        :param symbols: a list of comma-separated currency codes to limit output currencies.
        :type symbols: list or tuple
        :param base: the three-letter currency code of your preferred base currency.
        :type base: str
        :return: the exchange rate data for the currencies you have requested.
        :rtype: dict
        :raises FixerioException: if any error making a request.
        """
        return self._latest(symbols=symbols, base=base)

    def get_historical_rates(self, date, symbols=None, base=None):
        """ Gets historical rates for any day since `date`.
        Historical rates are available for most currencies all the way back to the year of 1999.

        :param date: a date in the past for which historical rates are requested.
        :type date: date or str (in YYYY-MM-DD format)
        :param symbols: a list of currency codes to limit output currencies.
        :type symbols: list or tuple
        :param base: the three-letter currency code of your preferred base currency.
        :type base: str
        :return: the historical exchange rate data for the currencies you have requested.
        :rtype: dict
        :raises FixerioException: if any error making a request.
        """
        return self._historical_rates(date, symbols=symbols, base=base)

    def convert_amount(self, from_ccy, to_ccy, amount, date=None):
        """ Converts an amount from one currency to another

        :param from_ccy: the three-letter currency code of the currency you would like to convert from.
        :type from_ccy: str
        :param to_ccy: the three-letter currency code of the currency you would like to convert to.
        :type to_ccy: str
        :param amount: the amount to be converted.
        :type amount: int or float
        :param date: specify a date to use historical rates for this conversion.
        :type date: date or str (in YYYY-MM-DD format)
        :return: your conversion result.
        :rtype: dict
        :raises FixerioException: if any error making a request.
        """
        return self._convert(from_ccy, to_ccy, amount, date=date)

    def get_time_series(self, start_date, end_date, symbols=None, base=None):
        """ Gets daily historical rates between two dates of your choice, with a maximum time frame of 365 days.

        :param start_date: the start date of your preferred timeframe.
        :type start_date: date or str (in YYYY-MM-DD format)
        :param end_date: the end date of your preferred timeframe.
        :type end_date: date or str (in YYYY-MM-DD format)
        :param symbols: a list of currency codes to limit output currencies.
        :type symbols: list or tuple
        :param base: the three-letter currency code of your preferred base currency.
        :type base: str
        :return: the time series of exchange rates for the currencies you have requested.
        :rtype: dict
        :raises FixerioException: if any error making a request.
        """
        return self._time_series(start_date, end_date, symbols=symbols, base=base)

    def get_fluctuation(self, start_date, end_date, symbols=None, base=None):
        """ Gets the starting and ending rates with absolute and percentage changes over the fluctuation timeframe.

        :param start_date: the start date of your preferred fluctuation timeframe.
        :type start_date: date or str (in YYYY-MM-DD format)
        :param end_date: the end date of your preferred fluctuation timeframe.
        :type end_date: date or str (in YYYY-MM-DD format)
        :param symbols: a list of currency codes to limit output currencies.
        :type symbols: list or tuple
        :param base: the three-letter currency code of your preferred base currency.
        :type base: str
        :return: the exchange rate and fluctuation data for the currencies you have requested.
        :rtype: dict
        :raises FixerioException: if any error making a request.
        """
        return self._fluctuation(start_date, end_date, symbols=symbols, base=base)

fixerio/professional_plus_client.py
isaaclm/fixerio-client
BASE_HTTP_URL = 'http://data.fixer.io/api'
BASE_HTTPS_URL = 'https://data.fixer.io/api'

fixerio/url.py
isaaclm/fixerio-client
from setuptools import setup, find_packages

setup(
    name='fixerio-client',
    version='0.1.0',
    description='A Python client for the fixer.io foreign exchange API.',
    long_description='A library of clients for the fixer.io foreign exchange API. It includes clients for each tier '
                     'of subscription from Free to Professional Plus. This library is an extended adaption of fixerio '
                     'by amatellanes (https://github.com/amatellanes/fixerio)',
    author='Isaac',
    url='https://github.com/isaaclm/fixerio-client',
    license='MIT License',
    packages=['fixerio'],
    package_dir={'fixerio': 'fixerio'},
    install_requires=[
        'requests'
    ],
    classifiers=[
        'Development Status :: 3 - Alpha',
        'Intended Audience :: Developers',
        'Natural Language :: English',
        'License :: OSI Approved :: MIT License',
        'Programming Language :: Python :: 3',
        'Programming Language :: Python :: 3.4',
        'Programming Language :: Python :: 3.5',
        'Programming Language :: Python :: 3.6',
        'Programming Language :: Python :: 3.7',
    ],
)
setup.py
isaaclm/fixerio-client
import unittest

from fixerio.fixerio_client import FixerioClient


class FixerioInitTestCase(unittest.TestCase):
    def test_raises_if_access_key_is_not_passed(self):
        with self.assertRaises(TypeError):
            FixerioClient()

    def test_sets_access_key(self):
        access_key = "test-access-key"

        client = FixerioClient(access_key)

        self.assertEqual(client.access_key, access_key)

    def test_sets_none_symbols_attribute_if_it_is_not_passed(self):
        client = FixerioClient("test-access-key")

        self.assertIsNone(client.symbols)

    def test_sets_symbols_attribute(self):
        symbols = ["USD", "GBP"]

        client = FixerioClient("test-access-key", symbols=symbols)

        self.assertEqual(client.symbols, symbols)

    def test_sets_none_base_attribute_if_it_is_not_passed(self):
        client = FixerioClient("test-access-key")

        self.assertIsNone(client.base)

    def test_sets_base_attribute(self):
        base = "USD"

        client = FixerioClient("test-access-key", base=base)

        self.assertEqual(client.base, base)

    def test_sets_base_url_attribute(self):
        base_url = "http://test.url"

        client = FixerioClient("test-access-key")
        client.base_url = base_url

        self.assertEqual(client.base_url, base_url)

    def test_access_key_in_payload(self):
        access_key = "test-access-key"
        expected_payload = {
            "access_key": access_key
        }

        client = FixerioClient("test-access-key")
        payload = client._create_payload()

        self.assertEqual(payload, expected_payload)

    def test_does_not_set_none_payloads(self):
        access_key = "test-access-key"
        payload_param = None
        expected_payload = {
            "access_key": access_key
        }

        client = FixerioClient("test-access-key")
        payload = client._create_payload(payload_param=payload_param)

        self.assertEqual(payload, expected_payload)

    def test_sets_non_none_payloads(self):
        access_key = "test-access-key"
        payload_param = "test-payload"
        expected_payload = {
            "access_key": access_key,
            "payload_param": payload_param
        }

        client = FixerioClient("test-access-key")
        payload = client._create_payload(payload_param=payload_param)

        self.assertEqual(payload, expected_payload)

    def test_list_payloads(self):
        access_key = "test-access-key"
        payload_param = ["a", "b", "c"]
        expected_payload = {
            "access_key": access_key,
            "payload_param": "a,b,c"
        }

        client = FixerioClient("test-access-key")
        payload = client._create_payload(payload_param=payload_param)

        self.assertEqual(payload, expected_payload)

    def test_single_item_list_payloads(self):
        access_key = "test-access-key"
        payload_param = ["a"]
        expected_payload = {
            "access_key": access_key,
            "payload_param": "a"
        }

        client = FixerioClient("test-access-key")
        payload = client._create_payload(payload_param=payload_param)

        self.assertEqual(payload, expected_payload)

    def test_sets_special_payload_mappings(self):
        access_key = "test-access-key"
        from_ccy = "ABC"
        to_ccy = "DEF"
        expected_payload = {
            "access_key": access_key,
            "from": from_ccy,
            "to": to_ccy
        }

        client = FixerioClient("test-access-key")
        payload = client._create_payload(from_ccy=from_ccy, to_ccy=to_ccy)

        self.assertEqual(payload, expected_payload)

tests/test_base_client.py
isaaclm/fixerio-client
import unittest
from unittest.mock import patch

import fixerio.free_client
from fixerio.basic_client import BasicClient


class FixerioBasicClientTestCase(unittest.TestCase):

    @patch('fixerio.basic_client.BasicClient._symbols')  # Mocking the _symbols method
    def test_get_symbols_calls_symbols_once_and_returns_value(self, mock_symbols):
        client = BasicClient(access_key='test-access-key')

        expected_return_value = {"USD": "US Dollar", "EUR": "Euro"}
        mock_symbols.return_value = expected_return_value

        result = client.get_symbols()

        mock_symbols.assert_called_once()

        self.assertEqual(result, expected_return_value)

    @patch('fixerio.basic_client.BasicClient._latest')  # Mocking the _latest method
    def test_get_latest_calls_latest_once_and_returns_value(self, mock_latest):
        client = BasicClient(access_key='test-access-key')

        expected_return_value = {"USD": 1.0, "EUR": 0.85}
        mock_latest.return_value = expected_return_value

        result = client.get_latest(symbols=["USD", "EUR"], base="USD")

        mock_latest.assert_called_once()

        self.assertEqual(result, expected_return_value)

    @patch('fixerio.basic_client.BasicClient._historical_rates')  # Mocking the _historical_rates method
    def test_get_historical_rates_calls_historical_rates_once_and_returns_value(self, mock_historical_rates):
        client = BasicClient(access_key='test-access-key')

        date = "2001-01-01"
        expected_return_value = {"USD": 1.1, "GBP": 0.9, "date": date}
        mock_historical_rates.return_value = expected_return_value

        result = client._historical_rates(date, symbols=["USD", "EUR"], base="USD")

        mock_historical_rates.assert_called_once()

        self.assertEqual(result, expected_return_value)

    @patch('fixerio.basic_client.BasicClient._convert')  # Mocking the _convert method
    def test_convert_amount_rates_calls_convert_once_and_returns_value(self, mock_convert):
        client = BasicClient(access_key='test-access-key')

        expected_return_value = {"result": 85.0}
        mock_convert.return_value = expected_return_value

        result = client.convert_amount("USD", "EUR", 100)

        mock_convert.assert_called_once()

        self.assertEqual(result, expected_return_value)

    def test_inherited_from_cheaper_subscriptions(self):
        self.assertTrue(issubclass(BasicClient, fixerio.free_client.FreeClient))

tests/test_basic_client.py
isaaclm/fixerio-client
import unittest
import requests
from datetime import datetime
from unittest.mock import patch
from fixerio.fixerio_client import FixerioClient, FixerioException


class FixerioConvertTestCase(unittest.TestCase):

    def setUp(self):
        self.access_key = 'your-access-key'
        self.date_as_str = "2006-12-24"
        self.date_as_date = datetime.strptime(self.date_as_str, "%Y-%m-%d").date()
        self.client = FixerioClient(self.access_key)
        self.expected_url = "http://data.fixer.io/api/convert"

    @patch('requests.get')
    def test_url(self, mock_requests_get):
        from_ccy = "USD"
        to_ccy = "EUR"
        amount = 100

        expected_payload = {
            "access_key": self.access_key,
            "from": from_ccy,
            "to": to_ccy,
            "amount": amount
        }

        self.client._convert(from_ccy, to_ccy, amount)

        mock_requests_get.assert_called_with(self.expected_url, params=expected_payload)

    @patch("requests.get")
    def test_request_error_raises_fixer_exception(self, mock_requests_get):
        from_ccy = "USD"
        to_ccy = "EUR"
        amount = 100

        mock_requests_get.side_effect = requests.exceptions.RequestException("Request failed")

        with self.assertRaises(FixerioException):
            self.client._convert(from_ccy, to_ccy, amount)

    @patch("requests.get")
    def test_date_in_payload(self, mock_requests_get):
        from_ccy = "USD"
        to_ccy = "EUR"
        amount = 100

        expected_payload = {
            "access_key": self.access_key,
            "from": from_ccy,
            "to": to_ccy,
            "amount": amount,
            "date": self.date_as_str
        }

        self.client._convert(from_ccy, to_ccy, amount, date=self.date_as_date)

        mock_requests_get.assert_called_with(self.expected_url, params=expected_payload)

    @patch("requests.get")
    def test_date_string_in_payload(self, mock_requests_get):
        from_ccy = "USD"
        to_ccy = "EUR"
        amount = 100

        expected_payload = {
            "access_key": self.access_key,
            "from": from_ccy,
            "to": to_ccy,
            "amount": amount,
            "date": self.date_as_str
        }

        self.client._convert(from_ccy, to_ccy, amount, date=self.date_as_str)

        mock_requests_get.assert_called_with(self.expected_url, params=expected_payload)

tests/test_convert_endpoint.py
isaaclm/fixerio-client
import unittest
import requests
from datetime import datetime
from unittest.mock import patch
from fixerio.fixerio_client import FixerioClient, FixerioException


class FixerioFluctuationTestCase(unittest.TestCase):

    def setUp(self):
        self.access_key = 'your-access-key'
        self.start_date_as_str = "2006-12-24"
        self.start_date_as_date = datetime.strptime(self.start_date_as_str, "%Y-%m-%d").date()
        self.end_date_as_str = "2006-12-24"
        self.end_date_as_date = datetime.strptime(self.start_date_as_str, "%Y-%m-%d").date()
        self.client = FixerioClient(self.access_key)
        self.expected_url = "http://data.fixer.io/api/fluctuation"

    @patch('requests.get')
    def test_url_with_dates(self, mock_requests_get):
        expected_payload = {
            "access_key": self.access_key,
            "start_date": self.start_date_as_str,
            "end_date": self.end_date_as_str
        }

        self.client._fluctuation(self.start_date_as_date, self.end_date_as_date)

        mock_requests_get.assert_called_with(self.expected_url, params=expected_payload)\

    @patch('requests.get')
    def test_url_with_date_strings(self, mock_requests_get):
        expected_payload = {
            "access_key": self.access_key,
            "start_date": self.start_date_as_str,
            "end_date": self.end_date_as_str
        }

        self.client._fluctuation(self.start_date_as_str, self.end_date_as_str)

        mock_requests_get.assert_called_with(self.expected_url, params=expected_payload)

    @patch("requests.get")
    def test_request_error_raises_fixer_exception(self, mock_requests_get):
        mock_requests_get.side_effect = requests.exceptions.RequestException("Request failed")

        with self.assertRaises(FixerioException):
            self.client._fluctuation(self.start_date_as_date, self.end_date_as_date)

    @patch("requests.get")
    def test_symbol_in_payload(self, mock_requests_get):
        symbols = ["USD", "EUR"]

        expected_payload = {
            "access_key": self.access_key,
            "start_date": self.start_date_as_str,
            "end_date": self.end_date_as_str,
            "symbols": ",".join(symbols)
        }

        self.client._fluctuation(self.start_date_as_date, self.end_date_as_date, symbols=symbols)

        mock_requests_get.assert_called_with(self.expected_url, params=expected_payload)

    @patch("requests.get")
    def test_base_in_payload(self, mock_requests_get):
        base = "GBP"

        expected_payload = {
            "access_key": self.access_key,
            "start_date": self.start_date_as_str,
            "end_date": self.end_date_as_str,
            "base": base
        }

        self.client._fluctuation(self.start_date_as_date, self.end_date_as_date, base=base)

        mock_requests_get.assert_called_with(self.expected_url, params=expected_payload)

    @patch("requests.get")
    def test_symbols_and_base_in_payload(self, mock_requests_get):
        symbols = ["USD", "EUR"]
        base = "GBP"

        expected_payload = {
            "access_key": self.access_key,
            "start_date": self.start_date_as_str,
            "end_date": self.end_date_as_str,
            "symbols" : ",".join(symbols),
            "base": base
        }

        self.client._fluctuation(self.start_date_as_date, self.end_date_as_date, symbols=symbols, base=base)

        mock_requests_get.assert_called_with(self.expected_url, params=expected_payload)

tests/test_fluctuation_endpoint.py
isaaclm/fixerio-client
import unittest
from unittest.mock import patch
from fixerio.free_client import FreeClient


class FixerioFreeClientTestCase(unittest.TestCase):

    @patch('fixerio.free_client.FreeClient._symbols')  # Mocking the _symbols method
    def test_get_symbols_calls_symbols_once_and_returns_value(self, mock_symbols):
        client = FreeClient(access_key='test-access-key')

        expected_return_value = {"USD": "US Dollar", "EUR": "Euro"}
        mock_symbols.return_value = expected_return_value

        result = client.get_symbols()

        mock_symbols.assert_called_once()

        self.assertEqual(result, expected_return_value)

    @patch('fixerio.free_client.FreeClient._latest')  # Mocking the _latest method
    def test_get_latest_calls_latest_once_and_returns_value(self, mock_latest):
        client = FreeClient(access_key='test-access-key')

        expected_return_value = {"USD": 1.1, "GBP": 0.9}
        mock_latest.return_value = expected_return_value

        result = client.get_latest()

        mock_latest.assert_called_once()

        self.assertEqual(result, expected_return_value)

    @patch('fixerio.free_client.FreeClient._historical_rates')  # Mocking the _historical_rates method
    def test_get_historical_rates_calls_historical_rates_once_and_returns_value(self, mock_historical_rates):
        client = FreeClient(access_key='test-access-key')

        date = "2001-01-01"
        expected_return_value = {"USD": 1.1, "EUR": 1.0, "date": date}
        mock_historical_rates.return_value = expected_return_value

        result = client._historical_rates(date, symbols=["USD", "EUR"])

        mock_historical_rates.assert_called_once()

        self.assertEqual(result, expected_return_value)

tests/test_free_client.py
isaaclm/fixerio-client
import unittest
import requests
from datetime import datetime
from unittest.mock import patch
from fixerio.fixerio_client import FixerioClient, FixerioException


class FixerioHistoricalRatesTestCase(unittest.TestCase):

    def setUp(self):
        self.access_key = 'your-access-key'
        self.date_as_str = "2006-12-24"
        self.date_as_date = datetime.strptime(self.date_as_str, "%Y-%m-%d").date()
        self.client = FixerioClient(self.access_key)
        self.expected_url = f"http://data.fixer.io/api/{self.date_as_str}"

    @patch('requests.get')
    def test_historical_rates_url_with_date(self, mock_requests_get):
        expected_payload = {
            "access_key": self.access_key
        }

        self.client._historical_rates(self.date_as_date)

        mock_requests_get.assert_called_with(self.expected_url, params=expected_payload)

    @patch('requests.get')
    def test_historical_rates_url_with_date_string(self, mock_requests_get):
        expected_payload = {
            "access_key": self.access_key
        }

        self.client._historical_rates(self.date_as_str)

        mock_requests_get.assert_called_with(self.expected_url, params=expected_payload)

    @patch("requests.get")
    def test_request_error_raises_fixer_exception(self, mock_requests_get):
        mock_requests_get.side_effect = requests.exceptions.RequestException("Request failed")

        with self.assertRaises(FixerioException):
            self.client._historical_rates(self.date_as_date)

    @patch("requests.get")
    def test_symbol_in_payload(self, mock_requests_get):
        symbols = ["USD", "EUR"]

        expected_payload = {
            "access_key": self.access_key,
            "symbols": ",".join(symbols)
        }

        self.client._historical_rates(self.date_as_date, symbols=symbols)

        mock_requests_get.assert_called_with(self.expected_url, params=expected_payload)

    @patch("requests.get")
    def test_base_in_payload(self, mock_requests_get):
        base = "GBP"

        expected_payload = {
            "access_key": self.access_key,
            "base": base
        }

        self.client._historical_rates(self.date_as_date, base=base)

        mock_requests_get.assert_called_with(self.expected_url, params=expected_payload)

    @patch("requests.get")
    def test_symbols_and_base_in_payload(self, mock_requests_get):
        symbols = ["USD", "EUR"]
        base = "GBP"

        expected_payload = {
            "access_key": self.access_key,
            "symbols": ",".join(symbols),
            "base": base
        }

        self.client._historical_rates(self.date_as_str, symbols=symbols, base=base)

        mock_requests_get.assert_called_with(self.expected_url, params=expected_payload)

tests/test_historical_rates_endpoint.py
isaaclm/fixerio-client
import unittest
import requests
from unittest.mock import patch
from fixerio.fixerio_client import FixerioClient, FixerioException


class FixerioLatestTestCase(unittest.TestCase):

    def setUp(self):
        self.access_key = 'your-access-key'
        self.client = FixerioClient(self.access_key)
        self.expected_url = "http://data.fixer.io/api/latest"

    @patch('requests.get')
    def test_url(self, mock_requests_get):
        expected_payload = {
            "access_key": self.access_key
        }

        self.client._latest()

        mock_requests_get.assert_called_with(self.expected_url, params=expected_payload)

    @patch("requests.get")
    def test_request_error_raises_fixer_exception(self, mock_requests_get):
        mock_requests_get.side_effect = requests.exceptions.RequestException("Request failed")

        with self.assertRaises(FixerioException):
            self.client._latest()

    @patch("requests.get")
    def test_symbol_in_payload(self, mock_requests_get):
        symbols = ["USD", "EUR"]

        expected_payload = {
            "access_key": self.access_key,
            "symbols": ",".join(symbols)
        }

        self.client._latest(symbols=symbols)

        mock_requests_get.assert_called_with(self.expected_url, params=expected_payload)

    @patch("requests.get")
    def test_base_in_payload(self, mock_requests_get):
        base = "GBP"

        expected_payload = {
            "access_key": self.access_key,
            "base": base
        }

        self.client._latest(base=base)

        mock_requests_get.assert_called_with(self.expected_url, params=expected_payload)

    @patch("requests.get")
    def test_symbols_and_base_in_payload(self, mock_requests_get):
        symbols = ["USD", "EUR"]
        base = "GBP"

        expected_payload = {
            "access_key": self.access_key,
            "symbols": ",".join(symbols),
            "base": base
        }

        self.client._latest(symbols=symbols, base=base)

        mock_requests_get.assert_called_with(self.expected_url, params=expected_payload)

tests/test_latest_endpoint.py
isaaclm/fixerio-client
import unittest
from unittest.mock import patch

import fixerio.basic_client
from fixerio.professional_client import ProfessionalClient


class FixerioBasicClientTestCase(unittest.TestCase):

    @patch('fixerio.professional_client.ProfessionalClient._symbols')  # Mocking the _symbols method
    def test_get_symbols_calls_symbols_once_and_returns_value(self, mock_symbols):
        client = ProfessionalClient(access_key='test-access-key')

        expected_return_value = {"USD": "US Dollar", "EUR": "Euro"}
        mock_symbols.return_value = expected_return_value

        result = client.get_symbols()

        mock_symbols.assert_called_once()

        self.assertEqual(result, expected_return_value)

    @patch('fixerio.professional_client.ProfessionalClient._latest')  # Mocking the _latest method
    def test_get_latest_calls_latest_once_and_returns_value(self, mock_latest):
        client = ProfessionalClient(access_key='test-access-key')

        expected_return_value = {"USD": 1.0, "EUR": 0.85}
        mock_latest.return_value = expected_return_value

        result = client.get_latest(symbols=["USD", "EUR"], base="USD")

        mock_latest.assert_called_once()

        self.assertEqual(result, expected_return_value)

    @patch('fixerio.professional_client.ProfessionalClient._historical_rates')  # Mocking the _historical_rates method
    def test_get_historical_rates_calls_historical_rates_once_and_returns_value(self, mock_historical_rates):
        client = ProfessionalClient(access_key='test-access-key')

        date = "2001-01-01"
        expected_return_value = {"USD": 1.1, "GBP": 0.9, "date": date}
        mock_historical_rates.return_value = expected_return_value

        result = client._historical_rates(date, symbols=["USD", "EUR"], base="USD")

        mock_historical_rates.assert_called_once()

        self.assertEqual(result, expected_return_value)

    @patch('fixerio.professional_client.ProfessionalClient._convert')  # Mocking the _convert method
    def test_convert_amount_rates_calls_convert_once_and_returns_value(self, mock_convert):
        client = ProfessionalClient(access_key='test-access-key')

        expected_return_value = {"result": 85.0}
        mock_convert.return_value = expected_return_value

        result = client.convert_amount("USD", "EUR", 100)

        mock_convert.assert_called_once()

        self.assertEqual(result, expected_return_value)

    @patch('fixerio.professional_client.ProfessionalClient._time_series')  # Mocking the _time_series method
    def test_get_time_series_rates_calls_time_series_once_and_returns_value(self, mock_time_series):
        client = ProfessionalClient(access_key='test-access-key')

        start_date = "2001-01-01"
        end_date = "2001-01-02"
        expected_return_value = {"2001-01-01": 1.0}
        mock_time_series.return_value = expected_return_value

        result = client.get_time_series(start_date, end_date, ["USD"], "USD")

        mock_time_series.assert_called_once()

        self.assertEqual(result, expected_return_value)

    def test_inherited_from_cheaper_subscriptions(self):
        self.assertTrue(issubclass(ProfessionalClient, fixerio.free_client.FreeClient))
        self.assertTrue(issubclass(ProfessionalClient, fixerio.basic_client.BasicClient))

tests/test_professional_client.py
isaaclm/fixerio-client
import unittest
from unittest.mock import patch

import fixerio.basic_client
from fixerio.professional_plus_client import ProfessionalPlusClient


class FixerioBasicClientTestCase(unittest.TestCase):

    @patch('fixerio.professional_plus_client.ProfessionalPlusClient._symbols')  # Mocking the _symbols method
    def test_get_symbols_calls_symbols_once_and_returns_value(self, mock_symbols):
        client = ProfessionalPlusClient(access_key='test-access-key')

        expected_return_value = {"USD": "US Dollar", "EUR": "Euro"}
        mock_symbols.return_value = expected_return_value

        result = client.get_symbols()

        mock_symbols.assert_called_once()

        self.assertEqual(result, expected_return_value)

    @patch('fixerio.professional_plus_client.ProfessionalPlusClient._latest')  # Mocking the _latest method
    def test_get_latest_calls_latest_once_and_returns_value(self, mock_latest):
        client = ProfessionalPlusClient(access_key='test-access-key')

        expected_return_value = {"USD": 1.0, "EUR": 0.85}
        mock_latest.return_value = expected_return_value

        result = client.get_latest(symbols=["USD", "EUR"], base="USD")

        mock_latest.assert_called_once()

        self.assertEqual(result, expected_return_value)

    @patch('fixerio.professional_plus_client.ProfessionalPlusClient._historical_rates')  # Mocking the _historical_rates method
    def test_get_historical_rates_calls_historical_rates_once_and_returns_value(self, mock_historical_rates):
        client = ProfessionalPlusClient(access_key='test-access-key')

        date = "2001-01-01"
        expected_return_value = {"USD": 1.1, "GBP": 0.9, "date": date}
        mock_historical_rates.return_value = expected_return_value

        result = client._historical_rates(date, symbols=["USD", "EUR"], base="USD")

        mock_historical_rates.assert_called_once()

        self.assertEqual(result, expected_return_value)

    @patch('fixerio.professional_plus_client.ProfessionalPlusClient._convert')  # Mocking the _convert method
    def test_convert_amount_rates_calls_convert_once_and_returns_value(self, mock_convert):
        client = ProfessionalPlusClient(access_key='test-access-key')

        expected_return_value = {"result": 85.0}
        mock_convert.return_value = expected_return_value

        result = client.convert_amount("USD", "EUR", 100)

        mock_convert.assert_called_once()

        self.assertEqual(result, expected_return_value)

    @patch('fixerio.professional_plus_client.ProfessionalPlusClient._time_series')  # Mocking the _time_series method
    def test_get_time_series_rates_calls_time_series_once_and_returns_value(self, mock_time_series):
        client = ProfessionalPlusClient(access_key='test-access-key')

        start_date = "2001-01-01"
        end_date = "2001-01-02"
        expected_return_value = {"2001-01-01": 1.0}
        mock_time_series.return_value = expected_return_value

        result = client.get_time_series(start_date, end_date, ["USD"], "USD")

        mock_time_series.assert_called_once()

        self.assertEqual(result, expected_return_value)

    @patch('fixerio.professional_plus_client.ProfessionalPlusClient._fluctuation')  # Mocking the _fluctuation method
    def test_get_fluctuation_series_rates_calls_time_series_once_and_returns_value(self, mock_fluctuation):
        client = ProfessionalPlusClient(access_key='test-access-key')

        start_date = "2001-01-01"
        end_date = "2001-01-02"
        expected_return_value = {"2001-01-01": 1.0}
        mock_fluctuation.return_value = expected_return_value

        result = client.get_fluctuation(start_date, end_date, ["USD"], "USD")

        mock_fluctuation.assert_called_once()

        self.assertEqual(result, expected_return_value)

    def test_inherited_from_cheaper_subscriptions(self):
        self.assertTrue(issubclass(ProfessionalPlusClient, fixerio.free_client.FreeClient))
        self.assertTrue(issubclass(ProfessionalPlusClient, fixerio.basic_client.BasicClient))
        self.assertTrue(issubclass(ProfessionalPlusClient, fixerio.professional_client.ProfessionalClient))

tests/test_professional_plus_client.py
isaaclm/fixerio-client
import unittest
import requests
from unittest.mock import patch
from fixerio.fixerio_client import FixerioClient, FixerioException


class FixerioSymbolsTestCase(unittest.TestCase):

    def setUp(self):
        self.access_key = 'your-access-key'
        self.client = FixerioClient(self.access_key)
        self.expected_url = "http://data.fixer.io/api/symbols"

    @patch('requests.get')
    def test_url(self, mock_requests_get):
        expected_payload = {
            "access_key": self.access_key
        }

        self.client._symbols()

        mock_requests_get.assert_called_with(self.expected_url, params=expected_payload)

    @patch("requests.get")
    def test_request_error_raises_fixer_exception(self, mock_requests_get):
        mock_requests_get.side_effect = requests.exceptions.RequestException("Request failed")

        with self.assertRaises(FixerioException):
            self.client._symbols()

tests/test_symbols_endpoint.py
isaaclm/fixerio-client
import unittest
import requests
from datetime import datetime
from unittest.mock import patch
from fixerio.fixerio_client import FixerioClient, FixerioException


class FixerioTimeSeriesTestCase(unittest.TestCase):

    def setUp(self):
        self.access_key = 'your-access-key'
        self.start_date_as_str = "2006-12-24"
        self.start_date_as_date = datetime.strptime(self.start_date_as_str, "%Y-%m-%d").date()
        self.end_date_as_str = "2006-12-24"
        self.end_date_as_date = datetime.strptime(self.start_date_as_str, "%Y-%m-%d").date()
        self.client = FixerioClient(self.access_key)
        self.expected_url = "http://data.fixer.io/api/timeseries"

    @patch('requests.get')
    def test_url_with_dates(self, mock_requests_get):
        expected_payload = {
            "access_key": self.access_key,
            "start_date": self.start_date_as_str,
            "end_date": self.end_date_as_str
        }

        self.client._time_series(self.start_date_as_date, self.end_date_as_date)

        mock_requests_get.assert_called_with(self.expected_url, params=expected_payload)\

    @patch('requests.get')
    def test_url_with_date_strings(self, mock_requests_get):
        expected_payload = {
            "access_key": self.access_key,
            "start_date": self.start_date_as_str,
            "end_date": self.end_date_as_str
        }

        self.client._time_series(self.start_date_as_str, self.end_date_as_str)

        mock_requests_get.assert_called_with(self.expected_url, params=expected_payload)

    @patch("requests.get")
    def test_request_error_raises_fixer_exception(self, mock_requests_get):
        mock_requests_get.side_effect = requests.exceptions.RequestException("Request failed")

        with self.assertRaises(FixerioException):
            self.client._time_series(self.start_date_as_date, self.end_date_as_date)

    @patch("requests.get")
    def test_symbol_in_payload(self, mock_requests_get):
        symbols = ["USD", "EUR"]

        expected_payload = {
            "access_key": self.access_key,
            "start_date": self.start_date_as_str,
            "end_date": self.end_date_as_str,
            "symbols": ",".join(symbols)
        }

        self.client._time_series(self.start_date_as_date, self.end_date_as_date, symbols=symbols)

        mock_requests_get.assert_called_with(self.expected_url, params=expected_payload)

    @patch("requests.get")
    def test_base_in_payload(self, mock_requests_get):
        base = "GBP"

        expected_payload = {
            "access_key": self.access_key,
            "start_date": self.start_date_as_str,
            "end_date": self.end_date_as_str,
            "base": base
        }

        self.client._time_series(self.start_date_as_date, self.end_date_as_date, base=base)

        mock_requests_get.assert_called_with(self.expected_url, params=expected_payload)

    @patch("requests.get")
    def test_symbols_and_base_in_payload(self, mock_requests_get):
        symbols = ["USD", "EUR"]
        base = "GBP"

        expected_payload = {
            "access_key": self.access_key,
            "start_date": self.start_date_as_str,
            "end_date": self.end_date_as_str,
            "symbols" : ",".join(symbols),
            "base": base
        }

        self.client._time_series(self.start_date_as_date, self.end_date_as_date, symbols=symbols, base=base)

        mock_requests_get.assert_called_with(self.expected_url, params=expected_payload)

tests/test_time_series_endpoint.py
isaaclm/fixerio-client