diff --git a/CHANGELOG.md b/CHANGELOG.md
index cc93198..3040bab 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,12 +2,23 @@
All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org/) for commit guidelines.
-## 1.1.0
+## [1.2.0](https://github.com/JustinCanton/Geo.NET/compare/1.1.1...1.2.0) (2022-08-20)
+### Features
+- adding extra information and logging surrounding exceptions ([#44](https://github.com/JustinCanton/Geo.NET/pull/44)) ([7b5b154](https://github.com/JustinCanton/Geo.NET/commit/7b5b15441181bda16b0a644e2b3ef8e7b06cc074))
+
+### Bug Fixes
+- fixing an issue where the query is not properly encoded, and fixing an issue where the query string building is using an outdated method ([#46](https://github.com/JustinCanton/Geo.NET/pull/46)) ([5b55f4d](https://github.com/JustinCanton/Geo.NET/commit/5b55f4d249a617e4667e92b5cb0b2c9b6b02ec6f))
+
+## [1.1.1](https://github.com/JustinCanton/Geo.NET/compare/1.1.0...1.1.1) (2022-07-21)
+### Bug Fixes
+- fixing a vulnerability in newtonsoft nuget ([#39](https://github.com/JustinCanton/Geo.NET/pull/39)) ([fa192ca](https://github.com/JustinCanton/Geo.NET/commit/fa192cab2a965503aa5a50885010836461cb822b))
+
+## [1.1.0](https://github.com/JustinCanton/Geo.NET/compare/1.0.0...1.1.0) (2022-06-04)
### Features
- Adding score into the HERE Geocoding response ([#33](https://github.com/JustinCanton/Geo.NET/pull/33)) ([c8f42e1](https://github.com/JustinCanton/Geo.NET/commit/c8f42e1f155da17dd3869f304c3b9e36a938da71))
- Adding net6.0 support ([#34](https://github.com/JustinCanton/Geo.NET/pull/34)) ([e8eebca](https://github.com/JustinCanton/Geo.NET/commit/e8eebca37d82e3659e7c5e6e2ea4f4777f45f4f7))
-## 1.0.0
+## 1.0.0 (2021-01-10)
### Features
- Adding support for ArcGIS Suggest API
- Adding support for ArcGIS Address Candidate API
diff --git a/README.md b/README.md
index 7b65bea..db27805 100644
--- a/README.md
+++ b/README.md
@@ -49,23 +49,7 @@ The configuration and sample usage for each supported interface can be found wit
- [MapQuest](https://github.com/JustinCanton/Geo.NET/tree/master/src/Geo.MapQuest)
-## Roadmap
-
-### 1.1.0
-|Status|Goal|
-|:--:|--|
-|✅|Adding score into the HERE Geocoding response|
-|✅|Adding net6.0 support|
-|✅|Adding ChangeLog|
-
-
-### 1.2.0
-|Status|Goal|
-|:--:|--|
-|❌|Adding support for conversion from coordinates to flexible polylines rather than requiring polylines as input for the HERE API ([#36](https://github.com/JustinCanton/Geo.NET/issues/36))|
-
-
-### Suggestions or Discussion Points
+## Suggestions or Discussion Points
If you would like to weigh in on any suggestion, please make a Github Issue and we can discuss further.
✅ = Accepted
diff --git a/src/Geo.ArcGIS/Models/Exceptions/ArcGISException.cs b/src/Geo.ArcGIS/Models/Exceptions/ArcGISException.cs
index 382b626..ad08971 100644
--- a/src/Geo.ArcGIS/Models/Exceptions/ArcGISException.cs
+++ b/src/Geo.ArcGIS/Models/Exceptions/ArcGISException.cs
@@ -9,6 +9,7 @@ namespace Geo.ArcGIS.Models.Exceptions
using System.Globalization;
using System.Net.Http;
using System.Threading.Tasks;
+ using Geo.Core.Models.Exceptions;
using Newtonsoft.Json;
///
@@ -23,7 +24,7 @@ namespace Geo.ArcGIS.Models.Exceptions
/// Thrown when the ArcGIS request is cancelled.
/// Thrown when an error occurs while reading the return JSON text.
/// Thrown when when an error occurs during JSON deserialization.
- public class ArcGISException : Exception
+ public sealed class ArcGISException : GeoCoreException
{
private const string DefaultMessage = "{0} See the inner exception for more information.";
diff --git a/src/Geo.ArcGIS/Services/ArcGISGeocoding.cs b/src/Geo.ArcGIS/Services/ArcGISGeocoding.cs
index 71332a6..980862f 100644
--- a/src/Geo.ArcGIS/Services/ArcGISGeocoding.cs
+++ b/src/Geo.ArcGIS/Services/ArcGISGeocoding.cs
@@ -7,14 +7,12 @@ namespace Geo.ArcGIS.Services
{
using System;
using System.Collections.Generic;
- using System.Collections.Specialized;
using System.Configuration;
using System.Globalization;
using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
- using System.Web;
using Geo.ArcGIS.Abstractions;
using Geo.ArcGIS.Enums;
using Geo.ArcGIS.Models.Exceptions;
@@ -22,6 +20,7 @@ namespace Geo.ArcGIS.Services
using Geo.ArcGIS.Models.Responses;
using Geo.Core;
using Geo.Core.Extensions;
+ using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
@@ -47,18 +46,20 @@ public class ArcGISGeocoding : ClientExecutor, IArcGISGeocoding
///
/// A used for making calls to the ArcGIS system.
/// A used for retreiving the ArcGIS token.
- /// A used to create a localizer for localizing log or exception messages.
- /// A used for logging information.
+ /// An used to provide exceptions based on an exception type.
+ /// An used to create a localizer for localizing log or exception messages.
+ /// An used to create a logger used for logging information.
public ArcGISGeocoding(
HttpClient client,
IArcGISTokenContainer tokenContainer,
+ IGeoNETExceptionProvider exceptionProvider,
IStringLocalizerFactory localizerFactory,
- ILogger logger = null)
- : base(client, localizerFactory)
+ ILoggerFactory loggerFactory = null)
+ : base(client, exceptionProvider, localizerFactory, loggerFactory)
{
_tokenContainer = tokenContainer ?? throw new ArgumentNullException(nameof(tokenContainer));
_localizer = localizerFactory?.Create(typeof(ArcGISGeocoding)) ?? throw new ArgumentNullException(nameof(localizerFactory));
- _logger = logger ?? NullLogger.Instance;
+ _logger = loggerFactory?.CreateLogger() ?? NullLogger.Instance;
}
///
@@ -159,15 +160,15 @@ internal async Task BuildAddressCandidateRequest(AddressCandidateParameters
}
var uriBuilder = new UriBuilder(CandidatesUri);
- var query = HttpUtility.ParseQueryString(uriBuilder.Query);
- query.Add("f", "json");
- query.Add("outFields", "Match_addr,Addr_type");
+ var query = QueryString.Empty;
+ query = query.Add("f", "json");
+ query = query.Add("outFields", "Match_addr,Addr_type");
- query.Add("singleLine", parameters.SingleLineAddress);
+ query = query.Add("singleLine", parameters.SingleLineAddress);
- AddStorageParameter(parameters, query);
+ AddStorageParameter(parameters, ref query);
- await AddArcGISToken(query, cancellationToken).ConfigureAwait(false);
+ query = await AddArcGISToken(query, cancellationToken).ConfigureAwait(false);
uriBuilder.Query = query.ToString();
@@ -183,13 +184,13 @@ internal async Task BuildAddressCandidateRequest(AddressCandidateParameters
internal async Task BuildPlaceCandidateRequest(PlaceCandidateParameters parameters, CancellationToken cancellationToken)
{
var uriBuilder = new UriBuilder(CandidatesUri);
- var query = HttpUtility.ParseQueryString(uriBuilder.Query);
- query.Add("f", "json");
- query.Add("outFields", "Place_addr,PlaceName");
+ var query = QueryString.Empty;
+ query = query.Add("f", "json");
+ query = query.Add("outFields", "Place_addr,PlaceName");
if (!string.IsNullOrWhiteSpace(parameters.Category))
{
- query.Add("category", parameters.Category);
+ query = query.Add("category", parameters.Category);
}
else
{
@@ -198,7 +199,7 @@ internal async Task BuildPlaceCandidateRequest(PlaceCandidateParameters par
if (parameters.Location != null)
{
- query.Add("location", parameters.Location.ToString());
+ query = query.Add("location", parameters.Location.ToString());
}
else
{
@@ -207,16 +208,16 @@ internal async Task BuildPlaceCandidateRequest(PlaceCandidateParameters par
if (parameters.MaximumLocations > 0 && parameters.MaximumLocations <= 50)
{
- query.Add("maxLocations", parameters.MaximumLocations.ToString(CultureInfo.InvariantCulture));
+ query = query.Add("maxLocations", parameters.MaximumLocations.ToString(CultureInfo.InvariantCulture));
}
else
{
_logger.ArcGISWarning(_localizer["Invalid Maximum Locations"]);
}
- AddStorageParameter(parameters, query);
+ AddStorageParameter(parameters, ref query);
- await AddArcGISToken(query, cancellationToken).ConfigureAwait(false);
+ query = await AddArcGISToken(query, cancellationToken).ConfigureAwait(false);
uriBuilder.Query = query.ToString();
@@ -232,8 +233,8 @@ internal async Task BuildPlaceCandidateRequest(PlaceCandidateParameters par
internal Task BuildSuggestRequest(SuggestParameters parameters, CancellationToken cancellationToken)
{
var uriBuilder = new UriBuilder(SuggestUri);
- var query = HttpUtility.ParseQueryString(uriBuilder.Query);
- query.Add("f", "json");
+ var query = QueryString.Empty;
+ query = query.Add("f", "json");
if (string.IsNullOrWhiteSpace(parameters.Text))
{
@@ -242,11 +243,11 @@ internal Task BuildSuggestRequest(SuggestParameters parameters, Cancellatio
throw new ArgumentException(error, nameof(parameters.Text));
}
- query.Add("text", parameters.Text);
+ query = query.Add("text", parameters.Text);
if (parameters.Location != null)
{
- query.Add("location", parameters.Location.ToString());
+ query = query.Add("location", parameters.Location.ToString());
}
else
{
@@ -255,7 +256,7 @@ internal Task BuildSuggestRequest(SuggestParameters parameters, Cancellatio
if (!string.IsNullOrWhiteSpace(parameters.Category))
{
- query.Add("category", parameters.Category);
+ query = query.Add("category", parameters.Category);
}
else
{
@@ -264,7 +265,7 @@ internal Task BuildSuggestRequest(SuggestParameters parameters, Cancellatio
if (parameters.SearchExtent != null)
{
- query.Add("searchExtent", parameters.SearchExtent.ToString());
+ query = query.Add("searchExtent", parameters.SearchExtent.ToString());
}
else
{
@@ -273,7 +274,7 @@ internal Task BuildSuggestRequest(SuggestParameters parameters, Cancellatio
if (parameters.MaximumLocations > 0 && parameters.MaximumLocations < 16)
{
- query.Add("maxLocations", parameters.MaximumLocations.ToString(CultureInfo.InvariantCulture));
+ query = query.Add("maxLocations", parameters.MaximumLocations.ToString(CultureInfo.InvariantCulture));
}
else
{
@@ -294,8 +295,8 @@ internal Task BuildSuggestRequest(SuggestParameters parameters, Cancellatio
internal async Task BuildReverseGeocodingRequest(ReverseGeocodingParameters parameters, CancellationToken cancellationToken)
{
var uriBuilder = new UriBuilder(ReverseGeocodingUri);
- var query = HttpUtility.ParseQueryString(uriBuilder.Query);
- query.Add("f", "json");
+ var query = QueryString.Empty;
+ query = query.Add("f", "json");
if (parameters.Location is null)
{
@@ -304,11 +305,11 @@ internal async Task BuildReverseGeocodingRequest(ReverseGeocodingParameters
throw new ArgumentException(error, nameof(parameters.Location));
}
- query.Add("location", parameters.Location.ToString());
+ query = query.Add("location", parameters.Location.ToString());
if (parameters.OutSpatialReference > 0)
{
- query.Add("outSR", parameters.OutSpatialReference.ToString(CultureInfo.InvariantCulture));
+ query = query.Add("outSR", parameters.OutSpatialReference.ToString(CultureInfo.InvariantCulture));
}
else
{
@@ -317,7 +318,7 @@ internal async Task BuildReverseGeocodingRequest(ReverseGeocodingParameters
if (parameters.LanguageCode != null)
{
- query.Add("langCode", parameters.LanguageCode.TwoLetterISOLanguageName);
+ query = query.Add("langCode", parameters.LanguageCode.TwoLetterISOLanguageName);
}
else
{
@@ -332,7 +333,7 @@ internal async Task BuildReverseGeocodingRequest(ReverseGeocodingParameters
featureTypesBuilder.Add(featureType.ToEnumString());
}
- query.Add("featureTypes", featureTypesBuilder.ToString());
+ query = query.Add("featureTypes", featureTypesBuilder.ToString());
}
else
{
@@ -341,7 +342,7 @@ internal async Task BuildReverseGeocodingRequest(ReverseGeocodingParameters
if (parameters.LocationType >= 0)
{
- query.Add("locationType", parameters.LocationType.ToEnumString());
+ query = query.Add("locationType", parameters.LocationType.ToEnumString());
}
else
{
@@ -350,16 +351,16 @@ internal async Task BuildReverseGeocodingRequest(ReverseGeocodingParameters
if (parameters.PreferredLabelValue >= 0)
{
- query.Add("preferredLabelValues", parameters.PreferredLabelValue.ToEnumString());
+ query = query.Add("preferredLabelValues", parameters.PreferredLabelValue.ToEnumString());
}
else
{
_logger.ArcGISWarning(_localizer["Invalid Preferred Label Value"]);
}
- AddStorageParameter(parameters, query);
+ AddStorageParameter(parameters, ref query);
- await AddArcGISToken(query, cancellationToken).ConfigureAwait(false);
+ query = await AddArcGISToken(query, cancellationToken).ConfigureAwait(false);
uriBuilder.Query = query.ToString();
@@ -375,8 +376,8 @@ internal async Task BuildReverseGeocodingRequest(ReverseGeocodingParameters
internal async Task BuildGeocodingRequest(GeocodingParameters parameters, CancellationToken cancellationToken)
{
var uriBuilder = new UriBuilder(GeocodingUri);
- var query = HttpUtility.ParseQueryString(uriBuilder.Query);
- query.Add("f", "json");
+ var query = QueryString.Empty;
+ query = query.Add("f", "json");
if (parameters.AddressAttributes is null || parameters.AddressAttributes.Count == 0)
{
@@ -400,15 +401,15 @@ internal async Task BuildGeocodingRequest(GeocodingParameters parameters, C
records = attributes,
};
- query.Add("addresses", JsonConvert.SerializeObject(addresses));
+ query = query.Add("addresses", JsonConvert.SerializeObject(addresses));
#pragma warning disable CA1308 // Normalize strings to uppercase
- query.Add("matchOutOfRange", parameters.MatchOutOfRange.ToString(CultureInfo.InvariantCulture).ToLowerInvariant());
+ query = query.Add("matchOutOfRange", parameters.MatchOutOfRange.ToString(CultureInfo.InvariantCulture).ToLowerInvariant());
#pragma warning restore CA1308 // Normalize strings to uppercase
if (!string.IsNullOrWhiteSpace(parameters.Category))
{
- query.Add("category", parameters.Category);
+ query = query.Add("category", parameters.Category);
}
else
{
@@ -417,7 +418,7 @@ internal async Task BuildGeocodingRequest(GeocodingParameters parameters, C
if (parameters.SourceCountry.Count != 0)
{
- query.Add("sourceCountry", string.Join(",", parameters.SourceCountry.Select(x => x.ThreeLetterISORegionName)));
+ query = query.Add("sourceCountry", string.Join(",", parameters.SourceCountry.Select(x => x.ThreeLetterISORegionName)));
}
else
{
@@ -426,7 +427,7 @@ internal async Task BuildGeocodingRequest(GeocodingParameters parameters, C
if (parameters.OutSpatialReference > 0)
{
- query.Add("outSR", parameters.OutSpatialReference.ToString(CultureInfo.InvariantCulture));
+ query = query.Add("outSR", parameters.OutSpatialReference.ToString(CultureInfo.InvariantCulture));
}
else
{
@@ -435,7 +436,7 @@ internal async Task BuildGeocodingRequest(GeocodingParameters parameters, C
if (parameters.SearchExtent != null)
{
- query.Add("searchExtent", parameters.SearchExtent.ToString());
+ query = query.Add("searchExtent", parameters.SearchExtent.ToString());
}
else
{
@@ -444,7 +445,7 @@ internal async Task BuildGeocodingRequest(GeocodingParameters parameters, C
if (parameters.LanguageCode != null)
{
- query.Add("langCode", parameters.LanguageCode.TwoLetterISOLanguageName);
+ query = query.Add("langCode", parameters.LanguageCode.TwoLetterISOLanguageName);
}
else
{
@@ -453,7 +454,7 @@ internal async Task BuildGeocodingRequest(GeocodingParameters parameters, C
if (parameters.LocationType >= 0)
{
- query.Add("locationType", parameters.LocationType.ToEnumString());
+ query = query.Add("locationType", parameters.LocationType.ToEnumString());
}
else
{
@@ -462,14 +463,14 @@ internal async Task BuildGeocodingRequest(GeocodingParameters parameters, C
if (parameters.PreferredLabelValue >= 0)
{
- query.Add("preferredLabelValues", parameters.PreferredLabelValue.ToEnumString());
+ query = query.Add("preferredLabelValues", parameters.PreferredLabelValue.ToEnumString());
}
else
{
_logger.ArcGISWarning(_localizer["Invalid Preferred Label Value"]);
}
- await AddArcGISToken(query, cancellationToken).ConfigureAwait(false);
+ query = await AddArcGISToken(query, cancellationToken).ConfigureAwait(false);
uriBuilder.Query = query.ToString();
@@ -480,11 +481,11 @@ internal async Task BuildGeocodingRequest(GeocodingParameters parameters, C
/// Adds the ArcGIS storage flag to the query parameters.
///
/// A with the storage information.
- /// A with the query parameters.
- internal void AddStorageParameter(StorageParameters parameters, NameValueCollection query)
+ /// A with the query parameters.
+ internal void AddStorageParameter(StorageParameters parameters, ref QueryString query)
{
#pragma warning disable CA1308 // Normalize strings to uppercase
- query.Add("forStorage", parameters.ForStorage.ToString(CultureInfo.InvariantCulture).ToLowerInvariant());
+ query = query.Add("forStorage", parameters.ForStorage.ToString(CultureInfo.InvariantCulture).ToLowerInvariant());
#pragma warning restore CA1308 // Normalize strings to uppercase
}
@@ -492,16 +493,18 @@ internal void AddStorageParameter(StorageParameters parameters, NameValueCollect
/// Adds the ArcGIS token to the query parameters.
/// If the token cannot be generated, it will not be added to the request.
///
- /// A with the query parameters.
+ /// A with the query parameters.
/// A used to cancel the request.
/// A .
- internal async Task AddArcGISToken(NameValueCollection query, CancellationToken cancellationToken)
+ internal async Task AddArcGISToken(QueryString query, CancellationToken cancellationToken)
{
var token = await _tokenContainer.GetTokenAsync(cancellationToken).ConfigureAwait(false);
if (!string.IsNullOrWhiteSpace(token))
{
- query.Add("token", token);
+ return query.Add("token", token);
}
+
+ return query;
}
}
}
diff --git a/src/Geo.Bing/Models/Exceptions/BingException.cs b/src/Geo.Bing/Models/Exceptions/BingException.cs
index 228d8f9..8d38566 100644
--- a/src/Geo.Bing/Models/Exceptions/BingException.cs
+++ b/src/Geo.Bing/Models/Exceptions/BingException.cs
@@ -9,6 +9,7 @@ namespace Geo.Bing.Models.Exceptions
using System.Globalization;
using System.Net.Http;
using System.Threading.Tasks;
+ using Geo.Core.Models.Exceptions;
using Newtonsoft.Json;
///
@@ -23,7 +24,7 @@ namespace Geo.Bing.Models.Exceptions
/// Thrown when the Bing request is cancelled.
/// Thrown when an error occurs while reading the return JSON text.
/// Thrown when when an error occurs during JSON deserialization.
- public class BingException : Exception
+ public sealed class BingException : GeoCoreException
{
private const string DefaultMessage = "{0} See the inner exception for more information.";
diff --git a/src/Geo.Bing/Services/BingGeocoding.cs b/src/Geo.Bing/Services/BingGeocoding.cs
index 9cc746e..e3d3f93 100644
--- a/src/Geo.Bing/Services/BingGeocoding.cs
+++ b/src/Geo.Bing/Services/BingGeocoding.cs
@@ -6,18 +6,17 @@
namespace Geo.Bing.Services
{
using System;
- using System.Collections.Specialized;
using System.Configuration;
using System.Globalization;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
- using System.Web;
using Geo.Bing.Abstractions;
using Geo.Bing.Models.Exceptions;
using Geo.Bing.Models.Parameters;
using Geo.Bing.Models.Responses;
using Geo.Core;
+ using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
@@ -39,18 +38,20 @@ public class BingGeocoding : ClientExecutor, IBingGeocoding
///
/// A used for placing calls to the Bing Geocoding API.
/// A used for fetching the Bing key.
- /// A used to create a localizer for localizing log or exception messages.
- /// A used for logging information.
+ /// An used to provide exceptions based on an exception type.
+ /// An used to create a localizer for localizing log or exception messages.
+ /// An used to create a logger used for logging information.
public BingGeocoding(
HttpClient client,
IBingKeyContainer keyContainer,
+ IGeoNETExceptionProvider exceptionProvider,
IStringLocalizerFactory localizerFactory,
- ILogger logger = null)
- : base(client, localizerFactory)
+ ILoggerFactory loggerFactory = null)
+ : base(client, exceptionProvider, localizerFactory, loggerFactory)
{
_keyContainer = keyContainer ?? throw new ArgumentNullException(nameof(keyContainer));
_localizer = localizerFactory?.Create(typeof(BingGeocoding)) ?? throw new ArgumentNullException(nameof(localizerFactory));
- _logger = logger ?? NullLogger.Instance;
+ _logger = loggerFactory?.CreateLogger() ?? NullLogger.Instance;
}
///
@@ -118,7 +119,7 @@ internal Uri ValidateAndBuildUri(TParameters parameters, Func 0)
{
- query.Add("includeEntityTypes", includes.ToString());
+ query = query.Add("includeEntityTypes", includes.ToString());
}
else
{
@@ -203,7 +204,7 @@ internal Uri BuildReverseGeocodingRequest(ReverseGeocodingParameters parameters)
BuildBaseQuery(parameters, ref query);
- AddBingKey(query);
+ AddBingKey(ref query);
uriBuilder.Query = query.ToString();
@@ -230,11 +231,11 @@ internal Uri BuildAddressGeocodingRequest(AddressGeocodingParameters parameters)
}
var uriBuilder = new UriBuilder(BaseUri);
- var query = HttpUtility.ParseQueryString(uriBuilder.Query);
+ var query = QueryString.Empty;
if (!string.IsNullOrWhiteSpace(parameters.AdministrationDistrict))
{
- query.Add("adminDistrict", parameters.AdministrationDistrict);
+ query = query.Add("adminDistrict", parameters.AdministrationDistrict);
}
else
{
@@ -243,7 +244,7 @@ internal Uri BuildAddressGeocodingRequest(AddressGeocodingParameters parameters)
if (!string.IsNullOrWhiteSpace(parameters.Locality))
{
- query.Add("locality", parameters.Locality);
+ query = query.Add("locality", parameters.Locality);
}
else
{
@@ -252,7 +253,7 @@ internal Uri BuildAddressGeocodingRequest(AddressGeocodingParameters parameters)
if (!string.IsNullOrWhiteSpace(parameters.PostalCode))
{
- query.Add("postalCode", parameters.PostalCode);
+ query = query.Add("postalCode", parameters.PostalCode);
}
else
{
@@ -261,7 +262,7 @@ internal Uri BuildAddressGeocodingRequest(AddressGeocodingParameters parameters)
if (!string.IsNullOrWhiteSpace(parameters.AddressLine))
{
- query.Add("addressLine", parameters.AddressLine);
+ query = query.Add("addressLine", parameters.AddressLine);
}
else
{
@@ -270,7 +271,7 @@ internal Uri BuildAddressGeocodingRequest(AddressGeocodingParameters parameters)
if (parameters.CountryRegion != null)
{
- query.Add("countryRegion", parameters.CountryRegion.TwoLetterISORegionName.ToUpperInvariant());
+ query = query.Add("countryRegion", parameters.CountryRegion.TwoLetterISORegionName.ToUpperInvariant());
}
else
{
@@ -279,7 +280,7 @@ internal Uri BuildAddressGeocodingRequest(AddressGeocodingParameters parameters)
BuildLimitedResultQuery(parameters, ref query);
- AddBingKey(query);
+ AddBingKey(ref query);
uriBuilder.Query = query.ToString();
@@ -290,12 +291,12 @@ internal Uri BuildAddressGeocodingRequest(AddressGeocodingParameters parameters)
/// Builds up the limited result query parameters.
///
/// A with the limited result parameters to build the uri with.
- /// A with the built up query parameters.
- internal void BuildLimitedResultQuery(ResultParameters parameters, ref NameValueCollection query)
+ /// A with the built up query parameters.
+ internal void BuildLimitedResultQuery(ResultParameters parameters, ref QueryString query)
{
if (parameters.MaximumResults > 0 && parameters.MaximumResults <= 20)
{
- query.Add("maxResults", parameters.MaximumResults.ToString(CultureInfo.InvariantCulture));
+ query = query.Add("maxResults", parameters.MaximumResults.ToString(CultureInfo.InvariantCulture));
}
else
{
@@ -309,12 +310,12 @@ internal void BuildLimitedResultQuery(ResultParameters parameters, ref NameValue
/// Builds up the base query parameters.
///
/// A with the base parameters to build the uri with.
- /// A with the built up query parameters.
- internal void BuildBaseQuery(BaseParameters parameters, ref NameValueCollection query)
+ /// A with the built up query parameters.
+ internal void BuildBaseQuery(BaseParameters parameters, ref QueryString query)
{
if (parameters.IncludeNeighbourhood == true)
{
- query.Add("includeNeighborhood", "1");
+ query = query.Add("includeNeighborhood", "1");
}
else
{
@@ -334,7 +335,7 @@ internal void BuildBaseQuery(BaseParameters parameters, ref NameValueCollection
if (includes.Count > 0)
{
- query.Add("include", includes.ToString());
+ query = query.Add("include", includes.ToString());
}
else
{
@@ -345,10 +346,10 @@ internal void BuildBaseQuery(BaseParameters parameters, ref NameValueCollection
///
/// Adds the Bing key to the query parameters.
///
- /// A with the query parameters.
- internal void AddBingKey(NameValueCollection query)
+ /// A with the query parameters.
+ internal void AddBingKey(ref QueryString query)
{
- query.Add("key", _keyContainer.GetKey());
+ query = query.Add("key", _keyContainer.GetKey());
}
}
}
diff --git a/src/Geo.Core/Abstractions/IGeoNETExceptionProvider.cs b/src/Geo.Core/Abstractions/IGeoNETExceptionProvider.cs
new file mode 100644
index 0000000..72768dc
--- /dev/null
+++ b/src/Geo.Core/Abstractions/IGeoNETExceptionProvider.cs
@@ -0,0 +1,26 @@
+//
+// Copyright (c) Geo.NET.
+// Licensed under the MIT license. See the LICENSE file in the solution root for full license information.
+//
+
+namespace Geo.Core
+{
+ using System;
+ using Geo.Core.Models.Exceptions;
+
+ ///
+ /// An interface used to provide an exception based on the exception type.
+ ///
+ public interface IGeoNETExceptionProvider
+ {
+ ///
+ /// Gets an exception of type .
+ ///
+ /// The type of the exception to get.
+ /// The message the exception should have.
+ /// Optional. The inner exception to include in the exception.
+ /// An exception of type .
+ TException GetException(string message, Exception innerException = null)
+ where TException : GeoCoreException;
+ }
+}
diff --git a/src/Geo.Core/ClientExecutor.cs b/src/Geo.Core/ClientExecutor.cs
index b7d4b89..89aba6c 100644
--- a/src/Geo.Core/ClientExecutor.cs
+++ b/src/Geo.Core/ClientExecutor.cs
@@ -6,13 +6,14 @@
namespace Geo.Core
{
using System;
- using System.Collections.Concurrent;
- using System.Collections.Generic;
- using System.Linq.Expressions;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
+ using Geo.Core.Models;
+ using Geo.Core.Models.Exceptions;
using Microsoft.Extensions.Localization;
+ using Microsoft.Extensions.Logging;
+ using Microsoft.Extensions.Logging.Abstractions;
using Newtonsoft.Json;
///
@@ -20,21 +21,28 @@ namespace Geo.Core
///
public class ClientExecutor
{
- private static readonly ConcurrentDictionary> _cachedExceptionDelegates = new ConcurrentDictionary>();
private readonly HttpClient _client;
+ private readonly IGeoNETExceptionProvider _exceptionProvider;
private readonly IStringLocalizer _localizer;
+ private readonly ILogger _logger;
///
/// Initializes a new instance of the class.
///
/// A used for placing calls to the APIs.
- /// A used to create a localizer for localizing log or exception messages.
+ /// An used to provide exceptions based on an exception type.
+ /// An used to create a localizer for localizing log or exception messages.
+ /// An used to create a logger used for logging information.
public ClientExecutor(
HttpClient client,
- IStringLocalizerFactory localizerFactory)
+ IGeoNETExceptionProvider exceptionProvider,
+ IStringLocalizerFactory localizerFactory,
+ ILoggerFactory loggerFactory = null)
{
_client = client ?? throw new ArgumentNullException(nameof(client));
+ _exceptionProvider = exceptionProvider ?? throw new ArgumentNullException(nameof(exceptionProvider));
_localizer = localizerFactory?.Create(typeof(ClientExecutor)) ?? throw new ArgumentNullException(nameof(localizerFactory));
+ _logger = loggerFactory?.CreateLogger() ?? NullLogger.Instance;
}
///
@@ -49,9 +57,14 @@ public ClientExecutor(
/// An exception of type thrown when any exception occurs and wraps the original exception.
public async Task CallAsync(Uri uri, string apiName, CancellationToken cancellationToken = default)
where TResult : class
- where TException : Exception
+ where TException : GeoCoreException
{
- (TResult Result, string JSON) response;
+ if (uri is null)
+ {
+ throw new ArgumentNullException(nameof(uri));
+ }
+
+ CallResult response;
try
{
@@ -59,45 +72,42 @@ public async Task CallAsync(Uri uri, string apiNam
}
catch (ArgumentNullException ex)
{
- var exceptionConstructor = GetExceptionConstructor(typeof(TException));
- throw exceptionConstructor(_localizer["Null Uri", apiName].ToString(), ex) as TException;
+ throw _exceptionProvider.GetException(_localizer["Null Uri", apiName].ToString(), ex);
}
catch (InvalidOperationException ex)
{
- var exceptionConstructor = GetExceptionConstructor(typeof(TException));
- throw exceptionConstructor(_localizer["Invalid Uri", apiName].ToString(), ex) as TException;
+ throw _exceptionProvider.GetException(_localizer["Invalid Uri", apiName].ToString(), ex);
}
catch (HttpRequestException ex)
{
- var exceptionConstructor = GetExceptionConstructor(typeof(TException));
- throw exceptionConstructor(_localizer["Request Failed", apiName].ToString(), ex) as TException;
+ throw _exceptionProvider.GetException(_localizer["Request Failed", apiName].ToString(), ex);
}
catch (TaskCanceledException ex)
{
- var exceptionConstructor = GetExceptionConstructor(typeof(TException));
- throw exceptionConstructor(_localizer["Request Cancelled", apiName].ToString(), ex) as TException;
+ throw _exceptionProvider.GetException(_localizer["Request Cancelled", apiName].ToString(), ex);
}
catch (JsonReaderException ex)
{
- var exceptionConstructor = GetExceptionConstructor(typeof(TException));
- throw exceptionConstructor(_localizer["Reader Failed To Parse", apiName].ToString(), ex) as TException;
+ throw _exceptionProvider.GetException(_localizer["Reader Failed To Parse", apiName].ToString(), ex);
}
catch (JsonSerializationException ex)
{
- var exceptionConstructor = GetExceptionConstructor(typeof(TException));
- throw exceptionConstructor(_localizer["Serializer Failed To Parse", apiName].ToString(), ex) as TException;
+ throw _exceptionProvider.GetException(_localizer["Serializer Failed To Parse", apiName].ToString(), ex);
}
catch (Exception ex)
{
- var exceptionConstructor = GetExceptionConstructor(typeof(TException));
- throw exceptionConstructor(_localizer["Request Failed Exception", apiName].ToString(), ex) as TException;
+ throw _exceptionProvider.GetException(_localizer["Request Failed Exception", apiName].ToString(), ex);
}
- if (response.Result is null || !string.IsNullOrEmpty(response.JSON))
+ if (!response.IsSuccessful)
{
- var exceptionConstructor = GetExceptionConstructor(typeof(TException));
- var ex = exceptionConstructor(_localizer["Request Failure", apiName].ToString(), null) as TException;
- ex.Data.Add("responseBody", response.JSON);
+ var ex = _exceptionProvider.GetException(_localizer["Request Failure", apiName].ToString(), null);
+ ex.Data.Add("uri", uri);
+ ex.Data.Add("responseStatusCode", response.StatusCode);
+ ex.Data.Add("responseBody", response.Body);
+
+ _logger.ApiCallFailed(uri, ex);
+
throw ex;
}
@@ -120,7 +130,7 @@ public async Task CallAsync(Uri uri, string apiNam
/// Thrown when the request is cancelled.
/// Thrown when an error occurs while reading the return JSON text.
/// Thrown when when an error occurs during JSON deserialization.
- internal async Task<(TResult Result, string JSON)> CallAsync(Uri uri, CancellationToken cancellationToken = default)
+ internal async Task> CallAsync(Uri uri, CancellationToken cancellationToken = default)
where TResult : class
{
var response = await _client.GetAsync(uri, cancellationToken).ConfigureAwait(false);
@@ -133,37 +143,10 @@ public async Task CallAsync(Uri uri, string apiNam
if (!response.IsSuccessStatusCode)
{
- return (null, json);
+ return new CallResult(response.StatusCode, json);
}
- return (JsonConvert.DeserializeObject(json), string.Empty);
- }
-
- private static Func GetExceptionConstructor(Type type)
- {
- if (type == null)
- {
- throw new ArgumentNullException(nameof(type));
- }
-
- if (_cachedExceptionDelegates.TryGetValue(type, out var cachedConstructor))
- {
- return cachedConstructor;
- }
-
- var constructorParameters = new Type[] { typeof(string), typeof(Exception) };
- var constructor = type.GetConstructor(constructorParameters);
- var parameters = new List()
- {
- Expression.Parameter(typeof(string)),
- Expression.Parameter(typeof(Exception)),
- };
-
- var expression = Expression.New(constructor, parameters);
- var lambdaExpression = Expression.Lambda>(expression, parameters);
- var function = lambdaExpression.Compile();
-
- return _cachedExceptionDelegates.AddOrUpdate(type, function, (key, value) => function);
+ return new CallResult(JsonConvert.DeserializeObject(json), response.StatusCode, json);
}
}
}
diff --git a/src/Geo.Core/DependencyInjection/ServiceCollectionExtensions.cs b/src/Geo.Core/DependencyInjection/ServiceCollectionExtensions.cs
index fd33d04..146c8b3 100644
--- a/src/Geo.Core/DependencyInjection/ServiceCollectionExtensions.cs
+++ b/src/Geo.Core/DependencyInjection/ServiceCollectionExtensions.cs
@@ -19,7 +19,9 @@ public static class ServiceCollectionExtensions
/// A with the added services.
public static IServiceCollection AddCoreServices(this IServiceCollection services)
{
- services.AddLocalization();
+ services
+ .AddLocalization()
+ .AddTransient();
return services;
}
diff --git a/src/Geo.Core/Extensions/LoggerExtensions.cs b/src/Geo.Core/Extensions/LoggerExtensions.cs
new file mode 100644
index 0000000..bdd393f
--- /dev/null
+++ b/src/Geo.Core/Extensions/LoggerExtensions.cs
@@ -0,0 +1,32 @@
+//
+// Copyright (c) Geo.NET.
+// Licensed under the MIT license. See the LICENSE file in the solution root for full license information.
+//
+
+namespace Geo.Core
+{
+ using System;
+ using Microsoft.Extensions.Logging;
+
+ ///
+ /// Extension methods for the class.
+ ///
+ internal static class LoggerExtensions
+ {
+ private static readonly Action _apiCallFailed = LoggerMessage.Define(
+ LogLevel.Error,
+ new EventId(1, nameof(ClientExecutor)),
+ "ClientExecutor: The call to the API {URI} failed.");
+
+ ///
+ /// "ClientExecutor: The call to the API {API} failed.".
+ ///
+ /// An used to log the error message.
+ /// The uri that failed.
+ /// The exception that occured.
+ public static void ApiCallFailed(this ILogger logger, Uri uri, Exception ex)
+ {
+ _apiCallFailed(logger, uri.ToString(), ex);
+ }
+ }
+}
diff --git a/src/Geo.Core/GeoNETExceptionProvider.cs b/src/Geo.Core/GeoNETExceptionProvider.cs
new file mode 100644
index 0000000..483386c
--- /dev/null
+++ b/src/Geo.Core/GeoNETExceptionProvider.cs
@@ -0,0 +1,101 @@
+//
+// Copyright (c) Geo.NET.
+// Licensed under the MIT license. See the LICENSE file in the solution root for full license information.
+//
+
+namespace Geo.Core
+{
+ using System;
+ using System.Collections.Concurrent;
+ using System.Collections.Generic;
+ using System.Linq.Expressions;
+ using Geo.Core.Models.Exceptions;
+
+ ///
+ /// Used to provide an exception based on the exception type.
+ ///
+ public sealed class GeoNETExceptionProvider : IGeoNETExceptionProvider
+ {
+ private static readonly ConcurrentDictionary> _cachedInnerExceptionDelegates = new ConcurrentDictionary>();
+ private static readonly ConcurrentDictionary> _cachedExceptionDelegates = new ConcurrentDictionary>();
+
+ ///
+ public TException GetException(string message, Exception innerException = null)
+ where TException : GeoCoreException
+ {
+ if (innerException == null)
+ {
+ var exceptionConstructor = GetExceptionConstructor(typeof(TException));
+ return exceptionConstructor(message) as TException;
+ }
+ else
+ {
+ var exceptionConstructor = GetInnerExceptionConstructor(typeof(TException));
+ return exceptionConstructor(message, innerException) as TException;
+ }
+ }
+
+ private static Func GetInnerExceptionConstructor(Type type)
+ {
+ if (type == null)
+ {
+ throw new ArgumentNullException(nameof(type));
+ }
+
+ if (_cachedInnerExceptionDelegates.TryGetValue(type, out var cachedConstructor))
+ {
+ return cachedConstructor;
+ }
+
+ var function = GetInnerExceptionDelegate(type);
+
+ return _cachedInnerExceptionDelegates.AddOrUpdate(type, function, (key, value) => function);
+ }
+
+ private static Func GetExceptionConstructor(Type type)
+ {
+ if (type == null)
+ {
+ throw new ArgumentNullException(nameof(type));
+ }
+
+ if (_cachedExceptionDelegates.TryGetValue(type, out var cachedConstructor))
+ {
+ return cachedConstructor;
+ }
+
+ var function = GetExceptionDelegate(type);
+
+ return _cachedExceptionDelegates.AddOrUpdate(type, function, (key, value) => function);
+ }
+
+ private static Func GetInnerExceptionDelegate(Type type)
+ {
+ var constructorParameters = new Type[] { typeof(string), typeof(Exception) };
+ var constructor = type.GetConstructor(constructorParameters);
+ var parameters = new List()
+ {
+ Expression.Parameter(typeof(string)),
+ Expression.Parameter(typeof(Exception)),
+ };
+
+ var expression = Expression.New(constructor, parameters);
+ var lambdaExpression = Expression.Lambda>(expression, parameters);
+ return lambdaExpression.Compile();
+ }
+
+ private static Func GetExceptionDelegate(Type type)
+ {
+ var constructorParameters = new Type[] { typeof(string) };
+ var constructor = type.GetConstructor(constructorParameters);
+ var parameters = new List()
+ {
+ Expression.Parameter(typeof(string)),
+ };
+
+ var expression = Expression.New(constructor, parameters);
+ var lambdaExpression = Expression.Lambda>(expression, parameters);
+ return lambdaExpression.Compile();
+ }
+ }
+}
diff --git a/src/Geo.Core/Models/CallResult.cs b/src/Geo.Core/Models/CallResult.cs
new file mode 100644
index 0000000..38cbdf9
--- /dev/null
+++ b/src/Geo.Core/Models/CallResult.cs
@@ -0,0 +1,64 @@
+//
+// Copyright (c) Geo.NET.
+// Licensed under the MIT license. See the LICENSE file in the solution root for full license information.
+//
+
+namespace Geo.Core.Models
+{
+ using System.Net;
+
+ ///
+ /// A results class used to store information relating to a call to an API.
+ ///
+ /// The type of the expected results.
+ internal class CallResult
+ where TResult : class
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The result from the call.
+ /// The status code of the call.
+ /// The string body of the call.
+ public CallResult(TResult result, HttpStatusCode statusCode, string body)
+ {
+ IsSuccessful = true;
+ Result = result;
+ StatusCode = statusCode;
+ Body = body;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The status code of the call.
+ /// The string body of the call.
+ public CallResult(HttpStatusCode statusCode, string body)
+ {
+ IsSuccessful = false;
+ Result = null;
+ StatusCode = statusCode;
+ Body = body;
+ }
+
+ ///
+ /// Gets a value indicating whether the call was successful or not.
+ ///
+ public bool IsSuccessful { get; }
+
+ ///
+ /// Gets the result of the call.
+ ///
+ public TResult Result { get; }
+
+ ///
+ /// Gets the status code resulting from the call.
+ ///
+ public HttpStatusCode StatusCode { get; }
+
+ ///
+ /// Gets the body of the result.
+ ///
+ public string Body { get; }
+ }
+}
diff --git a/src/Geo.Core/Models/Exceptions/GeoCoreException.cs b/src/Geo.Core/Models/Exceptions/GeoCoreException.cs
new file mode 100644
index 0000000..c95f0eb
--- /dev/null
+++ b/src/Geo.Core/Models/Exceptions/GeoCoreException.cs
@@ -0,0 +1,69 @@
+//
+// Copyright (c) Geo.NET.
+// Licensed under the MIT license. See the LICENSE file in the solution root for full license information.
+//
+
+namespace Geo.Core.Models.Exceptions
+{
+ using System;
+ using System.Collections;
+ using System.Globalization;
+
+ ///
+ /// A base exception typ eused by all derived exceptions, which overrides the ToString method to display the Data in the exception as well.
+ ///
+ public abstract class GeoCoreException : Exception
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ protected GeoCoreException()
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The message that describes the error.
+ protected GeoCoreException(string message)
+ : base(message)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The message that describes the error.
+ /// The exception that is the cause of the current exception.
+ protected GeoCoreException(string message, Exception inner)
+ : base(message, inner)
+ {
+ }
+
+ ///
+ public override string ToString()
+ {
+ return base.ToString() + GetData();
+ }
+
+ ///
+ /// Formats the information in the Data field of the exception and displays it when printing.
+ ///
+ /// A string with the formatted data.
+ private string GetData()
+ {
+ if (Data.Count == 0)
+ {
+ return string.Empty;
+ }
+
+ var data = Environment.NewLine + "Data:";
+ foreach (DictionaryEntry item in Data)
+ {
+ data += Environment.NewLine + " " + string.Format(CultureInfo.InvariantCulture, "{0}: {1}", item.Key, item.Value);
+ }
+
+ return data;
+ }
+ }
+}
diff --git a/src/Geo.Google/Models/Exceptions/GoogleException.cs b/src/Geo.Google/Models/Exceptions/GoogleException.cs
index 5cc6e86..b02e6ca 100644
--- a/src/Geo.Google/Models/Exceptions/GoogleException.cs
+++ b/src/Geo.Google/Models/Exceptions/GoogleException.cs
@@ -9,6 +9,7 @@ namespace Geo.Google.Models.Exceptions
using System.Globalization;
using System.Net.Http;
using System.Threading.Tasks;
+ using Geo.Core.Models.Exceptions;
using Newtonsoft.Json;
///
@@ -23,7 +24,7 @@ namespace Geo.Google.Models.Exceptions
/// Thrown when the Google request is cancelled.
/// Thrown when an error occurs while reading the return JSON text.
/// Thrown when when an error occurs during JSON deserialization.
- public class GoogleException : Exception
+ public sealed class GoogleException : GeoCoreException
{
private const string DefaultMessage = "{0} See the inner exception for more information.";
diff --git a/src/Geo.Google/Services/GoogleGeocoding.cs b/src/Geo.Google/Services/GoogleGeocoding.cs
index 99243a4..4afed56 100644
--- a/src/Geo.Google/Services/GoogleGeocoding.cs
+++ b/src/Geo.Google/Services/GoogleGeocoding.cs
@@ -6,14 +6,12 @@
namespace Geo.Google.Services
{
using System;
- using System.Collections.Specialized;
using System.Globalization;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
- using System.Web;
using Geo.Core;
using Geo.Core.Extensions;
using Geo.Google.Abstractions;
@@ -22,6 +20,7 @@ namespace Geo.Google.Services
using Geo.Google.Models.Exceptions;
using Geo.Google.Models.Parameters;
using Geo.Google.Models.Responses;
+ using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
@@ -49,18 +48,20 @@ public class GoogleGeocoding : ClientExecutor, IGoogleGeocoding
///
/// A used for placing calls to the Google Geocoding API.
/// A used for fetching the Google key.
- /// A used to create a localizer for localizing log or exception messages.
- /// A used for logging information.
+ /// An used to provide exceptions based on an exception type.
+ /// An used to create a localizer for localizing log or exception messages.
+ /// An used to create a logger used for logging information.
public GoogleGeocoding(
HttpClient client,
IGoogleKeyContainer keyContainer,
+ IGeoNETExceptionProvider exceptionProvider,
IStringLocalizerFactory localizerFactory,
- ILogger logger = null)
- : base(client, localizerFactory)
+ ILoggerFactory loggerFactory = null)
+ : base(client, exceptionProvider, localizerFactory, loggerFactory)
{
_keyContainer = keyContainer ?? throw new ArgumentNullException(nameof(keyContainer));
_localizer = localizerFactory?.Create(typeof(GoogleGeocoding)) ?? throw new ArgumentNullException(nameof(localizerFactory));
- _logger = logger ?? NullLogger.Instance;
+ _logger = loggerFactory?.CreateLogger() ?? NullLogger.Instance;
}
///
@@ -180,7 +181,7 @@ internal Uri ValidateAndBuildUri(TParameters parameters, Func());
}
- query.Add("result_type", resultTypesBuilder.ToString());
+ query = query.Add("result_type", resultTypesBuilder.ToString());
}
else
{
@@ -282,16 +283,16 @@ internal Uri BuildReverseGeocodingRequest(ReverseGeocodingParameters parameters)
locationTypesBuilder.Append(locationType.ToEnumString());
}
- query.Add("location_type", locationTypesBuilder.ToString());
+ query = query.Add("location_type", locationTypesBuilder.ToString());
}
else
{
_logger.GoogleDebug(_localizer["Invalid Location Types"]);
}
- AddBaseParameters(parameters, query);
+ AddBaseParameters(parameters, ref query);
- AddGoogleKey(query);
+ AddGoogleKey(ref query);
uriBuilder.Query = query.ToString();
@@ -307,7 +308,7 @@ internal Uri BuildReverseGeocodingRequest(ReverseGeocodingParameters parameters)
internal Uri BuildFindPlaceRequest(FindPlacesParameters parameters)
{
var uriBuilder = new UriBuilder(FindPlaceUri);
- var query = HttpUtility.ParseQueryString(uriBuilder.Query);
+ var query = QueryString.Empty;
if (string.IsNullOrWhiteSpace(parameters.Input))
{
@@ -323,13 +324,13 @@ internal Uri BuildFindPlaceRequest(FindPlacesParameters parameters)
throw new ArgumentException(error, nameof(parameters.InputType));
}
- query.Add("input", parameters.Input);
+ query = query.Add("input", parameters.Input);
- query.Add("inputtype", parameters.InputType.ToEnumString());
+ query = query.Add("inputtype", parameters.InputType.ToEnumString());
if (parameters.Fields != null && parameters.Fields.Count > 0)
{
- query.Add("fields", string.Join(",", parameters.Fields));
+ query = query.Add("fields", string.Join(",", parameters.Fields));
}
else
{
@@ -341,13 +342,13 @@ internal Uri BuildFindPlaceRequest(FindPlacesParameters parameters)
switch (parameters.LocationBias)
{
case Coordinate coordinate:
- query.Add("locationbias", $"point:{coordinate}");
+ query = query.Add("locationbias", $"point:{coordinate}");
break;
case Circle circle:
- query.Add("locationbias", $"circle:{circle}");
+ query = query.Add("locationbias", $"circle:{circle}");
break;
case Boundaries boundary:
- query.Add("locationbias", $"rectangle:{boundary}");
+ query = query.Add("locationbias", $"rectangle:{boundary}");
break;
default:
_logger.GoogleWarning(_localizer["Invalid Location Bias Type"]);
@@ -359,9 +360,9 @@ internal Uri BuildFindPlaceRequest(FindPlacesParameters parameters)
_logger.GoogleDebug(_localizer["Invalid Location Bias"]);
}
- AddBaseParameters(parameters, query);
+ AddBaseParameters(parameters, ref query);
- AddGoogleKey(query);
+ AddGoogleKey(ref query);
uriBuilder.Query = query.ToString();
@@ -381,7 +382,7 @@ internal Uri BuildFindPlaceRequest(FindPlacesParameters parameters)
internal Uri BuildNearbySearchRequest(NearbySearchParameters parameters)
{
var uriBuilder = new UriBuilder(NearbySearchUri);
- var query = HttpUtility.ParseQueryString(uriBuilder.Query);
+ var query = QueryString.Empty;
if (parameters.Location == null)
{
@@ -415,7 +416,7 @@ internal Uri BuildNearbySearchRequest(NearbySearchParameters parameters)
if (!string.IsNullOrWhiteSpace(parameters.Keyword))
{
- query.Add("keyword", parameters.Keyword);
+ query = query.Add("keyword", parameters.Keyword);
}
else
{
@@ -424,16 +425,16 @@ internal Uri BuildNearbySearchRequest(NearbySearchParameters parameters)
if (parameters.RankBy >= RankType.Prominence && parameters.RankBy <= RankType.Distance)
{
- query.Add("rankby", parameters.RankBy.ToEnumString());
+ query = query.Add("rankby", parameters.RankBy.ToEnumString());
}
else
{
_logger.GoogleDebug(_localizer["Invalid RankBy"]);
}
- AddBaseSearchParameters(parameters, query);
+ AddBaseSearchParameters(parameters, ref query);
- AddGoogleKey(query);
+ AddGoogleKey(ref query);
uriBuilder.Query = query.ToString();
@@ -449,7 +450,7 @@ internal Uri BuildNearbySearchRequest(NearbySearchParameters parameters)
internal Uri BuildTextSearchRequest(TextSearchParameters parameters)
{
var uriBuilder = new UriBuilder(TextSearchUri);
- var query = HttpUtility.ParseQueryString(uriBuilder.Query);
+ var query = QueryString.Empty;
if (string.IsNullOrWhiteSpace(parameters.Query))
{
@@ -458,20 +459,20 @@ internal Uri BuildTextSearchRequest(TextSearchParameters parameters)
throw new ArgumentException(error, nameof(parameters.Query));
}
- query.Add("query", parameters.Query);
+ query = query.Add("query", parameters.Query);
if (parameters.Region != null)
{
- query.Add("region", RegionInfoToCCTLD(parameters.Region));
+ query = query.Add("region", RegionInfoToCCTLD(parameters.Region));
}
else
{
_logger.GoogleDebug(_localizer["Invalid Region"]);
}
- AddBaseSearchParameters(parameters, query);
+ AddBaseSearchParameters(parameters, ref query);
- AddGoogleKey(query);
+ AddGoogleKey(ref query);
uriBuilder.Query = query.ToString();
@@ -487,7 +488,7 @@ internal Uri BuildTextSearchRequest(TextSearchParameters parameters)
internal Uri BuildDetailsRequest(DetailsParameters parameters)
{
var uriBuilder = new UriBuilder(DetailsUri);
- var query = HttpUtility.ParseQueryString(uriBuilder.Query);
+ var query = QueryString.Empty;
if (string.IsNullOrWhiteSpace(parameters.PlaceId))
{
@@ -496,11 +497,11 @@ internal Uri BuildDetailsRequest(DetailsParameters parameters)
throw new ArgumentException(error, nameof(parameters.PlaceId));
}
- query.Add("place_id", parameters.PlaceId);
+ query = query.Add("place_id", parameters.PlaceId);
if (parameters.Region != null)
{
- query.Add("region", RegionInfoToCCTLD(parameters.Region));
+ query = query.Add("region", RegionInfoToCCTLD(parameters.Region));
}
else
{
@@ -509,7 +510,7 @@ internal Uri BuildDetailsRequest(DetailsParameters parameters)
if (!string.IsNullOrWhiteSpace(parameters.SessionToken))
{
- query.Add("sessiontoken", parameters.SessionToken);
+ query = query.Add("sessiontoken", parameters.SessionToken);
}
else
{
@@ -518,16 +519,16 @@ internal Uri BuildDetailsRequest(DetailsParameters parameters)
if (parameters.Fields != null && parameters.Fields.Count > 0)
{
- query.Add("fields", string.Join(",", parameters.Fields));
+ query = query.Add("fields", string.Join(",", parameters.Fields));
}
else
{
_logger.GoogleDebug(_localizer["Invalid Fields"]);
}
- AddBaseParameters(parameters, query);
+ AddBaseParameters(parameters, ref query);
- AddGoogleKey(query);
+ AddGoogleKey(ref query);
uriBuilder.Query = query.ToString();
@@ -543,7 +544,7 @@ internal Uri BuildDetailsRequest(DetailsParameters parameters)
internal Uri BuildPlaceAutocompleteRequest(PlacesAutocompleteParameters parameters)
{
var uriBuilder = new UriBuilder(PlaceAutocompleteUri);
- var query = HttpUtility.ParseQueryString(uriBuilder.Query);
+ var query = QueryString.Empty;
if (string.IsNullOrWhiteSpace(parameters.Input))
{
@@ -554,7 +555,7 @@ internal Uri BuildPlaceAutocompleteRequest(PlacesAutocompleteParameters paramete
if (!string.IsNullOrWhiteSpace(parameters.SessionToken))
{
- query.Add("sessiontoken", parameters.SessionToken);
+ query = query.Add("sessiontoken", parameters.SessionToken);
}
else
{
@@ -563,7 +564,7 @@ internal Uri BuildPlaceAutocompleteRequest(PlacesAutocompleteParameters paramete
if (parameters.Origin != null)
{
- query.Add("origin", parameters.Origin.ToString());
+ query = query.Add("origin", parameters.Origin.ToString());
}
else
{
@@ -572,7 +573,7 @@ internal Uri BuildPlaceAutocompleteRequest(PlacesAutocompleteParameters paramete
if (parameters.Types != null && parameters.Types.Count > 0)
{
- query.Add("types", string.Join(",", parameters.Types.Select(x => x.ToEnumString())));
+ query = query.Add("types", string.Join(",", parameters.Types.Select(x => x.ToEnumString())));
}
else
{
@@ -581,7 +582,7 @@ internal Uri BuildPlaceAutocompleteRequest(PlacesAutocompleteParameters paramete
if (parameters.Components != null)
{
- query.Add("components", parameters.Components.ToString());
+ query = query.Add("components", parameters.Components.ToString());
}
else
{
@@ -589,12 +590,12 @@ internal Uri BuildPlaceAutocompleteRequest(PlacesAutocompleteParameters paramete
}
#pragma warning disable CA1308 // Normalize strings to uppercase
- query.Add("strictbounds", parameters.StrictBounds.ToString(CultureInfo.InvariantCulture).ToLowerInvariant());
+ query = query.Add("strictbounds", parameters.StrictBounds.ToString(CultureInfo.InvariantCulture).ToLowerInvariant());
#pragma warning restore CA1308 // Normalize strings to uppercase
- AddAutocompleteParameters(parameters, query);
+ AddAutocompleteParameters(parameters, ref query);
- AddGoogleKey(query);
+ AddGoogleKey(ref query);
uriBuilder.Query = query.ToString();
@@ -610,7 +611,7 @@ internal Uri BuildPlaceAutocompleteRequest(PlacesAutocompleteParameters paramete
internal Uri BuildQueryAutocompleteRequest(QueryAutocompleteParameters parameters)
{
var uriBuilder = new UriBuilder(QueryAutocompleteUri);
- var query = HttpUtility.ParseQueryString(uriBuilder.Query);
+ var query = QueryString.Empty;
if (string.IsNullOrWhiteSpace(parameters.Input))
{
@@ -619,9 +620,9 @@ internal Uri BuildQueryAutocompleteRequest(QueryAutocompleteParameters parameter
throw new ArgumentException(error, nameof(parameters.Input));
}
- AddAutocompleteParameters(parameters, query);
+ AddAutocompleteParameters(parameters, ref query);
- AddGoogleKey(query);
+ AddGoogleKey(ref query);
uriBuilder.Query = query.ToString();
@@ -632,12 +633,12 @@ internal Uri BuildQueryAutocompleteRequest(QueryAutocompleteParameters parameter
/// Adds the autocomplete parameters based on the allowed logic.
///
/// A with the autocomplete parameters to build the uri with.
- /// A with the query parameters.
- internal void AddAutocompleteParameters(QueryAutocompleteParameters parameters, NameValueCollection query)
+ /// A with the query parameters.
+ internal void AddAutocompleteParameters(QueryAutocompleteParameters parameters, ref QueryString query)
{
if (parameters.Offset > 0)
{
- query.Add("offset", parameters.Offset.ToString(CultureInfo.InvariantCulture));
+ query = query.Add("offset", parameters.Offset.ToString(CultureInfo.InvariantCulture));
}
else
{
@@ -646,26 +647,26 @@ internal void AddAutocompleteParameters(QueryAutocompleteParameters parameters,
if (!string.IsNullOrWhiteSpace(parameters.Input))
{
- query.Add("input", parameters.Input);
+ query = query.Add("input", parameters.Input);
}
else
{
_logger.GoogleDebug(_localizer["Invalid Input Info"]);
}
- AddCoordinateParameters(parameters, query);
+ AddCoordinateParameters(parameters, ref query);
}
///
/// Adds the base search parameters based on the allowed logic.
///
/// A with the base search parameters to build the uri with.
- /// A with the query parameters.
- internal void AddBaseSearchParameters(BaseSearchParameters parameters, NameValueCollection query)
+ /// A with the query parameters.
+ internal void AddBaseSearchParameters(BaseSearchParameters parameters, ref QueryString query)
{
if (parameters.MinimumPrice >= 0 && parameters.MinimumPrice <= 4 && parameters.MinimumPrice <= parameters.MaximumPrice)
{
- query.Add("minprice", parameters.MinimumPrice.ToString(CultureInfo.InvariantCulture));
+ query = query.Add("minprice", parameters.MinimumPrice.ToString(CultureInfo.InvariantCulture));
}
else
{
@@ -674,7 +675,7 @@ internal void AddBaseSearchParameters(BaseSearchParameters parameters, NameValue
if (parameters.MaximumPrice >= 0 && parameters.MaximumPrice <= 4 && parameters.MinimumPrice <= parameters.MaximumPrice)
{
- query.Add("maxprice", parameters.MaximumPrice.ToString(CultureInfo.InvariantCulture));
+ query = query.Add("maxprice", parameters.MaximumPrice.ToString(CultureInfo.InvariantCulture));
}
else
{
@@ -682,12 +683,12 @@ internal void AddBaseSearchParameters(BaseSearchParameters parameters, NameValue
}
#pragma warning disable CA1308 // Normalize strings to uppercase
- query.Add("opennow", parameters.OpenNow.ToString(CultureInfo.InvariantCulture).ToLowerInvariant());
+ query = query.Add("opennow", parameters.OpenNow.ToString(CultureInfo.InvariantCulture).ToLowerInvariant());
#pragma warning restore CA1308 // Normalize strings to uppercase
if (parameters.PageToken > 0)
{
- query.Add("pagetoken", parameters.PageToken.ToString(CultureInfo.InvariantCulture));
+ query = query.Add("pagetoken", parameters.PageToken.ToString(CultureInfo.InvariantCulture));
}
else
{
@@ -696,26 +697,26 @@ internal void AddBaseSearchParameters(BaseSearchParameters parameters, NameValue
if (!string.IsNullOrWhiteSpace(parameters.Type))
{
- query.Add("type", parameters.Type);
+ query = query.Add("type", parameters.Type);
}
else
{
_logger.GoogleDebug(_localizer["Invalid Type"]);
}
- AddCoordinateParameters(parameters, query);
+ AddCoordinateParameters(parameters, ref query);
}
///
/// Adds the coordinate parameters based on the allowed logic.
///
/// A with the coordinate parameters to build the uri with.
- /// A with the query parameters.
- internal void AddCoordinateParameters(CoordinateParameters parameters, NameValueCollection query)
+ /// A with the query parameters.
+ internal void AddCoordinateParameters(CoordinateParameters parameters, ref QueryString query)
{
if (parameters.Location != null)
{
- query.Add("location", parameters.Location.ToString());
+ query = query.Add("location", parameters.Location.ToString());
}
else
{
@@ -724,26 +725,26 @@ internal void AddCoordinateParameters(CoordinateParameters parameters, NameValue
if (parameters.Radius > 0 && parameters.Radius <= 50000)
{
- query.Add("radius", parameters.Radius.ToString(CultureInfo.InvariantCulture));
+ query = query.Add("radius", parameters.Radius.ToString(CultureInfo.InvariantCulture));
}
else
{
_logger.GoogleWarning(_localizer["Invalid Radius Value"]);
}
- AddBaseParameters(parameters, query);
+ AddBaseParameters(parameters, ref query);
}
///
/// Adds the base query parameters based on the allowed logic.
///
/// A with the base parameters to build the uri with.
- /// A with the query parameters.
- internal void AddBaseParameters(BaseParameters parameters, NameValueCollection query)
+ /// A with the query parameters.
+ internal void AddBaseParameters(BaseParameters parameters, ref QueryString query)
{
if (parameters.Language != null)
{
- query.Add("language", parameters.Language.Name);
+ query = query.Add("language", parameters.Language.Name);
}
else
{
@@ -754,10 +755,10 @@ internal void AddBaseParameters(BaseParameters parameters, NameValueCollection q
///
/// Adds the Google key to the query parameters.
///
- /// A with the query parameters.
- internal void AddGoogleKey(NameValueCollection query)
+ /// A with the query parameters.
+ internal void AddGoogleKey(ref QueryString query)
{
- query.Add("key", _keyContainer.GetKey());
+ query = query.Add("key", _keyContainer.GetKey());
}
///
diff --git a/src/Geo.Here/Models/Exceptions/HereException.cs b/src/Geo.Here/Models/Exceptions/HereException.cs
index b6d5100..96a59eb 100644
--- a/src/Geo.Here/Models/Exceptions/HereException.cs
+++ b/src/Geo.Here/Models/Exceptions/HereException.cs
@@ -9,6 +9,7 @@ namespace Geo.Here.Models.Exceptions
using System.Globalization;
using System.Net.Http;
using System.Threading.Tasks;
+ using Geo.Core.Models.Exceptions;
using Newtonsoft.Json;
///
@@ -23,7 +24,7 @@ namespace Geo.Here.Models.Exceptions
/// Thrown when the HERE request is cancelled.
/// Thrown when an error occurs while reading the return JSON text.
/// Thrown when when an error occurs during JSON deserialization.
- public class HereException : Exception
+ public sealed class HereException : GeoCoreException
{
private const string DefaultMessage = "{0} See the inner exception for more information.";
diff --git a/src/Geo.Here/Services/HereGeocoding.cs b/src/Geo.Here/Services/HereGeocoding.cs
index 6cb44ef..2d6a3db 100644
--- a/src/Geo.Here/Services/HereGeocoding.cs
+++ b/src/Geo.Here/Services/HereGeocoding.cs
@@ -6,18 +6,17 @@
namespace Geo.Here.Services
{
using System;
- using System.Collections.Specialized;
using System.Globalization;
using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
- using System.Web;
using Geo.Core;
using Geo.Here.Abstractions;
using Geo.Here.Models.Exceptions;
using Geo.Here.Models.Parameters;
using Geo.Here.Models.Responses;
+ using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
@@ -44,18 +43,20 @@ public class HereGeocoding : ClientExecutor, IHereGeocoding
///
/// A used for placing calls to the HERE Geocoding API.
/// A used for fetching the HERE key.
- /// A used to create a localizer for localizing log or exception messages.
- /// A used for logging information.
+ /// An used to provide exceptions based on an exception type.
+ /// An used to create a localizer for localizing log or exception messages.
+ /// An used to create a logger used for logging information.
public HereGeocoding(
HttpClient client,
IHereKeyContainer keyContainer,
+ IGeoNETExceptionProvider exceptionProvider,
IStringLocalizerFactory localizerFactory,
- ILogger logger = null)
- : base(client, localizerFactory)
+ ILoggerFactory loggerFactory = null)
+ : base(client, exceptionProvider, localizerFactory, loggerFactory)
{
_keyContainer = keyContainer ?? throw new ArgumentNullException(nameof(keyContainer));
_localizer = localizerFactory?.Create(typeof(HereGeocoding)) ?? throw new ArgumentNullException(nameof(localizerFactory));
- _logger = logger ?? NullLogger.Instance;
+ _logger = loggerFactory?.CreateLogger() ?? NullLogger.Instance;
}
///
@@ -155,7 +156,7 @@ internal Uri ValidateAndBuildUri(TParameters parameters, Func 0)
{
- query.Add("in", string.Join(",", parameters.InCountry.Select(x => x.ThreeLetterISORegionName)));
+ query = query.Add("in", string.Join(",", parameters.InCountry.Select(x => x.ThreeLetterISORegionName)));
}
else
{
_logger.HereDebug(_localizer["Invalid In Country"]);
}
- AddLocatingParameters(parameters, query);
+ AddLocatingParameters(parameters, ref query);
- AddHereKey(query);
+ AddHereKey(ref query);
uriBuilder.Query = query.ToString();
@@ -209,7 +210,7 @@ internal Uri BuildGeocodingRequest(GeocodeParameters parameters)
internal Uri BuildReverseGeocodingRequest(ReverseGeocodeParameters parameters)
{
var uriBuilder = new UriBuilder(ReverseGeocodeUri);
- var query = HttpUtility.ParseQueryString(uriBuilder.Query);
+ var query = QueryString.Empty;
if (parameters.At is null)
{
@@ -218,9 +219,9 @@ internal Uri BuildReverseGeocodingRequest(ReverseGeocodeParameters parameters)
throw new ArgumentException(error, nameof(parameters.At));
}
- AddLocatingParameters(parameters, query);
+ AddLocatingParameters(parameters, ref query);
- AddHereKey(query);
+ AddHereKey(ref query);
uriBuilder.Query = query.ToString();
@@ -236,7 +237,7 @@ internal Uri BuildReverseGeocodingRequest(ReverseGeocodeParameters parameters)
internal Uri BuildDiscoverRequest(DiscoverParameters parameters)
{
var uriBuilder = new UriBuilder(DiscoverUri);
- var query = HttpUtility.ParseQueryString(uriBuilder.Query);
+ var query = QueryString.Empty;
if (string.IsNullOrWhiteSpace(parameters.Query))
{
@@ -245,11 +246,11 @@ internal Uri BuildDiscoverRequest(DiscoverParameters parameters)
throw new ArgumentException(error, nameof(parameters.Query));
}
- query.Add("q", parameters.Query);
+ query = query.Add("q", parameters.Query);
- AddBoundingParameters(parameters, query);
+ AddBoundingParameters(parameters, ref query);
- AddHereKey(query);
+ AddHereKey(ref query);
uriBuilder.Query = query.ToString();
@@ -265,7 +266,7 @@ internal Uri BuildDiscoverRequest(DiscoverParameters parameters)
internal Uri BuildAutosuggestRequest(AutosuggestParameters parameters)
{
var uriBuilder = new UriBuilder(AutosuggestUri);
- var query = HttpUtility.ParseQueryString(uriBuilder.Query);
+ var query = QueryString.Empty;
if (string.IsNullOrWhiteSpace(parameters.Query))
{
@@ -274,20 +275,20 @@ internal Uri BuildAutosuggestRequest(AutosuggestParameters parameters)
throw new ArgumentException(error, nameof(parameters.Query));
}
- query.Add("q", parameters.Query);
+ query = query.Add("q", parameters.Query);
if (parameters.TermsLimit <= 10)
{
- query.Add("termsLimit", parameters.TermsLimit.ToString(CultureInfo.InvariantCulture));
+ query = query.Add("termsLimit", parameters.TermsLimit.ToString(CultureInfo.InvariantCulture));
}
else
{
_logger.HereWarning(_localizer["Invalid Terms Limit"]);
}
- AddBoundingParameters(parameters, query);
+ AddBoundingParameters(parameters, ref query);
- AddHereKey(query);
+ AddHereKey(ref query);
uriBuilder.Query = query.ToString();
@@ -303,7 +304,7 @@ internal Uri BuildAutosuggestRequest(AutosuggestParameters parameters)
internal Uri BuildBrowseRequest(BrowseParameters parameters)
{
var uriBuilder = new UriBuilder(BrowseUri);
- var query = HttpUtility.ParseQueryString(uriBuilder.Query);
+ var query = QueryString.Empty;
if (parameters.At is null)
{
@@ -314,7 +315,7 @@ internal Uri BuildBrowseRequest(BrowseParameters parameters)
if (!string.IsNullOrWhiteSpace(parameters.Categories))
{
- query.Add("categories", parameters.Categories);
+ query = query.Add("categories", parameters.Categories);
}
else
{
@@ -323,16 +324,16 @@ internal Uri BuildBrowseRequest(BrowseParameters parameters)
if (!string.IsNullOrWhiteSpace(parameters.Name))
{
- query.Add("name", parameters.Name);
+ query = query.Add("name", parameters.Name);
}
else
{
_logger.HereDebug(_localizer["Invalid Name"]);
}
- AddBoundingParameters(parameters, query);
+ AddBoundingParameters(parameters, ref query);
- AddHereKey(query);
+ AddHereKey(ref query);
uriBuilder.Query = query.ToString();
@@ -348,7 +349,7 @@ internal Uri BuildBrowseRequest(BrowseParameters parameters)
internal Uri BuildLookupRequest(LookupParameters parameters)
{
var uriBuilder = new UriBuilder(LookupUri);
- var query = HttpUtility.ParseQueryString(uriBuilder.Query);
+ var query = QueryString.Empty;
if (string.IsNullOrWhiteSpace(parameters.Id))
{
@@ -357,11 +358,11 @@ internal Uri BuildLookupRequest(LookupParameters parameters)
throw new ArgumentException(error, nameof(parameters.Id));
}
- query.Add("id", parameters.Id);
+ query = query.Add("id", parameters.Id);
- AddBaseParameters(parameters, query);
+ AddBaseParameters(parameters, ref query);
- AddHereKey(query);
+ AddHereKey(ref query);
uriBuilder.Query = query.ToString();
@@ -372,8 +373,8 @@ internal Uri BuildLookupRequest(LookupParameters parameters)
/// Adds the bounding query parameters based on the allowed logic.
///
/// A with the bounding parameters to build the uri with.
- /// A with the query parameters.
- internal void AddBoundingParameters(AreaParameters parameters, NameValueCollection query)
+ /// A with the query parameters.
+ internal void AddBoundingParameters(AreaParameters parameters, ref QueryString query)
{
var hasAt = parameters.At != null && parameters.At.IsValid();
var hasCircle = parameters.InCircle != null && parameters.InCircle.IsValid();
@@ -399,7 +400,7 @@ internal void AddBoundingParameters(AreaParameters parameters, NameValueCollecti
if (hasAt)
{
- query.Add("at", parameters.At.ToString());
+ query = query.Add("at", parameters.At.ToString());
}
else
{
@@ -408,7 +409,7 @@ internal void AddBoundingParameters(AreaParameters parameters, NameValueCollecti
if (!string.IsNullOrWhiteSpace(parameters.InCountry))
{
- query.Add("in", $"countryCode:{parameters.InCountry}");
+ query = query.Add("in", $"countryCode:{parameters.InCountry}");
}
else
{
@@ -417,7 +418,7 @@ internal void AddBoundingParameters(AreaParameters parameters, NameValueCollecti
if (hasCircle)
{
- query.Add("in", $"circle:{parameters.InCircle}");
+ query = query.Add("in", $"circle:{parameters.InCircle}");
}
else
{
@@ -426,7 +427,7 @@ internal void AddBoundingParameters(AreaParameters parameters, NameValueCollecti
if (hasBoundingBox)
{
- query.Add("in", $"bbox:{parameters.InBoundingBox}");
+ query = query.Add("in", $"bbox:{parameters.InBoundingBox}");
}
else
{
@@ -435,64 +436,64 @@ internal void AddBoundingParameters(AreaParameters parameters, NameValueCollecti
if (!string.IsNullOrWhiteSpace(parameters.Route))
{
- query.Add("route", parameters.Route);
+ query = query.Add("route", parameters.Route);
}
else
{
_logger.HereDebug(_localizer["Invalid Route"]);
}
- AddLimitingParameters(parameters, query);
+ AddLimitingParameters(parameters, ref query);
}
///
/// Adds the locating query parameters based on the allowed logic.
///
/// A with the base limiting parameters to build the uri with.
- /// A with the query parameters.
- internal void AddLocatingParameters(BaseFilterParameters parameters, NameValueCollection query)
+ /// A with the query parameters.
+ internal void AddLocatingParameters(BaseFilterParameters parameters, ref QueryString query)
{
if (parameters.At != null)
{
- query.Add("at", parameters.At.ToString());
+ query = query.Add("at", parameters.At.ToString());
}
else
{
_logger.HereDebug(_localizer["Invalid At Debug"]);
}
- AddLimitingParameters(parameters, query);
+ AddLimitingParameters(parameters, ref query);
}
///
/// Adds the base limiting query parameters based on the allowed logic.
///
/// A with the base limiting parameters to build the uri with.
- /// A with the query parameters.
- internal void AddLimitingParameters(BaseFilterParameters parameters, NameValueCollection query)
+ /// A with the query parameters.
+ internal void AddLimitingParameters(BaseFilterParameters parameters, ref QueryString query)
{
if (parameters.Limit > 0 && parameters.Limit <= 100)
{
- query.Add("limit", parameters.Limit.ToString(CultureInfo.InvariantCulture));
+ query = query.Add("limit", parameters.Limit.ToString(CultureInfo.InvariantCulture));
}
else
{
_logger.HereDebug(_localizer["Invalid Limit"]);
}
- AddBaseParameters(parameters, query);
+ AddBaseParameters(parameters, ref query);
}
///
/// Adds the base query parameters based on the allowed logic.
///
/// A with the base parameters to build the uri with.
- /// A with the query parameters.
- internal void AddBaseParameters(BaseParameters parameters, NameValueCollection query)
+ /// A with the query parameters.
+ internal void AddBaseParameters(BaseParameters parameters, ref QueryString query)
{
if (parameters.Language != null)
{
- query.Add("lang", parameters.Language.Name);
+ query = query.Add("lang", parameters.Language.Name);
}
else
{
@@ -503,10 +504,10 @@ internal void AddBaseParameters(BaseParameters parameters, NameValueCollection q
///
/// Adds the HERE key to the query parameters.
///
- /// A with the query parameters.
- internal void AddHereKey(NameValueCollection query)
+ /// A with the query parameters.
+ internal void AddHereKey(ref QueryString query)
{
- query.Add("apiKey", _keyContainer.GetKey());
+ query = query.Add("apiKey", _keyContainer.GetKey());
}
}
}
diff --git a/src/Geo.MapBox/Models/Exceptions/MapBoxException.cs b/src/Geo.MapBox/Models/Exceptions/MapBoxException.cs
index c776f52..0a9a7fb 100644
--- a/src/Geo.MapBox/Models/Exceptions/MapBoxException.cs
+++ b/src/Geo.MapBox/Models/Exceptions/MapBoxException.cs
@@ -9,6 +9,7 @@ namespace Geo.MapBox.Models.Exceptions
using System.Globalization;
using System.Net.Http;
using System.Threading.Tasks;
+ using Geo.Core.Models.Exceptions;
using Newtonsoft.Json;
///
@@ -23,7 +24,7 @@ namespace Geo.MapBox.Models.Exceptions
/// Thrown when the MapBox request is cancelled.
/// Thrown when an error occurs while reading the return JSON text.
/// Thrown when when an error occurs during JSON deserialization.
- public class MapBoxException : Exception
+ public sealed class MapBoxException : GeoCoreException
{
private const string DefaultMessage = "{0} See the inner exception for more information.";
diff --git a/src/Geo.MapBox/README.md b/src/Geo.MapBox/README.md
index 6c91244..781c0af 100644
--- a/src/Geo.MapBox/README.md
+++ b/src/Geo.MapBox/README.md
@@ -37,7 +37,7 @@ public MyService(IMapBoxGeocoding mapBoxGeocoding)
Now simply call the geocoding methods in the interface.
## Endpoint Types
-MapQuest has 2 endpoint types, permanent and non-permanent.
+MapBox has 2 endpoint types, permanent and non-permanent.
- Requests to the non-permanent endpoint must be triggered by user activity. Any results must be displayed on a Mapbox map and cannot be stored permanently, as described in Mapbox�s [terms of service](https://www.mapbox.com/tos/#geocoding).
- The permanent endpoint gives you access to two services: permanent geocoding and batch geocoding.
\ No newline at end of file
diff --git a/src/Geo.MapBox/Services/MapBoxGeocoding.cs b/src/Geo.MapBox/Services/MapBoxGeocoding.cs
index be965fa..7054b57 100644
--- a/src/Geo.MapBox/Services/MapBoxGeocoding.cs
+++ b/src/Geo.MapBox/Services/MapBoxGeocoding.cs
@@ -7,13 +7,12 @@ namespace Geo.MapBox.Services
{
using System;
using System.Collections.Generic;
- using System.Collections.Specialized;
using System.Globalization;
using System.Linq;
using System.Net.Http;
+ using System.Text.Encodings.Web;
using System.Threading;
using System.Threading.Tasks;
- using System.Web;
using Geo.Core;
using Geo.MapBox.Abstractions;
using Geo.MapBox.Enums;
@@ -21,6 +20,7 @@ namespace Geo.MapBox.Services
using Geo.MapBox.Models.Exceptions;
using Geo.MapBox.Models.Parameters;
using Geo.MapBox.Models.Responses;
+ using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
@@ -45,18 +45,20 @@ public class MapBoxGeocoding : ClientExecutor, IMapBoxGeocoding
///
/// A used for placing calls to the here Geocoding API.
/// A used for fetching the here key.
- /// A used to create a localizer for localizing log or exception messages.
- /// A used for logging information.
+ /// An used to provide exceptions based on an exception type.
+ /// An used to create a localizer for localizing log or exception messages.
+ /// An used to create a logger used for logging information.
public MapBoxGeocoding(
HttpClient client,
IMapBoxKeyContainer keyContainer,
+ IGeoNETExceptionProvider exceptionProvider,
IStringLocalizerFactory localizerFactory,
- ILogger logger = null)
- : base(client, localizerFactory)
+ ILoggerFactory loggerFactory = null)
+ : base(client, exceptionProvider, localizerFactory, loggerFactory)
{
_keyContainer = keyContainer ?? throw new ArgumentNullException(nameof(keyContainer));
_localizer = localizerFactory?.Create(typeof(MapBoxGeocoding)) ?? throw new ArgumentNullException(nameof(localizerFactory));
- _logger = logger ?? NullLogger.Instance;
+ _logger = loggerFactory?.CreateLogger() ?? NullLogger.Instance;
}
///
@@ -122,16 +124,16 @@ internal Uri BuildGeocodingRequest(GeocodingParameters parameters)
throw new ArgumentException(error, nameof(parameters.Query));
}
- var uriBuilder = new UriBuilder(string.Format(CultureInfo.InvariantCulture, GeocodeUri, parameters.EndpointType == EndpointType.Places ? PlacesEndpoint : PermanentEndpoint, parameters.Query));
- var query = HttpUtility.ParseQueryString(uriBuilder.Query);
+ var uriBuilder = new UriBuilder(string.Format(CultureInfo.InvariantCulture, GeocodeUri, parameters.EndpointType == EndpointType.Places ? PlacesEndpoint : PermanentEndpoint, UrlEncoder.Default.Encode(parameters.Query)));
+ var query = QueryString.Empty;
#pragma warning disable CA1308 // Normalize strings to uppercase
- query.Add("autocomplete", parameters.ReturnAutocomplete.ToString(CultureInfo.InvariantCulture).ToLowerInvariant());
+ query = query.Add("autocomplete", parameters.ReturnAutocomplete.ToString(CultureInfo.InvariantCulture).ToLowerInvariant());
#pragma warning restore CA1308 // Normalize strings to uppercase
if (parameters.BoundingBox != null)
{
- query.Add("bbox", parameters.BoundingBox.ToString());
+ query = query.Add("bbox", parameters.BoundingBox.ToString());
}
else
{
@@ -139,21 +141,21 @@ internal Uri BuildGeocodingRequest(GeocodingParameters parameters)
}
#pragma warning disable CA1308 // Normalize strings to uppercase
- query.Add("fuzzyMatch", parameters.FuzzyMatch.ToString(CultureInfo.InvariantCulture).ToLowerInvariant());
+ query = query.Add("fuzzyMatch", parameters.FuzzyMatch.ToString(CultureInfo.InvariantCulture).ToLowerInvariant());
#pragma warning restore CA1308 // Normalize strings to uppercase
if (parameters.Proximity != null)
{
- query.Add("proximity", parameters.Proximity.ToString());
+ query = query.Add("proximity", parameters.Proximity.ToString());
}
else
{
_logger.MapBoxDebug(_localizer["Invalid Proximity"]);
}
- AddBaseParameters(parameters, query);
+ AddBaseParameters(parameters, ref query);
- AddMapBoxKey(query);
+ AddMapBoxKey(ref query);
uriBuilder.Query = query.ToString();
@@ -176,15 +178,15 @@ internal Uri BuildReverseGeocodingRequest(ReverseGeocodingParameters parameters)
}
var uriBuilder = new UriBuilder(string.Format(CultureInfo.InvariantCulture, ReverseGeocodeUri, parameters.EndpointType == EndpointType.Places ? PlacesEndpoint : PermanentEndpoint, parameters.Coordinate));
- var query = HttpUtility.ParseQueryString(uriBuilder.Query);
+ var query = QueryString.Empty;
#pragma warning disable CA1308 // Normalize strings to uppercase
- query.Add("reverseMode", parameters.ReverseMode.ToString().ToLowerInvariant());
+ query = query.Add("reverseMode", parameters.ReverseMode.ToString().ToLowerInvariant());
#pragma warning restore CA1308 // Normalize strings to uppercase
- AddBaseParameters(parameters, query);
+ AddBaseParameters(parameters, ref query);
- AddMapBoxKey(query);
+ AddMapBoxKey(ref query);
uriBuilder.Query = query.ToString();
@@ -195,12 +197,12 @@ internal Uri BuildReverseGeocodingRequest(ReverseGeocodingParameters parameters)
/// Adds the base query parameters based on the allowed logic.
///
/// A with the base parameters to build the uri with.
- /// A with the query parameters.
- internal void AddBaseParameters(BaseParameters parameters, NameValueCollection query)
+ /// A with the query parameters.
+ internal void AddBaseParameters(BaseParameters parameters, ref QueryString query)
{
if (parameters.Countries.Count > 0)
{
- query.Add("country", string.Join(",", parameters.Countries.Select(x => x.TwoLetterISORegionName)));
+ query = query.Add("country", string.Join(",", parameters.Countries.Select(x => x.TwoLetterISORegionName)));
}
else
{
@@ -209,7 +211,7 @@ internal void AddBaseParameters(BaseParameters parameters, NameValueCollection q
if (parameters.Languages.Count > 0)
{
- query.Add("language", string.Join(",", parameters.Languages.Select(x => x.Name)));
+ query = query.Add("language", string.Join(",", parameters.Languages.Select(x => x.Name)));
}
else
{
@@ -218,7 +220,7 @@ internal void AddBaseParameters(BaseParameters parameters, NameValueCollection q
if (parameters.Limit > 0 && parameters.Limit < 6)
{
- query.Add("limit", parameters.Limit.ToString(CultureInfo.InvariantCulture));
+ query = query.Add("limit", parameters.Limit.ToString(CultureInfo.InvariantCulture));
}
else
{
@@ -226,13 +228,13 @@ internal void AddBaseParameters(BaseParameters parameters, NameValueCollection q
}
#pragma warning disable CA1308 // Normalize strings to uppercase
- query.Add("routing", parameters.Routing.ToString(CultureInfo.InvariantCulture).ToLowerInvariant());
+ query = query.Add("routing", parameters.Routing.ToString(CultureInfo.InvariantCulture).ToLowerInvariant());
#pragma warning restore CA1308 // Normalize strings to uppercase
if (parameters.Types != null && parameters.Types.Count > 0)
{
#pragma warning disable CA1308 // Normalize strings to uppercase
- query.Add("types", string.Join(",", parameters.Types.Select(x => x.ToString().ToLowerInvariant())));
+ query = query.Add("types", string.Join(",", parameters.Types.Select(x => x.ToString().ToLowerInvariant())));
#pragma warning restore CA1308 // Normalize strings to uppercase
}
else
@@ -244,10 +246,10 @@ internal void AddBaseParameters(BaseParameters parameters, NameValueCollection q
///
/// Adds the here key to the query parameters.
///
- /// A with the query parameters.
- internal void AddMapBoxKey(NameValueCollection query)
+ /// A with the query parameters.
+ internal void AddMapBoxKey(ref QueryString query)
{
- query.Add("access_token", _keyContainer.GetKey());
+ query = query.Add("access_token", _keyContainer.GetKey());
}
}
}
diff --git a/src/Geo.MapQuest/Models/Exceptions/MapQuestException.cs b/src/Geo.MapQuest/Models/Exceptions/MapQuestException.cs
index 67cec43..a4c977c 100644
--- a/src/Geo.MapQuest/Models/Exceptions/MapQuestException.cs
+++ b/src/Geo.MapQuest/Models/Exceptions/MapQuestException.cs
@@ -9,6 +9,7 @@ namespace Geo.MapQuest.Models.Exceptions
using System.Globalization;
using System.Net.Http;
using System.Threading.Tasks;
+ using Geo.Core.Models.Exceptions;
using Newtonsoft.Json;
///
@@ -23,7 +24,7 @@ namespace Geo.MapQuest.Models.Exceptions
/// Thrown when the MapQuest request is cancelled.
/// Thrown when an error occurs while reading the return JSON text.
/// Thrown when when an error occurs during JSON deserialization.
- public class MapQuestException : Exception
+ public sealed class MapQuestException : GeoCoreException
{
private const string DefaultMessage = "{0} See the inner exception for more information.";
diff --git a/src/Geo.MapQuest/Services/MapQuestGeocoding.cs b/src/Geo.MapQuest/Services/MapQuestGeocoding.cs
index b48f88f..c789bc4 100644
--- a/src/Geo.MapQuest/Services/MapQuestGeocoding.cs
+++ b/src/Geo.MapQuest/Services/MapQuestGeocoding.cs
@@ -6,18 +6,17 @@
namespace Geo.MapQuest.Services
{
using System;
- using System.Collections.Specialized;
using System.Globalization;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
- using System.Web;
using Geo.Core;
using Geo.MapQuest.Abstractions;
using Geo.MapQuest.Enums;
using Geo.MapQuest.Models.Exceptions;
using Geo.MapQuest.Models.Parameters;
using Geo.MapQuest.Models.Responses;
+ using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
@@ -44,20 +43,22 @@ public class MapQuestGeocoding : ClientExecutor, IMapQuestGeocoding
/// A used for placing calls to the MapQuest Geocoding API.
/// A used for fetching the MapQuest key.
/// A used for fetching which MapQuest endpoint to use.
- /// A used to create a localizer for localizing log or exception messages.
- /// A used for logging information.
+ /// An used to provide exceptions based on an exception type.
+ /// An used to create a localizer for localizing log or exception messages.
+ /// An used to create a logger used for logging information.
public MapQuestGeocoding(
HttpClient client,
IMapQuestKeyContainer keyContainer,
IMapQuestEndpoint endpoint,
+ IGeoNETExceptionProvider exceptionProvider,
IStringLocalizerFactory localizerFactory,
- ILogger logger = null)
- : base(client, localizerFactory)
+ ILoggerFactory loggerFactory = null)
+ : base(client, exceptionProvider, localizerFactory, loggerFactory)
{
_keyContainer = keyContainer ?? throw new ArgumentNullException(nameof(keyContainer));
_endpoint = endpoint ?? throw new ArgumentNullException(nameof(endpoint));
_localizer = localizerFactory?.Create(typeof(MapQuestGeocoding)) ?? throw new ArgumentNullException(nameof(localizerFactory));
- _logger = logger ?? NullLogger.Instance;
+ _logger = loggerFactory?.CreateLogger() ?? NullLogger.Instance;
}
///
@@ -117,7 +118,7 @@ internal Uri ValidateAndBuildUri(TParameters parameters, Func 0)
{
- query.Add("maxResults", parameters.MaxResults.ToString(CultureInfo.InvariantCulture));
+ query = query.Add("maxResults", parameters.MaxResults.ToString(CultureInfo.InvariantCulture));
}
else
{
@@ -154,24 +155,24 @@ internal Uri BuildGeocodingRequest(GeocodingParameters parameters)
if (parameters.IntlMode == InternationalMode.FiveBox)
{
- query.Add("intlMode", "5BOX");
+ query = query.Add("intlMode", "5BOX");
}
else if (parameters.IntlMode == InternationalMode.OneBox)
{
- query.Add("intlMode", "1BOX");
+ query = query.Add("intlMode", "1BOX");
}
else if (parameters.IntlMode == InternationalMode.Auto)
{
- query.Add("intlMode", "AUTO");
+ query = query.Add("intlMode", "AUTO");
}
else
{
_logger.MapQuestWarning(_localizer["Invalid Intl Mode"]);
}
- AddBaseParameters(parameters, query);
+ AddBaseParameters(parameters, ref query);
- AddMapQuestKey(query);
+ AddMapQuestKey(ref query);
uriBuilder.Query = query.ToString();
@@ -187,7 +188,7 @@ internal Uri BuildGeocodingRequest(GeocodingParameters parameters)
internal Uri BuildReverseGeocodingRequest(ReverseGeocodingParameters parameters)
{
var uriBuilder = new UriBuilder(_endpoint.UseLicensedEndpoint() ? ReverseGeocodeUri : OpenReverseGeocodeUri);
- var query = HttpUtility.ParseQueryString(uriBuilder.Query);
+ var query = QueryString.Empty;
if (parameters.Location is null)
{
@@ -196,19 +197,19 @@ internal Uri BuildReverseGeocodingRequest(ReverseGeocodingParameters parameters)
throw new ArgumentException(error, nameof(parameters.Location));
}
- query.Add("location", parameters.Location.ToString());
+ query = query.Add("location", parameters.Location.ToString());
#pragma warning disable CA1308 // Normalize strings to uppercase
- query.Add("includeNearestIntersection", parameters.IncludeNearestIntersection.ToString(CultureInfo.InvariantCulture).ToLowerInvariant());
+ query = query.Add("includeNearestIntersection", parameters.IncludeNearestIntersection.ToString(CultureInfo.InvariantCulture).ToLowerInvariant());
#pragma warning restore CA1308 // Normalize strings to uppercase
#pragma warning disable CA1308 // Normalize strings to uppercase
- query.Add("includeRoadMetadata", parameters.IncludeRoadMetadata.ToString(CultureInfo.InvariantCulture).ToLowerInvariant());
+ query = query.Add("includeRoadMetadata", parameters.IncludeRoadMetadata.ToString(CultureInfo.InvariantCulture).ToLowerInvariant());
#pragma warning restore CA1308 // Normalize strings to uppercase
- AddBaseParameters(parameters, query);
+ AddBaseParameters(parameters, ref query);
- AddMapQuestKey(query);
+ AddMapQuestKey(ref query);
uriBuilder.Query = query.ToString();
@@ -219,21 +220,21 @@ internal Uri BuildReverseGeocodingRequest(ReverseGeocodingParameters parameters)
/// Adds the base query parameters based on the allowed logic.
///
/// A with the base parameters to build the uri with.
- /// A with the query parameters.
- internal void AddBaseParameters(BaseParameters parameters, NameValueCollection query)
+ /// A with the query parameters.
+ internal void AddBaseParameters(BaseParameters parameters, ref QueryString query)
{
#pragma warning disable CA1308 // Normalize strings to uppercase
- query.Add("thumbMaps", parameters.IncludeThumbMaps.ToString(CultureInfo.InvariantCulture).ToLowerInvariant());
+ query = query.Add("thumbMaps", parameters.IncludeThumbMaps.ToString(CultureInfo.InvariantCulture).ToLowerInvariant());
#pragma warning restore CA1308 // Normalize strings to uppercase
}
///
/// Adds the MapQuest key to the query parameters.
///
- /// A with the query parameters.
- internal void AddMapQuestKey(NameValueCollection query)
+ /// A with the query parameters.
+ internal void AddMapQuestKey(ref QueryString query)
{
- query.Add("key", _keyContainer.GetKey());
+ query = query.Add("key", _keyContainer.GetKey());
}
}
}
diff --git a/src/Source.props b/src/Source.props
index 7e58f1c..a31909d 100644
--- a/src/Source.props
+++ b/src/Source.props
@@ -16,4 +16,13 @@
+
+
+
+
+
+
+
+
+
diff --git a/test/Geo.ArcGIS.Tests/Services/ArcGISGeocodingShould.cs b/test/Geo.ArcGIS.Tests/Services/ArcGISGeocodingShould.cs
index da5c8f2..66e2787 100644
--- a/test/Geo.ArcGIS.Tests/Services/ArcGISGeocodingShould.cs
+++ b/test/Geo.ArcGIS.Tests/Services/ArcGISGeocodingShould.cs
@@ -7,7 +7,6 @@ namespace Geo.ArcGIS.Tests.Services
{
using System;
using System.Collections.Generic;
- using System.Collections.Specialized;
using System.Globalization;
using System.Net;
using System.Net.Http;
@@ -22,6 +21,7 @@ namespace Geo.ArcGIS.Tests.Services
using Geo.ArcGIS.Models.Responses;
using Geo.ArcGIS.Services;
using Geo.Core;
+ using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
@@ -36,6 +36,7 @@ public class ArcGISGeocodingShould : IDisposable
{
private readonly HttpClient _httpClient;
private readonly Mock _mockTokenContainer;
+ private readonly IGeoNETExceptionProvider _exceptionProvider;
private readonly IStringLocalizerFactory _localizerFactory;
private readonly List _responseMessages = new List();
private bool _disposed;
@@ -144,6 +145,7 @@ public ArcGISGeocodingShould()
var options = Options.Create(new LocalizationOptions { ResourcesPath = "Resources" });
_localizerFactory = new ResourceManagerStringLocalizerFactory(options, NullLoggerFactory.Instance);
_httpClient = new HttpClient(mockHandler.Object);
+ _exceptionProvider = new GeoNETExceptionProvider();
}
///
@@ -162,11 +164,13 @@ public async Task AddArcGISTokenSuccessfully()
{
var sut = BuildService();
- var query = new NameValueCollection();
+ var query = QueryString.Empty;
- await sut.AddArcGISToken(query, CancellationToken.None).ConfigureAwait(false);
- query.Count.Should().Be(1);
- query["token"].Should().Be("token123");
+ query = await sut.AddArcGISToken(query, CancellationToken.None).ConfigureAwait(false);
+
+ var queryParameters = HttpUtility.ParseQueryString(query.ToString());
+ queryParameters.Count.Should().Be(1);
+ queryParameters["token"].Should().Be("token123");
}
///
@@ -177,25 +181,29 @@ public void AddStorageParameterSuccessfully()
{
var sut = BuildService();
- var query = new NameValueCollection();
+ var query = QueryString.Empty;
var parameters = new StorageParameters()
{
ForStorage = false,
};
- sut.AddStorageParameter(parameters, query);
- query.Count.Should().Be(1);
- query["forStorage"].Should().Be("false");
+ sut.AddStorageParameter(parameters, ref query);
+
+ var queryParameters = HttpUtility.ParseQueryString(query.ToString());
+ queryParameters.Count.Should().Be(1);
+ queryParameters["forStorage"].Should().Be("false");
- query.Clear();
+ query = QueryString.Empty;
parameters = new StorageParameters()
{
ForStorage = true,
};
- sut.AddStorageParameter(parameters, query);
- query.Count.Should().Be(1);
- query["forStorage"].Should().Be("true");
+ sut.AddStorageParameter(parameters, ref query);
+
+ queryParameters = HttpUtility.ParseQueryString(query.ToString());
+ queryParameters.Count.Should().Be(1);
+ queryParameters["forStorage"].Should().Be("true");
}
///
@@ -568,7 +576,7 @@ protected virtual void Dispose(bool disposing)
private ArcGISGeocoding BuildService()
{
- return new ArcGISGeocoding(_httpClient, _mockTokenContainer.Object, _localizerFactory);
+ return new ArcGISGeocoding(_httpClient, _mockTokenContainer.Object, _exceptionProvider, _localizerFactory);
}
}
}
\ No newline at end of file
diff --git a/test/Geo.Bing.Tests/Services/BingGeocodingShould.cs b/test/Geo.Bing.Tests/Services/BingGeocodingShould.cs
index 88ad5e9..8eb1afa 100644
--- a/test/Geo.Bing.Tests/Services/BingGeocodingShould.cs
+++ b/test/Geo.Bing.Tests/Services/BingGeocodingShould.cs
@@ -7,7 +7,6 @@ namespace Geo.Bing.Tests.Services
{
using System;
using System.Collections.Generic;
- using System.Collections.Specialized;
using System.Globalization;
using System.Net;
using System.Net.Http;
@@ -19,6 +18,7 @@ namespace Geo.Bing.Tests.Services
using Geo.Bing.Models.Parameters;
using Geo.Bing.Services;
using Geo.Core;
+ using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
@@ -33,6 +33,7 @@ public class BingGeocodingShould : IDisposable
{
private readonly HttpClient _httpClient;
private readonly BingKeyContainer _keyContainer;
+ private readonly IGeoNETExceptionProvider _exceptionProvider;
private readonly IStringLocalizerFactory _localizerFactory;
private readonly List _responseMessages = new List();
private bool _disposed;
@@ -129,6 +130,7 @@ public BingGeocodingShould()
var options = Options.Create(new LocalizationOptions { ResourcesPath = "Resources" });
_localizerFactory = new ResourceManagerStringLocalizerFactory(options, NullLoggerFactory.Instance);
_httpClient = new HttpClient(mockHandler.Object);
+ _exceptionProvider = new GeoNETExceptionProvider();
}
///
@@ -146,11 +148,13 @@ public void AddBingKeySuccessfully()
{
var sut = BuildService();
- var query = new NameValueCollection();
+ var query = QueryString.Empty;
- sut.AddBingKey(query);
- query.Count.Should().Be(1);
- query["key"].Should().Be("123abc");
+ sut.AddBingKey(ref query);
+
+ var queryParameters = HttpUtility.ParseQueryString(query.ToString());
+ queryParameters.Count.Should().Be(1);
+ queryParameters["key"].Should().Be("123abc");
}
///
@@ -161,7 +165,7 @@ public void BuildBaseQuerySuccessfully()
{
var sut = BuildService();
- var query = new NameValueCollection();
+ var query = QueryString.Empty;
var parameters = new BaseParameters()
{
IncludeNeighbourhood = true,
@@ -170,9 +174,11 @@ public void BuildBaseQuerySuccessfully()
};
sut.BuildBaseQuery(parameters, ref query);
- query.Count.Should().Be(2);
- query["includeNeighborhood"].Should().Be("1");
- query["include"].Should().Contain("queryParse").And.Contain("ciso2");
+
+ var queryParameters = HttpUtility.ParseQueryString(query.ToString());
+ queryParameters.Count.Should().Be(2);
+ queryParameters["includeNeighborhood"].Should().Be("1");
+ queryParameters["include"].Should().Contain("queryParse").And.Contain("ciso2");
}
///
@@ -183,7 +189,7 @@ public void BuildLimitedResultQuerySuccessfully()
{
var sut = BuildService();
- var query = new NameValueCollection();
+ var query = QueryString.Empty;
var parameters = new ResultParameters()
{
MaximumResults = 7,
@@ -193,10 +199,12 @@ public void BuildLimitedResultQuerySuccessfully()
};
sut.BuildLimitedResultQuery(parameters, ref query);
- query.Count.Should().Be(3);
- query["maxResults"].Should().Be("7");
- query["includeNeighborhood"].Should().Be("1");
- query["include"].Should().Contain("queryParse").And.Contain("ciso2");
+
+ var queryParameters = HttpUtility.ParseQueryString(query.ToString());
+ queryParameters.Count.Should().Be(3);
+ queryParameters["maxResults"].Should().Be("7");
+ queryParameters["includeNeighborhood"].Should().Be("1");
+ queryParameters["include"].Should().Contain("queryParse").And.Contain("ciso2");
}
///
@@ -432,7 +440,7 @@ protected virtual void Dispose(bool disposing)
private BingGeocoding BuildService()
{
- return new BingGeocoding(_httpClient, _keyContainer, _localizerFactory);
+ return new BingGeocoding(_httpClient, _keyContainer, _exceptionProvider, _localizerFactory);
}
}
}
\ No newline at end of file
diff --git a/test/Geo.Core.Tests/ClientExecutorShould.cs b/test/Geo.Core.Tests/ClientExecutorShould.cs
index 80dee25..e150c5a 100644
--- a/test/Geo.Core.Tests/ClientExecutorShould.cs
+++ b/test/Geo.Core.Tests/ClientExecutorShould.cs
@@ -29,6 +29,7 @@ public class ClientExecutorShould : IDisposable
{
private const string ApiName = "Test";
private readonly HttpClient _httpClient;
+ private readonly IGeoNETExceptionProvider _exceptionProvider;
private readonly IStringLocalizerFactory _localizerFactory;
private readonly List _responseMessages = new List();
private bool _disposed;
@@ -125,6 +126,7 @@ public ClientExecutorShould()
var options = Options.Create(new LocalizationOptions { ResourcesPath = "Resources" });
_localizerFactory = new ResourceManagerStringLocalizerFactory(options, NullLoggerFactory.Instance);
_httpClient = new HttpClient(mockHandler.Object);
+ _exceptionProvider = new GeoNETExceptionProvider();
}
///
@@ -140,7 +142,7 @@ public void Dispose()
[Fact]
public void ThrowExceptionOnNullUri()
{
- var sut = new TestClientExecutor(_httpClient, _localizerFactory);
+ var sut = new TestClientExecutor(_httpClient, _exceptionProvider, _localizerFactory);
sut.Invoking(x => x.CallAsync(new Uri("http://test.com/ArgumentNullException")))
.Should()
@@ -154,7 +156,7 @@ public void ThrowExceptionOnNullUri()
[Fact]
public void ThrowExceptionOnInvalidUri()
{
- var sut = new TestClientExecutor(_httpClient, _localizerFactory);
+ var sut = new TestClientExecutor(_httpClient, _exceptionProvider, _localizerFactory);
sut.Invoking(x => x.CallAsync(new Uri("http://test.com/InvalidOperationException")))
.Should()
@@ -168,7 +170,7 @@ public void ThrowExceptionOnInvalidUri()
[Fact]
public void ThrowExceptionOnHttpFailure()
{
- var sut = new TestClientExecutor(_httpClient, _localizerFactory);
+ var sut = new TestClientExecutor(_httpClient, _exceptionProvider, _localizerFactory);
sut.Invoking(x => x.CallAsync(new Uri("http://test.com/HttpRequestException")))
.Should()
@@ -182,7 +184,7 @@ public void ThrowExceptionOnHttpFailure()
[Fact]
public void ThrowExceptionOnCancelledRequest()
{
- var sut = new TestClientExecutor(_httpClient, _localizerFactory);
+ var sut = new TestClientExecutor(_httpClient, _exceptionProvider, _localizerFactory);
sut.Invoking(x => x.CallAsync(new Uri("http://test.com/TaskCanceledException")))
.Should()
@@ -195,7 +197,7 @@ public void ThrowExceptionOnCancelledRequest()
[Fact]
public void ThrowExceptionOnInvalidJson1()
{
- var sut = new TestClientExecutor(_httpClient, _localizerFactory);
+ var sut = new TestClientExecutor(_httpClient, _exceptionProvider, _localizerFactory);
sut.Invoking(x => x.CallAsync(new Uri("http://test.com/JsonReaderException")))
.Should()
@@ -208,7 +210,7 @@ public void ThrowExceptionOnInvalidJson1()
[Fact]
public void ThrowExceptionOnInvalidJson2()
{
- var sut = new TestClientExecutor(_httpClient, _localizerFactory);
+ var sut = new TestClientExecutor(_httpClient, _exceptionProvider, _localizerFactory);
sut.Invoking(x => x.CallAsync(new Uri("http://test.com/JsonSerializationException")))
.Should()
@@ -222,10 +224,11 @@ public void ThrowExceptionOnInvalidJson2()
[Fact]
public async Task ReturnsErrorJson()
{
- var sut = new TestClientExecutor(_httpClient, _localizerFactory);
+ var sut = new TestClientExecutor(_httpClient, _exceptionProvider, _localizerFactory);
var result = await sut.CallAsync(new Uri("http://test.com/Failure")).ConfigureAwait(false);
+ result.IsSuccessful.Should().BeFalse();
result.Result.Should().BeNull();
- result.JSON.Should().Be("{'Message':'Access denied'}");
+ result.Body.Should().Be("{'Message':'Access denied'}");
}
///
@@ -235,9 +238,10 @@ public async Task ReturnsErrorJson()
[Fact]
public async Task SuccesfullyReturnObject()
{
- var sut = new TestClientExecutor(_httpClient, _localizerFactory);
+ var sut = new TestClientExecutor(_httpClient, _exceptionProvider, _localizerFactory);
var result = await sut.CallAsync(new Uri("http://test.com/Success")).ConfigureAwait(false);
+ result.IsSuccessful.Should().BeTrue();
result.Result.TestField.Should().Be(1);
}
@@ -247,7 +251,7 @@ public async Task SuccesfullyReturnObject()
[Fact]
public void ThrowWrappedExceptionOnNullUri()
{
- var sut = new TestClientExecutor(_httpClient, _localizerFactory);
+ var sut = new TestClientExecutor(_httpClient, _exceptionProvider, _localizerFactory);
sut.Invoking(x => x.CallAsync(new Uri("http://test.com/ArgumentNullException"), ApiName))
.Should()
@@ -261,7 +265,7 @@ public void ThrowWrappedExceptionOnNullUri()
[Fact]
public void ThrowWrappedExceptionOnInvalidUri()
{
- var sut = new TestClientExecutor(_httpClient, _localizerFactory);
+ var sut = new TestClientExecutor(_httpClient, _exceptionProvider, _localizerFactory);
sut.Invoking(x => x.CallAsync(new Uri("http://test.com/InvalidOperationException"), ApiName))
.Should()
@@ -275,7 +279,7 @@ public void ThrowWrappedExceptionOnInvalidUri()
[Fact]
public void ThrowWrappedExceptionOnHttpFailure()
{
- var sut = new TestClientExecutor(_httpClient, _localizerFactory);
+ var sut = new TestClientExecutor(_httpClient, _exceptionProvider, _localizerFactory);
sut.Invoking(x => x.CallAsync(new Uri("http://test.com/HttpRequestException"), ApiName))
.Should()
@@ -289,7 +293,7 @@ public void ThrowWrappedExceptionOnHttpFailure()
[Fact]
public void ThrowWrappedExceptionOnCancelledRequest()
{
- var sut = new TestClientExecutor(_httpClient, _localizerFactory);
+ var sut = new TestClientExecutor(_httpClient, _exceptionProvider, _localizerFactory);
sut.Invoking(x => x.CallAsync(new Uri("http://test.com/TaskCanceledException"), ApiName))
.Should()
@@ -303,7 +307,7 @@ public void ThrowWrappedExceptionOnCancelledRequest()
[Fact]
public void ThrowWrappedExceptionOnInvalidJson1()
{
- var sut = new TestClientExecutor(_httpClient, _localizerFactory);
+ var sut = new TestClientExecutor(_httpClient, _exceptionProvider, _localizerFactory);
sut.Invoking(x => x.CallAsync(new Uri("http://test.com/JsonReaderException"), ApiName))
.Should()
@@ -317,7 +321,7 @@ public void ThrowWrappedExceptionOnInvalidJson1()
[Fact]
public void ThrowWrappedExceptionOnInvalidJson2()
{
- var sut = new TestClientExecutor(_httpClient, _localizerFactory);
+ var sut = new TestClientExecutor(_httpClient, _exceptionProvider, _localizerFactory);
sut.Invoking(x => x.CallAsync(new Uri("http://test.com/JsonSerializationException"), ApiName))
.Should()
@@ -331,12 +335,16 @@ public void ThrowWrappedExceptionOnInvalidJson2()
[Fact]
public void ThrowWrappedExceptionOnErrorJson()
{
- var sut = new TestClientExecutor(_httpClient, _localizerFactory);
+ var sut = new TestClientExecutor(_httpClient, _exceptionProvider, _localizerFactory);
sut.Invoking(x => x.CallAsync(new Uri("http://test.com/Failure"), ApiName))
.Should()
.Throw()
- .Where(x => x.Data.Count == 1 && x.Data["responseBody"].ToString() == "{'Message':'Access denied'}");
+ .Where(x =>
+ x.Data.Count == 3 &&
+ x.Data["responseBody"].ToString() == "{'Message':'Access denied'}" &&
+ (HttpStatusCode)x.Data["responseStatusCode"] == HttpStatusCode.Forbidden &&
+ x.Data["uri"].ToString() == "http://test.com/Failure");
}
///
@@ -346,7 +354,7 @@ public void ThrowWrappedExceptionOnErrorJson()
[Fact]
public async Task SuccesfullyReturnOnlyObject()
{
- var sut = new TestClientExecutor(_httpClient, _localizerFactory);
+ var sut = new TestClientExecutor(_httpClient, _exceptionProvider, _localizerFactory);
var result = await sut.CallAsync(new Uri("http://test.com/Success"), ApiName).ConfigureAwait(false);
result.TestField.Should().Be(1);
diff --git a/test/Geo.Core.Tests/GeoNETExceptionProviderTests.cs b/test/Geo.Core.Tests/GeoNETExceptionProviderTests.cs
new file mode 100644
index 0000000..7f921fa
--- /dev/null
+++ b/test/Geo.Core.Tests/GeoNETExceptionProviderTests.cs
@@ -0,0 +1,47 @@
+//
+// Copyright (c) Geo.NET.
+// Licensed under the MIT license. See the LICENSE file in the solution root for full license information.
+//
+
+namespace Geo.Core.Tests
+{
+ using System;
+ using FluentAssertions;
+ using Geo.Core.Tests.Models;
+ using Xunit;
+
+ ///
+ /// Unit tests for the class.
+ ///
+ public class GeoNETExceptionProviderTests
+ {
+ [Fact]
+ public void GetException_WithoutInnerException_ReturnsException()
+ {
+ // Arrange
+ var sut = new GeoNETExceptionProvider();
+
+ // Act
+ var ex = sut.GetException("Test exception");
+
+ // Assert
+ ex.Message.Should().Be("Test exception");
+ ex.InnerException.Should().BeNull();
+ }
+
+ [Fact]
+ public void GetException_WithInnerException_ReturnsException()
+ {
+ // Arrange
+ var sut = new GeoNETExceptionProvider();
+
+ // Act
+ var ex = sut.GetException("Test exception", new ArgumentNullException());
+
+ // Assert
+ ex.Message.Should().Be("Test exception");
+ ex.InnerException.Should().NotBeNull();
+ ex.InnerException.Should().BeOfType();
+ }
+ }
+}
diff --git a/test/Geo.Core.Tests/GlobalSuppressions.cs b/test/Geo.Core.Tests/GlobalSuppressions.cs
new file mode 100644
index 0000000..bd98412
--- /dev/null
+++ b/test/Geo.Core.Tests/GlobalSuppressions.cs
@@ -0,0 +1,9 @@
+// This file is used by Code Analysis to maintain SuppressMessage
+// attributes that are applied to this project.
+// Project-level suppressions either have no target or are given
+// a specific target and scoped to a namespace, type, member, etc.
+
+using System.Diagnostics.CodeAnalysis;
+
+[assembly: SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Using Microsoft recommended unit test naming", Scope = "namespaceanddescendants", Target = "~N:Geo.Core.Tests")]
+[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "The name of the test should explain the test", Scope = "namespaceanddescendants", Target = "~N:Geo.Core.Tests")]
diff --git a/test/Geo.Core.Tests/Models/TestClientExecutor.cs b/test/Geo.Core.Tests/Models/TestClientExecutor.cs
index 2a0e644..bd1be26 100644
--- a/test/Geo.Core.Tests/Models/TestClientExecutor.cs
+++ b/test/Geo.Core.Tests/Models/TestClientExecutor.cs
@@ -19,8 +19,11 @@ public class TestClientExecutor : ClientExecutor
///
/// A used for calls.
/// A used for localizing log or exception messages.
- public TestClientExecutor(HttpClient client, IStringLocalizerFactory localizer)
- : base(client, localizer)
+ public TestClientExecutor(
+ HttpClient client,
+ IGeoNETExceptionProvider exceptionProvider,
+ IStringLocalizerFactory localizer)
+ : base(client, exceptionProvider, localizer)
{
}
}
diff --git a/test/Geo.Core.Tests/Models/TestException.cs b/test/Geo.Core.Tests/Models/TestException.cs
index fa9e38f..827fb87 100644
--- a/test/Geo.Core.Tests/Models/TestException.cs
+++ b/test/Geo.Core.Tests/Models/TestException.cs
@@ -6,8 +6,9 @@
namespace Geo.Core.Tests.Models
{
using System;
+ using Geo.Core.Models.Exceptions;
- public class TestException : Exception
+ public sealed class TestException : GeoCoreException
{
///
/// Initializes a new instance of the class.
diff --git a/test/Geo.Google.Tests/Services/GoogleGeocodingShould.cs b/test/Geo.Google.Tests/Services/GoogleGeocodingShould.cs
index 82cc7d1..d991ebb 100644
--- a/test/Geo.Google.Tests/Services/GoogleGeocodingShould.cs
+++ b/test/Geo.Google.Tests/Services/GoogleGeocodingShould.cs
@@ -7,7 +7,6 @@ namespace Geo.Google.Tests.Services
{
using System;
using System.Collections.Generic;
- using System.Collections.Specialized;
using System.Globalization;
using System.Linq;
using System.Net;
@@ -22,6 +21,7 @@ namespace Geo.Google.Tests.Services
using Geo.Google.Models;
using Geo.Google.Models.Parameters;
using Geo.Google.Services;
+ using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
@@ -36,6 +36,7 @@ public class GoogleGeocodingShould : IDisposable
{
private readonly HttpClient _httpClient;
private readonly GoogleKeyContainer _keyContainer;
+ private readonly IGeoNETExceptionProvider _exceptionProvider;
private readonly IStringLocalizerFactory _localizerFactory;
private readonly List _responseMessages = new List();
private bool _disposed;
@@ -105,6 +106,7 @@ public GoogleGeocodingShould()
var options = Options.Create(new LocalizationOptions { ResourcesPath = "Resources" });
_localizerFactory = new ResourceManagerStringLocalizerFactory(options, NullLoggerFactory.Instance);
_httpClient = new HttpClient(mockHandler.Object);
+ _exceptionProvider = new GeoNETExceptionProvider();
}
///
@@ -122,11 +124,13 @@ public void AddGoogleKeySuccessfully()
{
var sut = BuildService();
- var query = new NameValueCollection();
+ var query = QueryString.Empty;
- sut.AddGoogleKey(query);
- query.Count.Should().Be(1);
- query["key"].Should().Be("abc123");
+ sut.AddGoogleKey(ref query);
+
+ var queryParameters = HttpUtility.ParseQueryString(query.ToString());
+ queryParameters.Count.Should().Be(1);
+ queryParameters["key"].Should().Be("abc123");
}
///
@@ -137,15 +141,17 @@ public void AddBaseParametersSuccessfully()
{
var sut = BuildService();
- var query = new NameValueCollection();
+ var query = QueryString.Empty;
var parameters = new BaseParameters()
{
Language = new CultureInfo("da"),
};
- sut.AddBaseParameters(parameters, query);
- query.Count.Should().Be(1);
- query["language"].Should().Be("da");
+ sut.AddBaseParameters(parameters, ref query);
+
+ var queryParameters = HttpUtility.ParseQueryString(query.ToString());
+ queryParameters.Count.Should().Be(1);
+ queryParameters["language"].Should().Be("da");
}
///
@@ -156,7 +162,7 @@ public void AddCoordinateParametersSuccessfully()
{
var sut = BuildService();
- var query = new NameValueCollection();
+ var query = QueryString.Empty;
var parameters = new CoordinateParameters()
{
Location = new Coordinate()
@@ -168,11 +174,13 @@ public void AddCoordinateParametersSuccessfully()
Language = new CultureInfo("da"),
};
- sut.AddCoordinateParameters(parameters, query);
- query.Count.Should().Be(3);
- query["location"].Should().Be("76.54,34.56");
- query["radius"].Should().Be("10000");
- query["language"].Should().Be("da");
+ sut.AddCoordinateParameters(parameters, ref query);
+
+ var queryParameters = HttpUtility.ParseQueryString(query.ToString());
+ queryParameters.Count.Should().Be(3);
+ queryParameters["location"].Should().Be("76.54,34.56");
+ queryParameters["radius"].Should().Be("10000");
+ queryParameters["language"].Should().Be("da");
}
///
@@ -183,7 +191,7 @@ public void AddBaseSearchParametersSuccessfully()
{
var sut = BuildService();
- var query = new NameValueCollection();
+ var query = QueryString.Empty;
var parameters = new BaseSearchParameters()
{
MinimumPrice = 2,
@@ -200,16 +208,18 @@ public void AddBaseSearchParametersSuccessfully()
Language = new CultureInfo("pt-BR"),
};
- sut.AddBaseSearchParameters(parameters, query);
- query.Count.Should().Be(8);
- query["minprice"].Should().Be("2");
- query["maxprice"].Should().Be("3");
- query["opennow"].Should().Be("false");
- query["pagetoken"].Should().Be("987654");
- query["type"].Should().Be("Restaurant");
- query["location"].Should().Be("76.14,34.54");
- query["radius"].Should().Be("10001");
- query["language"].Should().Be("pt-BR");
+ sut.AddBaseSearchParameters(parameters, ref query);
+
+ var queryParameters = HttpUtility.ParseQueryString(query.ToString());
+ queryParameters.Count.Should().Be(8);
+ queryParameters["minprice"].Should().Be("2");
+ queryParameters["maxprice"].Should().Be("3");
+ queryParameters["opennow"].Should().Be("false");
+ queryParameters["pagetoken"].Should().Be("987654");
+ queryParameters["type"].Should().Be("Restaurant");
+ queryParameters["location"].Should().Be("76.14,34.54");
+ queryParameters["radius"].Should().Be("10001");
+ queryParameters["language"].Should().Be("pt-BR");
}
///
@@ -220,7 +230,7 @@ public void AddAutocompleteParametersSuccessfully()
{
var sut = BuildService();
- var query = new NameValueCollection();
+ var query = QueryString.Empty;
var parameters = new QueryAutocompleteParameters()
{
Offset = 64,
@@ -234,13 +244,15 @@ public void AddAutocompleteParametersSuccessfully()
Language = new CultureInfo("fr"),
};
- sut.AddAutocompleteParameters(parameters, query);
- query.Count.Should().Be(5);
- query["offset"].Should().Be("64");
- query["input"].Should().Be("123 East");
- query["location"].Should().Be("6.14,3.54");
- query["radius"].Should().Be("25000");
- query["language"].Should().Be("fr");
+ sut.AddAutocompleteParameters(parameters, ref query);
+
+ var queryParameters = HttpUtility.ParseQueryString(query.ToString());
+ queryParameters.Count.Should().Be(5);
+ queryParameters["offset"].Should().Be("64");
+ queryParameters["input"].Should().Be("123 East");
+ queryParameters["location"].Should().Be("6.14,3.54");
+ queryParameters["radius"].Should().Be("25000");
+ queryParameters["language"].Should().Be("fr");
}
///
@@ -251,7 +263,7 @@ public void AddBaseSearchParametersWithRestrictions1()
{
var sut = BuildService();
- var query = new NameValueCollection();
+ var query = QueryString.Empty;
var parameters = new BaseSearchParameters()
{
MinimumPrice = 5,
@@ -266,11 +278,13 @@ public void AddBaseSearchParametersWithRestrictions1()
Language = new CultureInfo("es"),
};
- sut.AddBaseSearchParameters(parameters, query);
- query.Count.Should().Be(3);
- query["opennow"].Should().Be("false");
- query["location"].Should().Be("76.14,34.54");
- query["language"].Should().Be("es");
+ sut.AddBaseSearchParameters(parameters, ref query);
+
+ var queryParameters = HttpUtility.ParseQueryString(query.ToString());
+ queryParameters.Count.Should().Be(3);
+ queryParameters["opennow"].Should().Be("false");
+ queryParameters["location"].Should().Be("76.14,34.54");
+ queryParameters["language"].Should().Be("es");
}
///
@@ -281,7 +295,7 @@ public void AddBaseSearchParametersWithRestrictions2()
{
var sut = BuildService();
- var query = new NameValueCollection();
+ var query = QueryString.Empty;
var parameters = new BaseSearchParameters()
{
MinimumPrice = 3,
@@ -296,11 +310,13 @@ public void AddBaseSearchParametersWithRestrictions2()
Language = new CultureInfo("es"),
};
- sut.AddBaseSearchParameters(parameters, query);
- query.Count.Should().Be(3);
- query["opennow"].Should().Be("false");
- query["location"].Should().Be("76.14,34.54");
- query["language"].Should().Be("es");
+ sut.AddBaseSearchParameters(parameters, ref query);
+
+ var queryParameters = HttpUtility.ParseQueryString(query.ToString());
+ queryParameters.Count.Should().Be(3);
+ queryParameters["opennow"].Should().Be("false");
+ queryParameters["location"].Should().Be("76.14,34.54");
+ queryParameters["language"].Should().Be("es");
}
///
@@ -876,7 +892,7 @@ protected virtual void Dispose(bool disposing)
private GoogleGeocoding BuildService()
{
- return new GoogleGeocoding(_httpClient, _keyContainer, _localizerFactory);
+ return new GoogleGeocoding(_httpClient, _keyContainer, _exceptionProvider, _localizerFactory);
}
}
}
\ No newline at end of file
diff --git a/test/Geo.Here.Tests/Services/HereGeocodingShould.cs b/test/Geo.Here.Tests/Services/HereGeocodingShould.cs
index d7100a2..c1c70b3 100644
--- a/test/Geo.Here.Tests/Services/HereGeocodingShould.cs
+++ b/test/Geo.Here.Tests/Services/HereGeocodingShould.cs
@@ -7,7 +7,6 @@ namespace Geo.Here.Tests.Services
{
using System;
using System.Collections.Generic;
- using System.Collections.Specialized;
using System.Globalization;
using System.Net;
using System.Net.Http;
@@ -20,6 +19,7 @@ namespace Geo.Here.Tests.Services
using Geo.Here.Models.Exceptions;
using Geo.Here.Models.Parameters;
using Geo.Here.Services;
+ using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
@@ -34,6 +34,7 @@ public class HereGeocodingShould : IDisposable
{
private readonly HttpClient _httpClient;
private readonly HereKeyContainer _keyContainer;
+ private readonly IGeoNETExceptionProvider _exceptionProvider;
private readonly IStringLocalizerFactory _localizerFactory;
private readonly List _responseMessages = new List();
private bool _disposed;
@@ -173,6 +174,7 @@ public HereGeocodingShould()
var options = Options.Create(new LocalizationOptions { ResourcesPath = "Resources" });
_localizerFactory = new ResourceManagerStringLocalizerFactory(options, NullLoggerFactory.Instance);
_httpClient = new HttpClient(mockHandler.Object);
+ _exceptionProvider = new GeoNETExceptionProvider();
}
///
@@ -190,11 +192,13 @@ public void AddHereKeySuccessfully()
{
var sut = BuildService();
- var query = new NameValueCollection();
+ var query = QueryString.Empty;
- sut.AddHereKey(query);
- query.Count.Should().Be(1);
- query["apiKey"].Should().Be("abc123");
+ sut.AddHereKey(ref query);
+
+ var queryParameters = HttpUtility.ParseQueryString(query.ToString());
+ queryParameters.Count.Should().Be(1);
+ queryParameters["apiKey"].Should().Be("abc123");
}
///
@@ -205,15 +209,17 @@ public void AddBaseParametersSuccessfully()
{
var sut = BuildService();
- var query = new NameValueCollection();
+ var query = QueryString.Empty;
var parameters = new BaseParameters()
{
Language = new CultureInfo("es"),
};
- sut.AddBaseParameters(parameters, query);
- query.Count.Should().Be(1);
- query["lang"].Should().Be("es");
+ sut.AddBaseParameters(parameters, ref query);
+
+ var queryParameters = HttpUtility.ParseQueryString(query.ToString());
+ queryParameters.Count.Should().Be(1);
+ queryParameters["lang"].Should().Be("es");
}
///
@@ -224,17 +230,19 @@ public void AddLimitingParametersSuccessfully()
{
var sut = BuildService();
- var query = new NameValueCollection();
+ var query = QueryString.Empty;
var parameters = new BaseFilterParameters()
{
Limit = 17,
Language = new CultureInfo("da"),
};
- sut.AddLimitingParameters(parameters, query);
- query.Count.Should().Be(2);
- query["limit"].Should().Be("17");
- query["lang"].Should().Be("da");
+ sut.AddLimitingParameters(parameters, ref query);
+
+ var queryParameters = HttpUtility.ParseQueryString(query.ToString());
+ queryParameters.Count.Should().Be(2);
+ queryParameters["limit"].Should().Be("17");
+ queryParameters["lang"].Should().Be("da");
}
///
@@ -245,7 +253,7 @@ public void AddLocatingParametersSuccessfully()
{
var sut = BuildService();
- var query = new NameValueCollection();
+ var query = QueryString.Empty;
var parameters = new BaseFilterParameters()
{
At = new Coordinate()
@@ -257,11 +265,13 @@ public void AddLocatingParametersSuccessfully()
Language = new CultureInfo("fr-FR"),
};
- sut.AddLocatingParameters(parameters, query);
- query.Count.Should().Be(3);
- query["at"].Should().Be("56.789,123.456");
- query["limit"].Should().Be("91");
- query["lang"].Should().Be("fr-FR");
+ sut.AddLocatingParameters(parameters, ref query);
+
+ var queryParameters = HttpUtility.ParseQueryString(query.ToString());
+ queryParameters.Count.Should().Be(3);
+ queryParameters["at"].Should().Be("56.789,123.456");
+ queryParameters["limit"].Should().Be("91");
+ queryParameters["lang"].Should().Be("fr-FR");
}
///
@@ -272,7 +282,7 @@ public void AddBoundingParametersSuccessfully()
{
var sut = BuildService();
- var query = new NameValueCollection();
+ var query = QueryString.Empty;
var parameters = new AreaParameters()
{
At = new Coordinate()
@@ -284,13 +294,15 @@ public void AddBoundingParametersSuccessfully()
Language = new CultureInfo("fr"),
};
- sut.AddBoundingParameters(parameters, query);
- query.Count.Should().Be(3);
- query["at"].Should().Be("56.789,123.456");
- query["limit"].Should().Be("91");
- query["lang"].Should().Be("fr");
+ sut.AddBoundingParameters(parameters, ref query);
+
+ var queryParameters = HttpUtility.ParseQueryString(query.ToString());
+ queryParameters.Count.Should().Be(3);
+ queryParameters["at"].Should().Be("56.789,123.456");
+ queryParameters["limit"].Should().Be("91");
+ queryParameters["lang"].Should().Be("fr");
- query.Clear();
+ query = QueryString.Empty;
parameters = new AreaParameters()
{
InCountry = "BEL",
@@ -303,14 +315,16 @@ public void AddBoundingParametersSuccessfully()
Language = new CultureInfo("nl"),
};
- sut.AddBoundingParameters(parameters, query);
- query.Count.Should().Be(4);
- query["in"].Should().Be("countryCode:BEL");
- query["at"].Should().Be("56.789,123.456");
- query["limit"].Should().Be("91");
- query["lang"].Should().Be("nl");
+ sut.AddBoundingParameters(parameters, ref query);
- query.Clear();
+ queryParameters = HttpUtility.ParseQueryString(query.ToString());
+ queryParameters.Count.Should().Be(4);
+ queryParameters["in"].Should().Be("countryCode:BEL");
+ queryParameters["at"].Should().Be("56.789,123.456");
+ queryParameters["limit"].Should().Be("91");
+ queryParameters["lang"].Should().Be("nl");
+
+ query = QueryString.Empty;
parameters = new AreaParameters()
{
InCircle = new Circle()
@@ -326,13 +340,15 @@ public void AddBoundingParametersSuccessfully()
Language = new CultureInfo("gl"),
};
- sut.AddBoundingParameters(parameters, query);
- query.Count.Should().Be(3);
- query["in"].Should().Be("circle:78.9,45.32;r=50000");
- query["limit"].Should().Be("83");
- query["lang"].Should().Be("gl");
+ sut.AddBoundingParameters(parameters, ref query);
+
+ queryParameters = HttpUtility.ParseQueryString(query.ToString());
+ queryParameters.Count.Should().Be(3);
+ queryParameters["in"].Should().Be("circle:78.9,45.32;r=50000");
+ queryParameters["limit"].Should().Be("83");
+ queryParameters["lang"].Should().Be("gl");
- query.Clear();
+ query = QueryString.Empty;
parameters = new AreaParameters()
{
InCountry = "DNK",
@@ -349,13 +365,15 @@ public void AddBoundingParametersSuccessfully()
Language = new CultureInfo("da"),
};
- sut.AddBoundingParameters(parameters, query);
- query.Count.Should().Be(3);
- query["in"].Should().Be("countryCode:DNK,circle:48.9,15.32;r=6000");
- query["limit"].Should().Be("48");
- query["lang"].Should().Be("da");
+ sut.AddBoundingParameters(parameters, ref query);
+
+ queryParameters = HttpUtility.ParseQueryString(query.ToString());
+ queryParameters.Count.Should().Be(3);
+ queryParameters["in"].Should().Be("countryCode:DNK,circle:48.9,15.32;r=6000");
+ queryParameters["limit"].Should().Be("48");
+ queryParameters["lang"].Should().Be("da");
- query.Clear();
+ query = QueryString.Empty;
parameters = new AreaParameters()
{
InBoundingBox = new BoundingBox()
@@ -369,13 +387,15 @@ public void AddBoundingParametersSuccessfully()
Language = new CultureInfo("ca"),
};
- sut.AddBoundingParameters(parameters, query);
- query.Count.Should().Be(3);
- query["in"].Should().Be("bbox:-1.5,87.99,1.5,89.99");
- query["limit"].Should().Be("65");
- query["lang"].Should().Be("ca");
+ sut.AddBoundingParameters(parameters, ref query);
- query.Clear();
+ queryParameters = HttpUtility.ParseQueryString(query.ToString());
+ queryParameters.Count.Should().Be(3);
+ queryParameters["in"].Should().Be("bbox:-1.5,87.99,1.5,89.99");
+ queryParameters["limit"].Should().Be("65");
+ queryParameters["lang"].Should().Be("ca");
+
+ query = QueryString.Empty;
parameters = new AreaParameters()
{
InCountry = "POL",
@@ -390,11 +410,13 @@ public void AddBoundingParametersSuccessfully()
Language = new CultureInfo("pl"),
};
- sut.AddBoundingParameters(parameters, query);
- query.Count.Should().Be(3);
- query["in"].Should().Be("countryCode:POL,bbox:-43.5,45.99,-39.5,54.99");
- query["limit"].Should().Be("33");
- query["lang"].Should().Be("pl");
+ sut.AddBoundingParameters(parameters, ref query);
+
+ queryParameters = HttpUtility.ParseQueryString(query.ToString());
+ queryParameters.Count.Should().Be(3);
+ queryParameters["in"].Should().Be("countryCode:POL,bbox:-43.5,45.99,-39.5,54.99");
+ queryParameters["limit"].Should().Be("33");
+ queryParameters["lang"].Should().Be("pl");
}
///
@@ -405,8 +427,8 @@ public void AddBoundingParametersWithException()
{
var sut = BuildService();
- var query = new NameValueCollection();
- Action act = () => sut.AddBoundingParameters(new AreaParameters(), query);
+ var query = QueryString.Empty;
+ Action act = () => sut.AddBoundingParameters(new AreaParameters(), ref query);
act.Should()
.Throw()
@@ -440,7 +462,7 @@ public void AddBoundingParametersWithException()
Language = new CultureInfo("pl"),
};
- act = () => sut.AddBoundingParameters(new AreaParameters(), query);
+ act = () => sut.AddBoundingParameters(new AreaParameters(), ref query);
act.Should()
.Throw()
@@ -958,7 +980,7 @@ protected virtual void Dispose(bool disposing)
private HereGeocoding BuildService()
{
- return new HereGeocoding(_httpClient, _keyContainer, _localizerFactory);
+ return new HereGeocoding(_httpClient, _keyContainer, _exceptionProvider, _localizerFactory);
}
}
}
diff --git a/test/Geo.MapBox.Tests/Services/MapBoxGeocodingShould.cs b/test/Geo.MapBox.Tests/Services/MapBoxGeocodingShould.cs
index 3e73e45..cbfd5f7 100644
--- a/test/Geo.MapBox.Tests/Services/MapBoxGeocodingShould.cs
+++ b/test/Geo.MapBox.Tests/Services/MapBoxGeocodingShould.cs
@@ -7,7 +7,6 @@ namespace Geo.MapBox.Tests.Services
{
using System;
using System.Collections.Generic;
- using System.Collections.Specialized;
using System.Globalization;
using System.Net;
using System.Net.Http;
@@ -21,6 +20,7 @@ namespace Geo.MapBox.Tests.Services
using Geo.MapBox.Models.Exceptions;
using Geo.MapBox.Models.Parameters;
using Geo.MapBox.Services;
+ using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
@@ -35,6 +35,7 @@ public class MapBoxGeocodingShould : IDisposable
{
private readonly HttpClient _httpClient;
private readonly MapBoxKeyContainer _keyContainer;
+ private readonly IGeoNETExceptionProvider _exceptionProvider;
private readonly IStringLocalizerFactory _localizerFactory;
private readonly List _responseMessages = new List();
private bool _disposed;
@@ -94,6 +95,7 @@ public MapBoxGeocodingShould()
var options = Options.Create(new LocalizationOptions { ResourcesPath = "Resources" });
_localizerFactory = new ResourceManagerStringLocalizerFactory(options, NullLoggerFactory.Instance);
_httpClient = new HttpClient(mockHandler.Object);
+ _exceptionProvider = new GeoNETExceptionProvider();
}
///
@@ -111,11 +113,13 @@ public void AddMapBoxKeySuccessfully()
{
var sut = BuildService();
- var query = new NameValueCollection();
+ var query = QueryString.Empty;
- sut.AddMapBoxKey(query);
- query.Count.Should().Be(1);
- query["access_token"].Should().Be("abc123");
+ sut.AddMapBoxKey(ref query);
+
+ var queryParameters = HttpUtility.ParseQueryString(query.ToString());
+ queryParameters.Count.Should().Be(1);
+ queryParameters["access_token"].Should().Be("abc123");
}
///
@@ -126,7 +130,7 @@ public void AddBaseParametersSuccessfully()
{
var sut = BuildService();
- var query = new NameValueCollection();
+ var query = QueryString.Empty;
var parameters = new BaseParameters()
{
Limit = 5,
@@ -142,13 +146,15 @@ public void AddBaseParametersSuccessfully()
parameters.Types.Add(FeatureType.Address);
parameters.Types.Add(FeatureType.Place);
- sut.AddBaseParameters(parameters, query);
- query.Count.Should().Be(5);
- query["country"].Should().Be("CA,FR");
- query["language"].Should().Be("en,fr-FR");
- query["limit"].Should().Be("5");
- query["routing"].Should().Be("true");
- query["types"].Should().Be("address,place");
+ sut.AddBaseParameters(parameters, ref query);
+
+ var queryParameters = HttpUtility.ParseQueryString(query.ToString());
+ queryParameters.Count.Should().Be(5);
+ queryParameters["country"].Should().Be("CA,FR");
+ queryParameters["language"].Should().Be("en,fr-FR");
+ queryParameters["limit"].Should().Be("5");
+ queryParameters["routing"].Should().Be("true");
+ queryParameters["types"].Should().Be("address,place");
}
///
@@ -207,6 +213,29 @@ public void BuildGeocodingRequestSuccessfully()
path.Should().Contain("mapbox.places/123 East");
}
+ [Fact]
+ public void BuildGeocodingRequest_WithCharacterNeedingEncoding_SuccessfullyBuildsAnEncodedUrl()
+ {
+ var sut = BuildService();
+
+ var parameters = new GeocodingParameters()
+ {
+ Query = "123 East #425",
+ Limit = 5,
+ Routing = true,
+ };
+
+ var uri = sut.BuildGeocodingRequest(parameters);
+ var query = HttpUtility.UrlDecode(uri.PathAndQuery);
+ query.Should().Contain("limit=5");
+ query.Should().Contain("routing=true");
+ query.Should().Contain("access_token=abc123");
+
+ var path = HttpUtility.UrlDecode(uri.AbsolutePath);
+ path.Should().Contain("mapbox.places/123 East #425");
+ uri.AbsolutePath.Should().Contain("mapbox.places/123%20East%20%23425");
+ }
+
///
/// Tests the building of the geocoding parameters fails if no query is provided.
///
@@ -479,7 +508,7 @@ protected virtual void Dispose(bool disposing)
private MapBoxGeocoding BuildService()
{
- return new MapBoxGeocoding(_httpClient, _keyContainer, _localizerFactory);
+ return new MapBoxGeocoding(_httpClient, _keyContainer, _exceptionProvider, _localizerFactory);
}
}
}
\ No newline at end of file
diff --git a/test/Geo.MapQuest.Tests/Services/MapQuestGeocodingShould.cs b/test/Geo.MapQuest.Tests/Services/MapQuestGeocodingShould.cs
index 634ff9c..df79b88 100644
--- a/test/Geo.MapQuest.Tests/Services/MapQuestGeocodingShould.cs
+++ b/test/Geo.MapQuest.Tests/Services/MapQuestGeocodingShould.cs
@@ -7,7 +7,6 @@ namespace Geo.MapQuest.Tests.Services
{
using System;
using System.Collections.Generic;
- using System.Collections.Specialized;
using System.Net;
using System.Net.Http;
using System.Threading;
@@ -20,6 +19,7 @@ namespace Geo.MapQuest.Tests.Services
using Geo.MapQuest.Models.Exceptions;
using Geo.MapQuest.Models.Parameters;
using Geo.MapQuest.Services;
+ using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
@@ -35,6 +35,7 @@ public class MapQuestGeocodingShould : IDisposable
private readonly HttpClient _httpClient;
private readonly MapQuestKeyContainer _keyContainer;
private readonly MapQuestEndpoint _endpoint;
+ private readonly IGeoNETExceptionProvider _exceptionProvider;
private readonly IStringLocalizerFactory _localizerFactory;
private readonly List _responseMessages = new List();
private bool _disposed;
@@ -106,6 +107,7 @@ public MapQuestGeocodingShould()
var options = Options.Create(new LocalizationOptions { ResourcesPath = "Resources" });
_localizerFactory = new ResourceManagerStringLocalizerFactory(options, NullLoggerFactory.Instance);
_httpClient = new HttpClient(mockHandler.Object);
+ _exceptionProvider = new GeoNETExceptionProvider();
}
///
@@ -123,11 +125,13 @@ public void AddMapBoxKeySuccessfully()
{
var sut = BuildService();
- var query = new NameValueCollection();
+ var query = QueryString.Empty;
- sut.AddMapQuestKey(query);
- query.Count.Should().Be(1);
- query["key"].Should().Be("abc123");
+ sut.AddMapQuestKey(ref query);
+
+ var queryParameters = HttpUtility.ParseQueryString(query.ToString());
+ queryParameters.Count.Should().Be(1);
+ queryParameters["key"].Should().Be("abc123");
}
///
@@ -138,15 +142,17 @@ public void AddBaseParametersSuccessfully()
{
var sut = BuildService();
- var query = new NameValueCollection();
+ var query = QueryString.Empty;
var parameters = new BaseParameters()
{
IncludeThumbMaps = true,
};
- sut.AddBaseParameters(parameters, query);
- query.Count.Should().Be(1);
- query["thumbMaps"].Should().Be("true");
+ sut.AddBaseParameters(parameters, ref query);
+
+ var queryParameters = HttpUtility.ParseQueryString(query.ToString());
+ queryParameters.Count.Should().Be(1);
+ queryParameters["thumbMaps"].Should().Be("true");
}
///
@@ -476,7 +482,7 @@ protected virtual void Dispose(bool disposing)
private MapQuestGeocoding BuildService(MapQuestEndpoint endpoint = null)
{
- return new MapQuestGeocoding(_httpClient, _keyContainer, endpoint ?? _endpoint, _localizerFactory);
+ return new MapQuestGeocoding(_httpClient, _keyContainer, endpoint ?? _endpoint, _exceptionProvider, _localizerFactory);
}
}
}
\ No newline at end of file