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 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;
///
/// Controller für die Verwaltung von Umschlagempfängern.
///
///
/// Dieser Controller bietet Endpunkte für das Abrufen und Verwalten von Umschlagempfängerdaten.
///
[Route("api/[controller]")]
[Authorize]
[ApiController]
public class EnvelopeReceiverController : ControllerBase
{
private readonly ILogger _logger;
private readonly IEnvelopeReceiverService _erService;
private readonly IMediator _mediator;
private readonly IMapper _mapper;
private readonly IEnvelopeExecutor _envelopeExecutor;
private readonly IEnvelopeReceiverExecutor _erExecutor;
private readonly IDocumentExecutor _documentExecutor;
private readonly string _cnnStr;
///
/// 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, IMapper mapper, IEnvelopeExecutor envelopeExecutor, IEnvelopeReceiverExecutor erExecutor, IDocumentExecutor documentExecutor, IOptions csOpt)
{
_logger = logger;
_erService = envelopeReceiverService;
_mediator = mediator;
_mapper = mapper;
_envelopeExecutor = envelopeExecutor;
_erExecutor = erExecutor;
_documentExecutor = documentExecutor;
_cnnStr = csOpt.Value.Value;
}
///
/// Ruft eine Liste von Umschlagempfängern basierend auf den angegebenen Abfrageparametern ab.
///
/// Die Abfrageparameter für die Filterung von Umschlagempfängern.
/// Eine HTTP-Antwort mit der Liste der gefundenen Umschlagempfänger oder einem Fehlerstatus.
///
/// Dieser Endpunkt ermöglicht es, Umschlagempfänger basierend auf dem Benutzernamen und optionalen Statusfiltern abzurufen.
/// Wenn der Benutzername nicht ermittelt werden kann, wird ein Serverfehler zurückgegeben.
///
/// Die Liste der Umschlagempfänger wurde erfolgreich abgerufen.
/// Wenn kein autorisierter Token vorhanden ist
/// Ein unerwarteter Fehler ist aufgetreten.
[Authorize]
[HttpGet]
public async Task GetEnvelopeReceiver([FromQuery] ReadEnvelopeReceiverQuery envelopeReceiver)
{
try
{
var username = User.GetUsername();
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());
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);
});
}
catch (Exception ex)
{
_logger.LogError(ex, "An unexpected error occurred. {message}", ex.Message);
return new StatusCodeResult(StatusCodes.Status500InternalServerError);
}
}
///
/// Ruft den Namen des zuletzt verwendeten Empfängers basierend auf der angegebenen E-Mail-Adresse ab.
///
/// Die Abfrage, die die E-Mail-Adresse des Empfängers enthält.
/// Eine HTTP-Antwort mit dem Namen des Empfängers oder einem Fehlerstatus.
///
/// Dieser Endpunkt ermöglicht es, den Namen des zuletzt verwendeten Empfängers basierend auf der E-Mail-Adresse abzurufen.
///
/// Der Name des Empfängers wurde erfolgreich abgerufen.
/// Wenn kein autorisierter Token vorhanden ist
/// Kein Empfänger gefunden.
/// Ein unerwarteter Fehler ist aufgetreten.
[Authorize]
[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);
}
}
///
/// Datenübertragungsobjekt mit Informationen zu Umschlägen, Empfängern und Unterschriften.
///
///
/// HTTP-Antwort
///
/// Sample request:
///
/// POST /api/envelope
/// {
/// "title": "Vertragsdokument",
/// "message": "Bitte unterschreiben Sie dieses Dokument.",
/// "document": {
/// "dataAsBase64": "dGVzdC1iYXNlNjQtZGF0YQ=="
/// },
/// "receivers": [
/// {
/// "emailAddress": "example@example.com",
/// "signatures": [
/// {
/// "x": 100,
/// "y": 200,
/// "page": 1
/// }
/// ],
/// "name": "Max Mustermann",
/// "phoneNumber": "+49123456789"
/// }
/// ],
/// "tfaEnabled": false
/// }
///
///
/// Envelope-Erstellung und Sendeprozessbefehl erfolgreich
/// Wenn ein Fehler im HTTP-Body auftritt
/// Wenn kein autorisierter Token vorhanden ist
/// Es handelt sich um einen unerwarteten Fehler. Die Protokolle sollten überprüft werden.
[Authorize]
[HttpPost]
public async Task CreateAsync([FromBody] CreateEnvelopeReceiverCommand request)
{
try
{
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();
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);
#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())
{
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);
}
catch (Exception ex)
{
_logger.LogError(ex, "{Message}", ex.Message);
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;
}
}
}