mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-13 23:16:45 +00:00
GateIO: Add websocket subscription configuration (#1599)
* GateIO: Switch TestMain to using testexch.Setup * GateIO: Test config updates * GateIO: Privatise and rename genSubs * GateIO: Subscription configuration
This commit is contained in:
@@ -15,7 +15,6 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/thrasher-corp/gocryptotrader/common"
|
||||
"github.com/thrasher-corp/gocryptotrader/common/key"
|
||||
"github.com/thrasher-corp/gocryptotrader/config"
|
||||
"github.com/thrasher-corp/gocryptotrader/core"
|
||||
"github.com/thrasher-corp/gocryptotrader/currency"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
|
||||
@@ -24,7 +23,9 @@ import (
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/kline"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/sharedtestvalues"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/subscription"
|
||||
testexch "github.com/thrasher-corp/gocryptotrader/internal/testing/exchange"
|
||||
testsubs "github.com/thrasher-corp/gocryptotrader/internal/testing/subscriptions"
|
||||
"github.com/thrasher-corp/gocryptotrader/portfolio/withdraw"
|
||||
)
|
||||
|
||||
@@ -36,30 +37,20 @@ const (
|
||||
canManipulateRealOrders = false
|
||||
)
|
||||
|
||||
var g = &Gateio{}
|
||||
var g *Gateio
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
g.SetDefaults()
|
||||
cfg := config.GetConfig()
|
||||
err := cfg.LoadConfig("../../testdata/configtest.json", true)
|
||||
if err != nil {
|
||||
log.Fatal("GateIO load config error", err)
|
||||
g = new(Gateio)
|
||||
if err := testexch.Setup(g); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
gConf, err := cfg.GetExchangeConfig("GateIO")
|
||||
if err != nil {
|
||||
log.Fatal("GateIO Setup() init error")
|
||||
}
|
||||
gConf.API.AuthenticatedSupport = true
|
||||
gConf.API.AuthenticatedWebsocketSupport = true
|
||||
gConf.API.Credentials.Key = apiKey
|
||||
gConf.API.Credentials.Secret = apiSecret
|
||||
g.Websocket = sharedtestvalues.NewTestWebsocket()
|
||||
gConf.Features.Enabled.FillsFeed = true
|
||||
gConf.Features.Enabled.TradeFeed = true
|
||||
err = g.Setup(gConf)
|
||||
if err != nil {
|
||||
log.Fatal("GateIO setup error", err)
|
||||
|
||||
if apiKey != "" && apiSecret != "" {
|
||||
g.API.AuthenticatedSupport = true
|
||||
g.API.AuthenticatedWebsocketSupport = true
|
||||
g.SetCredentials(apiKey, apiSecret, "", "", "", "")
|
||||
}
|
||||
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
|
||||
@@ -2963,12 +2954,66 @@ func TestFuturesCandlestickPushData(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateDefaultSubscriptions(t *testing.T) {
|
||||
// TestGenerateSubscriptions exercises generateSubscriptions
|
||||
func TestGenerateSubscriptions(t *testing.T) {
|
||||
t.Parallel()
|
||||
if _, err := g.GenerateDefaultSubscriptions(); err != nil {
|
||||
t.Error(err)
|
||||
|
||||
g := new(Gateio) //nolint:govet // Intentional shadow to avoid future copy/paste mistakes
|
||||
require.NoError(t, testexch.Setup(g), "Test instance Setup must not error")
|
||||
|
||||
g.Websocket.SetCanUseAuthenticatedEndpoints(true)
|
||||
g.Features.Subscriptions = append(g.Features.Subscriptions, &subscription.Subscription{
|
||||
Enabled: true, Channel: spotOrderbookChannel, Asset: asset.Spot, Interval: kline.ThousandMilliseconds, Levels: 5,
|
||||
})
|
||||
subs, err := g.generateSubscriptions()
|
||||
require.NoError(t, err, "generateSubscriptions must not error")
|
||||
exp := subscription.List{}
|
||||
for _, s := range g.Features.Subscriptions {
|
||||
for _, a := range g.GetAssetTypes(true) {
|
||||
if s.Asset != asset.All && s.Asset != a {
|
||||
continue
|
||||
}
|
||||
pairs, err := g.GetEnabledPairs(a)
|
||||
require.NoErrorf(t, err, "GetEnabledPairs %s must not error", a)
|
||||
pairs = common.SortStrings(pairs).Format(currency.PairFormat{Uppercase: true, Delimiter: "_"})
|
||||
s := s.Clone() //nolint:govet // Intentional lexical scope shadow
|
||||
s.Asset = a
|
||||
if singleSymbolChannel(channelName(s)) {
|
||||
for i := range pairs {
|
||||
s := s.Clone() //nolint:govet // Intentional lexical scope shadow
|
||||
switch s.Channel {
|
||||
case subscription.CandlesChannel:
|
||||
s.QualifiedChannel = "5m," + pairs[i].String()
|
||||
case subscription.OrderbookChannel:
|
||||
s.QualifiedChannel = pairs[i].String() + ",100ms"
|
||||
case spotOrderbookChannel:
|
||||
s.QualifiedChannel = pairs[i].String() + ",5,1000ms"
|
||||
}
|
||||
s.Pairs = pairs[i : i+1]
|
||||
exp = append(exp, s)
|
||||
}
|
||||
} else {
|
||||
s.Pairs = pairs
|
||||
s.QualifiedChannel = pairs.Join()
|
||||
exp = append(exp, s)
|
||||
}
|
||||
}
|
||||
}
|
||||
testsubs.EqualLists(t, exp, subs)
|
||||
}
|
||||
|
||||
func TestSubscribe(t *testing.T) {
|
||||
t.Parallel()
|
||||
g := new(Gateio) //nolint:govet // Intentional shadow to avoid future copy/paste mistakes
|
||||
require.NoError(t, testexch.Setup(g), "Test instance Setup must not error")
|
||||
subs, err := g.Features.Subscriptions.ExpandTemplates(g)
|
||||
require.NoError(t, err, "ExpandTemplates must not error")
|
||||
g.Features.Subscriptions = subscription.List{}
|
||||
testexch.SetupWs(t, g)
|
||||
err = g.Subscribe(subs)
|
||||
require.NoError(t, err, "Subscribe must not error")
|
||||
}
|
||||
|
||||
func TestGenerateDeliveryFuturesDefaultSubscriptions(t *testing.T) {
|
||||
t.Parallel()
|
||||
if _, err := g.GenerateDeliveryFuturesDefaultSubscriptions(); err != nil {
|
||||
|
||||
@@ -11,8 +11,10 @@ import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/Masterminds/sprig/v3"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/thrasher-corp/gocryptotrader/common"
|
||||
"github.com/thrasher-corp/gocryptotrader/currency"
|
||||
@@ -50,14 +52,25 @@ const (
|
||||
crossMarginLoanChannel = "spot.cross_loan"
|
||||
)
|
||||
|
||||
var defaultSubscriptions = []string{
|
||||
spotTickerChannel,
|
||||
spotCandlesticksChannel,
|
||||
spotOrderbookTickerChannel,
|
||||
var defaultSubscriptions = subscription.List{
|
||||
{Enabled: true, Channel: subscription.TickerChannel, Asset: asset.Spot},
|
||||
{Enabled: true, Channel: subscription.CandlesChannel, Asset: asset.Spot, Interval: kline.FiveMin},
|
||||
{Enabled: true, Channel: subscription.OrderbookChannel, Asset: asset.Spot, Interval: kline.HundredMilliseconds},
|
||||
{Enabled: true, Channel: spotBalancesChannel, Asset: asset.Spot, Authenticated: true},
|
||||
{Enabled: true, Channel: crossMarginBalanceChannel, Asset: asset.CrossMargin, Authenticated: true},
|
||||
{Enabled: true, Channel: marginBalancesChannel, Asset: asset.Margin, Authenticated: true},
|
||||
{Enabled: false, Channel: subscription.AllTradesChannel, Asset: asset.Spot},
|
||||
}
|
||||
|
||||
var fetchedCurrencyPairSnapshotOrderbook = make(map[string]bool)
|
||||
|
||||
var subscriptionNames = map[string]string{
|
||||
subscription.TickerChannel: spotTickerChannel,
|
||||
subscription.OrderbookChannel: spotOrderbookUpdateChannel,
|
||||
subscription.CandlesChannel: spotCandlesticksChannel,
|
||||
subscription.AllTradesChannel: spotTradesChannel,
|
||||
}
|
||||
|
||||
// WsConnect initiates a websocket connection
|
||||
func (g *Gateio) WsConnect() error {
|
||||
if !g.Websocket.IsEnabled() || !g.IsEnabled() {
|
||||
@@ -86,8 +99,8 @@ func (g *Gateio) WsConnect() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *Gateio) generateWsSignature(secret, event, channel string, dtime time.Time) (string, error) {
|
||||
msg := "channel=" + channel + "&event=" + event + "&time=" + strconv.FormatInt(dtime.Unix(), 10)
|
||||
func (g *Gateio) generateWsSignature(secret, event, channel string, t int64) (string, error) {
|
||||
msg := "channel=" + channel + "&event=" + event + "&time=" + strconv.FormatInt(t, 10)
|
||||
mac := hmac.New(sha512.New, []byte(secret))
|
||||
if _, err := mac.Write([]byte(msg)); err != nil {
|
||||
return "", err
|
||||
@@ -628,232 +641,91 @@ func (g *Gateio) processCrossMarginLoans(data []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GenerateDefaultSubscriptions returns default subscriptions
|
||||
func (g *Gateio) GenerateDefaultSubscriptions() (subscription.List, error) {
|
||||
channelsToSubscribe := defaultSubscriptions
|
||||
if g.Websocket.CanUseAuthenticatedEndpoints() {
|
||||
channelsToSubscribe = append(channelsToSubscribe, []string{
|
||||
crossMarginBalanceChannel,
|
||||
marginBalancesChannel,
|
||||
spotBalancesChannel}...)
|
||||
}
|
||||
|
||||
if g.IsSaveTradeDataEnabled() || g.IsTradeFeedEnabled() {
|
||||
channelsToSubscribe = append(channelsToSubscribe, spotTradesChannel)
|
||||
}
|
||||
|
||||
var subscriptions subscription.List
|
||||
var err error
|
||||
for i := range channelsToSubscribe {
|
||||
var pairs []currency.Pair
|
||||
var assetType asset.Item
|
||||
switch channelsToSubscribe[i] {
|
||||
case marginBalancesChannel:
|
||||
assetType = asset.Margin
|
||||
pairs, err = g.GetEnabledPairs(asset.Margin)
|
||||
case crossMarginBalanceChannel:
|
||||
assetType = asset.CrossMargin
|
||||
pairs, err = g.GetEnabledPairs(asset.CrossMargin)
|
||||
default:
|
||||
assetType = asset.Spot
|
||||
pairs, err = g.GetEnabledPairs(asset.Spot)
|
||||
}
|
||||
if err != nil {
|
||||
if errors.Is(err, asset.ErrNotEnabled) {
|
||||
continue // Skip if asset is not enabled.
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for j := range pairs {
|
||||
params := make(map[string]interface{})
|
||||
switch channelsToSubscribe[i] {
|
||||
case spotOrderbookChannel:
|
||||
params["level"] = 100
|
||||
params["interval"] = kline.HundredMilliseconds
|
||||
case spotCandlesticksChannel:
|
||||
params["interval"] = kline.FiveMin
|
||||
case spotOrderbookUpdateChannel:
|
||||
params["interval"] = kline.HundredMilliseconds
|
||||
}
|
||||
|
||||
fpair, err := g.FormatExchangeCurrency(pairs[j], asset.Spot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
subscriptions = append(subscriptions, &subscription.Subscription{
|
||||
Channel: channelsToSubscribe[i],
|
||||
Pairs: currency.Pairs{fpair.Upper()},
|
||||
Asset: assetType,
|
||||
Params: params,
|
||||
})
|
||||
}
|
||||
}
|
||||
return subscriptions, nil
|
||||
// generateSubscriptions returns configured subscriptions
|
||||
func (g *Gateio) generateSubscriptions() (subscription.List, error) {
|
||||
return g.Features.Subscriptions.ExpandTemplates(g)
|
||||
}
|
||||
|
||||
// handleSubscription sends a websocket message to receive data from the channel
|
||||
func (g *Gateio) handleSubscription(event string, channelsToSubscribe subscription.List) error {
|
||||
payloads, err := g.generatePayload(event, channelsToSubscribe)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// GetSubscriptionTemplate returns a subscription channel template
|
||||
func (g *Gateio) GetSubscriptionTemplate(_ *subscription.Subscription) (*template.Template, error) {
|
||||
return template.New("master.tmpl").Funcs(sprig.FuncMap()).Funcs(template.FuncMap{
|
||||
"channelName": channelName,
|
||||
"singleSymbolChannel": singleSymbolChannel,
|
||||
"interval": g.GetIntervalString,
|
||||
}).Parse(subTplText)
|
||||
}
|
||||
|
||||
// manageSubs sends a websocket message to subscribe or unsubscribe from a list of channel
|
||||
func (g *Gateio) manageSubs(event string, subs subscription.List) error {
|
||||
var errs error
|
||||
for k := range payloads {
|
||||
result, err := g.Websocket.Conn.SendMessageReturnResponse(context.TODO(), request.Unset, payloads[k].ID, payloads[k])
|
||||
if err != nil {
|
||||
errs = common.AppendError(errs, err)
|
||||
continue
|
||||
}
|
||||
var resp WsEventResponse
|
||||
if err = json.Unmarshal(result, &resp); err != nil {
|
||||
errs = common.AppendError(errs, err)
|
||||
} else {
|
||||
if resp.Error != nil && resp.Error.Code != 0 {
|
||||
errs = common.AppendError(errs, fmt.Errorf("error while %s to channel %s error code: %d message: %s", payloads[k].Event, payloads[k].Channel, resp.Error.Code, resp.Error.Message))
|
||||
continue
|
||||
}
|
||||
if payloads[k].Event == "subscribe" {
|
||||
err = g.Websocket.AddSuccessfulSubscriptions(channelsToSubscribe[k])
|
||||
} else {
|
||||
err = g.Websocket.RemoveSubscriptions(channelsToSubscribe[k])
|
||||
}
|
||||
subs, errs = subs.ExpandTemplates(g)
|
||||
if errs != nil {
|
||||
return errs
|
||||
}
|
||||
|
||||
for _, s := range subs {
|
||||
if err := func() error {
|
||||
msg, err := g.manageSubReq(event, s)
|
||||
if err != nil {
|
||||
errs = common.AppendError(errs, err)
|
||||
return err
|
||||
}
|
||||
result, err := g.Websocket.Conn.SendMessageReturnResponse(context.TODO(), request.Unset, msg.ID, msg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var resp WsEventResponse
|
||||
if err := json.Unmarshal(result, &resp); err != nil {
|
||||
return err
|
||||
}
|
||||
if resp.Error != nil && resp.Error.Code != 0 {
|
||||
return fmt.Errorf("(%d) %s", resp.Error.Code, resp.Error.Message)
|
||||
}
|
||||
if event == "unsubscribe" {
|
||||
return g.Websocket.RemoveSubscriptions(s)
|
||||
}
|
||||
return g.Websocket.AddSuccessfulSubscriptions(s)
|
||||
}(); err != nil {
|
||||
errs = common.AppendError(errs, fmt.Errorf("%s %s %s: %w", s.Channel, s.Asset, s.Pairs, err))
|
||||
}
|
||||
}
|
||||
return errs
|
||||
}
|
||||
|
||||
func (g *Gateio) generatePayload(event string, channelsToSubscribe subscription.List) ([]WsInput, error) {
|
||||
if len(channelsToSubscribe) == 0 {
|
||||
return nil, errors.New("cannot generate payload, no channels supplied")
|
||||
// manageSubReq constructs the subscription management message for a subscription
|
||||
func (g *Gateio) manageSubReq(event string, s *subscription.Subscription) (*WsInput, error) {
|
||||
req := &WsInput{
|
||||
ID: g.Websocket.Conn.GenerateMessageID(false),
|
||||
Event: event,
|
||||
Channel: channelName(s),
|
||||
Time: time.Now().Unix(),
|
||||
Payload: strings.Split(s.QualifiedChannel, ","),
|
||||
}
|
||||
var creds *account.Credentials
|
||||
var err error
|
||||
if g.Websocket.CanUseAuthenticatedEndpoints() {
|
||||
creds, err = g.GetCredentials(context.TODO())
|
||||
if s.Authenticated {
|
||||
creds, err := g.GetCredentials(context.TODO())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sig, err := g.generateWsSignature(creds.Secret, event, req.Channel, req.Time)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Auth = &WsAuthInput{
|
||||
Method: "api_key",
|
||||
Key: creds.Key,
|
||||
Sign: sig,
|
||||
}
|
||||
}
|
||||
var batch *[]string
|
||||
var intervalString string
|
||||
payloads := make([]WsInput, 0, len(channelsToSubscribe))
|
||||
for i := range channelsToSubscribe {
|
||||
if len(channelsToSubscribe[i].Pairs) != 1 {
|
||||
return nil, subscription.ErrNotSinglePair
|
||||
}
|
||||
var auth *WsAuthInput
|
||||
timestamp := time.Now()
|
||||
channelsToSubscribe[i].Pairs[0].Delimiter = currency.UnderscoreDelimiter
|
||||
params := []string{channelsToSubscribe[i].Pairs[0].String()}
|
||||
switch channelsToSubscribe[i].Channel {
|
||||
case spotOrderbookChannel:
|
||||
interval, okay := channelsToSubscribe[i].Params["interval"].(kline.Interval)
|
||||
if !okay {
|
||||
return nil, errors.New("invalid interval parameter")
|
||||
}
|
||||
level, okay := channelsToSubscribe[i].Params["level"].(int)
|
||||
if !okay {
|
||||
return nil, errors.New("invalid spot order level")
|
||||
}
|
||||
intervalString, err = g.GetIntervalString(interval)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
params = append(params,
|
||||
strconv.Itoa(level),
|
||||
intervalString,
|
||||
)
|
||||
case spotCandlesticksChannel:
|
||||
interval, ok := channelsToSubscribe[i].Params["interval"].(kline.Interval)
|
||||
if !ok {
|
||||
return nil, errors.New("missing spot candlesticks interval")
|
||||
}
|
||||
intervalString, err = g.GetIntervalString(interval)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
params = append(
|
||||
[]string{intervalString},
|
||||
params...)
|
||||
}
|
||||
switch channelsToSubscribe[i].Channel {
|
||||
case spotUserTradesChannel,
|
||||
spotBalancesChannel,
|
||||
marginBalancesChannel,
|
||||
spotFundingBalanceChannel,
|
||||
crossMarginBalanceChannel,
|
||||
crossMarginLoanChannel:
|
||||
if !g.Websocket.CanUseAuthenticatedEndpoints() {
|
||||
continue
|
||||
}
|
||||
value, ok := channelsToSubscribe[i].Params["user"].(string)
|
||||
if ok {
|
||||
params = append(
|
||||
[]string{value},
|
||||
params...)
|
||||
}
|
||||
var sigTemp string
|
||||
sigTemp, err = g.generateWsSignature(creds.Secret, event, channelsToSubscribe[i].Channel, timestamp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
auth = &WsAuthInput{
|
||||
Method: "api_key",
|
||||
Key: creds.Key,
|
||||
Sign: sigTemp,
|
||||
}
|
||||
case spotOrderbookUpdateChannel:
|
||||
interval, ok := channelsToSubscribe[i].Params["interval"].(kline.Interval)
|
||||
if !ok {
|
||||
return nil, errors.New("missing spot orderbook interval")
|
||||
}
|
||||
intervalString, err = g.GetIntervalString(interval)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
params = append(params, intervalString)
|
||||
}
|
||||
|
||||
payload := WsInput{
|
||||
ID: g.Websocket.Conn.GenerateMessageID(false),
|
||||
Event: event,
|
||||
Channel: channelsToSubscribe[i].Channel,
|
||||
Payload: params,
|
||||
Auth: auth,
|
||||
Time: timestamp.Unix(),
|
||||
}
|
||||
|
||||
if channelsToSubscribe[i].Channel == "spot.book_ticker" {
|
||||
// To get all orderbook assets subscribed it needs to be batched and
|
||||
// only spot.book_ticker can be batched, if not it will take about
|
||||
// half an hour for initial sync.
|
||||
if batch != nil {
|
||||
*batch = append(*batch, params...)
|
||||
} else {
|
||||
// Sets up pointer to the field for the outbound payload.
|
||||
payloads = append(payloads, payload)
|
||||
batch = &payloads[len(payloads)-1].Payload
|
||||
}
|
||||
continue
|
||||
}
|
||||
payloads = append(payloads, payload)
|
||||
}
|
||||
return payloads, nil
|
||||
return req, nil
|
||||
}
|
||||
|
||||
// Subscribe sends a websocket message to stop receiving data from the channel
|
||||
func (g *Gateio) Subscribe(channelsToUnsubscribe subscription.List) error {
|
||||
return g.handleSubscription("subscribe", channelsToUnsubscribe)
|
||||
func (g *Gateio) Subscribe(subs subscription.List) error {
|
||||
return g.manageSubs("subscribe", subs)
|
||||
}
|
||||
|
||||
// Unsubscribe sends a websocket message to stop receiving data from the channel
|
||||
func (g *Gateio) Unsubscribe(channelsToUnsubscribe subscription.List) error {
|
||||
return g.handleSubscription("unsubscribe", channelsToUnsubscribe)
|
||||
func (g *Gateio) Unsubscribe(subs subscription.List) error {
|
||||
return g.manageSubs("unsubscribe", subs)
|
||||
}
|
||||
|
||||
func (g *Gateio) listOfAssetsCurrencyPairEnabledFor(cp currency.Pair) map[asset.Item]bool {
|
||||
@@ -870,8 +742,43 @@ func (g *Gateio) listOfAssetsCurrencyPairEnabledFor(cp currency.Pair) map[asset.
|
||||
return assetPairEnabled
|
||||
}
|
||||
|
||||
// GenerateWebsocketMessageID generates a message ID for the individual
|
||||
// connection.
|
||||
// GenerateWebsocketMessageID generates a message ID for the individual connection
|
||||
func (g *Gateio) GenerateWebsocketMessageID(bool) int64 {
|
||||
return g.Counter.IncrementAndGet()
|
||||
}
|
||||
|
||||
// channelName converts global channel names to gateio specific channel names
|
||||
func channelName(s *subscription.Subscription) string {
|
||||
if name, ok := subscriptionNames[s.Channel]; ok {
|
||||
return name
|
||||
}
|
||||
return s.Channel
|
||||
}
|
||||
|
||||
// singleSymbolChannel returns if the channel should be fanned out into single symbol requests
|
||||
func singleSymbolChannel(name string) bool {
|
||||
switch name {
|
||||
case spotCandlesticksChannel, spotOrderbookUpdateChannel, spotOrderbookChannel:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
const subTplText = `
|
||||
{{- with $name := channelName $.S }}
|
||||
{{- range $asset, $pairs := $.AssetPairs }}
|
||||
{{- if singleSymbolChannel $name }}
|
||||
{{- range $i, $p := $pairs -}}
|
||||
{{- if eq $name "spot.candlesticks" }}{{ interval $.S.Interval -}} , {{- end }}
|
||||
{{- $p }}
|
||||
{{- if eq "spot.order_book" $name -}} , {{- $.S.Levels }}{{ end }}
|
||||
{{- if hasPrefix "spot.order_book" $name -}} , {{- interval $.S.Interval }}{{ end }}
|
||||
{{- $.PairSeparator }}
|
||||
{{- end }}
|
||||
{{- $.AssetSeparator }}
|
||||
{{- else }}
|
||||
{{- $pairs.Join }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
`
|
||||
|
||||
@@ -146,6 +146,7 @@ func (g *Gateio) SetDefaults() {
|
||||
GlobalResultLimit: 1000,
|
||||
},
|
||||
},
|
||||
Subscriptions: defaultSubscriptions.Clone(),
|
||||
}
|
||||
g.Requester, err = request.New(g.Name,
|
||||
common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout),
|
||||
@@ -217,7 +218,7 @@ func (g *Gateio) Setup(exch *config.Exchange) error {
|
||||
Connector: g.WsConnect,
|
||||
Subscriber: g.Subscribe,
|
||||
Unsubscriber: g.Unsubscribe,
|
||||
GenerateSubscriptions: g.GenerateDefaultSubscriptions,
|
||||
GenerateSubscriptions: g.generateSubscriptions,
|
||||
Features: &g.Features.Supports.WebsocketCapabilities,
|
||||
FillsFeed: g.Features.Enabled.FillsFeed,
|
||||
TradeFeed: g.Features.Enabled.TradeFeed,
|
||||
|
||||
@@ -266,7 +266,7 @@ func (g *Gateio) generateDeliveryFuturesPayload(event string, channelsToSubscrib
|
||||
params = append([]string{value}, params...)
|
||||
}
|
||||
var sigTemp string
|
||||
sigTemp, err = g.generateWsSignature(creds.Secret, event, channelsToSubscribe[i].Channel, timestamp)
|
||||
sigTemp, err = g.generateWsSignature(creds.Secret, event, channelsToSubscribe[i].Channel, timestamp.Unix())
|
||||
if err != nil {
|
||||
return [2][]WsInput{}, err
|
||||
}
|
||||
|
||||
@@ -351,7 +351,7 @@ func (g *Gateio) generateFuturesPayload(event string, channelsToSubscribe subscr
|
||||
params...)
|
||||
}
|
||||
var sigTemp string
|
||||
sigTemp, err = g.generateWsSignature(creds.Secret, event, channelsToSubscribe[i].Channel, timestamp)
|
||||
sigTemp, err = g.generateWsSignature(creds.Secret, event, channelsToSubscribe[i].Channel, timestamp.Unix())
|
||||
if err != nil {
|
||||
return [2][]WsInput{}, err
|
||||
}
|
||||
|
||||
@@ -238,7 +238,7 @@ func (g *Gateio) generateOptionsPayload(event string, channelsToSubscribe subscr
|
||||
return nil, err
|
||||
}
|
||||
var sigTemp string
|
||||
sigTemp, err = g.generateWsSignature(creds.Secret, event, channelsToSubscribe[i].Channel, timestamp)
|
||||
sigTemp, err = g.generateWsSignature(creds.Secret, event, channelsToSubscribe[i].Channel, timestamp.Unix())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
8
go.mod
8
go.mod
@@ -3,6 +3,7 @@ module github.com/thrasher-corp/gocryptotrader
|
||||
go 1.23.0
|
||||
|
||||
require (
|
||||
github.com/Masterminds/sprig/v3 v3.2.3
|
||||
github.com/buger/jsonparser v1.1.1
|
||||
github.com/d5/tengo/v2 v2.17.0
|
||||
github.com/gofrs/uuid v4.4.0+incompatible
|
||||
@@ -33,14 +34,21 @@ require (
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||
github.com/Masterminds/semver/v3 v3.2.0 // indirect
|
||||
github.com/boombuler/barcode v1.0.1 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/friendsofgo/errors v0.9.2 // indirect
|
||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/huandu/xstrings v1.3.3 // indirect
|
||||
github.com/imdario/mergo v0.3.11 // indirect
|
||||
github.com/magiconair/properties v1.8.7 // indirect
|
||||
github.com/mitchellh/copystructure v1.0.0 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/mitchellh/reflectwalk v1.0.0 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
|
||||
41
go.sum
41
go.sum
@@ -6,6 +6,12 @@ cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykW
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/DATA-DOG/go-sqlmock v1.3.3 h1:CWUqKXe0s8A2z6qCgkP4Kru7wC11YoAnoupUKFDnH08=
|
||||
github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
|
||||
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
|
||||
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
|
||||
github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g=
|
||||
github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
|
||||
github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA=
|
||||
github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
@@ -83,6 +89,9 @@ github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
|
||||
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
|
||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
@@ -97,6 +106,10 @@ github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjw
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1NhHmEPPzzuhWgcza5M1Ji1I=
|
||||
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/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4=
|
||||
github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||
github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA=
|
||||
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
@@ -125,10 +138,14 @@ github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3v
|
||||
github.com/mattn/go-sqlite3 v1.14.23 h1:gbShiuAP1W5j9UOksQ06aiiqPMxYecovVGwmTxWtuw0=
|
||||
github.com/mattn/go-sqlite3 v1.14.23/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ=
|
||||
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY=
|
||||
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
@@ -166,6 +183,7 @@ github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgY
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
|
||||
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
|
||||
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
|
||||
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
|
||||
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
@@ -179,6 +197,7 @@ github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTd
|
||||
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
|
||||
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
|
||||
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
||||
@@ -199,6 +218,7 @@ github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
@@ -232,6 +252,7 @@ github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGC
|
||||
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
@@ -250,6 +271,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
|
||||
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
||||
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
|
||||
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
@@ -261,6 +284,7 @@ golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHl
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -272,6 +296,9 @@ golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||
golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
|
||||
golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
@@ -283,6 +310,7 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -293,12 +321,22 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190927073244-c990c680b611/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
|
||||
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
|
||||
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
@@ -314,6 +352,7 @@ golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtn
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@@ -352,6 +391,8 @@ gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bl
|
||||
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.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
|
||||
29
testdata/configtest.json
vendored
29
testdata/configtest.json
vendored
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user