Compare commits

...

21 Commits

Author SHA1 Message Date
Developer 02
f877910a73 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`.
2025-05-07 02:18:33 +02:00
Developer 02
f6f4137332 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.
2025-05-07 02:17:49 +02:00
Developer 02
486b717801 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<ConnectionString>` 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.
2025-05-07 02:13:26 +02:00
Developer 02
f2a09ea10e 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.
2025-05-07 01:57:22 +02:00
Developer 02
cc86e5fadd 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.
2025-05-07 01:32:21 +02:00
Developer 02
bce29aa31a 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.
2025-05-07 01:29:21 +02:00
Developer 02
95a1fd1355 Add DocumentCreateReadSQL class for SQL operations
Introduces the `DocumentCreateReadSQL` class in the
`EnvelopeGenerator.Application.SQL` namespace, implementing
the `ISQL<EnvelopeDocument>` 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.
2025-05-07 01:20:27 +02:00
Developer 02
613b2130a5 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.
2025-05-07 01:02:30 +02:00
Developer 02
e4eb3e1192 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.
2025-05-07 00:35:43 +02:00
Developer 02
1c4f7f2386 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.
2025-05-07 00:27:34 +02:00
Developer 02
a7e4d6e58f 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.
2025-05-07 00:17:18 +02:00
Developer 02
b9c4f7da1c 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.
2025-05-07 00:03:01 +02:00
Developer 02
cec79e5b6d 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.
2025-05-06 23:26:08 +02:00
Developer 02
2f08991eeb 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.
2025-05-06 23:25:34 +02:00
Developer 02
21859ca6e6 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.
2025-05-06 23:23:33 +02:00
Developer 02
27d9a149bc 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.
2025-05-06 22:54:19 +02:00
Developer 02
a76a079736 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.
2025-05-06 22:26:26 +02:00
Developer 02
10341fd3cc 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<EmailTemplate>`, 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`.
2025-05-06 20:58:52 +02:00
Developer 02
05de44bc13 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.
2025-05-06 19:23:01 +02:00
Developer 02
d2c45f71a0 Refactor email template query handling with MediatR
- Updated `ReadEmailTemplateQuery` to implement `IRequest<ReadEmailTemplateResponse?>`.
- 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.
2025-05-06 19:06:21 +02:00
Developer 02
42451a767b 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<EnvelopeExecutor>` parameter from the constructor, simplifying its signature while retaining necessary dependencies.
2025-05-06 17:06:24 +02:00
30 changed files with 681 additions and 73 deletions

View File

@@ -0,0 +1,18 @@
using EnvelopeGenerator.Domain.Entities;
namespace EnvelopeGenerator.Application.Contracts.SQLExecutor;
/// <summary>
///
/// </summary>
public interface IDocumentExecutor
{
/// <summary>
///
/// </summary>
/// <param name="base64"></param>
/// <param name="envelope_uuid"></param>
/// <param name="cancellation"></param>
/// <returns></returns>
Task<EnvelopeDocument> CreateDocumentAsync(string base64, string envelope_uuid, CancellationToken cancellation = default);
}

View File

@@ -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<EnvelopeReceiverDt
Task<DataResult<bool>> IsExisting(string envelopeReceiverId);
Task<DataResult<IEnumerable<EnvelopeReceiverDto>>> ReadByUsernameAsync(string username, int? min_status = null, int? max_status = null, params int[] ignore_statuses);
Task<DataResult<IEnumerable<EnvelopeReceiverDto>>> ReadByUsernameAsync(string username, int? min_status = null, int? max_status = null, EnvelopeQuery? envelopeQuery = null, ReadReceiverQuery? receiverQuery = null, params int[] ignore_statuses);
Task<DataResult<string?>> ReadLastUsedReceiverNameByMail(string mail);

View File

@@ -3,10 +3,30 @@ using Microsoft.AspNetCore.Mvc;
namespace EnvelopeGenerator.Application.DTOs
{
/// <summary>
///
/// </summary>
[ApiExplorerSettings(IgnoreApi = true)]
public record EmailTemplateDto(
int Id,
string Name,
string Body,
string Subject) : IUnique<int>;
public record EmailTemplateDto : IUnique<int>
{
/// <summary>
///
/// </summary>
public int Id{ get; init; }
/// <summary>
///
/// </summary>
public required string Name { get; init; }
/// <summary>
///
/// </summary>
public required string Body { get; set; }
/// <summary>
///
/// </summary>
public required string Subject { get; set; }
};
}

View File

@@ -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.
/// </param>
public record ResetEnvelopeTemplateCommand(int? Id, Constants.EmailTemplateType? Type) : EmailTemplateQuery(Id, Type);
public record ResetEnvelopeTemplateCommand(int? Id = null, Constants.EmailTemplateType? Type = null) : EmailTemplateQuery(Id, Type), IRequest;

View File

@@ -0,0 +1,108 @@
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;
/// <summary>
///
/// </summary>
public class ResetEnvelopeTemplateCommandHandler : IRequestHandler<ResetEnvelopeTemplateCommand>
{
private readonly IRepository<EmailTemplate> _repository;
/// <summary>
///
/// </summary>
/// <param name="repository"></param>
public ResetEnvelopeTemplateCommandHandler(IRepository<EmailTemplate> repository)
{
_repository = repository;
}
public async Task Handle(ResetEnvelopeTemplateCommand request, CancellationToken cancel)
{
var temps = request.Id is not null
? await _repository.ReadAllAsync<EmailTemplateDto>(t => t.Id == request.Id, cancel)
: request.Type is not null
? await _repository.ReadAllAsync<EmailTemplateDto>(t => t.Name == request.Type.ToString(), cancel)
: await _repository.ReadAllAsync<EmailTemplateDto>(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);
}
}
/// <summary>
///
/// </summary>
public static readonly IEnumerable<EmailTemplateDto> Defaults = new List<EmailTemplateDto>()
{
new(){
Id = 1,
Name = "DocumentReceived",
Body = "Guten Tag [NAME_RECEIVER],<br />\r\n<br /><B><I>\r\n[NAME_SENDER]</I></B> hat Ihnen ein Dokument zum [SIGNATURE_TYPE] gesendet.<br />\r\n<br />\r\nÜber den folgenden Link können Sie das Dokument einsehen und elektronisch unterschreiben: <a href=\"[LINK_TO_DOCUMENT]\">[LINK_TO_DOCUMENT_TEXT]</a><br />\r\n<br />\r\n[MESSAGE]<br />\r\n<br />\r\nMit freundlichen Grüßen<br />\r\n<br />\r\n[NAME_PORTAL]",
Subject = "Dokument erhalten: '[DOCUMENT_TITLE]'"
},
new(){
Id = 2,
Name = "DocumentDeleted",
Body = "Guten Tag [NAME_RECEIVER],<br />\r\n<br /><B><I>\r\n[NAME_SENDER]</I></B> hat den Umschlag <B><I>'[DOCUMENT_TITLE]'</I></B> gelöscht/zurückgezogen.<br /><p>\rBegründung: <br /> <I>[REASON]</I> <p>\r\n<br />\r\nMit freundlichen Grüßen<br />\r\n<br />\r\n[NAME_PORTAL]",
Subject = "Umschlag zurückgezogen: '[DOCUMENT_TITLE]'"
},
new(){
Id = 3,
Name = "DocumentSigned",
Body = "Guten Tag [NAME_RECEIVER],<br />\r\n<br />\r\nhiermit bestätigen wir Ihnen die erfolgreiche Signatur für den Vorgang <B><I>'[DOCUMENT_TITLE]'</I></B>.<br />\r\nWenn alle Vertragspartner unterzeichnet haben, erhalten Sie ebenfalls per email ein unterschriebenes Exemplar mit dem Signierungszertifikat!\r\n<br />\r\nMit freundlichen Grüßen<br />\r\n<br />\r\n[NAME_PORTAL]",
Subject = "Dokument unterschrieben: '[DOCUMENT_TITLE]'"
},
new(){
Id = 4,
Name = "DocumentCompleted",
Body = "Guten Tag [NAME_RECEIVER],<br />\r\n<br />\r\nDer Signaturvorgang <B><I>'[DOCUMENT_TITLE]'</I></B> wurde erfolgreich abgeschlossen.<br />\r\n<br />\r\nSie erhalten das Dokument mit einem detaillierten Ergebnisbericht als Anhang zu dieser Email.<br />\r\n<br />\r\nMit freundlichen Grüßen<br />\r\n<br />\r\n[NAME_PORTAL]",
Subject = "Umschlag abgeschlossen: '[DOCUMENT_TITLE]'"
},
new(){
Id = 5,
Name = "DocumentAccessCodeReceived",
Body = "Guten Tag [NAME_RECEIVER],<br />\r\n<br /><B><I>\r\n[NAME_SENDER]</I></B> hat Ihnen ein Dokument zum [SIGNATURE_TYPE] gesendet. <br />\r\n<br />\r\nVerwenden Sie den folgenden Zugriffscode, um das Dokument einzusehen:<br />\r\n<br />\r\n[DOCUMENT_ACCESS_CODE]<br />\r\n<br />\r\nMit freundlichen Grüßen<br />\r\n<br />\r\n[NAME_PORTAL]",
Subject = "Zugriffscode für Dokument erhalten: '[DOCUMENT_TITLE]'"
},
new(){
Id = 6,
Name = "DocumentRejected_ADM",
Body = "Guten Tag [NAME_SENDER],<p><B><I>[NAME_RECEIVER]</I></B> hat den Umschlag <B><I>'[DOCUMENT_TITLE]'</I></B> mit folgendem Grund abgelehnt: <p>\r\n[REASON] \r\n<p>Der Umschlag wurde auf den Status Rejected gesetzt. <p> \r\nMit freundlichen Grüßen<br />\r\n<br />\r\n[NAME_PORTAL]",
Subject = "'[DOCUMENT_TITLE]' - Unterzeichnungsvorgang zurückgezogen"
},
new(){
Id = 9,
Name = "DocumentRejected_REC",
Body = "Guten Tag [NAME_RECEIVER],\r\n<p>Hiermit bestätigen wir Ihnen die Ablehnung des Unterzeichnungsvorganges <B><I>'[DOCUMENT_TITLE]'</I></B>!<p>Der Vertragsinhaber <B><I>[NAME_SENDER]</I></B> wurde über die Ablehnung informiert. <p> \r\nMit freundlichen Grüßen<br />\r\n<br />\r\n[NAME_PORTAL]",
Subject = "'[DOCUMENT_TITLE]' - Bestätigung Ablehnung"
},
new(){
Id = 10,
Name = "DocumentRejected_REC_2",
Body = "Guten Tag [NAME_RECEIVER],\r\n<p>Der Unterzeichnungsvorganges <B><I>'[DOCUMENT_TITLE]'</I></B> wurde durch einen anderen Vertragspartner abgelehnt! Ihre notwendige Unterzeichnung wurde verworfen.<p> Der Vertragsinhaber <B><I>[NAME_SENDER]</I></B> wird sich bei Bedarf mit Ihnen in Verbindung setzen. <p> \r\nMit freundlichen Grüßen<br />\r\n<br />\r\n[NAME_PORTAL]",
Subject = "'[DOCUMENT_TITLE]' - Unterzeichnungsvorgang abgelehnt."
},
new(){
Id = 11,
Name = "DocumentShared",
Body = "Guten Tag,<br /> <br /><B><I> [NAME_RECEIVER]</I></B> hat Ihnen ein Dokument zum Ansehen gesendet.<br /> <br /> Über den folgenden Link können Sie das Dokument einsehen: <a href=\"[LINK_TO_DOCUMENT]\">[LINK_TO_DOCUMENT_TEXT]</a><br /> <br /> <br /> Mit freundlichen Grüßen<br /> <br /> [NAME_PORTAL]",
Subject = "Dokument geteilt: '[DOCUMENT_TITLE]'"
},
new(){
Id = 12,
Name = "TotpSecret",
Body = "Guten Tag,<br /> <br />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.<br /> <br /> <img src=\"data:image/png;base64,[TFA_QR_CODE]\" style=\"width: 13rem; height: 13rem;\"><br /> <br />\r\n<br /> Mit freundlichen Grüßen<br /> <br /> [NAME_PORTAL]",
Subject = "2-Faktor-Verifizierung QR-Code"
}
};
}

View File

@@ -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;
/// <param name="Subject">
/// (Optional) Der neue Betreff der E-Mail. Wenn null, bleibt der vorhandene Betreff unverändert.
/// </param>
public record UpdateEmailTemplateCommand(string? Body = null, string? Subject = null)
public record UpdateEmailTemplateCommand(string? Body = null, string? Subject = null) : IRequest
{
/// <param>
/// Die Abfrage, die die E-Mail-Vorlage darstellt, die aktualisiert werden soll.

View File

@@ -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;
/// <summary>
///
/// </summary>
public class UpdateEmailTemplateCommandHandler : IRequestHandler<UpdateEmailTemplateCommand>
{
private readonly IRepository<EmailTemplate> _repository;
/// <summary>
///
/// </summary>
/// <param name="repository"></param>
public UpdateEmailTemplateCommandHandler(IRepository<EmailTemplate> repository)
{
_repository = repository;
}
public async Task Handle(UpdateEmailTemplateCommand request, CancellationToken cancel)
{
var temp = (request.EmailTemplateQuery?.Id is int id
? await _repository.ReadOrDefaultAsync<EmailTemplateDto>(t => t.Id == id, single: false, cancel)
: request!.EmailTemplateQuery!.Type is Common.Constants.EmailTemplateType type
? await _repository.ReadOrDefaultAsync<EmailTemplateDto>(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);
}
}

View File

@@ -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;
/// <summary>
///
/// </summary>
public class ReadEmailTemplateMappingProfile : Profile
{
/// <summary>
///
/// </summary>
public ReadEmailTemplateMappingProfile()
{
CreateMap<EmailTemplate, ReadEmailTemplateResponse>();
}
}

View File

@@ -1,10 +1,12 @@
namespace EnvelopeGenerator.Application.EmailTemplates.Queries.Read;
using MediatR;
namespace EnvelopeGenerator.Application.EmailTemplates.Queries.Read;
/// <summary>
/// Stellt eine Abfrage dar, um eine E-Mail-Vorlage zu lesen.
/// Diese Klasse erbt von <see cref="EmailTemplateQuery"/>.
/// </summary>
public record ReadEmailTemplateQuery : EmailTemplateQuery
public record ReadEmailTemplateQuery : EmailTemplateQuery, IRequest<ReadEmailTemplateResponse?>
{
}

View File

@@ -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;
/// <summary>
///
/// </summary>
public class ReadEmailTemplateQueryHandler : IRequestHandler<ReadEmailTemplateQuery, ReadEmailTemplateResponse?>
{
private readonly IMapper _mapper;
private readonly IEmailTemplateRepository _repository;
/// <summary>
/// Initialisiert eine neue Instanz der <see cref="EmailTemplateController"/>-Klasse.
/// </summary>
/// <param name="mapper">
/// <param name="repository">
/// Die AutoMapper-Instanz, die zum Zuordnen von Objekten verwendet wird.
/// </param>
public ReadEmailTemplateQueryHandler(IMapper mapper, IEmailTemplateRepository repository)
{
_mapper = mapper;
_repository = repository;
}
/// <summary>
///
/// </summary>
/// <param name="request"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
/// <exception cref="InvalidOperationException"></exception>
public async Task<ReadEmailTemplateResponse?> 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<ReadEmailTemplateResponse>(temp);
}
}

View File

@@ -3,18 +3,35 @@
/// <summary>
/// Stellt die Antwort für eine Abfrage von E-Mail-Vorlagen bereit.
/// </summary>
/// <param name="Id">Die eindeutige Kennung der E-Mail-Vorlage.</param>
/// <param name="Type">Der Typ der E-Mail-Vorlage.</param>
/// <param name="AddedWhen">Das Datum und die Uhrzeit, wann die Vorlage hinzugefügt wurde.</param>
/// <param name="Body">Der Inhalt (Body) der E-Mail-Vorlage. Kann null sein.</param>
/// <param name="Subject">Der Betreff der E-Mail-Vorlage. Kann null sein.</param>
/// <param name="ChangedWhen">Das Datum und die Uhrzeit, wann die Vorlage zuletzt geändert wurde. Kann null sein.</param>
public record ReadEmailTemplateResponse(
int Id,
int Type,
DateTime AddedWhen,
string? Body = null,
string? Subject = null,
DateTime? ChangedWhen = null)
public class ReadEmailTemplateResponse
{
/// <summary>
/// Die eindeutige Kennung der E-Mail-Vorlage.
/// </summary>
public int Id { get; init; }
/// <summary>
/// Der Typ der E-Mail-Vorlage.
/// </summary>
public int Type { get; init; }
/// <summary>
/// Das Datum und die Uhrzeit, wann die Vorlage hinzugefügt wurde.
/// </summary>
public DateTime AddedWhen { get; init; }
/// <summary>
/// Der Inhalt (Body) der E-Mail-Vorlage. Kann null sein.
/// </summary>
public string? Body { get; set; }
/// <summary>
/// Der Betreff der E-Mail-Vorlage. Kann null sein.
/// </summary>
public string? Subject { get; set; }
/// <summary>
/// Das Datum und die Uhrzeit, wann die Vorlage zuletzt geändert wurde. Kann null sein.
/// </summary>
public DateTime? ChangedWhen { get; set; }
}

View File

@@ -14,7 +14,7 @@ namespace EnvelopeGenerator.Application.EnvelopeReceivers.Commands.Create;
/// <param name="X">X-Position</param>
/// <param name="Y">Y-Position</param>
/// <param name="Page">Seite, auf der sie sich befindet</param>
public record Signature([Required] int X, [Required] int Y, [Required] int Page);
public record Signature([Required] double X, [Required] double Y, [Required] int Page);
/// <summary>
/// DTO für Empfänger, die erstellt oder abgerufen werden sollen.
@@ -39,8 +39,11 @@ public record ReceiverGetOrCreateCommand([Required] IEnumerable<Signature> Signa
/// <param name="DataAsByte">
/// Die Dokumentdaten im Byte-Array-Format. Wird verwendet, wenn das Dokument als Roh-Binärdaten bereitgestellt wird.
/// </param>
/// <param name="DataAsBase64">
/// Die Dokumentdaten im Base64-String-Format. Wird verwendet, wenn das Dokument als Base64-codierter String bereitgestellt wird.
/// </param>
public record DocumentCreateCommand(byte[]? DataAsByte = null, string? DataAsBase64 = null);
public record DocumentCreateCommand(byte[]? DataAsByte = null)
{
/// <summary>
/// Die Dokumentdaten im Base64-String-Format. Wird verwendet, wenn das Dokument als Base64-codierter String bereitgestellt wird.
/// </summary>
public string? DataAsBase64 { get; set; }
};
#endregion

View File

@@ -0,0 +1,12 @@
namespace EnvelopeGenerator.Application.Exceptions;
public class NotFoundException : Exception
{
public NotFoundException()
{
}
public NotFoundException(string? message) : base(message)
{
}
}

View File

@@ -4,17 +4,14 @@ using EnvelopeGenerator.Common;
namespace EnvelopeGenerator.Application.Histories.Queries.Read;
//TODO: Add sender query
/// <summary>
/// Repräsentiert eine Abfrage für die Verlaufshistorie eines Umschlags.
/// </summary>
/// <param name="EnvelopeId">Die eindeutige Kennung des Umschlags.</param>
/// <param name="Envelope">Die Abfrage, die den Umschlag beschreibt.</param>
/// <param name="Receiver">Die Abfrage, die den Empfänger beschreibt.</param>
/// <param name="Related">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.</param>
/// <param name="OnlyLast">Abfrage zur Steuerung, ob nur der aktuelle Status oder der gesamte Datensatz zurückgegeben wird.</param>
public record ReadHistoryQuery(
int EnvelopeId,
ReadEnvelopeQuery? Envelope = null,
ReadReceiverQuery? Receiver = null,
int? EnvelopeId = null,
Constants.ReferenceType? Related = null,
bool? OnlyLast = true);

View File

@@ -0,0 +1,45 @@
using Dapper;
using EnvelopeGenerator.Application.Contracts.SQLExecutor;
using EnvelopeGenerator.Domain.Entities;
namespace EnvelopeGenerator.Application.SQL;
/// <summary>
///
/// </summary>
public class DocumentCreateReadSQL : ISQL<EnvelopeDocument>
{
/// <summary>
///
/// </summary>
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
";
/// <summary>
///
/// </summary>
/// <param name="base64"></param>
/// <param name="envelope_uuid"></param>
/// <returns></returns>
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;
}
}

View File

@@ -14,12 +14,11 @@ public class EnvelopeReceiverAddReadSQL : ISQL<Envelope>
/// </summary>
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<Envelope>
WHERE [GUID] = @OUT_RECEIVER_ID;
";
/// <summary>
///
/// </summary>
/// <param name="envelope_uuid"></param>
/// <param name="emailAddress"></param>
/// <param name="salutation"></param>
/// <param name="phone"></param>
/// <returns></returns>
/// <summary>
///
/// </summary>
/// <param name="envelope_uuid"></param>
/// <param name="emailAddress"></param>
/// <param name="salutation"></param>
/// <param name="phone"></param>
/// <returns></returns>
public static DynamicParameters CreateParameters(string envelope_uuid, string emailAddress, string? salutation = null, string? phone = null)
{
var parameters = new DynamicParameters();

View File

@@ -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<IEnvelopeReceiverReposit
: Result.Success(code);
}
public async Task<DataResult<IEnumerable<EnvelopeReceiverDto>>> ReadByUsernameAsync(string username, int? min_status = null, int? max_status = null, params int[] ignore_statuses)
public async Task<DataResult<IEnumerable<EnvelopeReceiverDto>>> 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<IEnumerable<EnvelopeReceiverDto>>(er_list);
return Result.Success(dto_list);
}

View File

@@ -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; }

View File

@@ -5,6 +5,12 @@ 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;
using System.Threading.Tasks;
using DigitalData.UserManager.Application.Services;
using EnvelopeGenerator.Application.Exceptions;
namespace EnvelopeGenerator.GeneratorAPI.Controllers;
@@ -17,17 +23,27 @@ namespace EnvelopeGenerator.GeneratorAPI.Controllers;
[Authorize]
public class EmailTemplateController : ControllerBase
{
private readonly ILogger<EmailTemplateController> _logger;
private readonly IMapper _mapper;
private readonly IEmailTemplateRepository _repository;
private readonly IMediator _mediator;
/// <summary>
/// Initialisiert eine neue Instanz der <see cref="EmailTemplateController"/>-Klasse.
/// </summary>
/// <param name="mapper">
/// <param name="repository">
/// Die AutoMapper-Instanz, die zum Zuordnen von Objekten verwendet wird.
/// </param>
public EmailTemplateController(IMapper mapper)
public EmailTemplateController(IMapper mapper, IEmailTemplateRepository repository, ILogger<EmailTemplateController> logger, IMediator mediator)
{
_mapper = mapper;
_repository = repository;
_logger = logger;
_mediator = mediator;
}
/// <summary>
@@ -45,10 +61,26 @@ public class EmailTemplateController : ControllerBase
/// <response code="401">Wenn der Benutzer nicht authentifiziert ist.</response>
/// <response code="404">Wenn die gesuchte Abfrage nicht gefunden wird.</response>
[HttpGet]
public IActionResult Get([FromQuery] ReadEmailTemplateQuery? emailTemplate = null)
public async Task<IActionResult> 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<IEnumerable<EmailTemplateDto>>(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);
}
}
/// <summary>
@@ -73,19 +105,32 @@ public class EmailTemplateController : ControllerBase
/// <response code="401">Wenn der Benutzer nicht authentifiziert ist.</response>
/// <response code="404">Wenn die gesuchte Abfrage nicht gefunden wird.</response>
[HttpPut]
public IActionResult Update([FromQuery] EmailTemplateQuery email, [FromBody] UpdateEmailTemplateCommand? update = null)
public async Task<IActionResult> Update([FromQuery] EmailTemplateQuery email, [FromBody] UpdateEmailTemplateCommand? update = null)
{
if (update is null)
try
{
var reset = _mapper.Map<ResetEnvelopeTemplateCommand>(email);
// Logic for resetting the email template
}
else
{
update.EmailTemplateQuery = email;
// Logic for updating the email template
}
if (update is null)
{
var reset = _mapper.Map<ResetEnvelopeTemplateCommand>(email);
await _mediator.Send(new ResetEnvelopeTemplateCommand(email?.Id, email?.Type));
return Ok();
}
else
{
var reset = _mapper.Map<ResetEnvelopeTemplateCommand>(email);
await _mediator.Send(update);
return Ok();
}
return Ok(); // Placeholder for actual implementation
}
catch(NotFoundException)
{
return BadRequest();
}
catch (Exception ex)
{
_logger.LogError(ex, "An unexpected error occurred. {message}", ex.Message);
return new StatusCodeResult(StatusCodes.Status500InternalServerError);
}
}
}

View File

@@ -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);

View File

@@ -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;
@@ -37,6 +41,10 @@ public class EnvelopeReceiverController : ControllerBase
private readonly IEnvelopeReceiverExecutor _erExecutor;
private readonly IDocumentExecutor _documentExecutor;
private readonly string _cnnStr;
/// <summary>
/// Konstruktor für den EnvelopeReceiverController.
/// </summary>
@@ -46,7 +54,7 @@ public class EnvelopeReceiverController : ControllerBase
/// <param name="mapper"></param>
/// <param name="envelopeExecutor"></param>
/// <param name="erExecutor"></param>
public EnvelopeReceiverController(ILogger<EnvelopeReceiverController> logger, IEnvelopeReceiverService envelopeReceiverService, IMediator mediator, IMapper mapper, IEnvelopeExecutor envelopeExecutor, IEnvelopeReceiverExecutor erExecutor)
public EnvelopeReceiverController(ILogger<EnvelopeReceiverController> logger, IEnvelopeReceiverService envelopeReceiverService, IMediator mediator, IMapper mapper, IEnvelopeExecutor envelopeExecutor, IEnvelopeReceiverExecutor erExecutor, IDocumentExecutor documentExecutor, IOptions<ConnectionString> csOpt)
{
_logger = logger;
_erService = envelopeReceiverService;
@@ -54,6 +62,8 @@ public class EnvelopeReceiverController : ControllerBase
_mapper = mapper;
_envelopeExecutor = envelopeExecutor;
_erExecutor = erExecutor;
_documentExecutor = documentExecutor;
_cnnStr = csOpt.Value.Value;
}
/// <summary>
@@ -83,7 +93,14 @@ 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,
max_status:envelopeReceiver.Status?.Max,
envelopeQuery: envelopeReceiver.Envelope,
receiverQuery: envelopeReceiver.Receiver,
ignore_statuses: envelopeReceiver.Status?.Ignore ?? Array.Empty<int>())
.ThenAsync(
Success: Ok,
Fail: IActionResult (msg, ntc) =>
{
@@ -180,8 +197,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<EnvelopeReceiver> sentRecipients = new();
List<ReceiverGetOrCreateCommand> unsentRecipients = new();
@@ -198,6 +218,104 @@ public class EnvelopeReceiverController : ControllerBase
var res = _mapper.Map<CreateEnvelopeReceiverResponse>(envelope);
res.UnsentRecipients = unsentRecipients;
res.SentRecipients = _mapper.Map<IEnumerable<ReceiverReadDto>>(sentRecipients);
#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
#region Add document element
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;
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<Signature>())
{
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
#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);
}
@@ -207,4 +325,26 @@ public class EnvelopeReceiverController : ControllerBase
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
/// <summary>
///
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public static bool IsBase64String(string input)
{
if (string.IsNullOrWhiteSpace(input))
return false;
try
{
Convert.FromBase64String(input);
return true;
}
catch (FormatException)
{
return false;
}
}
}

View File

@@ -131,6 +131,8 @@ public class HistoryController : ControllerBase
[Authorize]
public async Task<IActionResult> GetAllAsync([FromQuery] ReadHistoryQuery history)
{
bool withReceiver = false;
bool withSender = false;

View File

@@ -38,17 +38,24 @@ public class ReceiverController : CRUDControllerBaseWithErrorHandling<IReceiverS
[HttpGet]
public async Task<IActionResult> 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)

View File

@@ -10,9 +10,9 @@
<Authors>Digital Data GmbH</Authors>
<Company>Digital Data GmbH</Company>
<Product>EnvelopeGenerator.GeneratorAPI</Product>
<Version>1.2.0</Version>
<FileVersion>1.2.0</FileVersion>
<AssemblyVersion>1.2.0</AssemblyVersion>
<Version>1.2.1</Version>
<FileVersion>1.2.1</FileVersion>
<AssemblyVersion>1.2.1</AssemblyVersion>
<PackageOutputPath>Copyright © 2025 Digital Data GmbH. All rights reserved.</PackageOutputPath>
<DocumentationFile>bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml</DocumentationFile>
</PropertyGroup>

View File

@@ -0,0 +1,12 @@
namespace EnvelopeGenerator.GeneratorAPI.Models;
/// <summary>
///
/// </summary>
public class ConnectionString
{
/// <summary>
///
/// </summary>
public string Value { get; set; } = string.Empty;
}

View File

@@ -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<ConnectionString>(cs => cs.Value = connStr);
builder.Services.AddDbContext<EGDbContext>(options => options.UseSqlServer(connStr));
builder.Services.AddAuthHubClient(config.GetSection("AuthClientParams"));

View File

@@ -84,6 +84,7 @@ public static class DIExtensions
services.AddScoped<IEnvelopeExecutor, EnvelopeExecutor>();
services.AddScoped<IEnvelopeReceiverExecutor, EnvelopeReceiverExecutor>();
services.AddScoped<IDocumentExecutor, DocumentExecutor>();
if (sqlExecutorConfiguration is not null || sqlExecutorConfigureOptions is not null)
services.AddSQLExecutor(sqlExecutorConfiguration, sqlExecutorConfigureOptions);

View File

@@ -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<SQLExecutorParams> sqlExecutorParamsOptions) : base(provider, sqlExecutorParamsOptions)
{
}
public async Task<EnvelopeDocument> CreateDocumentAsync(string base64, string envelope_uuid, CancellationToken cancellation = default)
{
using var connection = new SqlConnection(Params.ConnectionString);
var sql = Provider.GetRequiredService<DocumentCreateReadSQL>();
await connection.OpenAsync(cancellation);
var parameters = DocumentCreateReadSQL.CreateParmas(base64, envelope_uuid);
var documents = await connection.QueryAsync<EnvelopeDocument>(sql.Raw, parameters);
return documents.FirstOrDefault()
?? throw new InvalidOperationException($"Document creation failed. Parameters:" +
$"base64={base64}, envelope_uuid='{envelope_uuid}'.");
}
}

View File

@@ -14,7 +14,7 @@ public class EnvelopeReceiverExecutor: SQLExecutor, IEnvelopeReceiverExecutor
{
private readonly IEnvelopeReceiverRepository _erRepository;
public EnvelopeReceiverExecutor(ILogger<EnvelopeExecutor> logger, IServiceProvider provider, IOptions<SQLExecutorParams> sqlExecutorParamsOptions, IEnvelopeReceiverRepository erRepository) : base(provider, sqlExecutorParamsOptions)
public EnvelopeReceiverExecutor(IServiceProvider provider, IOptions<SQLExecutorParams> sqlExecutorParamsOptions, IEnvelopeReceiverRepository erRepository) : base(provider, sqlExecutorParamsOptions)
{
_erRepository = erRepository;
}

View File

@@ -13,7 +13,7 @@ public class EnvelopeHistoryRepository : CRUDRepository<EnvelopeHistory, long, E
private IQueryable<EnvelopeHistory> 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);