package handler import ( "context" "encoding/json" "fmt" "io/ioutil" "net/http" "net/url" "strconv" "github.com/micro/micro/v3/service/config" "github.com/micro/micro/v3/service/errors" "github.com/micro/micro/v3/service/logger" pb "github.com/micro/services/weather/proto" ) type Weather struct { Api string Key string } func New() *Weather { // TODO: look for "weather.provider" to determine the handler v, err := config.Get("weatherapi.api") if err != nil { logger.Fatalf("weatherapi.api config not found: %v", err) } api := v.String("") if len(api) == 0 { logger.Fatal("weatherapi.api config not found") } v, err = config.Get("weatherapi.key") if err != nil { logger.Fatalf("weatherapi.key config not found: %v", err) } key := v.String("") if len(key) == 0 { logger.Fatal("weatherapi.key config not found") } return &Weather{ Api: api, Key: key, } } func (w *Weather) Forecast(ctx context.Context, req *pb.ForecastRequest, rsp *pb.ForecastResponse) error { if len(req.Location) <= 0 { return errors.BadRequest("weather.forecast", "invalid location") } if req.Days <= 0 || req.Days > 10 { req.Days = 1 } vals := url.Values{} vals.Set("key", w.Key) vals.Set("aqi", "no") vals.Set("q", req.Location) vals.Set("days", fmt.Sprintf("%d", req.Days)) resp, err := http.Get(w.Api + "forecast.json?" + vals.Encode()) if err != nil { logger.Errorf("Failed to get weather forecast: %v\n", err) return errors.InternalServerError("weather.forecast", "failed to get weather forecast") } defer resp.Body.Close() b, _ := ioutil.ReadAll(resp.Body) if resp.StatusCode != 200 { logger.Errorf("Failed to get weather forecast (non 200): %d %v\n", resp.StatusCode, string(b)) return errors.InternalServerError("weather.forecast", "failed to get weather forecast") } var respBody map[string]interface{} if err := json.Unmarshal(b, &respBody); err != nil { logger.Errorf("Failed to unmarshal forecast: %v\n", err) return errors.InternalServerError("weather.forecast", "failed to get forecast") } forecast := respBody["forecast"].(map[string]interface{}) for _, v := range forecast["forecastday"].([]interface{}) { fc := v.(map[string]interface{}) day := fc["day"].(map[string]interface{}) willrain := false chancerain := int32(0) if v := day["daily_will_it_rain"].(float64); v == 1.0 { willrain = true } // is this a string or a float64? Try both if dcr, ok := day["daily_chance_of_rain"].(string); ok { if v, _ := strconv.Atoi(dcr); v > 0 { chancerain = int32(v) } } else if dcr, ok := day["daily_chance_of_rain"].(float64); ok { chancerain = int32(dcr) } // set the daily forecast rsp.Forecast = append(rsp.Forecast, &pb.Forecast{ Date: fc["date"].(string), MaxTempC: day["maxtemp_c"].(float64), MinTempC: day["mintemp_c"].(float64), AvgTempC: day["avgtemp_c"].(float64), MaxTempF: day["maxtemp_f"].(float64), MinTempF: day["mintemp_f"].(float64), AvgTempF: day["avgtemp_f"].(float64), WillItRain: willrain, ChanceOfRain: chancerain, Condition: day["condition"].(map[string]interface{})["text"].(string), IconUrl: day["condition"].(map[string]interface{})["icon"].(string), Sunrise: fc["astro"].(map[string]interface{})["sunrise"].(string), Sunset: fc["astro"].(map[string]interface{})["sunset"].(string), }) } return nil } func (w *Weather) Now(ctx context.Context, req *pb.NowRequest, rsp *pb.NowResponse) error { if len(req.Location) <= 0 { return errors.BadRequest("weather.current", "invalid location") } vals := url.Values{} vals.Set("key", w.Key) vals.Set("aqi", "no") vals.Set("q", req.Location) resp, err := http.Get(w.Api + "current.json?" + vals.Encode()) if err != nil { logger.Errorf("Failed to get current weather: %v\n", err) return errors.InternalServerError("weather.current", "failed to get current weather") } defer resp.Body.Close() b, _ := ioutil.ReadAll(resp.Body) if resp.StatusCode != 200 { logger.Errorf("Failed to get current weather (non 200): %d %v\n", resp.StatusCode, string(b)) return errors.InternalServerError("weather.current", "failed to get current weather") } var respBody map[string]interface{} if err := json.Unmarshal(b, &respBody); err != nil { logger.Errorf("Failed to unmarshal current: %v\n", err) return errors.InternalServerError("weather.current", "failed to get current") } location := respBody["location"].(map[string]interface{}) current := respBody["current"].(map[string]interface{}) // set the location rsp.Location = location["name"].(string) rsp.Region = location["region"].(string) rsp.Country = location["region"].(string) rsp.Latitude = location["lat"].(float64) rsp.Longitude = location["lon"].(float64) rsp.Timezone = location["tz_id"].(string) rsp.LocalTime = location["localtime"].(string) // set the time of day if current["is_day"].(float64) == 1.0 { rsp.Daytime = true } rsp.TempC = current["temp_c"].(float64) rsp.TempF = current["temp_f"].(float64) rsp.FeelsLikeC = current["feelslike_c"].(float64) rsp.FeelsLikeF = current["feelslike_f"].(float64) rsp.Humidity = int32(current["humidity"].(float64)) rsp.Cloud = int32(current["cloud"].(float64)) rsp.Condition = current["condition"].(map[string]interface{})["text"].(string) rsp.IconUrl = current["condition"].(map[string]interface{})["icon"].(string) rsp.WindMph = current["wind_mph"].(float64) rsp.WindKph = current["wind_kph"].(float64) rsp.WindDirection = current["wind_dir"].(string) rsp.WindDegree = int32(current["wind_degree"].(float64)) return nil }