SomniaGuestLog - Address search/query using Nominatim - Initial implementation

Initial implementation for address search/query using external APIs (OSM Nominatim) & Added required classes for supporting more APIs in the future.
This commit is contained in:
2026-08-07 22:08:25 +02:00
parent 2335c223ea
commit 3fe558c231
12 changed files with 322 additions and 5 deletions
@@ -0,0 +1,109 @@
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.Options;
using Somnia.GuestLog.Models.Configuration;
using Somnia.GuestLog.Models.DTOs;
namespace Somnia.GuestLog.Services.AddressQuery;
public class NominatimAddressQueryService(
IHttpClientFactory httpClientFactory,
ILogger<NominatimAddressQueryService> logger,
IOptions<AddressQueryConfig> configOptions
) : IAddressQueryService
{
private const string defaultBaseAddr = "https://nominatim.openstreetmap.org";
private const string defaultUserAgent = "Somnia-GuestLog";
private const string apiUri = "/search";
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);
List<ApiResult> apiList = (await http.GetFromJsonAsync<List<ApiResult>>(uri)) ?? [];
foreach (ApiResult result in apiList)
{
if (result.Address is null)
{
logger.LogWarning("Skipping result without address details. '{result}'", result);
continue;
}
rsList.Add(new()
{
HouseNumber = result.Address.House_number,
AddressRoad = result.Address.Road ?? result.Address.Square,
AddressCity = result.Address.City ?? result.Address.Municipality ?? result.Address.Village ?? result.Address.Village,
AddressProvince = result.Address.Province,
AddressState = result.Address.State,
AddressCountry = result.Address.Country,
ZipCode = result.Address.Postcode
});
}
return rsList;
}
catch (System.Exception ex)
{
logger.LogError(ex, "Error getting info from API!");
return [];
}
}
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.AcceptLanguage.ParseAdd("es");
return client;
}
private static string GetRequestUri(string query)
{
return QueryHelpers.AddQueryString(apiUri, [
new KeyValuePair<string, string?>("format", "jsonv2"),
new KeyValuePair<string, string?>("addressdetails", "1"),
new KeyValuePair<string, string?>("layer", "address"),
new KeyValuePair<string, string?>("q", query),
]);
}
private record class ApiResultAddress(
string? House_number = null,
string? Road = null,
string? Square = null,
string? City = null,
string? Municipality = null,
string? Village = null,
string? Town = null,
string? Province = null,
string? State = null,
string? Country = null,
string? Postcode = null
);
private record class ApiResult(
string? Osm_type = null,
long? Osm_id = null,
string? Category = null,
string? AddressType = null,
string? Name = null,
string? Display_name = null,
ApiResultAddress? Address = null
);
}