From dfb48e02cc1df3cca31b6180bc200925491cc690 Mon Sep 17 00:00:00 2001
From: JustinC <67930245+JustinCanton@users.noreply.github.com>
Date: Sat, 20 Aug 2022 13:26:13 -0400
Subject: [PATCH] 1.2.0 Release (#47)
* feat(core): adding extra information and logging surrounding exceptions (#44)
* feat(core): adding extra information and logging surrounding exceptions
Adding extra information into the data including the http status code response, and the uri that failed.
Updating exception handling to use an exception provider.
Creating a base Exception class that prints out the exception data on ToString.
* refactor: fixing code warnings
* test(core): adding tests for the exception provider
* fix(mapbox-geocoding): 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)
* fix(mapbox-geocoding): fixing an issue where the query is not properly encoded
* feat: updating how query strings are created to be more inline with standards
* test(mapbox-geocoding): adding tests for encoding of query information
* feat(mapbox-geocoding): changing the encoding method to UrlEncoder for consistency
* docs: updating the README and CHANGELOG
---
CHANGELOG.md | 15 +-
README.md | 18 +-
.../Models/Exceptions/ArcGISException.cs | 3 +-
src/Geo.ArcGIS/Services/ArcGISGeocoding.cs | 115 ++++++------
.../Models/Exceptions/BingException.cs | 3 +-
src/Geo.Bing/Services/BingGeocoding.cs | 61 +++----
.../Abstractions/IGeoNETExceptionProvider.cs | 26 +++
src/Geo.Core/ClientExecutor.cs | 95 +++++-----
.../ServiceCollectionExtensions.cs | 4 +-
src/Geo.Core/Extensions/LoggerExtensions.cs | 32 ++++
src/Geo.Core/GeoNETExceptionProvider.cs | 101 +++++++++++
src/Geo.Core/Models/CallResult.cs | 64 +++++++
.../Models/Exceptions/GeoCoreException.cs | 69 ++++++++
.../Models/Exceptions/GoogleException.cs | 3 +-
src/Geo.Google/Services/GoogleGeocoding.cs | 163 +++++++++---------
.../Models/Exceptions/HereException.cs | 3 +-
src/Geo.Here/Services/HereGeocoding.cs | 113 ++++++------
.../Models/Exceptions/MapBoxException.cs | 3 +-
src/Geo.MapBox/README.md | 2 +-
src/Geo.MapBox/Services/MapBoxGeocoding.cs | 60 +++----
.../Models/Exceptions/MapQuestException.cs | 3 +-
.../Services/MapQuestGeocoding.cs | 59 +++----
src/Source.props | 9 +
.../Services/ArcGISGeocodingShould.cs | 36 ++--
.../Services/BingGeocodingShould.cs | 38 ++--
test/Geo.Core.Tests/ClientExecutorShould.cs | 44 +++--
.../GeoNETExceptionProviderTests.cs | 47 +++++
test/Geo.Core.Tests/GlobalSuppressions.cs | 9 +
.../Models/TestClientExecutor.cs | 7 +-
test/Geo.Core.Tests/Models/TestException.cs | 3 +-
.../Services/GoogleGeocodingShould.cs | 110 +++++++-----
.../Services/HereGeocodingShould.cs | 144 +++++++++-------
.../Services/MapBoxGeocodingShould.cs | 57 ++++--
.../Services/MapQuestGeocodingShould.cs | 26 +--
34 files changed, 999 insertions(+), 546 deletions(-)
create mode 100644 src/Geo.Core/Abstractions/IGeoNETExceptionProvider.cs
create mode 100644 src/Geo.Core/Extensions/LoggerExtensions.cs
create mode 100644 src/Geo.Core/GeoNETExceptionProvider.cs
create mode 100644 src/Geo.Core/Models/CallResult.cs
create mode 100644 src/Geo.Core/Models/Exceptions/GeoCoreException.cs
create mode 100644 test/Geo.Core.Tests/GeoNETExceptionProviderTests.cs
create mode 100644 test/Geo.Core.Tests/GlobalSuppressions.cs
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