From 64d38b4ef77bb398ae2a8f237adeef405c595b61 Mon Sep 17 00:00:00 2001 From: Sebastian Jensen Date: Tue, 22 Jul 2025 18:53:31 +0200 Subject: [PATCH 01/10] Update dependencies and target framework for tests Upgraded Spectre.Console in the console sample to v0.50.0. Updated test project to target net9.0 and refreshed package versions for xunit, test SDK, and coverlet. Removed GlobalUsings.cs and added explicit Xunit using in test file. --- .../OpenWeatherMapSharp.Console.csproj | 2 +- tests/OpenWeatherMapSharp.UnitTests/GlobalUsings.cs | 1 - .../OpenWeatherMapServiceTests.cs | 1 + .../OpenWeatherMapSharp.UnitTests.csproj | 12 ++++++------ 4 files changed, 8 insertions(+), 8 deletions(-) delete mode 100644 tests/OpenWeatherMapSharp.UnitTests/GlobalUsings.cs diff --git a/samples/OpenWeatherMapSharp.Console/OpenWeatherMapSharp.Console.csproj b/samples/OpenWeatherMapSharp.Console/OpenWeatherMapSharp.Console.csproj index 194867c..a59b047 100644 --- a/samples/OpenWeatherMapSharp.Console/OpenWeatherMapSharp.Console.csproj +++ b/samples/OpenWeatherMapSharp.Console/OpenWeatherMapSharp.Console.csproj @@ -8,7 +8,7 @@ - + diff --git a/tests/OpenWeatherMapSharp.UnitTests/GlobalUsings.cs b/tests/OpenWeatherMapSharp.UnitTests/GlobalUsings.cs deleted file mode 100644 index 8c927eb..0000000 --- a/tests/OpenWeatherMapSharp.UnitTests/GlobalUsings.cs +++ /dev/null @@ -1 +0,0 @@ -global using Xunit; \ No newline at end of file diff --git a/tests/OpenWeatherMapSharp.UnitTests/OpenWeatherMapServiceTests.cs b/tests/OpenWeatherMapSharp.UnitTests/OpenWeatherMapServiceTests.cs index 42c8802..67223d2 100644 --- a/tests/OpenWeatherMapSharp.UnitTests/OpenWeatherMapServiceTests.cs +++ b/tests/OpenWeatherMapSharp.UnitTests/OpenWeatherMapServiceTests.cs @@ -1,4 +1,5 @@ using OpenWeatherMapSharp.Models; +using Xunit; namespace OpenWeatherMapSharp.UnitTests; diff --git a/tests/OpenWeatherMapSharp.UnitTests/OpenWeatherMapSharp.UnitTests.csproj b/tests/OpenWeatherMapSharp.UnitTests/OpenWeatherMapSharp.UnitTests.csproj index 200077d..2dfe0b0 100644 --- a/tests/OpenWeatherMapSharp.UnitTests/OpenWeatherMapSharp.UnitTests.csproj +++ b/tests/OpenWeatherMapSharp.UnitTests/OpenWeatherMapSharp.UnitTests.csproj @@ -1,7 +1,7 @@ - + - net7.0 + net9.0 enable enable @@ -10,13 +10,13 @@ - - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all From ec5b2d4b7072ecaf1b5f0063ac5da89253274775 Mon Sep 17 00:00:00 2001 From: Sebastian Jensen Date: Tue, 22 Jul 2025 19:01:46 +0200 Subject: [PATCH 02/10] Update target framework to .NET 9.0 Changed the TargetFramework in the console project from net7.0 to net9.0 to support the latest .NET features and improvements. --- .../OpenWeatherMapSharp.Console.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/OpenWeatherMapSharp.Console/OpenWeatherMapSharp.Console.csproj b/samples/OpenWeatherMapSharp.Console/OpenWeatherMapSharp.Console.csproj index a59b047..f035b45 100644 --- a/samples/OpenWeatherMapSharp.Console/OpenWeatherMapSharp.Console.csproj +++ b/samples/OpenWeatherMapSharp.Console/OpenWeatherMapSharp.Console.csproj @@ -2,7 +2,7 @@ Exe - net7.0 + net9.0 enable enable From 5ed1f914eacaa1f07a115ee6e011e7baab06c594 Mon Sep 17 00:00:00 2001 From: Sebastian Jensen Date: Tue, 22 Jul 2025 19:02:06 +0200 Subject: [PATCH 03/10] Migrate models to System.Text.Json serialization Replaced Newtonsoft.Json attributes with System.Text.Json equivalents across all model classes for improved performance and native .NET support. Updated project dependencies and refactored HttpService to use System.Text.Json for deserialization. Also removed the ThreeHours property from Volume and added 2x/4x icon URL properties to WeatherRoot. --- src/OpenWeatherMapSharp/Models/City.cs | 18 ++++---- src/OpenWeatherMapSharp/Models/Clouds.cs | 4 +- src/OpenWeatherMapSharp/Models/Coordinates.cs | 6 +-- .../Models/ForecastItem.cs | 24 +++++----- .../Models/ForecastRoot.cs | 14 +++--- src/OpenWeatherMapSharp/Models/GeocodeInfo.cs | 16 +++---- .../Models/GeocodeZipInfo.cs | 12 ++--- src/OpenWeatherMapSharp/Models/Main.cs | 18 ++++---- src/OpenWeatherMapSharp/Models/System.cs | 14 +++--- src/OpenWeatherMapSharp/Models/Volume.cs | 10 +---- src/OpenWeatherMapSharp/Models/WeatherInfo.cs | 10 ++--- src/OpenWeatherMapSharp/Models/WeatherRoot.cs | 45 ++++++++++++------- src/OpenWeatherMapSharp/Models/Wind.cs | 8 ++-- .../OpenWeatherMapSharp.csproj | 6 +-- src/OpenWeatherMapSharp/Utils/HttpService.cs | 11 +++-- 15 files changed, 114 insertions(+), 102 deletions(-) diff --git a/src/OpenWeatherMapSharp/Models/City.cs b/src/OpenWeatherMapSharp/Models/City.cs index 2d82590..04b7ae2 100644 --- a/src/OpenWeatherMapSharp/Models/City.cs +++ b/src/OpenWeatherMapSharp/Models/City.cs @@ -1,6 +1,6 @@ -using Newtonsoft.Json; -using OpenWeatherMapSharp.Utils; +using OpenWeatherMapSharp.Utils; using System; +using System.Text.Json.Serialization; namespace OpenWeatherMapSharp.Models { @@ -12,37 +12,37 @@ public class City /// /// City ID /// - [JsonProperty("id")] + [JsonPropertyName("id")] public int Id { get; set; } /// /// City name /// - [JsonProperty("name")] + [JsonPropertyName("name")] public string Name { get; set; } /// /// Coordinates /// - [JsonProperty("coord")] + [JsonPropertyName("coord")] public Coordinates Coordinates { get; set; } /// /// Country code (GB, JP etc.) /// - [JsonProperty("country")] + [JsonPropertyName("country")] public string Country { get; set; } /// /// City population /// - [JsonProperty("population")] + [JsonPropertyName("population")] public int Population { get; set; } /// /// Sunrise time, unix, UTC /// - [JsonProperty("sunrise")] + [JsonPropertyName("sunrise")] public long SunriseUnix { get; set; } /// @@ -54,7 +54,7 @@ public class City /// /// Sunset time, unix, UTC /// - [JsonProperty("sunset")] + [JsonPropertyName("sunset")] public long SunsetUnix { get; set; } /// diff --git a/src/OpenWeatherMapSharp/Models/Clouds.cs b/src/OpenWeatherMapSharp/Models/Clouds.cs index 4333f77..e982642 100644 --- a/src/OpenWeatherMapSharp/Models/Clouds.cs +++ b/src/OpenWeatherMapSharp/Models/Clouds.cs @@ -1,4 +1,4 @@ -using Newtonsoft.Json; +using System.Text.Json.Serialization; namespace OpenWeatherMapSharp.Models { @@ -10,7 +10,7 @@ public class Clouds /// /// Cloudiness, % /// - [JsonProperty("all")] + [JsonPropertyName("all")] public int Cloudiness { get; set; } } } diff --git a/src/OpenWeatherMapSharp/Models/Coordinates.cs b/src/OpenWeatherMapSharp/Models/Coordinates.cs index 2b3d30f..2295a21 100644 --- a/src/OpenWeatherMapSharp/Models/Coordinates.cs +++ b/src/OpenWeatherMapSharp/Models/Coordinates.cs @@ -1,4 +1,4 @@ -using Newtonsoft.Json; +using System.Text.Json.Serialization; namespace OpenWeatherMapSharp.Models { @@ -10,13 +10,13 @@ public class Coordinates /// /// Longitude of the location /// - [JsonProperty("lon")] + [JsonPropertyName("lon")] public double Longitude { get; set; } /// /// Latitude of the location /// - [JsonProperty("lat")] + [JsonPropertyName("lat")] public double Latitude { get; set; } } } diff --git a/src/OpenWeatherMapSharp/Models/ForecastItem.cs b/src/OpenWeatherMapSharp/Models/ForecastItem.cs index 332a49e..9dd3527 100644 --- a/src/OpenWeatherMapSharp/Models/ForecastItem.cs +++ b/src/OpenWeatherMapSharp/Models/ForecastItem.cs @@ -1,7 +1,7 @@ -using Newtonsoft.Json; -using OpenWeatherMapSharp.Utils; +using OpenWeatherMapSharp.Utils; using System; using System.Collections.Generic; +using System.Text.Json.Serialization; namespace OpenWeatherMapSharp.Models { @@ -13,61 +13,61 @@ public class ForecastItem /// /// Main weather information /// - [JsonProperty("main")] + [JsonPropertyName("main")] public Main MainWeather { get; set; } /// /// List of weather infos /// - [JsonProperty("weather")] + [JsonPropertyName("weather")] public List WeatherInfos { get; set; } /// /// Cloud information /// - [JsonProperty("clouds")] + [JsonPropertyName("clouds")] public Clouds Clouds { get; set; } /// /// Wind information /// - [JsonProperty("wind")] + [JsonPropertyName("wind")] public Wind Wind { get; set; } /// /// Average visibility, metres /// - [JsonProperty("visibility")] + [JsonPropertyName("visibility")] public double Visibility { get; set; } /// /// Probability of precipitation /// - [JsonProperty("pop")] + [JsonPropertyName("pop")] public double Probability { get; set; } /// /// Rain information /// - [JsonProperty("rain")] + [JsonPropertyName("rain")] public Volume Rain { get; set; } /// /// Snow information /// - [JsonProperty("snow")] + [JsonPropertyName("snow")] public Volume Snow { get; set; } /// /// City information /// - [JsonProperty("city")] + [JsonPropertyName("city")] public City City { get; set; } /// /// Date, Unix, UTC /// - [JsonProperty("dt")] + [JsonPropertyName("dt")] public long DateUnix { get; set; } /// diff --git a/src/OpenWeatherMapSharp/Models/ForecastRoot.cs b/src/OpenWeatherMapSharp/Models/ForecastRoot.cs index 0e2705a..b7641e9 100644 --- a/src/OpenWeatherMapSharp/Models/ForecastRoot.cs +++ b/src/OpenWeatherMapSharp/Models/ForecastRoot.cs @@ -1,5 +1,5 @@ -using Newtonsoft.Json; -using System.Collections.Generic; +using System.Collections.Generic; +using System.Text.Json.Serialization; namespace OpenWeatherMapSharp.Models { @@ -11,31 +11,31 @@ public class ForecastRoot /// /// Internal parameter /// - [JsonProperty("cod")] + [JsonPropertyName("cod")] public string Code { get; set; } /// /// Internal parameter /// - [JsonProperty("message")] + [JsonPropertyName("message")] public double Message { get; set; } /// /// A number of timestamps returned in the API response /// - [JsonProperty("cnt")] + [JsonPropertyName("cnt")] public int Count { get; set; } /// /// List of forecast items /// - [JsonProperty("list")] + [JsonPropertyName("list")] public List Items { get; set; } /// /// City information /// - [JsonProperty("city")] + [JsonPropertyName("city")] public City City { get; set; } } } diff --git a/src/OpenWeatherMapSharp/Models/GeocodeInfo.cs b/src/OpenWeatherMapSharp/Models/GeocodeInfo.cs index 9645d82..8328f04 100644 --- a/src/OpenWeatherMapSharp/Models/GeocodeInfo.cs +++ b/src/OpenWeatherMapSharp/Models/GeocodeInfo.cs @@ -1,5 +1,5 @@ -using Newtonsoft.Json; -using System.Collections.Generic; +using System.Collections.Generic; +using System.Text.Json.Serialization; namespace OpenWeatherMapSharp.Models { @@ -8,37 +8,37 @@ public class GeocodeInfo /// /// Name of the found location /// - [JsonProperty("name")] + [JsonPropertyName("name")] public string Name { get; set; } /// /// Name of the found location in dufferent languages /// - [JsonProperty("local_names")] + [JsonPropertyName("local_names")] public Dictionary LocalNames { get; set; } /// /// Geographical coordinates of the found location: Latitude /// - [JsonProperty("lat")] + [JsonPropertyName("lat")] public double Latitude { get; set; } /// /// Geographical coordinates of the found location: Longitude /// - [JsonProperty("lon")] + [JsonPropertyName("lon")] public double Longitude { get; set; } /// /// Country of the found location /// - [JsonProperty("country")] + [JsonPropertyName("country")] public string Country { get; set; } /// /// State of the found location, where available /// - [JsonProperty("state")] + [JsonPropertyName("state")] public string State { get; set; } } } diff --git a/src/OpenWeatherMapSharp/Models/GeocodeZipInfo.cs b/src/OpenWeatherMapSharp/Models/GeocodeZipInfo.cs index 180675e..f927a05 100644 --- a/src/OpenWeatherMapSharp/Models/GeocodeZipInfo.cs +++ b/src/OpenWeatherMapSharp/Models/GeocodeZipInfo.cs @@ -1,4 +1,4 @@ -using Newtonsoft.Json; +using System.Text.Json.Serialization; namespace OpenWeatherMapSharp.Models { @@ -7,31 +7,31 @@ public class GeocodeZipInfo /// /// Specified zip/post code in the API request /// - [JsonProperty("zip")] + [JsonPropertyName("zip")] public string ZipCode { get; set; } /// /// Name of the found area /// - [JsonProperty("name")] + [JsonPropertyName("name")] public string Name { get; set; } /// /// Geographical coordinates of the found location: Latitude /// - [JsonProperty("lat")] + [JsonPropertyName("lat")] public double Latiude { get; set; } /// /// Geographical coordinates of the found location: Longitude /// - [JsonProperty("lon")] + [JsonPropertyName("lon")] public double Longitude { get; set; } /// /// Country of the found zip/post code /// - [JsonProperty("country")] + [JsonPropertyName("country")] public string Country { get; set; } } } diff --git a/src/OpenWeatherMapSharp/Models/Main.cs b/src/OpenWeatherMapSharp/Models/Main.cs index 8b66653..14317ec 100644 --- a/src/OpenWeatherMapSharp/Models/Main.cs +++ b/src/OpenWeatherMapSharp/Models/Main.cs @@ -1,4 +1,4 @@ -using Newtonsoft.Json; +using System.Text.Json.Serialization; namespace OpenWeatherMapSharp.Models { @@ -11,26 +11,26 @@ public class Main /// Temperature. /// Unit Default: Kelvin, Metric: Celcius, Imperial: Fahrenheit /// - [JsonProperty("temp")] + [JsonPropertyName("temp")] public double Temperature { get; set; } /// /// Temperature. This temperature parameter accounts for the human perception of weather. /// Unit Default: Kelvin, Metric: Celcius, Imperial: Fahrenheit /// - [JsonProperty("feels_like")] + [JsonPropertyName("feels_like")] public double FeelsLikeTemperature { get; set; } /// /// Atmospheric pressure of the sea level, hPa /// - [JsonProperty("pressure")] + [JsonPropertyName("pressure")] public double Pressure { get; set; } /// /// Humidity, % /// - [JsonProperty("humidity")] + [JsonPropertyName("humidity")] public double Humidity { get; set; } /// @@ -38,7 +38,7 @@ public class Main /// This is minimal currently observed temperature (within large megalopolises and urban areas). /// Unit Default: Kelvin, Metric: Celsius, Imperial: Fahrenheit /// - [JsonProperty("temp_min")] + [JsonPropertyName("temp_min")] public double MinTemperature { get; set; } /// @@ -46,19 +46,19 @@ public class Main /// This is maximal currently observed temperature (within large megalopolises and urban areas). /// Unit Default: Kelvin, Metric: Celsius, Imperial: Fahrenheit /// - [JsonProperty("temp_max")] + [JsonPropertyName("temp_max")] public double MaxTemperature { get; set; } /// /// Atmospheric pressure of the sea level, hPa /// - [JsonProperty("sea_level")] + [JsonPropertyName("sea_level")] public double SeaLevelPressure { get; set; } /// /// Atmospheric pressure of the ground level, hPa /// - [JsonProperty("grnd_level")] + [JsonPropertyName("grnd_level")] public double GroundLevelPressure { get; set; } } } diff --git a/src/OpenWeatherMapSharp/Models/System.cs b/src/OpenWeatherMapSharp/Models/System.cs index 4d445eb..f119763 100644 --- a/src/OpenWeatherMapSharp/Models/System.cs +++ b/src/OpenWeatherMapSharp/Models/System.cs @@ -1,6 +1,6 @@ -using Newtonsoft.Json; -using OpenWeatherMapSharp.Utils; +using OpenWeatherMapSharp.Utils; using System; +using System.Text.Json.Serialization; namespace OpenWeatherMapSharp.Models { @@ -12,25 +12,25 @@ public class System /// /// Internal parameter /// - [JsonProperty("type")] + [JsonPropertyName("type")] public int Type { get; set; } /// /// Internal parameter /// - [JsonProperty("id")] + [JsonPropertyName("id")] public int Id { get; set; } /// /// Country code (GB, JP etc.) /// - [JsonProperty("country")] + [JsonPropertyName("country")] public string Country { get; set; } /// /// Sunrise time, unix, UTC /// - [JsonProperty("sunrise")] + [JsonPropertyName("sunrise")] public long SunriseUnix { get; set; } /// @@ -42,7 +42,7 @@ public class System /// /// Sunset time, unix, UTC /// - [JsonProperty("sunset")] + [JsonPropertyName("sunset")] public long SunsetUnix { get; set; } /// diff --git a/src/OpenWeatherMapSharp/Models/Volume.cs b/src/OpenWeatherMapSharp/Models/Volume.cs index 4fa5291..2c304e9 100644 --- a/src/OpenWeatherMapSharp/Models/Volume.cs +++ b/src/OpenWeatherMapSharp/Models/Volume.cs @@ -1,4 +1,4 @@ -using Newtonsoft.Json; +using System.Text.Json.Serialization; namespace OpenWeatherMapSharp.Models { @@ -10,13 +10,7 @@ public class Volume /// /// Volume for the last 1 hour, mm. /// - [JsonProperty("1h")] + [JsonPropertyName("1h")] public double OneHour { get; set; } - - /// - /// Volume for the last 3 hour, mm. - /// - [JsonProperty("3h")] - public double ThreeHours { get; set; } } } diff --git a/src/OpenWeatherMapSharp/Models/WeatherInfo.cs b/src/OpenWeatherMapSharp/Models/WeatherInfo.cs index 41d6945..7c352da 100644 --- a/src/OpenWeatherMapSharp/Models/WeatherInfo.cs +++ b/src/OpenWeatherMapSharp/Models/WeatherInfo.cs @@ -1,4 +1,4 @@ -using Newtonsoft.Json; +using System.Text.Json.Serialization; namespace OpenWeatherMapSharp.Models { @@ -10,25 +10,25 @@ public class WeatherInfo /// /// Weather condition id /// - [JsonProperty("id")] + [JsonPropertyName("id")] public int Id { get; set; } /// /// Group of weather parameters (Rain, Snow, Clouds etc.) /// - [JsonProperty("main")] + [JsonPropertyName("main")] public string Main { get; set; } /// /// Weather coniditon with the group /// - [JsonProperty("description")] + [JsonPropertyName("description")] public string Description { get; set; } /// /// Weather icon id /// - [JsonProperty("icon")] + [JsonPropertyName("icon")] public string Icon { get; set; } } } diff --git a/src/OpenWeatherMapSharp/Models/WeatherRoot.cs b/src/OpenWeatherMapSharp/Models/WeatherRoot.cs index d95b299..6f55bd9 100644 --- a/src/OpenWeatherMapSharp/Models/WeatherRoot.cs +++ b/src/OpenWeatherMapSharp/Models/WeatherRoot.cs @@ -1,7 +1,7 @@ -using Newtonsoft.Json; -using OpenWeatherMapSharp.Utils; +using OpenWeatherMapSharp.Utils; using System; using System.Collections.Generic; +using System.Text.Json.Serialization; namespace OpenWeatherMapSharp.Models { @@ -10,61 +10,61 @@ public class WeatherRoot /// /// Coordinates information /// - [JsonProperty("coord")] + [JsonPropertyName("coord")] public Coordinates Coordinates { get; set; } /// /// List of weather infos /// - [JsonProperty("weather")] + [JsonPropertyName("weather")] public List WeatherInfos { get; set; } /// /// Internal parameter /// - [JsonProperty("base")] + [JsonPropertyName("base")] public string Base { get; set; } = string.Empty; /// /// Main weather information /// - [JsonProperty("main")] + [JsonPropertyName("main")] public Main MainWeather { get; set; } /// /// Average visibility, metres /// - [JsonProperty("visibility")] + [JsonPropertyName("visibility")] public double Visibility { get; set; } = 0; /// /// Wind information /// - [JsonProperty("wind")] + [JsonPropertyName("wind")] public Wind Wind { get; set; } /// /// Clouds information /// - [JsonProperty("clouds")] + [JsonPropertyName("clouds")] public Clouds Clouds { get; set; } /// /// Rain information /// - [JsonProperty("rain")] + [JsonPropertyName("rain")] public Volume Rain { get; set; } /// /// Snow information /// - [JsonProperty("snow")] + [JsonPropertyName("snow")] public Volume Snow { get; set; } /// /// Date, Unix, UTC /// - [JsonProperty("dt")] + [JsonPropertyName("dt")] public long DateUnix { get; set; } /// @@ -76,25 +76,25 @@ public class WeatherRoot /// /// System information /// - [JsonProperty("sys")] + [JsonPropertyName("sys")] public System System { get; set; } /// /// City ID /// - [JsonProperty("id")] + [JsonPropertyName("id")] public int CityId { get; set; } /// /// City name /// - [JsonProperty("name")] + [JsonPropertyName("name")] public string Name { get; set; } /// /// Internal parameter /// - [JsonProperty("cod")] + [JsonPropertyName("cod")] public int Code { get; set; } /// @@ -103,6 +103,19 @@ public class WeatherRoot [JsonIgnore] public string Icon => $"https://openweathermap.org/img/w/{WeatherInfos?[0]?.Icon}.png"; + /// + /// Icon url (2x) + /// + [JsonIgnore] + public string Icon2x => $"https://openweathermap.org/img/w/{WeatherInfos?[0]?.Icon}@2x.png"; + + /// + /// Icon url (4x) + /// + [JsonIgnore] + public string Icon4x => $"https://openweathermap.org/img/w/{WeatherInfos?[0]?.Icon}@4x.png"; + + /// /// Icon name /// diff --git a/src/OpenWeatherMapSharp/Models/Wind.cs b/src/OpenWeatherMapSharp/Models/Wind.cs index 16a9e89..24a5630 100644 --- a/src/OpenWeatherMapSharp/Models/Wind.cs +++ b/src/OpenWeatherMapSharp/Models/Wind.cs @@ -1,4 +1,4 @@ -using Newtonsoft.Json; +using System.Text.Json.Serialization; namespace OpenWeatherMapSharp.Models { @@ -11,20 +11,20 @@ public class Wind /// Wind speed /// Unit Default: meter/sec, Metric: meter/sec, Imperial: miles/hour /// - [JsonProperty("speed")] + [JsonPropertyName("speed")] public double Speed { get; set; } /// /// Wind direction, degrees (meteorological) /// - [JsonProperty("deg")] + [JsonPropertyName("deg")] public double Degrees { get; set; } /// /// Wind gust /// Unit Default: meter/sec, Metric: meter/sec, Imperial: miles/hour /// - [JsonProperty("gust")] + [JsonPropertyName("gust")] public double Gust { get; set; } } } diff --git a/src/OpenWeatherMapSharp/OpenWeatherMapSharp.csproj b/src/OpenWeatherMapSharp/OpenWeatherMapSharp.csproj index 26e50c4..efbce50 100644 --- a/src/OpenWeatherMapSharp/OpenWeatherMapSharp.csproj +++ b/src/OpenWeatherMapSharp/OpenWeatherMapSharp.csproj @@ -21,13 +21,13 @@ en - + - + - + diff --git a/src/OpenWeatherMapSharp/Utils/HttpService.cs b/src/OpenWeatherMapSharp/Utils/HttpService.cs index 739d19d..816b5c0 100644 --- a/src/OpenWeatherMapSharp/Utils/HttpService.cs +++ b/src/OpenWeatherMapSharp/Utils/HttpService.cs @@ -1,13 +1,18 @@ -using Newtonsoft.Json; -using OpenWeatherMapSharp.Models; +using OpenWeatherMapSharp.Models; using System; using System.Net.Http; +using System.Text.Json; using System.Threading.Tasks; namespace OpenWeatherMapSharp.Utils { internal static class HttpService { + private static readonly JsonSerializerOptions defaultJsonSerializerOptions = new JsonSerializerOptions + { + PropertyNameCaseInsensitive = true + }; + internal static async Task> GetDataAsync(string url) where TClass : class { using (HttpClient client = new HttpClient()) @@ -24,7 +29,7 @@ internal static async Task> GetDataAsync { IsSuccess = true, - Response = JsonConvert.DeserializeObject(json) + Response = JsonSerializer.Deserialize(json, defaultJsonSerializerOptions) }; } catch (Exception ex) From 7f8bbbbfc09b53ceee9121e46ac7ac7760f4f41f Mon Sep 17 00:00:00 2001 From: Sebastian Jensen Date: Tue, 22 Jul 2025 19:09:04 +0200 Subject: [PATCH 04/10] Update LanguageCode enum values Added new language codes (BE, IS, KU, SK, SQ) and reordered some entries in the LanguageCode enum to better reflect supported languages. Removed AL and adjusted placement of SV and NO for consistency. --- src/OpenWeatherMapSharp/Models/Enums/LanguageCode.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/OpenWeatherMapSharp/Models/Enums/LanguageCode.cs b/src/OpenWeatherMapSharp/Models/Enums/LanguageCode.cs index ba4b932..38230a2 100644 --- a/src/OpenWeatherMapSharp/Models/Enums/LanguageCode.cs +++ b/src/OpenWeatherMapSharp/Models/Enums/LanguageCode.cs @@ -7,9 +7,9 @@ public enum LanguageCode { EN, AF, - AL, AR, AZ, + BE, BG, CA, CZ, @@ -26,25 +26,28 @@ public enum LanguageCode HR, HU, ID, + IS, IT, JA, KR, + KU, LA, LT, MK, - NO, NL, + NO, PL, PT, PT_BR, RO, RU, - SV, SE, + SK, SL, SP, - ES, + SQ, SR, + SV, TH, TR, UA, From 66faa243150a71dd7a51d5a3317f947444bab343 Mon Sep 17 00:00:00 2001 From: Sebastian Jensen Date: Tue, 22 Jul 2025 19:09:06 +0200 Subject: [PATCH 05/10] Update WeatherRoot.cs --- src/OpenWeatherMapSharp/Models/WeatherRoot.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/OpenWeatherMapSharp/Models/WeatherRoot.cs b/src/OpenWeatherMapSharp/Models/WeatherRoot.cs index 6f55bd9..5170a69 100644 --- a/src/OpenWeatherMapSharp/Models/WeatherRoot.cs +++ b/src/OpenWeatherMapSharp/Models/WeatherRoot.cs @@ -79,6 +79,12 @@ public class WeatherRoot [JsonPropertyName("sys")] public System System { get; set; } + /// + /// Shift in seconds from UTC + /// + [JsonPropertyName("timezone")] + public int Timezone { get; set; } + /// /// City ID /// From 053288a9fbfceef899973e2bc17fbf071fcefaa1 Mon Sep 17 00:00:00 2001 From: Sebastian Jensen Date: Tue, 22 Jul 2025 20:07:17 +0200 Subject: [PATCH 06/10] Update .NET version to 9.0.x in workflow Changed the GitHub Actions workflow to use .NET 9.0.x instead of 7.0.x for setup. This ensures the CI pipeline uses the latest .NET version. --- .github/workflows/dotnet.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 0c14f89..4a812de 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -20,7 +20,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v3 with: - dotnet-version: 7.0.x + dotnet-version: 9.0.x - name: Restore dependencies run: dotnet restore From 5dd9404dea0007aa3934e2a082cd72de8266d609 Mon Sep 17 00:00:00 2001 From: Sebastian Jensen Date: Tue, 22 Jul 2025 20:08:13 +0200 Subject: [PATCH 07/10] Improve XML documentation and code clarity Enhanced XML documentation comments across models, enums, interfaces, and service classes for better clarity and maintainability. Updated method parameter descriptions, added missing remarks, and improved summary tags. Refactored some code for consistency, fixed typos, and clarified obsolete warnings. No functional changes were made. --- .../IOpenWeatherMapService.cs | 102 ++++++++--------- src/OpenWeatherMapSharp/Models/City.cs | 32 ++++-- src/OpenWeatherMapSharp/Models/Clouds.cs | 4 +- src/OpenWeatherMapSharp/Models/Coordinates.cs | 9 +- .../Models/Enums/LanguageCode.cs | 104 +++++++++++++++++- src/OpenWeatherMapSharp/Models/Enums/Unit.cs | 21 +++- .../Models/ForecastItem.cs | 33 +++--- .../Models/ForecastRoot.cs | 18 ++- src/OpenWeatherMapSharp/Models/GeocodeInfo.cs | 23 +++- .../Models/GeocodeZipInfo.cs | 21 +++- src/OpenWeatherMapSharp/Models/Main.cs | 42 ++++--- .../Models/OpenWeatherMapServiceResponse.cs | 21 +++- src/OpenWeatherMapSharp/Models/System.cs | 26 +++-- src/OpenWeatherMapSharp/Models/Volume.cs | 6 +- src/OpenWeatherMapSharp/Models/WeatherInfo.cs | 14 ++- src/OpenWeatherMapSharp/Models/WeatherRoot.cs | 61 +++++----- src/OpenWeatherMapSharp/Models/Wind.cs | 16 ++- .../OpenWeatherMapService.cs | 65 ++++++++--- src/OpenWeatherMapSharp/Utils/HttpService.cs | 26 ++++- .../Utils/LongExtensions.cs | 20 +++- src/OpenWeatherMapSharp/Utils/Statics.cs | 75 ++++++++++--- 21 files changed, 538 insertions(+), 201 deletions(-) diff --git a/src/OpenWeatherMapSharp/IOpenWeatherMapService.cs b/src/OpenWeatherMapSharp/IOpenWeatherMapService.cs index efd159e..d0acf45 100644 --- a/src/OpenWeatherMapSharp/IOpenWeatherMapService.cs +++ b/src/OpenWeatherMapSharp/IOpenWeatherMapService.cs @@ -7,18 +7,19 @@ namespace OpenWeatherMapSharp { /// - /// This interface defines the methods to communicate with the Open Weather Map API + /// Defines the contract for communicating with the OpenWeatherMap API. /// public interface IOpenWeatherMapService { /// - /// Gets forecast for a location given its longitude and latitude + /// Gets forecast data for a location based + /// on its geographic coordinates. /// - /// The latitude of the location - /// The longitude of the location - /// The language used for the response - /// The unit of measurement for the response - /// The OpenWeatherMapServiceResponse containing the forecast information + /// Latitude of the location. + /// Longitude of the location. + /// Language of the response (default: English). + /// Unit of measurement (default: Standard). + /// A forecast response wrapped in . Task> GetForecastAsync( double latitude, double longitude, @@ -26,39 +27,39 @@ Task> GetForecastAsync( Unit unit = Unit.Standard); /// - /// Gets forecast for a location given its city ID + /// Gets forecast data based on a city ID. /// - /// The ID of the location's city - /// The language used for the response - /// The unit of measurement for the response - /// The OpenWeatherMapServiceResponse containing the forecast information - [Obsolete("Please note that API requests by city name, zip-codes and city id have been deprecated. Although they are still available for use, bug fixing and updates are no longer available for this functionality.")] + /// The city ID. + /// Language of the response. + /// Unit of measurement. + /// A forecast response wrapped in . + [Obsolete("API requests by city name, zip-code, and city ID are deprecated and no longer maintained.")] Task> GetForecastAsync( int cityId, LanguageCode language = LanguageCode.EN, Unit unit = Unit.Standard); /// - /// Gets forecast for a location given its city name + /// Gets forecast data based on a city name. /// - /// The name of the location's city - /// The language used for the response - /// The unit of measurement for the response - /// The OpenWeatherMapServiceResponse containing the forecast information - [Obsolete("Please note that API requests by city name, zip-codes and city id have been deprecated. Although they are still available for use, bug fixing and updates are no longer available for this functionality.")] + /// The city name. + /// Language of the response. + /// Unit of measurement. + /// A forecast response wrapped in . + [Obsolete("API requests by city name, zip-code, and city ID are deprecated and no longer maintained.")] Task> GetForecastAsync( string city, LanguageCode language = LanguageCode.EN, Unit unit = Unit.Standard); /// - /// Gets weather information for a location given its longitude and latitude + /// Gets current weather data for a location based on its geographic coordinates. /// - /// The latitude of the location - /// The longitude of the location - /// The language used for the response - /// The unit of measurement for the response - /// The OpenWeatherMapServiceResponse containing the weather information + /// Latitude of the location. + /// Longitude of the location. + /// Language of the response (default: English). + /// Unit of measurement (default: Standard). + /// A weather response wrapped in . Task> GetWeatherAsync( double latitude, double longitude, @@ -66,60 +67,59 @@ Task> GetWeatherAsync( Unit unit = Unit.Standard); /// - /// Gets weather information for a location given its city ID + /// Gets current weather data based on a city ID. /// - /// The ID of the location's city - /// The language used for the response - /// The unit of measurement for the response - /// The OpenWeatherMapServiceResponse containing the weather information - [Obsolete("Please note that API requests by city name, zip-codes and city id have been deprecated. Although they are still available for use, bug fixing and updates are no longer available for this functionality.")] + /// The city ID. + /// Language of the response. + /// Unit of measurement. + /// A weather response wrapped in . + [Obsolete("API requests by city name, zip-code, and city ID are deprecated and no longer maintained.")] Task> GetWeatherAsync( int cityId, LanguageCode language = LanguageCode.EN, Unit unit = Unit.Standard); /// - /// Gets weather information for a location given its city name + /// Gets current weather data based on a city name. /// - /// The name of the location's city - /// The language used for the response - /// The unit of measurement for the response - /// The OpenWeatherMapServiceResponse containing the weather information - [Obsolete("Please note that API requests by city name, zip-codes and city id have been deprecated. Although they are still available for use, bug fixing and updates are no longer available for this functionality.")] + /// The city name. + /// Language of the response. + /// Unit of measurement. + /// A weather response wrapped in . + [Obsolete("API requests by city name, zip-code, and city ID are deprecated and no longer maintained.")] Task> GetWeatherAsync( string city, LanguageCode language = LanguageCode.EN, Unit unit = Unit.Standard); - /// - /// Gets a list of locations given a query string + /// Gets a list of matching locations for a given query string. /// - /// The query string to search for - /// The maximum number of locations to return - /// The OpenWeatherMapServiceResponse containing the list of relevant locations + /// The location name or part of it to search for. + /// The maximum number of results to return (default: 5). + /// A response with a list of matching objects. Task>> GetLocationByNameAsync( string query, int limit = 5); /// - /// Gets location information for a given zip code + /// Gets geolocation information based on a ZIP or postal code. /// - /// The zip code to search for - /// The OpenWeatherMapServiceResponse containing the location information + /// The ZIP/postal code to search for. + /// A response containing a object. Task> GetLocationByZipAsync( string zipCode); /// - /// Gets location information for a given latitude and longitude + /// Gets a list of matching locations for a given pair of coordinates. /// - /// The latitude of the location - /// The longitude of the location - /// The maximum number of locations to return - /// The OpenWeatherMapServiceResponse containing the location information + /// Latitude of the location. + /// Longitude of the location. + /// The maximum number of results to return (default: 5). + /// A response with a list of objects. Task>> GetLocationByLatLonAsync( double latitude, double longitude, int limit = 5); } -} \ No newline at end of file +} diff --git a/src/OpenWeatherMapSharp/Models/City.cs b/src/OpenWeatherMapSharp/Models/City.cs index 04b7ae2..2a38e0a 100644 --- a/src/OpenWeatherMapSharp/Models/City.cs +++ b/src/OpenWeatherMapSharp/Models/City.cs @@ -5,62 +5,70 @@ namespace OpenWeatherMapSharp.Models { /// - /// City information + /// Represents basic city information, + /// including coordinates, country, + /// population, and sunrise/sunset times. /// public class City { /// - /// City ID + /// The unique city ID as defined + /// by OpenWeatherMap. /// [JsonPropertyName("id")] public int Id { get; set; } /// - /// City name + /// The name of the city. /// [JsonPropertyName("name")] public string Name { get; set; } /// - /// Coordinates + /// Geographic coordinates + /// (latitude and longitude) of the city. /// [JsonPropertyName("coord")] public Coordinates Coordinates { get; set; } /// - /// Country code (GB, JP etc.) + /// ISO 3166 country code (e.g., "GB", "JP"). /// [JsonPropertyName("country")] public string Country { get; set; } /// - /// City population + /// Total population of the city. /// [JsonPropertyName("population")] public int Population { get; set; } /// - /// Sunrise time, unix, UTC + /// Sunrise time in Unix timestamp format (UTC). /// [JsonPropertyName("sunrise")] public long SunriseUnix { get; set; } /// - /// Sunrise time, DateTime + /// Sunrise time converted to a + /// UTC . /// [JsonIgnore] - public DateTime Sunrise => SunriseUnix.ToDateTime(); + public DateTime Sunrise + => SunriseUnix.ToDateTime(); /// - /// Sunset time, unix, UTC + /// Sunset time in Unix timestamp format (UTC). /// [JsonPropertyName("sunset")] public long SunsetUnix { get; set; } /// - /// Sunset time, DateTime + /// Sunset time converted to a + /// UTC . /// [JsonIgnore] - public DateTime Sunset => SunsetUnix.ToDateTime(); + public DateTime Sunset + => SunsetUnix.ToDateTime(); } } diff --git a/src/OpenWeatherMapSharp/Models/Clouds.cs b/src/OpenWeatherMapSharp/Models/Clouds.cs index e982642..b5bddf0 100644 --- a/src/OpenWeatherMapSharp/Models/Clouds.cs +++ b/src/OpenWeatherMapSharp/Models/Clouds.cs @@ -3,12 +3,12 @@ namespace OpenWeatherMapSharp.Models { /// - /// Clouds information + /// Represents cloud coverage information. /// public class Clouds { /// - /// Cloudiness, % + /// Cloudiness percentage (0–100). /// [JsonPropertyName("all")] public int Cloudiness { get; set; } diff --git a/src/OpenWeatherMapSharp/Models/Coordinates.cs b/src/OpenWeatherMapSharp/Models/Coordinates.cs index 2295a21..ec13883 100644 --- a/src/OpenWeatherMapSharp/Models/Coordinates.cs +++ b/src/OpenWeatherMapSharp/Models/Coordinates.cs @@ -3,18 +3,21 @@ namespace OpenWeatherMapSharp.Models { /// - /// Coordinates information + /// Represents the geographic coordinates + /// of a location. /// public class Coordinates { /// - /// Longitude of the location + /// Longitude of the location, + /// in decimal degrees. /// [JsonPropertyName("lon")] public double Longitude { get; set; } /// - /// Latitude of the location + /// Latitude of the location, + /// in decimal degrees. /// [JsonPropertyName("lat")] public double Latitude { get; set; } diff --git a/src/OpenWeatherMapSharp/Models/Enums/LanguageCode.cs b/src/OpenWeatherMapSharp/Models/Enums/LanguageCode.cs index 38230a2..fab2f9d 100644 --- a/src/OpenWeatherMapSharp/Models/Enums/LanguageCode.cs +++ b/src/OpenWeatherMapSharp/Models/Enums/LanguageCode.cs @@ -1,60 +1,162 @@ namespace OpenWeatherMapSharp.Models.Enums { /// - /// Supported languages + /// Represents supported language codes for API localization. + /// These codes align with the OpenWeatherMap API specification. /// public enum LanguageCode { + /// English EN, + + /// Afrikaans AF, + + /// Arabic AR, + + /// Azerbaijani AZ, + + /// Belarusian BE, + + /// Bulgarian BG, + + /// Catalan CA, + + /// Czech (note: 'CZ' used instead of ISO 'CS') CZ, + + /// Danish DA, + + /// German DE, + + /// Greek EL, + + /// Basque EU, + + /// Persian (Farsi) FA, + + /// Finnish FI, + + /// French FR, + + /// Galician GL, + + /// Hebrew HE, + + /// Hindi HI, + + /// Croatian HR, + + /// Hungarian HU, + + /// Indonesian ID, + + /// Icelandic IS, + + /// Italian IT, + + /// Japanese JA, + + /// Korean KR, + + /// Kurdish KU, + + /// Latin LA, + + /// Lithuanian LT, + + /// Macedonian MK, + + /// Dutch NL, + + /// Norwegian NO, + + /// Polish PL, + + /// Portuguese PT, + + /// Portuguese (Brazil) PT_BR, + + /// Romanian RO, + + /// Russian RU, + + /// Northern Sami SE, + + /// Slovak SK, + + /// Slovenian SL, + + /// Spanish (note: 'SP' used instead of ISO 'ES') SP, + + /// Albanian SQ, + + /// Serbian SR, + + /// Swedish SV, + + /// Thai TH, + + /// Turkish TR, + + /// Ukrainian (note: 'UA' used instead of ISO 'UK') UA, + + /// Ukrainian UK, + + /// Vietnamese VI, + + /// Chinese (Simplified) ZH_CN, + + /// Chinese (Traditional) ZH_TW, + + /// Zulu ZU } } diff --git a/src/OpenWeatherMapSharp/Models/Enums/Unit.cs b/src/OpenWeatherMapSharp/Models/Enums/Unit.cs index d643020..e0ae237 100644 --- a/src/OpenWeatherMapSharp/Models/Enums/Unit.cs +++ b/src/OpenWeatherMapSharp/Models/Enums/Unit.cs @@ -1,12 +1,31 @@ namespace OpenWeatherMapSharp.Models.Enums { /// - /// Supported units + /// Represents the unit system to be used + /// for temperature, wind speed, and other + /// measurements. These options correspond + /// to the units supported by the + /// OpenWeatherMap API. /// public enum Unit { + /// + /// Standard units: temperature in Kelvin, + /// wind speed in meter/sec. + /// This is the default if no unit is specified. + /// Standard, + + /// + /// Imperial units: temperature in Fahrenheit, + /// wind speed in miles/hour. + /// Imperial, + + /// + /// Metric units: temperature in Celsius, + /// wind speed in meter/sec. + /// Metric } } diff --git a/src/OpenWeatherMapSharp/Models/ForecastItem.cs b/src/OpenWeatherMapSharp/Models/ForecastItem.cs index 9dd3527..f498a57 100644 --- a/src/OpenWeatherMapSharp/Models/ForecastItem.cs +++ b/src/OpenWeatherMapSharp/Models/ForecastItem.cs @@ -6,74 +6,81 @@ namespace OpenWeatherMapSharp.Models { /// - /// A Forcast Item + /// Represents a single forecast entry + /// containing weather data for a + /// specific point in time. /// public class ForecastItem { /// - /// Main weather information + /// Main weather parameters such as + /// temperature, pressure, and humidity. /// [JsonPropertyName("main")] public Main MainWeather { get; set; } /// - /// List of weather infos + /// A list of weather conditions + /// (e.g., rain, clouds, clear sky). /// [JsonPropertyName("weather")] public List WeatherInfos { get; set; } /// - /// Cloud information + /// Cloud coverage information. /// [JsonPropertyName("clouds")] public Clouds Clouds { get; set; } /// - /// Wind information + /// Wind speed and direction information. /// [JsonPropertyName("wind")] public Wind Wind { get; set; } /// - /// Average visibility, metres + /// Average visibility in meters. /// [JsonPropertyName("visibility")] public double Visibility { get; set; } /// - /// Probability of precipitation + /// Probability of precipitation (0.0–1.0). /// [JsonPropertyName("pop")] public double Probability { get; set; } /// - /// Rain information + /// Rain volume information. /// [JsonPropertyName("rain")] public Volume Rain { get; set; } /// - /// Snow information + /// Snow volume information. /// [JsonPropertyName("snow")] public Volume Snow { get; set; } /// - /// City information + /// City details associated with the forecast + /// (if applicable). /// [JsonPropertyName("city")] public City City { get; set; } /// - /// Date, Unix, UTC + /// Forecast time as Unix timestamp (UTC). /// [JsonPropertyName("dt")] public long DateUnix { get; set; } /// - /// Date, DateTime + /// Forecast time as a + /// UTC . /// [JsonIgnore] - public DateTime Date => DateUnix.ToDateTime(); + public DateTime Date + => DateUnix.ToDateTime(); } } diff --git a/src/OpenWeatherMapSharp/Models/ForecastRoot.cs b/src/OpenWeatherMapSharp/Models/ForecastRoot.cs index b7641e9..944a08f 100644 --- a/src/OpenWeatherMapSharp/Models/ForecastRoot.cs +++ b/src/OpenWeatherMapSharp/Models/ForecastRoot.cs @@ -4,36 +4,42 @@ namespace OpenWeatherMapSharp.Models { /// - /// The Forcast Root object + /// Represents the root object of a + /// weather forecast API response. /// public class ForecastRoot { /// - /// Internal parameter + /// Response status code + /// (e.g., "200" for success). /// [JsonPropertyName("cod")] public string Code { get; set; } /// - /// Internal parameter + /// Internal message or status value + /// (usage may vary by API version). /// [JsonPropertyName("message")] public double Message { get; set; } /// - /// A number of timestamps returned in the API response + /// Number of forecast entries returned + /// in the response. /// [JsonPropertyName("cnt")] public int Count { get; set; } /// - /// List of forecast items + /// List of forecast items for + /// different timestamps. /// [JsonPropertyName("list")] public List Items { get; set; } /// - /// City information + /// Information about the city to which + /// the forecast applies. /// [JsonPropertyName("city")] public City City { get; set; } diff --git a/src/OpenWeatherMapSharp/Models/GeocodeInfo.cs b/src/OpenWeatherMapSharp/Models/GeocodeInfo.cs index 8328f04..6e83a46 100644 --- a/src/OpenWeatherMapSharp/Models/GeocodeInfo.cs +++ b/src/OpenWeatherMapSharp/Models/GeocodeInfo.cs @@ -3,40 +3,51 @@ namespace OpenWeatherMapSharp.Models { + /// + /// Represents a geocoded location with name, + /// coordinates, and optional metadata such + /// as country and state. + /// public class GeocodeInfo { /// - /// Name of the found location + /// Name of the found location. /// [JsonPropertyName("name")] public string Name { get; set; } /// - /// Name of the found location in dufferent languages + /// Localized names of the location in + /// different languages, keyed by + /// language code (e.g., "en", "de"). /// [JsonPropertyName("local_names")] public Dictionary LocalNames { get; set; } /// - /// Geographical coordinates of the found location: Latitude + /// Latitude of the location + /// in decimal degrees. /// [JsonPropertyName("lat")] public double Latitude { get; set; } /// - /// Geographical coordinates of the found location: Longitude + /// Longitude of the location + /// in decimal degrees. /// [JsonPropertyName("lon")] public double Longitude { get; set; } /// - /// Country of the found location + /// Country code of the location + /// (ISO 3166 format, e.g., "US", "DE"). /// [JsonPropertyName("country")] public string Country { get; set; } /// - /// State of the found location, where available + /// Optional state or administrative + /// region of the location, if available. /// [JsonPropertyName("state")] public string State { get; set; } diff --git a/src/OpenWeatherMapSharp/Models/GeocodeZipInfo.cs b/src/OpenWeatherMapSharp/Models/GeocodeZipInfo.cs index f927a05..36e9d47 100644 --- a/src/OpenWeatherMapSharp/Models/GeocodeZipInfo.cs +++ b/src/OpenWeatherMapSharp/Models/GeocodeZipInfo.cs @@ -2,34 +2,43 @@ namespace OpenWeatherMapSharp.Models { + /// + /// Represents geolocation data + /// returned from a ZIP/postal code + /// geocoding request. + /// public class GeocodeZipInfo { /// - /// Specified zip/post code in the API request + /// The ZIP or postal code specified + /// in the API request. /// [JsonPropertyName("zip")] public string ZipCode { get; set; } /// - /// Name of the found area + /// The name of the found area or locality. /// [JsonPropertyName("name")] public string Name { get; set; } /// - /// Geographical coordinates of the found location: Latitude + /// Latitude of the found location in + /// decimal degrees. /// [JsonPropertyName("lat")] - public double Latiude { get; set; } + public double Latitude { get; set; } /// - /// Geographical coordinates of the found location: Longitude + /// Longitude of the found location + /// in decimal degrees. /// [JsonPropertyName("lon")] public double Longitude { get; set; } /// - /// Country of the found zip/post code + /// Country code of the location + /// (ISO 3166 format, e.g., "US", "DE"). /// [JsonPropertyName("country")] public string Country { get; set; } diff --git a/src/OpenWeatherMapSharp/Models/Main.cs b/src/OpenWeatherMapSharp/Models/Main.cs index 14317ec..db85c4d 100644 --- a/src/OpenWeatherMapSharp/Models/Main.cs +++ b/src/OpenWeatherMapSharp/Models/Main.cs @@ -3,60 +3,72 @@ namespace OpenWeatherMapSharp.Models { /// - /// Main information + /// Represents the main weather data, + /// including temperature, pressure, and humidity. /// public class Main { /// - /// Temperature. - /// Unit Default: Kelvin, Metric: Celcius, Imperial: Fahrenheit + /// Current temperature. + /// Units — Default: Kelvin, + /// Metric: Celsius, + /// Imperial: Fahrenheit. /// [JsonPropertyName("temp")] public double Temperature { get; set; } /// - /// Temperature. This temperature parameter accounts for the human perception of weather. - /// Unit Default: Kelvin, Metric: Celcius, Imperial: Fahrenheit + /// Perceived temperature accounting + /// for human perception. + /// Units — Default: Kelvin, + /// Metric: Celsius, + /// Imperial: Fahrenheit. /// [JsonPropertyName("feels_like")] public double FeelsLikeTemperature { get; set; } /// - /// Atmospheric pressure of the sea level, hPa + /// Atmospheric pressure at sea level, in hPa. /// [JsonPropertyName("pressure")] public double Pressure { get; set; } /// - /// Humidity, % + /// Humidity percentage (0–100). /// [JsonPropertyName("humidity")] public double Humidity { get; set; } /// - /// Minimum temperature at the moment. - /// This is minimal currently observed temperature (within large megalopolises and urban areas). - /// Unit Default: Kelvin, Metric: Celsius, Imperial: Fahrenheit + /// Minimum observed temperature + /// at the moment (typically within urban areas). + /// Units — Default: Kelvin, + /// Metric: Celsius, + /// Imperial: Fahrenheit. /// [JsonPropertyName("temp_min")] public double MinTemperature { get; set; } /// - /// Maximum temperature at the moment. - /// This is maximal currently observed temperature (within large megalopolises and urban areas). - /// Unit Default: Kelvin, Metric: Celsius, Imperial: Fahrenheit + /// Maximum observed temperature + /// at the moment (typically within urban areas). + /// Units — Default: Kelvin, + /// Metric: Celsius, + /// Imperial: Fahrenheit. /// [JsonPropertyName("temp_max")] public double MaxTemperature { get; set; } /// - /// Atmospheric pressure of the sea level, hPa + /// Atmospheric pressure at sea level, + /// in hPa. /// [JsonPropertyName("sea_level")] public double SeaLevelPressure { get; set; } /// - /// Atmospheric pressure of the ground level, hPa + /// Atmospheric pressure at ground level, + /// in hPa. /// [JsonPropertyName("grnd_level")] public double GroundLevelPressure { get; set; } diff --git a/src/OpenWeatherMapSharp/Models/OpenWeatherMapServiceResponse.cs b/src/OpenWeatherMapSharp/Models/OpenWeatherMapServiceResponse.cs index 598c62d..c364048 100644 --- a/src/OpenWeatherMapSharp/Models/OpenWeatherMapServiceResponse.cs +++ b/src/OpenWeatherMapSharp/Models/OpenWeatherMapServiceResponse.cs @@ -1,23 +1,32 @@ namespace OpenWeatherMapSharp.Models { /// - /// Wrapper for the OpenWeatherMapService response + /// Generic wrapper for responses returned + /// by the OpenWeatherMapService. + /// Encapsulates success status, + /// response data, and potential error messages. /// - /// - public class OpenWeatherMapServiceResponse where T : class + /// + /// The type of the response object. + /// + public class OpenWeatherMapServiceResponse + where T : class { /// - /// Indicates if the request was successful + /// Indicates whether the request + /// was successful. /// public bool IsSuccess { get; set; } /// - /// Contains the provided response object + /// The response object returned by + /// the service if the request was successful. /// public T Response { get; set; } /// - /// Error information + /// Error message or details + /// if the request failed. /// public string Error { get; set; } } diff --git a/src/OpenWeatherMapSharp/Models/System.cs b/src/OpenWeatherMapSharp/Models/System.cs index f119763..c7c539a 100644 --- a/src/OpenWeatherMapSharp/Models/System.cs +++ b/src/OpenWeatherMapSharp/Models/System.cs @@ -5,50 +5,56 @@ namespace OpenWeatherMapSharp.Models { /// - /// System information + /// Contains system-related metadata + /// from the OpenWeatherMap API response. /// public class System { /// - /// Internal parameter + /// Internal parameter used by the API. /// [JsonPropertyName("type")] public int Type { get; set; } /// - /// Internal parameter + /// Internal system identifier. /// [JsonPropertyName("id")] public int Id { get; set; } /// - /// Country code (GB, JP etc.) + /// Country code (ISO 3166 format), + /// e.g., "GB", "JP". /// [JsonPropertyName("country")] public string Country { get; set; } /// - /// Sunrise time, unix, UTC + /// Sunrise time as a Unix timestamp (UTC). /// [JsonPropertyName("sunrise")] public long SunriseUnix { get; set; } /// - /// Sunrise time, DateTime + /// Sunrise time converted to a + /// UTC . /// [JsonIgnore] - public DateTime Sunrise => SunriseUnix.ToDateTime(); + public DateTime Sunrise + => SunriseUnix.ToDateTime(); /// - /// Sunset time, unix, UTC + /// Sunset time as a Unix timestamp (UTC). /// [JsonPropertyName("sunset")] public long SunsetUnix { get; set; } /// - /// Sunset time, DateTime + /// Sunset time converted to a + /// UTC . /// [JsonIgnore] - public DateTime Sunset => SunsetUnix.ToDateTime(); + public DateTime Sunset + => SunsetUnix.ToDateTime(); } } diff --git a/src/OpenWeatherMapSharp/Models/Volume.cs b/src/OpenWeatherMapSharp/Models/Volume.cs index 2c304e9..9832972 100644 --- a/src/OpenWeatherMapSharp/Models/Volume.cs +++ b/src/OpenWeatherMapSharp/Models/Volume.cs @@ -3,12 +3,14 @@ namespace OpenWeatherMapSharp.Models { /// - /// Volume information + /// Represents precipitation volume over + /// a specific time period. /// public class Volume { /// - /// Volume for the last 1 hour, mm. + /// Precipitation volume for the last + /// 1 hour, in millimeters. /// [JsonPropertyName("1h")] public double OneHour { get; set; } diff --git a/src/OpenWeatherMapSharp/Models/WeatherInfo.cs b/src/OpenWeatherMapSharp/Models/WeatherInfo.cs index 7c352da..a45ad40 100644 --- a/src/OpenWeatherMapSharp/Models/WeatherInfo.cs +++ b/src/OpenWeatherMapSharp/Models/WeatherInfo.cs @@ -3,30 +3,34 @@ namespace OpenWeatherMapSharp.Models { /// - /// Weather information + /// Represents detailed weather condition information. /// public class WeatherInfo { /// - /// Weather condition id + /// Weather condition ID as + /// defined by OpenWeatherMap. /// [JsonPropertyName("id")] public int Id { get; set; } /// - /// Group of weather parameters (Rain, Snow, Clouds etc.) + /// Broad category of weather + /// conditions (e.g., Rain, Snow, Clouds). /// [JsonPropertyName("main")] public string Main { get; set; } /// - /// Weather coniditon with the group + /// More detailed description of + /// the weather condition. /// [JsonPropertyName("description")] public string Description { get; set; } /// - /// Weather icon id + /// Weather icon ID that can be used + /// to retrieve the corresponding icon. /// [JsonPropertyName("icon")] public string Icon { get; set; } diff --git a/src/OpenWeatherMapSharp/Models/WeatherRoot.cs b/src/OpenWeatherMapSharp/Models/WeatherRoot.cs index 5170a69..516489a 100644 --- a/src/OpenWeatherMapSharp/Models/WeatherRoot.cs +++ b/src/OpenWeatherMapSharp/Models/WeatherRoot.cs @@ -5,127 +5,138 @@ namespace OpenWeatherMapSharp.Models { + /// + /// Represents the root object of the + /// current weather data response from + /// the OpenWeatherMap API. + /// public class WeatherRoot { /// - /// Coordinates information + /// Geographic coordinates of the location. /// [JsonPropertyName("coord")] public Coordinates Coordinates { get; set; } /// - /// List of weather infos + /// List of weather conditions. /// [JsonPropertyName("weather")] public List WeatherInfos { get; set; } /// - /// Internal parameter + /// Internal parameter (usually "stations"). /// [JsonPropertyName("base")] public string Base { get; set; } = string.Empty; /// - /// Main weather information + /// Main weather parameters such as + /// temperature, humidity, and pressure. /// [JsonPropertyName("main")] public Main MainWeather { get; set; } /// - /// Average visibility, metres + /// Visibility in meters. /// [JsonPropertyName("visibility")] public double Visibility { get; set; } = 0; /// - /// Wind information + /// Wind data including speed and direction. /// [JsonPropertyName("wind")] public Wind Wind { get; set; } /// - /// Clouds information + /// Cloud coverage data. /// [JsonPropertyName("clouds")] public Clouds Clouds { get; set; } /// - /// Rain information + /// Rain volume data. /// [JsonPropertyName("rain")] public Volume Rain { get; set; } /// - /// Snow information + /// Snow volume data. /// [JsonPropertyName("snow")] public Volume Snow { get; set; } /// - /// Date, Unix, UTC + /// Timestamp of the weather data (Unix, UTC). /// [JsonPropertyName("dt")] public long DateUnix { get; set; } /// - /// Date, DateTime + /// Timestamp of the weather data as a + /// UTC . /// [JsonIgnore] public DateTime Date => DateUnix.ToDateTime(); /// - /// System information + /// System data such as sunrise, sunset, + /// and country code. /// [JsonPropertyName("sys")] public System System { get; set; } /// - /// Shift in seconds from UTC + /// Shift in seconds from UTC. /// [JsonPropertyName("timezone")] public int Timezone { get; set; } /// - /// City ID + /// City ID. /// [JsonPropertyName("id")] public int CityId { get; set; } /// - /// City name + /// City name. /// [JsonPropertyName("name")] public string Name { get; set; } /// - /// Internal parameter + /// Response status code. /// [JsonPropertyName("cod")] public int Code { get; set; } /// - /// Icon url + /// Weather icon URL (default size). /// [JsonIgnore] - public string Icon => $"https://openweathermap.org/img/w/{WeatherInfos?[0]?.Icon}.png"; + public string Icon + => $"https://openweathermap.org/img/w/{WeatherInfos?[0]?.Icon}.png"; /// - /// Icon url (2x) + /// Weather icon URL (2x resolution). /// [JsonIgnore] - public string Icon2x => $"https://openweathermap.org/img/w/{WeatherInfos?[0]?.Icon}@2x.png"; + public string Icon2x + => $"https://openweathermap.org/img/w/{WeatherInfos?[0]?.Icon}@2x.png"; /// - /// Icon url (4x) + /// Weather icon URL (4x resolution). /// [JsonIgnore] - public string Icon4x => $"https://openweathermap.org/img/w/{WeatherInfos?[0]?.Icon}@4x.png"; - + public string Icon4x + => $"https://openweathermap.org/img/w/{WeatherInfos?[0]?.Icon}@4x.png"; /// - /// Icon name + /// Weather icon name. /// [JsonIgnore] - public string IconName => $"{WeatherInfos?[0]?.Icon}"; + public string IconName + => WeatherInfos?[0]?.Icon; } } diff --git a/src/OpenWeatherMapSharp/Models/Wind.cs b/src/OpenWeatherMapSharp/Models/Wind.cs index 24a5630..4c733ee 100644 --- a/src/OpenWeatherMapSharp/Models/Wind.cs +++ b/src/OpenWeatherMapSharp/Models/Wind.cs @@ -3,26 +3,30 @@ namespace OpenWeatherMapSharp.Models { /// - /// Wind information + /// Represents wind-related weather data, + /// including speed, direction, and gusts. /// public class Wind { /// - /// Wind speed - /// Unit Default: meter/sec, Metric: meter/sec, Imperial: miles/hour + /// Wind speed. + /// Units — Default & Metric: + /// meters/second, Imperial: miles/hour. /// [JsonPropertyName("speed")] public double Speed { get; set; } /// - /// Wind direction, degrees (meteorological) + /// Wind direction in + /// meteorological degrees (0°–360°). /// [JsonPropertyName("deg")] public double Degrees { get; set; } /// - /// Wind gust - /// Unit Default: meter/sec, Metric: meter/sec, Imperial: miles/hour + /// Wind gust speed. + /// Units — Default & Metric: + /// meters/second, Imperial: miles/hour. /// [JsonPropertyName("gust")] public double Gust { get; set; } diff --git a/src/OpenWeatherMapSharp/OpenWeatherMapService.cs b/src/OpenWeatherMapSharp/OpenWeatherMapService.cs index 10f0264..463ba19 100644 --- a/src/OpenWeatherMapSharp/OpenWeatherMapService.cs +++ b/src/OpenWeatherMapSharp/OpenWeatherMapService.cs @@ -8,16 +8,17 @@ namespace OpenWeatherMapSharp { /// - /// This class is used to communicate with the Open Weather Map API + /// Provides access to the OpenWeatherMap API + /// for retrieving weather and geolocation data. /// public class OpenWeatherMapService : IOpenWeatherMapService { private readonly string _apiKey; /// - /// Create an instance of the OpenWeatherMapService. + /// Initializes a new instance of the class with the specified API key. /// - /// + /// Your OpenWeatherMap API key. public OpenWeatherMapService(string apiKey) { _apiKey = apiKey; @@ -30,29 +31,48 @@ public async Task> GetWeatherAsync( LanguageCode language = LanguageCode.EN, Unit unit = Unit.Standard) { - string url = string.Format(Statics.WeatherCoordinatesUri, latitude, longitude, unit.ToString().ToLower(), language.ToString().ToLower(), _apiKey); + string url = string.Format( + Statics.WeatherCoordinatesUri, + latitude, + longitude, + unit.ToString("G").ToLowerInvariant(), + language.ToString("G").ToLowerInvariant(), + _apiKey); + return await HttpService.GetDataAsync(url); } /// - [Obsolete("Please note that API requests by city name, zip-codes and city id have been deprecated. Although they are still available for use, bug fixing and updates are no longer available for this functionality.")] + [Obsolete("API requests by city name, ZIP code, and city ID are deprecated. They are still supported, but no longer maintained or updated.")] public async Task> GetWeatherAsync( string city, LanguageCode language = LanguageCode.EN, Unit unit = Unit.Standard) { - string url = string.Format(Statics.WeatherCityUri, city, unit.ToString().ToLower(), language.ToString().ToLower(), _apiKey); + string url = string.Format( + Statics.WeatherCityUri, + city, + unit.ToString("G").ToLowerInvariant(), + language.ToString("G").ToLowerInvariant(), + _apiKey); + return await HttpService.GetDataAsync(url); } /// - [Obsolete("Please note that API requests by city name, zip-codes and city id have been deprecated. Although they are still available for use, bug fixing and updates are no longer available for this functionality.")] + [Obsolete("API requests by city name, ZIP code, and city ID are deprecated. They are still supported, but no longer maintained or updated.")] public async Task> GetWeatherAsync( int cityId, LanguageCode language = LanguageCode.EN, Unit unit = Unit.Standard) { - string url = string.Format(Statics.WeatherIdUri, cityId, unit.ToString().ToLower(), language.ToString().ToLower(), _apiKey); + string url = string.Format( + Statics.WeatherIdUri, + cityId, + unit.ToString("G").ToLowerInvariant(), + language.ToString("G").ToLowerInvariant(), + _apiKey); + return await HttpService.GetDataAsync(url); } @@ -63,29 +83,48 @@ public async Task> GetForecastAsync( LanguageCode language = LanguageCode.EN, Unit unit = Unit.Standard) { - string url = string.Format(Statics.ForecastCoordinatesUri, latitude, longitude, unit.ToString().ToLower(), language.ToString().ToLower(), _apiKey); + string url = string.Format( + Statics.ForecastCoordinatesUri, + latitude, + longitude, + unit.ToString("G").ToLowerInvariant(), + language.ToString("G").ToLowerInvariant(), + _apiKey); + return await HttpService.GetDataAsync(url); } /// - [Obsolete("Please note that API requests by city name, zip-codes and city id have been deprecated. Although they are still available for use, bug fixing and updates are no longer available for this functionality.")] + [Obsolete("API requests by city name, ZIP code, and city ID are deprecated. They are still supported, but no longer maintained or updated.")] public async Task> GetForecastAsync( string city, LanguageCode language = LanguageCode.EN, Unit unit = Unit.Standard) { - string url = string.Format(Statics.ForecastCityUri, city, unit.ToString().ToLower(), language.ToString().ToLower(), _apiKey); + string url = string.Format( + Statics.ForecastCityUri, + city, + unit.ToString("G").ToLowerInvariant(), + language.ToString("G").ToLowerInvariant(), + _apiKey); + return await HttpService.GetDataAsync(url); } /// - [Obsolete("Please note that API requests by city name, zip-codes and city id have been deprecated. Although they are still available for use, bug fixing and updates are no longer available for this functionality.")] + [Obsolete("API requests by city name, ZIP code, and city ID are deprecated. They are still supported, but no longer maintained or updated.")] public async Task> GetForecastAsync( int id, LanguageCode language = LanguageCode.EN, Unit unit = Unit.Standard) { - string url = string.Format(Statics.ForecastIdUri, id, unit.ToString().ToLower(), language.ToString().ToLower(), _apiKey); + string url = string.Format( + Statics.ForecastIdUri, + id, + unit.ToString("G").ToLowerInvariant(), + language.ToString("G").ToLowerInvariant(), + _apiKey); + return await HttpService.GetDataAsync(url); } diff --git a/src/OpenWeatherMapSharp/Utils/HttpService.cs b/src/OpenWeatherMapSharp/Utils/HttpService.cs index 816b5c0..cc4c797 100644 --- a/src/OpenWeatherMapSharp/Utils/HttpService.cs +++ b/src/OpenWeatherMapSharp/Utils/HttpService.cs @@ -6,6 +6,11 @@ namespace OpenWeatherMapSharp.Utils { + /// + /// Provides internal HTTP functionality + /// for retrieving and deserializing + /// OpenWeatherMap API responses. + /// internal static class HttpService { private static readonly JsonSerializerOptions defaultJsonSerializerOptions = new JsonSerializerOptions @@ -13,6 +18,21 @@ internal static class HttpService PropertyNameCaseInsensitive = true }; + /// + /// Sends an HTTP GET request to the + /// specified URL and deserializes the + /// JSON response into the specified class. + /// + /// + /// The type to deserialize the response into. + /// + /// + /// The URL to send the request to. + /// + /// A task representing the asynchronous operation. + /// The result contains a wrapped response with + /// either data or an error message. + /// internal static async Task> GetDataAsync(string url) where TClass : class { using (HttpClient client = new HttpClient()) @@ -23,7 +43,11 @@ internal static async Task> GetDataAsync + { + IsSuccess = false, + Error = "Empty response received from server." + }; } return new OpenWeatherMapServiceResponse diff --git a/src/OpenWeatherMapSharp/Utils/LongExtensions.cs b/src/OpenWeatherMapSharp/Utils/LongExtensions.cs index 2f4d33c..75becf7 100644 --- a/src/OpenWeatherMapSharp/Utils/LongExtensions.cs +++ b/src/OpenWeatherMapSharp/Utils/LongExtensions.cs @@ -2,13 +2,27 @@ namespace OpenWeatherMapSharp.Utils { + /// + /// Provides extension methods for + /// converting Unix timestamps to . + /// internal static class LongExtensions { + /// + /// Converts a Unix timestamp + /// (seconds since 1970-01-01 UTC) + /// to a local . + /// + /// + /// The Unix timestamp to convert. + /// + /// A object representing + /// the local time. + /// internal static DateTime ToDateTime(this long unixTimeStamp) { - DateTime dateTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); - dateTime = dateTime.AddSeconds(unixTimeStamp).ToLocalTime(); - return dateTime; + var dateTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); + return dateTime.AddSeconds(unixTimeStamp).ToLocalTime(); } } } diff --git a/src/OpenWeatherMapSharp/Utils/Statics.cs b/src/OpenWeatherMapSharp/Utils/Statics.cs index bf7c671..32129ef 100644 --- a/src/OpenWeatherMapSharp/Utils/Statics.cs +++ b/src/OpenWeatherMapSharp/Utils/Statics.cs @@ -1,37 +1,84 @@ namespace OpenWeatherMapSharp.Utils { + /// + /// Contains static URI templates for accessing various OpenWeatherMap API endpoints. + /// internal static class Statics { - private static readonly string BaseUri = "https://api.openweathermap.org"; - private static readonly string WeatherBaseUri = $"{BaseUri}/data/2.5/weather"; - private static readonly string ForecastBaseUri = $"{BaseUri}/data/2.5/forecast"; - private static readonly string GeocodeBaseUri = $"{BaseUri}/geo/1.0"; + private static readonly string BaseUri + = "https://api.openweathermap.org"; - public static readonly string WeatherCoordinatesUri + private static readonly string WeatherBaseUri + = $"{BaseUri}/data/2.5/weather"; + + private static readonly string ForecastBaseUri + = $"{BaseUri}/data/2.5/forecast"; + + private static readonly string GeocodeBaseUri + = $"{BaseUri}/geo/1.0"; + + + /// + /// Weather by geographic coordinates (latitude, longitude). + /// Format: lat={0}&lon={1}&units={2}&lang={3}&appid={4} + /// + public static readonly string WeatherCoordinatesUri = WeatherBaseUri + "?lat={0}&lon={1}&units={2}&lang={3}&appid={4}"; - public static readonly string WeatherCityUri + /// + /// Weather by city name. + /// Format: q={0}&units={1}&lang={2}&appid={3} + /// + public static readonly string WeatherCityUri = WeatherBaseUri + "?q={0}&units={1}&lang={2}&appid={3}"; - public static readonly string WeatherIdUri + /// + /// Weather by city ID. + /// Format: id={0}&units={1}&lang={2}&appid={3} + /// + public static readonly string WeatherIdUri = WeatherBaseUri + "?id={0}&units={1}&lang={2}&appid={3}"; - public static readonly string ForecastCoordinatesUri + /// + /// Forecast by geographic coordinates. + /// Format: lat={0}&lon={1}&units={2}&lang={3}&appid={4} + /// + public static readonly string ForecastCoordinatesUri = ForecastBaseUri + "?lat={0}&lon={1}&units={2}&lang={3}&appid={4}"; - - public static readonly string ForecastCityUri + + /// + /// Forecast by city name. + /// Format: q={0}&units={1}&lang={2}&appid={3} + /// + public static readonly string ForecastCityUri = ForecastBaseUri + "?q={0}&units={1}&lang={2}&appid={3}"; - public static readonly string ForecastIdUri + /// + /// Forecast by city ID. + /// Format: id={0}&units={1}&lang={2}&appid={3} + /// + public static readonly string ForecastIdUri = ForecastBaseUri + "?id={0}&units={1}&lang={2}&appid={3}"; - public static readonly string GeocodeLocationUri + /// + /// Geocoding by location name. + /// Format: q={0}&limit={1}&appid={2} + /// + public static readonly string GeocodeLocationUri = GeocodeBaseUri + "/direct?q={0}&limit={1}&appid={2}"; - public static readonly string GeocodeZipUri + /// + /// Geocoding by ZIP/postal code. + /// Format: zip={0}&appid={1} + /// + public static readonly string GeocodeZipUri = GeocodeBaseUri + "/zip?zip={0}&appid={1}"; - public static readonly string GeocodeReverseUri + /// + /// Reverse geocoding by geographic coordinates. + /// Format: lat={0}&lon={1}&limit={2}&appid={3} + /// + public static readonly string GeocodeReverseUri = GeocodeBaseUri + "/reverse?lat={0}&lon={1}&limit={2}&appid={3}"; } } From a2481a4110edfcbaf8e651e72f8b0c60c41912f4 Mon Sep 17 00:00:00 2001 From: Sebastian Jensen Date: Tue, 22 Jul 2025 20:08:32 +0200 Subject: [PATCH 08/10] Expand and refactor OpenWeatherMapService unit tests Improved test naming for clarity and consistency, added new tests for language and unit options, invalid city and coordinates, and constructor behavior. Refactored existing tests for readability and updated assertions to use correct argument order. These changes enhance coverage and maintainability of the integration tests for OpenWeatherMapService. --- .../OpenWeatherMapServiceTests.cs | 219 +++++++++++------- 1 file changed, 137 insertions(+), 82 deletions(-) diff --git a/tests/OpenWeatherMapSharp.UnitTests/OpenWeatherMapServiceTests.cs b/tests/OpenWeatherMapSharp.UnitTests/OpenWeatherMapServiceTests.cs index 67223d2..43f22d4 100644 --- a/tests/OpenWeatherMapSharp.UnitTests/OpenWeatherMapServiceTests.cs +++ b/tests/OpenWeatherMapSharp.UnitTests/OpenWeatherMapServiceTests.cs @@ -1,213 +1,268 @@ using OpenWeatherMapSharp.Models; +using OpenWeatherMapSharp.Models.Enums; using Xunit; namespace OpenWeatherMapSharp.UnitTests; +/// +/// Integration tests for the OpenWeatherMapService using actual API requests. +/// public class OpenWeatherMapServiceTests { private const string OPENWEATHERMAPAPIKEY = "OWMAPIKEY"; - [Fact] public async Task BasicInvalidCredentials() { - // arrange - OpenWeatherMapService service = new("apikey"); + // Arrange + OpenWeatherMapService service = new("invalid_api_key"); string cityName = "Pforzheim"; - // act - OpenWeatherMapServiceResponse serviceResponse = await service.GetWeatherAsync(cityName); + // Act + OpenWeatherMapServiceResponse serviceResponse + = await service.GetWeatherAsync(cityName); - // assert + // Assert Assert.NotNull(serviceResponse); Assert.False(serviceResponse.IsSuccess); Assert.NotNull(serviceResponse.Error); } - [Fact] - public async Task GetWeatherLocationTest() + public async Task GetWeatherByCoordinates_ShouldReturnCorrectLocation() { - // arrange + // Arrange OpenWeatherMapService service = new(OPENWEATHERMAPAPIKEY); double latitude = 48.89; double longitude = 8.69; - // act - OpenWeatherMapServiceResponse serviceResponse = await service.GetWeatherAsync(latitude, longitude); + // Act + OpenWeatherMapServiceResponse serviceResponse + = await service.GetWeatherAsync(latitude, longitude); - // assert + // Assert Assert.NotNull(serviceResponse); Assert.NotNull(serviceResponse.Response); Assert.Null(serviceResponse.Error); Assert.True(serviceResponse.IsSuccess); - Assert.Equal(serviceResponse.Response.Coordinates.Latitude, latitude); - Assert.Equal(serviceResponse.Response.Coordinates.Longitude, longitude); + Assert.Equal(latitude, serviceResponse.Response.Coordinates.Latitude); + Assert.Equal(longitude, serviceResponse.Response.Coordinates.Longitude); } [Fact] - public async Task GetWeatherCityNameTest() + public async Task GetWeatherByCityName_ShouldReturnCorrectCity() { - // arrange + // Arrange OpenWeatherMapService service = new(OPENWEATHERMAPAPIKEY); string cityName = "Pforzheim"; - // act - OpenWeatherMapServiceResponse serviceResponse = await service.GetWeatherAsync(cityName); + // Act + OpenWeatherMapServiceResponse serviceResponse + = await service.GetWeatherAsync(cityName); - // assert + // Assert Assert.NotNull(serviceResponse); Assert.NotNull(serviceResponse.Response); Assert.Null(serviceResponse.Error); Assert.True(serviceResponse.IsSuccess); - Assert.Equal(serviceResponse.Response.Name, cityName); + Assert.Equal(cityName, serviceResponse.Response.Name); } [Fact] - public async Task GetWeatherCityIdTest() + public async Task GetWeatherByCityId_ShouldReturnCorrectCity() { - // arrange + // Arrange OpenWeatherMapService service = new(OPENWEATHERMAPAPIKEY); int cityId = 2928810; string cityName = "Essen"; - // act - OpenWeatherMapServiceResponse serviceResponse = await service.GetWeatherAsync(cityId); + // Act + OpenWeatherMapServiceResponse serviceResponse + = await service.GetWeatherAsync(cityId); - // assert + // Assert Assert.NotNull(serviceResponse); Assert.NotNull(serviceResponse.Response); Assert.Null(serviceResponse.Error); Assert.True(serviceResponse.IsSuccess); - Assert.Equal(serviceResponse.Response.Name, cityName); + Assert.Equal(cityName, serviceResponse.Response.Name); } - [Fact] - public async Task GetForecastLocationTest() + public async Task GetForecastByCoordinates_ShouldReturnCorrectLocation() { - // arrange + // Arrange OpenWeatherMapService service = new(OPENWEATHERMAPAPIKEY); double latitude = 48.89; double longitude = 8.69; - // act - OpenWeatherMapServiceResponse serviceResponse = await service.GetForecastAsync(latitude, longitude); + // Act + OpenWeatherMapServiceResponse serviceResponse + = await service.GetForecastAsync(latitude, longitude); - // assert + // Assert Assert.NotNull(serviceResponse); Assert.NotNull(serviceResponse.Response); Assert.Null(serviceResponse.Error); Assert.True(serviceResponse.IsSuccess); - Assert.Equal(serviceResponse.Response.City.Coordinates.Latitude, latitude); - Assert.Equal(serviceResponse.Response.City.Coordinates.Longitude, longitude); + Assert.Equal(latitude, serviceResponse.Response.City.Coordinates.Latitude); + Assert.Equal(longitude, serviceResponse.Response.City.Coordinates.Longitude); } [Fact] - public async Task GetForecastCityNameTest() + public async Task GetForecastByCityName_ShouldReturnCorrectCity() { - // arrange + // Arrange OpenWeatherMapService service = new(OPENWEATHERMAPAPIKEY); string cityName = "Pforzheim"; - // act - OpenWeatherMapServiceResponse serviceResponse = await service.GetForecastAsync(cityName); + // Act + OpenWeatherMapServiceResponse serviceResponse + = await service.GetForecastAsync(cityName); - // assert + // Assert Assert.NotNull(serviceResponse); Assert.NotNull(serviceResponse.Response); Assert.Null(serviceResponse.Error); Assert.True(serviceResponse.IsSuccess); - Assert.Equal(serviceResponse.Response.City.Name, cityName); + Assert.Equal(cityName, serviceResponse.Response.City.Name); } [Fact] - public async Task GetForecastCityIdTest() + public async Task GetForecastByCityId_ShouldReturnCorrectCity() { - // arrange + // Arrange OpenWeatherMapService service = new(OPENWEATHERMAPAPIKEY); int cityId = 2928810; string cityName = "Essen"; - // act - OpenWeatherMapServiceResponse serviceResponse = await service.GetForecastAsync(cityId); + // Act + OpenWeatherMapServiceResponse serviceResponse + = await service.GetForecastAsync(cityId); - // assert + // Assert Assert.NotNull(serviceResponse); Assert.NotNull(serviceResponse.Response); Assert.Null(serviceResponse.Error); Assert.True(serviceResponse.IsSuccess); - Assert.Equal(serviceResponse.Response.City.Name, cityName); + Assert.Equal(cityName, serviceResponse.Response.City.Name); } - [Fact] - public async Task GetGeolocationFromNameTest() + public async Task GetGeolocationByName_ShouldReturnCorrectLocation() { - // arrange + // Arrange OpenWeatherMapService service = new(OPENWEATHERMAPAPIKEY); string name = "Pforzheim"; string country = "DE"; - // act - OpenWeatherMapServiceResponse> serviceResponse = await service.GetLocationByNameAsync(name); + // Act + OpenWeatherMapServiceResponse> serviceResponse + = await service.GetLocationByNameAsync(name); - // assert + // Assert Assert.NotNull(serviceResponse); Assert.NotNull(serviceResponse.Response); Assert.Null(serviceResponse.Error); Assert.True(serviceResponse.IsSuccess); - GeocodeInfo? firstCountry = serviceResponse.Response.FirstOrDefault(); - Assert.NotNull(firstCountry); - Assert.Equal(name, firstCountry.Name); - Assert.Equal(country, firstCountry.Country); + var firstResult = serviceResponse.Response.FirstOrDefault(); + Assert.NotNull(firstResult); + Assert.Equal(name, firstResult.Name); + Assert.Equal(country, firstResult.Country); } [Fact] - public async Task GetGeolocationFromZipTest() + public async Task GetGeolocationByCoordinates_ShouldReturnCorrectLocation() { - // arrange + // Arrange OpenWeatherMapService service = new(OPENWEATHERMAPAPIKEY); - string zipCode = "75173"; + double latitude = 48.89; + double longitude = 8.69; string cityName = "Pforzheim"; string country = "DE"; - // act - OpenWeatherMapServiceResponse serviceResponse = await service.GetLocationByZipAsync($"{zipCode},{country}"); + // Act + OpenWeatherMapServiceResponse> serviceResponse + = await service.GetLocationByLatLonAsync(latitude, longitude); - // assert + // Assert Assert.NotNull(serviceResponse); Assert.NotNull(serviceResponse.Response); Assert.Null(serviceResponse.Error); Assert.True(serviceResponse.IsSuccess); - Assert.Equal(zipCode, serviceResponse.Response.ZipCode); - Assert.Equal(cityName, serviceResponse.Response.Name); - Assert.Equal(country, serviceResponse.Response.Country); + + var firstResult = serviceResponse.Response.FirstOrDefault(); + Assert.NotNull(firstResult); + Assert.Equal(cityName, firstResult.Name); + Assert.Equal(country, firstResult.Country); } [Fact] - public async Task GetGeolocationFromLatLonTest() + public void Constructor_WithEmptyApiKey_ShouldNotThrowButLikelyFailAtRuntime() { - // arrange + // Arrange + OpenWeatherMapService service = new(""); + + // Act + + // Assert + Assert.NotNull(service); + } + + [Theory] + [InlineData(LanguageCode.EN, Unit.Standard)] + [InlineData(LanguageCode.DE, Unit.Metric)] + [InlineData(LanguageCode.FR, Unit.Imperial)] + public async Task GetWeather_WithDifferentLanguagesAndUnits_ShouldSucceed(LanguageCode lang, Unit unit) + { + // Arrange OpenWeatherMapService service = new(OPENWEATHERMAPAPIKEY); - double latitude = 48.89; - double longitude = 8.69; - string cityName = "Pforzheim"; - string country = "DE"; - // act - OpenWeatherMapServiceResponse> serviceResponse = await service.GetLocationByLatLonAsync(latitude, longitude); + // Act + OpenWeatherMapServiceResponse response + = await service.GetWeatherAsync(48.89, 8.69, lang, unit); - // assert - Assert.NotNull(serviceResponse); - Assert.NotNull(serviceResponse.Response); - Assert.Null(serviceResponse.Error); - Assert.True(serviceResponse.IsSuccess); + // Assert + Assert.NotNull(response); + Assert.True(response.IsSuccess); + Assert.NotNull(response.Response); + Assert.Null(response.Error); + } + + [Fact] + public async Task GetGeolocation_WithInvalidCity_ShouldReturnEmptyResult() + { + // Arrange + OpenWeatherMapService service = new(OPENWEATHERMAPAPIKEY); + + // Act + OpenWeatherMapServiceResponse> response + = await service.GetLocationByNameAsync("NowherevilleXYZ"); + + // Assert + Assert.NotNull(response); + Assert.True(response.IsSuccess); + Assert.NotNull(response.Response); + Assert.Empty(response.Response); + } + + [Theory] + [InlineData(-999, 10)] + [InlineData(91, 10)] + [InlineData(10, -200)] + public async Task GetWeather_WithInvalidCoordinates_ShouldFail(double lat, double lon) + { + // Arrange + OpenWeatherMapService service = new(OPENWEATHERMAPAPIKEY); + + // Act + OpenWeatherMapServiceResponse response + = await service.GetWeatherAsync(lat, lon); - GeocodeInfo? firstCountry = serviceResponse.Response.FirstOrDefault(); - Assert.NotNull(firstCountry); - Assert.Equal(cityName, firstCountry.Name); - Assert.Equal(country, firstCountry.Country); + // Assert + Assert.NotNull(response); + Assert.False(response.IsSuccess); + Assert.NotNull(response.Error); } } \ No newline at end of file From 1a4672732b883b0e384aca335f0c700cd02f043a Mon Sep 17 00:00:00 2001 From: Sebastian Jensen Date: Tue, 22 Jul 2025 20:08:48 +0200 Subject: [PATCH 09/10] Refactor console app for improved readability Reorganized the OpenWeatherMapSharp.Console Program.cs for clearer structure and readability. Improved variable naming, grouped related logic, enhanced error handling messages, and simplified panel creation for location and weather display. --- .../OpenWeatherMapSharp.Console/Program.cs | 117 +++++++++--------- 1 file changed, 56 insertions(+), 61 deletions(-) diff --git a/samples/OpenWeatherMapSharp.Console/Program.cs b/samples/OpenWeatherMapSharp.Console/Program.cs index 4c89098..d756613 100644 --- a/samples/OpenWeatherMapSharp.Console/Program.cs +++ b/samples/OpenWeatherMapSharp.Console/Program.cs @@ -3,92 +3,87 @@ using OpenWeatherMapSharp.Models.Enums; using Spectre.Console; +// Your OpenWeatherMap API key (replace with your actual one) string openWeatherMapApiKey = "OWMAPIKEY"; -// HEADER -Grid headerGrid = new(); +// == HEADER == +var headerGrid = new Grid(); headerGrid.AddColumn(); headerGrid.AddRow(new FigletText("OpenWeatherMap").Centered().Color(Color.Red)); AnsiConsole.Write(headerGrid); -// ASK FOR CITY NAME +// == ASK FOR CITY == AnsiConsole.WriteLine(); string cityName = AnsiConsole.Ask("[white]Insert the name of the[/] [red]city[/][white]?[/]"); AnsiConsole.WriteLine(); -// GET WEATHER -OpenWeatherMapService openWeatherMapService = new(openWeatherMapApiKey); +// == INIT SERVICE == +var openWeatherMapService = new OpenWeatherMapService(openWeatherMapApiKey); + +// == GEOCODE == var geolocationResponse = await openWeatherMapService.GetLocationByNameAsync(cityName); -if (!geolocationResponse.IsSuccess) +if (!geolocationResponse.IsSuccess || geolocationResponse.Response?.FirstOrDefault() is not GeocodeInfo geolocation) { - AnsiConsole.Write("Unfortunately I can't recognize the city. Please try again."); - Console.WriteLine(); + AnsiConsole.MarkupLine("[bold red]Unfortunately I can't recognize the city. Please try again.[/]"); return; } -var geolocations = geolocationResponse.Response; -var geolocation = geolocations.FirstOrDefault(); +// == WEATHER == +var weatherResponse = await openWeatherMapService.GetWeatherAsync( + geolocation.Latitude, + geolocation.Longitude, + unit: Unit.Metric +); -if (geolocation is null) +if (!weatherResponse.IsSuccess || weatherResponse.Response is not WeatherRoot weatherRoot) { - AnsiConsole.Write("Unfortunately I can't recognize the city. Please try again."); - Console.WriteLine(); + AnsiConsole.MarkupLine("[bold red]Unfortunately I can't retrieve weather data. Please try again.[/]"); return; } -OpenWeatherMapServiceResponse weatherResponse = await openWeatherMapService.GetWeatherAsync(geolocation.Latitude, geolocation.Longitude, unit: Unit.Metric); +var mainWeather = weatherRoot.MainWeather; -if (weatherResponse.IsSuccess) +// == LOCATION PANEL == +var locationPanel = new Panel(new Rows(new List +{ + new($"[red]City: [/]{weatherRoot.Name}"), + new($"[red]Latitude: [/]{weatherRoot.Coordinates.Latitude:0.0000}"), + new($"[red]Longitude: [/]{weatherRoot.Coordinates.Longitude:0.0000}"), + new($"[red]Country: [/]{geolocation.Country}"), + new($"[red]State: [/]{geolocation.State ?? "[not available]"}") +})) { - WeatherRoot weatherRoot = weatherResponse.Response; - Main mainWeather = weatherRoot.MainWeather; + Header = new PanelHeader("Location"), + Width = 120 +}; +AnsiConsole.Write(locationPanel); - // LOCATION - List locationMarkupList = new() - { - new Markup($"[red]City: [/]{weatherRoot.Name}"), - new Markup($"[red]Latitude: [/]{weatherRoot.Coordinates.Latitude:0.0000}"), - new Markup($"[red]Longitude: [/]{weatherRoot.Coordinates.Longitude:0.0000}"), - new Markup($"[red]Country: [/]{geolocation.Country}"), - new Markup($"[red]State: [/]{geolocation.State}") - }; - Rows locationRows = new(locationMarkupList); - Panel locationPanel = new(locationRows) - { - Header = new PanelHeader("Location"), - Width = 120 - }; - AnsiConsole.Write(locationPanel); +// == WEATHER PANEL == +var weatherMarkupList = new List +{ + new($"[red]Temperature: [/]{mainWeather.Temperature}° C"), + new($"[red]Feels Like: [/]{mainWeather.FeelsLikeTemperature}° C"), + new($"[red]Min Temperature: [/]{mainWeather.MinTemperature}° C"), + new($"[red]Max Temperature: [/]{mainWeather.MaxTemperature}° C"), + new("-----"), + new($"[red]Pressure (Sea Level): [/]{mainWeather.Pressure} hPa"), + new($"[red]Humidity: [/]{mainWeather.Humidity} %"), + new($"[red]Sunrise: [/]{weatherRoot.System.Sunrise:g}"), + new($"[red]Sunset: [/]{weatherRoot.System.Sunset:g}") +}; - // WEATHER - List weatherMarkupList = new() - { - new Markup($"[red]Temperature: [/]{mainWeather.Temperature}° C"), - new Markup($"[red]Temperature (Feels Like): [/]{mainWeather.FeelsLikeTemperature}° C"), - new Markup($"[red]Minimal Temperature: [/]{mainWeather.MinTemperature}° C"), - new Markup($"[red]Maximal Temperature: [/]{mainWeather.MaxTemperature}° C"), - new Markup("-----"), - new Markup($"[red]Pressure Sea Level hPa: [/]{mainWeather.Pressure} hPa"), - new Markup($"[red]Humidity: [/]{mainWeather.Humidity} %"), - new Markup($"[red]Sunrise: [/]{weatherRoot.System.Sunrise:g}"), - new Markup($"[red]Sunset: [/]{weatherRoot.System.Sunset:g}") - }; - foreach (WeatherInfo weatherInfo in weatherRoot.WeatherInfos) - { - weatherMarkupList.Add(new Markup($"[red]Current Conditions: [/]{weatherInfo.Description}")); - } - Rows currentWeatherRows = new(weatherMarkupList); - Panel currentWeatherPanel = new(currentWeatherRows) - { - Header = new PanelHeader("Current Weather"), - Width = 120 - }; - AnsiConsole.Write(currentWeatherPanel); -} -else +// Add weather descriptions +foreach (var weatherInfo in weatherRoot.WeatherInfos) { - AnsiConsole.Write("Unfortunately I can't recognize the city. Please try again."); + weatherMarkupList.Add(new($"[red]Conditions: [/]{weatherInfo.Description}")); } -Console.ReadLine(); \ No newline at end of file +var weatherPanel = new Panel(new Rows(weatherMarkupList)) +{ + Header = new PanelHeader("Current Weather"), + Width = 120 +}; +AnsiConsole.Write(weatherPanel); + +Console.ReadLine(); From 4ff38e0caa4e0b5c78093cb9ef0449da770cb226 Mon Sep 17 00:00:00 2001 From: Sebastian Jensen Date: Tue, 22 Jul 2025 20:11:06 +0200 Subject: [PATCH 10/10] Refactor variable declarations to use explicit types Updated variable declarations in Program.cs, LongExtensions.cs, and OpenWeatherMapServiceTests.cs to use explicit types and object initializers for improved readability and consistency. No functional changes were made. --- .../OpenWeatherMapSharp.Console/Program.cs | 31 ++++++++++--------- .../Utils/LongExtensions.cs | 2 +- .../OpenWeatherMapServiceTests.cs | 4 +-- 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/samples/OpenWeatherMapSharp.Console/Program.cs b/samples/OpenWeatherMapSharp.Console/Program.cs index d756613..62001ac 100644 --- a/samples/OpenWeatherMapSharp.Console/Program.cs +++ b/samples/OpenWeatherMapSharp.Console/Program.cs @@ -7,7 +7,7 @@ string openWeatherMapApiKey = "OWMAPIKEY"; // == HEADER == -var headerGrid = new Grid(); +Grid headerGrid = new(); headerGrid.AddColumn(); headerGrid.AddRow(new FigletText("OpenWeatherMap").Centered().Color(Color.Red)); AnsiConsole.Write(headerGrid); @@ -18,10 +18,11 @@ AnsiConsole.WriteLine(); // == INIT SERVICE == -var openWeatherMapService = new OpenWeatherMapService(openWeatherMapApiKey); +OpenWeatherMapService openWeatherMapService = new(openWeatherMapApiKey); // == GEOCODE == -var geolocationResponse = await openWeatherMapService.GetLocationByNameAsync(cityName); +OpenWeatherMapServiceResponse> geolocationResponse + = await openWeatherMapService.GetLocationByNameAsync(cityName); if (!geolocationResponse.IsSuccess || geolocationResponse.Response?.FirstOrDefault() is not GeocodeInfo geolocation) { @@ -30,11 +31,11 @@ } // == WEATHER == -var weatherResponse = await openWeatherMapService.GetWeatherAsync( - geolocation.Latitude, - geolocation.Longitude, - unit: Unit.Metric -); +OpenWeatherMapServiceResponse weatherResponse + = await openWeatherMapService.GetWeatherAsync( + geolocation.Latitude, + geolocation.Longitude, + unit: Unit.Metric); if (!weatherResponse.IsSuccess || weatherResponse.Response is not WeatherRoot weatherRoot) { @@ -42,10 +43,10 @@ return; } -var mainWeather = weatherRoot.MainWeather; +Main mainWeather = weatherRoot.MainWeather; // == LOCATION PANEL == -var locationPanel = new Panel(new Rows(new List +Panel locationPanel = new(new Rows(new List { new($"[red]City: [/]{weatherRoot.Name}"), new($"[red]Latitude: [/]{weatherRoot.Coordinates.Latitude:0.0000}"), @@ -60,8 +61,8 @@ AnsiConsole.Write(locationPanel); // == WEATHER PANEL == -var weatherMarkupList = new List -{ +List weatherMarkupList = +[ new($"[red]Temperature: [/]{mainWeather.Temperature}° C"), new($"[red]Feels Like: [/]{mainWeather.FeelsLikeTemperature}° C"), new($"[red]Min Temperature: [/]{mainWeather.MinTemperature}° C"), @@ -71,15 +72,15 @@ new($"[red]Humidity: [/]{mainWeather.Humidity} %"), new($"[red]Sunrise: [/]{weatherRoot.System.Sunrise:g}"), new($"[red]Sunset: [/]{weatherRoot.System.Sunset:g}") -}; +]; // Add weather descriptions -foreach (var weatherInfo in weatherRoot.WeatherInfos) +foreach (WeatherInfo? weatherInfo in weatherRoot.WeatherInfos) { weatherMarkupList.Add(new($"[red]Conditions: [/]{weatherInfo.Description}")); } -var weatherPanel = new Panel(new Rows(weatherMarkupList)) +Panel weatherPanel = new(new Rows(weatherMarkupList)) { Header = new PanelHeader("Current Weather"), Width = 120 diff --git a/src/OpenWeatherMapSharp/Utils/LongExtensions.cs b/src/OpenWeatherMapSharp/Utils/LongExtensions.cs index 75becf7..df55bee 100644 --- a/src/OpenWeatherMapSharp/Utils/LongExtensions.cs +++ b/src/OpenWeatherMapSharp/Utils/LongExtensions.cs @@ -21,7 +21,7 @@ internal static class LongExtensions /// internal static DateTime ToDateTime(this long unixTimeStamp) { - var dateTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); + DateTime dateTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); return dateTime.AddSeconds(unixTimeStamp).ToLocalTime(); } } diff --git a/tests/OpenWeatherMapSharp.UnitTests/OpenWeatherMapServiceTests.cs b/tests/OpenWeatherMapSharp.UnitTests/OpenWeatherMapServiceTests.cs index 43f22d4..a751675 100644 --- a/tests/OpenWeatherMapSharp.UnitTests/OpenWeatherMapServiceTests.cs +++ b/tests/OpenWeatherMapSharp.UnitTests/OpenWeatherMapServiceTests.cs @@ -166,7 +166,7 @@ OpenWeatherMapServiceResponse> serviceResponse Assert.Null(serviceResponse.Error); Assert.True(serviceResponse.IsSuccess); - var firstResult = serviceResponse.Response.FirstOrDefault(); + GeocodeInfo? firstResult = serviceResponse.Response.FirstOrDefault(); Assert.NotNull(firstResult); Assert.Equal(name, firstResult.Name); Assert.Equal(country, firstResult.Country); @@ -192,7 +192,7 @@ OpenWeatherMapServiceResponse> serviceResponse Assert.Null(serviceResponse.Error); Assert.True(serviceResponse.IsSuccess); - var firstResult = serviceResponse.Response.FirstOrDefault(); + GeocodeInfo? firstResult = serviceResponse.Response.FirstOrDefault(); Assert.NotNull(firstResult); Assert.Equal(cityName, firstResult.Name); Assert.Equal(country, firstResult.Country);