Retry Bancas

This commit is contained in:
2025-08-20 18:02:17 -03:00
parent 1a6f7dd5a3
commit 4fb2b87aa1

View File

@@ -1,13 +1,12 @@
using Elecciones.Core.DTOs; using Elecciones.Core.DTOs;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using System.Collections.Generic; using System.Collections.Generic;
using System.Net.Http; using System.Net.Http;
using System.Net.Http.Json; using System.Net.Http.Json;
using System.Text.Json; using System.Text.Json;
using Microsoft.Extensions.Logging; using System.Threading.RateLimiting;
using System.Threading.Tasks; using System.Threading.Tasks;
using static Elecciones.Core.DTOs.BancaDto;
//using System.Threading.RateLimiting;
namespace Elecciones.Infrastructure.Services; namespace Elecciones.Infrastructure.Services;
@@ -16,29 +15,25 @@ public class ElectoralApiService : IElectoralApiService
private readonly IHttpClientFactory _httpClientFactory; private readonly IHttpClientFactory _httpClientFactory;
private readonly IConfiguration _configuration; private readonly IConfiguration _configuration;
private readonly ILogger<ElectoralApiService> _logger; private readonly ILogger<ElectoralApiService> _logger;
//private readonly RateLimiter _rateLimiter; private readonly RateLimiter _rateLimiter;
public ElectoralApiService(IHttpClientFactory httpClientFactory, public ElectoralApiService(
IHttpClientFactory httpClientFactory,
IConfiguration configuration, IConfiguration configuration,
ILogger<ElectoralApiService> logger) ILogger<ElectoralApiService> logger,
//RateLimiter rateLimiter) RateLimiter rateLimiter)
{ {
_httpClientFactory = httpClientFactory; _httpClientFactory = httpClientFactory;
_configuration = configuration; _configuration = configuration;
_logger = logger; _logger = logger;
//_rateLimiter = rateLimiter; _rateLimiter = rateLimiter;
} }
public async Task<TokenResponse?> GetAuthTokenAsync() public async Task<TokenResponse?> GetAuthTokenAsync()
{ {
// "Pedir una ficha". Este método ahora devuelve un "lease" (permiso).
// Si no hay fichas, esperará aquí automáticamente hasta que se rellene el cubo.
/*
using RateLimitLease lease = await _rateLimiter.AcquireAsync(1); using RateLimitLease lease = await _rateLimiter.AcquireAsync(1);
// Si se nos concede el permiso para proceder...
if (lease.IsAcquired) if (lease.IsAcquired)
{*/ {
var client = _httpClientFactory.CreateClient("ElectoralApiClient"); var client = _httpClientFactory.CreateClient("ElectoralApiClient");
var username = _configuration["ElectoralApi:Username"]; var username = _configuration["ElectoralApi:Username"];
var password = _configuration["ElectoralApi:Password"]; var password = _configuration["ElectoralApi:Password"];
@@ -51,11 +46,9 @@ public class ElectoralApiService : IElectoralApiService
var response = await client.SendAsync(request); var response = await client.SendAsync(request);
if (!response.IsSuccessStatusCode) return null; if (!response.IsSuccessStatusCode) return null;
// Ahora esto es válido, porque el método devuelve Task<TokenResponse?>
return await response.Content.ReadFromJsonAsync<TokenResponse>(); return await response.Content.ReadFromJsonAsync<TokenResponse>();
/* } }
// Si no se pudo obtener un permiso (ej. la cola está llena), devolvemos null. return null;
return null;*/
} }
public async Task<CatalogoDto?> GetCatalogoAmbitosAsync(string authToken, int categoriaId) public async Task<CatalogoDto?> GetCatalogoAmbitosAsync(string authToken, int categoriaId)
@@ -131,17 +124,11 @@ public class ElectoralApiService : IElectoralApiService
public async Task<RepartoBancasDto?> GetBancasAsync(string authToken, string distritoId, string? seccionProvincialId, int categoriaId) public async Task<RepartoBancasDto?> GetBancasAsync(string authToken, string distritoId, string? seccionProvincialId, int categoriaId)
{ {
// "Pedir una ficha". Este método ahora devuelve un "lease" (permiso).
// Si no hay fichas, esperará aquí automáticamente hasta que se rellene el cubo.
/*
using RateLimitLease lease = await _rateLimiter.AcquireAsync(1); using RateLimitLease lease = await _rateLimiter.AcquireAsync(1);
if (!lease.IsAcquired) return null;
// Si se nos concede el permiso para proceder...
if (lease.IsAcquired)
{*/
var client = _httpClientFactory.CreateClient("ElectoralApiClient"); var client = _httpClientFactory.CreateClient("ElectoralApiClient");
var requestUri = $"/api/resultados/getBancas?distritoId={distritoId}&categoriaId={categoriaId}"; var requestUri = $"/api/resultados/getBancas?distritoId={distritoId}&categoriaId={categoriaId}";
if (!string.IsNullOrEmpty(seccionProvincialId)) if (!string.IsNullOrEmpty(seccionProvincialId))
{ {
requestUri += $"&seccionProvincialId={seccionProvincialId}"; requestUri += $"&seccionProvincialId={seccionProvincialId}";
@@ -150,44 +137,34 @@ public class ElectoralApiService : IElectoralApiService
var request = new HttpRequestMessage(HttpMethod.Get, requestUri); var request = new HttpRequestMessage(HttpMethod.Get, requestUri);
request.Headers.Add("Authorization", $"Bearer {authToken}"); request.Headers.Add("Authorization", $"Bearer {authToken}");
HttpResponseMessage response;
try try
{ {
response = await client.SendAsync(request); var response = await client.SendAsync(request);
}
catch (Exception ex)
{
// Captura errores de red (ej. la API se cae momentáneamente)
_logger.LogError(ex, "La petición HTTP a getBancas falló. URI: {requestUri}", requestUri);
return null;
}
// Comprobamos que la respuesta fue exitosa Y que contiene datos antes de intentar leerla. // --- CORRECCIÓN FINAL ---
if (response.IsSuccessStatusCode && response.Content?.Headers.ContentLength > 0) // Eliminamos la comprobación de ContentLength. Confiamos en el try-catch.
if (response.IsSuccessStatusCode)
{ {
try try
{ {
// Solo si hay contenido, intentamos deserializar.
return await response.Content.ReadFromJsonAsync<RepartoBancasDto>(); return await response.Content.ReadFromJsonAsync<RepartoBancasDto>();
} }
catch (JsonException ex) catch (JsonException ex)
{ {
// Si el contenido no es un JSON válido, lo registramos y devolvemos null. // Esto se activará si el cuerpo está vacío o no es un JSON válido.
_logger.LogWarning(ex, "La API devolvió una respuesta no-JSON para getBancas. URI: {requestUri}, Status: {statusCode}", requestUri, response.StatusCode); _logger.LogWarning(ex, "La API devolvió una respuesta no-JSON para getBancas. URI: {uri}", requestUri);
return null; return null;
} }
} }
else if (!response.IsSuccessStatusCode)
{
// Si la API devolvió un error HTTP, lo registramos.
_logger.LogWarning("La API devolvió un código de error {statusCode} para getBancas. URI: {requestUri}", response.StatusCode, requestUri);
}
// Si la respuesta fue 200 OK pero con cuerpo vacío, o si fue un error HTTP, devolvemos null. _logger.LogWarning("La API devolvió un código de error {statusCode} para getBancas. URI: {uri}", response.StatusCode, requestUri);
return null; return null;
/* } }
// Si no se pudo obtener un permiso (ej. la cola está llena), devolvemos null. catch (Exception ex)
return null;*/ {
_logger.LogError(ex, "La petición HTTP a getBancas falló. URI: {uri}", requestUri);
return null;
}
} }
public async Task<List<string>?> GetTelegramasTotalizadosAsync(string authToken, string distritoId, string seccionId, int? categoriaId = null) public async Task<List<string>?> GetTelegramasTotalizadosAsync(string authToken, string distritoId, string seccionId, int? categoriaId = null)
@@ -221,31 +198,20 @@ public class ElectoralApiService : IElectoralApiService
public async Task<TelegramaFileDto?> GetTelegramaFileAsync(string authToken, string mesaId) public async Task<TelegramaFileDto?> GetTelegramaFileAsync(string authToken, string mesaId)
{ {
// "Pedir una ficha". Este método ahora devuelve un "lease" (permiso).
// Si no hay fichas, esperará aquí automáticamente hasta que se rellene el cubo.
/*
using RateLimitLease lease = await _rateLimiter.AcquireAsync(1); using RateLimitLease lease = await _rateLimiter.AcquireAsync(1);
if (!lease.IsAcquired) return null;
// Si se nos concede el permiso para proceder...
if (lease.IsAcquired)
{*/
var client = _httpClientFactory.CreateClient("ElectoralApiClient"); var client = _httpClientFactory.CreateClient("ElectoralApiClient");
var requestUri = $"/api/resultados/getFile?mesaId={mesaId}"; var requestUri = $"/api/resultados/getFile?mesaId={mesaId}";
var request = new HttpRequestMessage(HttpMethod.Get, requestUri); var request = new HttpRequestMessage(HttpMethod.Get, requestUri);
request.Headers.Add("Authorization", $"Bearer {authToken}"); request.Headers.Add("Authorization", $"Bearer {authToken}");
HttpResponseMessage response;
try try
{ {
response = await client.SendAsync(request); var response = await client.SendAsync(request);
}
catch (Exception ex)
{
_logger.LogError(ex, "La petición HTTP a getFile falló para la mesa {mesaId}", mesaId);
return null;
}
if (response.IsSuccessStatusCode && response.Content?.Headers.ContentLength > 0) // --- APLICAMOS LA MISMA CORRECCIÓN AQUÍ POR CONSISTENCIA ---
if (response.IsSuccessStatusCode)
{ {
try try
{ {
@@ -253,22 +219,19 @@ public class ElectoralApiService : IElectoralApiService
} }
catch (JsonException ex) catch (JsonException ex)
{ {
// Si la deserialización falla, ahora lo sabremos exactamente. _logger.LogWarning(ex, "La API devolvió una respuesta no-JSON para getFile para la mesa {mesaId}", mesaId);
_logger.LogWarning(ex, "La API devolvió una respuesta no-JSON para la mesa {mesaId}. Status: {statusCode}", mesaId, response.StatusCode);
return null; return null;
} }
} }
else if (!response.IsSuccessStatusCode)
{
_logger.LogWarning("La API devolvió un código de error {statusCode} para la mesa {mesaId}", response.StatusCode, mesaId);
}
// Si la respuesta fue exitosa pero sin contenido, no es necesario loguearlo como un error,
// simplemente devolvemos null y el Worker lo ignorará.
_logger.LogWarning("La API devolvió un código de error {statusCode} para la mesa {mesaId}", response.StatusCode, mesaId);
return null; return null;
/* } }
// Si no se pudo obtener un permiso (ej. la cola está llena), devolvemos null. catch (Exception ex)
return null;*/ {
_logger.LogError(ex, "La petición HTTP a getFile falló para la mesa {mesaId}", mesaId);
return null;
}
} }
public async Task<ResumenDto?> GetResumenAsync(string authToken, string distritoId) public async Task<ResumenDto?> GetResumenAsync(string authToken, string distritoId)