SHA256
GuestLog - Added AddressQuery service using Google Maps Geocode API.
This commit is contained in:
@@ -15,7 +15,7 @@ public class AddressQueryConfig
|
|||||||
public enum AddressQueryProviderEnum
|
public enum AddressQueryProviderEnum
|
||||||
{
|
{
|
||||||
Nominatim,
|
Nominatim,
|
||||||
GoogleApi
|
GoogleApiV4
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ public class ConfigFileAddressQueryFactory(
|
|||||||
public static void RegisterFactoryItems(IServiceCollection services)
|
public static void RegisterFactoryItems(IServiceCollection services)
|
||||||
{
|
{
|
||||||
services.AddKeyedTransient<IAddressQueryService, NominatimAddressQueryService>(AddressQueryConfig.AddressQueryProviderEnum.Nominatim);
|
services.AddKeyedTransient<IAddressQueryService, NominatimAddressQueryService>(AddressQueryConfig.AddressQueryProviderEnum.Nominatim);
|
||||||
|
services.AddKeyedTransient<IAddressQueryService, GoogleApiAddressQueryService>(AddressQueryConfig.AddressQueryProviderEnum.GoogleApiV4);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IAddressQueryService GetService()
|
public IAddressQueryService GetService()
|
||||||
|
|||||||
@@ -0,0 +1,125 @@
|
|||||||
|
using Microsoft.AspNetCore.WebUtilities;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using Somnia.GuestLog.Models.Configuration;
|
||||||
|
using Somnia.GuestLog.Models.DTOs;
|
||||||
|
using Somnia.GuestLog.Utils;
|
||||||
|
|
||||||
|
namespace Somnia.GuestLog.Services.AddressQuery;
|
||||||
|
|
||||||
|
public class GoogleApiAddressQueryService(
|
||||||
|
IHttpClientFactory httpClientFactory,
|
||||||
|
ILogger<GoogleApiAddressQueryService> logger,
|
||||||
|
IOptions<AddressQueryConfig> configOptions
|
||||||
|
) : IAddressQueryService
|
||||||
|
{
|
||||||
|
private const string defaultBaseAddr = "https://geocode.googleapis.com/v4/";
|
||||||
|
private const string defaultUserAgent = "Somnia-GuestLog";
|
||||||
|
private const string apiUri = "geocode/address/";
|
||||||
|
|
||||||
|
private readonly AddressQueryConfig config = configOptions.Value;
|
||||||
|
|
||||||
|
|
||||||
|
public async Task<List<AddressResultDTO>> SearchAddressAsync(string query)
|
||||||
|
{
|
||||||
|
var http = GetHttpClient();
|
||||||
|
List<AddressResultDTO> rsList = [];
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string uri = GetRequestUri(query);
|
||||||
|
GeocodeAddressResponse? apiList = await http.GetFromJsonAsync<GeocodeAddressResponse>(uri)
|
||||||
|
?? throw new InvalidOperationException("Response without results??");
|
||||||
|
|
||||||
|
foreach (GeocodeResult result in apiList.Results)
|
||||||
|
{
|
||||||
|
if (result.PostalAddress is null)
|
||||||
|
{
|
||||||
|
logger.LogWarning("Skipping result without PostalAddress. '{result}'", result);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
string? alpha3CountryCode = CountryCodesUtils.Alpha2ToAlpha3(result.PostalAddress.RegionCode);
|
||||||
|
string? addressLine = result.PostalAddress.AddressLines is not null ? string.Join(", ", result.PostalAddress.AddressLines) : null;
|
||||||
|
|
||||||
|
string[]? formatedParts = result.FormattedAddress?.Split(",");
|
||||||
|
if (formatedParts is not null && formatedParts.Length < 2)
|
||||||
|
{
|
||||||
|
formatedParts = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
rsList.Add(new()
|
||||||
|
{
|
||||||
|
HouseNumber = null,
|
||||||
|
AddressRoad = addressLine,
|
||||||
|
AddressCity = result.PostalAddress.Locality,
|
||||||
|
AddressProvince = result.PostalAddress.AdministrativeArea,
|
||||||
|
AddressState = null,
|
||||||
|
AddressCountry = formatedParts?[^1],
|
||||||
|
ZipCode = result.PostalAddress.PostalCode,
|
||||||
|
AddressCountryCode = alpha3CountryCode
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return rsList;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
logger.LogError(ex, "Error getting info from API!");
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Internal Methods
|
||||||
|
|
||||||
|
private HttpClient GetHttpClient()
|
||||||
|
{
|
||||||
|
HttpClient client = httpClientFactory.CreateClient();
|
||||||
|
|
||||||
|
Uri baseAddr = new(config.ServiceURL ?? defaultBaseAddr);
|
||||||
|
|
||||||
|
client.BaseAddress = baseAddr;
|
||||||
|
client.DefaultRequestHeaders.UserAgent.ParseAdd(config.UserAgent ?? defaultUserAgent);
|
||||||
|
client.DefaultRequestHeaders.Add("X-Goog-Api-Key", config.ApiKey);
|
||||||
|
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetRequestUri(string query)
|
||||||
|
{
|
||||||
|
string escapedQuery = Uri.EscapeDataString(query).TrimStart('/');
|
||||||
|
string finalUri = $"{apiUri.TrimEnd('/')}/{escapedQuery}";
|
||||||
|
|
||||||
|
List<KeyValuePair<string, string?>> queryParams = [];
|
||||||
|
if (config.ResultLang is not null)
|
||||||
|
{
|
||||||
|
queryParams.Add(new KeyValuePair<string, string?>("languageCode", config.ResultLang));
|
||||||
|
}
|
||||||
|
|
||||||
|
return QueryHelpers.AddQueryString(finalUri, queryParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Internal Models
|
||||||
|
|
||||||
|
private record class PostalAddress(
|
||||||
|
string[]? AddressLines = null,
|
||||||
|
string? PostalCode = null,
|
||||||
|
|
||||||
|
string? AdministrativeArea = null, //Province
|
||||||
|
string? Locality = null, //City
|
||||||
|
string? RegionCode = null //Country Code (CLDR region code)
|
||||||
|
);
|
||||||
|
|
||||||
|
private record class GeocodeResult(
|
||||||
|
string? FormattedAddress = null,
|
||||||
|
PostalAddress? PostalAddress = null
|
||||||
|
);
|
||||||
|
|
||||||
|
private record class GeocodeAddressResponse(
|
||||||
|
GeocodeResult[] Results = null!
|
||||||
|
);
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
}
|
||||||
@@ -11,8 +11,12 @@
|
|||||||
"AllowedHosts": "*",
|
"AllowedHosts": "*",
|
||||||
"AddressQuery": {
|
"AddressQuery": {
|
||||||
"EnableAddressQuery": true,
|
"EnableAddressQuery": true,
|
||||||
|
// Nominatim
|
||||||
"Provider": "Nominatim",
|
"Provider": "Nominatim",
|
||||||
"ServiceURL": "https://nominatim.openstreetmap.org/",
|
"ServiceURL": "https://nominatim.openstreetmap.org/",
|
||||||
|
// Google Map API
|
||||||
|
// "Provider": "GoogleApiV4",
|
||||||
|
// "ServiceURL": "https://geocode.googleapis.com/v4/",
|
||||||
"ApiKey": "",
|
"ApiKey": "",
|
||||||
"UserAgent": "Somnia-GuestLog-Dev",
|
"UserAgent": "Somnia-GuestLog-Dev",
|
||||||
"ResultLang": "es"
|
"ResultLang": "es"
|
||||||
|
|||||||
Reference in New Issue
Block a user