base

package
v1.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 16, 2025 License: Apache-2.0 Imports: 15 Imported by: 0

README

Base Provider

Overview

The Base Provider is a special provider that is used to provide the base functionality for all other providers. It is not intended to be used directly, but instead serves as a base for other providers to extend.

The base provider is responsible for the following:

  • Implementing the oracle Provider interface directly just by inheriting the base provider.
  • Having the provider data available in constant time when requested.

Each base provider implementation will be run in a separate goroutine by the main oracle process. This allows the provider to fetch data from the underlying data source asynchronously. The base provider will then store the data in a thread safe map. The main oracle service utilizing this provider can determine if the data is stale or not based on result timestamp associated with each data point.

The base provider constructs a response channel that it is always listening to and making updates as needed. Every interval, the base provider will fetch the data from the underlying data source and send the response to the response channel, respecting the number of concurrent requests to the rate limit parameters of the underlying source (if it has any).

Architecture Overview

API (HTTP) Based Providers

In order to implement API based providers, you must implement the APIDataHandler interface and the RequestHandler interfaces. The APIDataHandler is responsible for creating the URL to be sent to the HTTP client and parsing the response from the HTTP response. The RequestHandler is responsible for making the HTTP request and returning the response.

Once these two interfaces are implemented, you can then instantiate an APIQueryHandler and pass it to the base provider. The APIQueryHandler is abstracts away the logic for making the HTTP request and parsing the response. The base provider will then take care of the rest. The responses from the APIQueryHandler are sent to the base provider via a buffered channel. The base provider will then store the data in a thread safe map. To read more about the various API provider configurations available, please visit the API provider configuration documentation.

Alternatively, you can directly implement the APIFetcher interface. This is appropriate if you want to abstract over the various processes of interacting with GRPC, JSON-RPC, REST, etc. APIs.

APIDataHandler

The APIDataHandler interface is primarily responsible for constructing the URL that will fetch the desired data and parsing the response. The interface is purposefully built with generics in mind. This allows the provider to fetch data of any type from the underlying data source.

// APIDataHandler defines an interface that must be implemented by all providers that
// want to fetch data from an API using HTTP requests. This interface is meant to be
// paired with the APIQueryHandler. The APIQueryHandler will use the APIDataHandler
// to create the URL to be sent to the HTTP client and to parse the response from the
// API.
type APIDataHandler[K providertypes.ResponseKey, V providertypes.providertypes.ResponseValue] interface {
	CreateURL(ids []K) (string, error)
	ParseResponse(ids []K, response *http.Response) GetResponse[K, V]
	Atomic() bool
	Name() string
}
Determining K and V

First developers must determine the type of data that they want to fetch from the underlying data source. This can be any type that is supported by the oracle. For example, the simplest example is price data for a given currency pair (base / quote). The K type would be the currency pair and the V type would be the price data.

APIDataHandler[types.ProviderTicker, *big.Float]
CreateURL

The CreateURL function is responsible for creating the URL that will be sent to the HTTP client. The function should utilize the IDs passed in as references to the data that needs to be fetched. For example, if the data source requires a currency pair to be passed in, the CreateURL function should use the currency pair to construct the URL.

ParseResponse

The ParseResponse function is responsible for parsing the response from the API. The response should be parsed into a map of IDs to results. If any IDs are not resolved, they should be returned in the unresolved map. The timestamp associated with the result should reflect either the time the data was fetched or the time the API last updated the data.

Atomic

The Atomic function is used to determine whether the handler can make a single request for all IDs or multiple requests for each ID. If true, the handler will make a single request for all IDs. If false, the handler will make a request for each ID.

RequestHandler

The request handler is responsible for making the HTTP request and returning the response.

// RequestHandler is an interface that encapsulates sending a request to a data provider.
type RequestHandler interface {
	Do(ctx context.Context, url string) (*http.Response, error)
}
Do

The Do function is responsible for making the HTTP request and returning the response.

This interface is particularly useful if a custom HTTP client is needed. For example, if the data provider requires a custom header to be sent with the request, the RequestHandler can be used to implement this logic.

APIFetcher

The APIFetcher interface is used to fetch data from the underlying data source. This interface is used by the APIQueryHandler to encapsulate the logic for fetching data - with metrics collection and more.

// APIFetcher is an interface that encapsulates fetching data from a provider. This interface
// is meant to abstract over the various processes of interacting w/ GRPC, JSON-RPC, REST, etc. APIs.
type APIFetcher[K providertypes.ResponseKey, V providertypes.ResponseValue] interface {
	Fetch(ctx context.Context,ids []K) providertypes.GetResponse[K, V]
}

Websocket-Based Providers

In order to implement websocket-based providers, you must implement the WebSocketDataHandler interface and the WebSocketConnHandler interfaces. The WebSocketDataHandler is responsible for parsing messages from the websocket connection, constructing heartbeats, and constructing the initial subscription message(s). This handler must manage all state associated with the websocket connection i.e. connection identifiers. The WebSocketConnHandler is responsible for making the websocket connection and maintaining it - including reads, writes, dialing, and closing.

Once these two interfaces are implemented, you can then instantiate an WebSocketQueryHandler and pass it to the base provider. The WebSocketQueryHandler abstracts away the logic for connecting, reading, sending updates, and parsing responses all using the two interfaces above. The base provider will then take care of the rest - including storing the data in a thread safe manner. To read more about the various configurations available for websocket providers, please visit the websocket provider configuration documentation.

WebSocketDataHandler

The WebSocketDataHandler interface is primarily responsible for constructing the initial set of subscription messages, parsing messages received from the websocket connection, and constructing heartbeat updates. The interface is purposefully built with generics in mind. This allows the provider to fetch data of any type from the underlying data source.

// WebSocketDataHandler defines an interface that must be implemented by all providers that
// want to fetch data from a websocket. This interface is meant to be paired with the
// WebSocketQueryHandler. The WebSocketQueryHandler will use the WebSocketDataHandler to
// create establish a connection to the correct host, create subscription messages to be sent
// to the data provider, and handle incoming events accordingly.
type WebSocketDataHandler[K providertypes.ResponseKey, V providertypes.ResponseValue] interface {
	HandleMessage(message []byte) (response providertypes.GetResponse[K, V], updateMessages []WebsocketEncodedMessage, err error)
	CreateMessages(ids []K) ([]WebsocketEncodedMessage, error)
	HeartBeatMessages() ([]WebsocketEncodedMessage, error)
	Copy() WebSocketDataHandler[K, V]
}
Determining K and V

First developers must determine the type of data that they want to fetch from the underlying data source. This can be any type that is supported by the oracle. For example, the simplest example is price data for a given currency pair (base / quote). The K type would be the currency pair and the V type would be the price data.

WebSocketDataHandler[types.ProviderTicker, *big.Float]
HandleMessage

HandleMessage is used to handle a message received from the data provider. Message parsing and response creation should be handled by this data handler. Given a message from the websocket the handler should either return a response or a set of update messages.

CreateMessages

CreateMessages is used to update the connection to the data provider. This can be used to subscribe to new events or unsubscribe from events.

HeartBeatMessages

HeartBeatMessages is used to construct a heartbeat messages to be sent to the data provider. As the provider is receiving messages from the data provider, it should store any relevant identification data that is required to construct the heartbeat messages.

Copy

Copy is used to create a copy of the data handler. This is useful if the data handler needs to be shared across multiple providers.

WebSocketConnHandler

WebSocketConnHandler is an interface the encapsulates the functionality of a websocket connection to a data provider.

// WebSocketConnHandler is an interface the encapsulates the functionality of a websocket
// connection to a data provider. It provides the simple CRUD operations for a websocket
// connection. The connection handler is responsible for managing the connection to the
// data provider. This includes creating the connection, reading messages, writing messages,
// and closing the connection.
type WebSocketConnHandler interface {
	Read() ([]byte, error)
	Write(message []byte) error
	Close() error
	Dial() error
	Copy() WebSocketConnHandler
}
Read

Read() is used to read data from the data provider. This should block until data is received from the data provider.

Write

Write() is used to write data to the data provider. This should block until the data is sent to the data provider.

Close

Close() is used to close the connection to the data provider. Any resources associated with the connection should be cleaned up.

Dial

Dial() is used to establish a connection to the data provider. This should block until the connection is established.

Copy

Copy() is used to create a copy of the connection handler. This is useful if the connection handler needs to be shared across multiple providers.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Provider

type Provider[K providertypes.ResponseKey, V providertypes.ResponseValue] struct {
	// contains filtered or unexported fields
}

Provider implements a base provider that can be used to build other providers.

func NewProvider

func NewProvider[K providertypes.ResponseKey, V providertypes.ResponseValue](opts ...ProviderOption[K, V]) (*Provider[K, V], error)

NewProvider returns a new Base provider.

func (*Provider[K, V]) GetAPIConfig

func (p *Provider[K, V]) GetAPIConfig() config.APIConfig

GetAPIConfig returns the API configuration for the provider.

func (*Provider[K, V]) GetAPIHandler

func (p *Provider[K, V]) GetAPIHandler() apihandler.APIQueryHandler[K, V]

GetAPIHandler returns the API handler that the provider will use to fetch data.

func (*Provider[K, V]) GetData

func (p *Provider[K, V]) GetData() map[K]providertypes.ResolvedResult[V]

GetData returns the latest data recorded by the provider. The data is constantly updated by the provider's main loop and provides access to the latest data - prices in constant time.

func (*Provider[K, V]) GetIDs

func (p *Provider[K, V]) GetIDs() []K

GetIDs returns the set of IDs that the provider is responsible for fetching data for.

func (*Provider[K, V]) GetWebSocketHandler

func (p *Provider[K, V]) GetWebSocketHandler() wshandlers.WebSocketQueryHandler[K, V]

GetWebSocketHandler returns the WebSocket handler that the provider will use to fetch data.

func (*Provider[K, V]) IsRunning

func (p *Provider[K, V]) IsRunning() bool

IsRunning returns true if the provider is running.

func (*Provider[K, V]) Name

func (p *Provider[K, V]) Name() string

Name returns the name of the provider.

func (*Provider[K, V]) Start

func (p *Provider[K, V]) Start(ctx context.Context) error

Start starts the provider's main loop. The provider will fetch the data from the handler and continuously update the data. This blocks until the provider is stopped.

func (*Provider[K, V]) Stop

func (p *Provider[K, V]) Stop()

Stop stops the provider's main loop.

func (*Provider[K, V]) Type

func (p *Provider[K, V]) Type() providertypes.ProviderType

Type returns the type of data handler the provider uses.

func (*Provider[K, V]) Update

func (p *Provider[K, V]) Update(opts ...UpdateOption[K, V])

Update updates the provider with the given options.

type ProviderOption

type ProviderOption[K providertypes.ResponseKey, V providertypes.ResponseValue] func(*Provider[K, V])

ProviderOption is a function that can be used to modify a provider.

func WithAPIConfig

WithAPIConfig sets the APIConfig for the provider.

func WithAPIQueryHandler

WithAPIQueryHandler sets the APIQueryHandler for the provider. If your provider utilizes a API (HTTP) based provider, you should use this option to set the APIQueryHandler.

func WithIDs

WithIDs sets the IDs that the provider is responsible for fetching data for.

func WithLogger

WithLogger sets the logger for the provider.

func WithMetrics

WithMetrics sets the metrics implementation for the provider.

func WithName

WithName sets the name of the provider.

func WithWebSocketConfig

WithWebSocketConfig sets the WebSocketConfig for the provider.

func WithWebSocketQueryHandler

WithWebSocketQueryHandler sets the WebSocketQueryHandler for the provider. If your provider utilizes a websocket based provider, you should use this option to set the WebSocketQueryHandler.

type UpdateOption

type UpdateOption[K providertypes.ResponseKey, V providertypes.ResponseValue] func(*Provider[K, V])

UpdateOption are the options that can be used to update the provider.

func WithNewAPIHandler

func WithNewAPIHandler[K providertypes.ResponseKey, V providertypes.ResponseValue](
	apiHandler apihandler.APIQueryHandler[K, V],
) UpdateOption[K, V]

WithNewAPIHandler returns an option that sets the new API handler that the provider will use to fetch data.

func WithNewIDs

func WithNewIDs[K providertypes.ResponseKey, V providertypes.ResponseValue](ids []K) UpdateOption[K, V]

WithNewIDs returns an option that sets the new set of IDs that the provider is responsible for fetching data for.

func WithNewWebSocketHandler

func WithNewWebSocketHandler[K providertypes.ResponseKey, V providertypes.ResponseValue](
	wsHandler wshandlers.WebSocketQueryHandler[K, V],
) UpdateOption[K, V]

WithNewWebSocketHandler returns an option that sets the new WebSocket handler that the provider will use to fetch data.

Directories

Path Synopsis
api
websocket

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL