From 85aec3c8d7f47be5f49cb6b6ab0dbd1d973773c2 Mon Sep 17 00:00:00 2001 From: Yendelevium Date: Sun, 20 Oct 2024 12:07:27 +0530 Subject: [PATCH 1/3] Implement SetQueryParams in the Provider Interface - Changed the function signature of the FetchData method in the Provides interface - Added the SetQueryParams method to the providers aligned to the concerns raised in #13 Resolves #13 References #5 --- providers/meteoblue.go | 29 +++++++++++++++++++++++++++-- providers/open_meteo.go | 11 ++++++----- providers/providers.go | 3 ++- server/server.go | 17 +++-------------- 4 files changed, 38 insertions(+), 22 deletions(-) diff --git a/providers/meteoblue.go b/providers/meteoblue.go index d89f043..5533b38 100644 --- a/providers/meteoblue.go +++ b/providers/meteoblue.go @@ -4,8 +4,11 @@ import ( "encoding/json" "errors" "fmt" + "log/slog" + "os" "github.com/tinkershack/meteomunch/config" + e "github.com/tinkershack/meteomunch/errors" "github.com/tinkershack/meteomunch/http/rest" "github.com/tinkershack/meteomunch/logger" "github.com/tinkershack/meteomunch/plumber" @@ -51,9 +54,13 @@ func NewMeteoBlueProvider() (*MeteoBlueProvider, error) { }, nil } -func (p *MeteoBlueProvider) FetchData(qp map[string]string) (*plumber.BaseData, error) { +func (p *MeteoBlueProvider) FetchData(coords *plumber.Coordinates) (*plumber.BaseData, error) { // Creating a new request everytime we fecth data p.client.NewRequest() + // Getting the query parameters + qp := p.SetQueryParams(coords) + + // Setting the paramters on the request and making the request resp, err := p.client.SetQueryParams(qp).Get(p.config.APIPath) if err != nil { return nil, err @@ -86,6 +93,7 @@ func (p *MeteoBlueProvider) FetchData(qp map[string]string) (*plumber.BaseData, return nil, fmt.Errorf("failed to unmarshal response: %w", err) } + // TO-DO: #12 // Map the JSON response to plumber.BaseData baseData := &plumber.BaseData{ Latitude: data.Latitude, @@ -96,4 +104,21 @@ func (p *MeteoBlueProvider) FetchData(qp map[string]string) (*plumber.BaseData, return baseData, nil } -// Maybe add a MeteoBlueQueryParams like OpenMeteoQueryParams to have some standard for all providers? +// Set the QueryParams for the request +func (p *MeteoBlueProvider) SetQueryParams(coords *plumber.Coordinates) map[string]string { + // Get the config to accesss the API Key + cfg, err := config.Get() + if err != nil { + slog.Error("Couldn't parse config", "error", err, "description", e.FAIL) + os.Exit(-1) + } + return map[string]string{ + "lat": fmt.Sprintf("%f", coords.Latitude), + "lon": fmt.Sprintf("%f", coords.Longitude), + "tz": "GMT", + "format": "json", + "forecast_days": "1", + "apikey": cfg.MeteoProviders[0].APIKey, + } + +} diff --git a/providers/open_meteo.go b/providers/open_meteo.go index 0c1bb71..fd045a1 100644 --- a/providers/open_meteo.go +++ b/providers/open_meteo.go @@ -53,8 +53,10 @@ func NewOpenMeteoProvider() (*OpenMeteoProvider, error) { } // FetchData fetches API data from open-meteo provider for the given query parameters map -func (p *OpenMeteoProvider) FetchData(qp map[string]string) (*plumber.BaseData, error) { +func (p *OpenMeteoProvider) FetchData(coords *plumber.Coordinates) (*plumber.BaseData, error) { p.client.NewRequest() + qp := p.SetQueryParams(coords) + resp, err := p.client.SetQueryParams(qp).Get(p.config.APIPath) if err != nil { return nil, err @@ -86,9 +88,9 @@ func (p *OpenMeteoProvider) FetchData(qp map[string]string) (*plumber.BaseData, return data, nil } -// OpenMeteoQueryParams forms the query parameters for OpenMeteo API based on given coordinates -func OpenMeteoQueryParams(coords *plumber.Coordinates) map[string]string { - qp := map[string]string{ +// SetQueryParams forms the query parameters for OpenMeteo API based on given coordinates +func (p *OpenMeteoProvider) SetQueryParams(coords *plumber.Coordinates) map[string]string { + return map[string]string{ "latitude": fmt.Sprintf("%f", coords.Latitude), "longitude": fmt.Sprintf("%f", coords.Longitude), "current": "temperature_2m,relative_humidity_2m,apparent_temperature,is_day,precipitation,rain,showers,snowfall,weather_code,cloud_cover,pressure_msl,surface_pressure,wind_speed_10m,wind_direction_10m,wind_gusts_10m", @@ -101,5 +103,4 @@ func OpenMeteoQueryParams(coords *plumber.Coordinates) map[string]string { "cell_selection": "nearest", "models": "best_match", } - return qp } diff --git a/providers/providers.go b/providers/providers.go index cb70ce2..9adfde8 100644 --- a/providers/providers.go +++ b/providers/providers.go @@ -40,7 +40,8 @@ func init() { // Provider interface defines the methods that each provider must implement type Provider interface { - FetchData(queryParams map[string]string) (*plumber.BaseData, error) + FetchData(coords *plumber.Coordinates) (*plumber.BaseData, error) + SetQueryParams(coords *plumber.Coordinates) map[string]string } // NewProvider returns the appropriate provider based on the name diff --git a/server/server.go b/server/server.go index e80be94..e03e4ac 100644 --- a/server/server.go +++ b/server/server.go @@ -43,7 +43,7 @@ func Serve(ctx context.Context, args []string) { w.WriteHeader(http.StatusInternalServerError) return } - bd, err := p.FetchData(providers.OpenMeteoQueryParams(plumber.NewCoordinates(11.0056, 76.9661))) + bd, err := p.FetchData(plumber.NewCoordinates(10.9018379, 76.8998445)) if err != nil { logger.Error(e.FAIL, "err", err, "description", "Couldn't fetch data from OpenMeteoProvider") w.WriteHeader(http.StatusInternalServerError) @@ -66,19 +66,8 @@ func Serve(ctx context.Context, args []string) { w.WriteHeader(http.StatusInternalServerError) return } - qp := map[string]string{ - "lat": "47.558", - "lon": "7.587", - "asl": "279", - "tz": "Europe/Zurich", - "name": "Test", - "windspeed": "kmh", - "format": "json", - "history_days": "1", - "forecast_days": "0", - "apikey": cfg.MeteoProviders[0].APIKey, - } - bd, err := p.FetchData(qp) + // TO-DO : After defining the MB response, write it to the response + bd, err := p.FetchData(plumber.NewCoordinates(10.9018379, 76.8998445)) if err != nil { logger.Error(e.FAIL, "err", err, "description", "Couldn't fetch data from MeteoBlueProvider") w.WriteHeader(http.StatusInternalServerError) From 300545f8edd6800129f48222bbd11ed562bb3f8d Mon Sep 17 00:00:00 2001 From: Yendelevium Date: Mon, 21 Oct 2024 12:08:19 +0530 Subject: [PATCH 2/3] Reduce redundant passing of queryParamters - Add queryParams field on the providers - Update SetQueryParams method to set the queryParams field on the provider - Update NewOpenMeteoProvider() and NewMeteoBlueProvider() to set default QPs on the client - Update FetchData() to only set the coords, and not the entire QPs --- providers/meteoblue.go | 45 ++++++++++++++++++++--------------------- providers/open_meteo.go | 28 ++++++++++++++++--------- providers/providers.go | 2 +- 3 files changed, 42 insertions(+), 33 deletions(-) diff --git a/providers/meteoblue.go b/providers/meteoblue.go index 5533b38..9683552 100644 --- a/providers/meteoblue.go +++ b/providers/meteoblue.go @@ -4,11 +4,8 @@ import ( "encoding/json" "errors" "fmt" - "log/slog" - "os" "github.com/tinkershack/meteomunch/config" - e "github.com/tinkershack/meteomunch/errors" "github.com/tinkershack/meteomunch/http/rest" "github.com/tinkershack/meteomunch/logger" "github.com/tinkershack/meteomunch/plumber" @@ -17,8 +14,9 @@ import ( const meteoBlueProviderName = "meteoblue" type MeteoBlueProvider struct { - client rest.HTTPClient - config config.MeteoProvider + client rest.HTTPClient + config config.MeteoProvider + queryParams map[string]string } func NewMeteoBlueProvider() (*MeteoBlueProvider, error) { @@ -48,20 +46,26 @@ func NewMeteoBlueProvider() (*MeteoBlueProvider, error) { client.EnableTrace() } - return &MeteoBlueProvider{ + provider := MeteoBlueProvider{ client: client, config: meteoConfig, - }, nil + } + // Setting the default location to 0,0 + provider.SetQueryParams(plumber.NewCoordinates(0, 0)) + + // Creating the new request(which will be reused in all FetchData calls) and setting the default queryParams on the client + provider.client.NewRequest() + provider.client.SetQueryParams(provider.queryParams) + return &provider, nil } func (p *MeteoBlueProvider) FetchData(coords *plumber.Coordinates) (*plumber.BaseData, error) { - // Creating a new request everytime we fecth data - p.client.NewRequest() - // Getting the query parameters - qp := p.SetQueryParams(coords) - - // Setting the paramters on the request and making the request - resp, err := p.client.SetQueryParams(qp).Get(p.config.APIPath) + resp, err := p.client. + SetQueryParams(map[string]string{ + "latitude": fmt.Sprintf("%f", coords.Latitude), + "longitude": fmt.Sprintf("%f", coords.Longitude), + }). + Get(p.config.APIPath) if err != nil { return nil, err } @@ -105,20 +109,15 @@ func (p *MeteoBlueProvider) FetchData(coords *plumber.Coordinates) (*plumber.Bas } // Set the QueryParams for the request -func (p *MeteoBlueProvider) SetQueryParams(coords *plumber.Coordinates) map[string]string { - // Get the config to accesss the API Key - cfg, err := config.Get() - if err != nil { - slog.Error("Couldn't parse config", "error", err, "description", e.FAIL) - os.Exit(-1) - } - return map[string]string{ +func (p *MeteoBlueProvider) SetQueryParams(coords *plumber.Coordinates) { + // Setting the queryParams on the provider + p.queryParams = map[string]string{ "lat": fmt.Sprintf("%f", coords.Latitude), "lon": fmt.Sprintf("%f", coords.Longitude), "tz": "GMT", "format": "json", "forecast_days": "1", - "apikey": cfg.MeteoProviders[0].APIKey, + "apikey": p.config.APIKey, } } diff --git a/providers/open_meteo.go b/providers/open_meteo.go index fd045a1..babd4fc 100644 --- a/providers/open_meteo.go +++ b/providers/open_meteo.go @@ -14,8 +14,9 @@ import ( const openMeteoProviderName = "open-meteo" type OpenMeteoProvider struct { - client rest.HTTPClient - config config.MeteoProvider + client rest.HTTPClient + config config.MeteoProvider + queryParams map[string]string } // NewOpenMeteoProvider returns a new instance of OpenMeteoProvider @@ -46,18 +47,27 @@ func NewOpenMeteoProvider() (*OpenMeteoProvider, error) { client.EnableTrace() } - return &OpenMeteoProvider{ + provider := OpenMeteoProvider{ client: client, config: meteoConfig, - }, nil + } + // Setting the default location to 0,0 + provider.SetQueryParams(plumber.NewCoordinates(0, 0)) + // Creating the new request(which will be reused in all FetchData calls) and setting the default queryParams on the client + provider.client.NewRequest() + provider.client.SetQueryParams(provider.queryParams) + return &provider, nil } // FetchData fetches API data from open-meteo provider for the given query parameters map func (p *OpenMeteoProvider) FetchData(coords *plumber.Coordinates) (*plumber.BaseData, error) { - p.client.NewRequest() - qp := p.SetQueryParams(coords) + resp, err := p.client. + SetQueryParams(map[string]string{ + "latitude": fmt.Sprintf("%f", coords.Latitude), + "longitude": fmt.Sprintf("%f", coords.Longitude), + }). + Get(p.config.APIPath) - resp, err := p.client.SetQueryParams(qp).Get(p.config.APIPath) if err != nil { return nil, err } @@ -89,8 +99,8 @@ func (p *OpenMeteoProvider) FetchData(coords *plumber.Coordinates) (*plumber.Bas } // SetQueryParams forms the query parameters for OpenMeteo API based on given coordinates -func (p *OpenMeteoProvider) SetQueryParams(coords *plumber.Coordinates) map[string]string { - return map[string]string{ +func (p *OpenMeteoProvider) SetQueryParams(coords *plumber.Coordinates) { + p.queryParams = map[string]string{ "latitude": fmt.Sprintf("%f", coords.Latitude), "longitude": fmt.Sprintf("%f", coords.Longitude), "current": "temperature_2m,relative_humidity_2m,apparent_temperature,is_day,precipitation,rain,showers,snowfall,weather_code,cloud_cover,pressure_msl,surface_pressure,wind_speed_10m,wind_direction_10m,wind_gusts_10m", diff --git a/providers/providers.go b/providers/providers.go index 9adfde8..e6775ce 100644 --- a/providers/providers.go +++ b/providers/providers.go @@ -41,7 +41,7 @@ func init() { // Provider interface defines the methods that each provider must implement type Provider interface { FetchData(coords *plumber.Coordinates) (*plumber.BaseData, error) - SetQueryParams(coords *plumber.Coordinates) map[string]string + SetQueryParams(coords *plumber.Coordinates) } // NewProvider returns the appropriate provider based on the name From b912216171de48d39b828c7c0114b6ae9424f206 Mon Sep 17 00:00:00 2001 From: Yendelevium Date: Tue, 22 Oct 2024 19:18:22 +0530 Subject: [PATCH 3/3] Address the latitude-longitude/lat-lon discrepancy in the queryParans of MB --- providers/meteoblue.go | 6 ++---- providers/open_meteo.go | 2 -- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/providers/meteoblue.go b/providers/meteoblue.go index 9683552..6bd97ca 100644 --- a/providers/meteoblue.go +++ b/providers/meteoblue.go @@ -62,8 +62,8 @@ func NewMeteoBlueProvider() (*MeteoBlueProvider, error) { func (p *MeteoBlueProvider) FetchData(coords *plumber.Coordinates) (*plumber.BaseData, error) { resp, err := p.client. SetQueryParams(map[string]string{ - "latitude": fmt.Sprintf("%f", coords.Latitude), - "longitude": fmt.Sprintf("%f", coords.Longitude), + "lat": fmt.Sprintf("%f", coords.Latitude), + "lon": fmt.Sprintf("%f", coords.Longitude), }). Get(p.config.APIPath) if err != nil { @@ -112,8 +112,6 @@ func (p *MeteoBlueProvider) FetchData(coords *plumber.Coordinates) (*plumber.Bas func (p *MeteoBlueProvider) SetQueryParams(coords *plumber.Coordinates) { // Setting the queryParams on the provider p.queryParams = map[string]string{ - "lat": fmt.Sprintf("%f", coords.Latitude), - "lon": fmt.Sprintf("%f", coords.Longitude), "tz": "GMT", "format": "json", "forecast_days": "1", diff --git a/providers/open_meteo.go b/providers/open_meteo.go index babd4fc..99acb45 100644 --- a/providers/open_meteo.go +++ b/providers/open_meteo.go @@ -101,8 +101,6 @@ func (p *OpenMeteoProvider) FetchData(coords *plumber.Coordinates) (*plumber.Bas // SetQueryParams forms the query parameters for OpenMeteo API based on given coordinates func (p *OpenMeteoProvider) SetQueryParams(coords *plumber.Coordinates) { p.queryParams = map[string]string{ - "latitude": fmt.Sprintf("%f", coords.Latitude), - "longitude": fmt.Sprintf("%f", coords.Longitude), "current": "temperature_2m,relative_humidity_2m,apparent_temperature,is_day,precipitation,rain,showers,snowfall,weather_code,cloud_cover,pressure_msl,surface_pressure,wind_speed_10m,wind_direction_10m,wind_gusts_10m", "hourly": "temperature_2m,relative_humidity_2m,dew_point_2m,apparent_temperature,precipitation_probability,precipitation,weather_code,pressure_msl,surface_pressure,cloud_cover,cloud_cover_low,cloud_cover_mid,cloud_cover_high,visibility,evapotranspiration,et0_fao_evapotranspiration,vapour_pressure_deficit,wind_speed_10m,wind_speed_80m,wind_speed_120m,wind_speed_180m,wind_direction_10m,wind_direction_80m,wind_direction_120m,wind_direction_180m,wind_gusts_10m,temperature_80m,temperature_120m,temperature_180m,uv_index,uv_index_clear_sky,is_day,sunshine_duration,total_column_integrated_water_vapour,cape,lifted_index,convective_inhibition,freezing_level_height,boundary_layer_height,temperature_1000hPa,temperature_975hPa,temperature_950hPa,temperature_925hPa,temperature_900hPa,temperature_850hPa,temperature_800hPa,temperature_700hPa,temperature_600hPa,temperature_500hPa,temperature_400hPa,relative_humidity_1000hPa,relative_humidity_975hPa,relative_humidity_950hPa,relative_humidity_925hPa,relative_humidity_900hPa,relative_humidity_850hPa,relative_humidity_800hPa,relative_humidity_700hPa,relative_humidity_600hPa,relative_humidity_500hPa,relative_humidity_400hPa,cloud_cover_1000hPa,cloud_cover_975hPa,cloud_cover_950hPa,cloud_cover_925hPa,cloud_cover_900hPa,cloud_cover_850hPa,cloud_cover_800hPa,cloud_cover_700hPa,cloud_cover_600hPa,cloud_cover_500hPa,cloud_cover_400hPa,wind_speed_1000hPa,wind_speed_975hPa,wind_speed_950hPa,wind_speed_925hPa,wind_speed_900hPa,wind_speed_850hPa,wind_speed_800hPa,wind_speed_700hPa,wind_speed_600hPa,wind_speed_500hPa,wind_speed_400hPa,wind_direction_1000hPa,wind_direction_975hPa,wind_direction_950hPa,wind_direction_925hPa,wind_direction_900hPa,wind_direction_850hPa,wind_direction_800hPa,wind_direction_700hPa,wind_direction_600hPa,wind_direction_500hPa,wind_direction_400hPa,geopotential_height_1000hPa,geopotential_height_975hPa,geopotential_height_950hPa,geopotential_height_925hPa,geopotential_height_900hPa,geopotential_height_850hPa,geopotential_height_800hPa,geopotential_height_700hPa,geopotential_height_600hPa,geopotential_height_500hPa,geopotential_height_400hPa", "daily": "weather_code,temperature_2m_max,temperature_2m_min,apparent_temperature_max,apparent_temperature_min,sunrise,sunset,daylight_duration,sunshine_duration,uv_index_max,uv_index_clear_sky_max,precipitation_sum,precipitation_hours,precipitation_probability_max,wind_speed_10m_max,wind_gusts_10m_max,wind_direction_10m_dominant,shortwave_radiation_sum,et0_fao_evapotranspiration",