@@ -3,10 +3,12 @@ package v2
3
3
import (
4
4
"encoding/json"
5
5
"fmt"
6
+ "log"
6
7
"net/http"
7
8
"time"
8
9
9
10
"github.com/gorilla/mux"
11
+ "github.com/robertoduessmann/weather-api/cache"
10
12
"github.com/robertoduessmann/weather-api/model"
11
13
)
12
14
@@ -15,60 +17,70 @@ const (
15
17
wttrURL = "https://wttr.in"
16
18
)
17
19
18
- // CurrentWeather gets the current weather to show in JSON format
19
- //
20
- // This endpoint uses wttr API with JSON response under the hood to make it
21
- // easier to handle with units and formats
20
+ func writeError (w http.ResponseWriter , status int , message string ) {
21
+ w .Header ().Set ("Content-Type" , "application/json" )
22
+ w .WriteHeader (status )
23
+ json .NewEncoder (w ).Encode (model.ErrorMessage {Message : message })
24
+ }
25
+
22
26
func CurrentWeather (w http.ResponseWriter , r * http.Request ) {
23
27
city := mux .Vars (r )["city" ]
28
+ unit := mux .Vars (r )["unit" ]
29
+ cacheKey := fmt .Sprintf ("%s-%s" , city , unit )
30
+
31
+ cacheManager := cache .NewCacheManager ()
32
+ weatherCache := cacheManager .NewCache ("weather" , 10 * time .Minute )
33
+
34
+ if cached , found := weatherCache .Get (cacheKey ); found {
35
+ log .Printf ("[CACHE HIT] key=%s" , cacheKey )
36
+ w .Header ().Set ("Content-Type" , "application/json" )
37
+ w .Write (cached .([]byte ))
38
+ return
39
+ }
40
+
41
+ log .Printf ("[CACHE MISS] key=%s" , cacheKey )
24
42
uri := fmt .Sprintf ("%s/%s?format=j1" , wttrURL , city )
25
43
res , err := http .Get (uri )
26
-
27
44
if err != nil {
28
- errJSON , _ := json .Marshal (model.ErrorMessage {Message : err .Error ()})
29
- w .WriteHeader (http .StatusInternalServerError )
30
- w .Header ().Set ("Content-Type" , "application/json" )
31
- w .Write (errJSON )
45
+ writeError (w , http .StatusInternalServerError , err .Error ())
32
46
return
33
47
}
48
+ defer res .Body .Close ()
34
49
35
50
if res .StatusCode != http .StatusOK {
36
- errJSON , _ := json .Marshal (model.ErrorMessage {Message : "NOT_FOUND" })
37
- w .WriteHeader (http .StatusNotFound )
38
- w .Header ().Set ("Content-Type" , "application/json" )
39
- w .Write (errJSON )
51
+ writeError (w , http .StatusNotFound , "NOT_FOUND" )
40
52
return
41
53
}
42
54
43
- defer res .Body .Close ()
44
-
45
55
var wttr wttrResponse
46
56
if err := json .NewDecoder (res .Body ).Decode (& wttr ); err != nil {
47
- errJSON , _ := json .Marshal (model.ErrorMessage {Message : err .Error ()})
48
- w .WriteHeader (http .StatusInternalServerError )
49
- w .Header ().Set ("Content-Type" , "application/json" )
50
- w .Write (errJSON )
57
+ writeError (w , http .StatusInternalServerError , err .Error ())
51
58
return
52
59
}
53
60
54
- var (
55
- cc = wttr . CurrentCondition [ 0 ]
56
- unit = mux . Vars ( r )[ "unit" ]
57
- )
61
+ if len ( wttr . CurrentCondition ) == 0 || len ( wttr . CurrentCondition [ 0 ]. WeatherDesc ) == 0 {
62
+ writeError ( w , http . StatusInternalServerError , "Unexpected API response" )
63
+ return
64
+ }
58
65
66
+ cc := wttr .CurrentCondition [0 ]
59
67
response := model.Weather {
60
68
Description : cc .WeatherDesc [0 ].Value ,
61
69
Temperature : cc .Temp (unit ),
62
70
Wind : cc .Windspeed (unit ),
71
+ Forecast : [3 ]model.Forecast {},
63
72
}
64
73
65
- for i , weather := range wttr .Weather {
74
+ for i := 0 ; i < len (wttr .Weather ) && i < 3 ; i ++ {
75
+ weather := wttr .Weather [i ]
76
+ if len (weather .Hourly ) == 0 {
77
+ writeError (w , http .StatusInternalServerError , "Incomplete forecast data" )
78
+ return
79
+ }
80
+
66
81
day , err := time .Parse (timeFormat , weather .Date )
67
82
if err != nil {
68
- errJSON , _ := json .Marshal (model.ErrorMessage {Message : err .Error ()})
69
- w .WriteHeader (http .StatusInternalServerError )
70
- w .Header ().Set ("Content-Type" , "application/json" )
71
- w .Write (errJSON )
83
+ writeError (w , http .StatusInternalServerError , err .Error ())
72
84
return
73
85
}
74
86
@@ -79,11 +91,14 @@ func CurrentWeather(w http.ResponseWriter, r *http.Request) {
79
91
}
80
92
}
81
93
82
- if err := json .NewEncoder (w ).Encode (response ); err != nil {
83
- errJSON , _ := json .Marshal (model.ErrorMessage {Message : err .Error ()})
84
- w .WriteHeader (http .StatusInternalServerError )
85
- w .Header ().Set ("Content-Type" , "application/json" )
86
- w .Write (errJSON )
94
+ encoded , err := json .Marshal (response )
95
+ if err != nil {
96
+ writeError (w , http .StatusInternalServerError , err .Error ())
87
97
return
88
98
}
99
+
100
+ weatherCache .Put (cacheKey , encoded )
101
+
102
+ w .Header ().Set ("Content-Type" , "application/json" )
103
+ w .Write (encoded )
89
104
}
0 commit comments