402 lines
16 KiB
C#
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;
|
|
}
|
|
}
|