From 0e56237180feef6fd1562a5cc1ec93de97ad5fb7 Mon Sep 17 00:00:00 2001 From: MaysWind Date: Tue, 16 Mar 2021 00:38:06 +0800 Subject: [PATCH] add Reserve Bank of Australia exchange rate data source --- conf/lab.ini | 2 +- .../exchange_rates_datasource_container.go | 3 + .../reserve_bank_of_australia_datasource.go | 143 ++++++++++++++++++ pkg/settings/setting.go | 11 +- 4 files changed, 154 insertions(+), 5 deletions(-) create mode 100644 pkg/exchangerates/reserve_bank_of_australia_datasource.go diff --git a/conf/lab.ini b/conf/lab.ini index f26f3c5c..3b22a3b4 100644 --- a/conf/lab.ini +++ b/conf/lab.ini @@ -111,7 +111,7 @@ enable_register = true enable_export = true [exchange_rates] -# Exchange rates data source, supports "euro_central_bank", "bank_of_canada", "czech_national_bank", "national_bank_of_poland" currently +# Exchange rates data source, supports "euro_central_bank", "bank_of_canada", "reserve_bank_of_australia", "czech_national_bank", "national_bank_of_poland" currently data_source = euro_central_bank # Requesting exchange rates data timeout (milliseconds), default is 10000 (10 seconds) diff --git a/pkg/exchangerates/exchange_rates_datasource_container.go b/pkg/exchangerates/exchange_rates_datasource_container.go index 90d2f90d..717c2e34 100644 --- a/pkg/exchangerates/exchange_rates_datasource_container.go +++ b/pkg/exchangerates/exchange_rates_datasource_container.go @@ -23,6 +23,9 @@ func InitializeExchangeRatesDataSource(config *settings.Config) error { } else if config.ExchangeRatesDataSource == settings.BankOfCanadaDataSource { Container.Current = &BankOfCanadaDataSource{} return nil + } else if config.ExchangeRatesDataSource == settings.ReserveBankOfAustraliaDataSource { + Container.Current = &ReserveBankOfAustraliaDataSource{} + return nil } else if config.ExchangeRatesDataSource == settings.CzechNationalBankDataSource { Container.Current = &CzechNationalBankDataSource{} return nil diff --git a/pkg/exchangerates/reserve_bank_of_australia_datasource.go b/pkg/exchangerates/reserve_bank_of_australia_datasource.go new file mode 100644 index 00000000..93cb07bf --- /dev/null +++ b/pkg/exchangerates/reserve_bank_of_australia_datasource.go @@ -0,0 +1,143 @@ +package exchangerates + +import ( + "encoding/xml" + "time" + + "github.com/mayswind/lab/pkg/core" + "github.com/mayswind/lab/pkg/errs" + "github.com/mayswind/lab/pkg/log" + "github.com/mayswind/lab/pkg/models" + "github.com/mayswind/lab/pkg/validators" +) + +const reserveBankOfAustraliaExchangeRateUrl = "https://www.rba.gov.au/rss/rss-cb-exchange-rates.xml" +const reserveBankOfAustraliaExchangeRateReferenceUrl = "https://www.rba.gov.au/statistics/frequency/exchange-rates.html" +const reserveBankOfAustraliaDataSource = "Reserve Bank of Australia" +const reserveBankOfAustraliaBaseCurrency = "AUD" + +const reserveBankOfAustraliaDataUpdateDateFormat = "2006-01-02T15:04:05Z07:00" + +// ReserveBankOfAustraliaDataSource defines the structure of exchange rates data source of the reserve bank of Australia +type ReserveBankOfAustraliaDataSource struct { + ExchangeRatesDataSource +} + +// ReserveBankOfAustraliaData represents the whole data from the reserve bank of Australia +type ReserveBankOfAustraliaData struct { + XMLName xml.Name `xml:"RDF"` + Channel *ReserveBankOfAustraliaRssChannel `xml:"channel"` + Items []*ReserveBankOfAustraliaRssItem `xml:"item"` +} + +// ReserveBankOfAustraliaRssChannel represents the rss channel from the reserve bank of Australia +type ReserveBankOfAustraliaRssChannel struct { + Date string `xml:"date"` +} + +// ReserveBankOfAustraliaRssItem represents the rss item from the reserve bank of Australia +type ReserveBankOfAustraliaRssItem struct { + Statistics *ReserveBankOfAustraliaItemStatistics `xml:"statistics"` +} + +// ReserveBankOfAustraliaItemStatistics represents the item statistics from the reserve bank of Australia +type ReserveBankOfAustraliaItemStatistics struct { + ExchangeRate *ReserveBankOfAustraliaExchangeRate `xml:"exchangeRate"` +} + +// ReserveBankOfAustraliaExchangeRate represents the exchange rate from the reserve bank of Australia +type ReserveBankOfAustraliaExchangeRate struct { + BaseCurrency string `xml:"baseCurrency"` + TargetCurrency string `xml:"targetCurrency"` + Observation *ReserveBankOfAustraliaExchangeRateObservation `xml:"observation"` +} + +// ReserveBankOfAustraliaExchangeRateObservation represents the exchange rate data from the reserve bank of Australia +type ReserveBankOfAustraliaExchangeRateObservation struct { + Value string `xml:"value"` + Unit string `xml:"unit"` +} + +// ToLatestExchangeRateResponse returns a view-object according to original data from the reserve bank of Australia +func (e *ReserveBankOfAustraliaData) ToLatestExchangeRateResponse(c *core.Context) *models.LatestExchangeRateResponse { + if e.Channel == nil { + log.ErrorfWithRequestId(c, "[reserve_bank_of_australia_datasource.ToLatestExchangeRateResponse] rss channel does not exist") + return nil + } + + if len(e.Items) < 1 { + log.ErrorfWithRequestId(c, "[reserve_bank_of_australia_datasource.ToLatestExchangeRateResponse] rss items is empty") + return nil + } + + exchangeRates := make(models.LatestExchangeRateSlice, 0, len(e.Items)) + + for i := 0; i < len(e.Items); i++ { + item := e.Items[i] + + if item.Statistics == nil || item.Statistics.ExchangeRate == nil || item.Statistics.ExchangeRate.Observation == nil { + continue + } + + if item.Statistics.ExchangeRate.BaseCurrency != reserveBankOfAustraliaBaseCurrency || item.Statistics.ExchangeRate.Observation.Unit != reserveBankOfAustraliaBaseCurrency { + continue + } + + if _, exists := validators.AllCurrencyNames[item.Statistics.ExchangeRate.TargetCurrency]; !exists { + continue + } + + exchangeRates = append(exchangeRates, item.Statistics.ExchangeRate.ToLatestExchangeRate()) + } + + updateDateTime := e.Channel.Date + updateTime, err := time.Parse(reserveBankOfAustraliaDataUpdateDateFormat, updateDateTime) + + if err != nil { + log.ErrorfWithRequestId(c, "[reserve_bank_of_australia_datasource.ToLatestExchangeRateResponse] failed to parse update date, datetime is %s", updateDateTime) + return nil + } + + latestExchangeRateResp := &models.LatestExchangeRateResponse{ + DataSource: reserveBankOfAustraliaDataSource, + ReferenceUrl: reserveBankOfAustraliaExchangeRateReferenceUrl, + UpdateTime: updateTime.Unix(), + BaseCurrency: reserveBankOfAustraliaBaseCurrency, + ExchangeRates: exchangeRates, + } + + return latestExchangeRateResp +} + +// ToLatestExchangeRate returns a data pair according to original data from the reserve bank of Australia +func (e *ReserveBankOfAustraliaExchangeRate) ToLatestExchangeRate() *models.LatestExchangeRate { + return &models.LatestExchangeRate{ + Currency: e.TargetCurrency, + Rate: e.Observation.Value, + } +} + +// GetRequestUrls returns the the reserve bank of Australia data source urls +func (e *ReserveBankOfAustraliaDataSource) GetRequestUrls() []string { + return []string{reserveBankOfAustraliaExchangeRateUrl} +} + +// Parse returns the common response entity according to the the reserve bank of Australia data source raw response +func (e *ReserveBankOfAustraliaDataSource) Parse(c *core.Context, content []byte) (*models.LatestExchangeRateResponse, error) { + reserveBankOfAustraliaData := &ReserveBankOfAustraliaData{} + err := xml.Unmarshal(content, reserveBankOfAustraliaData) + + if err != nil { + log.ErrorfWithRequestId(c, "[reserve_bank_of_australia_datasource.Parse] failed to parse xml data, content is %s, because %s", string(content), err.Error()) + return nil, errs.ErrFailedToRequestRemoteApi + } + + latestExchangeRateResponse := reserveBankOfAustraliaData.ToLatestExchangeRateResponse(c) + + if latestExchangeRateResponse == nil { + log.ErrorfWithRequestId(c, "[reserve_bank_of_australia_datasource.Parse] failed to parse latest exchange rate data, content is %s", string(content)) + return nil, errs.ErrFailedToRequestRemoteApi + } + + return latestExchangeRateResponse, nil +} diff --git a/pkg/settings/setting.go b/pkg/settings/setting.go index 85931aff..ffca5739 100644 --- a/pkg/settings/setting.go +++ b/pkg/settings/setting.go @@ -64,10 +64,11 @@ const ( // Exchange rates data source types const ( - EuroCentralBankDataSource string = "euro_central_bank" - BankOfCanadaDataSource string = "bank_of_canada" - CzechNationalBankDataSource string = "czech_national_bank" - NationalBankOfPolandDataSource string = "national_bank_of_poland" + EuroCentralBankDataSource string = "euro_central_bank" + BankOfCanadaDataSource string = "bank_of_canada" + ReserveBankOfAustraliaDataSource string = "reserve_bank_of_australia" + CzechNationalBankDataSource string = "czech_national_bank" + NationalBankOfPolandDataSource string = "national_bank_of_poland" ) const ( @@ -417,6 +418,8 @@ func loadExchangeRatesConfiguration(config *Config, configFile *ini.File, sectio config.ExchangeRatesDataSource = EuroCentralBankDataSource } else if getConfigItemStringValue(configFile, sectionName, "data_source") == BankOfCanadaDataSource { config.ExchangeRatesDataSource = BankOfCanadaDataSource + } else if getConfigItemStringValue(configFile, sectionName, "data_source") == ReserveBankOfAustraliaDataSource { + config.ExchangeRatesDataSource = ReserveBankOfAustraliaDataSource } else if getConfigItemStringValue(configFile, sectionName, "data_source") == CzechNationalBankDataSource { config.ExchangeRatesDataSource = CzechNationalBankDataSource } else if getConfigItemStringValue(configFile, sectionName, "data_source") == NationalBankOfPolandDataSource {