diff --git a/api/worker.go b/api/worker.go index 85789f84..bb40fc70 100644 --- a/api/worker.go +++ b/api/worker.go @@ -1153,17 +1153,15 @@ func (w *Worker) getFiatRatesResult(currency string, ticker *db.CurrencyRatesTic Rates: ticker.Rates, }, nil } - rates := make(map[string]float64, 1) timestamp := ticker.Timestamp.UTC().Unix() if rate, found := ticker.Rates[currency]; !found { return nil, NewAPIError(fmt.Sprintf("Currency %q is not available for timestamp %d.", currency, timestamp), true) } else { - rates[currency] = rate + return &db.ResultTickerAsString{ + Timestamp: timestamp, + Rate: rate, + }, nil } - return &db.ResultTickerAsString{ - Timestamp: timestamp, - Rates: rates, - }, nil } // GetFiatRatesForBlockID returns fiat rates for block height or block hash @@ -1207,7 +1205,7 @@ func (w *Worker) GetCurrentFiatRates(currency string) (*db.ResultTickerAsString, } // GetFiatRatesForTimestamps returns fiat rates for each of the provided dates -func (w *Worker) GetFiatRatesForTimestamps(timestamps []string, currency string) (*db.ResultTickersAsString, error) { +func (w *Worker) GetFiatRatesForTimestamps(timestamps []int64, currency string) (*db.ResultTickersAsString, error) { if currency == "" { return nil, NewAPIError("Missing or empty \"currency\" parameter.", true) } else if len(timestamps) == 0 { @@ -1216,23 +1214,20 @@ func (w *Worker) GetFiatRatesForTimestamps(timestamps []string, currency string) ret := &db.ResultTickersAsString{} for _, timestamp := range timestamps { - date, err := db.FiatRatesTimestampToTime(timestamp) - if err != nil { - ret.Tickers = append(ret.Tickers, db.ResultTickerAsString{Error: fmt.Sprintf("%q is not a valid Unix timestamp.", timestamp)}) - continue - } - ticker, err := w.db.FiatRatesFindTicker(date) + date := time.Unix(timestamp, 0) + date = date.UTC() + ticker, err := w.db.FiatRatesFindTicker(&date) if err != nil { glog.Errorf("Error finding ticker for date %v. Error: %v", date, err) - ret.Tickers = append(ret.Tickers, db.ResultTickerAsString{Timestamp: date.Unix(), Error: "Ticker not found."}) + ret.Tickers = append(ret.Tickers, db.ResultTickerAsString{Timestamp: date.Unix(), Rate: -1}) continue } else if ticker == nil { - ret.Tickers = append(ret.Tickers, db.ResultTickerAsString{Timestamp: date.Unix(), Error: fmt.Sprintf("No tickers available for currency %q", currency)}) + ret.Tickers = append(ret.Tickers, db.ResultTickerAsString{Timestamp: date.Unix(), Rate: -1}) continue } result, err := w.getFiatRatesResult(currency, ticker) if err != nil { - ret.Tickers = append(ret.Tickers, db.ResultTickerAsString{Error: fmt.Sprintf("%v", err)}) + ret.Tickers = append(ret.Tickers, db.ResultTickerAsString{Timestamp: date.Unix(), Rate: -1}) continue } ret.Tickers = append(ret.Tickers, *result) @@ -1241,17 +1236,11 @@ func (w *Worker) GetFiatRatesForTimestamps(timestamps []string, currency string) } // GetFiatRatesTickersList returns the list of available fiatRates tickers -func (w *Worker) GetFiatRatesTickersList(timestampString string) (*db.ResultTickerListAsString, error) { - if timestampString == "" { - return nil, NewAPIError("Missing or empty \"timestamp\" parameter", true) - } +func (w *Worker) GetFiatRatesTickersList(timestamp int64) (*db.ResultTickerListAsString, error) { + date := time.Unix(timestamp, 0) + date = date.UTC() - date, err := db.FiatRatesTimestampToTime(timestampString) - if err != nil { - 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) if err != nil { return nil, NewAPIError(fmt.Sprintf("Error finding ticker: %v", err), false) } else if ticker == nil { @@ -1263,7 +1252,6 @@ func (w *Worker) GetFiatRatesTickersList(timestampString string) (*db.ResultTick keys = append(keys, k) } sort.Strings(keys) // sort to get deterministic results - timestamp := ticker.Timestamp.UTC().Unix() return &db.ResultTickerListAsString{ Timestamp: timestamp, diff --git a/db/rocksdb.go b/db/rocksdb.go index 07dfb3d8..16840835 100644 --- a/db/rocksdb.go +++ b/db/rocksdb.go @@ -42,8 +42,9 @@ type CurrencyRatesTicker struct { // ResultTickerAsString contains formatted CurrencyRatesTicker data type ResultTickerAsString struct { - Timestamp int64 `json:"ts,omitempty"` + Timestamp int64 `json:"rate_ts,omitempty"` Rates map[string]float64 `json:"rates,omitempty"` + Rate float64 `json:"rate,omitempty"` Error string `json:"error,omitempty"` } @@ -190,17 +191,6 @@ func FiatRatesConvertDate(date string) (*time.Time, error) { 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 func (d *RocksDB) FiatRatesStoreTicker(ticker *CurrencyRatesTicker) error { if len(ticker.Rates) == 0 { diff --git a/server/public.go b/server/public.go index 6e418e0d..80115db4 100644 --- a/server/public.go +++ b/server/public.go @@ -1130,7 +1130,11 @@ func (s *PublicServer) apiSendTx(r *http.Request, apiVersion int) (interface{}, // apiTickersList returns a list of available FiatRates currencies func (s *PublicServer) apiTickersList(r *http.Request, apiVersion int) (interface{}, error) { s.metrics.ExplorerViews.With(common.Labels{"action": "api-tickers-list"}).Inc() - timestamp := strings.ToLower(r.URL.Query().Get("timestamp")) + timestampString := strings.ToLower(r.URL.Query().Get("timestamp")) + timestamp, err := strconv.ParseInt(timestampString, 10, 64) + if err != nil { + return nil, api.NewAPIError("Parameter \"timestamp\" is not a valid Unix timestamp.", true) + } result, err := s.api.GetFiatRatesTickersList(timestamp) return result, err } @@ -1145,10 +1149,16 @@ func (s *PublicServer) apiTickers(r *http.Request, apiVersion int) (interface{}, // Get tickers for specified block height or block hash s.metrics.ExplorerViews.With(common.Labels{"action": "api-tickers-block"}).Inc() result, err = s.api.GetFiatRatesForBlockID(block, currency) - } else if timestamp := r.URL.Query().Get("timestamp"); timestamp != "" { + } else if timestampString := r.URL.Query().Get("timestamp"); timestampString != "" { // Get tickers for specified timestamp s.metrics.ExplorerViews.With(common.Labels{"action": "api-tickers-date"}).Inc() - resultTickers, err := s.api.GetFiatRatesForTimestamps([]string{timestamp}, currency) + + timestamp, err := strconv.ParseInt(timestampString, 10, 64) + if err != nil { + return nil, api.NewAPIError("Parameter \"timestamp\" is not a valid Unix timestamp.", true) + } + + resultTickers, err := s.api.GetFiatRatesForTimestamps([]int64{timestamp}, currency) if err != nil { return nil, err } diff --git a/server/public_test.go b/server/public_test.go index 462fbcd6..2e0b8580 100644 --- a/server/public_test.go +++ b/server/public_test.go @@ -524,7 +524,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "application/json; charset=utf-8", body: []string{ - `{"ts":1574346615,"rates":{"eur":7134.1,"usd":7914.5}}`, + `{"rate_ts":1574346615,"rates":{"eur":7134.1,"usd":7914.5}}`, }, }, { @@ -533,7 +533,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "application/json; charset=utf-8", body: []string{ - `{"ts":1574346615,"rates":{"usd":7914.5}}`, + `{"rate_ts":1574346615,"rate":7914.5}`, }, }, { @@ -542,16 +542,16 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "application/json; charset=utf-8", body: []string{ - `{"ts":1574344800,"rates":{"usd":7814.5}}`, + `{"rate_ts":1574344800,"rate":7814.5}`, }, }, { name: "apiFiatRates incorrect timestamp", r: newGetRequest(ts.URL + "/api/v2/tickers?currency=usd×tamp=yesterday"), - status: http.StatusOK, + status: http.StatusBadRequest, contentType: "application/json; charset=utf-8", body: []string{ - `{"error":"\"yesterday\" is not a valid Unix timestamp."}`, + `{"error":"Parameter \"timestamp\" is not a valid Unix timestamp."}`, }, }, { @@ -560,7 +560,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "application/json; charset=utf-8", body: []string{ - `{"ts":7980386400,"error":"No tickers available for currency \"usd\""}`, + `{"rate_ts":7980386400,"rate":-1}`, }, }, { @@ -569,7 +569,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "application/json; charset=utf-8", body: []string{ - `{"ts":1574344800,"rates":{"eur":7100}}`, + `{"rate_ts":1574344800,"rate":7100}`, }, }, { @@ -578,7 +578,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "application/json; charset=utf-8", body: []string{ - `{"ts":1521511200,"rates":{"usd":2000}}`, + `{"rate_ts":1521511200,"rate":2000}`, }, }, { @@ -587,7 +587,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "application/json; charset=utf-8", body: []string{ - `{"ts":1521611721,"rates":{"usd":2003}}`, + `{"rate_ts":1521611721,"rate":2003}`, }, }, { @@ -596,7 +596,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "application/json; charset=utf-8", body: []string{ - `{"ts":1574346615,"rates":{"eur":7134.1}}`, + `{"rate_ts":1574346615,"rate":7134.1}`, }, }, { @@ -605,7 +605,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "application/json; charset=utf-8", body: []string{ - `{"error":"Currency \"does_not_exist\" is not available for timestamp 1574346615."}`, + `{"rate_ts":1574346615,"rate":-1}`, }, }, { @@ -1202,7 +1202,7 @@ func websocketTestsBitcoinType(t *testing.T, ts *httptest.Server) { "": "", }, }, - want: `{"id":"17","data":{"ts":1574346615,"rates":{"eur":7134.1,"usd":7914.5}}}`, + want: `{"id":"17","data":{"rate_ts":1574346615,"rates":{"eur":7134.1,"usd":7914.5}}}`, }, { name: "websocket getCurrentFiatRates usd", @@ -1212,7 +1212,7 @@ func websocketTestsBitcoinType(t *testing.T, ts *httptest.Server) { "currency": "usd", }, }, - want: `{"id":"18","data":{"ts":1574346615,"rates":{"usd":7914.5}}}`, + want: `{"id":"18","data":{"rate_ts":1574346615,"rate":7914.5}}`, }, { name: "websocket getCurrentFiatRates eur", @@ -1222,7 +1222,7 @@ func websocketTestsBitcoinType(t *testing.T, ts *httptest.Server) { "currency": "eur", }, }, - want: `{"id":"19","data":{"ts":1574346615,"rates":{"eur":7134.1}}}`, + want: `{"id":"19","data":{"rate_ts":1574346615,"rate":7134.1}}`, }, { name: "websocket getCurrentFiatRates incorrect currency", @@ -1253,7 +1253,7 @@ func websocketTestsBitcoinType(t *testing.T, ts *httptest.Server) { "timestamps": []string{"yesterday"}, }, }, - want: `{"id":"22","data":{"tickers":[{"error":"\"yesterday\" is not a valid Unix timestamp."}]}}`, + want: `{"id":"22","data":{"error":{"message":"json: cannot unmarshal string into Go struct field .timestamps of type int64"}}}`, }, { name: "websocket getFiatRatesForTimestamps incorrect (future) date", @@ -1261,10 +1261,10 @@ func websocketTestsBitcoinType(t *testing.T, ts *httptest.Server) { Method: "getFiatRatesForTimestamps", Params: map[string]interface{}{ "currency": "usd", - "timestamps": []string{"7885693815"}, + "timestamps": []int64{7885693815}, }, }, - want: `{"id":"23","data":{"tickers":[{"ts":7885693815,"error":"No tickers available for currency \"usd\""}]}}`, + want: `{"id":"23","data":{"tickers":[{"rate_ts":7885693815,"rate":-1}]}}`, }, { name: "websocket getFiatRatesForTimestamps exact date", @@ -1272,10 +1272,10 @@ func websocketTestsBitcoinType(t *testing.T, ts *httptest.Server) { Method: "getFiatRatesForTimestamps", Params: map[string]interface{}{ "currency": "usd", - "timestamps": []string{"1574346615"}, + "timestamps": []int64{1574346615}, }, }, - want: `{"id":"24","data":{"tickers":[{"ts":1574346615,"rates":{"usd":7914.5}}]}}`, + want: `{"id":"24","data":{"tickers":[{"rate_ts":1574346615,"rate":7914.5}]}}`, }, { name: "websocket getFiatRatesForTimestamps closest date, eur", @@ -1283,10 +1283,10 @@ func websocketTestsBitcoinType(t *testing.T, ts *httptest.Server) { Method: "getFiatRatesForTimestamps", Params: map[string]interface{}{ "currency": "eur", - "timestamps": []string{"1521507600"}, + "timestamps": []int64{1521507600}, }, }, - want: `{"id":"25","data":{"tickers":[{"ts":1521511200,"rates":{"eur":1300}}]}}`, + want: `{"id":"25","data":{"tickers":[{"rate_ts":1521511200,"rate":1300}]}}`, }, { name: "websocket getFiatRatesForTimestamps multiple timestamps usd", @@ -1294,10 +1294,10 @@ func websocketTestsBitcoinType(t *testing.T, ts *httptest.Server) { Method: "getFiatRatesForTimestamps", Params: map[string]interface{}{ "currency": "usd", - "timestamps": []string{"1570346615", "1574346615"}, + "timestamps": []int64{1570346615, 1574346615}, }, }, - want: `{"id":"26","data":{"tickers":[{"ts":1574344800,"rates":{"usd":7814.5}},{"ts":1574346615,"rates":{"usd":7914.5}}]}}`, + want: `{"id":"26","data":{"tickers":[{"rate_ts":1574344800,"rate":7814.5},{"rate_ts":1574346615,"rate":7914.5}]}}`, }, { name: "websocket getFiatRatesForTimestamps multiple timestamps eur", @@ -1305,10 +1305,10 @@ func websocketTestsBitcoinType(t *testing.T, ts *httptest.Server) { Method: "getFiatRatesForTimestamps", Params: map[string]interface{}{ "currency": "eur", - "timestamps": []string{"1570346615", "1574346615"}, + "timestamps": []int64{1570346615, 1574346615}, }, }, - want: `{"id":"27","data":{"tickers":[{"ts":1574344800,"rates":{"eur":7100}},{"ts":1574346615,"rates":{"eur":7134.1}}]}}`, + want: `{"id":"27","data":{"tickers":[{"rate_ts":1574344800,"rate":7100},{"rate_ts":1574346615,"rate":7134.1}]}}`, }, { name: "websocket getFiatRatesForTimestamps multiple timestamps with an error", @@ -1316,10 +1316,10 @@ func websocketTestsBitcoinType(t *testing.T, ts *httptest.Server) { Method: "getFiatRatesForTimestamps", Params: map[string]interface{}{ "currency": "usd", - "timestamps": []string{"1570346615", "1574346615", "not-a-real-date"}, + "timestamps": []int64{1570346615, 1574346615, 2000000000}, }, }, - 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."}]}}`, + want: `{"id":"28","data":{"tickers":[{"rate_ts":1574344800,"rate":7814.5},{"rate_ts":1574346615,"rate":7914.5},{"rate_ts":2000000000,"rate":-1}]}}`, }, { name: "websocket getFiatRatesForTimestamps multiple errors", @@ -1327,20 +1327,20 @@ func websocketTestsBitcoinType(t *testing.T, ts *httptest.Server) { Method: "getFiatRatesForTimestamps", Params: map[string]interface{}{ "currency": "usd", - "timestamps": []string{"7832854800", "not-a-real-date"}, + "timestamps": []int64{7832854800, 2000000000}, }, }, - 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."}]}}`, + want: `{"id":"29","data":{"tickers":[{"rate_ts":7832854800,"rate":-1},{"rate_ts":2000000000,"rate":-1}]}}`, }, { name: "websocket getTickersList", req: websocketReq{ Method: "getFiatRatesTickersList", Params: map[string]interface{}{ - "date": "1570346615", + "timestamp": 1570346615, }, }, - want: `{"id":"30","data":{"data_timestamp":1574344800,"available_currencies":["eur","usd"]}}`, + want: `{"id":"30","data":{"data_timestamp":1570346615,"available_currencies":["eur","usd"]}}`, }, { name: "websocket getBalanceHistory Addr2", diff --git a/server/websocket.go b/server/websocket.go index 51fceab4..59d93615 100644 --- a/server/websocket.go +++ b/server/websocket.go @@ -364,8 +364,8 @@ var requestHandlers = map[string]func(*WebsocketServer, *websocketChannel, *webs }, "getFiatRatesForTimestamps": func(s *WebsocketServer, c *websocketChannel, req *websocketReq) (rv interface{}, err error) { r := struct { - Timestamps []string `json:"timestamps"` - Currency string `json:"currency"` + Timestamps []int64 `json:"timestamps"` + Currency string `json:"currency"` }{} err = json.Unmarshal(req.Params, &r) if err == nil { @@ -375,11 +375,11 @@ var requestHandlers = map[string]func(*WebsocketServer, *websocketChannel, *webs }, "getFiatRatesTickersList": func(s *WebsocketServer, c *websocketChannel, req *websocketReq) (rv interface{}, err error) { r := struct { - Date string `json:"date"` + Timestamp int64 `json:"timestamp"` }{} err = json.Unmarshal(req.Params, &r) if err == nil { - rv, err = s.getFiatRatesTickersList(r.Date) + rv, err = s.getFiatRatesTickersList(r.Timestamp) } return }, @@ -833,12 +833,13 @@ func (s *WebsocketServer) getCurrentFiatRates(currency string) (interface{}, err return ret, err } -func (s *WebsocketServer) getFiatRatesForTimestamps(timestamps []string, currency string) (interface{}, error) { +func (s *WebsocketServer) getFiatRatesForTimestamps(timestamps []int64, currency string) (interface{}, error) { ret, err := s.api.GetFiatRatesForTimestamps(timestamps, currency) return ret, err } -func (s *WebsocketServer) getFiatRatesTickersList(timestamp string) (interface{}, error) { +func (s *WebsocketServer) getFiatRatesTickersList(timestamp int64) (interface{}, error) { + glog.Errorf("ts: %v", timestamp) ret, err := s.api.GetFiatRatesTickersList(timestamp) return ret, err } diff --git a/static/test-websocket.html b/static/test-websocket.html index ef6a0639..47cb9b5e 100644 --- a/static/test-websocket.html +++ b/static/test-websocket.html @@ -301,7 +301,7 @@ const method = 'getFiatRatesForTimestamps'; var timestamps = document.getElementById('getFiatRatesForTimestampsList').value.split(","); var currency = document.getElementById('getFiatRatesForTimestampsCurrency').value; - timestamps = timestamps.map(s => s.trim()); + timestamps = timestamps.map(Number); const params = { timestamps, "currency": currency @@ -324,9 +324,10 @@ function getFiatRatesTickersList() { const method = 'getFiatRatesTickersList'; - var date = document.getElementById('getFiatRatesTickersListDate').value; + var timestamp = document.getElementById('getFiatRatesTickersListDate').value; + timestamp = parseInt(timestamp); const params = { - date, + timestamp, }; send(method, params, function (result) { document.getElementById('getFiatRatesTickersListResult').innerText = JSON.stringify(result).replace(/,/g, ", ");