Retry Bancas
This commit is contained in:
		| @@ -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) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user