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 NominatimAddressQueryService( IHttpClientFactory httpClientFactory, ILogger logger, IOptions 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> SearchAddressAsync(string query) { var http = GetHttpClient(); List rsList = []; try { string uri = GetRequestUri(query); List apiList = (await http.GetFromJsonAsync>(uri)) ?? []; foreach (ApiResult result in apiList) { if (result.Address is null) { logger.LogWarning("Skipping result without address details. '{result}'", result); continue; } string? alpha3CountryCode = CountryCodesUtils.Alpha2ToAlpha3(result.Address.Country_code); rsList.Add(new() { HouseNumber = result.Address.House_number, AddressRoad = result.Address.Road ?? result.Address.Square, AddressCity = result.Address.City ?? result.Address.Municipality ?? result.Address.Town ?? result.Address.Village, AddressProvince = result.Address.Province, AddressState = result.Address.State, AddressCountry = result.Address.Country, ZipCode = result.Address.Postcode, AddressCountryCode = alpha3CountryCode }); } 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); if (config.ResultLang is not null) { client.DefaultRequestHeaders.AcceptLanguage.ParseAdd(config.ResultLang); } return client; } private static string GetRequestUri(string query) { return QueryHelpers.AddQueryString(apiUri, [ new KeyValuePair("format", "jsonv2"), new KeyValuePair("addressdetails", "1"), new KeyValuePair("layer", "address"), new KeyValuePair("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, string? Country_code = 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 ); }