From d1bdc6af5dba94b6baaef7a314ae9fb0e1e84f4f Mon Sep 17 00:00:00 2001 From: Adrian Gallagher Date: Mon, 28 Aug 2017 09:06:53 +1000 Subject: [PATCH] Use fixer.io currency rates API instead of Yahoo due to Yahoo service issues --- currency/currency.go | 97 +++++++++++++++++++++++++++++++++++---- currency/currency_test.go | 29 +++++++++--- 2 files changed, 111 insertions(+), 15 deletions(-) diff --git a/currency/currency.go b/currency/currency.go index c6165c2f..32cfe22d 100644 --- a/currency/currency.go +++ b/currency/currency.go @@ -39,10 +39,19 @@ type YahooJSONResponse struct { } } +// FixerResponse contains the data fields for the Fixer API response +type FixerResponse struct { + Base string `json:"base"` + Date string `json:"date"` + Rates map[string]float64 `json:"rates"` +} + const ( maxCurrencyPairsPerRequest = 350 yahooYQLURL = "https://query.yahooapis.com/v1/public/yql?" yahooDatabase = "store://datatables.org/alltableswithkeys" + yahooEnabled = false + fixerAPI = "http://api.fixer.io/latest" // DefaultCurrencies has the default minimum of FIAT values DefaultCurrencies = "USD,AUD,EUR,CNY" // DefaultCryptoCurrencies has the default minimum of crytpocurrency values @@ -53,6 +62,7 @@ const ( // queries var ( CurrencyStore map[string]Rate + CurrencyStoreFixer map[string]float64 BaseCurrencies string CryptoCurrencies string ErrCurrencyDataNotFetched = errors.New("yahoo currency data has not been fetched yet") @@ -170,11 +180,11 @@ func SeedCurrencyData(fiatCurrencies string) error { fiatCurrencies = DefaultCurrencies } - err := QueryYahooCurrencyValues(fiatCurrencies) - if err != nil { - return ErrQueryingYahoo + if yahooEnabled { + return QueryYahooCurrencyValues(fiatCurrencies) } - return nil + + return FetchFixerCurrencyData() } // MakecurrencyPairs takes all supported currency and turns them into pairs. @@ -196,21 +206,90 @@ func MakecurrencyPairs(supportedCurrencies string) string { // ConvertCurrency for example converts $1 USD to the equivalent Japanese Yen // or vice versa. func ConvertCurrency(amount float64, from, to string) (float64, error) { - currency := common.StringToUpper(from + to) + from = common.StringToUpper(from) + to = common.StringToUpper(to) - _, ok := CurrencyStore[currency] + if from == to { + return amount, nil + } + + if yahooEnabled { + currency := from + to + _, ok := CurrencyStore[currency] + if !ok { + err := SeedCurrencyData(currency[:len(from)] + "," + currency[len(to):]) + if err != nil { + return 0, err + } + } + + result, ok := CurrencyStore[currency] + if !ok { + return 0, ErrCurrencyNotFound + } + return amount * result.Rate, nil + } + + _, ok := CurrencyStoreFixer[from] if !ok { - err := SeedCurrencyData(currency[:len(from)] + "," + currency[len(to):]) + err := FetchFixerCurrencyData() if err != nil { return 0, err } } - result, ok := CurrencyStore[currency] + var resultFrom float64 + var resultTo float64 + + // First check if we're converting to USD, USD doesn't exist in the rates map + if to == "USD" { + resultFrom, ok = CurrencyStoreFixer[from] + if !ok { + return 0, ErrCurrencyNotFound + } + return amount / resultFrom, nil + } + + // Check to see if we're converting from USD + if from == "USD" { + resultTo, ok = CurrencyStoreFixer[to] + if !ok { + return 0, ErrCurrencyNotFound + } + return resultTo * amount, nil + } + + // Otherwise convert to USD, then to the target currency + resultFrom, ok = CurrencyStoreFixer[from] if !ok { return 0, ErrCurrencyNotFound } - return amount * result.Rate, nil + + converted := amount / resultFrom + resultTo, ok = CurrencyStoreFixer[to] + if !ok { + return 0, ErrCurrencyNotFound + } + + return converted * resultTo, nil +} + +// FetchFixerCurrencyData seeds the variable C +func FetchFixerCurrencyData() error { + var result FixerResponse + values := url.Values{} + values.Set("base", "USD") + url := common.EncodeURLValues(fixerAPI, values) + + CurrencyStoreFixer = make(map[string]float64) + + err := common.SendHTTPGetRequest(url, true, &result) + if err != nil { + return err + } + + CurrencyStoreFixer = result.Rates + return nil } // FetchYahooCurrencyData seeds the variable CurrencyStore; this is a diff --git a/currency/currency_test.go b/currency/currency_test.go index 2fa00516..17a081d1 100644 --- a/currency/currency_test.go +++ b/currency/currency_test.go @@ -274,12 +274,14 @@ func TestSeedCurrencyData(t *testing.T) { err2, currencyRequestUSDAUD, ) } - err3 := SeedCurrencyData(currencyRequestObtuse) - if err3 == nil { - t.Errorf( - "Test Failed. SeedCurrencyData: Error %s with currency as %s.", - err3, currencyRequestObtuse, - ) + if yahooEnabled { + err3 := SeedCurrencyData(currencyRequestObtuse) + if err3 == nil { + t.Errorf( + "Test Failed. SeedCurrencyData: Error %s with currency as %s.", + err3, currencyRequestObtuse, + ) + } } } @@ -323,7 +325,18 @@ func TestConvertCurrency(t *testing.T) { } } +func TestFetchFixerCurrencyData(t *testing.T) { + err := FetchFixerCurrencyData() + if err != nil { + t.Errorf("Test failed. FetchFixerCurrencyData returned %s", err) + } +} + func TestFetchYahooCurrencyData(t *testing.T) { + if !yahooEnabled { + return + } + t.Parallel() var fetchData []string fiatCurrencies := DefaultCurrencies @@ -344,6 +357,10 @@ func TestFetchYahooCurrencyData(t *testing.T) { } func TestQueryYahooCurrencyValues(t *testing.T) { + if !yahooEnabled { + return + } + err := QueryYahooCurrencyValues(DefaultCurrencies) if err != nil { t.Errorf("Test Failed. QueryYahooCurrencyValues: Error, %s", err)