-
Notifications
You must be signed in to change notification settings - Fork 50
/
api.go
136 lines (106 loc) · 4.2 KB
/
api.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
package config
import (
"fmt"
"time"
)
// APIConfig defines a config for an API based data provider.
type APIConfig struct {
// Enabled indicates if the provider is enabled.
Enabled bool `json:"enabled"`
// Timeout is the amount of time the provider should wait for a response from
// its API before timing out.
Timeout time.Duration `json:"timeout"`
// Interval is the interval at which the provider should update the prices.
Interval time.Duration `json:"interval"`
// ReconnectTimeout is the amount of time the provider should wait before
// reconnecting to the API.
ReconnectTimeout time.Duration `json:"reconnectTimeout"`
// MaxQueries is the maximum number of concurrent queries that the provider will make
// within the interval.
MaxQueries int `json:"maxQueries"`
// Atomic is a flag that indicates whether the provider can fulfill its queries
// in a single request.
Atomic bool `json:"atomic"`
// Endpoints is a list of endpoints that the provider can query.
Endpoints []Endpoint `json:"endpoints"`
// BatchSize is the maximum number of IDs that the provider can query in a single
// request. This parameter must be 0 for atomic providers. Otherwise, the effective
// value will be max(1, BatchSize). Notice, if numCPs > batchSize * maxQueries then
// some currency-pairs may not be fetched each interval.
BatchSize int `json:"batchSize"`
// Name is the name of the provider that corresponds to this config.
Name string `json:"name"`
// MaxBlockHeightAge is the oldest an update from an on-chain data source can be without having its
// block height incremented. In the case where a data source has exceeded this limit and the block
// height is not increasing, price reporting will be skipped until the block height increases.
MaxBlockHeightAge time.Duration `json:"maxBlockHeightAge"`
}
// Endpoint holds all data necessary for an API provider to connect to a given endpoint
// i.e. URL, headers, authentication, etc.
type Endpoint struct {
// URL is the URL that is used to fetch data from the API.
URL string `json:"url"`
// Authentication holds all data necessary for an API provider to authenticate with
// an endpoint.
Authentication Authentication `json:"authentication"`
}
// ValidateBasic performs basic validation of the API endpoint.
func (e Endpoint) ValidateBasic() error {
if len(e.URL) == 0 {
return fmt.Errorf("endpoint url cannot be empty")
}
return e.Authentication.ValidateBasic()
}
// Authentication holds all data necessary for an API provider to authenticate with an
// endpoint.
type Authentication struct {
// HTTPHeaderAPIKey is the API-key that will be set under the X-Api-Key header
APIKey string `json:"apiKey"`
// APIKeyHeader is the header that will be used to set the API key.
APIKeyHeader string `json:"apiKeyHeader"`
}
// Enabled returns true if the authentication is enabled.
func (a Authentication) Enabled() bool {
return a.APIKey != "" && a.APIKeyHeader != ""
}
// ValidateBasic performs basic validation of the API authentication. Specifically, the APIKey + APIKeyHeader
// must be set atomically.
func (a Authentication) ValidateBasic() error {
if a.APIKey != "" && a.APIKeyHeader == "" {
return fmt.Errorf("api key header cannot be empty when api key is set")
}
if a.APIKey == "" && a.APIKeyHeader != "" {
return fmt.Errorf("api key cannot be empty when api key header is set")
}
return nil
}
// ValidateBasic performs basic validation of the API config.
func (c *APIConfig) ValidateBasic() error {
if !c.Enabled {
return nil
}
if c.MaxQueries < 1 {
return fmt.Errorf("api max queries must be greater than 0")
}
if c.Interval <= 0 || c.Timeout <= 0 || c.ReconnectTimeout <= 0 {
return fmt.Errorf("provider interval, timeout and reconnect timeout must be strictly positive")
}
if len(c.Name) == 0 {
return fmt.Errorf("provider name cannot be empty")
}
if c.BatchSize > 0 && c.Atomic {
return fmt.Errorf("batch size cannot be set for atomic providers")
}
if len(c.Endpoints) == 0 {
return fmt.Errorf("endpoints cannot be empty")
}
for _, e := range c.Endpoints {
if err := e.ValidateBasic(); err != nil {
return err
}
}
if c.MaxBlockHeightAge < 0 {
return fmt.Errorf("max_block_height_age cannot be negative")
}
return nil
}