fixed linting config

This commit is contained in:
Stephan D
2026-02-12 13:00:37 +01:00
parent 97395acd8f
commit 7cbcbb4b3c
42 changed files with 1813 additions and 237 deletions

View File

@@ -29,29 +29,36 @@ type coingeckoConnector struct {
const defaultCoinGeckoBaseURL = "https://api.coingecko.com/api/v3"
const (
defaultDialTimeoutSeconds = 5 * time.Second
defaultDialKeepAliveSeconds = 30 * time.Second
defaultTLSHandshakeTimeoutSeconds = 5 * time.Second
defaultResponseHeaderTimeoutSeconds = 10 * time.Second
defaultRequestTimeoutSeconds = 10 * time.Second
defaultDialTimeout = 5 * time.Second
defaultDialKeepAlive = 30 * time.Second
defaultTLSHandshakeTimeout = 5 * time.Second
defaultResponseHeaderTimeout = 10 * time.Second
defaultRequestTimeout = 10 * time.Second
)
func NewConnector(logger mlogger.Logger, settings model.SettingsT) (mmodel.Connector, error) {
const (
expectedSymbolParts = 2
tsToMillis = 1000
)
func NewConnector(logger mlogger.Logger, settings model.SettingsT) (mmodel.Connector, error) { //nolint:ireturn
baseURL := defaultCoinGeckoBaseURL
provider := strings.ToLower(mmodel.DriverCoinGecko.String())
dialTimeout := defaultDialTimeoutSeconds
dialKeepAlive := defaultDialKeepAliveSeconds
tlsHandshakeTimeout := defaultTLSHandshakeTimeoutSeconds
responseHeaderTimeout := defaultResponseHeaderTimeoutSeconds
requestTimeout := defaultRequestTimeoutSeconds
dialTimeout := defaultDialTimeout
dialKeepAlive := defaultDialKeepAlive
tlsHandshakeTimeout := defaultTLSHandshakeTimeout
responseHeaderTimeout := defaultResponseHeaderTimeout
requestTimeout := defaultRequestTimeout
if settings != nil {
if value, ok := settings["base_url"].(string); ok && strings.TrimSpace(value) != "" {
baseURL = strings.TrimSpace(value)
}
if value, ok := settings["provider"].(string); ok && strings.TrimSpace(value) != "" {
provider = strings.TrimSpace(value)
}
dialTimeout = common.DurationSetting(settings, "dial_timeout_seconds", dialTimeout)
dialKeepAlive = common.DurationSetting(settings, "dial_keep_alive_seconds", dialKeepAlive)
tlsHandshakeTimeout = common.DurationSetting(settings, "tls_handshake_timeout_seconds", tlsHandshakeTimeout)
@@ -88,6 +95,7 @@ func (c *coingeckoConnector) ID() mmodel.Driver {
return c.id
}
//nolint:cyclop,funlen
func (c *coingeckoConnector) FetchTicker(ctx context.Context, symbol string) (*mmodel.Ticker, error) {
coinID, vsCurrency, err := parseSymbol(symbol)
if err != nil {
@@ -98,6 +106,7 @@ func (c *coingeckoConnector) FetchTicker(ctx context.Context, symbol string) (*m
if err != nil {
return nil, merrors.InternalWrap(err, "coingecko: parse base url")
}
endpoint.Path = strings.TrimRight(endpoint.Path, "/") + "/simple/price"
query := endpoint.Query()
query.Set("ids", coinID)
@@ -113,44 +122,51 @@ func (c *coingeckoConnector) FetchTicker(ctx context.Context, symbol string) (*m
resp, err := c.client.Do(req)
if err != nil {
c.logger.Warn("CoinGecko request failed", zap.String("symbol", symbol), zap.Error(err))
return nil, merrors.InternalWrap(err, "coingecko: request failed")
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
c.logger.Warn("CoinGecko returned non-OK status", zap.String("symbol", symbol), zap.Int("status", resp.StatusCode))
return nil, merrors.Internal("coingecko: unexpected status " + strconv.Itoa(resp.StatusCode))
}
decoder := json.NewDecoder(resp.Body)
decoder.UseNumber()
var payload map[string]map[string]interface{}
if err := decoder.Decode(&payload); err != nil {
c.logger.Warn("CoinGecko decode failed", zap.String("symbol", symbol), zap.Error(err))
return nil, merrors.InternalWrap(err, "coingecko: decode response")
var payload map[string]map[string]any
decodeErr := decoder.Decode(&payload)
if decodeErr != nil {
c.logger.Warn("CoinGecko decode failed", zap.String("symbol", symbol), zap.Error(decodeErr))
return nil, merrors.InternalWrap(decodeErr, "coingecko: decode response")
}
coinData, ok := payload[coinID]
if !ok {
coinData, coinFound := payload[coinID]
if !coinFound {
return nil, merrors.Internal("coingecko: coin id not found in response")
}
priceValue, ok := coinData[vsCurrency]
if !ok {
priceValue, priceFound := coinData[vsCurrency]
if !priceFound {
return nil, merrors.Internal("coingecko: vs currency not found in response")
}
price, ok := toFloat(priceValue)
if !ok || price <= 0 {
price, priceOk := toFloat(priceValue)
if !priceOk || price <= 0 {
return nil, merrors.Internal("coingecko: invalid price value in response")
}
priceStr := strconv.FormatFloat(price, 'f', -1, 64)
timestamp := time.Now().UnixMilli()
if tsValue, ok := coinData["last_updated_at"]; ok {
if tsFloat, ok := toFloat(tsValue); ok && tsFloat > 0 {
tsMillis := int64(tsFloat * 1000)
if tsValue, tsFound := coinData["last_updated_at"]; tsFound {
if tsFloat, tsOk := toFloat(tsValue); tsOk && tsFloat > 0 {
tsMillis := int64(tsFloat * tsToMillis)
if tsMillis > 0 {
timestamp = tsMillis
}
@@ -179,14 +195,16 @@ func parseSymbol(symbol string) (string, string, error) {
case ':', '/', '-', '_':
return true
}
return false
})
if len(parts) != 2 {
if len(parts) != expectedSymbolParts {
return "", "", merrors.InvalidArgument("coingecko: symbol must be <coin_id>/<vs_currency>", "symbol")
}
coinID := strings.TrimSpace(parts[0])
vsCurrency := strings.TrimSpace(parts[1])
if coinID == "" || vsCurrency == "" {
return "", "", merrors.InvalidArgument("coingecko: symbol contains empty segments", "symbol")
@@ -195,28 +213,31 @@ func parseSymbol(symbol string) (string, string, error) {
return coinID, vsCurrency, nil
}
func toFloat(value interface{}) (float64, bool) {
switch v := value.(type) {
func toFloat(value any) (float64, bool) {
switch val := value.(type) {
case json.Number:
f, err := v.Float64()
f, err := val.Float64()
if err != nil {
return 0, false
}
return f, true
case float64:
return v, true
return val, true
case float32:
return float64(v), true
return float64(val), true
case int:
return float64(v), true
return float64(val), true
case int64:
return float64(v), true
return float64(val), true
case uint64:
return float64(v), true
return float64(val), true
case string:
if parsed, err := strconv.ParseFloat(v, 64); err == nil {
parsed, parseErr := strconv.ParseFloat(val, 64)
if parseErr == nil {
return parsed, true
}
}
return 0, false
}