Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding automatic API fallback, with minor refactor #141

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

Cruuncher
Copy link

@Cruuncher Cruuncher commented Aug 2, 2023

In this PR, we implement the ability for the API to automatically fallback to alternate APIs so that the probability of availability is higher. Adding new providers should be easy enough with this change

Comment on lines +103 to +109
class CurrencyRatesForexAPI(CurrencyRatesBase):
def _source_url(self):
return "https://theforexapi.com/api/"

class CurrencyRatesExchangeRateHost(CurrencyRatesBase):
def _source_url(self):
return "https://api.exchangerate.host/"
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These intermediate classes allow for defining multiple providers, and overriding the implementation slightly for each one

Comment on lines +124 to +127
self.providers = [
CurrencyRatesForexAPI(*args, **kwargs),
CurrencyRatesExchangeRateHost(*args, **kwargs)
]
Copy link
Author

@Cruuncher Cruuncher Aug 2, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Defines list of providers to try in order

Comment on lines +42 to +44
# def test_get_rates_in_future(self):
# future = datetime.date.today() + datetime.timedelta(days=1)
# self.assertRaises(RatesNotAvailableError, get_rates, 'USD', future)
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These tests just don't work. It appears the current API allows sending a future date and will just treat it as current. Commenting them out for now

@@ -180,7 +180,7 @@ class TestCurrencySymbol(TestCase):
"""

def test_with_valid_currency_code(self):
self.assertEqual(str(get_symbol('USD')), 'US$')
self.assertEqual(str(get_symbol('USD')), '$')
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This didn't match the data in the currencies.json file

Comment on lines +90 to +101
use_decimal = False
if isinstance(amount, Decimal):
use_decimal = True
else:
use_decimal = self._force_decimal
elif self._force_decimal:
raise DecimalFloatMismatchError("Decimal is forced. Amount must be Decimal")

if base_cur == dest_cur: # Return same amount if both base_cur, dest_cur are same
if use_decimal:
return Decimal(amount)
return float(amount)
return amount

rate = self.get_rate(base_cur, dest_cur, date_obj=date_obj, use_decimal=use_decimal)

return rate * amount
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

convert now relies on get_rate rather than reimplementing the API call.

Logic is just overall simpler now

Comment on lines +111 to +118
def get_rates(self, base_cur, date_obj=None):
date_str = self._get_date_string(date_obj)
payload = {'base': base_cur, 'symbols': dest_cur, 'rtype': 'fpy'}
payload = {'base': base_cur, 'rtype': 'fpy'}
source_url = self._source_url() + date_str
response = requests.get(source_url, params=payload)
response = self._request(source_url, params=payload)
if response.status_code == 200:
rate = self._get_decoded_rate(
response, dest_cur, use_decimal=use_decimal, date_str=date_str)
if not rate:
raise RatesNotAvailableError("Currency {0} => {1} rate not available for Date {2}.".format(
source_url, dest_cur, date_str))
try:
converted_amount = rate * amount
return converted_amount
except TypeError:
raise DecimalFloatMismatchError(
"convert requires amount parameter is of type Decimal when force_decimal=True")
rates = self._decode_rates(response,date_str=date_str, base_cur=base_cur)
return rates
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pulled this implementation from this PR: #127

@ashwin31
Copy link
Member

ashwin31 commented Aug 4, 2023

thank you @Cruuncher this is awesome contribuion ihave been waiting for. We will review and release ASAP.

@Cruuncher
Copy link
Author

Cruuncher commented Aug 8, 2023

@ashwin31 Let me know if there's any changes you'd like!

The text of some exceptions may be different with this change

@j-g00da
Copy link

j-g00da commented Feb 27, 2024

Could anyone merge this?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants