Files
Antifraude.Net/Antifraude.Net/GestionaDenunciasAN/Services/ApiDenunciasClient.cs
Pedro 6f9392f3d0 cambiso en denuncias, mejoras de 30 min en inicio de sesion e incorporacion de grupos
Cambiso de certificado en registro de personal para aañadir el origen
2026-07-27 08:46:41 +02:00

402 lines
16 KiB
C#

using System.Net;
using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text.Json;
using GestionaDenuncias.Shared.Models;
using Microsoft.AspNetCore.Components.Authorization;
namespace GestionaDenunciasAN.Services;
public sealed class ApiDenunciasClient
{
public const string HttpClientName = "ApiDenuncias";
public const string AccessTokenClaim = "api_access_token";
public const string TokenExpiresAtClaim = "api_token_expires_at";
private static readonly JsonSerializerOptions JsonOptions = new(JsonSerializerDefaults.Web);
private readonly IHttpClientFactory _httpClientFactory;
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly AuthenticationStateProvider _authenticationStateProvider;
private readonly ILogger<ApiDenunciasClient> _logger;
public ApiDenunciasClient(
IHttpClientFactory httpClientFactory,
IHttpContextAccessor httpContextAccessor,
AuthenticationStateProvider authenticationStateProvider,
ILogger<ApiDenunciasClient> logger)
{
_httpClientFactory = httpClientFactory;
_httpContextAccessor = httpContextAccessor;
_authenticationStateProvider = authenticationStateProvider;
_logger = logger;
}
public Task<ApiLoginResponse> LoginAsync(LoginRequest request, CancellationToken cancellationToken = default)
=> SendAsync<ApiLoginResponse>(HttpMethod.Post, "api/auth/login", request, authorize: false, cancellationToken);
public Task<ApiLoginPrepareResponse> PrepareLoginAsync(ApiLoginPrepareRequest request, CancellationToken cancellationToken = default)
=> SendAsync<ApiLoginPrepareResponse>(HttpMethod.Post, "api/auth/login/prepare", request, authorize: false, cancellationToken);
public Task<ApiLoginResponse> CompleteLoginAsync(ApiLoginCompleteRequest request, CancellationToken cancellationToken = default)
=> SendAsync<ApiLoginResponse>(HttpMethod.Post, "api/auth/login/complete", request, authorize: false, cancellationToken);
public Task LogoutAsync(CancellationToken cancellationToken = default)
=> SendAsync<object?>(HttpMethod.Post, "api/auth/logout", body: null, authorize: true, cancellationToken);
public Task<ApiGlobalLeaksSessionDto?> GetGlobalLeaksSessionAsync(CancellationToken cancellationToken = default)
=> SendAsync<ApiGlobalLeaksSessionDto?>(HttpMethod.Get, "api/inbox/session", body: null, authorize: true, cancellationToken, allowNull: true);
public Task<ApiGlobalLeaksSessionDto?> KeepGlobalLeaksSessionAliveAsync(CancellationToken cancellationToken = default)
=> SendAsync<ApiGlobalLeaksSessionDto?>(
HttpMethod.Post,
"api/inbox/session/keepalive",
body: null,
authorize: true,
cancellationToken,
allowNull: true);
public Task<ApiLoginPrepareResponse> PrepareGlobalLeaksSessionRenewalAsync(CancellationToken cancellationToken = default)
=> SendAsync<ApiLoginPrepareResponse>(
HttpMethod.Post,
"api/inbox/session/renew/prepare",
body: null,
authorize: true,
cancellationToken);
public Task<ApiGlobalLeaksSessionDto> RenewGlobalLeaksSessionAsync(
string authcode,
string? pendingLoginId = null,
CancellationToken cancellationToken = default)
=> SendAsync<ApiGlobalLeaksSessionDto>(
HttpMethod.Post,
"api/inbox/session/renew",
new RenewGlobalLeaksSessionRequest(authcode, pendingLoginId),
authorize: true,
cancellationToken);
public Task ClearGlobalLeaksSessionAsync(CancellationToken cancellationToken = default)
=> SendAsync<object?>(HttpMethod.Post, "api/inbox/session/clear", body: null, authorize: true, cancellationToken);
public Task<InboxSnapshotResponse> LoadInboxAsync(CancellationToken cancellationToken = default)
=> SendAsync<InboxSnapshotResponse>(HttpMethod.Get, "api/inbox/reports", body: null, authorize: true, cancellationToken);
public Task<ImportSummary> ImportReportAsync(
ReportDto report,
bool confirmDifferentOwner = false,
CancellationToken cancellationToken = default)
=> SendAsync<ImportSummary>(
HttpMethod.Post,
$"api/inbox/reports/{Uri.EscapeDataString(report.Id)}/import",
new ImportReportRequest(report, confirmDifferentOwner),
authorize: true,
cancellationToken);
public Task<ReportDetailDto> GetReportDetailAsync(
string reportId,
string? lastAccess = null,
CancellationToken cancellationToken = default)
{
var path = $"api/inbox/reports/{Uri.EscapeDataString(reportId)}/detail";
if (!string.IsNullOrWhiteSpace(lastAccess))
{
path += $"?lastAccess={Uri.EscapeDataString(lastAccess)}";
}
return SendAsync<ReportDetailDto>(
HttpMethod.Get,
path,
body: null,
authorize: true,
cancellationToken);
}
public Task<GestionaCreateFileResponse> CreateGestionaFileAsync(
string subject,
string documentSeries,
string siaCode,
CancellationToken cancellationToken = default)
=> PostAsync<GestionaCreateFileResponse>(
"api/gestiona/files",
new GestionaCreateFileRequest(subject, documentSeries, siaCode),
cancellationToken);
public Task OpenGestionaFileAsync(
string fileUrl,
string? fileOpenUrl,
string assignedGroupCode,
bool confidential,
string freeTitle,
CancellationToken cancellationToken = default)
=> PostAsync(
"api/gestiona/files/open",
new GestionaOpenFileRequest(fileUrl, fileOpenUrl, assignedGroupCode, confidential, freeTitle),
cancellationToken);
public Task AssignGestionaFileAsync(
string fileUrl,
string assignedGroupCode,
CancellationToken cancellationToken = default)
=> PostAsync(
"api/gestiona/files/assignees",
new GestionaAssignFileRequest(fileUrl, assignedGroupCode),
cancellationToken);
public Task<GestionaExpedienteInfo?> GetGestionaExpedienteAsync(
string fileUrl,
CancellationToken cancellationToken = default)
=> GetAsync<GestionaExpedienteInfo?>(
$"api/gestiona/files/by-url?fileUrl={Uri.EscapeDataString(fileUrl)}",
cancellationToken,
allowNull: true);
public Task<GestionaAuditInfo?> GetGestionaFileLatestAuditAsync(
string fileUrl,
CancellationToken cancellationToken = default)
=> GetAsync<GestionaAuditInfo?>(
$"api/gestiona/files/audit/latest?fileUrl={Uri.EscapeDataString(fileUrl)}",
cancellationToken,
allowNull: true);
public Task<List<GestionaUploadHistoryEntry>> GetGestionaUploadHistoryAsync(CancellationToken cancellationToken = default)
=> GetAsync<List<GestionaUploadHistoryEntry>>("api/denuncias/gestiona-history", cancellationToken);
public Task RegisterGestionaUploadHistoryAsync(
GestionaUploadHistoryCreateRequest request,
CancellationToken cancellationToken = default)
=> PostAsync("api/denuncias/gestiona-history", request, cancellationToken);
public Task<GestionaEnsureThirdResponse> EnsureGestionaThirdAndLinkAsync(
string fileUrl,
ThirdPartyIdentityData thirdParty,
CancellationToken cancellationToken = default)
=> PostAsync<GestionaEnsureThirdResponse>(
"api/gestiona/thirds/ensure-link",
new GestionaEnsureThirdRequest(fileUrl, thirdParty),
cancellationToken);
public Task<GestionaCreateFolderResponse> CreateGestionaFolderAsync(
string fileUrl,
string folderName,
CancellationToken cancellationToken = default)
=> PostAsync<GestionaCreateFolderResponse>(
"api/gestiona/folders",
new GestionaCreateFolderRequest(fileUrl, folderName),
cancellationToken);
public async Task<string> UploadGestionaDocumentAsync(
string fileUrl,
byte[] contentBytes,
string fileName,
CancellationToken cancellationToken = default)
{
var response = await PostAsync<GestionaUploadDocumentResponse>(
"api/gestiona/documents",
new GestionaUploadDocumentRequest(fileUrl, contentBytes, fileName),
cancellationToken);
return response.DocumentUrl;
}
public Task TramitarGestionaDocumentAsync(
string documentUrl,
string assignedGroupCode,
int? complaintId,
bool isUpdate = false,
CancellationToken cancellationToken = default)
=> PostAsync(
"api/gestiona/documents/tramitar",
new GestionaTramitarDocumentoRequest(documentUrl, assignedGroupCode, complaintId, isUpdate),
cancellationToken);
public Task<List<ExpedienteTerceroDto>> GetGestionaExpedientesPorTerceroAsync(
string nif,
DateTimeOffset? desde = null,
DateTimeOffset? hasta = null,
int maxPages = 1,
int maxResults = 30,
int maxParallel = 6,
CancellationToken cancellationToken = default)
{
var query = new List<string>
{
$"maxPages={maxPages}",
$"maxResults={maxResults}",
$"maxParallel={maxParallel}"
};
if (desde.HasValue)
{
query.Add($"desde={Uri.EscapeDataString(desde.Value.ToString("O"))}");
}
if (hasta.HasValue)
{
query.Add($"hasta={Uri.EscapeDataString(hasta.Value.ToString("O"))}");
}
var path = $"api/gestiona/thirds/{Uri.EscapeDataString(nif)}/files?{string.Join('&', query)}";
return GetAsync<List<ExpedienteTerceroDto>>(path, cancellationToken);
}
public Task<ManualPurgeResponse> ExecuteManualPurgeAsync(
string date,
CancellationToken cancellationToken = default)
=> PostAsync<ManualPurgeResponse>(
"api/purge/manual",
new ManualPurgeRequest(date),
cancellationToken);
public Task<ManualPurgeResponse> ExecuteCurrentManualPurgeAsync(
CancellationToken cancellationToken = default)
=> PostAsync<ManualPurgeResponse>(
"api/purge/manual/current",
body: null,
cancellationToken);
public Task<AppConfigurationDto> GetAppConfigurationAsync(CancellationToken cancellationToken = default)
=> GetAsync<AppConfigurationDto>("api/configuration", cancellationToken);
public Task<AppConfigurationDto> UpdateExternalUpdateCutoffDateAsync(
string? date,
CancellationToken cancellationToken = default)
=> SendAsync<AppConfigurationDto>(
HttpMethod.Put,
"api/configuration/external-update-cutoff",
new UpdateExternalUpdateCutoffRequest(date),
authorize: true,
cancellationToken);
public Task<WorkGroupAdministrationDto> GetWorkGroupAdministrationAsync(
CancellationToken cancellationToken = default)
=> GetAsync<WorkGroupAdministrationDto>(
"api/configuration/work-groups",
cancellationToken);
public Task<WorkGroupAdministrationDto> UpdateUserWorkGroupsAsync(
string username,
IReadOnlyList<string> groupCodes,
CancellationToken cancellationToken = default)
=> SendAsync<WorkGroupAdministrationDto>(
HttpMethod.Put,
$"api/configuration/work-groups/users/{Uri.EscapeDataString(username)}",
new UpdateUserWorkGroupsRequest(groupCodes),
authorize: true,
cancellationToken);
internal Task<T> GetAsync<T>(string path, CancellationToken cancellationToken = default, bool allowNull = false)
=> SendAsync<T>(HttpMethod.Get, path, body: null, authorize: true, cancellationToken, allowNull);
internal Task<T> PostAsync<T>(string path, object? body, CancellationToken cancellationToken = default)
=> SendAsync<T>(HttpMethod.Post, path, body, authorize: true, cancellationToken);
internal Task PostAsync(string path, object? body, CancellationToken cancellationToken = default)
=> SendAsync<object?>(HttpMethod.Post, path, body, authorize: true, cancellationToken);
private async Task<T> SendAsync<T>(
HttpMethod method,
string path,
object? body,
bool authorize,
CancellationToken cancellationToken,
bool allowNull = false)
{
var client = _httpClientFactory.CreateClient(HttpClientName);
using var request = new HttpRequestMessage(method, path);
if (body is not null)
{
request.Content = JsonContent.Create(body, options: JsonOptions);
}
if (authorize)
{
var token = await GetAccessTokenAsync();
if (string.IsNullOrWhiteSpace(token))
{
_logger.LogWarning("No hay token de API disponible para llamar a {Path}. Usuario autenticado={IsAuthenticated}",
path,
_httpContextAccessor.HttpContext?.User.Identity?.IsAuthenticated);
throw new UnauthorizedAccessException("No hay token de API activo. Vuelve a iniciar sesion.");
}
_logger.LogInformation("Llamando a API protegida {Path}. Token presente={TokenPresent}", path, true);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
}
using var response = await client.SendAsync(request, cancellationToken);
if (response.StatusCode == HttpStatusCode.Unauthorized)
{
var message = await ReadErrorMessageAsync(response, cancellationToken);
if (!string.IsNullOrWhiteSpace(message) &&
!message.StartsWith("La API de denuncias ha respondido con", StringComparison.OrdinalIgnoreCase))
{
throw new UnauthorizedAccessException(message);
}
throw new UnauthorizedAccessException(authorize
? "La sesion de la aplicacion ha caducado. Vuelve a iniciar sesion."
: "No se ha podido autorizar la peticion. Vuelve a iniciar sesion.");
}
if (!response.IsSuccessStatusCode)
{
var message = await ReadErrorMessageAsync(response, cancellationToken);
throw new InvalidOperationException(message);
}
if (typeof(T) == typeof(object))
{
return default!;
}
var result = await response.Content.ReadFromJsonAsync<T>(JsonOptions, cancellationToken);
if (result is null && !allowNull)
{
throw new InvalidOperationException("La API de denuncias no ha devuelto contenido valido.");
}
return result!;
}
private async Task<string?> GetAccessTokenAsync()
{
var token = _httpContextAccessor.HttpContext?.User.FindFirst(AccessTokenClaim)?.Value;
if (!string.IsNullOrWhiteSpace(token))
{
return token;
}
try
{
var authState = await _authenticationStateProvider.GetAuthenticationStateAsync();
return authState.User.FindFirst(AccessTokenClaim)?.Value;
}
catch
{
return null;
}
}
private static async Task<string> ReadErrorMessageAsync(HttpResponseMessage response, CancellationToken cancellationToken)
{
try
{
var apiError = await response.Content.ReadFromJsonAsync<ApiError>(JsonOptions, cancellationToken);
if (!string.IsNullOrWhiteSpace(apiError?.Error))
{
return apiError.Error;
}
}
catch
{
// Fallback to raw content below.
}
var raw = await response.Content.ReadAsStringAsync(cancellationToken);
return string.IsNullOrWhiteSpace(raw)
? $"La API de denuncias ha respondido con {(int)response.StatusCode}."
: raw;
}
}