Files
nofx/provider/coinank/coinank_http.go
shinchan-zhai 88f6fa7911 robustness: add HTTP status code checks to market data & provider APIs
- market/api_client.go: check StatusCode for exchangeInfo, klines, price
  endpoints + add truncateBody helper for safe error messages
- market/data.go: check StatusCode for openInterest and premiumIndex
- provider/coinank: check StatusCode for GET and POST requests
- provider/twelvedata: check StatusCode for time series and quote APIs
- Prevents confusing JSON unmarshal errors when APIs return 429/500/etc
2026-03-23 11:00:24 +08:00

103 lines
2.5 KiB
Go

package coinank
import (
"nofx/safe"
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"net/http"
"net/url"
"time"
)
// CoinankClient coinank openapi url and apikey
type CoinankClient struct {
Url string
Apikey string
}
// CoinankResponse coinank openapi common response
type CoinankResponse[T any] struct {
Success bool `json:"success"`
Code string `json:"code"`
Data T `json:"data"`
}
// PageData coinank openapi pageData in response
type PageData[T any] struct {
List []T `json:"list"`
Pagination struct {
Current int `json:"current"`
Total int `json:"total"`
PageSize int `json:"pageSize"`
} `json:"pagination"`
}
var HttpError error = errors.New("http client error")
// NewCoinankClient new coinank http client for coinank openapi
func NewCoinankClient(url, apikey string) *CoinankClient {
return &CoinankClient{url, apikey}
}
// Get coinank openapi get request
func (c *CoinankClient) Get(ctx context.Context, path string, paramsMap map[string]string) (string, error) {
data := url.Values{}
for key, value := range paramsMap {
data.Add(key, value)
}
fullURL := fmt.Sprintf("%s%s?%s", c.Url, path, data.Encode())
request, err := http.NewRequestWithContext(ctx, "GET", fullURL, nil)
if err != nil {
return "", err
}
request.Header.Add("apikey", c.Apikey)
resp, err := client.Do(request)
if err != nil {
return "", err
}
defer resp.Body.Close()
body, err := safe.ReadAllLimited(resp.Body)
if err != nil {
return "", err
}
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("CoinAnk GET error (status %d): %.512s", resp.StatusCode, string(body))
}
return string(body), nil
}
// Post coinank openapi post request
func (c *CoinankClient) Post(ctx context.Context, path string, data any) (string, error) {
fullURL := fmt.Sprintf("%s%s", c.Url, path)
postData, err := json.Marshal(data)
if err != nil {
return "", err
}
request, err := http.NewRequestWithContext(ctx, "POST", fullURL, bytes.NewBuffer(postData))
if err != nil {
return "", err
}
request.Header.Set("Content-Type", "application/json")
request.Header.Add("apikey", c.Apikey)
resp, err := client.Do(request)
if err != nil {
return "", err
}
defer resp.Body.Close()
body, err := safe.ReadAllLimited(resp.Body)
if err != nil {
return "", err
}
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("CoinAnk POST error (status %d): %.512s", resp.StatusCode, string(body))
}
return string(body), nil
}
var client = &http.Client{
Timeout: 30 * time.Second,
}