From 78ed49a077a34a96f15d31b6c08da55c682b82a8 Mon Sep 17 00:00:00 2001 From: TekH Date: Thu, 25 Jun 2026 13:16:57 +0200 Subject: [PATCH] Refactor AuthService and add new service classes Refactored `AuthService` to introduce a reusable `CreateDefaultClient` method, reducing code duplication. Updated all relevant methods in `AuthService` to use this new method. Added `CultureService` to manage application culture/localization, including support for setting, getting, and initializing culture from `localStorage` or browser settings. Introduced `DocReceiverElementService` for retrieving document receiver elements (signatures) and `EnvelopeService` for managing envelope data retrieval with optional filters. Both services include error handling and consistent JSON deserialization. These changes improve code maintainability, reusability, and adhere to the single responsibility principle. --- .../Services/AuthService.cs | 16 ++-- .../Services/CultureService.cs | 74 +++++++++++++++++++ .../Services/DocReceiverElementService.cs | 24 ++++++ .../Services/EnvelopeService.cs | 72 ++++++++++++++++++ 4 files changed, 179 insertions(+), 7 deletions(-) create mode 100644 EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Services/CultureService.cs create mode 100644 EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Services/DocReceiverElementService.cs create mode 100644 EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Services/EnvelopeService.cs diff --git a/EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Services/AuthService.cs b/EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Services/AuthService.cs index 12db705b..c5e74eef 100644 --- a/EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Services/AuthService.cs +++ b/EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Services/AuthService.cs @@ -1,5 +1,4 @@ using System.Net; -using System.Net.Http; using System.Net.Http.Json; namespace EnvelopeGenerator.Server.Client.Services; @@ -10,6 +9,7 @@ public enum SenderLoginResult { Success, InvalidCredentials, Error } public class AuthService(IHttpClientFactory httpClientFactory) { + private HttpClient CreateDefaultClient() => httpClientFactory.CreateClient("EnvelopeGenerator.Server"); /// /// Checks whether the current user holds a valid receiver token for the given envelope key. @@ -17,7 +17,7 @@ public class AuthService(IHttpClientFactory httpClientFactory) /// public async Task CheckEnvelopeAccessAsync(string envelopeKey, CancellationToken cancel = default) { - using var http = httpClientFactory.CreateClient("EnvelopeGenerator.Server"); + using var http = CreateDefaultClient(); var response = await http.GetAsync($"/api/auth/check/envelope/{Uri.EscapeDataString(envelopeKey)}", cancel); return response.StatusCode == HttpStatusCode.OK; } @@ -29,9 +29,11 @@ public class AuthService(IHttpClientFactory httpClientFactory) /// public async Task LoginEnvelopeReceiverAsync(string envelopeKey, string accessCode, CancellationToken cancel = default) { - using var http = httpClientFactory.CreateClient("EnvelopeGenerator.Server"); - var form = new MultipartFormDataContent(); - form.Add(new StringContent(accessCode), "AccessCode"); + using var http = CreateDefaultClient(); + var form = new MultipartFormDataContent + { + { new StringContent(accessCode), "AccessCode" } + }; var response = await http.PostAsync( $"/api/Auth/envelope-receiver/{Uri.EscapeDataString(envelopeKey)}", @@ -52,7 +54,7 @@ public class AuthService(IHttpClientFactory httpClientFactory) /// public async Task LogoutEnvelopeReceiverAsync(string envelopeKey, CancellationToken cancel = default) { - using var http = httpClientFactory.CreateClient("EnvelopeGenerator.Server"); + using var http = CreateDefaultClient(); var response = await http.PostAsync( $"/api/auth/logout/envelope/{Uri.EscapeDataString(envelopeKey)}", null, cancel); @@ -66,7 +68,7 @@ public class AuthService(IHttpClientFactory httpClientFactory) /// public async Task LoginSenderAsync(string username, string password, CancellationToken cancel = default) { - using var http = httpClientFactory.CreateClient("EnvelopeGenerator.Server"); + using var http = CreateDefaultClient(); var requestBody = new { username, password }; var response = await http.PostAsJsonAsync( diff --git a/EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Services/CultureService.cs b/EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Services/CultureService.cs new file mode 100644 index 00000000..a7b6a7b6 --- /dev/null +++ b/EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Services/CultureService.cs @@ -0,0 +1,74 @@ +using System.Globalization; +using Microsoft.JSInterop; + +namespace EnvelopeGenerator.Server.Client.Services; + +/// +/// Service for managing application culture/localization. +/// +public class CultureService +{ + private readonly IJSRuntime _jsRuntime; + private const string CULTURE_KEY = "AppCulture"; + + public CultureService(IJSRuntime jsRuntime) + { + _jsRuntime = jsRuntime; + } + + /// + /// Gets the list of supported cultures. + /// + public static CultureInfo[] SupportedCultures { get; } = new[] + { + new CultureInfo("de-DE"), + new CultureInfo("en-US"), + new CultureInfo("fr-FR") + }; + + /// + /// Sets the application culture and stores it in localStorage. + /// + public async Task SetCultureAsync(string culture) + { + if (!SupportedCultures.Any(c => c.Name == culture)) + throw new ArgumentException($"Culture '{culture}' is not supported.", nameof(culture)); + + await _jsRuntime.InvokeVoidAsync("localStorage.setItem", CULTURE_KEY, culture); + } + + /// + /// Gets the stored culture from localStorage. + /// + public async Task GetCultureAsync() + { + try + { + return await _jsRuntime.InvokeAsync("localStorage.getItem", CULTURE_KEY); + } + catch + { + return null; + } + } + + /// + /// Initializes the culture from localStorage or browser settings. + /// + public async Task InitializeCultureAsync() + { + var storedCulture = await GetCultureAsync(); + + if (!string.IsNullOrEmpty(storedCulture) && + SupportedCultures.Any(c => c.Name == storedCulture)) + { + return new CultureInfo(storedCulture); + } + + // Fallback to browser culture or default + var browserCulture = CultureInfo.CurrentCulture.Name; + var matchedCulture = SupportedCultures.FirstOrDefault(c => c.Name == browserCulture); + + return matchedCulture ?? SupportedCultures[0]; // Default to German + } +} diff --git a/EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Services/DocReceiverElementService.cs b/EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Services/DocReceiverElementService.cs new file mode 100644 index 00000000..b64e242e --- /dev/null +++ b/EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Services/DocReceiverElementService.cs @@ -0,0 +1,24 @@ +using System.Net.Http.Json; +using System.Text.Json; +using EnvelopeGenerator.Server.Client.Models; +using EnvelopeGenerator.Server.Client.Options; +using Microsoft.Extensions.Options; + +namespace EnvelopeGenerator.Server.Client.Services; + +public class DocReceiverElementService(HttpClient http, IOptions apiOptions) +{ + private static readonly JsonSerializerOptions _jsonOptions = new(JsonSerializerDefaults.Web); + + public async Task> GetAsync(string envelopeKey, CancellationToken cancel = default) + { + var url = $"{apiOptions.Value.BaseUrl}/api/DocReceiverElement/{Uri.EscapeDataString(envelopeKey)}"; + var response = await http.GetAsync(url, cancel); + + if (!response.IsSuccessStatusCode) + throw new HttpRequestException($"Failed to retrieve signatures for envelope {envelopeKey}: {response.StatusCode} {response.ReasonPhrase}"); + + var result = await response.Content.ReadFromJsonAsync>(_jsonOptions, cancel); + return result ?? []; + } +} diff --git a/EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Services/EnvelopeService.cs b/EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Services/EnvelopeService.cs new file mode 100644 index 00000000..1acfa669 --- /dev/null +++ b/EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Services/EnvelopeService.cs @@ -0,0 +1,72 @@ +using System.Net.Http.Json; +using System.Text.Json; +using EnvelopeGenerator.Server.Client.Models; +using EnvelopeGenerator.Server.Client.Options; +using Microsoft.AspNetCore.WebUtilities; +using Microsoft.Extensions.Options; + +namespace EnvelopeGenerator.Server.Client.Services; + +/// +/// Retrieves s from the API. +/// +public class EnvelopeService +{ + private readonly HttpClient _http; + private readonly ApiOptions _apiOptions; + private static readonly JsonSerializerOptions _jsonOptions = new(JsonSerializerDefaults.Web); + + public EnvelopeService(HttpClient http, IOptions apiOptions) + { + _http = http; + _apiOptions = apiOptions.Value; + } + + /// + /// Fetches envelopes from the API with optional filters. + /// + /// Thrown when the API request fails. + public async Task?> GetAsync( + int? id = null, + string? uuid = null, + bool? onlyActive = null, + bool? onlyCompleted = null, + CancellationToken cancel = default) + { + var baseUrl = $"{_apiOptions.BaseUrl}/api/Envelope"; + var queryParams = new Dictionary(); + + if (id.HasValue) + { + queryParams["Id"] = id.Value.ToString(); + } + if (!string.IsNullOrEmpty(uuid)) + { + queryParams["Uuid"] = uuid; + } + if (onlyActive.HasValue) + { + queryParams["OnlyActive"] = onlyActive.Value.ToString(); + } + if (onlyCompleted.HasValue) + { + queryParams["OnlyCompleted"] = onlyCompleted.Value.ToString(); + } + + var url = QueryHelpers.AddQueryString(baseUrl, queryParams); + + var response = await _http.GetAsync(url, cancel); + + if (!response.IsSuccessStatusCode) + { + var statusCode = (int)response.StatusCode; + var reasonPhrase = response.ReasonPhrase ?? "Unknown error"; + throw new HttpRequestException( + $"Failed to load envelopes. Status: {statusCode} ({reasonPhrase})", + null, + response.StatusCode); + } + + return await response.Content.ReadFromJsonAsync>(_jsonOptions, cancel); + } +}