GuestLog - Added AddressQuery service using Google Maps Geocode API.

This commit is contained in:
2026-08-12 14:10:18 +02:00
parent 8232e7d7c9
commit ec7efd867b
4 changed files with 131 additions and 1 deletions
@@ -15,7 +15,7 @@ public class AddressQueryConfig
public enum AddressQueryProviderEnum
{
Nominatim,
GoogleApi
GoogleApiV4
}
}
@@ -12,6 +12,7 @@ public class ConfigFileAddressQueryFactory(
public static void RegisterFactoryItems(IServiceCollection services)
{
services.AddKeyedTransient<IAddressQueryService, NominatimAddressQueryService>(AddressQueryConfig.AddressQueryProviderEnum.Nominatim);
services.AddKeyedTransient<IAddressQueryService, GoogleApiAddressQueryService>(AddressQueryConfig.AddressQueryProviderEnum.GoogleApiV4);
}
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
}
+4
View File
@@ -11,8 +11,12 @@
"AllowedHosts": "*",
"AddressQuery": {
"EnableAddressQuery": true,
// Nominatim
"Provider": "Nominatim",
"ServiceURL": "https://nominatim.openstreetmap.org/",
// Google Map API
// "Provider": "GoogleApiV4",
// "ServiceURL": "https://geocode.googleapis.com/v4/",
"ApiKey": "",
"UserAgent": "Somnia-GuestLog-Dev",
"ResultLang": "es"