Skip to content

Commit

Permalink
Refactor URL builder code into its own class and support apiKeys
Browse files Browse the repository at this point in the history
  • Loading branch information
colinnuk committed Aug 29, 2024
1 parent 0ed740f commit 7e802f0
Show file tree
Hide file tree
Showing 4 changed files with 376 additions and 270 deletions.
2 changes: 1 addition & 1 deletion OpenMeteo/ElevationOptions.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace OpenMeteo
{
internal class ElevationOptions
public class ElevationOptions
{
public ElevationOptions(float latitude, float longitude)
{
Expand Down
276 changes: 7 additions & 269 deletions OpenMeteo/OpenMeteoClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
using System.Net.Http;
using System.Threading.Tasks;
using System.Text.Json;
using System.Globalization;

namespace OpenMeteo
{
Expand All @@ -11,14 +10,9 @@ namespace OpenMeteo
/// </summary>
public class OpenMeteoClient
{
private readonly string _weatherApiUrl = "https://api.open-meteo.com/v1/forecast";
private readonly string _geocodeApiUrl = "https://geocoding-api.open-meteo.com/v1/search";
private readonly string _airQualityApiUrl = "https://air-quality-api.open-meteo.com/v1/air-quality";
private readonly string _elevationApiUrl = "https://api.open-meteo.com/v1/elevation";
private readonly HttpController httpController;

private readonly UrlFactory _urlFactory = new();
private readonly IOpenMeteoLogger? _logger = default!;
private readonly string _apiKey = string.Empty;

/// <summary>
/// If set to true, exceptions from the OpenMeteo API will be rethrown. Default is false.
Expand Down Expand Up @@ -51,7 +45,7 @@ public OpenMeteoClient(IOpenMeteoLogger logger)
public OpenMeteoClient(string apiKey)
{
httpController = new HttpController();
_apiKey = apiKey;
_urlFactory = new UrlFactory(apiKey);
}

/// <summary>
Expand All @@ -64,7 +58,7 @@ public OpenMeteoClient(IOpenMeteoLogger logger, string apiKey)
{
httpController = new HttpController();
_logger = logger;
_apiKey = apiKey;
_urlFactory = new UrlFactory(apiKey);
}

/// <summary>
Expand Down Expand Up @@ -252,7 +246,7 @@ public OpenMeteoClient(IOpenMeteoLogger logger, string apiKey)
{
try
{
var url = MergeUrlWithOptions(_airQualityApiUrl, options);
var url = _urlFactory.GetUrlWithOptions(options);
_logger?.Debug($"{nameof(OpenMeteoClient)}.GetAirQualityAsync(). URL: {url}");
HttpResponseMessage response = await httpController.Client.GetAsync(url);
response.EnsureSuccessStatusCode();
Expand All @@ -272,7 +266,7 @@ public OpenMeteoClient(IOpenMeteoLogger logger, string apiKey)
{
try
{
var url = MergeUrlWithOptions(_weatherApiUrl, options);
var url = _urlFactory.GetUrlWithOptions(options);
_logger?.Debug($"{nameof(OpenMeteoClient)}.GetElevationAsync(). URL: {url}");
HttpResponseMessage response = await httpController.Client.GetAsync(url);
if(response.IsSuccessStatusCode)
Expand Down Expand Up @@ -311,7 +305,7 @@ public OpenMeteoClient(IOpenMeteoLogger logger, string apiKey)
try
{

var url = MergeUrlWithOptions(_geocodeApiUrl, options);
var url = _urlFactory.GetUrlWithOptions(options);
_logger?.Debug($"{nameof(OpenMeteoClient)}.GetGeocodingDataAsync(). URL: {url}");
HttpResponseMessage response = await httpController.Client.GetAsync(url);
response.EnsureSuccessStatusCode();
Expand All @@ -333,7 +327,7 @@ public OpenMeteoClient(IOpenMeteoLogger logger, string apiKey)
{
try
{
var url = MergeUrlWithOptions(_elevationApiUrl, options);
var url = _urlFactory.GetUrlWithOptions(options);
_logger?.Debug($"{nameof(OpenMeteoClient)}.GetElevationAsync(). URL: {url}");
HttpResponseMessage response = await httpController.Client.GetAsync(url);
response.EnsureSuccessStatusCode();
Expand All @@ -351,262 +345,6 @@ public OpenMeteoClient(IOpenMeteoLogger logger, string apiKey)
return null;
}
}

private string MergeUrlWithOptions(string url, WeatherForecastOptions? options)
{
if (options == null) return url;

UriBuilder uri = new UriBuilder(url);
bool isFirstParam = false;

// If no query given, add '?' to start the query string
if (uri.Query == string.Empty)
{
uri.Query = "?";

// isFirstParam becomes true because the query string is new
isFirstParam = true;
}

// Add the properties

// Begin with Latitude and Longitude since they're required
if (isFirstParam)
uri.Query += "latitude=" + options.Latitude.ToString(CultureInfo.InvariantCulture);
else
uri.Query += "&latitude=" + options.Latitude.ToString(CultureInfo.InvariantCulture);

uri.Query += "&longitude=" + options.Longitude.ToString(CultureInfo.InvariantCulture);

uri.Query += "&temperature_unit=" + options.Temperature_Unit.ToString();
uri.Query += "&windspeed_unit=" + options.Windspeed_Unit.ToString();
uri.Query += "&precipitation_unit=" + options.Precipitation_Unit.ToString();
if (options.Timezone != string.Empty)
uri.Query += "&timezone=" + options.Timezone;

uri.Query += "&timeformat=" + options.Timeformat.ToString();

uri.Query += "&past_days=" + options.Past_Days;

if (options.Start_date != string.Empty)
uri.Query += "&start_date=" + options.Start_date;
if (options.End_date != string.Empty)
uri.Query += "&end_date=" + options.End_date;

// Now we iterate through hourly and daily

// Hourly
if (options.Hourly.Count > 0)
{
bool firstHourlyElement = true;
uri.Query += "&hourly=";

foreach (var option in options.Hourly)
{
if (firstHourlyElement)
{
uri.Query += option.ToString();
firstHourlyElement = false;
}
else
{
uri.Query += "," + option.ToString();
}
}
}

// Daily
if (options.Daily.Count > 0)
{
bool firstDailyElement = true;
uri.Query += "&daily=";
foreach (var option in options.Daily)
{
if (firstDailyElement)
{
uri.Query += option.ToString();
firstDailyElement = false;
}
else
{
uri.Query += "," + option.ToString();
}
}
}

// 0.2.0 Weather models
// cell_selection
uri.Query += "&cell_selection=" + options.Cell_Selection;

// Models
if (options.Models.Count > 0)
{
bool firstModelsElement = true;
uri.Query += "&models=";
foreach (var option in options.Models)
{
if (firstModelsElement)
{
uri.Query += option.ToString();
firstModelsElement = false;
}
else
{
uri.Query += "," + option.ToString();
}
}
}

// new current parameter
if (options.Current.Count > 0)
{
bool firstCurrentElement = true;
uri.Query += "&current=";
foreach (var option in options.Current)
{
if (firstCurrentElement)
{
uri.Query += option.ToString();
firstCurrentElement = false;
}
else
{
uri.Query += "," + option.ToString();
}
}
}

// new minutely_15 parameter
if (options.Minutely15.Count > 0)
{
bool firstMinutelyElement = true;
uri.Query += "&minutely_15=";
foreach (var option in options.Minutely15)
{
if (firstMinutelyElement)
{
uri.Query += option.ToString();
firstMinutelyElement = false;
}
else
{
uri.Query += "," + option.ToString();
}
}
}

return uri.ToString();
}

/// <summary>
/// Combines a given url with an options object to create a url for GET requests
/// </summary>
/// <returns>url+queryString</returns>
private string MergeUrlWithOptions(string url, GeocodingOptions options)
{
if (options == null) return url;

UriBuilder uri = new UriBuilder(url);
bool isFirstParam = false;

// If no query given, add '?' to start the query string
if (uri.Query == string.Empty)
{
uri.Query = "?";

// isFirstParam becomes true because the query string is new
isFirstParam = true;
}

// Now we check every property and set the value, if neccessary
if (isFirstParam)
uri.Query += "name=" + options.Name;
else
uri.Query += "&name=" + options.Name;

if(options.Count >0)
uri.Query += "&count=" + options.Count;

if (options.Format != string.Empty)
uri.Query += "&format=" + options.Format;

if (options.Language != string.Empty)
uri.Query += "&language=" + options.Language;

return uri.ToString();
}

/// <summary>
/// Combines a given url with an options object to create a url for GET requests
/// </summary>
/// <returns>url+queryString</returns>
private string MergeUrlWithOptions(string url, AirQualityOptions options)
{
if (options == null) return url;

UriBuilder uri = new UriBuilder(url);
bool isFirstParam = false;

// If no query given, add '?' to start the query string
if (uri.Query == string.Empty)
{
uri.Query = "?";

// isFirstParam becomes true because the query string is new
isFirstParam = true;
}

// Now we check every property and set the value, if neccessary
if (isFirstParam)
uri.Query += "latitude=" + options.Latitude.ToString(CultureInfo.InvariantCulture);
else
uri.Query += "&latitude=" + options.Latitude.ToString(CultureInfo.InvariantCulture);

uri.Query += "&longitude=" + options.Longitude.ToString(CultureInfo.InvariantCulture);

if (options.Domains != string.Empty)
uri.Query += "&domains=" + options.Domains;

if (options.Timeformat != string.Empty)
uri.Query += "&timeformat=" + options.Timeformat;

if (options.Timezone != string.Empty)
uri.Query += "&timezone=" + options.Timezone;

// Finally add hourly array
if (options.Hourly.Count >= 0)
{
bool firstHourlyElement = true;
uri.Query += "&hourly=";

foreach (var option in options.Hourly)
{
if (firstHourlyElement)
{
uri.Query += option.ToString();
firstHourlyElement = false;
}
else
{
uri.Query += "," + option.ToString();
}
}
}

return uri.ToString();
}

private string MergeUrlWithOptions(string url, ElevationOptions options)
{
if (options == null) return url;

UriBuilder uri = new UriBuilder(url)
{
Query = $"?latitude={options.Latitude.ToString(CultureInfo.InvariantCulture)}&longitude={options.Longitude.ToString(CultureInfo.InvariantCulture)}"
};

return uri.ToString();
}
}
}

Loading

0 comments on commit 7e802f0

Please sign in to comment.