-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathclient.go
executable file
·331 lines (273 loc) · 8.44 KB
/
client.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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
package apixu
import (
"encoding/json"
"errors"
"io/ioutil"
"net/http"
"net/url"
)
const (
apiVersion = "v1"
apiBaseURL = "http://api.apixu.com/" + apiVersion + "/"
currentPath = "current.json"
forecastPath = "forecast.json"
historyPath = "history.json"
searchPath = "search.json"
)
// OptionalParam describes optional query parameters used in client methods.
type OptionalParam struct {
Name string
Value string
}
// Location describes essential data of the matched location.
type Location struct {
Name string `json:"name"`
Region string `json:"region"`
Country string `json:"country"`
Lat float64 `json:"lat"`
Lon float64 `json:"lon"`
TzID string `json:"tz_id"`
LocaltimeEpoch int `json:"localtime_epoch"`
Localtime string `json:"localtime"`
}
// Current describes realtime weather information.
type Current struct {
LastUpdatedEpoch int `json:"last_updated_epoch"`
LastUpdated string `json:"last_updated"`
TempC float64 `json:"temp_c"`
TempF float64 `json:"temp_f"`
IsDay int `json:"is_day"`
Condition struct {
Text string `json:"text"`
Icon string `json:"icon"`
Code int `json:"code"`
} `json:"condition"`
WindMph float64 `json:"wind_mph"`
WindKph float64 `json:"wind_kph"`
WindDegree int `json:"wind_degree"`
WindDir string `json:"wind_dir"`
PressureMb float64 `json:"pressure_mb"`
PressureIn float64 `json:"pressure_in"`
PrecipMm float64 `json:"precip_mm"`
PrecipIn float64 `json:"precip_in"`
Humidity int `json:"humidity"`
Cloud int `json:"cloud"`
FeelslikeC float64 `json:"feelslike_c"`
FeelslikeF float64 `json:"feelslike_f"`
VisKm float64 `json:"vis_km"`
VisMiles float64 `json:"vis_miles"`
}
// Forecast describes astronomy data.
type Forecast struct {
Forecastday []struct {
Date string `json:"date"`
DateEpoch int `json:"date_epoch"`
Day struct {
MaxtempC float64 `json:"maxtemp_c"`
MaxtempF float64 `json:"maxtemp_f"`
MintempC float64 `json:"mintemp_c"`
MintempF float64 `json:"mintemp_f"`
AvgtempC float64 `json:"avgtemp_c"`
AvgtempF float64 `json:"avgtemp_f"`
MaxwindMph float64 `json:"maxwind_mph"`
MaxwindKph float64 `json:"maxwind_kph"`
TotalprecipMm float64 `json:"totalprecip_mm"`
TotalprecipIn float64 `json:"totalprecip_in"`
AvgvisKm float64 `json:"avgvis_km"`
AvgvisMiles float64 `json:"avgvis_miles"`
Avghumidity float64 `json:"avghumidity"`
Condition struct {
Text string `json:"text"`
Icon string `json:"icon"`
Code int `json:"code"`
} `json:"condition"`
Uv float64 `json:"uv"`
} `json:"day"`
Astro struct {
Sunrise string `json:"sunrise"`
Sunset string `json:"sunset"`
Moonrise string `json:"moonrise"`
Moonset string `json:"moonset"`
} `json:"astro"`
Hour []struct {
TimeEpoch int `json:"time_epoch"`
Time string `json:"time"`
TempC float64 `json:"temp_c"`
TempF float64 `json:"temp_f"`
IsDay int `json:"is_day"`
Condition struct {
Text string `json:"text"`
Icon string `json:"icon"`
Code int `json:"code"`
} `json:"condition"`
WindMph float64 `json:"wind_mph"`
WindKph float64 `json:"wind_kph"`
WindDegree int `json:"wind_degree"`
WindDir string `json:"wind_dir"`
PressureMb float64 `json:"pressure_mb"`
PressureIn float64 `json:"pressure_in"`
PrecipMm float64 `json:"precip_mm"`
PrecipIn float64 `json:"precip_in"`
Humidity int `json:"humidity"`
Cloud int `json:"cloud"`
FeelslikeC float64 `json:"feelslike_c"`
FeelslikeF float64 `json:"feelslike_f"`
WindchillC float64 `json:"windchill_c"`
WindchillF float64 `json:"windchill_f"`
HeatindexC float64 `json:"heatindex_c"`
HeatindexF float64 `json:"heatindex_f"`
DewpointC float64 `json:"dewpoint_c"`
DewpointF float64 `json:"dewpoint_f"`
WillItRain int `json:"will_it_rain"`
ChanceOfRain string `json:"chance_of_rain"`
WillItSnow int `json:"will_it_snow"`
ChanceOfSnow string `json:"chance_of_snow"`
VisKm float64 `json:"vis_km"`
VisMiles float64 `json:"vis_miles"`
} `json:"hour"`
} `json:"forecastday"`
}
// CurrentWeather describes data returned by current.
type CurrentWeather struct {
Location Location `json:"location"`
Current Current `json:"current"`
}
// ForecastWeather describes data returned by forecast.
type ForecastWeather struct {
Location Location `json:"location"`
Current Current `json:"current"`
Forecast Forecast `json:"forecast"`
}
// HistoryWeather describes data returned by history.
type HistoryWeather struct {
Location Location `json:"location"`
Forecast Forecast `json:"forecast"`
}
// MatchingCities describes data returned by search.
type MatchingCities []struct {
ID int `json:"id"`
Name string `json:"name"`
Region string `json:"region"`
Country string `json:"country"`
Lat float64 `json:"lat"`
Lon float64 `json:"lon"`
URL string `json:"url"`
}
type errorResponse struct {
Error struct {
Code int `json:"code"`
Message string `json:"message"`
} `json:"error"`
}
// Client describes apixu api client.
type Client struct {
apiKey string
}
// Current returns CurrentWeather obj representing current weather status.
func (client *Client) Current(q string, optionalParams ...OptionalParam) (*CurrentWeather, error) {
url, err := client.getURL(currentPath, q, optionalParams...)
if err != nil {
return nil, err
}
body, err := request(url)
if err != nil {
return nil, err
}
var currentWeather CurrentWeather
if err := json.Unmarshal(body, ¤tWeather); err != nil {
return nil, err
}
return ¤tWeather, nil
}
// Forecast returns ForecastWeather obj representing forecast status.
func (client *Client) Forecast(q string, days int, optionalParams ...OptionalParam) (*ForecastWeather, error) {
optionalParams = append(optionalParams, OptionalParam{"days", string(days)})
url, err := client.getURL(forecastPath, q, optionalParams...)
if err != nil {
return nil, err
}
body, err := request(url)
if err != nil {
return nil, err
}
var forecastWeather ForecastWeather
if err := json.Unmarshal(body, &forecastWeather); err != nil {
return nil, err
}
return &forecastWeather, nil
}
// History returns HistoryWeather obj representing history status.
func (client *Client) History(q string, dt string, optionalParams ...OptionalParam) (*HistoryWeather, error) {
optionalParams = append(optionalParams, OptionalParam{"dt", dt})
url, err := client.getURL(historyPath, q, optionalParams...)
if err != nil {
return nil, err
}
body, err := request(url)
if err != nil {
return nil, err
}
var historyWeather HistoryWeather
if err := json.Unmarshal(body, &historyWeather); err != nil {
return nil, err
}
return &historyWeather, nil
}
// Search returns MatchingCities obj representing a list of matched cities.
func (client *Client) Search(q string) (*MatchingCities, error) {
url, err := client.getURL(searchPath, q)
if err != nil {
return nil, err
}
body, err := request(url)
if err != nil {
return nil, err
}
var matchingCities MatchingCities
if err := json.Unmarshal(body, &matchingCities); err != nil {
return nil, err
}
return &matchingCities, nil
}
func (client *Client) getURL(path string, q string, optionalParams ...OptionalParam) (string, error) {
baseURL, err := url.Parse(apiBaseURL)
if err != nil {
return "", err
}
pathURL, err := url.Parse(path)
if err != nil {
return "", err
}
URL := baseURL.ResolveReference(pathURL)
query := URL.Query()
query.Set("key", client.apiKey)
query.Set("q", q)
// Set optional params.
for _, param := range optionalParams {
query.Set(param.Name, param.Value)
}
URL.RawQuery = query.Encode()
return URL.String(), nil
}
// NewClient returns a new client reference.
func NewClient(apiKey string) *Client {
client := &Client{apiKey: apiKey}
return client
}
func request(url string) ([]byte, error) {
response, err := http.Get(url)
if err != nil {
return nil, err
}
defer response.Body.Close()
body, err := ioutil.ReadAll(response.Body)
// Validate response.
if response.StatusCode != 200 {
var errorJSON errorResponse
if err := json.Unmarshal(body, &errorJSON); err != nil {
return nil, err
}
return nil, errors.New(errorJSON.Error.Message)
}
return body, err
}