From 715d5e353323d08d1f90a96e56fdd87d7fcd2ae4 Mon Sep 17 00:00:00 2001 From: Andrew Scott Date: Fri, 2 Sep 2022 22:50:27 -0400 Subject: [PATCH] Refactor Current forecast and Five day --- server/current.go | 37 ------- server/extended.go | 83 ---------------- server/five_day.go | 57 +++++++++++ server/forecast.go | 120 +++++++++++++++++++++++ test-client/{extended.go => five_day.go} | 11 ++- test-client/{current.go => forecast.go} | 14 +-- 6 files changed, 191 insertions(+), 131 deletions(-) delete mode 100644 server/current.go delete mode 100644 server/extended.go create mode 100644 server/five_day.go create mode 100644 server/forecast.go rename test-client/{extended.go => five_day.go} (60%) rename test-client/{current.go => forecast.go} (58%) diff --git a/server/current.go b/server/current.go deleted file mode 100644 index 7c194eb..0000000 --- a/server/current.go +++ /dev/null @@ -1,37 +0,0 @@ -package main - -import ( - "context" - "fmt" - "io/ioutil" - "log" - "net/http" - - pb "codeberg.org/andcscott/OpenWeather-gRPC-API/proto" -) - -// Receives a gRPC request for the current forecast -// Returns a SendCurrent message containing the forecast in JSON -func (s *Server) Current(ctx context.Context, in *pb.RequestCurrent) (*pb.SendCurrent, error) { - log.Printf("'Current' called: %v\n", in) - - url := "https://api.openweathermap.org/data/2.5/weather?" - lat, lon, err := getLocation(in.Location.String(), s.ApiKey) - units := "&units=imperial" - token := "&appid=" + s.ApiKey - - url = url + fmt.Sprintf("lat=%f", lat) + fmt.Sprintf("&lon=%f", lon) + units + token - - res, err := http.Get(url) - if err != nil { - log.Printf("Error fetching weather: %v\n", err) - } - defer res.Body.Close() - - body, err := ioutil.ReadAll(res.Body) - if err != nil { - log.Printf("Error reading weather response: %v", err) - } - - return &pb.SendCurrent{Payload: string(body)}, nil -} diff --git a/server/extended.go b/server/extended.go deleted file mode 100644 index ce77156..0000000 --- a/server/extended.go +++ /dev/null @@ -1,83 +0,0 @@ -package main - -import ( - "context" - "fmt" - "io/ioutil" - "log" - "net/http" - - pb "codeberg.org/andcscott/OpenWeather-gRPC-API/proto" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" -) - -// Receives a gRPC request for an extended forecast -// Returns a SendFiveDay message with the forecast as JSON -func (s *Server) FiveDay(ctx context.Context, in *pb.RequestFiveDay) (*pb.SendFiveDay, error) { - log.Printf("'FiveDay' called: %v\n", in) - - url, err := s.createFiveDayUrl(in) - if err != nil { - return nil, status.Errorf( - codes.InvalidArgument, - fmt.Sprintf("Invalid location or location type: %s, %s\n", - in.Location.String(), - in.LocationType.String()), - ) - } - - token := "&appid=" + s.ApiKey - res, err := http.Get(url + token) - if err != nil { - log.Printf("Error fetching extended weather: %v\n", err) - } - defer res.Body.Close() - - body, err := ioutil.ReadAll(res.Body) - if err != nil { - log.Printf("Error reading extending weather: %v\n", err) - } - - return &pb.SendFiveDay{ - Payload: string(body), - }, nil -} - -// Assembles the OpenWeather API URL for FiveDay -func (s *Server) createFiveDayUrl(in *pb.RequestFiveDay) (string, error) { - - var lat, lon float32 - var err error - url := "https://api.openweathermap.org/data/2.5/forecast?" - units := "&units=" - - switch in.Units { - case pb.Units_UNITS_IMPERIAL: - units += "imperial" - case pb.Units_UNITS_METRIC: - units += "metric" - default: - units += "standard" - } - - switch in.LocationType { - case pb.LocationType_LOCATION_TYPE_CITY: - lat, lon, err = getLocation(in.Location.GetCity(), s.ApiKey) - case pb.LocationType_LOCATION_TYPE_ZIP_CODE: - lat, lon, err = getZipLocation(in.Location.GetZipCode(), s.ApiKey) - case pb.LocationType_LOCATION_TYPE_COORDS: - lat = in.Location.GetCoords().Latitude - lon = in.Location.GetCoords().Longitude - default: - fmt.Println(in.Location.String()) - lat, lon, err = getLocation(in.Location.String(), s.ApiKey) - } - if err != nil { - return "", err - } - - url = url + fmt.Sprintf("lat=%f", lat) + fmt.Sprintf("&lon=%f", lon) + units - return url, nil - -} diff --git a/server/five_day.go b/server/five_day.go new file mode 100644 index 0000000..e6d9921 --- /dev/null +++ b/server/five_day.go @@ -0,0 +1,57 @@ +package main + +import ( + "context" + "fmt" + "log" + + pb "codeberg.org/andcscott/OpenWeather-gRPC-API/proto" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +// Receives a gRPC request for an extended forecast +// Returns a SendFiveDay message with the forecast as JSON +func (s *Server) FiveDay(ctx context.Context, in *pb.RequestFiveDay) (*pb.SendFiveDay, error) { + log.Printf("'FiveDay' called: %v\n", in) + + token := "&appid=" + s.ApiKey + url, err := s.createFiveDayUrl(in) + if err != nil { + return nil, status.Errorf( + codes.InvalidArgument, + fmt.Sprintf( + "Invalid location or location type: %v, %v\n", + in.Location, + in.LocationType, + )) + } + + fcst, err := fetchForecast(url + token) + if err != nil { + return nil, status.Errorf( + codes.Internal, + fmt.Sprintf( + "A server error occurred while fetching the forecast: %v\n", + err, + )) + } + + return &pb.SendFiveDay{ + Payload: fcst, + }, nil +} + +// Assembles the URL for five day requests to Open Weather +func (s *Server) createFiveDayUrl(in *pb.RequestFiveDay) (string, error) { + + url := "https://api.openweathermap.org/data/2.5/forecast?" + units := updateUnits(in.Units) + lat, lon, err := s.fetchLocation(in.LocationType, in.Location) + if err != nil { + return "", err + } + + url += fmt.Sprintf("lat=%f", lat) + fmt.Sprintf("&lon=%f", lon) + units + return url, nil +} diff --git a/server/forecast.go b/server/forecast.go new file mode 100644 index 0000000..1164b97 --- /dev/null +++ b/server/forecast.go @@ -0,0 +1,120 @@ +package main + +import ( + "context" + "fmt" + "io/ioutil" + "log" + "net/http" + "strconv" + + pb "codeberg.org/andcscott/OpenWeather-gRPC-API/proto" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +// Receives a gRPC request for the current forecast +// Returns a SendCurrent message containing the forecast in JSON +func (s *Server) Current(ctx context.Context, in *pb.RequestCurrent) (*pb.SendCurrent, error) { + log.Printf("'Current' called: %v\n", in) + + token := "&appid=" + s.ApiKey + url, err := s.createCurrentUrl(in) + if err != nil { + return nil, status.Errorf( + codes.InvalidArgument, + fmt.Sprintf("Invalid location or location type: %s, %s\n", + in.Location.String(), + in.LocationType.String()), + ) + } + + fcst, err := fetchForecast(url + token) + if err != nil { + return nil, status.Errorf( + codes.Internal, + fmt.Sprintf( + "A server error occurred while fetching the forecast: %v\n", + err, + )) + } + + return &pb.SendCurrent{ + Payload: fcst, + }, nil +} + +// Assembles the URL for current weather requests to OpenWeather +func (s *Server) createCurrentUrl(in *pb.RequestCurrent) (string, error) { + url := "https://api.openweathermap.org/data/2.5/weather?" + units := updateUnits(in.Units) + lat, lon, err := s.fetchLocation(in.LocationType, in.Location) + if err != nil { + return "", err + } + url += fmt.Sprintf("lat=%f", lat) + fmt.Sprintf("&lon=%f", lon) + units + return url, nil +} + +// Creates the units portion of the request to OpenWeater +func updateUnits(units pb.Units) string { + unitStr := "&units=" + switch units { + case pb.Units_UNITS_IMPERIAL: + unitStr += "imperial" + case pb.Units_UNITS_METRIC: + unitStr += "metric" + default: + unitStr += "standard" + } + return unitStr +} + +// Obtains a locations exact coordinates for the request to OpenWeather +func (s *Server) fetchLocation(locType pb.LocationType, loc *pb.OneOfLocation) (float32, float32, error) { + + var lat, lon float32 + var err error + + switch locType { + case pb.LocationType_LOCATION_TYPE_CITY: + lat, lon, err = fetchCityCoords(loc.GetCity(), s.ApiKey) + case pb.LocationType_LOCATION_TYPE_ZIP_CODE: + lat, lon, err = fetchZipCoords(loc.GetZipCode(), s.ApiKey) + case pb.LocationType_LOCATION_TYPE_COORDS: + lat = loc.GetCoords().Latitude + lon = loc.GetCoords().Longitude + default: + _, err = strconv.Atoi(loc.GetZipCode()) + if err != nil { + lat, lon, err = fetchCityCoords(loc.GetCity(), s.ApiKey) + } else { + lat, lon, err = fetchZipCoords(loc.GetZipCode(), s.ApiKey) + } + } + if err != nil { + return 0, 0, err + } + return lat, lon, nil +} + +// Obtains forecasts from OpenWeather +// Receives the URL as a string +// Returns the JSON response from OpenWeather +func fetchForecast(url string) (string, error) { + + res, err := http.Get(url) + if err != nil { + log.Printf("Error fetching extended weather: %v\n", err) + return "", err + } + defer res.Body.Close() + + body, err := ioutil.ReadAll(res.Body) + if err != nil { + log.Printf("Error reading extending weather: %v\n", err) + return "", err + } + + return string(body), nil +} diff --git a/test-client/extended.go b/test-client/five_day.go similarity index 60% rename from test-client/extended.go rename to test-client/five_day.go index 812b142..061d308 100644 --- a/test-client/extended.go +++ b/test-client/five_day.go @@ -10,11 +10,14 @@ import ( func doFiveDay(c pb.WeatherServiceClient) { res, err := c.FiveDay(context.Background(), &pb.RequestFiveDay{ - LocationType: pb.LocationType_LOCATION_TYPE_UNSPECIFIED, - Units: pb.Units_UNITS_METRIC, + LocationType: pb.LocationType_LOCATION_TYPE_COORDS, + Units: pb.Units_UNITS_IMPERIAL, Location: &pb.OneOfLocation{ - LocationId: &pb.OneOfLocation_ZipCode{ - ZipCode: "97330", + LocationId: &pb.OneOfLocation_Coords{ + Coords: &pb.Coordinates{ + Latitude: 41, + Longitude: -123, + }, }, }, }) diff --git a/test-client/current.go b/test-client/forecast.go similarity index 58% rename from test-client/current.go rename to test-client/forecast.go index 8884d98..fbf204c 100644 --- a/test-client/current.go +++ b/test-client/forecast.go @@ -10,14 +10,14 @@ import ( func doCurrent(c pb.WeatherServiceClient) { res, err := c.Current(context.Background(), &pb.RequestCurrent{ - LocationType: pb.LocationType_LOCATION_TYPE_CITY, - Units: pb.Units_UNITS_METRIC, + LocationType: pb.LocationType_LOCATION_TYPE_COORDS, + Units: pb.Units_UNITS_UNSPECIFIED, Location: &pb.OneOfLocation{ - LocationId: &pb.OneOfLocation_City{ - City: "Corvallis", - }, - }, - }) + LocationId: &pb.OneOfLocation_Coords{ + Coords: &pb.Coordinates{ + Latitude: 41, + Longitude: -123, + }}}}) if err != nil { log.Fatalln(err) }