Compare commits
28 Commits
dee58bec4b
...
838d7e3ab8
| Author | SHA1 | Date | |
|---|---|---|---|
| 838d7e3ab8 | |||
| 542d80c439 | |||
| 145a2ebe8f | |||
| 9cf776fa98 | |||
| 68878c0fc8 | |||
| d3e5d3d791 | |||
| a7f6b94d20 | |||
| 777f20eddb | |||
| c14ffceee4 | |||
| e9202ad23e | |||
| 954eff7101 | |||
| ac501dffb1 | |||
| baf2207d03 | |||
| 6863ada4be | |||
| 8a22075abe | |||
| bcb2e79fa1 | |||
| c8dae1d8ff | |||
| cc2db8716e | |||
| b939e19334 | |||
| 16e769d916 | |||
| befbacad7c | |||
| aa1e218b37 | |||
| ab9a6cd595 | |||
| 8783cb9cd8 | |||
| e49be2b7c3 | |||
| 14a565d202 | |||
| dc42a76f31 | |||
| 79dc4ba599 |
@@ -5,6 +5,7 @@ using EnvelopeGenerator.Application.Dto.EnvelopeReceiverReadOnly;
|
||||
using EnvelopeGenerator.Application.Dto.Messaging;
|
||||
using EnvelopeGenerator.Application.Dto.Receiver;
|
||||
using EnvelopeGenerator.Application.Extensions;
|
||||
using EnvelopeGenerator.Application.Receivers.Commands;
|
||||
using EnvelopeGenerator.Domain.Entities;
|
||||
|
||||
namespace EnvelopeGenerator.Application.Dto;
|
||||
@@ -34,8 +35,6 @@ public class MappingProfile : Profile
|
||||
CreateMap<Domain.Entities.EnvelopeReceiver, EnvelopeReceiverSecretDto>();
|
||||
CreateMap<EnvelopeType, EnvelopeTypeDto>();
|
||||
CreateMap<Domain.Entities.Receiver, ReceiverReadDto>();
|
||||
CreateMap<Domain.Entities.Receiver, ReceiverCreateDto>();
|
||||
CreateMap<Domain.Entities.Receiver, ReceiverUpdateDto>();
|
||||
CreateMap<Domain.Entities.EnvelopeReceiverReadOnly, EnvelopeReceiverReadOnlyDto>();
|
||||
|
||||
// DTO to Entity mappings
|
||||
@@ -50,8 +49,6 @@ public class MappingProfile : Profile
|
||||
CreateMap<EnvelopeReceiverDto, Domain.Entities.EnvelopeReceiver>();
|
||||
CreateMap<EnvelopeTypeDto, EnvelopeType>();
|
||||
CreateMap<ReceiverReadDto, Domain.Entities.Receiver>().ForMember(rcv => rcv.EnvelopeReceivers, rcvReadDto => rcvReadDto.Ignore());
|
||||
CreateMap<ReceiverCreateDto, Domain.Entities.Receiver>();
|
||||
CreateMap<ReceiverUpdateDto, Domain.Entities.Receiver>();
|
||||
CreateMap<Domain.Entities.EnvelopeReceiver, EnvelopeReceiverBasicDto>();
|
||||
CreateMap<EnvelopeReceiverReadOnlyCreateDto, Domain.Entities.EnvelopeReceiverReadOnly>();
|
||||
CreateMap<EnvelopeReceiverReadOnlyUpdateDto, Domain.Entities.EnvelopeReceiverReadOnly>();
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace EnvelopeGenerator.Application.Dto.Receiver;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
[ApiExplorerSettings(IgnoreApi = true)]
|
||||
public record ReceiverCreateDto
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public ReceiverCreateDto()
|
||||
{
|
||||
_sha256HexOfMail = new(() =>
|
||||
{
|
||||
var bytes_arr = Encoding.UTF8.GetBytes(EmailAddress!.ToUpper());
|
||||
var hash_arr = SHA256.HashData(bytes_arr);
|
||||
var hexa_str = BitConverter.ToString(hash_arr);
|
||||
return hexa_str.Replace("-", string.Empty);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
[EmailAddress]
|
||||
public required string EmailAddress { get; init; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string? TotpSecretkey { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// var bytes_arr = Encoding.UTF8.GetBytes(EmailAddress.ToUpper());<br/>
|
||||
/// var hash_arr = SHA256.HashData(bytes_arr);
|
||||
/// var hexa_str = BitConverter.ToString(hash_arr);
|
||||
/// return hexa_str.Replace("-", string.Empty);
|
||||
/// </summary>
|
||||
public string Signature => _sha256HexOfMail.Value;
|
||||
|
||||
private readonly Lazy<string> _sha256HexOfMail;
|
||||
|
||||
/// <summary>
|
||||
/// Default value is DateTime.Now
|
||||
/// </summary>
|
||||
public DateTime AddedWhen { get; } = DateTime.Now;
|
||||
};
|
||||
@@ -7,18 +7,20 @@ namespace EnvelopeGenerator.Application.EnvelopeReceivers.Commands;
|
||||
/// <summary>
|
||||
/// Befehl zur Erstellung eines Umschlags.
|
||||
/// </summary>
|
||||
/// <param name="Title">Der Titel des Umschlags. Dies ist ein Pflichtfeld.</param>
|
||||
/// <param name="Message">Die Nachricht, die im Umschlag enthalten sein soll. Dies ist ein Pflichtfeld.</param>
|
||||
/// <param name="Document">Das mit dem Umschlag verknüpfte Dokument. Dies ist ein Pflichtfeld.</param>
|
||||
/// <param name="Receivers">Eine Sammlung von Empfängern, die den Umschlag erhalten. Dies ist ein Pflichtfeld.</param>
|
||||
/// <param name="TFAEnabled">Gibt an, ob die Zwei-Faktor-Authentifizierung für den Umschlag aktiviert ist. Standardmäßig false.</param>
|
||||
public record CreateEnvelopeReceiverCommand(
|
||||
[Required] string Title,
|
||||
[Required] string Message,
|
||||
[Required] DocumentCreateCommand Document,
|
||||
[Required] IEnumerable<ReceiverGetOrCreateCommand> Receivers,
|
||||
bool TFAEnabled = false
|
||||
) : CreateEnvelopeCommand(Title, Message, TFAEnabled), IRequest<CreateEnvelopeReceiverResponse>;
|
||||
public record CreateEnvelopeReceiverCommand : CreateEnvelopeCommand, IRequest<CreateEnvelopeReceiverResponse>
|
||||
{
|
||||
/// <summary>
|
||||
/// Das mit dem Umschlag verknüpfte Dokument. Dies ist ein Pflichtfeld.
|
||||
/// </summary>
|
||||
[Required]
|
||||
public required DocumentCreateCommand Document { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Eine Sammlung von Empfängern, die den Umschlag erhalten. Dies ist ein Pflichtfeld.
|
||||
/// </summary>
|
||||
[Required]
|
||||
public List<ReceiverGetOrCreateCommand> Receivers { get; set; } = new();
|
||||
}
|
||||
|
||||
#region Subcommands
|
||||
/// <summary>
|
||||
@@ -33,19 +35,37 @@ public record Signature([Required] double X, [Required] double Y, [Required] int
|
||||
/// DTO für Empfänger, die erstellt oder abgerufen werden sollen.
|
||||
/// Wenn nicht, wird sie erstellt und mit einer Signatur versehen.
|
||||
/// </summary>
|
||||
/// <param name="Signatures">Unterschriften auf Dokumenten.</param>
|
||||
/// <param name="Salution">Der Name, mit dem der Empfänger angesprochen werden soll. Bei Null oder keinem Wert wird der zuletzt verwendete Name verwendet.</param>
|
||||
/// <param name="PhoneNumber">Sollte mit Vorwahl geschrieben werden</param>
|
||||
public record ReceiverGetOrCreateCommand([Required] IEnumerable<Signature> Signatures, string? Salution = null, string? PhoneNumber = null)
|
||||
public class ReceiverGetOrCreateCommand
|
||||
{
|
||||
/// <summary>
|
||||
/// Unterschriften auf Dokumenten.
|
||||
/// </summary>
|
||||
[Required]
|
||||
public List<Signature> Signatures { get; init; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Der Name, mit dem der Empfänger angesprochen werden soll.
|
||||
/// Bei Null oder keinem Wert wird der zuletzt verwendete Name verwendet.
|
||||
/// </summary>
|
||||
public string? Salution { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Sollte mit Vorwahl geschrieben werden
|
||||
/// </summary>
|
||||
public string? PhoneNumber { get; init; }
|
||||
|
||||
private string _emailAddress = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// E-Mail-Adresse des Empfängers.
|
||||
/// </summary>
|
||||
[Required]
|
||||
public string EmailAddress { get => _emailAddress.ToLower(); init => _emailAddress = value.ToLower(); }
|
||||
};
|
||||
public string EmailAddress
|
||||
{
|
||||
get => _emailAddress.ToLower();
|
||||
init => _emailAddress = value.ToLower();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// DTO zum Erstellen eines Dokuments.
|
||||
|
||||
@@ -42,9 +42,7 @@ public class CreateEnvelopeReceiverCommandHandler : IRequestHandler<CreateEnvelo
|
||||
/// <returns>A task representing the asynchronous operation.</returns>
|
||||
public async Task<CreateEnvelopeReceiverResponse> Handle(CreateEnvelopeReceiverCommand request, CancellationToken cancel)
|
||||
{
|
||||
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);
|
||||
var envelope = await _envelopeExecutor.CreateEnvelopeAsync(request.UserId, request.Title, request.Message, request.TFAEnabled, cancel);
|
||||
|
||||
List<EnvelopeReceiver> sentRecipients = new();
|
||||
List<ReceiverGetOrCreateCommand> unsentRecipients = new();
|
||||
|
||||
@@ -1,28 +1,33 @@
|
||||
using EnvelopeGenerator.Application.Dto;
|
||||
using EnvelopeGenerator.Application.Envelopes.Queries;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace EnvelopeGenerator.Application.Envelopes.Commands;
|
||||
|
||||
/// <summary>
|
||||
/// Befehl zur Erstellung eines Umschlags.
|
||||
/// </summary>
|
||||
/// <param name="Title">Der Titel des Umschlags. Dies ist ein Pflichtfeld.</param>
|
||||
/// <param name="Message">Die Nachricht, die im Umschlag enthalten sein soll. Dies ist ein Pflichtfeld.</param>
|
||||
/// <param name="TFAEnabled">Gibt an, ob die Zwei-Faktor-Authentifizierung für den Umschlag aktiviert ist. Standardmäßig false.</param>
|
||||
public record CreateEnvelopeCommand(
|
||||
[Required] string Title,
|
||||
[Required] string Message,
|
||||
bool TFAEnabled = false
|
||||
) : IRequest<EnvelopeDto?>
|
||||
public record CreateEnvelopeCommand : IRequest<EnvelopeDto?>
|
||||
{
|
||||
/// <summary>
|
||||
/// Id of receiver
|
||||
/// Der Titel des Umschlags. Dies ist ein Pflichtfeld.
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
[BindNever]
|
||||
public int? UserId { get; set; }
|
||||
};
|
||||
[Required]
|
||||
public required string Title { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Die Nachricht, die im Umschlag enthalten sein soll. Dies ist ein Pflichtfeld.
|
||||
/// </summary>
|
||||
[Required]
|
||||
public required string Message { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gibt an, ob die Zwei-Faktor-Authentifizierung für den Umschlag aktiviert ist. Standardmäßig false.
|
||||
/// </summary>
|
||||
public bool TFAEnabled { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// ID des Absenders
|
||||
/// </summary>
|
||||
public int UserId { get; set; }
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
using DigitalData.Core.Abstraction.Application;
|
||||
using DigitalData.Core.Abstraction.Application.DTO;
|
||||
using EnvelopeGenerator.Application.Dto.Receiver;
|
||||
using EnvelopeGenerator.Application.Receivers.Commands;
|
||||
using EnvelopeGenerator.Domain.Entities;
|
||||
|
||||
namespace EnvelopeGenerator.Application.Interfaces.Services;
|
||||
@@ -9,7 +10,7 @@ namespace EnvelopeGenerator.Application.Interfaces.Services;
|
||||
///
|
||||
/// </summary>
|
||||
[Obsolete("Use MediatR")]
|
||||
public interface IReceiverService : ICRUDService<ReceiverCreateDto, ReceiverReadDto, Receiver, int>
|
||||
public interface IReceiverService : ICRUDService<CreateReceiverCommand, ReceiverReadDto, Receiver, int>
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
using DigitalData.Core.Abstraction.Application.Repository;
|
||||
using EnvelopeGenerator.Domain.Entities;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace EnvelopeGenerator.Application.Receivers.Commands;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
[ApiExplorerSettings(IgnoreApi = true)]
|
||||
public record CreateReceiverCommand : IRequest<(int Id, bool AlreadyExists)>
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
[EmailAddress]
|
||||
public required string EmailAddress { get; init; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string? TotpSecretkey { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// var bytes_arr = Encoding.UTF8.GetBytes(EmailAddress.ToUpper());<br/>
|
||||
/// var hash_arr = SHA256.HashData(bytes_arr);
|
||||
/// var hexa_str = BitConverter.ToString(hash_arr);
|
||||
/// return hexa_str.Replace("-", string.Empty);
|
||||
/// </summary>
|
||||
public string Signature
|
||||
{
|
||||
get
|
||||
{
|
||||
var bytes_arr = Encoding.UTF8.GetBytes(EmailAddress!.ToUpper());
|
||||
var hash_arr = SHA256.HashData(bytes_arr);
|
||||
var hexa_str = BitConverter.ToString(hash_arr);
|
||||
return hexa_str.Replace("-", string.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default value is DateTime.Now
|
||||
/// </summary>
|
||||
public DateTime AddedWhen { get; } = DateTime.Now;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public class CreateReceiverCommandHandler : IRequestHandler<CreateReceiverCommand, (int Id, bool AlreadyExists)>
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
private readonly IRepository<Receiver> _repo;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="repo"></param>
|
||||
public CreateReceiverCommandHandler(IRepository<Receiver> repo)
|
||||
{
|
||||
_repo = repo;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="request"></param>
|
||||
/// <param name="cancel"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<(int Id, bool AlreadyExists)> Handle(CreateReceiverCommand request, CancellationToken cancel)
|
||||
{
|
||||
var receiver = await _repo.ReadOnly()
|
||||
.Where(r => r.EmailAddress == request.EmailAddress)
|
||||
.SingleOrDefaultAsync(cancel);
|
||||
|
||||
var alreadyExists = receiver is not null;
|
||||
|
||||
if (!alreadyExists)
|
||||
receiver = await _repo.CreateAsync(request, cancel);
|
||||
|
||||
return (receiver!.Id, alreadyExists);
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace EnvelopeGenerator.Application.Dto.Receiver;
|
||||
namespace EnvelopeGenerator.Application.Receivers.Commands;
|
||||
|
||||
/// <summary>
|
||||
/// Data Transfer Object for updating a receiver's information.
|
||||
/// </summary>
|
||||
[ApiExplorerSettings(IgnoreApi = true)]
|
||||
public class ReceiverUpdateDto
|
||||
public class UpdateReceiverCommand
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the unique identifier of the receiver.
|
||||
23
EnvelopeGenerator.Application/Receivers/MappingProfile.cs
Normal file
23
EnvelopeGenerator.Application/Receivers/MappingProfile.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using AutoMapper;
|
||||
using EnvelopeGenerator.Application.Receivers.Commands;
|
||||
using EnvelopeGenerator.Domain.Entities;
|
||||
|
||||
namespace EnvelopeGenerator.Application.Receivers;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public class MappingProfile : Profile
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public MappingProfile()
|
||||
{
|
||||
CreateMap<Receiver, UpdateReceiverCommand>();
|
||||
CreateMap<UpdateReceiverCommand, Receiver>();
|
||||
|
||||
CreateMap<Receiver, CreateReceiverCommand>();
|
||||
CreateMap<CreateReceiverCommand, Receiver>();
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ using EnvelopeGenerator.Application.Interfaces.Repositories;
|
||||
using EnvelopeGenerator.Application.Dto.Receiver;
|
||||
using DigitalData.Core.Abstraction.Application.DTO;
|
||||
using EnvelopeGenerator.Application.Interfaces.Services;
|
||||
using EnvelopeGenerator.Application.Receivers.Commands;
|
||||
|
||||
namespace EnvelopeGenerator.Application.Services;
|
||||
|
||||
@@ -12,7 +13,7 @@ namespace EnvelopeGenerator.Application.Services;
|
||||
///
|
||||
/// </summary>
|
||||
[Obsolete("Use MediatR")]
|
||||
public class ReceiverService : CRUDService<IReceiverRepository, ReceiverCreateDto, ReceiverReadDto, Receiver, int>, IReceiverService
|
||||
public class ReceiverService : CRUDService<IReceiverRepository, CreateReceiverCommand, ReceiverReadDto, Receiver, int>, IReceiverService
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
using DigitalData.Core.Abstraction.Application.Repository;
|
||||
using DigitalData.UserManager.Domain.Entities;
|
||||
using MediatR;
|
||||
|
||||
namespace EnvelopeGenerator.Application.Users.Commands;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public record CreateUserCommand : IRequest<int>
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string? Prename { get; init; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string? Name { get; init; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public required string Username { get; init; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string? Shortname { get; init; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string? Email { get; init; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string Language { get; init; } = "de-DE";
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string? Comment { get; init; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public bool Deleted { get; } = false;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string DateFormat { get; init; } = "dd.MM.yyyy";
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public bool Active { get; } = true;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string GeneralViewer { get; init; } = "NONE";
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public bool WanEnvironment { get; } = false;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public int UserIdFkIntEcm { get; init; } = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public class CreateUserCommandHandler : IRequestHandler<CreateUserCommand, int>
|
||||
{
|
||||
private readonly IRepository<User> _repo;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="repo"></param>
|
||||
public CreateUserCommandHandler(IRepository<User> repo)
|
||||
{
|
||||
_repo = repo;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="request"></param>
|
||||
/// <param name="cancel"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<int> Handle(CreateUserCommand request, CancellationToken cancel = default)
|
||||
{
|
||||
var user = await _repo.CreateAsync(request, cancel);
|
||||
return user.Id;
|
||||
}
|
||||
}
|
||||
19
EnvelopeGenerator.Application/Users/MappingProfile.cs
Normal file
19
EnvelopeGenerator.Application/Users/MappingProfile.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using AutoMapper;
|
||||
using DigitalData.UserManager.Domain.Entities;
|
||||
using EnvelopeGenerator.Application.Users.Commands;
|
||||
|
||||
namespace EnvelopeGenerator.Application.Users;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public class MappingProfile : Profile
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public MappingProfile()
|
||||
{
|
||||
CreateMap<CreateUserCommand, User>();
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ using EnvelopeGenerator.Application.Receivers.Queries;
|
||||
using EnvelopeGenerator.Domain.Entities;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using EnvelopeGenerator.Application.Receivers.Commands;
|
||||
|
||||
namespace EnvelopeGenerator.GeneratorAPI.Controllers;
|
||||
|
||||
@@ -20,7 +21,7 @@ namespace EnvelopeGenerator.GeneratorAPI.Controllers;
|
||||
[ApiController]
|
||||
[Authorize]
|
||||
[Obsolete("Use MediatR")]
|
||||
public class ReceiverController : CRUDControllerBaseWithErrorHandling<IReceiverService, ReceiverCreateDto, ReceiverReadDto, ReceiverUpdateDto, Receiver, int>
|
||||
public class ReceiverController : CRUDControllerBaseWithErrorHandling<IReceiverService, CreateReceiverCommand, ReceiverReadDto, UpdateReceiverCommand, Receiver, int>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initialisiert eine neue Instanz des <see cref="ReceiverController"/>-Controllers.
|
||||
@@ -75,13 +76,13 @@ public class ReceiverController : CRUDControllerBaseWithErrorHandling<IReceiverS
|
||||
/// Diese Methode ist deaktiviert und wird nicht verwendet.
|
||||
/// </summary>
|
||||
[NonAction]
|
||||
public override Task<IActionResult> Update(ReceiverUpdateDto updateDto) => base.Update(updateDto);
|
||||
public override Task<IActionResult> Update(UpdateReceiverCommand updateDto) => base.Update(updateDto);
|
||||
|
||||
/// <summary>
|
||||
/// Diese Methode ist deaktiviert und wird nicht verwendet.
|
||||
/// </summary>
|
||||
[NonAction]
|
||||
public override Task<IActionResult> Create(ReceiverCreateDto createDto)
|
||||
public override Task<IActionResult> Create(CreateReceiverCommand createDto)
|
||||
{
|
||||
return base.Create(createDto);
|
||||
}
|
||||
|
||||
@@ -54,6 +54,7 @@ public static class DIExtensions
|
||||
services.TryAddScoped<IReceiverRepository, ReceiverRepository>();
|
||||
services.TryAddScoped<IEnvelopeReceiverReadOnlyRepository, EnvelopeReceiverReadOnlyRepository>();
|
||||
|
||||
services.AddDbRepository<EGDbContext, User>(context => context.Users).UseAutoMapper();
|
||||
services.AddDbRepository<EGDbContext, Config>(context => context.Configs).UseAutoMapper();
|
||||
services.AddDbRepository<EGDbContext, DocumentReceiverElement>(context => context.DocumentReceiverElements).UseAutoMapper();
|
||||
services.AddDbRepository<EGDbContext, EnvelopeDocument>(context => context.EnvelopeDocument).UseAutoMapper();
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
<PackageReference Include="DigitalData.Core.Infrastructure" Version="2.3.1" />
|
||||
<PackageReference Include="DigitalData.Core.Infrastructure.AutoMapper" Version="1.0.3" />
|
||||
<PackageReference Include="DigitalData.EmailProfilerDispatcher" Version="3.1.1" />
|
||||
<PackageReference Include="QuestPDF" Version="2025.7.1" />
|
||||
<PackageReference Include="UserManager" Version="1.1.3" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Bogus" Version="35.6.3" />
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.0" />
|
||||
<PackageReference Include="DigitalData.Core.Abstraction.Application" Version="1.2.1" />
|
||||
<PackageReference Include="DigitalData.Core.Abstractions" Version="4.0.0" />
|
||||
|
||||
211
EnvelopeGenerator.Tests.Application/Fake.cs
Normal file
211
EnvelopeGenerator.Tests.Application/Fake.cs
Normal file
@@ -0,0 +1,211 @@
|
||||
using Bogus;
|
||||
using DigitalData.Core.Abstraction.Application.Repository;
|
||||
using DigitalData.UserManager.Domain.Entities;
|
||||
using EnvelopeGenerator.Application;
|
||||
using EnvelopeGenerator.Application.EnvelopeReceivers.Commands;
|
||||
using EnvelopeGenerator.Application.Envelopes.Commands;
|
||||
using EnvelopeGenerator.Application.Receivers.Commands;
|
||||
using EnvelopeGenerator.Application.Users.Commands;
|
||||
using EnvelopeGenerator.Infrastructure;
|
||||
using MediatR;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Infrastructure;
|
||||
|
||||
namespace EnvelopeGenerator.Tests.Application;
|
||||
|
||||
public class Fake
|
||||
{
|
||||
public static readonly Faker Provider = new("de");
|
||||
|
||||
public static Host CreateHost() => Microsoft.Extensions.Hosting.Host.CreateDefaultBuilder()
|
||||
.ConfigureAppConfiguration((context, config) =>
|
||||
{
|
||||
// add appsettings.json
|
||||
config.SetBasePath(Directory.GetCurrentDirectory());
|
||||
config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
|
||||
})
|
||||
.ConfigureServices((context, services) =>
|
||||
{
|
||||
IConfiguration configuration = context.Configuration;
|
||||
|
||||
// add Application and Infrastructure services
|
||||
#pragma warning disable CS0618
|
||||
services.AddEnvelopeGeneratorServices(configuration);
|
||||
services.AddEnvelopeGeneratorInfrastructureServices(
|
||||
(sp, options) => options.UseInMemoryDatabase("EnvelopeGeneratorTestDb"),
|
||||
context.Configuration
|
||||
);
|
||||
#pragma warning restore CS0618
|
||||
})
|
||||
.Build()
|
||||
.ToFake();
|
||||
|
||||
public class Host : IHost
|
||||
{
|
||||
#region Root
|
||||
private readonly IHost _root;
|
||||
|
||||
public Host(IHost root)
|
||||
{
|
||||
_root = root;
|
||||
}
|
||||
|
||||
public IServiceProvider Services => _root.Services;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_root.Dispose();
|
||||
}
|
||||
|
||||
public Task StartAsync(CancellationToken cancel = default)
|
||||
{
|
||||
return _root.StartAsync(cancel);
|
||||
}
|
||||
|
||||
public Task StopAsync(CancellationToken cancel = default)
|
||||
{
|
||||
return _root.StopAsync(cancel);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Shortcuts
|
||||
public IMediator Mediator => Services.GetRequiredService<IMediator>();
|
||||
|
||||
public IRepository<TEntity> GetRepository<TEntity>() => Services.GetRequiredService<IRepository<TEntity>>();
|
||||
|
||||
public async Task<Host> AddSamples()
|
||||
{
|
||||
await AddSampleReceivers();
|
||||
await AddSampleUser();
|
||||
return this;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Sample Receivers
|
||||
public List<(int Id, string EmailAddress)> _sampleReceivers = new();
|
||||
|
||||
public IEnumerable<(int Id, string EmailAddress)> SampleReceivers
|
||||
=> _sampleReceivers.Any()
|
||||
? _sampleReceivers
|
||||
: throw new InvalidOperationException(
|
||||
"No sample receivers have been initialized. Call AddSampleReceivers() before accessing this property."
|
||||
);
|
||||
|
||||
public async Task<Host> AddSampleReceivers()
|
||||
{
|
||||
var mediator = Mediator;
|
||||
foreach (var cmd in Provider.CreateReceiverCommands())
|
||||
{
|
||||
var (Id, _) = await mediator.Send(cmd);
|
||||
_sampleReceivers.Add((Id, cmd.EmailAddress));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Sample User
|
||||
private User? _user;
|
||||
|
||||
public User User => _user ?? throw new InvalidOperationException(
|
||||
"The 'User' instance has not been initialized. Call AddSampleUser() before accessing this property.");
|
||||
|
||||
public async Task<Host> AddSampleUser()
|
||||
{
|
||||
var repo = GetRepository<User>();
|
||||
var cmd = Provider.CreateUserCommand();
|
||||
_user = await repo.CreateAsync(cmd);
|
||||
return this;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
public static class Extensions
|
||||
{
|
||||
public static Fake.Host ToFake(this IHost host) => new(host);
|
||||
|
||||
#region Receiver Command
|
||||
public static CreateReceiverCommand CreateReceiverCommand(this Faker fake) => new()
|
||||
{
|
||||
EmailAddress = fake.Internet.Email(),
|
||||
};
|
||||
|
||||
public static List<CreateReceiverCommand> CreateReceiverCommands(this Faker fake, int minCount = 10, int maxCount = 20)
|
||||
=> Enumerable.Range(0, fake.Random.Number(minCount, maxCount))
|
||||
.Select(_ => fake.CreateReceiverCommand())
|
||||
.ToList();
|
||||
#endregion
|
||||
|
||||
#region Envelope Command
|
||||
public static CreateEnvelopeCommand CreateEnvelopeCommand(this Faker fake, int userId) => new()
|
||||
{
|
||||
Message = fake.Lorem.Paragraph(fake.Random.Number(2, 5)),
|
||||
Title = fake.Lorem.Paragraph(fake.Random.Number(1, 2)),
|
||||
UserId = userId
|
||||
};
|
||||
|
||||
public static List<CreateEnvelopeCommand> CreateEnvelopeCommands(this Faker fake, params int[] userIDs)
|
||||
=> Enumerable.Range(0, userIDs.Length)
|
||||
.Select(fake.CreateEnvelopeCommand)
|
||||
.ToList();
|
||||
#endregion
|
||||
|
||||
#region Envelope Document
|
||||
public static string CreatePdfAsBase64(this Faker faker)
|
||||
{
|
||||
string name = faker.Name.FullName();
|
||||
string address = faker.Address.FullAddress();
|
||||
string lorem = faker.Lorem.Paragraphs(2);
|
||||
|
||||
QuestPDF.Settings.License = LicenseType.Community;
|
||||
var document = Document.Create(container =>
|
||||
{
|
||||
container.Page(page =>
|
||||
{
|
||||
page.Margin(50);
|
||||
page.Header().Text("Random PDF").FontSize(20).Bold();
|
||||
page.Content().Column(col =>
|
||||
{
|
||||
col.Item().Text($"Vor- und Nachname: {name}");
|
||||
col.Item().Text($"Adresse: {address}");
|
||||
col.Item().Text(lorem);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
using var ms = new MemoryStream();
|
||||
document.GeneratePdf(ms);
|
||||
return Convert.ToBase64String(ms.ToArray());
|
||||
}
|
||||
|
||||
public static DocumentCreateCommand CreateDocumentCommand(this Faker faker) => new()
|
||||
{
|
||||
DataAsBase64 = faker.CreatePdfAsBase64()
|
||||
};
|
||||
|
||||
public static List<DocumentCreateCommand> CreateDocumentCommands(this Faker fake, int minCount = 10, int maxCount = 20)
|
||||
=> Enumerable.Range(0, fake.Random.Number(minCount, maxCount))
|
||||
.Select(_ => fake.CreateDocumentCommand())
|
||||
.ToList();
|
||||
#endregion
|
||||
|
||||
#region User Command
|
||||
public static CreateUserCommand CreateUserCommand(this Faker fake) => new()
|
||||
{
|
||||
Prename = fake.Name.FirstName(),
|
||||
Name = fake.Name.LastName(),
|
||||
Username = fake.Internet.UserName(),
|
||||
Shortname = fake.Random.String2(3, 8),
|
||||
Email = fake.Internet.Email()
|
||||
};
|
||||
|
||||
public static List<CreateUserCommand> CreateUserCommands(this Faker fake, int minCount = 10, int maxCount = 20)
|
||||
=> Enumerable.Range(0, fake.Random.Number(minCount, maxCount))
|
||||
.Select(_ => fake.CreateUserCommand())
|
||||
.ToList();
|
||||
#endregion
|
||||
}
|
||||
@@ -1,47 +1,19 @@
|
||||
using EnvelopeGenerator.Application;
|
||||
using EnvelopeGenerator.Application.Histories.Commands;
|
||||
using EnvelopeGenerator.Application.Histories.Commands;
|
||||
using EnvelopeGenerator.Application.Histories.Queries;
|
||||
using EnvelopeGenerator.Domain;
|
||||
using EnvelopeGenerator.Infrastructure;
|
||||
using MediatR;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
namespace EnvelopeGenerator.Tests.Application;
|
||||
|
||||
[TestFixture]
|
||||
public class HistoryTests
|
||||
{
|
||||
private IHost _host;
|
||||
|
||||
private IServiceProvider Provider => _host.Services;
|
||||
private Fake.Host _host;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
public async Task Setup()
|
||||
{
|
||||
_host = Host.CreateDefaultBuilder()
|
||||
.ConfigureAppConfiguration((context, config) =>
|
||||
{
|
||||
// add appsettings.json
|
||||
config.SetBasePath(Directory.GetCurrentDirectory());
|
||||
config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
|
||||
})
|
||||
.ConfigureServices((context, services) =>
|
||||
{
|
||||
IConfiguration configuration = context.Configuration;
|
||||
|
||||
// add Application and Infrastructure services
|
||||
#pragma warning disable CS0618
|
||||
services.AddEnvelopeGeneratorServices(configuration);
|
||||
services.AddEnvelopeGeneratorInfrastructureServices(
|
||||
(sp, options) => options.UseInMemoryDatabase("EnvelopeGeneratorTestDb"),
|
||||
context.Configuration
|
||||
);
|
||||
#pragma warning restore CS0618
|
||||
})
|
||||
.Build();
|
||||
_host = Fake.CreateHost();
|
||||
await _host.AddSampleReceivers();
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
@@ -50,12 +22,6 @@ public class HistoryTests
|
||||
_host.Dispose();
|
||||
}
|
||||
|
||||
private async Task<TResponse> Send<TResponse>(IRequest<TResponse> request)
|
||||
{
|
||||
var mediator = Provider.GetRequiredService<IMediator>();
|
||||
return await mediator.Send(request);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task CreateHistory_And_ReadHistory_Should_Work()
|
||||
{
|
||||
@@ -69,14 +35,14 @@ public class HistoryTests
|
||||
};
|
||||
|
||||
// Act
|
||||
var id = await Send(createCmd);
|
||||
var id = await _host.Mediator.Send(createCmd);
|
||||
|
||||
// Assert
|
||||
Assert.That(id, Is.Not.Null);
|
||||
|
||||
// ReadHistory sorgusu
|
||||
// ReadHistory query
|
||||
var query = new ReadHistoryQuery(1);
|
||||
var result = await Send(query);
|
||||
var result = await _host.Mediator.Send(query);
|
||||
|
||||
Assert.That(result, Is.Not.Empty);
|
||||
}
|
||||
@@ -99,11 +65,11 @@ public class HistoryTests
|
||||
Status = Constants.EnvelopeStatus.EnvelopePartlySigned
|
||||
};
|
||||
|
||||
await Send(createCmd1);
|
||||
await Send(createCmd2);
|
||||
await _host.Mediator.Send(createCmd1);
|
||||
await _host.Mediator.Send(createCmd2);
|
||||
|
||||
// Act
|
||||
var result = await Send(new ReadHistoryQuery(2, Constants.EnvelopeStatus.EnvelopePartlySigned));
|
||||
var result = await _host.Mediator.Send(new ReadHistoryQuery(2, Constants.EnvelopeStatus.EnvelopePartlySigned));
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Has.Exactly(1).Items);
|
||||
@@ -115,7 +81,7 @@ public class HistoryTests
|
||||
public async Task ReadHistory_Should_Return_Empty_When_No_Record()
|
||||
{
|
||||
// Act
|
||||
var result = await Send(new ReadHistoryQuery(999));
|
||||
var result = await _host.Mediator.Send(new ReadHistoryQuery(999));
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.Empty);
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using EnvelopeGenerator.Infrastructure;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using EnvelopeGenerator.Application.Services;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using EnvelopeGenerator.Application;
|
||||
|
||||
namespace EnvelopeGenerator.Tests.Application;
|
||||
|
||||
public class Mock
|
||||
{
|
||||
[Obsolete("Use MediatR")]
|
||||
public static IHost CreateHost(Action<HostApplicationBuilder>? builderOptions = null, string configPath = "appsettings.json", bool useRealDb = false, params string[] args)
|
||||
{
|
||||
var builder = Host.CreateApplicationBuilder(args.Any() ? args : null);
|
||||
var config = builder.Configuration;
|
||||
builder.Configuration.AddJsonFile(configPath, optional: true, reloadOnChange: true);
|
||||
|
||||
builder.Services
|
||||
.AddEnvelopeGeneratorInfrastructureServices((provider, opt) =>
|
||||
{
|
||||
if (useRealDb)
|
||||
{
|
||||
var connStr = config.GetConnectionString("Default")
|
||||
?? throw new InvalidOperationException("There is no default connection string in appsettings.json.");
|
||||
opt.UseSqlServer(connStr);
|
||||
}
|
||||
else
|
||||
opt.UseInMemoryDatabase("MockDB");
|
||||
})
|
||||
.AddEnvelopeGeneratorServices(builder.Configuration)
|
||||
.AddScoped<DocumentStatusService>();
|
||||
|
||||
builderOptions?.Invoke(builder);
|
||||
|
||||
var host = builder.Build();
|
||||
|
||||
return host;
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
namespace EnvelopeGenerator.Tests.Application.Services;
|
||||
|
||||
[TestFixture]
|
||||
public class DocumentStatusServiceTests
|
||||
{
|
||||
private IHost _host;
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
_host = Mock.CreateHost(useRealDb: true);
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
{
|
||||
_host.StopAsync();
|
||||
_host.Dispose();
|
||||
}
|
||||
}
|
||||
@@ -2,11 +2,12 @@
|
||||
using EnvelopeGenerator.Application.Interfaces.Services;
|
||||
using EnvelopeGenerator.Application.Dto.Receiver;
|
||||
using EnvelopeGenerator.Domain.Entities;
|
||||
using EnvelopeGenerator.Application.Receivers.Commands;
|
||||
|
||||
namespace EnvelopeGenerator.Web.Controllers.Test;
|
||||
|
||||
[Obsolete("Use MediatR")]
|
||||
public class TestReceiverController : CRUDControllerBase<IReceiverService, ReceiverCreateDto, ReceiverReadDto, ReceiverUpdateDto, Receiver, int>
|
||||
public class TestReceiverController : CRUDControllerBase<IReceiverService, CreateReceiverCommand, ReceiverReadDto, UpdateReceiverCommand, Receiver, int>
|
||||
{
|
||||
public TestReceiverController(ILogger<TestReceiverController> logger, IReceiverService service) : base(logger, service)
|
||||
{
|
||||
|
||||
@@ -5,8 +5,6 @@ VisualStudioVersion = 17.5.33516.290
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "EnvelopeGenerator.BBTests", "EnvelopeGenerator.BBTests\EnvelopeGenerator.BBTests.vbproj", "{089D5634-FB6B-42D0-B912-7AA7457044E7}"
|
||||
EndProject
|
||||
Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "EnvelopeGenerator.Form", "EnvelopeGenerator.Form\EnvelopeGenerator.Form.vbproj", "{6D56C01F-D6CB-4D8A-BD3D-4FD34326998C}"
|
||||
EndProject
|
||||
Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "EnvelopeGenerator.CommonServices", "EnvelopeGenerator.CommonServices\EnvelopeGenerator.CommonServices.vbproj", "{6EA0C51F-C2B1-4462-8198-3DE0B32B74F8}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EnvelopeGenerator.Web", "EnvelopeGenerator.Web\EnvelopeGenerator.Web.csproj", "{5E0E17C0-FF5A-4246-BF87-1ADD85376A27}"
|
||||
@@ -45,10 +43,6 @@ Global
|
||||
{089D5634-FB6B-42D0-B912-7AA7457044E7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{089D5634-FB6B-42D0-B912-7AA7457044E7}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{089D5634-FB6B-42D0-B912-7AA7457044E7}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{6D56C01F-D6CB-4D8A-BD3D-4FD34326998C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{6D56C01F-D6CB-4D8A-BD3D-4FD34326998C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{6D56C01F-D6CB-4D8A-BD3D-4FD34326998C}.Release|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{6D56C01F-D6CB-4D8A-BD3D-4FD34326998C}.Release|Any CPU.Build.0 = Debug|Any CPU
|
||||
{6EA0C51F-C2B1-4462-8198-3DE0B32B74F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{6EA0C51F-C2B1-4462-8198-3DE0B32B74F8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{6EA0C51F-C2B1-4462-8198-3DE0B32B74F8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
@@ -91,7 +85,6 @@ Global
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{089D5634-FB6B-42D0-B912-7AA7457044E7} = {0CBC2432-A561-4440-89BC-671B66A24146}
|
||||
{6D56C01F-D6CB-4D8A-BD3D-4FD34326998C} = {E3C758DC-914D-4B7E-8457-0813F1FDB0CB}
|
||||
{6EA0C51F-C2B1-4462-8198-3DE0B32B74F8} = {9943209E-1744-4944-B1BA-4F87FC1A0EEB}
|
||||
{5E0E17C0-FF5A-4246-BF87-1ADD85376A27} = {E3C758DC-914D-4B7E-8457-0813F1FDB0CB}
|
||||
{83ED2617-B398-4859-8F59-B38F8807E83E} = {9943209E-1744-4944-B1BA-4F87FC1A0EEB}
|
||||
|
||||
Reference in New Issue
Block a user