From 3ae1b94eb7599df9cd63ff7229907d2e2b21fd50 Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Tue, 29 Apr 2025 11:39:21 +0200 Subject: [PATCH 01/94] refactor(Login): Id in UserId umbenennen --- EnvelopeGenerator.GeneratorAPI/Models/Login.cs | 4 ++-- EnvelopeGenerator.GeneratorAPI/Properties/launchSettings.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/EnvelopeGenerator.GeneratorAPI/Models/Login.cs b/EnvelopeGenerator.GeneratorAPI/Models/Login.cs index 7aaeea50..a3f9d30e 100644 --- a/EnvelopeGenerator.GeneratorAPI/Models/Login.cs +++ b/EnvelopeGenerator.GeneratorAPI/Models/Login.cs @@ -6,8 +6,8 @@ namespace EnvelopeGenerator.GeneratorAPI.Models; /// Repräsentiert ein Login-Modell mit erforderlichem Passwort und optionaler ID und Benutzername. /// /// Das erforderliche Passwort für das Login. -/// Die optionale ID des Benutzers. +/// Die optionale ID des Benutzers. /// Der optionale Benutzername. -public record Login([Required] string Password, int? Id = null, string? Username = null) +public record Login([Required] string Password, int? UserId = null, string? Username = null) { } diff --git a/EnvelopeGenerator.GeneratorAPI/Properties/launchSettings.json b/EnvelopeGenerator.GeneratorAPI/Properties/launchSettings.json index 8857b777..ec221d16 100644 --- a/EnvelopeGenerator.GeneratorAPI/Properties/launchSettings.json +++ b/EnvelopeGenerator.GeneratorAPI/Properties/launchSettings.json @@ -22,7 +22,7 @@ "https": { "commandName": "Project", "dotnetRunMessages": true, - "launchBrowser": true, + "launchBrowser": false, "launchUrl": "swagger", "applicationUrl": "https://localhost:7174;http://localhost:5131", "environmentVariables": { From 33048e185bf60f0ae7202c9f98197b5724d5b6fb Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Tue, 29 Apr 2025 14:41:21 +0200 Subject: [PATCH 02/94] feat(CreateEnvelopeReceiverCommandHandler): initialized --- .../CreateEnvelopeReceiverCommandHandler.cs | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverCommandHandler.cs diff --git a/EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverCommandHandler.cs b/EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverCommandHandler.cs new file mode 100644 index 00000000..edc15d8b --- /dev/null +++ b/EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverCommandHandler.cs @@ -0,0 +1,24 @@ +using MediatR; + +namespace EnvelopeGenerator.Application.EnvelopeReceivers.Commands.Create; + +/// +/// Handles the creation of an envelope along with its associated document and recipients. +/// This command processes the envelope data, including title, message, document content, +/// recipient list, and optional two-factor authentication settings. +/// +public class CreateEnvelopeReceiverCommandHandler : IRequestHandler +{ + /// + /// Handles the execution of the . + /// Responsible for validating input data, creating or retrieving recipients, associating signatures, + /// and storing the envelope and document details. + /// + /// The command containing all necessary information to create an envelope. + /// Token to observe while waiting for the task to complete. + /// A task representing the asynchronous operation. + public Task Handle(CreateEnvelopeReceiverCommand request, CancellationToken cancellationToken) + { + throw new NotImplementedException(); + } +} From 3b4ad2960a7134df184103d881205921a7ab51c2 Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Tue, 29 Apr 2025 16:26:26 +0200 Subject: [PATCH 03/94] =?UTF-8?q?feat:=20SQLExecutor-Klasse=20f=C3=BCr=20d?= =?UTF-8?q?ie=20Ausf=C3=BChrung=20von=20Roh-SQL-Abfragen=20mit=20Entity=20?= =?UTF-8?q?Framework=20Core=20hinzuf=C3=BCgen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Contracts/SQLExecutor/ISQLExecutor.cs | 35 +++++++++++++++++++ .../EnvelopeGenerator.Application.csproj | 4 +++ .../SQLExecutor.cs | 32 +++++++++++++++++ 3 files changed, 71 insertions(+) create mode 100644 EnvelopeGenerator.Application/Contracts/SQLExecutor/ISQLExecutor.cs create mode 100644 EnvelopeGenerator.Infrastructure/SQLExecutor.cs diff --git a/EnvelopeGenerator.Application/Contracts/SQLExecutor/ISQLExecutor.cs b/EnvelopeGenerator.Application/Contracts/SQLExecutor/ISQLExecutor.cs new file mode 100644 index 00000000..e147218f --- /dev/null +++ b/EnvelopeGenerator.Application/Contracts/SQLExecutor/ISQLExecutor.cs @@ -0,0 +1,35 @@ +namespace EnvelopeGenerator.Application.Contracts.SQLExecutor; + +/// +/// Defines methods for executing raw SQL queries and mapping the results to objects. +/// +/// The entity type to which the SQL query results will be mapped. +public interface ISQLExecutor +{ + /// + /// Executes a raw SQL query and returns the first result or null if no result is found. + /// + /// The raw SQL query to execute. + /// Optional cancellation token. + /// Optional parameters for the SQL query. + /// The first result, or null if none found. + Task ExecuteFirstAsync(string sql, CancellationToken cancellation = default, params object[] parameters); + + /// + /// Executes a raw SQL query and expects a single result, or null if no result is found. + /// + /// The raw SQL query to execute. + /// Optional cancellation token. + /// Optional parameters for the SQL query. + /// The single result, or null if none found. + Task ExecuteSingleAsync(string sql, CancellationToken cancellation = default, params object[] parameters); + + /// + /// Executes a raw SQL query and returns all results as a collection of . + /// + /// The raw SQL query to execute. + /// Optional cancellation token. + /// Optional parameters for the SQL query. + /// An containing all matching results. + Task> ExecuteAllAsync(string sql, CancellationToken cancellation = default, params object[] parameters); +} diff --git a/EnvelopeGenerator.Application/EnvelopeGenerator.Application.csproj b/EnvelopeGenerator.Application/EnvelopeGenerator.Application.csproj index 73ba45fe..0406d46a 100644 --- a/EnvelopeGenerator.Application/EnvelopeGenerator.Application.csproj +++ b/EnvelopeGenerator.Application/EnvelopeGenerator.Application.csproj @@ -80,4 +80,8 @@ + + + + diff --git a/EnvelopeGenerator.Infrastructure/SQLExecutor.cs b/EnvelopeGenerator.Infrastructure/SQLExecutor.cs new file mode 100644 index 00000000..fdb69dd4 --- /dev/null +++ b/EnvelopeGenerator.Infrastructure/SQLExecutor.cs @@ -0,0 +1,32 @@ +using EnvelopeGenerator.Application.Contracts.SQLExecutor.cs; +using Microsoft.EntityFrameworkCore; + +namespace EnvelopeGenerator.Infrastructure; + +public sealed class SQLExecutor : ISQLExecutor where T : class +{ + private readonly EGDbContext _context; + + public SQLExecutor(EGDbContext context) + { + _context = context; + } + + public async Task ExecuteFirstAsync(string sql, CancellationToken cancellation = default, params object[] parameters) + => await _context + .Set() + .FromSqlRaw(sql, parameters) + .FirstOrDefaultAsync(cancellation); + + public async Task ExecuteSingleAsync(string sql, CancellationToken cancellation = default, params object[] parameters) + => await _context + .Set() + .FromSqlRaw(sql, parameters) + .SingleOrDefaultAsync(cancellation); + + public async Task> ExecuteAllAsync(string sql, CancellationToken cancellation = default, params object[] parameters) + => await _context + .Set() + .FromSqlRaw(sql, parameters) + .ToListAsync(cancellation); +} From 5331efe3c137e7dcf9c4a1d257cea886329c71b6 Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Tue, 29 Apr 2025 16:39:18 +0200 Subject: [PATCH 04/94] =?UTF-8?q?feat(sql):=20Hinzuf=C3=BCgen=20generische?= =?UTF-8?q?r=20=C3=9Cberladungen=20zu=20SQLExecutor=20unter=20Verwendung?= =?UTF-8?q?=20von=20ISQL=20=C3=BCber=20DI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Es wurden Überladungen für ExecuteFirstAsync, ExecuteSingleAsync und ExecuteAllAsync eingeführt, die SQL-Definitionen aus dem Dependency Injection Container mittels ISQL auflösen. Außerdem wurde ein Fehler bei der Verwendung der Direktive von .cs zu standardmäßigem Schnittstellenimport korrigiert. --- .../Contracts/SQLExecutor/ISQL.cs | 20 ++++++++++++++++ .../Contracts/SQLExecutor/ISQLExecutor.cs | 6 +++++ .../SQLExecutor.cs | 23 ++++++++++++++++++- 3 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 EnvelopeGenerator.Application/Contracts/SQLExecutor/ISQL.cs diff --git a/EnvelopeGenerator.Application/Contracts/SQLExecutor/ISQL.cs b/EnvelopeGenerator.Application/Contracts/SQLExecutor/ISQL.cs new file mode 100644 index 00000000..00c0cd8d --- /dev/null +++ b/EnvelopeGenerator.Application/Contracts/SQLExecutor/ISQL.cs @@ -0,0 +1,20 @@ +namespace EnvelopeGenerator.Application.Contracts.SQLExecutor; + +/// +/// Represents a raw SQL query contract. +/// +public interface ISQL +{ + /// + /// Gets the raw SQL query string. + /// + string Raw { get; } +} + +/// +/// Represents a typed SQL query contract for a specific entity. +/// +/// The type of the entity associated with the SQL query. +public interface ISQL : ISQL +{ +} \ No newline at end of file diff --git a/EnvelopeGenerator.Application/Contracts/SQLExecutor/ISQLExecutor.cs b/EnvelopeGenerator.Application/Contracts/SQLExecutor/ISQLExecutor.cs index e147218f..245657e9 100644 --- a/EnvelopeGenerator.Application/Contracts/SQLExecutor/ISQLExecutor.cs +++ b/EnvelopeGenerator.Application/Contracts/SQLExecutor/ISQLExecutor.cs @@ -32,4 +32,10 @@ public interface ISQLExecutor /// Optional parameters for the SQL query. /// An containing all matching results. Task> ExecuteAllAsync(string sql, CancellationToken cancellation = default, params object[] parameters); + + Task ExecuteFirstAsync(CancellationToken cancellation = default, params object[] parameters) where TSQL : ISQL; + + Task ExecuteSingleAsync(CancellationToken cancellation = default, params object[] parameters) where TSQL : ISQL; + + Task> ExecuteAllAsync(CancellationToken cancellation = default, params object[] parameters) where TSQL : ISQL; } diff --git a/EnvelopeGenerator.Infrastructure/SQLExecutor.cs b/EnvelopeGenerator.Infrastructure/SQLExecutor.cs index fdb69dd4..050beb28 100644 --- a/EnvelopeGenerator.Infrastructure/SQLExecutor.cs +++ b/EnvelopeGenerator.Infrastructure/SQLExecutor.cs @@ -1,5 +1,6 @@ -using EnvelopeGenerator.Application.Contracts.SQLExecutor.cs; +using EnvelopeGenerator.Application.Contracts.SQLExecutor; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; namespace EnvelopeGenerator.Infrastructure; @@ -7,6 +8,8 @@ public sealed class SQLExecutor : ISQLExecutor where T : class { private readonly EGDbContext _context; + private readonly IServiceProvider _provider; + public SQLExecutor(EGDbContext context) { _context = context; @@ -29,4 +32,22 @@ public sealed class SQLExecutor : ISQLExecutor where T : class .Set() .FromSqlRaw(sql, parameters) .ToListAsync(cancellation); + + public Task ExecuteFirstAsync(CancellationToken cancellation = default, params object[] parameters) where TSQL : ISQL + { + var sql = _provider.GetRequiredService(); + return ExecuteFirstAsync(sql.Raw); + } + + public Task ExecuteSingleAsync(CancellationToken cancellation = default, params object[] parameters) where TSQL : ISQL + { + var sql = _provider.GetRequiredService(); + return ExecuteSingleAsync(sql.Raw); + } + + public Task> ExecuteAllAsync(CancellationToken cancellation = default, params object[] parameters) where TSQL : ISQL + { + var sql = _provider.GetRequiredService(); + return ExecuteAllAsync(sql.Raw); + } } From 6e82b24578753262ad1069703749f61ddd9b1b1f Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Tue, 29 Apr 2025 16:56:13 +0200 Subject: [PATCH 05/94] feat: Implement QueryExecutor for executing queries using IQueryable - Added a record `QueryExecutor` implementing `IQueryExecutor` for executing common query operations like First, Single, and ToList both synchronously and asynchronously. - Methods leverage the IQueryable interface to perform database queries for entity types. --- .../Contracts/SQLExecutor/IQueryExecutor.cs | 70 +++++++++++++++++++ .../QueryExecutor.cs | 34 +++++++++ 2 files changed, 104 insertions(+) create mode 100644 EnvelopeGenerator.Application/Contracts/SQLExecutor/IQueryExecutor.cs create mode 100644 EnvelopeGenerator.Infrastructure/QueryExecutor.cs diff --git a/EnvelopeGenerator.Application/Contracts/SQLExecutor/IQueryExecutor.cs b/EnvelopeGenerator.Application/Contracts/SQLExecutor/IQueryExecutor.cs new file mode 100644 index 00000000..b64073e9 --- /dev/null +++ b/EnvelopeGenerator.Application/Contracts/SQLExecutor/IQueryExecutor.cs @@ -0,0 +1,70 @@ +namespace EnvelopeGenerator.Application.Contracts.SQLExecutor; + +/// +/// Provides methods for executing common Entity Framework queries on a given entity type. +/// This interface abstracts away the direct usage of Entity Framework methods for querying data +/// and provides asynchronous and synchronous operations for querying a collection or single entity. +/// +/// The type of the entity being queried. +public interface IQueryExecutor +{ + /// + /// Asynchronously retrieves the first entity or a default value if no entity is found. + /// + /// A task that represents the asynchronous operation. The task result contains the entity or a default value. + public Task FirstOrDefaultAsync(); + + /// + /// Asynchronously retrieves a single entity or a default value if no entity is found. + /// + /// A task that represents the asynchronous operation. The task result contains the entity or a default value. + public Task SingleOrDefaultAsync(); + + /// + /// Asynchronously retrieves a list of entities. + /// + /// A task that represents the asynchronous operation. The task result contains the list of entities. + public Task> ToListAsync(); + + /// + /// Asynchronously retrieves the first entity. Throws an exception if no entity is found. + /// + /// A task that represents the asynchronous operation. The task result contains the first entity. + public Task FirstAsync(); + + /// + /// Asynchronously retrieves a single entity. Throws an exception if no entity is found. + /// + /// A task that represents the asynchronous operation. The task result contains the single entity. + public Task SingleAsync(); + + /// + /// Synchronously retrieves the first entity or a default value if no entity is found. + /// + /// The first entity or a default value. + public TEntity? FirstOrDefault(); + + /// + /// Synchronously retrieves a single entity or a default value if no entity is found. + /// + /// The single entity or a default value. + public TEntity? SingleOrDefault(); + + /// + /// Synchronously retrieves a list of entities. + /// + /// The list of entities. + public IEnumerable ToList(); + + /// + /// Synchronously retrieves the first entity. Throws an exception if no entity is found. + /// + /// The first entity. + public TEntity First(); + + /// + /// Synchronously retrieves a single entity. Throws an exception if no entity is found. + /// + /// The single entity. + public TEntity Single(); +} diff --git a/EnvelopeGenerator.Infrastructure/QueryExecutor.cs b/EnvelopeGenerator.Infrastructure/QueryExecutor.cs new file mode 100644 index 00000000..dcf148a2 --- /dev/null +++ b/EnvelopeGenerator.Infrastructure/QueryExecutor.cs @@ -0,0 +1,34 @@ +using EnvelopeGenerator.Application.Contracts.SQLExecutor; +using Microsoft.EntityFrameworkCore; + +namespace EnvelopeGenerator.Infrastructure; + +public record QueryExecutor(IQueryable Queryable) : IQueryExecutor +{ + public TEntity First() => Queryable.First(); + + public Task FirstAsync() => Queryable.FirstAsync(); + + public TEntity? FirstOrDefault() => Queryable.FirstOrDefault(); + + + public Task FirstOrDefaultAsync() => Queryable.FirstOrDefaultAsync(); + + + public TEntity Single() => Queryable.Single(); + + + public Task SingleAsync() => Queryable.SingleAsync(); + + + public TEntity? SingleOrDefault() => Queryable.SingleOrDefault(); + + + public Task SingleOrDefaultAsync() => Queryable.SingleOrDefaultAsync(); + + + public IEnumerable ToList() => Queryable.ToList(); + + + public async Task> ToListAsync() => await Queryable.ToListAsync(); +} From 1e54b775a2879ef500679fef232214b5c94ac7a3 Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Tue, 29 Apr 2025 17:14:38 +0200 Subject: [PATCH 06/94] =?UTF-8?q?refactor:=20Vereinfachung=20der=20SQLExec?= =?UTF-8?q?utor-Implementierung=20zur=20R=C3=BCckgabe=20von=20IQueryExecut?= =?UTF-8?q?or?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Aktualisiert `SQLExecutor` um `ISQLExecutor` zu implementieren und `IQueryExecutor` für die weitere Abfrageausführung zurückzugeben. - Umstrukturierte Methoden zur Verwendung von `ToExecutor()` für die Konvertierung von rohen SQL-Abfragen in einen `IQueryExecutor`. - Geänderter Konstruktor, um `IServiceProvider` für die Injektion von Abhängigkeiten von benutzerdefinierten SQL-Abfrageklassen zu akzeptieren. --- .../Contracts/SQLExecutor/ISQLExecutor.cs | 36 +++++----------- .../QueryExecutor.cs | 34 +++++++++------ .../QueryExecutorExtension.cs | 9 ++++ .../SQLExecutor.cs | 41 ++++--------------- 4 files changed, 50 insertions(+), 70 deletions(-) create mode 100644 EnvelopeGenerator.Infrastructure/QueryExecutorExtension.cs diff --git a/EnvelopeGenerator.Application/Contracts/SQLExecutor/ISQLExecutor.cs b/EnvelopeGenerator.Application/Contracts/SQLExecutor/ISQLExecutor.cs index 245657e9..5ff732e4 100644 --- a/EnvelopeGenerator.Application/Contracts/SQLExecutor/ISQLExecutor.cs +++ b/EnvelopeGenerator.Application/Contracts/SQLExecutor/ISQLExecutor.cs @@ -1,41 +1,27 @@ namespace EnvelopeGenerator.Application.Contracts.SQLExecutor; /// -/// Defines methods for executing raw SQL queries and mapping the results to objects. +/// Defines methods for executing raw SQL queries or custom SQL query classes and returning query executors for further operations. +/// Provides abstraction for raw SQL execution as well as mapping the results to objects. /// /// The entity type to which the SQL query results will be mapped. public interface ISQLExecutor { /// - /// Executes a raw SQL query and returns the first result or null if no result is found. + /// Executes a raw SQL query and returns an for further querying operations on the result. /// /// The raw SQL query to execute. - /// Optional cancellation token. + /// Optional cancellation token for the operation. /// Optional parameters for the SQL query. - /// The first result, or null if none found. - Task ExecuteFirstAsync(string sql, CancellationToken cancellation = default, params object[] parameters); + /// An instance for further query operations on the result. + IQueryExecutor Execute(string sql, CancellationToken cancellation = default, params object[] parameters); /// - /// Executes a raw SQL query and expects a single result, or null if no result is found. + /// Executes a custom SQL query defined by a class that implements and returns an for further querying operations on the result. /// - /// The raw SQL query to execute. - /// Optional cancellation token. + /// The type of the custom SQL query class implementing . + /// Optional cancellation token for the operation. /// Optional parameters for the SQL query. - /// The single result, or null if none found. - Task ExecuteSingleAsync(string sql, CancellationToken cancellation = default, params object[] parameters); - - /// - /// Executes a raw SQL query and returns all results as a collection of . - /// - /// The raw SQL query to execute. - /// Optional cancellation token. - /// Optional parameters for the SQL query. - /// An containing all matching results. - Task> ExecuteAllAsync(string sql, CancellationToken cancellation = default, params object[] parameters); - - Task ExecuteFirstAsync(CancellationToken cancellation = default, params object[] parameters) where TSQL : ISQL; - - Task ExecuteSingleAsync(CancellationToken cancellation = default, params object[] parameters) where TSQL : ISQL; - - Task> ExecuteAllAsync(CancellationToken cancellation = default, params object[] parameters) where TSQL : ISQL; + /// An instance for further query operations on the result. + IQueryExecutor Execute(CancellationToken cancellation = default, params object[] parameters) where TSQL : ISQL; } diff --git a/EnvelopeGenerator.Infrastructure/QueryExecutor.cs b/EnvelopeGenerator.Infrastructure/QueryExecutor.cs index dcf148a2..72553370 100644 --- a/EnvelopeGenerator.Infrastructure/QueryExecutor.cs +++ b/EnvelopeGenerator.Infrastructure/QueryExecutor.cs @@ -1,34 +1,42 @@ -using EnvelopeGenerator.Application.Contracts.SQLExecutor; +using AngleSharp.Dom; +using EnvelopeGenerator.Application.Contracts.SQLExecutor; using Microsoft.EntityFrameworkCore; namespace EnvelopeGenerator.Infrastructure; -public record QueryExecutor(IQueryable Queryable) : IQueryExecutor +public sealed record QueryExecutor : IQueryExecutor { - public TEntity First() => Queryable.First(); + private readonly IQueryable _query; - public Task FirstAsync() => Queryable.FirstAsync(); + internal QueryExecutor(IQueryable queryable) + { + _query = queryable; + } - public TEntity? FirstOrDefault() => Queryable.FirstOrDefault(); + public TEntity First() => _query.First(); + public Task FirstAsync() => _query.FirstAsync(); - public Task FirstOrDefaultAsync() => Queryable.FirstOrDefaultAsync(); + public TEntity? FirstOrDefault() => _query.FirstOrDefault(); - public TEntity Single() => Queryable.Single(); + public Task FirstOrDefaultAsync() => _query.FirstOrDefaultAsync(); - public Task SingleAsync() => Queryable.SingleAsync(); + public TEntity Single() => _query.Single(); - public TEntity? SingleOrDefault() => Queryable.SingleOrDefault(); + public Task SingleAsync() => _query.SingleAsync(); - public Task SingleOrDefaultAsync() => Queryable.SingleOrDefaultAsync(); + public TEntity? SingleOrDefault() => _query.SingleOrDefault(); - public IEnumerable ToList() => Queryable.ToList(); + public Task SingleOrDefaultAsync() => _query.SingleOrDefaultAsync(); - public async Task> ToListAsync() => await Queryable.ToListAsync(); -} + public IEnumerable ToList() => _query.ToList(); + + + public async Task> ToListAsync() => await _query.ToListAsync(); +} \ No newline at end of file diff --git a/EnvelopeGenerator.Infrastructure/QueryExecutorExtension.cs b/EnvelopeGenerator.Infrastructure/QueryExecutorExtension.cs new file mode 100644 index 00000000..dbc0aa18 --- /dev/null +++ b/EnvelopeGenerator.Infrastructure/QueryExecutorExtension.cs @@ -0,0 +1,9 @@ +namespace EnvelopeGenerator.Infrastructure; + +public static class QueryExecutorExtension +{ + public static QueryExecutor ToExecutor(this IQueryable queryable) where TEntity : class + { + return new QueryExecutor(queryable); + } +} \ No newline at end of file diff --git a/EnvelopeGenerator.Infrastructure/SQLExecutor.cs b/EnvelopeGenerator.Infrastructure/SQLExecutor.cs index 050beb28..2c25840a 100644 --- a/EnvelopeGenerator.Infrastructure/SQLExecutor.cs +++ b/EnvelopeGenerator.Infrastructure/SQLExecutor.cs @@ -10,44 +10,21 @@ public sealed class SQLExecutor : ISQLExecutor where T : class private readonly IServiceProvider _provider; - public SQLExecutor(EGDbContext context) + public SQLExecutor(EGDbContext context, IServiceProvider provider) { _context = context; + _provider = provider; } - public async Task ExecuteFirstAsync(string sql, CancellationToken cancellation = default, params object[] parameters) - => await _context - .Set() - .FromSqlRaw(sql, parameters) - .FirstOrDefaultAsync(cancellation); + public IQueryExecutor Execute(string sql, CancellationToken cancellation = default, params object[] parameters) + => _context + .Set() + .FromSqlRaw(sql, parameters) + .ToExecutor(); - public async Task ExecuteSingleAsync(string sql, CancellationToken cancellation = default, params object[] parameters) - => await _context - .Set() - .FromSqlRaw(sql, parameters) - .SingleOrDefaultAsync(cancellation); - - public async Task> ExecuteAllAsync(string sql, CancellationToken cancellation = default, params object[] parameters) - => await _context - .Set() - .FromSqlRaw(sql, parameters) - .ToListAsync(cancellation); - - public Task ExecuteFirstAsync(CancellationToken cancellation = default, params object[] parameters) where TSQL : ISQL - { - var sql = _provider.GetRequiredService(); - return ExecuteFirstAsync(sql.Raw); - } - - public Task ExecuteSingleAsync(CancellationToken cancellation = default, params object[] parameters) where TSQL : ISQL - { - var sql = _provider.GetRequiredService(); - return ExecuteSingleAsync(sql.Raw); - } - - public Task> ExecuteAllAsync(CancellationToken cancellation = default, params object[] parameters) where TSQL : ISQL + public IQueryExecutor Execute(CancellationToken cancellation = default, params object[] parameters) where TSQL : ISQL { var sql = _provider.GetRequiredService(); - return ExecuteAllAsync(sql.Raw); + return Execute(sql.Raw); } } From adbfd694188872a22c53ebb6bc0c35a1953b8f2f Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Wed, 30 Apr 2025 16:49:26 +0200 Subject: [PATCH 07/94] feat(IQueryExecutor): IQuery umbenennen --- .../SQLExecutor/{IQueryExecutor.cs => IQuery.cs} | 6 +++--- .../Contracts/SQLExecutor/ISQLExecutor.cs | 12 ++++++------ .../{QueryExecutor.cs => Query.cs} | 4 ++-- .../QueryExecutorExtension.cs | 9 --------- EnvelopeGenerator.Infrastructure/QueryExtension.cs | 9 +++++++++ EnvelopeGenerator.Infrastructure/SQLExecutor.cs | 6 +++--- 6 files changed, 23 insertions(+), 23 deletions(-) rename EnvelopeGenerator.Application/Contracts/SQLExecutor/{IQueryExecutor.cs => IQuery.cs} (92%) rename EnvelopeGenerator.Infrastructure/{QueryExecutor.cs => Query.cs} (87%) delete mode 100644 EnvelopeGenerator.Infrastructure/QueryExecutorExtension.cs create mode 100644 EnvelopeGenerator.Infrastructure/QueryExtension.cs diff --git a/EnvelopeGenerator.Application/Contracts/SQLExecutor/IQueryExecutor.cs b/EnvelopeGenerator.Application/Contracts/SQLExecutor/IQuery.cs similarity index 92% rename from EnvelopeGenerator.Application/Contracts/SQLExecutor/IQueryExecutor.cs rename to EnvelopeGenerator.Application/Contracts/SQLExecutor/IQuery.cs index b64073e9..c0be3674 100644 --- a/EnvelopeGenerator.Application/Contracts/SQLExecutor/IQueryExecutor.cs +++ b/EnvelopeGenerator.Application/Contracts/SQLExecutor/IQuery.cs @@ -1,12 +1,12 @@ namespace EnvelopeGenerator.Application.Contracts.SQLExecutor; /// -/// Provides methods for executing common Entity Framework queries on a given entity type. -/// This interface abstracts away the direct usage of Entity Framework methods for querying data +/// Provides methods for executing common queries on a given entity type. +/// This interface abstracts away the direct usage of ORM libraries (such as Entity Framework) for querying data /// and provides asynchronous and synchronous operations for querying a collection or single entity. /// /// The type of the entity being queried. -public interface IQueryExecutor +public interface IQuery { /// /// Asynchronously retrieves the first entity or a default value if no entity is found. diff --git a/EnvelopeGenerator.Application/Contracts/SQLExecutor/ISQLExecutor.cs b/EnvelopeGenerator.Application/Contracts/SQLExecutor/ISQLExecutor.cs index 5ff732e4..5016c18f 100644 --- a/EnvelopeGenerator.Application/Contracts/SQLExecutor/ISQLExecutor.cs +++ b/EnvelopeGenerator.Application/Contracts/SQLExecutor/ISQLExecutor.cs @@ -8,20 +8,20 @@ public interface ISQLExecutor { /// - /// Executes a raw SQL query and returns an for further querying operations on the result. + /// Executes a raw SQL query and returns an for further querying operations on the result. /// /// The raw SQL query to execute. /// Optional cancellation token for the operation. /// Optional parameters for the SQL query. - /// An instance for further query operations on the result. - IQueryExecutor Execute(string sql, CancellationToken cancellation = default, params object[] parameters); + /// An instance for further query operations on the result. + IQuery Execute(string sql, CancellationToken cancellation = default, params object[] parameters); /// - /// Executes a custom SQL query defined by a class that implements and returns an for further querying operations on the result. + /// Executes a custom SQL query defined by a class that implements and returns an for further querying operations on the result. /// /// The type of the custom SQL query class implementing . /// Optional cancellation token for the operation. /// Optional parameters for the SQL query. - /// An instance for further query operations on the result. - IQueryExecutor Execute(CancellationToken cancellation = default, params object[] parameters) where TSQL : ISQL; + /// An instance for further query operations on the result. + IQuery Execute(CancellationToken cancellation = default, params object[] parameters) where TSQL : ISQL; } diff --git a/EnvelopeGenerator.Infrastructure/QueryExecutor.cs b/EnvelopeGenerator.Infrastructure/Query.cs similarity index 87% rename from EnvelopeGenerator.Infrastructure/QueryExecutor.cs rename to EnvelopeGenerator.Infrastructure/Query.cs index 72553370..6bd9e3c2 100644 --- a/EnvelopeGenerator.Infrastructure/QueryExecutor.cs +++ b/EnvelopeGenerator.Infrastructure/Query.cs @@ -4,11 +4,11 @@ using Microsoft.EntityFrameworkCore; namespace EnvelopeGenerator.Infrastructure; -public sealed record QueryExecutor : IQueryExecutor +public sealed record Query : IQuery { private readonly IQueryable _query; - internal QueryExecutor(IQueryable queryable) + internal Query(IQueryable queryable) { _query = queryable; } diff --git a/EnvelopeGenerator.Infrastructure/QueryExecutorExtension.cs b/EnvelopeGenerator.Infrastructure/QueryExecutorExtension.cs deleted file mode 100644 index dbc0aa18..00000000 --- a/EnvelopeGenerator.Infrastructure/QueryExecutorExtension.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace EnvelopeGenerator.Infrastructure; - -public static class QueryExecutorExtension -{ - public static QueryExecutor ToExecutor(this IQueryable queryable) where TEntity : class - { - return new QueryExecutor(queryable); - } -} \ No newline at end of file diff --git a/EnvelopeGenerator.Infrastructure/QueryExtension.cs b/EnvelopeGenerator.Infrastructure/QueryExtension.cs new file mode 100644 index 00000000..f23a7aef --- /dev/null +++ b/EnvelopeGenerator.Infrastructure/QueryExtension.cs @@ -0,0 +1,9 @@ +namespace EnvelopeGenerator.Infrastructure; + +public static class QueryExtension +{ + public static Query ToQuery(this IQueryable queryable) where TEntity : class + { + return new Query(queryable); + } +} \ No newline at end of file diff --git a/EnvelopeGenerator.Infrastructure/SQLExecutor.cs b/EnvelopeGenerator.Infrastructure/SQLExecutor.cs index 2c25840a..499f587d 100644 --- a/EnvelopeGenerator.Infrastructure/SQLExecutor.cs +++ b/EnvelopeGenerator.Infrastructure/SQLExecutor.cs @@ -16,13 +16,13 @@ public sealed class SQLExecutor : ISQLExecutor where T : class _provider = provider; } - public IQueryExecutor Execute(string sql, CancellationToken cancellation = default, params object[] parameters) + public IQuery Execute(string sql, CancellationToken cancellation = default, params object[] parameters) => _context .Set() .FromSqlRaw(sql, parameters) - .ToExecutor(); + .ToQuery(); - public IQueryExecutor Execute(CancellationToken cancellation = default, params object[] parameters) where TSQL : ISQL + public IQuery Execute(CancellationToken cancellation = default, params object[] parameters) where TSQL : ISQL { var sql = _provider.GetRequiredService(); return Execute(sql.Raw); From 841da3c26133e931e37da8af410010431c85f8d3 Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Mon, 5 May 2025 00:31:58 +0200 Subject: [PATCH 08/94] Refactor DI extensions and add SQL executor support Removed `DIExtensions.cs` and integrated its content into `DependencyExtensions.cs`, enhancing the dependency injection setup with repository and SQL executor registrations. Added a new `CreateEnvelopeSQL` class implementing the `ISQL` interface, currently with a placeholder implementation. --- .../Envelopes/Commands/CreateEnvelopeSQL.cs | 14 +++++++ ...IExtensions.cs => DependencyExtensions.cs} | 37 +++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeSQL.cs rename EnvelopeGenerator.Infrastructure/{DIExtensions.cs => DependencyExtensions.cs} (78%) diff --git a/EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeSQL.cs b/EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeSQL.cs new file mode 100644 index 00000000..f07daffa --- /dev/null +++ b/EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeSQL.cs @@ -0,0 +1,14 @@ +using EnvelopeGenerator.Application.Contracts.SQLExecutor; +using EnvelopeGenerator.Domain.Entities; + +namespace EnvelopeGenerator.Application.Envelopes.Commands; +/// +/// +/// +public class CreateEnvelopeSQL : ISQL +{ + /// + /// + /// + public string Raw => string.Empty; +} diff --git a/EnvelopeGenerator.Infrastructure/DIExtensions.cs b/EnvelopeGenerator.Infrastructure/DependencyExtensions.cs similarity index 78% rename from EnvelopeGenerator.Infrastructure/DIExtensions.cs rename to EnvelopeGenerator.Infrastructure/DependencyExtensions.cs index c457d264..1f731b2b 100644 --- a/EnvelopeGenerator.Infrastructure/DIExtensions.cs +++ b/EnvelopeGenerator.Infrastructure/DependencyExtensions.cs @@ -6,6 +6,7 @@ using Microsoft.EntityFrameworkCore; using DigitalData.Core.Infrastructure; using EnvelopeGenerator.Domain.Entities; using DigitalData.Core.Infrastructure.AutoMapper; +using EnvelopeGenerator.Application.Contracts.SQLExecutor; namespace EnvelopeGenerator.Infrastructure; @@ -59,6 +60,42 @@ public static class DIExtensions services.AddDbRepository(context => context.UserReceivers).UseAutoMapper(); services.AddDbRepository(context => context.EnvelopeReceiverReadOnlys).UseAutoMapper(); + services.AddSQLExecutor(); + services.AddSQLExecutor(); + services.AddSQLExecutor(); + services.AddSQLExecutor(); + services.AddSQLExecutor(); + + return services; + } + + public static IServiceCollection AddSQLExecutor(this IServiceCollection services) where T : class + { + services.AddSingleton, SQLExecutor>(); + + var interfaceType = typeof(ISQL<>); + var targetGenericType = interfaceType.MakeGenericType(typeof(T)); + + var implementations = AppDomain.CurrentDomain.GetAssemblies() + .SelectMany(a => + { + try { return a.GetTypes(); } + catch { return Array.Empty(); } + }) + .Where(t => + t is { IsClass: true, IsAbstract: false } && + t.GetInterfaces().Any(i => + i.IsGenericType && + i.GetGenericTypeDefinition() == interfaceType && + i.GenericTypeArguments[0] == typeof(T) + ) + ); + + foreach (var impl in implementations) + { + services.AddSingleton(impl); + } + return services; } } From 7cffc3f7bc01cdb7c258d6040c87f6e1af57d233 Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Mon, 5 May 2025 00:36:00 +0200 Subject: [PATCH 09/94] fix: Change SQLExecutor service registration to scoped Updated the `AddSQLExecutor` method in the `DIExtensions` class to register `ISQLExecutor` as a scoped service instead of a singleton. This ensures that a new instance of `SQLExecutor` is created for each request within the same scope, improving resource management and request isolation. --- EnvelopeGenerator.Infrastructure/DependencyExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EnvelopeGenerator.Infrastructure/DependencyExtensions.cs b/EnvelopeGenerator.Infrastructure/DependencyExtensions.cs index 1f731b2b..d15742be 100644 --- a/EnvelopeGenerator.Infrastructure/DependencyExtensions.cs +++ b/EnvelopeGenerator.Infrastructure/DependencyExtensions.cs @@ -71,7 +71,7 @@ public static class DIExtensions public static IServiceCollection AddSQLExecutor(this IServiceCollection services) where T : class { - services.AddSingleton, SQLExecutor>(); + services.AddScoped, SQLExecutor>(); var interfaceType = typeof(ISQL<>); var targetGenericType = interfaceType.MakeGenericType(typeof(T)); From d46aa6e2b86239e9f75057b8d1d78d50370319c3 Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Mon, 5 May 2025 02:01:01 +0200 Subject: [PATCH 10/94] Add envelope creation functionality and SQL integration - Updated `EnvelopeGenerator.Application.csproj` to include `Microsoft.Data.SqlClient` package. - Refactored `CreateEnvelopeReceiverCommand` to inherit from `CreateEnvelopeCommand`. - Enhanced `CreateEnvelopeSQL` with a SQL script for envelope creation. - Introduced `CreateEnvelopeCommand` to encapsulate envelope creation data. - Added `CreateEnvelopeCommandHandler` to process commands and interact with the database. - Created `CreateEnvelopeResponse` class for handling responses from envelope creation. --- .../EnvelopeGenerator.Application.csproj | 1 + .../Create/CreateEnvelopeReceiverCommand.cs | 5 +- .../Commands/CreateEnvelopeCommand.cs | 24 +++++++++ .../Commands/CreateEnvelopeCommandHandler.cs | 49 +++++++++++++++++++ .../Commands/CreateEnvelopeResponse.cs | 8 +++ .../Envelopes/Commands/CreateEnvelopeSQL.cs | 17 ++++++- 6 files changed, 101 insertions(+), 3 deletions(-) create mode 100644 EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeCommand.cs create mode 100644 EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeCommandHandler.cs create mode 100644 EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeResponse.cs diff --git a/EnvelopeGenerator.Application/EnvelopeGenerator.Application.csproj b/EnvelopeGenerator.Application/EnvelopeGenerator.Application.csproj index 0406d46a..7f3ef53c 100644 --- a/EnvelopeGenerator.Application/EnvelopeGenerator.Application.csproj +++ b/EnvelopeGenerator.Application/EnvelopeGenerator.Application.csproj @@ -20,6 +20,7 @@ + diff --git a/EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverCommand.cs b/EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverCommand.cs index 7000950d..e9783a98 100644 --- a/EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverCommand.cs +++ b/EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverCommand.cs @@ -1,4 +1,5 @@ -using MediatR; +using EnvelopeGenerator.Application.Envelopes.Commands; +using MediatR; using System.ComponentModel.DataAnnotations; namespace EnvelopeGenerator.Application.EnvelopeReceivers.Commands.Create; @@ -17,7 +18,7 @@ public record CreateEnvelopeReceiverCommand( [Required] DocumentCreateCommand Document, [Required] IEnumerable Receivers, bool TFAEnabled = false - ) : IRequest; + ) : CreateEnvelopeCommand(Title, Message, TFAEnabled), IRequest; #region DTOs /// diff --git a/EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeCommand.cs b/EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeCommand.cs new file mode 100644 index 00000000..147d6802 --- /dev/null +++ b/EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeCommand.cs @@ -0,0 +1,24 @@ +using MediatR; +using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; + +namespace EnvelopeGenerator.Application.Envelopes.Commands; + +/// +/// Befehl zur Erstellung eines Umschlags. +/// +/// Der Titel des Umschlags. Dies ist ein Pflichtfeld. +/// Die Nachricht, die im Umschlag enthalten sein soll. Dies ist ein Pflichtfeld. +/// Gibt an, ob die Zwei-Faktor-Authentifizierung für den Umschlag aktiviert ist. Standardmäßig false. +public record CreateEnvelopeCommand( + [Required] string Title, + [Required] string Message, + bool TFAEnabled = false + ) : IRequest +{ + /// + /// Id of receiver + /// + [JsonIgnore] + public int? UserId { get; set; } +}; diff --git a/EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeCommandHandler.cs b/EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeCommandHandler.cs new file mode 100644 index 00000000..28801f45 --- /dev/null +++ b/EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeCommandHandler.cs @@ -0,0 +1,49 @@ +using AutoMapper; +using EnvelopeGenerator.Application.Contracts.SQLExecutor; +using EnvelopeGenerator.Domain.Entities; +using MediatR; +using Microsoft.Data.SqlClient; + +namespace EnvelopeGenerator.Application.Envelopes.Commands; + +/// +/// +/// +public class CreateEnvelopeCommandHandler : IRequestHandler +{ + private readonly ISQLExecutor _sqlExecutor; + + private readonly IMapper _mapper; + + /// + /// + /// + /// + /// + public CreateEnvelopeCommandHandler(ISQLExecutor sqlExecutor, IMapper mapper) + { + _sqlExecutor = sqlExecutor; + _mapper = mapper; + } + + /// + /// + /// + /// + /// + /// + public async Task Handle(CreateEnvelopeCommand request, CancellationToken cancellationToken) + { + object[] parameters = new object[] + { + new SqlParameter("@UserId", request.UserId), + new SqlParameter("@Title", request.Title), + new SqlParameter("@TfaEnabled", request.TFAEnabled ? 1 : 0), + new SqlParameter("@Message", request.Message) + }; + + var envelope = await _sqlExecutor.Execute(cancellationToken, parameters).FirstOrDefaultAsync(); + + return _mapper.Map(envelope); + } +} diff --git a/EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeResponse.cs b/EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeResponse.cs new file mode 100644 index 00000000..805de24d --- /dev/null +++ b/EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeResponse.cs @@ -0,0 +1,8 @@ +namespace EnvelopeGenerator.Application.Envelopes.Commands; + +/// +/// +/// +public class CreateEnvelopeResponse +{ +} diff --git a/EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeSQL.cs b/EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeSQL.cs index f07daffa..c3f2ebdd 100644 --- a/EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeSQL.cs +++ b/EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeSQL.cs @@ -1,5 +1,6 @@ using EnvelopeGenerator.Application.Contracts.SQLExecutor; using EnvelopeGenerator.Domain.Entities; +using Microsoft.Data.SqlClient; namespace EnvelopeGenerator.Application.Envelopes.Commands; /// @@ -10,5 +11,19 @@ public class CreateEnvelopeSQL : ISQL /// /// /// - public string Raw => string.Empty; + public string Raw => @" + USE [DD_ECM]; + DECLARE @OUT_UID varchar(36); + + EXEC [dbo].[PRSIG_API_CREATE_ENVELOPE] + @USER_ID = @UserId, + @TITLE = @Title, + @TFAEnabled = @TfaEnabled, + @MESSAGE = @Message, + @OUT_UID = @OUT_UID OUTPUT; + + SELECT * + FROM [dbo].[TBSIG_ENVELOPE] + WHERE [ENVELOPE_UUID] = @OUT_UID; + "; } From 928f2a778012f43fd6c3c511231081a0942ffd78 Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Mon, 5 May 2025 02:03:56 +0200 Subject: [PATCH 11/94] Add AutoMapper profile for envelope mapping Introduces `CreateEnvelopeMappingProfile` class to define mapping between `Envelope` entity and `CreateEnvelopeResponse` DTO using AutoMapper's `CreateMap` method. --- .../Commands/CreateEnvelopeMappingProfile.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeMappingProfile.cs diff --git a/EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeMappingProfile.cs b/EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeMappingProfile.cs new file mode 100644 index 00000000..8976d4bd --- /dev/null +++ b/EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeMappingProfile.cs @@ -0,0 +1,18 @@ +using AutoMapper; +using EnvelopeGenerator.Domain.Entities; + +namespace EnvelopeGenerator.Application.Envelopes.Commands; + +/// +/// +/// +public class CreateEnvelopeMappingProfile : Profile +{ + /// + /// + /// + public CreateEnvelopeMappingProfile() + { + CreateMap(); + } +} From 5166f41941743feeb8c6faf15c9c99b3ef08bfb8 Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Mon, 5 May 2025 02:06:33 +0200 Subject: [PATCH 12/94] Refactor CreateEnvelopeResponse to use record type Changed CreateEnvelopeResponse from a class to a record type for improved immutability and conciseness. The new definition inherits from ReadEnvelopeResponse, reusing its functionality. Added XML documentation comments for better clarity on each parameter. --- .../Commands/CreateEnvelopeResponse.cs | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeResponse.cs b/EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeResponse.cs index 805de24d..998b80e0 100644 --- a/EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeResponse.cs +++ b/EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeResponse.cs @@ -1,8 +1,20 @@ -namespace EnvelopeGenerator.Application.Envelopes.Commands; +using EnvelopeGenerator.Application.Envelopes.Queries.Read; + +namespace EnvelopeGenerator.Application.Envelopes.Commands; /// /// /// -public class CreateEnvelopeResponse -{ -} +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +public record CreateEnvelopeResponse(int Id, int UserId, int Status, string Uuid, string? Message, DateTime AddedWhen, DateTime? ChangedWhen, string? Title, string Language, bool TFAEnabled, DigitalData.UserManager.Domain.Entities.User User) + : ReadEnvelopeResponse(Id, UserId, Status, Uuid, Message, AddedWhen, ChangedWhen, Title, Language, TFAEnabled, User); From a757749767c60215bb723dd59e67a12be5f03ef2 Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Mon, 5 May 2025 10:15:36 +0200 Subject: [PATCH 13/94] Refactor SQL execution and enhance envelope creation - Updated `ISQLExecutor` to inherit from new `ISQLExecutor` interface for improved SQL execution flexibility. - Added package references for `Dapper` and `DigitalData.Core` in project files. - Modified `CreateEnvelopeCommand` to include `[BindNever]` on `UserId` for better model binding control. - Refactored `CreateEnvelopeCommandHandler` to use `DynamicParameters` for SQL parameter handling. - Updated `CreateEnvelopeSQL` to select only the top record for performance. - Introduced `GetIdOrDefault` method in `ControllerExtensions` for user ID retrieval with fallback. - Added `CreateAsync` method in `EnvelopeController` for envelope creation using `IMediator`. - Ensured infrastructure project has necessary package references. - Refactored `SQLExecutor` to implement new interface and simplified constructor. - Introduced `SQLExecutorBaseEntity` for entity-specific SQL command execution. --- .../Contracts/SQLExecutor/ISQLExecutor.cs | 2 +- .../SQLExecutor/ISQLExecutorBaseEntity.cs | 29 +++++++++++++++ .../EnvelopeGenerator.Application.csproj | 1 + .../Commands/CreateEnvelopeCommand.cs | 2 + .../Commands/CreateEnvelopeCommandHandler.cs | 17 ++++----- .../Envelopes/Commands/CreateEnvelopeSQL.cs | 2 +- .../Controllers/ControllerExtensions.cs | 10 +++-- .../Controllers/EnvelopeController.cs | 37 ++++++++++++++++++- .../EnvelopeGenerator.Infrastructure.csproj | 1 + .../SQLExecutor.cs | 29 ++++++++------- .../SQLExecutorBaseEntity.cs | 30 +++++++++++++++ 11 files changed, 131 insertions(+), 29 deletions(-) create mode 100644 EnvelopeGenerator.Application/Contracts/SQLExecutor/ISQLExecutorBaseEntity.cs create mode 100644 EnvelopeGenerator.Infrastructure/SQLExecutorBaseEntity.cs diff --git a/EnvelopeGenerator.Application/Contracts/SQLExecutor/ISQLExecutor.cs b/EnvelopeGenerator.Application/Contracts/SQLExecutor/ISQLExecutor.cs index 5016c18f..0c6ea474 100644 --- a/EnvelopeGenerator.Application/Contracts/SQLExecutor/ISQLExecutor.cs +++ b/EnvelopeGenerator.Application/Contracts/SQLExecutor/ISQLExecutor.cs @@ -5,7 +5,7 @@ /// Provides abstraction for raw SQL execution as well as mapping the results to objects. /// /// The entity type to which the SQL query results will be mapped. -public interface ISQLExecutor +public interface ISQLExecutor: ISQLExecutor { /// /// Executes a raw SQL query and returns an for further querying operations on the result. diff --git a/EnvelopeGenerator.Application/Contracts/SQLExecutor/ISQLExecutorBaseEntity.cs b/EnvelopeGenerator.Application/Contracts/SQLExecutor/ISQLExecutorBaseEntity.cs new file mode 100644 index 00000000..afe03e9d --- /dev/null +++ b/EnvelopeGenerator.Application/Contracts/SQLExecutor/ISQLExecutorBaseEntity.cs @@ -0,0 +1,29 @@ +using Dapper; + +namespace EnvelopeGenerator.Application.Contracts.SQLExecutor; + +/// +/// +/// +public interface ISQLExecutor +{ + /// + /// Executes a raw SQL query and returns an for further querying operations on the result. + /// + /// The entity type to which the SQL query results will be mapped. + /// The raw SQL query to execute. + /// Parameters for the SQL query. + /// Optional cancellation token for the operation. + /// An instance for further query operations on the result. + Task> Execute(string sql, DynamicParameters parameters, CancellationToken cancellation = default); + + /// + /// Executes a custom SQL query defined by a class that implements and returns an for further querying operations on the result. + /// + /// The entity type to which the SQL query results will be mapped. + /// The type of the custom SQL query class implementing . + /// Parameters for the SQL query. + /// Optional cancellation token for the operation. + /// An instance for further query operations on the result. + Task> Execute(DynamicParameters parameters, CancellationToken cancellation = default) where TSQL : ISQL; +} \ No newline at end of file diff --git a/EnvelopeGenerator.Application/EnvelopeGenerator.Application.csproj b/EnvelopeGenerator.Application/EnvelopeGenerator.Application.csproj index 7f3ef53c..0f944763 100644 --- a/EnvelopeGenerator.Application/EnvelopeGenerator.Application.csproj +++ b/EnvelopeGenerator.Application/EnvelopeGenerator.Application.csproj @@ -13,6 +13,7 @@ + diff --git a/EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeCommand.cs b/EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeCommand.cs index 147d6802..bf267203 100644 --- a/EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeCommand.cs +++ b/EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeCommand.cs @@ -1,4 +1,5 @@ using MediatR; +using Microsoft.AspNetCore.Mvc.ModelBinding; using System.ComponentModel.DataAnnotations; using System.Text.Json.Serialization; @@ -20,5 +21,6 @@ public record CreateEnvelopeCommand( /// Id of receiver /// [JsonIgnore] + [BindNever] public int? UserId { get; set; } }; diff --git a/EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeCommandHandler.cs b/EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeCommandHandler.cs index 28801f45..d33d8d3d 100644 --- a/EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeCommandHandler.cs +++ b/EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeCommandHandler.cs @@ -1,4 +1,5 @@ using AutoMapper; +using Dapper; using EnvelopeGenerator.Application.Contracts.SQLExecutor; using EnvelopeGenerator.Domain.Entities; using MediatR; @@ -34,16 +35,14 @@ public class CreateEnvelopeCommandHandler : IRequestHandler public async Task Handle(CreateEnvelopeCommand request, CancellationToken cancellationToken) { - object[] parameters = new object[] - { - new SqlParameter("@UserId", request.UserId), - new SqlParameter("@Title", request.Title), - new SqlParameter("@TfaEnabled", request.TFAEnabled ? 1 : 0), - new SqlParameter("@Message", request.Message) - }; + var parameters = new DynamicParameters(); + parameters.Add("@UserId", request.UserId); + parameters.Add("@Title", request.Title); + parameters.Add("@TfaEnabled", request.TFAEnabled ? 1 : 0); + parameters.Add("@Message", request.Message); - var envelope = await _sqlExecutor.Execute(cancellationToken, parameters).FirstOrDefaultAsync(); + var envelopes = await _sqlExecutor.Execute(parameters, cancellationToken); - return _mapper.Map(envelope); + return _mapper.Map(envelopes.FirstOrDefault()); } } diff --git a/EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeSQL.cs b/EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeSQL.cs index c3f2ebdd..e9b19ca7 100644 --- a/EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeSQL.cs +++ b/EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeSQL.cs @@ -22,7 +22,7 @@ public class CreateEnvelopeSQL : ISQL @MESSAGE = @Message, @OUT_UID = @OUT_UID OUTPUT; - SELECT * + SELECT TOP(1) * FROM [dbo].[TBSIG_ENVELOPE] WHERE [ENVELOPE_UUID] = @OUT_UID; "; diff --git a/EnvelopeGenerator.GeneratorAPI/Controllers/ControllerExtensions.cs b/EnvelopeGenerator.GeneratorAPI/Controllers/ControllerExtensions.cs index 1121a9b8..2f3ca908 100644 --- a/EnvelopeGenerator.GeneratorAPI/Controllers/ControllerExtensions.cs +++ b/EnvelopeGenerator.GeneratorAPI/Controllers/ControllerExtensions.cs @@ -5,10 +5,14 @@ namespace EnvelopeGenerator.GeneratorAPI.Controllers { public static class ControllerExtensions { - public static int? GetId(this ClaimsPrincipal user) - => int.TryParse(user.FindFirst(ClaimTypes.NameIdentifier)?.Value, out int result) + public static int? GetIdOrDefault(this ClaimsPrincipal user) + => int.TryParse(user.FindFirstValue(ClaimTypes.NameIdentifier) ?? user.FindFirstValue("sub"), out int result) ? result : null; - + + public static int GetId(this ClaimsPrincipal user) + => user.GetIdOrDefault() + ?? throw new InvalidOperationException("User ID claim is missing or invalid. This may indicate a misconfigured or forged JWT token."); + public static string? GetUsername(this ClaimsPrincipal user) => user.FindFirst(ClaimTypes.Name)?.Value; diff --git a/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeController.cs b/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeController.cs index 049c0186..47508d54 100644 --- a/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeController.cs +++ b/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeController.cs @@ -1,8 +1,11 @@ using DigitalData.Core.DTO; using EnvelopeGenerator.Application.Contracts.Services; +using EnvelopeGenerator.Application.Envelopes.Commands; using EnvelopeGenerator.Application.Envelopes.Queries.Read; +using MediatR; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using Newtonsoft.Json; namespace EnvelopeGenerator.GeneratorAPI.Controllers; @@ -27,16 +30,19 @@ public class EnvelopeController : ControllerBase { private readonly ILogger _logger; private readonly IEnvelopeService _envelopeService; + private readonly IMediator _mediator; /// /// Erstellt eine neue Instanz des EnvelopeControllers. /// /// Der Logger, der für das Protokollieren von Informationen verwendet wird. /// Der Dienst, der für die Verarbeitung von Umschlägen zuständig ist. - public EnvelopeController(ILogger logger, IEnvelopeService envelopeService) + /// + public EnvelopeController(ILogger logger, IEnvelopeService envelopeService, IMediator mediator) { _logger = logger; _envelopeService = envelopeService; + _mediator = mediator; } /// @@ -75,4 +81,33 @@ public class EnvelopeController : ControllerBase return StatusCode(StatusCodes.Status500InternalServerError); } } + + /// + /// + /// + /// + /// + [Authorize] + [HttpPost] + public async Task CreateAsync([FromQuery] CreateEnvelopeCommand envelope) + { + try + { + envelope.UserId = User.GetId(); + var res = await _mediator.Send(envelope); + + if (res is null) + { + _logger.LogError("Failed to create envelope. Envelope details: {EnvelopeDetails}", JsonConvert.SerializeObject(envelope)); + return StatusCode(StatusCodes.Status500InternalServerError); + } + else + return Ok(res); + } + catch (Exception ex) + { + _logger.LogError(ex, "{Message}", ex.Message); + return StatusCode(StatusCodes.Status500InternalServerError); + } + } } diff --git a/EnvelopeGenerator.Infrastructure/EnvelopeGenerator.Infrastructure.csproj b/EnvelopeGenerator.Infrastructure/EnvelopeGenerator.Infrastructure.csproj index f93f27c1..8ad9d9f9 100644 --- a/EnvelopeGenerator.Infrastructure/EnvelopeGenerator.Infrastructure.csproj +++ b/EnvelopeGenerator.Infrastructure/EnvelopeGenerator.Infrastructure.csproj @@ -7,6 +7,7 @@ + diff --git a/EnvelopeGenerator.Infrastructure/SQLExecutor.cs b/EnvelopeGenerator.Infrastructure/SQLExecutor.cs index 499f587d..d39f7709 100644 --- a/EnvelopeGenerator.Infrastructure/SQLExecutor.cs +++ b/EnvelopeGenerator.Infrastructure/SQLExecutor.cs @@ -1,30 +1,31 @@ -using EnvelopeGenerator.Application.Contracts.SQLExecutor; -using Microsoft.EntityFrameworkCore; +using Dapper; +using EnvelopeGenerator.Application.Contracts.SQLExecutor; +using Microsoft.Data.SqlClient; using Microsoft.Extensions.DependencyInjection; namespace EnvelopeGenerator.Infrastructure; -public sealed class SQLExecutor : ISQLExecutor where T : class +public class SQLExecutor : ISQLExecutor { - private readonly EGDbContext _context; + private readonly string _cnnStr = "Server=SDD-VMP04-SQL17\\DD_DEVELOP01;Database=DD_ECM;User Id=sa;Password=dd;Encrypt=false;TrustServerCertificate=True;"; private readonly IServiceProvider _provider; - public SQLExecutor(EGDbContext context, IServiceProvider provider) + public SQLExecutor(IServiceProvider provider) { - _context = context; _provider = provider; } - public IQuery Execute(string sql, CancellationToken cancellation = default, params object[] parameters) - => _context - .Set() - .FromSqlRaw(sql, parameters) - .ToQuery(); + public async Task> Execute(string sql, DynamicParameters parameters, CancellationToken cancellation = default) + { + using var connection = new SqlConnection(_cnnStr); + await connection.OpenAsync(cancellation); + return await connection.QueryAsync(sql, parameters); + } - public IQuery Execute(CancellationToken cancellation = default, params object[] parameters) where TSQL : ISQL + public Task> Execute(DynamicParameters parameters, CancellationToken cancellation = default) where TSQL : ISQL { var sql = _provider.GetRequiredService(); - return Execute(sql.Raw); + return Execute(sql.Raw, parameters, cancellation); } -} +} \ No newline at end of file diff --git a/EnvelopeGenerator.Infrastructure/SQLExecutorBaseEntity.cs b/EnvelopeGenerator.Infrastructure/SQLExecutorBaseEntity.cs new file mode 100644 index 00000000..cc2c4c26 --- /dev/null +++ b/EnvelopeGenerator.Infrastructure/SQLExecutorBaseEntity.cs @@ -0,0 +1,30 @@ +using EnvelopeGenerator.Application.Contracts.SQLExecutor; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; + +namespace EnvelopeGenerator.Infrastructure; + +public sealed class SQLExecutor : SQLExecutor, ISQLExecutor where T : class +{ + private readonly EGDbContext _context; + + private readonly IServiceProvider _provider; + + public SQLExecutor(EGDbContext context, IServiceProvider provider) : base(provider) + { + _context = context; + _provider = provider; + } + + public IQuery Execute(string sql, CancellationToken cancellation = default, params object[] parameters) + => _context + .Set() + .FromSqlRaw(sql, parameters) + .ToQuery(); + + public IQuery Execute(CancellationToken cancellation = default, params object[] parameters) where TSQL : ISQL + { + var sql = _provider.GetRequiredService(); + return Execute(sql.Raw); + } +} From a42e4287ffa7673acaafa5078f82d2cbe3bf4790 Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Mon, 5 May 2025 10:47:00 +0200 Subject: [PATCH 14/94] Refactor envelope generator service dependencies This commit replaces the `AddEnvelopeGeneratorRepositories` method with `AddEnvelopeGeneratorInfrastructureServices`, allowing for more flexible configuration through `IConfiguration` and `Action`. The `SQLExecutor` class now utilizes `SQLExecutorParams` for its connection string, enhancing configurability. A new `SQLExecutorParams` class has been introduced to encapsulate connection string management. Various service registration calls have been updated to integrate the new infrastructure services, improving modularity and ease of database connection management. --- EnvelopeGenerator.GeneratorAPI/Program.cs | 2 +- .../DependencyExtensions.cs | 31 +++++++++++++++++-- .../SQLExecutor.cs | 8 +++-- .../SQLExecutorBaseEntity.cs | 3 +- .../SQLExecutorParams.cs | 6 ++++ .../DependencyInjection.cs | 4 +-- EnvelopeGenerator.Tests.Application/Mock.cs | 2 +- EnvelopeGenerator.Web/Program.cs | 2 +- 8 files changed, 47 insertions(+), 11 deletions(-) create mode 100644 EnvelopeGenerator.Infrastructure/SQLExecutorParams.cs diff --git a/EnvelopeGenerator.GeneratorAPI/Program.cs b/EnvelopeGenerator.GeneratorAPI/Program.cs index bfdb024b..121d83a4 100644 --- a/EnvelopeGenerator.GeneratorAPI/Program.cs +++ b/EnvelopeGenerator.GeneratorAPI/Program.cs @@ -159,7 +159,7 @@ builder.Services.AddCookieBasedLocalizer() ; // Envelope generator serives builder.Services - .AddEnvelopeGeneratorRepositories() + .AddEnvelopeGeneratorInfrastructureServices(sqlExecutorConfigureOptions: executor => executor.ConnectionString = connStr) .AddEnvelopeGeneratorServices(config); var app = builder.Build(); diff --git a/EnvelopeGenerator.Infrastructure/DependencyExtensions.cs b/EnvelopeGenerator.Infrastructure/DependencyExtensions.cs index d15742be..2aeb192a 100644 --- a/EnvelopeGenerator.Infrastructure/DependencyExtensions.cs +++ b/EnvelopeGenerator.Infrastructure/DependencyExtensions.cs @@ -7,6 +7,7 @@ using DigitalData.Core.Infrastructure; using EnvelopeGenerator.Domain.Entities; using DigitalData.Core.Infrastructure.AutoMapper; using EnvelopeGenerator.Application.Contracts.SQLExecutor; +using Microsoft.Extensions.Configuration; namespace EnvelopeGenerator.Infrastructure; @@ -26,7 +27,10 @@ public static class DIExtensions /// This method ensures that the repositories are registered as scoped services, meaning that a new instance of each repository /// will be created per HTTP request (or per scope) within the dependency injection container. /// - public static IServiceCollection AddEnvelopeGeneratorRepositories(this IServiceCollection services, Action? dbContextOptions = null) + public static IServiceCollection AddEnvelopeGeneratorInfrastructureServices(this IServiceCollection services, + Action? dbContextOptions = null, + IConfiguration? sqlExecutorConfiguration = null, + Action? sqlExecutorConfigureOptions = null) { if(dbContextOptions is not null) services.AddDbContext(dbContextOptions); @@ -66,11 +70,34 @@ public static class DIExtensions services.AddSQLExecutor(); services.AddSQLExecutor(); + if (sqlExecutorConfiguration is not null || sqlExecutorConfigureOptions is not null) + services.AddSQLExecutor(sqlExecutorConfiguration, sqlExecutorConfigureOptions); + return services; } - public static IServiceCollection AddSQLExecutor(this IServiceCollection services) where T : class + public static IServiceCollection AddSQLExecutor(this IServiceCollection services, IConfiguration? configuration = null, Action? configureOptions = null) { + if(configuration is not null && configureOptions is not null) + throw new InvalidOperationException("Cannot use both 'configuration' and 'configureOptions'. Only one should be provided."); + + if (configuration is not null) + services.Configure(configuration); + + if(configureOptions is not null) + services.Configure(configureOptions); + + return services.AddSingleton(); + } + + public static IServiceCollection AddSQLExecutor(this IServiceCollection services, IConfiguration? configuration = null, Action? configureOptions = null) where T : class + { + if (configuration is not null && configureOptions is not null) + throw new InvalidOperationException("Cannot use both 'configuration' and 'configureOptions'. Only one should be provided."); + + if (configuration is not null) + services.Configure(configuration); + services.AddScoped, SQLExecutor>(); var interfaceType = typeof(ISQL<>); diff --git a/EnvelopeGenerator.Infrastructure/SQLExecutor.cs b/EnvelopeGenerator.Infrastructure/SQLExecutor.cs index d39f7709..33c3ccfe 100644 --- a/EnvelopeGenerator.Infrastructure/SQLExecutor.cs +++ b/EnvelopeGenerator.Infrastructure/SQLExecutor.cs @@ -2,23 +2,25 @@ using EnvelopeGenerator.Application.Contracts.SQLExecutor; using Microsoft.Data.SqlClient; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; namespace EnvelopeGenerator.Infrastructure; public class SQLExecutor : ISQLExecutor { - private readonly string _cnnStr = "Server=SDD-VMP04-SQL17\\DD_DEVELOP01;Database=DD_ECM;User Id=sa;Password=dd;Encrypt=false;TrustServerCertificate=True;"; + private readonly SQLExecutorParams _params; private readonly IServiceProvider _provider; - public SQLExecutor(IServiceProvider provider) + public SQLExecutor(IServiceProvider provider, IOptions sqlExecutorParamsOptions) { _provider = provider; + _params = sqlExecutorParamsOptions.Value; } public async Task> Execute(string sql, DynamicParameters parameters, CancellationToken cancellation = default) { - using var connection = new SqlConnection(_cnnStr); + using var connection = new SqlConnection(_params.ConnectionString); await connection.OpenAsync(cancellation); return await connection.QueryAsync(sql, parameters); } diff --git a/EnvelopeGenerator.Infrastructure/SQLExecutorBaseEntity.cs b/EnvelopeGenerator.Infrastructure/SQLExecutorBaseEntity.cs index cc2c4c26..e1f20d65 100644 --- a/EnvelopeGenerator.Infrastructure/SQLExecutorBaseEntity.cs +++ b/EnvelopeGenerator.Infrastructure/SQLExecutorBaseEntity.cs @@ -1,6 +1,7 @@ using EnvelopeGenerator.Application.Contracts.SQLExecutor; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; namespace EnvelopeGenerator.Infrastructure; @@ -10,7 +11,7 @@ public sealed class SQLExecutor : SQLExecutor, ISQLExecutor where T : clas private readonly IServiceProvider _provider; - public SQLExecutor(EGDbContext context, IServiceProvider provider) : base(provider) + public SQLExecutor(EGDbContext context, IServiceProvider provider, IOptions options) : base(provider, options) { _context = context; _provider = provider; diff --git a/EnvelopeGenerator.Infrastructure/SQLExecutorParams.cs b/EnvelopeGenerator.Infrastructure/SQLExecutorParams.cs new file mode 100644 index 00000000..b2d2f677 --- /dev/null +++ b/EnvelopeGenerator.Infrastructure/SQLExecutorParams.cs @@ -0,0 +1,6 @@ +namespace EnvelopeGenerator.Infrastructure; + +public class SQLExecutorParams +{ + public string? ConnectionString { get; set; } +} diff --git a/EnvelopeGenerator.Terminal/DependencyInjection.cs b/EnvelopeGenerator.Terminal/DependencyInjection.cs index ad011146..421a57d2 100644 --- a/EnvelopeGenerator.Terminal/DependencyInjection.cs +++ b/EnvelopeGenerator.Terminal/DependencyInjection.cs @@ -27,11 +27,11 @@ public static class DependencyInjection }); // Add envelope generator services - services.AddEnvelopeGeneratorRepositories(options => options.UseSqlServer(connStr)); + services.AddEnvelopeGeneratorInfrastructureServices(options => options.UseSqlServer(connStr)); return services .AddSingleton() - .AddEnvelopeGeneratorRepositories() + .AddEnvelopeGeneratorInfrastructureServices() .AddEnvelopeGeneratorServices(configuration) .AddSingleton(sp => { diff --git a/EnvelopeGenerator.Tests.Application/Mock.cs b/EnvelopeGenerator.Tests.Application/Mock.cs index 10bc7ea6..b14108e8 100644 --- a/EnvelopeGenerator.Tests.Application/Mock.cs +++ b/EnvelopeGenerator.Tests.Application/Mock.cs @@ -17,7 +17,7 @@ public class Mock builder.Configuration.AddJsonFile(configPath, optional: true, reloadOnChange: true); builder.Services - .AddEnvelopeGeneratorRepositories(opt => + .AddEnvelopeGeneratorInfrastructureServices(opt => { if (useRealDb) { diff --git a/EnvelopeGenerator.Web/Program.cs b/EnvelopeGenerator.Web/Program.cs index 9448b5e4..e9324e3e 100644 --- a/EnvelopeGenerator.Web/Program.cs +++ b/EnvelopeGenerator.Web/Program.cs @@ -90,7 +90,7 @@ try }); // Add envelope generator services - builder.Services.AddEnvelopeGeneratorRepositories(options => options.UseSqlServer(connStr)); + builder.Services.AddEnvelopeGeneratorInfrastructureServices(options => options.UseSqlServer(connStr)); builder.Services.AddEnvelopeGeneratorServices(config); From 7b7aba6efdcf247824d614e49b21565548069d1e Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Mon, 5 May 2025 10:54:09 +0200 Subject: [PATCH 15/94] Refactor SQL execution classes into new namespace Restructure and refactor classes related to SQL execution within the `EnvelopeGenerator.Infrastructure` namespace. Key changes include: - Added `EnvelopeGenerator.Infrastructure.Executor` namespace. - Moved and redefined `Query`, `QueryExtension`, `SQLExecutor`, `SQLExecutorBaseEntity`, and `SQLExecutorParams` classes to the new namespace. - Maintained existing functionality while improving code organization and clarity. --- EnvelopeGenerator.Infrastructure/DependencyExtensions.cs | 1 + EnvelopeGenerator.Infrastructure/{ => Executor}/Query.cs | 5 ++--- .../{ => Executor}/QueryExtension.cs | 2 +- .../{ => Executor}/SQLExecutor.cs | 2 +- .../{ => Executor}/SQLExecutorBaseEntity.cs | 2 +- .../{ => Executor}/SQLExecutorParams.cs | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) rename EnvelopeGenerator.Infrastructure/{ => Executor}/Query.cs (87%) rename EnvelopeGenerator.Infrastructure/{ => Executor}/QueryExtension.cs (78%) rename EnvelopeGenerator.Infrastructure/{ => Executor}/SQLExecutor.cs (95%) rename EnvelopeGenerator.Infrastructure/{ => Executor}/SQLExecutorBaseEntity.cs (95%) rename EnvelopeGenerator.Infrastructure/{ => Executor}/SQLExecutorParams.cs (60%) diff --git a/EnvelopeGenerator.Infrastructure/DependencyExtensions.cs b/EnvelopeGenerator.Infrastructure/DependencyExtensions.cs index 2aeb192a..7a2a1ce6 100644 --- a/EnvelopeGenerator.Infrastructure/DependencyExtensions.cs +++ b/EnvelopeGenerator.Infrastructure/DependencyExtensions.cs @@ -8,6 +8,7 @@ using EnvelopeGenerator.Domain.Entities; using DigitalData.Core.Infrastructure.AutoMapper; using EnvelopeGenerator.Application.Contracts.SQLExecutor; using Microsoft.Extensions.Configuration; +using EnvelopeGenerator.Infrastructure.Executor; namespace EnvelopeGenerator.Infrastructure; diff --git a/EnvelopeGenerator.Infrastructure/Query.cs b/EnvelopeGenerator.Infrastructure/Executor/Query.cs similarity index 87% rename from EnvelopeGenerator.Infrastructure/Query.cs rename to EnvelopeGenerator.Infrastructure/Executor/Query.cs index 6bd9e3c2..5664d0be 100644 --- a/EnvelopeGenerator.Infrastructure/Query.cs +++ b/EnvelopeGenerator.Infrastructure/Executor/Query.cs @@ -1,8 +1,7 @@ -using AngleSharp.Dom; -using EnvelopeGenerator.Application.Contracts.SQLExecutor; +using EnvelopeGenerator.Application.Contracts.SQLExecutor; using Microsoft.EntityFrameworkCore; -namespace EnvelopeGenerator.Infrastructure; +namespace EnvelopeGenerator.Infrastructure.Executor; public sealed record Query : IQuery { diff --git a/EnvelopeGenerator.Infrastructure/QueryExtension.cs b/EnvelopeGenerator.Infrastructure/Executor/QueryExtension.cs similarity index 78% rename from EnvelopeGenerator.Infrastructure/QueryExtension.cs rename to EnvelopeGenerator.Infrastructure/Executor/QueryExtension.cs index f23a7aef..58c72850 100644 --- a/EnvelopeGenerator.Infrastructure/QueryExtension.cs +++ b/EnvelopeGenerator.Infrastructure/Executor/QueryExtension.cs @@ -1,4 +1,4 @@ -namespace EnvelopeGenerator.Infrastructure; +namespace EnvelopeGenerator.Infrastructure.Executor; public static class QueryExtension { diff --git a/EnvelopeGenerator.Infrastructure/SQLExecutor.cs b/EnvelopeGenerator.Infrastructure/Executor/SQLExecutor.cs similarity index 95% rename from EnvelopeGenerator.Infrastructure/SQLExecutor.cs rename to EnvelopeGenerator.Infrastructure/Executor/SQLExecutor.cs index 33c3ccfe..c8edeeff 100644 --- a/EnvelopeGenerator.Infrastructure/SQLExecutor.cs +++ b/EnvelopeGenerator.Infrastructure/Executor/SQLExecutor.cs @@ -4,7 +4,7 @@ using Microsoft.Data.SqlClient; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; -namespace EnvelopeGenerator.Infrastructure; +namespace EnvelopeGenerator.Infrastructure.Executor; public class SQLExecutor : ISQLExecutor { diff --git a/EnvelopeGenerator.Infrastructure/SQLExecutorBaseEntity.cs b/EnvelopeGenerator.Infrastructure/Executor/SQLExecutorBaseEntity.cs similarity index 95% rename from EnvelopeGenerator.Infrastructure/SQLExecutorBaseEntity.cs rename to EnvelopeGenerator.Infrastructure/Executor/SQLExecutorBaseEntity.cs index e1f20d65..d3e8b6d3 100644 --- a/EnvelopeGenerator.Infrastructure/SQLExecutorBaseEntity.cs +++ b/EnvelopeGenerator.Infrastructure/Executor/SQLExecutorBaseEntity.cs @@ -3,7 +3,7 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; -namespace EnvelopeGenerator.Infrastructure; +namespace EnvelopeGenerator.Infrastructure.Executor; public sealed class SQLExecutor : SQLExecutor, ISQLExecutor where T : class { diff --git a/EnvelopeGenerator.Infrastructure/SQLExecutorParams.cs b/EnvelopeGenerator.Infrastructure/Executor/SQLExecutorParams.cs similarity index 60% rename from EnvelopeGenerator.Infrastructure/SQLExecutorParams.cs rename to EnvelopeGenerator.Infrastructure/Executor/SQLExecutorParams.cs index b2d2f677..734d4a5e 100644 --- a/EnvelopeGenerator.Infrastructure/SQLExecutorParams.cs +++ b/EnvelopeGenerator.Infrastructure/Executor/SQLExecutorParams.cs @@ -1,4 +1,4 @@ -namespace EnvelopeGenerator.Infrastructure; +namespace EnvelopeGenerator.Infrastructure.Executor; public class SQLExecutorParams { From 39ff4b8867a2d86e75130c71c8082ed235f40ccb Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Mon, 5 May 2025 13:53:31 +0200 Subject: [PATCH 16/94] Refactor envelope creation logic and SQL execution - Changed `_sqlExecutor` type in `CreateEnvelopeCommandHandler` to non-generic `ISQLExecutor`. - Updated `Handle` method to use `CreateEnvelopeAsync` for simplified parameter handling. - Restructured `CreateEnvelopeSQL` to implement `ISQL` with a raw SQL command for creating envelopes. - Added `SetDappeTypeMap` method in `DependencyExtensions` for Dapper type mappings. - Improved overall code structure for better separation of concerns and maintainability. --- .../Commands/CreateEnvelopeCommandHandler.cs | 14 ++--- .../Envelopes/Commands/CreateEnvelopeSQL.cs | 29 --------- .../SQL/CreateEnvelopeSQL.cs | 60 +++++++++++++++++++ .../DependencyExtensions.cs | 25 ++++++++ 4 files changed, 90 insertions(+), 38 deletions(-) delete mode 100644 EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeSQL.cs create mode 100644 EnvelopeGenerator.Application/SQL/CreateEnvelopeSQL.cs diff --git a/EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeCommandHandler.cs b/EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeCommandHandler.cs index d33d8d3d..7ac1c166 100644 --- a/EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeCommandHandler.cs +++ b/EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeCommandHandler.cs @@ -1,9 +1,9 @@ using AutoMapper; using Dapper; using EnvelopeGenerator.Application.Contracts.SQLExecutor; +using EnvelopeGenerator.Application.SQL; using EnvelopeGenerator.Domain.Entities; using MediatR; -using Microsoft.Data.SqlClient; namespace EnvelopeGenerator.Application.Envelopes.Commands; @@ -12,7 +12,7 @@ namespace EnvelopeGenerator.Application.Envelopes.Commands; /// public class CreateEnvelopeCommandHandler : IRequestHandler { - private readonly ISQLExecutor _sqlExecutor; + private readonly ISQLExecutor _sqlExecutor; private readonly IMapper _mapper; @@ -35,14 +35,10 @@ public class CreateEnvelopeCommandHandler : IRequestHandler public async Task Handle(CreateEnvelopeCommand request, CancellationToken cancellationToken) { - var parameters = new DynamicParameters(); - parameters.Add("@UserId", request.UserId); - parameters.Add("@Title", request.Title); - parameters.Add("@TfaEnabled", request.TFAEnabled ? 1 : 0); - parameters.Add("@Message", request.Message); + int userId = request.UserId ?? throw new InvalidOperationException("UserId cannot be null when creating an envelope."); - var envelopes = await _sqlExecutor.Execute(parameters, cancellationToken); + var envelope = await _sqlExecutor.CreateEnvelopeAsync(userId, request.Title, request.Message, request.TFAEnabled, cancellationToken); - return _mapper.Map(envelopes.FirstOrDefault()); + return _mapper.Map(envelope); } } diff --git a/EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeSQL.cs b/EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeSQL.cs deleted file mode 100644 index e9b19ca7..00000000 --- a/EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeSQL.cs +++ /dev/null @@ -1,29 +0,0 @@ -using EnvelopeGenerator.Application.Contracts.SQLExecutor; -using EnvelopeGenerator.Domain.Entities; -using Microsoft.Data.SqlClient; - -namespace EnvelopeGenerator.Application.Envelopes.Commands; -/// -/// -/// -public class CreateEnvelopeSQL : ISQL -{ - /// - /// - /// - public string Raw => @" - USE [DD_ECM]; - DECLARE @OUT_UID varchar(36); - - EXEC [dbo].[PRSIG_API_CREATE_ENVELOPE] - @USER_ID = @UserId, - @TITLE = @Title, - @TFAEnabled = @TfaEnabled, - @MESSAGE = @Message, - @OUT_UID = @OUT_UID OUTPUT; - - SELECT TOP(1) * - FROM [dbo].[TBSIG_ENVELOPE] - WHERE [ENVELOPE_UUID] = @OUT_UID; - "; -} diff --git a/EnvelopeGenerator.Application/SQL/CreateEnvelopeSQL.cs b/EnvelopeGenerator.Application/SQL/CreateEnvelopeSQL.cs new file mode 100644 index 00000000..fb231334 --- /dev/null +++ b/EnvelopeGenerator.Application/SQL/CreateEnvelopeSQL.cs @@ -0,0 +1,60 @@ +using Dapper; +using EnvelopeGenerator.Application.Contracts.SQLExecutor; +using EnvelopeGenerator.Domain.Entities; + +namespace EnvelopeGenerator.Application.SQL; + +/// +/// +/// +public class CreateEnvelopeSQL : ISQL +{ + /// + /// + /// + public string Raw => @" + USE [DD_ECM]; + DECLARE @OUT_UID varchar(36); + + EXEC [dbo].[PRSIG_API_CREATE_ENVELOPE] + @USER_ID = @UserId, + @TITLE = @Title, + @TFAEnabled = @TfaEnabled, + @MESSAGE = @Message, + @OUT_UID = @OUT_UID OUTPUT; + + SELECT TOP(1) * + FROM [dbo].[TBSIG_ENVELOPE] + WHERE [ENVELOPE_UUID] = @OUT_UID; + "; +} + +/// +/// +/// +public static class Extension +{ + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public static async Task CreateEnvelopeAsync(this ISQLExecutor executor, int userId, string title = "", string message = "", bool tfaEnabled = false, CancellationToken cancellation = default) + where TEntity : class + { + var parameters = new DynamicParameters(); + parameters.Add("@UserId", userId); + parameters.Add("@Title", title); + parameters.Add("@TfaEnabled", tfaEnabled ? 1 : 0); + parameters.Add("@Message", message); + + var envelopes = await executor.Execute(parameters, cancellation); + return envelopes.FirstOrDefault(); + } +} \ No newline at end of file diff --git a/EnvelopeGenerator.Infrastructure/DependencyExtensions.cs b/EnvelopeGenerator.Infrastructure/DependencyExtensions.cs index 7a2a1ce6..dd8458ed 100644 --- a/EnvelopeGenerator.Infrastructure/DependencyExtensions.cs +++ b/EnvelopeGenerator.Infrastructure/DependencyExtensions.cs @@ -9,6 +9,9 @@ using DigitalData.Core.Infrastructure.AutoMapper; using EnvelopeGenerator.Application.Contracts.SQLExecutor; using Microsoft.Extensions.Configuration; using EnvelopeGenerator.Infrastructure.Executor; +using Dapper; +using System.ComponentModel.DataAnnotations.Schema; +using System.Reflection; namespace EnvelopeGenerator.Infrastructure; @@ -71,6 +74,12 @@ public static class DIExtensions services.AddSQLExecutor(); services.AddSQLExecutor(); + SetDappeTypeMap(); + SetDappeTypeMap(); + SetDappeTypeMap(); + SetDappeTypeMap(); + SetDappeTypeMap(); + if (sqlExecutorConfiguration is not null || sqlExecutorConfigureOptions is not null) services.AddSQLExecutor(sqlExecutorConfiguration, sqlExecutorConfigureOptions); @@ -91,6 +100,22 @@ public static class DIExtensions return services.AddSingleton(); } + private static void SetDappeTypeMap() + { + Dapper.SqlMapper.SetTypeMap(typeof(TModel), new CustomPropertyTypeMap( + typeof(TModel), + (type, columnName) => + { + return type.GetProperties().FirstOrDefault(prop => + { + var attr = prop.GetCustomAttribute(); + return attr != null && string.Equals(attr.Name, columnName, StringComparison.OrdinalIgnoreCase) + || string.Equals(prop.Name, columnName, StringComparison.OrdinalIgnoreCase); + })!; + } + )); + } + public static IServiceCollection AddSQLExecutor(this IServiceCollection services, IConfiguration? configuration = null, Action? configureOptions = null) where T : class { if (configuration is not null && configureOptions is not null) From b93ba6be1759b7ebe7b18fbeafa33b6813e36d1b Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Mon, 5 May 2025 16:11:29 +0200 Subject: [PATCH 17/94] Refactor envelope creation logic and add IEnvelopeExecutor Updated CreateEnvelopeCommandHandler to use IEnvelopeExecutor for managing envelope creation. Introduced CreateParmas method for parameter handling in CreateEnvelopeSQL. Corrected Dapper type mapping method name and added service registration for IEnvelopeExecutor in DependencyExtensions. Refactored SQLExecutor to enhance encapsulation. Created EnvelopeExecutor class implementing IEnvelopeExecutor for dedicated envelope operations. --- .../SQLExecutor/IEnvelopeExecutor.cs | 19 ++++++++ .../Commands/CreateEnvelopeCommandHandler.cs | 12 +++--- .../SQL/CreateEnvelopeSQL.cs | 33 +++++++++++--- .../DependencyExtensions.cs | 16 ++++--- .../Executor/EnvelopeExecutor.cs | 43 +++++++++++++++++++ .../Executor/SQLExecutor.cs | 12 +++--- 6 files changed, 111 insertions(+), 24 deletions(-) create mode 100644 EnvelopeGenerator.Application/Contracts/SQLExecutor/IEnvelopeExecutor.cs create mode 100644 EnvelopeGenerator.Infrastructure/Executor/EnvelopeExecutor.cs diff --git a/EnvelopeGenerator.Application/Contracts/SQLExecutor/IEnvelopeExecutor.cs b/EnvelopeGenerator.Application/Contracts/SQLExecutor/IEnvelopeExecutor.cs new file mode 100644 index 00000000..f197cadf --- /dev/null +++ b/EnvelopeGenerator.Application/Contracts/SQLExecutor/IEnvelopeExecutor.cs @@ -0,0 +1,19 @@ +using Dapper; +using EnvelopeGenerator.Domain.Entities; + +namespace EnvelopeGenerator.Application.Contracts.SQLExecutor; + +/// +/// +/// +public interface IEnvelopeExecutor : ISQLExecutor +{ + /// + /// + /// + /// + /// + /// + /// + Task CreateEnvelopeAsync(DynamicParameters parameters, bool addUser = true, CancellationToken cancellation = default); +} diff --git a/EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeCommandHandler.cs b/EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeCommandHandler.cs index 7ac1c166..a4b7d698 100644 --- a/EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeCommandHandler.cs +++ b/EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeCommandHandler.cs @@ -1,8 +1,6 @@ using AutoMapper; -using Dapper; using EnvelopeGenerator.Application.Contracts.SQLExecutor; using EnvelopeGenerator.Application.SQL; -using EnvelopeGenerator.Domain.Entities; using MediatR; namespace EnvelopeGenerator.Application.Envelopes.Commands; @@ -12,18 +10,18 @@ namespace EnvelopeGenerator.Application.Envelopes.Commands; /// public class CreateEnvelopeCommandHandler : IRequestHandler { - private readonly ISQLExecutor _sqlExecutor; + private readonly IEnvelopeExecutor _envelopeExecutor; private readonly IMapper _mapper; /// /// /// - /// + /// /// - public CreateEnvelopeCommandHandler(ISQLExecutor sqlExecutor, IMapper mapper) + public CreateEnvelopeCommandHandler(IEnvelopeExecutor envelopeExecutor, IMapper mapper) { - _sqlExecutor = sqlExecutor; + _envelopeExecutor = envelopeExecutor; _mapper = mapper; } @@ -37,7 +35,7 @@ public class CreateEnvelopeCommandHandler : IRequestHandler(userId, request.Title, request.Message, request.TFAEnabled, cancellationToken); + var envelope = await _envelopeExecutor.CreateEnvelopeAsync(userId, request.Title, request.Message, request.TFAEnabled, cancellationToken); return _mapper.Map(envelope); } diff --git a/EnvelopeGenerator.Application/SQL/CreateEnvelopeSQL.cs b/EnvelopeGenerator.Application/SQL/CreateEnvelopeSQL.cs index fb231334..14f70af3 100644 --- a/EnvelopeGenerator.Application/SQL/CreateEnvelopeSQL.cs +++ b/EnvelopeGenerator.Application/SQL/CreateEnvelopeSQL.cs @@ -34,6 +34,16 @@ public class CreateEnvelopeSQL : ISQL /// public static class Extension { + private static DynamicParameters CreateParmas(int userId, string title = "", string message = "", bool tfaEnabled = false) + { + var parameters = new DynamicParameters(); + parameters.Add("@UserId", userId); + parameters.Add("@Title", title); + parameters.Add("@TfaEnabled", tfaEnabled ? 1 : 0); + parameters.Add("@Message", message); + return parameters; + } + /// /// /// @@ -48,13 +58,26 @@ public static class Extension public static async Task CreateEnvelopeAsync(this ISQLExecutor executor, int userId, string title = "", string message = "", bool tfaEnabled = false, CancellationToken cancellation = default) where TEntity : class { - var parameters = new DynamicParameters(); - parameters.Add("@UserId", userId); - parameters.Add("@Title", title); - parameters.Add("@TfaEnabled", tfaEnabled ? 1 : 0); - parameters.Add("@Message", message); + var parameters = CreateParmas(userId, title, message, tfaEnabled); var envelopes = await executor.Execute(parameters, cancellation); return envelopes.FirstOrDefault(); } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public static async Task CreateEnvelopeAsync(this IEnvelopeExecutor executor, int userId, string title = "", string message = "", bool tfaEnabled = false, CancellationToken cancellation = default) + { + var parameters = CreateParmas(userId, title, message, tfaEnabled); + + return await executor.CreateEnvelopeAsync(parameters, cancellation: cancellation); + } } \ No newline at end of file diff --git a/EnvelopeGenerator.Infrastructure/DependencyExtensions.cs b/EnvelopeGenerator.Infrastructure/DependencyExtensions.cs index dd8458ed..d9d3bd92 100644 --- a/EnvelopeGenerator.Infrastructure/DependencyExtensions.cs +++ b/EnvelopeGenerator.Infrastructure/DependencyExtensions.cs @@ -12,6 +12,7 @@ using EnvelopeGenerator.Infrastructure.Executor; using Dapper; using System.ComponentModel.DataAnnotations.Schema; using System.Reflection; +using DigitalData.UserManager.Domain.Entities; namespace EnvelopeGenerator.Infrastructure; @@ -74,11 +75,14 @@ public static class DIExtensions services.AddSQLExecutor(); services.AddSQLExecutor(); - SetDappeTypeMap(); - SetDappeTypeMap(); - SetDappeTypeMap(); - SetDappeTypeMap(); - SetDappeTypeMap(); + SetDapperTypeMap(); + SetDapperTypeMap(); + SetDapperTypeMap(); + SetDapperTypeMap(); + SetDapperTypeMap(); + SetDapperTypeMap(); + + services.AddScoped(); if (sqlExecutorConfiguration is not null || sqlExecutorConfigureOptions is not null) services.AddSQLExecutor(sqlExecutorConfiguration, sqlExecutorConfigureOptions); @@ -100,7 +104,7 @@ public static class DIExtensions return services.AddSingleton(); } - private static void SetDappeTypeMap() + private static void SetDapperTypeMap() { Dapper.SqlMapper.SetTypeMap(typeof(TModel), new CustomPropertyTypeMap( typeof(TModel), diff --git a/EnvelopeGenerator.Infrastructure/Executor/EnvelopeExecutor.cs b/EnvelopeGenerator.Infrastructure/Executor/EnvelopeExecutor.cs new file mode 100644 index 00000000..c178eb0b --- /dev/null +++ b/EnvelopeGenerator.Infrastructure/Executor/EnvelopeExecutor.cs @@ -0,0 +1,43 @@ +using Dapper; +using DigitalData.UserManager.Application.Contracts.Repositories; +using DigitalData.UserManager.Infrastructure.Repositories; +using EnvelopeGenerator.Application.Contracts.SQLExecutor; +using EnvelopeGenerator.Application.SQL; +using EnvelopeGenerator.Domain.Entities; +using Microsoft.Data.SqlClient; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; + +namespace EnvelopeGenerator.Infrastructure.Executor; + +public class EnvelopeExecutor : SQLExecutor, IEnvelopeExecutor +{ + private readonly IUserRepository _userRepository; + + public EnvelopeExecutor(ILogger logger, IServiceProvider provider, IOptions sqlExecutorParamsOptions, IUserRepository userRepository) : base(provider, sqlExecutorParamsOptions) + { + _userRepository = userRepository; + } + + public async Task CreateEnvelopeAsync(DynamicParameters parameters, bool addUser = true, CancellationToken cancellation = default) + { + using var connection = new SqlConnection(Params.ConnectionString); + var sql = Provider.GetRequiredService(); + await connection.OpenAsync(cancellation); + var envelopes = await connection.QueryAsync(sql.Raw, parameters); + var envelope = envelopes.FirstOrDefault(); + + if (envelope is null) + return null; + + // Add User + if (addUser) + { + var user = await _userRepository.ReadByIdAsync(envelope.UserId); + envelope.User = user; + } + + return envelope; + } +} \ No newline at end of file diff --git a/EnvelopeGenerator.Infrastructure/Executor/SQLExecutor.cs b/EnvelopeGenerator.Infrastructure/Executor/SQLExecutor.cs index c8edeeff..5b8a0a3f 100644 --- a/EnvelopeGenerator.Infrastructure/Executor/SQLExecutor.cs +++ b/EnvelopeGenerator.Infrastructure/Executor/SQLExecutor.cs @@ -8,26 +8,26 @@ namespace EnvelopeGenerator.Infrastructure.Executor; public class SQLExecutor : ISQLExecutor { - private readonly SQLExecutorParams _params; + protected readonly SQLExecutorParams Params; - private readonly IServiceProvider _provider; + protected readonly IServiceProvider Provider; public SQLExecutor(IServiceProvider provider, IOptions sqlExecutorParamsOptions) { - _provider = provider; - _params = sqlExecutorParamsOptions.Value; + Provider = provider; + Params = sqlExecutorParamsOptions.Value; } public async Task> Execute(string sql, DynamicParameters parameters, CancellationToken cancellation = default) { - using var connection = new SqlConnection(_params.ConnectionString); + using var connection = new SqlConnection(Params.ConnectionString); await connection.OpenAsync(cancellation); return await connection.QueryAsync(sql, parameters); } public Task> Execute(DynamicParameters parameters, CancellationToken cancellation = default) where TSQL : ISQL { - var sql = _provider.GetRequiredService(); + var sql = Provider.GetRequiredService(); return Execute(sql.Raw, parameters, cancellation); } } \ No newline at end of file From 3955a3232df1fe343572f63215b105c173dff23a Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Mon, 5 May 2025 16:30:39 +0200 Subject: [PATCH 18/94] Refactor envelope creation SQL logic Removed `CreateEnvelopeSQL` and introduced `EnvelopeCreateReadSQL` to handle envelope creation logic. Updated the `Extension` class methods and modified `CreateEnvelopeAsync` in `EnvelopeExecutor` to utilize the new SQL class. --- .../SQL/{CreateEnvelopeSQL.cs => EnvelopeCreateReadSQL.cs} | 4 ++-- EnvelopeGenerator.Infrastructure/Executor/EnvelopeExecutor.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename EnvelopeGenerator.Application/SQL/{CreateEnvelopeSQL.cs => EnvelopeCreateReadSQL.cs} (94%) diff --git a/EnvelopeGenerator.Application/SQL/CreateEnvelopeSQL.cs b/EnvelopeGenerator.Application/SQL/EnvelopeCreateReadSQL.cs similarity index 94% rename from EnvelopeGenerator.Application/SQL/CreateEnvelopeSQL.cs rename to EnvelopeGenerator.Application/SQL/EnvelopeCreateReadSQL.cs index 14f70af3..005932f6 100644 --- a/EnvelopeGenerator.Application/SQL/CreateEnvelopeSQL.cs +++ b/EnvelopeGenerator.Application/SQL/EnvelopeCreateReadSQL.cs @@ -7,7 +7,7 @@ namespace EnvelopeGenerator.Application.SQL; /// /// /// -public class CreateEnvelopeSQL : ISQL +public class EnvelopeCreateReadSQL : ISQL { /// /// @@ -60,7 +60,7 @@ public static class Extension { var parameters = CreateParmas(userId, title, message, tfaEnabled); - var envelopes = await executor.Execute(parameters, cancellation); + var envelopes = await executor.Execute(parameters, cancellation); return envelopes.FirstOrDefault(); } diff --git a/EnvelopeGenerator.Infrastructure/Executor/EnvelopeExecutor.cs b/EnvelopeGenerator.Infrastructure/Executor/EnvelopeExecutor.cs index c178eb0b..dca802ba 100644 --- a/EnvelopeGenerator.Infrastructure/Executor/EnvelopeExecutor.cs +++ b/EnvelopeGenerator.Infrastructure/Executor/EnvelopeExecutor.cs @@ -23,7 +23,7 @@ public class EnvelopeExecutor : SQLExecutor, IEnvelopeExecutor public async Task CreateEnvelopeAsync(DynamicParameters parameters, bool addUser = true, CancellationToken cancellation = default) { using var connection = new SqlConnection(Params.ConnectionString); - var sql = Provider.GetRequiredService(); + var sql = Provider.GetRequiredService(); await connection.OpenAsync(cancellation); var envelopes = await connection.QueryAsync(sql.Raw, parameters); var envelope = envelopes.FirstOrDefault(); From 8cfa28a863f10815bf378ed1e9d6388c2579f6b5 Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Mon, 5 May 2025 16:36:16 +0200 Subject: [PATCH 19/94] Refactor envelope creation logic into Extension class Added a new static class `Extension` in `Extension.cs` to encapsulate methods for creating parameters and asynchronous envelope creation. Removed previous implementations from `EnvelopeCreateReadSQL.cs` for better organization. Updated `using` directives in `CreateEnvelopeCommandHandler.cs` and `EnvelopeCreateReadSQL.cs` for consistency. --- .../Contracts/SQLExecutor/Extension.cs | 58 +++++++++++++++++++ .../Commands/CreateEnvelopeCommandHandler.cs | 1 - .../SQL/EnvelopeCreateReadSQL.cs | 56 +----------------- 3 files changed, 59 insertions(+), 56 deletions(-) create mode 100644 EnvelopeGenerator.Application/Contracts/SQLExecutor/Extension.cs diff --git a/EnvelopeGenerator.Application/Contracts/SQLExecutor/Extension.cs b/EnvelopeGenerator.Application/Contracts/SQLExecutor/Extension.cs new file mode 100644 index 00000000..a765a090 --- /dev/null +++ b/EnvelopeGenerator.Application/Contracts/SQLExecutor/Extension.cs @@ -0,0 +1,58 @@ +using Dapper; +using EnvelopeGenerator.Application.SQL; +using EnvelopeGenerator.Domain.Entities; + +namespace EnvelopeGenerator.Application.Contracts.SQLExecutor; + +/// +/// +/// +public static class Extension +{ + private static DynamicParameters CreateParmas(int userId, string title = "", string message = "", bool tfaEnabled = false) + { + var parameters = new DynamicParameters(); + parameters.Add("@UserId", userId); + parameters.Add("@Title", title); + parameters.Add("@TfaEnabled", tfaEnabled ? 1 : 0); + parameters.Add("@Message", message); + return parameters; + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public static async Task CreateEnvelopeAsync(this ISQLExecutor executor, int userId, string title = "", string message = "", bool tfaEnabled = false, CancellationToken cancellation = default) + where TEntity : class + { + var parameters = CreateParmas(userId, title, message, tfaEnabled); + + var envelopes = await executor.Execute(parameters, cancellation); + return envelopes.FirstOrDefault(); + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public static async Task CreateEnvelopeAsync(this IEnvelopeExecutor executor, int userId, string title = "", string message = "", bool tfaEnabled = false, CancellationToken cancellation = default) + { + var parameters = CreateParmas(userId, title, message, tfaEnabled); + + return await executor.CreateEnvelopeAsync(parameters, cancellation: cancellation); + } +} \ No newline at end of file diff --git a/EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeCommandHandler.cs b/EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeCommandHandler.cs index a4b7d698..5c8cb3e9 100644 --- a/EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeCommandHandler.cs +++ b/EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeCommandHandler.cs @@ -1,6 +1,5 @@ using AutoMapper; using EnvelopeGenerator.Application.Contracts.SQLExecutor; -using EnvelopeGenerator.Application.SQL; using MediatR; namespace EnvelopeGenerator.Application.Envelopes.Commands; diff --git a/EnvelopeGenerator.Application/SQL/EnvelopeCreateReadSQL.cs b/EnvelopeGenerator.Application/SQL/EnvelopeCreateReadSQL.cs index 005932f6..9dc83fde 100644 --- a/EnvelopeGenerator.Application/SQL/EnvelopeCreateReadSQL.cs +++ b/EnvelopeGenerator.Application/SQL/EnvelopeCreateReadSQL.cs @@ -1,5 +1,4 @@ -using Dapper; -using EnvelopeGenerator.Application.Contracts.SQLExecutor; +using EnvelopeGenerator.Application.Contracts.SQLExecutor; using EnvelopeGenerator.Domain.Entities; namespace EnvelopeGenerator.Application.SQL; @@ -27,57 +26,4 @@ public class EnvelopeCreateReadSQL : ISQL FROM [dbo].[TBSIG_ENVELOPE] WHERE [ENVELOPE_UUID] = @OUT_UID; "; -} - -/// -/// -/// -public static class Extension -{ - private static DynamicParameters CreateParmas(int userId, string title = "", string message = "", bool tfaEnabled = false) - { - var parameters = new DynamicParameters(); - parameters.Add("@UserId", userId); - parameters.Add("@Title", title); - parameters.Add("@TfaEnabled", tfaEnabled ? 1 : 0); - parameters.Add("@Message", message); - return parameters; - } - - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public static async Task CreateEnvelopeAsync(this ISQLExecutor executor, int userId, string title = "", string message = "", bool tfaEnabled = false, CancellationToken cancellation = default) - where TEntity : class - { - var parameters = CreateParmas(userId, title, message, tfaEnabled); - - var envelopes = await executor.Execute(parameters, cancellation); - return envelopes.FirstOrDefault(); - } - - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public static async Task CreateEnvelopeAsync(this IEnvelopeExecutor executor, int userId, string title = "", string message = "", bool tfaEnabled = false, CancellationToken cancellation = default) - { - var parameters = CreateParmas(userId, title, message, tfaEnabled); - - return await executor.CreateEnvelopeAsync(parameters, cancellation: cancellation); - } } \ No newline at end of file From 4cabaf31917c8860411349c0e5fc1c62e7c5c384 Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Tue, 6 May 2025 01:30:59 +0200 Subject: [PATCH 20/94] Refactor envelope handling to use ISQLExecutor This commit removes the IEnvelopeExecutor interface and its implementation, replacing it with ISQLExecutor. The CreateEnvelopeAsync method in Extension.cs now directly creates DynamicParameters. The CreateEnvelopeCommandHandler has been updated to utilize ISQLExecutor. Additionally, the EnvelopeExecutor class has been removed, and a new SQL command class, EnvelopeReceiverCreateReadSQL, has been added for managing envelope receiver SQL operations. --- .../Contracts/SQLExecutor/Extension.cs | 40 +++-------------- .../SQLExecutor/IEnvelopeExecutor.cs | 19 -------- .../Commands/CreateEnvelopeCommandHandler.cs | 10 ++--- .../SQL/EnvelopeReceiverCreateReadSQL.cs | 33 ++++++++++++++ .../DependencyExtensions.cs | 2 - .../Executor/EnvelopeExecutor.cs | 43 ------------------- 6 files changed, 45 insertions(+), 102 deletions(-) delete mode 100644 EnvelopeGenerator.Application/Contracts/SQLExecutor/IEnvelopeExecutor.cs create mode 100644 EnvelopeGenerator.Application/SQL/EnvelopeReceiverCreateReadSQL.cs delete mode 100644 EnvelopeGenerator.Infrastructure/Executor/EnvelopeExecutor.cs diff --git a/EnvelopeGenerator.Application/Contracts/SQLExecutor/Extension.cs b/EnvelopeGenerator.Application/Contracts/SQLExecutor/Extension.cs index a765a090..2972ce0d 100644 --- a/EnvelopeGenerator.Application/Contracts/SQLExecutor/Extension.cs +++ b/EnvelopeGenerator.Application/Contracts/SQLExecutor/Extension.cs @@ -9,20 +9,9 @@ namespace EnvelopeGenerator.Application.Contracts.SQLExecutor; /// public static class Extension { - private static DynamicParameters CreateParmas(int userId, string title = "", string message = "", bool tfaEnabled = false) - { - var parameters = new DynamicParameters(); - parameters.Add("@UserId", userId); - parameters.Add("@Title", title); - parameters.Add("@TfaEnabled", tfaEnabled ? 1 : 0); - parameters.Add("@Message", message); - return parameters; - } - /// /// /// - /// /// /// /// @@ -30,29 +19,14 @@ public static class Extension /// /// /// - public static async Task CreateEnvelopeAsync(this ISQLExecutor executor, int userId, string title = "", string message = "", bool tfaEnabled = false, CancellationToken cancellation = default) - where TEntity : class + public static async Task CreateEnvelopeAsync(this ISQLExecutor executor, int userId, string title = "", string message = "", bool tfaEnabled = false, CancellationToken cancellation = default) { - var parameters = CreateParmas(userId, title, message, tfaEnabled); - - var envelopes = await executor.Execute(parameters, cancellation); + var parameters = new DynamicParameters(); + parameters.Add("@UserId", userId); + parameters.Add("@Title", title); + parameters.Add("@TfaEnabled", tfaEnabled ? 1 : 0); + parameters.Add("@Message", message); + var envelopes = await executor.Execute(parameters, cancellation); return envelopes.FirstOrDefault(); } - - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public static async Task CreateEnvelopeAsync(this IEnvelopeExecutor executor, int userId, string title = "", string message = "", bool tfaEnabled = false, CancellationToken cancellation = default) - { - var parameters = CreateParmas(userId, title, message, tfaEnabled); - - return await executor.CreateEnvelopeAsync(parameters, cancellation: cancellation); - } } \ No newline at end of file diff --git a/EnvelopeGenerator.Application/Contracts/SQLExecutor/IEnvelopeExecutor.cs b/EnvelopeGenerator.Application/Contracts/SQLExecutor/IEnvelopeExecutor.cs deleted file mode 100644 index f197cadf..00000000 --- a/EnvelopeGenerator.Application/Contracts/SQLExecutor/IEnvelopeExecutor.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Dapper; -using EnvelopeGenerator.Domain.Entities; - -namespace EnvelopeGenerator.Application.Contracts.SQLExecutor; - -/// -/// -/// -public interface IEnvelopeExecutor : ISQLExecutor -{ - /// - /// - /// - /// - /// - /// - /// - Task CreateEnvelopeAsync(DynamicParameters parameters, bool addUser = true, CancellationToken cancellation = default); -} diff --git a/EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeCommandHandler.cs b/EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeCommandHandler.cs index 5c8cb3e9..35bfe61f 100644 --- a/EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeCommandHandler.cs +++ b/EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeCommandHandler.cs @@ -9,18 +9,18 @@ namespace EnvelopeGenerator.Application.Envelopes.Commands; /// public class CreateEnvelopeCommandHandler : IRequestHandler { - private readonly IEnvelopeExecutor _envelopeExecutor; + private readonly ISQLExecutor _executor; private readonly IMapper _mapper; /// /// /// - /// + /// /// - public CreateEnvelopeCommandHandler(IEnvelopeExecutor envelopeExecutor, IMapper mapper) + public CreateEnvelopeCommandHandler(ISQLExecutor executor, IMapper mapper) { - _envelopeExecutor = envelopeExecutor; + _executor = executor; _mapper = mapper; } @@ -34,7 +34,7 @@ public class CreateEnvelopeCommandHandler : IRequestHandler(envelope); } diff --git a/EnvelopeGenerator.Application/SQL/EnvelopeReceiverCreateReadSQL.cs b/EnvelopeGenerator.Application/SQL/EnvelopeReceiverCreateReadSQL.cs new file mode 100644 index 00000000..3f9b2db8 --- /dev/null +++ b/EnvelopeGenerator.Application/SQL/EnvelopeReceiverCreateReadSQL.cs @@ -0,0 +1,33 @@ +using EnvelopeGenerator.Application.Contracts.SQLExecutor; +using EnvelopeGenerator.Domain.Entities; + +namespace EnvelopeGenerator.Application.SQL; + +/// +/// +/// +public class EnvelopeReceiverCreateReadSQL : ISQL +{ + /// + /// + /// + public string Raw => @" + USE [DD_ECM] + GO + + DECLARE @OUT_RECEIVER_ID int + + DECLARE @ENV_UID varchar(36) = @ENV_UID + + EXEC [dbo].[PRSIG_API_CREATE_RECEIVER] + @ENV_UID = @ENV_UID, + @EMAIL_ADRESS = @EMAIL_ADRESS , + @SALUTATION = @SALUTATION, + @PHONE = @PHONE, + @OUT_RECEIVER_ID = @OUT_RECEIVER_ID OUTPUT + + SELECT TOP(1) * + FROM TBSIG_ENVELOPE_RECEIVER + WHERE [GUID] = @OUT_RECEIVER_ID; + "; +} diff --git a/EnvelopeGenerator.Infrastructure/DependencyExtensions.cs b/EnvelopeGenerator.Infrastructure/DependencyExtensions.cs index d9d3bd92..c1138538 100644 --- a/EnvelopeGenerator.Infrastructure/DependencyExtensions.cs +++ b/EnvelopeGenerator.Infrastructure/DependencyExtensions.cs @@ -82,8 +82,6 @@ public static class DIExtensions SetDapperTypeMap(); SetDapperTypeMap(); - services.AddScoped(); - if (sqlExecutorConfiguration is not null || sqlExecutorConfigureOptions is not null) services.AddSQLExecutor(sqlExecutorConfiguration, sqlExecutorConfigureOptions); diff --git a/EnvelopeGenerator.Infrastructure/Executor/EnvelopeExecutor.cs b/EnvelopeGenerator.Infrastructure/Executor/EnvelopeExecutor.cs deleted file mode 100644 index dca802ba..00000000 --- a/EnvelopeGenerator.Infrastructure/Executor/EnvelopeExecutor.cs +++ /dev/null @@ -1,43 +0,0 @@ -using Dapper; -using DigitalData.UserManager.Application.Contracts.Repositories; -using DigitalData.UserManager.Infrastructure.Repositories; -using EnvelopeGenerator.Application.Contracts.SQLExecutor; -using EnvelopeGenerator.Application.SQL; -using EnvelopeGenerator.Domain.Entities; -using Microsoft.Data.SqlClient; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; - -namespace EnvelopeGenerator.Infrastructure.Executor; - -public class EnvelopeExecutor : SQLExecutor, IEnvelopeExecutor -{ - private readonly IUserRepository _userRepository; - - public EnvelopeExecutor(ILogger logger, IServiceProvider provider, IOptions sqlExecutorParamsOptions, IUserRepository userRepository) : base(provider, sqlExecutorParamsOptions) - { - _userRepository = userRepository; - } - - public async Task CreateEnvelopeAsync(DynamicParameters parameters, bool addUser = true, CancellationToken cancellation = default) - { - using var connection = new SqlConnection(Params.ConnectionString); - var sql = Provider.GetRequiredService(); - await connection.OpenAsync(cancellation); - var envelopes = await connection.QueryAsync(sql.Raw, parameters); - var envelope = envelopes.FirstOrDefault(); - - if (envelope is null) - return null; - - // Add User - if (addUser) - { - var user = await _userRepository.ReadByIdAsync(envelope.UserId); - envelope.User = user; - } - - return envelope; - } -} \ No newline at end of file From 1b515ea9040aba4a4fb52f82488d92d1ca142c15 Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Tue, 6 May 2025 01:41:42 +0200 Subject: [PATCH 21/94] Add AddReceiver method and refactor SQL classes Introduced a new `AddReceiver` method in the `Extension` class to facilitate adding a receiver to an envelope. This method includes parameters for `envelope_uuid`, `emailAdress`, `salutation`, and an optional `phone`, along with XML documentation for clarity. Removed the `EnvelopeReceiverCreateReadSQL` class and added the `EnvelopeReceiverAddReadSQL` class, which defines the SQL command for adding a receiver. The new class also includes XML documentation comments for better understanding. --- .../Contracts/SQLExecutor/Extension.cs | 22 +++++++++++++++++++ ...adSQL.cs => EnvelopeReceiverAddReadSQL.cs} | 2 +- 2 files changed, 23 insertions(+), 1 deletion(-) rename EnvelopeGenerator.Application/SQL/{EnvelopeReceiverCreateReadSQL.cs => EnvelopeReceiverAddReadSQL.cs} (91%) diff --git a/EnvelopeGenerator.Application/Contracts/SQLExecutor/Extension.cs b/EnvelopeGenerator.Application/Contracts/SQLExecutor/Extension.cs index 2972ce0d..2d21afc5 100644 --- a/EnvelopeGenerator.Application/Contracts/SQLExecutor/Extension.cs +++ b/EnvelopeGenerator.Application/Contracts/SQLExecutor/Extension.cs @@ -29,4 +29,26 @@ public static class Extension var envelopes = await executor.Execute(parameters, cancellation); return envelopes.FirstOrDefault(); } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public static async Task AddReceiver(this ISQLExecutor executor, string envelope_uuid, string emailAdress, string salutation, string? phone = null, CancellationToken cancellation = default) + { + var parameters = new DynamicParameters(); + parameters.Add("@ENV_UID", envelope_uuid); + parameters.Add("@EMAIL_ADRESS", emailAdress); + parameters.Add("@SALUTATION", salutation); + parameters.Add("@PHONE", phone); + + var envelopeReceivers = await executor.Execute(parameters, cancellation); + return envelopeReceivers.FirstOrDefault(); + } } \ No newline at end of file diff --git a/EnvelopeGenerator.Application/SQL/EnvelopeReceiverCreateReadSQL.cs b/EnvelopeGenerator.Application/SQL/EnvelopeReceiverAddReadSQL.cs similarity index 91% rename from EnvelopeGenerator.Application/SQL/EnvelopeReceiverCreateReadSQL.cs rename to EnvelopeGenerator.Application/SQL/EnvelopeReceiverAddReadSQL.cs index 3f9b2db8..e4fe1881 100644 --- a/EnvelopeGenerator.Application/SQL/EnvelopeReceiverCreateReadSQL.cs +++ b/EnvelopeGenerator.Application/SQL/EnvelopeReceiverAddReadSQL.cs @@ -6,7 +6,7 @@ namespace EnvelopeGenerator.Application.SQL; /// /// /// -public class EnvelopeReceiverCreateReadSQL : ISQL +public class EnvelopeReceiverAddReadSQL : ISQL { /// /// From b0eb1b6389fc5f00e00a37d9a149869e09581203 Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Tue, 6 May 2025 09:44:15 +0200 Subject: [PATCH 22/94] Revert "Add AddReceiver method and refactor SQL classes" This reverts commit 1b515ea9040aba4a4fb52f82488d92d1ca142c15. --- .../Contracts/SQLExecutor/Extension.cs | 22 ------------------- ...QL.cs => EnvelopeReceiverCreateReadSQL.cs} | 2 +- 2 files changed, 1 insertion(+), 23 deletions(-) rename EnvelopeGenerator.Application/SQL/{EnvelopeReceiverAddReadSQL.cs => EnvelopeReceiverCreateReadSQL.cs} (91%) diff --git a/EnvelopeGenerator.Application/Contracts/SQLExecutor/Extension.cs b/EnvelopeGenerator.Application/Contracts/SQLExecutor/Extension.cs index 2d21afc5..2972ce0d 100644 --- a/EnvelopeGenerator.Application/Contracts/SQLExecutor/Extension.cs +++ b/EnvelopeGenerator.Application/Contracts/SQLExecutor/Extension.cs @@ -29,26 +29,4 @@ public static class Extension var envelopes = await executor.Execute(parameters, cancellation); return envelopes.FirstOrDefault(); } - - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public static async Task AddReceiver(this ISQLExecutor executor, string envelope_uuid, string emailAdress, string salutation, string? phone = null, CancellationToken cancellation = default) - { - var parameters = new DynamicParameters(); - parameters.Add("@ENV_UID", envelope_uuid); - parameters.Add("@EMAIL_ADRESS", emailAdress); - parameters.Add("@SALUTATION", salutation); - parameters.Add("@PHONE", phone); - - var envelopeReceivers = await executor.Execute(parameters, cancellation); - return envelopeReceivers.FirstOrDefault(); - } } \ No newline at end of file diff --git a/EnvelopeGenerator.Application/SQL/EnvelopeReceiverAddReadSQL.cs b/EnvelopeGenerator.Application/SQL/EnvelopeReceiverCreateReadSQL.cs similarity index 91% rename from EnvelopeGenerator.Application/SQL/EnvelopeReceiverAddReadSQL.cs rename to EnvelopeGenerator.Application/SQL/EnvelopeReceiverCreateReadSQL.cs index e4fe1881..3f9b2db8 100644 --- a/EnvelopeGenerator.Application/SQL/EnvelopeReceiverAddReadSQL.cs +++ b/EnvelopeGenerator.Application/SQL/EnvelopeReceiverCreateReadSQL.cs @@ -6,7 +6,7 @@ namespace EnvelopeGenerator.Application.SQL; /// /// /// -public class EnvelopeReceiverAddReadSQL : ISQL +public class EnvelopeReceiverCreateReadSQL : ISQL { /// /// From b609253893d00da160bccb8ad05867e41ce050c8 Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Tue, 6 May 2025 09:45:38 +0200 Subject: [PATCH 23/94] Revert "Refactor envelope handling to use ISQLExecutor" This reverts commit 4cabaf31917c8860411349c0e5fc1c62e7c5c384. --- .../Contracts/SQLExecutor/Extension.cs | 40 ++++++++++++++--- .../SQLExecutor/IEnvelopeExecutor.cs | 19 ++++++++ .../Commands/CreateEnvelopeCommandHandler.cs | 10 ++--- .../SQL/EnvelopeReceiverCreateReadSQL.cs | 33 -------------- .../DependencyExtensions.cs | 2 + .../Executor/EnvelopeExecutor.cs | 43 +++++++++++++++++++ 6 files changed, 102 insertions(+), 45 deletions(-) create mode 100644 EnvelopeGenerator.Application/Contracts/SQLExecutor/IEnvelopeExecutor.cs delete mode 100644 EnvelopeGenerator.Application/SQL/EnvelopeReceiverCreateReadSQL.cs create mode 100644 EnvelopeGenerator.Infrastructure/Executor/EnvelopeExecutor.cs diff --git a/EnvelopeGenerator.Application/Contracts/SQLExecutor/Extension.cs b/EnvelopeGenerator.Application/Contracts/SQLExecutor/Extension.cs index 2972ce0d..a765a090 100644 --- a/EnvelopeGenerator.Application/Contracts/SQLExecutor/Extension.cs +++ b/EnvelopeGenerator.Application/Contracts/SQLExecutor/Extension.cs @@ -9,9 +9,20 @@ namespace EnvelopeGenerator.Application.Contracts.SQLExecutor; /// public static class Extension { + private static DynamicParameters CreateParmas(int userId, string title = "", string message = "", bool tfaEnabled = false) + { + var parameters = new DynamicParameters(); + parameters.Add("@UserId", userId); + parameters.Add("@Title", title); + parameters.Add("@TfaEnabled", tfaEnabled ? 1 : 0); + parameters.Add("@Message", message); + return parameters; + } + /// /// /// + /// /// /// /// @@ -19,14 +30,29 @@ public static class Extension /// /// /// - public static async Task CreateEnvelopeAsync(this ISQLExecutor executor, int userId, string title = "", string message = "", bool tfaEnabled = false, CancellationToken cancellation = default) + public static async Task CreateEnvelopeAsync(this ISQLExecutor executor, int userId, string title = "", string message = "", bool tfaEnabled = false, CancellationToken cancellation = default) + where TEntity : class { - var parameters = new DynamicParameters(); - parameters.Add("@UserId", userId); - parameters.Add("@Title", title); - parameters.Add("@TfaEnabled", tfaEnabled ? 1 : 0); - parameters.Add("@Message", message); - var envelopes = await executor.Execute(parameters, cancellation); + var parameters = CreateParmas(userId, title, message, tfaEnabled); + + var envelopes = await executor.Execute(parameters, cancellation); return envelopes.FirstOrDefault(); } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public static async Task CreateEnvelopeAsync(this IEnvelopeExecutor executor, int userId, string title = "", string message = "", bool tfaEnabled = false, CancellationToken cancellation = default) + { + var parameters = CreateParmas(userId, title, message, tfaEnabled); + + return await executor.CreateEnvelopeAsync(parameters, cancellation: cancellation); + } } \ No newline at end of file diff --git a/EnvelopeGenerator.Application/Contracts/SQLExecutor/IEnvelopeExecutor.cs b/EnvelopeGenerator.Application/Contracts/SQLExecutor/IEnvelopeExecutor.cs new file mode 100644 index 00000000..f197cadf --- /dev/null +++ b/EnvelopeGenerator.Application/Contracts/SQLExecutor/IEnvelopeExecutor.cs @@ -0,0 +1,19 @@ +using Dapper; +using EnvelopeGenerator.Domain.Entities; + +namespace EnvelopeGenerator.Application.Contracts.SQLExecutor; + +/// +/// +/// +public interface IEnvelopeExecutor : ISQLExecutor +{ + /// + /// + /// + /// + /// + /// + /// + Task CreateEnvelopeAsync(DynamicParameters parameters, bool addUser = true, CancellationToken cancellation = default); +} diff --git a/EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeCommandHandler.cs b/EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeCommandHandler.cs index 35bfe61f..5c8cb3e9 100644 --- a/EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeCommandHandler.cs +++ b/EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeCommandHandler.cs @@ -9,18 +9,18 @@ namespace EnvelopeGenerator.Application.Envelopes.Commands; /// public class CreateEnvelopeCommandHandler : IRequestHandler { - private readonly ISQLExecutor _executor; + private readonly IEnvelopeExecutor _envelopeExecutor; private readonly IMapper _mapper; /// /// /// - /// + /// /// - public CreateEnvelopeCommandHandler(ISQLExecutor executor, IMapper mapper) + public CreateEnvelopeCommandHandler(IEnvelopeExecutor envelopeExecutor, IMapper mapper) { - _executor = executor; + _envelopeExecutor = envelopeExecutor; _mapper = mapper; } @@ -34,7 +34,7 @@ public class CreateEnvelopeCommandHandler : IRequestHandler(envelope); } diff --git a/EnvelopeGenerator.Application/SQL/EnvelopeReceiverCreateReadSQL.cs b/EnvelopeGenerator.Application/SQL/EnvelopeReceiverCreateReadSQL.cs deleted file mode 100644 index 3f9b2db8..00000000 --- a/EnvelopeGenerator.Application/SQL/EnvelopeReceiverCreateReadSQL.cs +++ /dev/null @@ -1,33 +0,0 @@ -using EnvelopeGenerator.Application.Contracts.SQLExecutor; -using EnvelopeGenerator.Domain.Entities; - -namespace EnvelopeGenerator.Application.SQL; - -/// -/// -/// -public class EnvelopeReceiverCreateReadSQL : ISQL -{ - /// - /// - /// - public string Raw => @" - USE [DD_ECM] - GO - - DECLARE @OUT_RECEIVER_ID int - - DECLARE @ENV_UID varchar(36) = @ENV_UID - - EXEC [dbo].[PRSIG_API_CREATE_RECEIVER] - @ENV_UID = @ENV_UID, - @EMAIL_ADRESS = @EMAIL_ADRESS , - @SALUTATION = @SALUTATION, - @PHONE = @PHONE, - @OUT_RECEIVER_ID = @OUT_RECEIVER_ID OUTPUT - - SELECT TOP(1) * - FROM TBSIG_ENVELOPE_RECEIVER - WHERE [GUID] = @OUT_RECEIVER_ID; - "; -} diff --git a/EnvelopeGenerator.Infrastructure/DependencyExtensions.cs b/EnvelopeGenerator.Infrastructure/DependencyExtensions.cs index c1138538..d9d3bd92 100644 --- a/EnvelopeGenerator.Infrastructure/DependencyExtensions.cs +++ b/EnvelopeGenerator.Infrastructure/DependencyExtensions.cs @@ -82,6 +82,8 @@ public static class DIExtensions SetDapperTypeMap(); SetDapperTypeMap(); + services.AddScoped(); + if (sqlExecutorConfiguration is not null || sqlExecutorConfigureOptions is not null) services.AddSQLExecutor(sqlExecutorConfiguration, sqlExecutorConfigureOptions); diff --git a/EnvelopeGenerator.Infrastructure/Executor/EnvelopeExecutor.cs b/EnvelopeGenerator.Infrastructure/Executor/EnvelopeExecutor.cs new file mode 100644 index 00000000..dca802ba --- /dev/null +++ b/EnvelopeGenerator.Infrastructure/Executor/EnvelopeExecutor.cs @@ -0,0 +1,43 @@ +using Dapper; +using DigitalData.UserManager.Application.Contracts.Repositories; +using DigitalData.UserManager.Infrastructure.Repositories; +using EnvelopeGenerator.Application.Contracts.SQLExecutor; +using EnvelopeGenerator.Application.SQL; +using EnvelopeGenerator.Domain.Entities; +using Microsoft.Data.SqlClient; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; + +namespace EnvelopeGenerator.Infrastructure.Executor; + +public class EnvelopeExecutor : SQLExecutor, IEnvelopeExecutor +{ + private readonly IUserRepository _userRepository; + + public EnvelopeExecutor(ILogger logger, IServiceProvider provider, IOptions sqlExecutorParamsOptions, IUserRepository userRepository) : base(provider, sqlExecutorParamsOptions) + { + _userRepository = userRepository; + } + + public async Task CreateEnvelopeAsync(DynamicParameters parameters, bool addUser = true, CancellationToken cancellation = default) + { + using var connection = new SqlConnection(Params.ConnectionString); + var sql = Provider.GetRequiredService(); + await connection.OpenAsync(cancellation); + var envelopes = await connection.QueryAsync(sql.Raw, parameters); + var envelope = envelopes.FirstOrDefault(); + + if (envelope is null) + return null; + + // Add User + if (addUser) + { + var user = await _userRepository.ReadByIdAsync(envelope.UserId); + envelope.User = user; + } + + return envelope; + } +} \ No newline at end of file From 7b7a4b4f65180dd354892db5be82b13ac1c769e3 Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Tue, 6 May 2025 09:50:20 +0200 Subject: [PATCH 24/94] Add AddEnvelopeReceiver method and SQL class Introduces the `AddEnvelopeReceiver` method in the `Extension` class to add envelope receivers with parameters for UUID, email, salutation, and optional phone. Also adds the `EnvelopeReceiverAddReadSQL` class implementing `ISQL`, containing a SQL query to create a new receiver using the stored procedure `PRSIG_API_CREATE_RECEIVER`. Includes XML documentation for improved code readability and maintainability. --- .../Contracts/SQLExecutor/Extension.cs | 22 +++++++++++++ .../SQL/EnvelopeReceiverAddReadSQL.cs | 33 +++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 EnvelopeGenerator.Application/SQL/EnvelopeReceiverAddReadSQL.cs diff --git a/EnvelopeGenerator.Application/Contracts/SQLExecutor/Extension.cs b/EnvelopeGenerator.Application/Contracts/SQLExecutor/Extension.cs index a765a090..e0a49e98 100644 --- a/EnvelopeGenerator.Application/Contracts/SQLExecutor/Extension.cs +++ b/EnvelopeGenerator.Application/Contracts/SQLExecutor/Extension.cs @@ -55,4 +55,26 @@ public static class Extension return await executor.CreateEnvelopeAsync(parameters, cancellation: cancellation); } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public static async Task AddEnvelopeReceiver(this ISQLExecutor executor, string envelope_uuid, string emailAdress, string salutation, string? phone = null, CancellationToken cancellation = default) + { + var parameters = new DynamicParameters(); + parameters.Add("@ENV_UID", envelope_uuid); + parameters.Add("@EMAIL_ADRESS", emailAdress); + parameters.Add("@SALUTATION", salutation); + parameters.Add("@PHONE", phone); + + var envelopeReceivers = await executor.Execute(parameters, cancellation); + return envelopeReceivers.FirstOrDefault(); + } } \ No newline at end of file diff --git a/EnvelopeGenerator.Application/SQL/EnvelopeReceiverAddReadSQL.cs b/EnvelopeGenerator.Application/SQL/EnvelopeReceiverAddReadSQL.cs new file mode 100644 index 00000000..e4fe1881 --- /dev/null +++ b/EnvelopeGenerator.Application/SQL/EnvelopeReceiverAddReadSQL.cs @@ -0,0 +1,33 @@ +using EnvelopeGenerator.Application.Contracts.SQLExecutor; +using EnvelopeGenerator.Domain.Entities; + +namespace EnvelopeGenerator.Application.SQL; + +/// +/// +/// +public class EnvelopeReceiverAddReadSQL : ISQL +{ + /// + /// + /// + public string Raw => @" + USE [DD_ECM] + GO + + DECLARE @OUT_RECEIVER_ID int + + DECLARE @ENV_UID varchar(36) = @ENV_UID + + EXEC [dbo].[PRSIG_API_CREATE_RECEIVER] + @ENV_UID = @ENV_UID, + @EMAIL_ADRESS = @EMAIL_ADRESS , + @SALUTATION = @SALUTATION, + @PHONE = @PHONE, + @OUT_RECEIVER_ID = @OUT_RECEIVER_ID OUTPUT + + SELECT TOP(1) * + FROM TBSIG_ENVELOPE_RECEIVER + WHERE [GUID] = @OUT_RECEIVER_ID; + "; +} From 40dc0ecda3d889ff0bbc3bc6976bfae077d8f44a Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Tue, 6 May 2025 09:56:35 +0200 Subject: [PATCH 25/94] Add CreateEnvelopeReceiverResponse record Introduces the `CreateEnvelopeReceiverResponse` record in the `EnvelopeGenerator.Application.EnvelopeReceivers.Commands.Create` namespace. This record inherits from `ReadEnvelopeReceiverQuery` and includes an optional `Status` parameter of type `EnvelopeStatusQuery`. Also adds necessary using directives. --- .../Commands/Create/CreateEnvelopeReceiverResponse.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverResponse.cs diff --git a/EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverResponse.cs b/EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverResponse.cs new file mode 100644 index 00000000..aff14353 --- /dev/null +++ b/EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverResponse.cs @@ -0,0 +1,11 @@ +using EnvelopeGenerator.Application.EnvelopeReceivers.Queries.Read; + +namespace EnvelopeGenerator.Application.EnvelopeReceivers.Commands.Create; + +/// +/// +/// +/// +public record CreateEnvelopeReceiverResponse(EnvelopeStatusQuery? Status = null) : ReadEnvelopeReceiverQuery(Status) +{ +} From 749366fff574c1d2961a59d11215e08c1720ebd1 Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Tue, 6 May 2025 09:59:18 +0200 Subject: [PATCH 26/94] Refactor DI for Envelope services and add new executor Updated DIExtensions to register IEnvelopeExecutor as a singleton and added IEnvelopeReceiverExecutor as a new singleton service. Introduced IEnvelopeReceiverExecutor interface and implemented it in the new EnvelopeReceiverExecutor class for future envelope processing functionality. --- .../Contracts/SQLExecutor/IEnvelopeReceiverExecutor.cs | 8 ++++++++ EnvelopeGenerator.Infrastructure/DependencyExtensions.cs | 3 ++- .../Executor/EnvelopeReceiverExecutor.cs | 7 +++++++ 3 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 EnvelopeGenerator.Application/Contracts/SQLExecutor/IEnvelopeReceiverExecutor.cs create mode 100644 EnvelopeGenerator.Infrastructure/Executor/EnvelopeReceiverExecutor.cs diff --git a/EnvelopeGenerator.Application/Contracts/SQLExecutor/IEnvelopeReceiverExecutor.cs b/EnvelopeGenerator.Application/Contracts/SQLExecutor/IEnvelopeReceiverExecutor.cs new file mode 100644 index 00000000..73120389 --- /dev/null +++ b/EnvelopeGenerator.Application/Contracts/SQLExecutor/IEnvelopeReceiverExecutor.cs @@ -0,0 +1,8 @@ +namespace EnvelopeGenerator.Application.Contracts.SQLExecutor; + +/// +/// +/// +public interface IEnvelopeReceiverExecutor +{ +} diff --git a/EnvelopeGenerator.Infrastructure/DependencyExtensions.cs b/EnvelopeGenerator.Infrastructure/DependencyExtensions.cs index d9d3bd92..90a3dbc6 100644 --- a/EnvelopeGenerator.Infrastructure/DependencyExtensions.cs +++ b/EnvelopeGenerator.Infrastructure/DependencyExtensions.cs @@ -82,7 +82,8 @@ public static class DIExtensions SetDapperTypeMap(); SetDapperTypeMap(); - services.AddScoped(); + services.AddSingleton(); + services.AddSingleton(); if (sqlExecutorConfiguration is not null || sqlExecutorConfigureOptions is not null) services.AddSQLExecutor(sqlExecutorConfiguration, sqlExecutorConfigureOptions); diff --git a/EnvelopeGenerator.Infrastructure/Executor/EnvelopeReceiverExecutor.cs b/EnvelopeGenerator.Infrastructure/Executor/EnvelopeReceiverExecutor.cs new file mode 100644 index 00000000..080d5ab3 --- /dev/null +++ b/EnvelopeGenerator.Infrastructure/Executor/EnvelopeReceiverExecutor.cs @@ -0,0 +1,7 @@ +using EnvelopeGenerator.Application.Contracts.SQLExecutor; + +namespace EnvelopeGenerator.Infrastructure.Executor; + +public class EnvelopeReceiverExecutor: IEnvelopeReceiverExecutor +{ +} From 82fc7fa7626dfddb24346fb2b6030103dd8b18fd Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Tue, 6 May 2025 10:33:17 +0200 Subject: [PATCH 27/94] Refactor EnvelopeReceiverExecutor and Repository methods - Updated `EnvelopeReceiverExecutor` to inherit from `SQLExecutor` and added `_erRepository` for enhanced data access. - Introduced `AddEnvelopeReceiverAsync` method for retrieving envelope receivers. - Modified `ReadById` in `EnvelopeReceiverRepository` to support flexible querying with new parameters. - Updated `ReadByIdAsync` to align with changes in `ReadById`. --- .../Executor/EnvelopeExecutor.cs | 3 +- .../Executor/EnvelopeReceiverExecutor.cs | 32 +++++++++++++++++-- .../Repositories/EnvlopeReceiverRepository.cs | 12 ++++++- 3 files changed, 42 insertions(+), 5 deletions(-) diff --git a/EnvelopeGenerator.Infrastructure/Executor/EnvelopeExecutor.cs b/EnvelopeGenerator.Infrastructure/Executor/EnvelopeExecutor.cs index dca802ba..9b16ec1e 100644 --- a/EnvelopeGenerator.Infrastructure/Executor/EnvelopeExecutor.cs +++ b/EnvelopeGenerator.Infrastructure/Executor/EnvelopeExecutor.cs @@ -1,6 +1,5 @@ using Dapper; using DigitalData.UserManager.Application.Contracts.Repositories; -using DigitalData.UserManager.Infrastructure.Repositories; using EnvelopeGenerator.Application.Contracts.SQLExecutor; using EnvelopeGenerator.Application.SQL; using EnvelopeGenerator.Domain.Entities; @@ -15,7 +14,7 @@ public class EnvelopeExecutor : SQLExecutor, IEnvelopeExecutor { private readonly IUserRepository _userRepository; - public EnvelopeExecutor(ILogger logger, IServiceProvider provider, IOptions sqlExecutorParamsOptions, IUserRepository userRepository) : base(provider, sqlExecutorParamsOptions) + public EnvelopeExecutor(IServiceProvider provider, IOptions sqlExecutorParamsOptions, IUserRepository userRepository) : base(provider, sqlExecutorParamsOptions) { _userRepository = userRepository; } diff --git a/EnvelopeGenerator.Infrastructure/Executor/EnvelopeReceiverExecutor.cs b/EnvelopeGenerator.Infrastructure/Executor/EnvelopeReceiverExecutor.cs index 080d5ab3..7120aaa4 100644 --- a/EnvelopeGenerator.Infrastructure/Executor/EnvelopeReceiverExecutor.cs +++ b/EnvelopeGenerator.Infrastructure/Executor/EnvelopeReceiverExecutor.cs @@ -1,7 +1,35 @@ -using EnvelopeGenerator.Application.Contracts.SQLExecutor; +using Dapper; +using EnvelopeGenerator.Application.Contracts.Repositories; +using EnvelopeGenerator.Application.Contracts.SQLExecutor; +using EnvelopeGenerator.Application.SQL; +using EnvelopeGenerator.Domain.Entities; +using Microsoft.Data.SqlClient; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; namespace EnvelopeGenerator.Infrastructure.Executor; -public class EnvelopeReceiverExecutor: IEnvelopeReceiverExecutor +public class EnvelopeReceiverExecutor: SQLExecutor, IEnvelopeReceiverExecutor { + private readonly IEnvelopeReceiverRepository _erRepository; + + public EnvelopeReceiverExecutor(ILogger logger, IServiceProvider provider, IOptions sqlExecutorParamsOptions, IEnvelopeReceiverRepository erRepository) : base(provider, sqlExecutorParamsOptions) + { + _erRepository = erRepository; + } + + public async Task AddEnvelopeReceiverAsync(DynamicParameters parameters, bool addEnvelope = true, CancellationToken cancellation = default) + { + using var connection = new SqlConnection(Params.ConnectionString); + var sql = Provider.GetRequiredService(); + await connection.OpenAsync(cancellation); + var envelopeReceivers = await connection.QueryAsync(sql.Raw, parameters); + var er = envelopeReceivers.FirstOrDefault(); + + if (er is null) + return null; + + return await _erRepository.ReadByIdAsync(envelopeId: er.EnvelopeId, receiverId: er.ReceiverId); + } } diff --git a/EnvelopeGenerator.Infrastructure/Repositories/EnvlopeReceiverRepository.cs b/EnvelopeGenerator.Infrastructure/Repositories/EnvlopeReceiverRepository.cs index 8d5f471b..279d7f77 100644 --- a/EnvelopeGenerator.Infrastructure/Repositories/EnvlopeReceiverRepository.cs +++ b/EnvelopeGenerator.Infrastructure/Repositories/EnvlopeReceiverRepository.cs @@ -48,9 +48,19 @@ public class EnvelopeReceiverRepository : CRUDRepository CountAsync(string uuid, string signature) => await ReadWhere(uuid: uuid, signature: signature).CountAsync(); - private IQueryable ReadById(int envelopeId, int receiverId, bool readOnly = true) + private IQueryable ReadById(int envelopeId, int receiverId, bool withEnvelope = true, bool withReceiver = true, bool readOnly = true) { var query = readOnly ? _dbSet.AsNoTracking() : _dbSet; + + if (withEnvelope) + query = query + .Include(er => er.Envelope).ThenInclude(e => e!.Documents!).ThenInclude(d => d.Elements!.Where(e => e.Receiver!.Id == receiverId)) + .Include(er => er.Envelope).ThenInclude(e => e!.History) + .Include(er => er.Envelope).ThenInclude(e => e!.User); + + if (withReceiver) + query = query.Include(er => er.Receiver); + return query.Where(er => er.EnvelopeId == envelopeId && er.ReceiverId == receiverId); } From 37032a48c9fb93e0d4506fab0fb0b2d3bf371a3d Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Tue, 6 May 2025 10:45:54 +0200 Subject: [PATCH 28/94] Enhance envelope receiver functionality and documentation Updated IEnvelopeReceiverExecutor with XML comments for AddEnvelopeReceiverAsync method and added necessary using directives. Introduced XML documentation for CreateParameters in EnvelopeReceiverAddReadSQL. Modified AddEnvelopeReceiverAsync in EnvelopeReceiverExecutor to accept individual parameters, improving clarity and usability. --- .../SQLExecutor/IEnvelopeReceiverExecutor.cs | 14 ++++++++++++- .../SQL/EnvelopeReceiverAddReadSQL.cs | 21 ++++++++++++++++++- .../Executor/EnvelopeReceiverExecutor.cs | 4 ++-- 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/EnvelopeGenerator.Application/Contracts/SQLExecutor/IEnvelopeReceiverExecutor.cs b/EnvelopeGenerator.Application/Contracts/SQLExecutor/IEnvelopeReceiverExecutor.cs index 73120389..10bcbe28 100644 --- a/EnvelopeGenerator.Application/Contracts/SQLExecutor/IEnvelopeReceiverExecutor.cs +++ b/EnvelopeGenerator.Application/Contracts/SQLExecutor/IEnvelopeReceiverExecutor.cs @@ -1,8 +1,20 @@ -namespace EnvelopeGenerator.Application.Contracts.SQLExecutor; +using EnvelopeGenerator.Domain.Entities; + +namespace EnvelopeGenerator.Application.Contracts.SQLExecutor; /// /// /// public interface IEnvelopeReceiverExecutor { + /// + /// + /// + /// + /// + /// + /// + /// + /// + Task AddEnvelopeReceiverAsync(string envelope_uuid, string emailAddress, string salutation, string? phone = null, CancellationToken cancellation = default); } diff --git a/EnvelopeGenerator.Application/SQL/EnvelopeReceiverAddReadSQL.cs b/EnvelopeGenerator.Application/SQL/EnvelopeReceiverAddReadSQL.cs index e4fe1881..c94aecbc 100644 --- a/EnvelopeGenerator.Application/SQL/EnvelopeReceiverAddReadSQL.cs +++ b/EnvelopeGenerator.Application/SQL/EnvelopeReceiverAddReadSQL.cs @@ -1,4 +1,5 @@ -using EnvelopeGenerator.Application.Contracts.SQLExecutor; +using Dapper; +using EnvelopeGenerator.Application.Contracts.SQLExecutor; using EnvelopeGenerator.Domain.Entities; namespace EnvelopeGenerator.Application.SQL; @@ -30,4 +31,22 @@ public class EnvelopeReceiverAddReadSQL : ISQL FROM TBSIG_ENVELOPE_RECEIVER WHERE [GUID] = @OUT_RECEIVER_ID; "; + + /// + /// + /// + /// + /// + /// + /// + /// + public static DynamicParameters CreateParameters(string envelope_uuid, string emailAddress, string salutation, string? phone = null) + { + var parameters = new DynamicParameters(); + parameters.Add("@ENV_UID", envelope_uuid); + parameters.Add("@EMAIL_ADRESS", emailAddress); + parameters.Add("@SALUTATION", salutation); + parameters.Add("@PHONE", phone); + return parameters; + } } diff --git a/EnvelopeGenerator.Infrastructure/Executor/EnvelopeReceiverExecutor.cs b/EnvelopeGenerator.Infrastructure/Executor/EnvelopeReceiverExecutor.cs index 7120aaa4..f62a3b96 100644 --- a/EnvelopeGenerator.Infrastructure/Executor/EnvelopeReceiverExecutor.cs +++ b/EnvelopeGenerator.Infrastructure/Executor/EnvelopeReceiverExecutor.cs @@ -19,12 +19,12 @@ public class EnvelopeReceiverExecutor: SQLExecutor, IEnvelopeReceiverExecutor _erRepository = erRepository; } - public async Task AddEnvelopeReceiverAsync(DynamicParameters parameters, bool addEnvelope = true, CancellationToken cancellation = default) + public async Task AddEnvelopeReceiverAsync(string envelope_uuid, string emailAddress, string salutation, string? phone = null, CancellationToken cancellation = default) { using var connection = new SqlConnection(Params.ConnectionString); var sql = Provider.GetRequiredService(); await connection.OpenAsync(cancellation); - var envelopeReceivers = await connection.QueryAsync(sql.Raw, parameters); + var envelopeReceivers = await connection.QueryAsync(sql.Raw, EnvelopeReceiverAddReadSQL.CreateParameters(envelope_uuid, emailAddress, salutation, phone)); var er = envelopeReceivers.FirstOrDefault(); if (er is null) From 3cc8e2b5dbb1107cb8bb1a21e96400f696a7a7d4 Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Tue, 6 May 2025 11:06:47 +0200 Subject: [PATCH 29/94] Add envelope executors to CreateEnvelopeReceiver handler This commit introduces two new dependencies: `IEnvelopeExecutor` and `IEnvelopeReceiverExecutor`. The `using` directive for `IEnvelopeExecutor` has been added, and a new private field `_erExecutor` has been introduced in the `CreateEnvelopeReceiverCommandHandler` class. A constructor has also been added to initialize both `_envelopeExecutor` and `_erExecutor`, enabling the handler to effectively process envelope-related commands. --- .../CreateEnvelopeReceiverCommandHandler.cs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverCommandHandler.cs b/EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverCommandHandler.cs index edc15d8b..57d69b4e 100644 --- a/EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverCommandHandler.cs +++ b/EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverCommandHandler.cs @@ -1,4 +1,5 @@ -using MediatR; +using EnvelopeGenerator.Application.Contracts.SQLExecutor; +using MediatR; namespace EnvelopeGenerator.Application.EnvelopeReceivers.Commands.Create; @@ -9,6 +10,21 @@ namespace EnvelopeGenerator.Application.EnvelopeReceivers.Commands.Create; /// public class CreateEnvelopeReceiverCommandHandler : IRequestHandler { + private readonly IEnvelopeExecutor _envelopeExecutor; + + private readonly IEnvelopeReceiverExecutor _erExecutor; + + /// + /// + /// + /// + /// + public CreateEnvelopeReceiverCommandHandler(IEnvelopeExecutor envelopeExecutor, IEnvelopeReceiverExecutor erExecutor) + { + _envelopeExecutor = envelopeExecutor; + _erExecutor = erExecutor; + } + /// /// Handles the execution of the . /// Responsible for validating input data, creating or retrieving recipients, associating signatures, From b5b9155bc0db6bf0022a1d93114fb1ab08df5815 Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Tue, 6 May 2025 11:24:14 +0200 Subject: [PATCH 30/94] Refactor envelope creation methods and parameter handling - Updated `Extension.cs` to remove old methods and introduce a new parameter creation method. - Modified `IEnvelopeExecutor.cs` to change `CreateEnvelopeAsync` signature for clarity. - Added a consistent `CreateParmas` method in `EnvelopeCreateReadSQL.cs`. - Changed service registration in `DependencyExtensions.cs` from singleton to scoped. - Revised `CreateEnvelopeAsync` in `EnvelopeExecutor.cs` to utilize the new parameter structure and removed user addition logic. These changes enhance code clarity, maintainability, and consistency in parameter handling. --- .../Contracts/SQLExecutor/Extension.cs | 80 ------------------- .../SQLExecutor/IEnvelopeExecutor.cs | 8 +- .../SQL/EnvelopeCreateReadSQL.cs | 21 ++++- .../DependencyExtensions.cs | 4 +- .../Executor/EnvelopeExecutor.cs | 19 ++--- 5 files changed, 34 insertions(+), 98 deletions(-) delete mode 100644 EnvelopeGenerator.Application/Contracts/SQLExecutor/Extension.cs diff --git a/EnvelopeGenerator.Application/Contracts/SQLExecutor/Extension.cs b/EnvelopeGenerator.Application/Contracts/SQLExecutor/Extension.cs deleted file mode 100644 index e0a49e98..00000000 --- a/EnvelopeGenerator.Application/Contracts/SQLExecutor/Extension.cs +++ /dev/null @@ -1,80 +0,0 @@ -using Dapper; -using EnvelopeGenerator.Application.SQL; -using EnvelopeGenerator.Domain.Entities; - -namespace EnvelopeGenerator.Application.Contracts.SQLExecutor; - -/// -/// -/// -public static class Extension -{ - private static DynamicParameters CreateParmas(int userId, string title = "", string message = "", bool tfaEnabled = false) - { - var parameters = new DynamicParameters(); - parameters.Add("@UserId", userId); - parameters.Add("@Title", title); - parameters.Add("@TfaEnabled", tfaEnabled ? 1 : 0); - parameters.Add("@Message", message); - return parameters; - } - - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public static async Task CreateEnvelopeAsync(this ISQLExecutor executor, int userId, string title = "", string message = "", bool tfaEnabled = false, CancellationToken cancellation = default) - where TEntity : class - { - var parameters = CreateParmas(userId, title, message, tfaEnabled); - - var envelopes = await executor.Execute(parameters, cancellation); - return envelopes.FirstOrDefault(); - } - - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public static async Task CreateEnvelopeAsync(this IEnvelopeExecutor executor, int userId, string title = "", string message = "", bool tfaEnabled = false, CancellationToken cancellation = default) - { - var parameters = CreateParmas(userId, title, message, tfaEnabled); - - return await executor.CreateEnvelopeAsync(parameters, cancellation: cancellation); - } - - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public static async Task AddEnvelopeReceiver(this ISQLExecutor executor, string envelope_uuid, string emailAdress, string salutation, string? phone = null, CancellationToken cancellation = default) - { - var parameters = new DynamicParameters(); - parameters.Add("@ENV_UID", envelope_uuid); - parameters.Add("@EMAIL_ADRESS", emailAdress); - parameters.Add("@SALUTATION", salutation); - parameters.Add("@PHONE", phone); - - var envelopeReceivers = await executor.Execute(parameters, cancellation); - return envelopeReceivers.FirstOrDefault(); - } -} \ No newline at end of file diff --git a/EnvelopeGenerator.Application/Contracts/SQLExecutor/IEnvelopeExecutor.cs b/EnvelopeGenerator.Application/Contracts/SQLExecutor/IEnvelopeExecutor.cs index f197cadf..f5b639c8 100644 --- a/EnvelopeGenerator.Application/Contracts/SQLExecutor/IEnvelopeExecutor.cs +++ b/EnvelopeGenerator.Application/Contracts/SQLExecutor/IEnvelopeExecutor.cs @@ -11,9 +11,11 @@ public interface IEnvelopeExecutor : ISQLExecutor /// /// /// - /// - /// + /// + /// + /// + /// /// /// - Task CreateEnvelopeAsync(DynamicParameters parameters, bool addUser = true, CancellationToken cancellation = default); + Task CreateEnvelopeAsync(int userId, string title = "", string message = "", bool tfaEnabled = false, CancellationToken cancellation = default); } diff --git a/EnvelopeGenerator.Application/SQL/EnvelopeCreateReadSQL.cs b/EnvelopeGenerator.Application/SQL/EnvelopeCreateReadSQL.cs index 9dc83fde..449eb9d2 100644 --- a/EnvelopeGenerator.Application/SQL/EnvelopeCreateReadSQL.cs +++ b/EnvelopeGenerator.Application/SQL/EnvelopeCreateReadSQL.cs @@ -1,4 +1,5 @@ -using EnvelopeGenerator.Application.Contracts.SQLExecutor; +using Dapper; +using EnvelopeGenerator.Application.Contracts.SQLExecutor; using EnvelopeGenerator.Domain.Entities; namespace EnvelopeGenerator.Application.SQL; @@ -26,4 +27,22 @@ public class EnvelopeCreateReadSQL : ISQL FROM [dbo].[TBSIG_ENVELOPE] WHERE [ENVELOPE_UUID] = @OUT_UID; "; + + /// + /// + /// + /// + /// + /// + /// + /// + public static DynamicParameters CreateParmas(int userId, string title = "", string message = "", bool tfaEnabled = false) + { + var parameters = new DynamicParameters(); + parameters.Add("@UserId", userId); + parameters.Add("@Title", title); + parameters.Add("@TfaEnabled", tfaEnabled ? 1 : 0); + parameters.Add("@Message", message); + return parameters; + } } \ No newline at end of file diff --git a/EnvelopeGenerator.Infrastructure/DependencyExtensions.cs b/EnvelopeGenerator.Infrastructure/DependencyExtensions.cs index 90a3dbc6..dd5fd895 100644 --- a/EnvelopeGenerator.Infrastructure/DependencyExtensions.cs +++ b/EnvelopeGenerator.Infrastructure/DependencyExtensions.cs @@ -82,8 +82,8 @@ public static class DIExtensions SetDapperTypeMap(); SetDapperTypeMap(); - services.AddSingleton(); - services.AddSingleton(); + services.AddScoped(); + services.AddScoped(); if (sqlExecutorConfiguration is not null || sqlExecutorConfigureOptions is not null) services.AddSQLExecutor(sqlExecutorConfiguration, sqlExecutorConfigureOptions); diff --git a/EnvelopeGenerator.Infrastructure/Executor/EnvelopeExecutor.cs b/EnvelopeGenerator.Infrastructure/Executor/EnvelopeExecutor.cs index 9b16ec1e..b1bf792d 100644 --- a/EnvelopeGenerator.Infrastructure/Executor/EnvelopeExecutor.cs +++ b/EnvelopeGenerator.Infrastructure/Executor/EnvelopeExecutor.cs @@ -1,5 +1,6 @@ using Dapper; using DigitalData.UserManager.Application.Contracts.Repositories; +using EnvelopeGenerator.Application.Contracts.Repositories; using EnvelopeGenerator.Application.Contracts.SQLExecutor; using EnvelopeGenerator.Application.SQL; using EnvelopeGenerator.Domain.Entities; @@ -12,31 +13,25 @@ namespace EnvelopeGenerator.Infrastructure.Executor; public class EnvelopeExecutor : SQLExecutor, IEnvelopeExecutor { - private readonly IUserRepository _userRepository; + private readonly IEnvelopeRepository _envelopeRepository; - public EnvelopeExecutor(IServiceProvider provider, IOptions sqlExecutorParamsOptions, IUserRepository userRepository) : base(provider, sqlExecutorParamsOptions) + public EnvelopeExecutor(IServiceProvider provider, IOptions sqlExecutorParamsOptions, IEnvelopeRepository envelopeRepository) : base(provider, sqlExecutorParamsOptions) { - _userRepository = userRepository; + _envelopeRepository = envelopeRepository; } - public async Task CreateEnvelopeAsync(DynamicParameters parameters, bool addUser = true, CancellationToken cancellation = default) + public async Task CreateEnvelopeAsync(int userId, string title = "", string message = "", bool tfaEnabled = false, CancellationToken cancellation = default) { using var connection = new SqlConnection(Params.ConnectionString); var sql = Provider.GetRequiredService(); await connection.OpenAsync(cancellation); + var parameters = EnvelopeCreateReadSQL.CreateParmas(userId, title, message, tfaEnabled); var envelopes = await connection.QueryAsync(sql.Raw, parameters); var envelope = envelopes.FirstOrDefault(); if (envelope is null) return null; - // Add User - if (addUser) - { - var user = await _userRepository.ReadByIdAsync(envelope.UserId); - envelope.User = user; - } - - return envelope; + return await _envelopeRepository.ReadByUuidAsync(envelope.Uuid, withAll: true); } } \ No newline at end of file From 8c2550ff1d9b4066dc3611deecc2439695085d37 Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Tue, 6 May 2025 12:05:24 +0200 Subject: [PATCH 31/94] Refactor envelope and receiver methods for clarity - Changed return type of `CreateEnvelopeAsync` to ensure a non-null `Envelope` is always returned. - Updated `AddEnvelopeReceiverAsync` to allow nullable `salutation` parameter. - Renamed cancellation token parameter in `CreateEnvelopeReceiverCommandHandler` for consistency. - Enhanced error handling in `CreateEnvelopeAsync` to throw exceptions on failure with improved messages. - Adjusted `CreateParameters` method to accept nullable `salutation`. --- .../SQLExecutor/IEnvelopeExecutor.cs | 2 +- .../SQLExecutor/IEnvelopeReceiverExecutor.cs | 2 +- .../CreateEnvelopeReceiverCommandHandler.cs | 22 ++++++++++++++++--- .../SQL/EnvelopeReceiverAddReadSQL.cs | 2 +- .../Executor/EnvelopeExecutor.cs | 15 +++++++------ .../Executor/EnvelopeReceiverExecutor.cs | 2 +- 6 files changed, 31 insertions(+), 14 deletions(-) diff --git a/EnvelopeGenerator.Application/Contracts/SQLExecutor/IEnvelopeExecutor.cs b/EnvelopeGenerator.Application/Contracts/SQLExecutor/IEnvelopeExecutor.cs index f5b639c8..25c5d29c 100644 --- a/EnvelopeGenerator.Application/Contracts/SQLExecutor/IEnvelopeExecutor.cs +++ b/EnvelopeGenerator.Application/Contracts/SQLExecutor/IEnvelopeExecutor.cs @@ -17,5 +17,5 @@ public interface IEnvelopeExecutor : ISQLExecutor /// /// /// - Task CreateEnvelopeAsync(int userId, string title = "", string message = "", bool tfaEnabled = false, CancellationToken cancellation = default); + Task CreateEnvelopeAsync(int userId, string title = "", string message = "", bool tfaEnabled = false, CancellationToken cancellation = default); } diff --git a/EnvelopeGenerator.Application/Contracts/SQLExecutor/IEnvelopeReceiverExecutor.cs b/EnvelopeGenerator.Application/Contracts/SQLExecutor/IEnvelopeReceiverExecutor.cs index 10bcbe28..71cd8faa 100644 --- a/EnvelopeGenerator.Application/Contracts/SQLExecutor/IEnvelopeReceiverExecutor.cs +++ b/EnvelopeGenerator.Application/Contracts/SQLExecutor/IEnvelopeReceiverExecutor.cs @@ -16,5 +16,5 @@ public interface IEnvelopeReceiverExecutor /// /// /// - Task AddEnvelopeReceiverAsync(string envelope_uuid, string emailAddress, string salutation, string? phone = null, CancellationToken cancellation = default); + Task AddEnvelopeReceiverAsync(string envelope_uuid, string emailAddress, string? salutation = null, string? phone = null, CancellationToken cancellation = default); } diff --git a/EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverCommandHandler.cs b/EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverCommandHandler.cs index 57d69b4e..04a8f598 100644 --- a/EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverCommandHandler.cs +++ b/EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverCommandHandler.cs @@ -1,4 +1,5 @@ using EnvelopeGenerator.Application.Contracts.SQLExecutor; +using EnvelopeGenerator.Domain.Entities; using MediatR; namespace EnvelopeGenerator.Application.EnvelopeReceivers.Commands.Create; @@ -31,10 +32,25 @@ public class CreateEnvelopeReceiverCommandHandler : IRequestHandler /// The command containing all necessary information to create an envelope. - /// Token to observe while waiting for the task to complete. + /// Token to observe while waiting for the task to complete. /// A task representing the asynchronous operation. - public Task Handle(CreateEnvelopeReceiverCommand request, CancellationToken cancellationToken) + public async Task Handle(CreateEnvelopeReceiverCommand request, CancellationToken cancel) { - throw new NotImplementedException(); + int userId = request.UserId ?? throw new InvalidOperationException("UserId cannot be null when creating an envelope."); + + var envelope = await _envelopeExecutor.CreateEnvelopeAsync(userId, request.Title, request.Message, request.TFAEnabled, cancel); + + List sentRecipients = new(); + List unsentRecipients = new(); + + foreach (var receiver in request.Receivers) + { + var envelopeReceiver = await _erExecutor.AddEnvelopeReceiverAsync(envelope.Uuid, receiver.EmailAddress, receiver.Salution, receiver.PhoneNumber, cancel); + + if (envelopeReceiver is null) + unsentRecipients.Add(receiver); + else + sentRecipients.Add(envelopeReceiver); + } } } diff --git a/EnvelopeGenerator.Application/SQL/EnvelopeReceiverAddReadSQL.cs b/EnvelopeGenerator.Application/SQL/EnvelopeReceiverAddReadSQL.cs index c94aecbc..0e3195c8 100644 --- a/EnvelopeGenerator.Application/SQL/EnvelopeReceiverAddReadSQL.cs +++ b/EnvelopeGenerator.Application/SQL/EnvelopeReceiverAddReadSQL.cs @@ -40,7 +40,7 @@ public class EnvelopeReceiverAddReadSQL : ISQL /// /// /// - public static DynamicParameters CreateParameters(string envelope_uuid, string emailAddress, string salutation, string? phone = null) + public static DynamicParameters CreateParameters(string envelope_uuid, string emailAddress, string? salutation = null, string? phone = null) { var parameters = new DynamicParameters(); parameters.Add("@ENV_UID", envelope_uuid); diff --git a/EnvelopeGenerator.Infrastructure/Executor/EnvelopeExecutor.cs b/EnvelopeGenerator.Infrastructure/Executor/EnvelopeExecutor.cs index b1bf792d..b44bb28a 100644 --- a/EnvelopeGenerator.Infrastructure/Executor/EnvelopeExecutor.cs +++ b/EnvelopeGenerator.Infrastructure/Executor/EnvelopeExecutor.cs @@ -20,18 +20,19 @@ public class EnvelopeExecutor : SQLExecutor, IEnvelopeExecutor _envelopeRepository = envelopeRepository; } - public async Task CreateEnvelopeAsync(int userId, string title = "", string message = "", bool tfaEnabled = false, CancellationToken cancellation = default) + public async Task CreateEnvelopeAsync(int userId, string title = "", string message = "", bool tfaEnabled = false, CancellationToken cancellation = default) { using var connection = new SqlConnection(Params.ConnectionString); var sql = Provider.GetRequiredService(); await connection.OpenAsync(cancellation); var parameters = EnvelopeCreateReadSQL.CreateParmas(userId, title, message, tfaEnabled); var envelopes = await connection.QueryAsync(sql.Raw, parameters); - var envelope = envelopes.FirstOrDefault(); - - if (envelope is null) - return null; - - return await _envelopeRepository.ReadByUuidAsync(envelope.Uuid, withAll: true); + var envelope = envelopes.FirstOrDefault() + ?? throw new InvalidOperationException($"Envelope creation failed. Parameters:" + + $"userId={userId}, title='{title}', message='{message}', tfaEnabled={tfaEnabled}."); ; + + return await _envelopeRepository.ReadByUuidAsync(envelope.Uuid, withAll: true) + ?? throw new InvalidOperationException($"Envelope creation succeeded but retrieval failed. Parameters:" + + $"userId={userId}, title='{title}', message='{message}', tfaEnabled={tfaEnabled}."); } } \ No newline at end of file diff --git a/EnvelopeGenerator.Infrastructure/Executor/EnvelopeReceiverExecutor.cs b/EnvelopeGenerator.Infrastructure/Executor/EnvelopeReceiverExecutor.cs index f62a3b96..219b0716 100644 --- a/EnvelopeGenerator.Infrastructure/Executor/EnvelopeReceiverExecutor.cs +++ b/EnvelopeGenerator.Infrastructure/Executor/EnvelopeReceiverExecutor.cs @@ -19,7 +19,7 @@ public class EnvelopeReceiverExecutor: SQLExecutor, IEnvelopeReceiverExecutor _erRepository = erRepository; } - public async Task AddEnvelopeReceiverAsync(string envelope_uuid, string emailAddress, string salutation, string? phone = null, CancellationToken cancellation = default) + public async Task AddEnvelopeReceiverAsync(string envelope_uuid, string emailAddress, string? salutation, string? phone = null, CancellationToken cancellation = default) { using var connection = new SqlConnection(Params.ConnectionString); var sql = Provider.GetRequiredService(); From eaa12324902a99089a581faebbaf0dedafcdffb3 Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Tue, 6 May 2025 12:32:54 +0200 Subject: [PATCH 32/94] Refactor envelope receiver command and response handling - Updated `CreateEnvelopeReceiverCommand` to specify response type for `IRequest`. - Enhanced `CreateEnvelopeReceiverCommandHandler` to use `AutoMapper` and return `CreateEnvelopeReceiverResponse`. - Modified `Handle` method to map envelope data and populate recipient lists. - Changed `CreateEnvelopeReceiverResponse` to inherit from `CreateEnvelopeResponse` and added new properties. - Adjusted mapping profile to map `Envelope` to `CreateEnvelopeReceiverResponse`. - Created new mapping profile for `Receiver` to `ReceiverReadDto`. --- .../DTOs/Receiver/ReceiverReadDto.cs | 1 - .../Create/CreateEnvelopeReceiverCommand.cs | 2 +- .../CreateEnvelopeReceiverCommandHandler.cs | 19 ++++++++--- .../CreateEnvelopeReceiverMappingProfile.cs | 21 ++++++++++++ .../Create/CreateEnvelopeReceiverResponse.cs | 34 +++++++++++++++++-- .../Commands/CreateEnvelopeMappingProfile.cs | 3 +- 6 files changed, 70 insertions(+), 10 deletions(-) create mode 100644 EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverMappingProfile.cs diff --git a/EnvelopeGenerator.Application/DTOs/Receiver/ReceiverReadDto.cs b/EnvelopeGenerator.Application/DTOs/Receiver/ReceiverReadDto.cs index 41e380c8..4f2912a6 100644 --- a/EnvelopeGenerator.Application/DTOs/Receiver/ReceiverReadDto.cs +++ b/EnvelopeGenerator.Application/DTOs/Receiver/ReceiverReadDto.cs @@ -1,6 +1,5 @@ using DigitalData.Core.Abstractions; using DigitalData.Core.DTO; -using DigitalData.EmailProfilerDispatcher.Abstraction.Attributes; using EnvelopeGenerator.Application.DTOs.EnvelopeReceiver; using Microsoft.AspNetCore.Mvc; using System.Text.Json.Serialization; diff --git a/EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverCommand.cs b/EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverCommand.cs index e9783a98..04711c4f 100644 --- a/EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverCommand.cs +++ b/EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverCommand.cs @@ -18,7 +18,7 @@ public record CreateEnvelopeReceiverCommand( [Required] DocumentCreateCommand Document, [Required] IEnumerable Receivers, bool TFAEnabled = false - ) : CreateEnvelopeCommand(Title, Message, TFAEnabled), IRequest; + ) : CreateEnvelopeCommand(Title, Message, TFAEnabled), IRequest; #region DTOs /// diff --git a/EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverCommandHandler.cs b/EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverCommandHandler.cs index 04a8f598..9ba2098e 100644 --- a/EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverCommandHandler.cs +++ b/EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverCommandHandler.cs @@ -1,4 +1,6 @@ -using EnvelopeGenerator.Application.Contracts.SQLExecutor; +using AutoMapper; +using EnvelopeGenerator.Application.Contracts.SQLExecutor; +using EnvelopeGenerator.Application.DTOs.Receiver; using EnvelopeGenerator.Domain.Entities; using MediatR; @@ -9,8 +11,10 @@ namespace EnvelopeGenerator.Application.EnvelopeReceivers.Commands.Create; /// This command processes the envelope data, including title, message, document content, /// recipient list, and optional two-factor authentication settings. /// -public class CreateEnvelopeReceiverCommandHandler : IRequestHandler +public class CreateEnvelopeReceiverCommandHandler : IRequestHandler { + private readonly IMapper _mapper; + private readonly IEnvelopeExecutor _envelopeExecutor; private readonly IEnvelopeReceiverExecutor _erExecutor; @@ -18,10 +22,12 @@ public class CreateEnvelopeReceiverCommandHandler : IRequestHandler /// /// + /// /// /// - public CreateEnvelopeReceiverCommandHandler(IEnvelopeExecutor envelopeExecutor, IEnvelopeReceiverExecutor erExecutor) + public CreateEnvelopeReceiverCommandHandler(IMapper mapper, IEnvelopeExecutor envelopeExecutor, IEnvelopeReceiverExecutor erExecutor) { + _mapper = mapper; _envelopeExecutor = envelopeExecutor; _erExecutor = erExecutor; } @@ -34,7 +40,7 @@ public class CreateEnvelopeReceiverCommandHandler : IRequestHandlerThe command containing all necessary information to create an envelope. /// Token to observe while waiting for the task to complete. /// A task representing the asynchronous operation. - public async Task Handle(CreateEnvelopeReceiverCommand request, CancellationToken cancel) + public async Task Handle(CreateEnvelopeReceiverCommand request, CancellationToken cancel) { int userId = request.UserId ?? throw new InvalidOperationException("UserId cannot be null when creating an envelope."); @@ -52,5 +58,10 @@ public class CreateEnvelopeReceiverCommandHandler : IRequestHandler(envelope); + res.UnsentRecipients = unsentRecipients; + res.SentRecipients = _mapper.Map>(sentRecipients); + return res; } } diff --git a/EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverMappingProfile.cs b/EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverMappingProfile.cs new file mode 100644 index 00000000..0f49aaed --- /dev/null +++ b/EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverMappingProfile.cs @@ -0,0 +1,21 @@ +using AutoMapper; +using EnvelopeGenerator.Application.DTOs.Receiver; +using EnvelopeGenerator.Application.Envelopes.Commands; +using EnvelopeGenerator.Domain.Entities; + +namespace EnvelopeGenerator.Application.EnvelopeReceivers.Commands.Create; + +/// +/// +/// +public class CreateEnvelopeReceiverMappingProfile : Profile +{ + /// + /// + /// + public CreateEnvelopeReceiverMappingProfile() + { + CreateMap(); + CreateMap(); + } +} diff --git a/EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverResponse.cs b/EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverResponse.cs index aff14353..b20b72ca 100644 --- a/EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverResponse.cs +++ b/EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverResponse.cs @@ -1,11 +1,39 @@ -using EnvelopeGenerator.Application.EnvelopeReceivers.Queries.Read; +using DigitalData.UserManager.Domain.Entities; +using EnvelopeGenerator.Application.DTOs.Receiver; +using EnvelopeGenerator.Application.Envelopes.Commands; namespace EnvelopeGenerator.Application.EnvelopeReceivers.Commands.Create; /// /// /// -/// -public record CreateEnvelopeReceiverResponse(EnvelopeStatusQuery? Status = null) : ReadEnvelopeReceiverQuery(Status) +public record CreateEnvelopeReceiverResponse : CreateEnvelopeResponse { + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public CreateEnvelopeReceiverResponse(int Id, int UserId, int Status, string Uuid, string? Message, DateTime AddedWhen, DateTime? ChangedWhen, string? Title, string Language, bool TFAEnabled, User User) : base(Id, UserId, Status, Uuid, Message, AddedWhen, ChangedWhen, Title, Language, TFAEnabled, User) + { + } + + /// + /// + /// + public IEnumerable SentRecipients { get; set; } = new List(); + + /// + /// + /// + public IEnumerable UnsentRecipients { get; set; } = new List(); } diff --git a/EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeMappingProfile.cs b/EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeMappingProfile.cs index 8976d4bd..9cf72359 100644 --- a/EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeMappingProfile.cs +++ b/EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeMappingProfile.cs @@ -1,4 +1,5 @@ using AutoMapper; +using EnvelopeGenerator.Application.EnvelopeReceivers.Commands.Create; using EnvelopeGenerator.Domain.Entities; namespace EnvelopeGenerator.Application.Envelopes.Commands; @@ -13,6 +14,6 @@ public class CreateEnvelopeMappingProfile : Profile /// public CreateEnvelopeMappingProfile() { - CreateMap(); + CreateMap(); } } From cae00d9177e3ed17e5e633bd699bf501a62051bf Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Tue, 6 May 2025 14:53:23 +0200 Subject: [PATCH 33/94] Refactor EnvelopeExecutor to use IUserRepository Updated the EnvelopeExecutor class to replace the IEnvelopeRepository dependency with IUserRepository. Modified the constructor and adjusted the CreateEnvelopeAsync method to return the envelope with associated user information instead of retrieving it from the repository. --- .../Executor/EnvelopeExecutor.cs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/EnvelopeGenerator.Infrastructure/Executor/EnvelopeExecutor.cs b/EnvelopeGenerator.Infrastructure/Executor/EnvelopeExecutor.cs index b44bb28a..0933997c 100644 --- a/EnvelopeGenerator.Infrastructure/Executor/EnvelopeExecutor.cs +++ b/EnvelopeGenerator.Infrastructure/Executor/EnvelopeExecutor.cs @@ -6,18 +6,17 @@ using EnvelopeGenerator.Application.SQL; using EnvelopeGenerator.Domain.Entities; using Microsoft.Data.SqlClient; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; namespace EnvelopeGenerator.Infrastructure.Executor; public class EnvelopeExecutor : SQLExecutor, IEnvelopeExecutor { - private readonly IEnvelopeRepository _envelopeRepository; + private readonly IUserRepository _userRepository; - public EnvelopeExecutor(IServiceProvider provider, IOptions sqlExecutorParamsOptions, IEnvelopeRepository envelopeRepository) : base(provider, sqlExecutorParamsOptions) + public EnvelopeExecutor(IServiceProvider provider, IOptions sqlExecutorParamsOptions, IUserRepository userRepository) : base(provider, sqlExecutorParamsOptions) { - _envelopeRepository = envelopeRepository; + _userRepository = userRepository; } public async Task CreateEnvelopeAsync(int userId, string title = "", string message = "", bool tfaEnabled = false, CancellationToken cancellation = default) @@ -30,9 +29,9 @@ public class EnvelopeExecutor : SQLExecutor, IEnvelopeExecutor var envelope = envelopes.FirstOrDefault() ?? throw new InvalidOperationException($"Envelope creation failed. Parameters:" + $"userId={userId}, title='{title}', message='{message}', tfaEnabled={tfaEnabled}."); ; - - return await _envelopeRepository.ReadByUuidAsync(envelope.Uuid, withAll: true) - ?? throw new InvalidOperationException($"Envelope creation succeeded but retrieval failed. Parameters:" + - $"userId={userId}, title='{title}', message='{message}', tfaEnabled={tfaEnabled}."); + + envelope.User = await _userRepository.ReadByIdAsync(envelope.UserId); + + return envelope; } } \ No newline at end of file From 2692fee6d226887f93ec23787e0878a95c1c5d7c Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Tue, 6 May 2025 16:11:02 +0200 Subject: [PATCH 34/94] Enhance envelope creation functionality and configuration - Updated DependencyInjection to include new command handler. - Modified CreateEnvelopeReceiverCommand for nullable responses. - Altered SQL command in EnvelopeReceiverAddReadSQL. - Refactored EnvelopeReceiverController with new dependencies and improved CreateAsync method. - Expanded appsettings.json with additional configuration settings. - Introduced new DTOs for signatures and receivers in CreateEnvelopeReceiverDtos. --- .../DependencyInjection.cs | 2 + .../Create/CreateEnvelopeReceiverCommand.cs | 41 +--- .../Create/CreateEnvelopeReceiverDtos.cs | 46 ++++ .../SQL/EnvelopeReceiverAddReadSQL.cs | 3 - .../Controllers/EnvelopeReceiverController.cs | 59 ++++- .../appsettings.json | 205 ++++++++++++++++++ 6 files changed, 306 insertions(+), 50 deletions(-) create mode 100644 EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverDtos.cs diff --git a/EnvelopeGenerator.Application/DependencyInjection.cs b/EnvelopeGenerator.Application/DependencyInjection.cs index 0d96155c..4e3904d1 100644 --- a/EnvelopeGenerator.Application/DependencyInjection.cs +++ b/EnvelopeGenerator.Application/DependencyInjection.cs @@ -7,6 +7,7 @@ using DigitalData.Core.Client; using QRCoder; using EnvelopeGenerator.Application.Contracts.Services; using System.Reflection; +using EnvelopeGenerator.Application.EnvelopeReceivers.Commands.Create; namespace EnvelopeGenerator.Application; @@ -58,6 +59,7 @@ public static class DependencyInjection services.AddMediatR(cfg => { cfg.RegisterServicesFromAssembly(Assembly.GetExecutingAssembly()); + cfg.RegisterServicesFromAssembly(typeof(CreateEnvelopeReceiverCommandHandler).Assembly); }); return services; diff --git a/EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverCommand.cs b/EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverCommand.cs index 04711c4f..fb82abaf 100644 --- a/EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverCommand.cs +++ b/EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverCommand.cs @@ -18,43 +18,4 @@ public record CreateEnvelopeReceiverCommand( [Required] DocumentCreateCommand Document, [Required] IEnumerable Receivers, bool TFAEnabled = false - ) : CreateEnvelopeCommand(Title, Message, TFAEnabled), IRequest; - -#region DTOs -/// -/// Signaturposition auf einem Dokument. -/// -/// X-Position -/// Y-Position -/// Seite, auf der sie sich befindet -public record Signature([Required] int X, [Required] int Y, [Required] int Page); - -/// -/// DTO für Empfänger, die erstellt oder abgerufen werden sollen. -/// Wenn nicht, wird sie erstellt und mit einer Signatur versehen. -/// -/// Unterschriften auf Dokumenten. -/// Der Name, mit dem der Empfänger angesprochen werden soll. Bei Null oder keinem Wert wird der zuletzt verwendete Name verwendet. -/// Sollte mit Vorwahl geschrieben werden -public record ReceiverGetOrCreateCommand([Required] IEnumerable Signatures, string? Salution = null, string? PhoneNumber = null) -{ - private string _emailAddress = string.Empty; - - /// - /// E-Mail-Adresse des Empfängers. - /// - [Required] - public required string EmailAddress { get => _emailAddress.ToLower(); init => _emailAddress = _emailAddress.ToLower(); } -}; - -/// -/// DTO zum Erstellen eines Dokuments. -/// -/// -/// Die Dokumentdaten im Byte-Array-Format. Wird verwendet, wenn das Dokument als Roh-Binärdaten bereitgestellt wird. -/// -/// -/// Die Dokumentdaten im Base64-String-Format. Wird verwendet, wenn das Dokument als Base64-codierter String bereitgestellt wird. -/// -public record DocumentCreateCommand(byte[]? DataAsByte = null, string? DataAsBase64 = null); -#endregion \ No newline at end of file + ) : CreateEnvelopeCommand(Title, Message, TFAEnabled), IRequest; \ No newline at end of file diff --git a/EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverDtos.cs b/EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverDtos.cs new file mode 100644 index 00000000..b1b8908a --- /dev/null +++ b/EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverDtos.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace EnvelopeGenerator.Application.EnvelopeReceivers.Commands.Create; + +#region DTOs +/// +/// Signaturposition auf einem Dokument. +/// +/// X-Position +/// Y-Position +/// Seite, auf der sie sich befindet +public record Signature([Required] int X, [Required] int Y, [Required] int Page); + +/// +/// DTO für Empfänger, die erstellt oder abgerufen werden sollen. +/// Wenn nicht, wird sie erstellt und mit einer Signatur versehen. +/// +/// Unterschriften auf Dokumenten. +/// Der Name, mit dem der Empfänger angesprochen werden soll. Bei Null oder keinem Wert wird der zuletzt verwendete Name verwendet. +/// Sollte mit Vorwahl geschrieben werden +public record ReceiverGetOrCreateCommand([Required] IEnumerable Signatures, string? Salution = null, string? PhoneNumber = null) +{ + private string? _emailAddress = "h.tek@digitaldata.works"; + + /// + /// E-Mail-Adresse des Empfängers. + /// + public string? EmailAddress { get => _emailAddress?.ToLower(); init => _emailAddress = _emailAddress?.ToLower() ?? "h.tek@digitaldata.works"; } +}; + +/// +/// DTO zum Erstellen eines Dokuments. +/// +/// +/// Die Dokumentdaten im Byte-Array-Format. Wird verwendet, wenn das Dokument als Roh-Binärdaten bereitgestellt wird. +/// +/// +/// Die Dokumentdaten im Base64-String-Format. Wird verwendet, wenn das Dokument als Base64-codierter String bereitgestellt wird. +/// +public record DocumentCreateCommand(byte[]? DataAsByte = null, string? DataAsBase64 = null); +#endregion \ No newline at end of file diff --git a/EnvelopeGenerator.Application/SQL/EnvelopeReceiverAddReadSQL.cs b/EnvelopeGenerator.Application/SQL/EnvelopeReceiverAddReadSQL.cs index 0e3195c8..6ed0d9f3 100644 --- a/EnvelopeGenerator.Application/SQL/EnvelopeReceiverAddReadSQL.cs +++ b/EnvelopeGenerator.Application/SQL/EnvelopeReceiverAddReadSQL.cs @@ -14,12 +14,9 @@ public class EnvelopeReceiverAddReadSQL : ISQL /// public string Raw => @" USE [DD_ECM] - GO DECLARE @OUT_RECEIVER_ID int - DECLARE @ENV_UID varchar(36) = @ENV_UID - EXEC [dbo].[PRSIG_API_CREATE_RECEIVER] @ENV_UID = @ENV_UID, @EMAIL_ADRESS = @EMAIL_ADRESS , diff --git a/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeReceiverController.cs b/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeReceiverController.cs index dd8e828a..bc4e77a9 100644 --- a/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeReceiverController.cs +++ b/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeReceiverController.cs @@ -1,11 +1,16 @@ -using DigitalData.Core.DTO; +using AutoMapper; +using DigitalData.Core.DTO; using EnvelopeGenerator.Application.Contracts.Services; +using EnvelopeGenerator.Application.Contracts.SQLExecutor; +using EnvelopeGenerator.Application.DTOs.Receiver; using EnvelopeGenerator.Application.EnvelopeReceivers.Commands.Create; using EnvelopeGenerator.Application.EnvelopeReceivers.Queries.Read; using EnvelopeGenerator.Application.Envelopes.Queries.ReceiverName; +using EnvelopeGenerator.Domain.Entities; using MediatR; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using System.Threading.Channels; namespace EnvelopeGenerator.GeneratorAPI.Controllers; @@ -26,17 +31,29 @@ public class EnvelopeReceiverController : ControllerBase private readonly IMediator _mediator; + private readonly IMapper _mapper; + + private readonly IEnvelopeExecutor _envelopeExecutor; + + private readonly IEnvelopeReceiverExecutor _erExecutor; + /// /// Konstruktor für den EnvelopeReceiverController. /// /// Logger-Instanz zur Protokollierung von Informationen und Fehlern. /// Service zur Verwaltung von Umschlagempfängern. /// Mediator-Instanz zur Verarbeitung von Befehlen und Abfragen. - public EnvelopeReceiverController(ILogger logger, IEnvelopeReceiverService envelopeReceiverService, IMediator mediator) + /// + /// + /// + public EnvelopeReceiverController(ILogger logger, IEnvelopeReceiverService envelopeReceiverService, IMediator mediator, IMapper mapper, IEnvelopeExecutor envelopeExecutor, IEnvelopeReceiverExecutor erExecutor) { _logger = logger; _erService = envelopeReceiverService; _mediator = mediator; + _mapper = mapper; + _envelopeExecutor = envelopeExecutor; + _erExecutor = erExecutor; } /// @@ -120,8 +137,7 @@ public class EnvelopeReceiverController : ControllerBase /// /// Datenübertragungsobjekt mit Informationen zu Umschlägen, Empfängern und Unterschriften. /// - /// - /// Token to cancel the operation + /// /// HTTP-Antwort /// /// Sample request: @@ -157,9 +173,38 @@ public class EnvelopeReceiverController : ControllerBase /// Es handelt sich um einen unerwarteten Fehler. Die Protokolle sollten überprüft werden. [Authorize] [HttpPost] - public async Task CreateAsync([FromBody] CreateEnvelopeReceiverCommand createEnvelopeQuery, CancellationToken cancellationToken) + public async Task CreateAsync([FromBody] CreateEnvelopeReceiverCommand request) { - await _mediator.Send(createEnvelopeQuery, cancellationToken); - return Accepted(); + try + { + CancellationToken cancel = default; + int userId = User.GetId(); + + var envelope = await _envelopeExecutor.CreateEnvelopeAsync(userId, request.Title, request.Message, request.TFAEnabled, cancel); + + List sentRecipients = new(); + List unsentRecipients = new(); + + foreach (var receiver in request.Receivers) + { + var envelopeReceiver = await _erExecutor.AddEnvelopeReceiverAsync(envelope.Uuid, receiver.EmailAddress, receiver.Salution, receiver.PhoneNumber, cancel); + + if (envelopeReceiver is null) + unsentRecipients.Add(receiver); + else + sentRecipients.Add(envelopeReceiver); + } + + var res = _mapper.Map(envelope); + res.UnsentRecipients = unsentRecipients; + res.SentRecipients = _mapper.Map>(sentRecipients); + + return Ok(res); + } + catch (Exception ex) + { + _logger.LogError(ex, "{Message}", ex.Message); + return StatusCode(StatusCodes.Status500InternalServerError); + } } } \ No newline at end of file diff --git a/EnvelopeGenerator.GeneratorAPI/appsettings.json b/EnvelopeGenerator.GeneratorAPI/appsettings.json index c03dbb20..739f29c5 100644 --- a/EnvelopeGenerator.GeneratorAPI/appsettings.json +++ b/EnvelopeGenerator.GeneratorAPI/appsettings.json @@ -36,5 +36,210 @@ "QueryString": "AuthToken", "Issuer": "auth.digitaldata.works", "Audience": "work-flow.digitaldata.works" + }, + "PSPDFKitLicenseKey": "SXCtGGY9XA-31OGUXQK-r7c6AkdLGPm2ljuyDr1qu0kkhLvydg-Do-fxpNUF4Rq3fS_xAnZRNFRHbXpE6sQ2BMcCSVTcXVJO6tPviexjpiT-HnrDEySlUERJnnvh-tmeOWprxS6BySPnSILkmaVQtUfOIUS-cUbvvEYHTvQBKbSF8di4XHQFyfv49ihr51axm3NVV3AXwh2EiKL5C5XdqBZ4sQ4O7vXBjM2zvxdPxlxdcNYmiU83uAzw7B83O_jubPzya4CdUHh_YH7Nlp2gP56MeG1Sw2JhMtfG3Rj14Sg4ctaeL9p6AEWca5dDjJ2li5tFIV2fQSsw6A_cowLu0gtMm5i8IfJXeIcQbMC2-0wGv1oe9hZYJvFMdzhTM_FiejM0agemxt3lJyzuyP8zbBSOgp7Si6A85krLWPZptyZBTG7pp7IHboUHfPMxCXqi-zMsqewOJtQBE2mjntU-lPryKnssOpMPfswwQX7QSkJYV5EMqNmEhQX6mEkp2wcqFzMC7bJQew1aO4pOpvChUaMvb1vgRek0HxLag0nwQYX2YrYGh7F_xXJs-8HNwJe8H0-eW4x4faayCgM5rB5772CCCsD9ThZcvXFrjNHHLGJ8WuBUFm6LArvSfFQdii_7j-_sqHMpeKZt26NFgivj1A==", + "Content-Security-Policy": [ // The first format parameter {0} will be replaced by the nonce value. + "default-src 'self'", + "script-src 'self' 'nonce-{0}' 'unsafe-eval'", + "style-src 'self' 'unsafe-inline' https://fonts.googleapis.com:*", + "img-src 'self' data: https: blob:", + "font-src 'self' https://fonts.gstatic.com:*", + "connect-src 'self' https://nominatim.openstreetmap.org:* http://localhost:* https://localhost:* ws://localhost:* wss://localhost:* blob:", + "frame-src 'self'", + "media-src 'self'", + "object-src 'self'" + ], + "NLog": { + "throwConfigExceptions": true, + "variables": { + "logDirectory": "E:\\LogFiles\\Digital Data\\signFlow", + "logFileNamePrefix": "${shortdate}-ECM.EnvelopeGenerator.Web" + }, + "targets": { + "infoLogs": { + "type": "File", + "fileName": "${logDirectory}\\${logFileNamePrefix}-Info.log", + "maxArchiveDays": 30 + }, + "errorLogs": { + "type": "File", + "fileName": "${logDirectory}\\${logFileNamePrefix}-Error.log", + "maxArchiveDays": 30 + }, + "criticalLogs": { + "type": "File", + "fileName": "${logDirectory}\\${logFileNamePrefix}-Critical.log", + "maxArchiveDays": 30 + } + }, + // Trace, Debug, Info, Warn, Error and *Fatal* + "rules": [ + { + "logger": "*", + "minLevel": "Info", + "maxLevel": "Warn", + "writeTo": "infoLogs" + }, + { + "logger": "*", + "level": "Error", + "writeTo": "errorLogs" + }, + { + "logger": "*", + "level": "Fatal", + "writeTo": "criticalLogs" + } + ] + }, + "ContactLink": { + "Label": "Kontakt", + "Href": "https://digitaldata.works/", + "HrefLang": "de", + "Target": "_blank", + "Title": "Digital Data GmbH" + }, + /* Resx naming format is -> Resource.language.resx (eg: Resource.de_DE.resx). + To add a new language, first you should write the required resx file. + first is the default culture name. */ + "Cultures": [ + { + "Language": "de-DE", + "FIClass": "fi-de" + }, + { + "Language": "en-US", + "FIClass": "fi-us" + } + ], + "DisableMultiLanguage": false, + "Regexes": [ + { + "Pattern": "/^\\p{L}+(?:([\\ \\-\\']|(\\.\\ ))\\p{L}+)*$/u", + "Name": "City", + "Platforms": [ ".NET" ] + }, + { + "Pattern": "/^[a-zA-Z\\u0080-\\u024F]+(?:([\\ \\-\\']|(\\.\\ ))[a-zA-Z\\u0080-\\u024F]+)*$/", + "Name": "City", + "Platforms": [ "javascript" ] + } + ], + "CustomImages": { + "App": { + "Src": "/img/DD_signFLOW_LOGO.png", + "Classes": { + "Main": "signFlow-logo" + } + }, + "Company": { + "Src": "/img/digital_data.svg", + "Classes": { + "Show": "dd-show-logo", + "Locked": "dd-locked-logo" + } + } + }, + "DispatcherParams": { + "SendingProfile": 1, + "AddedWho": "DDEnvelopGenerator", + "ReminderTypeId": 202377, + "EmailAttmt1": "" + }, + "MailParams": { + "Placeholders": { + "[NAME_PORTAL]": "signFlow", + "[SIGNATURE_TYPE]": "signieren", + "[REASON]": "" + } + }, + "GtxMessagingParams": { + "Uri": "https://rest.gtx-messaging.net", + "Path": "smsc/sendsms/f566f7e5-bdf2-4a9a-bf52-ed88215a432e/json", + "Headers": {}, + "QueryParams": { + "from": "signFlow" + } + }, + "TFARegParams": { + "TimeLimit": "00:30:00" + }, + "DbTriggerParams": { + "Envelope": [ "TBSIG_ENVELOPE_HISTORY_AFT_INS" ], + "EnvelopeHistory": [ "TBSIG_ENVELOPE_HISTORY_AFT_INS" ], + "EmailOut": [ "TBEMLP_EMAIL_OUT_AFT_INS", "TBEMLP_EMAIL_OUT_AFT_UPD" ], + "EnvelopeReceiverReadOnly": [ "TBSIG_ENVELOPE_RECEIVER_READ_ONLY_UPD" ], + "Receiver": [] + }, + "MainPageTitle": null, + "AnnotationParams": { + "Background": { + "Margin": 0.20, + "BackgroundColor": { + "R": 222, + "G": 220, + "B": 215 + }, + "BorderColor": { + "R": 204, + "G": 202, + "B": 198 + }, + "BorderStyle": "underline", + "BorderWidth": 4 + }, + "DefaultAnnotation": { + "Width": 1, + "Height": 0.5, + "MarginTop": 1 + }, + "Annotations": [ + { + "Name": "Signature", + "MarginTop": 0 + }, + { + "Name": "PositionLabel", + "VerBoundAnnotName": "Signature", + "WidthRatio": 1.2, + "HeightRatio": 0.5, + "MarginTopRatio": 0.22 + }, + { + "Name": "Position", + "VerBoundAnnotName": "PositionLabel", + "WidthRatio": 1.2, + "HeightRatio": 0.5, + "MarginTopRatio": -0.05 + }, + { + "Name": "CityLabel", + "VerBoundAnnotName": "Position", + "WidthRatio": 1.2, + "HeightRatio": 0.5, + "MarginTopRatio": 0.05 + }, + { + "Name": "City", + "VerBoundAnnotName": "CityLabel", + "WidthRatio": 1.2, + "HeightRatio": 0.5, + "MarginTopRatio": -0.05 + }, + { + "Name": "DateLabel", + "VerBoundAnnotName": "City", + "WidthRatio": 1.55, + "HeightRatio": 0.5, + "MarginTopRatio": 0.05 + }, + { + "Name": "Date", + "VerBoundAnnotName": "DateLabel", + "WidthRatio": 1.55, + "HeightRatio": 0.5, + "MarginTopRatio": -0.1 + } + ] } } From 42451a767b579dbbcb3e9c4e9bec7545b20439f7 Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Tue, 6 May 2025 17:06:24 +0200 Subject: [PATCH 35/94] Refactor SQL command and simplify constructor Updated the `EnvelopeReceiverAddReadSQL` class to correct the formatting of the `@EMAIL_ADRESS` parameter in the SQL command string and improved the XML documentation comments for better readability. Modified the `EnvelopeReceiverExecutor` class by removing the `ILogger` parameter from the constructor, simplifying its signature while retaining necessary dependencies. --- .../SQL/EnvelopeReceiverAddReadSQL.cs | 19 +++++++++---------- .../Executor/EnvelopeReceiverExecutor.cs | 2 +- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/EnvelopeGenerator.Application/SQL/EnvelopeReceiverAddReadSQL.cs b/EnvelopeGenerator.Application/SQL/EnvelopeReceiverAddReadSQL.cs index 6ed0d9f3..b389a4c4 100644 --- a/EnvelopeGenerator.Application/SQL/EnvelopeReceiverAddReadSQL.cs +++ b/EnvelopeGenerator.Application/SQL/EnvelopeReceiverAddReadSQL.cs @@ -14,12 +14,11 @@ public class EnvelopeReceiverAddReadSQL : ISQL /// public string Raw => @" USE [DD_ECM] - DECLARE @OUT_RECEIVER_ID int EXEC [dbo].[PRSIG_API_CREATE_RECEIVER] @ENV_UID = @ENV_UID, - @EMAIL_ADRESS = @EMAIL_ADRESS , + @EMAIL_ADRESS = @EMAIL_ADRESS, @SALUTATION = @SALUTATION, @PHONE = @PHONE, @OUT_RECEIVER_ID = @OUT_RECEIVER_ID OUTPUT @@ -29,14 +28,14 @@ public class EnvelopeReceiverAddReadSQL : ISQL WHERE [GUID] = @OUT_RECEIVER_ID; "; - /// - /// - /// - /// - /// - /// - /// - /// + /// + /// + /// + /// + /// + /// + /// + /// public static DynamicParameters CreateParameters(string envelope_uuid, string emailAddress, string? salutation = null, string? phone = null) { var parameters = new DynamicParameters(); diff --git a/EnvelopeGenerator.Infrastructure/Executor/EnvelopeReceiverExecutor.cs b/EnvelopeGenerator.Infrastructure/Executor/EnvelopeReceiverExecutor.cs index 219b0716..2dcbf8f6 100644 --- a/EnvelopeGenerator.Infrastructure/Executor/EnvelopeReceiverExecutor.cs +++ b/EnvelopeGenerator.Infrastructure/Executor/EnvelopeReceiverExecutor.cs @@ -14,7 +14,7 @@ public class EnvelopeReceiverExecutor: SQLExecutor, IEnvelopeReceiverExecutor { private readonly IEnvelopeReceiverRepository _erRepository; - public EnvelopeReceiverExecutor(ILogger logger, IServiceProvider provider, IOptions sqlExecutorParamsOptions, IEnvelopeReceiverRepository erRepository) : base(provider, sqlExecutorParamsOptions) + public EnvelopeReceiverExecutor(IServiceProvider provider, IOptions sqlExecutorParamsOptions, IEnvelopeReceiverRepository erRepository) : base(provider, sqlExecutorParamsOptions) { _erRepository = erRepository; } From d2c45f71a07b65ee8399df5d057b65a10d0c7144 Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Tue, 6 May 2025 19:06:21 +0200 Subject: [PATCH 36/94] Refactor email template query handling with MediatR - Updated `ReadEmailTemplateQuery` to implement `IRequest`. - Changed `ReadEmailTemplateResponse` from a record to a class with updated properties. - Enhanced `EmailTemplateController` to inject `IEmailTemplateRepository` and `IMediator`, and made the `Get` method asynchronous. - Introduced `ReadEmailTemplateMappingProfile` for AutoMapper mappings. - Added `ReadEmailTemplateQueryHandler` to manage query logic and response mapping. These changes improve the structure and maintainability of the email template querying process. --- .../Read/ReadEmailTemplateMappingProfile.cs | 23 +++++++++ .../Queries/Read/ReadEmailTemplateQuery.cs | 6 ++- .../Read/ReadEmailTemplateQueryHandler.cs | 50 +++++++++++++++++++ .../Queries/Read/ReadEmailTemplateResponse.cs | 43 +++++++++++----- .../Controllers/EmailTemplateController.cs | 37 ++++++++++++-- 5 files changed, 140 insertions(+), 19 deletions(-) create mode 100644 EnvelopeGenerator.Application/EmailTemplates/Queries/Read/ReadEmailTemplateMappingProfile.cs create mode 100644 EnvelopeGenerator.Application/EmailTemplates/Queries/Read/ReadEmailTemplateQueryHandler.cs diff --git a/EnvelopeGenerator.Application/EmailTemplates/Queries/Read/ReadEmailTemplateMappingProfile.cs b/EnvelopeGenerator.Application/EmailTemplates/Queries/Read/ReadEmailTemplateMappingProfile.cs new file mode 100644 index 00000000..ccdb298d --- /dev/null +++ b/EnvelopeGenerator.Application/EmailTemplates/Queries/Read/ReadEmailTemplateMappingProfile.cs @@ -0,0 +1,23 @@ +using AutoMapper; +using EnvelopeGenerator.Domain.Entities; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace EnvelopeGenerator.Application.EmailTemplates.Queries.Read; + +/// +/// +/// +public class ReadEmailTemplateMappingProfile : Profile +{ + /// + /// + /// + public ReadEmailTemplateMappingProfile() + { + CreateMap(); + } +} diff --git a/EnvelopeGenerator.Application/EmailTemplates/Queries/Read/ReadEmailTemplateQuery.cs b/EnvelopeGenerator.Application/EmailTemplates/Queries/Read/ReadEmailTemplateQuery.cs index 0c82bab2..b3108a30 100644 --- a/EnvelopeGenerator.Application/EmailTemplates/Queries/Read/ReadEmailTemplateQuery.cs +++ b/EnvelopeGenerator.Application/EmailTemplates/Queries/Read/ReadEmailTemplateQuery.cs @@ -1,10 +1,12 @@ -namespace EnvelopeGenerator.Application.EmailTemplates.Queries.Read; +using MediatR; + +namespace EnvelopeGenerator.Application.EmailTemplates.Queries.Read; /// /// Stellt eine Abfrage dar, um eine E-Mail-Vorlage zu lesen. /// Diese Klasse erbt von . /// -public record ReadEmailTemplateQuery : EmailTemplateQuery +public record ReadEmailTemplateQuery : EmailTemplateQuery, IRequest { } diff --git a/EnvelopeGenerator.Application/EmailTemplates/Queries/Read/ReadEmailTemplateQueryHandler.cs b/EnvelopeGenerator.Application/EmailTemplates/Queries/Read/ReadEmailTemplateQueryHandler.cs new file mode 100644 index 00000000..13d6aa73 --- /dev/null +++ b/EnvelopeGenerator.Application/EmailTemplates/Queries/Read/ReadEmailTemplateQueryHandler.cs @@ -0,0 +1,50 @@ +using AutoMapper; +using EnvelopeGenerator.Application.Contracts.Repositories; +using EnvelopeGenerator.Application.DTOs; +using EnvelopeGenerator.Common; +using MediatR; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Logging; + +namespace EnvelopeGenerator.Application.EmailTemplates.Queries.Read; + +/// +/// +/// +public class ReadEmailTemplateQueryHandler : IRequestHandler +{ + private readonly IMapper _mapper; + + private readonly IEmailTemplateRepository _repository; + + /// + /// Initialisiert eine neue Instanz der -Klasse. + /// + /// + /// + /// Die AutoMapper-Instanz, die zum Zuordnen von Objekten verwendet wird. + /// + public ReadEmailTemplateQueryHandler(IMapper mapper, IEmailTemplateRepository repository) + { + _mapper = mapper; + _repository = repository; + } + + /// + /// + /// + /// + /// + /// + /// + public async Task Handle(ReadEmailTemplateQuery request, CancellationToken cancellationToken) + { + var temp = request.Id is int id + ? await _repository.ReadByIdAsync(id) + : request.Type is Constants.EmailTemplateType type + ? await _repository.ReadByNameAsync(type) + : throw new InvalidOperationException("Either a valid integer ID or a valid EmailTemplateType must be provided in the request."); + + return temp is null ? null : _mapper.Map(temp); + } +} diff --git a/EnvelopeGenerator.Application/EmailTemplates/Queries/Read/ReadEmailTemplateResponse.cs b/EnvelopeGenerator.Application/EmailTemplates/Queries/Read/ReadEmailTemplateResponse.cs index 3234c86a..7616411d 100644 --- a/EnvelopeGenerator.Application/EmailTemplates/Queries/Read/ReadEmailTemplateResponse.cs +++ b/EnvelopeGenerator.Application/EmailTemplates/Queries/Read/ReadEmailTemplateResponse.cs @@ -3,18 +3,35 @@ /// /// Stellt die Antwort für eine Abfrage von E-Mail-Vorlagen bereit. /// -/// Die eindeutige Kennung der E-Mail-Vorlage. -/// Der Typ der E-Mail-Vorlage. -/// Das Datum und die Uhrzeit, wann die Vorlage hinzugefügt wurde. -/// Der Inhalt (Body) der E-Mail-Vorlage. Kann null sein. -/// Der Betreff der E-Mail-Vorlage. Kann null sein. -/// Das Datum und die Uhrzeit, wann die Vorlage zuletzt geändert wurde. Kann null sein. -public record ReadEmailTemplateResponse( - int Id, - int Type, - DateTime AddedWhen, - string? Body = null, - string? Subject = null, - DateTime? ChangedWhen = null) +public class ReadEmailTemplateResponse { + /// + /// Die eindeutige Kennung der E-Mail-Vorlage. + /// + public int Id { get; init; } + + /// + /// Der Typ der E-Mail-Vorlage. + /// + public int Type { get; init; } + + /// + /// Das Datum und die Uhrzeit, wann die Vorlage hinzugefügt wurde. + /// + public DateTime AddedWhen { get; init; } + + /// + /// Der Inhalt (Body) der E-Mail-Vorlage. Kann null sein. + /// + public string? Body { get; set; } + + /// + /// Der Betreff der E-Mail-Vorlage. Kann null sein. + /// + public string? Subject { get; set; } + + /// + /// Das Datum und die Uhrzeit, wann die Vorlage zuletzt geändert wurde. Kann null sein. + /// + public DateTime? ChangedWhen { get; set; } } diff --git a/EnvelopeGenerator.GeneratorAPI/Controllers/EmailTemplateController.cs b/EnvelopeGenerator.GeneratorAPI/Controllers/EmailTemplateController.cs index fa4fb84a..0e90bfd7 100644 --- a/EnvelopeGenerator.GeneratorAPI/Controllers/EmailTemplateController.cs +++ b/EnvelopeGenerator.GeneratorAPI/Controllers/EmailTemplateController.cs @@ -5,6 +5,9 @@ using EnvelopeGenerator.Application.EmailTemplates.Commands.Reset; using EnvelopeGenerator.Application.EmailTemplates.Queries.Read; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using EnvelopeGenerator.Application.Contracts.Repositories; +using EnvelopeGenerator.Application.DTOs; +using MediatR; namespace EnvelopeGenerator.GeneratorAPI.Controllers; @@ -17,17 +20,27 @@ namespace EnvelopeGenerator.GeneratorAPI.Controllers; [Authorize] public class EmailTemplateController : ControllerBase { + private readonly ILogger _logger; + private readonly IMapper _mapper; + private readonly IEmailTemplateRepository _repository; + + private readonly IMediator _mediator; + /// /// Initialisiert eine neue Instanz der -Klasse. /// /// + /// /// Die AutoMapper-Instanz, die zum Zuordnen von Objekten verwendet wird. /// - public EmailTemplateController(IMapper mapper) + public EmailTemplateController(IMapper mapper, IEmailTemplateRepository repository, ILogger logger, IMediator mediator) { _mapper = mapper; + _repository = repository; + _logger = logger; + _mediator = mediator; } /// @@ -45,10 +58,26 @@ public class EmailTemplateController : ControllerBase /// Wenn der Benutzer nicht authentifiziert ist. /// Wenn die gesuchte Abfrage nicht gefunden wird. [HttpGet] - public IActionResult Get([FromQuery] ReadEmailTemplateQuery? emailTemplate = null) + public async Task Get([FromQuery] ReadEmailTemplateQuery? emailTemplate = null) { - // Implementation logic here - return Ok(); // Placeholder for actual implementation + try + { + if (emailTemplate is null || (emailTemplate.Id is null && emailTemplate.Type is null)) + { + var temps = await _repository.ReadAllAsync(); + return Ok(_mapper.Map>(temps)); + } + else + { + var temp = await _mediator.Send(emailTemplate); + return temp is null ? NotFound() : Ok(temp); + } + } + catch (Exception ex) + { + _logger.LogError(ex, "{Message}", ex.Message); + return StatusCode(StatusCodes.Status500InternalServerError); + } } /// From 05de44bc133b8a9f1f097d4aaad89bd2c1a718b5 Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Tue, 6 May 2025 19:23:01 +0200 Subject: [PATCH 37/94] Implement MediatR support for envelope template reset - Modified `ResetEnvelopeTemplateCommand` to implement `IRequest`. - Introduced `ResetEnvelopeTemplateCommandHandler` to handle requests. - Added a collection of default email templates for notifications. --- .../Reset/ResetEnvelopeTemplateCommand.cs | 3 +- .../ResetEnvelopeTemplateCommandHandler.cs | 89 +++++++++++++++++++ 2 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 EnvelopeGenerator.Application/EmailTemplates/Commands/Reset/ResetEnvelopeTemplateCommandHandler.cs diff --git a/EnvelopeGenerator.Application/EmailTemplates/Commands/Reset/ResetEnvelopeTemplateCommand.cs b/EnvelopeGenerator.Application/EmailTemplates/Commands/Reset/ResetEnvelopeTemplateCommand.cs index 79ce1295..38926dd6 100644 --- a/EnvelopeGenerator.Application/EmailTemplates/Commands/Reset/ResetEnvelopeTemplateCommand.cs +++ b/EnvelopeGenerator.Application/EmailTemplates/Commands/Reset/ResetEnvelopeTemplateCommand.cs @@ -1,4 +1,5 @@ using EnvelopeGenerator.Common; +using MediatR; namespace EnvelopeGenerator.Application.EmailTemplates.Commands.Reset; @@ -19,4 +20,4 @@ namespace EnvelopeGenerator.Application.EmailTemplates.Commands.Reset; /// 8 - DocumentRejected_REC (Für den ablehnenden Empfänger): Mail an den ablehnenden Empfänger, wenn das Dokument abgelehnt wird. /// 9 - DocumentRejected_REC_2 (Für sonstige Empfänger): Mail an andere Empfänger (Brief), wenn das Dokument abgelehnt wird. /// -public record ResetEnvelopeTemplateCommand(int? Id, Constants.EmailTemplateType? Type) : EmailTemplateQuery(Id, Type); +public record ResetEnvelopeTemplateCommand(int? Id, Constants.EmailTemplateType? Type) : EmailTemplateQuery(Id, Type), IRequest; diff --git a/EnvelopeGenerator.Application/EmailTemplates/Commands/Reset/ResetEnvelopeTemplateCommandHandler.cs b/EnvelopeGenerator.Application/EmailTemplates/Commands/Reset/ResetEnvelopeTemplateCommandHandler.cs new file mode 100644 index 00000000..7e5b36e3 --- /dev/null +++ b/EnvelopeGenerator.Application/EmailTemplates/Commands/Reset/ResetEnvelopeTemplateCommandHandler.cs @@ -0,0 +1,89 @@ +using EnvelopeGenerator.Domain.Entities; +using MediatR; + +namespace EnvelopeGenerator.Application.EmailTemplates.Commands.Reset; + +/// +/// +/// +public class ResetEnvelopeTemplateCommandHandler : IRequestHandler +{ + /// + /// + /// + /// + /// + /// + /// + public Task Handle(ResetEnvelopeTemplateCommand request, CancellationToken cancellationToken) + { + throw new NotImplementedException(); + } + + /// + /// + /// + public static readonly IEnumerable Default = new List() + { + new(){ + Id = 1, + Name = "DocumentReceived", + Body = "Guten Tag [NAME_RECEIVER],
\r\n
\r\n[NAME_SENDER] hat Ihnen ein Dokument zum [SIGNATURE_TYPE] gesendet.
\r\n
\r\nÜber den folgenden Link können Sie das Dokument einsehen und elektronisch unterschreiben: [LINK_TO_DOCUMENT_TEXT]
\r\n
\r\n[MESSAGE]
\r\n
\r\nMit freundlichen Grüßen
\r\n
\r\n[NAME_PORTAL]", + Subject = "Dokument erhalten: '[DOCUMENT_TITLE]'" + }, + new(){ + Id = 2, + Name = "DocumentDeleted", + Body = "Guten Tag [NAME_RECEIVER],
\r\n
\r\n[NAME_SENDER] hat den Umschlag '[DOCUMENT_TITLE]' gelöscht/zurückgezogen.

\rBegründung:
[REASON]

\r\n
\r\nMit freundlichen Grüßen
\r\n
\r\n[NAME_PORTAL]", + Subject = "Umschlag zurückgezogen: '[DOCUMENT_TITLE]'" + }, + new(){ + Id = 3, + Name = "DocumentSigned", + Body = "Guten Tag [NAME_RECEIVER],
\r\n
\r\nhiermit bestätigen wir Ihnen die erfolgreiche Signatur für den Vorgang '[DOCUMENT_TITLE]'.
\r\nWenn alle Vertragspartner unterzeichnet haben, erhalten Sie ebenfalls per email ein unterschriebenes Exemplar mit dem Signierungszertifikat!\r\n
\r\nMit freundlichen Grüßen
\r\n
\r\n[NAME_PORTAL]", + Subject = "Dokument unterschrieben: '[DOCUMENT_TITLE]'" + }, + new(){ + Id = 4, + Name = "DocumentCompleted", + Body = "Guten Tag [NAME_RECEIVER],
\r\n
\r\nDer Signaturvorgang '[DOCUMENT_TITLE]' wurde erfolgreich abgeschlossen.
\r\n
\r\nSie erhalten das Dokument mit einem detaillierten Ergebnisbericht als Anhang zu dieser Email.
\r\n
\r\nMit freundlichen Grüßen
\r\n
\r\n[NAME_PORTAL]", + Subject = "Umschlag abgeschlossen: '[DOCUMENT_TITLE]'" + }, + new(){ + Id = 5, + Name = "DocumentAccessCodeReceived", + Body = "Guten Tag [NAME_RECEIVER],
\r\n
\r\n[NAME_SENDER] hat Ihnen ein Dokument zum [SIGNATURE_TYPE] gesendet.
\r\n
\r\nVerwenden Sie den folgenden Zugriffscode, um das Dokument einzusehen:
\r\n
\r\n[DOCUMENT_ACCESS_CODE]
\r\n
\r\nMit freundlichen Grüßen
\r\n
\r\n[NAME_PORTAL]", + Subject = "Zugriffscode für Dokument erhalten: '[DOCUMENT_TITLE]'" + }, + new(){ + Id = 6, + Name = "DocumentRejected_ADM", + Body = "Guten Tag [NAME_SENDER],

[NAME_RECEIVER] hat den Umschlag '[DOCUMENT_TITLE]' mit folgendem Grund abgelehnt:

\r\n[REASON] \r\n

Der Umschlag wurde auf den Status Rejected gesetzt.

\r\nMit freundlichen Grüßen
\r\n
\r\n[NAME_PORTAL]", + Subject = "'[DOCUMENT_TITLE]' - Unterzeichnungsvorgang zurückgezogen" + }, + new(){ + Id = 9, + Name = "DocumentRejected_REC", + Body = "Guten Tag [NAME_RECEIVER],\r\n

Hiermit bestätigen wir Ihnen die Ablehnung des Unterzeichnungsvorganges '[DOCUMENT_TITLE]'!

Der Vertragsinhaber [NAME_SENDER] wurde über die Ablehnung informiert.

\r\nMit freundlichen Grüßen
\r\n
\r\n[NAME_PORTAL]", + Subject = "'[DOCUMENT_TITLE]' - Bestätigung Ablehnung" + }, + new(){ + Id = 10, + Name = "DocumentRejected_REC_2", + Body = "Guten Tag [NAME_RECEIVER],\r\n

Der Unterzeichnungsvorganges '[DOCUMENT_TITLE]' wurde durch einen anderen Vertragspartner abgelehnt! Ihre notwendige Unterzeichnung wurde verworfen.

Der Vertragsinhaber [NAME_SENDER] wird sich bei Bedarf mit Ihnen in Verbindung setzen.

\r\nMit freundlichen Grüßen
\r\n
\r\n[NAME_PORTAL]", + Subject = "'[DOCUMENT_TITLE]' - Unterzeichnungsvorgang abgelehnt." + }, + new(){ + Id = 11, + Name = "DocumentShared", + Body = "Guten Tag,

[NAME_RECEIVER] hat Ihnen ein Dokument zum Ansehen gesendet.

Über den folgenden Link können Sie das Dokument einsehen: [LINK_TO_DOCUMENT_TEXT]


Mit freundlichen Grüßen

[NAME_PORTAL]", + Subject = "Dokument geteilt: '[DOCUMENT_TITLE]'" + }, + new(){ + Id = 12, + Name = "TotpSecret", + Body = "Guten Tag,

Sie können auf Ihren Zwei-Faktor-Authentifizierungscode zugreifen, indem Sie den unten stehenden QR-Code mit einer beliebigen Authentifizierungs-App auf Ihrem Telefon scannen (Google Authenticator, Microsoft Authenticator usw.). Dieser Code ist bis zum [TFA_EXPIRATION] gültig.



\r\n
Mit freundlichen Grüßen

[NAME_PORTAL]", + Subject = "2-Faktor-Verifizierung QR-Code" + } + }; +} From 10341fd3ccf9d625a170b47414735fedfb363bb2 Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Tue, 6 May 2025 20:58:52 +0200 Subject: [PATCH 38/94] Refactor EmailTemplateDto and update command handler The `EmailTemplateDto` class has been changed from a record with positional parameters to a class with explicit properties, including XML documentation and required modifiers for `Name`, `Body`, and `Subject`. In the `ResetEnvelopeTemplateCommandHandler`, added necessary using directives, modified the constructor to accept an `IRepository`, and updated the `Handle` method to read and update email templates based on the request's ID or type. The static `Default` collection has been renamed to `Defaults` and now uses `EmailTemplateDto`. --- .../DTOs/EmailTemplateDto.cs | 30 +++++++++++++--- .../ResetEnvelopeTemplateCommandHandler.cs | 35 ++++++++++++++----- 2 files changed, 52 insertions(+), 13 deletions(-) diff --git a/EnvelopeGenerator.Application/DTOs/EmailTemplateDto.cs b/EnvelopeGenerator.Application/DTOs/EmailTemplateDto.cs index e22a4844..fbad5c4e 100644 --- a/EnvelopeGenerator.Application/DTOs/EmailTemplateDto.cs +++ b/EnvelopeGenerator.Application/DTOs/EmailTemplateDto.cs @@ -3,10 +3,30 @@ using Microsoft.AspNetCore.Mvc; namespace EnvelopeGenerator.Application.DTOs { + ///

+ /// + /// [ApiExplorerSettings(IgnoreApi = true)] - public record EmailTemplateDto( - int Id, - string Name, - string Body, - string Subject) : IUnique; + public record EmailTemplateDto : IUnique + { + /// + /// + /// + public int Id{ get; init; } + + /// + /// + /// + public required string Name { get; init; } + + /// + /// + /// + public required string Body { get; init; } + + /// + /// + /// + public required string Subject { get; init; } + }; } \ No newline at end of file diff --git a/EnvelopeGenerator.Application/EmailTemplates/Commands/Reset/ResetEnvelopeTemplateCommandHandler.cs b/EnvelopeGenerator.Application/EmailTemplates/Commands/Reset/ResetEnvelopeTemplateCommandHandler.cs index 7e5b36e3..323b751c 100644 --- a/EnvelopeGenerator.Application/EmailTemplates/Commands/Reset/ResetEnvelopeTemplateCommandHandler.cs +++ b/EnvelopeGenerator.Application/EmailTemplates/Commands/Reset/ResetEnvelopeTemplateCommandHandler.cs @@ -1,4 +1,7 @@ -using EnvelopeGenerator.Domain.Entities; +using DigitalData.Core.Abstractions.Infrastructure; +using EnvelopeGenerator.Application.DTOs; +using EnvelopeGenerator.Application.EmailTemplates.Queries.Read; +using EnvelopeGenerator.Domain.Entities; using MediatR; namespace EnvelopeGenerator.Application.EmailTemplates.Commands.Reset; @@ -8,22 +11,37 @@ namespace EnvelopeGenerator.Application.EmailTemplates.Commands.Reset; ///
public class ResetEnvelopeTemplateCommandHandler : IRequestHandler { + private readonly IRepository _repository; + /// /// /// - /// - /// - /// - /// - public Task Handle(ResetEnvelopeTemplateCommand request, CancellationToken cancellationToken) + /// + public ResetEnvelopeTemplateCommandHandler(IRepository repository) { - throw new NotImplementedException(); + _repository = repository; + } + + public async Task Handle(ResetEnvelopeTemplateCommand request, CancellationToken cancel) + { + var temps = request.Id is not null + ? await _repository.ReadAllAsync(t => t.Id == request.Id, cancel) + : request.Type is not null + ? await _repository.ReadAllAsync(t => t.Name == request.Type.ToString(), cancel) + : await _repository.ReadAllAsync(ct: cancel); + + foreach (var temp in temps) + { + var def = Defaults.Where(t => t.Name == temp.Name).FirstOrDefault(); + if(def is not null) + await _repository.UpdateAsync(def, t => t.Id == temp.Id, cancel); + } } /// /// /// - public static readonly IEnumerable Default = new List() + public static readonly IEnumerable Defaults = new List() { new(){ Id = 1, @@ -86,4 +104,5 @@ public class ResetEnvelopeTemplateCommandHandler : IRequestHandler Date: Tue, 6 May 2025 22:26:26 +0200 Subject: [PATCH 39/94] Enhance Get method in ReceiverController Updated the Get method to check for receiver.Id along with EmailAddress and Signature. If Id is provided, it attempts to read by ID and returns NotFound if not found. If Id is not provided, it falls back to reading by EmailAddress and Signature, now also returning NotFound instead of a 500 error for missing receivers. --- .../Controllers/ReceiverController.cs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/EnvelopeGenerator.GeneratorAPI/Controllers/ReceiverController.cs b/EnvelopeGenerator.GeneratorAPI/Controllers/ReceiverController.cs index 9920b5de..95243234 100644 --- a/EnvelopeGenerator.GeneratorAPI/Controllers/ReceiverController.cs +++ b/EnvelopeGenerator.GeneratorAPI/Controllers/ReceiverController.cs @@ -38,17 +38,24 @@ public class ReceiverController : CRUDControllerBaseWithErrorHandling Get([FromQuery] ReadReceiverQuery receiver) { - if (receiver.EmailAddress is null && receiver.Signature is null) + if (receiver.Id is null && receiver.EmailAddress is null && receiver.Signature is null) return await base.GetAll(); try { + if(receiver.Id is int id) + return await _service.ReadByIdAsync(id).ThenAsync( + Success: Ok, + Fail: IActionResult (msg, ntc) => + { + return NotFound(); + }); + return await _service.ReadByAsync(emailAddress: receiver.EmailAddress, signature: receiver.Signature).ThenAsync( Success: Ok, Fail: IActionResult (msg, ntc) => { - _logger.LogNotice(ntc); - return StatusCode(StatusCodes.Status500InternalServerError); + return NotFound(); }); } catch (Exception ex) From 27d9a149bc96219ee0b2985f0b48c0144cdf8c43 Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Tue, 6 May 2025 22:54:19 +0200 Subject: [PATCH 40/94] Refactor Envelope properties in Envelope.cs - Added required `Uuid` property with column mapping. - Removed `Message` property. - Changed `Language` from required to nullable string. - Other properties (`ContractType`, `ExpiresWhen`, `SendReminderEmails`) remain unchanged. --- EnvelopeGenerator.Domain/Entities/Envelope.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/EnvelopeGenerator.Domain/Entities/Envelope.cs b/EnvelopeGenerator.Domain/Entities/Envelope.cs index e4db872e..8a8ee2ac 100644 --- a/EnvelopeGenerator.Domain/Entities/Envelope.cs +++ b/EnvelopeGenerator.Domain/Entities/Envelope.cs @@ -28,7 +28,6 @@ namespace EnvelopeGenerator.Domain.Entities [Column("ENVELOPE_UUID", TypeName = "nvarchar(36)")] public required string Uuid { get; init; } - [Required] [Column("MESSAGE", TypeName = "nvarchar(max)")] public string? Message { get; set; } @@ -52,7 +51,7 @@ namespace EnvelopeGenerator.Domain.Entities public int? ContractType { get; set; } [Column("LANGUAGE", TypeName = "nvarchar(5)")] - public required string Language { get; set; } + public string? Language { get; set; } [Column("SEND_REMINDER_EMAILS")] public bool? SendReminderEmails { get; set; } From 21859ca6e661624067cb33c5096c774aebf8d10a Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Tue, 6 May 2025 23:23:33 +0200 Subject: [PATCH 41/94] Refactor ReadHistoryQuery and optimize repository queries - Changed `EnvelopeId` in `ReadHistoryQuery` to nullable for improved flexibility in queries. - Introduced `withReceiver` variable in `HistoryController` to enhance query logic for retrieving history records. - Updated `EnvelopeHistoryRepository` to use `AsNoTracking()` for better performance in read-only scenarios. --- .../Histories/Queries/Read/ReadHistoryQuery.cs | 2 +- EnvelopeGenerator.GeneratorAPI/Controllers/HistoryController.cs | 2 ++ .../Repositories/EnvelopeHistoryRepository.cs | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/EnvelopeGenerator.Application/Histories/Queries/Read/ReadHistoryQuery.cs b/EnvelopeGenerator.Application/Histories/Queries/Read/ReadHistoryQuery.cs index 2f8ba313..452fc714 100644 --- a/EnvelopeGenerator.Application/Histories/Queries/Read/ReadHistoryQuery.cs +++ b/EnvelopeGenerator.Application/Histories/Queries/Read/ReadHistoryQuery.cs @@ -13,7 +13,7 @@ namespace EnvelopeGenerator.Application.Histories.Queries.Read; /// Abfrage, die angibt, worauf sich der Datensatz bezieht. Ob er sich auf den Empfänger, den Sender oder das System bezieht, wird durch 0, 1 bzw. 2 dargestellt. /// Abfrage zur Steuerung, ob nur der aktuelle Status oder der gesamte Datensatz zurückgegeben wird. public record ReadHistoryQuery( - int EnvelopeId, + int? EnvelopeId = null, ReadEnvelopeQuery? Envelope = null, ReadReceiverQuery? Receiver = null, Constants.ReferenceType? Related = null, diff --git a/EnvelopeGenerator.GeneratorAPI/Controllers/HistoryController.cs b/EnvelopeGenerator.GeneratorAPI/Controllers/HistoryController.cs index 0569bf0d..8c934f3e 100644 --- a/EnvelopeGenerator.GeneratorAPI/Controllers/HistoryController.cs +++ b/EnvelopeGenerator.GeneratorAPI/Controllers/HistoryController.cs @@ -131,6 +131,8 @@ public class HistoryController : ControllerBase [Authorize] public async Task GetAllAsync([FromQuery] ReadHistoryQuery history) { + + bool withReceiver = false; bool withSender = false; diff --git a/EnvelopeGenerator.Infrastructure/Repositories/EnvelopeHistoryRepository.cs b/EnvelopeGenerator.Infrastructure/Repositories/EnvelopeHistoryRepository.cs index 18ccc27e..cfa8313a 100644 --- a/EnvelopeGenerator.Infrastructure/Repositories/EnvelopeHistoryRepository.cs +++ b/EnvelopeGenerator.Infrastructure/Repositories/EnvelopeHistoryRepository.cs @@ -13,7 +13,7 @@ public class EnvelopeHistoryRepository : CRUDRepository By(int? envelopeId = null, string? userReference = null, int? status = null, bool withSender = false, bool withReceiver = false) { - var query = _dbSet.AsQueryable(); + var query = _dbSet.AsNoTracking(); if (envelopeId is not null) query = query.Where(eh => eh.EnvelopeId == envelopeId); From 2f08991eebcda0301a127fdbccd840e3a25ca673 Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Tue, 6 May 2025 23:25:34 +0200 Subject: [PATCH 42/94] Refactor ReadHistoryQuery by removing parameters Removed the `Envelope` and `Receiver` parameters from the `ReadHistoryQuery` record in `ReadHistoryQuery.cs`. This simplifies the record by reducing the number of parameters it accepts, enhancing clarity and maintainability. --- .../Histories/Queries/Read/ReadHistoryQuery.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/EnvelopeGenerator.Application/Histories/Queries/Read/ReadHistoryQuery.cs b/EnvelopeGenerator.Application/Histories/Queries/Read/ReadHistoryQuery.cs index 452fc714..8109baf2 100644 --- a/EnvelopeGenerator.Application/Histories/Queries/Read/ReadHistoryQuery.cs +++ b/EnvelopeGenerator.Application/Histories/Queries/Read/ReadHistoryQuery.cs @@ -8,13 +8,10 @@ namespace EnvelopeGenerator.Application.Histories.Queries.Read; /// Repräsentiert eine Abfrage für die Verlaufshistorie eines Umschlags. ///
/// Die eindeutige Kennung des Umschlags. -/// Die Abfrage, die den Umschlag beschreibt. /// Die Abfrage, die den Empfänger beschreibt. /// Abfrage, die angibt, worauf sich der Datensatz bezieht. Ob er sich auf den Empfänger, den Sender oder das System bezieht, wird durch 0, 1 bzw. 2 dargestellt. /// Abfrage zur Steuerung, ob nur der aktuelle Status oder der gesamte Datensatz zurückgegeben wird. public record ReadHistoryQuery( int? EnvelopeId = null, - ReadEnvelopeQuery? Envelope = null, - ReadReceiverQuery? Receiver = null, Constants.ReferenceType? Related = null, bool? OnlyLast = true); \ No newline at end of file From cec79e5b6df93da8d96443e8fb1c6e0307d21da1 Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Tue, 6 May 2025 23:26:08 +0200 Subject: [PATCH 43/94] Add TODO for sender query and update documentation Added a comment to indicate a TODO for implementing a sender query in the `ReadHistoryQuery` record. Removed the parameter description for "Receiver" from the summary documentation while keeping the rest of the documentation intact. --- .../Histories/Queries/Read/ReadHistoryQuery.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EnvelopeGenerator.Application/Histories/Queries/Read/ReadHistoryQuery.cs b/EnvelopeGenerator.Application/Histories/Queries/Read/ReadHistoryQuery.cs index 8109baf2..7439b900 100644 --- a/EnvelopeGenerator.Application/Histories/Queries/Read/ReadHistoryQuery.cs +++ b/EnvelopeGenerator.Application/Histories/Queries/Read/ReadHistoryQuery.cs @@ -4,11 +4,11 @@ using EnvelopeGenerator.Common; namespace EnvelopeGenerator.Application.Histories.Queries.Read; +//TODO: Add sender query /// /// Repräsentiert eine Abfrage für die Verlaufshistorie eines Umschlags. /// /// Die eindeutige Kennung des Umschlags. -/// Die Abfrage, die den Empfänger beschreibt. /// Abfrage, die angibt, worauf sich der Datensatz bezieht. Ob er sich auf den Empfänger, den Sender oder das System bezieht, wird durch 0, 1 bzw. 2 dargestellt. /// Abfrage zur Steuerung, ob nur der aktuelle Status oder der gesamte Datensatz zurückgegeben wird. public record ReadHistoryQuery( From b9c4f7da1c14aec92b5bde12d0aa6d0f55a8f960 Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Wed, 7 May 2025 00:03:01 +0200 Subject: [PATCH 44/94] Enhance ReadByUsernameAsync with status filtering Updated the `ReadByUsernameAsync` method in the `EnvelopeReceiverController` to accept additional parameters: `min_status`, `max_status`, and `ignore_statuses`. These parameters are now derived from the `envelopeReceiver` object, allowing for improved status filtering when retrieving records by username. --- .../Controllers/EnvelopeReceiverController.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeReceiverController.cs b/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeReceiverController.cs index bc4e77a9..90b5b955 100644 --- a/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeReceiverController.cs +++ b/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeReceiverController.cs @@ -83,7 +83,12 @@ public class EnvelopeReceiverController : ControllerBase return StatusCode(StatusCodes.Status500InternalServerError); } - return await _erService.ReadByUsernameAsync(username: username).ThenAsync( + return await _erService.ReadByUsernameAsync( + username: username, + min_status: envelopeReceiver.Status?.Min ?? envelopeReceiver.Envelope?.Status, + max_status:envelopeReceiver.Status?.Max ?? envelopeReceiver.Envelope?.Status, + ignore_statuses: envelopeReceiver.Status?.Ignore ?? Array.Empty()) + .ThenAsync( Success: Ok, Fail: IActionResult (msg, ntc) => { From a7e4d6e58f41cda5872eaf4c87bd1d718fe77f7f Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Wed, 7 May 2025 00:17:18 +0200 Subject: [PATCH 45/94] Enhance ReadByUsernameAsync with new query parameters Updated IEnvelopeReceiverService to include EnvelopeQuery and ReadReceiverQuery in ReadByUsernameAsync for improved filtering capabilities. Modified EnvelopeReceiverService to implement these changes, allowing for more precise data retrieval. Updated EnvelopeReceiverController to pass the new parameters when calling the service method. --- .../Services/IEnvelopeReceiverService.cs | 4 +++- .../Services/EnvelopeReceiverService.cs | 20 ++++++++++++++++++- .../Controllers/EnvelopeReceiverController.cs | 6 ++++-- 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/EnvelopeGenerator.Application/Contracts/Services/IEnvelopeReceiverService.cs b/EnvelopeGenerator.Application/Contracts/Services/IEnvelopeReceiverService.cs index cc384954..98a05635 100644 --- a/EnvelopeGenerator.Application/Contracts/Services/IEnvelopeReceiverService.cs +++ b/EnvelopeGenerator.Application/Contracts/Services/IEnvelopeReceiverService.cs @@ -3,6 +3,8 @@ using DigitalData.Core.Abstractions.Application; using DigitalData.Core.DTO; using EnvelopeGenerator.Application.DTOs.EnvelopeReceiver; using EnvelopeGenerator.Application.DTOs.Messaging; +using EnvelopeGenerator.Application.Envelopes; +using EnvelopeGenerator.Application.Receivers.Queries.Read; using EnvelopeGenerator.Domain.Entities; namespace EnvelopeGenerator.Application.Contracts.Services; @@ -31,7 +33,7 @@ public interface IEnvelopeReceiverService : IBasicCRUDService> IsExisting(string envelopeReceiverId); - Task>> ReadByUsernameAsync(string username, int? min_status = null, int? max_status = null, params int[] ignore_statuses); + Task>> ReadByUsernameAsync(string username, int? min_status = null, int? max_status = null, EnvelopeQuery? envelopeQuery = null, ReadReceiverQuery? receiverQuery = null, params int[] ignore_statuses); Task> ReadLastUsedReceiverNameByMail(string mail); diff --git a/EnvelopeGenerator.Application/Services/EnvelopeReceiverService.cs b/EnvelopeGenerator.Application/Services/EnvelopeReceiverService.cs index ddcd3e2a..dcde6d75 100644 --- a/EnvelopeGenerator.Application/Services/EnvelopeReceiverService.cs +++ b/EnvelopeGenerator.Application/Services/EnvelopeReceiverService.cs @@ -10,6 +10,8 @@ using Microsoft.Extensions.Logging; using EnvelopeGenerator.Extensions; using EnvelopeGenerator.Application.DTOs.Messaging; using EnvelopeGenerator.Application.Contracts.Services; +using EnvelopeGenerator.Application.Envelopes; +using EnvelopeGenerator.Application.Receivers.Queries.Read; namespace EnvelopeGenerator.Application.Services; @@ -137,9 +139,25 @@ public class EnvelopeReceiverService : BasicCRUDService>> ReadByUsernameAsync(string username, int? min_status = null, int? max_status = null, params int[] ignore_statuses) + public async Task>> ReadByUsernameAsync(string username, int? min_status = null, int? max_status = null, EnvelopeQuery? envelopeQuery = null, ReadReceiverQuery? receiverQuery = null, params int[] ignore_statuses) { var er_list = await _repository.ReadByUsernameAsync(username: username, min_status: min_status, max_status: max_status, ignore_statuses: ignore_statuses); + + if(envelopeQuery?.Id is int eId) + er_list = er_list.Where(er => er.EnvelopeId == eId); + + if (envelopeQuery?.Uuid is string uuid) + er_list = er_list.Where(er => er.Envelope?.Uuid == uuid); + + if (envelopeQuery?.Status is int status) + er_list = er_list.Where(er => er.Envelope?.Status == status); + + if(receiverQuery?.Id is int id) + er_list = er_list.Where(er => er.Receiver?.Id == id); + + if (receiverQuery?.Signature is string signature) + er_list = er_list.Where(er => er.Receiver?.Signature == signature); + var dto_list = _mapper.Map>(er_list); return Result.Success(dto_list); } diff --git a/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeReceiverController.cs b/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeReceiverController.cs index 90b5b955..3aea39ee 100644 --- a/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeReceiverController.cs +++ b/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeReceiverController.cs @@ -85,8 +85,10 @@ public class EnvelopeReceiverController : ControllerBase return await _erService.ReadByUsernameAsync( username: username, - min_status: envelopeReceiver.Status?.Min ?? envelopeReceiver.Envelope?.Status, - max_status:envelopeReceiver.Status?.Max ?? envelopeReceiver.Envelope?.Status, + min_status: envelopeReceiver.Status?.Min, + max_status:envelopeReceiver.Status?.Max, + envelopeQuery: envelopeReceiver.Envelope, + receiverQuery: envelopeReceiver.Receiver, ignore_statuses: envelopeReceiver.Status?.Ignore ?? Array.Empty()) .ThenAsync( Success: Ok, From 1c4f7f23868402d0a42a29fa3dea424d83ccfb4e Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Wed, 7 May 2025 00:27:34 +0200 Subject: [PATCH 46/94] Enhance envelope filtering in EnvelopeController Updated the success handler to filter envelopes by Id, Status, and Uuid based on the provided envelope object. The method now returns the specific envelope instead of a list of envelopes. --- .../Controllers/EnvelopeController.cs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeController.cs b/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeController.cs index 47508d54..d8f618c0 100644 --- a/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeController.cs +++ b/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeController.cs @@ -63,7 +63,19 @@ public class EnvelopeController : ControllerBase { if (User.GetId() is int intId) return await _envelopeService.ReadByUserAsync(intId, min_status: envelope.Status, max_status: envelope.Status).ThenAsync( - Success: Ok, + Success: envelopes => + { + if(envelope.Id is int id) + envelopes = envelopes.Where(e => e.Id == id); + + if(envelope.Status is int status) + envelopes = envelopes.Where(e => e.Status == status); + + if (envelope.Uuid is string uuid) + envelopes = envelopes.Where(e => e.Uuid == uuid); + + return Ok(envelope); + }, Fail: IActionResult (msg, ntc) => { _logger.LogNotice(ntc); From e4eb3e1192e8f2a6d2846f3ebbbf8ca691bce275 Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Wed, 7 May 2025 00:35:43 +0200 Subject: [PATCH 47/94] Refactor email template command and controller Updated namespace for `ResetEnvelopeTemplateCommand` and added default values for constructor parameters. Enhanced `EmailTemplateController` by making the `Update` method asynchronous, adding error handling, and incorporating a `try-catch` block for improved responsiveness and error management. --- .../Reset/ResetEnvelopeTemplateCommand.cs | 2 +- .../Controllers/EmailTemplateController.cs | 28 +++++++++++++------ 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/EnvelopeGenerator.Application/EmailTemplates/Commands/Reset/ResetEnvelopeTemplateCommand.cs b/EnvelopeGenerator.Application/EmailTemplates/Commands/Reset/ResetEnvelopeTemplateCommand.cs index 38926dd6..29a51f19 100644 --- a/EnvelopeGenerator.Application/EmailTemplates/Commands/Reset/ResetEnvelopeTemplateCommand.cs +++ b/EnvelopeGenerator.Application/EmailTemplates/Commands/Reset/ResetEnvelopeTemplateCommand.cs @@ -20,4 +20,4 @@ namespace EnvelopeGenerator.Application.EmailTemplates.Commands.Reset; /// 8 - DocumentRejected_REC (Für den ablehnenden Empfänger): Mail an den ablehnenden Empfänger, wenn das Dokument abgelehnt wird. /// 9 - DocumentRejected_REC_2 (Für sonstige Empfänger): Mail an andere Empfänger (Brief), wenn das Dokument abgelehnt wird. /// -public record ResetEnvelopeTemplateCommand(int? Id, Constants.EmailTemplateType? Type) : EmailTemplateQuery(Id, Type), IRequest; +public record ResetEnvelopeTemplateCommand(int? Id = null, Constants.EmailTemplateType? Type = null) : EmailTemplateQuery(Id, Type), IRequest; diff --git a/EnvelopeGenerator.GeneratorAPI/Controllers/EmailTemplateController.cs b/EnvelopeGenerator.GeneratorAPI/Controllers/EmailTemplateController.cs index 0e90bfd7..fd93c584 100644 --- a/EnvelopeGenerator.GeneratorAPI/Controllers/EmailTemplateController.cs +++ b/EnvelopeGenerator.GeneratorAPI/Controllers/EmailTemplateController.cs @@ -8,6 +8,8 @@ using Microsoft.AspNetCore.Mvc; using EnvelopeGenerator.Application.Contracts.Repositories; using EnvelopeGenerator.Application.DTOs; using MediatR; +using System.Threading.Tasks; +using DigitalData.UserManager.Application.Services; namespace EnvelopeGenerator.GeneratorAPI.Controllers; @@ -102,19 +104,27 @@ public class EmailTemplateController : ControllerBase /// Wenn der Benutzer nicht authentifiziert ist. /// Wenn die gesuchte Abfrage nicht gefunden wird. [HttpPut] - public IActionResult Update([FromQuery] EmailTemplateQuery email, [FromBody] UpdateEmailTemplateCommand? update = null) + public async Task Update([FromQuery] EmailTemplateQuery email, [FromBody] UpdateEmailTemplateCommand? update = null) { - if (update is null) + try { - var reset = _mapper.Map(email); - // Logic for resetting the email template + if (update is null) + { + var reset = _mapper.Map(email); + await _mediator.Send(new ResetEnvelopeTemplateCommand(email?.Id, email?.Type)); + return Ok(); + } + else + { + update.EmailTemplateQuery = email; + // Logic for updating the email template + } + } - else + catch (Exception ex) { - update.EmailTemplateQuery = email; - // Logic for updating the email template + _logger.LogError(ex, "An unexpected error occurred. {message}", ex.Message); + return new StatusCodeResult(StatusCodes.Status500InternalServerError); } - - return Ok(); // Placeholder for actual implementation } } From 613b2130a5f03b23f57974f2a6dd642552d2344a Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Wed, 7 May 2025 01:02:30 +0200 Subject: [PATCH 48/94] Refactor email template handling and error management - Updated EmailTemplateDto to allow mutable Body and Subject properties. - Implemented IRequest interface in UpdateEmailTemplateCommand for MediatR. - Enhanced error handling in EmailTemplateController with NotFoundException. - Introduced UpdateEmailTemplateCommandHandler for processing update commands. - Added NotFoundException class for improved error handling. --- .../DTOs/EmailTemplateDto.cs | 4 +- .../Update/UpdateEmailTemplateCommand.cs | 5 ++- .../UpdateEmailTemplateCommandHandler.cs | 43 +++++++++++++++++++ .../Exceptions/NotFoundException.cs | 12 ++++++ .../Controllers/EmailTemplateController.cs | 10 ++++- 5 files changed, 68 insertions(+), 6 deletions(-) create mode 100644 EnvelopeGenerator.Application/EmailTemplates/Commands/Update/UpdateEmailTemplateCommandHandler.cs create mode 100644 EnvelopeGenerator.Application/Exceptions/NotFoundException.cs diff --git a/EnvelopeGenerator.Application/DTOs/EmailTemplateDto.cs b/EnvelopeGenerator.Application/DTOs/EmailTemplateDto.cs index fbad5c4e..d9b82487 100644 --- a/EnvelopeGenerator.Application/DTOs/EmailTemplateDto.cs +++ b/EnvelopeGenerator.Application/DTOs/EmailTemplateDto.cs @@ -22,11 +22,11 @@ namespace EnvelopeGenerator.Application.DTOs /// /// /// - public required string Body { get; init; } + public required string Body { get; set; } /// /// /// - public required string Subject { get; init; } + public required string Subject { get; set; } }; } \ No newline at end of file diff --git a/EnvelopeGenerator.Application/EmailTemplates/Commands/Update/UpdateEmailTemplateCommand.cs b/EnvelopeGenerator.Application/EmailTemplates/Commands/Update/UpdateEmailTemplateCommand.cs index 63d2a7fd..b6350bd1 100644 --- a/EnvelopeGenerator.Application/EmailTemplates/Commands/Update/UpdateEmailTemplateCommand.cs +++ b/EnvelopeGenerator.Application/EmailTemplates/Commands/Update/UpdateEmailTemplateCommand.cs @@ -1,4 +1,5 @@ -using System.Text.Json.Serialization; +using MediatR; +using System.Text.Json.Serialization; namespace EnvelopeGenerator.Application.EmailTemplates.Commands.Update; @@ -12,7 +13,7 @@ namespace EnvelopeGenerator.Application.EmailTemplates.Commands.Update; /// /// (Optional) Der neue Betreff der E-Mail. Wenn null, bleibt der vorhandene Betreff unverändert. /// -public record UpdateEmailTemplateCommand(string? Body = null, string? Subject = null) +public record UpdateEmailTemplateCommand(string? Body = null, string? Subject = null) : IRequest { /// /// Die Abfrage, die die E-Mail-Vorlage darstellt, die aktualisiert werden soll. diff --git a/EnvelopeGenerator.Application/EmailTemplates/Commands/Update/UpdateEmailTemplateCommandHandler.cs b/EnvelopeGenerator.Application/EmailTemplates/Commands/Update/UpdateEmailTemplateCommandHandler.cs new file mode 100644 index 00000000..3d1462e9 --- /dev/null +++ b/EnvelopeGenerator.Application/EmailTemplates/Commands/Update/UpdateEmailTemplateCommandHandler.cs @@ -0,0 +1,43 @@ +using DigitalData.Core.Abstractions.Infrastructure; +using EnvelopeGenerator.Application.DTOs; +using EnvelopeGenerator.Application.Exceptions; +using EnvelopeGenerator.Domain.Entities; +using MediatR; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; + +namespace EnvelopeGenerator.Application.EmailTemplates.Commands.Update; + +/// +/// +/// +public class UpdateEmailTemplateCommandHandler : IRequestHandler +{ + private readonly IRepository _repository; + + /// + /// + /// + /// + public UpdateEmailTemplateCommandHandler(IRepository repository) + { + _repository = repository; + } + + public async Task Handle(UpdateEmailTemplateCommand request, CancellationToken cancel) + { + var temp = (request.EmailTemplateQuery?.Id is int id + ? await _repository.ReadOrDefaultAsync(t => t.Id == id, single: false, cancel) + : request!.EmailTemplateQuery!.Type is Common.Constants.EmailTemplateType type + ? await _repository.ReadOrDefaultAsync(t => t.Name == type.ToString(), single: false, cancel) + : throw new InvalidOperationException("Both id and type is null. Id: " + request.EmailTemplateQuery.Id + ". Type: " + request.EmailTemplateQuery.Type.ToString())) ?? throw new NotFoundException(); + + if(request.Body is not null) + temp.Body = request.Body; + + if (request.Subject is not null) + temp.Subject = request.Subject; + + await _repository.UpdateAsync(temp, t => t.Id == temp.Id, cancel); + } +} diff --git a/EnvelopeGenerator.Application/Exceptions/NotFoundException.cs b/EnvelopeGenerator.Application/Exceptions/NotFoundException.cs new file mode 100644 index 00000000..2e887b44 --- /dev/null +++ b/EnvelopeGenerator.Application/Exceptions/NotFoundException.cs @@ -0,0 +1,12 @@ +namespace EnvelopeGenerator.Application.Exceptions; + +public class NotFoundException : Exception +{ + public NotFoundException() + { + } + + public NotFoundException(string? message) : base(message) + { + } +} diff --git a/EnvelopeGenerator.GeneratorAPI/Controllers/EmailTemplateController.cs b/EnvelopeGenerator.GeneratorAPI/Controllers/EmailTemplateController.cs index fd93c584..db98a28e 100644 --- a/EnvelopeGenerator.GeneratorAPI/Controllers/EmailTemplateController.cs +++ b/EnvelopeGenerator.GeneratorAPI/Controllers/EmailTemplateController.cs @@ -10,6 +10,7 @@ using EnvelopeGenerator.Application.DTOs; using MediatR; using System.Threading.Tasks; using DigitalData.UserManager.Application.Services; +using EnvelopeGenerator.Application.Exceptions; namespace EnvelopeGenerator.GeneratorAPI.Controllers; @@ -116,11 +117,16 @@ public class EmailTemplateController : ControllerBase } else { - update.EmailTemplateQuery = email; - // Logic for updating the email template + var reset = _mapper.Map(email); + await _mediator.Send(update); + return Ok(); } } + catch(NotFoundException) + { + return BadRequest(); + } catch (Exception ex) { _logger.LogError(ex, "An unexpected error occurred. {message}", ex.Message); From 95a1fd13552ab258fa32d254807040f6276edf29 Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Wed, 7 May 2025 01:20:27 +0200 Subject: [PATCH 49/94] Add DocumentCreateReadSQL class for SQL operations Introduces the `DocumentCreateReadSQL` class in the `EnvelopeGenerator.Application.SQL` namespace, implementing the `ISQL` interface. This class includes a `Raw` property for a SQL query to insert and retrieve documents, along with a static method `CreateParmas` to generate `DynamicParameters` for SQL queries using base64 encoded strings and envelope UUIDs. --- .../SQL/DocumentCreateReadSQL.cs | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 EnvelopeGenerator.Application/SQL/DocumentCreateReadSQL.cs diff --git a/EnvelopeGenerator.Application/SQL/DocumentCreateReadSQL.cs b/EnvelopeGenerator.Application/SQL/DocumentCreateReadSQL.cs new file mode 100644 index 00000000..02b4ec73 --- /dev/null +++ b/EnvelopeGenerator.Application/SQL/DocumentCreateReadSQL.cs @@ -0,0 +1,45 @@ +using Dapper; +using EnvelopeGenerator.Application.Contracts.SQLExecutor; +using EnvelopeGenerator.Domain.Entities; + +namespace EnvelopeGenerator.Application.SQL; + +/// +/// +/// +public class DocumentCreateReadSQL : ISQL +{ + /// + /// + /// + public string Raw => @" + USE [DD_ECM] + + DECLARE @BYTE_DATA1 as VARBINARY(MAX) + SET @BYTE_DATA1 = CONVERT(VARBINARY(MAX),'@Base64') + DECLARE @OUT_DOCID int + + EXEC [dbo].[PRSIG_API_ADD_DOC] + @ENV_UID = @OUT_UID, + @BYTE_DATA = @BYTE_DATA1, + @OUT_DOCID = @OUT_DOCID OUTPUT + + SELECT TOP(1) * + FROM [dbo].[TBSIG_ENVELOPE_DOCUMENT] + WHERE [GUID] = @OUT_DOCID + "; + + /// + /// + /// + /// + /// + /// + public static DynamicParameters CreateParmas(string base64, string envelope_uuid) + { + var parameters = new DynamicParameters(); + parameters.Add("@Base64", base64); + parameters.Add("@OUT_UID", envelope_uuid); + return parameters; + } +} From bce29aa31a006a097fd47da518e90f6492631385 Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Wed, 7 May 2025 01:29:21 +0200 Subject: [PATCH 50/94] Add IDocumentExecutor interface and DocumentExecutor class Introduces the `IDocumentExecutor` interface with a method `CreateDocumentAsync` for asynchronous document creation. Implements the `DocumentExecutor` class that inherits from `SQLExecutor`, providing the functionality to create documents using a SQL connection and handle potential errors during the process. --- .../SQLExecutor/IDocumentExecutor.cs | 18 ++++++++++++ .../Executor/DocumentExecutor.cs | 28 +++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 EnvelopeGenerator.Application/Contracts/SQLExecutor/IDocumentExecutor.cs create mode 100644 EnvelopeGenerator.Infrastructure/Executor/DocumentExecutor.cs diff --git a/EnvelopeGenerator.Application/Contracts/SQLExecutor/IDocumentExecutor.cs b/EnvelopeGenerator.Application/Contracts/SQLExecutor/IDocumentExecutor.cs new file mode 100644 index 00000000..e3163f5e --- /dev/null +++ b/EnvelopeGenerator.Application/Contracts/SQLExecutor/IDocumentExecutor.cs @@ -0,0 +1,18 @@ +using EnvelopeGenerator.Domain.Entities; + +namespace EnvelopeGenerator.Application.Contracts.SQLExecutor; + +/// +/// +/// +public interface IDocumentExecutor +{ + /// + /// + /// + /// + /// + /// + /// + Task CreateDocumentAsync(string base64, string envelope_uuid, CancellationToken cancellation = default); +} diff --git a/EnvelopeGenerator.Infrastructure/Executor/DocumentExecutor.cs b/EnvelopeGenerator.Infrastructure/Executor/DocumentExecutor.cs new file mode 100644 index 00000000..7143b81e --- /dev/null +++ b/EnvelopeGenerator.Infrastructure/Executor/DocumentExecutor.cs @@ -0,0 +1,28 @@ +using Dapper; +using EnvelopeGenerator.Application.Contracts.SQLExecutor; +using EnvelopeGenerator.Application.SQL; +using EnvelopeGenerator.Domain.Entities; +using Microsoft.Data.SqlClient; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; + +namespace EnvelopeGenerator.Infrastructure.Executor; + +public class DocumentExecutor : SQLExecutor, IDocumentExecutor +{ + public DocumentExecutor(IServiceProvider provider, IOptions sqlExecutorParamsOptions) : base(provider, sqlExecutorParamsOptions) + { + } + + public async Task CreateDocumentAsync(string base64, string envelope_uuid, CancellationToken cancellation = default) + { + using var connection = new SqlConnection(Params.ConnectionString); + var sql = Provider.GetRequiredService(); + await connection.OpenAsync(cancellation); + var parameters = DocumentCreateReadSQL.CreateParmas(base64, envelope_uuid); + var documents = await connection.QueryAsync(sql.Raw, parameters); + return documents.FirstOrDefault() + ?? throw new InvalidOperationException($"Document creation failed. Parameters:" + + $"base64={base64}, envelope_uuid='{envelope_uuid}'."); + } +} From cc86e5faddd3d76d5d1ea8480a0072bcf3f19a9e Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Wed, 7 May 2025 01:32:21 +0200 Subject: [PATCH 51/94] Organize EnvelopeReceiverController and add DocumentExecutor Updated `EnvelopeReceiverController` with new regions for creating envelopes and managing recipients, improving code readability. Added service registration for `IDocumentExecutor` in `DependencyExtensions.cs` to enhance document management. --- .../Controllers/EnvelopeReceiverController.cs | 8 ++++++++ EnvelopeGenerator.Infrastructure/DependencyExtensions.cs | 1 + 2 files changed, 9 insertions(+) diff --git a/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeReceiverController.cs b/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeReceiverController.cs index 3aea39ee..9ac91445 100644 --- a/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeReceiverController.cs +++ b/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeReceiverController.cs @@ -187,8 +187,11 @@ public class EnvelopeReceiverController : ControllerBase CancellationToken cancel = default; int userId = User.GetId(); + #region Create Envelope var envelope = await _envelopeExecutor.CreateEnvelopeAsync(userId, request.Title, request.Message, request.TFAEnabled, cancel); + #endregion + #region Add receivers List sentRecipients = new(); List unsentRecipients = new(); @@ -205,6 +208,11 @@ public class EnvelopeReceiverController : ControllerBase var res = _mapper.Map(envelope); res.UnsentRecipients = unsentRecipients; res.SentRecipients = _mapper.Map>(sentRecipients); + #endregion + + #region Add document + + #endregion return Ok(res); } diff --git a/EnvelopeGenerator.Infrastructure/DependencyExtensions.cs b/EnvelopeGenerator.Infrastructure/DependencyExtensions.cs index dd5fd895..f7e4adb4 100644 --- a/EnvelopeGenerator.Infrastructure/DependencyExtensions.cs +++ b/EnvelopeGenerator.Infrastructure/DependencyExtensions.cs @@ -84,6 +84,7 @@ public static class DIExtensions services.AddScoped(); services.AddScoped(); + services.AddScoped(); if (sqlExecutorConfiguration is not null || sqlExecutorConfigureOptions is not null) services.AddSQLExecutor(sqlExecutorConfiguration, sqlExecutorConfigureOptions); From f2a09ea10ea1456c9401879b3a415bbd00875839 Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Wed, 7 May 2025 01:57:22 +0200 Subject: [PATCH 52/94] Refactor DocumentCreateCommand and enhance validation Updated the `DocumentCreateCommand` record to include a nullable `DataAsBase64` property for better encapsulation. Enhanced the `EnvelopeReceiverController` with logic to validate document data, ensuring that either `DataAsBase64` or `DataAsByte` is provided, but not both. Introduced a new static method `IsBase64String` to validate base64 string format. --- .../Create/CreateEnvelopeReceiverDtos.cs | 11 +++-- .../Controllers/EnvelopeReceiverController.cs | 41 ++++++++++++++++++- 2 files changed, 47 insertions(+), 5 deletions(-) diff --git a/EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverDtos.cs b/EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverDtos.cs index b1b8908a..54de38a9 100644 --- a/EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverDtos.cs +++ b/EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverDtos.cs @@ -39,8 +39,11 @@ public record ReceiverGetOrCreateCommand([Required] IEnumerable Signa /// /// Die Dokumentdaten im Byte-Array-Format. Wird verwendet, wenn das Dokument als Roh-Binärdaten bereitgestellt wird. /// -/// -/// Die Dokumentdaten im Base64-String-Format. Wird verwendet, wenn das Dokument als Base64-codierter String bereitgestellt wird. -/// -public record DocumentCreateCommand(byte[]? DataAsByte = null, string? DataAsBase64 = null); +public record DocumentCreateCommand(byte[]? DataAsByte = null) +{ + /// + /// Die Dokumentdaten im Base64-String-Format. Wird verwendet, wenn das Dokument als Base64-codierter String bereitgestellt wird. + /// + public string? DataAsBase64 { get; set; } +}; #endregion \ No newline at end of file diff --git a/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeReceiverController.cs b/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeReceiverController.cs index 9ac91445..2c021dcb 100644 --- a/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeReceiverController.cs +++ b/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeReceiverController.cs @@ -37,6 +37,8 @@ public class EnvelopeReceiverController : ControllerBase private readonly IEnvelopeReceiverExecutor _erExecutor; + private readonly IDocumentExecutor _documentExecutor; + /// /// Konstruktor für den EnvelopeReceiverController. /// @@ -46,7 +48,7 @@ public class EnvelopeReceiverController : ControllerBase /// /// /// - public EnvelopeReceiverController(ILogger logger, IEnvelopeReceiverService envelopeReceiverService, IMediator mediator, IMapper mapper, IEnvelopeExecutor envelopeExecutor, IEnvelopeReceiverExecutor erExecutor) + public EnvelopeReceiverController(ILogger logger, IEnvelopeReceiverService envelopeReceiverService, IMediator mediator, IMapper mapper, IEnvelopeExecutor envelopeExecutor, IEnvelopeReceiverExecutor erExecutor, IDocumentExecutor documentExecutor) { _logger = logger; _erService = envelopeReceiverService; @@ -54,6 +56,7 @@ public class EnvelopeReceiverController : ControllerBase _mapper = mapper; _envelopeExecutor = envelopeExecutor; _erExecutor = erExecutor; + _documentExecutor = documentExecutor; } /// @@ -211,6 +214,20 @@ public class EnvelopeReceiverController : ControllerBase #endregion #region Add document + if(request.Document.DataAsBase64 is null) + if (request.Document.DataAsByte is null) + return BadRequest("No document data is found"); + else + request.Document.DataAsBase64 = Convert.ToBase64String(request.Document.DataAsByte); + else if (request.Document.DataAsByte is not null) + return BadRequest("Document data cannot be assigned as both byte data and base64 string."); + else if (!IsBase64String(request.Document.DataAsBase64)) + return BadRequest("Document data is not a base64 string"); + + var document = await _documentExecutor.CreateDocumentAsync(request.Document.DataAsBase64, envelope.Uuid, cancel); + + if(document is null) + return StatusCode(StatusCodes.Status500InternalServerError, "Document creation is failed."); #endregion @@ -222,4 +239,26 @@ public class EnvelopeReceiverController : ControllerBase return StatusCode(StatusCodes.Status500InternalServerError); } } + + /// + /// + /// + /// + /// + public static bool IsBase64String(string input) + { + if (string.IsNullOrWhiteSpace(input)) + return false; + + try + { + Convert.FromBase64String(input); + return true; + } + catch (FormatException) + { + return false; + } + } + } \ No newline at end of file From 486b71780167ab048c99fe862c2a443cf92a6d52 Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Wed, 7 May 2025 02:13:26 +0200 Subject: [PATCH 53/94] Update signature handling and database connection management - Changed `Signature` properties from `int` to `double` for precise positioning. - Added necessary using directives in `EnvelopeReceiverController.cs` for database operations. - Introduced a private `_cnnStr` field in `EnvelopeReceiverController` to store the connection string. - Updated the constructor to accept `IOptions` for dependency injection of the connection string. - Added a SQL command block to handle document element additions using a stored procedure. - Configured the `ConnectionString` class in `Program.cs` to bind the connection string from app settings. - Created a new `ConnectionString` class to manage the connection string value. --- .../Create/CreateEnvelopeReceiverDtos.cs | 2 +- .../Controllers/EnvelopeReceiverController.cs | 47 ++++++++++++++++++- .../Models/ConnectionString.cs | 12 +++++ EnvelopeGenerator.GeneratorAPI/Program.cs | 3 ++ 4 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 EnvelopeGenerator.GeneratorAPI/Models/ConnectionString.cs diff --git a/EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverDtos.cs b/EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverDtos.cs index 54de38a9..77aa2869 100644 --- a/EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverDtos.cs +++ b/EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverDtos.cs @@ -14,7 +14,7 @@ namespace EnvelopeGenerator.Application.EnvelopeReceivers.Commands.Create; /// X-Position /// Y-Position /// Seite, auf der sie sich befindet -public record Signature([Required] int X, [Required] int Y, [Required] int Page); +public record Signature([Required] double X, [Required] double Y, [Required] int Page); /// /// DTO für Empfänger, die erstellt oder abgerufen werden sollen. diff --git a/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeReceiverController.cs b/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeReceiverController.cs index 2c021dcb..ef9b77b6 100644 --- a/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeReceiverController.cs +++ b/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeReceiverController.cs @@ -7,9 +7,13 @@ using EnvelopeGenerator.Application.EnvelopeReceivers.Commands.Create; using EnvelopeGenerator.Application.EnvelopeReceivers.Queries.Read; using EnvelopeGenerator.Application.Envelopes.Queries.ReceiverName; using EnvelopeGenerator.Domain.Entities; +using EnvelopeGenerator.GeneratorAPI.Models; using MediatR; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using Microsoft.Data.SqlClient; +using Microsoft.Extensions.Options; +using System.Data; using System.Threading.Channels; namespace EnvelopeGenerator.GeneratorAPI.Controllers; @@ -39,6 +43,8 @@ public class EnvelopeReceiverController : ControllerBase private readonly IDocumentExecutor _documentExecutor; + private readonly string _cnnStr; + /// /// Konstruktor für den EnvelopeReceiverController. /// @@ -48,7 +54,7 @@ public class EnvelopeReceiverController : ControllerBase /// /// /// - public EnvelopeReceiverController(ILogger logger, IEnvelopeReceiverService envelopeReceiverService, IMediator mediator, IMapper mapper, IEnvelopeExecutor envelopeExecutor, IEnvelopeReceiverExecutor erExecutor, IDocumentExecutor documentExecutor) + public EnvelopeReceiverController(ILogger logger, IEnvelopeReceiverService envelopeReceiverService, IMediator mediator, IMapper mapper, IEnvelopeExecutor envelopeExecutor, IEnvelopeReceiverExecutor erExecutor, IDocumentExecutor documentExecutor, IOptions csOpt) { _logger = logger; _erService = envelopeReceiverService; @@ -57,6 +63,7 @@ public class EnvelopeReceiverController : ControllerBase _envelopeExecutor = envelopeExecutor; _erExecutor = erExecutor; _documentExecutor = documentExecutor; + _cnnStr = csOpt.Value.Value; } /// @@ -228,7 +235,45 @@ public class EnvelopeReceiverController : ControllerBase if(document is null) return StatusCode(StatusCodes.Status500InternalServerError, "Document creation is failed."); + #endregion + + #region Add document element + string sql = @" + DECLARE @OUT_SUCCESS bit; + + EXEC [dbo].[PRSIG_API_ADD_DOC_RECEIVER_ELEM] + @DOC_ID = @DOC_ID, + @RECEIVER_ID = @RECEIVER_ID, + @POSITION_X = @POSITION_X, + @POSITION_Y = @POSITION_Y, + @PAGE = @PAGE, + @OUT_SUCCESS = @OUT_SUCCESS OUTPUT; + + SELECT @OUT_SUCCESS as [@OUT_SUCCESS];"; + + foreach(var rcv in res.SentRecipients) + foreach(var sign in request.Receivers.Where(r => r.EmailAddress == rcv.EmailAddress).FirstOrDefault()?.Signatures ?? Array.Empty()) + { + using (SqlConnection conn = new(_cnnStr)) + { + conn.Open(); + + using SqlCommand cmd = new SqlCommand(sql, conn); + cmd.CommandType = CommandType.Text; + + cmd.Parameters.AddWithValue("@DOC_ID", document.Id); + cmd.Parameters.AddWithValue("@RECEIVER_ID", rcv.Id); + cmd.Parameters.AddWithValue("@POSITION_X", sign.X.ToString()); + cmd.Parameters.AddWithValue("@POSITION_Y", sign.Y.ToString()); + cmd.Parameters.AddWithValue("@PAGE", sign.Page.ToString()); + using SqlDataReader reader = cmd.ExecuteReader(); + if (reader.Read()) + { + bool outSuccess = reader.GetBoolean(0); + } + } + } #endregion return Ok(res); diff --git a/EnvelopeGenerator.GeneratorAPI/Models/ConnectionString.cs b/EnvelopeGenerator.GeneratorAPI/Models/ConnectionString.cs new file mode 100644 index 00000000..0adb8e5b --- /dev/null +++ b/EnvelopeGenerator.GeneratorAPI/Models/ConnectionString.cs @@ -0,0 +1,12 @@ +namespace EnvelopeGenerator.GeneratorAPI.Models; + +/// +/// +/// +public class ConnectionString +{ + /// + /// + /// + public string Value { get; set; } = string.Empty; +} diff --git a/EnvelopeGenerator.GeneratorAPI/Program.cs b/EnvelopeGenerator.GeneratorAPI/Program.cs index 121d83a4..4c09198f 100644 --- a/EnvelopeGenerator.GeneratorAPI/Program.cs +++ b/EnvelopeGenerator.GeneratorAPI/Program.cs @@ -90,6 +90,9 @@ builder.Services.AddSwaggerGen(options => builder.Services.AddOpenApi(); // DbContext var connStr = config.GetConnectionString("Default") ?? throw new InvalidOperationException("There is no default connection string in appsettings.json."); + +builder.Services.Configure(cs => cs.Value = connStr); + builder.Services.AddDbContext(options => options.UseSqlServer(connStr)); builder.Services.AddAuthHubClient(config.GetSection("AuthClientParams")); From f6f41373320ce9d33baafe332f6265f2c29dd065 Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Wed, 7 May 2025 02:17:49 +0200 Subject: [PATCH 54/94] Add history creation functionality to EnvelopeReceiverController Updated the `EnvelopeReceiverController` to include a new region for creating history. Added a SQL command to insert a history state into the database, established a database connection, and executed the command within a `using` block. The result is read to check for success, and the method now returns an `Ok` response with the result. --- .../Controllers/EnvelopeReceiverController.cs | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeReceiverController.cs b/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeReceiverController.cs index ef9b77b6..b007e6d6 100644 --- a/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeReceiverController.cs +++ b/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeReceiverController.cs @@ -239,6 +239,8 @@ public class EnvelopeReceiverController : ControllerBase #region Add document element string sql = @" + USE [DD_ECM] + DECLARE @OUT_SUCCESS bit; EXEC [dbo].[PRSIG_API_ADD_DOC_RECEIVER_ELEM] @@ -276,6 +278,45 @@ public class EnvelopeReceiverController : ControllerBase } #endregion + #region Create history + string connectionString = "Server=YOUR_SERVER;Database=YOUR_DATABASE;Trusted_Connection=True;"; + + string sql_hist = @" + USE [DD_ECM] + + DECLARE @OUT_SUCCESS bit; + + EXEC [dbo].[PRSIG_API_ADD_HISTORY_STATE] + @ENV_UID = @ENV_UID, + @STATUS_ID = @STATUS_ID, + @USER_ID = @USER_ID, + @OUT_SUCCESS = @OUT_SUCCESS OUTPUT; + + SELECT @OUT_SUCCESS as [@OUT_SUCCESS];"; + + using (SqlConnection conn = new(connectionString)) + { + conn.Open(); + + using (SqlCommand cmd = new SqlCommand(sql_hist, conn)) + { + cmd.CommandType = CommandType.Text; + + cmd.Parameters.AddWithValue("@ENV_UID", envelope.Uuid); + cmd.Parameters.AddWithValue("@STATUS_ID", 1003); + cmd.Parameters.AddWithValue("@USER_ID", userId); + + using (SqlDataReader reader = cmd.ExecuteReader()) + { + if (reader.Read()) + { + bool outSuccess = reader.GetBoolean(0); + } + } + } + } + #endregion + return Ok(res); } catch (Exception ex) From f877910a7342b8570c1eafcba09a86842aad00df Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Wed, 7 May 2025 02:18:33 +0200 Subject: [PATCH 55/94] Bump version to 1.2.1 in project file Updated the `EnvelopeGenerator.GeneratorAPI.csproj` file to reflect the new versioning information, changing `Version`, `FileVersion`, and `AssemblyVersion` from `1.2.0` to `1.2.1`. --- .../EnvelopeGenerator.GeneratorAPI.csproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/EnvelopeGenerator.GeneratorAPI/EnvelopeGenerator.GeneratorAPI.csproj b/EnvelopeGenerator.GeneratorAPI/EnvelopeGenerator.GeneratorAPI.csproj index 6e922bad..d2c598e0 100644 --- a/EnvelopeGenerator.GeneratorAPI/EnvelopeGenerator.GeneratorAPI.csproj +++ b/EnvelopeGenerator.GeneratorAPI/EnvelopeGenerator.GeneratorAPI.csproj @@ -10,9 +10,9 @@ Digital Data GmbH Digital Data GmbH EnvelopeGenerator.GeneratorAPI - 1.2.0 - 1.2.0 - 1.2.0 + 1.2.1 + 1.2.1 + 1.2.1 Copyright © 2025 Digital Data GmbH. All rights reserved. bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml From 49ccd9fef27c03e584e36789f16cfa0cef80c112 Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Wed, 7 May 2025 10:22:21 +0200 Subject: [PATCH 56/94] Add using directives and update CreateAsync method - Added `System.ComponentModel` and `System.ComponentModel.DataAnnotations.Schema` using directives for enhanced data annotation support. - Applied the `[NonAction]` attribute to the `CreateAsync` method to prevent it from being treated as an action method by the ASP.NET MVC framework. --- .../Controllers/EnvelopeController.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeController.cs b/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeController.cs index d8f618c0..6daa2d85 100644 --- a/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeController.cs +++ b/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeController.cs @@ -6,6 +6,8 @@ using MediatR; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Newtonsoft.Json; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations.Schema; namespace EnvelopeGenerator.GeneratorAPI.Controllers; @@ -99,6 +101,7 @@ public class EnvelopeController : ControllerBase /// /// /// + [NonAction] [Authorize] [HttpPost] public async Task CreateAsync([FromQuery] CreateEnvelopeCommand envelope) From 55b01cf3962eb79b190111b38fca101246cd4eb9 Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Wed, 7 May 2025 12:04:01 +0200 Subject: [PATCH 57/94] Refactor command records and simplify document handling - Updated `ReceiverGetOrCreateCommand` to include a required `IEnumerable`. - Modified `DocumentCreateCommand` to remove `DataAsByte` and enforce a required `DataAsBase64` property with immutability. - Cleaned up SQL command formatting in `EnvelopeReceiverAddReadSQL`. - Simplified document validation logic in `EnvelopeReceiverController` by removing redundant checks. - Changed hardcoded connection string to a variable `_cnnStr` for improved security and flexibility. --- .../Commands/Create/CreateEnvelopeReceiverDtos.cs | 8 +++----- .../SQL/EnvelopeReceiverAddReadSQL.cs | 11 +++++------ .../Controllers/EnvelopeReceiverController.cs | 14 +------------- 3 files changed, 9 insertions(+), 24 deletions(-) diff --git a/EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverDtos.cs b/EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverDtos.cs index 77aa2869..d26354cd 100644 --- a/EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverDtos.cs +++ b/EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverDtos.cs @@ -36,14 +36,12 @@ public record ReceiverGetOrCreateCommand([Required] IEnumerable Signa /// /// DTO zum Erstellen eines Dokuments. /// -/// -/// Die Dokumentdaten im Byte-Array-Format. Wird verwendet, wenn das Dokument als Roh-Binärdaten bereitgestellt wird. -/// -public record DocumentCreateCommand(byte[]? DataAsByte = null) +public record DocumentCreateCommand() { /// /// Die Dokumentdaten im Base64-String-Format. Wird verwendet, wenn das Dokument als Base64-codierter String bereitgestellt wird. /// - public string? DataAsBase64 { get; set; } + [Required] + public required string DataAsBase64 { get; init; } }; #endregion \ No newline at end of file diff --git a/EnvelopeGenerator.Application/SQL/EnvelopeReceiverAddReadSQL.cs b/EnvelopeGenerator.Application/SQL/EnvelopeReceiverAddReadSQL.cs index b389a4c4..c065accd 100644 --- a/EnvelopeGenerator.Application/SQL/EnvelopeReceiverAddReadSQL.cs +++ b/EnvelopeGenerator.Application/SQL/EnvelopeReceiverAddReadSQL.cs @@ -13,15 +13,14 @@ public class EnvelopeReceiverAddReadSQL : ISQL /// /// public string Raw => @" - USE [DD_ECM] DECLARE @OUT_RECEIVER_ID int EXEC [dbo].[PRSIG_API_CREATE_RECEIVER] - @ENV_UID = @ENV_UID, - @EMAIL_ADRESS = @EMAIL_ADRESS, - @SALUTATION = @SALUTATION, - @PHONE = @PHONE, - @OUT_RECEIVER_ID = @OUT_RECEIVER_ID OUTPUT + @ENV_UID, + @EMAIL_ADRESS, + @SALUTATION, + @PHONE, + @OUT_RECEIVER_ID OUTPUT SELECT TOP(1) * FROM TBSIG_ENVELOPE_RECEIVER diff --git a/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeReceiverController.cs b/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeReceiverController.cs index b007e6d6..62127f4a 100644 --- a/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeReceiverController.cs +++ b/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeReceiverController.cs @@ -221,16 +221,6 @@ public class EnvelopeReceiverController : ControllerBase #endregion #region Add document - if(request.Document.DataAsBase64 is null) - if (request.Document.DataAsByte is null) - return BadRequest("No document data is found"); - else - request.Document.DataAsBase64 = Convert.ToBase64String(request.Document.DataAsByte); - else if (request.Document.DataAsByte is not null) - return BadRequest("Document data cannot be assigned as both byte data and base64 string."); - else if (!IsBase64String(request.Document.DataAsBase64)) - return BadRequest("Document data is not a base64 string"); - var document = await _documentExecutor.CreateDocumentAsync(request.Document.DataAsBase64, envelope.Uuid, cancel); if(document is null) @@ -279,8 +269,6 @@ public class EnvelopeReceiverController : ControllerBase #endregion #region Create history - string connectionString = "Server=YOUR_SERVER;Database=YOUR_DATABASE;Trusted_Connection=True;"; - string sql_hist = @" USE [DD_ECM] @@ -294,7 +282,7 @@ public class EnvelopeReceiverController : ControllerBase SELECT @OUT_SUCCESS as [@OUT_SUCCESS];"; - using (SqlConnection conn = new(connectionString)) + using (SqlConnection conn = new(_cnnStr)) { conn.Open(); From 06d25b6f5b46f5ca6a65abd50111c1ca9a8e3906 Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Wed, 7 May 2025 13:09:59 +0200 Subject: [PATCH 58/94] Refactor SQL handling in EnvelopeGenerator application - Added `System.Data` using directive in `EnvelopeCreateReadSQL.cs`. - Updated SQL command strings to use parameter placeholders. - Corrected method name from `CreateParmas` to `CreateParams` and added output parameter `@OutUid`. - Made similar updates in `EnvelopeReceiverAddReadSQL.cs`. - Introduced `ParamsExtensions` class with `ToSqlParam` method for converting .NET objects to SQL-safe parameter strings. --- .../SQL/EnvelopeCreateReadSQL.cs | 17 +++++------ .../SQL/EnvelopeReceiverAddReadSQL.cs | 10 +++---- .../SQL/ParamsExtensions.cs | 28 +++++++++++++++++++ 3 files changed, 42 insertions(+), 13 deletions(-) create mode 100644 EnvelopeGenerator.Application/SQL/ParamsExtensions.cs diff --git a/EnvelopeGenerator.Application/SQL/EnvelopeCreateReadSQL.cs b/EnvelopeGenerator.Application/SQL/EnvelopeCreateReadSQL.cs index 449eb9d2..40b05e68 100644 --- a/EnvelopeGenerator.Application/SQL/EnvelopeCreateReadSQL.cs +++ b/EnvelopeGenerator.Application/SQL/EnvelopeCreateReadSQL.cs @@ -1,6 +1,7 @@ using Dapper; using EnvelopeGenerator.Application.Contracts.SQLExecutor; using EnvelopeGenerator.Domain.Entities; +using System.Data; namespace EnvelopeGenerator.Application.SQL; @@ -10,18 +11,17 @@ namespace EnvelopeGenerator.Application.SQL; public class EnvelopeCreateReadSQL : ISQL { /// - /// + /// USER_ID, TITLE, TFAEnabled, MESSAGE /// public string Raw => @" - USE [DD_ECM]; DECLARE @OUT_UID varchar(36); EXEC [dbo].[PRSIG_API_CREATE_ENVELOPE] - @USER_ID = @UserId, - @TITLE = @Title, - @TFAEnabled = @TfaEnabled, - @MESSAGE = @Message, - @OUT_UID = @OUT_UID OUTPUT; + {0}, + {1}, + {2}, + {3}, + @OUT_UID OUTPUT; SELECT TOP(1) * FROM [dbo].[TBSIG_ENVELOPE] @@ -36,13 +36,14 @@ public class EnvelopeCreateReadSQL : ISQL /// /// /// - public static DynamicParameters CreateParmas(int userId, string title = "", string message = "", bool tfaEnabled = false) + public static DynamicParameters CreateParams(int userId, string title = "", string message = "", bool tfaEnabled = false) { var parameters = new DynamicParameters(); parameters.Add("@UserId", userId); parameters.Add("@Title", title); parameters.Add("@TfaEnabled", tfaEnabled ? 1 : 0); parameters.Add("@Message", message); + parameters.Add("@OutUid", dbType: DbType.String, size: 36, direction: ParameterDirection.Output); return parameters; } } \ No newline at end of file diff --git a/EnvelopeGenerator.Application/SQL/EnvelopeReceiverAddReadSQL.cs b/EnvelopeGenerator.Application/SQL/EnvelopeReceiverAddReadSQL.cs index c065accd..1c499858 100644 --- a/EnvelopeGenerator.Application/SQL/EnvelopeReceiverAddReadSQL.cs +++ b/EnvelopeGenerator.Application/SQL/EnvelopeReceiverAddReadSQL.cs @@ -10,16 +10,16 @@ namespace EnvelopeGenerator.Application.SQL; public class EnvelopeReceiverAddReadSQL : ISQL { /// - /// + /// ENV_UID, EMAIL_ADRESS, SALUTATION, PHONE, /// public string Raw => @" DECLARE @OUT_RECEIVER_ID int EXEC [dbo].[PRSIG_API_CREATE_RECEIVER] - @ENV_UID, - @EMAIL_ADRESS, - @SALUTATION, - @PHONE, + {0}, + {1}, + {2}, + {3}, @OUT_RECEIVER_ID OUTPUT SELECT TOP(1) * diff --git a/EnvelopeGenerator.Application/SQL/ParamsExtensions.cs b/EnvelopeGenerator.Application/SQL/ParamsExtensions.cs new file mode 100644 index 00000000..2558eb5e --- /dev/null +++ b/EnvelopeGenerator.Application/SQL/ParamsExtensions.cs @@ -0,0 +1,28 @@ +namespace EnvelopeGenerator.Application.SQL; + +/// +/// Extension method for converting objects to SQL parameter strings. +/// +public static class ParamsExtensions +{ + /// + /// Converts a .NET object to its corresponding SQL-safe parameter string. + /// + /// The object to convert. + /// A string representing the SQL parameter. + public static string ToSqlParam(this object? obj) + { + if (obj is null) + return "NULL"; + else if (obj is string strVal) + return $"'{strVal}'"; + else if (obj is bool boolVal) + return boolVal ? "1" : "0"; + else if (obj is double doubleVal) + return $"'{doubleVal}'"; + else if (obj is int intVal) + return intVal.ToString(); + else + throw new NotSupportedException($"Type '{obj.GetType().FullName}' is not supported for SQL parameter conversion."); + } +} From 38d05850e3116c06cf70d12e127584761396f4d3 Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Wed, 7 May 2025 13:11:52 +0200 Subject: [PATCH 59/94] Refactor CreateEnvelopeAsync to use string formatting Updated the `CreateEnvelopeAsync` method in the `EnvelopeExecutor` class to handle SQL parameters by directly formatting the SQL string with `string.Format`, replacing the previous parameterized query approach. This change enhances readability but may introduce potential SQL injection risks if not managed carefully. --- EnvelopeGenerator.Infrastructure/Executor/EnvelopeExecutor.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/EnvelopeGenerator.Infrastructure/Executor/EnvelopeExecutor.cs b/EnvelopeGenerator.Infrastructure/Executor/EnvelopeExecutor.cs index 0933997c..945f1ac7 100644 --- a/EnvelopeGenerator.Infrastructure/Executor/EnvelopeExecutor.cs +++ b/EnvelopeGenerator.Infrastructure/Executor/EnvelopeExecutor.cs @@ -23,9 +23,9 @@ public class EnvelopeExecutor : SQLExecutor, IEnvelopeExecutor { using var connection = new SqlConnection(Params.ConnectionString); var sql = Provider.GetRequiredService(); + var formattedSql = string.Format(sql.Raw, userId.ToSqlParam(), title.ToSqlParam(), tfaEnabled.ToSqlParam(), message.ToSqlParam()); await connection.OpenAsync(cancellation); - var parameters = EnvelopeCreateReadSQL.CreateParmas(userId, title, message, tfaEnabled); - var envelopes = await connection.QueryAsync(sql.Raw, parameters); + var envelopes = await connection.QueryAsync(formattedSql); var envelope = envelopes.FirstOrDefault() ?? throw new InvalidOperationException($"Envelope creation failed. Parameters:" + $"userId={userId}, title='{title}', message='{message}', tfaEnabled={tfaEnabled}."); ; From 5fc689ee4d65c54e7db3ea049215c8088f767f02 Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Wed, 7 May 2025 13:14:40 +0200 Subject: [PATCH 60/94] Refactor SQL query execution in AddEnvelopeReceiverAsync Updated the SQL query execution in the EnvelopeReceiverExecutor class to use a formatted SQL string directly with parameters instead of a parameterized query method. This change simplifies the execution but may introduce SQL injection risks and affect performance. --- .../Executor/EnvelopeReceiverExecutor.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/EnvelopeGenerator.Infrastructure/Executor/EnvelopeReceiverExecutor.cs b/EnvelopeGenerator.Infrastructure/Executor/EnvelopeReceiverExecutor.cs index 2dcbf8f6..1ff26eff 100644 --- a/EnvelopeGenerator.Infrastructure/Executor/EnvelopeReceiverExecutor.cs +++ b/EnvelopeGenerator.Infrastructure/Executor/EnvelopeReceiverExecutor.cs @@ -23,8 +23,9 @@ public class EnvelopeReceiverExecutor: SQLExecutor, IEnvelopeReceiverExecutor { using var connection = new SqlConnection(Params.ConnectionString); var sql = Provider.GetRequiredService(); + var formattedSql = string.Format(sql.Raw, envelope_uuid.ToSqlParam(), emailAddress.ToSqlParam(), salutation.ToSqlParam(), phone.ToSqlParam()); await connection.OpenAsync(cancellation); - var envelopeReceivers = await connection.QueryAsync(sql.Raw, EnvelopeReceiverAddReadSQL.CreateParameters(envelope_uuid, emailAddress, salutation, phone)); + var envelopeReceivers = await connection.QueryAsync(formattedSql); var er = envelopeReceivers.FirstOrDefault(); if (er is null) From a7814402520acf2708fbb89e0f28f3c6432f42c0 Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Wed, 7 May 2025 13:15:15 +0200 Subject: [PATCH 61/94] Refactor EnvelopeReceiverExecutor dependencies Added the namespace `EnvelopeGenerator.Application.Contracts.Repositories` and removed the unused `Microsoft.Extensions.Logging` import in `EnvelopeReceiverExecutor.cs` to streamline code dependencies. --- .../Executor/EnvelopeReceiverExecutor.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/EnvelopeGenerator.Infrastructure/Executor/EnvelopeReceiverExecutor.cs b/EnvelopeGenerator.Infrastructure/Executor/EnvelopeReceiverExecutor.cs index 1ff26eff..71f37495 100644 --- a/EnvelopeGenerator.Infrastructure/Executor/EnvelopeReceiverExecutor.cs +++ b/EnvelopeGenerator.Infrastructure/Executor/EnvelopeReceiverExecutor.cs @@ -5,7 +5,6 @@ using EnvelopeGenerator.Application.SQL; using EnvelopeGenerator.Domain.Entities; using Microsoft.Data.SqlClient; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; namespace EnvelopeGenerator.Infrastructure.Executor; From ad855b77cdd847ea53f68570f42b34c529886be7 Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Wed, 7 May 2025 13:21:17 +0200 Subject: [PATCH 62/94] Refactor SQL execution in DocumentCreateReadSQL Changed namespace for DocumentCreateReadSQL and updated SQL command to use formatted strings for parameters. Simplified DocumentExecutor to directly use the formatted SQL, enhancing clarity and reducing complexity in parameter handling. --- EnvelopeGenerator.Application/SQL/DocumentCreateReadSQL.cs | 6 +++--- .../Executor/DocumentExecutor.cs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/EnvelopeGenerator.Application/SQL/DocumentCreateReadSQL.cs b/EnvelopeGenerator.Application/SQL/DocumentCreateReadSQL.cs index 02b4ec73..f4f1ffb1 100644 --- a/EnvelopeGenerator.Application/SQL/DocumentCreateReadSQL.cs +++ b/EnvelopeGenerator.Application/SQL/DocumentCreateReadSQL.cs @@ -10,17 +10,17 @@ namespace EnvelopeGenerator.Application.SQL; public class DocumentCreateReadSQL : ISQL { /// - /// + /// Base64, OUT_UID /// public string Raw => @" USE [DD_ECM] DECLARE @BYTE_DATA1 as VARBINARY(MAX) - SET @BYTE_DATA1 = CONVERT(VARBINARY(MAX),'@Base64') + SET @BYTE_DATA1 = CONVERT(VARBINARY(MAX),{0}) DECLARE @OUT_DOCID int EXEC [dbo].[PRSIG_API_ADD_DOC] - @ENV_UID = @OUT_UID, + @ENV_UID = {1}, @BYTE_DATA = @BYTE_DATA1, @OUT_DOCID = @OUT_DOCID OUTPUT diff --git a/EnvelopeGenerator.Infrastructure/Executor/DocumentExecutor.cs b/EnvelopeGenerator.Infrastructure/Executor/DocumentExecutor.cs index 7143b81e..aec62eed 100644 --- a/EnvelopeGenerator.Infrastructure/Executor/DocumentExecutor.cs +++ b/EnvelopeGenerator.Infrastructure/Executor/DocumentExecutor.cs @@ -18,9 +18,9 @@ public class DocumentExecutor : SQLExecutor, IDocumentExecutor { using var connection = new SqlConnection(Params.ConnectionString); var sql = Provider.GetRequiredService(); + var formattedSql = string.Format(sql.Raw, base64, envelope_uuid); await connection.OpenAsync(cancellation); - var parameters = DocumentCreateReadSQL.CreateParmas(base64, envelope_uuid); - var documents = await connection.QueryAsync(sql.Raw, parameters); + var documents = await connection.QueryAsync(formattedSql); return documents.FirstOrDefault() ?? throw new InvalidOperationException($"Document creation failed. Parameters:" + $"base64={base64}, envelope_uuid='{envelope_uuid}'."); From 6bdf0d52206e66073a4df6360dd196d988465829 Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Wed, 7 May 2025 13:28:06 +0200 Subject: [PATCH 63/94] Enhance SQL parameter handling in CreateDocumentAsync Updated the CreateDocumentAsync method in the DocumentExecutor class to use ToSqlParam() for formatting SQL query parameters. This change improves security by preventing potential SQL injection vulnerabilities associated with direct variable insertion into the SQL string. --- EnvelopeGenerator.Infrastructure/Executor/DocumentExecutor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EnvelopeGenerator.Infrastructure/Executor/DocumentExecutor.cs b/EnvelopeGenerator.Infrastructure/Executor/DocumentExecutor.cs index aec62eed..0b42a766 100644 --- a/EnvelopeGenerator.Infrastructure/Executor/DocumentExecutor.cs +++ b/EnvelopeGenerator.Infrastructure/Executor/DocumentExecutor.cs @@ -18,7 +18,7 @@ public class DocumentExecutor : SQLExecutor, IDocumentExecutor { using var connection = new SqlConnection(Params.ConnectionString); var sql = Provider.GetRequiredService(); - var formattedSql = string.Format(sql.Raw, base64, envelope_uuid); + var formattedSql = string.Format(sql.Raw, base64.ToSqlParam(), envelope_uuid.ToSqlParam()); await connection.OpenAsync(cancellation); var documents = await connection.QueryAsync(formattedSql); return documents.FirstOrDefault() From 1875acb7b5d085dc6cb3e95e477f445441eda623 Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Wed, 7 May 2025 14:10:20 +0200 Subject: [PATCH 64/94] Refactor SQL queries and data mappings - Updated SQL query in `EnvelopeReceiverAddReadSQL.cs` to select specific columns (`ENVELOPE_ID` and `RECEIVER_ID`) for improved performance and clarity. - Changed mapping of `SentRecipients` in `EnvelopeReceiverController.cs` to expect a single `ReceiverReadDto` instead of a collection, reflecting a change in data handling. - Modified `QueryAsync` in `EnvelopeReceiverExecutor.cs` to remove the generic type parameter, allowing for more flexible data handling. --- .../SQL/EnvelopeReceiverAddReadSQL.cs | 4 ++-- .../Controllers/EnvelopeReceiverController.cs | 2 +- .../Executor/EnvelopeReceiverExecutor.cs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/EnvelopeGenerator.Application/SQL/EnvelopeReceiverAddReadSQL.cs b/EnvelopeGenerator.Application/SQL/EnvelopeReceiverAddReadSQL.cs index 1c499858..73fc5de2 100644 --- a/EnvelopeGenerator.Application/SQL/EnvelopeReceiverAddReadSQL.cs +++ b/EnvelopeGenerator.Application/SQL/EnvelopeReceiverAddReadSQL.cs @@ -22,8 +22,8 @@ public class EnvelopeReceiverAddReadSQL : ISQL {3}, @OUT_RECEIVER_ID OUTPUT - SELECT TOP(1) * - FROM TBSIG_ENVELOPE_RECEIVER + SELECT TOP(1) [ENVELOPE_ID] As EnvelopeId, [RECEIVER_ID] As ReceiverId + FROM [dbo].[TBSIG_ENVELOPE_RECEIVER] WHERE [GUID] = @OUT_RECEIVER_ID; "; diff --git a/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeReceiverController.cs b/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeReceiverController.cs index 62127f4a..dfeb2628 100644 --- a/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeReceiverController.cs +++ b/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeReceiverController.cs @@ -217,7 +217,7 @@ public class EnvelopeReceiverController : ControllerBase var res = _mapper.Map(envelope); res.UnsentRecipients = unsentRecipients; - res.SentRecipients = _mapper.Map>(sentRecipients); + res.SentRecipients = _mapper.Map>(sentRecipients); #endregion #region Add document diff --git a/EnvelopeGenerator.Infrastructure/Executor/EnvelopeReceiverExecutor.cs b/EnvelopeGenerator.Infrastructure/Executor/EnvelopeReceiverExecutor.cs index 71f37495..ddcd9d28 100644 --- a/EnvelopeGenerator.Infrastructure/Executor/EnvelopeReceiverExecutor.cs +++ b/EnvelopeGenerator.Infrastructure/Executor/EnvelopeReceiverExecutor.cs @@ -24,7 +24,7 @@ public class EnvelopeReceiverExecutor: SQLExecutor, IEnvelopeReceiverExecutor var sql = Provider.GetRequiredService(); var formattedSql = string.Format(sql.Raw, envelope_uuid.ToSqlParam(), emailAddress.ToSqlParam(), salutation.ToSqlParam(), phone.ToSqlParam()); await connection.OpenAsync(cancellation); - var envelopeReceivers = await connection.QueryAsync(formattedSql); + var envelopeReceivers = await connection.QueryAsync(formattedSql); var er = envelopeReceivers.FirstOrDefault(); if (er is null) From c8f21be9051cbeb1ff7ddd5acf6c8debc54fb51e Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Wed, 7 May 2025 14:26:50 +0200 Subject: [PATCH 65/94] Rename recipient properties for consistency Updated `CreateEnvelopeReceiverResponse` and `EnvelopeReceiverController` to rename `SentRecipients` to `SentReceiver` and `UnsentRecipients` to `UnsentReceivers`. Adjusted corresponding lists and mappings to ensure uniformity in naming conventions across the codebase. --- .../Create/CreateEnvelopeReceiverCommandHandler.cs | 4 ++-- .../Create/CreateEnvelopeReceiverResponse.cs | 4 ++-- .../Controllers/EnvelopeReceiverController.cs | 14 +++++++------- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverCommandHandler.cs b/EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverCommandHandler.cs index 9ba2098e..3ab6fda1 100644 --- a/EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverCommandHandler.cs +++ b/EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverCommandHandler.cs @@ -60,8 +60,8 @@ public class CreateEnvelopeReceiverCommandHandler : IRequestHandler(envelope); - res.UnsentRecipients = unsentRecipients; - res.SentRecipients = _mapper.Map>(sentRecipients); + res.UnsentReceivers = unsentRecipients; + res.SentReceiver = _mapper.Map>(sentRecipients); return res; } } diff --git a/EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverResponse.cs b/EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverResponse.cs index b20b72ca..251e6ceb 100644 --- a/EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverResponse.cs +++ b/EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverResponse.cs @@ -30,10 +30,10 @@ public record CreateEnvelopeReceiverResponse : CreateEnvelopeResponse /// /// /// - public IEnumerable SentRecipients { get; set; } = new List(); + public IEnumerable SentReceiver { get; set; } = new List(); /// /// /// - public IEnumerable UnsentRecipients { get; set; } = new List(); + public IEnumerable UnsentReceivers { get; set; } = new List(); } diff --git a/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeReceiverController.cs b/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeReceiverController.cs index dfeb2628..86411eea 100644 --- a/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeReceiverController.cs +++ b/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeReceiverController.cs @@ -202,22 +202,22 @@ public class EnvelopeReceiverController : ControllerBase #endregion #region Add receivers - List sentRecipients = new(); - List unsentRecipients = new(); + List sentReceivers = new(); + List unsentReceivers = new(); foreach (var receiver in request.Receivers) { var envelopeReceiver = await _erExecutor.AddEnvelopeReceiverAsync(envelope.Uuid, receiver.EmailAddress, receiver.Salution, receiver.PhoneNumber, cancel); if (envelopeReceiver is null) - unsentRecipients.Add(receiver); + unsentReceivers.Add(receiver); else - sentRecipients.Add(envelopeReceiver); + sentReceivers.Add(envelopeReceiver); } var res = _mapper.Map(envelope); - res.UnsentRecipients = unsentRecipients; - res.SentRecipients = _mapper.Map>(sentRecipients); + res.UnsentReceivers = unsentReceivers; + res.SentReceiver = _mapper.Map>(sentReceivers.Select(er => er.Receiver)); #endregion #region Add document @@ -243,7 +243,7 @@ public class EnvelopeReceiverController : ControllerBase SELECT @OUT_SUCCESS as [@OUT_SUCCESS];"; - foreach(var rcv in res.SentRecipients) + foreach(var rcv in res.SentReceiver) foreach(var sign in request.Receivers.Where(r => r.EmailAddress == rcv.EmailAddress).FirstOrDefault()?.Signatures ?? Array.Empty()) { using (SqlConnection conn = new(_cnnStr)) From 2ba7f41a2164d562cfc1f54201bad5b504387f03 Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Wed, 7 May 2025 14:27:09 +0200 Subject: [PATCH 66/94] Remove SQL command string from DocumentCreateReadSQL This commit removes a SQL command string from the `Raw` property in the `DocumentCreateReadSQL` class. The changes include the deletion of a `USE` statement for the `[DD_ECM]` database, variable declarations, and the execution of a stored procedure. This alters the structure and execution of SQL commands within the class. --- EnvelopeGenerator.Application/SQL/DocumentCreateReadSQL.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/EnvelopeGenerator.Application/SQL/DocumentCreateReadSQL.cs b/EnvelopeGenerator.Application/SQL/DocumentCreateReadSQL.cs index f4f1ffb1..b243b0ba 100644 --- a/EnvelopeGenerator.Application/SQL/DocumentCreateReadSQL.cs +++ b/EnvelopeGenerator.Application/SQL/DocumentCreateReadSQL.cs @@ -13,8 +13,6 @@ public class DocumentCreateReadSQL : ISQL /// Base64, OUT_UID /// public string Raw => @" - USE [DD_ECM] - DECLARE @BYTE_DATA1 as VARBINARY(MAX) SET @BYTE_DATA1 = CONVERT(VARBINARY(MAX),{0}) DECLARE @OUT_DOCID int From 645153113c6e2430585b193d2bf3f7195844aeaf Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Wed, 7 May 2025 14:33:07 +0200 Subject: [PATCH 67/94] Update SQL parameters in DocumentCreateReadSQL class Modified the parameters for the stored procedure `[dbo].[PRSIG_API_ADD_DOC]` in the `DocumentCreateReadSQL` class. Replaced `@BYTE_DATA` with `@BYTE_DATA1`, repositioned `@ENV_UID`, and ensured `@OUT_DOCID` is explicitly marked as an output parameter. --- EnvelopeGenerator.Application/SQL/DocumentCreateReadSQL.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/EnvelopeGenerator.Application/SQL/DocumentCreateReadSQL.cs b/EnvelopeGenerator.Application/SQL/DocumentCreateReadSQL.cs index b243b0ba..7fec8262 100644 --- a/EnvelopeGenerator.Application/SQL/DocumentCreateReadSQL.cs +++ b/EnvelopeGenerator.Application/SQL/DocumentCreateReadSQL.cs @@ -18,9 +18,9 @@ public class DocumentCreateReadSQL : ISQL DECLARE @OUT_DOCID int EXEC [dbo].[PRSIG_API_ADD_DOC] - @ENV_UID = {1}, - @BYTE_DATA = @BYTE_DATA1, - @OUT_DOCID = @OUT_DOCID OUTPUT + {1}, + @BYTE_DATA1, + @OUT_DOCID OUTPUT SELECT TOP(1) * FROM [dbo].[TBSIG_ENVELOPE_DOCUMENT] From 5f8e8deb5b27d288e70c2e9c00037a7bb8cb8716 Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Wed, 7 May 2025 15:03:27 +0200 Subject: [PATCH 68/94] Refactor SQL parameter handling in EnvelopeReceiverController Updated the SQL command execution in `EnvelopeReceiverController.cs` to use a formatted SQL string with `string.Format` instead of parameterized commands. This change simplifies command preparation but may increase the risk of SQL injection if input values are not properly sanitized. --- .../Controllers/EnvelopeReceiverController.cs | 25 ++++++++----------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeReceiverController.cs b/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeReceiverController.cs index 86411eea..f16c9030 100644 --- a/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeReceiverController.cs +++ b/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeReceiverController.cs @@ -228,18 +228,17 @@ public class EnvelopeReceiverController : ControllerBase #endregion #region Add document element + // @DOC_ID, @RECEIVER_ID, @POSITION_X, @POSITION_Y, @PAGE string sql = @" - USE [DD_ECM] - DECLARE @OUT_SUCCESS bit; EXEC [dbo].[PRSIG_API_ADD_DOC_RECEIVER_ELEM] - @DOC_ID = @DOC_ID, - @RECEIVER_ID = @RECEIVER_ID, - @POSITION_X = @POSITION_X, - @POSITION_Y = @POSITION_Y, - @PAGE = @PAGE, - @OUT_SUCCESS = @OUT_SUCCESS OUTPUT; + {0}, + {1}, + {2}, + {3}, + {4}, + @OUT_SUCCESS OUTPUT; SELECT @OUT_SUCCESS as [@OUT_SUCCESS];"; @@ -250,14 +249,10 @@ public class EnvelopeReceiverController : ControllerBase { conn.Open(); - using SqlCommand cmd = new SqlCommand(sql, conn); - cmd.CommandType = CommandType.Text; + var formattedSQL = string.Format(sql, document.Id, rcv.Id, sign.X, sign.Y, sign.Page); - cmd.Parameters.AddWithValue("@DOC_ID", document.Id); - cmd.Parameters.AddWithValue("@RECEIVER_ID", rcv.Id); - cmd.Parameters.AddWithValue("@POSITION_X", sign.X.ToString()); - cmd.Parameters.AddWithValue("@POSITION_Y", sign.Y.ToString()); - cmd.Parameters.AddWithValue("@PAGE", sign.Page.ToString()); + using SqlCommand cmd = new SqlCommand(formattedSQL, conn); + cmd.CommandType = CommandType.Text; using SqlDataReader reader = cmd.ExecuteReader(); if (reader.Read()) From 9a71d2b805eb3e983d722ee24030ec6f9f024f70 Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Wed, 7 May 2025 15:09:00 +0200 Subject: [PATCH 69/94] Enhance SQL handling in EnvelopeReceiverController - Added using directive for EnvelopeGenerator.Application.SQL. - Updated SQL command formatting to use ToSqlParam() for improved security against SQL injection. - Modified history creation SQL command to use string interpolation for parameters. - Removed explicit parameter addition, streamlining SQL parameter handling. --- .../Controllers/EnvelopeReceiverController.cs | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeReceiverController.cs b/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeReceiverController.cs index f16c9030..d4df62db 100644 --- a/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeReceiverController.cs +++ b/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeReceiverController.cs @@ -6,6 +6,7 @@ using EnvelopeGenerator.Application.DTOs.Receiver; using EnvelopeGenerator.Application.EnvelopeReceivers.Commands.Create; using EnvelopeGenerator.Application.EnvelopeReceivers.Queries.Read; using EnvelopeGenerator.Application.Envelopes.Queries.ReceiverName; +using EnvelopeGenerator.Application.SQL; using EnvelopeGenerator.Domain.Entities; using EnvelopeGenerator.GeneratorAPI.Models; using MediatR; @@ -249,7 +250,7 @@ public class EnvelopeReceiverController : ControllerBase { conn.Open(); - var formattedSQL = string.Format(sql, document.Id, rcv.Id, sign.X, sign.Y, sign.Page); + var formattedSQL = string.Format(sql, document.Id.ToSqlParam(), rcv.Id.ToSqlParam(), sign.X.ToSqlParam(), sign.Y.ToSqlParam(), sign.Page.ToSqlParam()); using SqlCommand cmd = new SqlCommand(formattedSQL, conn); cmd.CommandType = CommandType.Text; @@ -264,31 +265,28 @@ public class EnvelopeReceiverController : ControllerBase #endregion #region Create history + // ENV_UID, STATUS_ID, USER_ID, string sql_hist = @" USE [DD_ECM] DECLARE @OUT_SUCCESS bit; EXEC [dbo].[PRSIG_API_ADD_HISTORY_STATE] - @ENV_UID = @ENV_UID, - @STATUS_ID = @STATUS_ID, - @USER_ID = @USER_ID, - @OUT_SUCCESS = @OUT_SUCCESS OUTPUT; + {0}, + {1}, + {2}, + @OUT_SUCCESS OUTPUT; SELECT @OUT_SUCCESS as [@OUT_SUCCESS];"; using (SqlConnection conn = new(_cnnStr)) { conn.Open(); - - using (SqlCommand cmd = new SqlCommand(sql_hist, conn)) + var formattedSQL_hist = string.Format(sql_hist, envelope.Uuid.ToSqlParam(), 1003.ToSqlParam(), userId.ToSqlParam()); + using (SqlCommand cmd = new SqlCommand(formattedSQL_hist, conn)) { cmd.CommandType = CommandType.Text; - cmd.Parameters.AddWithValue("@ENV_UID", envelope.Uuid); - cmd.Parameters.AddWithValue("@STATUS_ID", 1003); - cmd.Parameters.AddWithValue("@USER_ID", userId); - using (SqlDataReader reader = cmd.ExecuteReader()) { if (reader.Read()) From 519df50404c45609cc65577421dc75ef023972c6 Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Wed, 7 May 2025 15:35:14 +0200 Subject: [PATCH 70/94] Add culture-specific formatting for SQL parameters Updated ParamsExtensions to include System.Globalization for culture-invariant formatting of double values. This change ensures proper SQL parameterization by avoiding issues with decimal separators across different cultures. --- EnvelopeGenerator.Application/SQL/ParamsExtensions.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/EnvelopeGenerator.Application/SQL/ParamsExtensions.cs b/EnvelopeGenerator.Application/SQL/ParamsExtensions.cs index 2558eb5e..9f8ad1ec 100644 --- a/EnvelopeGenerator.Application/SQL/ParamsExtensions.cs +++ b/EnvelopeGenerator.Application/SQL/ParamsExtensions.cs @@ -1,4 +1,6 @@ -namespace EnvelopeGenerator.Application.SQL; +using System.Globalization; + +namespace EnvelopeGenerator.Application.SQL; /// /// Extension method for converting objects to SQL parameter strings. @@ -19,7 +21,7 @@ public static class ParamsExtensions else if (obj is bool boolVal) return boolVal ? "1" : "0"; else if (obj is double doubleVal) - return $"'{doubleVal}'"; + return $"'{doubleVal.ToString(CultureInfo.InvariantCulture)}'"; else if (obj is int intVal) return intVal.ToString(); else From b15616cf53c5a93bd8ddf08bef3c1b3370e6422e Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Wed, 7 May 2025 16:07:43 +0200 Subject: [PATCH 71/94] Refactor email template command and handler Introduced `ResetEmailTemplateCommand` and `ResetEmailTemplateCommandHandler`, replacing the previous `ResetEnvelopeTemplateCommand` and its handler. Updated the controller to utilize the new command, ensuring consistent mapping and command sending. --- ...elopeTemplateCommand.cs => ResetEmailTemplateCommand.cs} | 2 +- ...ommandHandler.cs => ResetEmailTemplateCommandHandler.cs} | 6 +++--- .../Controllers/EmailTemplateController.cs | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) rename EnvelopeGenerator.Application/EmailTemplates/Commands/Reset/{ResetEnvelopeTemplateCommand.cs => ResetEmailTemplateCommand.cs} (91%) rename EnvelopeGenerator.Application/EmailTemplates/Commands/Reset/{ResetEnvelopeTemplateCommandHandler.cs => ResetEmailTemplateCommandHandler.cs} (95%) diff --git a/EnvelopeGenerator.Application/EmailTemplates/Commands/Reset/ResetEnvelopeTemplateCommand.cs b/EnvelopeGenerator.Application/EmailTemplates/Commands/Reset/ResetEmailTemplateCommand.cs similarity index 91% rename from EnvelopeGenerator.Application/EmailTemplates/Commands/Reset/ResetEnvelopeTemplateCommand.cs rename to EnvelopeGenerator.Application/EmailTemplates/Commands/Reset/ResetEmailTemplateCommand.cs index 29a51f19..6e3bce7a 100644 --- a/EnvelopeGenerator.Application/EmailTemplates/Commands/Reset/ResetEnvelopeTemplateCommand.cs +++ b/EnvelopeGenerator.Application/EmailTemplates/Commands/Reset/ResetEmailTemplateCommand.cs @@ -20,4 +20,4 @@ namespace EnvelopeGenerator.Application.EmailTemplates.Commands.Reset; /// 8 - DocumentRejected_REC (Für den ablehnenden Empfänger): Mail an den ablehnenden Empfänger, wenn das Dokument abgelehnt wird. /// 9 - DocumentRejected_REC_2 (Für sonstige Empfänger): Mail an andere Empfänger (Brief), wenn das Dokument abgelehnt wird. /// -public record ResetEnvelopeTemplateCommand(int? Id = null, Constants.EmailTemplateType? Type = null) : EmailTemplateQuery(Id, Type), IRequest; +public record ResetEmailTemplateCommand(int? Id = null, Constants.EmailTemplateType? Type = null) : EmailTemplateQuery(Id, Type), IRequest; diff --git a/EnvelopeGenerator.Application/EmailTemplates/Commands/Reset/ResetEnvelopeTemplateCommandHandler.cs b/EnvelopeGenerator.Application/EmailTemplates/Commands/Reset/ResetEmailTemplateCommandHandler.cs similarity index 95% rename from EnvelopeGenerator.Application/EmailTemplates/Commands/Reset/ResetEnvelopeTemplateCommandHandler.cs rename to EnvelopeGenerator.Application/EmailTemplates/Commands/Reset/ResetEmailTemplateCommandHandler.cs index 323b751c..df20bc52 100644 --- a/EnvelopeGenerator.Application/EmailTemplates/Commands/Reset/ResetEnvelopeTemplateCommandHandler.cs +++ b/EnvelopeGenerator.Application/EmailTemplates/Commands/Reset/ResetEmailTemplateCommandHandler.cs @@ -9,7 +9,7 @@ namespace EnvelopeGenerator.Application.EmailTemplates.Commands.Reset; /// /// /// -public class ResetEnvelopeTemplateCommandHandler : IRequestHandler +public class ResetEmailTemplateCommandHandler : IRequestHandler { private readonly IRepository _repository; @@ -17,12 +17,12 @@ public class ResetEnvelopeTemplateCommandHandler : IRequestHandler /// - public ResetEnvelopeTemplateCommandHandler(IRepository repository) + public ResetEmailTemplateCommandHandler(IRepository repository) { _repository = repository; } - public async Task Handle(ResetEnvelopeTemplateCommand request, CancellationToken cancel) + public async Task Handle(ResetEmailTemplateCommand request, CancellationToken cancel) { var temps = request.Id is not null ? await _repository.ReadAllAsync(t => t.Id == request.Id, cancel) diff --git a/EnvelopeGenerator.GeneratorAPI/Controllers/EmailTemplateController.cs b/EnvelopeGenerator.GeneratorAPI/Controllers/EmailTemplateController.cs index db98a28e..e65cfd51 100644 --- a/EnvelopeGenerator.GeneratorAPI/Controllers/EmailTemplateController.cs +++ b/EnvelopeGenerator.GeneratorAPI/Controllers/EmailTemplateController.cs @@ -111,13 +111,13 @@ public class EmailTemplateController : ControllerBase { if (update is null) { - var reset = _mapper.Map(email); - await _mediator.Send(new ResetEnvelopeTemplateCommand(email?.Id, email?.Type)); + var reset = _mapper.Map(email); + await _mediator.Send(new ResetEmailTemplateCommand(email?.Id, email?.Type)); return Ok(); } else { - var reset = _mapper.Map(email); + var reset = _mapper.Map(email); await _mediator.Send(update); return Ok(); } From 629c0d51b2de1356ac24fdca5436f621d7606488 Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Wed, 7 May 2025 17:00:26 +0200 Subject: [PATCH 72/94] Enhance email template handling and documentation - Added XML documentation to the `Handle` method in `UpdateEmailTemplateCommandHandler`. - Improved readability in `ReadEmailTemplateQueryHandler` by storing the mapped response in a variable. - Updated properties in `ReadEmailTemplateResponse` to be mutable and renamed `Type` to `Name` with a type change from `int` to `string`. - Added data annotations in `EmailTemplate` for `AddedWhen` and introduced a new nullable `ChangedWhen` property. - Included necessary using directives for data annotations in `EmailTemplate.cs`. --- .../Commands/Update/UpdateEmailTemplateCommandHandler.cs | 8 ++++++++ .../Queries/Read/ReadEmailTemplateQueryHandler.cs | 4 +++- .../Queries/Read/ReadEmailTemplateResponse.cs | 8 ++++---- EnvelopeGenerator.Domain/Entities/EmailTemplate.cs | 9 +++++++++ 4 files changed, 24 insertions(+), 5 deletions(-) diff --git a/EnvelopeGenerator.Application/EmailTemplates/Commands/Update/UpdateEmailTemplateCommandHandler.cs b/EnvelopeGenerator.Application/EmailTemplates/Commands/Update/UpdateEmailTemplateCommandHandler.cs index 3d1462e9..09803a6c 100644 --- a/EnvelopeGenerator.Application/EmailTemplates/Commands/Update/UpdateEmailTemplateCommandHandler.cs +++ b/EnvelopeGenerator.Application/EmailTemplates/Commands/Update/UpdateEmailTemplateCommandHandler.cs @@ -24,6 +24,14 @@ public class UpdateEmailTemplateCommandHandler : IRequestHandler + /// + /// + /// + /// + /// + /// + /// public async Task Handle(UpdateEmailTemplateCommand request, CancellationToken cancel) { var temp = (request.EmailTemplateQuery?.Id is int id diff --git a/EnvelopeGenerator.Application/EmailTemplates/Queries/Read/ReadEmailTemplateQueryHandler.cs b/EnvelopeGenerator.Application/EmailTemplates/Queries/Read/ReadEmailTemplateQueryHandler.cs index 13d6aa73..6059ec33 100644 --- a/EnvelopeGenerator.Application/EmailTemplates/Queries/Read/ReadEmailTemplateQueryHandler.cs +++ b/EnvelopeGenerator.Application/EmailTemplates/Queries/Read/ReadEmailTemplateQueryHandler.cs @@ -45,6 +45,8 @@ public class ReadEmailTemplateQueryHandler : IRequestHandler(temp); + var res = _mapper.Map(temp); + + return res; } } diff --git a/EnvelopeGenerator.Application/EmailTemplates/Queries/Read/ReadEmailTemplateResponse.cs b/EnvelopeGenerator.Application/EmailTemplates/Queries/Read/ReadEmailTemplateResponse.cs index 7616411d..df68fa94 100644 --- a/EnvelopeGenerator.Application/EmailTemplates/Queries/Read/ReadEmailTemplateResponse.cs +++ b/EnvelopeGenerator.Application/EmailTemplates/Queries/Read/ReadEmailTemplateResponse.cs @@ -8,17 +8,17 @@ public class ReadEmailTemplateResponse /// /// Die eindeutige Kennung der E-Mail-Vorlage. /// - public int Id { get; init; } + public int Id { get; set; } /// - /// Der Typ der E-Mail-Vorlage. + /// Name des Typs /// - public int Type { get; init; } + public required string Name { get; set; } /// /// Das Datum und die Uhrzeit, wann die Vorlage hinzugefügt wurde. /// - public DateTime AddedWhen { get; init; } + public DateTime AddedWhen { get; set; } /// /// Der Inhalt (Body) der E-Mail-Vorlage. Kann null sein. diff --git a/EnvelopeGenerator.Domain/Entities/EmailTemplate.cs b/EnvelopeGenerator.Domain/Entities/EmailTemplate.cs index aef2df9d..689df6b9 100644 --- a/EnvelopeGenerator.Domain/Entities/EmailTemplate.cs +++ b/EnvelopeGenerator.Domain/Entities/EmailTemplate.cs @@ -1,4 +1,5 @@ using DigitalData.Core.Abstractions; +using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; @@ -20,5 +21,13 @@ namespace EnvelopeGenerator.Domain.Entities [Column("SUBJECT", TypeName = "nvarchar(512)")] public string? Subject { get; set; } + + [Required] + [Column("ADDED_WHEN", TypeName = "datetime")] + [DefaultValue("GETDATE()")] + public DateTime AddedWhen { get; set; } + + [Column("CHANGED_WHEN", TypeName = "datetime")] + public DateTime? ChangedWhen { get; set; } } } \ No newline at end of file From 126370ebdb8ae3f5c0d49fd48a9080b22ed0501e Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Wed, 7 May 2025 18:00:28 +0200 Subject: [PATCH 73/94] Add user management services to Program.cs This commit introduces a new using directive for `DigitalData.UserManager.DependencyInjection` and registers the user manager service with `builder.Services.AddUserManager();`. These changes enhance the application's capabilities related to user management. --- EnvelopeGenerator.Web/Program.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/EnvelopeGenerator.Web/Program.cs b/EnvelopeGenerator.Web/Program.cs index e9324e3e..08e71c8a 100644 --- a/EnvelopeGenerator.Web/Program.cs +++ b/EnvelopeGenerator.Web/Program.cs @@ -16,6 +16,7 @@ using EnvelopeGenerator.Infrastructure; using EnvelopeGenerator.Web.Sanitizers; using EnvelopeGenerator.Application.Contracts.Services; using EnvelopeGenerator.Web.Models.Annotation; +using DigitalData.UserManager.DependencyInjection; var logger = LogManager.Setup().LoadConfigurationFromAppSettings().GetCurrentClassLogger(); logger.Info("Logging initialized!"); @@ -168,6 +169,8 @@ try builder.ConfigureBySection(); + builder.Services.AddUserManager(); + var app = builder.Build(); // Configure the HTTP request pipeline. From 41e0c5105588a0cadc7085c873cac8fed61cb570 Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Wed, 7 May 2025 18:14:06 +0200 Subject: [PATCH 74/94] Refactor DocumentCreateReadSQL to remove envelope_uuid Updated CreateParmas method to accept only base64 string. Converted base64 to byte array and added it as ByteData. Adjusted SQL command and DocumentExecutor to reflect these changes. --- .../SQL/DocumentCreateReadSQL.cs | 11 +++++------ .../Executor/DocumentExecutor.cs | 5 +++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/EnvelopeGenerator.Application/SQL/DocumentCreateReadSQL.cs b/EnvelopeGenerator.Application/SQL/DocumentCreateReadSQL.cs index 7fec8262..ed682b0e 100644 --- a/EnvelopeGenerator.Application/SQL/DocumentCreateReadSQL.cs +++ b/EnvelopeGenerator.Application/SQL/DocumentCreateReadSQL.cs @@ -14,11 +14,11 @@ public class DocumentCreateReadSQL : ISQL /// public string Raw => @" DECLARE @BYTE_DATA1 as VARBINARY(MAX) - SET @BYTE_DATA1 = CONVERT(VARBINARY(MAX),{0}) + SET @BYTE_DATA1 = @ByteData DECLARE @OUT_DOCID int EXEC [dbo].[PRSIG_API_ADD_DOC] - {1}, + {0}, @BYTE_DATA1, @OUT_DOCID OUTPUT @@ -31,13 +31,12 @@ public class DocumentCreateReadSQL : ISQL /// /// /// - /// /// - public static DynamicParameters CreateParmas(string base64, string envelope_uuid) + public static DynamicParameters CreateParmas(string base64) { var parameters = new DynamicParameters(); - parameters.Add("@Base64", base64); - parameters.Add("@OUT_UID", envelope_uuid); + byte[] byteData = Convert.FromBase64String(base64); + parameters.Add("ByteData", byteData, System.Data.DbType.Binary); return parameters; } } diff --git a/EnvelopeGenerator.Infrastructure/Executor/DocumentExecutor.cs b/EnvelopeGenerator.Infrastructure/Executor/DocumentExecutor.cs index 0b42a766..11088c63 100644 --- a/EnvelopeGenerator.Infrastructure/Executor/DocumentExecutor.cs +++ b/EnvelopeGenerator.Infrastructure/Executor/DocumentExecutor.cs @@ -18,9 +18,10 @@ public class DocumentExecutor : SQLExecutor, IDocumentExecutor { using var connection = new SqlConnection(Params.ConnectionString); var sql = Provider.GetRequiredService(); - var formattedSql = string.Format(sql.Raw, base64.ToSqlParam(), envelope_uuid.ToSqlParam()); + var formattedSql = string.Format(sql.Raw, envelope_uuid.ToSqlParam()); + var param = DocumentCreateReadSQL.CreateParmas(base64); await connection.OpenAsync(cancellation); - var documents = await connection.QueryAsync(formattedSql); + var documents = await connection.QueryAsync(formattedSql, param); return documents.FirstOrDefault() ?? throw new InvalidOperationException($"Document creation failed. Parameters:" + $"base64={base64}, envelope_uuid='{envelope_uuid}'."); From d873d6dcf169491920cc2d3e44db9780c9885bd8 Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Thu, 8 May 2025 09:47:03 +0200 Subject: [PATCH 75/94] Refactor Signature record and update EnvelopeReceiverController - Removed default email assignment in Signature record. - Eliminated unused using directive in EnvelopeReceiverController. - Added new parameters for document execution and connection string options in the controller's constructor. --- .../Commands/Create/CreateEnvelopeReceiverDtos.cs | 4 ++-- .../Controllers/EnvelopeReceiverController.cs | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverDtos.cs b/EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverDtos.cs index d26354cd..3f751c26 100644 --- a/EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverDtos.cs +++ b/EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverDtos.cs @@ -25,12 +25,12 @@ public record Signature([Required] double X, [Required] double Y, [Required] int /// Sollte mit Vorwahl geschrieben werden public record ReceiverGetOrCreateCommand([Required] IEnumerable Signatures, string? Salution = null, string? PhoneNumber = null) { - private string? _emailAddress = "h.tek@digitaldata.works"; + private string? _emailAddress; /// /// E-Mail-Adresse des Empfängers. /// - public string? EmailAddress { get => _emailAddress?.ToLower(); init => _emailAddress = _emailAddress?.ToLower() ?? "h.tek@digitaldata.works"; } + public string? EmailAddress { get => _emailAddress?.ToLower(); init => _emailAddress = _emailAddress?.ToLower(); } }; /// diff --git a/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeReceiverController.cs b/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeReceiverController.cs index d4df62db..6a1a927f 100644 --- a/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeReceiverController.cs +++ b/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeReceiverController.cs @@ -15,7 +15,6 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.Data.SqlClient; using Microsoft.Extensions.Options; using System.Data; -using System.Threading.Channels; namespace EnvelopeGenerator.GeneratorAPI.Controllers; @@ -55,6 +54,8 @@ public class EnvelopeReceiverController : ControllerBase /// /// /// + /// + /// public EnvelopeReceiverController(ILogger logger, IEnvelopeReceiverService envelopeReceiverService, IMediator mediator, IMapper mapper, IEnvelopeExecutor envelopeExecutor, IEnvelopeReceiverExecutor erExecutor, IDocumentExecutor documentExecutor, IOptions csOpt) { _logger = logger; From 2db0748e6035a72e28d6f38dffbabd1505aa835c Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Thu, 8 May 2025 10:25:06 +0200 Subject: [PATCH 76/94] Update ReceiverGetOrCreateCommand email handling Changed _emailAddress from nullable to non-nullable, initialized to an empty string. Updated EmailAddress property to be required, with modified getter and setter to ensure lowercase formatting. --- .../Commands/Create/CreateEnvelopeReceiverDtos.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverDtos.cs b/EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverDtos.cs index 3f751c26..dc8072e0 100644 --- a/EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverDtos.cs +++ b/EnvelopeGenerator.Application/EnvelopeReceivers/Commands/Create/CreateEnvelopeReceiverDtos.cs @@ -25,12 +25,13 @@ public record Signature([Required] double X, [Required] double Y, [Required] int /// Sollte mit Vorwahl geschrieben werden public record ReceiverGetOrCreateCommand([Required] IEnumerable Signatures, string? Salution = null, string? PhoneNumber = null) { - private string? _emailAddress; + private string _emailAddress = string.Empty; /// /// E-Mail-Adresse des Empfängers. /// - public string? EmailAddress { get => _emailAddress?.ToLower(); init => _emailAddress = _emailAddress?.ToLower(); } + [Required] + public string EmailAddress { get => _emailAddress.ToLower(); init => _emailAddress = value.ToLower(); } }; /// From 3a1fe455248e64c97fb11e004d0406f21461f4cb Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Thu, 8 May 2025 10:56:46 +0200 Subject: [PATCH 77/94] Add "OrDefault" methods for user claim retrieval Introduce new extension methods in `ControllerExtensions` to safely extract user information from a `ClaimsPrincipal`. Updated methods for ID, username, surname, given name, and email to return `null` if not found, enhancing flexibility. Updated `EnvelopeReceiverController` to utilize these new methods for improved handling of absent user claims. --- .../Controllers/ControllerExtensions.cs | 48 ++++++++++++++++--- .../Controllers/EnvelopeReceiverController.cs | 4 +- 2 files changed, 43 insertions(+), 9 deletions(-) diff --git a/EnvelopeGenerator.GeneratorAPI/Controllers/ControllerExtensions.cs b/EnvelopeGenerator.GeneratorAPI/Controllers/ControllerExtensions.cs index 2f3ca908..beac46da 100644 --- a/EnvelopeGenerator.GeneratorAPI/Controllers/ControllerExtensions.cs +++ b/EnvelopeGenerator.GeneratorAPI/Controllers/ControllerExtensions.cs @@ -3,26 +3,60 @@ using System.Security.Claims; namespace EnvelopeGenerator.GeneratorAPI.Controllers { + /// + /// Provides extension methods for extracting user information from a . + /// public static class ControllerExtensions { + /// + /// Attempts to retrieve the user's ID from the claims. Returns null if the ID is not found or invalid. + /// + /// The representing the user. + /// The user's ID as an integer, or null if not found or invalid. public static int? GetIdOrDefault(this ClaimsPrincipal user) => int.TryParse(user.FindFirstValue(ClaimTypes.NameIdentifier) ?? user.FindFirstValue("sub"), out int result) ? result : null; + /// + /// Retrieves the user's ID from the claims. Throws an exception if the ID is missing or invalid. + /// + /// The representing the user. + /// The user's ID as an integer. + /// Thrown if the user ID claim is missing or invalid. public static int GetId(this ClaimsPrincipal user) => user.GetIdOrDefault() ?? throw new InvalidOperationException("User ID claim is missing or invalid. This may indicate a misconfigured or forged JWT token."); - public static string? GetUsername(this ClaimsPrincipal user) + /// + /// Retrieves the username from the claims, if available. + /// + /// The representing the user. + /// The username as a string, or null if not found. + public static string? GetUsernameOrDefault(this ClaimsPrincipal user) => user.FindFirst(ClaimTypes.Name)?.Value; - - public static string? GetName(this ClaimsPrincipal user) + + /// + /// Retrieves the user's surname (last name) from the claims, if available. + /// + /// The representing the user. + /// The surname as a string, or null if not found. + public static string? GetNameOrDefault(this ClaimsPrincipal user) => user.FindFirst(ClaimTypes.Surname)?.Value; - - public static string? GetPrename(this ClaimsPrincipal user) + + /// + /// Retrieves the user's given name (first name) from the claims, if available. + /// + /// The representing the user. + /// The given name as a string, or null if not found. + public static string? GetPrenameOrDefault(this ClaimsPrincipal user) => user.FindFirst(ClaimTypes.GivenName)?.Value; - - public static string? GetEmail(this ClaimsPrincipal user) + + /// + /// Retrieves the user's email address from the claims, if available. + /// + /// The representing the user. + /// The email address as a string, or null if not found. + public static string? GetEmailOrDefault(this ClaimsPrincipal user) => user.FindFirst(ClaimTypes.Email)?.Value; } } \ No newline at end of file diff --git a/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeReceiverController.cs b/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeReceiverController.cs index 6a1a927f..39fc6587 100644 --- a/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeReceiverController.cs +++ b/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeReceiverController.cs @@ -86,12 +86,12 @@ public class EnvelopeReceiverController : ControllerBase { try { - var username = User.GetUsername(); + var username = User.GetUsernameOrDefault(); if (username is null) { _logger.LogError(@"Envelope Receiver dto cannot be sent because username claim is null. Potential authentication and authorization error. The value of other claims are [id: {id}], [username: {username}], [name: {name}], [prename: {prename}], [email: {email}].", - User.GetId(), User.GetUsername(), User.GetName(), User.GetPrename(), User.GetEmail()); + User.GetId(), User.GetUsernameOrDefault(), User.GetNameOrDefault(), User.GetPrenameOrDefault(), User.GetEmailOrDefault()); return StatusCode(StatusCodes.Status500InternalServerError); } From 3035ec7e9ce785585c91fc9b1f1ec24c46cfb165 Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Thu, 8 May 2025 11:30:46 +0200 Subject: [PATCH 78/94] Add memory caching support in HistoryController - Updated `EnvelopeGenerator.Extensions.csproj` to include `Microsoft.Extensions.Caching.Memory` package. - Refactored `HistoryController` to use `IMemoryCache` for caching functionality. - Replaced manual dictionary creation in `GetReferenceTypes` with a call to `GetEnumAsDictionary()`. - Changed enum type in `GetEnvelopeStatus` to `ReferenceType` for consistency. - Introduced `MemoryCacheExtensions` class with `GetEnumAsDictionary` method for efficient enum to dictionary conversion. --- .../EnvelopeGenerator.Extensions.csproj | 1 + .../MemoryCacheExtensions.cs | 21 +++++++++++++++ .../Controllers/HistoryController.cs | 26 +++++++------------ 3 files changed, 32 insertions(+), 16 deletions(-) create mode 100644 EnvelopeGenerator.Extensions/MemoryCacheExtensions.cs diff --git a/EnvelopeGenerator.Extensions/EnvelopeGenerator.Extensions.csproj b/EnvelopeGenerator.Extensions/EnvelopeGenerator.Extensions.csproj index cb187fa2..dbaad97c 100644 --- a/EnvelopeGenerator.Extensions/EnvelopeGenerator.Extensions.csproj +++ b/EnvelopeGenerator.Extensions/EnvelopeGenerator.Extensions.csproj @@ -13,6 +13,7 @@ + diff --git a/EnvelopeGenerator.Extensions/MemoryCacheExtensions.cs b/EnvelopeGenerator.Extensions/MemoryCacheExtensions.cs new file mode 100644 index 00000000..21b60956 --- /dev/null +++ b/EnvelopeGenerator.Extensions/MemoryCacheExtensions.cs @@ -0,0 +1,21 @@ +using Microsoft.Extensions.Caching.Memory; + +namespace EnvelopeGenerator.Extensions; + +public static class MemoryCacheExtensions +{ + public static IDictionary GetEnumAsDictionary(this IMemoryCache memoryCache) + where TEnum : Enum + { + var referenceTypes = Enum.GetValues(typeof(TEnum)) + .Cast() + .ToDictionary(rt => + { + var key = rt.ToString(); + var keyAsCamelCase = char.ToLowerInvariant(key[0]) + key[1..]; + return keyAsCamelCase; + }, rt => Convert.ToInt32(rt)); + + return referenceTypes; + } +} diff --git a/EnvelopeGenerator.GeneratorAPI/Controllers/HistoryController.cs b/EnvelopeGenerator.GeneratorAPI/Controllers/HistoryController.cs index 8c934f3e..236ffee5 100644 --- a/EnvelopeGenerator.GeneratorAPI/Controllers/HistoryController.cs +++ b/EnvelopeGenerator.GeneratorAPI/Controllers/HistoryController.cs @@ -1,8 +1,9 @@ -using DigitalData.EmailProfilerDispatcher.Abstraction.Entities; -using EnvelopeGenerator.Application.Contracts.Services; +using EnvelopeGenerator.Application.Contracts.Services; using EnvelopeGenerator.Application.Histories.Queries.Read; +using EnvelopeGenerator.Extensions; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Caching.Memory; using static EnvelopeGenerator.Common.Constants; @@ -20,15 +21,18 @@ public class HistoryController : ControllerBase private readonly IEnvelopeHistoryService _service; + private readonly IMemoryCache _memoryCache; + /// /// Konstruktor für den HistoryController. /// /// Der Logger, der für das Protokollieren von Informationen verwendet wird. /// Der Dienst, der für die Verarbeitung der Umschlaghistorie verantwortlich ist. - public HistoryController(ILogger logger, IEnvelopeHistoryService service) + public HistoryController(ILogger logger, IEnvelopeHistoryService service, IMemoryCache memoryCache) { _logger = logger; _service = service; + _memoryCache = memoryCache; } /// @@ -49,17 +53,7 @@ public class HistoryController : ControllerBase [Authorize] public IActionResult GetReferenceTypes() { - // Enum zu Schlüssel-Wert-Paar - var referenceTypes = Enum.GetValues(typeof(ReferenceType)) - .Cast() - .ToDictionary(rt => - { - var key = rt.ToString(); - var keyAsCamelCase = char.ToLower(key[0]) + key[1..]; - return keyAsCamelCase; - }, rt => (int)rt); - - return Ok(referenceTypes); + return Ok(_memoryCache.GetEnumAsDictionary()); } /// @@ -105,8 +99,8 @@ public class HistoryController : ControllerBase public IActionResult GetEnvelopeStatus([FromQuery] ReferenceType? related = null) { // Enum zu Schlüssel-Wert-Paar - var referenceTypes = Enum.GetValues(typeof(EnvelopeStatus)) - .Cast() + var referenceTypes = Enum.GetValues(typeof(ReferenceType)) + .Cast() .ToDictionary(rt => { var key = rt.ToString(); From 7fefc680614521729bba5777800e8e52858cb5ee Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Thu, 8 May 2025 12:00:48 +0200 Subject: [PATCH 79/94] Refactor enum handling in MemoryCache and HistoryController Updated `GetEnumAsDictionary` in `MemoryCacheExtensions.cs` to use a loop for populating a dictionary of enum values, removing LINQ for simplicity. Modified `HistoryController.cs` to adjust method signatures for `GetReferenceTypes` and `GetEnvelopeStatus`, allowing optional parameters for better conditional responses. Added necessary using directives. --- .../MemoryCacheExtensions.cs | 17 +++++++------ .../Controllers/HistoryController.cs | 24 +++++++------------ 2 files changed, 16 insertions(+), 25 deletions(-) diff --git a/EnvelopeGenerator.Extensions/MemoryCacheExtensions.cs b/EnvelopeGenerator.Extensions/MemoryCacheExtensions.cs index 21b60956..31df714d 100644 --- a/EnvelopeGenerator.Extensions/MemoryCacheExtensions.cs +++ b/EnvelopeGenerator.Extensions/MemoryCacheExtensions.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.Caching.Memory; +using System; namespace EnvelopeGenerator.Extensions; @@ -7,15 +8,13 @@ public static class MemoryCacheExtensions public static IDictionary GetEnumAsDictionary(this IMemoryCache memoryCache) where TEnum : Enum { - var referenceTypes = Enum.GetValues(typeof(TEnum)) - .Cast() - .ToDictionary(rt => - { - var key = rt.ToString(); - var keyAsCamelCase = char.ToLowerInvariant(key[0]) + key[1..]; - return keyAsCamelCase; - }, rt => Convert.ToInt32(rt)); + var dict = new Dictionary(); - return referenceTypes; + foreach (TEnum role in Enum.GetValues(typeof(TEnum))) + { + dict[role.ToString()] = Convert.ToInt32(role); + } + + return dict; } } diff --git a/EnvelopeGenerator.GeneratorAPI/Controllers/HistoryController.cs b/EnvelopeGenerator.GeneratorAPI/Controllers/HistoryController.cs index 236ffee5..2adbd4e0 100644 --- a/EnvelopeGenerator.GeneratorAPI/Controllers/HistoryController.cs +++ b/EnvelopeGenerator.GeneratorAPI/Controllers/HistoryController.cs @@ -1,9 +1,11 @@ -using EnvelopeGenerator.Application.Contracts.Services; +using DigitalData.EmailProfilerDispatcher.Abstraction.Entities; +using EnvelopeGenerator.Application.Contracts.Services; using EnvelopeGenerator.Application.Histories.Queries.Read; using EnvelopeGenerator.Extensions; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Caching.Memory; +using Microsoft.IdentityModel.Tokens; using static EnvelopeGenerator.Common.Constants; @@ -51,9 +53,9 @@ public class HistoryController : ControllerBase /// [HttpGet("related")] [Authorize] - public IActionResult GetReferenceTypes() + public IActionResult GetReferenceTypes(ReferenceType? referenceType = null) { - return Ok(_memoryCache.GetEnumAsDictionary()); + return referenceType is null ? Ok(_memoryCache.GetEnumAsDictionary()) : Ok(referenceType.ToString()); } /// @@ -85,7 +87,7 @@ public class HistoryController : ControllerBase /// 3004: MessageDeletionSent /// 3005: MessageCompletionSent /// - /// + /// /// Abfrageparameter, der angibt, auf welche Referenz sich der Status bezieht. /// 0 - Sender: Historische Datensätze, die sich auf den Status des Absenders beziehen. Sie haben Statuscodes, die mit 1* beginnen. /// 1 - Receiver: Historische Datensätze über den Status der Empfänger. Diese haben Statuscodes, die mit 2* beginnen. @@ -96,19 +98,9 @@ public class HistoryController : ControllerBase /// [HttpGet("status")] [Authorize] - public IActionResult GetEnvelopeStatus([FromQuery] ReferenceType? related = null) + public IActionResult GetEnvelopeStatus([FromQuery] EnvelopeStatus? status = null) { - // Enum zu Schlüssel-Wert-Paar - var referenceTypes = Enum.GetValues(typeof(ReferenceType)) - .Cast() - .ToDictionary(rt => - { - var key = rt.ToString(); - var keyAsCamelCase = char.ToLower(key[0]) + key[1..]; - return keyAsCamelCase; - }, rt => (int)rt); - - return Ok(referenceTypes); + return status is null ? Ok(_memoryCache.GetEnumAsDictionary()) : Ok(status.ToString()); } /// From 0698b44b68b5de2e9e2760e7c3f4151ea03005c0 Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Thu, 8 May 2025 13:52:57 +0200 Subject: [PATCH 80/94] Refactor MemoryCacheExtensions and clean up HistoryController - Introduced a static readonly field `BaseId` in `MemoryCacheExtensions.cs`. - Refactored `GetEnumAsDictionary` to use expression-bodied syntax and LINQ for improved readability and efficiency. - Removed the import statement for `Microsoft.IdentityModel.Tokens` in `HistoryController.cs`, indicating a potential shift in authentication/authorization handling. --- .../MemoryCacheExtensions.cs | 18 +++++++----------- .../Controllers/HistoryController.cs | 1 - 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/EnvelopeGenerator.Extensions/MemoryCacheExtensions.cs b/EnvelopeGenerator.Extensions/MemoryCacheExtensions.cs index 31df714d..86ac56bb 100644 --- a/EnvelopeGenerator.Extensions/MemoryCacheExtensions.cs +++ b/EnvelopeGenerator.Extensions/MemoryCacheExtensions.cs @@ -1,20 +1,16 @@ using Microsoft.Extensions.Caching.Memory; -using System; namespace EnvelopeGenerator.Extensions; public static class MemoryCacheExtensions { + private static readonly Guid BaseId = Guid.NewGuid(); + public static IDictionary GetEnumAsDictionary(this IMemoryCache memoryCache) where TEnum : Enum - { - var dict = new Dictionary(); - - foreach (TEnum role in Enum.GetValues(typeof(TEnum))) - { - dict[role.ToString()] = Convert.ToInt32(role); - } - - return dict; - } + => memoryCache.GetOrCreate(BaseId + typeof(TEnum).FullName, _ => + Enum.GetValues(typeof(TEnum)) + .Cast() + .ToDictionary(e => e.ToString(), e => Convert.ToInt32(e))) + ?? throw new InvalidOperationException($"Failed to cache or retrieve enum dictionary for type '{typeof(TEnum).FullName}'."); } diff --git a/EnvelopeGenerator.GeneratorAPI/Controllers/HistoryController.cs b/EnvelopeGenerator.GeneratorAPI/Controllers/HistoryController.cs index 2adbd4e0..5c55b567 100644 --- a/EnvelopeGenerator.GeneratorAPI/Controllers/HistoryController.cs +++ b/EnvelopeGenerator.GeneratorAPI/Controllers/HistoryController.cs @@ -5,7 +5,6 @@ using EnvelopeGenerator.Extensions; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Caching.Memory; -using Microsoft.IdentityModel.Tokens; using static EnvelopeGenerator.Common.Constants; From ce0b1f178567194c1db2761b80d64acd3ffb4bca Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Thu, 8 May 2025 13:53:16 +0200 Subject: [PATCH 81/94] Comment out unused constants in Constants.vb The `SignatureConfirmed` and `DocumentMod_Rotation` constants have been commented out to indicate that they are no longer active or used in the code. This helps to clean up the codebase and improve maintainability. --- EnvelopeGenerator.Common/Constants.vb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/EnvelopeGenerator.Common/Constants.vb b/EnvelopeGenerator.Common/Constants.vb index 59570ee1..9631744e 100644 --- a/EnvelopeGenerator.Common/Constants.vb +++ b/EnvelopeGenerator.Common/Constants.vb @@ -21,7 +21,7 @@ DocumentOpened = 2004 DocumentSigned = 2005 DocumentForwarded = 4001 - SignatureConfirmed = 2006 + 'SignatureConfirmed = 2006 DocumentRejected = 2007 EnvelopeShared = 2008 EnvelopeViewed = 2009 @@ -30,7 +30,7 @@ MessageConfirmationSent = 3003 MessageDeletionSent = 3004 MessageCompletionSent = 3005 - DocumentMod_Rotation = 4001 + 'DocumentMod_Rotation = 4001 End Enum 'TODO: standardize in xwiki From 1a978c0ab7cf2569ea5b898894e49a552317617e Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Thu, 8 May 2025 14:26:36 +0200 Subject: [PATCH 82/94] Update Constants.vb with new statuses and cleanup Removed comment for SignatureConfirmed. Added a new shared read-only list, NonHistStatuses, containing EnvelopeStatus values: Invalid, EnvelopeSaved, EnvelopeSent, and EnvelopePartlySigned. Added TODO for standardization in xwiki. ReferenceType enum remains unchanged. --- EnvelopeGenerator.Common/Constants.vb | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/EnvelopeGenerator.Common/Constants.vb b/EnvelopeGenerator.Common/Constants.vb index 9631744e..c2a00304 100644 --- a/EnvelopeGenerator.Common/Constants.vb +++ b/EnvelopeGenerator.Common/Constants.vb @@ -21,7 +21,6 @@ DocumentOpened = 2004 DocumentSigned = 2005 DocumentForwarded = 4001 - 'SignatureConfirmed = 2006 DocumentRejected = 2007 EnvelopeShared = 2008 EnvelopeViewed = 2009 @@ -30,9 +29,15 @@ MessageConfirmationSent = 3003 MessageDeletionSent = 3004 MessageCompletionSent = 3005 - 'DocumentMod_Rotation = 4001 End Enum + Public Shared ReadOnly NonHistStatuses As IReadOnlyList(Of EnvelopeStatus) = New List(Of EnvelopeStatus) From { + EnvelopeStatus.Invalid, + EnvelopeStatus.EnvelopeSaved, + EnvelopeStatus.EnvelopeSent, + EnvelopeStatus.EnvelopePartlySigned + } + 'TODO: standardize in xwiki Public Enum ReferenceType Receiver = 0 From 8d118308cd00f81c390369b129f7fdcde9632e41 Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Thu, 8 May 2025 15:15:01 +0200 Subject: [PATCH 83/94] Remove Procedures folder and update configurations - Removed folder reference for "Procedures" in the project file. - Added `DocumentMod_Rotation` to the `Constants.vb` enum. - Replaced `NonHistStatuses` with a new `Status` class containing `NonHist` and `RelatedToFormApp` lists. - Updated `TimeLimit` in `appsettings.json` from "00:30:00" to "90.00:00:00". --- .../EnvelopeGenerator.Application.csproj | 4 ---- EnvelopeGenerator.Common/Constants.vb | 20 +++++++++++++------ EnvelopeGenerator.Web/appsettings.json | 2 +- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/EnvelopeGenerator.Application/EnvelopeGenerator.Application.csproj b/EnvelopeGenerator.Application/EnvelopeGenerator.Application.csproj index 0f944763..e285a702 100644 --- a/EnvelopeGenerator.Application/EnvelopeGenerator.Application.csproj +++ b/EnvelopeGenerator.Application/EnvelopeGenerator.Application.csproj @@ -82,8 +82,4 @@ - - - - diff --git a/EnvelopeGenerator.Common/Constants.vb b/EnvelopeGenerator.Common/Constants.vb index c2a00304..2e61cefb 100644 --- a/EnvelopeGenerator.Common/Constants.vb +++ b/EnvelopeGenerator.Common/Constants.vb @@ -29,14 +29,22 @@ MessageConfirmationSent = 3003 MessageDeletionSent = 3004 MessageCompletionSent = 3005 + DocumentMod_Rotation = 4001 End Enum - Public Shared ReadOnly NonHistStatuses As IReadOnlyList(Of EnvelopeStatus) = New List(Of EnvelopeStatus) From { - EnvelopeStatus.Invalid, - EnvelopeStatus.EnvelopeSaved, - EnvelopeStatus.EnvelopeSent, - EnvelopeStatus.EnvelopePartlySigned - } + Public Class Status + Public Shared ReadOnly NonHist As IReadOnlyList(Of EnvelopeStatus) = New List(Of EnvelopeStatus) From { + EnvelopeStatus.Invalid, + EnvelopeStatus.EnvelopeSaved, + EnvelopeStatus.EnvelopeSent, + EnvelopeStatus.EnvelopePartlySigned + } + + Public Shared ReadOnly RelatedToFormApp As IReadOnlyList(Of EnvelopeStatus) = New List(Of EnvelopeStatus) From { + EnvelopeStatus.EnvelopeCreated, + EnvelopeStatus.DocumentMod_Rotation + } + End Class 'TODO: standardize in xwiki Public Enum ReferenceType diff --git a/EnvelopeGenerator.Web/appsettings.json b/EnvelopeGenerator.Web/appsettings.json index 1c6fb8c4..4a0f5c70 100644 --- a/EnvelopeGenerator.Web/appsettings.json +++ b/EnvelopeGenerator.Web/appsettings.json @@ -140,7 +140,7 @@ } }, "TFARegParams": { - "TimeLimit": "00:30:00" + "TimeLimit": "90.00:00:00" }, "DbTriggerParams": { "Envelope": [ "TBSIG_ENVELOPE_HISTORY_AFT_INS" ], From 2007ae91fb65b969d2152244f75bf33a2faf1ab7 Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Thu, 8 May 2025 15:46:02 +0200 Subject: [PATCH 84/94] Add DocResult property and update EnvelopeController - Introduced a new `DocResult` property in `EnvelopeDto.cs` and `Envelope.cs` for handling binary data. - Rearranged properties in `EnvelopeDto.cs` for better organization. - Modified `EnvelopeController` to return a collection of envelopes instead of a single envelope. --- EnvelopeGenerator.Application/DTOs/EnvelopeDto.cs | 5 ++++- EnvelopeGenerator.Domain/Entities/Envelope.cs | 3 +++ .../Controllers/EnvelopeController.cs | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/EnvelopeGenerator.Application/DTOs/EnvelopeDto.cs b/EnvelopeGenerator.Application/DTOs/EnvelopeDto.cs index 6a038d83..c748b038 100644 --- a/EnvelopeGenerator.Application/DTOs/EnvelopeDto.cs +++ b/EnvelopeGenerator.Application/DTOs/EnvelopeDto.cs @@ -66,6 +66,9 @@ namespace EnvelopeGenerator.Application.DTOs public string? StatusTranslated { get; set; } public string? ContractTypeTranslated { get; set; } - public IEnumerable? Documents { get; set; } + + public byte[]? DocResult { get; init; } + + public IEnumerable? Documents { get; set; } } } \ No newline at end of file diff --git a/EnvelopeGenerator.Domain/Entities/Envelope.cs b/EnvelopeGenerator.Domain/Entities/Envelope.cs index 8a8ee2ac..1711113f 100644 --- a/EnvelopeGenerator.Domain/Entities/Envelope.cs +++ b/EnvelopeGenerator.Domain/Entities/Envelope.cs @@ -86,6 +86,9 @@ namespace EnvelopeGenerator.Domain.Entities [Column("TFA_ENABLED", TypeName = "bit")] public bool TFAEnabled { get; set; } + [Column("DOC_RESULT", TypeName = "varbinary(max)")] + public byte[]? DocResult { get; init; } + /// /// The sender of envelope /// diff --git a/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeController.cs b/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeController.cs index 6daa2d85..ff1e7af1 100644 --- a/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeController.cs +++ b/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeController.cs @@ -76,7 +76,7 @@ public class EnvelopeController : ControllerBase if (envelope.Uuid is string uuid) envelopes = envelopes.Where(e => e.Uuid == uuid); - return Ok(envelope); + return Ok(envelopes); }, Fail: IActionResult (msg, ntc) => { From 09a231d01faa00407ceeb6f4f14269d5c57ff014 Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Thu, 8 May 2025 16:36:26 +0200 Subject: [PATCH 85/94] Add GetDocResultAsync method to EnvelopeController Implemented a new asynchronous method `GetDocResultAsync` in the `EnvelopeController` class to retrieve document results by ID. Added XML documentation for clarity on parameters and responses. Enhanced user authorization handling and error logging for improved reliability. --- .../Controllers/EnvelopeController.cs | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeController.cs b/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeController.cs index ff1e7af1..13faf709 100644 --- a/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeController.cs +++ b/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeController.cs @@ -96,6 +96,59 @@ public class EnvelopeController : ControllerBase } } + /// + /// Ruft das Ergebnis eines Dokuments basierend auf der ID ab. + /// + /// Die eindeutige ID des Umschlags. + /// Gibt an, ob das Dokument inline angezeigt werden soll (true) oder als Download bereitgestellt wird (false). + /// Eine IActionResult-Instanz, die das Dokument oder einen Fehlerstatus enthält. + /// Das Dokument wurde erfolgreich abgerufen. + /// Das Dokument wurde nicht gefunden oder ist nicht verfügbar. + /// Ein unerwarteter Fehler ist aufgetreten. + [HttpGet("doc-result")] + public async Task GetDocResultAsync([FromQuery] int id, [FromQuery] bool view = false) + { + try + { + if (User.GetId() is int intId) + return await _envelopeService.ReadByUserAsync(intId).ThenAsync( + Success: envelopes => + { + var envelope = envelopes.Where(e => e.Id == id).FirstOrDefault(); + + if (envelope is null) + return NotFound("Envelope not available."); + else if (envelope?.DocResult is null) + return NotFound("The document has not been fully signed or the result has not yet been released."); + else + { + if (view) + { + Response.Headers.Append("Content-Disposition", "inline; filename=\"" + envelope.Uuid + ".pdf\""); + return File(envelope.DocResult, "application/pdf"); + } + else + return File(envelope.DocResult, "application/pdf", $"{envelope.Uuid}.pdf"); + } + }, + Fail: IActionResult (msg, ntc) => + { + _logger.LogNotice(ntc); + return StatusCode(StatusCodes.Status500InternalServerError); + }); + else + { + _logger.LogError("Trotz erfolgreicher Autorisierung wurde die Benutzer-ID nicht als Ganzzahl erkannt. Dies könnte auf eine fehlerhafte Erstellung der Anspruchsliste zurückzuführen sein."); + return StatusCode(StatusCodes.Status500InternalServerError); + } + } + catch (Exception ex) + { + _logger.LogError(ex, "{Message}", ex.Message); + return StatusCode(StatusCodes.Status500InternalServerError); + } + } + /// /// /// From 83fa5a29e8aa13d0d8f5db9337be600700765581 Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Thu, 8 May 2025 16:55:43 +0200 Subject: [PATCH 86/94] Add XML documentation for Handle method Enhanced the `Handle` method in the `ResetEmailTemplateCommandHandler` class with XML documentation comments, including a summary and detailed parameter descriptions for better code clarity and maintainability. --- .../Commands/Reset/ResetEmailTemplateCommandHandler.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/EnvelopeGenerator.Application/EmailTemplates/Commands/Reset/ResetEmailTemplateCommandHandler.cs b/EnvelopeGenerator.Application/EmailTemplates/Commands/Reset/ResetEmailTemplateCommandHandler.cs index df20bc52..ab5300cf 100644 --- a/EnvelopeGenerator.Application/EmailTemplates/Commands/Reset/ResetEmailTemplateCommandHandler.cs +++ b/EnvelopeGenerator.Application/EmailTemplates/Commands/Reset/ResetEmailTemplateCommandHandler.cs @@ -22,6 +22,12 @@ public class ResetEmailTemplateCommandHandler : IRequestHandler + /// + /// + /// + /// + /// public async Task Handle(ResetEmailTemplateCommand request, CancellationToken cancel) { var temps = request.Id is not null From 8b4ad5e28d171f68195ac671389905a63c5f9fc3 Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Thu, 8 May 2025 17:47:18 +0200 Subject: [PATCH 87/94] Refactor email template commands and controller logic - Updated namespace for `ResetEmailTemplateCommand` and added a constructor for flexible initialization. - Introduced `ChangedWhen` property in `UpdateEmailTemplateCommand` to track modification time. - Refactored `UpdateEmailTemplateCommandHandler` for improved email template retrieval logic. - Modified `EmailTemplateController` method signatures and logic for clarity and consistency. - Added `EmailTemplate` entry in `appsettings.json` for database trigger configuration. --- .../Reset/ResetEmailTemplateCommand.cs | 24 ++++++++++++++--- .../Update/UpdateEmailTemplateCommand.cs | 6 +++++ .../UpdateEmailTemplateCommandHandler.cs | 26 ++++++++++++++----- .../Controllers/EmailTemplateController.cs | 17 +++++++----- .../appsettings.json | 3 ++- EnvelopeGenerator.Web/appsettings.json | 3 ++- 6 files changed, 61 insertions(+), 18 deletions(-) diff --git a/EnvelopeGenerator.Application/EmailTemplates/Commands/Reset/ResetEmailTemplateCommand.cs b/EnvelopeGenerator.Application/EmailTemplates/Commands/Reset/ResetEmailTemplateCommand.cs index 6e3bce7a..1c8fb830 100644 --- a/EnvelopeGenerator.Application/EmailTemplates/Commands/Reset/ResetEmailTemplateCommand.cs +++ b/EnvelopeGenerator.Application/EmailTemplates/Commands/Reset/ResetEmailTemplateCommand.cs @@ -7,8 +7,7 @@ namespace EnvelopeGenerator.Application.EmailTemplates.Commands.Reset; /// Ein Befehl zum Zurücksetzen einer E-Mail-Vorlage auf die Standardwerte. /// Erbt von und ermöglicht die Angabe einer optionalen ID und eines Typs der E-Mail-Vorlage. /// -/// Die optionale ID der E-Mail-Vorlage, die zurückgesetzt werden soll. -/// Der Typ der E-Mail-Vorlage, z. B. (optional). Beispiele: +/// Beispiele: /// 0 - DocumentReceived: Benachrichtigung über den Empfang eines Dokuments. /// 1 - DocumentSigned: Benachrichtigung über die Unterzeichnung eines Dokuments. /// 2 - DocumentDeleted: Benachrichtigung über das Löschen eines Dokuments. @@ -20,4 +19,23 @@ namespace EnvelopeGenerator.Application.EmailTemplates.Commands.Reset; /// 8 - DocumentRejected_REC (Für den ablehnenden Empfänger): Mail an den ablehnenden Empfänger, wenn das Dokument abgelehnt wird. /// 9 - DocumentRejected_REC_2 (Für sonstige Empfänger): Mail an andere Empfänger (Brief), wenn das Dokument abgelehnt wird. /// -public record ResetEmailTemplateCommand(int? Id = null, Constants.EmailTemplateType? Type = null) : EmailTemplateQuery(Id, Type), IRequest; +public record ResetEmailTemplateCommand : EmailTemplateQuery, IRequest +{ + /// + /// + /// + /// + public ResetEmailTemplateCommand(EmailTemplateQuery? orginal = null) : base(orginal ?? new()) + { + + } + + /// + /// + /// + /// Die optionale ID der E-Mail-Vorlage, die zurückgesetzt werden soll. + /// Der Typ der E-Mail-Vorlage, z. B. (optional). + public ResetEmailTemplateCommand(int? Id = null, Constants.EmailTemplateType? Type = null) : base(Id, Type) + { + } +}; diff --git a/EnvelopeGenerator.Application/EmailTemplates/Commands/Update/UpdateEmailTemplateCommand.cs b/EnvelopeGenerator.Application/EmailTemplates/Commands/Update/UpdateEmailTemplateCommand.cs index b6350bd1..1f3d7525 100644 --- a/EnvelopeGenerator.Application/EmailTemplates/Commands/Update/UpdateEmailTemplateCommand.cs +++ b/EnvelopeGenerator.Application/EmailTemplates/Commands/Update/UpdateEmailTemplateCommand.cs @@ -20,4 +20,10 @@ public record UpdateEmailTemplateCommand(string? Body = null, string? Subject = /// [JsonIgnore] public EmailTemplateQuery? EmailTemplateQuery { get; set; } + + /// + /// + /// + [JsonIgnore] + public DateTime ChangedWhen { get; init; } = DateTime.Now; } diff --git a/EnvelopeGenerator.Application/EmailTemplates/Commands/Update/UpdateEmailTemplateCommandHandler.cs b/EnvelopeGenerator.Application/EmailTemplates/Commands/Update/UpdateEmailTemplateCommandHandler.cs index 09803a6c..69ef1d26 100644 --- a/EnvelopeGenerator.Application/EmailTemplates/Commands/Update/UpdateEmailTemplateCommandHandler.cs +++ b/EnvelopeGenerator.Application/EmailTemplates/Commands/Update/UpdateEmailTemplateCommandHandler.cs @@ -34,13 +34,27 @@ public class UpdateEmailTemplateCommandHandler : IRequestHandler public async Task Handle(UpdateEmailTemplateCommand request, CancellationToken cancel) { - var temp = (request.EmailTemplateQuery?.Id is int id - ? await _repository.ReadOrDefaultAsync(t => t.Id == id, single: false, cancel) - : request!.EmailTemplateQuery!.Type is Common.Constants.EmailTemplateType type - ? await _repository.ReadOrDefaultAsync(t => t.Name == type.ToString(), single: false, cancel) - : throw new InvalidOperationException("Both id and type is null. Id: " + request.EmailTemplateQuery.Id + ". Type: " + request.EmailTemplateQuery.Type.ToString())) ?? throw new NotFoundException(); + EmailTemplateDto? temp; - if(request.Body is not null) + if (request.EmailTemplateQuery?.Id is int id) + { + temp = await _repository.ReadOrDefaultAsync(t => t.Id == id, single: false, cancel); + } + else if (request!.EmailTemplateQuery!.Type is Common.Constants.EmailTemplateType type) + { + temp = await _repository.ReadOrDefaultAsync(t => t.Name == type.ToString(), single: false, cancel); + } + else + { + throw new InvalidOperationException("Both id and type is null. Id: " + request.EmailTemplateQuery.Id +". Type: " + request.EmailTemplateQuery.Type.ToString()); + } + + if (temp == null) + { + throw new NotFoundException(); + } + + if (request.Body is not null) temp.Body = request.Body; if (request.Subject is not null) diff --git a/EnvelopeGenerator.GeneratorAPI/Controllers/EmailTemplateController.cs b/EnvelopeGenerator.GeneratorAPI/Controllers/EmailTemplateController.cs index e65cfd51..7e1efe52 100644 --- a/EnvelopeGenerator.GeneratorAPI/Controllers/EmailTemplateController.cs +++ b/EnvelopeGenerator.GeneratorAPI/Controllers/EmailTemplateController.cs @@ -15,7 +15,7 @@ using EnvelopeGenerator.Application.Exceptions; namespace EnvelopeGenerator.GeneratorAPI.Controllers; /// -/// Controller for managing email templates. +/// Controller for managing temp templates. /// Steuerung zur Verwaltung von E-Mail-Vorlagen. /// [Route("api/[controller]")] @@ -84,10 +84,10 @@ public class EmailTemplateController : ControllerBase } /// - /// Updates an email template or resets it if no update command is provided. + /// Updates an temp template or resets it if no update command is provided. /// Aktualisiert eine E-Mail-Vorlage oder setzt sie zurück, wenn kein Aktualisierungsbefehl angegeben ist. /// - /// Die E-Mail-Vorlagenabfrage. + /// Die E-Mail-Vorlagenabfrage. /// Der Aktualisierungsbefehl für die E-Mail-Vorlage. /// Wird auf Standardwert aktualisiert, wenn die Anfrage ohne http-Body gesendet wird. /// @@ -105,19 +105,22 @@ public class EmailTemplateController : ControllerBase /// Wenn der Benutzer nicht authentifiziert ist. /// Wenn die gesuchte Abfrage nicht gefunden wird. [HttpPut] - public async Task Update([FromQuery] EmailTemplateQuery email, [FromBody] UpdateEmailTemplateCommand? update = null) + public async Task Update([FromQuery] EmailTemplateQuery? temp = null, [FromBody] UpdateEmailTemplateCommand? update = null) { try { if (update is null) { - var reset = _mapper.Map(email); - await _mediator.Send(new ResetEmailTemplateCommand(email?.Id, email?.Type)); + await _mediator.Send(new ResetEmailTemplateCommand(temp)); return Ok(); } + else if(temp is null) + { + return BadRequest("No both id and type"); + } else { - var reset = _mapper.Map(email); + update.EmailTemplateQuery = temp; await _mediator.Send(update); return Ok(); } diff --git a/EnvelopeGenerator.GeneratorAPI/appsettings.json b/EnvelopeGenerator.GeneratorAPI/appsettings.json index 739f29c5..a4ab1aa8 100644 --- a/EnvelopeGenerator.GeneratorAPI/appsettings.json +++ b/EnvelopeGenerator.GeneratorAPI/appsettings.json @@ -169,7 +169,8 @@ "EnvelopeHistory": [ "TBSIG_ENVELOPE_HISTORY_AFT_INS" ], "EmailOut": [ "TBEMLP_EMAIL_OUT_AFT_INS", "TBEMLP_EMAIL_OUT_AFT_UPD" ], "EnvelopeReceiverReadOnly": [ "TBSIG_ENVELOPE_RECEIVER_READ_ONLY_UPD" ], - "Receiver": [] + "Receiver": [], + "EmailTemplate": [ "TBSIG_EMAIL_TEMPLATE_AFT_UPD" ] }, "MainPageTitle": null, "AnnotationParams": { diff --git a/EnvelopeGenerator.Web/appsettings.json b/EnvelopeGenerator.Web/appsettings.json index 4a0f5c70..6c058cd7 100644 --- a/EnvelopeGenerator.Web/appsettings.json +++ b/EnvelopeGenerator.Web/appsettings.json @@ -147,7 +147,8 @@ "EnvelopeHistory": [ "TBSIG_ENVELOPE_HISTORY_AFT_INS" ], "EmailOut": [ "TBEMLP_EMAIL_OUT_AFT_INS", "TBEMLP_EMAIL_OUT_AFT_UPD" ], "EnvelopeReceiverReadOnly": [ "TBSIG_ENVELOPE_RECEIVER_READ_ONLY_UPD" ], - "Receiver": [] + "Receiver": [], + "EmailTemplate": [ "TBSIG_EMAIL_TEMPLATE_AFT_UPD" ] }, "MainPageTitle": null, "AnnotationParams": { From 89d6abbb6cdbe36a58ffcc8a006e19cd4ea2530f Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Fri, 9 May 2025 09:56:42 +0200 Subject: [PATCH 88/94] Cleanup unused directives and add BadRequestException Removed unnecessary using directives in `UpdateEmailTemplateCommandHandler.cs`. Added a new `BadRequestException` class in `BadRequestException.cs` to handle bad request scenarios with customizable error messages. --- .../UpdateEmailTemplateCommandHandler.cs | 2 -- .../Exceptions/BadRequestException.cs | 22 +++++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 EnvelopeGenerator.Application/Exceptions/BadRequestException.cs diff --git a/EnvelopeGenerator.Application/EmailTemplates/Commands/Update/UpdateEmailTemplateCommandHandler.cs b/EnvelopeGenerator.Application/EmailTemplates/Commands/Update/UpdateEmailTemplateCommandHandler.cs index 69ef1d26..4912e2e6 100644 --- a/EnvelopeGenerator.Application/EmailTemplates/Commands/Update/UpdateEmailTemplateCommandHandler.cs +++ b/EnvelopeGenerator.Application/EmailTemplates/Commands/Update/UpdateEmailTemplateCommandHandler.cs @@ -3,8 +3,6 @@ using EnvelopeGenerator.Application.DTOs; using EnvelopeGenerator.Application.Exceptions; using EnvelopeGenerator.Domain.Entities; using MediatR; -using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; namespace EnvelopeGenerator.Application.EmailTemplates.Commands.Update; diff --git a/EnvelopeGenerator.Application/Exceptions/BadRequestException.cs b/EnvelopeGenerator.Application/Exceptions/BadRequestException.cs new file mode 100644 index 00000000..ce45ba75 --- /dev/null +++ b/EnvelopeGenerator.Application/Exceptions/BadRequestException.cs @@ -0,0 +1,22 @@ +namespace EnvelopeGenerator.Application.Exceptions; + +/// +/// Represents an exception that is thrown when a bad request is encountered. +/// +public class BadRequestException : Exception +{ + /// + /// Initializes a new instance of the class. + /// + public BadRequestException() + { + } + + /// + /// Initializes a new instance of the class with a specified error message. + /// + /// The message that describes the error. + public BadRequestException(string? message) : base(message) + { + } +} From 3fce0924867b05d01cdad5c607e85bb176c7a9d1 Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Fri, 9 May 2025 10:07:20 +0200 Subject: [PATCH 89/94] Enhance NotFoundException documentation Added XML documentation comments to the `NotFoundException` class and its constructors. This improves code readability and provides clear descriptions for developers using this exception. --- .../Exceptions/NotFoundException.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/EnvelopeGenerator.Application/Exceptions/NotFoundException.cs b/EnvelopeGenerator.Application/Exceptions/NotFoundException.cs index 2e887b44..67b58ef3 100644 --- a/EnvelopeGenerator.Application/Exceptions/NotFoundException.cs +++ b/EnvelopeGenerator.Application/Exceptions/NotFoundException.cs @@ -1,11 +1,21 @@ namespace EnvelopeGenerator.Application.Exceptions; +/// +/// Represents an exception that is thrown when a requested resource is not found. +/// public class NotFoundException : Exception { + /// + /// Initializes a new instance of the class. + /// public NotFoundException() { } + /// + /// Initializes a new instance of the class with a specified error message. + /// + /// The message that describes the error. public NotFoundException(string? message) : base(message) { } From 4ee5250b01823aec442fffba9953753b85205891 Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Fri, 9 May 2025 10:11:28 +0200 Subject: [PATCH 90/94] Add global exception handling middleware Introduce `ExceptionHandlingMiddleware` to capture and log exceptions globally in the application. This middleware returns appropriate JSON error responses based on specific exception types, such as `BadRequestException` and `NotFoundException`. The middleware is registered in `Program.cs` to ensure it processes all HTTP requests. --- .../Middleware/ExceptionHandlingMiddleware.cs | 90 +++++++++++++++++++ EnvelopeGenerator.GeneratorAPI/Program.cs | 3 + 2 files changed, 93 insertions(+) create mode 100644 EnvelopeGenerator.GeneratorAPI/Middleware/ExceptionHandlingMiddleware.cs diff --git a/EnvelopeGenerator.GeneratorAPI/Middleware/ExceptionHandlingMiddleware.cs b/EnvelopeGenerator.GeneratorAPI/Middleware/ExceptionHandlingMiddleware.cs new file mode 100644 index 00000000..2ac95f3e --- /dev/null +++ b/EnvelopeGenerator.GeneratorAPI/Middleware/ExceptionHandlingMiddleware.cs @@ -0,0 +1,90 @@ +namespace EnvelopeGenerator.GeneratorAPI.Middleware; + +using EnvelopeGenerator.Application.Exceptions; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Logging; +using System.Net; +using System.Text.Json; + +/// +/// Middleware for handling exceptions globally in the application. +/// Captures exceptions thrown during the request pipeline execution, +/// logs them, and returns an appropriate HTTP response with a JSON error message. +/// +public class ExceptionHandlingMiddleware +{ + private readonly RequestDelegate _next; + private readonly ILogger _logger; + + /// + /// Initializes a new instance of the class. + /// + /// The next middleware in the request pipeline. + /// The logger instance for logging exceptions. + public ExceptionHandlingMiddleware(RequestDelegate next, ILogger logger) + { + _next = next; + _logger = logger; + } + + /// + /// Invokes the middleware to handle the HTTP request. + /// + /// The HTTP context of the current request. + /// A task that represents the asynchronous operation. + public async Task InvokeAsync(HttpContext context) + { + try + { + await _next(context); // Continue down the pipeline + } + catch (Exception ex) + { + await HandleExceptionAsync(context, ex, _logger); + } + } + + /// + /// Handles exceptions by logging them and writing an appropriate JSON response. + /// + /// The HTTP context of the current request. + /// The exception that occurred. + /// The logger instance for logging the exception. + /// A task that represents the asynchronous operation. + private static async Task HandleExceptionAsync(HttpContext context, Exception exception, ILogger logger) + { + context.Response.ContentType = "application/json"; + var response = context.Response; + + string message; + int statusCode; + + switch (exception) + { + case BadRequestException badRequestEx: + statusCode = (int)HttpStatusCode.BadRequest; + message = badRequestEx.Message; + break; + + case NotFoundException notFoundEx: + statusCode = (int)HttpStatusCode.NotFound; + message = notFoundEx.Message; + break; + + default: + logger.LogError(exception, "Unhandled exception occurred."); + statusCode = (int)HttpStatusCode.InternalServerError; + message = "An unexpected error occurred."; + break; + } + + response.StatusCode = statusCode; + + var result = JsonSerializer.Serialize(new + { + error = message + }); + + await context.Response.WriteAsync(result); + } +} diff --git a/EnvelopeGenerator.GeneratorAPI/Program.cs b/EnvelopeGenerator.GeneratorAPI/Program.cs index 4c09198f..b41d4b62 100644 --- a/EnvelopeGenerator.GeneratorAPI/Program.cs +++ b/EnvelopeGenerator.GeneratorAPI/Program.cs @@ -15,6 +15,7 @@ using EnvelopeGenerator.GeneratorAPI.Models; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.IdentityModel.Tokens; using DigitalData.Core.Abstractions.Security.Extensions; +using EnvelopeGenerator.GeneratorAPI.Middleware; var builder = WebApplication.CreateBuilder(args); @@ -169,6 +170,8 @@ var app = builder.Build(); deferredProvider.Factory = () => app.Services; +app.UseMiddleware(); + app.MapOpenApi(); // Configure the HTTP request pipeline. From 972b258706e80a2b40fd650da572d7943f8c3ab1 Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Fri, 9 May 2025 10:35:04 +0200 Subject: [PATCH 91/94] Refactor exception handling in middleware Updated `HandleExceptionAsync` to set response status directly for each exception type and streamlined JSON serialization of error messages. --- .../Middleware/ExceptionHandlingMiddleware.cs | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/EnvelopeGenerator.GeneratorAPI/Middleware/ExceptionHandlingMiddleware.cs b/EnvelopeGenerator.GeneratorAPI/Middleware/ExceptionHandlingMiddleware.cs index 2ac95f3e..f9d700a1 100644 --- a/EnvelopeGenerator.GeneratorAPI/Middleware/ExceptionHandlingMiddleware.cs +++ b/EnvelopeGenerator.GeneratorAPI/Middleware/ExceptionHandlingMiddleware.cs @@ -54,37 +54,31 @@ public class ExceptionHandlingMiddleware private static async Task HandleExceptionAsync(HttpContext context, Exception exception, ILogger logger) { context.Response.ContentType = "application/json"; - var response = context.Response; string message; - int statusCode; switch (exception) { case BadRequestException badRequestEx: - statusCode = (int)HttpStatusCode.BadRequest; + context.Response.StatusCode = (int)HttpStatusCode.BadRequest; message = badRequestEx.Message; break; case NotFoundException notFoundEx: - statusCode = (int)HttpStatusCode.NotFound; + context.Response.StatusCode = (int)HttpStatusCode.NotFound; message = notFoundEx.Message; break; default: logger.LogError(exception, "Unhandled exception occurred."); - statusCode = (int)HttpStatusCode.InternalServerError; + context.Response.StatusCode = (int)HttpStatusCode.InternalServerError; message = "An unexpected error occurred."; break; } - response.StatusCode = statusCode; - - var result = JsonSerializer.Serialize(new + await context.Response.WriteAsync(JsonSerializer.Serialize(new { - error = message - }); - - await context.Response.WriteAsync(result); + message + })); } } From 5ce6c2539343bd32dc01f49056a58c1482be9d97 Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Fri, 9 May 2025 10:42:11 +0200 Subject: [PATCH 92/94] Refactor error handling in controllers Removed try-catch blocks from various controller methods to simplify error handling and allow exceptions to propagate naturally. Streamlined error logging and response handling using the `ThenAsync` method, enhancing code readability and reducing redundancy. Adjusted conditional checks for improved clarity. --- .../Controllers/AuthController.cs | 12 +- .../Controllers/EmailTemplateController.cs | 51 ++--- .../Controllers/EnvelopeController.cs | 126 +++++------ .../Controllers/EnvelopeReceiverController.cs | 200 ++++++++---------- .../Controllers/EnvelopeTypeController.cs | 22 +- .../Controllers/ReceiverController.cs | 34 ++- 6 files changed, 176 insertions(+), 269 deletions(-) diff --git a/EnvelopeGenerator.GeneratorAPI/Controllers/AuthController.cs b/EnvelopeGenerator.GeneratorAPI/Controllers/AuthController.cs index a5e8f748..23b9bfdb 100644 --- a/EnvelopeGenerator.GeneratorAPI/Controllers/AuthController.cs +++ b/EnvelopeGenerator.GeneratorAPI/Controllers/AuthController.cs @@ -118,16 +118,8 @@ public partial class AuthController : ControllerBase [HttpPost("logout")] public async Task Logout() { - try - { - await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); - return Ok(); - } - catch (Exception ex) - { - _logger.LogError(ex, "Unexpected error occurred.\n{ErrorMessage}", ex.Message); - return StatusCode(StatusCodes.Status500InternalServerError); - } + await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); + return Ok(); } /// diff --git a/EnvelopeGenerator.GeneratorAPI/Controllers/EmailTemplateController.cs b/EnvelopeGenerator.GeneratorAPI/Controllers/EmailTemplateController.cs index 7e1efe52..41927e85 100644 --- a/EnvelopeGenerator.GeneratorAPI/Controllers/EmailTemplateController.cs +++ b/EnvelopeGenerator.GeneratorAPI/Controllers/EmailTemplateController.cs @@ -63,23 +63,15 @@ public class EmailTemplateController : ControllerBase [HttpGet] public async Task Get([FromQuery] ReadEmailTemplateQuery? emailTemplate = null) { - try + if (emailTemplate is null || (emailTemplate.Id is null && emailTemplate.Type is null)) { - if (emailTemplate is null || (emailTemplate.Id is null && emailTemplate.Type is null)) - { - var temps = await _repository.ReadAllAsync(); - return Ok(_mapper.Map>(temps)); - } - else - { - var temp = await _mediator.Send(emailTemplate); - return temp is null ? NotFound() : Ok(temp); - } + var temps = await _repository.ReadAllAsync(); + return Ok(_mapper.Map>(temps)); } - catch (Exception ex) + else { - _logger.LogError(ex, "{Message}", ex.Message); - return StatusCode(StatusCodes.Status500InternalServerError); + var temp = await _mediator.Send(emailTemplate); + return temp is null ? NotFound() : Ok(temp); } } @@ -107,33 +99,20 @@ public class EmailTemplateController : ControllerBase [HttpPut] public async Task Update([FromQuery] EmailTemplateQuery? temp = null, [FromBody] UpdateEmailTemplateCommand? update = null) { - try + if (update is null) { - if (update is null) - { - await _mediator.Send(new ResetEmailTemplateCommand(temp)); - return Ok(); - } - else if(temp is null) - { - return BadRequest("No both id and type"); - } - else - { - update.EmailTemplateQuery = temp; - await _mediator.Send(update); - return Ok(); - } - + await _mediator.Send(new ResetEmailTemplateCommand(temp)); + return Ok(); } - catch(NotFoundException) + else if (temp is null) { - return BadRequest(); + return BadRequest("No both id and type"); } - catch (Exception ex) + else { - _logger.LogError(ex, "An unexpected error occurred. {message}", ex.Message); - return new StatusCodeResult(StatusCodes.Status500InternalServerError); + update.EmailTemplateQuery = temp; + await _mediator.Send(update); + return Ok(); } } } diff --git a/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeController.cs b/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeController.cs index 13faf709..9deca564 100644 --- a/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeController.cs +++ b/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeController.cs @@ -61,37 +61,29 @@ public class EnvelopeController : ControllerBase [HttpGet] public async Task GetAsync([FromQuery] ReadEnvelopeQuery envelope) { - try - { - if (User.GetId() is int intId) - return await _envelopeService.ReadByUserAsync(intId, min_status: envelope.Status, max_status: envelope.Status).ThenAsync( - Success: envelopes => - { - if(envelope.Id is int id) - envelopes = envelopes.Where(e => e.Id == id); + if (User.GetId() is int intId) + return await _envelopeService.ReadByUserAsync(intId, min_status: envelope.Status, max_status: envelope.Status).ThenAsync( + Success: envelopes => + { + if (envelope.Id is int id) + envelopes = envelopes.Where(e => e.Id == id); - if(envelope.Status is int status) - envelopes = envelopes.Where(e => e.Status == status); + if (envelope.Status is int status) + envelopes = envelopes.Where(e => e.Status == status); - if (envelope.Uuid is string uuid) - envelopes = envelopes.Where(e => e.Uuid == uuid); + if (envelope.Uuid is string uuid) + envelopes = envelopes.Where(e => e.Uuid == uuid); - return Ok(envelopes); - }, - Fail: IActionResult (msg, ntc) => - { - _logger.LogNotice(ntc); - return StatusCode(StatusCodes.Status500InternalServerError); - }); - else - { - _logger.LogError("Trotz erfolgreicher Autorisierung wurde die Benutzer-ID nicht als Ganzzahl erkannt. Dies könnte auf eine fehlerhafte Erstellung der Anspruchsliste zurückzuführen sein."); - return StatusCode(StatusCodes.Status500InternalServerError); - } - } - catch (Exception ex) + return Ok(envelopes); + }, + Fail: IActionResult (msg, ntc) => + { + _logger.LogNotice(ntc); + return StatusCode(StatusCodes.Status500InternalServerError); + }); + else { - _logger.LogError(ex, "{Message}", ex.Message); + _logger.LogError("Trotz erfolgreicher Autorisierung wurde die Benutzer-ID nicht als Ganzzahl erkannt. Dies könnte auf eine fehlerhafte Erstellung der Anspruchsliste zurückzuführen sein."); return StatusCode(StatusCodes.Status500InternalServerError); } } @@ -108,43 +100,35 @@ public class EnvelopeController : ControllerBase [HttpGet("doc-result")] public async Task GetDocResultAsync([FromQuery] int id, [FromQuery] bool view = false) { - try - { - if (User.GetId() is int intId) - return await _envelopeService.ReadByUserAsync(intId).ThenAsync( - Success: envelopes => - { - var envelope = envelopes.Where(e => e.Id == id).FirstOrDefault(); + if (User.GetId() is int intId) + return await _envelopeService.ReadByUserAsync(intId).ThenAsync( + Success: envelopes => + { + var envelope = envelopes.Where(e => e.Id == id).FirstOrDefault(); - if (envelope is null) - return NotFound("Envelope not available."); - else if (envelope?.DocResult is null) - return NotFound("The document has not been fully signed or the result has not yet been released."); - else + if (envelope is null) + return NotFound("Envelope not available."); + else if (envelope?.DocResult is null) + return NotFound("The document has not been fully signed or the result has not yet been released."); + else + { + if (view) { - if (view) - { - Response.Headers.Append("Content-Disposition", "inline; filename=\"" + envelope.Uuid + ".pdf\""); - return File(envelope.DocResult, "application/pdf"); - } - else - return File(envelope.DocResult, "application/pdf", $"{envelope.Uuid}.pdf"); + Response.Headers.Append("Content-Disposition", "inline; filename=\"" + envelope.Uuid + ".pdf\""); + return File(envelope.DocResult, "application/pdf"); } - }, - Fail: IActionResult (msg, ntc) => - { - _logger.LogNotice(ntc); - return StatusCode(StatusCodes.Status500InternalServerError); - }); - else - { - _logger.LogError("Trotz erfolgreicher Autorisierung wurde die Benutzer-ID nicht als Ganzzahl erkannt. Dies könnte auf eine fehlerhafte Erstellung der Anspruchsliste zurückzuführen sein."); - return StatusCode(StatusCodes.Status500InternalServerError); - } - } - catch (Exception ex) + else + return File(envelope.DocResult, "application/pdf", $"{envelope.Uuid}.pdf"); + } + }, + Fail: IActionResult (msg, ntc) => + { + _logger.LogNotice(ntc); + return StatusCode(StatusCodes.Status500InternalServerError); + }); + else { - _logger.LogError(ex, "{Message}", ex.Message); + _logger.LogError("Trotz erfolgreicher Autorisierung wurde die Benutzer-ID nicht als Ganzzahl erkannt. Dies könnte auf eine fehlerhafte Erstellung der Anspruchsliste zurückzuführen sein."); return StatusCode(StatusCodes.Status500InternalServerError); } } @@ -159,23 +143,15 @@ public class EnvelopeController : ControllerBase [HttpPost] public async Task CreateAsync([FromQuery] CreateEnvelopeCommand envelope) { - try - { - envelope.UserId = User.GetId(); - var res = await _mediator.Send(envelope); - - if (res is null) - { - _logger.LogError("Failed to create envelope. Envelope details: {EnvelopeDetails}", JsonConvert.SerializeObject(envelope)); - return StatusCode(StatusCodes.Status500InternalServerError); - } - else - return Ok(res); - } - catch (Exception ex) + envelope.UserId = User.GetId(); + var res = await _mediator.Send(envelope); + + if (res is null) { - _logger.LogError(ex, "{Message}", ex.Message); + _logger.LogError("Failed to create envelope. Envelope details: {EnvelopeDetails}", JsonConvert.SerializeObject(envelope)); return StatusCode(StatusCodes.Status500InternalServerError); } + else + return Ok(res); } } diff --git a/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeReceiverController.cs b/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeReceiverController.cs index 39fc6587..922927f1 100644 --- a/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeReceiverController.cs +++ b/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeReceiverController.cs @@ -84,37 +84,29 @@ public class EnvelopeReceiverController : ControllerBase [HttpGet] public async Task GetEnvelopeReceiver([FromQuery] ReadEnvelopeReceiverQuery envelopeReceiver) { - try - { - var username = User.GetUsernameOrDefault(); - - if (username is null) - { - _logger.LogError(@"Envelope Receiver dto cannot be sent because username claim is null. Potential authentication and authorization error. The value of other claims are [id: {id}], [username: {username}], [name: {name}], [prename: {prename}], [email: {email}].", - User.GetId(), User.GetUsernameOrDefault(), User.GetNameOrDefault(), User.GetPrenameOrDefault(), User.GetEmailOrDefault()); - return StatusCode(StatusCodes.Status500InternalServerError); - } + var username = User.GetUsernameOrDefault(); - return await _erService.ReadByUsernameAsync( - username: username, - min_status: envelopeReceiver.Status?.Min, - max_status:envelopeReceiver.Status?.Max, - envelopeQuery: envelopeReceiver.Envelope, - receiverQuery: envelopeReceiver.Receiver, - ignore_statuses: envelopeReceiver.Status?.Ignore ?? Array.Empty()) - .ThenAsync( - Success: Ok, - Fail: IActionResult (msg, ntc) => - { - _logger.LogNotice(ntc); - return StatusCode(StatusCodes.Status500InternalServerError, msg); - }); - } - catch (Exception ex) + if (username is null) { - _logger.LogError(ex, "An unexpected error occurred. {message}", ex.Message); - return new StatusCodeResult(StatusCodes.Status500InternalServerError); + _logger.LogError(@"Envelope Receiver dto cannot be sent because username claim is null. Potential authentication and authorization error. The value of other claims are [id: {id}], [username: {username}], [name: {name}], [prename: {prename}], [email: {email}].", + User.GetId(), User.GetUsernameOrDefault(), User.GetNameOrDefault(), User.GetPrenameOrDefault(), User.GetEmailOrDefault()); + return StatusCode(StatusCodes.Status500InternalServerError); } + + return await _erService.ReadByUsernameAsync( + username: username, + min_status: envelopeReceiver.Status?.Min, + max_status: envelopeReceiver.Status?.Max, + envelopeQuery: envelopeReceiver.Envelope, + receiverQuery: envelopeReceiver.Receiver, + ignore_statuses: envelopeReceiver.Status?.Ignore ?? Array.Empty()) + .ThenAsync( + Success: Ok, + Fail: IActionResult (msg, ntc) => + { + _logger.LogNotice(ntc); + return StatusCode(StatusCodes.Status500InternalServerError, msg); + }); } /// @@ -133,24 +125,16 @@ public class EnvelopeReceiverController : ControllerBase [HttpGet("salute")] public async Task GetReceiverName([FromQuery] ReadReceiverNameQuery receiverName) { - try - { - return await _erService.ReadLastUsedReceiverNameByMail(receiverName.EmailAddress).ThenAsync( - Success: res => res is null ? Ok(string.Empty) : Ok(res), - Fail: IActionResult (msg, ntc) => - { - if (ntc.HasFlag(Flag.NotFound)) - return NotFound(); - - _logger.LogNotice(ntc); - return StatusCode(StatusCodes.Status500InternalServerError); - }); - } - catch (Exception ex) - { - _logger.LogError(ex, "{message}", ex.Message); - return StatusCode(StatusCodes.Status500InternalServerError); - } + return await _erService.ReadLastUsedReceiverNameByMail(receiverName.EmailAddress).ThenAsync( + Success: res => res is null ? Ok(string.Empty) : Ok(res), + Fail: IActionResult (msg, ntc) => + { + if (ntc.HasFlag(Flag.NotFound)) + return NotFound(); + + _logger.LogNotice(ntc); + return StatusCode(StatusCodes.Status500InternalServerError); + }); } /// @@ -194,44 +178,42 @@ public class EnvelopeReceiverController : ControllerBase [HttpPost] public async Task CreateAsync([FromBody] CreateEnvelopeReceiverCommand request) { - try - { - CancellationToken cancel = default; - int userId = User.GetId(); + CancellationToken cancel = default; + int userId = User.GetId(); - #region Create Envelope - var envelope = await _envelopeExecutor.CreateEnvelopeAsync(userId, request.Title, request.Message, request.TFAEnabled, cancel); - #endregion + #region Create Envelope + var envelope = await _envelopeExecutor.CreateEnvelopeAsync(userId, request.Title, request.Message, request.TFAEnabled, cancel); + #endregion - #region Add receivers - List sentReceivers = new(); - List unsentReceivers = new(); + #region Add receivers + List sentReceivers = new(); + List unsentReceivers = new(); - foreach (var receiver in request.Receivers) - { - var envelopeReceiver = await _erExecutor.AddEnvelopeReceiverAsync(envelope.Uuid, receiver.EmailAddress, receiver.Salution, receiver.PhoneNumber, cancel); + foreach (var receiver in request.Receivers) + { + var envelopeReceiver = await _erExecutor.AddEnvelopeReceiverAsync(envelope.Uuid, receiver.EmailAddress, receiver.Salution, receiver.PhoneNumber, cancel); - if (envelopeReceiver is null) - unsentReceivers.Add(receiver); - else - sentReceivers.Add(envelopeReceiver); - } + if (envelopeReceiver is null) + unsentReceivers.Add(receiver); + else + sentReceivers.Add(envelopeReceiver); + } - var res = _mapper.Map(envelope); - res.UnsentReceivers = unsentReceivers; - res.SentReceiver = _mapper.Map>(sentReceivers.Select(er => er.Receiver)); - #endregion + var res = _mapper.Map(envelope); + res.UnsentReceivers = unsentReceivers; + res.SentReceiver = _mapper.Map>(sentReceivers.Select(er => er.Receiver)); + #endregion - #region Add document - var document = await _documentExecutor.CreateDocumentAsync(request.Document.DataAsBase64, envelope.Uuid, cancel); + #region Add document + var document = await _documentExecutor.CreateDocumentAsync(request.Document.DataAsBase64, envelope.Uuid, cancel); - if(document is null) - return StatusCode(StatusCodes.Status500InternalServerError, "Document creation is failed."); - #endregion + if (document is null) + return StatusCode(StatusCodes.Status500InternalServerError, "Document creation is failed."); + #endregion - #region Add document element - // @DOC_ID, @RECEIVER_ID, @POSITION_X, @POSITION_Y, @PAGE - string sql = @" + #region Add document element + // @DOC_ID, @RECEIVER_ID, @POSITION_X, @POSITION_Y, @PAGE + string sql = @" DECLARE @OUT_SUCCESS bit; EXEC [dbo].[PRSIG_API_ADD_DOC_RECEIVER_ELEM] @@ -244,30 +226,30 @@ public class EnvelopeReceiverController : ControllerBase SELECT @OUT_SUCCESS as [@OUT_SUCCESS];"; - foreach(var rcv in res.SentReceiver) - foreach(var sign in request.Receivers.Where(r => r.EmailAddress == rcv.EmailAddress).FirstOrDefault()?.Signatures ?? Array.Empty()) + foreach (var rcv in res.SentReceiver) + foreach (var sign in request.Receivers.Where(r => r.EmailAddress == rcv.EmailAddress).FirstOrDefault()?.Signatures ?? Array.Empty()) + { + using (SqlConnection conn = new(_cnnStr)) { - using (SqlConnection conn = new(_cnnStr)) - { - conn.Open(); + conn.Open(); - var formattedSQL = string.Format(sql, document.Id.ToSqlParam(), rcv.Id.ToSqlParam(), sign.X.ToSqlParam(), sign.Y.ToSqlParam(), sign.Page.ToSqlParam()); + var formattedSQL = string.Format(sql, document.Id.ToSqlParam(), rcv.Id.ToSqlParam(), sign.X.ToSqlParam(), sign.Y.ToSqlParam(), sign.Page.ToSqlParam()); - using SqlCommand cmd = new SqlCommand(formattedSQL, conn); - cmd.CommandType = CommandType.Text; + using SqlCommand cmd = new SqlCommand(formattedSQL, conn); + cmd.CommandType = CommandType.Text; - using SqlDataReader reader = cmd.ExecuteReader(); - if (reader.Read()) - { - bool outSuccess = reader.GetBoolean(0); - } + using SqlDataReader reader = cmd.ExecuteReader(); + if (reader.Read()) + { + bool outSuccess = reader.GetBoolean(0); } } - #endregion + } + #endregion - #region Create history - // ENV_UID, STATUS_ID, USER_ID, - string sql_hist = @" + #region Create history + // ENV_UID, STATUS_ID, USER_ID, + string sql_hist = @" USE [DD_ECM] DECLARE @OUT_SUCCESS bit; @@ -280,32 +262,26 @@ public class EnvelopeReceiverController : ControllerBase SELECT @OUT_SUCCESS as [@OUT_SUCCESS];"; - using (SqlConnection conn = new(_cnnStr)) + using (SqlConnection conn = new(_cnnStr)) + { + conn.Open(); + var formattedSQL_hist = string.Format(sql_hist, envelope.Uuid.ToSqlParam(), 1003.ToSqlParam(), userId.ToSqlParam()); + using (SqlCommand cmd = new SqlCommand(formattedSQL_hist, conn)) { - conn.Open(); - var formattedSQL_hist = string.Format(sql_hist, envelope.Uuid.ToSqlParam(), 1003.ToSqlParam(), userId.ToSqlParam()); - using (SqlCommand cmd = new SqlCommand(formattedSQL_hist, conn)) - { - cmd.CommandType = CommandType.Text; + cmd.CommandType = CommandType.Text; - using (SqlDataReader reader = cmd.ExecuteReader()) + using (SqlDataReader reader = cmd.ExecuteReader()) + { + if (reader.Read()) { - if (reader.Read()) - { - bool outSuccess = reader.GetBoolean(0); - } + bool outSuccess = reader.GetBoolean(0); } } } - #endregion - - return Ok(res); - } - catch (Exception ex) - { - _logger.LogError(ex, "{Message}", ex.Message); - return StatusCode(StatusCodes.Status500InternalServerError); } + #endregion + + return Ok(res); } /// diff --git a/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeTypeController.cs b/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeTypeController.cs index bf2ea067..c49d9a3b 100644 --- a/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeTypeController.cs +++ b/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeTypeController.cs @@ -21,20 +21,12 @@ public class EnvelopeTypeController : ControllerBase [HttpGet] public async Task GetAllAsync() { - try - { - return await _service.ReadAllAsync().ThenAsync( - Success: Ok, - Fail: IActionResult (msg, ntc) => - { - _logger.LogNotice(ntc); - return ntc.HasFlag(Flag.NotFound) ? NotFound() : StatusCode(StatusCodes.Status500InternalServerError); - }); - } - catch (Exception ex) - { - _logger.LogError(ex, "{Message}", ex.Message); - return StatusCode(StatusCodes.Status500InternalServerError); - } + return await _service.ReadAllAsync().ThenAsync( + Success: Ok, + Fail: IActionResult (msg, ntc) => + { + _logger.LogNotice(ntc); + return ntc.HasFlag(Flag.NotFound) ? NotFound() : StatusCode(StatusCodes.Status500InternalServerError); + }); } } \ No newline at end of file diff --git a/EnvelopeGenerator.GeneratorAPI/Controllers/ReceiverController.cs b/EnvelopeGenerator.GeneratorAPI/Controllers/ReceiverController.cs index 95243234..0062a43f 100644 --- a/EnvelopeGenerator.GeneratorAPI/Controllers/ReceiverController.cs +++ b/EnvelopeGenerator.GeneratorAPI/Controllers/ReceiverController.cs @@ -41,28 +41,20 @@ public class ReceiverController : CRUDControllerBaseWithErrorHandling - { - return NotFound(); - }); + if (receiver.Id is int id) + return await _service.ReadByIdAsync(id).ThenAsync( + Success: Ok, + Fail: IActionResult (msg, ntc) => + { + return NotFound(); + }); - return await _service.ReadByAsync(emailAddress: receiver.EmailAddress, signature: receiver.Signature).ThenAsync( - Success: Ok, - Fail: IActionResult (msg, ntc) => - { - return NotFound(); - }); - } - catch (Exception ex) - { - _logger.LogError(ex, "{Message}", ex.Message); - return StatusCode(StatusCodes.Status500InternalServerError); - } + return await _service.ReadByAsync(emailAddress: receiver.EmailAddress, signature: receiver.Signature).ThenAsync( + Success: Ok, + Fail: IActionResult (msg, ntc) => + { + return NotFound(); + }); } #region REMOVED ENDPOINTS From 7f97fd3113cb480c05e621995e45294cd8cfa7bd Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Fri, 9 May 2025 10:53:44 +0200 Subject: [PATCH 93/94] Refactor ReadReceiverNameQuery and improve validation - Removed parameters from ReadReceiverNameQuery, simplifying its structure. - Added null check for EmailAddress in GetReceiverName method to enhance input validation. --- .../Envelopes/Queries/ReceiverName/ReadReceiverNameQuery.cs | 4 +--- .../Controllers/EnvelopeReceiverController.cs | 3 +++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/EnvelopeGenerator.Application/Envelopes/Queries/ReceiverName/ReadReceiverNameQuery.cs b/EnvelopeGenerator.Application/Envelopes/Queries/ReceiverName/ReadReceiverNameQuery.cs index fe114dd8..55b477a3 100644 --- a/EnvelopeGenerator.Application/Envelopes/Queries/ReceiverName/ReadReceiverNameQuery.cs +++ b/EnvelopeGenerator.Application/Envelopes/Queries/ReceiverName/ReadReceiverNameQuery.cs @@ -6,8 +6,6 @@ namespace EnvelopeGenerator.Application.Envelopes.Queries.ReceiverName; /// Eine Abfrage, um die zuletzt verwendete Anrede eines Empfängers zu ermitteln, /// damit diese für zukünftige Umschläge wiederverwendet werden kann. /// -/// Der Umschlag, für den die Anrede des Empfängers ermittelt werden soll. -/// Gibt an, ob nur die zuletzt verwendete Anrede zurückgegeben werden soll. -public record ReadReceiverNameQuery(EnvelopeQuery? Envelope = null, bool OnlyLast = true) : ReadReceiverQuery +public record ReadReceiverNameQuery() : ReadReceiverQuery { } diff --git a/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeReceiverController.cs b/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeReceiverController.cs index 922927f1..2fa2245b 100644 --- a/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeReceiverController.cs +++ b/EnvelopeGenerator.GeneratorAPI/Controllers/EnvelopeReceiverController.cs @@ -125,6 +125,9 @@ public class EnvelopeReceiverController : ControllerBase [HttpGet("salute")] public async Task GetReceiverName([FromQuery] ReadReceiverNameQuery receiverName) { + if (receiverName.EmailAddress is null) + return BadRequest(); + return await _erService.ReadLastUsedReceiverNameByMail(receiverName.EmailAddress).ThenAsync( Success: res => res is null ? Ok(string.Empty) : Ok(res), Fail: IActionResult (msg, ntc) => From 93019362b79cdef074917954296cebd6c2b88a54 Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Fri, 9 May 2025 13:54:30 +0200 Subject: [PATCH 94/94] Bump version to 1.2.2 and update launch settings Updated the version in `EnvelopeGenerator.GeneratorAPI.csproj` from `1.2.1` to `1.2.2` for all version elements. Modified the `applicationUrl` in `launchSettings.json` to change the HTTPS port from `7174` to `8088`, while keeping the HTTP port at `5131`. --- .../EnvelopeGenerator.GeneratorAPI.csproj | 6 +++--- .../Properties/launchSettings.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/EnvelopeGenerator.GeneratorAPI/EnvelopeGenerator.GeneratorAPI.csproj b/EnvelopeGenerator.GeneratorAPI/EnvelopeGenerator.GeneratorAPI.csproj index d2c598e0..418df9b2 100644 --- a/EnvelopeGenerator.GeneratorAPI/EnvelopeGenerator.GeneratorAPI.csproj +++ b/EnvelopeGenerator.GeneratorAPI/EnvelopeGenerator.GeneratorAPI.csproj @@ -10,9 +10,9 @@ Digital Data GmbH Digital Data GmbH EnvelopeGenerator.GeneratorAPI - 1.2.1 - 1.2.1 - 1.2.1 + 1.2.2 + 1.2.2 + 1.2.2 Copyright © 2025 Digital Data GmbH. All rights reserved. bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml diff --git a/EnvelopeGenerator.GeneratorAPI/Properties/launchSettings.json b/EnvelopeGenerator.GeneratorAPI/Properties/launchSettings.json index ec221d16..1b89a1c9 100644 --- a/EnvelopeGenerator.GeneratorAPI/Properties/launchSettings.json +++ b/EnvelopeGenerator.GeneratorAPI/Properties/launchSettings.json @@ -24,7 +24,7 @@ "dotnetRunMessages": true, "launchBrowser": false, "launchUrl": "swagger", - "applicationUrl": "https://localhost:7174;http://localhost:5131", + "applicationUrl": "https://localhost:8088;http://localhost:5131", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" }