@@ -2,83 +2,43 @@ package currencies
2
2
3
3
import (
4
4
"encoding/json"
5
+ "fmt"
5
6
"io/ioutil"
6
7
"net/http"
7
8
"sync/atomic"
8
9
"time"
9
10
10
11
"github.com/golang/glog"
12
+ "github.com/prebid/prebid-server/errortypes"
13
+ "github.com/prebid/prebid-server/util/timeutil"
11
14
)
12
15
13
16
// RateConverter holds the currencies conversion rates dictionary
14
17
type RateConverter struct {
15
18
httpClient httpClient
16
- done chan bool
17
- updateNotifier chan <- int
18
- fetchingInterval time.Duration
19
19
staleRatesThreshold time.Duration
20
20
syncSourceURL string
21
21
rates atomic.Value // Should only hold Rates struct
22
22
lastUpdated atomic.Value // Should only hold time.Time
23
23
constantRates Conversions
24
+ time timeutil.Time
24
25
}
25
26
26
27
// NewRateConverter returns a new RateConverter
27
28
func NewRateConverter (
28
29
httpClient httpClient ,
29
30
syncSourceURL string ,
30
- fetchingInterval time.Duration ,
31
31
staleRatesThreshold time.Duration ,
32
32
) * RateConverter {
33
- return NewRateConverterWithNotifier (
34
- httpClient ,
35
- syncSourceURL ,
36
- fetchingInterval ,
37
- staleRatesThreshold ,
38
- nil , // no notifier channel specified, won't send any notifications
39
- )
40
- }
41
-
42
- // NewRateConverterDefault returns a RateConverter with default values.
43
- // By default there will be no currencies conversions done.
44
- // `currencies.ConstantRate` will be used.
45
- func NewRateConverterDefault () * RateConverter {
46
- return NewRateConverter (& http.Client {}, "" , time .Duration (0 ), time .Duration (0 ))
47
- }
48
-
49
- // NewRateConverterWithNotifier returns a new RateConverter
50
- // it allow to pass an update chan in which the number of ticks will be passed after each tick
51
- // allowing clients to listen on updates
52
- // Do not use this method
53
- func NewRateConverterWithNotifier (
54
- httpClient httpClient ,
55
- syncSourceURL string ,
56
- fetchingInterval time.Duration ,
57
- staleRatesThreshold time.Duration ,
58
- updateNotifier chan <- int ,
59
- ) * RateConverter {
60
- rc := & RateConverter {
33
+ return & RateConverter {
61
34
httpClient : httpClient ,
62
- done : make (chan bool ),
63
- updateNotifier : updateNotifier ,
64
- fetchingInterval : fetchingInterval ,
65
35
staleRatesThreshold : staleRatesThreshold ,
66
36
syncSourceURL : syncSourceURL ,
67
37
rates : atomic.Value {},
68
38
lastUpdated : atomic.Value {},
69
39
constantRates : NewConstantRates (),
40
+ time : & timeutil.RealTime {},
70
41
}
71
-
72
- // In case host do not want to support currency lookup
73
- // we just stop here and do nothing
74
- if rc .fetchingInterval == time .Duration (0 ) {
75
- return rc
76
- }
77
-
78
- rc .Update () // Make sure to populate data before to return the converter
79
- go rc .startPeriodicFetching () // Start periodic ticking
80
-
81
- return rc
82
42
}
83
43
84
44
// fetch allows to retrieve the currencies rates from the syncSourceURL provided
@@ -93,6 +53,11 @@ func (rc *RateConverter) fetch() (*Rates, error) {
93
53
return nil , err
94
54
}
95
55
56
+ if response .StatusCode >= 400 {
57
+ message := fmt .Sprintf ("The currency rates request failed with status code %d" , response .StatusCode )
58
+ return nil , & errortypes.BadServerResponse {Message : message }
59
+ }
60
+
96
61
defer response .Body .Close ()
97
62
98
63
bytesJSON , err := ioutil .ReadAll (response .Body )
@@ -110,14 +75,14 @@ func (rc *RateConverter) fetch() (*Rates, error) {
110
75
}
111
76
112
77
// Update updates the internal currencies rates from remote sources
113
- func (rc * RateConverter ) Update () error {
78
+ func (rc * RateConverter ) update () error {
114
79
rates , err := rc .fetch ()
115
80
if err == nil {
116
81
rc .rates .Store (rates )
117
- rc .lastUpdated .Store (time .Now ())
82
+ rc .lastUpdated .Store (rc . time .Now ())
118
83
} else {
119
- if rc .CheckStaleRates () {
120
- rc .ClearRates ()
84
+ if rc .checkStaleRates () {
85
+ rc .clearRates ()
121
86
glog .Errorf ("Error updating conversion rates, falling back to constant rates: %v" , err )
122
87
} else {
123
88
glog .Errorf ("Error updating conversion rates: %v" , err )
@@ -127,37 +92,8 @@ func (rc *RateConverter) Update() error {
127
92
return err
128
93
}
129
94
130
- // startPeriodicFetching starts the periodic fetching at the given interval
131
- // triggers a first fetch when called before the first tick happen in order to initialize currencies rates map
132
- // returns a chan in which the number of data updates everytime a new update was done
133
- func (rc * RateConverter ) startPeriodicFetching () {
134
-
135
- ticker := time .NewTicker (rc .fetchingInterval )
136
- updatesTicksCount := 0
137
-
138
- for {
139
- select {
140
- case <- ticker .C :
141
- // Retries are handled by clients directly.
142
- rc .Update ()
143
- updatesTicksCount ++
144
- if rc .updateNotifier != nil {
145
- rc .updateNotifier <- updatesTicksCount
146
- }
147
- case <- rc .done :
148
- if ticker != nil {
149
- ticker .Stop ()
150
- ticker = nil
151
- }
152
- return
153
- }
154
- }
155
- }
156
-
157
- // StopPeriodicFetching stops the periodic fetching while keeping the latest currencies rates map
158
- func (rc * RateConverter ) StopPeriodicFetching () {
159
- rc .done <- true
160
- close (rc .done )
95
+ func (rc * RateConverter ) Run () error {
96
+ return rc .update ()
161
97
}
162
98
163
99
// LastUpdated returns time when currencies rates were updated
@@ -178,18 +114,19 @@ func (rc *RateConverter) Rates() Conversions {
178
114
return rc .constantRates
179
115
}
180
116
181
- // ClearRates sets the rates to nil
182
- func (rc * RateConverter ) ClearRates () {
117
+ // clearRates sets the rates to nil
118
+ func (rc * RateConverter ) clearRates () {
183
119
// atomic.Value field rates must be of type *Rates so we cast nil to that type
184
120
rc .rates .Store ((* Rates )(nil ))
185
121
}
186
122
187
- // CheckStaleRates checks if loaded third party conversion rates are stale
188
- func (rc * RateConverter ) CheckStaleRates () bool {
123
+ // checkStaleRates checks if loaded third party conversion rates are stale
124
+ func (rc * RateConverter ) checkStaleRates () bool {
189
125
if rc .staleRatesThreshold <= 0 {
190
126
return false
191
127
}
192
- currentTime := time .Now ().UTC ()
128
+
129
+ currentTime := rc .time .Now ().UTC ()
193
130
if lastUpdated := rc .lastUpdated .Load (); lastUpdated != nil {
194
131
delta := currentTime .Sub (lastUpdated .(time.Time ).UTC ())
195
132
if delta .Seconds () > rc .staleRatesThreshold .Seconds () {
@@ -202,14 +139,11 @@ func (rc *RateConverter) CheckStaleRates() bool {
202
139
// GetInfo returns setup information about the converter
203
140
func (rc * RateConverter ) GetInfo () ConverterInfo {
204
141
var rates * map [string ]map [string ]float64
205
- if rc .Rates () != nil {
206
- rates = rc .Rates ().GetRates ()
207
- }
142
+ rates = rc .Rates ().GetRates ()
208
143
return converterInfo {
209
- source : rc .syncSourceURL ,
210
- fetchingInterval : rc .fetchingInterval ,
211
- lastUpdated : rc .LastUpdated (),
212
- rates : rates ,
144
+ source : rc .syncSourceURL ,
145
+ lastUpdated : rc .LastUpdated (),
146
+ rates : rates ,
213
147
}
214
148
}
215
149
0 commit comments