Skip to content

Commit

Permalink
(Breaking Changes) Client is now an interface and uses new package in…
Browse files Browse the repository at this point in the history
…terfaces
  • Loading branch information
mrz1836 committed Jan 23, 2022
1 parent 8935e08 commit ea1ffef
Show file tree
Hide file tree
Showing 13 changed files with 403 additions and 282 deletions.
96 changes: 66 additions & 30 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,39 +9,26 @@ import (
"github.com/mrz1836/go-whatsonchain"
)

// httpInterface is used for the http client (mocking heimdall)
type httpInterface interface {
// HTTPInterface is used for the http client (mocking heimdall)
type HTTPInterface interface {
Do(req *http.Request) (*http.Response, error)
}

// preevInterface is an interface for the Preev Client
type preevInterface interface {
GetPair(ctx context.Context, pairID string) (pair *preev.Pair, err error)
GetPairs(ctx context.Context) (pairList *preev.PairList, err error)
GetTicker(ctx context.Context, pairID string) (ticker *preev.Ticker, err error)
GetTickers(ctx context.Context) (tickerList *preev.TickerList, err error)
}

// whatsOnChainInterface is an interface for the WOC Client
type whatsOnChainInterface interface {
GetExchangeRate(ctx context.Context) (rate *whatsonchain.ExchangeRate, err error)
}

// coinPaprikaInterface is an interface for the Coin Paprika Client
type coinPaprikaInterface interface {
// CoinPaprikaInterface is an interface for the Coin Paprika Client
type CoinPaprikaInterface interface {
GetBaseAmountAndCurrencyID(currency string, amount float64) (string, float64)
GetHistoricalTickers(ctx context.Context, coinID string, start, end time.Time, limit int, quote tickerQuote, interval tickerInterval) (response *HistoricalResponse, err error)
GetMarketPrice(ctx context.Context, coinID string) (response *TickerResponse, err error)
GetPriceConversion(ctx context.Context, baseCurrencyID, quoteCurrencyID string, amount float64) (response *PriceConversionResponse, err error)
IsAcceptedCurrency(currency string) bool
GetHistoricalTickers(ctx context.Context, coinID string, start, end time.Time, limit int, quote tickerQuote, interval tickerInterval) (response *HistoricalResponse, err error)
}

// Client is the parent struct that contains the provider clients and list of providers to use
type Client struct {
CoinPaprika coinPaprikaInterface // Coin Paprika client
Preev preevInterface // Preev Client
Providers []Provider // List of providers to use (in order for fail-over)
WhatsOnChain whatsOnChainInterface // WhatsOnChain client
coinPaprika CoinPaprikaInterface // Coin Paprika client
preev preev.ClientInterface // Preev Client
providers []Provider // List of providers to use (in order for fail-over)
whatsOnChain whatsonchain.ChainService // WhatsOnChain (chain services)
}

// ClientOptions holds all the configuration for connection, dialer and transport
Expand Down Expand Up @@ -120,14 +107,16 @@ func DefaultClientOptions() (clientOptions *ClientOptions) {
}

// NewClient creates a new client for requests
func NewClient(clientOptions *ClientOptions, customHTTPClient *http.Client, providers ...Provider) (client *Client) {
client = new(Client)
func NewClient(clientOptions *ClientOptions, customHTTPClient HTTPInterface,
providers ...Provider) ClientInterface {

c := new(Client)

// No providers? (Use the default set for now)
if len(providers) == 0 {
client.Providers = defaultProviders
c.providers = defaultProviders
} else {
client.Providers = providers
c.providers = providers
}

// Set default options if none are provided
Expand All @@ -136,13 +125,60 @@ func NewClient(clientOptions *ClientOptions, customHTTPClient *http.Client, prov
}

// Create a client for Coin Paprika
client.CoinPaprika = createPaprikaClient(clientOptions, customHTTPClient)
c.coinPaprika = createPaprikaClient(
clientOptions, customHTTPClient,
)

// Create a client for Preev
client.Preev = preev.NewClient(clientOptions.ToPreevOptions(), customHTTPClient)
c.preev = preev.NewClient(
clientOptions.ToPreevOptions(), customHTTPClient,
)

// Create a client for WhatsOnChain
client.WhatsOnChain = whatsonchain.NewClient(whatsonchain.NetworkMain, clientOptions.ToWhatsOnChainOptions(), customHTTPClient)
c.whatsOnChain = whatsonchain.NewClient(
whatsonchain.NetworkMain, clientOptions.ToWhatsOnChainOptions(), customHTTPClient,
)

return
return c
}

// Providers is the list of providers
func (c *Client) Providers() []Provider {
return c.providers
}

// CoinPaprika will return the client
func (c *Client) CoinPaprika() CoinPaprikaInterface {
return c.coinPaprika
}

// SetCoinPaprika will set the client
func (c *Client) SetCoinPaprika(client CoinPaprikaInterface) {
if client != nil {
c.coinPaprika = client
}
}

// Preev will return the client
func (c *Client) Preev() preev.ClientInterface {
return c.preev
}

// SetPreev will set the client
func (c *Client) SetPreev(client preev.ClientInterface) {
if client != nil {
c.preev = client
}
}

// WhatsOnChain will return the client
func (c *Client) WhatsOnChain() whatsonchain.ChainService {
return c.whatsOnChain
}

// SetWhatsOnChain will set the client
func (c *Client) SetWhatsOnChain(client whatsonchain.ChainService) {
if client != nil {
c.whatsOnChain = client
}
}
8 changes: 4 additions & 4 deletions client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,17 @@ func TestNewClient(t *testing.T) {
assert.NotNil(t, client)

// Test default providers
assert.Equal(t, ProviderCoinPaprika, client.Providers[0])
assert.Equal(t, ProviderWhatsOnChain, client.Providers[1])
assert.Equal(t, ProviderPreev, client.Providers[2])
assert.Equal(t, ProviderCoinPaprika, client.Providers()[0])
assert.Equal(t, ProviderWhatsOnChain, client.Providers()[1])
assert.Equal(t, ProviderPreev, client.Providers()[2])
})

t.Run("custom http client", func(t *testing.T) {
client := NewClient(nil, http.DefaultClient, ProviderPreev)
assert.NotNil(t, client)

// Test custom providers
assert.Equal(t, ProviderPreev, client.Providers[0])
assert.Equal(t, ProviderPreev, client.Providers()[0])
})

}
Expand Down
20 changes: 10 additions & 10 deletions coinpaprika.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ func (p PriceConversionResponse) GetSatoshi() (satoshi int64, err error) {

// PaprikaClient is the client for Coin Paprika
type PaprikaClient struct {
HTTPClient httpInterface // carries out the http operations (heimdall client)
HTTPClient HTTPInterface // carries out the http operations (heimdall client)
UserAgent string
}

Expand All @@ -148,16 +148,10 @@ type lastRequest struct {
}

// createPaprikaClient will make a new http client based on the options provided
func createPaprikaClient(options *ClientOptions, customHTTPClient *http.Client) (c *PaprikaClient) {
func createPaprikaClient(options *ClientOptions, customHTTPClient HTTPInterface) CoinPaprikaInterface {

// Create a client
c = new(PaprikaClient)

// Is there a custom HTTP client to use?
if customHTTPClient != nil {
c.HTTPClient = customHTTPClient
return
}
c := new(PaprikaClient)

// Set options (either default or user modified)
if options == nil {
Expand All @@ -167,6 +161,12 @@ func createPaprikaClient(options *ClientOptions, customHTTPClient *http.Client)
// Set the user agent
c.UserAgent = options.UserAgent

// Is there a custom HTTP client to use?
if customHTTPClient != nil {
c.HTTPClient = customHTTPClient
return c
}

// dial is the net dialer for clientDefaultTransport
dial := &net.Dialer{KeepAlive: options.DialerKeepAlive, Timeout: options.DialerTimeout}

Expand Down Expand Up @@ -209,7 +209,7 @@ func createPaprikaClient(options *ClientOptions, customHTTPClient *http.Client)
)
}

return
return c
}

// GetBaseAmountAndCurrencyID will return an ID and default amount
Expand Down
39 changes: 19 additions & 20 deletions coinpaprika_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,11 +114,10 @@ func (m *mockHTTPPaprika) Do(req *http.Request) (*http.Response, error) {
}

// newMockPaprikaClient returns a client for mocking (using a custom HTTP interface)
func newMockPaprikaClient(httpClient httpInterface) *Client {
client := NewClient(nil, nil)
cp := createPaprikaClient(nil, nil)
cp.HTTPClient = httpClient
client.CoinPaprika = cp
func newMockPaprikaClient(httpClient HTTPInterface) ClientInterface {
client := NewClient(nil, httpClient)
// cp := createPaprikaClient(nil, httpClient)
// client.CoinPaprika = cp
return client
}

Expand Down Expand Up @@ -195,7 +194,7 @@ func TestPaprikaClient_GetBaseAmountAndCurrencyID(t *testing.T) {
}
for _, test := range tests {
t.Run(test.testCase, func(t *testing.T) {
currencyName, amount := client.CoinPaprika.GetBaseAmountAndCurrencyID(test.currency, test.amount)
currencyName, amount := client.CoinPaprika().GetBaseAmountAndCurrencyID(test.currency, test.amount)
assert.Equal(t, test.expectedAmount, amount)
assert.Equal(t, test.expectedCurrency, currencyName)
})
Expand Down Expand Up @@ -245,7 +244,7 @@ func TestPaprikaClient_GetPriceConversion(t *testing.T) {
}
for _, test := range tests {
t.Run(test.testCase, func(t *testing.T) {
output, err := client.CoinPaprika.GetPriceConversion(context.Background(), test.baseCurrency, test.quoteCurrency, test.amount)
output, err := client.CoinPaprika().GetPriceConversion(context.Background(), test.baseCurrency, test.quoteCurrency, test.amount)
assert.NoError(t, err)
assert.NotNil(t, output)
assert.Equal(t, test.expectedPrice, output.Price)
Expand Down Expand Up @@ -279,7 +278,7 @@ func TestPaprikaClient_GetPriceConversion(t *testing.T) {
}
for _, test := range tests {
t.Run(test.testCase, func(t *testing.T) {
output, err := client.CoinPaprika.GetPriceConversion(context.Background(), test.baseCurrency, test.quoteCurrency, test.amount)
output, err := client.CoinPaprika().GetPriceConversion(context.Background(), test.baseCurrency, test.quoteCurrency, test.amount)
assert.Error(t, err)
assert.NotNil(t, output)
assert.Equal(t, test.expectedStatusCode, output.LastRequest.StatusCode)
Expand Down Expand Up @@ -311,7 +310,7 @@ func TestPaprikaClient_GetMarketPrice(t *testing.T) {
}
for _, test := range tests {
t.Run(test.testCase, func(t *testing.T) {
output, err := client.CoinPaprika.GetMarketPrice(context.Background(), test.coinID)
output, err := client.CoinPaprika().GetMarketPrice(context.Background(), test.coinID)
assert.NoError(t, err)
assert.NotNil(t, output)
assert.Equal(t, test.expectedPrice, output.Quotes.USD.Price)
Expand Down Expand Up @@ -339,7 +338,7 @@ func TestPaprikaClient_GetMarketPrice(t *testing.T) {
}
for _, test := range tests {
t.Run(test.testCase, func(t *testing.T) {
output, err := client.CoinPaprika.GetMarketPrice(context.Background(), test.coinID)
output, err := client.CoinPaprika().GetMarketPrice(context.Background(), test.coinID)
assert.Error(t, err)
assert.NotNil(t, output)
assert.Equal(t, test.expectedStatusCode, output.LastRequest.StatusCode)
Expand Down Expand Up @@ -388,7 +387,7 @@ func TestPaprikaClient_IsAcceptedCurrency(t *testing.T) {
}
for _, test := range tests {
t.Run(test.testCase, func(t *testing.T) {
found := client.CoinPaprika.IsAcceptedCurrency(test.currency)
found := client.CoinPaprika().IsAcceptedCurrency(test.currency)
assert.Equal(t, test.expectedFound, found)
})
}
Expand Down Expand Up @@ -476,7 +475,7 @@ func TestPaprikaClient_GetHistoricalTickers(t *testing.T) {
}
for _, test := range tests {
t.Run(test.testCase, func(t *testing.T) {
output, err := client.CoinPaprika.GetHistoricalTickers(
output, err := client.CoinPaprika().GetHistoricalTickers(
context.Background(), test.coinID, test.start, test.end, test.limit, test.quote, test.interval,
)
assert.NoError(t, err)
Expand All @@ -489,7 +488,7 @@ func TestPaprikaClient_GetHistoricalTickers(t *testing.T) {
})

t.Run("invalid start time", func(t *testing.T) {
output, err := client.CoinPaprika.GetHistoricalTickers(
output, err := client.CoinPaprika().GetHistoricalTickers(
context.Background(), CoinPaprikaQuoteID, time.Time{}, time.Time{}, 100, TickerQuoteUSD, TickerInterval1h,
)
assert.Error(t, err)
Expand All @@ -498,7 +497,7 @@ func TestPaprikaClient_GetHistoricalTickers(t *testing.T) {
})

t.Run("empty end time, bad start time", func(t *testing.T) {
output, err := client.CoinPaprika.GetHistoricalTickers(
output, err := client.CoinPaprika().GetHistoricalTickers(
context.Background(), CoinPaprikaQuoteID, time.Now().UTC().Add(2*time.Hour), time.Time{}, 100, TickerQuoteUSD, TickerInterval1h,
)
assert.Error(t, err)
Expand All @@ -507,7 +506,7 @@ func TestPaprikaClient_GetHistoricalTickers(t *testing.T) {
})

t.Run("same times", func(t *testing.T) {
output, err := client.CoinPaprika.GetHistoricalTickers(
output, err := client.CoinPaprika().GetHistoricalTickers(
context.Background(), CoinPaprikaQuoteID,
time.Date(2021, 1, 1, 1, 1, 1, 1, time.UTC),
time.Date(2021, 1, 1, 1, 1, 1, 1, time.UTC),
Expand All @@ -519,7 +518,7 @@ func TestPaprikaClient_GetHistoricalTickers(t *testing.T) {
})

t.Run("over the limit", func(t *testing.T) {
output, err := client.CoinPaprika.GetHistoricalTickers(
output, err := client.CoinPaprika().GetHistoricalTickers(
context.Background(), CoinPaprikaQuoteID,
time.Date(2021, 1, 1, 1, 1, 1, 1, time.UTC),
time.Date(2021, 1, 2, 1, 1, 1, 1, time.UTC),
Expand All @@ -531,7 +530,7 @@ func TestPaprikaClient_GetHistoricalTickers(t *testing.T) {
})

t.Run("no limit set - use default", func(t *testing.T) {
output, err := client.CoinPaprika.GetHistoricalTickers(
output, err := client.CoinPaprika().GetHistoricalTickers(
context.Background(), CoinPaprikaQuoteID,
time.Date(2021, 1, 1, 1, 1, 1, 1, time.UTC),
time.Date(2021, 1, 2, 1, 1, 1, 1, time.UTC),
Expand All @@ -543,7 +542,7 @@ func TestPaprikaClient_GetHistoricalTickers(t *testing.T) {
})

t.Run("invalid ticker", func(t *testing.T) {
output, err := client.CoinPaprika.GetHistoricalTickers(
output, err := client.CoinPaprika().GetHistoricalTickers(
context.Background(), "unknown",
time.Date(2021, 1, 1, 1, 1, 1, 1, time.UTC),
time.Date(2021, 1, 2, 1, 1, 1, 1, time.UTC),
Expand All @@ -556,7 +555,7 @@ func TestPaprikaClient_GetHistoricalTickers(t *testing.T) {
})

t.Run("error response", func(t *testing.T) {
output, err := client.CoinPaprika.GetHistoricalTickers(
output, err := client.CoinPaprika().GetHistoricalTickers(
context.Background(), "error",
time.Date(2021, 1, 1, 1, 1, 1, 1, time.UTC),
time.Date(2021, 1, 2, 1, 1, 1, 1, time.UTC),
Expand All @@ -572,7 +571,7 @@ func TestPaprikaClient_GetHistoricalTickers(t *testing.T) {
client = newMockClient(&mockWOCValid{}, &mockPaprikaFailed{}, &mockPreevValid{})
assert.NotNil(t, client)

resp, rateErr := client.CoinPaprika.GetHistoricalTickers(
resp, rateErr := client.CoinPaprika().GetHistoricalTickers(
context.Background(), CoinPaprikaQuoteID, time.Now().UTC().Add(-1*time.Hour), time.Now().UTC(), maxHistoricalLimit+1, TickerQuoteUSD, TickerInterval1h,
)
assert.Error(t, rateErr)
Expand Down
2 changes: 1 addition & 1 deletion config.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import "strings"
const (

// version is the current package version
version = "v0.1.15"
version = "v0.2.0"

// defaultUserAgent is the default user agent for all requests
defaultUserAgent string = "go-bsvrates: " + version
Expand Down
8 changes: 4 additions & 4 deletions conversions.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,27 +20,27 @@ func (c *Client) GetConversion(ctx context.Context, currency Currency, amount fl
}

// Loop providers and get a conversion value
for _, provider := range c.Providers {
for _, provider := range c.Providers() {
providerUsed = provider
switch provider {
case ProviderCoinPaprika:
var response *PriceConversionResponse
if response, err = c.CoinPaprika.GetPriceConversion(
if response, err = c.CoinPaprika().GetPriceConversion(
ctx, USDCurrencyID, CoinPaprikaQuoteID, amount,
); err == nil && response != nil {
satoshis, err = response.GetSatoshi()
}
case ProviderWhatsOnChain:
var response *whatsonchain.ExchangeRate
if response, err = c.WhatsOnChain.GetExchangeRate(ctx); err == nil && response != nil {
if response, err = c.WhatsOnChain().GetExchangeRate(ctx); err == nil && response != nil {
var rate float64
if rate, err = strconv.ParseFloat(response.Rate, 64); err == nil {
satoshis, err = ConvertPriceToSatoshis(rate, amount)
}
}
case ProviderPreev:
var response *preev.Ticker
if response, err = c.Preev.GetTicker(
if response, err = c.Preev().GetTicker(
ctx, PreevTickerID,
); err == nil && response != nil {
satoshis, err = ConvertPriceToSatoshis(response.Prices.Ppi.LastPrice, amount)
Expand Down
Loading

0 comments on commit ea1ffef

Please sign in to comment.