-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* add: options * update: refactored to new client structure add: options * add: coins endpoints
- Loading branch information
1 parent
ff22ecc
commit 1ac956e
Showing
27 changed files
with
989 additions
and
193 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
package client | ||
|
||
import ( | ||
"net/http" | ||
|
||
"github.com/JulianToledano/goingecko/api" | ||
|
||
"github.com/JulianToledano/goingecko/api/coins" | ||
geckohttp "github.com/JulianToledano/goingecko/http" | ||
) | ||
|
||
// proApiHeader returns a function that sets the Pro API key header on requests | ||
func proApiHeader(apiKey string) func(r *http.Request) { | ||
return func(r *http.Request) { | ||
r.Header.Set("x-cg-pro-api-key", apiKey) | ||
} | ||
} | ||
|
||
// demoApiHeader returns a function that sets the Demo API key header on requests | ||
func demoApiHeader(apiKey string) func(r *http.Request) { | ||
return func(r *http.Request) { | ||
r.Header.Set("x-cg-demo-api-key", apiKey) | ||
} | ||
} | ||
|
||
// Client wraps the CoinGecko API client functionality | ||
type Client struct { | ||
*coins.Client | ||
|
||
url string | ||
} | ||
|
||
// NewDefaultClient creates a new Client using the default HTTP client and base URL | ||
func NewDefaultClient() *Client { | ||
return newClient( | ||
geckohttp.NewClient(geckohttp.WithHttpClient(http.DefaultClient)), | ||
api.BaseURL, | ||
) | ||
} | ||
|
||
// NewDemoApiClient creates a new Client configured for the Demo API with the provided API key and HTTP client | ||
func NewDemoApiClient(apiKey string, c *http.Client) *Client { | ||
return newClient( | ||
geckohttp.NewClient(geckohttp.WithHttpClient(c), geckohttp.WithApiHeaderFn(demoApiHeader(apiKey))), | ||
api.BaseURL, | ||
) | ||
} | ||
|
||
// NewProApiClient creates a new Client configured for the Pro API with the provided API key and HTTP client | ||
func NewProApiClient(apiKey string, c *http.Client) *Client { | ||
return newClient( | ||
geckohttp.NewClient(geckohttp.WithHttpClient(c), geckohttp.WithApiHeaderFn(proApiHeader(apiKey))), | ||
api.ProBaseURL, | ||
) | ||
} | ||
|
||
// newClient creates a new Client with the provided HTTP client and base URL | ||
func newClient(c *geckohttp.Client, url string) *Client { | ||
return &Client{ | ||
Client: coins.NewCoinsClient(c, url), | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package client | ||
|
||
import ( | ||
"context" | ||
"testing" | ||
) | ||
|
||
func TestClient_CoinsList(t *testing.T) { | ||
c := NewDefaultClient() | ||
|
||
got, err := c.CoinsList(context.Background()) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
if got != nil { | ||
t.Fatal("nil response") | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package coins | ||
|
||
import ( | ||
geckohttp "github.com/JulianToledano/goingecko/http" | ||
) | ||
|
||
type Client struct { | ||
*geckohttp.Client | ||
|
||
url string | ||
} | ||
|
||
func NewCoinsClient(c *geckohttp.Client, url string) *Client { | ||
return &Client{ | ||
c, | ||
url, | ||
} | ||
} | ||
|
||
func (c *Client) coinsUrl() string { | ||
return c.url + "/coins" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
package coins | ||
|
||
import ( | ||
"context" | ||
"encoding/json" | ||
"fmt" | ||
"net/url" | ||
"strconv" | ||
|
||
"github.com/JulianToledano/goingecko/api" | ||
"github.com/JulianToledano/goingecko/api/coins/types" | ||
) | ||
|
||
// coinsIdOption is specific to the CoinsId function | ||
type coinsIdOption interface { | ||
api.Option | ||
isCoinsIdOption() | ||
} | ||
|
||
// WithLocalization includes localized data in the response if true. | ||
// Default: true | ||
func WithLocalization(localization bool) coinsIdOption { return localizationOption{localization} } | ||
|
||
// WithTickers includes tickers data in the response if true. | ||
// Default: true | ||
func WithTickers(tickers bool) coinsIdOption { return tickersOption{tickers} } | ||
|
||
// WithMarketData includes market data in the response if true. | ||
// Default: true | ||
func WithMarketData(marketData bool) coinsIdOption { return marketDataOption{marketData} } | ||
|
||
// WithCommunityData includes community data in the response if true. | ||
// Default: true | ||
func WithCommunityData(communityData bool) coinsIdOption { | ||
return communityDataOption{communityData} | ||
} | ||
|
||
// WithDeveloperData includes developer data in the response if true. | ||
// Default: true | ||
func WithDeveloperData(developerData bool) coinsIdOption { | ||
return developerDataOption{developerData} | ||
} | ||
|
||
// WithCoinSparkline includes sparkline data in the response if true. | ||
// Default: false | ||
func WithCoinSparkline(sparkline bool) coinsIdOption { return coinSparklineOption{sparkline} } | ||
|
||
// CoinsId allows you to query all the coin data of a coin (name, price, market .... including exchange tickers) on | ||
// CoinGecko coin page based on a particular coin id. | ||
// | ||
// 👍 Tips | ||
// | ||
// You may obtain the coin id (api id) via several ways: | ||
// refers to respective coin page and find ‘api id’ | ||
// refers to /coins/list endpoint | ||
// refers to google sheets here | ||
// You may also flag to include more data such as tickers, market data, community data, developer data and sparkline | ||
// You may refer to last_updated in the endpoint response to check whether the price is stale | ||
// | ||
// 📘 Notes | ||
// | ||
// Tickers are limited to 100 items, to get more tickers, please go to /coins/{id}/tickers | ||
// Cache/Update Frequency: | ||
// Every 60 seconds for all the API plans | ||
// Community data for Twitter and Telegram will be updated on weekly basis (Reddit community data is no longer supported) | ||
func (c *Client) CoinsId(ctx context.Context, id string, options ...coinsIdOption) (*types.CoinID, error) { | ||
params := url.Values{} | ||
|
||
// Apply all the options | ||
for _, opt := range options { | ||
opt.Apply(¶ms) | ||
} | ||
|
||
rUrl := fmt.Sprintf("%s/%s?%s", c.coinsUrl(), id, params.Encode()) | ||
resp, err := c.MakeReq(ctx, rUrl) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
var data *types.CoinID | ||
err = json.Unmarshal(resp, &data) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return data, nil | ||
} | ||
|
||
// Define option types | ||
type localizationOption struct{ localization bool } | ||
type tickersOption struct{ tickers bool } | ||
type marketDataOption struct{ marketData bool } | ||
type communityDataOption struct{ communityData bool } | ||
type developerDataOption struct{ developerData bool } | ||
type coinSparklineOption struct{ sparkline bool } | ||
|
||
// Implement Option interface | ||
func (o localizationOption) Apply(v *url.Values) { | ||
v.Set("localization", strconv.FormatBool(o.localization)) | ||
} | ||
func (o tickersOption) Apply(v *url.Values) { v.Set("tickers", strconv.FormatBool(o.tickers)) } | ||
func (o marketDataOption) Apply(v *url.Values) { | ||
v.Set("market_data", strconv.FormatBool(o.marketData)) | ||
} | ||
func (o communityDataOption) Apply(v *url.Values) { | ||
v.Set("community_data", strconv.FormatBool(o.communityData)) | ||
} | ||
func (o developerDataOption) Apply(v *url.Values) { | ||
v.Set("developer_data", strconv.FormatBool(o.developerData)) | ||
} | ||
func (o coinSparklineOption) Apply(v *url.Values) { | ||
v.Set("sparkline", strconv.FormatBool(o.sparkline)) | ||
} | ||
|
||
// Implement CoinsIdOption interface | ||
func (localizationOption) isCoinsIdOption() {} | ||
func (tickersOption) isCoinsIdOption() {} | ||
func (marketDataOption) isCoinsIdOption() {} | ||
func (communityDataOption) isCoinsIdOption() {} | ||
func (developerDataOption) isCoinsIdOption() {} | ||
func (coinSparklineOption) isCoinsIdOption() {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
package coins | ||
|
||
import ( | ||
"context" | ||
"encoding/json" | ||
"fmt" | ||
"net/url" | ||
"strconv" | ||
|
||
"github.com/JulianToledano/goingecko/api" | ||
"github.com/JulianToledano/goingecko/api/coins/types" | ||
) | ||
|
||
// idHistoryOption is an interface that extends api.Option to provide | ||
// specific options for the CoinsIdHistory endpoint. It includes a marker | ||
// method isIdHistoryOption() to ensure type safety for history-specific options. | ||
type idHistoryOption interface { | ||
api.Option | ||
isIdHistoryOption() | ||
} | ||
|
||
// WithLocalizationIdHistoryOption sets whether to include localized data. | ||
// If true, returns localized data in response (name, description, etc.) | ||
// If false, returns data in English. | ||
func WithLocalizationIdHistoryOption(loc bool) idHistoryOption { | ||
return localizationIdHistoryOption{loc} | ||
} | ||
|
||
// CoinsIdHistory allows you to query the historical data (price, market cap, 24hrs volume, etc) at a given date for a | ||
// coin based on a particular coin id. | ||
// | ||
// 👍 Tips | ||
// | ||
// You may obtain the coin id (api id) via several ways: | ||
// refers to respective coin page and find ‘api id’ | ||
// refers to /coins/list endpoint | ||
// refers to google sheets here | ||
// | ||
// 📘 Notes | ||
// | ||
// The data returned is at 00:00:00 UTC | ||
// The last completed UTC day (00:00) is available 35 minutes after midnight on the next UTC day (00:35) | ||
func (c *Client) CoinsIdHistory(ctx context.Context, id, date string, options ...idHistoryOption) (*types.History, error) { | ||
params := url.Values{} | ||
params.Set("date", date) | ||
|
||
// Apply all the options | ||
for _, opt := range options { | ||
opt.Apply(¶ms) | ||
} | ||
|
||
rUrl := fmt.Sprintf("%s/%s/%s?%s", c.coinsUrl(), id, "history", params.Encode()) | ||
resp, err := c.MakeReq(ctx, rUrl) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
var data *types.History | ||
err = json.Unmarshal(resp, &data) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return data, nil | ||
} | ||
|
||
type localizationIdHistoryOption struct{ localization bool } | ||
|
||
func (o localizationIdHistoryOption) Apply(v *url.Values) { | ||
v.Set("localization", strconv.FormatBool(o.localization)) | ||
} | ||
func (localizationIdHistoryOption) isIdHistoryOption() {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
package coins | ||
|
||
import ( | ||
"context" | ||
"encoding/json" | ||
"fmt" | ||
"net/url" | ||
|
||
"github.com/JulianToledano/goingecko/api" | ||
"github.com/JulianToledano/goingecko/types" | ||
) | ||
|
||
// idMarketChartOption is an interface that extends api.Option to provide | ||
// specific options for the CoinsIdMarketChart endpoint. It includes a marker | ||
// method isIdMarketChartOptions() to ensure type safety for market chart-specific options. | ||
type idMarketChartOption interface { | ||
api.Option | ||
isIdMarketChartOption() | ||
} | ||
|
||
// WithIntervalIdMarketChart sets the interval between data points in the response. | ||
// Valid values: 5m, hourly, daily | ||
func WithIntervalIdMarketChart(interval string) idMarketChartOption { | ||
return intervalIdMarketChartOptions{interval} | ||
} | ||
|
||
// WithPrecisionIdMarketChart sets the number of decimal places in the response data. | ||
// Valid values: from 1 to 18 | ||
func WithPrecisionIdMarketChart(precision string) idMarketChartOption { | ||
return precisionIdMarketChartOptions{precision} | ||
} | ||
|
||
// CoinsIdMarketChart allows you to get the historical chart data of a coin including time in UNIX, price, market cap | ||
// and 24hrs volume based on particular coin id. | ||
// | ||
// 👍Tips | ||
// | ||
// You may obtain the coin id (api id) via several ways: | ||
// refers to respective coin page and find ‘api id’ | ||
// refers to /coins/list endpoint | ||
// refers to google sheets here | ||
// You may use tools like epoch converter to convert human readable date to UNIX timestamp | ||
// | ||
// 📘Notes | ||
// You may leave the interval params as empty for automatic granularity: | ||
// 1 day from current time = 5-minutely data | ||
// 2 - 90 days from current time = hourly data | ||
// above 90 days from current time = daily data (00:00 UTC) | ||
// For non-Enterprise plan subscribers who would like to get hourly data, please leave the interval params empty for auto granularity | ||
// The 5-minutely and hourly interval params are also exclusively available to Enterprise plan subscribers, bypassing auto-granularity: | ||
// interval=5m: 5-minutely historical data (responses include information from the past 10 days, up until 2 days ago) | ||
// interval=hourly: hourly historical data (responses include information from the past 100 days, up until now) | ||
// Cache / Update Frequency: | ||
// every 30 seconds for all the API plans (for last data point) | ||
// The last completed UTC day (00:00) data is available 10 minutes after midnight on the next UTC day (00:10). | ||
func (c *Client) CoinsIdMarketChart(ctx context.Context, id, vsCurrency, days string, options ...idMarketChartOption) (*types.MarketChart, error) { | ||
params := url.Values{} | ||
params.Add("vs_currency", vsCurrency) | ||
params.Add("days", days) | ||
params.Add("interval", "daily") | ||
|
||
for _, opt := range options { | ||
opt.Apply(¶ms) | ||
} | ||
|
||
rUrl := fmt.Sprintf("%s/%s/%s?%s", c.coinsUrl(), id, "market_chart", params.Encode()) | ||
resp, err := c.MakeReq(ctx, rUrl) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
var data *types.MarketChart | ||
err = json.Unmarshal(resp, &data) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return data, nil | ||
} | ||
|
||
type intervalIdMarketChartOptions struct{ interval string } | ||
type precisionIdMarketChartOptions struct{ precision string } | ||
|
||
func (o intervalIdMarketChartOptions) Apply(v *url.Values) { v.Set("interval", o.interval) } | ||
func (o precisionIdMarketChartOptions) Apply(v *url.Values) { v.Set("precision", o.precision) } | ||
|
||
func (o intervalIdMarketChartOptions) isIdMarketChartOption() {} | ||
func (o precisionIdMarketChartOptions) isIdMarketChartOption() {} |
Oops, something went wrong.