From 1977bcbeef60a93de32eaa6e0d64326222b5d4b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BC=AA=E6=98=8C=E6=96=B0?= Date: Sun, 12 Apr 2020 18:16:08 +0800 Subject: [PATCH] OKex changed their api --- exchange/kraken.go | 73 ++++++++------------ exchange/okex.go | 150 +++++++++++++++++------------------------- exchange/okex_test.go | 5 +- go.mod | 3 +- go.sum | 49 +++----------- http/http.go | 17 +++-- 6 files changed, 110 insertions(+), 187 deletions(-) diff --git a/exchange/kraken.go b/exchange/kraken.go index 10e662b..a62468b 100644 --- a/exchange/kraken.go +++ b/exchange/kraken.go @@ -9,10 +9,10 @@ import ( "time" "github.com/polyrabbit/my-token/exchange/model" - - "github.com/buger/jsonparser" "github.com/polyrabbit/my-token/http" + "github.com/sirupsen/logrus" + "github.com/tidwall/gjson" ) // https://www.kraken.com/help/api @@ -27,20 +27,16 @@ func (client *krakenClient) GetName() string { return "Kraken" } -/** -Read response and check any potential errors -*/ -func (client *krakenClient) readResponse(respBytes []byte) ([]byte, error) { - var errorMsg []string - jsonparser.ArrayEach(respBytes, func(value []byte, dataType jsonparser.ValueType, offset int, err error) { - if dataType == jsonparser.String { - errorMsg = append(errorMsg, string(value)) +// Check to see if we have error in the response +func (client *krakenClient) extractError(respByte []byte) error { + errorArray := gjson.GetBytes(respByte, "error").Array() + if len(errorArray) > 0 { + errMsg := errorArray[0].Get("0").String() + if len(errMsg) != 0 { + return errors.New(errMsg) } - }, "error") - if len(errorMsg) != 0 { - return nil, errors.New(strings.Join(errorMsg, ", ")) } - return respBytes, nil + return nil } func (client *krakenClient) GetKlinePrice(symbol string, since time.Time, interval int) (float64, error) { @@ -50,55 +46,40 @@ func (client *krakenClient) GetKlinePrice(symbol string, since time.Time, interv "since": strconv.FormatInt(since.Unix(), 10), "interval": strconv.Itoa(interval), }) - if err != nil { - return 0, err - } - - content, err := client.readResponse(respByte) - if err != nil { - return 0, err + if err := client.extractError(respByte); err != nil { + return 0, fmt.Errorf("kraken get kline: %w", err) } - // jsonparser saved my life, no need to struggle with different/weird response types - klineBytes, dataType, _, err := jsonparser.Get(content, "result", symbolUpperCase, "[0]") if err != nil { return 0, err } - if dataType != jsonparser.Array { - return 0, fmt.Errorf("kline should be an array, getting %s", dataType) - } - timestamp, err := jsonparser.GetInt(klineBytes, "[0]") - if err != nil { - return 0, err - } - openPrice, err := jsonparser.GetString(klineBytes, "[1]") - if err != nil { - return 0, err + // gjson saved my life, no need to struggle with different/weird response types + candleV := gjson.GetBytes(respByte, fmt.Sprintf("result.%s.0", strings.ToUpper(symbol))).Array() + if len(candleV) != 8 { + return 0, fmt.Errorf("kraken malformed kline response, expecting 8 elements, got %d", len(candleV)) } + + timestamp := candleV[0].Int() + openPrice := candleV[1].Float() logrus.Debugf("%s - Kline for %s uses open price at %s", client.GetName(), since.Local(), time.Unix(timestamp, 0).Local()) - return strconv.ParseFloat(openPrice, 64) + return openPrice, nil } func (client *krakenClient) GetSymbolPrice(symbol string) (*model.SymbolPrice, error) { respByte, err := http.Get(krakenBaseApi+"Ticker", map[string]string{"pair": strings.ToUpper(symbol)}) - if err != nil { - return nil, err + if err := client.extractError(respByte); err != nil { + return nil, fmt.Errorf("kraken get ticker: %w", err) } - - content, err := client.readResponse(respByte) if err != nil { return nil, err } - lastPriceString, err := jsonparser.GetString(content, "result", strings.ToUpper(symbol), "c", "[0]") - if err != nil { - return nil, err - } - lastPrice, err := strconv.ParseFloat(lastPriceString, 64) - if err != nil { - return nil, err + lastPriceV := gjson.GetBytes(respByte, fmt.Sprintf("result.%s.c.0", strings.ToUpper(symbol))) + if !lastPriceV.Exists() { + return nil, fmt.Errorf("kraken malformed ticker response, missing key %s", fmt.Sprintf("result.%s.c.0", strings.ToUpper(symbol))) } + lastPrice := lastPriceV.Float() time.Sleep(time.Second) // API call rate limit var ( @@ -121,7 +102,7 @@ func (client *krakenClient) GetSymbolPrice(symbol string) (*model.SymbolPrice, e return &model.SymbolPrice{ Symbol: symbol, - Price: lastPriceString, + Price: lastPriceV.String(), UpdateAt: time.Now(), Source: client.GetName(), PercentChange1h: percentChange1h, diff --git a/exchange/okex.go b/exchange/okex.go index 00a4adc..90bd998 100644 --- a/exchange/okex.go +++ b/exchange/okex.go @@ -1,148 +1,120 @@ package exchange import ( - "encoding/json" + "errors" "fmt" "math" "strconv" - "strings" "time" "github.com/polyrabbit/my-token/exchange/model" - "github.com/polyrabbit/my-token/http" "github.com/sirupsen/logrus" + "github.com/tidwall/gjson" ) -// https://github.com/okcoin-okex/API-docs-OKEx.com -const okexBaseApi = "https://www.okex.com/api/v1" +// https://www.okex.com/docs/zh/#spot-some +const okexBaseApi = "https://www.okex.com/api/spot/v3/instruments/" type okexClient struct { AccessKey string SecretKey string } -type okexErrorResponse struct { - ErrorCode int `json:"error_code"` -} - -type okexTickerResponse struct { - okexErrorResponse - Date int64 `json:",string"` - Ticker struct { - Last float64 `json:",string"` - } -} - -type okexKlineResponse struct { - okexErrorResponse - Data [][]interface{} -} - -func (resp *okexTickerResponse) getCommonResponse() okexErrorResponse { - return resp.okexErrorResponse -} - -func (resp *okexTickerResponse) getInternalData() interface{} { - return resp -} - -func (resp *okexKlineResponse) getCommonResponse() okexErrorResponse { - return resp.okexErrorResponse -} - -func (resp *okexKlineResponse) getInternalData() interface{} { - return &resp.Data -} - -// Any way to hold the common response, instead of adding an interface here? -type okexCommonResponseProvider interface { - getCommonResponse() okexErrorResponse - getInternalData() interface{} -} - func (client *okexClient) GetName() string { return "OKEx" } -func (client *okexClient) decodeResponse(respBytes []byte, respJSON okexCommonResponseProvider) error { - // What a messy - respBody := strings.TrimSpace(string(respBytes)) - if respBody[0] == '[' { - return json.Unmarshal(respBytes, respJSON.getInternalData()) +func (client *okexClient) GetKlinePrice(symbol, granularity string, start, end time.Time) (float64, error) { + respByte, err := http.Get(okexBaseApi+symbol+"/candles", map[string]string{ + "granularity": granularity, + "start": start.UTC().Format(time.RFC3339), + "end": end.UTC().Format(time.RFC3339), + }) + if err := client.extractError(respByte); err != nil { + return 0, fmt.Errorf("okex get candles: %w", err) } - - if err := json.Unmarshal(respBytes, &respJSON); err != nil { - return err + if err != nil { + return 0, fmt.Errorf("okex get candles: %w", err) } - // All I need is to get the common part, I don't like this - commonResponse := respJSON.getCommonResponse() - if commonResponse.ErrorCode != 0 { - return fmt.Errorf("error_code: %v", commonResponse.ErrorCode) + klines := gjson.ParseBytes(respByte).Array() + if len(klines) == 0 { + return 0, fmt.Errorf("okex got empty candles response") } - return nil -} - -func (client *okexClient) GetKlinePrice(symbol, period string, size int) (float64, error) { - symbol = strings.ToLower(symbol) - respByte, err := http.Get(okexBaseApi+"/kline.do", map[string]string{ - "symbol": symbol, - "type": period, - "size": strconv.Itoa(size), - }) - if err != nil { - return 0, err + lastKline := klines[len(klines)-1] + if len(lastKline.Array()) != 6 { + return 0, fmt.Errorf(`okex malformed kline response, got size %d`, len(lastKline.Array())) } - - var respJSON okexKlineResponse - err = client.decodeResponse(respByte, &respJSON) - if err != nil { - return 0, err + updated := time.Now() + if parsed, err := time.Parse(time.RFC3339, lastKline.Get("0").String()); err == nil { + updated = parsed } - logrus.Debugf("%s - Kline for %s*%v uses price at %s", client.GetName(), period, size, - time.Unix(int64(respJSON.Data[0][0].(float64))/1000, 0)) - return strconv.ParseFloat(respJSON.Data[0][1].(string), 64) + logrus.Debugf("%s - Kline for %s seconds uses price at %s", + client.GetName(), granularity, updated.Local()) + return lastKline.Get("1").Float(), nil } func (client *okexClient) GetSymbolPrice(symbol string) (*model.SymbolPrice, error) { - respByte, err := http.Get(okexBaseApi+"/ticker.do", map[string]string{"symbol": strings.ToLower(symbol)}) + respByte, err := http.Get(okexBaseApi+symbol+"/ticker", nil) + if err := client.extractError(respByte); err != nil { + // Extract more readable first if have + return nil, fmt.Errorf("okex get symbol price: %w", err) + } if err != nil { - return nil, err + return nil, fmt.Errorf("okex get symbol price: %w", err) } - - var respJSON okexTickerResponse - err = client.decodeResponse(respByte, &respJSON) + lastV := gjson.GetBytes(respByte, "last") + if !lastV.Exists() { + return nil, fmt.Errorf(`okex malformed get symbol price response, missing "last" key`) + } + lastPrice := lastV.Float() + updateAtV := gjson.GetBytes(respByte, "timestamp") + if !updateAtV.Exists() { + return nil, fmt.Errorf(`okex malformed get symbol price response, missing "timestamp" key`) + } + updateAt, err := time.Parse(time.RFC3339, updateAtV.String()) if err != nil { - return nil, err + return nil, fmt.Errorf("okex parse timestamp: %w", err) } var percentChange1h, percentChange24h = math.MaxFloat64, math.MaxFloat64 - price1hAgo, err := client.GetKlinePrice(symbol, "1min", 60) + price1hAgo, err := client.GetKlinePrice(symbol, "60", updateAt.Add(-time.Hour), updateAt) if err != nil { logrus.Warnf("%s - Failed to get price 1 hour ago, error: %v\n", client.GetName(), err) } else if price1hAgo != 0 { - percentChange1h = (respJSON.Ticker.Last - price1hAgo) / price1hAgo * 100 + percentChange1h = (lastPrice - price1hAgo) / price1hAgo * 100 } - time.Sleep(time.Second) // Limit 1 req/sec for Kline - price24hAgo, err := client.GetKlinePrice(symbol, "3min", 492) // Why not 480? + price24hAgo, err := client.GetKlinePrice(symbol, "900", updateAt.Add(-24*time.Hour), updateAt) if err != nil { logrus.Warnf("%s - Failed to get price 24 hours ago, error: %v\n", client.GetName(), err) } else if price24hAgo != 0 { - percentChange24h = (respJSON.Ticker.Last - price24hAgo) / price24hAgo * 100 + percentChange24h = (lastPrice - price24hAgo) / price24hAgo * 100 } return &model.SymbolPrice{ Symbol: symbol, - Price: strconv.FormatFloat(respJSON.Ticker.Last, 'f', -1, 64), - UpdateAt: time.Unix(respJSON.Date, 0), + Price: strconv.FormatFloat(lastPrice, 'f', -1, 64), + UpdateAt: updateAt, Source: client.GetName(), PercentChange1h: percentChange1h, PercentChange24h: percentChange24h, }, nil } +// Check to see if we have error in the response +func (client *okexClient) extractError(respByte []byte) error { + errorMsg := gjson.GetBytes(respByte, "error_message") + if !errorMsg.Exists() { + errorMsg = gjson.GetBytes(respByte, "message") + } + if len(errorMsg.String()) != 0 { + return errors.New(errorMsg.String()) + } + return nil +} + func init() { model.Register(new(okexClient)) } diff --git a/exchange/okex_test.go b/exchange/okex_test.go index cc0e037..1bb9326 100644 --- a/exchange/okex_test.go +++ b/exchange/okex_test.go @@ -2,6 +2,7 @@ package exchange import ( "testing" + "time" ) func TestOKExClient(t *testing.T) { @@ -9,7 +10,7 @@ func TestOKExClient(t *testing.T) { var client = new(okexClient) t.Run("GetKlinePrice", func(t *testing.T) { - _, err := client.GetKlinePrice("bTC_usdt", "1min", 60) + _, err := client.GetKlinePrice("bTC_usdt", "60", time.Now().Add(-time.Hour), time.Now()) if err != nil { t.Fatalf("Unexpected error: %v", err) @@ -17,7 +18,7 @@ func TestOKExClient(t *testing.T) { }) t.Run("GetKlinePrice of unknown symbol", func(t *testing.T) { - _, err := client.GetKlinePrice("abcedfg", "1min", 60) + _, err := client.GetKlinePrice("abcedfg", "60", time.Now().Add(-time.Hour), time.Now()) if err == nil { t.Fatalf("Expecting error when fetching unknown price, but get nil") diff --git a/go.mod b/go.mod index bb810bd..573c958 100644 --- a/go.mod +++ b/go.mod @@ -3,16 +3,15 @@ module github.com/polyrabbit/my-token go 1.12 require ( - github.com/buger/jsonparser v0.0.0-20180318095312-2cac668e8456 github.com/fatih/color v0.0.0-20180213133403-507f6050b856 github.com/gosuri/uilive v0.0.4 github.com/mattn/go-colorable v0.0.0-20180310133214-efa589957cd0 github.com/mattn/go-isatty v0.0.12 // indirect github.com/mattn/go-runewidth v0.0.8 // indirect - github.com/mitchellh/gox v1.0.1 // indirect github.com/olekukonko/tablewriter v0.0.0-20180506121414-d4647c9c7a84 github.com/preichenberger/go-coinbasepro/v2 v2.0.5 github.com/sirupsen/logrus v1.4.2 github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.6.2 + github.com/tidwall/gjson v1.6.0 ) diff --git a/go.sum b/go.sum index 45739eb..14de799 100644 --- a/go.sum +++ b/go.sum @@ -7,8 +7,6 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= -github.com/buger/jsonparser v0.0.0-20180318095312-2cac668e8456 h1:SnUWpAH4lEUoS86woR12h21VMUbDe+DYp88V646wwMI= -github.com/buger/jsonparser v0.0.0-20180318095312-2cac668e8456/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= @@ -16,7 +14,6 @@ github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= @@ -44,16 +41,11 @@ github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGa github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM= -github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gosuri/uilive v0.0.3/go.mod h1:qkLSc0A5EXSP6B04TrN4oQoxqFI7A8XvoXSlJi8cwk8= github.com/gosuri/uilive v0.0.4 h1:hUEBpQDj8D8jXgtCdBu7sWsy5sbW/5GhuO8KBwJ2jyY= github.com/gosuri/uilive v0.0.4/go.mod h1:V/epo5LjjlDE5RJUcqx8dbw+zc93y5Ya3yg8tfZ74VI= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/hashicorp/go-version v1.0.0 h1:21MVWPKDphxa7ineQQTrCU5brh7OuVVAzGOCnnCPtE8= -github.com/hashicorp/go-version v1.0.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= @@ -64,32 +56,21 @@ github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvW github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= -github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mattn/go-colorable v0.0.0-20180310133214-efa589957cd0 h1:cDvUG90i1ssGJGqMNx2Ubbn+bx7VOzjdvQ45zpy0X4w= github.com/mattn/go-colorable v0.0.0-20180310133214-efa589957cd0/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-isatty v0.0.0-20171107050531-6ca4dbf54d38 h1:eIoKWEzLDzEb1w9dJWDilISnn03Bzjfbyy5oSuK0Q1E= -github.com/mattn/go-isatty v0.0.0-20171107050531-6ca4dbf54d38/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-runewidth v0.0.0-20180408055351-ce7b0b5c7b45 h1:gEaKFUUdIzJu+JNbGluisX5K1E8MGfe7vkoqR2KH5Mk= -github.com/mattn/go-runewidth v0.0.0-20180408055351-ce7b0b5c7b45/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.8 h1:3tS41NlGYSmhhe/8fhGRzc+z3AYCw1Fe1WAyLuujKs0= github.com/mattn/go-runewidth v0.0.8/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/mitchellh/gox v1.0.1 h1:x0jD3dcHk9a9xPSDN6YEL4xL6Qz0dvNYm8yZqui5chI= -github.com/mitchellh/gox v1.0.1/go.mod h1:ED6BioOGXMswlXa2zxfh/xdd5QhwYliBFn9V18Ap4z4= -github.com/mitchellh/iochan v1.0.0 h1:C+X3KsSTLFVBr/tK1eYN/vs4rJcvsiLU338UhYPJWeY= -github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= @@ -98,13 +79,9 @@ github.com/olekukonko/tablewriter v0.0.0-20180506121414-d4647c9c7a84 h1:fiKJgB4J github.com/olekukonko/tablewriter v0.0.0-20180506121414-d4647c9c7a84/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml v1.5.0 h1:5BakdOZdtKJ1FFk6QdL8iSGrMWsXgchNJcrnarjbmJQ= -github.com/pelletier/go-toml v1.5.0/go.mod h1:5N711Q9dKgbdkxHL+MEfF31hpT7l0S0s/t2kKREewys= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/preichenberger/go-coinbasepro/v2 v2.0.4 h1:9A9hFh+uz6wuO8yBaSKsyIpvmUBEOP2i540eG4nudPo= -github.com/preichenberger/go-coinbasepro/v2 v2.0.4/go.mod h1:tsiN/OFQ5FiE+T2i3r88GHDVvR/Jxkx+CGKw7JSYLrE= github.com/preichenberger/go-coinbasepro/v2 v2.0.5 h1:Mf1k3vZTuLzuzcmCJBIbT8eiRD+N2uEG2C6Ug3upXiA= github.com/preichenberger/go-coinbasepro/v2 v2.0.5/go.mod h1:tsiN/OFQ5FiE+T2i3r88GHDVvR/Jxkx+CGKw7JSYLrE= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= @@ -128,29 +105,26 @@ github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4k github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc= -github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= -github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= -github.com/spf13/viper v1.6.1 h1:VPZzIkznI1YhVMRi6vNFLHSwhnhReBfgTxIPccpfdZk= -github.com/spf13/viper v1.6.1/go.mod h1:t3iDnF5Jlj76alVNuyFBk5oUMCvsrkbvZK0WQdfDi5k= github.com/spf13/viper v1.6.2 h1:7aKfF+e8/k68gda3LOjo5RxiUqddoFxVq4BKBPrxk5E= github.com/spf13/viper v1.6.2/go.mod h1:t3iDnF5Jlj76alVNuyFBk5oUMCvsrkbvZK0WQdfDi5k= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/tidwall/gjson v1.6.0 h1:9VEQWz6LLMUsUl6PueE49ir4Ka6CzLymOAZDxpFsTDc= +github.com/tidwall/gjson v1.6.0/go.mod h1:P256ACg0Mn+j1RXIDXoss50DeIABTYK1PULOJHhxOls= +github.com/tidwall/match v1.0.1 h1:PnKP62LPNxHKTwvHHZZzdOAOCtsJTjo6dZLCwpKm5xc= +github.com/tidwall/match v1.0.1/go.mod h1:LujAq0jyVjBy028G1WhWfIzbpQfMO8bBZ6Tyb0+pL9E= +github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= +github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= @@ -183,17 +157,12 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191018095205-727590c5006e h1:ZtoklVMHQy6BFRHkbG6JzK+S6rX82//Yeok1vMlizfQ= -golang.org/x/sys v0.0.0-20191018095205-727590c5006e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= @@ -204,16 +173,14 @@ google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ij gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/http/http.go b/http/http.go index 8e9280c..4af6bab 100644 --- a/http/http.go +++ b/http/http.go @@ -1,6 +1,7 @@ package http import ( + "fmt" "io/ioutil" "net/http" "net/url" @@ -43,23 +44,24 @@ type HTTPError struct { } func (e *HTTPError) Error() string { - return e.Status + return "HTTP " + e.Status + ", body " + string(e.Body) } func Get(rawURL string, params map[string]string) ([]byte, error) { - parsedURL, err := url.Parse(rawURL) - if err != nil { - logrus.Fatalln(err) - } if params != nil { + parsedURL, err := url.Parse(rawURL) + if err != nil { + return nil, fmt.Errorf("parse url %s: %w", rawURL, err) + } query := url.Values{} for k, v := range params { query.Set(k, v) } parsedURL.RawQuery = query.Encode() + rawURL = parsedURL.String() } - req, err := http.NewRequest("GET", parsedURL.String(), nil) + req, err := http.NewRequest("GET", rawURL, nil) if err != nil { return nil, err } @@ -78,7 +80,8 @@ func Get(rawURL string, params map[string]string) ([]byte, error) { return nil, err } if !(resp.StatusCode >= 200 && resp.StatusCode < 300) { - return nil, &HTTPError{resp.Status, respBytes} + // Most non-200 responses have valid json body + return respBytes, &HTTPError{resp.Status, respBytes} } return respBytes, err }