FiatRates: change input & output time values to Unix timestamps

make currency parameter optional, return all rates if it's empty

update tests
This commit is contained in:
Vladyslav Burzakovskyy 2019-12-19 17:11:54 +01:00 committed by Martin
parent f2ff7985b1
commit 14c64410f7
6 changed files with 156 additions and 153 deletions

View File

@ -13,7 +13,6 @@ import (
"os" "os"
"sort" "sort"
"strconv" "strconv"
"strings"
"time" "time"
"github.com/golang/glog" "github.com/golang/glog"
@ -1148,36 +1147,27 @@ func (w *Worker) GetBlocks(page int, blocksOnPage int) (*Blocks, error) {
// getFiatRatesResult checks if CurrencyRatesTicker contains all necessary data and returns formatted result // getFiatRatesResult checks if CurrencyRatesTicker contains all necessary data and returns formatted result
func (w *Worker) getFiatRatesResult(currency string, ticker *db.CurrencyRatesTicker) (*db.ResultTickerAsString, error) { func (w *Worker) getFiatRatesResult(currency string, ticker *db.CurrencyRatesTicker) (*db.ResultTickerAsString, error) {
rates := make(map[string]json.Number, 2) if currency == "" {
timeFormatted := ticker.Timestamp.Format(db.FiatRatesTimeFormat) return &db.ResultTickerAsString{
Timestamp: ticker.Timestamp.UTC().Unix(),
Rates: ticker.Rates,
}, nil
}
rates := make(map[string]json.Number, 1)
timestamp := ticker.Timestamp.UTC().Unix()
if rate, found := ticker.Rates[currency]; !found { if rate, found := ticker.Rates[currency]; !found {
availableCurrencies := make([]string, 0, len(ticker.Rates)) return nil, NewAPIError(fmt.Sprintf("Currency %q is not available for timestamp %d.", currency, timestamp), true)
for availableCurrency := range ticker.Rates {
availableCurrencies = append(availableCurrencies, availableCurrency)
}
sort.Strings(availableCurrencies) // sort to get deterministic results
return nil, NewAPIError(fmt.Sprintf("Currency %q is not available for timestamp %s. Available currencies are: %s", currency, timeFormatted, strings.Join(availableCurrencies, ",")), true)
} else { } else {
rates[currency] = rate rates[currency] = rate
} }
// add default usd currency return &db.ResultTickerAsString{
if currency != "usd" { Timestamp: timestamp,
if rate, found := ticker.Rates["usd"]; found {
rates["usd"] = rate
}
}
result := &db.ResultTickerAsString{
Timestamp: timeFormatted,
Rates: rates, Rates: rates,
} }, nil
return result, nil
} }
// GetFiatRatesForBlockID returns fiat rates for block height or block hash // GetFiatRatesForBlockID returns fiat rates for block height or block hash
func (w *Worker) GetFiatRatesForBlockID(bid string, currency string) (*db.ResultTickerAsString, error) { func (w *Worker) GetFiatRatesForBlockID(bid string, currency string) (*db.ResultTickerAsString, error) {
if currency == "" {
return nil, NewAPIError("Missing or empty \"currency\" parameter", true)
}
var ticker *db.CurrencyRatesTicker var ticker *db.CurrencyRatesTicker
bi, err := w.getBlockInfoFromBlockID(bid) bi, err := w.getBlockInfoFromBlockID(bid)
if err != nil { if err != nil {
@ -1186,7 +1176,7 @@ func (w *Worker) GetFiatRatesForBlockID(bid string, currency string) (*db.Result
} }
return nil, NewAPIError(fmt.Sprintf("Block %v not found, error: %v", bid, err), false) return nil, NewAPIError(fmt.Sprintf("Block %v not found, error: %v", bid, err), false)
} }
dbi := &db.BlockInfo{Time: bi.Time} // get timestamp from block dbi := &db.BlockInfo{Time: bi.Time} // get Unix timestamp from block
tm := time.Unix(dbi.Time, 0) // convert it to Time object tm := time.Unix(dbi.Time, 0) // convert it to Time object
ticker, err = w.db.FiatRatesFindTicker(&tm) ticker, err = w.db.FiatRatesFindTicker(&tm)
if err != nil { if err != nil {
@ -1203,9 +1193,6 @@ func (w *Worker) GetFiatRatesForBlockID(bid string, currency string) (*db.Result
// GetCurrentFiatRates returns current fiat rates // GetCurrentFiatRates returns current fiat rates
func (w *Worker) GetCurrentFiatRates(currency string) (*db.ResultTickerAsString, error) { func (w *Worker) GetCurrentFiatRates(currency string) (*db.ResultTickerAsString, error) {
if currency == "" {
return nil, NewAPIError("Missing or empty \"currency\" parameter", true)
}
ticker, err := w.db.FiatRatesFindLastTicker() ticker, err := w.db.FiatRatesFindLastTicker()
if err != nil { if err != nil {
return nil, NewAPIError(fmt.Sprintf("Error finding ticker: %v", err), false) return nil, NewAPIError(fmt.Sprintf("Error finding ticker: %v", err), false)
@ -1219,28 +1206,28 @@ func (w *Worker) GetCurrentFiatRates(currency string) (*db.ResultTickerAsString,
return result, nil return result, nil
} }
// GetFiatRatesForDates returns fiat rates for each of the provided dates // GetFiatRatesForTimestamps returns fiat rates for each of the provided dates
func (w *Worker) GetFiatRatesForDates(dateStrings []string, currency string) (*db.ResultTickersAsString, error) { func (w *Worker) GetFiatRatesForTimestamps(timestamps []string, currency string) (*db.ResultTickersAsString, error) {
if currency == "" { if currency == "" {
return nil, NewAPIError("Missing or empty \"currency\" parameter", true) return nil, NewAPIError("Missing or empty \"currency\" parameter.", true)
} else if len(dateStrings) == 0 { } else if len(timestamps) == 0 {
return nil, NewAPIError("No dates provided", true) return nil, NewAPIError("No timestamps provided", true)
} }
ret := &db.ResultTickersAsString{} ret := &db.ResultTickersAsString{}
for _, dateString := range dateStrings { for _, timestamp := range timestamps {
date, err := db.FiatRatesConvertDate(dateString) date, err := db.FiatRatesTimestampToTime(timestamp)
if err != nil { if err != nil {
ret.Tickers = append(ret.Tickers, db.ResultTickerAsString{Error: fmt.Sprintf("%v", err)}) ret.Tickers = append(ret.Tickers, db.ResultTickerAsString{Error: fmt.Sprintf("%q is not a valid Unix timestamp.", timestamp)})
continue continue
} }
ticker, err := w.db.FiatRatesFindTicker(date) ticker, err := w.db.FiatRatesFindTicker(date)
if err != nil { if err != nil {
glog.Errorf("Error finding ticker by date %v. Error: %v", dateString, err) glog.Errorf("Error finding ticker for date %v. Error: %v", date, err)
ret.Tickers = append(ret.Tickers, db.ResultTickerAsString{Error: "Ticker not found."}) ret.Tickers = append(ret.Tickers, db.ResultTickerAsString{Timestamp: date.Unix(), Error: "Ticker not found."})
continue continue
} else if ticker == nil { } else if ticker == nil {
ret.Tickers = append(ret.Tickers, db.ResultTickerAsString{Error: fmt.Sprintf("No tickers available for %s (%s)", date, currency)}) ret.Tickers = append(ret.Tickers, db.ResultTickerAsString{Timestamp: date.Unix(), Error: fmt.Sprintf("No tickers available for currency %q", currency)})
continue continue
} }
result, err := w.getFiatRatesResult(currency, ticker) result, err := w.getFiatRatesResult(currency, ticker)
@ -1254,13 +1241,14 @@ func (w *Worker) GetFiatRatesForDates(dateStrings []string, currency string) (*d
} }
// GetFiatRatesTickersList returns the list of available fiatRates tickers // GetFiatRatesTickersList returns the list of available fiatRates tickers
func (w *Worker) GetFiatRatesTickersList(dateString string) (*db.ResultTickerListAsString, error) { func (w *Worker) GetFiatRatesTickersList(timestampString string) (*db.ResultTickerListAsString, error) {
if dateString == "" { if timestampString == "" {
return nil, NewAPIError("Missing or empty \"date\" parameter", true) return nil, NewAPIError("Missing or empty \"timestamp\" parameter", true)
} }
date, err := db.FiatRatesConvertDate(dateString)
date, err := db.FiatRatesTimestampToTime(timestampString)
if err != nil { if err != nil {
return nil, err return nil, NewAPIError(fmt.Sprintf("%v is not a valid Unix timestamp.", timestampString), false)
} }
ticker, err := w.db.FiatRatesFindTicker(date) ticker, err := w.db.FiatRatesFindTicker(date)
@ -1275,10 +1263,10 @@ func (w *Worker) GetFiatRatesTickersList(dateString string) (*db.ResultTickerLis
keys = append(keys, k) keys = append(keys, k)
} }
sort.Strings(keys) // sort to get deterministic results sort.Strings(keys) // sort to get deterministic results
timeFormatted := ticker.Timestamp.Format(db.FiatRatesTimeFormat) timestamp := ticker.Timestamp.UTC().Unix()
return &db.ResultTickerListAsString{ return &db.ResultTickerListAsString{
Timestamp: timeFormatted, Timestamp: timestamp,
Tickers: keys, Tickers: keys,
}, nil }, nil
} }

View File

@ -42,7 +42,7 @@ type CurrencyRatesTicker struct {
// ResultTickerAsString contains formatted CurrencyRatesTicker data // ResultTickerAsString contains formatted CurrencyRatesTicker data
type ResultTickerAsString struct { type ResultTickerAsString struct {
Timestamp string `json:"data_timestamp,omitempty"` Timestamp int64 `json:"ts,omitempty"`
Rates map[string]json.Number `json:"rates,omitempty"` Rates map[string]json.Number `json:"rates,omitempty"`
Error string `json:"error,omitempty"` Error string `json:"error,omitempty"`
} }
@ -54,7 +54,7 @@ type ResultTickersAsString struct {
// ResultTickerListAsString contains formatted data about available currency tickers // ResultTickerListAsString contains formatted data about available currency tickers
type ResultTickerListAsString struct { type ResultTickerListAsString struct {
Timestamp string `json:"data_timestamp,omitempty"` Timestamp int64 `json:"data_timestamp,omitempty"`
Tickers []string `json:"available_currencies"` Tickers []string `json:"available_currencies"`
Error string `json:"error,omitempty"` Error string `json:"error,omitempty"`
} }
@ -190,6 +190,17 @@ func FiatRatesConvertDate(date string) (*time.Time, error) {
return nil, errors.New(msg) return nil, errors.New(msg)
} }
// FiatRatesTimestampToTime converts the unix timestamp string to a Time object
func FiatRatesTimestampToTime(timestamp string) (*time.Time, error) {
i, err := strconv.ParseInt(timestamp, 10, 64)
if err != nil {
return nil, err
}
ts := time.Unix(i, 0)
ts = ts.UTC()
return &ts, nil
}
// FiatRatesStoreTicker stores ticker data at the specified time // FiatRatesStoreTicker stores ticker data at the specified time
func (d *RocksDB) FiatRatesStoreTicker(ticker *CurrencyRatesTicker) error { func (d *RocksDB) FiatRatesStoreTicker(ticker *CurrencyRatesTicker) error {
if len(ticker.Rates) == 0 { if len(ticker.Rates) == 0 {

View File

@ -1130,12 +1130,12 @@ func (s *PublicServer) apiSendTx(r *http.Request, apiVersion int) (interface{},
// apiTickersList returns a list of available FiatRates currencies // apiTickersList returns a list of available FiatRates currencies
func (s *PublicServer) apiTickersList(r *http.Request, apiVersion int) (interface{}, error) { func (s *PublicServer) apiTickersList(r *http.Request, apiVersion int) (interface{}, error) {
s.metrics.ExplorerViews.With(common.Labels{"action": "api-tickers-list"}).Inc() s.metrics.ExplorerViews.With(common.Labels{"action": "api-tickers-list"}).Inc()
date := strings.ToLower(r.URL.Query().Get("date")) timestamp := strings.ToLower(r.URL.Query().Get("timestamp"))
result, err := s.api.GetFiatRatesTickersList(date) result, err := s.api.GetFiatRatesTickersList(timestamp)
return result, err return result, err
} }
// apiTickers returns FiatRates ticker prices for the specified block or date. // apiTickers returns FiatRates ticker prices for the specified block or timestamp.
func (s *PublicServer) apiTickers(r *http.Request, apiVersion int) (interface{}, error) { func (s *PublicServer) apiTickers(r *http.Request, apiVersion int) (interface{}, error) {
var result *db.ResultTickerAsString var result *db.ResultTickerAsString
var err error var err error
@ -1145,10 +1145,10 @@ func (s *PublicServer) apiTickers(r *http.Request, apiVersion int) (interface{},
// Get tickers for specified block height or block hash // Get tickers for specified block height or block hash
s.metrics.ExplorerViews.With(common.Labels{"action": "api-tickers-block"}).Inc() s.metrics.ExplorerViews.With(common.Labels{"action": "api-tickers-block"}).Inc()
result, err = s.api.GetFiatRatesForBlockID(block, currency) result, err = s.api.GetFiatRatesForBlockID(block, currency)
} else if date := r.URL.Query().Get("date"); date != "" { } else if timestamp := r.URL.Query().Get("timestamp"); timestamp != "" {
// Get tickers for specified date // Get tickers for specified timestamp
s.metrics.ExplorerViews.With(common.Labels{"action": "api-tickers-date"}).Inc() s.metrics.ExplorerViews.With(common.Labels{"action": "api-tickers-date"}).Inc()
resultTickers, err := s.api.GetFiatRatesForDates([]string{date}, currency) resultTickers, err := s.api.GetFiatRatesForTimestamps([]string{timestamp}, currency)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -521,10 +521,10 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) {
{ {
name: "apiFiatRates missing currency", name: "apiFiatRates missing currency",
r: newGetRequest(ts.URL + "/api/v2/tickers"), r: newGetRequest(ts.URL + "/api/v2/tickers"),
status: http.StatusBadRequest, status: http.StatusOK,
contentType: "application/json; charset=utf-8", contentType: "application/json; charset=utf-8",
body: []string{ body: []string{
`{"error":"Missing or empty \"currency\" parameter"}`, `{"ts":1574346615,"rates":{"eur":7134.1,"usd":7914.5}}`,
}, },
}, },
{ {
@ -533,52 +533,52 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) {
status: http.StatusOK, status: http.StatusOK,
contentType: "application/json; charset=utf-8", contentType: "application/json; charset=utf-8",
body: []string{ body: []string{
`{"data_timestamp":"20191121143015","rates":{"usd":7914.5}}`, `{"ts":1574346615,"rates":{"usd":7914.5}}`,
}, },
}, },
{ {
name: "apiFiatRates get rate by exact date", name: "apiFiatRates get rate by exact timestamp",
r: newGetRequest(ts.URL + "/api/v2/tickers?currency=usd&date=20191121140000"), r: newGetRequest(ts.URL + "/api/v2/tickers?currency=usd&timestamp=1574344800"),
status: http.StatusOK, status: http.StatusOK,
contentType: "application/json; charset=utf-8", contentType: "application/json; charset=utf-8",
body: []string{ body: []string{
`{"data_timestamp":"20191121140000","rates":{"usd":7814.5}}`, `{"ts":1574344800,"rates":{"usd":7814.5}}`,
}, },
}, },
{ {
name: "apiFiatRates incorrect date", name: "apiFiatRates incorrect timestamp",
r: newGetRequest(ts.URL + "/api/v2/tickers?currency=usd&date=yesterday"), r: newGetRequest(ts.URL + "/api/v2/tickers?currency=usd&timestamp=yesterday"),
status: http.StatusOK, status: http.StatusOK,
contentType: "application/json; charset=utf-8", contentType: "application/json; charset=utf-8",
body: []string{ body: []string{
`{"error":"Date \"yesterday\" does not match any of available formats. Possible formats are: YYYYMMDDhhmmss, YYYYMMDDhhmm, YYYYMMDDhh, YYYYMMDD"}`, `{"error":"\"yesterday\" is not a valid Unix timestamp."}`,
}, },
}, },
{ {
name: "apiFiatRates future date", name: "apiFiatRates future timestamp",
r: newGetRequest(ts.URL + "/api/v2/tickers?currency=usd&date=20200101000000"), r: newGetRequest(ts.URL + "/api/v2/tickers?currency=usd&timestamp=7980386400"),
status: http.StatusOK, status: http.StatusOK,
contentType: "application/json; charset=utf-8", contentType: "application/json; charset=utf-8",
body: []string{ body: []string{
`{"error":"No tickers available for 2020-01-01 00:00:00 +0000 UTC (usd)"}`, `{"ts":7980386400,"error":"No tickers available for currency \"usd\""}`,
}, },
}, },
{ {
name: "apiFiatRates get EUR rate (exact date)", name: "apiFiatRates get EUR rate (exact timestamp)",
r: newGetRequest(ts.URL + "/api/v2/tickers?date=20191121140000&currency=eur"), r: newGetRequest(ts.URL + "/api/v2/tickers?timestamp=1574344800&currency=eur"),
status: http.StatusOK, status: http.StatusOK,
contentType: "application/json; charset=utf-8", contentType: "application/json; charset=utf-8",
body: []string{ body: []string{
`{"data_timestamp":"20191121140000","rates":{"eur":7100.0,"usd":7814.5}}`, `{"ts":1574344800,"rates":{"eur":7100.0}}`,
}, },
}, },
{ {
name: "apiFiatRates get closest rate", name: "apiFiatRates get closest rate",
r: newGetRequest(ts.URL + "/api/v2/tickers?date=20191121130000&currency=usd"), r: newGetRequest(ts.URL + "/api/v2/tickers?timestamp=1357045200&currency=usd"),
status: http.StatusOK, status: http.StatusOK,
contentType: "application/json; charset=utf-8", contentType: "application/json; charset=utf-8",
body: []string{ body: []string{
`{"data_timestamp":"20191121140000","rates":{"usd":7814.5}}`, `{"ts":1521511200,"rates":{"usd":2000.0}}`,
}, },
}, },
{ {
@ -587,34 +587,34 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) {
status: http.StatusOK, status: http.StatusOK,
contentType: "application/json; charset=utf-8", contentType: "application/json; charset=utf-8",
body: []string{ body: []string{
`{"data_timestamp":"20180321055521","rates":{"usd":2003.0}}`, `{"ts":1521611721,"rates":{"usd":2003.0}}`,
}, },
}, },
{ {
name: "apiFiatRates get rate for EUR", name: "apiFiatRates get rate for EUR",
r: newGetRequest(ts.URL + "/api/v2/tickers?date=20191121140000&currency=eur"), r: newGetRequest(ts.URL + "/api/v2/tickers?timestamp=1574346615&currency=eur"),
status: http.StatusOK, status: http.StatusOK,
contentType: "application/json; charset=utf-8", contentType: "application/json; charset=utf-8",
body: []string{ body: []string{
`{"data_timestamp":"20191121140000","rates":{"eur":7100.0,"usd":7814.5}}`, `{"ts":1574346615,"rates":{"eur":7134.1}}`,
}, },
}, },
{ {
name: "apiFiatRates get exact rate for an incorrect currency", name: "apiFiatRates get exact rate for an incorrect currency",
r: newGetRequest(ts.URL + "/api/v2/tickers?date=20191121140000&currency=does_not_exist"), r: newGetRequest(ts.URL + "/api/v2/tickers?timestamp=1574346615&currency=does_not_exist"),
status: http.StatusOK, status: http.StatusOK,
contentType: "application/json; charset=utf-8", contentType: "application/json; charset=utf-8",
body: []string{ body: []string{
`{"error":"Currency \"does_not_exist\" is not available for timestamp 20191121140000. Available currencies are: eur,usd"}`, `{"error":"Currency \"does_not_exist\" is not available for timestamp 1574346615."}`,
}, },
}, },
{ {
name: "apiTickerList", name: "apiTickerList",
r: newGetRequest(ts.URL + "/api/v2/tickers-list?date=20191121140000"), r: newGetRequest(ts.URL + "/api/v2/tickers-list?timestamp=1574346615"),
status: http.StatusOK, status: http.StatusOK,
contentType: "application/json; charset=utf-8", contentType: "application/json; charset=utf-8",
body: []string{ body: []string{
`{"data_timestamp":"20191121140000","available_currencies":["eur","usd"]}`, `{"data_timestamp":1574346615,"available_currencies":["eur","usd"]}`,
}, },
}, },
{ {
@ -1202,7 +1202,7 @@ func websocketTestsBitcoinType(t *testing.T, ts *httptest.Server) {
"": "", "": "",
}, },
}, },
want: `{"id":"17","data":{"error":{"message":"Missing or empty \"currency\" parameter"}}}`, want: `{"id":"17","data":{"ts":1574346615,"rates":{"eur":7134.1,"usd":7914.5}}}`,
}, },
{ {
name: "websocket getCurrentFiatRates usd", name: "websocket getCurrentFiatRates usd",
@ -1212,7 +1212,7 @@ func websocketTestsBitcoinType(t *testing.T, ts *httptest.Server) {
"currency": "usd", "currency": "usd",
}, },
}, },
want: `{"id":"18","data":{"data_timestamp":"20191121143015","rates":{"usd":7914.5}}}`, want: `{"id":"18","data":{"ts":1574346615,"rates":{"usd":7914.5}}}`,
}, },
{ {
name: "websocket getCurrentFiatRates eur", name: "websocket getCurrentFiatRates eur",
@ -1222,7 +1222,7 @@ func websocketTestsBitcoinType(t *testing.T, ts *httptest.Server) {
"currency": "eur", "currency": "eur",
}, },
}, },
want: `{"id":"19","data":{"data_timestamp":"20191121143015","rates":{"eur":7134.1,"usd":7914.5}}}`, want: `{"id":"19","data":{"ts":1574346615,"rates":{"eur":7134.1}}}`,
}, },
{ {
name: "websocket getCurrentFiatRates incorrect currency", name: "websocket getCurrentFiatRates incorrect currency",
@ -1232,115 +1232,115 @@ func websocketTestsBitcoinType(t *testing.T, ts *httptest.Server) {
"currency": "does-not-exist", "currency": "does-not-exist",
}, },
}, },
want: `{"id":"20","data":{"error":{"message":"Currency \"does-not-exist\" is not available for timestamp 20191121143015. Available currencies are: eur,usd"}}}`, want: `{"id":"20","data":{"error":{"message":"Currency \"does-not-exist\" is not available for timestamp 1574346615."}}}`,
}, },
{ {
name: "websocket getFiatRatesForDates missing date", name: "websocket getFiatRatesForTimestamps missing date",
req: websocketReq{ req: websocketReq{
Method: "getFiatRatesForDates", Method: "getFiatRatesForTimestamps",
Params: map[string]interface{}{ Params: map[string]interface{}{
"currency": "usd", "currency": "usd",
}, },
}, },
want: `{"id":"21","data":{"error":{"message":"No dates provided"}}}`, want: `{"id":"21","data":{"error":{"message":"No timestamps provided"}}}`,
}, },
{ {
name: "websocket getFiatRatesForDates incorrect date", name: "websocket getFiatRatesForTimestamps incorrect date",
req: websocketReq{ req: websocketReq{
Method: "getFiatRatesForDates", Method: "getFiatRatesForTimestamps",
Params: map[string]interface{}{ Params: map[string]interface{}{
"currency": "usd", "currency": "usd",
"dates": []string{"yesterday"}, "timestamps": []string{"yesterday"},
}, },
}, },
want: `{"id":"22","data":{"tickers":[{"error":"Date \"yesterday\" does not match any of available formats. Possible formats are: YYYYMMDDhhmmss, YYYYMMDDhhmm, YYYYMMDDhh, YYYYMMDD"}]}}`, want: `{"id":"22","data":{"tickers":[{"error":"\"yesterday\" is not a valid Unix timestamp."}]}}`,
}, },
{ {
name: "websocket getFiatRatesForDates incorrect (future) date", name: "websocket getFiatRatesForTimestamps incorrect (future) date",
req: websocketReq{ req: websocketReq{
Method: "getFiatRatesForDates", Method: "getFiatRatesForTimestamps",
Params: map[string]interface{}{ Params: map[string]interface{}{
"currency": "usd", "currency": "usd",
"dates": []string{"20200101000000"}, "timestamps": []string{"7885693815"},
}, },
}, },
want: `{"id":"23","data":{"tickers":[{"error":"No tickers available for 2020-01-01 00:00:00 +0000 UTC (usd)"}]}}`, want: `{"id":"23","data":{"tickers":[{"ts":7885693815,"error":"No tickers available for currency \"usd\""}]}}`,
}, },
{ {
name: "websocket getFiatRatesForDates exact date", name: "websocket getFiatRatesForTimestamps exact date",
req: websocketReq{ req: websocketReq{
Method: "getFiatRatesForDates", Method: "getFiatRatesForTimestamps",
Params: map[string]interface{}{ Params: map[string]interface{}{
"currency": "usd", "currency": "usd",
"dates": []string{"20191121140000"}, "timestamps": []string{"1574346615"},
}, },
}, },
want: `{"id":"24","data":{"tickers":[{"data_timestamp":"20191121140000","rates":{"usd":7814.5}}]}}`, want: `{"id":"24","data":{"tickers":[{"ts":1574346615,"rates":{"usd":7914.5}}]}}`,
}, },
{ {
name: "websocket getFiatRatesForDates closest date, eur", name: "websocket getFiatRatesForTimestamps closest date, eur",
req: websocketReq{ req: websocketReq{
Method: "getFiatRatesForDates", Method: "getFiatRatesForTimestamps",
Params: map[string]interface{}{ Params: map[string]interface{}{
"currency": "eur", "currency": "eur",
"dates": []string{"20191121130000"}, "timestamps": []string{"1521507600"},
}, },
}, },
want: `{"id":"25","data":{"tickers":[{"data_timestamp":"20191121140000","rates":{"eur":7100.0,"usd":7814.5}}]}}`, want: `{"id":"25","data":{"tickers":[{"ts":1521511200,"rates":{"eur":1300.0}}]}}`,
}, },
{ {
name: "websocket getFiatRatesForDates multiple dates usd", name: "websocket getFiatRatesForTimestamps multiple timestamps usd",
req: websocketReq{ req: websocketReq{
Method: "getFiatRatesForDates", Method: "getFiatRatesForTimestamps",
Params: map[string]interface{}{ Params: map[string]interface{}{
"currency": "usd", "currency": "usd",
"dates": []string{"20191121140000", "20191121143015"}, "timestamps": []string{"1570346615", "1574346615"},
}, },
}, },
want: `{"id":"26","data":{"tickers":[{"data_timestamp":"20191121140000","rates":{"usd":7814.5}},{"data_timestamp":"20191121143015","rates":{"usd":7914.5}}]}}`, want: `{"id":"26","data":{"tickers":[{"ts":1574344800,"rates":{"usd":7814.5}},{"ts":1574346615,"rates":{"usd":7914.5}}]}}`,
}, },
{ {
name: "websocket getFiatRatesForDates multiple dates eur", name: "websocket getFiatRatesForTimestamps multiple timestamps eur",
req: websocketReq{ req: websocketReq{
Method: "getFiatRatesForDates", Method: "getFiatRatesForTimestamps",
Params: map[string]interface{}{ Params: map[string]interface{}{
"currency": "eur", "currency": "eur",
"dates": []string{"20191121140000", "20191121143015"}, "timestamps": []string{"1570346615", "1574346615"},
}, },
}, },
want: `{"id":"27","data":{"tickers":[{"data_timestamp":"20191121140000","rates":{"eur":7100.0,"usd":7814.5}},{"data_timestamp":"20191121143015","rates":{"eur":7134.1,"usd":7914.5}}]}}`, want: `{"id":"27","data":{"tickers":[{"ts":1574344800,"rates":{"eur":7100.0}},{"ts":1574346615,"rates":{"eur":7134.1}}]}}`,
}, },
{ {
name: "websocket getFiatRatesForDates multiple dates with an error", name: "websocket getFiatRatesForTimestamps multiple timestamps with an error",
req: websocketReq{ req: websocketReq{
Method: "getFiatRatesForDates", Method: "getFiatRatesForTimestamps",
Params: map[string]interface{}{ Params: map[string]interface{}{
"currency": "usd", "currency": "usd",
"dates": []string{"20191121140000", "20191121143015", "not-a-real-date"}, "timestamps": []string{"1570346615", "1574346615", "not-a-real-date"},
}, },
}, },
want: `{"id":"28","data":{"tickers":[{"data_timestamp":"20191121140000","rates":{"usd":7814.5}},{"data_timestamp":"20191121143015","rates":{"usd":7914.5}},{"error":"Date \"not-a-real-date\" does not match any of available formats. Possible formats are: YYYYMMDDhhmmss, YYYYMMDDhhmm, YYYYMMDDhh, YYYYMMDD"}]}}`, want: `{"id":"28","data":{"tickers":[{"ts":1574344800,"rates":{"usd":7814.5}},{"ts":1574346615,"rates":{"usd":7914.5}},{"error":"\"not-a-real-date\" is not a valid Unix timestamp."}]}}`,
}, },
{ {
name: "websocket getFiatRatesForDates multiple errors", name: "websocket getFiatRatesForTimestamps multiple errors",
req: websocketReq{ req: websocketReq{
Method: "getFiatRatesForDates", Method: "getFiatRatesForTimestamps",
Params: map[string]interface{}{ Params: map[string]interface{}{
"currency": "usd", "currency": "usd",
"dates": []string{"20200101000000", "not-a-real-date"}, "timestamps": []string{"7832854800", "not-a-real-date"},
}, },
}, },
want: `{"id":"29","data":{"tickers":[{"error":"No tickers available for 2020-01-01 00:00:00 +0000 UTC (usd)"},{"error":"Date \"not-a-real-date\" does not match any of available formats. Possible formats are: YYYYMMDDhhmmss, YYYYMMDDhhmm, YYYYMMDDhh, YYYYMMDD"}]}}`, want: `{"id":"29","data":{"tickers":[{"ts":7832854800,"error":"No tickers available for currency \"usd\""},{"error":"\"not-a-real-date\" is not a valid Unix timestamp."}]}}`,
}, },
{ {
name: "websocket getTickersList", name: "websocket getTickersList",
req: websocketReq{ req: websocketReq{
Method: "getFiatRatesTickersList", Method: "getFiatRatesTickersList",
Params: map[string]interface{}{ Params: map[string]interface{}{
"date": "20191121140000", "date": "1570346615",
}, },
}, },
want: `{"id":"30","data":{"data_timestamp":"20191121140000","available_currencies":["eur","usd"]}}`, want: `{"id":"30","data":{"data_timestamp":1574344800,"available_currencies":["eur","usd"]}}`,
}, },
{ {
name: "websocket getBalanceHistory Addr2", name: "websocket getBalanceHistory Addr2",

View File

@ -359,14 +359,14 @@ var requestHandlers = map[string]func(*WebsocketServer, *websocketChannel, *webs
} }
return return
}, },
"getFiatRatesForDates": func(s *WebsocketServer, c *websocketChannel, req *websocketReq) (rv interface{}, err error) { "getFiatRatesForTimestamps": func(s *WebsocketServer, c *websocketChannel, req *websocketReq) (rv interface{}, err error) {
r := struct { r := struct {
Dates []string `json:"dates"` Timestamps []string `json:"timestamps"`
Currency string `json:"currency"` Currency string `json:"currency"`
}{} }{}
err = json.Unmarshal(req.Params, &r) err = json.Unmarshal(req.Params, &r)
if err == nil { if err == nil {
rv, err = s.getFiatRatesForDates(r.Dates, r.Currency) rv, err = s.getFiatRatesForTimestamps(r.Timestamps, r.Currency)
} }
return return
}, },
@ -698,6 +698,9 @@ func (s *WebsocketServer) subscribeFiatRates(c *websocketChannel, currency strin
s.fiatRatesSubscriptionsLock.Lock() s.fiatRatesSubscriptionsLock.Lock()
defer s.fiatRatesSubscriptionsLock.Unlock() defer s.fiatRatesSubscriptionsLock.Unlock()
if currency == "" {
currency = "!ALL!"
}
as, ok := s.fiatRatesSubscriptions[currency] as, ok := s.fiatRatesSubscriptions[currency]
if !ok { if !ok {
as = make(map[*websocketChannel]string) as = make(map[*websocketChannel]string)
@ -788,18 +791,18 @@ func (s *WebsocketServer) OnNewTxAddr(tx *bchain.Tx, addrDesc bchain.AddressDesc
} }
} }
func (s *WebsocketServer) broadcastTicker(coin string, rate json.Number) { func (s *WebsocketServer) broadcastTicker(currency string, rates map[string]json.Number) {
s.fiatRatesSubscriptionsLock.Lock() s.fiatRatesSubscriptionsLock.Lock()
defer s.fiatRatesSubscriptionsLock.Unlock() defer s.fiatRatesSubscriptionsLock.Unlock()
as, ok := s.fiatRatesSubscriptions[coin] as, ok := s.fiatRatesSubscriptions[currency]
if ok && len(as) > 0 { if ok && len(as) > 0 {
data := struct { data := struct {
Rate interface{} `json:"rate"` Rates interface{} `json:"rates"`
}{ }{
Rate: rate, Rates: rates,
} }
// get the list of subscriptions again, this time keep the lock // get the list of subscriptions again, this time keep the lock
as, ok = s.fiatRatesSubscriptions[coin] as, ok = s.fiatRatesSubscriptions[currency]
if ok { if ok {
for c, id := range as { for c, id := range as {
if c.IsAlive() { if c.IsAlive() {
@ -809,7 +812,7 @@ func (s *WebsocketServer) broadcastTicker(coin string, rate json.Number) {
} }
} }
} }
glog.Info("broadcasting new rate ", rate, " for coin ", coin, " to ", len(as), " channels") glog.Info("broadcasting new rates for currency ", currency, " to ", len(as), " channels")
} }
} }
} }
@ -817,8 +820,9 @@ func (s *WebsocketServer) broadcastTicker(coin string, rate json.Number) {
// OnNewFiatRatesTicker is a callback that broadcasts info about fiat rates affecting subscribed currency // OnNewFiatRatesTicker is a callback that broadcasts info about fiat rates affecting subscribed currency
func (s *WebsocketServer) OnNewFiatRatesTicker(ticker *db.CurrencyRatesTicker) { func (s *WebsocketServer) OnNewFiatRatesTicker(ticker *db.CurrencyRatesTicker) {
for currency, rate := range ticker.Rates { for currency, rate := range ticker.Rates {
s.broadcastTicker(currency, rate) s.broadcastTicker(currency, map[string]json.Number{currency: rate})
} }
s.broadcastTicker("!ALL!", ticker.Rates)
} }
func (s *WebsocketServer) getCurrentFiatRates(currency string) (interface{}, error) { func (s *WebsocketServer) getCurrentFiatRates(currency string) (interface{}, error) {
@ -826,12 +830,12 @@ func (s *WebsocketServer) getCurrentFiatRates(currency string) (interface{}, err
return ret, err return ret, err
} }
func (s *WebsocketServer) getFiatRatesForDates(dates []string, currency string) (interface{}, error) { func (s *WebsocketServer) getFiatRatesForTimestamps(timestamps []string, currency string) (interface{}, error) {
ret, err := s.api.GetFiatRatesForDates(dates, currency) ret, err := s.api.GetFiatRatesForTimestamps(timestamps, currency)
return ret, err return ret, err
} }
func (s *WebsocketServer) getFiatRatesTickersList(date string) (interface{}, error) { func (s *WebsocketServer) getFiatRatesTickersList(timestamp string) (interface{}, error) {
ret, err := s.api.GetFiatRatesTickersList(date) ret, err := s.api.GetFiatRatesTickersList(timestamp)
return ret, err return ret, err
} }

View File

@ -297,17 +297,17 @@
}); });
} }
function getFiatRatesForDates() { function getFiatRatesForTimestamps() {
const method = 'getFiatRatesForDates'; const method = 'getFiatRatesForTimestamps';
var dates = document.getElementById('getFiatRatesForDatesList').value.split(","); var timestamps = document.getElementById('getFiatRatesForTimestampsList').value.split(",");
var currency = document.getElementById('getFiatRatesForDatesCurrency').value; var currency = document.getElementById('getFiatRatesForTimestampsCurrency').value;
dates = dates.map(s => s.trim()); timestamps = timestamps.map(s => s.trim());
const params = { const params = {
dates, timestamps,
"currency": currency "currency": currency
}; };
send(method, params, function (result) { send(method, params, function (result) {
document.getElementById('getFiatRatesForDatesResult').innerText = JSON.stringify(result).replace(/,/g, ", "); document.getElementById('getFiatRatesForTimestampsResult').innerText = JSON.stringify(result).replace(/,/g, ", ");
}); });
} }
@ -533,24 +533,24 @@
</div> </div>
<div class="row"> <div class="row">
<div class="col-2"> <div class="col-2">
<input class="btn btn-secondary" type="button" value="get fiat rates for dates" onclick="getFiatRatesForDates()"> <input class="btn btn-secondary" type="button" value="get fiat rates for dates" onclick="getFiatRatesForTimestamps()">
</div> </div>
<div class="col-1"> <div class="col-1">
<input type="text" class="form-control" id="getFiatRatesForDatesCurrency" value="usd"> <input type="text" class="form-control" id="getFiatRatesForTimestampsCurrency" value="usd">
</div> </div>
<div class="col-7"> <div class="col-7">
<input type="text" class="form-control" id="getFiatRatesForDatesList" value="20191121140000,20191121143015"> <input type="text" class="form-control" id="getFiatRatesForTimestampsList" value="1575288000,1575550800">
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col" id="getFiatRatesForDatesResult"></div> <div class="col" id="getFiatRatesForTimestampsResult"></div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-2"> <div class="col-2">
<input class="btn btn-secondary" type="button" value="get current fiat rates" onclick="getCurrentFiatRates()"> <input class="btn btn-secondary" type="button" value="get current fiat rates" onclick="getCurrentFiatRates()">
</div> </div>
<div class="col-1"> <div class="col-1">
<input type="text" class="form-control" id="getCurrentFiatRatesCurrency" value="usd"> <input type="text" class="form-control" id="getCurrentFiatRatesCurrency" placeholder="usd">
</div> </div>
</div> </div>
<div class="row"> <div class="row">
@ -561,7 +561,7 @@
<input class="btn btn-secondary" type="button" value="get fiat rates tickers" onclick="getFiatRatesTickersList()"> <input class="btn btn-secondary" type="button" value="get fiat rates tickers" onclick="getFiatRatesTickersList()">
</div> </div>
<div class="col-8"> <div class="col-8">
<input type="text" class="form-control" id="getFiatRatesTickersListDate" value="20191121140000"> <input type="text" class="form-control" id="getFiatRatesTickersListDate" value="1576591569" placeholder="Unix timestamp">
</div> </div>
</div> </div>
<div class="row"> <div class="row">